HyperLedger Fabric 架构设计中的模式创新

发布时间:2018年12月09日 价值:20000.00 / 共识:34

引子

上一篇文章 分析了 PBFT 共识的安全边界,相信大部分同学都是通过 HyperLedger Fabric 知道的 PBFT。虽然 Fabric 作为联盟链解决方案的事实标准备受关注,但 Fabric 在架构设计中引入的创新却很少被提及,这正是本文接下来要和大家一起探讨的。

交易流程简介

还是先从大家熟悉的交易流程开始,Fabric 处理交易可以分为 9 个步骤:
1、客户端提交交易并发送给背书节点(endorsing peers);
2、背书节点模拟执行交易并生成交易相关的读写集,这里背书节点执行了交易的合约代码(也是合约唯一执行的地方)但并没有更新账本;
3、经过背书的交易(包含读写集)会再次返回给客户端;
4、客户端提交背书交易和读写集到排序节点(ordering peers);
5、排序节点对交易进行分 channel 的排序并打包进区块;
6、排序节点广播区块给所有的记账节点(committing peers);
7、记账节点验证交易;
8、记账节点更新账本;
9、记账节点给客户端返回交易的执行结果。

新的交易模型

看到上面的交易流程大家有没有问题自己一个问题:为啥要引入 Endorser (背书节点),在其他区块链系统设计里面并没有看到这个角色的存在啊。没错,这就是 Fabric 在交易模型方面的创新:从 order-executeexecute-order-validate

想想 Bitcoin、Ethereum、包括基于 BFT 的其他区块链系统,是不是都是先对交易排序并达成全网一致,然后再各自在本地更新账本的,这就是 order-execute 模型。但这种模型有如下的限制:
1、只能顺序执行:区块链网络的吞吐等同于单节点吞吐;
2、只能执行确定性代码:Ethereum 智能合约只能用 Solidity 语言编写,而不能用 Go, Java 等 x86 语言;
3、对交易内容没有隐私保护:部署在 Ethereum 的智能合约都是全节点明文可见的。

其实 Fabric v0.6 就是基于 order-execute 模型的,但系统的 TPS 和 交易 latency 都不能满足要求,所以从 v1 开始就设计了新的 execute-order-validate 模型。

execute-order-validate

核心思想就是:把交易的执行和账本的更新分开处理;先执行,再排序,最后验证和更新账本。对应的节点角色也在逻辑上分为:Endorser, Orderer, Committer。

Endorser 负责执行 chaincode(Fabric 中的合约代码)并返回给客户端,客户端和 Endorser 的交互通过一套 背书策略 来控制。

首先,这样就不需要把交易发给全网节点执行从而提高执行效率;
其次,通过背书策略来控制哪些节点可以看到交易的内容,从而做到交易的隐私保护(账本的隐私保护是通过 channel 机制)。
再次,chaincode 可以用 Go, Java 等 x86 语言编写。

这样就通过背书节点的引入解决了上述 order-execute 模型的 3 个问题,当然也需要 Orderer 和 Committer 的合作。

Orderer 负责接收客户端发过来的经过背书的交易;对所有交易进行排序并打包进区块,同时为每个区块分配一个全网唯一的连续递增的编号;然后广播给 Committer 节点。Fabric 中号称对做 ordering 的共识算法可以做到可插拔就是因为 Orderer 节点既不关心上层应用的状态,也不关心底层交易的执行和验证。

Committer 负责验证交易和更新账本。这里需要说明的是:即使一个交易没有通过验证,也会跟随 Block 被写入账本,但交易状态会被标记为 invalid。

备注:Endorser, Orderer, Committer 三种节点在逻辑上区分是为了让角色分工更明确,在物理部署上可以是一体的。

核心问题探讨

一致性如何保证

对于新的交易模型,相信大家能想到的第一个问题就是:交易先执行再排序,一致性如何保证。

这里需要了解交易背书策略中的读写集以及交易验证阶段的读写冲突检查

账本的状态存储在一个带 version 的 key-value store 中,当背书节点在模拟执行交易时,从 key-value store 中读取数据时对应的 version 就形成了 readset,而模拟执行交易后状态更新值就形成了 writeset

读写集会随着背书交易一起发给排序节点进而广播给记账节点。记账节点在交易验证的第二步就会检查交易 readset 中的 version 和当前账本状态的 version 是否匹配,如果匹配就可以更新 writeset 到账本,否则交易会被置为无效。

看到这里对于写过多线程程序的同学是否联想到了什么。没错,就是 CAS (compare and swap) ,还有 cpu 的 cmpxchg 指令;原理都是相同的。

正确的交易是否一定能上链

答案其实是否定的。仔细分析背书策略会发现,客户端需要收集到和背书策略完全相符的结果才会继续发送交易给排序节点,但背书节点之间的也会由于下面的情况影响而打不成一致:
1)chaincode (x86语言)执行结果引入的不一致;
2)账本状态(背书节点都是读本地账本的)不一致引入的读写集不一致;
3)背书策略配置不合理引入的不一致。

排序节点是拜占庭容错的吗

答案是否定的。排序节点目前支持的是 SOLO(test) 和 Kafka(product)两种共识算法,而 Kafka 只能做到 crash fault-tolerant 。当然能够拜占庭容错的共识算法 BFT-SMaRt 已经在试验阶段。

Fabric 和 PBFT 核心流程对比

把下面两张图放在一起看,其实分阶段做的事情都是类似的,只是在阶段的顺序上略有差别。

首先明确 PBFT 是 order-execute 模型,而 Fabric 是 execute-order-validate 模型,在这个前提下去比较就明朗了。

1、Fabric 中的 invocation 对应 PBFT 中的 client 发送 request;
2、Fabric 中的 Chaincode execution 对应 PBFT 中的 commit 阶段节点达到 committed-local 状态之后本地执行request 的前半部分;
3、Fabric 中的 Endorsement collection 对应 PBFT 中的 reply 阶段;
4、Fabric 中的 Ordering 对应 PBFT 中的 pre-prepare 阶段加上 prepare 前半段;
5、Fabric 中的 Broadcast/Delivery 对应 PBFT 中的 prepare 后半段;
6、Fabric 中的 Validation 对应 PBFT 中的 commit 阶段中验证 prepared message 部分;
7、Fabric 中的 Commit 对应 PBFT 中的 commit 阶段写本地账本的后半部分。


  • 分享 收藏
0 条评论
  • 这篇文章暂无评论,赶紧评论一下吧~