Go语言Hyperledger区块链开发实战
上QQ阅读APP看书,第一时间看更新

2.1.3 排序服务与交易的流程

排序服务与交易的流程

在Fabric网络中,交易是各成员间资产转移和管理的记录,是区块链中保存的数据。本小节介绍Fabric网络的交易流程。在交易的流程中,排序服务发挥着重要的作用,它决定了交易被记录到区块中的顺序。

1.什么是排序

在比特币、以太坊等公有链中,任何节点都可以不经授权就参与共识的过程,也就是所谓的挖矿。在形成共识的过程中,交易被排序并打包到区块中。正因如此,这些公有链都依赖具有概率性的共识算法(不确定由谁记账,也不确定记账交易的顺序)。最终共识算法保障了账本在很大概率上一致。这可能会造成账本的分叉,因为从网络中不同参与者的视角来看,交易的顺序各不相同。

Fabric区块链的工作原理与前文提到的公有链的工作原理不同,它设置了一种特殊的节点,即排序节点,专门负责对交易进行排序。多个排序节点构成排序服务。因为Fabric区块链采用确定的共识算法,到达记账节点的区块顺序是一致的,而且交易内容经过多次验证,所以不可能存在互相冲突的交易,比如一笔钱被使用两次。这样就保证了区块链不可能出现分叉。

为了提升效率,Fabric区块链将背书操作从排序过程中分离出来,由Peer节点完成。这就优化了自身性能、提高了可扩展性。

2.排序节点和通道配置

除了负责排序,排序节点还负责维护一个可以创建通道的组织的列表,这些组织构成联盟,联盟信息保存在系统通道的配置中。关于系统通道将在第5章具体介绍。默认情况下,系统通道的配置数据只能由其所在的排序节点的管理员编辑。

排序节点还可以增强对通道的基本访问控制,例如约束哪些组织的哪些用户可以读/写通道上的数据。当创建联盟或通道时,管理员设置的策略中定义了谁有权限更新通道的配置数据。

更新通道配置的交易由排序节点处理,以确认交易的发起者是否拥有指定的管理员权限。如果有,则排序节点会根据当前的配置数据验证配置更新请求,生成一个新的配置交易,并将配置更新请求打包到一个区块中,然后将该区块发送至通道中的所有Peer节点。Peer 节点对经过排序节点授权的交易数据(其中包含对通道配置的修改)进行验证,确认对配置的修改符合通道定义的策略。通过验证后对通道配置的修改将被写入区块链中。

3.身份标识

与Fabric区块链的所有组件(包括Peer节点、应用程序、管理员和排序节点)进行交互都需要组件所属组织的身份标识,身份标识由数字证书和组织的MSP所定义。

与Peer节点一样,排序节点也属于一个组织。同样,每个独立的CA也应该只为一个组织服务。可以选择使用一个单独的CA,也可以部署一个根CA,然后设置中间CA,这取决于每个组织的选择。

4.基本交易流程

Fabric网络可以为应用程序提供账本和智能合约(链码)服务。智能合约用于生成交易,交易会被顺序地派发至网络中的每个节点,然后会被永久地记录在节点上的账本副本中。应用程序的用户可以是客户端应用的用户,也可以是区块链网络的管理员。

账本存储在 Peer 节点上,可以在应用程序中通过智能合约访问账本数据,如图2-5所示。

图2-5 应用程序访问账本数据的流程

向账本中写入数据实际上是一个交易。交易的基本流程分为图2-6所示的3个阶段。这3个阶段的目的是确保区块链网络中所有Peer节点上的账本保持一致。

图2-6 Fabric网络中交易的基本流程

图2-7描述了更详细的交易流程。

图2-7 更详细的交易流程

(1)提案/背书

在提案/背书阶段,客户端应用会发送一个交易提案到一组Peer节点,Peer节点会调用智能合约发起一个更新账本的申请,然后对结果进行背书。此时,背书Peer节点不会将更新提案应用到自己的账本副本中,而是会将背书的结果返回至客户端应用。背书结果中包含背书节点的签名和对应的带有版本号的交易数据读/写集。

客户端应用如果收集了策略中规定的足够数量的背书,则将交易发送至排序服务。

(2)排序/打包

在交易的第2阶段,排序服务会通过共识协议对交易进行排序。在Fabric网络中,共识协议是可插拔的,即可以从多个配置好的共识协议中选择使用其中的一个,并且可以动态增加共识协议。

排序服务从不同的客户端接收交易,并按通道对它们进行排序。排序服务不需要查看交易的内容,也不会执行交易的操作。排序服务可以为每个通道的交易创建区块,将交易打包到区块,并使用自己的数字证书为区块签名,然后利用Gossip协议将签名后的区块向所有Peer节点广播。

(3)验证/记账

根据应用程序在提交交易到账本之前指定的背书策略对交易进行验证。应用程序指定的背书策略中包括需要哪个Peer节点或需要多少个Peer节点对给定智能合约的正确执行提供担保。每个交易都需要经过背书策略中指定数量的Peer节点进行背书。如果要求多个Peer节点对交易进行背书,则并发执行背书可以提高系统的性能。

所有的Peer节点都会从排序服务接收到经过排序的区块。Peer节点会验证排序服务对区块的签名,然后对读集合中的数据进行验证,以判断交易是否有效。每个有效的区块都会被写入区块链账本中。区块中每个交易中的写集合都会被更新到状态数据库中。

5.完整的交易流程

下面以一个由多组织构成的Fabric网络为示例演示完整的交易流程。示例网络的拓扑如图2-8所示。

图2-8中使用的图例如表2-1所示。

示例网络中包含3个组织,分别是Org1、Org2和Org3。Org1提供5个Peer节点,Org2提供4个Peer节点和1个排序节点,Org3提供3个Peer节点和1个排序节点。

这些组织共同组成一个通道。任何组织发起的交易都会通过通道传送给所有3个组织。通道中包含一个智能合约的实例和通道策略。通道策略在配置文件configtx.yaml中定义,在创建通道时指定其内容。默认情况下,每个Peer节点都有一个记账者角色(记账节点),在图2-8中用三角形图标表示。Peer节点可以有多个角色,比如可以是背书节点、领导节点和锚节点。每个节点上都保存着一个账本的副本,其中包括区块链和状态数据库。背书节点上都安装了智能合约,以便对交易进行确认。

图2-8 示例网络的拓扑

表2-1 图2-8中使用的图例

每个组织都有自己的CA。Fabric网络采用模块化设计,每个组织都可以选择使用任何CA产品。在本例中Org1和Org2使用Fabric CA,而Org3使用Comodo CA。

Fabric网络中交易的完整流程如图2-9所示。

图2-9 Fabric网络中交易的完整流程

整个交易过程可以分为以下6个步骤。

(1)客户端初始化交易

在Fabric网络中交易由客户端应用发起,发起交易的方式是向背书节点发送交易提案。提案是向通道上指定的Peer节点提出的背书请求。提案可以是初始化(init)请求,也可以是读/写请求。

可以使用 Fabric SDK 构造交易提案。交易提案的作用是请求调用一个链码,以便读/写账本。Fabric SDK是一个将请求绑定到对应程序包的进程。SDK持有客户端的数字证书,因此为客户端所接受,可以代表客户端对其发起的交易提案进行签名,然后向选择的背书节点发送请求。

在发送交易提案到背书节点前,客户端SDK需要知道网络中所有背书节点的列表。在图2-8中有5个背书节点,分别是P12、P14、P22、P24和P32。背书节点在配置文件configtx. yaml中定义,configtx.yaml中保存着前文提到的背书策略。

每个背书节点上都安装了一个链码,还存储着一个账本副本。所有Peer节点上的账本副本都保持同步。

(2)背书节点验证签名并执行交易

背书是指定Peer节点执行链码交易并对客户端应用的交易提案返回一个“提案响应”的处理过程。提案响应中包括执行链码的响应信息、结果(读集合和写集合)、事件,以及作为Peer节点执行链码证据的数字签名。

链码应用程序应符合背书策略。背书策略可以指定如下配置信息。

● 背书节点:指通道中必须执行特定交易的 Peer 节点。这些特定交易都与特定链码应用程序绑定在一起。

● 交易被接受的前提条件:比如要求交易得到最少数量背书节点的背书、最小百分比背书节点的背书或分配给特定链码应用程序的所有背书节点的背书。

有些背书节点可能已经离线了,其余的背书节点在收到交易提案后,会按照事先定义好的步骤对交易进行验证,具体如下。

● 交易提案消息是否符合要求。

● 交易提案之前有没有被记账过。

● 签名是否有效。

● 提交交易提案的客户端是否有权限在当前通道上执行交易。

背书节点会以交易提案作为参数调用链码的函数,链码会基于数据库的当前状态执行模拟交易,并得到交易的结果。交易结果包括链码的响应值和交易的读/写集。读/写集中包含模拟交易时从当前状态数据库中读取的数据,以及执行交易时向状态数据库中写入的数据。此步骤中并不实际更新账本,读/写集和背书节点的签名将作为提案响应返回SDK。

(3)检查提案响应

客户端应用在收到所有背书节点的响应后,对背书响应列表进行验证,检查其是否满足背书策略的规则。不同的通道拥有不同的背书策略。

客户端应用会对背书节点的签名进行有效性校验。如果链码只希望查询账本,则客户端应用将检查查询响应,而不会将交易提交至排序服务。在交易发送至排序服务之前,客户端应用会检查提案响应以确保满足指定的背书策略的规则。

(4)客户端将背书信息集成到交易中

背书响应通过验证后,客户端应用会将其发送到合适的排序服务。背书信息最终会出现在区块中。在图2-8所示的示例网络中由组织Org2和Org3提供的两个排序节点组成排序服务。在生产网络中,通常会启动多个排序节点,以防出现故障影响网络的正常运行。

SDK会将交易提案和背书响应都封装到交易信息中,然后将其发送至排序服务。发送至排序服务的交易信息中包含以下信息。

● 模拟交易时产生的读/写集。

● 背书节点的签名。

● 通道ID。

排序服务并不会对交易信息中的所有信息进行检查,而只会从网络的所有通道中接收所有交易,然后按时间对它们进行排序,为每个通道创建一个区块,用于存储该通道的交易。

对交易的排序在网络范围内进行,不同应用程序的交易背书和提交的读/写集会同时被排序。

排序服务由一组排序节点组成,它并不对交易和智能合约进行处理,也不维护共享账本。排序服务接收经过背书的交易,并为这些交易指定一个记账节点,由记账节点将这些交易写入账本中。

(5)验证交易

当区块被经过排序的交易填满后,它会被发送至网络中的领导节点。理想状态下,Fabric网络中应该有一个领导节点,但是这并不是强制要求。如果没有领导节点,则排序节点需要逐一确认此区块已经被传送至所有的记账节点,这样做会增加网络的负载。为了避免这种情况,联盟中的每个组织都会提名自己的领导节点。每个组织可以有多个领导节点,以防止出现故障。有的组织如果只有一个领导节点,而且该领导节点没有对请求做出响应,则会在活动的Peer节点中启动一个投票机制,选举新的领导节点。

借助通道,所有的排序节点都可以得到通道中的领导节点列表。排序节点会将准备好的区块发送至所有活动的领导节点。

领导节点会将区块发送给同一组织内的其他记账节点。区块会通过Gossip协议而被传送至通道中的所有Peer节点。区块中的交易会被验证以确认如下事项。

● 满足背书策略的规则。

● 从交易执行生成读集合后,读集合中的变量与账本状态相比并没有发生变化。

根据验证的结果,区块中的交易会被标记为有效或无效。

(6)更新账本

Peer节点会根据链码的策略检查背书是否有效,同时要对读/写集进行检查,判断其是否与世界状态数据一致;特别是当背书节点模拟交易时,需要判断已经存在的读数据是否与世界状态数据一致。

当记账节点完成验证交易后,交易将会被写入账本中(也就是将区块追加到区块链中),并使用读/写集中的写数据更新状态数据库。

每个Peer节点均可与多个通道相关联。因此,在Fabric网络中可以存在多个区块链。

最后,每个记账节点都会将交易记账的结果异步通知到发起交易的客户端应用。

6.交易模拟和读/写集

在对交易进行背书时,有一个模拟交易的过程。在此过程中会生成交易的读/写集。读集合中包含一组唯一的关键字和它们的版本(注意不是由键和键的值数据所组成的键值对,而是由键和版本号组成的数据),以记录模拟过程中所读取的数据;写集合中包含一组唯一的关键字及它们的新值,新值就是在交易过程中写入区块链的值。如果交易删除某个关键字,则在写集合里该关键字会被记录一个删除标记。

如果在模拟交易的过程中多次修改一个关键字的值,则写集合里只保留最后一次写入的值。

关键字的版本号只在读集合中记录,写集合中只保存关键字及其最新值,并不包含版本号。版本号的最低要求是可用于标识关键字的不重复的标识符,可以通过以下两种方式实现版本号。

● 使用自增数字作为版本号。

● 基于区块链的高度生成版本号。

下面是一个读/写集的示例。

<TxReadWriteSet>
    <NsReadWriteSet name="chaincode1">
      <read-set>
        <read key="K1", version="1">
        <read key="K2", version="1">
      </read-set>
      <write-set>
        <write key="K1", value="V1">
        <write key="K3", value="V2">
        <write key="K4", isDelete="true">
      </write-set>
    </NsReadWriteSet>
<TxReadWriteSet>

读集合中包含K1和K2两个关键字;写集合中包含3个关键字,即K1、K3和K4。

记账节点会利用读集合检查交易的有效性,利用写集合更新相关关键字的版本号和值。由于篇幅所限,这里不展开介绍使用读/写集进行交易有效性校验和记账的过程。