如何降低運行區塊鏈全節點成本?這裡有全新輕節點方案

鏈得得app 發佈 2020-05-13T10:49:24+00:00

摘要: Ultrain在比特幣及以太坊方案基礎上,提出了輕節點世界狀態校驗技術方案 隨著區塊鏈網絡的運行,節點數據量越來越大,運行區塊鏈全節點的成本越來越高,現在一個區塊鏈的全節點,區塊數據存儲量動輒就要幾百G到上T,一方面直接導致運行全節點的區塊鏈計算機的存儲成本極大提高,如果

摘要: Ultrain在比特幣及以太坊方案基礎上,提出了輕節點世界狀態校驗技術方案

隨著區塊鏈網絡的運行,節點數據量越來越大,運行區塊鏈全節點的成本越來越高,現在一個區塊鏈的全節點,區塊數據存儲量動輒就要幾百G到上T,一方面直接導致運行全節點的區塊鏈計算機的存儲成本極大提高,如果我們想把區塊鏈節點跑在一台普通PC上,光區塊鏈數據就使用了普通PC一大半的硬碟存儲空間,這顯然是很難讓人接收的;一方面也大大限制了區塊鏈技術的應用場景,無論是手機還是IoT物聯網設備,現在都不具備加載幾T存儲硬碟的能力。

為了解決這個問題,不同的區塊鏈平台都提出了各自的輕節點解決方案,其中比較典型的是比特幣的SPV方案和以太坊的狀態校驗方案。但是這兩種方案都存在一定的不足,比特幣的SPV方案可驗證交易確實發生過,但無法驗證在某個時刻帳戶的具體數值;以太坊的狀態校驗方案即可以驗證交易發生過,也可以驗證某個時刻帳戶的具體數值,但由於該方案需要將數據寫入塊頭,導致共識的性能下降以及健壯性降低。

Ultrain在比特幣的SPV方案和以太坊的狀態校驗方案基礎上,提出了新的輕節點方案,既可以滿足驗證交易發生過和驗證某個時刻帳戶的具體數值的功能性要求,同時也能滿足共識在性能和健壯性方面的要求,其中在性能方面是以太坊方案的10倍以上。

採取輕節點模式的節點不需要同步巨量的區塊數據,利用世界狀態默克爾校驗方式,節點不需要重放歷史區塊即可校驗狀態的正確性,從而可以使能更多應用場景(比如在資源受限的嵌入式設備運行輕節點等)。本文將從世界狀態校驗問題引出,比特幣SPV方案,以太坊世界狀態校驗方案以及Ultrain所採取的具體方案等方面對輕節點世界狀態校驗技術方案進行詳細闡述。

一、 節點同步數據量

區塊鏈節點網絡可以理解為一個分布式的狀態機,各個節點從相同的創世狀態開始,根據每個區塊內包含的交易,將創世狀態逐塊更新,形成不斷更新的世界狀態。隨著時間的推移,每個節點本地存儲的數據會不斷增長,主要包括歷史區塊數據以及不斷更新的世界狀態數據。比特幣創世至今135個月(2009/01~2020/03)所生產的區塊累積數據量已經達263.6GB,且現在以每個月四個多GB的速度在增長。

圖1 比特幣區塊大小

以太坊全節點以full模式進行同步時(節點會從網絡同步所有的區塊頭,區塊體並重放區塊中的交易以生成世界狀態數據),當前需同步的數據已經達到兩三百GB,如對歷史世界狀態進行archive,則數據量已經超過4T。以太坊節點還支持fast模式進行同步,該模式與full模式的區別在於不重放區塊中的交易去重構世界狀態,而是從網絡同步狀態數據。此外,以太坊節點也支持light同步模式,此種模式下節點僅同步區塊頭數據,不同步區塊體和狀態數據,僅在需要相應的區塊和狀態時去從網絡上獲取。

圖2 以太坊全節點同步數據量

圖3 以太坊全節點Archive模式數據量

從以上比特幣和以太坊的數據我們可以看出,部署運行一個全節點所需同步的數據量越來越大,從而導致節點機器硬體規格要求以及網絡帶寬門檻越來越越高。一個新節點需要花數天時間才能完成歷史數據同步,然後才能開始參與共識過程進行出塊。運行全節點成本比較高,在比特幣白皮書中提到了輕節點可採用的SPV方案,可以在不同步全部區塊數據的情況下,也能進行支付驗證。這裡的支付驗證,只是驗證該筆交易支付發生過,並不是驗證交易的合法性(比如帳戶餘額是否大於轉帳金額等)。

二、 比特幣SPV方案

比特幣白皮書中描述了SPV ( Simple Payment Verification ),即簡單支付驗證。SPV是一個在輕節點環境下,不用運行全節點、只需保存所有的區塊頭,就能驗證支付有效性的技術方案。比特幣中區塊結構分為區塊頭和區塊體兩部分,區塊頭包含區塊的必要屬性,僅 80 字節大小;區塊體包含當前區塊下的所有交易,一般一個區塊包含成百上千筆交易,每筆交易一般要 400 多字節。

圖4 比特幣區塊結構

如圖4所示,比特幣使用了默克爾樹的數據結構來組織區塊體內包含的交易:將區塊內所有交易成對分組,並對交易進行哈希處理,然後對所得的哈希繼續進行哈希處理,重複此過程,直到只剩下一個哈希,稱為默克爾根(merkle root)。默克爾樹底部的節點,就是交易數據的哈希值。每一個父節點,都是兩個子節點哈希值組合後的哈希值。通過層層往上計算,最終算得根節點。這個默克爾根數值存儲於比特幣的區塊頭中。

輕節點從區塊鏈網絡上獲取了最長鏈上的所有區塊頭並在本地進行存儲後,即可進行進行SPV支付驗證:輕客戶端首先計算待驗證支付交易的哈希值;然後定位到包含該交易哈希所在的區塊,並從區塊中獲取構建待驗證支付交易默克爾樹所需的哈希值序列;根據待校驗交易的哈希值以及其所需哈希值序列,計算出默克爾根;將計算所得默克爾根數值與區塊頭中的默克爾根進行比對,如相等則證明交易是真實存在的。

比特幣區塊頭的大小始終是固定的80個字節,每小時出塊約為6個,每年出塊52560個,則每年新增的存儲量約為4M 字節,SPV方案極大節省了輕節點的存儲空間。從上述過程我們可以看出,比特幣的這種SPV方案,僅確保校驗的交易確實發生過,但不能用來校驗交易所導致的世界狀態的變化。以太坊在區塊頭中存儲了交易的默克爾根數值,可以適用類似比特幣的SPV方案;此外以太坊的區塊頭中還存儲了世界狀態的默克爾根數值,可以用來進行世界狀態數值的校驗。

三、 以太坊世界狀態校驗方案

以太坊的帳戶包含四個屬性,nonce,balance,storageRoot和codeHash;以太坊用stateObject來管理帳戶狀態,帳戶以Address為唯一標示;所有帳戶對象逐一插入Merkle-PatricaTrie(MPT)結構里,形成stateTrie。區塊頭數據結構中的Root欄位存儲了stateTrie的root數值,即世界狀態的哈希值。

圖5 以太坊區塊頭世界狀態哈希欄位

我們假設在以太坊區塊鏈上有兩個帳戶A和帳戶B,初始帳戶餘額均為10個以太幣,在編號為1000的區塊中發生過這樣一筆交易:帳戶A向帳戶B轉帳一個以太幣,將帳戶A的餘額修改為9個以太幣,帳戶B的餘額修改為11個以太幣(不考慮礦工費)。在與比特幣SPV類似的以太坊的輕客戶端中,我們可以利用在區塊編號為1000塊的塊頭中存儲的交易的默克爾根數據,校驗確實發生過帳戶A向帳戶B轉帳一個以太幣這樣一筆交易,但是僅利用交易的默克爾根數據,不能校驗帳戶A的餘額是不是9個以太幣(或者帳戶B的餘額是不是11個以太幣),即不能校驗世界狀態的具體數值。

在以太坊的區塊頭的世界狀態默克爾根數值(欄位Root)可以用來完成世界狀態具體數值的校驗。該欄位為以太坊StateDB中的「state Trie」的根節點的RLP哈希值。區塊中,每個帳戶以stateObject對象表示,帳戶以Address為唯一標示,其信息在相關交易(Transaction)的執行中被修改。所有帳戶對象可以逐個插入一個Merkle-PatricaTrie(MPT)結構里,形成「state Trie」。

以太坊EIP-1186中增加了eth_getProof接口用於返回帳號(account)及其狀態(storage values)的默克爾校驗所需的信息(merkle path),用於完成世界狀態數值校驗。eth_getProof有三個輸入參數:

1) DATA: 20字節 – 為帳戶的地址(外部帳戶/合約帳戶)

2) ARRAY:32字節 – 為代校驗狀態數據的地址

3) QUANTITY|TAG: – 為指定的區塊編號或者字符串"latest" 或 "earliest"

圖6 以太坊eth_getProof接口請求參數

該接口返回數據如下:

1) balance:帳戶餘額

2) codeHash:合約帳戶代碼哈希數值,外部帳戶則返回固定數值

3) nonce:帳戶nonce值,表示發了多少筆交易或創建了多少個合約

4) storageHash:狀態數值的默克爾根數值

5) accountProof:帳戶校驗所需的默克爾序列數組

6) storageProof:狀態數值校驗所需的信息數組,包括以下欄位:

Ø key:狀態數值對應地址

Ø value:狀態具體數值

Ø proof:狀態數值校驗所需的默克爾序列數組

圖7 以太坊eth_getProof接口返回數據

輕節點通過eth_getProof接口獲取上述返回信息後,首先根據返回的四個屬性值[nonce, balance, codeHash,storageHash]構建帳戶對象,進行RLP編碼後進行哈希操作,得到的數值與accountProof結合可以與在區塊頭中存儲的世界狀態默克爾樹根數值進行校驗;校驗通過後,則相當於確認了storageHash欄位的正確性,再結合storageProof中的默克爾序列可以完成對狀態數值的校驗。相關校驗示例代碼如下所示:

圖8 以太坊狀態校驗示例代碼

四、 Ultrain世界狀態校驗方案

以太坊採用MPT樹(Merkle-PatricaTrie)管理所有帳戶對象,stateTrie存儲了所有帳戶的信息,比如餘額,發起交易次數,虛擬機指令數組等信息,隨著每次交易的執行,stateTrie 其實一直在變化,區塊內所有交易完成後,所有帳戶信息的即時狀態的默克爾根數值會記錄到區塊頭中。

Ultrain採用chainbase存儲所有世界狀態信息,區塊內交易執行會更新chainbase內相關對象的狀態數值,因此chainbase內存儲了世界狀態的實時信息。客戶端通過鏈上接口(get_table_records)查詢chainbase內信息時,實際上是信任接口提供方,從而信任其提供的數據的正確性;且該接口只能提供實時狀態信息,不能查詢指定塊高狀態數值。Ultrain世界狀態校驗方案可指定塊高查詢狀態信息,並提供該狀態信息校驗所需的默克爾路徑信息,從而去除對接口提供方的信任依賴。

Ultrain世界狀態校驗方案中,每個礦工節點在區塊交易執行結束時,將本區塊內所有交易對世界狀態所做的所有修改(即對chainbase內存儲數據的修改)進行序列化處理,並按一定順序組織為默克爾樹的葉子節點,再逐層計算得出該區塊世界狀態變化的默克爾根數值,計算所得的默克爾根數值會保存到內存中。當區塊高度到一定間隔(比如每100塊)時,將間隔內所有區塊的世界狀態變化的默克爾根數值再做一次默克爾根數值計算,並將該結果(稱之為世界狀態變化累計默克爾根數值)以及間隔內每個區塊的塊高與對應的默克爾根數值寫入文件系統。為防止對主線程性能造成影響,這部分邏輯採用單獨的線程完成。所有礦工節點會將世界狀態變化累計默克爾根數值通過鏈上交易寫入系統合約,只有超過2/3礦工匯報相同默克爾根數值時,該數值才會生效。

為提供世界狀態校驗查詢功能,需設置提供世界狀態查詢服務的節點。選取非礦工節點(即只接收交易和區塊,不參與共識過程的節點)在每個區塊內交易執行完全結束後,將該區塊內所有對世界狀態的修改序列化後存儲到文件系統,並響應校驗查詢節點的數據請求,將狀態變化數據發送給查詢節點進行存儲。查詢節點採用rocksdb將每個區塊對世界狀態的修改進行存儲,包括塊高(block_num),修改序號(sequence),修改內容(修改後的具體狀態內容);查詢節點在存儲每塊數據的同時,與礦工節點類似計算每個區塊對應的世界狀態變化的默克爾根數值,並將該數值存儲到rocksdb中。此外,查詢節點為輕節點提供查詢接口,響應其查詢請求。

圖9 Ultrain世界狀態校驗查詢節點與非礦工節點交互

輕節點對特定世界狀態進行查詢校驗的時候需經過如下三個步驟:

1)首先採用 get_table_rows接口查詢具體世界狀態數值,該接口需輸入要查詢的世界狀態所在的數據表信息(code,scope,table)以及限定的區塊塊高信息:

返回數據包括該條狀態的具體信息(如示例中data欄位的帳戶餘額)以及該信息對應的二進位字節(示例中的raw欄位,該欄位也可以由輕節點根據合約的ABI文件自己生成);此外,該接口會返回這條世界狀態修改對應的塊高(block_num)及序號信息(sequence)。

2)根據get_table_rows接口返回的塊高及序號信息,獲取對應的校驗該狀態所需的默克爾樹序列信息,輕節點調用如下接口:

該接口返回的paths欄位即為默克爾樹序列信息:

3)根據get_table_rows返回的raw數據以及get_table_row_proof返回的paths數據,輕節點可以調用如下接口,計算默克爾樹根數值(輕節點也可以自己實現根據raw和paths數據計算默克爾根的邏輯):

該接口返回計算所得的默克爾根數值,該值可以與礦工節點通過交易寫入系統合約的對應的數值進行比對,相同則表示校驗通過(該示例中發生世界狀態改變的區塊高度為34186,如礦工節點所選取的區塊間隔為100的話,則應該從系統合約讀取塊高34200對應的默克爾根數值進行比對)。

綜上所述,輕節點與查詢服務節點交互流程如下圖所示:

圖10 Ultrain世界狀態校驗接口流程圖

文中所述比特幣的SPV方案,以太坊的狀態校驗方案以及Ultrain的狀態校驗方案,我們可以看到輕節點不需要再同步大量的數據,即可驗證交易確實發生過,以及驗證在某個時刻世界狀態具體數值,由此輕節點所需的存儲及計算資源大幅降低,可以在資源受限的嵌入式設備上運行輕節點,比如物聯網設備,實現物聯網與區塊鏈技術的結合,使能更多商業場景。

作者:Ultrain超腦鏈;來自鏈得得內容開放平台「得得號」,本文僅代表作者觀點,不代表鏈得得官方立場凡「得得號」文章,原創性和內容的真實性由投稿人保證,如果稿件因抄襲、作假等行為導致的法律後果,由投稿人本人負責得得號平台發布文章,如有侵權、違規及其他不當言論內容,請廣大讀者監督,一經證實,平台會立即下線。如遇文章內容問題,請發送至郵箱:linggeqi@chaindd.com

關鍵字: