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) {
if blockchain.size == 0 {
return nil, nil
}
return blockchain.getBlock(blockchain.size - 1)
}
getSize
getSize用於返回塊大小
func (blockchain *blockchain) getSize() uint64 {
return blockchain.size
}
getBlock
在blockchain中通過任意高度獲取塊
func (blockchain *blockchain) getBlock(blockNumber uint64) (*protos.Block, error) {
return fetchBlockFromDB(blockNumber)
}
getBlockByHash
通過塊的哈希獲取塊
func (blockchain *blockchain) getBlockByHash(blockHash []byte) (*protos.Block, error) {
blockNumber, err := blockchain.indexer.fetchBlockNumberByBlockHash(blockHash)
if err != nil {
return nil, err
}
return blockchain.getBlock(blockNumber)
}
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) {
block, err := blockchain.getBlock(blockNumber)
if err != nil {
return nil, err
}
return block.GetTransactions()[txIndex], nil
}
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) {
if blockchain.getSize() == 0 {
return &protos.BlockchainInfo{Height: 0}, nil
}
lastBlock, err := blockchain.getLastBlock()
if err != nil {
return nil, err
}
info := &protos.BlockchainInfo{
Height: blockchain.getSize(),
CurrentBlockHash: blockchain.previousBlockHash,
PreviousBlockHash: lastBlock.PreviousBlockHash}
return info, nil
}
buildBlock
創建塊
func (blockchain *blockchain) buildBlock(block *protos.Block, stateHash []byte) *protos.Block {
block.SetPreviousBlockHash(blockchain.previousBlockHash)
block.StateHash = stateHash
return 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) {
if success {
blockchain.size++
blockchain.previousBlockHash = blockchain.lastProcessedBlock.blockHash
if !blockchain.indexer.isSynchronous() {
blockchain.indexer.createIndexesAsync(blockchain.lastProcessedBlock.block,
blockchain.lastProcessedBlock.blockNumber, blockchain.lastProcessedBlock.blockHash)
}
}
blockchain.lastProcessedBlock = nil
}
persistRawBlock
持久化原始塊
func (blockchain *blockchain) persistRawBlock(block *protos.Block, blockNumber uint64) error {
blockBytes, blockBytesErr := block.Bytes()
if blockBytesErr != nil {
return blockBytesErr
}
writeBatch := gorocksdb.NewWriteBatch()
defer writeBatch.Destroy()
writeBatch.PutCF(db.GetDBHandle().BlockchainCF, encodeBlockNumberDBKey(blockNumber), blockBytes)
// 它需要檢查,因爲我們在這樣的情況下塊/狀態同步支持亂序塊。其真正意義區塊鏈的高度,而不是規模。
if blockchain.getSize() < blockNumber+1 {
sizeBytes := encodeUint64(blockNumber + 1)
writeBatch.PutCF(db.GetDBHandle().BlockchainCF, blockCountKey, sizeBytes)
blockchain.size = blockNumber + 1
}
blockHash, err := block.GetHash()
if err != nil {
return err
}
if blockchain.indexer.isSynchronous() {
blockchain.indexer.createIndexesSync(block, blockNumber, blockHash, writeBatch)
}
opt := gorocksdb.NewDefaultWriteOptions()
defer opt.Destroy()
err = db.GetDBHandle().DB.Write(opt, writeBatch)
if err != nil {
return err
}
return nil
}
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) {
block, err := fetchBlockFromDB(blockNum)
if err != nil {
return nil, err
}
return block.GetTransactions()[txIndex], nil
}
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 {
return prependKeyPrefix(prefixTxUUIDKey, []byte(txUUID))
}
對區塊號地址的複合鍵值進行編碼
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 {
modifiedKey := []byte{}
modifiedKey = append(modifiedKey, prefix)
modifiedKey = append(modifiedKey, key...)
return modifiedKey
}
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 {
err := ledger.checkValidIDBegin()
if err != nil {
return err
}
ledger.currentID = id
return nil
}
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 {
err := ledger.checkValidIDCommitORRollback(id)
if err != nil {
return err
}
stateHash, err := ledger.state.GetHash()
if err != nil {
ledger.resetForNextTxGroup(false)
ledger.blockchain.blockPersistenceStatus(false)
return err
}
writeBatch := gorocksdb.NewWriteBatch()
defer writeBatch.Destroy()
block := protos.NewBlock(transactions, metadata)
block.NonHashData = &protos.NonHashData{TransactionResults: transactionResults}
newBlockNumber, err := ledger.blockchain.addPersistenceChangesForNewBlock(context.TODO(), block, stateHash, writeBatch)
if err != nil {
ledger.resetForNextTxGroup(false)
ledger.blockchain.blockPersistenceStatus(false)
return err
}
ledger.state.AddChangesForPersistence(newBlockNumber, writeBatch)
opt := gorocksdb.NewDefaultWriteOptions()
defer opt.Destroy()
dbErr := db.GetDBHandle().DB.Write(opt, writeBatch)
if dbErr != nil {
ledger.resetForNextTxGroup(false)
ledger.blockchain.blockPersistenceStatus(false)
return dbErr
}
ledger.resetForNextTxGroup(true)
ledger.blockchain.blockPersistenceStatus(true)
sendProducerBlockEvent(block)
return nil
}
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) {
return ledger.state.Get(chaincodeID, key, committed)
}
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) {
dbSnapshot := db.GetDBHandle().GetSnapshot()
blockHeight, err := fetchBlockchainSizeFromSnapshot(dbSnapshot)
if err != nil {
dbSnapshot.Release()
return nil, err
}
if 0 == blockHeight {
dbSnapshot.Release()
return nil, fmt.Errorf("Blockchain has no blocks, cannot determine block number")
}
return ledger.state.GetSnapshot(blockHeight-1, dbSnapshot)
}
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) {
// 從部署刪除交易的有效載荷。這樣做是爲了創建塊
//這些類型的交易使事件更輕巧,有效載荷有可能非常大
blockTransactions := block.GetTransactions()
for _, transaction := range blockTransactions {
if transaction.Type == protos.Transaction_CHAINCODE_NEW {
deploymentSpec := &protos.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(transaction.Payload, deploymentSpec)
if err != nil {
ledgerLogger.Error(fmt.Sprintf("Error unmarshalling deployment transaction for block event: %s", err))
continue
}
deploymentSpec.CodePackage = nil
deploymentSpecBytes, err := proto.Marshal(deploymentSpec)
if err != nil {
ledgerLogger.Error(fmt.Sprintf("Error marshalling deployment transaction for block event: %s", err))
continue
}
transaction.Payload = deploymentSpecBytes
}
}
producer.Send(producer.CreateBlockEvent(block))
}
genesis
類似於chaincode,調用go-logging中logging庫的MustGetLogger函數對genesis package進行記錄
var genesisLogger = logging.MustGetLogger("genesis")
MakeGenesis
MakeGenesis基於在openchain.yaml中配置創建創世區塊,並把它添加到blockchain。
func MakeGenesis() error {
once.Do(func() {
ledger, err := ledger.GetLedger()
if err != nil {
makeGenesisError = err
return
}
if ledger.GetBlockchainSize() > 0 {
// 獲取blockchain的大小,如果大於0代表創世區塊已經存在
return
}
genesisLogger.Info("Creating genesis block.")
ledger.BeginTxBatch(0)
var genesisTransactions []*protos.Transaction
//我們現在禁用在有效期內部署,甚至不應該允許它在配置中啓用,將其設置爲false
allowDeployValidityPeriod := false
if(deploySystemChaincodeEnabled() && allowDeployValidityPeriod){
vpTransaction, deployErr := deployUpdateValidityPeriodChaincode()
if deployErr != nil {
genesisLogger.Error("Error deploying validity period system chaincode for genesis block.", deployErr)
makeGenesisError = deployErr
return
}
genesisTransactions = append(genesisTransactions, vpTransaction)
}
genesis := viper.GetStringMap("ledger.blockchain.genesisBlock")
if genesis == nil {
genesisLogger.Info("No genesis block chaincodes defined.")
} else {
chaincodes, chaincodesOK := genesis["chaincode"].([]interface{})
if !chaincodesOK {
genesisLogger.Info("No genesis block chaincodes defined.")
ledger.CommitTxBatch(0, genesisTransactions, nil, nil)
return
}
genesisLogger.Debug("Genesis chaincodes are %s", chaincodes)
for i := 0; i < len(chaincodes); i++ {
genesisLogger.Debug("Chaincode %d is %s", i, chaincodes[i])
chaincodeMap, chaincodeMapOK := chaincodes[i].(map[interface{}]interface{})
if !chaincodeMapOK {
genesisLogger.Error("Invalid chaincode defined in genesis configuration:", chaincodes[i])
makeGenesisError = fmt.Errorf("Invalid chaincode defined in genesis configuration: %s", chaincodes[i])
return
}
path, pathOK := chaincodeMap["path"].(string)
if !pathOK {
genesisLogger.Error("Invalid chaincode URL defined in genesis configuration:", chaincodeMap["path"])
makeGenesisError = fmt.Errorf("Invalid chaincode URL defined in genesis configuration: %s", chaincodeMap["path"])
return
}
chaincodeType, chaincodeTypeOK := chaincodeMap["type"].(string)
if !chaincodeTypeOK {
genesisLogger.Error("Invalid chaincode type defined in genesis configuration:", chaincodeMap["type"])
makeGenesisError = fmt.Errorf("Invalid chaincode type defined in genesis configuration: %s", chaincodeMap["type"])
return
}
chaincodeID := &protos.ChaincodeID{Path: path, Name: ""}
genesisLogger.Debug("Genesis chaincodeID %s", chaincodeID)
genesisLogger.Debug("Genesis chaincode type %s", chaincodeType)
constructorMap, constructorMapOK := chaincodeMap["constructor"].(map[interface{}]interface{})
if !constructorMapOK {
genesisLogger.Error("Invalid chaincode constructor defined in genesis configuration:", chaincodeMap["constructor"])
makeGenesisError = fmt.Errorf("Invalid chaincode constructor defined in genesis configuration: %s", chaincodeMap["constructor"])
return
}
var spec protos.ChaincodeSpec
if constructorMap == nil {
genesisLogger.Debug("Genesis chaincode has no constructor.")
spec = protos.ChaincodeSpec{Type: protos.ChaincodeSpec_Type(protos.ChaincodeSpec_Type_value[chaincodeType]), ChaincodeID: chaincodeID}
} else {
ctorFunc, ctorFuncOK := constructorMap["func"].(string)
if !ctorFuncOK {
genesisLogger.Error("Invalid chaincode constructor function defined in genesis configuration:", constructorMap["func"])
makeGenesisError = fmt.Errorf("Invalid chaincode constructor function args defined in genesis configuration: %s", constructorMap["func"])
return
}
ctorArgs, ctorArgsOK := constructorMap["args"].([]interface{})
if !ctorArgsOK {
genesisLogger.Error("Invalid chaincode constructor args defined in genesis configuration:", constructorMap["args"])
makeGenesisError = fmt.Errorf("Invalid chaincode constructor args defined in genesis configuration: %s", constructorMap["args"])
return
}
genesisLogger.Debug("Genesis chaincode constructor func %s", ctorFunc)
genesisLogger.Debug("Genesis chaincode constructor args %s", ctorArgs)
var ctorArgsStringArray []string
for j := 0; j < len(ctorArgs); j++ {
ctorArgsStringArray = append(ctorArgsStringArray, ctorArgs[j].(string))
}
spec = protos.ChaincodeSpec{Type: protos.ChaincodeSpec_Type(protos.ChaincodeSpec_Type_value[chaincodeType]), ChaincodeID: chaincodeID, CtorMsg: &protos.ChaincodeInput{Function: ctorFunc, Args: ctorArgsStringArray}}
}
transaction, _, deployErr := DeployLocal(context.Background(), &spec)
if deployErr != nil {
genesisLogger.Error("Error deploying chaincode for genesis block.", deployErr)
makeGenesisError = deployErr
return
}
genesisTransactions = append(genesisTransactions, transaction)
}//for
}//else
genesisLogger.Info("Adding %d system chaincodes to the genesis block.", len(genesisTransactions))
ledger.CommitTxBatch(0, genesisTransactions, nil, nil)
})
return makeGenesisError
}
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) {
// 首先建立並得到部署規範
chaincodeDeploymentSpec, err := BuildLocal(ctx, spec)
if err != nil {
genesisLogger.Error(fmt.Sprintf("Error deploying chaincode spec: %v\n\n error: %s", spec, err))
return nil, nil, err
}
transaction, err := protos.NewChaincodeDeployTransaction(chaincodeDeploymentSpec, chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name)
if err != nil {
return nil, nil, fmt.Errorf("Error deploying chaincode: %s ", err)
}
//chaincode.NewChaincodeSupport(chaincode.DefaultChain, peer.GetPeerEndpoint, false, 120000)
// secHelper設置在ChaincodeSupport創建期間,因此我們不需要這一步
//ctx = context.WithValue(ctx, "security", secCxt)
result, err := chaincode.Execute(ctx, chaincode.GetChain(chaincode.DefaultChain), transaction)
return transaction, result, err
}
設置是否部署系統chaincode
func 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) {
c.hashingData = append(c.hashingData, proto.EncodeVarint(uint64(size))...)
}
bucket_key
bucket key的結構如下
type bucketKey struct {
level int //級別
bucketNumber int //bucket號
}
newBucketKey
當level爲0,bucketNumber爲1時,構造bucket樹根節點;
當level爲bucketKey.level-1, bucketNumber爲conf.computeParentBucketNumber(bucketKey.bucketNumber)
時構建的是父節點的bucketkey
func 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 {
bucketKey *bucketKey
childrenCryptoHash [][]byte //子節點的加密哈希
childrenUpdated []bool //子節點更新
markedForDeletion bool //刪除標記
}
unmarshalBucketNode
重組bucketnode
func 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樹增量
func newBucketTreeDelta() *bucketTreeDelta {
return &bucketTreeDelta{make(map[int]byBucketNumber)}
}
//獲取或者創建Bucket節點
func (bucketTreeDelta *bucketTreeDelta) getOrCreateBucketNode(bucketKey *bucketKey) *bucketNode {
byBucketNumber := bucketTreeDelta.byLevel[bucketKey.level]
if byBucketNumber == nil {
byBucketNumber = make(map[int]*bucketNode)
bucketTreeDelta.byLevel[bucketKey.level] = byBucketNumber
}
bucketNode := byBucketNumber[bucketKey.bucketNumber]
if bucketNode == nil {
bucketNode = newBucketNode(bucketKey)
byBucketNumber[bucketKey.bucketNumber] = bucketNode
}
return bucketNode
}
//獲取某一級別下的bucket節點
func (bucketTreeDelta *bucketTreeDelta) getBucketNodesAt(level int) []*bucketNode {
bucketNodes := []*bucketNode{}
byBucketNumber := bucketTreeDelta.byLevel[level]
if byBucketNumber == nil {
return nil
}
for _, bucketNode := range byBucketNumber {
bucketNodes = append(bucketNodes, bucketNode)
}
return bucketNodes
}
//獲取根節點
func (bucketTreeDelta *bucketTreeDelta) getRootNode() *bucketNode {
bucketNodes := bucketTreeDelta.getBucketNodesAt(0)
if bucketNodes == nil || len(bucketNodes) == 0 {
panic("This method should be called after processing is completed (i.e., the root node has been created)")
}
return bucketNodes[0]
}
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
func newDataKey(chaincodeID string, key string) *dataKey {
logger.Debug("Enter - newDataKey. chaincodeID=[%s], key=[%s]", chaincodeID, key)
compositeKey := statemgmt.ConstructCompositeKey(chaincodeID, key)
bucketHash := conf.computeBucketHash(compositeKey)
// 添加一個,因爲 - 我們開始啓動bucket的數爲1
bucketNumber := int(bucketHash)%conf.getNumBucketsAtLowestLevel() + 1
dataKey := &dataKey{newBucketKeyAtLowestLevel(bucketNumber), compositeKey}
logger.Debug("Exit - newDataKey=[%s]", dataKey)
return dataKey
}
//最小化DataKey可能的字節
func minimumPossibleDataKeyBytesFor(bucketKey *bucketKey) []byte {
min := encodeBucketNumber(bucketKey.bucketNumber)
min = append(min, byte(0))
return min
}
func minimumPossibleDataKeyBytes(bucketNumber int, chaincodeID string, key string) []byte {
b := encodeBucketNumber(bucketNumber)
b = append(b, statemgmt.ConstructCompositeKey(chaincodeID, key)...)
return b
}
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
獲取受到影響的buckets
func (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
//接口實現
type StateSnapshotIterator struct {
dbItr *gorocksdb.Iterator
}
//創建迭代器
func newStateSnapshotIterator(snapshot *gorocksdb.Snapshot) (*StateSnapshotIterator, error) {
dbItr := db.GetDBHandle().GetStateCFSnapshotIterator(snapshot)
dbItr.Seek([]byte{0x01})
dbItr.Prev()
return &StateSnapshotIterator{dbItr}, nil
}
// 接口實現細節-Next
func (snapshotItr *StateSnapshotIterator) Next() bool {
snapshotItr.dbItr.Next()
return snapshotItr.dbItr.Valid()
}
// 接口實現細節-GetRawKeyValue
func (snapshotItr *StateSnapshotIterator) GetRawKeyValue() ([]byte, []byte) {
//創建鍵 - 值字節的副本,因爲潛在的鍵值字節由UTR重用。關閉時沒有必要爲迭代器釋放內存而釋放切片。
keyBytes := statemgmt.Copy(snapshotItr.dbItr.Key().Data())
valueBytes := statemgmt.Copy(snapshotItr.dbItr.Value().Data())
dataNode := unmarshalDataNodeFromBytes(keyBytes, valueBytes)
return dataNode.getCompositeKey(), dataNode.getValue()
}
// 接口實現細節-Close
func (snapshotItr *StateSnapshotIterator) Close() {
snapshotItr.dbItr.Close()
}
state_impl
實現了 ‘statemgmt.HashableState’接口
NewStateImpl
構建一個新的StateImpl
func 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 {
currentItrNumber := itr.currentItrNumber
currentItr := itr.itrs[currentItrNumber]
logger.Debug("Operating on iterator number = %d", currentItrNumber)
keyAvailable := currentItr.Next()
for keyAvailable {
key, _ := currentItr.GetKeyValue()
logger.Debug("Retrieved key = %s", key)
skipKey := false
for i := currentItrNumber - 1; i >= 0; i-- {
logger.Debug("Evaluating key = %s in itr number = %d. currentItrNumber = %d", key, i, currentItrNumber)
previousItr := itr.itrs[i]
if previousItr.(*statemgmt.StateDeltaIterator).ContainsKey(key) {
skipKey = true
break
}
}
if skipKey {
logger.Debug("Skipping key = %s", key)
keyAvailable = currentItr.Next()
continue
}
break
}
if keyAvailable || currentItrNumber == 2 {
logger.Debug("Returning for current key")
return keyAvailable
}
logger.Debug("Moving to next iterator")
itr.currentItrNumber++
return itr.Next()
}
state_snapshot
// 按實際的實施和數據庫快照封裝了對狀態快照的迭代
type StateSnapshot struct {
blockNumber uint64
stateImplItr statemgmt.StateSnapshotIterator
dbSnapshot *gorocksdb.Snapshot
}
// 創建當前塊全局狀態的新快照
func newStateSnapshot(blockNumber uint64, dbSnapshot *gorocksdb.Snapshot) (*StateSnapshot, error) {
itr, err := stateImpl.GetStateSnapshotIterator(dbSnapshot)
if err != nil {
return nil, err
}
snapshot := &StateSnapshot{blockNumber, itr, dbSnapshot}
return snapshot, nil
}
//當您使用這個資源做這必須調用釋放快照
func (ss *StateSnapshot) Release() {
ss.stateImplItr.Close()
ss.dbSnapshot.Release()
}
//接下來將迭代器移動到下一個鍵/值對的狀態
func (ss *StateSnapshot) Next() bool {
return ss.stateImplItr.Next()
}
//返回在當前迭代器位置的鍵和值的原始字節
func (ss *StateSnapshot) GetRawKeyValue() ([]byte, []byte) {
return ss.stateImplItr.GetRawKeyValue()
}
// 返回與此全局狀態的快照相關聯的塊號
func (ss *StateSnapshot) GetBlockNumber() uint64 {
return ss.blockNumber
}
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 key
func newTrieKey(chaincodeID string, key string) *trieKey {
compositeKey := statemgmt.ConstructCompositeKey(chaincodeID, key)
return newTrieKeyFromCompositeKey(compositeKey)
}
newTrieKeyFromCompositeKey
從組合鍵中創建trie key
func 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 key
func (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 key
func (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 {
trieKey *trieKey
value []byte //值
childrenCryptoHashes map[int][]byte//孩子節點的哈希加密,key爲int,value爲byte
valueUpdated bool //值是否更新
childrenCryptoHashesUpdated map[int]bool//是否產生新的哈希加密
markedForDeletion bool //節店刪除狀態標記
}
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) {
buffer := proto.NewBuffer([]byte{})
// 寫入值
err := buffer.EncodeRawBytes(trieNode.value)
if err != nil {
return nil, err
}
numCryptoHashes := trieNode.getNumChildren()
//寫加密哈希數
err = buffer.EncodeVarint(uint64(numCryptoHashes))
if err != nil {
return nil, err
}
if numCryptoHashes == 0 {
return buffer.Bytes(), nil
}
for i, cryptoHash := range trieNode.childrenCryptoHashes {
//寫入加密哈希索引
err = buffer.EncodeVarint(uint64(i))
if err != nil {
return nil, err
}
// 寫入加密哈希
err = buffer.EncodeRawBytes(cryptoHash)
if err != nil {
return nil, err
}
}
return buffer.Bytes(), nil
}
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 {
len := len(originalBytes)
remainingBytes := len % numBytesAtEachLevel
//剩餘字節=長度和每一個級別字節數的餘數
bytesToAppend := 0
if remainingBytes != 0 {
bytesToAppend = numBytesAtEachLevel - remainingBytes
}
for i := 0; i < bytesToAppend; i++ {
originalBytes = append(originalBytes, byte(0))
}
return byteTrieKey(originalBytes)
}
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 {
if itr.done {
return false
}
for ; itr.dbItr.Valid(); itr.dbItr.Next() {
//使得鍵 - 值字節的副本,以至於使得潛在的鍵值字節由ITR重用。
//關閉時沒有必要爲迭代器釋放內存釋放切片。
trieKeyBytes := statemgmt.Copy(itr.dbItr.Key().Data())
trieNodeBytes := statemgmt.Copy(itr.dbItr.Value().Data())
value := unmarshalTrieNodeValue(trieNodeBytes)
if util.IsNil(value) {
continue
}
// 找到一個實際的鍵值
currentCompositeKey := trieKeyEncoderImpl.decodeTrieKeyBytes(statemgmt.Copy(trieKeyBytes))
currentChaincodeID, currentKey := statemgmt.DecodeCompositeKey(currentCompositeKey)
if currentChaincodeID == itr.chaincodeID && (itr.endKey == "" || currentKey <= itr.endKey) {
itr.currentKey = currentKey
itr.currentValue = value
itr.dbItr.Next()
return true
}
// 檢索指定的範圍內的所有的密鑰
break
}
itr.done = true
return false
}
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字節。
//這假定chaincodeID不包含0×00字節
//但鍵值的可以強制實施的限制chaincodeID或使用長度前綴,而不是這裏的分隔符
func ConstructCompositeKey(chaincodeID string, key string) []byte {
return bytes.Join([][]byte{[]byte(chaincodeID), []byte(key)}, stateKeyDelimiter)
}
//通過解碼構建了compositeKey構造複合鍵的方法,返回原始chaincodeID和鍵形式
func DecodeCompositeKey(compositeKey []byte) (string, string) {
split := bytes.SplitN(compositeKey, stateKeyDelimiter, 2)
return string(split[0]), string(split[1])
}
//返回指定字節的副本
func Copy(src []byte) []byte {
dest := make([]byte, len(src))
copy(dest, src)
return dest
}
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 {
updates map[string]*UpdatedValue //更新
relevantKeys []string // 關聯鍵
currentKeyIndex int // 當前鍵的索引
done bool //完成標識
}
state_delta
控制變現有的狀態。這個結構被用於TX-batchAlso的執行期間中保持未提交的變化,以用於以塊的狀態轉移到另一peer
type StateDelta struct {
ChaincodeStateDeltas map[string]*ChaincodeStateDelta
//允許一個控制此增量是否會向前或向後回滾的狀態
RollBackwards bool
}
IsUpdatedValueSet
如果更新值已經設置爲給定的chaincode ID和密鑰,則爲true
func (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加密算法,以及狀態增量的計算。小編接下來將對各個模塊之間進行結構功能圖解,敬請期待