Fabric 1.0源代码分析(2) blockfile(区块文件存储)

# Fabric 1.0源代码笔记 之 blockfile(区块文件存储)

## 1、blockfile概述

blockfile,即Fabric区块链区块文件存储,默认目录/var/hyperledger/production/ledgersData/chains,含index和chains两个子目录。

其中index为索引目录,采用leveldb实现。而chains为各ledger的区块链文件,子目录以ledgerid为名,使用文件系统实现。

区块文件以blockfile_为前缀,最大大小默认64M。

blockfile,相关代码集中在common/ledger/blkstorage/fsblkstorage目录,目录结构如下:

* blockfile_mgr.go,blockfileMgr和checkpointInfo结构体及方法。

* block_stream.go,blockfileStream、blockStream、blockPlacementInfo结构体及方法。

* blockfile_rw.go,blockfileWriter和blockfileReader结构体及方法(blockfileReader未使用)。

* blockindex.go,index接口定义,index接口实现即blockIndex结构体及方法定义,以及blockIdxInfo、locPointer、fileLocPointer结构体及方法。

* blockfile_helper.go,定义了4个工具函数,constructCheckpointInfoFromBlockFiles、retrieveLastFileSuffix、isBlockFileName、getFileInfoOrPanic。

作用分别为:扫描最新的blockfile并重新构造检查点信息、获取最新的文件后缀、根据文件前缀判断是否为区块文件、获取文件状态信息。

* block_serialization.go,block序列化相关工具函数。

* blocks_itr.go,blocksItr结构体及方法。

## 2、Block结构体定、以及Block序列化

### 2.1、Block相关结构体

Block结构体:

```go

type Block struct {

Header *BlockHeader //BlockHeader

Data *BlockData //BlockData

Metadata *BlockMetadata

}

func (m *Block) GetHeader() *BlockHeader //获取BlockHeader,即m.Header

func (m *Block) GetData() *BlockData //获取BlockData,即m.Data

func (m *Block) GetMetadata() *BlockMetadata //m.Metadata

//代码在protos/common/common.pb.go

```

BlockHeader结构体:

```go

type BlockHeader struct {

Number uint64 //区块编号

PreviousHash [] byte //前一个区块哈希

DataHash [] byte //当前区块哈希

}

func (m *BlockHeader) GetNumber() uint64 //获取区块编号,即m.Number

func (m *BlockHeader) GetPreviousHash() [] byte //获取前一个区块哈希,即m.PreviousHash

func (m *BlockHeader) GetDataHash() [] byte //获取当前区块哈希,即m.DataHash

//代码在protos/common/common.pb.go

```

BlockData结构体:

```go

type BlockData struct {

Data [][] byte //Data,存储交易信息

}

func (m *BlockData) GetData() [][] byte //获取Data,即m.Data

//代码在protos/common/common.pb.go

```

BlockMetadata结构体:

```go

type BlockMetadata struct {

Metadata [][] byte //K/V均为[]byte格式

}

func (m *BlockMetadata) GetMetadata() [][] byte //m.Metadata

//代码在protos/common/common.pb.go

```

补充BlockMetadataIndex:

```go

type BlockMetadataIndex int32

const (

BlockMetadataIndex_SIGNATURES BlockMetadataIndex = 0

BlockMetadataIndex_LAST_CONFIG BlockMetadataIndex = 1

BlockMetadataIndex_TRANSACTIONS_FILTER BlockMetadataIndex = 2

BlockMetadataIndex_ORDERER BlockMetadataIndex = 3

)

```

### 2.2、Block序列化

serializedBlockInfo结构体定义及工具函数:

```go

type serializedBlockInfo struct {

blockHeader *common.BlockHeader //BlockHeader

txOffsets []*txindexInfo //交易索引信息

metadata *common.BlockMetadata

}

type txindexInfo struct {

txID string //交易ID

loc *locPointer //文件指针

}

//序列化区块,返回序列化后字节,以及serializedBlockInfo(含BlockHeader和交易索引信息)

func serializeBlock(block *common.Block) ([] byte , *serializedBlockInfo, error )

//反序列化区块,构建Block结构体

func deserializeBlock(serializedBlockBytes [] byte ) (*common.Block, error )

//反序列化区块,并构造serializedBlockInfo

func extractSerializedBlockInfo(serializedBlockBytes [] byte ) (*serializedBlockInfo, error )

//序列化中添加BlockHeader,即Number、DataHash和PreviousHash

func addHeaderBytes(blockHeader *common.BlockHeader, buf *proto.Buffer) error

//序列化中添加BlockData,并从BlockData中解析txid,返回交易索引信息数组

func addDataBytes(blockData *common.BlockData, buf *proto.Buffer) ([]*txindexInfo, error )

//序列化中添加Metadata

func addMetadataBytes(blockMetadata *common.BlockMetadata, buf *proto.Buffer) error

//反序列化出BlockHeader

func extractHeader(buf *ledgerutil.Buffer) (*common.BlockHeader, error )

//反序列化出BlockData,并返回交易索引信息数组

func extractData(buf *ledgerutil.Buffer) (*common.BlockData, []*txindexInfo, error )

//反序列化出Metadata

func extractMetadata(buf *ledgerutil.Buffer) (*common.BlockMetadata, error )

//从BlockData中解析出交易ID

func extractTxID(txEnvelopBytes [] byte ) ( string , error )

//代码在common/ledger/blkstorage/fsblkstorage/block_serialization.go

```

## 3、checkpointInfo结构体定义及方法

checkpointInfo,即检查点信息,结构体定义如下:

```go

type checkpointInfo struct {

latestFileChunkSuffixNum int //最新的区块文件后缀,如blockfile_000000

latestFileChunksize int //最新的区块文件大小

isChainEmpty bool //是否空链

lastBlockNumber uint64 //最新的区块编号

}

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

涉及方法如下:

```go

func (i *checkpointInfo) marshal() ([] byte , error ) //checkpointInfo序列化

func (i *checkpointInfo) unmarshal(b [] byte ) error //checkpointInfo反序列化

func (i *checkpointInfo) String() string //转换为string

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

## 4、blockfileStream相关结构体及方法

### 4.1、blockfileStream

blockfileStream定义如下:

```go

type blockfileStream struct {

fileNum int //blockfile文件后缀

file *os.File //os.File

reader *bufio.Reader //bufio.Reader

currentOffset int64 //当前偏移量

}

//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go

```

涉及方法如下:

```go

//构造blockfileStream

func newBlockfileStream(rootDir string , fileNum int , startOffset int64 ) (*blockfileStream, error )

func (s *blockfileStream) nextBlockBytes() ([] byte , error ) //下一个块,调取s.nextBlockBytesAndPlacementInfo()

//下一个块和位置信息

func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([] byte , *blockPlacementInfo, error )

func (s *blockfileStream) close() error //关闭blockfileStream

//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go

```

func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) 代码如下:

```go

var lenBytes []byte

var err error

var fileInfo os.FileInfo

moreContentAvailable := true

fileInfo , err = s.file.Stat() //获取文件状态

remainingBytes := fileInfo.Size() - s.currentOffset //文件读取剩余字节

peekBytes := 8

if remainingBytes < int64(peekBytes) { //剩余字节小于8,按实际剩余字节,否则按8

peekBytes = int(remainingBytes)

moreContentAvailable = false

}

//存储形式:前n位存储block长度length,之后length位为实际block

lenBytes , err = s.reader.Peek(peekBytes) //Peek 返回缓存的一个切片,该切片引用缓存中前 peekBytes 个字节的数据

length , n := proto.DecodeVarint(lenBytes) //从切片中读取 varint 编码的整数,它返回整数和被消耗的字节数。

err = s.reader.Discard(n) //丢弃存储block长度length的前n位

blockBytes := make ([] byte , length)

_, err = io.ReadAtLeast(s.reader, blockBytes, int(length))

blockPlacementInfo := &blockPlacementInfo{

fileNum: s.fileNum,

blockStartOffset: s.currentOffset,

blockBytesOffset: s.currentOffset + int64(n)}

s.currentOffset += int64(n) + int64(length)

return blockBytes, blockPlacementInfo, nil

//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go

```

补充blockPlacementInfo:块位置信息

```go

type blockPlacementInfo struct {

fileNum int //块文件后缀

blockStartOffset int64 //n+length,n之前

blockBytesOffset int64 //n+length,length之前

}

//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go

```

## 5、blockfileWriter结构体定义及方法

```go

type blockfileWriter struct {

filePath string //路径

file *os.File //os.File

}

func newBlockfileWriter(filePath string ) (*blockfileWriter, error ) //构造blockfileWriter,并调用writer.open()

func (w *blockfileWriter) truncateFile(targetSize int ) error //截取文件

func (w *blockfileWriter) append(b [] byte , sync bool ) error //追加文件

func (w *blockfileWriter) open() error //打开文件

func (w *blockfileWriter) close() error //关闭文件

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_rw.go

```

## 6、blockIndex相关结构体及方法

### 6.1、index接口定义

```go

type index interface {

getLastBlockIndexed() ( uint64 , error ) //获取最后一个块索引(或编号)

indexBlock(blockIdxInfo *blockIdxInfo) error //索引区块

getBlockLocByHash(blockHash [] byte ) (*fileLocPointer, error ) //根据区块哈希,获取文件区块指针

getBlockLocByBlockNum(blockNum uint64 ) (*fileLocPointer, error ) //根据区块编号,获取文件区块指针

getTxLoc(txID string ) (*fileLocPointer, error ) //根据交易ID,获取文件交易指针

getTXLocByBlockNumTranNum(blockNum uint64 , tranNum uint64 ) (*fileLocPointer, error ) //根据区块编号和交易编号,获取文件交易指针

getBlockLocByTxID(txID string ) (*fileLocPointer, error ) //根据交易ID,获取文件区块指针

getTxValidationCodeByTxID(txID string ) (peer.TxValidationCode, error ) //根据交易ID,获取交易验证代码

}

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

```

### 6.2、blockIndex结构体

blockIndex结构体定义如下:

```go

type blockIndex struct {

indexItemsMap map [blkstorage.IndexableAttr] bool //index属性映射

db *leveldbhelper.DBHandle //index leveldb操作

}

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

```

补充IndexableAttr:

```go

const (

IndexableAttrBlockNum = IndexableAttr( "BlockNum" )

IndexableAttrBlockHash = IndexableAttr( "BlockHash" )

IndexableAttrTxID = IndexableAttr( "TxID" )

IndexableAttrBlockNumTranNum = IndexableAttr( "BlockNumTranNum" )

IndexableAttrBlockTxID = IndexableAttr( "BlockTxID" )

IndexableAttrTxValidationCode = IndexableAttr( "TxValidationCode" )

)

//代码在common/ledger/blkstorage/blockstorage.go

```

涉及方法如下:

```go

//构造blockIndex

func newBlockIndex(indexConfig *blkstorage.IndexConfig, db *leveldbhelper.DBHandle) *blockIndex

//获取最后一个块索引(或编号),取key为"indexCheckpointKey"的值,即为最新的区块编号

func (index *blockIndex) getLastBlockIndexed() ( uint64 , error )

func (index *blockIndex) indexBlock(blockIdxInfo *blockIdxInfo) error //索引区块

//根据区块哈希,获取文件区块指针

func (index *blockIndex) getBlockLocByHash(blockHash [] byte ) (*fileLocPointer, error )

//根据区块编号,获取文件区块指针

func (index *blockIndex) getBlockLocByBlockNum(blockNum uint64 ) (*fileLocPointer, error )

//根据交易ID,获取文件交易指针

func (index *blockIndex) getTxLoc(txID string ) (*fileLocPointer, error )

//根据交易ID,获取文件区块指针

func (index *blockIndex) getBlockLocByTxID(txID string ) (*fileLocPointer, error )

//根据区块编号和交易编号,获取文件交易指针

func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64 , tranNum uint64 ) (*fileLocPointer, error )

//根据交易ID,获取交易验证代码

func (index *blockIndex) getTxValidationCodeByTxID(txID string ) (peer.TxValidationCode, error )

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

```

补充blockIdxInfo结构体定义:块索引信息。

```

type blockIdxInfo struct {

blockNum uint64 //区块编号

blockHash []byte //区块哈希

flp *fileLocPointer //文件指针

txOffsets []*txindexInfo //交易索引信息

metadata *common.BlockMetadata

}

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

```

补充fileLocPointer、txindexInfo和common.BlockMetadata:

```go

type locPointer struct { //定义指针

offset int //偏移位置

bytesLength int //字节长度

}

type fileLocPointer struct { //定义文件指针

fileSuffixNum int //文件后缀

locPointer //嵌入locPointer

}

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

type txindexInfo struct { //交易索引信息

txID string //交易ID

loc *locPointer //文件指针

}

//代码在common/ledger/blkstorage/fsblkstorage/block_serialization.go

type BlockMetadata struct {

Metadata [][] byte `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata,omitempty"`

}

//代码在protos/common/common.pb.go

```

func (index *blockIndex) indexBlock(blockIdxInfo *blockIdxInfo) error代码如下:

```go

flp := blockIdxInfo.flp //文件指针

txOffsets := blockIdxInfo.txOffsets //交易索引信息

txsfltr := ledgerUtil.TxValidationFlags(blockIdxInfo.metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) //type TxValidationFlags []uint8

batch := leveldbhelper.NewUpdateBatch() //leveldb批量更新

flpBytes , err := flp.marshal() //文件指针序列化,含文件后缀、偏移位置、字节长度

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockHash]; ok { //使用区块哈希索引文件区块指针

batch.Put(constructBlockHashKey(blockIdxInfo.blockHash), flpBytes) //区块哈希,blockHash:flpBytes存入leveldb

}

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockNum]; ok { //使用区块编号索引文件区块指针

batch.Put(constructBlockNumKey(blockIdxInfo.blockNum), flpBytes) //区块编号,blockNum:flpBytes存入leveldb

}

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrTxID]; ok { //使用交易ID索引文件交易指针

for _, txoffset := range txOffsets {

txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc)

txFlpBytes, marshalErr := txFlp.marshal()

batch.Put(constructTxIDKey(txoffset.txID), txFlpBytes) //交易ID,txID:txFlpBytes存入leveldb

}

}

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockNumTranNum]; ok { //使用区块编号和交易编号索引文件交易指针

for txIterator, txoffset := range txOffsets {

txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc)

txFlpBytes, marshalErr := txFlp.marshal()

batch.Put(constructBlockNumTranNumKey(blockIdxInfo.blockNum, uint64(txIterator)), txFlpBytes) //区块编号和交易编号,blockNum+txIterator:txFlpBytes

}

}

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockTxID]; ok { //使用交易ID索引文件区块指针

for _, txoffset := range txOffsets {

batch.Put(constructBlockTxIDKey(txoffset.txID), flpBytes) //交易ID,txID:flpBytes

}

}

if _, ok := index.indexItemsMap[blkstorage.IndexableAttrTxValidationCode]; ok { //使用交易ID索引交易验证代码

for idx, txoffset := range txOffsets {

batch.Put(constructTxValidationCodeIDKey(txoffset.txID), [] byte {byte(txsfltr.Flag(idx))})

}

}

batch.Put(indexCheckpointKey, encodeBlockNum(blockIdxInfo.blockNum)) //key为"indexCheckpointKey"的值,即为最新的区块编号

err := index.db.WriteBatch(batch, true ) //批量更新

}

//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go

```

## 7、blocksItr结构体及方法

```go

type blocksItr struct {

mgr *blockfileMgr //blockfileMgr

maxBlockNumAvailable uint64 //最大的区块编号

blockNumToRetrieve uint64 //起始区块编号

stream *blockStream //blockStream

closeMarker bool

closeMarkerLock *sync.Mutex

}

func newBlockItr(mgr *blockfileMgr, startBlockNum uint64 ) *blocksItr //构造blocksItr

func (itr *blocksItr) waitForBlock(blockNum uint64 ) uint64

func (itr *blocksItr) initStream() error

func (itr *blocksItr) shouldClose() bool

func (itr *blocksItr) Next() (ledger.QueryResult, error )

func (itr *blocksItr) Close()

//代码在common/ledger/blkstorage/fsblkstorage/blocks_itr.go

```

## 8、blockfileMgr结构体定义及方法

blockfileMgr结构体定义:

```go

type blockfileMgr struct {

rootDir string //ledger文件存储目录,如/var/hyperledger/production/ledgersData/chains/chains/mychannel

conf *Conf //即type Conf struct,存放路径和区块文件大小

db *leveldbhelper.DBHandle //用于操作index

index index //type index interface,其实现为blockIndex结构体

cpInfo *checkpointInfo //type checkpointInfo struct

cpInfoCond *sync.Cond //定期唤醒锁

currentFileWriter *blockfileWriter //type blockfileWriter struct

bcInfo atomic.Value //原子操作

}

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

涉及方法如下:

```go

//构建blockfileMgr

func newBlockfileMgr(id string , conf *Conf, indexConfig *blkstorage.IndexConfig, indexStore *leveldbhelper.DBHandle) *blockfileMgr

func syncCPInfoFromFS(rootDir string , cpInfo *checkpointInfo) //从文件系统中更新检查点信息

func deriveBlockfilePath(rootDir string , suffixNum int ) string //构造Blockfile路径

func (mgr *blockfileMgr) close() //关闭blockfileWriter

func (mgr *blockfileMgr) moveToNextFile() //转至下个新区块文件,打开新文件、并更新检查点信息

func (mgr *blockfileMgr) addBlock(block *common.Block) error //添加区块,区块写入文件,索引区块

func (mgr *blockfileMgr) syncIndex() error //同步区块索引

func (mgr *blockfileMgr) getBlockchainInfo() *common.BlockchainInfo //获取 BlockchainInfo

func (mgr *blockfileMgr) updateCheckpoint(cpInfo *checkpointInfo) //更新检查点信息

func (mgr *blockfileMgr) updateBlockchainInfo(latestBlockHash [] byte , latestBlock *common.Block) //更新BlockchainInfo

//按区块哈希获取块,mgr.index.getBlockLocByHash(blockHash),mgr.fetchBlock(loc)

func (mgr *blockfileMgr) retrieveBlockByHash(blockHash [] byte ) (*common.Block, error )

//按区块编号获取块,mgr.index.getBlockLocByBlockNum(blockNum),mgr.fetchBlock(loc)

func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64 ) (*common.Block, error )

//按交易ID获取块,mgr.index.getBlockLocByTxID(txID),mgr.fetchBlock(loc)

func (mgr *blockfileMgr) retrieveBlockByTxID(txID string ) (*common.Block, error )

//按交易ID获取交易验证代码,mgr.index.getTxValidationCodeByTxID(txID)

func (mgr *blockfileMgr) retrieveTxValidationCodeByTxID(txID string ) (peer.TxValidationCode, error )

//按区块编号获取BlockHeader:按区块编号从索引中取文件区块指针,按文件指针取区块Bytes,按区块Bytes构建serializedBlockInfo,取serializedBlockInfo.blockHeader

func (mgr *blockfileMgr) retrieveBlockHeaderByNumber(blockNum uint64 ) (*common.BlockHeader, error )

//构造blocksItr

func (mgr *blockfileMgr) retrieveBlocks(startNum uint64 ) (*blocksItr, error )

//按交易ID获取交易,mgr.index.getTxLoc(txID),mgr.fetchTransactionEnvelope(loc)

func (mgr *blockfileMgr) retrieveTransactionByID(txID string ) (*common.Envelope, error )

//按区块编号和交易编号获取交易,mgr.index.getTXLocByBlockNumTranNum(blockNum, tranNum),mgr.fetchTransactionEnvelope(loc)

func (mgr *blockfileMgr) retrieveTransactionByBlockNumTranNum(blockNum uint64 , tranNum uint64 ) (*common.Envelope, error )

func (mgr *blockfileMgr) fetchBlock(lp *fileLocPointer) (*common.Block, error ) //获取下一个块

//获取交易

//type Envelope struct {

//  Payload []byte

//  Signature []byte

//}

func (mgr *blockfileMgr) fetchTransactionEnvelope(lp *fileLocPointer) (*common.Envelope, error )

//按文件指针获取区块Bytes

func (mgr *blockfileMgr) fetchBlockBytes(lp *fileLocPointer) ([] byte , error )

func (mgr *blockfileMgr) fetchRawBytes(lp *fileLocPointer) ([] byte , error ) //按文件指针获取原始字节

func (mgr *blockfileMgr) loadCurrentInfo() (*checkpointInfo, error ) //获取存储在index库中最新检查点信息,key为"blkMgrInfo"

func (mgr *blockfileMgr) saveCurrentInfo(i *checkpointInfo, sync bool ) error //将最新检查点信息,序列化后存入index库

//扫描给定的块文件并检测文件中的最后完整块,返回最后一个块字节、文件最新偏移量、块数

func scanForLastCompleteBlock(rootDir string , fileNum int , startingOffset int64 ) ([] byte , int64 , int , error ) {

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

func newBlockfileMgr(id string, conf *Conf, indexConfig *blkstorage.IndexConfig, indexStore *leveldbhelper.DBHandle) *blockfileMgr实现如下:构建blockfileMgr。

```go

rootDir := conf.getLedgerBlockDir(id) //如/var/hyperledger/production/ledgersData/chains/chains/mychannel

_ , err := util.CreateDirIfMissing(rootDir) //检查rootDir是否存在,如不存在则创建

mgr := &blockfileMgr{rootDir: rootDir, conf: conf, db: indexStore} //构造blockfileMgr,包括ledger路径、块文件大小、index目录leveldb句柄

cpInfo , err := mgr.loadCurrentInfo() //获取存储在index库中最新检查点信息,key为"blkMgrInfo"

if cpInfo == nil { //找不到,第一次创建ledger或index被删除

//扫描最新的blockfile,并重新构造检查点信息

cpInfo, err = constructCheckpointInfoFromBlockFiles(rootDir)

} else {

syncCPInfoFromFS(rootDir, cpInfo) //从文件系统中更新检查点信息

}

err = mgr.saveCurrentInfo(cpInfo, true ) //将最新检查点信息,序列化后存入index库

currentFileWriter , err := newBlockfileWriter(deriveBlockfilePath(rootDir, cpInfo.latestFileChunkSuffixNum))

err = currentFileWriter.truncateFile(cpInfo.latestFileChunksize) //按最新的区块文件大小截取文件

mgr.index = newBlockIndex(indexConfig, indexStore) 构造blockIndex

mgr.cpInfo = cpInfo

mgr.cpInfoCond = sync.NewCond(&sync.Mutex{})

mgr.syncIndex()

bcInfo := &common.BlockchainInfo{

Height: 0 ,

CurrentBlockHash: nil ,

PreviousBlockHash: nil }

if !cpInfo.isChainEmpty { //如果不是空链

lastBlockHeader, err := mgr.retrieveBlockHeaderByNumber(cpInfo.lastBlockNumber) //获取最后一个块的Header

lastBlockHash := lastBlockHeader.Hash() //最后一个块的哈希

previousBlockHash := lastBlockHeader.PreviousHash //前一个块的哈希

bcInfo = &common.BlockchainInfo{ //构造区块链信息

Height: cpInfo.lastBlockNumber + 1 ,

CurrentBlockHash: lastBlockHash,

PreviousBlockHash: previousBlockHash}

}

mgr.bcInfo.Store(bcInfo) //bcInfo赋值给mgr.bcInfo

return mgr

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

func syncCPInfoFromFS(rootDir string, cpInfo *checkpointInfo)代码如下://从文件系统中更新检查点信息。

```go

filePath := deriveBlockfilePath(rootDir, cpInfo.latestFileChunkSuffixNum) //最新区块文件路径

exists , size , err := util.FileExists(filePath)

_ , endOffsetLastBlock , numBlocks , err := scanForLastCompleteBlock( //扫描最后一个完整块

rootDir, cpInfo.latestFileChunkSuffixNum, int64(cpInfo.latestFileChunksize))

cpInfo.latestFileChunksize = int(endOffsetLastBlock) //最新的区块文件大小

if cpInfo.isChainEmpty { //空链

cpInfo.lastBlockNumber = uint64(numBlocks - 1 ) //最新的区块编号

} else {

cpInfo.lastBlockNumber += uint64(numBlocks)

}

cpInfo.isChainEmpty = false //不再是空链

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

func (mgr *blockfileMgr) addBlock(block *common.Block) error代码如下:添加区块,区块写入文件,索引区块。

```go

//序列化区块,返回序列化后字节,以及serializedBlockInfo(含BlockHeader和交易索引信息)

blockBytes , info , err := serializeBlock(block)

blockHash := block.Header.Hash() //blockHash

txOffsets := info.txOffsets //交易索引信息

currentOffset := mgr.cpInfo.latestFileChunksize //最新的区块文件大小

blockBytesLen := len (blockBytes)

blockBytesEncodedLen := proto.EncodeVarint(uint64(blockBytesLen)) //blockBytesLen

totalBytesToAppend := blockBytesLen + len (blockBytesEncodedLen) //blockBytesLen + blockBytesEncodedLen

if currentOffset+totalBytesToAppend > mgr.conf.maxBlockfileSize { //超出文件大小限定,创建新文件

mgr.moveToNextFile()

currentOffset = 0

}

err = mgr.currentFileWriter. append (blockBytesEncodedLen, false ) //追加写入blockBytesLen

if err == nil {

err = mgr.currentFileWriter. append (blockBytes, true ) //追加写入blockBytes

}

if err != nil { //追加写入失败,回滚按原大小截取文件

truncateErr := mgr.currentFileWriter.truncateFile(mgr.cpInfo.latestFileChunksize)

return fmt.Errorf( "Error while appending block to file: %s" , err)

}

currentCPInfo := mgr.cpInfo

newCPInfo := &checkpointInfo{

latestFileChunkSuffixNum: currentCPInfo.latestFileChunkSuffixNum,

latestFileChunksize: currentCPInfo.latestFileChunksize + totalBytesToAppend,

isChainEmpty: false ,

lastBlockNumber: block.Header.Number}

if err = mgr.saveCurrentInfo(newCPInfo, false ); err != nil { //更新检查点信息

truncateErr := mgr.currentFileWriter.truncateFile(currentCPInfo.latestFileChunksize) //更新失败,回滚按原大小截取文件

return fmt.Errorf( "Error while saving current file info to db: %s" , err)

}

blockFLP := &fileLocPointer{fileSuffixNum: newCPInfo.latestFileChunkSuffixNum}

blockFLP.offset = currentOffset

for _, txOffset := range txOffsets {

txOffset.loc.offset += len (blockBytesEncodedLen) //更新文件交易指针

}

mgr.index.indexBlock(&blockIdxInfo{ //索引区块

blockNum: block.Header.Number, blockHash: blockHash,

flp: blockFLP, txOffsets: txOffsets, metadata: block.Metadata})

mgr.updateCheckpoint(newCPInfo) //更新检查点信息

mgr.updateBlockchainInfo(blockHash, block) //更新BlockchainInfo

return nil

//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

```

网址:http://www.qukuailianxueyuan.io/

欲领取造币技术与全套虚拟机资料

区块链技术交流QQ群: 756146052   备注:CSDN

尹成学院微信:备注:CSDN

网址:http://www.qukuailianxueyuan.io/

欲领取造币技术与全套虚拟机资料

区块链技术交流QQ群: 756146052   备注:CSDN

尹成学院微信:备注:CSDN

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章