前面几个篇幅,我们赏析了核心包之 BCCSP包,今天,我们赏析CORE核心包,CORE核心包是Fabric的相关核心模块。包括背书、链码等。我们先从commiter包开始。
commiter负责在接受交易结果前再次检查合法性,接受合法交易对账本的修改,并写入区块链结构。
方法:CommitLegacy(blockAndPvtData *ledger.BlockAndPvtData, commitOpts *ledger.CommitOptions) error
// 将区块和私有数据写入账本
GetPvtDataAndBlockByNum(seqNum uint64) (*ledger.BlockAndPvtData, error)
// sequence number 使用给定的私有数据检索块
GetPvtDataByNum(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error)
从给定区块中返回私有数据切片,并根据过滤器指示要检索的私有数据的集合和名称空间的映射
LedgerHeight() (uint64, error)
// 获取最近的区块序列号
DoesPvtDataInfoExistInLedger(blockNum uint64) (bool, error)
//根据参数提供的序列号获取区块是否存在
GetBlocks(blockSeqs []uint64) []*common.Block
// 根据参数提供的序列号获取区块
CommitPvtDataOfOldBlocks(reconciledPvtdata []*ledger.ReconciledPvtdata) ([]*ledger.PvtdataHashMismatch, error)
在确认的块中提交一笔数据,如果匹配,就返回hash,否则返回错误。
结构体:
type LedgerCommitter struct {
PeerLedgerSupport
}
// LedgerCommitter 是commiter接口的实现,它保持对账本的引用来提交区块和检索链信息
func NewLedgerCommitter(ledger PeerLedgerSupport) *LedgerCommitter {
return &LedgerCommitter{PeerLedgerSupport: ledger}
}
// NewLedgerCommitter 是一个工厂函数,用于创建提交者的一个实例,他通过验证传入传入块,并将它们提交到分类账中
func (lc *LedgerCommitter) CommitLegacy(blockAndPvtData *ledger.BlockAndPvtData, commitOpts *ledger.CommitOptions) error {
// Committing new block
if err := lc.PeerLedgerSupport.CommitLegacy(blockAndPvtData, commitOpts); err != nil {
return err
}
return nil
}
提交一个block
func (lc *LedgerCommitter) GetPvtDataAndBlockByNum(seqNum uint64) (*ledger.BlockAndPvtData, error) {
return lc.PeerLedgerSupport.GetPvtDataAndBlockByNum(seqNum, nil)
}
根据给定点的block的序号,返回数据
func NewLedgerCommitterReactive(ledger ledger.PeerLedger, eventer ConfigBlockEventer) *LedgerCommitter {
return &LedgerCommitter{PeerLedger: ledger, eventer: eventer}
}
// NewLedgerCommitterReactive 是一个工厂函数,用于创建与NewLedgerCommitter相同的提交者实例,同时还提供一个选项来指定在新的配置块到达和提交事件时调用的回调
func (lc *LedgerCommitter) preCommit(block *common.Block) error {
// Updating CSCC with new configuration block
if utils.IsConfigBlock(block) {
logger.Debug("Received configuration update, calling CSCC ConfigUpdate")
if err := lc.eventer(block); err != nil {
return errors.WithMessage(err, "could not update CSCC with new configuration update")
}
}
return nil
}
// preCommit负责验证块并根据其内容进行更新
func (lc *LedgerCommitter) CommitWithPvtData(blockAndPvtData *ledger.BlockAndPvtData) error {
// Do validation and whatever needed before
// committing new block
if err := lc.preCommit(blockAndPvtData.Block); err != nil {
return err
}
// CommitWithPvtData以私有数据自动提交块
func (lc *LedgerCommitter) GetPvtDataAndBlockByNum(seqNum uint64) (*ledger.BlockAndPvtData, error) {
return lc.PeerLedger.GetPvtDataAndBlockByNum(seqNum, nil)
}
// GetPvtDataAndBlockByNum通过给定的私有数据检索块序列号
func (lc *LedgerCommitter) postCommit(block *common.Block) {
// create/send block events *after* the block has been committed
bevent, fbevent, channelID, err := producer.CreateBlockEvents(block)
if err != nil {
logger.Errorf("Channel [%s] Error processing block events for block number [%d]: %+v", channelID, block.Header.Number, err)
} else {
if err := producer.Send(bevent); err != nil {
logger.Errorf("Channel [%s] Error sending block event for block number [%d]: %+v", channelID, block.Header.Number, err)
}
if err := producer.Send(fbevent); err != nil {
logger.Errorf("Channel [%s] Error sending filtered block event for block number [%d]: %+v", channelID, block.Header.Number, err)
}
}
}
// postCommit 一旦提交到分类账本,则通过postcommit发布事件或处理其他任务
func (lc *LedgerCommitter) LedgerHeight() (uint64, error) {
var info *common.BlockchainInfo
var err error
if info, err = lc.GetBlockchainInfo(); err != nil {
logger.Errorf("Cannot get blockchain info, %s", info)
return uint64(0), err
}
return info.Height, nil
}
// LedgerHeight返回最近提交的块顺序号
func (lc *LedgerCommitter) GetBlocks(blockSeqs []uint64) []*common.Block {
var blocks []*common.Block
for _, seqNum := range blockSeqs {
if blck, err := lc.GetBlockByNumber(seqNum); err != nil {
logger.Errorf("Not able to acquire block num %d, from the ledger skipping...", seqNum)
continue
} else {
logger.Debug("Appending next block with seqNum = ", seqNum, " to the resulting set")
blocks = append(blocks, blck)
}
}
return blocks
}
// GetBlocks 用于检索切片中提供的序号的块
Acquire(ctx context.Context, n int64) error
// Acquire 实现信号量的获取语义
// Release 实现了信号量释放语义
Release(n int64)
// Ledger returns the ledger associated with this validator
// Ledger返回与此验证程序相关联的账本
Ledger() ledger.PeerLedger
// MSPManager returns the MSP manager for this channel
// MSPManager 返回此channel的MSP管理器
MSPManager() msp.MSPManager
// Apply attempts to apply a configtx to become the new config
// Apply尝试使用configtx成为新的配置
Apply(configtx *common.ConfigEnvelope) error
// GetMSPIDs返回已在channel中定义的应用程序MSP的ID
GetMSPIDs(cid string) []string
// Capabilities 定义此channel的应用程序部分的功能
Capabilities() channelconfig.ApplicationCapabilities
// ChaincodeByName 返回指定channel的chaincode定义,并判断是否存在
ChaincodeByName(chainname, ccname string) (resourcesconfig.ChaincodeDefinition, bool)
}
// Validator 定义API以验证块事务并返回位数组掩码,指示未通过验证的无效事务
type Validator interface {
Validate(block *common.Block) error
}
// vsccValidator 用来解耦tx validator和vscc execution,以提高txValidator的可测性
type vsccValidator interface {
VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode)
}
// vsccValidatorImpl vsccValidator的实现,用于调用vscc chaincode并验证块事务
type vsccValidatorImpl struct {
support Support
ccprovider ccprovider.ChaincodeProvider
sccprovider sysccprovider.SystemChaincodeProvider
}
// txValidator Validator的实现,继续参考账本以启用tx模拟和执行vscc
type txValidator struct {
support Support
vscc vsccValidator
}
// NewTxValidator 创建新的事务验证器
func NewTxValidator(support Support) Validator {
// Encapsulates interface implementation
return &txValidator{support,
&vsccValidatorImpl{
support: support,
ccprovider: ccprovider.GetChaincodeProvider(),
sccprovider: sysccprovider.GetSystemChaincodeProvider()}}
}
/*
Validate执行块的验证,并执行块中每个是事务的验证方法如下:提交程序线程在goroutine中启动tx验证函数,
(使用信号量来限制并发验证goroutine的数量)。该thread然后从结果channel中读取验证结果(在完成goroutine的订购者中)。
goroutine执行块中txs的验证并将结果排入结果channel中
1)为了保持验证方法简单,提交者线程将块中的所有事务排入队列,然后继续读取结果
2)为了使并行验证正常工作,重要的是验证功能不会改变系统的状态。此处验证执行的顺序很重要,我们不得不求助于顺序验证(或者一些锁定)
。目前就是这样做的,因为影响状态的函数是收到交易并验证交易,但这必须保证交易独立在区块中,如果这个条件改变了,代码也要改变。
*/
func (v *txValidator) Validate(block *common.Block) error
// generateCCKey 为特定channel中的chaincode生成唯一标识符
func (v *txValidator) generateCCKey(ccName, chainID string) string {
return fmt.Sprintf("%s/%s", ccName, chainID)
}
// invalidTXsForUpgradeCC 无效所有由于chaincode升级txs而应该被废止的txs
func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*sysccprovider.ChaincodeInstance, txsUpgradedChaincodes map[int]*sysccprovider.ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags
// GetInfoForValidate 从lscc获取tx、vscc和policy的chaincode实例(最新版本)
func (v *vsccValidatorImpl) GetInfoForValidate(txid, chID, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error)
// txWritesToNamespace 确认提供NsRwSet执行账本写入
func (v *vsccValidatorImpl) txWritesToNamespace(ns *rwsetutil.NsRwSet) bool
2020年7月31日整理于深圳