前言

尽管Hyperledger Fabric的官方技术文档十分详细,但是其也有些许不足:中文版本稍旧且少量翻译有不够妥当之处;文档同时包含了理论知识和代码,行文顺序不够完美,不少知识细节杂乱地散布在各处;有很多浅显的文字对于初次阅读颇有帮助,但是也不便于查找那些值得反复体会的关键内容。本文基于官方技术文档,删减了代码相关内容和过于繁琐的文字描述,力求通过1W余字对Fabric的各个概念做一个完整的总结。


一、什么是区块链?

1.1. 分布式账本

区块链网络的核心是一个分布式账本,记录网络上发生的所有交易。它会被复制到许多网络参与者中,每个参与者都在协作维护这个账本。

1.2. 智能合约

为了支持以同样的方式更新信息并实现一整套账本功能(交易,查询等),区块链使用智能合约来提供对账本的受控访问。

1.3. 共识Consensus

保持账本在整个网络中同步的过程称为共识。该过程确保所有账本仅在交易被相应参与者批准时更新,并且当账本更新时,所有账本都以相同的顺序更新相同的交易。

共识机制(Mechanism)、共识算法(Algorithm)、共识协议(Protocol)三个概念经常互换使用。个人理解:
共识机制是指达成共识的整个流程,包含多个步骤;
共识算法代表某种具体方法,用于解决特定步骤中的问题,不同步骤使用的算法不同,同一步骤也可以用多种算法实现;
共识协议指代标准中规定的一系列规则,同一个算法在不同区块链技术中可以有各自的协议。


二、Hyperledger Fabric的特点

Hyperledger Fabric是一个开源的企业级许可分布式账本技术(DLT)平台,专为在企业环境中使用而设计。与其他流行的DLT或区块链平台相比,它提供了一些关键的差异化功能:

  • 不需要原生加密货币。
  • 是许可的。
  • 具有高度模块化的架构。
  • 独特的共识机制,并支持多种共识协议。
  • 支持通用编程语言编写智能合约。

2.1. 许可区块链

许可区块链在一组已知的、已识别的且经常经过审查的参与者中运行,这些参与者在产生一定程度信任的治理模型下运作。许可区块链提供了一种方法来保护具有共同目标但可能彼此不完全信任的一组实体之间的交互。

2.2. 模块化

Hyperledger Fabric被专门设计为模块化架构。总体来看,Fabric 由以下模块化的组件组成:

  • 可插拔的 排序服务(Ordering Service) 对交易顺序建立共识;
  • 可插拔的 成员服务提供者(Membership Service Provider) 负责将网络中的实体与数字身份相关联;
  • 可选的 P2P gossip服务 ,将区块发送到其他节点;
  • 智能合约隔离运行在容器环境中,不能直接访问账本状态;
  • 账本支持多种DBMS;
  • 可插拔的背书和验证策略,每个应用程序可以独立配置。

2.3. 共识机制

大多数现有的区块链平台遵循顺序执行架构:

  • 验证并将交易排序,然后将它们传播到所有的节点;
  • 每个节点按顺序执行交易。

Fabric引入了一种新的架构,称为执行-排序-验证。它将交易流分为三个步骤:

  • 执行一个交易并检查其正确性,从而给它背书;
  • 通过共识协议将交易排序;
  • 提交交易到账本前先根据特定应用程序的背书策略验证交易。

交易的排序被委托给模块化组件——排序服务——以达成共识,该组件在逻辑上与执行交易和维护帐本的节点解耦,因而可以根据特定部署或解决方案的信任假设来定制其实现。 这种模块化架构允许平台依赖完善的工具包进行CFT(崩溃容错)或BFT(拜占庭容错)的排序。

2.4. 链码

在Fabric中,智能合约和链码经常交替使用。严格来讲,智能合约定义了交易逻辑,它控制了在世界状态中包含的一个业务对象的生命周期。然后它会被打包进一个链码中,这个链码会被部署到一个区块链网络中。可以把智能合约理解为管理交易,链码则管理着智能合约应该如何被打包部署。

Fabric的共识机制消除了任何非确定性,因为在排序之前可以过滤掉不一致的结果,因而能使用标准编程语言。

此外,特定应用程序的背书策略可以指定需要哪些节点或多少节点来保证给定的链码正确执行。因此,每个交易只需要由满足交易的背书策略所必需的节点的子集来执行(背书)。这样可以并行执行,从而提高系统的整体性能和规模。

2.5. 隐私和保密

Hyperledger Fabric通过其通道架构和私有数据特性实现保密。

通道是Fabric网络中的成员组建的一个子网络,在子网络中的成员可以看到其所参与到的交易。因此,参与到通道的节点才有权访问智能合约(链码)和交易数据,以此保证了隐私性和保密性。

私有数据通过在通道中的成员间使用集合,实现了和通道相同的隐私能力并且不用创建和维护独立的通道。

说明:更多细节详见原文,有删减。初次看这一节如果有不甚明白之处可以先看完后续内容再回过头来理解。


三、Fabric网络的架构

3.1. 术语

组织(Organization):也被称为成员(Member),是Fabric网络中的参与方。组织加入网络的方式是将成员服务提供者(MSP)添加到网络。MSP定义了网络的其他组织如何验证签名是由该组织颁发的有效身份生成的。

通道(Channel):基于数据隔离和保密构建的一个区块链。特定通道的账本在该通道中的所有节点共享。账本可以视作通道在操作系统上的实体。

对等节点(Peer Node):也被简称为对等体(Peer)。由组织所有的一个网络实体,维护账本并运行链码容器来对账本做读写操作。

排序服务(Ordering Service):也被称为排序者(Orderer)。一个由若干排序节点组成的节点集合,将交易排序到一个区块中,然后将区块分发给对等节点进行验证和提交。

注:要区分排序节点和排序服务的区别,排序节点才是一个个的网络实体,排序节点的集合称为排序服务,是一个相对抽象的概念。

3.2. 示例网络

本节将同时提到“网络”和“通道”,在Hyperledger Fabric中,这两个术语实际上是同义词。 多数情况下,网络中的多个组织聚集在一起形成一个通道。

R1、R2、R0三个组织共同决定建立网络。这个网络有一个所有组织都同意的配置CC1,它列出了组织的定义并定义了每个组织将在通道上扮演的角色的策略。

在通道C1上,R1和R2分别拥有对等节点P1和P2,而R0拥有通道的排序服务O。

所有这些节点包含记录交易的通道账本L1的副本。其中,由排序服务保存的账本副本不包含状态数据库。

R1和R2可以通过它们拥有的应用程序A1和A2与通道进行交互。

每个组织都有一个证书颁发机构,该机构为其组织的节点、管理员和应用程序生成必要的证书。

在这里插入图片描述

3.3. 创建示例网络的流程

3.3.1. 创建通道

创建通道的第一步是定义并同意其配置:

在这里插入图片描述

通道配置CC1已经得到组织R1、R2和R0的同意,并且包含在称为“配置区块”的区块中,该区块通常由configtxgen工具从configtx.yaml文件创建。 虽然一个组织可以单方面创建一个通道,然后邀请其他组织加入它,但是现在我们假设三个组织希望从一开始就在通道上协作。

一旦配置区块存在,通道就可以说在逻辑上存在了,即使没有组件物理上连接到它。 该配置区块包含可以在通道上连接组件和进行交互的组织的记录,以及定义如何做出决策和达成特定结果的结构的策略。虽然对等节点和应用程序是网络中的关键角色,但它们在通道中的行为更多地取决于通道配置策略,而不是其他因素。

这些组织的定义及其管理员的身份必须由与每个组织关联的证书颁发机构(CA)创建。在这个示例中,组织R1、R2和R0已经分别由CA1、CA2和CA0创建了它们的证书和组织定义。

3.3.2. 添加节点到通道上

对等节点是网络的基本元素,因为它们是在通道上进行交易的组织连接到通道的物理点之一(另一个是应用程序),并且承载着账本。

一个对等节点可以属于组织认为合适的任意多个通道。

在这里插入图片描述

排序服务收集已背书的交易,并将它们排序到区块中,这些区块随后分发到通道中的每个对等节点。

排序服务对于特定的通道是惟一的。即使一个节点(或一组节点)服务于多个通道,每个通道的排序服务也被认为是排序服务的不同实例。

注意,上图中虽然只有一个排序的节点1连接到该通道,但在生产场景中,排序服务应该至少包含三个节点。

此外,将排序服务添加到通道后,就可以提议并提交对通道配置的更新。

3.3.3. 安装批准和提交链码

在Fabric中,定义组织如何与账本交互的业务逻辑包含在智能合约中。包含智能合约的结构(称为链码)安装在相关的对等节点上,由对应的组织批准,并在通道上提交。安装、批准和提交链代码的过程被称为链码的“生命周期”。 通过这种方式,可以认为链码物理上托管在对等节点上,但逻辑上托管在通道上。在这个的例子中,链码S5被安装在每个对等节点上。注意,排序服务上没有安装链码。

在这里插入图片描述

3.3.4. 在通道上使用应用程序

提交链码后,组织可以使用客户端应用程序调用链码上的智能合约。

在这里插入图片描述

3.4. 扩展示例网络

3.4.1. 创建新通道

创建新通道的第一步是创建其配置。这个通道不仅包括R2和R0,还包括一个新的组织R3,它的身份和证书是由CA3创建的。R1对该通道没有任何权限,也不能将组件加入其中,事实上,它甚至不知道新通道的存在。

在这里插入图片描述

创建了通道配置CC2后,就可以说通道在逻辑上是存在的,即使没有组件连接到它。之后将组件加入到C2中,如同2、3、4步中所示的。

在这里插入图片描述

注意,虽然C1和C2都有相同的排序组织R0,但有不同的排序节点为每个通道服务。这不是强制配置,因为即使相同的排序节点连接到多个通道,每个通道也有一个单独的排序服务实例。更常见的场景是多个排序组织一起为排序服务提供节点。

3.4.2. 为已有通道添加组织

更新通道配置会创建一个新的配置区块CC1.1,它将作为通道配置,直到再次更新。在这个例子中,我们将组织R3的对等节点添加到C1中。

在这里插入图片描述

3.5. 变更说明

Hyperledger Fabric V2.3相较于与以前的版本在网络架构上有很大的变化:

  • 为了简化通道创建过程并增强通道的隐私性和可扩展性,V2.3可以创建应用通道,而无需首先创建由排序服务管理的系统通道。此过程允许节点根据需要加入(或离开)任意数量的通道。

  • V2.3不再需要定义一组被称为“联盟”的组织,这些组织被允许在特定排序服务上创建通道。这是因为在新版本中,所有通道都是应用通道,因此可以创建通道的组织列表的概念不再适用。任何一组组织都可以聚集在一起,使用一组定义的排序节点(成为该通道的排序服务)创建一个通道。

说明:更多细节详见原文,有删减。注意中文版只更新到了V2.2版本,与这里的内容有较大差别。V2.2版本的两个重要概念就是系统通道和联盟,在新版本中都没有了。然而,除了新版本的官方技术文档里的内容外,网上大多数视频、博客以及各种书籍,都是基于V2.2版本而来的。如果是先看了最新版的技术文档里的代码了解测试网络,再参考第三方内容来手动搭建自己的网络,容易出现困惑。因此,也非常有必要了解旧版本的内容,这里就不展开了,详见中文版的技术文档。


四、身份管理

4.1. 身份

4.1.1. X.509证书

Fabric网络中的不同参与者,包括Peer节点、排序节点、管理员、用户等都具有一个封装在X.509数字证书中的数字身份,用于确定对资源的确切权限以及对参与者在区块链网络中拥有的信息的访问权限。

同时,Fabric采用公钥基础结构(Public Key Infrastructure,PKI)分层模型,确保身份可以被验证。

PKI由向各方发布数字证书的CA组成,它向参与者颁发符合X.509标准的数字证书,这些证书由CA进行签名。CA可以分为有两种形式:根CA和中间CA,它们形成一个信任链。

只要其他人信任CA,那么参与者就可以提交证书以证明其身份。证书上包含参与者的公钥,参与者可以用它进行签名。

4.1.2. 身份的生成

Fabric提供了一个内置的CA组件,允许在区块链网络中创建CA。此组件称为Fabric CA,是一个私有根CA提供者,能够管理具有X.509证书形式的Fabric参与者的数字身份。

4.2. 角色

4.2.1. 成员服务提供者

成员服务提供者(Membership Service Provider,MSP)是一种机制,它将CA分发的身份与组织中的角色建立绑定关系。

类比同样是身份认证相关的信用卡场景,CA就像众多信用卡提供商,它们分发许多不同类型的可验证身份。另一方面,MSP则用来确定接受哪些信用卡提供商。

在区块链网络中,MSP出现在两个位置:

  • 在参与者节点本地(本地MSP)
  • 在通道配置中(通道MSP)

本地MSP是为用户和节点(peer节点和排序节点)定义的。每个节点都必须定义一个本地MSP。它定义了在该级别上谁拥有管理权或参与权。

相比之下,通道MSP在通道层面上定义了管理权和参与权。

本地MSP表现为文件系统上的文件夹结构, 而通道MSP则在通道配置中被描述。

4.2.2. 组织单元

一个组织可以被划分为多个组织单元,视作组织内部的多个部门。例如,ORG1组织可能同时拥有ORG1.MANUFACTURING和ORG1.DISTRIBUTION组织单元。当CA颁发X.509证书时,证书中的OU字段可以指定对应的组织单元。使用组织单元的一个好处是这些值可以用于定义策略以限制访问,或者用于基于属性的访问控制的智能合约。

此外,OU字段可以指定一种特殊类型的组织单元,称为节点组织单元,用于授予证书一种Fabric网络中的角色。这些角色定义在MSP文件夹下的config.yaml文件中,包含一个组织单元列表,这些组织单元被认为是MSP所代表的组织的一部分:

在这里插入图片描述

在上述例子中, MSP有4种节点组织单元:client,peer,admin,orderer。因此,CA颁发的任何证书,字段OU=client的将被认定为客户端,字段OU=peer的将被认定为Peer,以此类推。

证书中的OU和角色如下图所示,字段OU=peer说明该证书在网络中的角色为Peer,字段OU=ORG1和字段OU=DISTRIBUTION说明该证书属于组织ORG1的DISTRIBUTION部门。

在这里插入图片描述


五、对等节点

在 Hyperledger Fabric 中,所有的 Peer 节点都是一样的,基于这个网络的配置,Peer 节点能够担当多个角色:

  • 提交节点(Committing Peer)。通道中的每个 Peer节点都是一个提交节点。他们会接收生成的区块,在这些区块被验证之后会以附加的方式提交到 Peer 节点的账本副本中。
  • 背书节点(Endorsing Peer)。每个安装了智能合约的 Peer节点都可以作为一个背书节点。然而,想要成为一个真正的背书节点,节点上的智能合约必须要被客户端应用使用,来生成一个被签名的交易响应。
    智能合约的背书策略明确了在交易被接受并且记录到提交节点的账本之前,需要哪些组织的 Peer 节点为交易签名。

这是 Peer 节点的两个主要类型,一个 Peer 节点还可以担任的两种其他的角色:

  • 主节点(Leader Peer)。当组织在通道中具有多个 Peer节点的时候,会有一个主节点,它负责将交易从排序服务分发到该组织中其他的提交节点。
    主节点可以通过静态或者动态的选举生产。对于静态选举,0个或者多个节点可以被配置为主节点。对于动态选举,一个节点会被选举成为主节点。另外,在动态选举主节点中,如果一个主节点出错了,那么剩下的节点将会重新选举一个主节点。
    一个组织可以有一个或者多个主节点连接到排序服务, 这有助于改进需要处理大量交易的大型网络的弹性以及可扩展性。
  • 锚节点(Anchor Peer)。如果一个 Peer 节点需要同另一个组织的 Peer节点通信的话,它可以使用对方组织通道配置中定义的锚节点。一个组织可以拥有0个或者多个锚节点,并且一个锚节点能够帮助不同组织的Peer节点进行跨组织间的通信。

需要注意的是,一个 Peer 节点可以同时是一个提交节点、背书节点、主节点和锚节点。在实际情况中只有锚节点是可选的,一般都会有一个主节点,至少一个背书节点和一个提交节点。


六、排序服务

许多区块链系统依靠概率共识算法最终保证账本一致性,但是网络中的不同参与者对于交易顺序仍然可能出现不同的观点(称为一个账本“分叉”)。

Hyperledger Fabric 的工作方式不同,它有一些称为排序节点的节点,共同使交易有序。 因为 Fabric 的设计依赖于确定性的共识算法,所以 Peer 节点所验证的区块都是最终的和正确的。账本不会产生分叉。

除了促进确定性之外,排序节点还将交易的背书与排序分离,这在性能和可伸缩性方面给 Fabric 提供了优势,消除了由同一个节点背书和排序时可能出现的瓶颈。

6.1. 实现方式

Fabric提供几种不同的实现方式在排序节点之间就交易的排序达成共识:

  • Raft:一种基于etcd库的Raft协议实现的崩溃容错(CFT)排序服务。
  • BFT(V2.5引入):一种基于SmartBFT库实现的拜占庭容错(BFT)排序服务,不仅可以承受崩溃故障,还可以承受恶意行为的节点子集。
  • Solo(已弃用):一种仅用于测试且只包含了单个排序节点的实现方式。可以用一个单节点的 Raft 实现方式获得同样的功能。
  • Kafka(已弃用):另一种基于Kafka 分布式流平台的CFT实现方式。

6.2. Raft

基于Raft协议的排序服务遵循“领导者-跟随者”模型,领导者是在一个通道的排序节点中动态选择的(这个节点的集合称为“共识者集合(consenter set)”),领导者将信息复制到跟随者。

Raft 是一种“崩溃容错”协议,系统可以承受节点的损失,包括领导者节点,前提是要剩余足够的排序节点(称为“法定人数(quorum)”)。换句话说,如果一个通道中有三个排序节点,它可以承受一个节点的丢失(剩下两个节点)。如果一个通道中有五个节点,则可以丢失两个节点(剩下三个节点)。

使用Raft,每个组织都可以有自己的排序节点参与排序服务, 从而形成一个更加分散的系统。

注:Raft的具体技术细节略。

6.3. BFT

基于SmartBFT协议的排序服务同样遵循“领导者-跟随者”模型,该协议指定一个单独的领导者,将交易批处理到一个块中,并将其发送到其它称为跟随者的节点。领导者定期向每个跟随者发送心跳,如果后者在一段时间内没有收到领导者的消息,它就会开始游说其它跟随者更换领导者。

BFT排序与Raft排序的不同之处如下:

  • 每一次当跟随者节点怀疑前一个领导者有故障时,BFT排序的领导者不是动态选择的,而是循环的。
  • Raft排序只需要一半以上的节点就能发挥作用,而BFT排序仅可以承受至多(不包括)三分之一节点的故障。如果三分之一或更多的节点崩溃或无法访问,则无法就任何区块达成一致。
  • BFT排序可以承受一些被破坏的节点。

如果多达(但不包括)三分之一的排序节点被恶意方控制,系统仍然可以接受新的交易,排序它们,最重要的是确保其余排序节点提交相同的区块。这与Raft形成了鲜明对比,Raft不适合部署在如此苛刻的对手模型中。

  • 当客户端向BFT排序服务提交交易时,它应该将交易发送到所有节点,而不是单个节点。

通过Gateway服务提交交易的应用程序不需要更改任何内容,因为Gateway服务知道它应该根据通道的配置提交给所有排序节点还是只提交给一个排序节点,并相应地采取行动。

虽然将交易发送到所有节点似乎是一个缺点,但也隐含了一个优点:在Raft中,将交易发送给排序者并不能保证它包含在区块中,也不能保证将它发送到所有节点,因为领导者可能会崩溃,然后交易可能会丢失。然而,在BFT中,将交易发送到所有节点不仅可以确保它已经到达领导者节点(因为即使领导者崩溃,交易仍将保留在跟随者节点的内存中,并最终被发送到领导者),还可以确保即使领导者节点是恶意的,并且忽略了客户端的交易,它最终也将别无选择,只能将交易包括在未来的某个区块中,或者面临被从客户端接收到交易并失去耐心等待其被包括在领导者发送的区块中的跟随者节点推翻。

注:SmartBFT的具体技术细节略。


七、交易流程

7.1. 查询的交易流程

查询账本的操作涉及到应用程序和 Peer 节点之间的一个简单的三步对话。

在这里插入图片描述

在这个例子中,应用程序 A 连接到了Peer 节点 P1 并且调用了链码 S1 来查询账本 L1。P1 调用了链码 S1 来生成提案响应,这个响应包含了查询结果。应用程序 A 接收到了提案的响应,对于查询来说,流程到这里就结束了。

Peer 节点可以马上将查询的结果返回给应用程序,因为满足这个查询的所有信息都保存在 Peer 节点本地的账本副本中。Peer节点从来不会为了应用程序的查询返回结果而去询问其他 Peer 节点,但是应用程序还是能够连接到多个 Peer 节点来执行一个查询。比如,为了协调在多个 Peer 节点间的一个结果,或者当怀疑数据不是最新的时候,需要从不同的 Peer 节点获得更新的结果。

7.2. 更新的交易流程

想要更新账本的应用程序会被引入到一个三阶段的流程,这确保了在一个区块链网络中所有的 Peer节点都彼此保持着一致的账本。

  • 在第一阶段,应用程序会跟背书节点的子集一起工作,这些节点都会为账本更新提供背书,但是不会将更新应用到他们的账本副本上。
  • 在第二阶段,这些分散的背书会被搜集到一起当做交易被打包进区块中。
  • 在第三阶段,这些区块会被分发回每个 Peer 节点,在这些Peer 节点上每笔交易在被应用到 Peer 节点的账本副本之前会被验证。

整个交易流程被称为共识,因为所有 Peer 节点在中对交易的排序及内容都达成了一致。

7.2.1. 提案和背书

第一阶段仅在应用程序和一系列的Peer节点之间的交互,并不涉及到排序服务。

开始时,应用程序会生成一笔交易的提案,它会把这个提案发送给一系列的被要求的节点来获得背书。其中的每一个背书节点接下来都会独立地使用交易提案来执行链码,以此来生成这个交易提案的响应,这并没有将这次更新应用到账本上。当应用程序接收到有效数量的被签过名的提案响应之后,交易流程中的第一个阶段就结束了。

在这里插入图片描述

在这个例子中,应用程序 A1 生成了交易 T1 的提案 P,应用程序会将交易提案发送给通道 C 上的 Peer 节点 P1 和 Peer 节点 P2。P1 使用提案 P 来执行链码 S1,这会生成对交易 T1 的响应 R1,并提供背书 E1。P2 使用提案 P 执行了链码 S1,这会生成对于交易 T1 的响应 R2,并提供背书 E2。应用程序 A1 对于交易 T1 接收到了两个背书响应,称为 E1 和 E2。

正常情况下,背书节点执行后的结果是一致的,只有背书节点对结果的签名不一样。

不同的 Peer 节点也能够返回不同的响应,因此对于同一个交易提案应用程序可能会接收到不同的交易响应。应用程序可以自由地放弃不一致的交易响应,这样就可以尽早有效地终结这个交易流程。如果应用程序尝试使用不一致的交易响应来更新账本,这会被拒绝。

7.2.2. 排序和出块

在此阶段,应用程序客户端把包含已背书交易提案响应的交易提交到排序服务。排序服务同时接收来自许多不同应用程序客户端的交易,将提交的交易按定义好的顺序安排成批次,并把它们打包成区块。

在这里插入图片描述

在这个例子中,应用程序 A1 向排序节点 O1 发送由 E1 和 E2 背书的交易 T1。同时,应用程序 A2 将 E1 背书的交易 T2 发送给排序节点 O1。O1 将来自应用程序 A1 的交易 T1 和来自应用程序 A2 的交易 T2 以及来自网络中其他应用程序的交易打包到区块 B2 中。

一个区块中交易的顺序不一定与排序服务接收的顺序相同,因为可能有多个排序节点几乎同时接收交易。

7.2.3. 验证和提交

最后一个阶段是区块的分发以及验证工作,这些区块最终会提交到账本中。具体来说,在每个 Peer 节点上,区块中的每笔交易都会被验证,以确保它在被提交到账本之前,已经被所有相关的组织一致地背书过了。失败的交易会被留下来方便审计,但是不会被提交到账本中。

在这里插入图片描述

在这个例子中,排序节点 O1 将区块 B2 分发给了 Peer 节点 P1 和 Peer 节点 P2。Peer P1 处理了区块 B2,产生了一个会被添加到 P1 的账本 L1 中的新区块。同时,peer P2 处理了区块 B2,产生了一个会被添加到 P2 的账本 L1 中的新区块。当这个过程结束之后, P1 和 P2会通知所连接的应用程序关于这笔交易已经被处理过的消息。

当接到区块的时候,Peer 节点会按照区块中的顺序处理每笔交易。对于每一笔交易,每个 Peer 节点都会确认这笔交易已经根据产生这笔交易的链码中定义的背书策略由要求的组织进行过背书了。

个人理解:Fabric的交易流程包括三个阶段,合称为共识。Raft作为一个CFT协议,只用于第二阶段,即交易顺序的共识。前文提到:“如果应用程序尝试使用不一致的交易响应来更新账本,这会被拒绝”,也就是说Fabric在交易内容的共识上是可以防止作恶的,这依赖于数字签名和公私钥的概念。举例来说,如果篡改了交易响应里的信息,那么提交节点使用改动后的信息生成的摘要和使用背书节点公钥解密背书节点签名来的摘要会不一致;如果顺手也改了签名,那么提交节点能通过数字证书知道这不是背书节点的签名;而背书节点用来签名的私钥不会被公开。另一方面,加上所谓的“背书策略”(见后文),如果背书节点返回的响应不一致,道理是类似的。

7.3. Fabric Gateway

Fabric Gateway是一个为Fabric网络提供单一入口的组件,能够简化应用的开发和网络的管理。使用Gateway后,应用就无需逐一连接每一个Fabric组件。从应用的角度看,Gateway代表了整个Fabric网络。

Fabric Gateway管理以下交易步骤:

  • 评估交易提案。这将在单个对等体上调用智能合约(链码)函数,并将结果返回给客户端。这通常用于查询账本的当前状态,而不进行任何账本更新。Gateway将优先选择与Gateway在相同组织中的对等体,并选择具有最高账本区块高度的对等体。如果Gateway的组织中没有可用的对等体,那么它将从另一个组织中选择一个对等体。
  • 背书交易提案。这将收集足够的背书响应以满足签名策略,并将准备好的交易信封返回给客户端进行签名。
  • 提交交易。这将向排序服务发送一个签名的交易信封,以提交到账本。
  • 等待提交状态事件。允许客户端等待交易提交到账本,并获得提交(验证/无效)状态代码。
  • 接收链码事件。允许客户端在交易提交到账本时响应智能合约函数发出的事件。

从Hyperledger Fabric v2.4开始,Fabric Gateway成为对等体中的一项服务,它为向Fabric网络提交交易提供了一个简化的、最小的API。先前对客户端SDK提出的要求(如从不同组织的对等体收集交易背书)被委托给在对等体中运行的Fabric Gateway服务, 以简化v2.4中的应用开发和交易提交。

与此同时,客户端应用程序应使用新的Fabric Gateway客户端API,这些API经过优化以与Fabric Gateway交互。

7.4. 在对等体中启用Gateway时的交易流程

7.4.1. 第一阶段

账本更新的第1阶段包括交易提案提交、执行和背书:

a)交易提案:客户端应用程序(A1)通过连接到P1上的Gateway服务来提交签名的交易提案。A1必须将背书所需组织的选择委托给网关服务或明确标识背书所需组织。

b)交易执行:Gateway服务选择P1或其组织中的另一个对等体来执行交易。所选对等体执行提案中指定的链码(S1),生成提案响应(包含读写集)。所选对等方为提案响应签名并将其返回给Gateway。

c)交易背书:Gateway为链码背书策略中要求的每个组织重复步骤(b)。Gateway服务收集签名的提案响应,并创建一个交易信封,然后返回给客户端进行签名。

7.4.2. 第二阶段

账本更新的第2阶段包括交易提交和排序:

a)交易提交:客户端将签名的交易信封发送到Gateway服务。Gateway将信封转发给排序服务,并向客户端返回成功消息。

b)交易排序:排序服务(O1)验证签名,将交易排序,并将其与其它排好序的交易打包成块。然后,排序服务将区块分发给通道中的所有对等体,以进行验证并提交到账本。

7.4.3. 第三阶段

账本更新的第3阶段包括交易验证、账本提交和提交事件:

a)交易验证:每个对等体检查交易信封上的客户端签名是否与原始交易提议上的签名匹配。每个对等体还检查所有读写集和状态响应是否相等(即来自所有对等体的背书匹配),以及背书是否满足背书策略。然后,每个对等体将每个交易标记为有效或无效,以用于交易的提交。

b)交易提交:每个对等体将有序的交易区块提交到通道账本(L1)。提交是对通道账本的不可改变的账本更新(写入)。通道的世界状态(本质上是所有有效交易的总和)仅更新有效交易的结果。

c)提交事件:提交交易的每个对等体向客户端发送一个提交状态事件以及账本更新的证据。

说明:Gateway是一个服务,它既可以放在APP中,也可以放在对等体中。7.2节是站在节点的层级的交易流程,不区分Gateway的部署细节,7.4节则是站在服务的层级的交易流程,本质上是一回事,只是视角不同。

7.5. 读写集

背书节点在模拟交易期间,会为交易准备一个读写集。读集包含了交易读取的键和键的版本的列表。写集包含了交易写入的键(可以与读取集中的键重叠)的新值。如果交易是删除一个键,该键就会被增加一个删除标识(在新值的位置)。

如果交易多次向同一个键写入数据,只有最后写入的数据会记录下来。同样,如果交易读取一个键的值,就会返回这个键的已提交状态的值,即使读取之前在同一个交易中更新了键值。

Fabric使用交易所在的区块高度来作为交易中所有修改的键的版本号。

下边是为模拟一个交易所准备的读写集示例。为了简化说明,使用了一个递增的数字来表示版本。

在这里插入图片描述

提交节点使用读写集中的读集来验证交易,使用写集来更新受影响的键的版本和值。

在验证阶段,如果读集中键的版本和世界状态中键的版本一致就认为该交易是 有效的。

如果交易通过了有效性验证,提交节点就会根据写集更新世界状态。在更新阶段,会根据写集更新世界状态中对应的键的值。然后,世界状态中键的版本会更新到最新的版本。


八、链码详解

8.1. 链码定义

尽管链码会被安装在组织的 Peer 节点上,但是它是在一个通道范围内被管理和维护的,只有等到链码被定义在通道上之后,该通道上的成员才能够执行其中的智能合约。

链码定义是一种包含了许多参数的结构, 包括链码名、版本以及背书策略,这些参数管理着链码的运行方式。各通道成员批准各自组织的一个链码定义,以表示其对该链码的参数表示同意。当足够数量(默认是大多数)的组织都已批准同一个链码定义,该定义可被提交至这些组织所在的通道。随后,通道成员可依据该链码定义中指明的背书策略来执行其中的智能合约。这个背书策略可同等使用于在相同链码中定义的所有智能合约。

8.2. 背书策略

在链码定义提供的信息中最重要的部分就是背书策略,适用于该链码中的所有智能合约。

背书策略描述了在交易被其他的组织接受并存储在他们的账本副本上之前,哪些组织必须要同意此交易,背书策略可以设置为通道中任何成员的组合,这取决于用例。例如,一个背书策略可能这样定义:在交易被认为有效之前,参与区块链网络的四个组织中有三个必须签署该交易。如果没有设置背书策略,则从通道配置中指定的默认背书策略继承。

如果一项背书策略指定了必须有不止一个组织来签署交易,那么只有当足够数量的组织都执行了智能合约,才能够生成有效交易。

背书策略是 Hyperledger Fabric 与以太坊(Ethereum)或比特币(Bitcoin)等其他区块链的区别所在。在这些区块链系统中,网络上的任何节点都可以生成有效的交易。而 Hyperledger Fabric 更真实地模拟了现实世界:交易必须由 Fabric 网络中受信任的组织验证。

8.3. 有效交易

每个交易都有一个标识符、一个交易提案和一个交易提案响应。交易提案是由发起交易的组织签名的一组输入参数,在智能合约执行时与程序逻辑结合起来使用以读写账本。交易提案响应(或简称交易响应)是由背书策略标识的组织签名的一个读写集,其中既含有已读取的状态,也含有还未书写的新状态(如果交易有效的话),代表世界状态的更改。

一项交易被分发给网络中的所有节点,各节点通过两个阶段对其进行验证。首先,根据背书策略检查交易,确保该交易已被足够的组织签署。其次,继续检查交易,以确保当该交易在受到背书节点签名时它的交易读集与世界状态的当前值匹配,并且中间过程中没有被更新。如果一个交易通过了这两个测试,它就被标记为有效。所有交易,不管是有效的还是无效的,都会被添加到区块链历史中,但是仅有效的交易才会更新世界状态。


九、账本详解

9.1. 账本

账本储存的其实并不是业务对象本身,而是与业务对象相关的事实信息。虽然与业务对象当前状态相关的事实可能会发生改变,但是与之相关的事实历史是不可变的,我们可以在事实历史上增加新的事实,但无法更改历史中已经存在的事实。

现实生活中的一个账本的示例:一个状态(银行余额)和一组促成该状态的有序交易(收入和支出)。Hyperledger Fabric 也致力于这两个方面,它旨在呈现一组账本状态的当前值,同时记录下促成了以上账本状态的交易的历史。

Hyperledger Fabric 的账本包括两个组件:“世界状态”和“区块链”。每个参与者都拥有他们所属的每个Hyperledger Fabric网络的账本副本。

世界状态是一个数据库,它存储了一组账本状态的当前值。通过世界状态,程序可以直接访问一个账本状态的当前值,不需要遍历整个交易日志来计算当前值。

区块链是交易日志,它记录了促成当前世界状态的所有改变。一旦把数据写入区块链,就无法修改。

9.2. 世界状态

世界状态将业务对象的属性的当前值保存为唯一的账本状态。账本状态记录了一组与这个业务对象有关的事实。账本状态都各有一个键和一个值,值可以是简单值也可以是复合值。

此外,每个状态都有一个版本号,初始为0。版本号是供 Hyperledger Fabric 内部使用的,并且每次状态更改时版本号会发生递增。每当更新状态时,都会检查该状态的版本,以确保当前状态与背书时的版本相匹配。这就确保了世界状态是按照预期进行更新的,没有发生并发更新。

下图是一个包含两个账本状态的世界状态。

在这里插入图片描述

9.3. 区块

如下图所示,区块由三个部分组成:区块头,区块数据和区块元数据。

在这里插入图片描述

  • 区块头

这个部分包含三个字段,这些字段是在创建一个区块时候被写入的。

  1. 区块编号:编号从0(初始区块)开始,每在区块链上增加一个新区块,编号的数字都会加1。
  2. 当前区块的哈希值:当前区块中包含的所有交易的哈希值。
  3. 前一个区块头的哈希值:区块链中前一个区块头的哈希值。
  • 区块数据

这个部分包含了一个有序的交易列表,在排序服务创建区块时被写入。

  • 区块元数据

这个部分包含了区块写入者的证书和签名,用于网络节点验证该区块。随后,区块提交者会为每一笔交易添加一个有效或无效的标记,以及直到并包括该区块的累积状态更新的哈希在区块元数据中,以检测状态分叉。与区块头和区块数据不同,这个部分不是区块哈希计算的输入。

9.4. 交易

区块中包含的交易如下图所示。

在这里插入图片描述

主要字段说明如下:

  • 头(H):它记录了关于交易的一些重要元数据,比如,相关链码的名字以及版本。
  • 签名(S):它包含了一个由客户端应用程序创建的加密签名。该字段是用来检查交易细节是否未经篡改,因为交易签名的生成需要用到应用程序的私钥。
  • 提案(P):它负责对应用程序供给智能合约的输入参数进行编码,随后该智能合约生成提案账本更新。在智能合约运行时,这个提案提供了一套输入参数,这些参数同当前的世界状态一起决定了新的账本世界状态。
  • 响应(R):它是以读写集(RW-set)的形式记录下世界状态之前和之后的值。交易响应是智能合约的输出,如果交易验证成功,那么该交易会被应用到账本上,从而更新世界状态。
  • 背书(E):它指的是一组签名交易响应,这些签名都来自背书策略规定的相关组织,并且这些组织的数量必须满足背书策略的要求。注意,在交易中只包含一个交易响应,但是会有多个背书。这是因为每个背书包含了它的组织特定的交易响应,这意味着不需要包含任何没有有效的背书的交易响应,因为它会被作为无效的交易被拒绝,并且不会更新世界状态。

9.5. 世界状态数据库的选项

世界状态数据库的选项包括 LevelDB 和 CouchDB 。

当账本状态结构是简单的键值对时,使用 LevelDB 非常合适。LevelDB 数据库与 peer 节点位于相同位置,它被嵌入与 peer 节点相同的操作系统进程中。LevelDB 是世界状态数据库的默认选项。

当账本状态结构为 JSON 文档时,使用 CouchDB非常合适,这是因为业务交易涉及的数据类型通常十分丰富,而 CouchDB 可支持对这些数据类型进行各种形式的查询和更新。在实现方面,CouchDB 是在单独的操作系统进程中运行的,但是节点和 CouchDB 实例之间仍然存在1:1的关系。


十、私有数据

如果一个通道上的一组组织需要对该通道上的其他组织保持数据私有,则可以选择创建一个新通道,其中只包含需要访问数据的组织。但是,创建单独的通道会产生额外的管理开销,并且不能在保持数据私有的同时让所有组织看到该交易。

Fabric 提供了创建私有数据集合的功能,它允许在通道上定义的组织子集能够背书、提交或查询私有数据,而无需创建单独的通道。

10.1. 私有数据集合

私有数据集合是两个元素的组合:

  • 实际的私有数据,通过Gossip协议点对点地发送给授权可以看到它的组织。私有数据保存在被授权的组织的节点的私有数据库上,它们可以被授权节点的链码访问。排序节点不能影响这里也不能看到私有数据。
  • 该数据的散列值,该散列值被背书、排序之后写入通道上每个节点的账本。散列值作为交易的证明用于状态验证,并可用于审计。

下图说明了被授权和未被授权拥有私有数据的节点的账本内容。

在这里插入图片描述

10.2. 使用私有数据的交易流程

  1. 客户端应用程序向目标对等节点提交调用链码函数(读取或写入私有数据)的提案请求,该对等节点将代表客户端管理交易的提交。 客户端应用程序可以指定哪些组织应该为提案请求背书,也可以将背书者选择逻辑委托给目标对等节点中的网关服务。在后一种情况下,网关将尝试选择一组背书节点,这些背书节点是受链码影响的授权组织集合的一部分。私有数据,或用于在链码中生成私有数据的数据,在提案的瞬态字段中发送。
  2. 背书节点模拟交易,并将私有数据存储在瞬态数据存储中。它们根据集合策略将私有数据通过Gossip协议分发给已授权的对等节点。
  3. 背书节点将提案响应发送回目标对等节点。 提案响应中包含经过背书的读写集,这其中包含了公共数据,还包含任何私有数据的键和值的散列。私有数据不会被发送回目标对等节点或客户端。
  4. 目标对等节点在将背书组装到交易中之前验证提案响应是否相同,然后将交易发送回客户端进行签名。目标对等节点向排序服务“广播”交易(包含带有私有数据散列的提案响应)。 带有私有数据散列的交易会像往常一样被包含在区块中。具有私有数据散列的区块被分发给所有对等节点。通过这种方式,通道上的所有对等节点都可以以一致的方式使用私有数据的哈希来验证交易,而不需要知道实际的私有数据。
  5. 在区块提交的时候,已授权的对等节点会根据集合策略来决定它们是否有权访问私有数据。如果有,它们会先检查本地的瞬态数据存储,以确定它们是否在链码背书的时候已经接收到了私有数据。如果没有,它们会尝试从其他已授权的对等节点那里拉取私有数据,然后对照公共区块上的 散列 来验证私有数据并提交交易和区块。当验证和提交结束后,私有数据会被移动到这些节点私有数据库和私有读写存储的副本中。随后瞬态数据存储中存储的这些私有数据会被删除。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐