# Hyperledger Fabric深入解读

By [leaf](https://paragraph.com/@leaf-6) · 2022-12-11

---

比特币被认为是区块链技术1.0时代的代表平台，随着以智能合约为主要特征的以太坊平台的诞生，区块链技术进入了2.0时代，而开源项目Hyperledger Fabric平台则标志着区块链技术3.0 时代的到来。最新发布的Fabric v1.4.1（LTS）提出了很多新的设计概念，添加了诸多新的特性，提供了高度模块化和可配置的架构，支持通用编程语言（如Java、Go和Node.js）编写智能合约，支持可拔插的共识协议，使得基于该平台开发企业级应用得以变为现实，平台的关注度也越来越高。本章将带领读者走进Hyperledger Fabric的世界，探究基本的运行原理，从而加深对该平台的了解，为后续学习基于Fabric的应用开发技术打下基础。

Hyperledger项目是致力于推进区块链数字技术和交易验证的开源项目，目标是让开源社区成员共同合作，建设开放平台，满足不同行业的用户需求，并简化业务流程。该项目通过创建分布式账本的公开标准，以实现虚拟和数字形式的价值交换。

在区块链技术的支撑下，比特币等“数字加密货币”成为热点，它们的活跃用户数量及交易量与日俱增，发展速度远远超出人们的估计。许多创业者、公司和金融机构渐渐意识到了区块链技术的价值，普遍认为它可以有更大的应用前景，而不仅仅局限于“数字加密货币”领域。

为此，Vitalik创立了Ethereum项目，希冀打造一个图灵完备的智能合约编程平台，让区块链爱好者可以更好更简单地构建开发区块链应用。继而，市场中涌现出了很多新型区块链应用，比如资产登记、预测市场、身份认证等各类应用。但是，当时的区块链技术自身仍存在着一些无法克服的问题。比如，首先交易效率低下，比特币整个网络只能支持每秒7笔左右的交易；其次，对于交易的确定性还无法得到很好的保证；最后，达成共识所采用的挖矿机制会造成很大的资源浪费。这些问题导致了当时的区块链技术无法满足大多数商业应用的需求。

因此，设计并实现一个满足商业需求的区块链平台成为当时区块链发展的一个关键。在社会各界的强烈呼声中，Linux基金会开源组织于2015年12月启动了名为Hyperledger的开源项目，意在通过各方合作，共同打造区块链技术的企业级应用平台，以此来促进跨行业区块链的发展。

Hyperledger在成立之初，就吸引了很多著名企业加入，如IBM、思科、Intel等科技互联网巨头，同时富国银行、摩根大通这类金融行业大鳄也成为第一批加入的成员。它的开发社区目前已经发展到超过270个组织。值得一提的是，项目成员中超过1/4的成员来自于中国的公司，比如趣链科技、小蚁、布比等新创区块链公司，同时也有万达、华为、招商银行等知名企业的参与。从成员阵容来看，Hyperledger开源项目声势异常浩大，汇集了众多的各行各业企业精英，集体进行合作探讨解决方案，推进企业级区块链平台的发展。

Hyperledger项目首次提出和实现完备的成员权限管理、创新的共识算法和可插拔的框架，对区块链相关技术和产业的发展都将产生深远的影响。实际上这已经说明区块链技术不单纯是一个开源技术了，它已经被其他行业的主流机构及市场正式认可。

Hyperledger项目是一个大型的开源项目，希望通过各方合作，共同促进和推进区块链技术在商业应用方面的发展。在组成结构上，包含了很多相关的具体子项目。这些子项目可以是一个独立的项目，也可以是与其他项目关联的项目，比如构建工具、区块链浏览器等。Hyperledger对于子项目的形式并没有给出太大的约束，只要是有与之相关的好的想法，都可以向Hyperledger委员会发出申请提案。

项目官方地址托管在 Linux 基金会网站，代码托管在 Gerrit上，并通过GitHub提供代码镜像。为了更好地管理子项目和发展项目，Hyperledger项目成立了一个称为技术指导委员会（Technical Steering Committee，TSC）的机构，这也是Hyperledger项目的最高权力机构，子项目的管理以及整个项目生态的发展等重要决定都将由它执行。Hyperledger项目在管理所属子项目时采用了一种生命周期的形式，赋予每个项目一个生命周期，方便项目的运行和管理。整个生命周期分为5个阶段，分别是提案（proposal）阶段、孵化（incubation）阶段、活跃（active）阶段、弃用（deprecated）阶段以及最后终止（end of Life）阶段。每个项目在开发运行过程中，一个时间点只会对应着一个阶段。当然，项目不一定会按照以上阶段顺序发展，项目可能会一直处于某个阶段，也可能会因为一些特殊原因在多个阶段之间进行变换。所有的项目需要重视包括交易、合同、一致性、身份、存储的技术场景在内的模块化设计，还需实现代码可读性以保障新功能和模块都可以很容易添加和扩展，并且需要不断增加和演化新的项目来满足日益深入的商业化需求和逐渐丰富的应用场景。

![](https://storage.googleapis.com/papyrus_images/c4459b75f8f3a67064ac98c2ce31dd31d4267f3c64943fe742ced686ecb70e22.png)

![](https://storage.googleapis.com/papyrus_images/097f1909ac124847317bfb0c76256d2c3cb8f66898b85d8a88bcb451b81d4d47.png)

Fabric是一种区块链技术的实现，也是一种基于交易调用和数字事件的分布式共享账本技术。比起其他的区块链技术实现，它采用了模块化的架构设计，支持可插拔组件的开发与使用。其总账上的数据由多方参与节点共同维护，并且一旦被记录，账本上的交易信息永远无法被篡改，并支持通过时间戳进行溯源查询。对于其他公有链而言，Fabric引入了成员管理服务，因此每个参与者均需要提供对应的证书证明身份才允许访问Fabric系统，同时引入多通道多账本的设计来增强系统的安全性和私密性。与以太坊相比，Fabric采用了强大的Docker容器技术来运行服务，支持比以太坊更便捷、更强大的智能合约服务，以太坊只能通过提供的Solidity语言进行合约编写，而Fabric支持多语言的合约编写，例如Go、Java和Node.js。除此之外，Fabric还提供了多语言的SDK开发接口，让开发者可以自由、便捷地使用其所提供的区块链服务。本章后面将会深入分析Fabric的架构和运行。

Iroha是一个受Fabric架构启发而提出的分布式账本项目，该项目在2016年10月13日通过技术指导委员会的批准，进入孵化阶段，2017年5月18日搬出孵化区。它旨在为C++和移动应用开发人员提供Hyperledger项目的开发环境。该项目希望用C++实现Fabric、Sawtooth Lake和其他潜在区块链项目的可重复使用组件，并且这些组件可以用Go语言进行调用。也就是说，Iroha是对现有项目的一个补充，其长期的目标是实现一个健全的可重用组件库，使Hyperledger技术项目在运行分布式账本时，能自由地选择并使用这些可重复使用的元素。

Sawtooth Lake于2016年4月14日通过TSC批准，2017年5月18日搬出孵化区，是一个由Intel发起的模块化分布式账本平台实验项目，它专为多功能性和可扩展性而设计。Sawtooth Lake提供了一个构建、部署和运行分布式账本的模块化平台，同时支持许可链和非许可链的部署。它包含了一个新的共识算法PoET。PoET与比特币采用的工作量证明算法一样，都是按照一定规则随机选取出一个节点，由该节点来作为区块的记账者，而其他节点则负责验证该区块和执行结果。不同的是，PoET不需要消耗大量的算力和能耗，但是需要CPU硬件支持SGX（software guard extensions）特性。由于PoET算法的硬件限制，因此目前暂时仅适合在生产环境中使用PoET算法。

Blockchain Explorer项目旨在为Hyperledger创建一个用户友好的Web应用程序，用于查询Hyperledger区块链上的信息，包括区块信息、交易相关数据信息、网络信息、合约代码以及分布式账本中存储的相关信息。项目于2016年8月11日通过TSC批准，之后项目启动进入孵化阶段。

Cello项目于2017年1月5日通过TSC的批准，进入孵化状态。Cello项目致力于提供一种区块链即服务（blockchain as a service，BasS），以此减少手动操纵（创建和销毁）区块链的工作量。通过Cello，操作者可以使用仪表盘（dashboard）来简单地创建和管理区块链，同时用户（合约代码开发者）可以通过单个请求立即获取区块链信息。也就是说，为操作者提供了一个简易便捷的区块链操作平台。

Hyperledger Fabric是分布式账本技术（DLT）的独特实现，它可在模块化的区块链架构基础上提供企业级的网络安全性、可扩展性、机密性以及高性能。当前Fabric的最新版本为v1.4.1（LTS），相比先前的v0.6版本，v1.4版本针对安全、保密、部署、维护、实际业务场景需求等方面进行了很多改进，例如架构设计上的Peer节点的功能分离、多通道的隐私隔离、共识的可插拔实现等，功能上引入了Raft崩溃容错共识服务，改进可维护性和可操作性，加入私有数据支持等，都为Fabric提供了更好的服务支持。因此，本书后面关于Fabric的内容均将基于v1.4版本进行描述。

Hyperledger Fabric v1.4具有以下特性。

*   身份管理（identity management）。Fabric区块链是一个许可链网络，因此Fabric提供了一个成员服务（member service），用于管理用户ID并对网络上所有的参与者进行认证。在Hyperledger Fabric区块链网络中，成员之间可以通过身份信息互相识别，但是他们并不知道彼此在做什么，这就是Fabric提供的机密性和隐私性。
    
*   隐私和保密（privacy and confidentiality）。Hyperledger Fabric允许竞争的商业组织机构和其他任意对交易信息有隐私和机密需求的团体在相同的许可链网络中共存。其通过通道来限制消息的传播路径，为网络成员提供了交易的隐私性和机密性保护。在通道中的所有数据，包括交易、成员以及通道信息都是不可见的，并且未订阅该通道的网络实体都是无法访问的。
    
*   高效的性能（efficient processing）。Hyperledger Fabric按照节点类型分配网络角色。为了提供更好的网络并发性和并行性，Fabric对事务执行、事务排序、事务提交进行了有效的分离。于排序之前执行事务可以使得每个Peer节点同时处理多个事务，这种并发执行极大地提高了Peer节点的处理效率，加速了交易到共识服务的交付过程。
    
*   函数式合约代码编程（chaincode functionality）。合约代码是通道中交易调用的编码逻辑，定义了用于更改资产所有权的参数，确保数字资产所有权转让的所有交易都遵守相同的规则和要求。
    
*   模块化设计（modular design）。Hyperledger Fabric实现的模块化架构可以为网络设计者提供功能选择。例如，特定的身份识别、共识和加密算法可以作为可插拔组件插入Fabric网络中，基于此，任何行业或公共领领域都可以采用通用的区块链架构，并确保其网络可跨市场、监管和地理边界进行互操作。
    
*   可维护性和可操作性（serviceability and operations）。日志记录的改进以及健康检查机制和运营指标的加入，使得v1.4版本在可维护行和可操作性上实现了巨大飞跃。新的RESTful运营服务为生产运营商提供三种服务来监控和管理对等节点和共识服务节点运营。第一种服务使用日志记录/logspec端点，允许操作员动态获取和设置对等节点和共识服务节点的日志记录级别；第二种服务使用健康检查/healthz端点，允许运营商和业务流程容器检查对等节点和共识服务节点的活跃度和健康情况；第三种服务使用运营指标/metrics端点，允许运营商利用Prometheus记录来自对等节点和共识服务节点的运用指标。
    
*   **锚节点**
    
    Gossip协议使用锚节点确保不同组织中的对等节点彼此了解。当提交包含锚节点更新的配置块时，对等节点能探测到锚节点并能获知锚节点已知的所有对等节点。由于组织间通过Gossip通信，因此通道配置中必须定义至少一个锚节点。每个组织都提供一组锚节点则可实现高可用性和减少冗余。
    
*   **访问控制列表**
    
    访问控制列表（ACL）将对特定对等节点资源（如系统合约代码APIs或事务服务）的访问与策略（指定所需的组织或角色的数量和类型）相关联。ACL是通道配置的一部分，可使用标准配置更新机制更新。
    
*   **区块**
    
    区块包含一组有序的交易，由共识系统创建，由对等节点验证。在通道中以加密的方式先与前序区块链接，然后连接到后序区块。第一个区块被称为创世区块。
    
*   **区块链**
    
    区块链是一个交易日志，由交易区块经过哈希连接结构化得到。对等节点从共识服务收到交易区块后，基于背书策略和并发冲突，标注区块的交易为有效或者无效，并将区块追加到对等节点文件系统的哈希链中。
    
*   **智能合约**
    
    智能合约是由区块链网络外部客户端调用的代码，可以用于管理世界状态中键值对的访问和修改，安装在对等节点上，并在通道上实例化。智能合约也被称为合约代码。
    
*   **通道**
    
    通道是构建在Fabric网络上的私有区块链，由配置块定义，保障数据的隔离及隐私性。所有对等节点共享通道中特定的账本，交易方与账本的交互必须通过通道的正确性验证。
    
*   **提交**
    
    一个通道中的每个对等节点都会验证交易区块的有序性，然后将区块提交（写或附加）至该通道上账本的各个副本。对等节点也会标记交易是否有效。
    
*   **并发控制版本检查**
    
    并发控制版本检查（CCVC）可以保持通道中的对等节点状态同步。对等节点并行地执行交易，在交易提交至账本之前，对等节点会检查交易在执行期间读取的数据是否被修改。如若被修改，则引发CCVC冲突，该交易就会在账本中被标记为无效，其值不会更新到状态数据库中。
    
*   **配置区块**
    
    包含系统链（共识服务）或通道定义成员和策略的配置数据。对某个通道或整个网络的配置修改（比如，成员离开或加入）将导致生成一个新的配置区块并追加到适当的链上。这个配置区块会包含创始区块的内容加上增量。
    
*   **共识**
    
    共识用于确认交易的排序以及交易集本身的正确性。
    
*   **意集**
    
    在Raft共识服务中，同意集是通道上积极参与共识机制的排序节点。如果系统通道上存在其他排序节点，但是不属于通道的一部分，则这些排序节点不属于通道的排序集。
    
*   **联合体**
    
    联合体是区块链网络上无序组织的集合。这些集合组建并加入通道，且拥有自己的对等节点，虽然区块链网络可以拥有多个联合体，但大多数网络只有一个联合体。在通道创建时，所有加入通道的组织必须是联合体的一部分。未在联合体中定义的组织可能会被添加到现有通道中。
    
*   **世界状态**
    
    世界状态也被称为账本的当前状态，表示区块链交易日志中所有key的最新值。对等节点将最近处理过的每笔交易对应修改的value值更新到账本的世界状态。由于世界状态可以直接访问key的最新值，而不是通过遍历整个交易日志，所以合约代码必须先知道key-value的世界状态，然后针对这个世界状态执行交易提案。
    
*   **动态成员管理**
    
    Fabric支持在不影响整个网络操作性的情况下，动态添加/移除成员、对等节点和共识服务节点。动态成员管理在业务关系调整或因各种原因需添加/移除实体时至关重要。
    
*   **创世区块**
    
    创世区块是初始化区块链网络或通道的配置区块，也是区块链上的第一个区块。
    
*   **Gossip协议**
    
    Gossip数据传输协议有3项功能：管理对等节点，发现通道上的成员；通道上的所有对等节点间广播账本数据；通道上的所有对等节点间同步账本数据。
    
*   **账本**
    
    账本由区块链和世界状态组成。区块链不可变，一旦将一个区块添加到链中，它就无法更改。而世界状态是一个数据库，包含已由区块链中验证和提交事务集添加、修改或删除的键值集合的当前值。网络中每个通道都有一个逻辑账本，实际上，通道中每个对等节点都维护着属于自己的账本副本，这些副本通过共识过程与其他对等节点的副本保持一致，逻辑上是单一的，但在一组网络节点（对等节点和共识服务）中分布着许多相同的副本。术语分布式账本技术（DLT）通常与这种账本相关联。
    
*   **追随者**
    
    在基于领导者的共识协议（如Raft）中，追随者复制由领导者生成的日志条目的节点。在Raft中，追随者也会收到领导者的“心跳”信息，如果领导者在可配置的时间内停止发送这些信息，追随者将发起领导者选举，其中一名追随者会被选为领导者。
    
*   **领导者**
    
    在基于领导者的共识协议（如Raft）中，领导者负责提取新的日志记录，将其复制到追随者共识节点，并且管理记录何时被认为是已提交。
    
*   **主要对等节点**
    
    每个组织可以在它们订阅的通道上拥有多个对等节点，它们中至少有一个作为主要对等节点，以便代表该组织与网络共识服务通信。共识服务向通道上主要对等节点提供块，然后将其分发给同一组织内的其他对等节点。
    
*   **日志记录**
    
    日志记录是Raft共识服务中的主要工作单元，从领导者分发给追随者。这些记录的完整序列称为“日志”。如果所有成员就记录及其排序达成一致，则该日志被认为是一致的。
    
*   **成员服务提供组件**
    
    成员服务提供组件（MSP）是指为客户端节点和对等节点提供证书的系统抽象组件。客户端节点用证书来认证他们的交易；对等节点用证书认证其交易（背书）。该接口与系统的交易处理组件密切相关，旨在使已定义的成员身份服务组件以这种方式顺利插入，而不会修改系统的交易处理组件的核心。
    
*   **成员管理服务**
    
    成员管理服务在许可区块链上认证、授权和管理身份。在对等节点和排序服务节点中运行成员管理服务的代理。
    
*   **排序服务或共识服务**
    
    将交易排序放入区块的节点的集合。排序服务独立于对等节点流程之外，并以先到先得的方式为网络上所有的通道做交易排序。排序服务支持可插拔实现，目前默认实现了Solo和Kafka。
    
*   **组织**
    
    组织也称为“成员”，由区块链服务提供商邀请加入区块链网络。组织通过将其MSP添加到网络加入网络。组织的交易端点是对等节点，一群组织形成一个联合体。虽然网络上的所有组织都是成员，但并非每个组织都会成为联盟的一部分。
    
*   **节点**
    
    维护账本并运行合约容器来对账本执行读写操作的网络实体。节点由成员拥有和维护。
    
*   **策略**
    
    *   策略是由数字标识（digital identity）的属性组成的表达式，如Org.Peer 和Org2.Peer，用于限制对区块链网络上资源的访问。策略可以在引导共识服务或创建通道之前定义，也可以在实例化通道上的合约代码时指定。
        
    *   **私有数据**
        
        私有数据是存储在每一个授权对等节点的私有数据库中的机密数据，在逻辑上与通道账本数据分开。对私有数据的访问仅限于私有数据集上定义的组织。未经授权的组织只能在通道账本上拥有私有数据的哈希值，作为交易数据的证据。此外，为了进一步保护隐私，私有数据的哈希值通过共识服务而不是私有数据本身传递，从而使得私有数据对共识服务节点保密。
        
    *   **私有数据集**
        
        私有数据集用于管理通道上两个或多个希望与该通道上其他组织保密的组织，描述了通道上有权存储私有数据的组织子集，只有这些组织能与私有数据交易。
        
    *   **Raft**
        
        Raft是v1.4.1新增的功能，基于Raft协议etcd库的崩溃容错（CFT）共识服务实现。Raft遵循“领导者和追随者”模型。与基于Kafka的共识服务相比，Raft共识服务更容易设置和管理，并且允许组织为分布式共识服务贡献节点。
        

Hyperledger是当前业界较为认可的联盟链实现，作为其最重要的子项目，Fabric备受关注。从孵化到发展至今，Fabric的架构设计也在演进过程中逐渐地改进与完善。前面已经对Fabric做了基本内容与功能的介绍，接下来将开始深入探索Fabric，对Fabric最新的总体架构进行分析，并通过与过往架构对比的方式探讨Fabric新架构的特点和优势。

abric在架构设计上采用了模块化的设计理念，从图4.1所示的整体逻辑架构来看，Fabric主要由3个服务模块部组成，分别是成员服务（membership service）、区块链服务（Blockchain service）和合约代码服务（Chaincode service）。其中，成员服务提供会员注册、身份管理和认证服务，使平台访问更加安全，且有利于权限管理；区块链服务负责节点之间的共识管理、账本的分布式计算、P2P网络协议的实现以及账本存储，作为区块链的核心组成部分，为区块链的主体功能提供底层服务支撑；合约代码服务则提供一个智能合约的执行引擎，为Fabric的合约代码（智能合约）程序提供部署运行环境。同时在逻辑架构图中，还能看到事件流（event stream）贯穿三大服务组件间，它的功能是为各个组件的异步通信提供技术支持。在Fabric的接口部分，提供了API、SDK和CLI这3种接口，用户可以用来对Fabric进行操作管理。

![](https://storage.googleapis.com/papyrus_images/b5aeda2a9621e2f19d79ce1834f74926db74be029113fb1000d0af0f6ed5d879.png)

展示了Fabric运行架构。v0.6版本的结构非常简单，应用-成员管理-Peer呈现三角形关系，系统所有的业务功能均由Peer节点完成。但是Peer节点承担了太多的业务功能，暴露出了扩展性、可维护性、安全性、业务隔离等方面的诸多问题。因此，在v1.4版本中，官方对架构进行了改进和重构，将共识服务部分从Peer节点中完全分离出来，独立形成一个新的节点，提供共识服务和广播服务。v1.4版本还引入了通道的概念，实现多通道结构和多链网络，带来了更为灵活的业务适应性。同时还支持更强的配置功能和策略管理功能，进一步增强系统的灵活性。

![](https://storage.googleapis.com/papyrus_images/7bcb3707a404bba8ef75a2a5a950c78c46ad1eed5fe7563a2aab426a80b58e9b.png)

![](https://storage.googleapis.com/papyrus_images/18fd83ff9ed6213e964e4deedd0a821511cff39baba12714ade2b8847ff14fe3.png)

　**运行时架构（v1.4）**

相比v0.6版本，新的架构使得系统在很多方面有很大的提升，主要有以下几大优势。

*   **合约代码信任的灵活性**（chaincode trust flexibility）。v1.4版本从架构上，将合约代码的信任假设（trust assumptions）与共识服务（ordering service）的信任假设进行了分离。新版本的共识服务可以由一组单独的节点（orderer）来提供，甚至允许出现一些失效节点或恶意节点。而对于合约代码程序而言，它可以指定不同的背书节点，这极大地增强了合约代码的灵活性。
    
*   **可扩展性**（scalability）。在新的架构下，负责为指定合约代码背书的背书节点与共识节点是一种正交的关系，所以相比v0.6架构的所有业务功能都在Peer节点上执行，v1.4版本架构的扩展性有了很大的提升。尤其是当不同的合约代码所指定的背书节点不存在交集时，系统可以同时进行多个合约代码程序的背书操作，这很好地提高了系统处理的效率。
    
*   **机密性**（confidentiality）。Mutichannel的设计使得对内容和执行状态更新有机密性需求的合约代码的部署变得容易了。同时增加了对私有数据的支持，并且正在开发的零知识证明（ZKP）将在未来可用。
    
*   **共识模块性**（consensus modularity）。v1.4架构将共识服务从Peer节点分离出来独自成为共识节点，共识服务被设计为可插拔的模块化组件，允许不同共识算法的实现来应用于复杂多样的商业场景。
    

成员服务可以为Fabric的参与者提供网络上的身份管理、隐私性、保密性和认证服务。下面重点介绍PKI体系的相关内容及用户的注册过程。

1.  **PKI体系**
    
    PKI（public key infrastructure，公钥基础设施）的目标就是实现不同成员在不见面的情况下进行安全通信，Fabric当前采用的模型是基于可信的第三方机构，也就是证书颁发机构（certification authority，CA）签发的证书。CA会在确认申请者的身份后签发证书，同时会在线提供其所签发证书的最新吊销信息，这样使用者就可以验证证书是否仍然有效。证书是一个包含公钥、申请者相关信息以及数字签名的文件。数字签名保证了证书中的内容不能被任何攻击者篡改，而且验证算法可以发现任何伪造的数字签名。这样公钥和身份被捆绑在一起，不能篡改，也不能伪造，就可以实现成员管理。
    
    成员服务将PKI体系和去中心化共识协议结合在一起，将非许可区块链转变为了一个许可区块链。在非许可区块链中，实体不需要经过授权，网络中的所有节点并不存在角色区别，都是统一的对等实体，都拥有平等提交交易及记账的权利。而在许可区块链中，实体需要注册来获取长期的身份证书（例如注册证书），这个身份证书可以根据实体类型来进行区分。对于用户而言，在注册时，交易证书颁发机构（transaction certificate authority，TCA）会给注册的用户颁发一个匿名的证书；而对于交易来说，需要提交的交易需通过交易证书的认证，并且交易证书会一直存储于区块链上以供认证服务追溯交易使用。实际上，成员服务是一个认证中心，负责为用户提供证书认证和权限管理的功能，对区块链网络中的节点和交易进行管理和认证。
    
    在Fabric的系统实现中，成员服务由几个基本实体组成，它们互相协作来管理网络上用户的身份和隐私。这些实体有的负责验证用户的身份，有的负责在系统中为用户注册身份，有的为用户在进入网络或者调用交易时提供所需的证书凭据。PKI是一个基于公钥加密的框架体系，它不仅可以确保网络上的数据安全交换，而且还可以用来确认管理对方的身份。同时在Fabric系统中，PKI还被运用于管理密钥和数字证书的生成、分发以及撤销。
    
    通常情况下，PKI体系包含证书颁布机构（CA）、注册机构（RA）、证书数据库和证书存储实体。其中，RA是一个信任实体，它负责对用户进行身份验证以及对数据、证书或者其他用于支持用户请求的材料进行合法性审查，同时还负责创建注册所需的注册凭证。CA则会根据RA的建议，给指定用户颁发数字证书，这些证书由根CA直接或分层进行认证。
    

![](https://storage.googleapis.com/papyrus_images/d4398d08604fc4f7325c57acca689ab54c9413557d014170c04a378b897b36d6.png)

对图中的实体进行进一步介绍说明。

*   Root Certificate Authority：根CA，代表PKI体系中信任的实体，同时也是PKI体系结构中的最顶层认证机构。
    
*   Enrollment CA（ECA）：在验证用户提供的注册凭证后，ECA负责发出注册证书（ECerts）。
    
*   Transaction CA（TCA）：在验证用户提供的注册凭证后，TCA负责发出交易证书（TCerts）。
    
*   TLS CA：负责颁发TLS（transport layer security，传输层安全协议）证书和凭据，以允许用户使用其网路。
    
*   ECerts（enrollment certificates）：ECerts是长期证书，针对所有角色颁发。
    
*   TCerts（transaction certificates）：TCerts是每个交易的短期证书，由TCA根据授权的用户请求颁发。用户可以配置TCerts为不携带用户身份的信息从而匿名地参与系统，还可以防止事务的可链接性。
    
*   TLS-Certs（TLS-Certificates）：TLS-Certs携带其所有者的身份，用于系统和组件之间进行通信以及维护网络级安全。
    
*   CodeSignerCerts（Code Signer Certificates）：负责对软件代码进行数字签名，标识软件来源及软件开发者的真实身份，以此保证代码在签名之后不被恶意篡改。
    

金融IC卡系统中也使用了PKI体系，它的架构如图4.5所示。与Fabric的PKI体系相比，它没有TCert，每次交易都是使用ECert完成的，所以这个系统中的交易是没有匿名的。

![](https://storage.googleapis.com/papyrus_images/3dca1e3aad275658c9f64a13ff87a7575410e738b0ecb56f180865eeddab7c85.png)

**前面介**绍了成员服务的PKI体系的实体及其基本功能，接下来针对具体的用户注册流程做一个简单的介绍。图展示了一个用户登记流程的高层描述，它分为两个阶段：离线过程与在线过程。

![](https://storage.googleapis.com/papyrus_images/c0ceeee761e41d7ae289814373a4e134c8939a61d5d6ddc122eeed46e05d50f0.png)

**用户注册过程**

*   **离线过程**
    
    (1) 每个用户或者Peer节点必须向RA注册机构提供身份证件（ID证明），同时这个流程必须通过带外数据（out-of-band，OOB）进行传输，以提供RA为用户创建（和存储）账户所需的证据。
    
    (2) RA注册机构返回用户有关的用户名和密码，以及信任锚（包含TLS-CA Cert）。如果用户可以访问本地客户端，那么客户端可以将TLS-CA证书作为信任锚的一种方式。
    
*   **在线过程**
    
    (1) 用户连接客户端以请求登录系统，在这一过程中，用户将用户名和密码发送给客户端。
    
    (2) 用户端代表用户向成员服务发送请求，成员服务接受请求。
    
    (3) 成员服务将包含几个证书的包发送给客户端。
    
    (4) 一旦客户端验证完成所有的加密材料是正确有效的，它就会将证书存储于本地数据库中并通知用户，至此，用户注册完成。
    

区块链服务包含4个模块：共识管理、分布式账本、账本存储以及P2P网络协议。共识管理用于在多个节点的分布式复杂网络中使消息达成共识，分布式账本与账本存储负责区块链系统中所有的数据存储，比如交易信息、世界状态、私有数据等。而P2P网络协议则是网络中节点的通信方式，负责Fabric中各节点间的通信与交互。

1.  **P2P网络**
    
    P2P网络这种分布式应用架构是对等计算模型在应用层形成的一种组网或网络形式，用于对等实体间分配任务和工作负载。彼此连接的多台计算机在P2P网络环境中处于对等地位，有相同的功能，不分主从。一台计算机可作为服务器，设定供网络中其他计算机使用的共享资源，又可以作为工作站来请求服务。一般来说，整个网络不依赖专用的集中服务器，也没有专用的工作站。而区块链所处的分布式环境中，各个节点间本应该是平等的，天然适合P2P网络协议。
    
    在Fabric的网络环境中，节点是区块链的通信实体。存在3类不同的节点，分别是客户端节点（Client）、Peer节点（Peer）以及共识服务节点（Ordering Service Node或者Orderer）。
    
    客户端节点代表着终端用户实体。它必须连接到Peer节点后才可以与区块链进行通信交互。同时客户端节点可以根据它自己的选择连接到任意的Peer节点上，创建交易和调用交易。在实际系统运行环境中，客户端负责与Peer节点通信提交实际交易调用，与共识服务通信请求广播交易的任务。
    
    Peer节点负责与共识服务节点通信来进行世界状态的维护和更新。它们会收到共识服务广播的消息，以区块的形式接收排序好的交易信息，然后更新和维护本地的世界状态与账本。与此同时，Peer节点可以额外地担当背书节点的角色，负责为交易背书。背书节点的特殊功能是针对特定的交易设置的，在它提交前对其进行背书操作。每个合约代码程序都可以指定一个包含多个背书节点集合的背书策略。这个策略将定义一个有效的交易背书（通常情况下是背书节点签名的集合）的充要条件。需要注意的是，存在一个特殊情况，在安装新的合约代码的部署交易中，（部署）背书策略是由一个系统合约代码的背书策略指定的，而不能自己指定。
    
    共识服务节点Orderer是共识服务的组成部分。共识服务可以看作一个提供交付保证的通信组织。共识服务节点的职责就是对交易进行排序，确保最后所有的交易以同样的序列输出，并提供送达保证服务的广播通信服务。关于共识服务.
    

节点的类型，再来看看网络的拓扑结构。在v0.6版本中，整个网络由两类节点构成：VP（validating Peer）验证节点和NVP非验证节点。如图所示，网络中包含了4个验证节点，并且每个节点还连接着2个非验证节点，整个网络的共识则由4个验证节点构成。在v1.4版本中，网络拓扑结构随着网络节点类型的变化也发生了很大的改变，其中共识服务节点一起组成共识服务，将共识服务抽离出来，而Peer节点中可以分为背书节点或者提交Peer节点，并且它们还可以进行分组，然后整个共识服务与Peer节点所构成的组一起形成新的完成网络。

![](https://storage.googleapis.com/papyrus_images/48a2e2aa24668c89d3bf24978f7344770a36658b43946d4d5250f55c0b33321f.png)

在v1.0之后的版本中，Fabric引入了新的通道概念，共识服务上的消息传递支持多通道，使得Peer节点可以基于应用访问控制策略来订阅任意数量的通道。Peer节点的子集可以被应用程序指定架设相关通道，指定相同通道的Peer节点组成集合，提交该通道的交易，而且只有这些Peer节点可以接收相关交易区块，与其他交易完全隔离。Fabric支持多链与多通道，即系统中可以存在多个通道以及多条链，如图所示。应用根据业务逻辑决定将每个交易发送到指定的一个或多个通道，不同通道上的交易不会存在任何联系。

![](https://storage.googleapis.com/papyrus_images/c3d5abdb368304fba119a3c04dd836d8ef0238cd55c3846b0731e0658528e664.png)

*   从v1.2开始，Fabric能够在账本中创建私有数据集，允许通道上组织的子集能够认可、提交或查询私有数据，不用创建单独的通道就能实现通道上的一组组织的数据向其他组织保密的功能。实际的私有数据存储在授权组织的对等节点上的私有状态数据库中（有时候被称为side数据库或SideDB），能被授权节点上的合约代码通过Gossip协议访问。共识服务不涉及其中也无法看到私有数据。由于Gossip协议在授权组织中对等分发私有数据，这需要在通道中建立锚节点，并在每个节点上配置CORE\_PEER\_GOSSIP\_EXTERNALENDPOINT，以便引导跨组织通信。私有数据的哈希值能够被认可、排序并写入通道上每个对等方的账本中，可作为交易的证据，用于状态验证，还可用于审计。
    
    总的来说，Fabric在节点和网络方面的一些重构和新特性使得Fabric的交易处理能力有了增强，而且很好地实现了隐私隔离。
    
*   **共识服务**
    
    网络中的Orderer节点聚集在一起形成了共识服务。它可以看作一个提供交付保证的通信组织。共识服务为客户端和Peer节点提供了一个共享的通信通道，还为包含交易的消息提供了广播服务的功能。客户端连接到通道后，可以通过共识服务广播消息将消息发送给所有的Peer节点。共识服务可以为所有消息提供原子交付保证，也就是说，在Fabric中共识服务保证了消息通信是序列化和可靠的。换句话说，共识服务输出给所有连接在通道上的Peer节点相同的消息，并且输出的逻辑顺序也是相同的。
    
    共识服务可以有不同的实现方式，在v1.4版本中，Fabric将共识服务设计成了可插拔模块，可以根据不同的应用场景配置不同的共识选项。目前，Fabric提供了3种模式实现：Solo、Kafka和Raft。
    
    Solo是一种部署在单个节点上的简单时序服务，主要用于开发测试，它只支持单链和单通道。Kafka是一种支持多通道分区的集群共识服务，可以支持CFT（crash faluts tolerance）。它容忍部分节点宕机失效，但是不能容忍恶意节点。其基本实现基于Zookeeper服务，使用的分布式环境中要求总节点数与失效节点数满足_n_≥2_f_+1。Raft遵循“领导者和追随者”模型，每个通道都选举一个“领导者”，它的决定将被复制给“追随者”，支持CFT。只要总节点数与失效节点数满足_n_≥2_f_+1，它就允许包括领导者在内的部分节点宕机失效。与基于Kafka的共识服务相比，Raft应该更容易设置和管理，并且它们的设计允许组织为分散的共识服务贡献节点。
    
*   区块链技术从其底层构造上分析，可以视为一种共享账本技术。账本是区块链的核心组成部分，在区块链的账本中存储了所有的历史交易和状态改变记录。在Fabric中，每个通道都对应着一个共享账本，而每个连接在共享账本上的Peer节点，都能参与网络和查看账本信息，即它允许网络中的所有节点参与和查看账本信息。账本上的信息是公开共享的，并且在每个Peer节点上都维持着一份账本的副本。图4.9展示了Fabric账本的结构。
    

![](https://storage.googleapis.com/papyrus_images/1feb7a013b3b6afe75db7f9cf70fd844c381032e6153b5f7824ad58d11ecc728.png)

**共享账本结构**

从图中可以看出，共享账本以文件系统的形式存储于本地。共享账本由两部分组成：图中链式结构的Chain部分和图中右边存储状态数据的State部分。其中，Chain部分存储着所有交易的信息，只可添加查询，不可删改。State部分存储着交易日志中所有变量的最新值，因为它表示的是通道中所有变量键值对的最新值，所以有时称为“世界状态”。

合约代码调用执行交易来更改目前的状态数据，为了使这些合约代码高效交互，设计将最新的键值对数据存储于状态数据库中。默认的状态数据库采用的是Level DB，但是可以通过配置切换到Couch DB或者其他数据库。

合约代码服务提供了一种安全且轻量级的方式，沙箱验证节点上的合约代码执行，提供安全容器服务以及安全的合约代码注册服务。其运行环境是一个“锁定”和安全的容器，合约代码首先会被编译成一个独立的应用程序，运行于隔离的Docker容器中。在合约代码部署时，将会自动生成一组带有签名的智能合约的Docker基础镜像。在Docker容器中，合约代码与Peer节点的交互过程如图所示。

![](https://storage.googleapis.com/papyrus_images/333f5da7904a30d681f98bc785aaad7bf1df26346d2e412de256f58e6e9ff114.png)

步骤如下。

(1) Peer节点收到客户端发来的合约代码执行请求后，通过gRPC与合约代码交互，发送一个合约代码消息对象给对应的合约代码。

(2) 合约代码通过调用`Invoke()`方法，执行`GetState()`操作和`PutState()`操作，向Peer节点获取账本状态数据库和发送账本预提交状态数。若要读取和写入私有数据，则通过`GetPrivateDate()`和`PutPrivateDate()`方法。

(3) 合约代码执行成功后，将输出结果发送给Peer节点，背书节点对输入和输出信息进行背书签名，完成后应答给客户端。

架构解读，可以得知合约代码服务是Fabric架构中的核心组成部分，本节将进一步研究合约代码服务中所运行的合约代码，介绍如何编写、部署及调用具体的合约代码。

合约代码是区块链上运行的一段代码，是Fabric中智能合约的实现方式。同时在Fabric中，合约代码还是交易生成的唯一来源。共享总账是由区块连接而成的一条不断增长的哈希链，而区块中包含了以Merkle树的数据结构表示的所有的交易信息，可以说交易是区块链上最基础的实体单元。那么交易又是怎样产生的呢？交易只能通过合约代码调用操作而产生，所以合约代码是Fabric的核心组件，也是与共享账本交互的唯一渠道。

目前，Fabric支持使用Java、Go和Node.js语言通过实现接口的方式来编写合约代码。按照Fabric的设计，位于/core/chaincode目录下的shim包是提供合约代码开发的SDK，理论上可以独立使用，但目前或许因为需要调用某些其他依赖模块，还不能很好地独立出来。

Fabric中的合约代码运行在Peer节点上，并且与合约代码相关的操作诸如部署、安装、调用等也都是在Peer节点上进行的。合约代码通过SDK或者CLI在Fabric网络的Peer节点上进行安装和初始化，使用户与Fabric网络的共享账本之间的交互成为可能。目前，合约代码的节点运行模式有两种：

一般模式和开发模式。

一般模式是系统默认模式，合约代码运行于Docker容器中。运用Docker容器来运行Fabric系统，这样就给Fabric系统和合约代码的运行提供了一个隔离的环境，可以提高整个系统的安全性。但是在这种模式下，对于开发人员而言，开发调试过程非常复杂和麻烦，因为每次修改代码之后都需要重新启动Docker容器，这会极大地降低程序开发的效率。

因此，考虑到开发人员的效率问题，Fabric提供了另外一种运行模式，即开发模式。在开发模式下，合约代码不再运行于Docker容器中，而是直接在本地部署、运行、调试，极大地简化了开发过程。

合约代码是Fabric开发中最主要的部分之一，通过合约代码可以实现对账本和交易等实体的交互与操作，同时实现各种业务逻辑。目前，合约代码支持使用Go、Java和Node.js语言进行编写，通过实现合约代码接口的方式来编写合约代码程序。下面以Go语言为例进行介绍。

合约代码的结构主要包括以下3个方面。

1.  在Fabric v1.4版本中，合约代码接口包含两个方法：`Init()`方法和`Invoke()`方法。`Init()`方法会在第一次部署合约代码时进行调用，有点类似于类中的构造方法。就如同其方法名所表达的，`Init()`方法中一般执行一些合约代码需要的初始化操作。`Invoke()`方法则是在调用合约代码方法进行一些实际操作时调用，每次调用会被视为一次交易执行。详细的交易流程将在4.6节进行介绍。Go语言中的合约代码接口代码如下所示：
    

    Type Chaincode interface {
        // 初始化工作，一般情况下仅被调用一次
        Init(stub ChaincodeStubInterface) pb.Response
        // 查询或更新world state，可被多次调用
        Invoke(stub ChaincodeStubInterface) pb.Response
    }
    

当合约的`Init`或者`Invoke`接口被调用时，Fabric传递给合约`shim.ChaincodeStubInterface`参数并返回`pb.Response`结果，这些参数可以通过调用API方法去操作账本服务，产生交易信息或者调用其他的合约代码。

目前API方法定义在/core/chaincode目录下的shim包中，并且可以由以下命令生成：

    godoc github.com/hyperledger/fabric/core/chaincode/shim
    

主要的API方法可以分为6类，分别是State读写操作、Args读写操作、Transaction读写操作、PrivateData读写操作、合约代码相互调用以及Event设置。表4.2展示了这些方法及其对应的功能。

**API方法及功能**

![](https://storage.googleapis.com/papyrus_images/17b5dd8422143463344969da2421fae8fb48425eb8a1c90caca6fe790b886220.png)

![](https://storage.googleapis.com/papyrus_images/79da22c872f0f62778d3018b3c2256f7b74107cc20daf516cfe525654974736f.png)

![](https://storage.googleapis.com/papyrus_images/c3a7019d9d99f1860fdeb9b893c01c02928ff6302d0f4cee0e58c412c716a80d.png)

合约代码是以`protobuffer`的形式返回的，定义如下所示

    message Response {
    // 状态码
        int32 status = 1;
        // 响应码信息
        string message = 2;
        // 响应内容
        bytes payload = 3;
    }
    

合约代码还会返回事件信息，包括Message events和Chaincode events，定义如下所示：

    messageEvent {
        oneof Event {
            Register register = 1;
            Block block = 2;
            ChaincodeEvent chaincodeEvent = 3;
            Rejection rejection = 4;
            Unregister unregister = 5;
        }
    }
    messageChaincodeEvent {
        string chaincodeID = 1;
        string txID = 2;
        string eventName = 3;
        bytes payload = 4;
    }
    

一旦完成了合约代码的开发，有两种方式可以与合约代码交互：通过SDK或者通过CLI命令行。通过CLI命令行的交互将在进行介绍，SDK的交互可以参考

编写完合约代码之后，就要了解如何部署合约代码以及如何调用合约代码。要想进行部署合约代码等相关操作，必然需要启动Fabric系统。Fabric提供了CLI接口，支持以命令行的形式完成与Peer节点相关的操作。通过CLI接口，Fabric支持Peer节点的启动停止操作、合约代码的各种相关操作以及通道的相关操作。

当前Fabric所支持的CLI命令如表4.3所示

![](https://storage.googleapis.com/papyrus_images/ce7c84319d219cb3cd73d94983a0af7cbb7c84788e981643add98a444e923e10.png)

![](https://storage.googleapis.com/papyrus_images/19d26eacf0d071582193f9602fb72bf4f6e1a36d539fe6b32b570ba98f4e70fb.png)

其中`logging getlevel`、`logging setlevel`及`logging revertlevels`不推荐使用，将在后续的版本中删除。

同时通过以下命令，可查看更多与`peer`命令相关的信息。

    # 此命令需要
    cd /opt/gopath/src/github.com/hyperledger/fabric
    build /bin/peer
    
    # 或者进入启动网络后进入cli容器
    docker exec -it cli bash
    # 进入cli容器后运行peer命令
    peer
    

在运行以上命令之后，将看到如图

![](https://storage.googleapis.com/papyrus_images/34dbe38cf2b27d4c9407feca204a599db932f7aec665e60a165b80067f0e7762.png)

合约代码执行过程如图4.12所示，具体介绍如下。

*   客户端（SDK/CLI）创建交易提案，包含合约代码函数和调用参数，并以proto消息格式发送到背书节点。
    
*   背书节点调用shim包的方法创建合约代码仿真交易执行内容。
    
*   背书节点初始化合约、调用参数，基于读取和写入的key生成读写操作集。
    
*   背书集群节点模拟提案执行：执行读操作，向账本发送查询状态数据库的请求；模拟写操作，获取key的value值版本号，模拟更新状态数据。
    
*   若返回执行成功，则执行背书操作；若返回失败，则推送错误码500。
    
*   背书节点对交易结果执行签名，将提案结果返回给客户端（SDK/CLI），提案结果包括执行返回值、交易结果、背书节点的签名和背书结果（同意或拒绝）
    

![](https://storage.googleapis.com/papyrus_images/f31591bade7abba14b45a1babdc95ea09fb4049eca4a25ecb0109f8d0489f46f.png)

合约代码编写的内容，可以查看项目/examples/chaincode/下的示例合约代码了解更多。

Hyperledger Fabric官方文档中的架构，分析Fabric中的交易背书过程，首先介绍了Fabric交易背书过程的机制，然后通过一个简单的案例描述了其通用流程，之后详细分析背书过程，最后简单地介绍了Fabirc的背书策略以及验证账本和PeerLedger检查点的使用。

在Fabric系统中，交易就是一次合约代码的调用，可能有如下两种类型。

*   部署交易：部署交易使用一个程序作为参数创建新的合约代码，成功执行部署交易后，合约代码被安装到区块链上。
    
*   调用交易：调用交易在先前部署的交易上下文中执行合约代码以及它所提供的功能，当成功执行调用交易时，合约代码执行指定的函数，可能修改相应账本的状态，并返回输出。
    

区块链中执行的交易会打包成区块，区块连接起来就形成了共享账本中的哈希链。本节将介绍Fabric系统中一次交易的执行流程。为了更好地理解Fabric系统的交易背书过程，本节将先使用一个简单的图示案例来展示一次成功的交易执行过程。

首先在这个图示案例中，需要做一些假设，也就是真实开发时需要进行的配置工作。假设如下。

*   节点类型：E0、E1、E2、E3、E4、E5均为Peer节点，其中特殊的是，E0、E1、E2为此次交易的背书节点，Ordering Service为共识服务节点组成的共识服务。
    
*   通道配置：本案例中存在两个通道，其中E0、E1、E2、E3均连接在同一个通道Channel1中，而E4和E5位于另一个通道Channel2中。
    
*   背书策略：E0、E1必须签名背书，E2、E3、E4、E5则不属于策略。
    

做好假设之后，开始案例流程，如图

![](https://storage.googleapis.com/papyrus_images/b108a80a4b54e95b4b4e2fd8c73fb9a50ec5910f249ce1495a87a69ffe7061a2.png)

(1) 客户端应用通过SDK发送出一个交易提案（transaction propose）给背书节点E0。它用来接收智能合约中相关功能函数的请求，然后更新账本数据（即资产的键/值）。同时在发送前客户端会将这一交易提案打包为一种可识别的格式（如gRPC上的protocol buffer），并使用用户的加密凭证为该交易提案签名。

(2) 背书节点E0收到客户端发送的交易提案之后，将先验证客户端签名是否正确，然后将交易提案的参数作为输入模拟执行，执行操作会生成包含执行返回值、读操作集合和写操作集合的交易结果（此时不会更新账本），再对这个交易提案进行背书操作，附上anchor信息发送回客户端。若这个交易提案中的数据带有私有数据，那么背书节点E0会先将私有数据存储到本地的临时数据库中，再通过Gossip协议向其他授权对等节点传播，只有传播到一定数量，背书节点才能向客户端返回交易结果，交易结果中不携带私有数据，只是私有数据键值对的哈希值。

(3) 客户端想要进一步得到E1的认可，因此需要发送交易提议给E1，并且此时可以决定是否附上从E0处得到的anchor信息。

(4) 背书节点E1与先前E0的方式一样，验证客户端签名，验证之后模拟执行，再将验证后的Transaction-valid信息发送回客户端。

(5) 客户端会一直等待，直到收集到了足够的背书信息之后，将交易提案和结果以广播的形式传给共识服务。交易中包括readset、背书节点的签名、通道ID以及私有数据的哈希值。共识服务并不会读取交易的详细信息，而是对接收到的交易信息按通道分类进行排序，打包生成区块，因此共识服务无法看到私有数据。

(6) 共识服务会会将达成一致的交易打包进区块并传送给连接在这一通道上的所有节点，E4和E5接收不到任何消息，因为它们没有连接在当前交易的通道上。

(7) 各节点验证收到的区块，验证是否满足背书策略以及验证账本上的状态值是否改变来判断交易是否有效。验证成功之后更新账本和世界状态，然后节点会通过事件机制通知客户端交易是否已被加入区块链和交易是否有效。若区块中含有私有数据的哈希值，在验证成功之后将临时数据库中的私有数据存入私有数据库。

Fabric中，交易是就指是一次合约代码调用，下面将详细分析一次交易背书的过程。

1.  **客户端发送交易提议给指定背书节点**
    
    为了调用一个交易，客户端会向它所选择的一组背书节点发送一个PROPOSE消息（这些消息可能不是同时发送的，比如上一节的例子）。对于如何选择背书节点集合，`client`可以通过Peer使用给定`chaincodeID`的背书节点集合，反过来也可以通过背书策略获取背书节点集合。例如，这个交易会被客户端通过`chaincodeID`发送给所有相关的背书节点。除此之外，某些背书节点存在离线或反对的可能，所以存在不签署该交易的背书节点。提交客户端会通过有效的背书节点来尽力满足背书策略。
    
    本节将首先对PROPOSE消息的格式进行介绍，然后介绍提交客户端和背书节点间可能的交互模式。
    

(1) PROPOSE消息格式

一条PROPOSE的格式为`PROPOSE = <PROPOSE, tx, [anchor]>`，包含两个参数，`tx`交易消息字段是必需的，`anchor`是可选的参数。下面对这两个参数进行详细分析。

`tx`参数包含了与交易相关的各种信息，字段格式如下。

    tx=<clientID, chaincodeID, txPayload, timestamp, clientSig>
    

*   `clientID`：提交客户端ID
    
*   `chaincodeID`：调用合约代码ID
    
*   `txPayload`：包含交易信息的载体
    
*   `timestamp`：时间戳
    
*   `clientSig`：客户端签名
    

而对于`txPayload`字段，调用交易和部署交易的详细信息会有些不同。

如果当前交易是调用交易，`txPayload`只包含2个字段。

    txPayload = <operation, metadata>
    

*   `operation`：指合约代码调用的函数和参数
    
*   `metadata`：指与此次调用相关的其他属性
    

如果当前交易是部署交易，`txPayload`还将还将包含一个`policies`字段。

    txPayload = <source, metadata, policies>
    

*   `source`：指合约代码的源代码
    
*   `metadata`：指与此次调用相关的其他属性
    
*   `policies`：指与合约代码相关的策略，例如背书策略
    

`anchor`参数中包含了`readset`（一个从原始账本中读取到的版本依赖键值对集合），也就是世界状态中的版本化依赖。如果客户端发送的PROPOSE消息中携带了`anchor`参数，那么背书节点还需要验证`anchor`参数中是否与本地匹配。

同时`tx`字段的加密哈希`tid`还会被所有节点用来作为交易的标识（`tid=HASH(tx)`），并且客户端会将它存储在内存中一直等待背书节点响应。

(2) 消息模式

因为客户端的消息是需要发送给一组背书节点的，所以对于它的发送顺序是可以由客户端控制的。例如，通常情况下，客户端会先发送给单个背书节点不携带`anchor`参数的PROPOSE消息，背书节点接收后，会处理消息并加上`anchor`参数返回给客户端。然后客户端将携带`anchor`参数的PROPOSE消息发送给剩下的其他背书节点。而另一种模式，客户端会直接将不携带`anchor`参数的PROPOSE消息发送给背书节点集合，等待它们的返回。客户端可以自由选择消息模式来进行与背书节点间的交互。  背书节点收到客户端的`<PROPOSE,tx,[anchor]>`消息之后，它会先验证客户端的签名，验证通过后就会模拟执行交易的内容。需要注意的是，如果客户端指定了`anchor`字段，那么需要验证本地KVS中相应键的值，只有与`anchor`参数中的一致时，背书节点才会模拟执行交易。

模拟执行将会通过调用chaincodeID对应的合约代码来试验性地执行`txPayload`中的操作，同时还会获取背书节点本地维护的世界状态的一个副本。在执行完成后，背书节点会更新`readset`和`writeset`（存储着状态更新）两个键值对的集合的信息，这个机制在DB数据库中也被称为MVCC+postimage信息。具体键值对操作如下。

给定背书节点执行交易之前的状态`s`，对于交易读取的每个键`k`，`(k,s(k).version)`将会被添加到`readset`中。对于交易修改的每个键`k`，`(k,v')`将会被添加到`writeset`中，其中`v'`是更新后的新值。另外，`v'`也可以是相对于之前值`(s(k).value)`的差值。

在模拟执行之后，Peer节点会根据所谓的背书逻辑来决定是否为这一交易进行背书，默认情况下，Peer节点会接收tran-proposal消息并简单地为其签名。然而背书逻辑可以被设置，例如，Peer节点会将`tx`作为输入，与遗留系统进行交互，来决定是否为这一交易背书。

如果决定为这一交易背书，它就会发送`<TRANSACTION-ENDORSED, tid, tran-proposal, epSig>`消息给客户端。

    tran-proposal := (epID,tid,chaincodeID,txContentBlob,readset,writeset)
    

*   `txContentBlob`：交易信息txPayload
    
*   `epSig`：背书节点的签名
    

如果拒绝为这一交易背书，它则会发送`(TRANSACTION-INVALID, tid, REJECTED)`消息给客户端。

需要注意的是，背书节点模拟执行不会更改任何的哈希链与世界状态信息，它只是模拟执行，然后将操作所引起的状态改变存储于`writeset`中。  

*   **客户端收集交易背书后并通过共识服务广播**
    
    在一定的时间间隔内，如果客户端收到了足够多的背书节点发回的背书消息（`TRANSACTIONENDORSED, tid, *, *`），则背书策略被满足，那么这笔交易就会被认为背书成功，需要注意的是此时还没有提交。否则，如果一定时间间隔内没有收到足够多的背书消息，那么客户端就会抛弃该笔交易或者稍后进行重试。
    
    对于有效的背书成功的交易，客户端会通过`broadcast(blob)`方法调用共识服务，其中`blob`指的就是背书消息。如果`client`没有直接调用共识服务的能力，它可以选择某个Peer节点代理调用，当然这个Peer节点必须是可信的，否则这个交易可能会被视为背书无效。
    
*   **共识服务传送区块给Peer节点**
    
    在共识服务对交易进行排序并达成区块之后，共识服务将会触发`deliver(seqno, prevhash, blob)`事件，然后将这一区块广播给所有链接在Fabric和同一通道上的Peer节点。
    
    Peer节点在收到共识服务广播的区块之后会进行两类校验。
    
    第一类是通过`(blob.tran-proposal.chaincodeID)`指向的合约代码所包含的背书策略来验证`blob.endorsement`是否有效；第二类则是在完成第一类验证之后，还将会验证`blob.endorsement.tran-proposal.readset`集合是否正确。
    
    针对`readset`集合的验证，根据一致性和隔离保证，可以采用不一样的方式。如果在合约代码中未指定相应的背书策略，那么可串行化（serializability）则是默认的验证方式。对于可串行化要求每个`readset`中的键的版本要与state中的版本对应，然后拒绝不满足条件的交易。
    
    假如上面的验证都通过了，这个交易就可以被视为有效或者已提交的了。在验证之后，Peer节点就会在`peerLedger`账本对应的位掩码中用1标记这笔交易，并将`writeset`中的更新应用到Fabric区块链的state世界状态中。而如果验证失败，那么这个交易就被认为是无效的，Peer节点则会在`peerledger`的位掩码中用0标记这笔交易，并且无效的交易不会引起任何改变更新。
    
    在共识服务的保证下，上述流程会保证所有正常的Peer节点在执行一个`deliver`事件之后拥有相同的世界状态。即所有正确的节点将会收到一个完全一样的`deliver`事件的序列。至此，本次交易流程结束。
    

Fabric所提供的背书策略机制是用于指定区块链节点交易验证的规则。每当背书节点收到交易请求的时候，系统就会通过VSCC（validation system Chaincode，系统合约代码验证）对交易的有效性进行验证。在交易流程中，一个交易可能会包含来自于背书节点的一个或多个背书，而VSCC机制将会根据以下规则决定交易的有效性。

*   背书的数量是否符合要求；
    
*   背书是否来自预期的来源；
    
*   所有来自背书节点的背书是否有效（即是否来自预期消息上的有效证书的有效签名）。
    

背书策略就是用来指定以上的背书数量要求和背书来源预期集合。每个背书策略由两个部分组成，原则（principal）和定限闸（threshold gate）。原则P用来识别预期签名的实体；定限闸T有两个输入参数，_t_ 表示背书数量，_n_表示背书节点列表，即满足_t_ 的条件，背书节点属于_n_。例如，`T(2, 'A', 'B', 'C')`表示需要获得2个以上来自于`'A', 'B', 'C'`的背书。`T(1, 'A', T(2, 'B', 'C'))`表示需要收到来自`'A'`的背书或者来自`'B'`和`'C'`的两个背书。

在CLI命令行交互中，背书策略的表示语法是`EXPR([E, E...])`，`EXPR`有两个选项`AND`或者`OR`，其中`AND`表示“与”，表示每个都需要，而`OR`则表示“或”。比如`AND('Org1.member', 'Org2.member', 'Org3.member')`表示请求3个组的签名，`OR('Org1.member', 'Org2.member')`表示请求两个组中任意一个的签名即可。而`OR('Org1.member', AND('Org2.member', 'Org3.member'))`表示有两种选择，第一种是请求组织1的签名，第二种是请求组织2和组织3的签名。

在使用CLI与区块链交互时，在命令后使用`-P`选项即可为执行的合约代码指定相应的背书策略，例如下面的合约代码部署命令：

    peer chaincode deploy -C testchainid -n mycc -p $ORDER_CA -c '{"Args":["init","a","100","b","200"]}' -P "AND('Org1.member', 'Org2.member')"
    

表示部署合约代码`mycc`需要请求组织1和组织2的签名。

Fabric未来会进一步增强和改进背书策略，除了目前通过与MSP的关系来识别原则，Fabric计划添加OU（organization unit）的形式来完成当前证书的功能，同时计划对背书策略的语法进行改进，使用更直观的语法验证账本是从仅包含有效且已提交事物的账本派生的哈希链。除了世界状态和账本之外，对等节点可以维护验证账本。

VLedger块（vBlock）是被过滤掉无效事务的块，具有动态的大小并且可以是空的。由于PeerLedger块可能包含无效事物（即具有无效认可或具有无效版本依赖性的事物），因此这些事物在被添加到vBlock之前，会被对等节点过滤掉。每个对等节点将vBlock链接到一个哈希链，每个vBlock包含前一个vBlock的哈希值、vBlock编号、由最后一个被估算的对等节点提交的所有有效事务的有序列表、从当前vBlock导出的相应块的哈希以及所有这些信息的哈希。

账本可能包含不一定永久记录的无效交易。然而，一旦对等节点与相应的vBlock建立连接，那么它们就不能简单地抛弃PeerLeger块以达到修剪PeerLedger的目的。为了便于修剪PeerLedger，v1.4版本的Hyperledger Fabric提供了一种检查点机制：使用横穿对等节点网络的vBlock，允许检查点的vBlock代替被丢弃的PeerLedger块。由于不需要存储无效事物且不需要在替换PeerLedger重构状态时建立个人交易的有效性，减少了存储空间，也减少了为新加入网络的对等节点重建状态的工作，但很可能只是替换了包含在验证账本中的状态更新。

对等节点周期性地执行检查点，其中`CHK`是可配置参数。对等节点以消息`<CHECKPOINT, blocknohash,blockno,stateHash,peerSig>`的形式广播到其他对等节点来启动一个检查点，其中`blocknohash`是各自的哈希，`blockno`是当前块序号，`stateHash`是`blockno`块验证上最新状态的哈希，`peerSig`是对等方的签名。`(CHECKPOINT,blocknohash,blockno,stateHash)`指的是经过验证的账本。

对等节点收集检查点消息，直到它获得足够多正确签名的消息（`blockno`，`blocknohash`和`stateHash`）来建立有效的检查点。如果`blockno`＞`latestValidCheckpoint.blockno`，那么对等节点应标记为`latestValidCheckpoint=(blocknohash,blockno)`，将构成有效检查点的相应对等节点签名集存储到集合`latestValidCheckpointProof`中，与`stateHash`对应的状态存储到`latestValidCheckpointedState`，修剪比块序号`blockno`大的PeerLedger。

检查点有效性策略不仅定义了对等节点何时可以修剪它的Peerledger，还定义了有多少`CHECKPOINT`消息算“足够多”。以下是两种可行的方法，也可以将它们组合使用。

第一种是本地检查点有效性策略（LCVP）：给定对等节点p的本地策略可以指定一组对等节点，这组对等节点是p信任的并且其`CHECKPOINT`消息足以建立有效的检查点。

第二种是全局检查点有效性政策（GCVP）：全局指定检查点有效性策略，这类似于本地对等节点策略，但它是在系统（区块链）粒度而不是对等节点粒度上规定的。

Hyperledger Fabric进行了深入解读，有助于读者深入理解Fabric的底层实现原理。首先，介绍了Hyperledger及其子项目的发展现状及管理模式，重点介绍了Hyperledger Fabric。其次，对Hyperledger Fabric架构进行了深入分析，从成员服务、区块链服务以及合约代码服务三个方面探讨Hyperledger Fabric的架构组成与特点，给出了Fabric架构设计和模块组件。再次，给出了合约代码的代码结构、调用方式和执行流程。最后，对交易背书流程展开了详细分析。

---

*Originally published on [leaf](https://paragraph.com/@leaf-6/hyperledger-fabric)*
