sample_chain/peer/ledger/blockfile_mgr.go

176 lines
5.0 KiB
Go

package ledger
import (
"bytes"
"math"
"sync"
"sync/atomic"
"schain/database"
pb "schain/proto"
"schain/proto/util"
log "github.com/corgi-kx/logcustom"
"github.com/pkg/errors"
)
const (
blockNumIdxKeyPrefix = 'n'
blockHashIdxKeyPrefix = 'h'
txIDIdxKeyPrefix = 't'
blockNumTranNumIdxKeyPrefix = 'a'
)
type blockfileMgr struct {
blockStorageDir string
db *database.DBProvider
bootstrappingSnapshotInfo *pb.LastBlockInfo
blkMutex *sync.Mutex
bcInfo atomic.Value
//用于存储区块链信息。atomic.Value 提供了原子操作,用于在多个 goroutine 之间安全地存储和访问区块链信息。
}
func newBlockfileMgr(path string, indexStore *database.DBProvider) (*blockfileMgr, error) {
mgr := &blockfileMgr{blockStorageDir: path, db: indexStore}
bcInfo := &pb.BlockchainInfo{}
if mgr.bootstrappingSnapshotInfo != nil {
bcInfo.Height = mgr.bootstrappingSnapshotInfo.LastBlockNum + 1
bcInfo.CurrentBlockHash = mgr.bootstrappingSnapshotInfo.LastBlockHash
bcInfo.PreviousBlockHash = mgr.bootstrappingSnapshotInfo.PreviousBlockHash
}
mgr.bcInfo.Store(bcInfo)
return mgr, nil
}
func (mgr *blockfileMgr) getBlockchainInfo() *pb.BlockchainInfo {
return mgr.bcInfo.Load().(*pb.BlockchainInfo)
}
func (mgr *blockfileMgr) updateBlockchainInfo(latestBlockHash []byte, latestBlock *pb.Block) {
currentBCInfo := mgr.getBlockchainInfo()
newBCInfo := &pb.BlockchainInfo{
Height: currentBCInfo.Height + 1,
CurrentBlockHash: latestBlockHash,
PreviousBlockHash: latestBlock.Header.PreviousHash,
}
mgr.bcInfo.Store(newBCInfo)
}
func (mgr *blockfileMgr) AddBlock(block *pb.Block) error {
bcInfo := mgr.getBlockchainInfo()
if block.Header.Number != bcInfo.Height {
return errors.Errorf(
"block number should have been %d but was %d",
mgr.getBlockchainInfo().Height, block.Header.Number,
)
}
if !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) {
return errors.Errorf(
"unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]",
bcInfo.CurrentBlockHash, block.Header.PreviousHash,
)
}
blockBytes := util.BlockBytes(block)
blockNumBytes := constructBlockNumKey(block.Header.Number)
if err := mgr.db.Put(blockNumBytes, blockBytes, true); err != nil {
return errors.WithMessage(err, "error saving block to db")
}
blockHashBytes := constructBlockHashKey(block.Header.Hash)
if err := mgr.db.Put(blockHashBytes, blockNumBytes, true); err != nil {
return errors.WithMessage(err, "error saving block to db")
}
blockHash := block.Header.Hash
mgr.updateBlockchainInfo(blockHash, block)
return nil
}
func (mgr *blockfileMgr) retrieveBlockByHash(blockHash []byte) (*pb.Block, error) {
log.Debugf("retrieveBlockByHash() - blockHash = [%#v]", blockHash)
return mgr.getBlockByBlockHash(blockHash)
}
func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64) (*pb.Block, error) {
log.Debugf("retrieveBlockByNumber() - blockNum = [%d]", blockNum)
// interpret math.MaxUint64 as a request for last block
if blockNum == math.MaxUint64 {
blockNum = mgr.getBlockchainInfo().Height - 1
}
if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() {
return nil, errors.Errorf(
"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
blockNum, mgr.firstPossibleBlockNumberInBlockFiles(),
)
}
return mgr.getBlockByBlockNum(blockNum)
}
func (mgr *blockfileMgr) getBlockByBlockNum(blockNum uint64) (*pb.Block, error) {
blockNumHash := constructBlockNumKey(blockNum)
blockByte, err := mgr.db.Get(blockNumHash)
if err != nil {
return nil, err
}
return util.UnmarshalBlock(blockByte)
}
func (mgr *blockfileMgr) getBlockByBlockHash(blockHash []byte) (*pb.Block, error) {
blockHashKey := constructBlockHashKey(blockHash)
blockNumHash, err := mgr.db.Get(blockHashKey)
if err != nil {
return nil, err
}
blockByte, err := mgr.db.Get(blockNumHash)
if err != nil {
return nil, err
}
return util.UnmarshalBlock(blockByte)
}
func (mgr *blockfileMgr) firstPossibleBlockNumberInBlockFiles() uint64 {
if mgr.bootstrappingSnapshotInfo == nil {
return 0
}
return mgr.bootstrappingSnapshotInfo.LastBlockNum + 1
}
func (mgr *blockfileMgr) retrieveBlocks(startNum uint64) (*database.Iterator, error) {
if startNum < mgr.firstPossibleBlockNumberInBlockFiles() {
return nil, errors.Errorf(
"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
startNum, mgr.firstPossibleBlockNumberInBlockFiles(),
)
}
sNum := constructBlockNumKey(startNum)
return mgr.db.GetIterator(sNum, nil)
}
func constructBlockNumKey(blockNum uint64) []byte {
blkNumBytes := EncodeUint64(blockNum)
return append([]byte{blockNumIdxKeyPrefix}, blkNumBytes...)
}
// 用于在数据库中存储区块哈希,以及后续的查询操作
func constructBlockHashKey(blockHash []byte) []byte {
return append([]byte{blockHashIdxKeyPrefix}, blockHash...)
}
func (mgr *blockfileMgr) close() {
mgr.db.Close()
}