超级账本是Linux基金会发起的项目,意在提供一套企业级区块链应用框架,便于大家开发基于区块链技术的应用。
Fabric的基本概念
身份
由于Fabric并不是公有链,不是所有人都有权限访问区块链上的所有数据,支撑这个特性,就需要有身份的概念,不同的身份拥有不同的权限。Fabric中使用X.509证书作为身份标志,采用传统的公钥基础结构(PKI)分层模型。
下面举个栗子来说明在Fabric中的身份使用:
你去超市买东西结账的时候,看到收银台只接受Visa卡支付,如果你尝试用MasterCard去支付,超市是不会接受的,尽管你的MasterCard是真实的,并且有余额。
你的Visa卡和MasterCard卡都代表着你的身份,你可以使用卡里的钱,但超市接不接受,是另外一回事了。在Fabric中,PKI证书颁发机构和MSP协同工作。PKI提供不同类型的身份证明,MSP则确认哪些身份是参与网络给定组织的成员。数字证书
数字证书中可以包含某个公钥,且证书是经过对应私钥签名的,而对应私钥则存放在CA(证书颁发机构),只要双方都信任CA,那么数字证书拥有者就可以分发他的数字证书来证明其身份,任何阅读数字证书的人都可以验证证书是否被篡改过。FabricCA
在Fabric系统中提供了CA组件来管理证书。因为证书在Fabric系统中很重要,是确认权限及身份的根本。FabricCA颁发的证书只能在Fabric内部使用,放到外部,比如浏览器中,是不被承认的。证书撤销列表
这个很好理解了,某些成员可能会退出Fabric系统,那么其对应的身份也应该被删除。MSP
MSP可以列出谁是网络参与者或通道(Channel)成员,并定义网络中的访问权限。MSP的配置会被传播到相应组织的参与者所有参与的所有通道(Channel)中,并维护一个本地的MSP以做本地验证用,而不用每次都去请求远端MSP。将MSP映射到组织
大多数情况下,组织和MSP之间的关系是独占的。某些情况下,一个组织也可能需要多个成员资格组,也就是多个MSP。这种情况下对MSP的命名就显得很重要,通常组织和MSP的命名是这样的:比如我们有个组织ORG1,对应的MSP命名为ORG1-MSP,如果是多个MSP,可以命名为ORG1-MSP-NATIONAL,ORG1-MSP-PERSONAL等,方便查看。-
本地和通道(Channel)MSP
MSP只出现在两个地方,一个是节点(peer)本地,一个是通道(Channel)中。本地定义的MSP是为了peer,orderers及用户使用,通道(Channel)的MSP是控制所有在通道(Channel)中的节点。用户本地的MSP允许用户侧在其交易中作为通道(Channel)成员来验证自己。相反,通道(Channel)的MSP在通道(Channel)层面定义了参与者的权利。参与通道(Channel)的组织必须为其定义一个MSP,通道(Channel)上的peer和orderers都共享通道(Channel)的MSP,因此能够验证通道参与者的身份。这意味着,如果一个组织想加入某个通道(Channel),则需要将这个组织的MSP加入通道(Channel)配置中,否则这个组织在通道(Channel)中的交易将被拒绝。
如上图所示,看看区块管理员安装和实例化合约会发生什么。
管理员B连接到peer是通过peer本地MSP验证由RCA1颁发给B的证书来确认B的身份。当B准备在peer上部署合约时,peer上的MSP(ORG1-MSP)会验证B的身份以确认B是否有权限这么做,如果验证通过,那么B可以在peer上部署合约。如果B想在通道(Channel)上部署合约,这就是一个在通道(Channel)上的操作,通道上所有的组织都需要同意后B才可以在通道上部署合约,peer必须先检查通道MSP以确认是否有足够的权限。 MSP级别
网络(Network)MSP:网络MSP的配置通过定义了网络中组织的MSP及网络中的管理员来确定谁是网络中的成员
通道(Channel)MSP:通道必须分别维护其成员的MSP。通道在特定组织之间提供了私密的通信,这些组织又对其进行管理控制。通道的策略解释了哪些参与者有权利加入到通道,并在通道上执行一些任务。请注意,管理通道的权限与管理网络配置通道(或任何其他通道)的权限之间没有必然的关系。管理权限存在于管理范围内。
对等(Peer)MSP:从概念上讲,对等节点的MSP和通道上的MSP功能相同,但仅对本地有效。
Orderer MSP:与Peer的MSP相同,orderer的本地MSP也在节点的文件系统上定义,仅适用于该节点。orderer也被单个组织拥有,也只有一个MSP能列出它信任的节点或参与者。-
MSP结构
如图所示,MSP有9个元素
根CA:此文件夹包含由此MSP表示的组织信任的根CA的自签名X.509证书列表。此MSP文件夹中必须至少有一个Root CA X.509证书
中间CA:此文件夹包含组织信任的中间CA的X.509证书列表,每个证书都必须被MSP中的根CA或在信任链中能回溯到根CA的中间CA签名。中间CA可以代表组织的子分支或组织自身。与根CA文件夹类似, 存放着能被组织确认身份的由CA颁发的证书。
组织单位(Organizational Units):这些单位列在$FABRIC_CFG_PATH/msp/config.yaml 文件中,是被MSP视为组织成员的。当您希望将组织成员限制为拥有其中包含特定OU的身份(由MSP指定的CA之一签名)的成员时,此功能尤其有用。指定OU是可选的。如果未列出任何OU,则作为MSP一部分的所有身份(由根CA和中间CA文件夹标识)将被视为组织的成员。当您希望将组织成员限制为拥有其中包含特定OU的身份(由MSP指定的CA之一签名)的成员时,此功能尤其有用。
管理员:此文件夹包含一个标识列表,用于定义具有此组织管理员角色的参与者。对于标准MSP类型,此列表中应该有一个或多个X.509证书。值得注意的是,一个参与者具有管理员的角色,并不意味着他们可以管理特定的资源!给定标识被赋予在管理系统方面的限定由管理系统资源的策略决定。例如,通道(Channel)策略可能指定ORG1-MANUFACTURING 管理员有权将新组织添加到通道,而 ORG1-DISTRIBUTION管理员则没有此类权限。即使X.509证书具有一个ROLE属性(例如,指定一个参与者是一个admin),这指的是一个参与者在其组织内而不是在区块链网络中的角色。这类似于OU属性的目的,如果已定义属性,则指的是参与者在组织中的位置。如果已编写该通道的策略以允许组织(或某些组织)的任何管理员执行某些通道上的功能(例如实例化链代码),则该ROLE属性可用于在通道(Channel)级别授予管理权限。通过这种方式,组织角色可以赋予网络角色。
证书撤销:如果已撤销参与者的身份,则在此文件夹中保存有关身份的信息 - 而不是身份本身。对于基于X.509的标识,这些标识符是SKI和AKI的字符串对,用来确保当证书被使用的时候没有被撤销。此列表在概念上与CA的证书吊销列表(CRL)相同,但它也与从组织中撤消成员身份有关。因此,MSP的管理员(本地Local或通道Channel)可以通过发布颁发证书的CA的CRL的更新来快速撤销组织中的参与者或者节点。这个“列表的列表”是可选的。它只会在证书被撤销时填充。
节点标识:此文件夹包含节点的标识,即加密材料 - 与内容的组合KeyStore- 将允许节点在发送给其信道和网络的其他参与者的消息中验证自身。对于基于X.509的标识,此文件夹包含一个X.509证书。这是peer在交易提议响应中放置的证书,例如,用于指示peer已经认可它 - 随后可以在验证时针对结果交易的认可策略进行检查。此文件夹对于本地MSP是必需的,并且该节点必须只有一个X.509证书。它不用于通道MSP。
Keystore的私钥:这个文件夹是用于存放peer或者orderer节点的(或者用户的本地MSP)签名密钥(signed key)。此密钥以加密方式匹配节点标识 文件夹中包含的节点标识,并用于签署数据 - 例如签署交易提议响应,作为认可阶段的一部分。
TLS根CA(TLS Root CA):这个文件夹包含了组织在进行TLS通信时信任的根CA的自签名证书列表。当peer需要连接到orderer以同步账本数据时会用到TLS通信。
TLS中间CA(TLS Intermediate CA):此文件夹包含由该MSP所代表的组织信任的名单中间CA证书的CA 的TLS通信。当商业CA用于组织的TLS证书时,此文件夹特别有用。与成员资格中间CA类似,指定中间TLS CA是可选的。 -
Peer
区块链的网络由节点构成,每个对等节点都拥有一份账本(Ledger)和智能合约(Smart Contract)的拷贝。在下图的示例中,网络N由对等节点(Peer)P1,P2,P3构成,每个节点都保存着属于它们自己的分布式分类账本L1的实例,P1,P2,P3都使用码链(ChainCode)S1来访问自己保存的账本。
Peer可以被创建,启动,停止,重新配置及删除。通过一组公开的API可以允许管理员及应用程序访问Peer的服务。
-
账本(Ledgers)和码链(Chaincode)
一个节点(P1)可以包含一个账本的实例(L1)和一个码链的实例(S1),也可以包含多个账本实例和多个码链实例。正因为peer上有账本和码链的实例,管理员或者应用程序想访问这些资源的话就必须和peer交互。这也是为什么peer是超级账本中最基本的区块链设施。新启动的peer上即没有账本实例,也没有码链实例的。
-
多账本(Ledgers)
一个节点可以只包含一个账本实例,也可以包含多个账本实例,这样可以增加系统设计时的灵活性。当需要peer存放多个账本时,也是可以做到的。
当节点(peer)包含多个账本时,每个账本可以对应0个或多个码链(Chaincode)。如上图所示,节点P1包含账本L1和L2,访问L1的数据可以使用S1提供的服务,访问L2的数据,可以使用S1和S2提供的服务。当然,你的账本可以不部署任何码链,这意味着你没有渠道访问账本内容,这样的配置比较少见。大部分的节点都有至少一个码链提供对账本数据的访问及更新。值得一提的是,不管你有没有为了外部可以访问账本数据而部署码链,节点都有一个特殊的系统码链,我们后面再详述这个系统码链。
多码链(Chaincode)
节点中码链数量和账本数量的关系并不是绝对一一对应的。如上图所示,P1有账本L1和L2,访问L1的数据可以通过码链S1和S2,访问L2的数据可以通过码链S1和S3。可以看出,S1可以访问L1和L2两个账本的数据。后面我们会详述为什么通道(Channel)的概念在peer拥有多个账本和多个码链时是那么重要。-
应用(Applications)和节点(Peer)
现在我们来介绍应用程序是如何连接到节点并访问节点上账本数据的。应用想在节点上查询账本信息的操作通过简单的三个交互步骤完成,账本数据的更新要复杂一点,需要多两步操作。为了更容易理解这些流程,我们简化了一下这些步骤。不用担心简化后的流程会影响你的理解,最重要的是理解应用程序连接到节点进行账本数据查询操作和更新账本操作的区别。
应用程序总是需要连接到节点上来获取账本数据或者使用码链。超级账本的SDK为程序员提供了很简便的方式去访问节点,调用码链生成交易,提交交易到网络并同步到其它分布式账本中,获取交易结果等。
通过连接到节点上,应用程序可以调用码链来查询或者更新账本,查询操作会立即返回,更新账本的操作要复杂一点,涉及到application,peer和orderer,我们来详细看看。
peer和orderers联合起来确保每个节点上的账本是最新的。在上面的例子中,应用程序A连接到P1,然后调用S1对L1进行查询或更新操作。P1调用S1生成一个包含查询结果或者账本更新提议。应用程序A收到响应后,能立即拿到查询结果。对于更新,A根据所有的响应构建一个交易(事务),并把它发送给O1进行排序(A builds a transaction from all of the responses, which it sends it to O1 for ordering)。O1搜集整个网络的交易(事务),将它们放入区块中,并分发给所有节点(peer),包括P1。P1在把这些交易加入到L1前会验证这些交易(事务)。一旦L1被更新了,P1会生成一个事件,通知A操作完成。
peer能立即返回A查询结果是因为满足查询条件的数据都在peer的本地账本中。peer不会联络其它peer以及时响应A的查询。应用程序可以连接到多个Peer上执行查询操作,比如你想验证多个peer上的查询结果是否一致,你就可以连接到多个peer上查询。从图中可以看出,查询操作很简单,三步而已。
更新操作与查询操作相似,但多了两步。尽管需要更新账本的应用程序可以连接到peer上调用Chaincode,但与查询不同的是,单个节点不能执行账本更新操作,因为其它peer必须先同意这次操作才行,这个过程叫共识。因此,peer会返回应用程序建议更新-如果其余peer意见一致,该peer才会执行这个操作(apply subject)。除了查询操作的三步外,第四步,应用程序需要发送一套合适的更新建议给所有的peer,作为一条交易放在他们各自的账本中。这个过程是通过order对交易进行打包并放入区块中,然后分发给网络的所有peer,peer在把区块加入到本地账本前会先验证区块有效性。整个ordering操作需要一段时间才能完成(几秒钟),应用程序会收到异步通知,也就是图中的第五步。 -
节点和通道(Peers and Channels)
peer节点,orderer节点和应用程序通过加入通道(Channel),共同管理,共享和加入的通道相关联的在peer上的独立拷贝的账本。你可以理解成你有多个朋友圈,每个朋友圈相互独立,每个朋友圈有一些全员参与的活动。或者,某个朋友可能处于多个朋友圈中,不管怎样,每个朋友圈是相对独立的。
通道允许一些特殊的peer和应用程序在一个区块链网络中互相通信。在上图的例子中,应用程序A可以直接在通道C中和P1,P2通信。你可以把通道理解成特殊的一些peer和应用程序通信的渠道。
通道并不像peer那样存在与物理环境中,你可以把通道理解成在peer组成的物理网络中的逻辑结构。另外一个重点是,peer提供了通道的访问和管理控制点。 -
节点和组织(Peers and Organizations)
现在你应该了解了peer和peer与账本(Ledgers),码链(Chaincode)和通道(Channel)之间的关系了。接下来你会了解到区块链网络中多个组织之间的关系。
区块链网络有多个组织,而不是单个组织管理。Peer是分布式网络如何构建的中心,因为组织拥有他们,也是构成组织网络的连接点。
在网络中的peer有多个组织。区块链网络是由属于不同组织的peer构建而成。上图的示例中我们看到4个组织,为网络提供了8个peer。通道C在区块链网络中连接了P1,P3,P5,P7,P8。其余的在组织中的peer没有加入到通道C中,但加入了至少一个其它的通道。一个被组织部署的应用程序会连接到组织的peer上,其余的组织也是如此。为了简化,orderer节点没有出现。
在这种组织架构下的区块链网络会发生什么情况是你需要了解的重点。这些组织构成了,并管理着区块链网络,也为网络贡献了资源。peer是我们要讨论的资源,当然,组织提供的资源不仅仅是peer。
没有组织提供的独立的资源,这个共享网络将不复存在。网络的扩大或缩小都由这些协作的组织决定。
你会发现,除了orderer外,没有中心化的资源。在上图的示例中,如果没有组织贡献的peer节点,网络就不存在。并且,网络并不被任何独立的组织所影响,只要有一个组织存在,网络就存在,这是去中心化的本质。
不同组织中的应用程序可以相同,也可以不同,这完全取决于组织中的应用程序如何操作对应peer中的账本副本。这意味着不同组织的应用程序及其展现的逻辑可能大不相同,尽管他们对应的peer都操作着相同的账本数据。
应用程序是连接到当前组织的peer还是其它组织的peer取决于应用程序需要以怎样的方式与账本交互。 -
节点和身份(Peers and Identity)
Peer通过来自特定证书颁发机构的数字证书为其分配身份。您可以阅读更多有关X.509数字证书的内容,但是,现在,将数字证书视为一张ID卡,它提供了许多关于Peer的验证信息。网络中的每个Peer都有一个由对应组织管理员颁发的数字证书。
当一个Peer连接到Channel,通过Channel的MSP,可以验证节点所属的组织。在上图的示例中,P1和P2的身份由CA1颁发。Channel C根据其Channel配置中的策略确定来自CA1的身份应使用ORG1.MSP与Org1相关联。类似地,ORG2.MSP将P3和P4识别为Org2的一部分。
当peer通过Channel连接到区块链网络中时,Channel根据配置的策略通过peer的身份来决定它有什么权限。组件Membership Service Provider (MSP)提供了组织身份的映像,这决定了peer在组织中能获得什么身份以及对应的访问区块链资源的能力。后面我们会介绍MSP,但就目前而言,将MSP视为在区块链网络中提供个人身份与特定组织角色之间联系的桥梁。
Peer或者任何与区块链网络交互时都需要从他们的数字证书和MSP中获取组织身份。如果Peers,应用程序,终端用户,管理员,orderers想访问区块链网络,他们必须拥有身份及相关的MSP。我们用身份-主体(an identity --- a principal)的方式来命名所有与区块链网络交互的实体。后面我们会说到主体(principal)和组织(organizations),但现在,只需要理解Peer。
最后,注意,Peer的物理位置在哪里并不重要,重要的是Peer的身份及属于哪个组织( it's the identity associated with it that identifies it as being owned by a particular organization)。在上面的例子中,P3可以不在Org1的数据中心,但只要它的数字证书是由CA2颁发的,那它就是Org2的Peer。 -
节点(Peers)和Orderers
应用程序和Peers交互时需要确认账本是一直保持同步的,而处理这个过程的特殊节点叫做Orderers,接下来我们就关注它。
更新一笔交易(事务)与查询一笔交易(事务)的区别很大,仅靠一个Peer是不能更新账本的,更新操作是需要网络中其它Peer同意的。一个Peer要更新账本信息,并同步到其它Peer节点上之前,需要经过统一。这个过程称为共识(consensus),这个过程比查询要花费更多的时间。但当所有Peer都同意一笔交易(事务)可以执行,这个交易(事务)才可以提交到账本中,Peers会通知应用程序账本更新了。接下来我们聊聊更多关于Peers和Orderers如何管理共识。
特别的,应用程序想更新账本需要经过3个阶段,这是为了保证网络中所有节点的账本保持一致。第一阶段,应用程序与背书Peer节点子网协同工作,每个背书Peer提供给应用程序账本更新操作的背书,确认这个操作,但并不会把这个更新操作实实在在的更新到账本上。第二阶段,这些分散的背书(或者叫操作记录吧)汇集到一起,当成一笔交易打包进区块。最后阶段,这些区块被分发给所有Peers,区块中的交易在被验证后,会加入到Peer本地的账本中。接下来我们更详细的说说这三个阶段,你将会了解到Orderers是共识过程中的关键。
过程1
第一阶段,交易(事务)的工作流程只涉及到应用程序和一些Peers的交互,不会牵扯到Orderers。阶段1只关心应用程序请求不同组织的背书Peer认同调用Chaincode的执行结果。在开始阶段1之前,应用程序生成交易(事务)提议,并发送给它需要的对其提议背书的Peer。每个背书Peer根据交易(事务)提议独立地执行Chaincode,以生成交易(事务)提议的响应。这个操作并没有把更新操作应用到账本中,而只是对交易(事务)提议进行签名然后返回给应用程序。一旦应用程序收到足够多的被签名的提议响应,第一阶段的交易流程就完成了。接下来详细看看第一阶段的流程。
交易提议被peer独立执行,并给返回被认可的响应。在上图示例中,应用程序A1生成交易T1及提议P,发送给通道C中的P1和P2。P1将T1P(transaction T1 proposal P)作为输入,执行S1,得到T1R1,被认证后记为E1。P2也独立地执行S1,将T1P作为输入,计算后得到T1R2,被认证后记为E2。应用程序A收到两个成功背书的响应,也就是E1和E2.
最开始,应用程序会选出一组peer来生成账本更新提议。哪些peer会被选出来是依据的背书策略,这个背书策略决定了哪些组织需要在广播账本更新提议前对更新提议进行背书。这会影响到共识方式,任何一个关心更新提议是否背书的组织都会在广播给peer更新提议并被peer接受前确认提议是否有背书。
peer对一个提议响应进行背书,就是把自己的数字签名加入到响应中,并用自己的私钥对整个响应签名。背书内容随后可以被用于证明这个响应是某个组织的peer生成的。在我们的例子中,如果peer P1属于组织1(Org1),那么背书E1就相当于可以证明L1上的交易T1和响应R1是由Org1的peer P1提供的。
当应用程序得到了足够多的签名的提议响应时,第一阶段就结束了。
我们注意到peer可能返回不同的信息,因此同一笔交易可能有不一致的返回信息。这可能由于响应是在不同时间,不同peer,在不同账本状态下生成的,大多数情况下应用程序可以多次请求更新的提议响应。另外更严重,但概率很小的原因是因为链码的不确定性导致的响应不一致。不确定性是链码和账本的大敌,如果这种情况发生了,对提议交易来说是很严重的,不一致的提议响应肯定不能提交到账本中。一个独立的节点是不可能知道交易结果是非确定性的交易,在检测到非确定性交易前,必须将交易汇总比较(严格地说,即使这还不够,但我们将此讨论推迟到交易部分,其中详细讨论了非确定性)。
在第一阶段结束时,如果应用程序希望如此的话,可以放心丢弃不一致的响应以提前结束交易流程。后面我们会看到如果应用程序使用不一致的响应提交到账本时,会被拒绝。
过程2 打包
第二个交易流程是打包。Orderer节点这个过程关键的点,它接收来自很多应用传来的背书过的提议交易响应。Orderer对交易进行排序,并将大量的交易打包进区块,并准备将区块分发到所有连接到Orderer的peer,包括背书peer。
orderer的第一个角色就是打包账本更新提议。在上图的例子中,应用A1发送给Orderer O1一个被E1和E2背书的交易T1。同时,应用A2发送给Orderer O1一个被E1背书的交易T2。O1将A1传来的交易和A2传来的交易以及其它交易共同打包进区块B2。我们可以看到区块B2里的交易排序是T1,T2,T3,T4,T6,T5,并不一定是按照到达orderer节点的顺序(这个例子展示了一个非常简单的orderer配置)。
Orderer节点会同时收到网络Channel中不同应用程序发送的账本更新提议。Orderer节点的任务就是按照事先定义好的顺序整理这些更新提议,并把它们打包进区块,为下一步的分发做准备。这些区块将构成区块链。一旦Orderer节点生成了期望大小的区块,或者超过最大等待时间,Orderer会向连接到它特定Channel的Peer发送区块。第三个过程会详述这个流程。
区块中的交易排列顺序和交易到达Orderer节点的顺序没有直接关系。交易在区块中可以是任意的排列顺序,这个次序就是交易执行的顺序。重点是有一个严格的交易排序,但具体是怎样的排序并不重要。
区块中的严格交易顺序排列使得Fabric与公链中一笔交易可以被打包进多个不同区块的情况不同。在Fabric中,这不可能发生,由多个Orderer生成的区块就是最终的区块,因为交易被写入区块后,交易的位置顺序就确定了。这意味着Fabric不会存在分叉。一旦交易被写入区块,以后就不能再重写了。
我们可以看到,peer是存储账本和链码的,orderer完全不会存储这些。每一笔交易到达orderer时,orderer只是机械的将交易打包进区块,而不会理会交易的价值,额度等。这是Fabric的一个重要特性,所有交易都会按照一个严格的顺序进行整理,没有交易会被抛弃掉。
到第二阶段结束时,我们可以了解到orderer的责任就是进行必要的,简单的收集交易更新提议,将他们排序,打包进区块,准备分发出去。
过程3 认证
最后一个交易工作流程是分发和验证从orderer到peer的区块,如果验证成功,将会被提交到账本中。
特别的,在每个peer中,在区块中的每一笔交易在更新到账本之前都是验证过的,以保证所有交易都是由相关的组织背书过的。失败的交易会保留,作为日后审查用,并不会更新到账本中。
Orderer除了在过程2中的打包角色外,在过程3中还负责分发区块到peer节点。在这个例子中,O1分发区块到P1和P2。P1处理区块2,然后将区块2添加到P1的账本L1中。同时,P2处理区块2,然后将区块2添加到P2的账本L1中。一旦操作完成,账本L1在P1和P2中都被更新了,每个Peer都可以向连接到他们的应用程序发送处理结果。
Orderer向连接到他的Peer分发区块是过程3的开始。连接到orderer节点的某个渠道的peer,会收到orderer生成的新区块的一份拷贝。每个peer节点都会独立的处理收到的区块,但所有peer处理区块的方式都是相同的。采用这种方式,不同peer中的账本可以达成共识。并不是所有的peer都必须连接到orderer节点,peer和peer之间可以通过gossip协议来传递区块,这样peer也可以独立的处理相同区块。
收到一个区块后,peer会按照交易在区块中出现的顺序依次处理。对于每一笔交易,peer会按照生成这笔交易的链码背书策略检查交易是否被与之相关组织的背书。例如,某些交易可能只需要一个组织背书,而另一些交易需要多个组织同时背书才有效。这个验证过程验证了所有相关组织产生的结果或者输出是否一致。同时请注意,第三阶段的验证和第一阶段不同,阶段一只是应用程序收到背书节点的响应,判断是否需要发送交易提议。如果应用程序发送错误的交易,违反了背书策略,在第三阶段的验证过程中peer还是可以拒绝本次交易。
如果交易背书正确,peer将尝试把交易提交到账本中。为了能写账本,peer必须进行账本一致性检查,保证当前账本的状态与账本更新后的状态一致。这个状态并不总会是一致的,即使交易拥有完整的背书。举个栗子,另外一笔交易可能已经更新了账本中的同一个资产,以至于我们正要更新的交易将永远不会被写入账本。这样的话,每个节点中的账本必须通过网络保持共识,每个节点的验证方式是一样的。
在peer验证完每笔独立交易后,将更新账本。失败的交易会保存下来作为审查资料。这意味着peer中的区块和从orderer中收到的区块一致,除了区块中指示交易成功或失败的标志。
我们也要注意到,第三阶段并没有执行链码,这一步只会在第一阶段完成,这很重要。这意味着链码只在背书节点可用,而不是整个网络中都可用,这保证了链码在背书组织中的安全及私密。这和收到链码的执行结果不同,执行结果会分享到所有在Channel里的peer,不论他是否能背书交易。背书节点的这种设计方式是为了方便扩展。
最后,每次区块被提交到peer的账本中时,这个peer会生成对应的事件。区块事件包含区块的所有内容,而区块交易事件只包含简要信息,比如每笔区块中的交易是否有效。由链码的执行而产生的链码事件也可以在这个时候发布。应用程序可以注册这些事件,当这些事件发生时,可以收到通知。这些通知在交易工作流程的第三阶段和最后阶段完成。
总的来说,我们可以知道第三阶段由orderer产生的区块被不断地同步到账本中。区块中交易的严格排序能让每个peer在区块链网络中始终如一地验证交易并提交到账本中。
Orderer和共识
整个交易工作流程被称为共识,因为所有peer都认同交易的排序和内容,在执行过程中由orderer节点来协调。共识是多步骤的过程,应用程序只会在共识过程结束时收到通知,但通知的时间在不同的peer上可能不同。
我们将会在后面更多的探讨orderer,现在,把orderer仅仅当做从应用程序收集、分发账本更新提议到peer,由peer进行验证及更新账本的过程。
- 私有数据
什么是私有数据
当一个Channel中的一些组织需要保证数据的私有,不让Channel中的其它组织访问,这些组织可以单独创建一个Channel,只包含有权限读取这些私有数据的组织。然而,如果出现这种需求就创建新Channel的话,会增加管理员的管理负担,并且不支持让所有在Channel中的人员都能看到所有交易的同时,保持一些私有数据。
这就是为什么Fabric在v1.2之后加入了私有数据集(private data collections),它允许在Channel中创建子组织,这些子组织可以背书,提交,查询这些私有数据,而不需要重新创建一个Channel。
什么是私有数据集
这个集合包含两个元素
- 真实的私有数据在有权限查看私有数据的组织间通过gossip协议点对点传输。这些私有数据被存放在peer的私有数据库中(有时候被称为“side”数据库,或者“SideDB”)。排序服务在这里就不需要了,也看不到私有数据。注意,设置gossip协议需要先初始化锚节点,因为需要用到跨组织间的通信。
-
私有数据哈希,这个哈希拥有背书,被排序了,同时写入了Channel中每个peer的账本。这个哈希服务可以作为交易的证据,状态验证的证据或者审计需要。
下面的图示展示了一个账本中被授权能访问私有数据的peer和一个没有权限访问私有数据的peer。
集合中的成员可以分享私有数据给其它成员,如果他们发生争执,或者他们想把资产转让给第三方。第三方可以计算私有数据的哈希,看他满不满足Channel中账本的状态。证明这个状态是在两个交易集间的某个确定时刻是存在的。
什么时候用Channel中的交易集,什么时候新建一个Channel
- 当整个交易需要对Channel中的某些组织保密的时候可以用新建Channel的方法
- 当交易需要对一些组织公开,但这些组织中又只有一部分可以存取交易中的某些信息时可以使用私有数据。另外,因为私有数据通过peer和peer之间传递,而不是通过区块传递,所以交易数据必须对orderer节点不可见。
使用私有数据的交易流程
当链码中使用了私有数据时,为了保证数据的私有,交易的提议,背书和提交到账本的过程会有些许不同。
- 客户端应用程序提交一个调用链码函数(读或者写私有数据)提议请求到集合中属于授权组织的背书节点。在链码中的私有数据或用于生成私有数据的数据被放置在交易提议的
transient
字段中。 - 背书节点模拟执行交易,把私有数据存储到
transient data store
中(peer节点中的本地临时存储区域),并根据数据集策略(collection policy),通过gossip协议把私有数据发送给授权的peer。 - 背书节点将交易提议的处理结果响应放在公共数据中返回给应用程序,这些处理结果包含私有数据的键值及内容的哈希。私有数据本身不会返回给应用程序。更多关于私有数据的细节可以阅读这篇文章
- 应用程序客户端(应该值的就是SDK)提交交易(包含私有数据的哈希)到orderer节点,orderer节点按照正常流程打包区块。包含私有数据哈希的区块被分发到各个peer节点。这样一来,在Channel中的所有peer都可以独立的验证包含私有数据哈希的交易,而不需要知道真正的私有数据内容。
- 在区块验证期间,被授权的peer根据数据集策略(collection policy)来判定自己是否有权限读取私有数据。如果有权限,他们会先验证本地的私有数据来判定他们是否在链码背书阶段就收到了私有数据,如果本地没有这些私有数据,他们会从其他peer中拉取。随后他们会验证私有数据与区块中的私有数据哈希值,然后提交交易和区块。在验证和提交过后,私有数据的拷贝被移动到了私有状态数据库及私有数据写集存储中。最后在peer的
transient data store
中的本地私有数据将会被删除。
举个栗子解释集合(collections)
比如现在我们在一个Channel中有5个组织来处理交易过程:
- 一个买产品的农民
- 一个运输产品的分销商
- 一个提供两地运输的物流商
- 一个从分销商批发产品的批发商
- 一个销售从批发商或物流商得到的产品的零售商
分销商可能想让他和农民及物流商的交易私有化,不让分销商和零售商看到他们之间的交易,以免暴露自己的价格加成。
分销商也很有可能想让他与批发商的私有数据独立,因为批发商向他们要的价格要比他们给零售商的低。
批发商也可能需要它与物流商和零售商的数据保持私有。
相比于为这些数据关系建立单独的Channel,私有数据集合(Pirvate Data Collection)可以用来共享私有数据在:
- PDC1:分销商,农民和物流公司间
- PDC2:分销商和批发商之间
- PDC3:批发商,零售商和物流公司间
在这个例子中,分销商拥有的peer的账本中包含多条私有数据,比如分销商,农民和物流公司之间的私有数据及分销商和批发商之间的私有数据。因为私有数据的数据库和通道中的账本是分开存储的,所以私有数据库也被称为“SideDB”。
私有数据是如何定义的
更多关于私有数据底层信息可以查看私有数据详情页面
清除数据
对于非常敏感的数据,有权力存取私有数据的节点可能需要,或者被政府要求在一定时间后清除这些数据,只保留这些数据的哈希作为证据。
在有些情况,私有数据只允许保留在peer的本地数据库中,直到它可以被拷贝出区块链网络以外的数据库。也有可能这些数据只能留在peer中,直到在链码执行完成后才能进行后续处理。为了支持后面的情况,一旦将一定数量的后续区块添加到私有数据库中,就可以清除私有数据。( To support the later use case, it is possible to purge private data if it has not been modified once a set number of subsequent blocks have been added to the private database)
- 账本
区块链账本
区块链账本包含两个不同但相关的部分,世界状态和区块链。
首先,这个世界状态是一个保存多个账本状态当前值的数据库。相对于遍历整个交易log而言,世界状态可以使得程序能很简便的获取这些状态的当前值(余额)。默认情况下世界状态是键值对形式的,后面我们会看到Fabric在这方面会有一定的灵活性。世界状态可以频繁改变,比如被创建,更新或删除。
第二,是区块链,一个记录了所有改变世界状态的日志。交易被记录在区块中,区块可以被添加到区块链中,区块链可以使你知道导致世界状态改变的历史记录。区块链的数据结构和世界状态很不同,因为区块链只要被写入,就不能修改。区块链是一个不能被修改的序列,每个区块都包含有序(已排序)的交易。
账本L由区块链B和世界状态W组成。区块链B决定了世界状态W,或者说世界状态W源于区块链B。
认为在Fabric网络中存在一个虚拟的账本是有助于你理解的。实际上,区块链网络中维护多份账本的拷贝,通过共识过程保持这些拷贝的一致性。接下来我们详细讲述一下这些概念。
- 世界状态
世界状态代表了所有账本状态当前的值。这个世界状态非常有用,因为程序通常需要某个账本的当前状态值,并且总是很容易就能获取到。你不需要遍历整个区块链去计算账本当前的状态的值(余额),你可以直接从世界状态获取。
图中有一个账本状态key=CAR1,value=Audi。还有一个账本状态key=CAR2,value={model:BMW, color=red, owner=Jane}。两个账本version都是0。
账本状态用来记录需要通过区块链分享的应用程序信息。图中显示了两辆车的账本状态,CAR1和CAR2。你可以发现状态信息包含一个key和一个value。你的应用程序调用链码,链码通过API访问这些状态。他们通过状态的key来存,取,删除状态。注意状态值什么时候可以简单(如同Audi),什么时候可以复杂(如同BMW)。
物理上,世界状态是一个数据库实现。这样做有很大的好处,因为数据库可以提供丰富的操作,更有效的存储及状态回滚功能。后面我们将会看到Fabric中针对应用程序的需要,配置不同的数据库,不同的状态值类型及存取模式,比如复杂的查询操作所需要的配置。
交易改变着世界状态,正如你所想的,交易有生命周期。他们被应用程序创建,在被提交到区块账本中时结束。整个生命周期的具体描述参见这里。但是Fabric设计的关键点是只有背书组织背书过的交易才能改变世界状态。如果交易被有被足够多的背书节点背书,那么验证将会失败,不会影响世界状态。
你也会注意到世界状态有一个版本号,CAR1状态和CAR2状态都在起始版本号,0。这个版本号在世界状态被修改是会自增。也可以检测版本号与交易创建时的版本号是否相同来判断世界状态是否更新过。这种检测保证了版本号从我们预期的相同的值变成了交易创建时预期相同的值。
最后,账本最初创建时,世界状态为空。因为任何一笔对世界状态有改变的交易都被记录在区块链上,这意味着世界状态可以通过区块链在任意时间重新生成。这是非常便利的,举个栗子,当一个peer启动时,世界状态会自动生成。而且,如果一个peer异常关闭,世界状态可以在peer重新启动后,交易被接收前重新生成。
- 区块链
区块链是一个交易记录的集合,由相连的区块构成,每个区块中包含一些列有序交易,每笔交易都可以当做对世界状态的一次查询或者更新。交易的排序机制在这个网页中有详细的讨论,重点就是区块的顺序,区块中的交易的顺序是在区块被创建之初就定了。
每个区块的区块头中都包含区块中所有交易的一个哈希值,同时也包含前一个区块头的哈希值。这样,账本中的交易都是有序的,加密的链接在一起。哈希和链接使得账本数据很安全。即使一个peer上的账本被篡改了,它也不能说服其他peer它自己拥有正确的账本,因为账本是分布式存储在网络中独立的节点上的。
物理上,区块链通常由文件存储方式来实现的,对于世界状态而言则相反,用的是数据库。这是一个明智设计,因为区块链的数据结构庞大,而且倾向于少量的简单操作。在区块链尾部添加区块是主要的操作,查询是相对较少的操作。
让我们稍稍进一步看看区块的细节
图中的区块链B包含B0~B3四个区块,B0是第一个区块,也就是创世块。
我们可以看到区块B2含有区块数据D2,D2中有T5,T6,T7三笔交易。
最重要的是,B2有区块头H2,H2包含D2中所有交易的哈希值,也包含前一个区块B1的所有交易的哈希值。这样,区块就永久不可分的链接在一起。
最后,你可以看到第一个区块叫创世块。这是账本的起点,它不包含任何用户的交易,取而代之的是包含Channel初始状态的配置的交易。我们会在讨论区块链网络和Channel时详细讨论创始区块。
- 区块
区块由三部分组成
区块头
区块头中有三个部分,区块号,当前区块哈希,上一个区块哈希。区块号最初为0,也就是创世块,有新区块被添加进来后,这个数值增加1。当前区块哈希是当前区块中所有交易生成的哈希。上一个区块哈希就是上一个区块的哈希值。
图中区块B2的区块头H2包含区块号2,当前区块数据D2的哈希值CH2和上一个区块的哈希值PH1
区块数据
这个部分包含排好序的交易,当区块生成时这些交易被打包进区块。这些交易的数据结构字段多,但很简单。
区块元数据
这个部分包含区块的创造时间,证书,公钥和生成区块者的签名。然后,区块提交者也会在区块中每笔交易里添加有效和无效的标签,但这些信息在计算哈希值时被忽略了。区块生成时便也生成了这些元数据。
- 交易
正如我们所了解的,交易记录了对世界状态的改变。现在我们来细致了解下交易的结构。
我们可以看到,区块B1里的数据D1中包含了交易T4,T4包含交易头部H4,交易签名S4,交易提议P4,交易响应R4和背书节点列表E4。
交易头部
图中交易头部H4包含了必不可少的元数据,比如相关链码的名字和版本。
交易签名
图中交易签名S4包含了一个加密的数字签名,由客户端应用程序创建。这个字段用来检测数据是否被篡改,因为生成签名必须要应用程序的私钥。
交易提议
图中的交易提议P4是将应用程序提供的创建交易提议的参数编码后的结果。当链码执行后,提议会提供一系列的输入参数,包含当前世界状态,这决定着新世界状态。
响应
图中的响应R4包含世界状态改变前,后的值,也就是读写集。响应是链码的输出结果,如果交易成功生效,它将会被应用到账本中并改变世界状态。
背书
图中的E4包含达到背书策略规定的足够多的组织背书的签名过的交易响应。你可能会注意到,只有一个交易响应包含在交易中,但它有很多背书节点。这是因为每个背书节点都有效的将交易响应编码了,这意味着没有必要包含不满足背书节点要求的响应,也就是无效响应,不会更新世界状态的响应。
这一节讨论了交易中主要的一些字段,此外还有其它字段,但我们提到的字段是必备的,你需要对账本数据结构有清晰扎实的了解。
- 世界状态数据库选项
为了提供简单有效的存储,可回溯的账本状态,世界状态采用数据库的实现方式。如我们所见到的,世界状态可以很简单,也可以很复杂,为了容纳这些简单或复杂的世界状态,世界状态数据库的实现方式可以有所差异,可以用高效的方式来实现这些数值的存储。当前世界状态数据库有两个选项,LevelDB和CouchDB。
LevelDB是默认选项,当世界状态是key-value键值对的时候,很适合使用LevelDB。LevelDB和网络中的节点结合紧密,它是嵌入到操作系统中的。
CouchDB适合在世界状态值是结构化数据时使用,例如Json数据,因为CouchDB支持丰富的查询操作及商业交易中的多数据更新。CouchDB在另一个操作系统中运行,但保持与网络中节点数量1:1的关系。这些对于链码来说是透明的。了解更多关于CouchDB信息可以点击查看。
我们可以看到,对于LevelDB和CouchDB在Fabric中重要的特点是插件化。世界状态数据库可以是关系型数据库,图表信息存储或者是传统数据库。这提供了Fabric很强的类型灵活性,以有效应对不同类型的问题。
- 账本例子:fabcar
在结束账本的话题之前,我们看一个账本的例子,fabcar。如果你运行过超级账本示例程序,那么它会创建这样一个账本。
fabcar的应用程序创建了10种车,包括不同颜色,不用厂商,不同型号及不同的拥有者。图中表示了前4种车被添加进账本后的情况。
账本L构成了世界状态W和区块链B。W包含了4个状态,它们的key分别是CAR0,CAR1,CAR2,CAR3。B包含两个区块,0和1,区块1包含4笔交易,T1,T2,T3,T4。
我们可以看到世界状态包含CAR0~3对应的状态。CAR0的值指明了这是一辆蓝色Toyota Prius,所有者是Tomoko,我们可以看到其它车辆类似的状态值。并且,我们可以看到所有车辆状态版本号都是0,表明这是他们的起始版本号,从被创建到现在没被改变过。
例子中的区块链包含两个区块,Block0是创始区块,它并没有包含任何与车辆相关的交易。Block1包含了交易T1T4,对应着CAR03的初始化世界状态。我们可以看到Block1是链接到Block0的。