以太坊 Block 、StateDB 與 Trie 的關係

前言

不要指望看完本文就能瞭解全部細節,本文的目的就是帶你快速瞭解相關概念,因爲直接深入代碼會越看越迷惑,其他文章很少提及本文所述內容,不知道是不是他們認爲這些基礎知識太簡單了

StateDB 用法與結構

以太坊是基於賬戶體系實現的,塊通過 parentHash 鏈在一起,每個塊都包含若干交易,每個交易都包含賬戶 from 和 to(部署合約時除外),全部的賬戶湊在一起就是組成了 StateDB,每個塊的 StateDB 都用一顆叫做 Trie 的樹來組織賬戶信息,具體結構如下圖:

保存賬戶信息的 StateDB 通常會存儲在磁盤上,通過 Block.StateRoot 來進行加載,StateRoot 是樹根,也是 leveldb 中的一個 key, 這個根只對應當前塊的交易相關的賬戶信息,value 是這棵樹的全部葉子節點,加載的時候會用葉子節點來構建下圖中的樹型結構

智能合約的地址也被當作賬戶管理,當 Account 爲一個智能合約時,那麼這個 stateObject 也會包含一顆樹,用來保存智能合約的最新狀態信息,這些信息是每次執行 evm 中 SSTORE 這個指令時的輸入信息,key 是合約的變量名,value 是最新值,這棵樹的加載過程與上圖中的過程完全一致

StateDB 的工作方式

用最少的代碼和最簡單的例子說明 StateDB 的主要工作原理與過程

  • StateDB 的結構定義
type StateDB struct {
    db   Database
    trie Trie
    stateObjects      map[common.Address]*stateObject
    stateObjectsDirty map[common.Address]struct{}
    dbErr error
    refund uint64
    thash, bhash common.Hash
    txIndex      int
    logs         map[common.Hash][]*types.Log
    logSize      uint
    preimages map[common.Hash][]byte
    journal        *journal
    // 由 snapshot id 和 journal 長度組成,用於回滾
    validRevisions []revision
    // 下一個可用的 snapshot id
    nextRevisionId int
    lock sync.Mutex
}

*StateDB 的關鍵方法

func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) 
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) 
func (s *StateDB) Finalise(deleteEmptyObjects bool) 

通過上面的三個方法可以演示整個 statedb 的操作流程
當執行 AddBalance 方法會設置 addrstateObject.Account.Balance += amount ,這時 addr 對應的 stateObject 會被放在 StateDB.stateObjects 中緩存起來,
當執行 Commit 方法時,會將 StateDB.stateObjects 中的數據構建成一顆默克爾樹存放在 StateDB.trie 中,修改數據時會產生 journal 日誌,爲了方便出錯時的回滾,
當執行 Finalise 方法時會刪除 journal 刷新 stateObjecttrie.root (如果 stateObject 存在 trie)

執行到此時樹的結構已經確定,最終的樹根也已經確定,但是在以太坊中數據此時還沒有進入數據庫
爲了提高效率StateDB對數據做了緩存處理,大部分時間都是放在內存中的,StateDB.db 是 state.Database 接口的實現,定義如下:

// Database wraps access to tries and contract code.
type Database interface {
    // blockchain 的 樹,root == block.stateRoot
    OpenTrie(root common.Hash) (Trie, error)
    // account 的 樹,addrHash == account.address
    OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
    ......
    // TrieDB retrieves the low level trie database used for data storage.
    TrieDB() *trie.Database
}

需要使用 TrieDB 這個方法返回的 trie.Database 對象來最終完成 trei 寫入 leveldb 的操作,這個方法在 WriteBlockWithState 中會被有條件調用,此處不再深入,謹以此文說明 Block、 StateDB、 Trie 之間的關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章