聲明:本文是要點筆記,介紹和系列筆記均收錄在專題:區塊鏈技術與應用
以太坊採用基於賬戶的模式,系統中顯式記錄每個賬戶的餘額。而以太坊這樣一個大型分佈式系統中,是採用的什麼樣的數據結構來實現對這些數據的管理的。
介紹
首先,我們要實現從賬戶地址到賬戶狀態的映射。在以太坊中,賬戶地址爲160字節,表示爲40個16進制數額。狀態包含了餘額(balance)、交易次數(nonce),合約賬戶中還包含了code(代碼)、存儲(stroge)。
直觀地來看,其本質上爲Key-value鍵值對,所以直觀想法便用哈希表實現。若不考慮哈希碰撞,查詢直接爲常數級別的查詢效率。但採用哈希表,難以提供Merkle proof(《02-BTC-數據結構》有講過)。
注意:在BTC和以太坊中,交易保存在區塊內部,一個區塊可以包含多個交易。通過區塊構成區塊鏈,而非交易。
想想如何組織賬戶的數據結構?
1、我們能否像 BTC 中,將哈希表的內容組織爲 Merkle Tree?
但當新區塊發佈,哈希表內容會改變,再次將其組織爲新的 Merkle Tree。如果這樣,每當產生新區塊(ETH中新區塊產生時間爲10s左右),都要重新組織 Merkle Tree,很明顯這是不現實的。
需要注意的是,比特幣系統中沒有賬戶概念,交易由區塊管理,而區塊包含上限爲4000個交易左右,所以Merkle Tree不是無限增大的。而 ETH 中,Merkle Tree來組織賬戶信息,很明顯其會越來越龐大。
實際中,發生變化的僅僅爲很少一部分數據,我們每次重新構建Merkle Tree代價很大。
2、那我們不要哈希表了,直接使用Merkle Tree,每次修改只需要修改其中一部分即可,這個可以嗎?
實際中,Merkle Tree並未提供一個高效的查找和更新的方案。此外,將所有賬戶構建爲一個大的Merkle Tree,爲了保證所有節點的一致性和查找速度,必須進行排序。
3、那麼經過排序,使用Sorted Merkle Tree可以嗎?
新增賬戶,由於其地址隨機,插入Merkle Tree時候很大可能在Tree中間,發現其必須進行重構。所以Sorted Merkle Tree插入、刪除(實際上可以不刪除)的代價太大。
既然哈希表和 Merkle Tree都不可以,那麼我們看一下實際中以太坊採取的數據結構:MPT。
BTC 系統中,雖然每個節點構建的 Merkle Tree不一致(不排序),但最終是獲得記賬權的節點的 Merkle Tree纔是有效的。
數據結構——trie(字典樹、前綴樹)
上圖爲一個通過5個單詞組成的 trie 數據結構(只畫出key,未畫出value)。它有如下特點:
- trie 中每個節點的分支數目取決於Key值中每個元素的取值範圍(圖例中最多26個英文字母分叉+一個結束標誌位)。
- trie查找效率取決於key的長度。實際應用中(以太坊地址長度爲160byte)。
- 理論上哈希會出現碰撞,而 trie上面不會發生碰撞。
- 給定輸入,無論如何順序插入,構造的 trie 都是一樣的。
- 更新操作局部性較好。
那麼 trie 有缺點嗎?當然有:trie 的存儲浪費。很多節點只存儲一個key,但其子節點只有一個,過於浪費。因此,爲了解決這一問題,我們引入帕特麗夏樹(Patricia tree/trie)。
帕特麗夏樹(Patricia tree)
Patricia trie 就是進行了路徑壓縮的 trie。如上圖例子,進行路徑壓縮後如下圖所示:
需要注意的是,如果新插入單詞,原本壓縮的路徑可能需要擴展開來。那麼,需要考慮什麼情況下路徑壓縮效果較好?樹中插入的鍵值分佈較爲稀疏的情況下,可見路徑壓縮效果較好。
在以太坊系統中,160位的地址存在2^160 種,該數實際上已經非常大了,和賬戶數目相比,可以認爲地址這一鍵值非常稀疏。
因此,我們可以在以太坊賬戶管理種使用Patricia tree這一數據結構!但實際上,在以太坊種使用的並非簡單的PT(Patricia tree),而是MPT(Merkle Patricia tree)。
MPT(Merkle Patricia tree)
Merkle Tree 和 Binary Tree
區塊鏈和鏈表的區別在於區塊鏈使用哈希指針,鏈表使用普通指針。同樣,Merkle Tree 相比 Binary Tree,也是普通指針換成了哈希指針。
所以,以太坊系統可以這樣,將所有賬戶組織爲一個經過路徑壓縮和排序的 Merkle Tree,其根哈希值存儲於block header中。
注意,BTC 系統中只有一個交易組成的Merkle Tree,而以太坊中有三個(三棵樹)。也就是說,在以太坊的block header中,存在有三個根哈希值。
根哈希值的用處:
- 防止篡改。
- 提供 Merkle proof,可以證明賬戶餘額,輕節點可以進行驗證。
- 證明某個發生了交易的賬戶是否存在。
MPT(Merkle Patricia tree)
以太坊中針對MPT(Merkle Patricia tree)進行了修改,我們稱其爲MPT(Modified Patricia tree)
下圖爲以太坊中使用的 MPT 結構示意圖。右上角表示四個賬戶(爲直觀,顯示較短地址)和其狀態(只顯示賬戶餘額)。(需要注意這裏的指針都是哈希指針)
每次發佈新區塊,狀態樹中部分節點狀態會改變。但改變並非在原地修改,而是新建一些分支,保留原本狀態。如下圖中,僅僅有新發生改變的節點才需要修改,其他未修改節點直接指向前一個區塊中的對應節點。
所以,系統中全節點並非維護一棵MPT,而是每次發佈新區塊都要新建MPT。只不過大部分節點共享。
爲什麼要保存原本狀態?爲何不直接修改?爲了便於回滾。
以太坊中的數據結構
block header 數據結構
解釋:
- parentHash:父區塊的哈希,即前一個區塊的哈希值
- UncleHash:叔父區塊的哈希
- Coinbase:礦工地址
- Root:狀態樹根哈希
- TxHash:交易樹根哈希
- ReceiptHash:收據樹根哈希
- Bloom:布隆過濾器,用於查詢,和收據樹相關
- Difficulty:挖礦難度
- Gaslimit、GasUsed:gas 費相關
- Time:區塊大致產生的時間
- MixDigest、Nonce:和挖礦過程相關
區塊結構
解釋:
- header:執行 block header 的指針
- uncles:指向叔父區塊的指針
- transactions:交易列表
區塊在網上真正發佈時的信息
最後說明
狀態樹中保存Key-value對,key就是地址,而value狀態通過RLP(Recursive Length Prefix,一種進行序列化的方法)編碼序列號之後再進行存儲。