Hyperledger Fabric Read-Write set semantics——讀寫集
Read-Write set semantics(讀寫集)
本文討論了關於讀寫集當前實現的細節。
Transaction simulation and read-write set(事務模擬和讀寫集)
客戶端提交事務到peer,peer會執行背書驗證並模擬該事務的請求結果,為該事務的請求準備一個讀寫集。讀集包含了該事務在讀取本地賬本時的一列事務版本信息及該信息對應的的一列唯一鍵,寫集包含了一個唯一鍵(可能也允許與讀集中的鍵重復)列表和事務寫入的最新值。如果事務的執行是刪除操作,那麽將為這一列唯一鍵設置一個delete標記(在新值的位置)。
此外,如果事務為鍵多次寫入一個值,則只保留最後一個寫入值。另外,即使在事務讀取結果發出之前更新了鍵值,也會返回事務讀取已提交狀態的值。換句話說,必須保證讀寫一致性。
正如上面提到的,鍵的版本只在讀取集中記錄,而寫集僅包含由事務設置的一列惟一鍵和它們的最新值。
可以有各種各樣的執行版本的方案。版本控制方案的最低要求是為給定的鍵生成不重復的標識符。例如,使用單調遞增的版本可以采用一個這樣的方案。在當前的實現中,我們使用一個基於區塊鏈頂點的版本控制方案,在這個方案中,提交事務的頂點被用作事務修改後的所有鍵的最新版本,事務的頂點由一個元組表示(txNumber是該塊內事務的頂點)。該方案與增量編號方案相比有許多優點—主要是它支持其他組件,如statedb、事務模擬和驗證,以實現有效的設計選擇。
下面是模擬假想事務的一個示例讀寫集的例子。為了簡單起見,我們使用增量數字表示版本。
<TxReadWriteSet> <NsReadWriteSet name="chaincode1"> <read-set> <read key="K1", version="1"> <read key="K2", version="1"> </read-set> <write-set> <write key="K1", value="V1"> <write key="K3", value="V2"> <write key="K4", isDelete="true"> </write-set> </NsReadWriteSet> <TxReadWriteSet>
此外,如果事務在模擬期間執行範圍查詢,那麽範圍查詢及其結果將被添加到讀寫集作為查詢信息。
Transaction validation and updating world state using read-write set(使用讀-寫集進行事務驗證和更新世界狀態)
提交者使用讀-寫集的讀集部分來檢查事務的有效性,並使用讀寫集的寫集部分更新受影響的鍵的版本和值。
在驗證階段,如果在事務讀集中每一個key的版本都能夠與world state(假定之前所有的事務都是有效的,包括之前在同一區塊中的已經被提交的事務)中的key版本一致,那麽該條事務則被認為是有效的。如果讀寫集還包含一個或多個查詢信息,則需要執行額外的驗證。
這些額外的驗證需要確保在本次事務查詢信息返回結果的範圍內的key沒有被插入/刪除/更新過,換句話說,如果我們在對提交狀態的驗證過程中重新執行任何一個範圍查詢(在模擬期間執行的事務),那麽它應該會產生與在模擬時所觀察到的結果相同的結果。此檢查確保如果事務在提交期間觀察到虛項,則該事務應被標記為無效。註意,這個虛項保護僅限於範圍查詢(例如:在chaincode中的GetStateByRange函數)並且還沒有為其他查詢(例如:在chaincode中的GetQueryResult函數)實現。其他查詢有可能出現虛項,因此只能在沒有提交到排序的只讀事務中使用,除非應用程序能夠保證模擬和驗證/提交時間之間的結果集的穩定性。
如果一個事務通過了有效性檢查,提交者將使用寫集來更新世界狀態。在更新階段,對於寫集中的每個鍵,相同鍵的值都設置為在寫集中指定的值,進一步地,這個世界狀態的鍵的版本會被改變,以反映最新的版本。
Example simulation and validation(模擬和驗證案例)
本節通過一個示例場景幫助理解語義。對於本例的目的,在世界狀態中,鍵k的存在是由元組(k、ver、val)表示的,其中,ver是鍵k的最新版本,它的值由val表示。
現在,考慮一組5個事務,T1、T2、T3、T4和T5,它們都在同一個快照上模擬世界狀態。下面的代碼片段顯示了對事務進行模擬的世界狀態的快照,以及由這些事務執行的讀取和寫入活動的順序。
World state: (k1,1,v1), (k2,1,v2), (k3,1,v3), (k4,1,v4), (k5,1,v5) T1 -> Write(k1, v1‘), Write(k2, v2‘) T2 -> Read(k1), Write(k3, v3‘) T3 -> Write(k2, v2‘‘) T4 -> Write(k2, v2‘‘‘), read(k2) T5 -> Write(k6, v6‘), read(k5)
現在,假設這些事務是在T1的序列中排序的。T5(可以包含在一個區塊或不同的區塊中)
- T1通過驗證,因為它不執行任何讀取操作。此外,世界狀態中的鍵k1和k2的元組被更新為(k1,2,v1‘)和(k2,2,v2‘)
- T2失敗了,因為它讀取了一個鍵k1,它被之前的事務修改為T1
- T3通過驗證,因為它不執行讀操作。進一步的,在這個世界狀態下的鍵的元組被更新到(k2,3,v2‘‘)
- T4失敗了,因為它讀取了一個鍵k2,它被之前的事務T1修改過
- T5通過驗證,因為它讀取了一個鍵k5,它沒有被前面的任何事務修改過
註意:目前還不支持具有多個讀寫集的事務。
Hyperledger Fabric Read-Write set semantics——讀寫集