以太坊 Block 、StateDB 與 Trie 的關系

前言

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

StateDB 用法與結構

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

BlockChain

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

StateDB

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

stateObject

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 的操作流程
當執(zhí)行 AddBalance 方法會設置 addrstateObject.Account.Balance += amount ,這時 addr 對應的 stateObject 會被放在 StateDB.stateObjects 中緩存起來,
當執(zhí)行 Commit 方法時,會將 StateDB.stateObjects 中的數(shù)據(jù)構建成一顆默克爾樹存放在 StateDB.trie 中,修改數(shù)據(jù)時會產(chǎn)生 journal 日志,為了方便出錯時的回滾,
當執(zhí)行 Finalise 方法時會刪除 journal 刷新 stateObjecttrie.root (如果 stateObject 存在 trie)

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

// 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 中會被有條件調(diào)用,此處不再深入,謹以此文說明 Block、 StateDB、 Trie 之間的關系

statedb 存儲結構

去數(shù)據(jù)庫里讀一下狀態(tài)信息,再去迭代一下 trie ,即可了解整個 statedb 的存儲結構,
觸發(fā) state 存儲只有 3 個條件

  1. preimages 到 4MB
  2. stateobject 到 20MB
  3. Stop node 時

比如 100塊時保存過 State,其中包含了 [A,B,C] 燦哥賬戶,到了 150 塊時又要保存 State 這是增加了 [D,E] 兩個賬戶的信息,此時 150 保存的就是 [A,B,C,D,E] ,如此看來整個 statedb 的不定期存儲,而且是全量備份數(shù)據(jù);

TODO : 是不是當快速同步時,下載到目標塊以后,開始下載狀態(tài)數(shù)據(jù),就只下載目標塊對應的狀態(tài)即可?這個還要繼續(xù)閱讀有關同步部分的代碼,才能確定.

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本篇文章分析的源碼地址為:https://github.com/ethereum/go-ethereum 分支:...
    fatcat22閱讀 1,860評論 0 2
  • 聲明:摘自github:https://github.com/ZtesoftCS/go-ethereum-code...
    藍Renly閱讀 851評論 0 0
  • 本篇文章分析的源碼地址為:https://github.com/ethereum/go-ethereum 分支:...
    fatcat22閱讀 1,984評論 0 4
  • 以太坊(Ethereum ):下一代智能合約和去中心化應用平臺 翻譯:巨蟹 、少平 譯者注:中文讀者可以到以太坊愛...
    車圣閱讀 3,928評論 1 7
  • 以眼還眼以牙還牙,很多人心里是認同的,我其實也是認同的,這句話包含著多么暢快的快意恩仇,有時候可能是我們更加努力生...
    五木籽閱讀 434評論 0 0

友情鏈接更多精彩內(nèi)容