IBM openblockchain学习(三)--Ledger源码分析
- 1. blockchain
- 1.1. newBlockchain
- 1.2. startIndexer()
- 1.3. getLastBlock
- 1.4. getSize
- 1.5. getBlock
- 1.6. getBlockByHash
- 1.7. getTransactionByUUID
- 1.8. getTransactions
- 1.9. getTransactionsByBlockHash
- 1.10. getTransaction
- 1.11. getTransactionByBlockHash
- 1.12. getBlockchainInfo
- 1.13. buildBlock
- 1.14. addPersistenceChangesForNewBlock
- 1.15. blockPersistenceStatus
- 1.16. persistRawBlock
- 1.17. fetchBlockFromDB
- 1.18. fetchTransactionFromDB
- 1.19. fetchBlockchainSizeFromDB
- 1.20. fetchBlockchainSizeFromSnapshot
- 1.21. String
- 2. blockchain_indexes
- 3. blockchain_indexes_async
- 4. ledger
- 4.1. GetLedger
- 4.2. BeginTxBatch
- 4.3. GetTXBatchPreviewBlock
- 4.4. CommitTxBatch
- 4.5. RollbackTxBatch
- 4.6. TxBegin
- 4.7. TxFinished
- 4.8. GetTempStateHash
- 4.9. GetTempStateHashWithTxDeltaStateHashes
- 4.10. GetState
- 4.11. GetStateRangeScanIterator
- 4.12. GetStateSnapshot
- 4.13. GetStateDelta
- 4.14. ApplyStateDelta
- 4.15. CommitStateDelta
- 4.16. RollbackStateDelta
- 4.17. VerifyChain
- 4.18. sendProducerBlockEvent
- 5. genesis
- 6. util
- 7. buckettree
- 8. state
- 9. trie
- 10. commons
- 11. state_delta_iterator
- 12. state_delta
Ledger是总账簿的意思,也就是blockchain中存储交易记录的部分。其代码包含如下,这块代码量大,可能分析时间会很长,希望读者耐心等待。
blockchain
先看下Blockchain在内存中保存的基本信息,Blockchain中的操作不是线程安全的type blockchain struct {
size uint64 //块大小
previousBlockHash []byte //上一个块的哈希
indexer blockchainIndexer //块索引
lastProcessedBlock *lastProcessedBlock //最后处理的块
}
最后处理的块的结构type lastProcessedBlock struct {
block *protos.Block
blockNumber uint64 块数
blockHash []byte 块哈希值
}
newBlockchain
newBlockchain()用于创建一个区块func newBlockchain() (*blockchain, error) {
size, err := fetchBlockchainSizeFromDB()//从数据库中读取blockchain的大小
if err != nil {
return nil, err
}
blockchain := &blockchain{0, nil, nil, nil}
blockchain.size = size
if size > 0 {
previousBlock, err := fetchBlockFromDB(size - 1)
//如果为创世区块,则上一个块是创世区块的大小减一
if err != nil {
return nil, err
}
previousBlockHash, err := previousBlock.GetHash()
//获取前驱块的哈希
if err != nil {
return nil, err
}
blockchain.previousBlockHash = previousBlockHash
}
err = blockchain.startIndexer()
//开始创建索引
if err != nil {
return nil, err
}
return blockchain, nil
}
startIndexer()
创建索引func (blockchain *blockchain) startIndexer() (err error) {
if indexBlockDataSynchronously {
blockchain.indexer = newBlockchainIndexerSync()
//同步创建区块链索引
} else {
blockchain.indexer = newBlockchainIndexerAsync()
}
err = blockchain.indexer.start(blockchain)
return
}
getLastBlock
getLastBlock创建最后区块
func (blockchain *blockchain) getLastBlock() (*protos.Block, error) { |
getSize
getSize用于返回块大小
func (blockchain *blockchain) getSize() uint64 { |
getBlock
在blockchain中通过任意高度获取块
func (blockchain *blockchain) getBlock(blockNumber uint64) (*protos.Block, error) {
return fetchBlockFromDB(blockNumber)
}
getBlockByHash
通过块的哈希获取块
func (blockchain *blockchain) getBlockByHash(blockHash []byte) (*protos.Block, error) { |
getTransactionByUUID
通过UUID获取交易记录func (blockchain *blockchain) getTransactionByUUID(txUUID string) (*protos.Transaction, error) {
blockNumber, txIndex, err := blockchain.indexer.fetchTransactionIndexByUUID(txUUID)
if err != nil {
return nil, err
}
block, err := blockchain.getBlock(blockNumber)
if err != nil {
return nil, err
}
transaction := block.GetTransactions()[txIndex]
return transaction, nil
}
getTransactions
通过有块号标识的块获取所有的交易func (blockchain *blockchain) getTransactions(blockNumber uint64) ([]*protos.Transaction, error) {
block, err := blockchain.getBlock(blockNumber)
if err != nil {
return nil, err
}
return block.GetTransactions(), nil
}
getTransactionsByBlockHash
通过块的哈希获取所有的交易func (blockchain *blockchain) getTransactionsByBlockHash(blockHash []byte) ([]*protos.Transaction, error) {
block, err := blockchain.getBlockByHash(blockHash)
if err != nil {
return nil, err
}
return block.GetTransactions(), nil
}
getTransaction
通过数块和确定块内索引获取交易
func (blockchain *blockchain) getTransaction(blockNumber uint64, txIndex uint64) (*protos.Transaction, error) { |
getTransactionByBlockHash
通过块内块的哈希和标识索引获取交易func (blockchain *blockchain) getTransactionByBlockHash(blockHash []byte, txIndex uint64) (*protos.Transaction, error) {
block, err := blockchain.getBlockByHash(blockHash)
if err != nil {
return nil, err
}
return block.GetTransactions()[txIndex], nil
}
getBlockchainInfo
获取区块链的信息
func (blockchain *blockchain) getBlockchainInfo() (*protos.BlockchainInfo, error) { |
buildBlock
创建块
func (blockchain *blockchain) buildBlock(block *protos.Block, stateHash []byte) *protos.Block { |
addPersistenceChangesForNewBlock
对于新块添加持久性的更改func (blockchain *blockchain) addPersistenceChangesForNewBlock(ctx context.Context,
block *protos.Block, stateHash []byte, writeBatch *gorocksdb.WriteBatch) (uint64, error) {
block = blockchain.buildBlock(block, stateHash)
if block.NonHashData == nil {
block.NonHashData = &protos.NonHashData{LocalLedgerCommitTimestamp: util.CreateUtcTimestamp()}
} else {
block.NonHashData.LocalLedgerCommitTimestamp = util.CreateUtcTimestamp()
}
blockNumber := blockchain.size
blockHash, err := block.GetHash()
if err != nil {
return 0, err
}
blockBytes, blockBytesErr := block.Bytes()
if blockBytesErr != nil {
return 0, blockBytesErr
}
writeBatch.PutCF(db.GetDBHandle().BlockchainCF, encodeBlockNumberDBKey(blockNumber), blockBytes)
writeBatch.PutCF(db.GetDBHandle().BlockchainCF, blockCountKey, encodeUint64(blockNumber+1))
if blockchain.indexer.isSynchronous() {
blockchain.indexer.createIndexesSync(block, blockNumber, blockHash, writeBatch)
}
blockchain.lastProcessedBlock = &lastProcessedBlock{block, blockNumber, blockHash}
return blockNumber, nil
}
blockPersistenceStatus
块持久状态
func (blockchain *blockchain) blockPersistenceStatus(success bool) { |
persistRawBlock
持久化原始块
func (blockchain *blockchain) persistRawBlock(block *protos.Block, blockNumber uint64) error { |
fetchBlockFromDB
从数据库中获取块func fetchBlockFromDB(blockNumber uint64) (*protos.Block, error) {
blockBytes, err := db.GetDBHandle().GetFromBlockchainCF(encodeBlockNumberDBKey(blockNumber))
if err != nil {
return nil, err
}
if blockBytes == nil {
return nil, nil
}
return protos.UnmarshallBlock(blockBytes)
}
fetchTransactionFromDB
从数据库中获取交易记录
func fetchTransactionFromDB(blockNum uint64, txIndex uint64) (*protos.Transaction, error) { |
fetchBlockchainSizeFromDB
从数据库中获取区块链的大小func fetchBlockchainSizeFromDB() (uint64, error) {
bytes, err := db.GetDBHandle().GetFromBlockchainCF(blockCountKey)
if err != nil {
return 0, err
}
if bytes == nil {
return 0, nil
}
return decodeToUint64(bytes), nil
}
fetchBlockchainSizeFromSnapshot
从快照中获取区块链大小func fetchBlockchainSizeFromSnapshot(snapshot *gorocksdb.Snapshot) (uint64, error) {
blockNumberBytes, err := db.GetDBHandle().GetFromBlockchainCFSnapshot(snapshot, blockCountKey)
if err != nil {
return 0, err
}
var blockNumber uint64
if blockNumberBytes != nil {
blockNumber = decodeToUint64(blockNumberBytes)
}
return blockNumber, nil
}
String
将区块链的信息以字符串形式输出func (blockchain *blockchain) String() string {
var buffer bytes.Buffer
size := blockchain.getSize()
for i := uint64(0); i < size; i++ {
block, blockErr := blockchain.getBlock(i)
if blockErr != nil {
return ""
}
buffer.WriteString("\n----------<block #")
buffer.WriteString(strconv.FormatUint(i, 10))
buffer.WriteString(">----------\n")
buffer.WriteString(block.String())
buffer.WriteString("\n----------<\\block #")
buffer.WriteString(strconv.FormatUint(i, 10))
buffer.WriteString(">----------\n")
}
return buffer.String()
}
blockchain_indexes
blockchainIndexer定义了以下几个接口type blockchainIndexer interface {
//同步标识
isSynchronous() bool
//开始创建
start(blockchain *blockchain) error
//同步创建索引
createIndexesSync(block *protos.Block, blockNumber uint64, blockHash []byte, writeBatch *gorocksdb.WriteBatch) error
//异步创建索引
createIndexesAsync(block *protos.Block, blockNumber uint64, blockHash []byte) error
//通过块哈希获取块号
fetchBlockNumberByBlockHash(blockHash []byte) (uint64, error)
//通过UUID获取块号
fetchTransactionIndexByUUID(txUUID string) (uint64, uint64, error)
//停止创建
stop()
}
addIndexDataForPersistence
持久化并且检索索引数据func addIndexDataForPersistence(block *protos.Block, blockNumber uint64, blockHash []byte, writeBatch *gorocksdb.WriteBatch) error {
openchainDB := db.GetDBHandle()
cf := openchainDB.IndexesCF
// 块号映射成块哈希值
indexLogger.Debug("Indexing block number [%d] by hash = [%x]", blockNumber, blockHash)
writeBatch.PutCF(cf, encodeBlockHashKey(blockHash), encodeBlockNumber(blockNumber))
addressToTxIndexesMap := make(map[string][]uint64)
addressToChaincodeIDsMap := make(map[string][]*protos.ChaincodeID)
transactions := block.GetTransactions()
for txIndex, tx := range transactions {
// 添加TXT UUID - >(块号,索引中块)
writeBatch.PutCF(cf, encodeTxUUIDKey(tx.Uuid), encodeBlockNumTxIndex(blockNumber, uint64(txIndex)))
txExecutingAddress := getTxExecutingAddress(tx)
addressToTxIndexesMap[txExecutingAddress] = append(addressToTxIndexesMap[txExecutingAddress], uint64(txIndex))
switch tx.Type {
case protos.Transaction_CHAINCODE_NEW, protos.Transaction_CHAINCODE_UPDATE:
authroizedAddresses, chaincodeID := getAuthorisedAddresses(tx)
for _, authroizedAddress := range authroizedAddresses {
addressToChaincodeIDsMap[authroizedAddress] = append(addressToChaincodeIDsMap[authroizedAddress], chaincodeID)
}
}
}
for address, txsIndexes := range addressToTxIndexesMap {
writeBatch.PutCF(cf, encodeAddressBlockNumCompositeKey(address, blockNumber), encodeListTxIndexes(txsIndexes))
}
return nil
}
getAuthorisedAddresses
获得授权地址func getAuthorisedAddresses(tx *protos.Transaction) ([]string, *protos.ChaincodeID) {
// 从chaincode的部署TX中获取取地址
// 这个方法也会返回错误
data := tx.ChaincodeID
cID := &protos.ChaincodeID{}
err := proto.Unmarshal(data, cID)
if err != nil {
return nil, nil
}
return []string{"address1", "address2"}, cID
}
encodeBlockNumber
编码/解码数据库键/值函数,索引数据编码/解码块数
func encodeBlockNumber(blockNumber uint64) []byte {
return proto.EncodeVarint(blockNumber)
}
func decodeBlockNumber(blockNumberBytes []byte) (blockNumber uint64) {
blockNumber, _ = proto.DecodeVarint(blockNumberBytes)
return
}
encodeBlockNumTxIndex
对 块号的Tx索引进行编码/解码func encodeBlockNumTxIndex(blockNumber uint64, txIndexInBlock uint64) []byte {
b := proto.NewBuffer([]byte{})
b.EncodeVarint(blockNumber)
b.EncodeVarint(txIndexInBlock)
return b.Bytes()
}
func decodeBlockNumTxIndex(bytes []byte) (blockNum uint64, txIndex uint64, err error) {
b := proto.NewBuffer(bytes)
blockNum, err = b.DecodeVarint()
if err != nil {
return
}
txIndex, err = b.DecodeVarint()
if err != nil {
return
}
return
}
对区块哈希的键值进行编码
func encodeBlockHashKey(blockHash []byte) []byte {
return prependKeyPrefix(prefixBlockHashKey, blockHash)
}
对TxUUID的键值进行编码
func encodeTxUUIDKey(txUUID string) []byte { |
对区块号地址的复合键值进行编码func encodeAddressBlockNumCompositeKey(address string, blockNumber uint64) []byte {
b := proto.NewBuffer([]byte{prefixAddressBlockNumCompositeKey})
b.EncodeRawBytes([]byte(address))
b.EncodeVarint(blockNumber)
return b.Bytes()
}
对Tx的索引清单进行编码func encodeListTxIndexes(listTx []uint64) []byte {
b := proto.NewBuffer([]byte{})
for i := range listTx {
b.EncodeVarint(listTx[i])
}
return b.Bytes()
}
对chaincode的ID进行编码func encodeChaincodeID(c *protos.ChaincodeID) []byte {
// 序列化chaincode ID
return []byte{}
}
前置键值前缀
func prependKeyPrefix(prefix byte, key []byte) []byte { |
blockchain_indexes_async
整个代码主要执行对blockchain的异步创建索引type blockchainIndexerAsync struct {
blockchain *blockchain
//从块链转移块索引的通道
blockChan chan blockWrapper
indexerState *blockchainIndexerState
}
createIndexesInternal
创建索引条目并逐步添加到数据库,用于创建各种属性的索引func (indexer *blockchainIndexerAsync) createIndexesInternal(block *protos.Block, blockNumber uint64, blockHash []byte) error {
openchainDB := db.GetDBHandle()
writeBatch := gorocksdb.NewWriteBatch()
defer writeBatch.Destroy()
addIndexDataForPersistence(block, blockNumber, blockHash, writeBatch)
writeBatch.PutCF(openchainDB.IndexesCF, lastIndexedBlockKey, encodeBlockNumber(blockNumber))
opt := gorocksdb.NewDefaultWriteOptions()
defer opt.Destroy()
err := openchainDB.DB.Write(opt, writeBatch)
if err != nil {
return err
}
indexer.indexerState.blockIndexed(blockNumber)
return nil
}
indexPendingBlocks
待定块的索引func (indexer *blockchainIndexerAsync) indexPendingBlocks() error {
blockchain := indexer.blockchain
if blockchain.getSize() == 0 {
// 链至今为空
return nil
}
lastCommittedBlockNum := blockchain.getSize() - 1
lastIndexedBlockNum := indexer.indexerState.getLastIndexedBlockNumber()
if lastCommittedBlockNum == lastIndexedBlockNum {
//所有块索引的提交
return nil
}
for ; lastIndexedBlockNum < lastCommittedBlockNum; lastIndexedBlockNum++ {
blockNumToIndex := lastIndexedBlockNum + 1
blockToIndex, errBlockFetch := blockchain.getBlock(blockNumToIndex)
if errBlockFetch != nil {
return errBlockFetch
}
blockHash, errBlockHash := blockToIndex.GetHash()
if errBlockHash != nil {
return errBlockHash
}
indexer.createIndexesInternal(blockToIndex, blockNumToIndex, blockHash)
}
return nil
}
blockIndexed
块索引func (indexerState *blockchainIndexerState) blockIndexed(blockNumber uint64) {
indexerState.newBlockIndexed.L.Lock()
defer indexerState.newBlockIndexed.L.Unlock()
indexerState.lastBlockIndexed = blockNumber
indexerState.zerothBlockIndexed = true
indexerState.newBlockIndexed.Broadcast()
}
waitForLastCommittedBlock
等待最后一个块的创建func (indexerState *blockchainIndexerState) waitForLastCommittedBlock() (err error) {
chain := indexerState.indexer.blockchain
if err != nil || chain.getSize() == 0 {
return
}
lastBlockCommitted := chain.getSize() - 1
indexerState.newBlockIndexed.L.Lock()
defer indexerState.newBlockIndexed.L.Unlock()
if !indexerState.zerothBlockIndexed {
indexLogger.Debug(
"Waiting for zeroth block to be indexed. lastBlockCommitted=[%d] and lastBlockIndexed=[%d]",
lastBlockCommitted, indexerState.lastBlockIndexed)
indexerState.newBlockIndexed.Wait()
}
for indexerState.lastBlockIndexed < lastBlockCommitted {
indexLogger.Debug(
"Waiting for index to catch up with block chain. lastBlockCommitted=[%d] and lastBlockIndexed=[%d]",
lastBlockCommitted, indexerState.lastBlockIndexed)
indexerState.newBlockIndexed.Wait()
}
return
}
fetchLastIndexedBlockNumFromDB
获取从数据库中得到上一个块号的块索引func fetchLastIndexedBlockNumFromDB() (zerothBlockIndexed bool, lastIndexedBlockNum uint64, err error) {
lastIndexedBlockNumberBytes, err := db.GetDBHandle().GetFromIndexesCF(lastIndexedBlockKey)
if err != nil {
return
}
if lastIndexedBlockNumberBytes == nil {
return
}
lastIndexedBlockNum = decodeBlockNumber(lastIndexedBlockNumberBytes)
zerothBlockIndexed = true
return
}
ledger
先看下ledger的结构type Ledger struct {
blockchain *blockchain //区块链
state *state.State //状态
currentID interface{} //当前ID
}
GetLedger
给出”单个“ledger的引用func GetLedger() (*Ledger, error) {
once.Do(func() {
ledger, ledgerError = newLedger()
})
return ledger, ledgerError
}
BeginTxBatch
开始批量发出
func (ledger *Ledger) BeginTxBatch(id interface{}) error { |
GetTXBatchPreviewBlock
返回将具有相同块的哈希,如果ledger.CommitTxBatch使用相同的参数则提交到数据库。如果该状态是由一个事务这两个调用之间修改,散列将不同。该块预览不包括非散列数据,如本地时间戳。func (ledger *Ledger) GetTXBatchPreviewBlock(id interface{},
transactions []*protos.Transaction, metadata []byte) (*protos.Block, error) {
err := ledger.checkValidIDCommitORRollback(id)
if err != nil {
return nil, err
}
stateHash, err := ledger.state.GetHash()
if err != nil {
return nil, err
}
return ledger.blockchain.buildBlock(protos.NewBlock(transactions, metadata), stateHash), nil
}
CommitTxBatch
CommitTxBatch被调用时,当前事务需要分批次提交,该函数成功返回了交易的细节和状态变化(可能在这个交易批量的执行过程中发生)一直致力于持久化存储
func (ledger *Ledger) CommitTxBatch(id interface{}, transactions []*protos.Transaction, transactionResults []*protos.TransactionResult, metadata []byte) error { |
RollbackTxBatch
批处理回滚时放弃当前事务批次执行过程中可能发生的所有状态变化func (ledger *Ledger) RollbackTxBatch(id interface{}) error {
ledgerLogger.Debug("RollbackTxBatch for id = [%s]", id)
err := ledger.checkValidIDCommitORRollback(id)
if err != nil {
return err
}
ledger.resetForNextTxGroup(false)
return nil
}
TxBegin
标志着在持续一批新的交易开始func (ledger *Ledger) TxBegin(txUUID string) {
ledger.state.TxBegin(txUUID)
}
TxFinished
标志着正在进行交易的完成。如果成功话设置为false,丢弃事务的状态变化func (ledger *Ledger) TxFinished(txUUID string, txSuccessful bool) {
ledger.state.TxFinish(txUUID, txSuccessful)
}
GetTempStateHash
计算哈希状态并考虑到当前事务批次执行过程中可能发生的状态变化func (ledger *Ledger) GetTempStateHash() ([]byte, error) {
return ledger.state.GetHash()
}
GetTempStateHashWithTxDeltaStateHashes
除状态散列(如在方法GetTempStateHash定义),
此方法返回一个映射[TX的txUuid - > cryptoHash(stateChange MadeBySIx),只有TX成功,才会出现在该映射中func (ledger *Ledger) GetTempStateHashWithTxDeltaStateHashes() ([]byte, map[string][]byte, error) {
stateHash, err := ledger.state.GetHash()
return stateHash, ledger.state.GetTxStateDeltaHash(), err
}
GetState
获取chaincode的id和键值。如果提交为false,它首先会在内存中查看。如果丢失的话,将从数据库中获取。如果提交为true,则仅仅只能在数据库中获取。
func (ledger *Ledger) GetState(chaincodeID string, key string, committed bool) ([]byte, error) { |
GetStateRangeScanIterator
返回一个迭代器来获取所有startKey和endKey之间的键(和值)(假设键的词汇顺序)为chaincodeID。如果提交为true,则从数据库检索的键值是唯一。如果提交为false,从数据库被mergerd后的结果与在存储器中的结果(优先考虑在内存中的数据)在返回的迭代的键值是不同的 guaranteed to be in any specific order
func (ledger *Ledger) GetStateRangeScanIterator(chaincodeID string, startKey string, endKey string, committed bool) (statemgmt.RangeScanIterator, error) {
return ledger.state.GetRangeScanIterator(chaincodeID, startKey, endKey, committed)
}
GetStateSnapshot
返回当前块点对点全局状态。 这个是在从一个端到另一个端转化中的状态时使用。必须调用状态Snapshot.Release()方法一旦你与快照是以释放资源完成的。
func (ledger *Ledger) GetStateSnapshot() (*state.StateSnapshot, error) { |
GetStateDelta
如果可用,则返回指定块的状态增量。func (ledger *Ledger) GetStateDelta(blockNumber uint64) (*statemgmt.StateDelta, error) {
if blockNumber >= ledger.GetBlockchainSize() {
return nil, ErrOutOfBounds
}
return ledger.state.FetchStateDeltaFromDB(blockNumber)
}
ApplyStateDelta
即适用于一个当前的状态状态增量。它只在内存改变。必须调用ledger.CommitStateDelta持久化到数据库。这应该只被用来作为状态同步的一部分。状态增量可以从另一对等虽然Ledger.GetStateDelta函数检索或者与来自Ledger.GetStateshot()获取密钥创建的状态增量。举一个例子,在ledger_test.go定义的TestSetRawState。请注意,没有在此功能上检查它是否被调用,以确保增量是在正确的顺序中使用。例如,如果你目前正处于块8,并调用Ledger.GetStateDelta(10)的功能检索增量,您现在会是在一个糟糕的状态,因为你没有块9.申请增量这是可能的回滚状态向前或向后使用stateDelta.RollBackwards。默认情况下,块3检索的增量可以被用来从状态向前回滚在块2到状态在块3.如果
stateDelta.RollBackwards =false,增量检索块3可用于向后滚动块3状态和块2的状态。func (ledger *Ledger) ApplyStateDelta(id interface{}, delta *statemgmt.StateDelta) error {
err := ledger.checkValidIDBegin()
if err != nil {
return err
}
ledger.currentID = id
ledger.state.ApplyStateDelta(delta)
return nil
}
CommitStateDelta
将提交ledger.ApplyState状态增量并传递到到数据库func (ledger *Ledger) CommitStateDelta(id interface{}) error {
err := ledger.checkValidIDCommitORRollback(id)
if err != nil {
return err
}
defer ledger.resetForNextTxGroup(true)
return ledger.state.CommitStateDelta()
}
RollbackStateDelta
放弃到ledger.ApplyStateDelta状态增量func (ledger *Ledger) RollbackStateDelta(id interface{}) error {
err := ledger.checkValidIDCommitORRollback(id)
if err != nil {
return err
}
ledger.resetForNextTxGroup(false)
return nil
}
VerifyChain
将验证blockchain的integrety。完成这一步
通过确保存储在每个块中的前一个块的哈希链中的前块的实际散列相匹配。返回值是包含不匹配的前一个块的散列块的块号。例如,如果验证链(0,99)称为与prevous哈希值存储在块8中,32和42不相匹配各自前块42的实际的哈希值将是从该函数的返回值。 highBlock在链中高级验证。 如果你要验证的整个链条中,使用ledger.GetBlockchainsize() - 1。低块是在链中被低级验证。如果您想验证整个链条,为创世区块使用0。func (ledger *Ledger) VerifyChain(highBlock, lowBlock uint64) (uint64, error) {
if highBlock >= ledger.GetBlockchainSize() {
return highBlock, ErrOutOfBounds
}
if highBlock <= lowBlock {
return lowBlock, ErrOutOfBounds
}
for i := highBlock; i > lowBlock; i-- {
currentBlock, err := ledger.GetBlockByNumber(i)
if err != nil {
return i, fmt.Errorf("Error fetching block %d.", i)
}
if currentBlock == nil {
return i, fmt.Errorf("Block %d is nil.", i)
}
previousBlock, err := ledger.GetBlockByNumber(i - 1)
if err != nil {
return i - 1, fmt.Errorf("Error fetching block %d.", i)
}
if previousBlock == nil {
return i - 1, fmt.Errorf("Block %d is nil.", i-1)
}
previousBlockHash, err := previousBlock.GetHash()
if err != nil {
return i - 1, fmt.Errorf("Error calculating block hash for block %d.", i-1)
}
if bytes.Compare(previousBlockHash, currentBlock.PreviousBlockHash) != 0 {
return i, nil
}
}
return 0, nil
}
sendProducerBlockEvent
func sendProducerBlockEvent(block *protos.Block) { |
genesis
类似于chaincode,调用go-logging中logging库的MustGetLogger函数对genesis package进行记录
var genesisLogger = logging.MustGetLogger("genesis") |
MakeGenesis
MakeGenesis基于在openchain.yaml中配置创建创世区块,并把它添加到blockchain。
func MakeGenesis() error { |
BuildLocal
构建一个指定的chaincode码func BuildLocal(context context.Context, spec *protos.ChaincodeSpec) (*protos.ChaincodeDeploymentSpec, error) {
genesisLogger.Debug("Received build request for chaincode spec: %v", spec)
mode := viper.GetString("chaincode.chaincoderunmode")
var codePackageBytes []byte
if mode != chaincode.DevModeUserRunsChaincode {
if err := openchain.CheckSpec(spec); err != nil {
genesisLogger.Debug("check spec failed: %s", err)
return nil, err
}
// 规范构建
var err error
codePackageBytes, err = container.GetChaincodePackageBytes(spec)
if err != nil {
genesisLogger.Error(fmt.Sprintf("Error getting VM: %s", err))
return nil, err
}
}
chaincodeDeploymentSpec := &protos.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
return chaincodeDeploymentSpec, nil
}
DeployLocal
部署供应链代码的映像到本地端
func DeployLocal(ctx context.Context, spec *protos.ChaincodeSpec) (*protos.Transaction, []byte, error) { |
设置是否部署系统chaincodefunc deploySystemChaincodeEnabled() bool {
// 如果系统chaincode的部署配置文件中能够返回所配置的值
if viper.IsSet("ledger.blockchain.deploy-system-chaincode") {
return viper.GetBool("ledger.blockchain.deploy-system-chaincode")
}
// 如果没有指定配置能够启用,系统chaincode将采用默认情况部署
return true
}
deployUpdateValidityPeriodChaincode
部署更新chaincode的有效期func deployUpdateValidityPeriodChaincode() (*protos.Transaction, error) {
//它应该是可配置的,不采取硬编码
vpChaincodePath := "github.com/openblockchain/obc-peer/openchain/system_chaincode/validity_period_update"
vpFunction := "init"
//这应该是负责有效期更新的组件的登录凭证。
//该组件需要在系统中注册,以便能够调用更新chaincode的有效期
vpToken := "system_chaincode_invoker"
var vpCtorArgsStringArray []string
validityPeriodSpec := &protos.ChaincodeSpec{Type: protos.ChaincodeSpec_GOLANG,
ChaincodeID: &protos.ChaincodeID{Path: vpChaincodePath,
Name: "",
},
CtorMsg: &protos.ChaincodeInput{Function: vpFunction,
Args: vpCtorArgsStringArray,
},
}
validityPeriodSpec.SecureContext = string(vpToken)
vpTransaction, _, deployErr := DeployLocal(context.Background(), validityPeriodSpec)
if deployErr != nil {
genesisLogger.Error("Error deploying validity period chaincode for genesis block.", deployErr)
makeGenesisError = deployErr
return nil, deployErr
}
return vpTransaction, nil
}
util
EncodeOrderPreservingVarUint64
返回一个字节表示要的int64数使得起始字节全零比特,以减少阵列的长度被修整,用于保存在一个缺省字节对比的顺序,第一个字节包含剩余的第一字节的bytes。存在的数量也允许使用返回的字节作为其它较大字节阵列的一部分,如以数据库复合键表示func EncodeOrderPreservingVarUint64(number uint64) []byte {
bytes := make([]byte, 8)
binary.BigEndian.PutUint64(bytes, number)
startingIndex := 0
size := 0
for i, b := range bytes {
if b != 0x00 {
startingIndex = i
size = 8 - i
break
}
}
sizeBytes := proto.EncodeVarint(uint64(size))
if len(sizeBytes) > 1 {
panic(fmt.Errorf("[]sizeBytes should not be more than one byte because the max number it needs to hold is 8. size=%d", size))
}
encodedBytes := make([]byte, size+1)
encodedBytes[0] = sizeBytes[0]
copy(encodedBytes[1:], bytes[startingIndex:])
return encodedBytes
}
DecodeOrderPreservingVarUint64
解码从由方法“EncodeOrderPreservingVarUint64’得到的字节数。
此外,返回在该过程中所消耗的字节数func DecodeOrderPreservingVarUint64(bytes []byte) (uint64, int) {
s, _ := proto.DecodeVarint(bytes)
size := int(s)
decodedBytes := make([]byte, 8)
copy(decodedBytes[8-size:], bytes[1:size+1])
numBytesConsumed := size + 1
return binary.BigEndian.Uint64(decodedBytes), numBytesConsumed
}
buckettree
bucket_hash
addNextNode
这个方法假定数据节点都按键的增加顺序添加func (c *bucketHashCalculator) addNextNode(dataNode *dataNode) {
chaincodeID, _ := dataNode.getKeyElements()
if chaincodeID != c.currentChaincodeID {
c.appendCurrentChaincodeData()
c.currentChaincodeID = chaincodeID
c.dataNodes = nil
}
c.dataNodes = append(c.dataNodes, dataNode)
}
computeCryptoHash
计算加密哈希func (c *bucketHashCalculator) computeCryptoHash() []byte {
if c.currentChaincodeID != "" {
c.appendCurrentChaincodeData()
c.currentChaincodeID = ""
c.dataNodes = nil
}
logger.Debug("Hashable content for bucket [%s]: length=%d, contentInStringForm=[%s]", c.bucketKey, len(c.hashingData), string(c.hashingData))
if util.IsNil(c.hashingData) {
return nil
}
return openchainUtil.ComputeCryptoHash(c.hashingData)
}
appendCurrentChaincodeData
添加当前chaincode数据func (c *bucketHashCalculator) appendCurrentChaincodeData() {
if c.currentChaincodeID == "" {
return
}
c.appendSizeAndData([]byte(c.currentChaincodeID))
c.appendSize(len(c.dataNodes))
for _, dataNode := range c.dataNodes {
_, key := dataNode.getKeyElements()
value := dataNode.getValue()
c.appendSizeAndData([]byte(key))
c.appendSizeAndData(value)
}
}
appendSizeAndData
添加数据和容量func (c *bucketHashCalculator) appendSizeAndData(b []byte) {
c.appendSize(len(b))
c.hashingData = append(c.hashingData, b...)
}
appendSize
增加容量
func (c *bucketHashCalculator) appendSize(size int) { |
bucket_key
bucket key的结构如下
type bucketKey struct { |
newBucketKey
当level为0,bucketNumber为1时,构造bucket树根节点;
当level为bucketKey.level-1, bucketNumber为conf.computeParentBucketNumber(bucketKey.bucketNumber)
时构建的是父节点的bucketkeyfunc newBucketKey(level int, bucketNumber int) *bucketKey {
if level > conf.getLowestLevel() || level < 0 {
panic(fmt.Errorf("Invalid Level [%d] for bucket key. Level can be between 0 and [%d]", level, conf.lowestLevel))
//如果级别大于最低级别或者级别小于0,则输出当前级别以及最小级别
}
//如果bucket号小于1或者大于bucket级别对应的级别好,则返回bucketkey的级别和级别号
if bucketNumber < 1 || bucketNumber > conf.getNumBuckets(level) {
panic(fmt.Errorf("Invalid bucket number [%d]. Bucket nuber at level [%d] can be between 1 and [%d]", bucketNumber, level, conf.getNumBuckets(level)))
}
return &bucketKey{level, bucketNumber}
}
getChildIndex
获取子节点的索引func (bucketKey *bucketKey) getChildIndex(childKey *bucketKey) int {
bucketNumberOfFirstChild := ((bucketKey.bucketNumber - 1) * conf.getMaxGroupingAtEachLevel()) + 1
bucketNumberOfLastChild := bucketKey.bucketNumber * conf.getMaxGroupingAtEachLevel()
if childKey.bucketNumber < bucketNumberOfFirstChild || childKey.bucketNumber > bucketNumberOfLastChild {
panic(fmt.Errorf("[%#v] is not a valid child bucket of [%#v]", childKey, bucketKey))
}
return childKey.bucketNumber - bucketNumberOfFirstChild
}
bucket_node
bucketnode的结构如下
type bucketNode struct { |
unmarshalBucketNode
重组bucketnodefunc unmarshalBucketNode(bucketKey *bucketKey, serializedBytes []byte) *bucketNode {
bucketNode := newBucketNode(bucketKey)
buffer := proto.NewBuffer(serializedBytes)
for i := 0; i < conf.getMaxGroupingAtEachLevel(); i++ {
childCryptoHash, err := buffer.DecodeRawBytes(false)
if err != nil {
panic(fmt.Errorf("this error should not occur: %s", err))
}
if !util.IsNil(childCryptoHash) {
bucketNode.childrenCryptoHash[i] = childCryptoHash
}
}
return bucketNode
}
mergeBucketNode
合并bucket节点func (bucketNode *bucketNode) mergeBucketNode(anotherBucketNode *bucketNode) {
if !bucketNode.bucketKey.equals(anotherBucketNode.bucketKey) {
panic(fmt.Errorf("Nodes with different keys can not be merged. BaseKey=[%#v], MergeKey=[%#v]", bucketNode.bucketKey, anotherBucketNode.bucketKey))
}
for i, childCryptoHash := range anotherBucketNode.childrenCryptoHash {
if !bucketNode.childrenUpdated[i] && util.IsNil(bucketNode.childrenCryptoHash[i]) {
bucketNode.childrenCryptoHash[i] = childCryptoHash
}
}
}
bucket_tree_delta
包含的功能比较少,直接上代码
//创建bucket树增量 |
config
计算父节点的bucket数量func (config *config) computeParentBucketNumber(bucketNumber int) int {
logger.Debug("Computing parent bucket number for bucketNumber [%d]", bucketNumber)
parentBucketNumber := bucketNumber / config.getMaxGroupingAtEachLevel()
if bucketNumber%config.getMaxGroupingAtEachLevel() != 0 {
parentBucketNumber++
}
return parentBucketNumber
}
Datakey
//创建datakey |
data_nodes_delta
newDataNodesDelta
创建datanode增量func newDataNodesDelta(stateDelta *statemgmt.StateDelta) *dataNodesDelta {
dataNodesDelta := &dataNodesDelta{make(map[bucketKey]dataNodes)}
chaincodeIDs := stateDelta.GetUpdatedChaincodeIds(false)
for _, chaincodeID := range chaincodeIDs {
updates := stateDelta.GetUpdates(chaincodeID)
for key, updatedValue := range updates {
if stateDelta.RollBackwards {
dataNodesDelta.add(chaincodeID, key, updatedValue.GetPreviousValue())
} else {
dataNodesDelta.add(chaincodeID, key, updatedValue.GetValue())
}
}
}
for _, dataNodes := range dataNodesDelta.byBucket {
sort.Sort(dataNodes)
}
return dataNodesDelta
}
getAffectedBuckets
获取受到影响的bucketsfunc (dataNodesDelta *dataNodesDelta) getAffectedBuckets() []*bucketKey {
changedBuckets := []*bucketKey{}
for bucketKey := range dataNodesDelta.byBucket {
copyOfBucketKey := bucketKey.clone()
logger.Debug("Adding changed bucket [%s]", copyOfBucketKey)
changedBuckets = append(changedBuckets, copyOfBucketKey)
}
logger.Debug("Changed buckets are = [%s]", changedBuckets)
return changedBuckets
}
range_scan_iterator
RangeScanIterator实现了 ‘statemgmt.RangeScanIterator’接口type RangeScanIterator struct {
dbItr *gorocksdb.Iterator
chaincodeID string
startKey string
endKey string
currentBucketNumber int
currentKey string
currentValue []byte
done bool
}
这是其中接口实现的一些细节func (itr *RangeScanIterator) Next() bool {
if itr.done {
return false
}
for itr.dbItr.Valid() {
//创建键 - 值字节的副本,因为潜在的键值字节由UTR重用。关闭时没有必要为迭代器释放内存而释放切片。
keyBytes := statemgmt.Copy(itr.dbItr.Key().Data())
valueBytes := statemgmt.Copy(itr.dbItr.Value().Data())
dataNode := unmarshalDataNodeFromBytes(keyBytes, valueBytes)
dataKey := dataNode.dataKey
chaincodeID, key := statemgmt.DecodeCompositeKey(dataNode.getCompositeKey())
value := dataNode.value
logger.Debug("Evaluating data-key = %s", dataKey)
bucketNumber := dataKey.bucketKey.bucketNumber
if bucketNumber > itr.currentBucketNumber {
itr.seekForStartKeyWithinBucket(bucketNumber)
continue
}
if chaincodeID == itr.chaincodeID && (itr.endKey == "" || key <= itr.endKey) {
logger.Debug("including data-key = %s", dataKey)
itr.currentKey = key
itr.currentValue = value
itr.dbItr.Next()
return true
}
itr.seekForStartKeyWithinBucket(bucketNumber + 1)
continue
}
itr.done = true
return false
}
snapshot_iterator
//接口实现 |
state_impl
实现了 ‘statemgmt.HashableState’接口
NewStateImpl
构建一个新的StateImplfunc NewStateImpl() *StateImpl {
return &StateImpl{}
}
Initialize
状态初始化func (stateImpl *StateImpl) Initialize(configs map[string]interface{}) error {
initConfig(configs)
rootBucketNode, err := fetchBucketNodeFromDB(constructRootBucketKey())
if err != nil {
return err
}
if rootBucketNode != nil {
stateImpl.persistedStateHash = rootBucketNode.computeCryptoHash()
stateImpl.lastComputedCryptoHash = stateImpl.persistedStateHash
}
return nil
//我们可以创建一个高速缓存,并保持所有的bucket节点预加载。
//因为,铲斗节点不包含实际数据和最大可能的bucket是预先确定的,所述存储器需求
//可能不是非常高,或者可以容易地控制 - 通过保持在高速缓存中选择性bucket(bucket
//树的最可能的前面几级 - 因为,较高的bucket的水平,
//更是将需要散列重新计算的几率)
}
PrepareWorkingSet
准备工作集func (stateImpl *StateImpl) PrepareWorkingSet(stateDelta *statemgmt.StateDelta) error {
logger.Debug("Enter - PrepareWorkingSet()")
if stateDelta.IsEmpty() {
logger.Debug("Ignoring working-set as it is empty")
return nil
}
stateImpl.dataNodesDelta = newDataNodesDelta(stateDelta)
stateImpl.bucketTreeDelta = newBucketTreeDelta()
stateImpl.recomputeCryptoHash = true
return nil
}
computeDataNodesCryptoHash
计算datanodes的哈希加密计算func computeDataNodesCryptoHash(bucketKey *bucketKey, updatedNodes dataNodes, existingNodes dataNodes) []byte {
logger.Debug("Computing crypto-hash for bucket [%s]. numUpdatedNodes=[%d], numExistingNodes=[%d]", bucketKey, len(updatedNodes), len(existingNodes))
bucketHashCalculator := newBucketHashCalculator(bucketKey)
i := 0
j := 0
for i < len(updatedNodes) && j < len(existingNodes) {
updatedNode := updatedNodes[i]
existingNode := existingNodes[j]
c := bytes.Compare(updatedNode.dataKey.compositeKey, existingNode.dataKey.compositeKey)
var nextNode *dataNode
switch c {
case -1:
nextNode = updatedNode
i++
case 0:
nextNode = updatedNode
i++
j++
case 1:
nextNode = existingNode
j++
}
if !nextNode.isDelete() {
bucketHashCalculator.addNextNode(nextNode)
}
}
var remainingNodes dataNodes
if i < len(updatedNodes) {
remainingNodes = updatedNodes[i:]
} else if j < len(existingNodes) {
remainingNodes = existingNodes[j:]
}
for _, remainingNode := range remainingNodes {
if !remainingNode.isDelete() {
bucketHashCalculator.addNextNode(remainingNode)
}
}
return bucketHashCalculator.computeCryptoHash()
}
state
composite_range_scan_iterator
包装了一个以上的潜在迭代,下面是具体实施,从第一底层迭代器开始,
耗尽第一底层迭代后,移动到第二个潜在的迭代器。实施重复这个直到已经耗尽之前的底层迭代器此外,如果键值是找到从底层迭代器的键值被跳过任一前代的迭代器
func (itr *CompositeRangeScanIterator) Next() bool { |
state_snapshot
// 按实际的实施和数据库快照封装了对状态快照的迭代 |
state
构造全局状态,封装状态持久性的特定管理实现,它不是线程安全的
NewState
构造一个新的状态。对初始化状态的实现进行封装func NewState() *State {
stateImplName := viper.GetString("ledger.state.dataStructure.name")
stateImplConfigs := viper.GetStringMap("ledger.state.dataStructure.configs")
if len(stateImplName) == 0 {
stateImplName = detaultStateImpl
stateImplConfigs = nil
}
switch stateImplName {
case "buckettree":
stateImpl = buckettree.NewStateImpl()
case "trie":
stateImpl = trie.NewStateTrie()
default:
panic(fmt.Errorf("Error during initialization of state implementation. State data structure '%s' is not valid.", stateImplName))
}
err := stateImpl.Initialize(stateImplConfigs)
if err != nil {
panic(fmt.Errorf("Error during initialization of state implementation: %s", err))
}
deltaHistorySize := viper.GetInt("ledger.state.deltaHistorySize")
if deltaHistorySize < 0 {
panic(fmt.Errorf("Delta history size must be greater than or equal to 0. Current value is %d.", deltaHistorySize))
}
return &State{stateImpl, statemgmt.NewStateDelta(), statemgmt.NewStateDelta(), "", make(map[string][]byte),
false, uint64(deltaHistorySize)}
}
TxBegin
标记开始新的tx。如果tx已在进行中,将调用混乱func (state *State) TxBegin(txUUID string) {
logger.Debug("txBegin() for txUuid [%s]", txUUID)
if state.txInProgress() {
panic(fmt.Errorf("A tx [%s] is already in progress. Received call for begin of another tx [%s]", state.currentTxUUID, txUUID))
}
state.currentTxUUID = txUUID
}
Get
返回chaincodeID和键的状态。如果提交为false,首先从内存中查找,如果缺失,从数据库获取。如果为true,仅仅可以从数据库中获取。func (state *State) Get(chaincodeID string, key string, committed bool) ([]byte, error) {
if !committed {
valueHolder := state.currentTxStateDelta.Get(chaincodeID, key)
if valueHolder != nil {
return valueHolder.GetValue(), nil
}
valueHolder = state.stateDelta.Get(chaincodeID, key)
if valueHolder != nil {
return valueHolder.GetValue(), nil
}
}
return state.stateImpl.Get(chaincodeID, key)
}
GetRangeScanIterator
返回一来获取所有startKey和endKey之间的键(和值)的迭代器
对于chaincodeID(假设按照键的词汇顺序)。func (state *State) GetRangeScanIterator(chaincodeID string, startKey string, endKey string, committed bool) (statemgmt.RangeScanIterator, error) {
stateImplItr, err := state.stateImpl.GetRangeScanIterator(chaincodeID, startKey, endKey)
if err != nil {
return nil, err
}
if committed {
return stateImplItr, nil
}
return newCompositeRangeScanIterator(
statemgmt.NewStateDeltaRangeScanIterator(state.currentTxStateDelta, chaincodeID, startKey, endKey),
statemgmt.NewStateDeltaRangeScanIterator(state.stateDelta, chaincodeID, startKey, endKey),
stateImplItr), nil
}
GetHash
如果计算要应用的状态增量如果是新状态的哈希值。
如果stateDelta已最近一次调用后,想要更改此功能只能重新计算func (state *State) GetHash() ([]byte, error) {
logger.Debug("Enter - GetHash()")
if state.updateStateImpl {
logger.Debug("updating stateImpl with working-set")
state.stateImpl.PrepareWorkingSet(state.stateDelta)
state.updateStateImpl = false
}
hash, err := state.stateImpl.ComputeCryptoHash()
if err != nil {
return nil, err
}
logger.Debug("Exit - GetHash()")
return hash, nil
}
trie
trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。
TrieKey
如下是trie key的接口定义type trieKeyInterface interface {
getLevel() int //获取级别
getParentTrieKey() trieKeyInterface //获取父 trie key
getIndexInParent() int //获取索引
getEncodedBytes() []byte
}
newTrieKey
创建一个trie keyfunc newTrieKey(chaincodeID string, key string) *trieKey {
compositeKey := statemgmt.ConstructCompositeKey(chaincodeID, key)
return newTrieKeyFromCompositeKey(compositeKey)
}
newTrieKeyFromCompositeKey
从组合键中创建trie keyfunc newTrieKeyFromCompositeKey(compositeKey []byte) *trieKey {
return &trieKey{trieKeyEncoderImpl.newTrieKey(compositeKey)}
}
getIndexInParent
获取父triekey的索引
func (key *trieKey) getIndexInParent() int {
if key.isRootKey() {
panic(fmt.Errorf("Parent for Trie root shoould not be asked for"))
}
return key.trieKeyImpl.getIndexInParent()
}
getParentTrieKey
获取父 trie keyfunc (key *trieKey) getParentTrieKey() *trieKey {
if key.isRootKey() {
panic(fmt.Errorf("Parent for Trie root shoould not be asked for"))
}
return &trieKey{key.trieKeyImpl.getParentTrieKey()}
}
getEncodedBytes
获得字节编码,如果字节编码为0,代表为根的键值func (key *trieKey) getEncodedBytes() []byte {
return key.trieKeyImpl.getEncodedBytes()
}
assertIsChildOf
断言是否为孩子节点的trie keyfunc (key *trieKey) assertIsChildOf(parentTrieKey *trieKey) {
if !bytes.Equal(key.getParentTrieKey().getEncodedBytes(), parentTrieKey.getEncodedBytes()) {
panic(fmt.Errorf("trie key [%s] is not a child of trie key [%s]", key, parentTrieKey))
}
}
trie_node
trienode的结构如下
type trieNode struct { |
setChildCryptoHash
设置孩子节点加密哈希func (trieNode *trieNode) setChildCryptoHash(index int, childCryptoHash []byte) {
if index >= trieKeyEncoderImpl.getMaxTrieWidth() {
panic(fmt.Errorf("Index for child crypto-hash cannot be greater than [%d]. Tried to access index value [%d]", trieKeyEncoderImpl.getMaxTrieWidth(), index))
}
if childCryptoHash != nil {
trieNode.childrenCryptoHashes[index] = childCryptoHash
}
trieNode.childrenCryptoHashesUpdated[index] = true
}
mergeMissingAttributesFrom
合并丢失属性func (trieNode *trieNode) mergeMissingAttributesFrom(dbTrieNode *trieNode) {
stateTrieLogger.Debug("Enter mergeMissingAttributesFrom() baseNode=[%s], mergeNode=[%s]", trieNode, dbTrieNode)
if !trieNode.valueUpdated {
trieNode.value = dbTrieNode.value
}
for k, v := range dbTrieNode.childrenCryptoHashes {
if !trieNode.childrenCryptoHashesUpdated[k] {
trieNode.childrenCryptoHashes[k] = v
}
}
stateTrieLogger.Debug("Exit mergeMissingAttributesFrom() mergedNode=[%s]", trieNode)
}
computeCryptoHash
哈希加密计算func (trieNode *trieNode) computeCryptoHash() []byte {
stateTrieLogger.Debug("Enter computeCryptoHash() for trieNode [%s]", trieNode)
var cryptoHashContent []byte
if trieNode.containsValue() {
stateTrieLogger.Debug("Adding value to hash computation for trieNode [%s]", trieNode)
key := trieNode.trieKey.getEncodedBytes()
cryptoHashContent = append(cryptoHashContent, proto.EncodeVarint(uint64(len(key)))...)
cryptoHashContent = append(cryptoHashContent, key...)
cryptoHashContent = append(cryptoHashContent, trieNode.value...)
}
sortedChildrenIndexes := trieNode.getSortedChildrenIndex()
for _, index := range sortedChildrenIndexes {
childCryptoHash := trieNode.childrenCryptoHashes[index]
stateTrieLogger.Debug("Adding hash [%#v] for child number [%d] to hash computation for trieNode [%s]", childCryptoHash, index, trieNode)
cryptoHashContent = append(cryptoHashContent, childCryptoHash...)
}
if cryptoHashContent == nil {
// 节点没有关联值,也没有关联孩子节点。
stateTrieLogger.Debug("Returning nil as hash for trieNode = [%s]. Also, marking this key for deletion.", trieNode)
trieNode.markedForDeletion = true
return nil
}
if !trieNode.containsValue() && trieNode.getNumChildren() == 1 {
// 节点没有关联值,并且只有一个孩子节点,传递的孩子hash丢失
stateTrieLogger.Debug("Returning hash as of a single child for trieKey = [%s]", trieNode.trieKey)
return cryptoHashContent
}
stateTrieLogger.Debug("Recomputing hash for trieKey = [%s]", trieNode)
return util.ComputeCryptoHash(cryptoHashContent)
}
marshal
func (trieNode *trieNode) marshal() ([]byte, error) { |
getSortedChildrenIndex
获得孩子节点排序后的索引func (trieNode *trieNode) getSortedChildrenIndex() []int {
keys := make([]int, trieNode.getNumChildren())
i := 0
for k := range trieNode.childrenCryptoHashes {
keys[i] = k
i++
}
sort.Ints(keys)
return keys
}
newTrieDelta
创建trie的增量func newTrieDelta(stateDelta *statemgmt.StateDelta) *trieDelta {
trieDelta := &trieDelta{0, make(map[int]levelDeltaMap)}
chaincodes := stateDelta.GetUpdatedChaincodeIds(false)
for _, chaincodeID := range chaincodes {
updates := stateDelta.GetUpdates(chaincodeID)
for key, updatedvalue := range updates {
if updatedvalue.IsDelete() {
trieDelta.delete(chaincodeID, key)
} else {
if stateDelta.RollBackwards {
trieDelta.set(chaincodeID, key, updatedvalue.GetPreviousValue())
} else {
trieDelta.set(chaincodeID, key, updatedvalue.GetValue())
}
}
}
}
return trieDelta
}
trie_db_helper
从数据库中获取trie节点func fetchTrieNodeFromDB(key *trieKey) (*trieNode, error) {
stateTrieLogger.Debug("Enter fetchTrieNodeFromDB() for trieKey [%s]", key)
openchainDB := db.GetDBHandle()
trieNodeBytes, err := openchainDB.GetFromStateCF(key.getEncodedBytes())
if err != nil {
stateTrieLogger.Error("Error in retrieving trie node from DB for triekey [%s]. Error:%s", key, err)
return nil, err
}
if trieNodeBytes == nil {
return nil, nil
}
trieNode, err := unmarshalTrieNode(key, trieNodeBytes)
if err != nil {
stateTrieLogger.Error("Error in unmarshalling trie node for triekey [%s]. Error:%s", key, err)
return nil, err
}
stateTrieLogger.Debug("Exit fetchTrieNodeFromDB() for trieKey [%s]", key)
return trieNode, nil
}
byteTrieKey
func (encoder *byteTrieKeyEncoder) newTrieKey(originalBytes []byte) trieKeyInterface { |
hexTrieKey
首先定义一个对于索引的映射,类十余bytetriekey,实现了 trieKeyInterface接口var charIndexMap = map[hexTrieKey]int{
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"a": 10,
"b": 11,
"c": 12,
"d": 13,
"e": 14,
"f": 15,
}
func (encoder *hexTrieKeyEncoder) newTrieKey(originalBytes []byte) trieKeyInterface {
return hexTrieKey(hex.EncodeToString(originalBytes))
}
range_scan_iterator
func (itr *RangeScanIterator) Next() bool { |
snapshot_iterator
和range_scan_iterator的实现方式类似func (snapshotItr *StateSnapshotIterator) Next() bool {
var available bool
for ; snapshotItr.dbItr.Valid(); snapshotItr.dbItr.Next() {
trieKeyBytes := statemgmt.Copy(snapshotItr.dbItr.Key().Data())
trieNodeBytes := statemgmt.Copy(snapshotItr.dbItr.Value().Data())
value := unmarshalTrieNodeValue(trieNodeBytes)
if util.NotNil(value) {
snapshotItr.currentKey = trieKeyEncoderImpl.decodeTrieKeyBytes(statemgmt.Copy(trieKeyBytes))
snapshotItr.currentValue = value
available = true
snapshotItr.dbItr.Next()
break
}
}
return available
}
state_trie
结构如下type StateTrie struct {
trieDelta *trieDelta
persistedStateHash []byte 持久化状态哈希
lastComputedCryptoHash []byte 最后哈希加密计算
recomputeCryptoHash bool 重新哈希加密计算
}
processChangedNode
节点改变流程func (stateTrie *StateTrie) processChangedNode(changedNode *trieNode) error {
stateTrieLogger.Debug("Enter - processChangedNode() for node [%s]", changedNode)
dbNode, err := fetchTrieNodeFromDB(changedNode.trieKey)
if err != nil {
return err
}
if dbNode != nil {
stateTrieLogger.Debug("processChangedNode() - merging attributes from db node [%s]", dbNode)
changedNode.mergeMissingAttributesFrom(dbNode)
}
newCryptoHash := changedNode.computeCryptoHash()
parentNode := stateTrie.trieDelta.getParentOf(changedNode)
if parentNode == nil {
parentNode = newTrieNode(changedNode.getParentTrieKey(), nil, false)
stateTrie.trieDelta.addTrieNode(parentNode)
}
parentNode.setChildCryptoHash(changedNode.getIndexInParent(), newCryptoHash)
if logHashOfEveryNode {
stateTrieLogger.Debug("Hash for changedNode[%s]", changedNode)
stateTrieLogger.Debug("%#v", newCryptoHash)
}
stateTrieLogger.Debug("Exit - processChangedNode() for node [%s]", changedNode)
return nil
}
AddChangesForPersistence
为持久化添加更改func (stateTrie *StateTrie) AddChangesForPersistence(writeBatch *gorocksdb.WriteBatch) error {
if stateTrie.recomputeCryptoHash {
_, err := stateTrie.ComputeCryptoHash()
if err != nil {
return err
}
}
if stateTrie.trieDelta == nil {
stateTrieLogger.Info("trieDelta is nil. Not writing anything to DB")
return nil
}
openchainDB := db.GetDBHandle()
lowestLevel := stateTrie.trieDelta.getLowestLevel()
for level := lowestLevel; level >= 0; level-- {
changedNodes := stateTrie.trieDelta.deltaMap[level]
for _, changedNode := range changedNodes {
if changedNode.markedForDeletion {
writeBatch.DeleteCF(openchainDB.StateCF, changedNode.trieKey.getEncodedBytes())
continue
}
serializedContent, err := changedNode.marshal()
if err != nil {
return err
}
writeBatch.PutCF(openchainDB.StateCF, changedNode.trieKey.getEncodedBytes(), serializedContent)
}
}
stateTrieLogger.Debug("Added changes to DB")
return nil
}
commons
commons位于statemgmt目录下
// 构建复合键,并返回唯一代表一个指定的chaincodeID和 []byte字节。 |
hashable_state
由stat实现不同的状态管理来实现接口,可以高效地为不同的工作负载条件下,计算加密哈希状态。type HashableState interface {
//提供了一个机会来初始化。例如, 可以加载数据库的一些数据来实现state
Initialize(configs map[string]interface{}) error
// 从数据库获取值
Get(chaincodeID string, key string) ([]byte, error)
// 需要施加到状态,通过捕获需要的变化的stateDelta
PrepareWorkingSet(stateDelta *StateDelta) error
//计算状态加密哈希来实现state假设状态增量适用以PrepareWorkingSet方法传递
ComputeCryptoHash() ([]byte, error)
//添加的所有键值对,它需要为数据库持续触发statedelta(在、、//PrepareWorkingSet方法传递)。
//除了在StateDelta中的信息,实现还可能希望
//更快进行持久化中间结果的加密哈希计算
AddChangesForPersistence(writeBatch *gorocksdb.WriteBatch) error
// ClearWorkingSet可能会清除state实现,它可能已经建立了计算哈希加密和持续变化的状态增量的数据结构
ClearWorkingSet(changesPersisted bool)
GetStateSnapshotIterator(snapshot *gorocksdb.Snapshot) (StateSnapshotIterator, error)
//提供一种应该给一个给定的chaincodeID使得返回键应该词法更大//所有键值迭代大于或等于startKey且小于或等于endKey。如果startKey参//数的值是假设一个空字符串startKey是在DB的chaincodeID可用最小的关//键。同样,对于endKey参数为空字符串假定endKey可用的分贝为chaincodeID//的最大键
GetRangeScanIterator(chaincodeID string, startKey string, endKey string) (RangeScanIterator, error)
//与StateDelta之前一些提示制备和PrepareWorkingSet方法通//过提供了可能。一个state的实现可以使用这个提示的预取相关数据,因此,//如果这可以提高哈希加密计算方法的性能(当被调用在以后的时间)
PerfHintKeyChanged(chaincodeID string, key string)
}
state_delta_iterator
state增量迭代器结构如下
type StateDeltaIterator struct { |
state_delta
控制变现有的状态。这个结构被用于TX-batchAlso的执行期间中保持未提交的变化,以用于以块的状态转移到另一peertype StateDelta struct {
ChaincodeStateDeltas map[string]*ChaincodeStateDelta
//允许一个控制此增量是否会向前或向后回滚的状态
RollBackwards bool
}
IsUpdatedValueSet
如果更新值已经设置为给定的chaincode ID和密钥,则为truefunc (stateDelta *StateDelta) IsUpdatedValueSet(chaincodeID, key string) bool {
chaincodeStateDelta, ok := stateDelta.ChaincodeStateDeltas[chaincodeID]
if !ok {
return false
}
if _, ok := chaincodeStateDelta.UpdatedKVs[key]; ok {
return true
}
return false
}
ApplyChanges
合并另一增量- 如果一个键值存在,则现有键的值被覆盖func (stateDelta *StateDelta) ApplyChanges(anotherStateDelta *StateDelta) {
for chaincodeID, chaincodeStateDelta := range anotherStateDelta.ChaincodeStateDeltas {
existingChaincodeStateDelta, existingChaincode := stateDelta.ChaincodeStateDeltas[chaincodeID]
for key, valueHolder := range chaincodeStateDelta.UpdatedKVs {
var previousValue []byte
if existingChaincode {
existingUpdateValue, existingUpdate := existingChaincodeStateDelta.UpdatedKVs[key]
if existingUpdate {
// 现有的状态增量已经为这个键值的更新值
previousValue = existingUpdateValue.PreviousValue
} else {
//使用以前的值在新的状态增量的设置
previousValue = valueHolder.PreviousValue
}
} else {
//使用之前值的状态增量
previousValue = valueHolder.PreviousValue
}
if valueHolder.IsDelete() {
stateDelta.Delete(chaincodeID, key, previousValue)
} else {
stateDelta.Set(chaincodeID, key, valueHolder.Value, previousValue)
}
}
}
}
GetUpdatedChaincodeIds
返回在存在于chaincodeIDs的状态增量
如果排序为true,方法按照字典顺序返回在排序之前的chaincodeIDs的排序顺序func (stateDelta *StateDelta) GetUpdatedChaincodeIds(sorted bool) []string {
updatedChaincodeIds := make([]string, len(stateDelta.ChaincodeStateDeltas))
i := 0
for k := range stateDelta.ChaincodeStateDeltas {
updatedChaincodeIds[i] = k
i++
}
if sorted {
sort.Strings(updatedChaincodeIds)
}
return updatedChaincodeIds
}
好了,ledger源码就分析到这里,其实仔细观察,会发现只要有一个iterrator,就会重写对应的next、getvalue方法等,各个模块之间相互协作,比较难的就是hash加密算法,以及状态增量的计算。小编接下来将对各个模块之间进行结构功能图解,敬请期待