區塊鏈的數據結構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失敗和失效的但願彈性,還有其餘因素。