位于multichain包下,ChainSupport接口实现了对通道操作交易数据的封装。接口的声明位于multichain/chainsupport.go中。
先来看看ChainSupport接口的定义:
// 该接口实际上与deliver.Support是结合在一起的,但是由于golang语言上的限制,这些方法必须显式声明。
// go语言上的限制可查看网址:https://github.com/golang/go/issues/6977
type ChainSupport interface {
// 交易提交到背书节点之前,需要针对每个智能合约配置背书策略。这个接口就是用来读取相关的背书策略的配置
PolicyManager() policies.Manager
// 读取账本的对象接口
Reader() ledger.Reader
// 返回一个该账本处理的过程中是否有错误发生。只是一个标识
Errored() <-chan struct{}
//下面两个是外置接口
broadcast.Support // 处理交易输入,前面在讲内部类server的时候曾提到过
ConsenterSupport // 定义一些共识机制的帮助方法,直接定义在文件chainsupport.go的开头
//每次对链上数据更改Sequence()会+1
Sequence() uint64 // + 1
//将交易转换成配置交易
ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error)
// 注:当客户端将背书结果发给orderer节点后,orderer节点始终没有打开这个背书结果(除非这个背书结果是配置交易),所以用Envelope信封作为类型名。一切都封装在里面。
}
所以说拿到背书结果后,orderer并不会对背书结果做处理,只是转发。除非是配置交易,因为配置交易本身就是发给orderer节点的,需要被orderer节点的系统通道进行处理,从而产生新的配置。
type ConsenterSupport interface {
// 加密签名
crypto.LocalSigner
// 区块切割对象(后面会讲,这个地方会解释fabric中如何将交易切割成区块的)
BlockCutter() blockcutter.Receiver
// orderer相关的配置
SharedConfig() config.Orderer
// 将切割好的交易打包成区块
CreateNextBlock(messages []*cb.Envelope) *cb.Block
// 写入临时区块
WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block
// 获取与此相关联的通道ID
ChainID() string
// 获取与此相关联的通道上区块高度
Height() uint64
}
下面看看chainSupport的定义
type chainSupport struct {
// 匿名变量,存有链的一些资源信息(配置和账本读写对象)
*ledgerResources
// 通道的接口
chain Chain
// 一个接口,作为用来广播已排好序的消息的存储池
cutter blockcutter.Receiver
// 过滤器。orderer节点不会打开信封来查看具体的交易内容,但是还会做一些过滤操作。比如:查看该交易是否为一个空交易或交易的签名是否正确等。
filters *filter.RuleSet
// 签名
signer crypto.LocalSigner
// 该通道的最新配置信息所在区块高度
lastConfig uint64
// 该通道的最新配置信息的序列号
lastConfigSeq uint64
}
func newChainSupport(
filters *filter.RuleSet,
ledgerResources *ledgerResources,
consenters map[string]Consenter,
signer crypto.LocalSigner,
) *chainSupport {
// 首先生成一个区块切割对象
cutter := blockcutter.NewReceiverImpl(ledgerResources.SharedConfig(), filters)
// 根据配置信息获取到该通道启动时采用的共识机制类型
consenterType := ledgerResources.SharedConfig().ConsensusType()
// 然后将表示共识机制的Consenter根据上面的类型查找出来
consenter, ok := consenters[consenterType]
if !ok {
// 如果系统中不存在配置文件中配置的共识机制类型
logger.Fatalf("Error retrieving consenter of type: %s", consenterType)
// 程序进程。logger.Fatalf()中包含语句:os.Exit(1)
}
// 初始化chainSupport对象
cs := &chainSupport{
ledgerResources: ledgerResources,
cutter: cutter,
filters: filters,
signer: signer,
}
// 更新chainSupport对象中的配置信息的序列号
cs.lastConfigSeq = cs.Sequence()
var err error
// 获取到最新的区块
lastBlock := ledger.GetBlock(cs.Reader(), cs.Reader().Height()-1)
// 如果最新区块的编号不是0,即不是创世区块。由于如果是创世区块的话,最新的配置区块高度就是0,所以不用进入下面的条件语句块中。
if lastBlock.Header.Number != 0 {
// 获取最新的配置信息所在区块高度
cs.lastConfig, err = utils.GetLastConfigIndexFromBlock(lastBlock)
if err != nil {
logger.Fatalf("[channel: %s] Error extracting last config block from block metadata: %s", cs.ChainID(), err)
// 结束进程
}
}
// 获取区块的元数据信息
metadata, err := utils.GetMetadataFromBlock(lastBlock, cb.BlockMetadataIndex_ORDERER)
if err != nil {
logger.Fatalf("[channel: %s] Error extracting orderer metadata: %s", cs.ChainID(), err)
// 结束进程
}
logger.Debugf("[channel: %s] Retrieved metadata for tip of chain (blockNumber=%d, lastConfig=%d, lastConfigSeq=%d): %+v", cs.ChainID(), lastBlock.Header.Number, cs.lastConfig, cs.lastConfigSeq, metadata)
// 利用获取到的元数据和创建的chainSupport对象来为cs中的chain字段(一个通道的接口实现对象)赋值。
cs.chain, err = consenter.HandleChain(cs, metadata)
if err != nil {
logger.Fatalf("[channel: %s] Error creating consenter: %s", cs.ChainID(), err)
// 进程结束
}
// 最后返回这个被初始化好的chainSupport对象,实例化过程结束。
return cs
}
Fabric的区块和其他区块链中的区块有所不同。Fabirc区块中多了一个字段——元数据,用来保存这条通道上最新的配置信息等。那到底元数据中都有那些信息呢?来看看cb.BlockMetadataIndex_ORDERER这个参数:
const (
// 签名
BlockMetadataIndex_SIGNATURES BlockMetadataIndex = 0
// 最新配置
BlockMetadataIndex_LAST_CONFIG BlockMetadataIndex = 1
// 交易过滤器
BlockMetadataIndex_TRANSACTIONS_FILTER BlockMetadataIndex = 2
// order节点相关的配置。不同的共识机制所存储的内容也不同。 比如在kafka模式下,这里面保存的就是kafka客户端读取的offset,在solo模式下并没有存储这个元数据。
BlockMetadataIndex_ORDERER BlockMetadataIndex = 3
)
所以调用utils.GetMetadataFromBlock方法可以从一个区块中获取到以上四种元数据信息
接下来是两个通道过滤器的实例化方法:
// 普通通道
func createStandardFilters(ledgerResources *ledgerResources) *filter.RuleSet {
return filter.NewRuleSet([]filter.Rule{
filter.EmptyRejectRule,
sizefilter.MaxBytesRule(ledgerResources.SharedConfig()),
sigfilter.New(policies.ChannelWriters, ledgerResources.PolicyManager()),
configtxfilter.NewFilter(ledgerResources),
filter.AcceptRule,
})
}
// 系统通道
func createSystemChainFilters(ml *multiLedger, ledgerResources *ledgerResources) *filter.RuleSet {
return filter.NewRuleSet([]filter.Rule{
filter.EmptyRejectRule,
sizefilter.MaxBytesRule(ledgerResources.SharedConfig()),
sigfilter.New(policies.ChannelWriters, ledgerResources.PolicyManager()),
newSystemChainFilter(ledgerResources, ml),
configtxfilter.NewFilter(ledgerResources),
filter.AcceptRule,
})
}
一般过滤器是依次执行的。前一个过滤器没有通过的话,后面的过滤器并不会实现,直接将该交易拒绝掉。逻辑类似一个串联电路,不通过前面,永远也到不了后面。
接下来这些chainSupport的成员方法就是为了简化外界调用而进行的一系列封装。不做详述。
func (cs *chainSupport) start() {
cs.chain.Start()
}
func (cs *chainSupport) NewSignatureHeader() (*cb.SignatureHeader, error) {
return cs.signer.NewSignatureHeader()
}
func (cs *chainSupport) Sign(message []byte) ([]byte, error) {
return cs.signer.Sign(message)
}
func (cs *chainSupport) Filters() *filter.RuleSet {
return cs.filters
}
func (cs *chainSupport) BlockCutter() blockcutter.Receiver {
return cs.cutter
}
func (cs *chainSupport) Reader() ledger.Reader {
return cs.ledger
}
func (cs *chainSupport) Enqueue(env *cb.Envelope) bool {
return cs.chain.Enqueue(env)
}
func (cs *chainSupport) Errored() <-chan struct{} {
return cs.chain.Errored()
}
func (cs *chainSupport) Height() uint64 {
return cs.Reader().Height()
}
下面的逻辑都比较简单,不用细看
// 创建下一个区块
func (cs *chainSupport) CreateNextBlock(messages []*cb.Envelope) *cb.Block {
...
}
// 给区块加数字签名
func (cs *chainSupport) addBlockSignature(block *cb.Block) {
...
}
// 增加最新配置的签名
func (cs *chainSupport) addLastConfigSignature(block *cb.Block) {
...
}
写区块的函数需要着重讲一下:
func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block {
// 这个通过参数传进来的committers切片是经过每一个过滤器都会产生一个Committer对象的集合
// 遍历这个Committer对象的切片
for _, committer := range committers {
// 这些Committer执行在提交消息时他们应该做的所有操作
committer.Commit()
}
// 写入与orderer节点有关的元数据
if encodedMetadataValue != nil {
block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = utils.MarshalOrPanic(&cb.Metadata{Value: encodedMetadataValue})
}
// 给该区块签名
cs.addBlockSignature(block)
// 给该区块最新配置签名
cs.addLastConfigSignature(block)
// 将该区块写入到账本中
err := cs.ledger.Append(block)
if err != nil {
logger.Panicf("[channel: %s] Could not append block: %s", cs.ChainID(), err)
// 程序结束。因为logger.Panicf()中封装了panic()方法
}
logger.Debugf("[channel: %s] Wrote block %d", cs.ChainID(), block.GetHeader().Number)
return block
}
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人