区块链的数据结构node
State数据结构 由peer维护,key/value store安全
Ledger 记录了全部成功和不成功的状态更新交易。Ledger被ordering service构造,是一个全排序的交易区块(有效的和无效的)哈希链。服务器
Ledger存储在peer节点和orderer的一个子集里。存储在peer上的Ledger和存储在Orderer上的Ledger不一样地方在于peer上的Ledger在本地维护一个位掩码(比特位)用来区分有效交易和无效交易。网络
PeerLedger在v1后续版本会被裁减。Orderers维护OrdererLedger用来保证peerLedger的容错和有效性。V1后续版本OrdererLedger可能也会被裁减,加入Ordering service的属性被维护。数据结构
Ledger容许peer重放历史全部交易并从新构建state状态。这样上文说起的state结构是可选择的。并发
Nodes Nodes是指区块链中的通讯实体。一个Node是指一个逻辑功能,所以不一样类型的Nodes可以运行在同一台物理服务器上。关键在于Nodes实例怎样被组合在一个信任域里以及和管控他们的逻辑实例关联。app
存在三种类型的Node:异步
Client:client提交交易调用到endorsers,广播交易建议到ordering service(client应该先提交交易到网络中的背书节点,获取背书签名后广播交易到ordering service指定的channel中)。分布式
Peer: peer发起交易,维护state状态和ledger的副本。另外,peer能够拥有endorser的角色。post
Orderer:orderer节点运行通讯服务完成分发保证,类如原子的或者所有的order广播。
Client client做为终端用户必须链接到peer才能与区块链通讯。Client可能链接到任何peer节点。Client建立并调用交易。Client与peer和ordering service都会通讯。
Peer peer接收排序后的状态更新(区块形式),维护状态和帐本。
Peer能够同时实现endorsing peer的角色或者一个endorser。Endorsing peer的功能和一个特定的chaincode相关,用于在一个交易在提交前进行背书。每一个chaincode可能指定一个背书策略(涉及一组endorsing peers)。这个策略定义了一个有效背书交易的必须条件和充分条件。在交易为deploy transactions的状况,deploy transaction用于安装新的chaincode。这时deploy transaction的背书策略特指system chaincode的背书策略。
Ordering service nodes
Orderers 提供ordering service,用来保证交付过程。Ordering service 可以经过不一样方式实现:针对不一样的网络和节点故障模型包括中心化的服务和分布式的协议。
Ordering service提供一个共享的通讯channel至clients和peers,提供广播交易消息服务。Clients 链接到channel后,能够在channel上广播消息,这些消息而后被分发到全部peers。
这个channel提供了原子分发消息方式(能够实现total-order分发和可靠性的消息通讯)。换句话说,channel对全部链接的peer输出一样的消息,而且按照一样的排序分发给全部peer。原子通讯保障也被成为total-order广播或者在分布式系统中成为共识。广播的消息内容是归入到区块链状态中的候选交易。
分区(ordering service channels)。Ordering service可以支持多channel,相似公有/订阅消息系统。Clients链接到一个给定channel,而后发送和接收消息。Channels能够被理解为分区-clients链接到一个channel,对于其余channel的存在是无感知的,可是clients能够链接到多个channel。尽管Hyperledger Fabric有的ordering service实现支持多channels,为了简单起见,下面的文档中,咱们假定ordering service包含一个独立channel/topic。
Ordering service API。Peers经过ordering service提供的接口链接到ordering service提供的channel。Ordering service API包含两个基本操做(通常为异步事件):
Booadcast(blob):client调用这个接口广播任意的消息到channel中。在BFT中档发送请求到一个服务也被称为request(blob)。
Deliver(seqno, prevhash, blob):ordering service调用这个接口分发消息到peers,分发过程指定一个非负整形序列号、最近分发blob的hash值。也就是,这是一个ordering service的输出事件。Deliver()在公共-订阅系统中也被称为notify()或者在BFT系统中被称为commit()。
帐本和区块构造。帐本包含ordering service的全部输出数据。概要来讲,这时一系列deliver(seqno,prevhash,blob)事件。经过prevhash构造一个hash链。
大多数时候,由于效率缘由,ordering service会在一个deliver事件中组合多个blobs和输出多个区块而不是一个交易。这种状况下,ordering service必须强制确认一个每个区块中blob的排序。区块中blob的个数会根据ordering service的实现动态选择。
下面,为便于描述,咱们定义ordering service属性并解释交易背书的工做流(假定一个deliver事件只包括一个blob)。这些内容很容易的能够拓展到区块同理到一系列的deliver事件(根据上文描述的一个区块中包括明确的blobs排序)。
Ordering service属性
Ordering service的保障(或者原子广播channel)规定了广播消息作了什么以及全部分发消息中存在的关系。这些保障以下:
一、安全性(一致性保障):只要peers链接到channel足够长的时间(peers会断开链接或crash,可是会重启和从新链接),那么他们就会收到彻底相同的deliver(seqno, prehash, blov)消息。这意味着在全部peer节点上接收的输出都按照一样的顺序。根据消息序列号以及同一序列号包含彻底同样的内容(blob和prevhash)能够实现peer节点排序的一致。注意这只是一个逻辑排序,一个peer节点接收到的deliver(seqno,prehash,blob)并不须要其余的peer节点实时的接收到相同的消息。可是,对于一个seqno,两个正确的peer节点都会收到有一样prevhash和blob的deliver。另外,除非有client(peer)调用broadcast(blob),不然是不会deliver任何blob的。每一个broadcast的blob只会deliver一次。
Deliver()事件包含以前deliver事件中所包含数据的hash值(prevhash)(相似merkel值)。当ordering service完成原子广播后,prevhash就是seqno-1序号的deliver event参数的hash值。这样全部的deliver事件构成了一个哈希链,能够用这个哈希链去验证ordering service的完整性。
二、活跃度(分发保障)ordering service的活跃度保障特指ordering service的一种实现方式。准确的保障可以可能会依靠网络或者节点错误模型。
原则上,若是提交的client没有fail,那么ordering service须要保证每个链接到ordering service的正常peer最终能够接收到全部提交的交易。
总结来讲,ordering service确保了一下属性:
一、合约。对于任何不一样的正常peer节点收到的deliver(seqno, prevhash0, blob0)和deliver(seqno, prevhash1, blob1)事件存在prehash0=prehash1和blob0=blob1。
二、哈希链的完整性。对于正常peer节点上接收到的deliver(seqno-1, prehash0, blob0)和deliver(seqno, prevhash, blob)存在prevhash=HASH(seqno-1||prevhash||blob0)。
三、连续性。若是一个正常的peer节点已经收到ordering service的输出deliver(seqno, prevhash, blob),那么这个节点已经收到事件deliver(seqno-1, prehash0,blob0)。
四、非创造性。Peer节点收到任何deliver(seqno, prevhash, blob)前确定存在某个peer节点发送了broadcast(blob)事件。
五、不可复制性(可选)。对于正常peers节点收到的任何两个事件broadcast(blob)和broadcast(blob’),若是blob=blob’,那么seqno0=seqno1而且prevhash0=prevhash1。
六、活跃度。若是一个正确的client调用了broadcast(blob)事件,那么每一个正确的peer最终都会收到一个deliver(*, *, blob)事件。
交易背书的工做过程
下面,咱们描述一个交易的更深层次的请求流程。
Client建立一个交易并发送交易到client所选择的背书节点。
为了调用一个交易,client发送一个PROPOSE消息到一系列的背书节点(可能不是同时发送)。对于一个给定chaincodeID对应的背书peer,client经过peer节点(client必须链接到peer节点,client发送交易消息到背书节点也是经过链接的peer节点发送的)使用这些endorsing peers。Client所链接的peer节点经过endorsement policy能够知道endorsing peer序列。举例来讲,交易能够被发送到一个chaincodeID对应的全部endorsers。即使这样,有些endorsers能够离线,其余的endorsers可能会反对或者选择不对这个交易背书。提交交易的client试图知足可用的endorsers的策略。
下面,咱们首先详细描述PROPOSE消息的格式,而后咱们讨论client和endorsers可能的交互模式。
PROPOSE交易格式
PROPOSE消息的格式是<PROPOSE, tx, [anchor]>,其中tx是必须的,anchor可选参数在下面描述。
Tx=<clientID, chaincodeID, txPayload, timestamp, clientSig>。其中
clientID是提交交易client的ID
chaincodeID指的是交易所属于的chaincode对应的ID
txPayload包含提交的交易自己
Timestamp对于一个新的交易单调递增的整形值,值由client维护。
clientSig是client上tx其余字段的签名
调用交易和deploy交易(引用特定用来部署的系统chaincode的invoke交易)的txPayload的详细信息是不一样的。对于invoke交易,txPayload包含两个字段:
txPayload = <operation, metadata>
其中operation表示chaincode操做(function)和参数
Metadata表示和调用相关联的属性
对于deploy交易,txPayload包含以下三个字段:
txPayload = <source, metadata, policies>
其中source表示chaincode的源码
Metadata表示与chaincode和application相关的属性
Policies包含全部peer节点均可以获取到的与chaincode相关的策略。例如背书策略。注意deploy交易背书策略并不在txPayload里,deploy交易的txPayload仅包含了背书策略的ID和它的参数。
Anchor包含read version依赖,更具体的说是key-version对(anchor是K*N的子集),这把PROPOSE请求和特定version的keys(存储在KVS)绑定在一块儿。若是client制定了anchor参数,那么endorser仅当本地KVS中相应keys的version和anchor中匹配时才会背书。
tx加密的哈希值对于全部节点用来做为一个惟一的交易标识tid(tid-HASH(tx))。Client把tid存储在内存中,等待endorsing peer的响应。
消息模式
Client决定和endorsers交互次序。例如一个client能够发送<PROPOSE,tx>(没有anchor参数)到一个单独的endorser,这个endorser而后生成了version dependencies(anchor)。Client而后可使用上述生成的anchor做为参数发送PROPOSE消息到其余endorsers。一样,client能够直接发送<PROPOSE, tx>(不包括anchor)到全部背书节点。不一样的通讯方式都有可能,client能够灵活地选择不一样方式。
Endorsing peer模拟交易,生成背书签名
当从client收到一个<PROPOSE,tx,[anchor]>消息后,endorsing peer epID 首先验证client的签名clientSig,而后模拟这个交易。若是PROPOSE消息指定了anchor,那么模拟交易仅仅依赖read version numbers(readset会在下文定义)。
模拟交易包括背书节点暂时执行交易(txPayload),经过交易所引用的chaincode(chaincodeID)和本地存储的状态副本。
做为运行的结果,endorsing peer计算read version依赖(readset)和状态更新(writeset),在DB语言中也被称为MVCC+postimage信息。
以前咱们提到状态包含key/value对,全部的k/v实例都是有版本的,每一个实例包含有序的版本信息,这个版本信息会在key所对应的value被更新时递增。Endorsing peer节点保存有全部k/v对,可以被chaincode获取,能够读和写,可是peer模拟交易时不会更新这个状态。特别是:
一、在endorsing peer执行一个交易以前假定状态为s,对于交易读取的每一个key值k,键值对(k,s(k).version)保存在readset中。
二、另外,对于交易要更新key k的值为新的值value v’时,键值对(k, v’)被添加到writeset中。做为可选,v’能够是新的值相对于原值的delta值。
若是client在PROPOSE消息中指定了anchor的值,那么指定的anchor信息必须和endorsing peer生成的readset一致时才能模拟交易。(anchor值应该在有些交易依赖于以前交易的完成状况或者对当前状态的条件有要求,只有状态知足必定要求或者某些交易完成后,才能模拟当前交易,这个时候anchor具备体现依赖关系的意义)。
而后peer节点把tran-proposal(也多是tx)发送至peer的背书交易逻辑,称为endorsing logic。默认状况下,endorsing logic接收tran-proposal而后签名这个tran-proposal。可是,endorsing logic可能有不少功能,例如经过tran-proposal和legacy系统交互以及使用tx做为输入来作决定是否背书一个交易。
若是endorsing logic决定背书这个交易,那么会发送<TRANSACTION-ENDORSED, tid, tran-proposal,epSig>消息给提交交易的client(tx.clientID),其中:
tran-proposal := (epID,tid,chaincodeID,txContentBlob,readset,writeset)
这里txContentBlob是是chaincode/transaction的特定消息,这样作是为了可使用txContentBlob在一些状况下代替Tx(例如:txContentBlob=tx.txPayload)。
epSig是endorsing peer对tran-proposal的签名。
若是endorsing logic拒绝背书交易,endorser会发送消息(TRANSACTION-INVALID, tid, REJECTED)到提交交易的client。
注意在这一步endorser不会改变他的状态,endorsement在模拟交易时产生的状态更新不会影响到状态。
Submitting client收集一个交易的endorsement而且经过ordering service广播这个endorsement。
Submitting client直到接收到足够的(TRANSACTION-ENDORSED, tid, *, *) 消息和签名后,代表这个transaction proposal已经被背书。这个过程并不必定一次性完成,可能会分屡次发送至背书节点完成背书。
足够的数量多少由背书的策略决定。若是背书策略知足了,那么这个交易就已经被背书了;注意这是交易尚未被提交。Client收到的用来表示一个交易已被背书的签名消息TRANSACTION-ENDORSED集合被称为endorsement。若是提交交易的client没有收到transaction proposal的endorsement,那么client会丢弃这个交易,稍后重试。
对于正常接收到endorsement的交易,咱们如今开始运行ordering service。Client经过boroadcast(blob)调用ordering service,其中blob=endorsement。若是client没有直接调用ordering service的能力,那么client可能会经过client所链接的代理peer调用ordering service。这里的peer必须是client所信任的,不会从endorsement中删掉任何消息,不然更改后的endorsement会被认为无效。
Ordering service分发交易信息到peer节点
当peer收到deliver(seqno, prevhash, blob)事件后,同时peer已经完成全部序列号小于seqno的deliver事件的更新。Peer节点会作如下操做:
一、根据chaincode的背书策略确认blob.endorsement是否有效。
二、通常状况,peer验证依赖关系blob.endorsement.tran-proposal.readset没有同时被violated。复杂状况是,endorsement中的tran-proposals字段可能会不相同(endorsement中有多个背书节点返回的tran-proposal),这时endorsement策略指定了状态怎样更新。
根据状态更新选择一致性属性仍是“isolation guarantee”,依赖的验证有不一样的实现方式。串行是默认的“isolation guarantee”,除非chaincode endorsement策略指定了不一样方式。
串行方式要求readset中每一个key所对应的的version和sate中一样key的version值相同,拒毫不知足该条件的交易。
三、全部上述验证都经过后,交易被认为有效的或者提交的。这时peer标记把PeerLedger上这个交易对应的bit位标记为1,同时应用blob.endorsement.tran-proposal.writeset更新到区块链状态(若是tran-proposal都是同样的,不然endorsement 策略定义了方法选择更新方式)。
四、若是blob.endorsement的背书策略验证失败,交易变为无效。Peer在peerLedger上的标记为上标记为0。无效的交易不会更改状态。
注意这已经足够知足对全部正常的peer,执行一个给定序列号的deliver事件后有一样的状态。也就是说,在ordering service的保证下,全部的正常peer将会受到一致的序列deliver事件。当endorsement策略是规范的,readset中的版本依赖是肯定的,全部的正常peer节点最终都会是同样的结局,不管交易是否在一个有效的区块里。所以全部peer节点按照一样的方式提交、应用一样序列的交易。
背书策略
背书策略是背书一个交易的条件。区块链peer节点有一系列预先设置的策略,用来背书安装指定chaincode的deploy交易引用。背书策略能够参数化,这些参数可以用deploy交易指定(安装chaincode时设定背书策略)。
动态添加背书策略(例如经过在部署chaincode时使用deploy transaction添加)是很是敏感的,因为有限的策略验证时间、决定性、执行和安全保证。所以动态添加背书策略是不容许的,可是将来会支持。
经过背书策略验证交易
一个交易只有根据背书策略签名后才会有效。一个invoke交易首先须要得到知足chaincode背书策略的endorsement,不然不会被提交。这个过程经过client和endorsing peer的交互完成。
背书策略的例子
背书策略可能会包含逻辑表达式,典型条件是让endorsing peer对交易请求进行数字签名。
假设chaincode指定背书节点集合为:
E = {Alice, Bob, Charlie, Dave, Eve, Frank, George}
那么背书策略可能以下面示例:
一、获取全部E中节点对tran-proposal的有效签名
二、获取E中任何一个节点的签名
三、获取E中节点对同一tran-proposal的签名,选择E中节点的条件是:(Alice OR Bob) AND (any two of: Charlie, Dave, Eve, Frank, George).
四、从7个endorsers中任意选择5个进行签名
五、假设endorses有金额或者权重,好比{Alice=49, Bob=15, Charlie=15, Dave=10, Eve=7, Frank=3, George=1},权重综合为100。背书策略要求得到具备绝大多数金额的背书节点签名(签名节点的集合金额加起来超过50)
六、5中金额的赋值能够是静态的(在chaincode中固定),也能够是动态的(依赖chaincode的状态而且可以在运行中更改)
七、获取(Alice Or Bob)对tran-proposal1的签名以及对tran-proposal2进行签名(any two of: Charlie, Dave, Eve, Frank, George)。Tran-proposal1和tran-proposal2不一样点在于endorsing peer和状态更新。
具体怎样使用背书策略须要根据应用,对endorsers失败和失效的但愿弹性,还有其余因素。