對官方的原文進行了簡單的翻譯:
arch-deep-dive
感覺原文有一點點啰嗦(更可能是本人英文有點搓),翻譯的更不到位,后面根據理解再整理個簡單點的
總覽
v1架構有如下優勢:
- chaincode信任的靈活性 架構將chaincode(區塊鏈應用)的信任假設和ordering的信任假設進行了分離。也即是說,ordering服務可能由一組節點(orderer)提供服務,容忍其中部分節點失效或作惡,而每個chaincode的背書都有可能不同。
- 可擴展性 由于執行chaincode的背書節點(endorser)與orderers節點是正交的,系統的擴展能力比將相關功能集成在同一個節點要好多的。特別的,當不同的chaincode指定了不相交的背書,其將引入endorsers之間chaincode的分區并允許chaincode并行執行(背書)。此外,chaincode執行一般成本是比較高的,這樣就從ordering服務的關鍵路徑上移除了(注:保證ordering服務的響應)
譯注:官方文檔,一而再,再而三的提到將orderer節點拆分出來是多么英明。只是記得,流水化操作、其提升擴展性、并發性都是常識。關鍵是要能識別出什么操作可以拆分出來,也就是哪些操作是正交的、并且確實有必要拆分的,這是設計中的難點。
- 保密性 該架構有助于chaincode的部署,其有內容和自身交易狀態更新的保密需求。
- 共識模塊化 架構是模塊化的,共識(如ordering服務)是可插拔的
本文分為兩部分:
I.Hyperledger Fabric v1 架構組件
1. 系統架構
2. 交易背書的基本流程
3. 背書策略
II. post-v1架構組件
4. 帳本checkpoint(修剪)
1. 系統架構
區塊鏈是包含很多相互通信的節點組成的分布式系統。區塊鏈執行的程序稱為chaincode,其擁有狀態、帳本數據、執行交易。chaincode是核心,因為交易是在chaincode上調用的操作。交易必須被“背書”,只有被背書的交易可以被提交,然后影響狀態。存在一個或多個為管理功能和參數的特別chaincode,統稱為系統chaincode。
1.1 交易
交易分為兩種:
- 發布交易 - 創建新的chaincode,攜帶程序作為參數。當一個發布交易成功執行,該chaincode就已經成功安裝在區塊鏈上。
- 執行交易 - 執行之前發布的chaincode內容中的操作。一個執行交易指一個chaincode和其提供的功能。當前chaincode執行成功,其可能涉及到相關狀態的修改和輸出
正如后續描述的,發布交易是執行交易的特例,當一個發布交易創建新的chaincode,對應一個系統chaincode的執行交易。
備注: 本文當前假設一個交易不論是創建新的chaincode還是執行一個操作,都是由一個已經發布的chaincode提供。本文不涉及到:a) 為只讀檢索優化的交易(包含在v1版本),b)支持跨chaincode的交易(post-v1 特性)
1.2 區塊鏈數據結構
1.2.1 狀態
區塊鏈的最新狀態(簡單說狀態)的模型是帶版本的鍵值存儲(k/v存儲),其中key是名字、值則是任意。這些條目運行在區塊鏈上的chaincode通過get
和put
KVS操作修改的。狀態存儲是持久的,狀態的更新會記錄日志。注意,帶版本的kv存儲是適配狀態模型的,其實現可能使用實際的kv存儲,不過RDBM存儲或其它方案也是可行的。
更正式的,狀態 s
的模型為 K -> (V X N)
映射。其中:
-
K
是一組key -
V
是一組value -
N
是有序的無窮版本號。映射函數:next: N -> N
需要一個N
元素,然后返回一個新的版本號。
V
和 N
包含特別的元素:\bot
,這是最低的N
元素。任何key的初始化都映射為(\bot, \bot)
。對s(k)=(v,ver)
我們表示 v
為 s(k).value
, ver
為s(k).version
kv存儲操作模型如下:
-
put(k,v)
, 其中k\in K
、v\in V
, 將區塊鏈狀態s
改變為s'
, 因此s'(k)=(v,next(s(k).version))
withs'(k')=s(k')
for allk'!=k
-
get(k)
返回s(k)
狀態有peer節點維護,而不是order或客戶端
狀態分區 KV存儲中的key可以根據名字識別其屬于哪個chaincode,在這個意義上,只有特定chaincode的交易可以修改屬于該chaincode的key。原理上,任何chaincode可以讀取屬于其它chaincode的key。跨chaincode交易的支持,也就是修改屬于2個及以上chaincode狀態是post-v1的特性。
1.2.2 帳本
帳本提供可驗證的歷史記錄,包括發生在系統操作期間的所有成功的狀態修改(有效的交易)和未成功的嘗試(無效交易)。
帳本由ordering服務構建,是完全有序交易塊的hash鏈。hash鏈強制區塊必須完全順序排列在帳本中,每個塊包含一個完全排序的交易隊列。這種強制排序是涉及所有交易的。
帳本保存在所有peer節點,也可以作為orderer的子集。一個orderer指向的帳本稱為OrdererLedger
,而peer指向的稱為PeerLedger
。PeerLedger
不同于OrdererLedger
的是,peer 本地維護了一個bitmask來區分有效和無效的交易。
peer可能對 PeerLedger
進行減枝(post-v1特性)。orderer維護OrdererLedger
是為了容錯和可用,其可能在任何時候對其減枝,只要維護ordering服務的提供。
帳本允許peer對所有歷史交易進行重訪來重構狀態。因此,1.2.1章節描述的狀態是一個可選數據結構。
譯注:比特幣的錢包余額就是如此通過所有相關交易計算出來的,而比特幣本事是沒有余額的。
1.3 節點
節點是區塊鏈的通信實體。“節點”只是邏輯上的概念,多種不同類型的節點可能運行在同一個物理服務器上。重要的是,節點是如何被分組到“信任域”,如何關聯到控制它們的邏輯實體。
目前有三種類型節點:
- 客戶端 或 提交客戶端:客戶端提交一個實際的交易調用給代理人(endorser),然后廣播交易提案給ordering服務
- Peer : 提交交易、維護狀態和一份帳本拷貝,此外,其還有一個特別的endorser角色
- Ordering-service-node 或 orderer:運行通信服務實現交付保證,如原子化或完全排序廣播。
詳細解釋如下。
1.3.1 客戶端
客戶端表示終端用戶操作的實體。其必須連接到peer來實現與區塊鏈的通信。客戶端可以連接到任何它選擇的peer。客戶端創建并調用交易。
如章節2描述的,客戶端同時與peer和orderer節點通信。
1.3.2 peer
peer從ordering服務接收放在塊中經過排序的狀態更新,并維護狀態和帳本。
peer此外可以作為背書peers,或者是endorser。背書節點的特殊功能在對應特定chaincode生效,在交易被提交前對其背書。每個chaincode都可能指定一個背書策略,其指向一組背書節點。策略定義了有效的交易背書所需的必要充分條件,在章節2、3內描述。在部署chaincode的特例中,安裝新chaincode的背書策略是指定的系統chaincode的背書策略。
1.3.3 ordering服務節點(orderer)
orderers節點組成 ordering服務,也就是提供交付保證的通信結構。 ordering服務可以用采用不同的實現方式:從中心化服務(比如使用在開發和測試中)到支持不同網絡和節點故障模型的分布式協議。
ordering服務提供共享的通信通道給客戶端和peer節點,提供廣播服務用語攜帶交易的消息。客戶端連接到通道,然后可能在通道內廣播消息提交給所有peer節點。通道支持所有消息的原子提交,也就是說,消息痛惜是全局排序發布和可靠的(依賴具體實現)。換句話說,通道輸出相同的消息給所有連接的peer節點,并且是相同的邏輯順序。此原子通信保證在分布式系統上下文中也被稱為total-order broadcast, 原子廣播、或共識。通信消息被包含在區塊鏈狀態中作為候選交易。
分區(ordering服務通道) Ordering服務支持多個channel,channel類似于 主題發布/訂閱(pub/sub)消息系統。客戶端連接到特定的channel,然后發送消息,獲取送達的消息。channel是分區的,客戶端連接到一個channel而不知道其它channel的存在,當然客戶端可以連接到多個channel。即使一些ordering服務的實現包括在Hyperledger Fabric v1中會支持多通道,但為了簡單起見,在本文其它部分都假設ordering服務僅包含單個channel/主題。
ordering服務API peer通過接口連接到ordering服務的提供channel。ordering服務API包含2個基本操作(異步事件):
-
廣播(blob)
: 客戶端調用該接口通過channel廣播任意消息blob
。發送請求給服務時,BFT上下文中也為稱為request(blob)
。 -
發布(seqno,prehash,blob)
: ordering服務在peer節點調用該接口來發布消息blob
,帶有非負整數序列號(seqno
)和最近發送的blob的hash值(prehash
)。也就是說,這是從ordering服務輸出的事件。deliver()
在發布-訂閱系統中也被稱為notify()
或者是BFT中的commit()
帳本和塊信息 帳本(參加 1.2.2)包含ordering服務的所有輸出數據。簡單來說,它是deliver(seqno, prehash, blob)
事件的序列,其根據前面描述的prehash
計算結果構成hash鏈。
大部分時候,為了效率考慮,ordering服務將多個blob批量在一個獨立的deliver
時間中輸出多個block,而不是僅輸出單個交易(blob)。這種情況下,ordering服務必須保證每個塊內的blob是明確排序的。一個block中blob的數目是根據ordering服務實現動態選擇。
本文后面,為了便于描述,我們定義ordering服務屬性和解釋交易背書流程時假設每個deliver
事件包含一個blob
。這很容易擴展到多個塊,假設一個塊的devilver
事件對應于塊內每個blob獨立的deliver
序列,而根據上述描述,每個塊內的blob都是確定排序的。
ordering服務屬性
ordering服務保證(或者 原子廣播channel)規定了廣播消息時會發生什么和發布消息之間的關系是什么。這些保證如下:
1. 安全(一致性保證) 只要peer節點連接到channel的時間足夠長,它們將看到相同的發布(seqno, prehash, blob)
消息序列。這意味著(delever()
事件)輸出在所有peer節點上是相同順序的,根據序列號,相同序列號有想吐的內容(blob
和prehash
)。注意,這僅僅是邏輯順序,并不是兩個節點實時的同時收到相同的消息。換個說法,一個給定的seqno
,沒有兩個正確的peer會發布不同的prehash
或blob
值。此外,沒有值blob
會被發布,除非一些客戶端(peer)調用了broadcast(blob)
,而且,每個廣播blob僅發布一次。
deliver()
事件包含上一個devliver()
事件中數據的加密hash - prehash
。當ordering服務實現原子廣播,prehash
是seqno-1
序列號deliver()
事件的加密hash。這構成了deliver()
事件的hash鏈,其用來驗證ordering服務的輸出,章節4、5會進一步討論。第一個delier()
事件是個特例,其prehash
是默認值。(因為沒有前一個事件啊)
2. Liveness(發布保證) ordering服務的Liveness保證是由ordering服務的具體實現確定的。實際的保證依賴于網絡和節點故障模型。
原理上,如果提交客戶端不故障,ordering服務將能保證每個連接到ordering服務的正常peer最終會發布每個提交的交易。
看到這里,有經驗的開發人員應該都能明白大概使用的什么分布式算法了。
總結下,ordering服務有如下屬性:
- 一致 任何兩個事件在正常的節點上,有相同的序列號就有相同的內容;
-
hashchain integrity 在任何正常節點上,后一個事件的prehash都是前一個事件的加密hash,
prehash=HASH(seqno-1||prevhash0||blob0)
- 不跳過 一個大于0的序列號事件被輸出,其前一個序列號事件必然已經被發布
-
不創建 任何正常的peer節點的任何
deliver(seqno, prevhash, blob)
事件必然有前置的broadcast(blob)
事件 -
不重復(可選,非必需) 對任何兩個
broadcast(blob)
事件,如果其blob相同,其deliver()
事件必然相同 -
Liveness 如果客戶端調用一個
broadcast(blob)
事件,每個正常的peer都最終(有限時間內)會產生一個deliver(*, *, blob)
事件。
2. 交易背書的基本流程
下面我們概要的描述下高緯度交易請求流程。
備注:注意下面的協議不假設所有交易是確定的,也就是說,它允許非確定的交易。
2.1 客戶端創建交易并發送給其選擇的背書節點
為了調用一個交易,客戶端發送 PROPOSE
(提案) 消息給其選擇的一組背書節點(可能不是同時 - 詳見 2.1.2和2.3)。這組給定chaincodeID
的背書節點通過peer使對客戶端可用??,反過來通過背書策略支持這組背書節點。比如,交易是發送給一個給定chaincodeID
的全部endorser。也就是說,一些endorser可能離線,其它一些可能會拒絕、選擇不背書該交易。提交客戶端的會嘗試用可用的endorser來滿足策略。
下面,我們第一次詳細描述 PROPOSE
消息格式,然后討論下提交客戶端和endorser之間交互可能的模式。
2.1.1 PROPOSE
消息格式
PROPOSE
消息格式為 <PROPOSE, tx, [anchor]>
,其中:tx
是必須的,anchor
是可選的,解釋如下:
-
tx=<clientID, chaincodeID, txPayload, timestamp,clientSig>
, 其中:-
clientID
是提交客戶端的ID -
chaincodeID
指向交易所屬的 chaincode -
txPayload
是包含交易中的 payload -
timestamp
(時間戳)是由客戶端維護的單調增正整數 -
clientSig
是客戶端的對tx
其它字段的簽名
txPayload
在執行交易和發布交易時不同的(執行交易指向一個發布用的系統chaincode)。對于執行交易,txPayload
將包含兩個字段:-
txPayload = <operation, metadata>
,其中:-
operation
(操作)表示chaincode操作(函數)和參數 -
metadata
(元數據)表示與調用相關的屬性
-
對于發布交易,
txPayload
則由三個字段組成:-
txPayload = <source, metadata, policies>
, 其中:-
source
(源碼)表示 chaincode的源碼 -
metadata
(元數據)表示chaincode和應用相關的屬性 -
policies
包含所有peer可訪問的chaincode相關的策略,如背書策略。注意:背書策略不是通過發布交易的txPayload
提供的,但是發布交易的txPayload
包含背書策略的ID和其參數。
-
-
anchor
包含讀取版本依賴,或者更明確地,key版本對(anchor
是KxN
的子集),綁定或"錨定"PROPOSE
的請求到KV存儲中特定版本的key。如果客戶端指定了anchor
參數,endorser僅對其本地KV存儲匹配的anchor
之上的讀版本交易進行背書。
tx
的加密hash被所有的節點作為唯一的交易識別tid
(tid = HAHS(tx)
).客戶端保存tid
在內存中,等待從背書節點返回的響應。
2.1.2 消息模式
客戶端決定與endorser交互的順序。比如,客戶端一般發送 <PROPOSE, tx>
給單個endorser,然后其產生一個版本依賴anchor
, 客戶端之后使用其作為PROPOSE
消息的參數發送給其它的endorser。又如,客戶端直接發送 <PROPOSE, tx>
(沒有anchor
)給其選擇的所有endorser。通信模式的差異是可能的,客戶端可以自由決定(參考2.3)
2.2 背書節點模擬交易并產生背書簽名
在接收到從客戶端發送的<PROPOSE, tx, [anchor]>
消息后,背書節點epID
首先研制客戶端簽名clientSig
,然后模擬交易。如果客戶端指定了 anchor
僅對其本地KV存儲匹配的anchor
之上的讀版本交易進行模擬。
模擬交易涉及到背書節點批準執行交易(txPayload
),通過執行chaincodeID
指向的chaincode,拷貝本地持有的狀態。
作為執行的結果,背書節點計算讀版本依賴(readset
-讀集)和更新狀態(writeset
- 寫集),也在DB語言中也被稱為 MVCC+postimage
回想一下,狀態由鍵/值對(k/v)組成。所有 k/v 條目是有版本的,也就是說,每個條目包括排序的版本信息,每次key中存儲的value更新時其值都會遞增。解釋交易執行的peer記錄所有的k/v對都可以被chaincode訪問,包括讀取和寫入,但是peer還沒更新其狀態。更具體的:
- 背書節點執行交易前的狀態
s
,對每個被交易讀取的keyk
,(k, s(k).version)
對被添加到readset
- 此外,對每個被交易修改的key
k
其新值為v'
,(k, v')
對被添加到writeset
。另外,v'
可以是新值到之前值(s(k).value
)的增量。
如果客戶端在PROPSOE
消息中指定了anchor
,客戶端指定的anchor
必須等于背書節點模擬交易產生的readset
。
然后,peer轉發內部的tran-proposal
(可能還有tx
)到其背書交易的邏輯部分,簡稱背書邏輯。默認情況下,peer的背書邏輯接受tran-proposal
然后簡單簽名tran-proposal
。然而,背書邏輯可能解釋任意功能,比如與legacy系統以tran-proposal
和tx
作為輸入進行交互,以決定是否背書交易。
如果背書邏輯決定背書交易,它會發送<TRANSACTION-ENDORSED, tid, tran-proposal, epSig>
消息到提交客戶端(tx.clientID
),其中:
-
tran-proposal := (epID, tid, chaincodeID, txContentBlob, readset, writeset)
, 其中txContentBlob
是 chaincode/交易指定的信息,將txContentBlob
作為tx
的某種表示(比如,txContentBlob = tx.txPayload
) -
epSig
是背書節點在tran-proposal
上的簽名
如果背書邏輯拒絕背書交易,endorser可能發送endorser (TRANSACTION-INVALID, tid, REJECTED)
消息給提交客戶端。
注意,endorser不會在這一步修改其狀態,在背書上下文中的交易模擬產生的更新并不會影響狀態!
2.3 提交客戶端收集交易背書并通過ordering服務廣播
提交客戶端會等待接收到"足夠"的信息后,簽名(TRANSACTION-ENDORSED, tid, *, *)
以確認交易提案已經被背書。如在2.1.2中討論的,這涉及到與 endorser的一輪或多輪交互。
"足夠"的具體數目取決與chaincode的背書策略(見章節3)。如果背書策略得到滿足,交易被背書,注意:其還沒有被提交。背書節點建立交易的TRANSACTION-ENDORSED
消息的簽名集合被贊同/背書稱為 endorsement,表示為endorsement
如果提交客戶端沒有為交易提案收集到提案,它會放棄該交易,提供稍后重試的選項。
對有有效背書的交易,我們現在開始使用ordering服務。提交客戶端執行ordering服務使用broadcast(blob)
,當blob=endorsement
。如果客戶端不能直接調用ordering服務,它會通過一些它選擇peer的做代理。這樣的peer必須備客戶端信任,不會從endorsement
中移除任何消息,否則交易可能被認為是無效的。注意,代理peer可能無法fabricate有效的endorsement
。
2.4 ordering 服務發布交易給peers
當deliver(seqno, prehash, blob)
事件發生,peer已經對對低于seqno
序列號的blobs狀態都已經更新了。peer執行如下:
- 它檢查
blob.endorsement
是否有效,根據chaincode(blob.tran-proposal.chaincodeID
)指向的策略。 - 在典型情況下,它將驗證(
blob.endorsement.tran-proposal.readset
)沒有違法依賴。在更復雜的用例中,endorsement 中trans-proposal
的字段可能不同,在這種情況下,背書策略(章節3)指定了狀態如何演變的。
驗證依賴可以采用不同的方式實現,根據其選擇的一致性屬性或"隔離保證"來做狀態更新。串行化是默認的隔離保證,除非chaincode背書策略特別指定了不同的部分。通過readset
中每個key關聯版本,等于狀態中的key版本,來提供串行化,不符合需求的交易會被拒絕。
- 如果所有的檢查通過,交易就被認為是有效或提交的。這種情況下,peer在
PeerLedger
中的bitmask中標記此交易為1(置1),應用blob.endorsement.tran-proposal.writeset
到區塊鏈狀態(如果tans-proposals
是像的,否則背書策略邏輯定義了blob.endorsement
的功能) - 如果
blob.endorsement
的背書策略驗證失敗,交易是無效,peer在PeerLedger
中的bitmask標記為0。特別注意, 無效交易不會改變狀態。
注意,在處理帶給定序列號的發布事件(塊)后,所有(正確的)peers有相同的狀態是充分的。也就是說,ordering服務的保證,所有正確的peers會接收到想吐的事件(deliver(seqno, prehash, blob)
)序列。由于背書策略的評估和readset
中的版本依賴的評估是確定的,所有正確的peers都將得出相同的結論,不論交易包含的blob是否有效。因此,所有peers提交和應用相同的交易序列,更新它們狀態在相同的路徑。
Figure 1. Illustration of one possible transaction flow (common-case path). 圖1. 一個可能交易流程的示意圖(常用路徑)
3. 背書策略
3.1 背書策略指定
背書策略就是背書交易所需的條件。區塊鏈peer還有一組預定義的背書策略,由安裝chaincode的deploy
交易引用。背書策略可以有參數,這些參數通過deploy
交易指定。
為保證區塊鏈和安全屬性,這組背書策略必須由一組證明策略來限制一組功能以保證有限的執行時間(可終止)、確定性、性能和安全。
動態的添加背書策略(比如,通過在chaincode部署時間進行deploy
交易)是非常敏感的,為了限制策略評估時間(終止)、確定性、性能和安全。因此,動態添加背書策略是不被允許的,但在將來會被支持。
3.2 對背書策略的交易評估
交易只有根據策略被背書后才被認為是有效的。chaincode執行交易首先必須滿足chaincode的策略以獲取背書,否則就不會被提交。正如章節2解釋的,這發生在提交客戶端和背書節點之間交互過程中。
正式的背書策略是對背書的斷言,后續狀態可能是TRUE或FALSE。對于發布交易,背書是根據系統范圍的策略獲取背書的。
背書策略斷言是指特定的變量。它可能是指:
- keys 或者 chaincode相關的ID(在chaincode的元數據中),比如,一組 endorsers
- chaincode更進一步的元數據
-
endorsement
和endorsement.tran-proposal
的元素 - 其它
上述列表是通過表現力和復雜度升序排列的,也就是說,支持僅涉及節點的密鑰和身份的策略將比較簡單。
背書策略的評估必須是確定的。背書必須被每個peer在本地評估,這樣就不需要與其它peer進行交互,也就是所有正常的節點都應該采用相同的方式評估背書策略。
3.3 背書策略的例子
斷言可能包含邏輯表達式,評估為 TRUE 或 FALSE。通常情況,由背書節點為chaincode發出的交易調用會使用數字簽名。
假設chaincode指定鏈endorsor集合為 E = {Alice, Bob, Charlie, Dave, Eve, Frank, George}
, 示例策略:
- 從 E 中所有成員來的相同
tran-proposal
有有效的簽名 - E中任何單個成員的有效簽名
- 從背書節點來的相同
tran-proposal
是驗證簽名有效的條件是:(Alice OR Bob) AND (any two of: Charlie, Dave, Eve, Frank, George)
- 對相同的
tran-proposal
驗證簽名有效通過7個endorsers中任意5個。(更通用的,對有n > 3f
個endorsers的chaincode,通過n
中2f+1
個endorsers即可驗證簽名,或者是超過(n+f)/2
個endorsers) - 假設endorsers有"權重"或"賭注",比如
{Alice=49, Bob=15, Charlie=15, Dave=10, Eve=7, Frank=3, George=1}
,總的賭注是100: 策略需要大部分賭注來驗證簽名(比如合并賭注嚴格大于50),比如{Alice, X}
集合,其中X
是除了 George 之外任意一個,又或者{everyone together except Alice}
,等等都滿足條件。 - 上面條件中的賭注分配可以是確定的(在chaincode的元數據中固定),也可以是動態的(比如,根據chaincode的狀態,在執行期間進行調整)
- 在
tran-proposal1
上驗證從(Alice OR Bob)
來的簽名,驗證在tran-proposal2
中(any two of: Charlie, Dave, Eve, Frank, George)
的簽名,其中其中tran-proposal1
andtran-proposal2
僅在背書節點和狀態更新是有差異。
這些策略的怎么用取決于應用,解決方案是要靈活一點還是要針對endorsers的失敗或作惡,同時還有其它各種屬性要考慮。
4. 驗證帳本和PeerLedger
checkpoint(修剪)
4.1 驗證帳本(VLedger)
為了維持帳本的抽象,僅包含有效和提交的交易(比如在比特幣中),peer可能需要額外的狀態的帳本來維護驗證帳本(或 VLedger)。這是從帳本中過濾掉無效交易的hash鏈。
VLedger區塊(稱為vBlocks)的構造過程如下。由于PeerLedger
區塊會包含無效的交易,這些交易可以在交易從區塊添加到vBlock時被peer過濾掉。每個peer都獨立的處理自己的部分。vBlock就是定義成沒有無效交易的區塊,無效的都被過濾掉了。因此vBlock的大小本質是動態的,可能會空的。構造vBlock的示意圖如下所示:
vBlocks是由每個peer通過hash鏈串在一起的。更進一步,驗證帳本包含的每個塊包含:
- 上一個vBlock的hash
- vBlock的編號
- 從上一個vBlock開始被peers提交的有效交易,這些交易是排序列表形式組織
- 派生出該vBlock的區塊(在
PeerLedger
中)的hash
checkpoint
所有這些信息由peer進行級聯和計算hash,在驗證帳本中生成vBlock的hash。
4.2 PeerLedger
checkpoint
帳本是包含無效交易的,其可能不需要永遠記錄。但是,peer不能簡單的將PeerLedger
中的塊丟掉,因此在建立vBlocks時修剪(整理)PeerLedger
. 也就是說,在這種情況下,如果一個新peer加入網絡,其它peers不會將丟棄的塊發給它,也不會說服新來的它們的vBlocks是有效的。
為了實現PeerLedger
的修剪,本文描述了一種checkpoint機制。這種機制建立在跨peer網絡的vBlock的有效性,允許checkpointvBlocks替代丟棄的PeerLedger
區塊。反過來說,因為不需要存儲無效的交易,可以降低存儲空間。同樣可以降低新加入網絡的peer重建狀態的工作量(因為不需要在重放PeerLedger
來重建狀態時再逐個交易進行驗證,只需要簡單的重放驗證過的帳本的狀態更新)。
4.2.1 checkpointing 協議
peer每CHK個區塊執行一次checkpointing,CHK是可配置的參數。為了初始化checkpoint,peer廣播(比如,gossip)給其它peers指向驗證帳本的消息:<CHECKPOINT,blocknohash,blockno,stateHash,peerSig>
,其中
-
blockno
是當前塊編號,blocknohash
是其對應的hash -
stateHash
是有效的blockno
區塊的最新狀態的hash(比如通過默克爾hash生成) -
peerSig
是peer對(CHECKPOINT, blocknohash,blockno,statehash)
的簽名
peer會持續收集 CHECKPOINT
消息,直到其得到足夠多的、經過正確簽名的、blockno
與blocknohash
和stateHash
匹配的消息,然后建立有效的checkpoint(4.2.2)
在有blocknohas
的區塊號blockno
之上建立有效的checkpoint:
- 如果
blockno>latestValidCheckpoint.blockno
,則peer設定latestValidCheckpoint=(blocknohash,blockno)
- 將構成有效checkpoint的相關peer簽名保存在
latestValidCheckpointProof
集合 - 保存狀態相關
stateHash
到latestValidCheckpointedState
- (可選) 修剪其
PeerLedger
到區塊號為blockno
(含)的區塊
4.2.2 驗證checkpoint
很容易想到checkpointing會帶來如下的問題:何時peer可以修剪其PeerLedger
? 多少個 CHECKPOINT
是"足夠多"?
這些都定義在checkpoint驗證策略中,至少有兩種可能的方式,也可以組合實現:
-
Local (peer-specific) checkpoint validity policy (LCVP) - 本地檢查點驗證策略 - 給定的peer p指定一組peer,這些peers是p信任的,它們的
CHECHPOINT
消息足夠建立有效的checkpoint。比如,LCVP在peer Alice可能定義了其需要接受到Bob的CHECKPOINT
消息,或者同時從Charlie和Dave收到。 -
Global checkpoint validity policy (GCVP) - 全局檢查點驗證策略 - 和本地策略很相似,除了它是在系統(區塊鏈)層面定義,而不是peer本地。比如,GCVP可能會指定:
- 如果從11個不同的peers得到確認,peer就可以信任checkpoint
- 在特定的部署中,每個orderer都和一個peer部署在同一個機器上(比如,信任域),其支持最多有f個orderer故障(拜占庭算法),每個peer收到 f+1個不同peers(與orderer搭檔的)的確認就可以信任該checkpoint