Fabric通道
Hyperledger Fabric架构使用具有保证的发布-订阅模式消息传递通道(如Kafka中的主题分区)将共识服务与交易日志(账本)分离。 共识服务由称为Orderers的网络节点提供,并且账本由Peer节点管理。
每个Peer节点连接到共识服务的一个或多个通道,就像发布-订阅通信系统中的客户端一样。 在通道上广播的交易按共识的顺序排列(例如PBFT、kafka),订阅通道的Peer节点接收到加密的区块。 每个peer节点验证区块并将其提交到账本,然后向应用程序提供其他使用账本的服务。
一、定义和术语
●Orderers:提供共识服务的网络节点,例如,使用Kafka或PBFT。
●Peers:维护账本的网络节点,通常在Hyperledger Fabric架构中存在各种角色,如endorser和committer。
●通道:Order 服务提供Peer节点供订阅的主题(如发布-订阅消息队列),每个主题是一个通道。 peer可以在订阅多个通道,并且只能访问订阅通道上的交易。
●账本:账本保存Orders提交经节点确认的交易记录。
●成员:访问和使用账本的网络节点。
●链:基本上,一个链由1个通道+ 1个账本+ N个成员组成。非链的成员无法访问该链上的交易。链的成员可以由应用程序动态指定。
二、数据隔离和保密
在共识服务上支持多通道消息传递,使得Peer节点可以基于应用访问控制策略来订阅任意数量的通道; 也就是说,应用程序指定Peer节点的子集中架设通道。 这些peer组成提交到该通道交易的相关者集合,而且只有这些peer可以接收包含相关交易的区块,与其他交易完全隔离。
此外,peers的子集将这些私有块提交到不同的账本上,允许它们保护这些私有交易,与其他peers子集的账本隔离开来。应用程序根据业务逻辑决定将交易发送到1个或多个通道。 这不是内置的限制,区块链网络不知道并假设不同通道上的交易之间没有关系。
例如,如上图所示,peer 1,2和N订阅红色通道,并共同维护红色账本; peer 1和N订阅蓝色通道并维护蓝色账本; 类似地,peer 2和peer N在黑色通道上并维护黑色账本。
在这个例子中,peer N在订阅了所有通道,我们看到每个通道都有一个相关的账本。 一般来说,我们称不涉及所有peer的账本为子账本,另一种是系统账本,即全账本。
通道和账本的组合是一个虚拟链,因此一个区块链网络可以具有1个共识服务的多个链。 系统通道和全账本构成系统链。 每个区块链网络只有1个系统链。如果交易是公开的,区块链网络可能永远不需要多个链; 所有的交易对所有Peers成员都可见。 然而,在成员间进行私密交易(例如双边合同),单独的链是隔离数据、提供保密的方式。
注意:共识服务接收所有链的所有交易,因此保密性仅与peer而不是Orderers相关。 当共识服务由被许可网络中的可信方和监管机构组成时,这样是合理的,也就是说,这些交易作为业务需求仅对他们可见。 另一方面,如果应用程序不希望Orderers知道交易的内容,它必须利用其他技术来隐藏敏感数据,例如哈希散列或加密。共识服务作为一个信任方存在,如果是由拜占庭容错(BFT)共识协议实现(例如PBFT),可以防止不可信的Orderers破坏账本的一致性和阻碍系统可用性。然而,现在还没有一种协议可以在有不可信的Orderers存在的情况下,提供多通道设计的同时提供数据保密性。
三、实现
当前的Fabric共识服务实现中,通道是隐含的; 当peer连接至区块链网络时,不会指定一个通道。 所以为了支持多渠道,Orderer和peer都需要改变。 Orderer必须提供多通道订阅,peer需要知道可以订阅哪些频道。 以下部分描述了技术细节。
1) 引导
共识服务由1个或多个Orderers组成。 每个Orderer配置有匹配的创世区块,其由引导CLI命令生成,其提供了一些必要的数据,包括一系列可信根节点的列表,Order证书和IP地址的列表,一组特定的共识算法配置以及访问控制策略(谁可以创建信道)。
要启动并连接到共识服务,peer至少需要以下配置:
1.准入网络的注册证书。 证书可以来自任意CA,只要CA是peer将连接到的共识服务的可信任根的一部分
2.来自共识服务管理CLI生成的Orderer证书和IP地址的列表
3.可信任根节点列表
4.peer可以订阅的通道可选列表。 除非明确配置,否则peer在启动时不订阅任何通道
注意,#2和#3来自引导的创世区块,我们可以从引导CLI命令获得。
通过CLI或使用SDK API的应用程序,peer可以订阅已经存在的通道。 order通过在通道创建或重新配置期间收到的消息决定谁可以加入通道。
2) 创建通道
可以通过发送通道配置事务和参与成员证书列表到共识服务来创建通道。
应用程序决定哪些成员创建通道。成员证书在Peers之间是公开的,且能被查询到。通道中成员间的共识的达成,为了避免在Orderer处理过程中不必要的麻烦,不需要使用Chaincode类型的交易背书,直接使用通道配置事务就行了。
我们将介绍一个新的系统chaincode,叫做配置系统chaincode(CSCC),主要负责处理所有的配置相关的事务。CSCC提供方法查询众多的配置数据,包括通道配置。
例如,假设peer A和B属于2个不同成员Alice和Bob。 请注意,Alice和Bob可能在网络上有多个Peer,并且他们的任何Peer都可以加入通道。 以下是一个典型的序列:
1.应用程序/ SDK获得A和B的背书用于创建通道“foo”的配置交易。
2.应用程序/ SDK调用Broadcast RPC,将背书过的配置交易传递给order服务。
3.应用程序/ SDK然后调用在通道foo上deliver RPC。此RPC将返回一个错误,直到order服务成功创建通道。
4.当通道最终被创建后,Deliver RPC将返回通道的信息到应用程序/ SDK。在这一时点,通道foo应当仅具有包含相关订阅者的创世区块,并且与该配置交易一起被(或最近的重新配置交易)引导。
5.应用程序/ SDK在A和B上调用JoinChannel API,将通道foo的创世区块传递给A和B,添加CSCC到通道上。
6.A和B上的CSCC检查创世区块,包括检查区块中的配置交易的背书。如果一切正确,他们调用在通道上的Deliver RPC来开始接收块。
如果通道已经存在,则参与者列表将被替换。 Orderers自动替换订阅者并且将该交易与该通道上的其他交易一起发送给新成员,新成员将会同步完整的块。
3) 关闭通道
应用程序可以通过发送类似于创建通道的配置交易来关闭其创建的通道。 它需要根据应用程序设置的策略从通道参与方得到背书。 peer不会自动销毁相关的账本,但是裁剪进程会在适当的时候处理。
应用程序可以继续从已关闭的账本中读取数据,只要该账本尚未被删除,但由于通道已被销毁,因此不能执行交易了。
4) 查询通道
通道只能被该通道的成员查询。也就是说,交易发起方的签名能够被存储在账本配置区块中的CA证书验证通过。这是通过发起一个查询交易到CSCC,同时附上链的ID,返回的结果是一个配置区块,里面包含了成员证书和一些其他的配置信息。
5) 链上的交易
一个交易必须包含目标的链ID(链ID =通道ID =账本ID)。 共识服务将把交易放置在由链ID标识的指定通道上,并且在该通道内被排序,而与其它通道上的交易无关。 最终在该通道上产生一个包含交易的区块并发送到订阅了该通道的那些peer。
注意,每个链都是独立和并行的,因此一个peer可以同时接收和处理不同链上的区块。
chaincode事务只能操作指定链中的状态变量(类似于对对象的实例变量操作的函数)。
6) 链上的Chaincode
chaincode被部署到链上并只能在这个链上被调用。 有些应用场景,我们要从另一个链调用数据,修改自己的链或其他链的状态值。当我们需要一个chaincode只部署一次但是可以在任何链上可以被调用的时候,跨链调用就变得特别有用。
例如,假设系统账本具有记录输入事件的Weather chaincode,ABC是新创建的账本。 我们可能想从ABC调用Weather,从系统链查询返回值,因此发送事务[ABC,Weather.worldMap()]将返回系统链上的当前天气图。 但我们可能还希望Weather chaincode操作局部变量,因此发送[ABC,Weather.temperature()]将返回ABC链上的当前温度,而不是系统链上的当前温度。该示例的需求是特殊的,但它说明我们需要从一个交易或从另一个chaincode调用不同链之间的chaincode的能力,这对于系统链上的那些chaincode特别有用,我们只需要部署一次并可以从任意私有链调用它们。
我们需要一个可编程模型来表达被调用chaincode的操作。 一个可选的方案是包括一个上下文,它封装了chaincode将运行的账本(数据对象)。 然而,chaincode可能在每个部署的账本上具有一些初始化参数(init函数),所以我们需要能够在每个新的账本上初始化chaincode,我们希望chaincode可以在本地执行。
在这一点上,我们应该设定一些约定以使得编程模型清晰:
1.从交易调用chaincode总是在交易被发送的链上进行操作
2.只有系统链上的chaincode可以被私有链上的其他chaincode调用并且是只读的
四、API
如上面实现部分所述,我们将在peer上增加一个新的gRPC API和一个新的顶层交易类型。API允许App / SDK通知peer已成功加入的通道。 加入通道API的输入是由新创建的通道上的共识服务返回的创世区块,peer使用此区块设置与通道关联的账本。
新的交易类型称为配置交易,这种类型的交易可以由Orderer和peer处理。 创建或重新配置通道的交易都属于配置交易,其中背书请求是让peer批准和不批准它们创建或重新配置通道。 peer可以通过提案请求返回接受或拒绝。 为了保持灵活性,我们将提供一个系统chaincode来处理通道创建的背书请求,它将自动响应签名请求。 chaincode还提供查询此通道上参与成员列表的功能。
配置交易具有ConfigurationEnvelope类型的payload,其嵌入序列号,链ID和签名配置条目的列表。配置交易包含整个链的配置,并且单个配置总是独立存在。这种简单的编码将允许在将来很容易进行裁剪和简化配置交易的开销。新配置交易必须包含所有先前的配置条目,并且所有新/修改的配置条目必须用其包含配置包络的序列号和链ID标记。每个配置条目都具有枚举类型,唯一(按类型划分)ID以及由名称引用的修改策略。Order服务将根据现有配置策略验证配置交易,如果不满足全部修改策略,则拒绝它。
SDK可以向API提供进一步的抽象。 例如,它可以提供1个API,创建通道(成员证书列表),它将执行在创建通道部分中讨论的所有6个步骤。 最后,SDK将调用应用程序上的回调,返回创建通道的状态。
五、Fabric v1.x 通道(Channel)机制运行原理
5.1 Channel实现原理
1.1 System Channel
channel是Orderer的一个模块,Fabric的启动会创建一个内建的system channel,是系统的一个默认链,用于管理其他的user channel。
orderer启动的时候必须要有该channel的genesis block,genesis block里规定了所有关于system channel的配置,因此所有的orderer都必须拿到相同的genesis block才能启动。
1.2 创建新channel
创建一个新的user channel时,其实是向system channel发送一个“New Channel” transaction(包括要创建的channel的名字,channel的配置信息,包括哪些组织,出块属性等),这个transaction会被提交到sysem channel,然后orderer中会创建一个新的user channel,刚刚发送的transction里的信息作为user channel的genesis block,这样user channel就创建完成了。
1.3 普通交易(Normal Transaction)
join channel的操作就是把genesis block拿下来,送给peer,让peer知道去哪儿找到orderer节点(genesis block里含有orderer的address信息),peer也会知道链里包含哪些组织(peer一定是属于这个channel允许的组织),这时候peer就可以参与transaction了,下图中的“Normal”就是用户的普通交易。
1.4 配置交易(Config Transaction)
当需要修改channel的属性(新增一个组织、修改batchsize等配置),其实是向链发送一个“Config”transaction,该transaction无需遵循出块规则,会立即生成一个块,因为配置的信息需要尽快生效。修改一个链的属性的时候,本身也是需要共识的,共识产生块后,提交到orderer的本身的账本后,进行真正的属性更新。
1.5 总结
使用上述同样的方法,system channel可以创建一个新的user channel B,system channel只负责创建user channel,创建完之后,user channel完全独立,与system channel也没有任何关系了。
system channel与user channel在实现上没有区别,同样样可以提交一个“Config”transaction,共识产生块后更新配置属性。
Fabric的channel之间是相互隔离的,仅有的联系是user channel需要通过system channel进行创建,但是创建完之后,这些channel相互之间没有任何影响了。以此类推,新的user channel B同样可以通过提交“Config” transaction修改配置属性,或者参与业务交易;通过向system channel提交“New Channel”交易创建user channel C。
5.2 Channel数据隔离
创建channel的交易在system channel共识完成后,channel A就会被创建,里面包含OrgA和OrgB两个组织,如下图所示:
使用上述方法,可以创建出channel B和channel C,组织OrgA同时在channelA和channelB中,组织OrgB同时在channelA、channelB、channelC中,组织OrgC在channelB、channelC中,如下图所示:
在这种情况下,channelB中包含全部3个组织,3个组织都可以在channelB中互相交易,而组织OrgC无法获取到channelA中的交易,组织OrgA无法获取到channelC中的交易,这样就实现了数据隔离和隐私。
5.3 Channel上部署chaincode
channel建立完成后,就可以在channel上部署chaincode,如下图所示,在channelA上部署了“mycc_v1.0”的chaincode,部署该chaincode时指定了背书策略为“AND(OrgA, OrgB)”,组织OrgA和OrgB都必须给该chaincode的交易背书才会被写入账本,这也意味着组织OrgA和OrgB都必须部署“mycc_v1.0”chaincode,如下图所示:
利用规定的语法可以组成一些比较复杂的背书策略,如下图所示,在channelB部署“mycc_v1.0”chaincode的背书策略为“OR(OrgA, AND(OrgB, OrgC))”,表示组织OrgA单独背书即可,或者组织OrgB和OrgC一起背书,channelC的背书策略为“OutOf(1, OrgB, OrgC)”,表示要求除组织B和组织C外的另一个组织进行背书。
另外,任何channel上都可以部署多个chaincode,并且这些chaincode可以相互调用,在同一个channel上的智能合约互相调用可以修改数据,不同channel上的智能合约也可以互相调用(取决于ACL),但是只能读取数据,不能修改数据,不同的chaincode组织起来可以完成一个复杂的业务逻辑。如下图所示,channelB和channelC上还部署了“evmcc_v0.1*”chaincode,用于运行以太坊的智能合约,channelA上还部署了“hercc_v2.0”chaincode。