比特币被认为是区块链技术 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)阶段。每个项目在开发运行过程中,一个时间点只会对应着一个阶段。当然,项目不一定会按照以上阶段顺序发展,项目可能会一直处于某个阶段,也可能会因为一些特殊原因在多个阶段之间进行变换。所有的项目需要重视包括交易、合同、一致性、身份、存储的技术场景在内的模块化设计,还需实现代码可读性以保障新功能和模块都可以很容易添加和扩展,并且需要不断增加和演化新的项目来满足日益深入的商业化需求和逐渐丰富的应用场景。
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 进行操作管理。
展示了 Fabric 运行架构。v0.6 版本的结构非常简单,应用 - 成员管理 - Peer 呈现三角形关系,系统所有的业务功能均由 Peer 节点完成。但是 Peer 节点承担了太多的业务功能,暴露出了扩展性、可维护性、安全性、业务隔离等方面的诸多问题。因此,在 v1.4 版本中,官方对架构进行了改进和重构,将共识服务部分从 Peer 节点中完全分离出来,独立形成一个新的节点,提供共识服务和广播服务。v1.4 版本还引入了通道的概念,实现多通道结构和多链网络,带来了更为灵活的业务适应性。同时还支持更强的配置功能和策略管理功能,进一步增强系统的灵活性。
运行时架构(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 体系的相关内容及用户的注册过程。
-
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 直接或分层进行认证。
对图中的实体进行进一步介绍说明。
-
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 完成的,所以这个系统中的交易是没有匿名的。
前面介绍了成员服务的 PKI 体系的实体及其基本功能,接下来针对具体的用户注册流程做一个简单的介绍。图展示了一个用户登记流程的高层描述,它分为两个阶段:离线过程与在线过程。
用户注册过程
-
离线过程
(1) 每个用户或者 Peer 节点必须向 RA 注册机构提供身份证件(ID 证明),同时这个流程必须通过带外数据(out-of-band,OOB)进行传输,以提供 RA 为用户创建(和存储)账户所需的证据。
(2) RA 注册机构返回用户有关的用户名和密码,以及信任锚(包含 TLS-CA Cert)。如果用户可以访问本地客户端,那么客户端可以将 TLS-CA 证书作为信任锚的一种方式。
-
在线过程
(1) 用户连接客户端以请求登录系统,在这一过程中,用户将用户名和密码发送给客户端。
(2) 用户端代表用户向成员服务发送请求,成员服务接受请求。
(3) 成员服务将包含几个证书的包发送给客户端。
(4) 一旦客户端验证完成所有的加密材料是正确有效的,它就会将证书存储于本地数据库中并通知用户,至此,用户注册完成。
区块链服务包含 4 个模块:共识管理、分布式账本、账本存储以及 P2P 网络协议。共识管理用于在多个节点的分布式复杂网络中使消息达成共识,分布式账本与账本存储负责区块链系统中所有的数据存储,比如交易信息、世界状态、私有数据等。而 P2P 网络协议则是网络中节点的通信方式,负责 Fabric 中各节点间的通信与交互。
-
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 节点所构成的组一起形成新的完成网络。
在 v1.0 之后的版本中,Fabric 引入了新的通道概念,共识服务上的消息传递支持多通道,使得 Peer 节点可以基于应用访问控制策略来订阅任意数量的通道。Peer 节点的子集可以被应用程序指定架设相关通道,指定相同通道的 Peer 节点组成集合,提交该通道的交易,而且只有这些 Peer 节点可以接收相关交易区块,与其他交易完全隔离。Fabric 支持多链与多通道,即系统中可以存在多个通道以及多条链,如图所示。应用根据业务逻辑决定将每个交易发送到指定的一个或多个通道,不同通道上的交易不会存在任何联系。
-
从 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≥2f+1。Raft 遵循 “领导者和追随者” 模型,每个通道都选举一个 “领导者”,它的决定将被复制给 “追随者”,支持 CFT。只要总节点数与失效节点数满足n≥2f+1,它就允许包括领导者在内的部分节点宕机失效。与基于 Kafka 的共识服务相比,Raft 应该更容易设置和管理,并且它们的设计允许组织为分散的共识服务贡献节点。
-
区块链技术从其底层构造上分析,可以视为一种共享账本技术。账本是区块链的核心组成部分,在区块链的账本中存储了所有的历史交易和状态改变记录。在 Fabric 中,每个通道都对应着一个共享账本,而每个连接在共享账本上的 Peer 节点,都能参与网络和查看账本信息,即它允许网络中的所有节点参与和查看账本信息。账本上的信息是公开共享的,并且在每个 Peer 节点上都维持着一份账本的副本。图 4.9 展示了 Fabric 账本的结构。
共享账本结构
从图中可以看出,共享账本以文件系统的形式存储于本地。共享账本由两部分组成:图中链式结构的 Chain 部分和图中右边存储状态数据的 State 部分。其中,Chain 部分存储着所有交易的信息,只可添加查询,不可删改。State 部分存储着交易日志中所有变量的最新值,因为它表示的是通道中所有变量键值对的最新值,所以有时称为 “世界状态”。
合约代码调用执行交易来更改目前的状态数据,为了使这些合约代码高效交互,设计将最新的键值对数据存储于状态数据库中。默认的状态数据库采用的是 Level DB,但是可以通过配置切换到 Couch DB 或者其他数据库。
合约代码服务提供了一种安全且轻量级的方式,沙箱验证节点上的合约代码执行,提供安全容器服务以及安全的合约代码注册服务。其运行环境是一个 “锁定” 和安全的容器,合约代码首先会被编译成一个独立的应用程序,运行于隔离的 Docker 容器中。在合约代码部署时,将会自动生成一组带有签名的智能合约的 Docker 基础镜像。在 Docker 容器中,合约代码与 Peer 节点的交互过程如图所示。
步骤如下。
(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 个方面。
- 在 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 方法及功能
合约代码是以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 所示
其中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
在运行以上命令之后,将看到如图
合约代码执行过程如图 4.12 所示,具体介绍如下。
-
客户端(SDK/CLI)创建交易提案,包含合约代码函数和调用参数,并以 proto 消息格式发送到背书节点。
-
背书节点调用 shim 包的方法创建合约代码仿真交易执行内容。
-
背书节点初始化合约、调用参数,基于读取和写入的 key 生成读写操作集。
-
背书集群节点模拟提案执行:执行读操作,向账本发送查询状态数据库的请求;模拟写操作,获取 key 的 value 值版本号,模拟更新状态数据。
-
若返回执行成功,则执行背书操作;若返回失败,则推送错误码 500。
-
背书节点对交易结果执行签名,将提案结果返回给客户端(SDK/CLI),提案结果包括执行返回值、交易结果、背书节点的签名和背书结果(同意或拒绝)
合约代码编写的内容,可以查看项目 /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 则不属于策略。
做好假设之后,开始案例流程,如图
(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 中,交易是就指是一次合约代码调用,下面将详细分析一次交易背书的过程。
-
客户端发送交易提议给指定背书节点
为了调用一个交易,客户端会向它所选择的一组背书节点发送一个 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 架构设计和模块组件。再次,给出了合约代码的代码结构、调用方式和执行流程。最后,对交易背书流程展开了详细分析。