1. 程式人生 > >HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

一、HyperLeger Fabric賬本簡介

Fabric裡的資料以分散式賬本的形式儲存。賬本由一系列有順序和防篡改的記錄組成,記錄包含著資料的全部狀態改變。賬本中的資料項以鍵值對的形式存放,賬本中所有的鍵值對構成了賬本的狀態,也稱為世界狀態(World State)。 
每個通道中有唯一的賬本,由通道中所有成員共同維護著賬本,每個記賬節點上都儲存了所屬通道的賬本的一個副本,因而是分散式賬本。對賬本的訪問需要通過鏈碼實現對賬本鍵值對的增加、刪除、更新和查詢等的操作。
賬本由區塊鏈和狀態資料庫兩部分組成。
區塊鏈是一組不可更改的有序的區塊(資料塊),記錄著全部交易的日誌。每個區塊中包含若干個交易的資料,不同區塊所包含的交易數量可以不同。區塊之間用雜湊鏈(Hashed-link)關聯:每個區塊頭包含該區塊所有交易的雜湊值以及上一個區塊頭的雜湊值。鏈式結構可以確保每個區塊的資料不可更改以及每個區塊之間的順序關係不可更改。因此,區塊鏈的區塊只可以新增在鏈的尾部。
狀態資料庫記錄了賬本中所有鍵值對的當前值,相當於對當前賬本的交易日誌做了索引。鏈碼執行交易的時候需要讀取賬本的當前狀態,從狀態資料庫可以迅速獲取鍵值的最新狀態。
如果沒有狀態資料庫,要獲得某個鍵值時,需要遍歷整個區塊鏈中和該鍵值相關的交易,效率非常低,因此,讀取狀態資料庫可以認為是快速定位和訪問某個鍵值的方法。另外,當狀態資料庫出現故障的時候,可以通過遍歷賬本重新生成。
當一個區塊附加到區塊鏈尾部的時候,如果區塊中的有效交易修改了鍵值對,則會在狀態資料庫中作相應的更新,確保區塊鏈和狀態資料庫始終保持一致。
區塊鏈的資料塊以檔案形式儲存在各個節點中。狀態資料庫可以是各種鍵值資料庫,Fabric預設使用LevelDB ,也支援CouchDB的選項。CouchDB除了支援鍵值資料外,也支援JSON格式的文件模型,能夠做複雜的查詢。

二、HyperLeger Fabric交易流程

1、客戶端向背書節點發送交易提案請求

客戶端首先構建交易提案,交易提案的作用是呼叫通道中的鏈碼來讀取或者寫入賬本的資料。客戶端使用Fabric SDK建立交易提案,並使用使用者的私鑰對交易提案進行簽名。
Fabric SDK作用有兩個,一個是將交易提案封裝成符合gRPC協議的Protobuf格式的訊息,一個是對建立的交易提案進行簽名。
客戶端打包完交易提案後,將交易提案提交給通道中的背書節點。背書節點的背書策略定義了哪些背書節點簽名背書後交易才能有效,客戶端根據背書策略選擇相應的背書節點,並向相應背書節點提交交易提案。
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
根據背書策略,E0,E1,E2三個背書節點需要完成簽名背書。

2、背書節點對交易提案進行簽名背書

背書節點收到交易提案後,使用MSP模組驗證簽名並確定請求者是否被合理授權進行交易提案的操作(使用每個channel對應的ACL進行驗證),背書節點以交易提案憑證為輸入,基於當前狀態的資料庫執行來生成交易結果,輸出包括反饋值、讀取集和寫入集。此時,賬本並未進行更新。背書節點會將背書節點輸出、背書節點簽名、是否背書宣告作為交易提案反饋回傳給客戶端的SDK。
首先校驗交易的簽名是否合法,然後根據簽名者的身份,確認其是否具有許可權進行相關交易。此外,背書節點還需要檢查交易提案的格式是否正確以及是否已經提交過(防止重放***)。
在所有合法性校驗通過後,背書節點按照交易提案呼叫鏈碼模擬執行交易。鏈碼執行時,讀取的資料(鍵值對)是背書節點中本地的狀態資料庫,鏈碼讀取過的資料回被歸總到讀集(Read Set);鏈碼對狀態資料庫的寫操作並不會對賬本做改變,所有的寫操作將歸總到一個寫入集(Write Set)中記錄下來。讀集和寫集將在確認節點中用於確定交易是否最終寫入賬本。
在鏈碼執行完成後,背書節點把鏈碼模擬執行後得到的讀寫集(Read-Write Set)等資訊簽名後發回給客戶端。此時,交易資訊只在客戶端和單個背書節點之間達成共識,並沒有完成全網共識,各個客戶端的交易順序沒有確定,可能存在雙花問題,所以不是一個有效交易。同時,客戶端需要收到大多數背書節點的驗證回覆後,才算驗證成功,具體的背書策略由智慧合約程式碼控制,可以由開發者自由配置。
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存


背書節點E0,E1,E2執行簽名背書。

3、背書節點將簽名背書結果返回給客戶端

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
背書節點完成簽名背書後,將反饋值、讀取集和寫入集、背書節點簽名、是否背書宣告作為交易提案反饋回傳給客戶端。

4、客戶端向排序服務提交交易

客戶端在收到背書節點的簽名背書結果後,檢查背書節點的簽名並比較不同背書節點的結果是否一致。如果交易提案是查詢賬本的請求,則客戶端無需提交交易給排序節點。如果交易提案是更新賬本的請求,客戶端在收集到滿足背書策略的足夠多背書節點的簽名背書結果後,把背書節點返回的讀寫集、所有背書節點的簽名和通道號發給排序服務節點。
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

5、排序服務節點生成區塊

排序服務節點在收到各個節點發來的交易後,並不檢查交易的全部內容,而是按照交易中的通道號對交易分類排序,然後把相同通道的交易批量打包成區塊。排序服務的共識演算法以元件化形式插入Fabric網路,即開發者可以自由選擇合適的共識演算法。
排序服務節點將為特定通道生成的區塊廣播給通道中所有組織的主導節點。區塊的廣播有兩種觸發條件,一種是當通道的交易數量達到某個預設的閾值,另一種是在交易數量沒有超過閾值但距離上次廣播的時間超過某個特定閾值,也可觸發廣播資料塊。兩種方式相結合,使得經過排序的交易及時生成區塊並廣播給通道的Peer節點(記賬節點)。
排序服務節點只是決定交易處理的順序,並不對交易的合法性進行校驗,也不負責維護賬本資訊,只有記賬節點才有賬本寫入許可權。
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

6、記賬節點進行交易驗證

記賬節點收到排序服務節點發來的區塊後,逐筆檢查區塊中的交易。先檢查交易的合法性以及該交易是否曾經出現過。然後呼叫校驗系統鏈碼(VSCC,Validation System Chaincode)檢驗交易的簽名背書是否合法,以及背書的數量是否滿足背書策略的要求。
記賬節點對交易進行多版本併發控制(MVCC)檢查,即校驗交易的讀集(Read Set)是否和當前賬本中的版本一致(即沒有變化)。如果沒有改變,說明交易寫集(Write Set)中對資料的修改有效,把該交易標註為有效,交易的寫集更新到狀態資料庫中。如果當前賬本的資料和讀集版本不一致,則該交易被標註為無效,不更新狀態資料庫。區塊中的交易資料在標註成有效或無效後封裝成區塊寫入賬本的區塊鏈中。
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
交易流程中,採用MVCC的樂觀鎖模型,提高了系統的併發能力。但MVCC也帶來了一些侷限性。例如,在同一個區塊中若有兩個交易先後對某個資料項做更新,順序在後的交易將失敗,因為後序交易的讀集版本和當前資料項版本已經不一致。

7、記賬節點通知客戶端

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
客戶端會收到每一個連線的記賬節點的通知。

8、交易流程總結

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
區塊鏈的賬本由Peer節點維護,並不是由排序服務叢集維護,所以,只有Peer節點(背書節點和記賬節點)包含完整的區塊鏈資訊,而排序服務叢集只負責對交易進行排序,只保留處理過程中的一部分割槽塊鏈資訊。
Hyperledger Fabric網路中的節點是一個邏輯的概念,並不一定是一個臺物理裝置,但對於生產環境,為了系統架構的解耦,提高擴充套件性以及通過主機隔離提高安全性,Peer節點不能和排序服務節點部署在一臺機器上,而背書節點和記賬節點可以部署在同一臺機器上。 背書節點校驗客戶端的簽名,然後執行智慧合約程式碼模擬交易。交易處理完成後,對交易資訊簽名,返回給客戶端。客戶端收到簽名後的交易資訊後,發給排序服務節點排序。排序服務節點將交易資訊排序打包成區塊後,廣播發給記賬節點,寫入區塊鏈中。

三、HyperLeger Fabric賬本儲存原理

1、Fabric賬本儲存原理

Fabric區塊鏈網路中,每個通道都有其賬本,每個Peer節點都儲存著其所加入通道的賬本,Peer節點的賬本包含如下資料:
A、賬本編號,用於快速查詢存在哪些賬本
B、賬本資料,用於區塊資料儲存
C、區塊索引,用於快速查詢區塊/交易
D、狀態資料,用於最新的世界狀態資料
E、歷史資料:跟蹤鍵的歷史
Fabric的Peer節點賬本中有四種資料庫,idStore(ledgerID資料庫)、blkstorage(block檔案儲存)、statedb(狀態資料庫)、historydb(歷史資料庫)。
賬本資料庫基於檔案系統,將區塊儲存在檔案塊中,然後在區塊索引LevelDB中儲存區塊交易對應的檔案塊及其偏移,即將區塊索引LevelDB作為賬本資料庫的索引。目前支援的區塊索引有:區塊編號、區塊雜湊、交易ID、區塊交易編號。
狀態資料庫儲存的是所有曾經在交易中出現的鍵值對的最新值。呼叫鏈碼執行交易可以改變狀態資料,為了高效的執行鏈碼呼叫,所有資料的最新值都被存放在狀態資料庫中;狀態資料庫是有序交易日誌的快照,任何時候都可以根據交易日誌重新生成狀態資料庫;狀態資料庫會在Peer節點啟動的時候自動恢復或重構,未完備前,本Peer節點不會接受新的交易;狀態資料庫可以使用LevelDB或者CouchDB,CouchDB能夠儲存任意的二進位制資料,支援富文字查詢。
歷史狀態資料庫用於查詢某個key的歷史修改記錄,歷史狀態資料庫並不儲存key具體的值,而只記錄在某個區塊的某個交易裡,某key變動了一次。後續需要查詢的時候,根據變動歷史去查詢實際變動的值。
ledgerID資料庫儲存chainID,用於快速查詢節點存在哪些賬本。
對於單鏈的Peer節點賬本(不包含ledgerID資料庫)結構如下:
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存

2、Peer賬本結構

ledgersData是Peer節點賬本的根目錄,Peer節點的賬本儲存在Peer節點容器的/var/hyperledger/production/ledgersData目錄下,通過命令列可以進入Peer節點容器進行檢視,命令如下:
docker exec -it peer0.org1.example.com bash
Peer節點的本地賬本結構如下:
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
chains/chains目錄下的mychannel目錄channel的名稱,Fabric支援多通道的機制,而通道之間的賬本是隔離的,每個通道都有自己的賬本空間。
chains/index目錄包含levelDB資料庫檔案,儲存區塊索引資料庫,使用leveldb實現。
historyLeveldb目錄儲存智慧合約中寫入的key的歷史記錄的索引地址,使用leveldb實現。
ledgerProvider目錄儲存當前節點所包含channel的資訊(已經建立的channel id 和正在建立中的channel id),使用leveldb實現。
stateLeveldb目錄儲存智慧合約寫入的資料,可選擇使用leveldb或couchDB

3、賬本資料

賬本資料以二進位制檔案的形式儲存的,每個賬本資料儲存在不同的目錄下。賬本資料的所有操作都通過區塊檔案管理器實現。區塊檔案管理器建立的檔案以blockfile_為字首,6位數字為字尾,字尾必須是從小到大連續的數字,中間不能有缺失,因此賬本最大可以持有1000000個檔案塊。預設的區塊檔案塊大小的上限是64M(目前硬編碼在程式碼中)。賬本資料的結構如下:
HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
B代表區塊鏈賬本資料。其中B0為創世區塊,其中包含了區塊頭H0,區塊資料D0(創世區塊裡不包含交易資料),區塊元資料M0。區塊B1區塊頭H1,其中包含了前一個區塊B0的加密hash和本身區塊的加密hash。

4、區塊結構

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
(1)區塊頭
區塊頭(Block Header)包含三部分:
A、區塊數(Block Number):一個從0(創世區塊)開始的整數,並且對於附加到區塊鏈的每個新塊增加1。
C、當前區塊hash(Current Block Hash):當前區塊中包含的所有交易的hash。
C、前一區塊hash(Previous Block Hash):前一個區塊的hash。
(2)區塊資料
區塊資料(Block Data)包含一組交易,交易在區塊建立時寫入。
(3)區塊元資料
區塊元資料(Block Matadata)包含區塊建立時間,寫入客戶端的證書,公鑰和簽名。

5、交易結構

HyperLeger Fabric開發(五)——HyperLeger Fabric賬本儲存
(1)交易頭
交易頭中包含了一些重要的交易元資料,例如鏈碼的名稱、版本。
簽名
(2)簽名
簽名包含由客戶端建立的加密簽名,由客戶端的私鑰生成,用於檢查交易有沒有被篡改。
(3)交易提案
交易提案包含由客戶端生成的交易請求引數。
(4)交易提案返回
交易提案返回包含由背書節點返回的模擬執行結果(讀寫集RWset)。
(5)背書
背書包含交易的背書,一個返回對應多個背書。

6、區塊索引

區塊索引用於快速定位區塊。
索引鍵可以是區塊高度、區塊雜湊、交易雜湊。
索引值為區塊檔案編號+檔案內偏移量+區塊資料長度。
Hyperledger Fabric提供了多種區塊索引的方式,以便能快速找到區塊。索引的內容是檔案位置指標(File Location Pointer)。檔案位置指標由三個部分組成:所在檔案的編號(fileSuffixNum)、檔案內的偏移量(offset)、區塊佔用的位元組數(bytesLength)。