fabric整理

论文的翻译

Posted by BY on April 15, 2018

Introduction

区块链可以被定义为用于记录交易的不可变账本,维护在相互不信任的节点的分布式网络中。每个节点都维护着账本的副本。节点执行共识协议来验证交易,将它们分组进区块,并根据区块信息生成哈希链。此过程通过对交易进行排序来形成账本,这对于保证连续性和一致性是十分必要的。区块链技术已经从比特币的名号中脱颖而出,并被广泛认为是在数字世界中进行可信交易的有前途的技术。

在一个公有链中,任何人都可以在没有特定身份的情况下参与。公有链常常需要原生的加密货币,以及工作量证明作为共识算法和产生经济利益。另一方面,私有链在一系列已知身份的节点中运行区块链。它可以通过在一组毋须信任对方但有相同目标的的实体中保证安全性,就像在商业中进行基金、信息和商品的交换(同样为了追求利益,而不需要信任对方)。由于节点身份已知,私有链可以使用传统的拜占庭容错(BFT)作为共识算法。

区块链可以以智能合约的形式执行任意的可编程交易逻辑,如以太坊。比特币中的脚本是合约的前身。智能合约的作用是作为一个可信分布式应用,从区块链和节点之间的基本共识中获得安全性。然而,使用拜占庭容错算法的区块链技术与传统的状态机副本区别如下:

  1. 多个而不是只有一个分布式应用在并行运行;
  2. 应用程序可以由任何人动态部署;
  3. 应用程序代码是不受信任的,甚至可能是恶意的。这些特性需要一个新的设计来实现。

目前实现智能合约的区块链很多是基于状态机复制的蓝图,实现了所谓的主动复制(active replication):

共识协议或原子广播首先对交易进行排列并将它们传播给所有节点;第二,每个节点顺序执行交易。我们称之为排序-执行架构;它要求所有节点执行每个交易,并且所有交易都是确定性的。

几乎所有现有的区块链系统都可以找到排序执行架构,无论是公有链,如以太坊(基于PoW共识)或者是使用拜占庭容错算法的私有链,如TendermintChainQuorum。尽管排序-执行设计并非在所有系统中,因为额外的交易可能会扰乱它,这种设计的局限性是它与生俱来的:每个Peer都要执行所有交易,并且交易必须是确定性的。

早先的私有链受到许多限制,这些限制来源于相似的公有链或是排序-执行架构。尤其是以下部分:

  • 共识在平台内是硬编码(hard-coded )的,这和之前所说的没有一种万能的(BFT)共识算法相矛盾。

  • 交易验证的信任模型由共识协议所决定,不能适应智能合约的一些要求。

  • 智能合约必须以固定,非标准的,特定领域的语言编写,这阻碍了它被进一步广泛的应用,并且可能导致一些编程错误。

  • 所有节点都对交易进行排序-执行过程会限制性能,并且还需要一些复杂的方法去防止对平台发起的拒绝攻击,这种攻击源自不可信的智能合约(例如在以太坊中考虑运行时”gas”)。

  • 交易必须是确定性的,这难以以编程层面的方式去保证。

  • 每个智能合约都在所有节点上运行,这和它的保密特性相矛盾,并且禁止仅向一部分节点发送合约代码与状态。

在本文中,我们描述了Hyperledger Fabric,一个克服了这些限制的开源区块链平台。 FabricHyperledger的项目之一,由Linux基金会赞助,同时已经在超过400种不同行业、不同用途的原型、概念或是分布式账本系统的开发中被使用。这些使用案例包括但不限于争议解决,物流贸易,外汇网络,食品安全,合同管理,珠宝鉴定,奖励积分管理,低流动性证券的交易和结算,身份管理以及通过数字货币结算等领域。

Fabric使用了新的区块链架构,具有弹性,灵活性,可扩展性和保密性。作为一种模块化可扩展的通用私有链,Fabric支持标准通用编程语言编写的分布式应用。这使得Fabric成为私有链的第一个分布式操作系统。

为了在不可信的环境中分布式执行不可信的代码,Fabric遵循一种新的排序-执行-验证(execute-order-validate)的范式。它将交易流程分为三个步骤:

  1. 执行交易并检查其正确性,从而背书它(这与其他区块链中的验证交易环节相对应)。
  2. 通过共识协议进行排序,而不考虑交易的内容。
  3. 根据特定背书策略进行交易的验证(这解决了由于并行产生的竞争问题),将相应区块写入区块链。
  • 首先,Fabric使用被动复制(passive replication)或主服务器备份,这是分布式数据库中很常见的手段,但Fabric通过基于中间件的非对称更新过程将其移植到存在拜占庭错误的不可信环境中。在Fabric中,依靠“执行-验证”拜占庭容错算法的副本,每个交易只会在一部分节点中执行(背书),这考虑到了并行执行的情况并解决了潜在的不确定性。灵活的背书策略将指定哪些或多少节点需要对这个智能合约的正确执行进行担保。

  • 其次,Fabric还结合了主动复制(active replication),在验证阶段,交易对账本状态的改变,只有在按照它们的全序达成一致后才被写入。Fabric根据交易背书来判断是否满足指定应用的背书策略。此外,为了达成一致性,状态更新的顺序被委派给了用于共识的模块化的组件,比如原子广播(一个无状态的组件,并且与那些执行交易和维持账本的节点逻辑上是解耦的)。由于共识算法是模块化的,因此可以根据特定的背书策略去自定义共识算法。虽然也可以直接通过区块链的节点去实现共识算法,但分开这两个角色显然可以更好地增加灵活性,并使得它可以直接使用非拜占庭容错CFT (Crash fault-tolerant,崩溃容错) 或拜占庭容错BFT (Byzantine fault-tolerant) 的工具包进行排序。

  • 综上,这个在拜占庭错误模型下结合了主动复制和被动复制的混合副本设计,以及执行-排序-验证的范式,就是Fabric架构的主要创新点。它们解决了前面提到的问题,并使得Fabric成为一个在私有链中支持灵活的可信假设的可扩展系统。

为实现此体系结构,Fabric包含了以下模块化组件:

  1. 排序服务以原子方式向节点原子广播状态的更新,并就交易顺序建立共识。

  2. 成员服务提供商通过将节点与加密身份相关联,使得Fabric拥有了许可性质。

  3. 可选的节点到节点的gossip服务通过排序服务向所有节点来传播区块的输出。

  4. Fabric中的智能合约在完全隔离的容器环境中执行。它们可以用通用编程语言编写,同时不与账本状态直接产生联系。

  5. 每个节点在本地以键值对的储存形式(KVS)用可追加的区块链来保存账本,并将它作为最新状态的快照。

Background

Order-Execute Architecture for Blockchains

所有以前的区块链系统,无论是否允许,都遵循排序-执行架构。这意味着区块链网络首先使用共识协议对交易进行排序,然后在所有节点上按照这个排序线性执行交易。

例如,基于PoW的公有链(如以太坊)将交易的共识和执行结合起来如下:

  1. 每个节点(即参与共识的节点)收集一个含有有效交易的区块(为了建立有效性,此节点已经预先执行过这些交易)。
  2. 节点尝试去解决一个数学难题完成工作量证明
  3. 如果节点解决了这个难题,它会通过gossip协议将该区块传播到网络
  4. 每个节点接收区块并验证难题的解决方案以及该区块中的所有交易是否合法。实际上,每个节点从而从第一步开始重复执行幸运节点的执行内容。此外,所有节点顺序地执行交易(在一个区块内以及区块间)。这种排序-执行架构如下图所示。

1

现有私有链(如TendermintChainQuorum)通常使用了拜占庭容错的一致性,这个一致性是由实用拜占庭容错算法(PBFT)或其他的实现原子广播的协议。 然而,它们都遵循相同的顺序-执行方法并实现了传统的主动状态机副本的架构。

Limitations of Order-Execute

排序-执行架构在概念上很简单,因此被广泛使用。但是,当它用于通用私有链时存在几个缺点。接下来我们将讨论其中最重要的三点。

  • 顺序执行。在所有节点上顺序执行交易限制了区块链可以达到的有效吞吐量。特别是由于吞吐量与执行操作的延迟成反比,这可能成为除最简单的智能合约之外所有的性能瓶颈。此外,与传统的状态机副本(SMR)相比,区块链实现了一个通用计算引擎,但其载荷应用可能会被对手恶意部署。拒绝服务(DoS)攻击严重降低了此类区块链的性能,可能会导致需要很长时间才能执行的智能合约。例如,执行无限循环的智能合约可以有这样致命的效果,并且由于停机问题不可解,使得错误很难被自动识别到。

为了解决这个问题,带有加密货币的公有可编程区块链可以解决执行成本问题。例如,以太坊引入了交易执行所消耗的gas的概念,该交易执行以gas价格转换为加密货币的成本并计入交易的提交者。以太坊使用了一个很长的环节去完成这个过程,首先为每一个底层计算步骤分配费用,然后使用自己的虚拟机监视器去控制交易的执行。尽管这看起来是为公有链提供了一个可选的解决方案,但在没有原生加密货币的通用系统的准入模型中,这是不够的。

与顺序执行相比,分布式系统文献提出了许多改进性能的方法,例如通过并行执行不相关的操作。不幸的是,这些技术仍然可以成功应用于智能合约的区块链环境中。例如,一个挑战是确定性地推断智能合约中所有依赖关系的要求,这在与可能的保密性约束相结合时尤其具有挑战性。此外,这些技术对来自不受信任的开发人员的合同代码的DoS攻击毫无帮助。

  • 非确定性代码。排序-执行架构的另一个重要问题是非确定性交易。在主动状态机副本(SMR)中达成共识后执行的操作必须是确定性的,否则分布式账本会产生”分叉”,这违反区块链的基本前提,即所有节点都保持相同的状态。通常是通过使用领域特定语言(比如以太坊的Solidity)编写合约来解决,这些语言足够编写这些区块链的应用但对于确定性合约的执行却增加了限制。然而,这些语言难以为实现者设计,并且需要程序员进行额外的学习。用通用语言(例如GoJavaC / C++)编写智能合约似乎更具吸引力,并加速了区块链解决方案的落地。

不幸的是,通用编程语言在确保确定性执行方面存在许多问题。即使应用程序开发人员没有显式引入的非确定性操作,一些隐式实现的细节可能也会导致相同的破坏性的效应(例如,映射迭代器在Go中是非确定性的)。更糟糕的是,在区块链上,创建确定性应用程序的负担取决于潜在的不可信的程序员。仅仅一个非确定性的被恶意创建的合约已经足以将整个区块链停机。模块化的过滤这种破坏性操作的解决方案已经被研究出来,但很显然这在实际应用中开销太大。

  • 执行保密。 根据公有链的蓝图,区块链需要在所有节点上运行所有智能合约。然而,许多需要准入机制的用途都要求保密性,即,对智能合约、交易信息或者账本状态的访问应当被限制。尽管从数据加密到高级零知识证明和可验证计算的加密技术可以帮助实现机密性,但这通常带来相当大的开销并且在实践中不可行。

幸运的是,Fabric能满足把相同的状态散布到所有的节点,而不直接把相同的代码到处运行。因此,智能合约的执行可以被限制在对于这个任务来说可信的节点集中。这个设计与主动复制不同,它通过各式被动复制适应了区块链的信任模型。

Further Limitations of Existing Architectures

  • 固定信任模型。大多数私有链依赖于异步BFT副本协议来建立共识。这样的协议通常依赖于安全性假设,即在n> 3f个节点中,高达f被容忍为行为不端并且表现出所谓的拜占庭故障。在相同的安全性假设下,相同的节点也经常执行应用程序(即使实际上可以将BFT执行限制为更少的节点)。然而,这样一个基于恶意节点数量,而且不考虑节点在系统中扮演的角色的信任假设,可能不会匹配智能合约执行时所要求的信任模型。在灵活的系统中,应用程序级别的信任不应固定为协议级别的信任。通用区块链应该将这两个假设分离,并且允许在应用上使用更灵活的信任模型。

  • 硬编码的共识。Fabric是第一个引入可插拔共识的区块链系统。在Fabric之前,几乎所有区域链系统,无论是否拥有准入机制,都使用了硬编码的共识协议。然而,多年来对共识协议的研究表明,没有一种万能的算法可以适应所有场景。例如,当BFT算法被部署在一个潜在的对抗环境中时,性能可能会有相当大的差别。具有“链”通信模式的协议在具有对称和同构链路的LAN集群上表现出可证明的最佳吞吐量,但这在其他的应用场景或非同质网络中可能会迅速降低,比如:广域,异构。此外,在给定部署中,一些外部因素,比如负载、网络参数、故障或者攻击随着时间变化可能会是多种多样的。出于这些原因,BFT算法应当天生是可重构的,理想情况下,甚至可以动态适应变化的环境。另一个重要方面是将协议的信任假设与给定的区块链部署方案相匹配。实际上,人们希望用基于替代信任模型(例如XFT)或CFT协议(例如Paxos/RaftZooKeeper)或甚至无权协议的协议替换BFT共识。

Experience with Order-Execute Blockchain

在实现Fabric的执行-排序-验证体系结构之前,我们获得了在排序-执行模型中使用PBFT共识算法的私有链平台中获取了一些经验。也就是说,早期版本的Fabric(2016年9月发布的v0.6)已经按照“传统”排序-执行架构进行了架构设计。

从许多概念的应用中获得的反馈来看,这种方式的局限性是十分明显的。例如,用户经常在节点观察到不同的状态然后反馈共识协议中的错误;在所有情况下,深入的检测显示罪魁祸首都是那些非确定性的交易代码。其他的一些负面评论则是关于性能,例如,”一秒怎么只有五个交易被执行”,直到用户发现他们每一个交易平均花费200毫秒去执行。我们已经了解到区块链系统的关键属性,即一致性,安全性和性能,而这些不能依赖于其用户的知识和善意,尤其是因为区块链应该在不受信任的环境中运行。

Architecture

Fabric overview

Fabric是一个在私有链上执行使用通用编程语言(例如GoJavaNode.js)编写的分布式应用的分布式操作系统。它没有内置的加密货币,并能通过一个可追加的账本数据副本来安全地追踪交易记录。

Fabric引入了执行-排序-验证的区块链体系结构(如下图所示),并不遵循标准的排序执行设计。在一个极小的容器中,Fabric的分布式应用程序由两部分组成:

一种名为chaincode的智能合约,它是实现应用程序逻辑并在执行阶段运行的程序代码。 chaincode是Fabric中分布式应用程序的核心部分,它可以被不可信的开发者编写。存在用于管理区块链系统和维护参数的特殊链码,统称为系统chaincode。

2

在验证阶段评估的背书策略。背书策略不能被那些不可信应用的开发者选择或是修改,这是系统的一部分。背书策略充当Fabric中交易验证的静态库,它只能被chaincode进行参数化。只有指定的管理员才有权通过系统管理功能修改背书策略。常见的背书策略允许chaincode以节点集的形式指定一些为这笔交易背书的节点,这是对于背书所必需的; 它在集合上使用单调逻辑表达式,例如”五个中的三个”或”A和B 或者 B和C”。自定义背书策略可以实现任意逻辑(例如,使用比特币类的加密货币)。

客户端根据背书策略将交易发送到指定的节点。然后交易被这些节点执行并记录结果,这个步骤就称作背书。执行后,交易将进入排序阶段,该阶段使用可插拔的共识协议生成在区块中产生一个所有已验证交易的全序。然后通过gossip通信组件(可选)的帮助,广播给所有节点。与标准的主动副本把全部的交易输入都排序不同,Fabric将交易输出和执行阶段产生的状态结合并排序。然后,每个节点将根据背书策略和验证阶段产生的共识,来逐个验证那些经过背书的交易所产生的状态变化。所有节点将按照同样的顺序去验证交易,验证的结果是确定性的。从这个意义上讲,Fabric在拜占庭模型中引入了一种新颖的混合副本范式,它结合了被动复制(状态更新的预共识计算)和主动复制(执行结果和状态变化的后共识验证)。

Fabric区块链由一组形成网络的节点组成(参见下图)。由于Fabric拥有准入机制,参与网络的所有节点都具有身份,这个身份是由模块化成员服务提供商(MSP)提供的。Fabric网络中的节点有以下三种身份中的一种:

3

  • 客户端提交交易提案以执行,帮助协调执行阶段,最后,广播这些交易以进行排序。

  • 节点执行交易提案并验证交易。同时,所有节点都维护区块链账本(一种可追加数据结构,以哈希链的形式记录所有交易,以及状态,即最新账本状态的简洁表示)。并不是所有节点都执行全部的交易,只有一部分被这笔交易所属的背书策略选定的背书节点会这样做。但是,全部节点随后必须完成账本的更新。

  • 排序服务节点(OSN)(或简称为排序者)是共同形成排序服务的节点。简而言之,Fabric的排序服务建立起所有交易的总顺序,其中每笔交易包含状态的更新和执行过程中计算得到的一些依赖,还包含那些用于计算的背书节点的加密签名。排序服务节点完全不知道应用的状态,也不参与进交易执行或是交易验证。 这样设计是尽可能的将共识算法模块化并简化Fabric中替换共识算法的流程。

Fabric网络实际上可以支持多条区块链连接到同一个排序服务上。每个这样的区块链被称为一个通道(channel),可能含有不同身份的成员节点。通道可用于划分区块链网络的状态,但是通道间的共识算法并不会做出相应调整,并且不同通道的交易全序也是不同的。对于特定部署而言,假如所有的排序节点都是可信任的,可以实现通过通道来访问控制节点。下文中我们提到通道时,只会简略地专注于单通道的情况。

Execution Phase

在执行阶段,客户向一个或多个背书节点发送交易提案。回想一下,每个chaincode都通过背书策略隐式地指定了一组背书节点。提案包含提交客户端的身份(根据MSP获取),执行操作形式的交易负载、参数、chaincode所有者的标识符、每个客户端仅使用一次的随机数(例如一个计数器或是一个随机数),以及从客户端标识符和随机数派生的交易标识符。下图是Fabric高级交易流程:

4

背书节点通过在区块链上预安装的特定链码上执行操作来模拟交易提案。chaincode通过在Docker容器中运行,与背书进程相隔离。

模拟交易提案是为了在不与其他任何节点同步的情况下改变背书节点的本地区块链状态。此外,背书节点并不保存模拟交易后账本状态的改变。区块链的状态是以版本化控制的键值对形式储存在节点交易管理员(PTM)中的,这里会以单调递增的版本号来记录数据的一次次更新。由chaincode创建的状态仅限于该chaincode,并且不能由另一个chaincode直接访问。请注意,chaincode不应该在程序代码中储存本地状态,只储存它可以被GetStatePutStateDelState这几个操作访问的区块链状态。在给定一定权限的情况下,chaincode可以调用另一个chaincode来访问同一通道内的状态。

模拟交易会产生这样的结果:每个背书节点产生一个值的写入集(writeset),包括由模拟产生的状态更新(比如修改过的key和他们新的value),还会产生一个读取集(readset),代表了交易提案模拟时的版本信息(比如所有的key在模拟中会通过他们的版本号读取数据)。在模拟之后,背书节点会加密生成一个叫做”背书”的信息,其中包含读取集和写入集(以及诸如交易ID背书节点ID背书者签名之类的元数据),并把它们作为交易提案的回应发送回客户端。客户会收集”背书”,直到他们满足交易所调用chaincode的背书策略的要求。特别是,这就要求背书策略选取的所有节点对这笔交易必须产生相同的结果(例如相同的读取集和写入集)。然后,客户端创建这笔交易并将其传递给排序服务。

  • 关于设计选择的讨论:由于背书节点模拟交易提案时不与其他节点进行同步,两个背书节点可能会在不同账本状态下模拟交易然后产生了不同的结果。对于要求多个背书节点产生相同结果的标准背书策略而言,这意味着在对相同属性进行高竞争性访问操作时,客户可能无法满足背书策略的要求。与通过中间件来同步副本数据库中的主备份方式相比,这是一个新的考虑因素。根据这个假设自然得到这样的推论:区块链中交易的正确执行,不可信任任何单一节点。

我们有意识地采用了这种设计,因为它相当大程度地简化了架构,非常适用于传统的区块链应用。比特币的实验表明,分布式应用可以被公式化,因此对同一个属性的操作竞争可以被减少甚至完全避免(例如,在比特币中,对同一个物品进行的2个修改操作是被禁止的,这叫做双花攻击)。

在排序阶段之前执行交易,这对于允许第二节中所讨论的非确定性链码是至关重要的。 Fabric中存在非确定性交易的链码只能对它自己产生威胁,因为这个客户节点也许不能收集到足够数量的“背书”。由于顺序-执行架构中非确定性操作会导致节点状态产生矛盾,因此这在现实情景中远远比排序-执行架构更加合适。

最后,一个背书节点的本地策略在怀疑有拒绝攻击(DoS)的可能性时可以很简单的终止交易执行,这使得Fabric可以解决不可信任的链码中那些非确定性操作可能进行拒绝攻击的问题。这将不会对系统的一致性产生威胁,与此相对,这种对某个执行单方终止的行为在排序-执行架构中也是不可能实现的。

Ordering Phase

当客户为一个交易提案收集到足够的背书时,它就会整合交易并提交给排序服务。交易信息包含了交易负载(例如,链码操作包含它需要的参数),交易元数据和一个背书集合。排序阶段为每个通道的所有提交交易建立总排序。换句话说,尽管可能会有错误的排序,排序节点依然会原子广播所有的背书并因此得以在这笔交易上建立共识。此外,排序服务会将多个交易在区块中打包并输出包含交易区块的哈希链。容错广播的一种常用方法是将交易组合或者是在不同区块中打包,这可以增加广播协议的吞吐量。

在上层应用中,排序服务的接口只支持两种操作。这些操作被节点调用并被一个信道标识符隐式地参数化:

  • broadcast(tx):客户端调用此操作来广播任意交易tx,该交易通常包含交易负载和客户签名,以便进行传播。

  • B←deliver(s):客户调用这个操作使用一个非负数序列号s去找到区块B。 该区块包含一个交易列表[tx1 ,…,txk]和一个哈希值h代表了序号是s-1的区块,例如B =([tx1,…,txk],h)。 只要区块有效返回值就是确定的,所以当节点通过第一次调用deliver(s)接收区块B时,之后依然可以通过s接收到B。

排序服务保证同一个信道上被提供的区块都是经过排序的。更具体地说,排序会保证每个信道上有以下的安全属性:

  • 一致性:对于任何两个带有序列号sB的区块和带有序列号s'B',在正确的节点处传送时,如果s = s',则B = B'

  • 哈希链完整性:当一个正确的节点通过序列号s返回了区块B而另一个正确的节点通过序列号s+1返回了B'=([tx1...txk],h'),那么h'=H(B)恒成立,H是加密哈希函数。

  • 无跳跃性:如果正确的节点p通过s>0返回一个区块,那么在这之前,对于i=0,...,s-1,节点p已经通过i返回了对应区块。

  • 无创建性:当一个正确的节点通过序列化s返回来区块B,那么对于B中的每一笔交易,都已经被某个客户广播过了。

为了活性(liveness),排序服务至少支持以下“最终”属性:

  • 合法性:如果正确的客户端调用广播(tx),那么每个正确的节点最终都会通过某个序列号接收到包含交易tx的区块B。

但是,根据不同客户的需求,不同的排序服务允许使用自己的活性和公平性担保。

由于区块链网络中可能会有很多节点,但只有较少的节点实现排序服务,因此可以将Fabric设置使用内置gossip服务将排序节点接收的区块分发给所有的节点(第4.3节)。gossip的实现是可扩展的,而且它并不需要清楚具体使用的排序服务,因此它可以同CFT和BFT排序服务同时合作,来保证Fabric的模块化。

排序服务还可以执行访问权限检查来决定客户是否有权广播一些信息或是在给定通道上接收区块。排序服务的这一功能和其他功能将在第4.2节中进一步说明。

  • 关于设计选择的讨论:排序服务不保存区块链的任何状态、验证和交易,这是十分重要的。这种架构是Fabric的一个重要的决定性特征,使Fabric成为第一个完全将共识与执行和验证分开的区块链系统。这还使得共识算法模块化成为可能,并使得它在共识算法系统中实现了排序服务。哈希链完整性属性和区块的链接的存在仅使节点对区块序列的完整性验证更有效。最后,请注意我们不要求排序服务来防止交易重复。 这简化了其实现并且不是问题,因为在验证期间,节点在读写检查中过滤了重复的交易。

Validation phase

区块可以直接通过排序服务直连或通过gossip传递给节点。当新区块产生时,它会进入验证阶段,该阶段包含三个连续步骤:

  1. 背书策略的评估在一个区块中所有的交易运行时进行。评估是所谓的验证系统链码(VSCC)的任务,VSCC是一个静态库,它是区块链配置的一部分,负责根据链码配置的相关背书策略来验证背书。如果不满足背书,则交易被标记为无效,其效果将被忽略。

  2. 按顺序对区块中的所有交易进行读写争议判定。对于每个交易,首先比较读取集和节点本地储存的当前账本状态中键的版本,保证它们是相同的。如果版本不匹配,则交易被标记为无效,其效果将被忽略。

  3. 账本更新阶段最后运行,这时区块会被追加在本地储存的账本中并更新了区块链的状态。特别的是,当为账本追加区块时,前两个步骤中的验证检查的结果还以位掩码的形式保留着,来指明区块中这些交易是有效的。随后这可以帮助重建账本状态。此外,状态更新都是通过将所有的键值对放进写入集添加到本地状态中来完成的。

Fabric中默认的VSCC允许使用逻辑单调表达式去表示链码上的背书节点集。当交易背书的有效签名满足这个表达式时,VSCC评估就会验证通过这个节点集。不同的VSCC策略可以被静态设置。

  • 关于设计选择的讨论:Fabric的账本包含所有交易,包括那些被视为无效的交易。这是从整体设计得出的,一方面因为排序服务是在不可知链码状态的情况下生成了区块链,另一方面则是因为验证过程是节点在达成共识后完成的。这个特性是在一些特定用途中所需的,比如有些时候需要在审计中追踪无效交易,这与只保存有效交易的其他区块链形成对比(例如比特币和以太坊)。

Trust and Fault model

Fabric可以适应灵活的信任和错误假设。通常,任何客户端都被视为潜在恶意或拜占庭。节点被分组到组织中,并且每个组织形成一个信任域,使得一个节点信任其组织内的所有节点但不信任另一组织的节点。排序服务将所有节点(和客户)视为可能的拜占庭。

Fabric网络的完整性依赖于排序服务的一致性。排序服务的信任模型直接取决于其实施。从版本v1.0.6开始,Fabric支持在开发和测试中使用的集中式单节点实现,以及在集群上运行的CFT排序服务。第三种实现,即基于BFT-SMaRt的概念证明,可容忍多达三分之一的拜占庭OSN。

请注意,Fabric将应用程序的信任模型与共识的信任模型分离。也就是说,分布式应用程序可以定义自己的信任假设,这些假设通过背书策略传达,并且独立于排序服务实现的共识。

Fabric components

Fabric是用Go编写的,它使用gRPC框架在客户端,节点和orderer之间进行通信。 在下文中,我们将更详细地描述一些重要组件。 下图显示了一个节点的组成部分。

5

Membership service

成员服务提供程序(MSP)维护系统中所有节点的身份(客户端,peer节点和OSN节点),并负责为节点发布认证和授权时需要的凭证。由于Fabric拥有准入机制,因此所有节点间通过信息传递的相互影响都是要经过验证的,通常使用数字签名的方式进行验证。成员管理服务由每个节点的部分组件组成,可以验证交易、检验交易完整性、签署背书、验证背书和核验其他的区块链操作。用于密钥管理和节点注册的工具也是MSP的一部分。

MSP可以是不同执行实体的抽象。 Fabric中的默认MSP实现处理基于数字签名的标准PKI方法,并且可以容纳商业证书颁发机构(CAs)。Fabric还提供独立CA,称为Fabric-CA。此外,还设计了可选择的MSP实现,例如一种依赖匿名证书而不是连接节点身份来验证节点调用交易的实现方式。

Fabric允许设置两种区块链网络的模式。 在离线模式下,凭CA会生成证书并通过不同频率分发给所有节点。peer节点和Orderer只能在离线模式下注册。 在登记用户时,Fabric-CA提供了一种在线模式,可以向其发出加密证书。MSP配置必须确保所有节点(尤其是所有节点)都能够识别出有效的相同身份和认证。

MSP允许身份联盟的存在,例如,当多个组织操纵一个区块链网络时,每个组织会发布它的成员的身份然后每个节点去识别所有组织的成员。使用多重MSP实体可以做到这一点,例如,在每个组织和MSP之间创建一个映射。

Ordering service

排序服务管理多个通道。在每个通道,它提供以下服务:

  1. 原子广播实现了broadcastdeliver的方法调用,在交易中建立排序。

  2. 当成员通过广播配置更新交易时,可以修改信道,进行通道的重新配置。

  3. 可选地,在排序服务充当可信实体的那些配置中,访问控制限制特定节点和客户广播交易和接收区块。

排序服务是在系统通道上通过创世区块自己生成的。这个区块携带定义了排序服务属性的交易配置。

当前的产品实现包括实现了以上操作并通过系统通道通信的排序服务节点(OSNs)。实际上,原子广播是通过基于ZooKeeperApache Kafka完成的。Apache Kafka提供了可扩展的发布-订阅通知,在节点崩溃情况下依然可以保持强一致性。Kafka可以在于OSNs分离出的物理节点上运行。OSNs在节点和Kafka间扮演代理服务器的角色。

一个OSN可以直接向原子广播(例如Kafka broker)中注入一个新接收到的交易。从另一方面说,节点会中打包从原子广播中接收到的交易并组建区块。一个区块在满足以下任意一个条件时立刻被切分:

  1. 区块包含交易数达到上。
  2. 区块在字节内存上已经达到上限。
  3. 距离接收新区块的第一个交易的时间点,交易已经超时。

打包过程是确定性的,因此在所有节点处会产生相同的块。从原子广播接收的交易流,很容易看出前两个条件明显是确定性的。为了保证区块在第三种情况中生成的确定性,节点会在它读取原子广播中的区块中的第一笔交易时打开一个计时器。如果在计时器到期时区块尚未被切断,则OSN在通道上广播一个特殊的中断时间交易,该交易指示它想要中断的区块的序列号。另一方面,每个OSN在接收到给定区块号的第一个切换时间交易时,立即中断新块。由于此交易以原子方式传递给所有连接的OSN,它们的这个区块中都包含了相同的交易列表(在当前的总量为f的拜占庭容错OSNs中部署这个方案时,只有当接收到f+1个带有中断信号的交易时才会终结区块)。OSN会直接在文件系统中保留一些最近接收到的区块,因此他们可以通过调用deliver恢复区块并回应给其他节点。

排序节点使用的Kafka是三种当前可行的排序服务方案之一。中心排序服务节点叫做Solo,在一个节点上运行并被用作部署。概念性的基于BFT-SMaRt的排序节点也是一种可行的方案,它能确保原子广播服务但现在还不能重新配置和控制访问。这也突出了Fabric共识算法的模块化特性。

Peer Gossip

分离执行,排序和验证阶段的一个优点是它们可以被独立地延展。但是,由于大多数共识算法(在CFT和BFT模型中)受带宽的限制,排序服务的吞吐量受其节点的网络容量限制。共识算法并不能通过增加更多的节点来延展,因为这样将会降低吞吐量。但是,由于排序和验证是分离的,在排序阶段之后,我们对如何高效地向节点广播交易执行结果来进行验证更感兴趣。这一点,以及状态转移到新连接的节点和长时间断开连接的节点,正是gossip组件的目标。Fabric gossip为此目的利用流行多播(epidemic multicast)。这些区块由排序服务签署。意味着任何一个节点可以通过接收所有的区块,独立的组装一个区块链并验证其完整性。

用于gossip的通讯层基于gRPC并且利用具有相互认证的TLS,这使得每一方能够将TLS证书绑定到远程节点的身份。gossip组件保存了系统中在线节点的最新成员管理视图。所有节点都独立的通过周期性散布成员数据来建立一个本地的视图。此外,在崩溃或网络中断后,节点可以重新连接到视图。

Fabric gossip使用两个阶段进行信息分发:在push阶段,每个节点从成员视图中随机选择一组它的活动邻居,并向它们传递信息;在pull期间,每个节点周期性地搜索一组随机选择的节点并请求丢失的消息。将这两种方法串联起来使用,可以最好地使用可用带宽并且保证所有的节点大概率可以接收到所有信息。为了减少从排序节点向网络的发送区块的负载,该协议还选择了一个leader 节点,它代表从排序服务拉取区块并初始化gossip到各个节点的传播。这种机制对leader 节点发送错误是有弹性的。

Ledger

每个节点的账本组件存储了账本并且持久了存储上的状态,并启用模拟,验证和账本更新的过程。从广义上讲,它由区块的存储和节点交易管理员(PTM)组成。

账本区块仓库长期存储交易区块的数据,并以一个可追加集合的形式实现的。由于区块是不可变的并且以固定顺序到达,这种可追加的结构能够发挥它最大的性能。此外,区块仓库会储存一些指向随机区块或者是区块中的一个交易的索引。

节点交易管理员(PTM)保存了版本化键值对数据库的最新状态。对于chaincode储存的每一个主键,它储存了一个(键,值,版本)形式的元祖,包含它最新的值val和最新的版本ver。版本包括区块的序列号和区块内这笔交易(保存了条目的)的序列号。这使得版本唯一且单调递增。PTM使用一个本地的键值对数据库来实现版本控制的键值对储存,使用LevelDB(在Go中)和Apache CouchDB实现。

在模拟期间,PTM提供一个最新交易状态的稳定快照。如3.2节所述,对于每个GetState访问的条目,PTM从读取集中记录了一个(键,版本)的元组,对于每个PutState访问的条目,PTM从写入集记录了(键,值)的元组。此外,PTM还支持范围查询,会根据查询结果计算一个加密的哈希值(一组元组(key,ver))并将查询语句和哈希值添加到读取集。

对于交易验证(第3.4节),PTM按顺序验证块中的所有交易。这将检查交易是否与先前的交易(在区块内或更早的交易中)冲突。对于读取集中的任何键,如果读取集中记录的版本与当前状态中存在的版本不同(假设之前所有有效交易都已经被提交),则PTM会将该交易标记为无效。对于范围查询,PTM重新执行查询并将hash与读取集中存在的hash进行比较,以确保不会发生==幻像读取==(phantom reads)。这个读写的矛盾将导致单副本可串行化。

账本组件允许节点在更新账本时发生崩溃。在接收到新区块后,PTM已经使用第3.4节中提到的位掩码在区块中执行验证并在区块中标记这笔交易是否有效。然后账本将区块添加到账本储存中,并写入硬盘,随后更新区块索引。之后,PTM会将写入集中有效交易的状态更改到本地版本化储存中。最后,它会计算并保存一个值的回滚点,这里记录了已经成功应用的区块的最大编号。这个回滚点用来在崩溃后重建时,从保存的区块中恢复索引和最新的状态。

Chaincode execution

链码在一个由其他松散联结的节点组成的环境中执行,支持一些添加其他编程语言编写链码的插件。当前有三种语言支持编写链码:GoJavaNode.js

每个用户级或应用级链码都在Docker容器环境中的单独进程中运行,这会将链码彼此隔离,并与节点隔离。这还简化了chaincode生命周期的管理(即,启动,停止或废弃链码)。链码和节点通过gRPC信息通讯。通过这种松散耦合,节点实际上无法知道链码实现是使用的是什么编程语言。

Confguration and System Chaincodes

Fabric通过通道配置和特殊链码(称为系统链码)进行定制。

在Fabric中的每个通道可以组成逻辑上的区块链。特殊的配置区块可以保存了一些元数据来记录信道的配置。每个配置区块都包含一个完整的信道配置并且不包含任何其他的交易。每个区块链都以称为创世区块的配置区块开始,该区块用于建立通道。通道配置包括:

  1. 参与节点的MSPs定义。
  2. OSNs的网络地址。
  3. 共识算法实现和排序服务的公有配置,例如打包区块的大小和超时限制。
  4. 管理对排序服务操作(广播和接收)的访问的规则。
  5. 管理进行通道修改配置的规则。

可以使用通道配置更新交易来更新通道的配置。这种交易包括所有需要更改的配置和签名。排序服务节点通过使用当前的配置检验修改使用的签名是否经过验证,来验证这个更新是否有效。然后,排序节点生成一个新的配置区块,其中嵌入了新的配置和配置更新交易。接收此区块的节点检验是否经过当前配置的验证;如果有效,他们会更新当前的配置。

应用链码通过背书系统链码(ESCC)和验证系统链码(VSCC)来部署。这两种链码以一种对称的方式被选择调用,因此ESCC的输出(背书)可能会被作为VSCC输入的一部分。 ESCC的输入是一个交易提案和模拟执行的结果。如果结果满足条件,ESCC会产生一个回应,包括结果和背书。对于默认的ESCC而言,这个背书只是一个基于节点本地身份的签名。VSCC的输入是交易本身,输出则是交易是否有效。对于默认的VSCC而言,收集背书并与链码指定的背书策略校对,就能验证交易是否有效。其他的系统链码有别的作用,比如配置和链码的生命周期。

文章在此