作者:Mercina-zy 如有侵权,请及时与我们联系
本文转载自区块链兄弟,区块链兄弟——技术专业问答先行者,区块链爱好者聚集地
本文约2500字,阅读(观看)需要20分钟
接上一篇,区块链原理之交易背书基本流程初探!
制图:Mercina-zy
交易背书的基本流程概括性的简述如下:
1)客户端创建交易后,发送请求到其选择的背书节点,即发送一个PROPOSE消息到交易所选择的背书节点集合。
2)背书节点模拟交易,然后生成背书签名。(在此首先会验证客户端的签名clientSig,然后模拟交易)
3)提交客户端获取交易的背书,通过排序服务进行广播。(之前提交节点会等待,当得到足够的消息及签名,得出交易已被背书的结论后,就会涉及其与背书节点之间的多轮交互;如果没有得到足够的消息及签名,其会放弃此次交易,稍后重试。在此的“足够多的消息及签名”和背书策略有关)
4)排序服务向所有节点投递传播交易的消息。
1)客户端创建交易后,发送请求到其选择的背书节点,即发送一个PROPOSE消息到交易所选择的背书节点集合。
为了调用交易,客户端会发送一个PROPOSE消息至交易所在的背书节点集合,这里可能不是同时发送。交易可以被发送到指定chaincodeID的所有背书节点。
当然,有一些背书者此时可能处于离线状态,还有可能一些可能会拒绝、不参与背书此次交易。这时就需要提交节点应尽可能的利用可用的背书者来完成背书以满足背书策略所需。
现对 PROPOSE 消息格式,提交客户端和背书者之间有可能存在的交互模式,进行说明。
PROPOSE消息格式
PROPOSE消息的格式为:
注:其中, tx 是必须要提供的,而 anchor 为可选参数。
如:
tx=
其中, clientID 是提交用户的 ID ;
chaincodeID 指的是交易所属的链代码的 ID ;
TxPayload 是发出的交易本身的有效载荷;
Timestamp 是对于每个新的交易,单调递增的整数,由客户端维护;
clientSig 是客户端交易消息中其它项的签名。
在进行调用交易和部署交易的时候,txPayload 的细节是不相同的,调用交易会应用一个特定部署的系统链代码。
对于一个调用交易,txPayload 是由以下两个域组成的:
txPayload =
注:其中,operation 是链代码提供的函数和参数;metadata 表示与调用相关的属性。
对于部署交易,txPayload 由以下三个域组成:
txPayload =
注:其中,source 表示链代码的源代码;metadata 表示与链代码和应用相关的属性;policies 包含了所有节点都能访问的链码策略,如背书策略。
需要说明的是,在部署交易当中,背书策略不是由 txPayload 来提供的,但是部署的 txPayload 包含了背书的策略 ID 和与之相对应的参数。
anchor 包含了读版本的依赖,如果客户端指定了 anchor 参数,那么背书者会在其本地的 KVS 匹配 anchor 中基于相应键的读版本号来进行背书交易。
交易加密 Hash 作为唯一的交易标识符 tid 被所有的节点使用。客户端会将 tid 存储在内存中,并且等待来自背书节点的响应。
注:(tid = hash(tx))
消息模式—提交客户端和背书者之间的交互模式
客户端和背书者决定了交互的序号。
举例,这样的典型场景:一个客户端向单个的背书节点发送
,此处没有 anchor 参数。背书节点产生版本依赖(anchor),客户端其后会用到版本依赖,并将其作为 PROPOSE 消息的参数发送给其它的背书者。
再举一个例子,客户端可以直接发送
消息到其选择的所有背书者上,至于采用何种通信的模式,客户端其可以自由选择。
2)背书节点模拟交易,然后生成背书签名。(在此首先会验证客户端的签名clientSig,然后模拟交易)
当接收到来自客户端的 PROPOSE 消息
,背书节点 epID 首先会验证客户端的签名 clientSig ,然后进行模拟交易。
在进行模拟交易时,会涉及背书节点,其将通过调用所属的链码临时执行一个交易(txPayload),并执行背书节点本地持有的状态副本。
在执行结果,背书节点会计算读版本依赖(readset)和状态更新(writeset)。这在 DB 语言中也称之为 MVCC+postimage。
状态由键值对组成,每个条目都包含排序后的版本信息,当存储在一个键下的值被更新时,这个排序的版本号就会增加。即,所有的键值对条目都是版本化的。
节点解释了通过链代码下所有的键值对访问的交易记录,读或者写。进一步说明就是,在背书节点执行交易之前,比如给定状态 s,对于每个键 k 通过交易读,(k, s(k).version)被增加到读集合(readset)当中。此外,对于每个键 k,通过交易修改 k 的值为 v' ,(k, v')被增加到写集合(writeset)当中。当然,v' 也可以是新值相对于先前的值增量。
需要注意的是若客户端在PROPOSE 消息中指定了 anchor ,那么客户端指定的 anchor 必须等于背书节点模拟交易产生的 readset 。
如果背书逻辑决定要对一个交易进行背书,其会发送 消息到提交客户端。
说明如下
tran-proposal:其等于(epID, tid, chaincodeID, txContentBlob, readset, writeset)
txContentBlob,是链代码/交易特定的信息,其目的是使 txContentBlob 作为交易的表示使用。
epSig,是背书节点针对交易提案的签名。
另外一种情况,若背书逻辑拒绝背书交易,那么背书者可以向提交客户端发送消息(TRANSACTION-INVALID, tid, REJECTED)。
注:这一步背书者不会改变其状态,在背书者上下文中通过交易模拟产生的更新也不会影响到状态。
3)提交客户端获取交易的背书,通过排序服务进行广播。
之前提交节点会等待,当得到足够的(TRANSACTION-ENDORSED, tid, *, *)消息及签名,就可以得出交易已被背书的结论。这里会涉及其与背书节点之间的多轮交互。
注:这里的“足够的”取决于链代码的背书策略。满足了背书策略,交易才算背书成功。需要说明的是,现在这时还没有提交交易。
如果客户端没有得到足够的消息及签名,其会放弃此次交易,稍后重试。
对于持有合法背书的交易,可以开始排序服务。在此提交客户端通过 broadcast (blob)调用排序服务,此时 blob=endorsement。如果客户端没有能力直接调用排序服务,则可以通过其所选择的节点代理执行 broadcast(blob),如此一个节点必须被客户端信任,并且不会从背书中删除任何消息,否则交易会被当作非法的。
注:一个代理节点是不可能伪造出一个合法的背书信息的。
4)排序服务向所有节点投递传播交易的消息。
当发生了一个 deliver 事件(seqno, prevhas,blob),并且节点已经对那些序列号低于 seqno 的 blob 应用了所有的状态更新时,节点会进行以下操作。
一、根据链代码(blob.tran-proposal.chaincodeID)的策略,节点检查 blob.endorsement 是否是合法的。
在某些复杂的用例中,背书信息的交易提案可能会不同。在这种情况下,背书策略指定了状态如何的演化。根据状态更新选择的一致性内容(consistency property)或隔离保证(isolation guarantee)的不同,依赖验证是有多种实现方式的。
其中串行性(serializability)要求每个 readset 和 writeset 里键对应的版本号必须与状态里键的版本号相同,并抛弃不能满足此要求的交易。
如果所有的检查都能通过,那么可以认为此交易合法,这时可提交交易。在这种情况下,节点在原始总账(peer ledger)的位掩码中为这个交易标记 1 然后应用 blob.tran-proposal.stateUpdates 到区块链状态。
注:只有提交了的交易才可以改变状态。
若出现 blob.endorsement 的背书策略核实失败,则交易为非法。节点会在原始总账的位掩码中用 0 标记交易。
注:非法的交易不会更改状态。
最后还需要注意的是,对于一个给定的序号,所有正常的节点在处理一个 deliver 事件(区块)之后,必须具备相同的状态。即,通过排序服务的处理,保证所有正常的节点都将收到 deliver(seqno, prevhash, blob)事件的相同序号。
热文推荐
区块链兄弟——技术专业问答先行者,区块链爱好者聚集地
领取专属 10元无门槛券
私享最新 技术干货