以太坊解析:默克爾樹、世界狀態、交易及其他


 

默克爾樹

以太坊的主要數據對象之前,我想先向各位簡要介紹一下默爾克樹到底是什么,以使得它得以發揮作用的屬性特征

假設由定制的默克爾-帕特里夏樹維護世界狀態和交易。

在默克爾樹中,由葉子節點保存區塊數據的哈希,而由非葉子節點保存其子節點的哈希。

-默克爾樹示意圖(包括節點以及他們之間的關系)

         默克爾樹所指向數據的任何改動都會引起節點哈希的變化。由於每一個父節點中所保存的哈希值都取決於子節點所包含的數據,所以子節點中數據的變更都會引起父節點哈希的變化。並且這樣的影響是連鎖反應,從葉子節點直達根節點的。因此對葉子節點所指向數據的改動會引起根節點所保存哈希的變化。由上述結構特征,我們可以引申出兩條重要的屬性:

  1. 在判斷兩棵默克爾樹所指向數據是否完全相同時,我們不需要比較每個葉子節點,而只需比較根節點所保存的哈希。

  2. 在判斷特定數據是否被樹所指向時,我們可以使用 默克爾證明 技術。此處不對該技術作過多介紹,只需知道這是證明數據存在於默克爾樹中的一種簡單、高效的方法。

第一種屬性的重要之處在於,我們能夠僅利用根節點的哈希值,就標示某一時刻整棵樹所指向的數據。這意味着僅通過保存根節點的哈希值就能標示區塊(無需儲存區塊鏈中所有的數據),且維護數據的不可篡改。

至此我們理清了默克爾樹中根節點哈希的作用,下面來介紹以太坊中的主要對象。

 

世界狀態

 

世界狀態是地址(賬戶)到賬戶狀態的映射。雖然世界狀態不保存在區塊鏈上,但在黃皮書的描述中,世界狀態也由樹來保存數據(此樹也被稱為狀態數據庫或者狀態樹)。世界狀態可以被視作為隨着交易的執行而持續更新的全局狀態。以太坊就像一個去中心化的計算機,世界狀態則是這台電腦的硬盤。

以太坊中所有的賬戶信息都體現在世界狀態之中,並由世界狀態樹保存。如果你想知道某一賬戶的余額,或者某智能合約當前的狀態,就需要通過查詢世界狀態樹來獲取該賬戶的具體狀態信息。下文中我也會簡要介紹這些信息是如何存儲的。

-世界狀態樹與賬戶存儲-

 

賬戶狀態

 

以太坊中有兩種賬戶類型:外部所有賬戶(Externally Owned Accounts 簡稱 EOA)以及合約賬戶。我們用來互相收發以太幣、部署智能合約的賬戶就是 EOA 賬戶,而部署智能合約時自動生成的賬戶則是合約賬戶。每一個智能合約都有其獨一無二的以太坊賬戶。

賬戶狀態反映了一個以太坊賬戶的各項信息。例如,它存儲了當前賬戶以太幣的余額信息、當前賬戶發送過的交易數量...每一個賬戶都有賬戶狀態。

下面就來看看賬戶狀態中都包括什么:

nonce

從此地址發送出去的交易數量(如果當前為 EOA 賬戶)或者此賬號產生的合約創建操作(現在先別管合約創建操作是什么)。

balance

此賬號所擁有的以太幣數量(以 Wei 計量)。

storageRoot

賬戶存儲樹的根節點哈希值(稍后介紹賬戶存儲是什么)。

codeHash

對於合約賬戶,就是此賬戶存儲 EVM 代碼的哈希值。對於 EOA 賬戶,此處留空。

賬戶狀態中不容忽視的一個細節是,上述對象在內的所有對象都可變(除了 codeHash)。舉例來說,當一個賬戶向其他賬戶發送以太幣時,除了 nonce 會增加,賬戶的余額也會相應改變。

而 codeHash 的不可變性使得,如果部署了有漏洞的智能合約,也無法修復更新此合約。對應的,只能部署一個新合約(而有漏洞的版本會一直存在於區塊鏈上)。這也是為什么使用 Truffle 進行智能合約的開發和部署十分必要,並且用 Solidity 編程時要遵循 最佳實踐 的要求。

賬戶存儲樹是保存與賬戶相關聯數據的結構。該項只有合約賬戶才有,而在 EOA 中, storageRoot 留空、 codeHash 則是一串空字符串的哈希值。所有智能合約的數據都以 32 字節映射的形式保存在賬戶存儲樹中。此處不再贅述賬戶狀態樹如何維持合約數據。如果讀者對其內部實現感興趣,強烈建議閱讀這篇文章。賬戶狀態中的 storageRoot 區域負責維持賬戶存儲樹根節點哈希值。

-賬戶狀態與賬戶存儲樹-

 

交易

 

交易推動當前狀態到下一狀態的轉變。在以太坊中有三種交易:

  1. EOA 之間傳輸值的交易(例如,改變發送方和接收方余額大小)。

  2. 發送消息來調用合約的交易(例如,通過發送消息調用來觸發 setter 方法,以設置合約中的值)。

  3. 用於部署合約的交易(由此創建了合約賬戶)。

(從技術角度來講,前兩種交易是一樣的...它們都是通過消息調用來改變賬戶狀態的交易,只不過一個是 EOA 賬戶,一個是合約賬戶。此處將交易分為三種是為了方便讀者的理解。)

交易由以下部分組成:

nonce

此賬戶發出的交易序號數(校對注:可以粗略理解為“這是該賬戶的第幾筆交易”)。

gasPrice

執行此交易、進行計算時為每單位 gas 所支付的費用(以 Wei 計量)。

gasLimit

執行此交易時可以使用的最大 gas 數量。

to

  • 如果此交易用於傳送以太幣,此處為接收以太幣的 EOA 地址。

  • 如果此交易用於向合約發送消息(例如,調用智能合約中的方法),此處為合約的地址。

  • 如果此交易用於創建合約,此處值為空。

value

  • 如果此交易用於收發以太幣,此處為接收賬戶以 Wei 計量的代幣數量。

  • 如果此交易用於發送對合約的消息調用,此處為向接收此消息智能合約所給付的 Wei 數量。

  • 如果此交易用於創建合約,此處為合約初始化時賬戶存放的以 Wei 計量的以太幣數量。

v, r, s

在交易的密碼學簽名中用到的值,可以用於確定交易的發送方。

data(只用於價值傳輸以及向智能合約發送消息調用)

發送消息調用時附帶的輸入數據(例如,假設你想要執行智能合約中的 setter 方法,數據區就應該包括 setter 方法的標識符,以及你想要設定的參數值)。

init(只用於合約創建)

用於初始化合約的 EVM 代碼。

別想着一下子就把這些概念消化完... 必須對以太坊的內部機理有更深的認識才真正理解、使用像 data 區、init 區這樣的概念。

相信不出你的意料,區塊中所有的交易也是存儲在默克爾樹中的。並且這棵樹的根節點哈希值由區塊頭保存!下面我們就來剖析一下以太坊區塊結構。

 

區塊

 

區塊分為兩部分,即區塊頭和區塊體。

區塊頭就是以太坊中的區塊鏈部分。它保存了前一個區塊(也可稱為父區塊)的哈希值,通過區塊頭的連接形成了一條由密碼學背書的鏈。

區塊體包含了此區塊中記錄的一系列交易,以及叔塊(ommer)區塊頭列表。如果想要進一步了解叔塊,我推薦閱讀這篇文章。

-以太坊區塊的抽象示意圖-

 

下面就來介紹區塊頭包括哪些部分。

parentHash

前一個區塊的區塊頭哈希值。每個區塊都包含前序區塊的哈希值,一路可回溯至鏈上的創世塊。這也就是維護數據不會被篡改的結構設計(任何對前序區塊的篡改都會影響后續所有區塊的哈希值)。

ommersHash

叔塊頭以及部分區塊體的哈希值。

beneficiary

因為挖到此區塊而獲得收益的以太坊賬戶。

stateRoot

世界狀態樹的根節點哈希值(在所有交易被執行后)。

transactionsRoot

交易樹根節點的哈希值。這棵樹包含了區塊體的所有交易。

receiptsRoot

每當交易執行時,以太坊都會生成對應結果的交易收據。此處就是這個交易收據樹的根節點哈希。

logsBloom

布隆過濾器,用於判斷某區塊的交易是否產生了某日志(如果對這方面感興趣,可以查閱 Stack Overflow 的這個答案)。這避免了在區塊中存儲日志信息(節省了大量空間)。

difficulty

此區塊的難度值。這是當前區塊挖礦難度的度量值(此處不對此概念的細節和計算作介紹)。

number

前序區塊的總數。這標示了區塊鏈的高度(即區塊鏈上有多少區塊)。創世區塊的 number 為 0 。

gasLimit

每一個交易都需要消耗 gas 。gas limit 標示了該區塊所記錄的所有交易可以使用的 gas 總量。這是限制區塊內交易數量的一種手段。

gasUsed

區塊中各條交易所實際消耗的 gas 總量。

timestamp

區塊創建時的 Unix 時間戳。謹記由於以太坊網絡去中心化的特性,我們不能信任這個值,特別是撰寫智能合約、涉及到時間相關的商業邏輯時不能依靠這個值。

extraData

能輸入任何東西的不定長字節數組。當礦工創建區塊時,可以在這個區域添加任何東西。

mixHash

用於驗證一個區塊是否被真正記錄到鏈上的哈希值(如果想要真正理解這個概念,建議閱讀這篇文章 Ethash proof-of-work function )。

nonce

和 mixHash 一樣,用於驗證區塊是否被真正記錄到鏈上的值。

哎呀...真是講到我嘴都酸了...建議你別着急,慢慢吸收!不過我要再次強調,閱讀本文不應以記住每一個名詞及其作用為目標(在谷歌上這些都能搜到)。我的寫作初衷是想用一種簡單的方式(至少比黃皮書簡單)介紹以太坊對象的方方面面,來幫助新手理解那些專業名詞代表什么。把這篇文章當作“笨方法學以太坊對象”就好了!🙂

 

結論

 

讓我們簡要回顧一下學到了什么!總體而言,以太坊有四種前綴樹:

  1. 世界狀態樹包括了從地址到賬戶狀態之間的映射。 世界狀態樹的根節點哈希值由區塊保存(在 stateRoot 字段),它標示了區塊創建時的當前狀態。整個網絡中只有一個世界狀態樹。

  2. 賬戶存儲樹保存了與某一智能合約相關的數據信息。由賬戶狀態保存賬戶存儲樹的根節點哈希值(在 storageRoot 字段)。每個賬戶都有一個賬戶存儲樹。

  3. 交易樹包含了一個區塊中的所有交易信息。由區塊頭(在 transactionsRoot 區域)保存交易樹的根節點哈希值。每個區塊都有一棵交易樹。

  4. 交易收據樹包含了一個區塊中所有交易的收據信息。同樣由區塊頭(在 receiptsRoot 區域)保存交易收據樹的根節點哈希值;每個區塊都有對應的交易收據樹。

我們今天討論的對象有:

  1. 世界狀態: 以太坊這台分布式計算機的硬盤。它是從地址到賬戶狀態的映射。

  2. 賬戶狀態: 保存着每個以太坊賬戶的狀態信息。賬戶狀態同樣保存着賬戶狀態樹的 storageRoot,后者包含了該賬戶的存儲數據。

  3. 交易: 標示了系統中的狀態轉移。它可以是資金的轉移、消息調用或是合約的部署。

  4. 區塊: 包括對前序區塊(parentHash)的鏈接,並且保存了當執行時會在系統中產生新狀態的交易。區塊同時保存了 stateRoot 、transactionRoot 、 receiptsRoot 、 世界狀態樹的根節點哈希、交易樹以及對應的交易收據樹。

我想用一張圖來表示文中提及的各種概念信息。

-區塊、交易、賬戶狀態對象以及以太坊的默克爾樹-

 

根據我的經驗,直接從黃皮書中學習以太坊並不方便,且需要巨大的耐心。如前所述,本文的主要目標就是用區塊鏈初學者能聽得懂的語言描述以太坊的主要對象。衷心希望這篇文章能指引你的學習之路!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM