Michael.W谈hyperledger Fabric第18期-详细带读Fabric的源码3-通道对交易数据的操作封装

Michael.W谈hyperledger Fabric第18期-详细带读Fabric的源码3-通道对交易数据的操作封装

    • 1 chainsupport.go文件
    • 2 ChainSupport接口的实例化方法
    • 3 写区块操作

1 chainsupport.go文件

位于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               
	}

2 ChainSupport接口的实例化方法

	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) {
		...
	}

3 写区块操作

写区块的函数需要着重讲一下:

	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神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

你可能感兴趣的:(Fabric,Fabric,fabric,hyperledger,ChainSupport,源码,metadata)