Fabric可插拔共识框架(Fabric-v0.6)

  1. 共识框架定义了每个共识插件都需要实现的接口:

    • consensus.Consenter: 允许共识插件从网络上接收消息的接口
    • consensus.Stack: 允许共识插件用来与栈交互的,这个接口可以分为几个部分:
      • consensus.Communicator: 用来发送(广播或单播)消息到其他的验证 peer
      • consensus.LedgerManager: 用来操作分类账
      • consensus.LegacyExecutor: 用来调用事务,可能修改备份分类账
      • Executor: Executor旨在最终取代旧的LegacyExecutor接口,必须与状态转移协调,以消除可能的races and ledger corruption
      • SecurityUtils: 处理消息签名的加密操作和验证签名
      • ReadOnlyLedger: 查询总账的本地备份,不会修改数据
      • StatePersitor: 存储共识状态,该状态应该再进程崩溃后继续存在
      • NetworkStack: 检索网络信息和发送消息

    consensus.LegacyExecutor接口是共识框架的核心部分。

    换句话说,consensus.LegacyExecutor接口允许一个(批量)交易启动和执行,根据需要回滚,预览和提交。每一个共识插件都需要满足以所有验证 peer 上全序的方式把批量(块)交易(通过consensus.LegacyExecutor.CommitTxBatch)被提交到总账中(参看下面的consensus.LegacyExecutor接口获得详细细节)。

Consenter
Stack

当前,共识框架由consensus, controllerhelper这三个包组成。

使用controllerhelper包的主要原因是防止Go语言的“循环引入”和当插件更新时的最小化代码变化

  • controller 包规范了验证 peer 所使用的共识插件
  • helper 是围绕公式插件的垫片,它是用来与剩下的栈交互的,如为其他 peer 维护消息。

这里有2个共识插件提供:pbftnoops

  • obcpbft包包含实现 PBFT [1] 和 Sieve 共识协议的共识插件。参看下一篇文章介绍。
  • noops 是一个为开发和测试提供的''假的''共识插件. 它处理所有共识消息但不提供共识功能,它也是一个好的学习如何开发一个共识插件的简单例子。

共识模块目录由下面几个包组成

consensus
├── controller
├── executor
├── helper
│ └── persist
├── noops
├── pbft
└── util
└── events

目录含义如下

  • controller 用来控制Fabric选择什么样的共识算法,默认是noops。
  • executor 封装了消息队列中对交易的处理。
  • helper 对外提供接口调用和数据持久化接口。
  • noops 提供了如何编写Fabric共识算法的Demo。
  • pbft PBFT算法的具体实现。
  • util 实现了一个peer节点到共识算法的一个消息通道,和一个消息队列。

3.4.1 Consenter 接口

定义:

type Consenter interface {
    RecvMsg(msg *pb.Message) error
    ExecutionConsumer
}

Consenter接口是插件对(外部的)客户端请求的入口,当处理共识时,共识消息在内部(如从共识模块)产生。

NewConsenter创建Consenter插件。RecvMsg以到达共识的顺序来处理进来的交易。

阅读上篇文章来理解 peer 是如何和这个接口来交互的。

3.4.2 Stack接口

定义:

type Stack interface {
 NetworkStack
 SecurityUtils
 Executor
 LegacyExecutor
 LedgerManager
 ReadOnlyLedger
 StatePersistor
}

Stack 允许插件和栈交互。它是由helper.Helper对象实现的。这个对象是:

  1. helper.NewConsensusHandler被调用时初始化的
  2. 当它们的插件构造了consensus.Consenter对象,那么它对插件的作者是可访问的

3.4.3 Inquirer接口

定义:

type Inquirer interface {
 GetNetworkInfo() (self *pb.PeerEndpoint, network []*pb.PeerEndpoint, err error)
 GetNetworkHandles() (self *pb.PeerID, network []*pb.PeerID, err error)
}

这个接口是consensus.Stack.Network接口的一部分。

它是用来获取网络中验证 peer 的(GetNetworkHandles)句柄,以及那些验证 peer 的明细(GetNetworkInfo):

注意peers由pb.PeerID对象确定。这是一个protobuf消息,当前定义为(注意这个定义很可能会被修改):

message PeerID {
    string name = 1;
}

3.4.4 Communicator接口

定义:

type Communicator interface {
 Broadcast(msg *pb.Message, peerType pb.PeerEndpoint_Type) error
 Unicast(msg *pb.Message, receiverHandle *pb.PeerID) error
}

这个接口是consensus.Stack.Network接口的一部分。

它是用来与网络上其它 peer 通信的(helper.Broadcast, helper.Unicast

3.4.5 SecurityUtils接口

定义:

type SecurityUtils interface {
        Sign(msg []byte) ([]byte, error)
        Verify(peerID *pb.PeerID, signature []byte, message []byte) error
}

这个接口是consensus.Stack接口的一部分。

它用来处理消息签名(Sign)的加密操作和验证签名(Verify)

3.4.6 LedgerManager接口

定义:

type LedgerManager interface {
 InvalidateState() // Invalidate informs the ledger that it is out of date and should reject queries
 ValidateState()   // Validate informs the ledger that it is back up to date and should resume replying to queries
}

这个接口是consensus.Stack接口的一部分。

它用于操纵分类帐的状态。在viewchange发生状态转移时,使用InvalidateState()说明当前状态不稳定,防止viewchange过程中查询出现不一致。

实现:

// InvalidateState is invoked to tell us that consensus realizes the ledger is out of sync
func (h *Helper) InvalidateState() 
 h.valid = false

// ValidateState is invoked to tell us that consensus has the ledger back in sync
func (h *Helper) ValidateState() 
 h.valid = true

3.4.7 LegacyExecutor 接口

定义:

type LegacyExecutor interface {
 BeginTxBatch(id interface{}) error
 ExecTxs(id interface{}, txs []*pb.Transaction) ([]byte, error)
 CommitTxBatch(id interface{}, metadata []byte) (*pb.Block, error)
 RollbackTxBatch(id interface{}) error
 PreviewCommitTxBatch(id interface{}, metadata []byte) ([]byte, error)
}

executor接口是LedgerStack接口最常使用的部分,且是共识网络工作的必要部分。接口允许交易启动,执行,根据需要回滚,预览和提交。这个接口由下面这些方法组成。

3.4.7.1 开始批量交易

BeginTxBatch(id interface{}) error

这个调用接受任意的,故意含糊的id,来使得共识插件可以保证与这个具体的批量相关的交易才会被执行。例如:在pbft实现中,这个id是被执行交易的编码过的哈希。

3.4.7.2 执行交易

ExecTXs(id interface{}, txs []*pb.Transaction) ([]byte, []error)

这个调用根据总账当前的状态接受一组交易,并返回带有对应着交易组的错误信息组的当前状态的哈希。注意一个交易所产生的错误不影响批量交易的安全提交。当遇到失败所采用的策略取决与共识插件的实现。这个接口调用多次是安全的。

3.4.7.3 回滚交易

RollbackTxBatch(id interface{}) error

这个调用中止了批量执行。这会废弃掉对当前状态的操作,并把总账状态回归到之前的状态。批量是从BeginBatchTx开始的,如果需要开始一个新的就需要在执行任意交易之前重新创建一个。

PreviewCommitTxBatch(id interface{}, metadata []byte) ([]byte, error)

这个调用是共识插件对非确定性交易执行的测试时最有用的方法。区块返回的哈希表部分会保证,当CommitTxBatch被立即调用时的区块是同一个。这个保证会被任意新的交易的执行所打破。

3.4.7.4 提交交易

CommitTxBatch(id interface{}, metadata []byte) (*pb.Block, error)

这个调用提交区块到区块链中。区块必须以全序提交到区块链中,CommitTxBatch结束批量交易,在执行或提交任意的交易之前必须先调用BeginTxBatch

3.4.8 ReadOnlyLedger 接口

定义:

type ReadOnlyLedger interface {
 GetBlock(id uint64) (block *pb.Block, err error)
 GetBlockchainSize() uint64
 GetBlockchainInfo() *pb.BlockchainInfo
 GetBlockchainInfoBlob() []byte
 GetBlockHeadMetadata() ([]byte, error)
}

ReadOnlyLedger 接口是为了查询总账的本地备份,而不会修改它。它是由下面这些函数组成的。

GetBlockchainSize() (uint64, error)

这个函数返回区块链总账的长度。一般来说,这个函数永远不会失败,在这种不太可能发生情况下,错误被传递给调用者,由它确定是否需要恢复。具有最大区块值的区块的值为GetBlockchainSize()-1

注意在区块链总账的本地副本是腐坏或不完整的情况下,这个调用会返回链中最大的区块值+1。这允许节点在旧的块是腐坏或丢失的情况下能继续操作当前状态/块。

GetBlock(id uint64) (block *pb.Block, err error)

这个调用返回区块链中块的数值id。一般来说这个调用是不会失败的,除非请求的区块超出当前区块链的长度,或者底层的区块链被腐坏了。GetBlock的失败可能可以通过状态转换机制来取回它。

3.4.9 Executor接口

type Executor interface {
 Start()                                                                     // Bring up the resources needed to use this interface
 Halt()                                                                      // Tear down the resources needed to use this interface
 Execute(tag interface{}, txs []*pb.Transaction)                             // Executes a set of transactions, this may be called in succession
 Commit(tag interface{}, metadata []byte)                                    // Commits whatever transactions have been executed
 Rollback(tag interface{})                                                   // Rolls back whatever transactions have been executed
 UpdateState(tag interface{}, target *pb.BlockchainInfo, peers []*pb.PeerID) // Attempts to synchronize state to a particular target, implicitly calls rollback if needed
}

这个接口是consensus.Stack接口的一部分。

Executor旨在最终取代旧的LegacyExecutor接口。LegacyExecutor直接调用的问题是它没有考虑到状态转移的影响。

Executor与状态转移协调,以消除可能的races和ledger损坏。

3.4.10 controller

3.4.10.1 controller.NewConsenter

签名:

func NewConsenter(stack consensus.Stack) consensus.Consenter 

这个函数读取为peer过程指定的core.yaml配置文件中的peer.validator.consensus的值。键peer.validator.consensus的有效值指定运行noops还是pbft共识插件。(注意,它最终被改变为noopscustom。在custom情况下,验证 peer 将会运行由consensus/config.yaml中定义的共识插件)

插件的作者需要编辑函数体,来保证路由到它们包中正确的构造函数。例如,对于pbft 我们指向pbft.GetPlugin构造器。

这个函数是当设置返回信息处理器的consenter域时,被helper.NewConsensusHandler调用的。输入参数cpi是由helper.NewHelper构造器输出的,并实现了consensus.CPI接口

3.4.11 helper

3.4.11.1 高层次概述

验证 peer 通过helper.NewConsesusHandler函数(一个处理器工厂),为每个连接的 peer 建立消息处理器(helper.ConsensusHandler)。每个进来的消息都会检查它的类型(helper.HandleMessage);如果这是为了共识必须到达的消息,它会传递到 peer 的共识对象(consensus.Consenter)。其它的信息会传递到栈中的下一个信息处理器。

3.4.11.2 helper.ConsensusHandler

定义:

type ConsensusHandler struct {
 peer.MessageHandler
 consenterChan chan *util.Message
 coordinator   peer.MessageHandlerCoordinator
}

共识中的上下文,我们只关注域coordinatorcoordinator就像名字隐含的那样,它被用来在 peer 的信息处理器之间做协调。例如,当 peer 希望Broadcast时,对象被访问。共识需要到达的共识者会接收到消息并处理它们。

注意,fabric/peer/peer.go定义了peer.MessageHandler (接口),和peer.MessageHandlerCoordinator(接口)类型。

3.4.11.3 helper.NewConsensusHandler

签名:

func NewConsensusHandler(coord peer.MessageHandlerCoordinator,
 stream peer.ChatStream, initiatedStream bool) (peer.MessageHandler, error)

创建一个helper.ConsensusHandler对象。为每个coordinator设置同样的消息处理器。同时把consenter设置为controller.NewConsenter(NewHelper(coord))

3.4.11.4 helper.Helper

定义:

type Helper struct {
 consenter    consensus.Consenter
 coordinator  peer.MessageHandlerCoordinator
 secOn        bool
 valid        bool // Whether we believe the state is up to date
 secHelper    crypto.Peer
 curBatch     []*pb.Transaction       // TODO, remove after issue 579
 curBatchErrs []*pb.TransactionResult // TODO, remove after issue 579
 persist.Helper

 executor consensus.Executor
}

包含验证peer的coordinator的引用。对象是否为peer实现了consensus.CPI接口。

3.4.11.5 helper.NewHelper

签名:

func NewHelper(mhc peer.MessageHandlerCoordinator) *Helper

返回coordinator被设置为输入参数mhchelper.ConsensusHandler消息处理器的coordinator域)的helper.Helper对象。这个对象实现了consensus.CPI接口,从而允许插件与栈进行交互。

3.4.11.6 helper.HandleMessage

回忆一下,helper.NewConsensusHandler返回的helper.ConsesusHandler对象实现了 peer.MessageHandler 接口:

type MessageHandler interface {
 RemoteLedger
 HandleMessage(msg *pb.Message) error
 SendMessage(msg *pb.Message) error
 To() (pb.PeerEndpoint, error)
 Stop() error
}

在共识的上下文中,我们只关心HandleMessage方法。签名:

func (handler *ConsensusHandler) HandleMessage(msg *pb.Message) error

这个函数检查进来的MessageType。有四种情况:

  1. 等于pb.Message_CONSENSUS:传递给处理器的consenter.RecvMsg函数。
  2. 等于pb.Message_CHAIN_TRANSACTION (如:一个外部部署的请求): 一个响应请求首先被发送给用户,然后把消息传递给consenter.RecvMsg函数
  3. 等于pb.Message_CHAIN_QUERY (如:查询): 传递给helper.doChainQuery方法来在本地执行
  4. 其它: 传递给栈中下一个处理器的HandleMessage方法
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容