区块链超级账本Hyperledger Fabric架构说明

翻译自:http://hyperledger-fabric.readthedocs.io/en/latest/arch-deep-dive.html

边学习边翻译,很多地方还不明白,请对照原文学习,转载请注明出处。

翻译说明:
- peer全部翻译为对等点,是逻辑节点,背书者和确认者都是对等点,排序者不是。
- state全部翻译为状态,这个状态是一个特定的概念,可理解为区块链某一时刻的快照。
- valid和invalid分别翻译成有效的和无效的,也可以理解为合法的和不合法的。
- replay翻译为重播,我觉得比较形象,也可以理解为回溯。

架构说明

Hyperledger Fabric带来如下优势:

  • 链码信任灵活性。架构将对链码(区块链应用程序)的信任假设与对排序的信任假设相分离。换种说法,排序服务可以由一组节点(排序者)提供并容忍其中一部分失败或腐化,每个链码可以有不同的背书者。
  • 可扩展性。由于负责特定链码的背书者与排序者不相交,系统可以比由相同节点完成背书和排序这两个功能更易扩展。特别是,当不同的链码指定不同的背书者时,导致了不同背书者之间的链码的分区,并且允许并行执行链码。此外,执行链码的资源消耗可能很大,于是让其与关键的排序服务分开单独部署。
  • 保密。架构有助于部署对交易的内容和状态更新有保密要求的链码。
  • 共识模块化。架构是模块化的并允许可插拔的共识(比如排序服务)实现。

第一部分:Hyperledger Fabric v1系统架构元素
1. 系统架构
2. 交易背书的基本工作流
3. 背书策略

第二部分:V1后版本的架构元素
4. 账本检查点(修订中)

1. 系统架构

区块链是一个由许多节点互相通信组成的分布式系统。区块链运行称为链码的应用程序,保存状态和账本数据,执行交易。因为交易是在链码上调用的操作,因而链码是区块链结构的中心元素。交易必须被“背书”而且只有背书过的交易才可以被承认和对状态造成影响。有一个或多个用于管理功能(函数)和参数的特殊的链码,它们统称为系统链码

1.1. 交易

交易有两种类型:
- 部署交易 创建新链码并以一个程序作为参数。当一个部署链码成功执行以后,链码被安装在区块链“上”。
- 调用交易 在之前部署的链码的上下文中执行操作。一笔调用交易指的是一个链码和他提供的若干功能中的一个功能。如果调用交易成功,链码执行了特定的函数,这个函数可能涉及修改相应的状态,然后返回一个输出。

备注:本文档目前假定一笔交易要么创建新链码要么调用一个由一个已经部署的链码提供的操作。本文档尚未阐述关于:a) 请求(只读)交易的优化(V1版本),b) 跨链码交易的支持(V1后版本)

后面会讲到,部署交易是调用交易的一种特例,部署交易创建新的链码,对应于一个系统链码上的调用交易。

1.2. 区块链数据结构

1.2.1. 状态(state)

区块链的最新状态被建模为一个版本化的键值存储(KVS),键是名字,值是任意二进制大对象(blobs)。这些键值对被运行在区块链上的链码(应用程序)通过putgetKVS操作来操纵。状态被持久化存储并且状态的更新被记录为日志。请注意状态模型采用的是版本化的KVS,具体实现可以使用实际的KVS存储,但是也可以用关系数据库系统或其他的解决方案。

更正式地说,状态s被建模为一个字典元素K -> (V X N),其中:

  • K是一个键集合
  • V是一个值集合
  • N是一个版本号的无穷有序集合。映射函数next: N -> NN中的一个元素并返回下一个版本号。

VN都包含一个特别的元素⊥ (空类型),是N的最小元素。初始是所有的键都映射为(⊥, ⊥),对于s(k)=(v,ver)我们通过s(k).value表示v,用s(k).version表示ver

KVS操作被如下建模:
- put(k,v) for k ∈ K and v ∈ V, 取得区块链的状态s然后转换为s`,这样对于所有的k’!=k,s’(k)=(v,next(s(k).version)),s’(k’)=s(k’) (后面这个等式没有太明白)
- get(k)返回s(k)

状态由对等点维护,而不是order节点和客户端。

状态分区。KVS中的键可以从它们链码的名字识别,在这个意义上,只有一个特定链码的交易可以修改属于这个链码的键。原则上,任何链码都可以读取属于其他链码的键。为了支持跨链码交易,修改属于两个或更多个链码的状态作为一个V1后的功能点。

1.2.2 账本

账本提供一份可验证历史信息,记录所有在系统操作中发生的成功的状态改变(即有效交易)和未成功的对改变状态的尝试(即无效交易)。

账本由排序服务构建为一个完整排序的交易(有效的和无效的)区块的哈希链。哈希链强制要求账本中的区块是完全排序的并且每个区块内包含的交易是完全排序的。

账本存储在所有的对等点里,也可以选择保存在一部分排序者里。存在排序者节点内的账本我们叫排序者的账本(OedererLedger),而存在对等点的账本我们叫对等点账本(PeerLedger)。对等点账本与排序者账本不同之处在于对等点账本本地维护一个比特掩码来区分有效交易和无效交易(更多细节见XX部分)

对等点可以像XX节(V1后功能)阐述的那样修订对等点账本。排序者维护排序者账本用于容错和(对等点账本的)可用性并可以随时决定修订对等点账本,前提是排序服务的属性(见1.3.3)保持不变。

账本允许对等点重播所有交易的历史来重构状态。因此,1.2.1节谈到的状态是一个可选的数据结构。

1.3. 节点(Nodes)

节点是区块链的通讯实体。一个“节点”只是一个逻辑功能,这样多个不同类型的节点可以运行在一台物理服务器上。重要的是节点如何组成“信任域”和如何与控制它们的逻辑实体相关联。

有三种节点类型:
1. 客户端(client)提交客户端(submitting-client):一个客户端向背书者提交一笔实际发生的交易调用,并将交易提案广播给排序服务。
2. 对等点(peer):确认交易和维护账本的状态、保存账本的副本(Sec,1.2)。除此之外,对等点有一个特别的背书者角色。
3. 排序服务节点(Ordering-service-node )或者排序者(orderer):运行保证送达(deliver)的通信服务,如原子广播或完全排序广播。

下面将详细解释节点的类型。

1.3.1. 客户端

一个客户端是一个代表最终用户的实体。它必须与一个对等点连接以实现同区块链的通信。客户端自己选择连接到任意的对等点。客户端创建然后调用交易。

详见第2节,客户端与对等点和排序服务都通信。

1.3.2. 对等点

一个对等点从排序服务接收以区块形式更新的有序状态,并且维护状态和账本。

对等点可以额外承担一个特殊的背书节点(endorsing peer)的角色,或者称为背书者(endorser)。背书节点的特殊功能发生在特定的链码上,在一笔交易确认前为这笔交易背书。每一个链码可以指定一个关联一组背书节点的背书策略。这个策略定义了一个有效交易背书的充要条件(一般是一组背书者的签名),在第2节和第3节会详述。对于安装新链码来部署交易的情况,(部署)背书策略被指定为系统链码的背书策略。

1.3.3. 排序服务节点(排序者)

排序者构建排序服务,即一个提供保证送达(deliver)的通信结构。排序服务可以由不同的方法实现:从一个中心化的服务排序(例如在部署和测试时)到针对不同的网络和节点故障模型的分布式协议。

排序服务为客户端和对等点提供一个共享的通信通道,和一个交易信息的广播服务。客户端连接到通道(channel)上,然后可以在通道内广播信息传送(deliver)到全部的对等点上。通道支持全部信息的原子传递,即消息通信是可靠(针对特定实现方法)的完全排序(total-order)传递。换种说法,通道输出相同的信息到所有连通的对等点,并且按照相同的逻辑顺序输出。这个原子通信保证也称为完全顺序广(total-order broadcast),原子广播,或分布式系统上下文中的共识。发送的消息是将要包含在区块链状态中的候选交易。

分区(排序服务通道)。排序服务可以支持多通道,这个通道就类似于发布/订阅消息系统里的主题(topic)。客户端可以连接到一个给定的通道发送消息和接收消息。通道可以被理解为分区——连接到一个通道的客户不知道其他通道的存在,不过客户端可以连接到多个通道。即便Hyperledger Fabric的一些排序服务支持多通道,为了简化表达,本文档后续阐述将假定排序服务由一个单通组成。

排序服务API。对等点通过排序服务提供的接口连接到排序服务提供的通道上,排序服务API由两个基本操作组成(通常为异步事件):

TODO添加用于获取客户端或对等点指定序号的特定区块的API。

  • broadcast(blob):广播,客户端调用这个操作在通道内广播任意信息blob。在拜占庭容错问题中向一个服务发送一个请求的时候也被称为request(blob)。
  • deliver(seqno, prevhash, blob):传递,排序服务在对等点调用这个操作传递包含指定的非负整数序列号(seqno)和前一个传递的blob(prevhash)的哈希值的信息blob。(此处原文的格式比较清晰)换种说法,它是排序服务的一个输出事件。deliver()在发布-订阅系统中有时也被称为notify(),在拜占庭容错系统中被称为commit()。

个人理解:类比比特币区块链,广播就是将交易向全网广播,传递像是将交易顺序打包成区块。

账本和区块编队。账本(另见1.2.2节)包括排序服务输出的全部数据。简言之,它是一个deliver(seqno, prevhash, blob)的序列,依据计算前面提到的prevhash
来构建一条哈希链。

大多数时候,为了提高效率,排序服务会将交易(blob)打包,在一个deliver事件中输出多个区块,而不是每一笔交易单独输出。在这种情况下,排序服务必须对每一个区块里的交易强加一个确定的顺序。一个区块里的交易数量可以由一个排序服务的具体实现动态选择。

接下来,为了表达简便,我们基于每次deliver一个blob的假设定义了排序服务属性并且解释了交易背书(第2节)的工作流。这个概念可以轻松扩展到区块,基于上面说的一个block里面的blob有确定的顺序,我们假定一个block的deliver事件对应一个block里的每一个blob的一系列deliver事件。

排序服务属性
排序服务(或原子广播通道)的保证规定了广播消息会发生什么和完成传递的消息之间存在什么关系。这些保证如下:

  1. 安全性(持续保证):只要对等点连接上通道足够长的时间(他们可以断开连接或者宕机,但是会重启和重连),他们会看到唯一一串完成传递的(seqno, prevhash, blob)的信息。这意味着输出(deliver()事件)在所有对等点以相同的顺序发生,并且输出(output)依据序号为同一个序号携带相同的内容(blob和prehash)。注意这仅仅是一个逻辑顺序,而且一个对等点上的delever(seqno, prevhash, blob)不要求与在其他对等点上输出相同消息的delever(seqno, prevhash, blob)实时发生。换句话说,给定一个特定的seqno,没有两个对等点传递不同的prevhash或blob值。此外,除非某个客户端(对等点)实际调用了broadcast(blob),没有blob值会被传送,每个广播过的blob只传递一次

    此外,deliver()事件包含了前一个deliver()事件(prevhash)的加密哈希值。当排序服务实施原子广播保证时,prevhash是序列号为seqno-1的deliver()事件的哈希值。这样建立了一条跨deliver()事件的哈希链用于校验排序服务输出的正确性,后面的4、5节还会谈到。作为特例的第一个deliver()事件,prevhash有一个默认值。

    1. 活跃度(传递保证):排序服务的活跃度保证由一个具体的排序服务实现来指定。精确地保证依赖于网络和节点故障模型。

    原则上,如果正在提交的客户端没发生故障,排序服务应该保证连接到排序服务的每个正确的对等点最终传递每一个提交的交易。

总的来说,排序服务保证如下属性
- Agreement。对于在正确的对等点的任意两个seqno相同的事件::deliver(seqno, prevhash0, blob0) 和 deliver(seqno, prevhash1, blob1),prevhash0==prevhash1 and blob0==blob1;
- 哈希链的完整性。对于正确对等点的任意两个事件:deliver(seqno-1, prevhash0, blob0) 和 deliver(seqno, prevhash, blob), prevhash = HASH(seqno-1||prevhash0||blob0).
- 无跳跃。如果一个排序服务在一个正确的对等点p输出
deliver(seqno, prevhash, blob),这个seqno>0,那么p已经传递过事件deliver(seqno-1, prevhash0, blob0);
- 无创建。正确的对等点的任何事件deliver(seqno, prevhash, blob) 必须被某些对等点(可能不同)的broadcast(blob)事件处理。
- 无复制(可选,但是可取)。对于任意两个事件broadcast(blob) 和 broadcast(blob’), 当两个事件deliver(seqno0, prevhash0, blob) and deliver(seqno1, prevhash1, blob’)在对等点发生并且blob == blob’,那么seqno0==seqno1 并且 prevhash0==prevhash1
- 活跃
如果一个正确的客户端调用了broadcast(blob)事件,那么每一个正确的对等点最终发出一个deliver(*, *, blob)事件,*代表任意值。

2. 交易背书的基本工作流

下面我们概述一笔交易的高层请求流程。

备注:注意下面的协议不假定所有的交易都被确认,它允许未确认的交易。

### 2.1. 客户端创建一笔交易发送给它选择的背书节点

客户端向它选定的一组背书节点发送一个PROPOSE消息(可能不是同时发送,见2.1.2和2.3节)来调用一笔交易。这一组给定chaincodeID的背书节点通过对等点对客户端提供服务,对等点通过背书策略认同一组背书节点。(见3节)。举个例子,交易可以被发送到给定chaincodeID的所有背书者。也就是说,某些背书者可以是离线的,其他背书者可以拒绝为这笔交易背书。提交的客户端尝试满足所有背书者的策略要求。

接下来,我们先详细介绍PROPOSE信息的格式然后讨论客户端和背书者之间可能的交互方式。

#### 2.1.1. PROPOSE信息格式
PROPOSE信息的格式是

2.1.2 信息模式

客户端决定与背书者交互的顺序。比如,客户端一般发送

2.2. 背书节点模拟交易执行并生成背书签名

从客户端收到一个

2.3. 提交客户端为一笔交易收集一套背书然后通过排序服务广播给其他对等点

提交客户端等待收集“足够”的信息以及对(TRANSACTION-ENDORSED, tid, , )声明的签名以得出交易提案被成功背书的结论。如2.1.2节所讨论的,这可能涉及一轮或多轮与背书者的交互。

“足够”的精确数字取决于链码的背书策略(另见3节)。如果满足了背书策略,交易就完成背书;注意并不是确认。从背书节点收集到的签过名的TRANSACTION-ENDORSED信息被称为背书endorsement。

如果提交客户端不想为一个交易提案收集背书,它可以丢弃这笔交易,并可以选择稍后重试。

对于得到有效背书的交易,我们现在开始用到排序服务。提交客户端使用broadcast(blob)来调用排序服务,其中blob=endorsement。如果客户端没有直接调用排序服务的能力,它可以通过它选择的对等点来代理广播。客户端必须信任对等点不会删减任何背书信息,否则交易被认定为无效。注意,无论如何,一个代理对等点不可以捏造一份有效的背书。

2.4. 排序服务向对等点传送一笔交易

当一个事件deliver(seqno, prevhash, blob)发生并且一个对等点已经更新了序号小于seqno的blobs的状态,一个对等点会做如下事情:

  • 它通过链码(blob.tran-proposal.chaincodeID) 的策略检查blob.endorsement是有效的。
  • 一般情况下,他同时验证没有违反依赖关系(blob.endorsement.tran-proposal.readset) 。在更复杂的用例中,背书中的tran-proposal字段可以不同,这时背书策略(3节)指定状态如何改变。

    依赖关系的验证可以通过不同的方法实现,这依据为状态更新选择的一致性属性或“隔离保证”。序列化是一种默认的隔离保证,除非链码的背书策略指定了其他方式。可序列化性可以由要求读集中的每一个键的版本等于这个键在状态中的版本来提供,并且拒绝不满足这个要求的交易。

    • 如果所有这些检查都通过了,交易被认定为有效的或者确认的。此时,对等点标记PeerLedger中这笔交易的比特掩码为1,将blob.endorsement.tran-proposal.writeset应用于区块链状态(仅当tran-proposals相同时,否则背书策略逻辑定义了函数使用blob.endorsement)。
    • 如果背书策略认证blob.endorsement失败了,交易是无效的,对等点标记PeerLedger的比特掩码为0.重要的是无效交易不改变状态。

请注意,这足以让所有(正确的)对等点在处理完传递给定序号的事件(区块)后达到相同的状态。亦即,借助排序服务的保证,所有正确的对等点会收到一串相同顺序地deliver(seqno, prevhash, blob)事件。因为对于背书策略的评估和读集中版本依赖关系的评估都是确定的,所有正确的对等点也会得出对于一个blob里的一笔交易是否有效的相同的结论。因此,所有的对等点承认和应用相同的交易序列,所有的对等点用相同的方式更新状态。
image
图1. 一个典型的交易流

3. 背书策略

3.1. 背书策略规范

背书策略,是背书一笔交易的条件。区块链对等点有一个预先设定的背书策略集,由安装特殊链码的部署交易设置。背书策略可以被参数化,这些参数可以由部署交易指定。

为了保障区块链的完整和安全属性,背书策略集应该是一套经过验证的提供有限功能的策略,用于确保有限的执行时间(可终止),决定机制、性能和安全。

动态添加背书策略(例如通过链码上的部署交易部署时间)在评估时间、决定机制、性能和安全保障方面是非常敏感的。因此,不允许动态添加背书策略,未来可能会支持。(不理解)

3.2. 对背书策略的交易评估

一笔交易仅在依据背书策略被成功背书以后才被声明为有效的。调用一个链码的调用交易首先务必获得一个满足这个链码的策略的背书,否则它不会被确认。这通过第2节解释的提交客户端和背书节点之间的交互来进行。

形式上背书策略是对背书的一个断言,判断其潜在地下一步状态是True还是False。部署交易的背书通过系统策略获得(比如从系统链码)。

背书策略断言某些特定的变量。可能是指:

  1. 链码的密钥或身份(从链码的元数据里找),例如一组背书者;
  2. 链码的下一步元数据;
  3. endorsement和endorsement.tran-proposal的元素;
  4. 更多其他可能。

以上列表是按表达性和复杂性增序排序的,也就是说,仅支持节点的密钥和身份的策略相对简单。

背书策略断言的评估必须是确定的。每一个对等点应该在本地评估一份背书,这样一个对等点不需要与其他对等点交互,而且所有正确的对等点用相同的方法评估背书策略。

3.3. 背书策略举例

断言可以包含逻辑表达式并判断真假。典型的条件会使用由链码的背书节点发布的对于交易调用的数字签名。

假设链码指定背书者集合为E = {Alice, Bob, Charlie, Dave, Eve, Frank, George}。一些实例策略如下:
- 来自E的全部成员的对于同一个tran-proposal的有效签名
- 来自E的某一个成员的有效签名
- 由(Alice OR Bob) AND (any two of: Charlie, Dave, Eve, Frank, George)这个条件决定的背书节点的对于同一个tran-proposal的有效签名
- 来自7个背书者中的任意5个的有效签名。(更普遍地,对于n > 3f个背书者的链码,有效签名来自n个背书者中的2f+1个,或者多于(n+f)/2个背书者)。
- 假设向背书者分配“股权”或“权重”,比如{Alice=49, Bob=15, Charlie=15, Dave=10, Eve=7, Frank=3, George=1},总的股权是100:策略要求有效签名来自一组拥有多数股权的背书者(即一组组合股权绝对超过50的背书者),比如{Alice, X},其中X是除了George以外的任意人,或者{everyone together except Alice},等等。
- 上例中的股权分配机制可以是静态的(固化在链码的元数据中),也可以是动态的(比如依据会在链码的执行过程中改变的状态)。
- 来自Alice或者Bob的对于tran-proposal1的有效签名,和来自于(any two of: Charlie, Dave, Eve, Frank, George)的对tran-proposal2的有效签名,其中tran-proposal1和tran-proposal2仅在它们的背书节点和状态更新上是不同的。

这些策略的有用程度取决于应用程序,对容忍失败或背书者腐化的要求,和其他的属性。

4(V1后). 有效的总账本和PeerLedger检查点(修剪)

4.1 有效账本(VLedger)

为了维护一个只包含有效的确认的交易的总账本(比如比特币),对等点可以除了状态和总账本之外再维护一个有效账本(VLedger)。这是总账本滤掉无效交易以后的一个派生哈稀链。

VLegder的区块结构(vBlocks)是这么构成的。PeerLedger可能包含无效交易(有无效背书的交易或有无效的版本依赖),这些无效交易在从一个block添加到一个vBlock之前被对等点过滤掉。每个对等点自己办这事(比如用PeerLedger关联的比特掩码)。一个vBlock定义为有一个不含无效交易的block,无效交易已经被过滤掉了。这些vBlock的尺寸是动态的,可能为空。下图展示了vBlock的结构。
image

图2. 从PeerLedger生成vBlock链

vBlock被每个对等点链进一条哈希链。进一步说,每个vBblock包括:
- 前一个vBlock的哈希
- vBlock编号
- 自上一个vBlock被计算出来以来所有被对等点确认的有效交易的列表(即一个block里的有效交易的列表)。
- 派生当前这个vBlock的block(在PeerLedger里)的哈希

全部这些信息被一个对等点级联和哈希,在VLedger里生成vBlock的哈希。

4.2. PeerLedger检查点

总账本包含了没必要永久记录的无效交易。但是对等点在建立了相应的vBlock以后不能简单地丢弃PeerLedger的block从而修订PeerLedger。也就是说,在这种情况下,如果一个新的对等点加入了网络,其他对等点不能传给它丢弃的block(有关PeerLedger),也不能说服刚加入的对等点他们的vBlocks是有效的。

为了实现PeerLedger的修订,文本描述了一种检查点机制。这个机制建立跨对等点的vBlocks的合法性并允许用检查过的vBlocks替换丢弃的PeerLedger block。这反过来减少了存储空间,因为无需存储无效交易。它也减少了网络为新加入的节点重新建立状态的工作(当通过重播PeerLedger来重建状态时它们不需要建立每笔交易的合法性,而是可以简单地重播VLedger包含的状态更新)。

4.2.1. 检查点协议

检查点由对等点在CHK周期性执行,CHK是一个可配置的参数。要启动检查点,一个对等点向其他对等点广播(例如gossip协议)

4.2.2 有效检查点

显然,检查点协议引发了如下问题:对等点什么时候可以修订它的PeerLedger?多少CHECKPOINT信息是足够多?这些由一个检查点有效策略定义,通过(至少)两个可能的方法,这两种方法也可以组合:

  • 本地(特定对等点)检查点有效策略(LCVP)。给定对等点p上的本地策略,可以指定一组p信任的对等点,这些对等点的CHECKPOINT信息足够建立一个有效的检查点。举个例子,Alice的LCVP可定义为Alice必须收到Bob的CHECKPOINT信息,或者收到Charlie和Dave两个人的。
  • 全局检查点有效策略(GCVP)。全局指定的检查点有效策略。类似于本地对等点策略,区别在于它是系统(区块链)粒度的规定,而不是对等点粒度。例如,GCVP可以指定:

    • 每个对等点可以信任一个被11个不同对等点认可的检查点。
    • 有一种特殊的部署,每个排序者和对等点搭配在同一台机器(即信任域)上,这时f个排序者可能出现(拜占庭)错误,每个对等点可以信任一个被f+1个搭配排序者的对等点信任的检查点。

猜你喜欢

转载自blog.csdn.net/qq_35753140/article/details/79511314