本系列目录:超级账本源码(V1.3)解析目录
当peer收到leader发来的block后,需要进行VSCC、MVCC、commit三步操作。
前两篇博客里我们讲了【VSCC】和【MVCC】这两个过程,下面分析Commit这个过程的核心代码(HLF v1.3)。
前置
- 在
gossip/state/state.go
的listen
函数中收到payload(其中有block和private data),通过queueNewMessage
将其放入payloads
中等待后续处理。 - 然后在
deliverPayloads
函数中开始处理payloads
中的payload,得到block和pvtData后,做了一些简单的检查,之后调用commitBlock
函数。 - 在
commitBblock
中,调用了gossip/privdata/coordinator.go
中的StoreBlock
函数和UpdateLedgerHeight函数(TODO)。 - 在
StoreBlock
函数中,先进行了【VSCC】,然后调用core/ledger/kvledger/lv_ledger.go
中的CommitWithPvtData
函数。
Commit
CommitWithPvtData
主要做了三件事情:1,MVCC(ValidateAndPrepare
);2,存储block(block_commit
);3,更新world state数据库(state_commit
)。(可选的4,更新history数据库l.historyDB.Commit
,可以在sampleconfig/core.yaml
中配置)。我们在上一篇博客中已经讲了【MVCC】这个过程,接下来主要讲解后两个过程。
block_commit
-
调用了
core/ledger/ledgerstorage/store.go
中的CommitWithPvtData
函数:a.
core/ledger/pvtdatastorage/store_impl.go
中的s.pvtdataStore.Prepare
,往数据库中写入了pendingCommitKey
“锁”,并准备好private data(其实已经put in db)b. 调用
common/ledger/blkstorage/fsblkstorage/fs_blockstore.go
中的AddBlock
,接着在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go
的addBlock中
,先获取了账本文件的最新信息(偏移量),然后写入了新block的长度与内容。接着更新了checkpoint信息,并调用saveCurrentInfo
将checkpoint信息存储数据库。然后调用了common/ledger/blkstorage/fsblkstorage/blockindex.go
中的indexBlock
(见7)建立了各个tx的索引,存到数据库中。c. 如果b失败,那么会调用
Rollback
撤销a中的操作(也就是删除db中新插入的这些key);如果b成功,s.pvtdataStore.Commit
删除了pendingCommitKey
“锁”,并更新commit信息这里private data其实就是两阶段提交(2PC),因为需要保证block commit与private data commit的原子性(都成功commit或者失败)。使用2PC,如果commit block之前或者之后机器crash了,就可以根据情况来rollback或者commit private data。 (不过1.3版本的代码里Rollback还没有实现=_=||,这里可以看一下最新版本的代码)
具体可以看core/ledger/ledgerstorage/store.go
中的syncPvtdataStoreWithBlockStore
函数的实现。 -
在
indexBlock
中,依次建立了如下索引:(是否建立该类型的索引在core/ledger/ledgerstorage/store.go
中的NewProvider
硬编码)map[blockHash] = blockchain[block]
其中blockchain是保存block的所有文件,blockchain[block]包含了该block的文件名和偏移量。map[blockNum] = blockchain[block]
以上两个索引使得我们可以快速根据blockNum或者blockHash定位到该block- 对该block中的每一个tx,
map[txid] = blockchain[tx]
blockchain[tx]包含了该tx所在的文件名以及偏移量,可以根据txid定位到该tx。
需要注意的是这一步还检查了txid是否重复,如果重复就不再对该tx建立索引。 - 对该block中的每一个tx,
map[blockNum + TxNumber] = blockchain[tx]
- 对该block中的每一个tx,
map[txid] = blockchain[block]
可以根据txid获得该tx所在block的位置。 - 对该block中的每一个tx,
map[txid] = valid/invalid
,记录交易是否有效 - 最后记录该checkpoint对应的blockNum(也就是当前block的number),
map[checkpoint] = blockNumber
state_commit
- 在
core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_txmgr.go
中的Commit
函数中进行了state_commit,也就是更新world state DB。其中调用了core/ledger/kvledger/txmgmt/privacyenabledstate/common_storage_db.go
中的ApplyPrivacyAwareUpdates
来将【MVCC】中得到的write set(txmgr.current.batch
)更新到DB。
总结
我们总结一下整个【VSCC】、【MVCC】、【Commit】阶段涉及到的和数据库有关的操作,可以更好地理清整个流程,并对细节更加清晰。
TODO.
get file location:
get state: chaincode
get state: tx key
batch pvt pending: ` private data write sets for block`
put in db: `jyp: checkpoint put in db`
get file location:
batch index: `Indexing block `
batch pvt data: `Committing private data for b`
batch state:
batch history: