fabric1.0学习笔记(3)

fabric1.0学习笔记(1)
fabric1.0学习笔记(2)
fabric1.0学习笔记(3)
fabric1.0学习笔记(4)
fabric1.0学习笔记(5)
fabric1.0学习笔记(6)

在 fabric1.0学习笔记(2)中对main函数中用到的manager包进行了了解,它就像一个管家,管理了orderer节点涉及的链(也可以说是通道)、共识机制、账本对象及创建链方法、对账本的操作方法等。其中用到了一个chainsupport对象,它就像是一条链的管家,管理一条链上信息,主要是账本资源及区块处理方法(过滤器、区块切割器)等,下面是对chainsupport的解析

  • ChainSupport包
// ConsenterSupport provides the resources available to a Consenter implementation
type ConsenterSupport interface {
	crypto.LocalSigner                                 // 签名
	BlockCutter() blockcutter.Receiver                 //区块切割对象
	SharedConfig() config.Orderer                      //orderer相关配置
	CreateNextBlock(messages []*cb.Envelope) *cb.Block //把切割好的交易打包成区块
	WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block                //将区块写入临时区块
	ChainID() string // ChainID returns the chain ID this specific consenter instance is associated with                          //链(channel)的ID
	Height() uint64  // Returns the number of blocks on the chain this specific consenter instance is associated with                //区块高度
}

// ChainSupport provides a wrapper for the resources backing a chain
type ChainSupport interface {
	// This interface is actually the union with the deliver.Support but because of a golang
	// limitation https://github.com/golang/go/issues/6977 the methods must be explicitly declared

	// PolicyManager returns the current policy manager as specified by the chain config
	'读取背书策略(返回当前的policy manager(策略管理器))'
	PolicyManager() policies.Manager

	// Reader returns the chain Reader for the chain
	'读取账本的对象接口(返回链的Reader(账本的操作对象))'
	Reader() ledger.Reader

	// Errored returns whether the backing consenter has errored
	'返回账本的处理过程是否有错误(返回打包的共识机制是否正确)'
	Errored() <-chan struct{}

	broadcast.Support '处理交易输入(返回广播所用的辅助资源)'
	ConsenterSupport '共识机制的帮助方法(返回共识机制实现所需要的资源)'

	// Sequence returns the current config sequence number
	'每次更新配置都会加一(返回当前配置序列号)'
	Sequence() uint64

	// ProposeConfigUpdate applies a CONFIG_UPDATE to an existing config to produce a *cb.ConfigEnvelope
	'将一个交易转换为配置交易(更新配置信息)'
	'envelope信封,交易就封装在其中,orderer节点收到的就是信封,并不会打开,不会对交易进行处理,配置交易除外,配置交易就是发给orderer节点的'
	ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error)
}

type chainSupport struct {
	*ledgerResources 					//账本资源类,配置、账本读写对象,匿名对象
	chain         Chain 				//链的接口(chain对象(定义了一种orderer节点消息注入的方法))
	cutter        blockcutter.Receiver  //区块切割(广播消息接受器)
	filters       *filter.RuleSet 		//过滤器(规则集),过滤空交易,过滤交易签名错误的交易
	signer        crypto.LocalSigner 	// 签名对象,一般由msp实现
	lastConfig    uint64			    //最新的配置信息所在的区块高度
	lastConfigSeq uint64 				//最新配置信息的序列号
}

func newChainSupport(
	filters *filter.RuleSet, 			//过滤器对象
	ledgerResources *ledgerResources,   //账本配置资源对象,配置、账本读写对象,匿名对象
	consenters map[string]Consenter, 	//支持的共识机制map
	signer crypto.LocalSigner, 			//签名对象
) *chainSupport {

	cutter := blockcutter.NewReceiverImpl(ledgerResources.SharedConfig(), filters)	'生成区块切割对象(根据账本资源配置注册区块切割器)'
	consenterType := ledgerResources.SharedConfig().ConsensusType()'(根据配置查询orderer启动时的共识机制)设置共识机制类型'
	consenter, ok := consenters[consenterType]'(共识机制类型对应的共识机制对象)'
	if !ok {
		logger.Fatalf("Error retrieving consenter of type: %s", consenterType)
	}
	//赋值
	cs := &chainSupport{
		ledgerResources: ledgerResources,
		cutter:          cutter,
		filters:         filters,
		signer:          signer,
	}
	//序列号
	cs.lastConfigSeq = cs.Sequence()

	var err error
	lastBlock := ledger.GetBlock(cs.Reader(), cs.Reader().Height()-1)//获取最新区块

	// If this is the genesis block, the lastconfig field may be empty, and, the last config is necessary 0
	// so no need to initialize lastConfig
	'获取最新的区块配置信息'
	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)'(获取某个区块里的元数据)元数据主要保存的是一条链上的配置信息'
	// Assuming a block created with cb.NewBlock(), this should not
	// error even if the orderer metadata is an empty byte slice
	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)

	cs.chain, err = consenter.HandleChain(cs, metadata)    //根据元数据填充chain对象(获取链的引用)
	if err != nil {
		logger.Fatalf("[channel: %s] Error creating consenter: %s", cs.ChainID(), err)
	}

	return cs
}

'一般前一个过滤器没有通过后面的不会执行,会将交易拒绝'
// createStandardFilters creates the set of filters for a normal (non-system) chain
'给非系统链创建过滤器'
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,
	})

}

// createSystemChainFilters creates the set of filters for the ordering system chain
'为系统链创建过滤器'
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,
	})
}
'链的启动方法'
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) CreateNextBlock(messages []*cb.Envelope) *cb.Block {
	return ledger.CreateNextBlock(cs.ledger, messages)
}

func (cs *chainSupport) addBlockSignature(block *cb.Block) {
	logger.Debugf("%+v", cs)
	logger.Debugf("%+v", cs.signer)

	blockSignature := &cb.MetadataSignature{
		SignatureHeader: utils.MarshalOrPanic(utils.NewSignatureHeaderOrPanic(cs.signer)),
	}

	// Note, this value is intentionally nil, as this metadata is only about the signature, there is no additional metadata
	// information required beyond the fact that the metadata item is signed.
	blockSignatureValue := []byte(nil)

	blockSignature.Signature = utils.SignOrPanic(cs.signer, util.ConcatenateBytes(blockSignatureValue, blockSignature.SignatureHeader, block.Header.Bytes()))

	block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = utils.MarshalOrPanic(&cb.Metadata{
		Value: blockSignatureValue,
		Signatures: []*cb.MetadataSignature{
			blockSignature,
		},
	})
}

func (cs *chainSupport) addLastConfigSignature(block *cb.Block) {
	configSeq := cs.Sequence()
	if configSeq > cs.lastConfigSeq {
		logger.Debugf("[channel: %s] Detected lastConfigSeq transitioning from %d to %d, setting lastConfig from %d to %d", cs.ChainID(), cs.lastConfigSeq, configSeq, cs.lastConfig, block.Header.Number)
		cs.lastConfig = block.Header.Number
		cs.lastConfigSeq = configSeq
	}

	lastConfigSignature := &cb.MetadataSignature{
		SignatureHeader: utils.MarshalOrPanic(utils.NewSignatureHeaderOrPanic(cs.signer)),
	}

	lastConfigValue := utils.MarshalOrPanic(&cb.LastConfig{Index: cs.lastConfig})
	logger.Debugf("[channel: %s] About to write block, setting its LAST_CONFIG to %d", cs.ChainID(), cs.lastConfig)

	lastConfigSignature.Signature = utils.SignOrPanic(cs.signer, util.ConcatenateBytes(lastConfigValue, lastConfigSignature.SignatureHeader, block.Header.Bytes()))

	block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = utils.MarshalOrPanic(&cb.Metadata{
		Value: lastConfigValue,
		Signatures: []*cb.MetadataSignature{
			lastConfigSignature,
		},
	})
}
'保存区块'
func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block {
	//过滤器额外操作
	for _, committer := range committers {
		committer.Commit()
	}
	// Set the orderer-related metadata field
	//写与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.Debugf("[channel: %s] Wrote block %d", cs.ChainID(), block.GetHeader().Number)

	return block
}
'返回账本里的区块高度'
func (cs *chainSupport) Height() uint64 {
	return cs.Reader().Height()
}

区块切割也是orderer的一大重要内容在上面的chainsupport类中有用到 ,相关内容保存在blockcutter包中。区块切割的整个实现看起来还是很简单的,具体就是分成几种需要切割的情况进行检测,检测到就进行切割,

  • 1 当前交易过大或者需要隔离一般是配置交易
  • 2 加上当前交易消息队列容量超出限制
  • 3 加上当前交易消息队列数量超出限制
    其中的cut方法就是返回当前消息队列(指针)并置空,传递到ordered方法中通过标记调用相关的方法实现切割。
cutter        blockcutter.Receiver //区块切割(广播消息接受器)
  • blockcutter包
func (r *receiver) Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, committerBatches [][]filter.Committer, validTx bool, pending bool) {
	// The messages must be filtered a second time in case configuration has changed since the message was received
	//对消息进行再次过滤,第一次是在orderer节点接受到消息时
	committer, err := r.filters.Apply(msg)
	if err != nil {
		logger.Debugf("Rejecting message: %s", err)
		return // We don't bother to determine `pending` here as it's not processed in error case
	}

	// message is valid
	//把交易标记为有效,是个返回值
	validTx = true
	//计算交易体所占存储空间,消息体body即payload大小加上签名大小
	messageSizeBytes := messageSizeBytes(msg)

	//处理需要隔离的消息一般是配置交易需要被单独切割到一个区块中 或者 某个消息所占存储容量超过最大限制则进行切割
	if committer.Isolated() || messageSizeBytes > r.sharedConfigManager.BatchSize().PreferredMaxBytes {

		if committer.Isolated() {
			logger.Debugf("Found message which requested to be isolated, cutting into its own batch")
		} else {
			logger.Debugf("The current message, with %v bytes, is larger than the preferred batch size of %v bytes and will be isolated.", messageSizeBytes, r.sharedConfigManager.BatchSize().PreferredMaxBytes)
		}

		//此时之前的交易队列里的交易就需要打包了
		// cut pending batch, if it has any messages
		if len(r.pendingBatch) > 0 {
			messageBatch, committerBatch := r.Cut()
			messageBatches = append(messageBatches, messageBatch)
			committerBatches = append(committerBatches, committerBatch)
		}
		//再单独打包当前的这个交易
		// create new batch with single message
		messageBatches = append(messageBatches, []*cb.Envelope{msg})
		committerBatches = append(committerBatches, []filter.Committer{committer})

		return
	}

	messageWillOverflowBatchSizeBytes := r.pendingBatchSizeBytes+messageSizeBytes > r.sharedConfigManager.BatchSize().PreferredMaxBytes
	//如果加上当前的消息容量超出最大限制则进行切割
	if messageWillOverflowBatchSizeBytes {
		logger.Debugf("The current message, with %v bytes, will overflow the pending batch of %v bytes.", messageSizeBytes, r.pendingBatchSizeBytes)
		logger.Debugf("Pending batch would overflow if current message is added, cutting batch now.")
		messageBatch, committerBatch := r.Cut()
		messageBatches = append(messageBatches, messageBatch)
		committerBatches = append(committerBatches, committerBatch)
	}
	//容量没有超限时加入消息队列 待切割的队列
	logger.Debugf("Enqueuing message into batch")
	r.pendingBatch = append(r.pendingBatch, msg)
	r.pendingBatchSizeBytes += messageSizeBytes
	r.pendingCommitters = append(r.pendingCommitters, committer)
	pending = true
	//消息队列长度超限进行切割
	if uint32(len(r.pendingBatch)) >= r.sharedConfigManager.BatchSize().MaxMessageCount {
		logger.Debugf("Batch size met, cutting batch")
		messageBatch, committerBatch := r.Cut()
		messageBatches = append(messageBatches, messageBatch)
		committerBatches = append(committerBatches, committerBatch)
		pending = false
	}

	return
}
//返回需要切割的交易队列pendingBatch和pendingcommitters并将其置空
// Cut returns the current batch and starts a new one
func (r *receiver) Cut() ([]*cb.Envelope, []filter.Committer) {
	batch := r.pendingBatch
	r.pendingBatch = nil
	committers := r.pendingCommitters
	r.pendingCommitters = nil
	r.pendingBatchSizeBytes = 0
	return batch, committers
}
//返回交易大小,消息体加上签名的大小
func messageSizeBytes(message *cb.Envelope) uint32 {
	return uint32(len(message.Payload) + len(message.Signature))
}


接着看一下solo共识机制的实现,整个solo共识(排序过程)就是不断的收集消息(交易)当交易积累到最大值或者超时就进行区块切割,区块切割借助上面的区块切割器实现,然后把区块添加到orderer本地的临时账本中去

  • solo/consensus
func (ch *chain) main() {
	var timer <-chan time.Time//定时器

	for {
		select {
		//获取一个交易,发送给区块切割器,进行检查切割
		case msg := <-ch.sendChan:
			batches, committers, ok, _ := ch.support.BlockCutter().Ordered(msg)
			//交易有效、且无新区块产生,计时器的值为nil,则重置计时器
			if ok && len(batches) == 0 && timer == nil {
				timer = time.After(ch.support.SharedConfig().BatchTimeout())
				continue
			}
			//有新区块产生,依次生成区块保存在orderer上的临时账本
			for i, batch := range batches {
				block := ch.support.CreateNextBlock(batch)
				ch.support.WriteBlock(block, committers[i], nil)
			}
			//有区块产生打包后重置计时器
			if len(batches) > 0 {
				timer = nil
			}
			//计时器触发,立即进行切割
		case <-timer:
			//clear the timer
			timer = nil

			batch, committers := ch.support.BlockCutter().Cut()
			//超时没有交易产生,报错
			if len(batch) == 0 {
				logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug")
				continue
			}
			//超时但有交易产生
			logger.Debugf("Batch timer expired, creating block")
			block := ch.support.CreateNextBlock(batch)
			ch.support.WriteBlock(block, committers, nil)
			//收到退出信号,退出
		case <-ch.exitChan:
			logger.Debugf("Exiting")
			return
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值