希望屏幕面前的你在閱讀之后能明白默克爾樹到底是什么,以及它在以太坊中的作用;能理解“世界狀態(tài)”和“賬戶狀態(tài)”的概念;能在腦海中勾勒出交易以及區(qū)塊的結(jié)構(gòu)。
默克爾樹
在討論以太坊的主要數(shù)據(jù)對(duì)象之前,我想先向各位簡(jiǎn)要介紹一下默爾克樹到底是什么,以使得它得以發(fā)揮作用的屬性特征。
黃皮書中假設(shè)由定制的默克爾-帕特里夏樹維護(hù)世界狀態(tài)和交易。附錄 D 描述了這個(gè)數(shù)據(jù)結(jié)構(gòu)。
默克爾-帕特里夏樹有許多有意思的屬性,如果你想更深入地了解其在以太坊中的應(yīng)用,我推薦你閱讀這篇文章。
在默克爾樹中,由葉子節(jié)點(diǎn)保存區(qū)塊數(shù)據(jù)的哈希,而由非葉子節(jié)點(diǎn)保存其子節(jié)點(diǎn)的哈希。
-默克爾樹示意圖(包括節(jié)點(diǎn)以及他們之間的關(guān)系)-
默克爾樹所指向數(shù)據(jù)的任何改動(dòng)都會(huì)引起節(jié)點(diǎn)哈希的變化。由于每一個(gè)父節(jié)點(diǎn)中所保存的哈希值都取決于子節(jié)點(diǎn)所包含的數(shù)據(jù),所以子節(jié)點(diǎn)中數(shù)據(jù)的變更都會(huì)引起父節(jié)點(diǎn)哈希的變化。并且這樣的影響是連鎖反應(yīng),從葉子節(jié)點(diǎn)直達(dá)根節(jié)點(diǎn)的。因此對(duì)葉子節(jié)點(diǎn)所指向數(shù)據(jù)的改動(dòng)會(huì)引起根節(jié)點(diǎn)所保存哈希的變化。由上述結(jié)構(gòu)特征,我們可以引申出兩條重要的屬性:
1. 在判斷兩棵默克爾樹所指向數(shù)據(jù)是否完全相同時(shí),我們不需要比較每個(gè)葉子節(jié)點(diǎn),而只需比較根節(jié)點(diǎn)所保存的哈希。
2. 在判斷特定數(shù)據(jù)是否被樹所指向時(shí),我們可以使用 默克爾證明 技術(shù)。此處不對(duì)該技術(shù)作過多介紹,只需知道這是證明數(shù)據(jù)存在于默克爾樹中的一種簡(jiǎn)單、高效的方法。
第一種屬性的重要之處在于,我們能夠僅利用根節(jié)點(diǎn)的哈希值,就標(biāo)示某一時(shí)刻整棵樹所指向的數(shù)據(jù)。這意味著僅通過保存根節(jié)點(diǎn)的哈希值就能標(biāo)示區(qū)塊(無需儲(chǔ)存區(qū)塊鏈中所有的數(shù)據(jù)),且維護(hù)數(shù)據(jù)的不可篡改。
至此我們理清了默克爾樹中根節(jié)點(diǎn)哈希的作用,下面來介紹以太坊中的主要對(duì)象。
世界狀態(tài)
世界狀態(tài)是地址(賬戶)到賬戶狀態(tài)的映射。雖然世界狀態(tài)不保存在區(qū)塊鏈上,但在黃皮書的描述中,世界狀態(tài)也由樹來保存數(shù)據(jù)(此樹也被稱為狀態(tài)數(shù)據(jù)庫或者狀態(tài)樹)。世界狀態(tài)可以被視作為隨著交易的執(zhí)行而持續(xù)更新的全局狀態(tài)。以太坊就像一個(gè)去中心化的計(jì)算機(jī),世界狀態(tài)則是這臺(tái)電腦的硬盤。
以太坊中所有的賬戶信息都體現(xiàn)在世界狀態(tài)之中,并由世界狀態(tài)樹保存。如果你想知道某一賬戶的余額,或者某智能合約當(dāng)前的狀態(tài),就需要通過查詢世界狀態(tài)樹來獲取該賬戶的具體狀態(tài)信息。下文中我也會(huì)簡(jiǎn)要介紹這些信息是如何存儲(chǔ)的。
-世界狀態(tài)樹與賬戶存儲(chǔ)-
賬戶狀態(tài)
以太坊中有兩種賬戶類型:外部所有賬戶(Externally Owned Accounts 簡(jiǎn)稱 EOA)以及合約賬戶。我們用來互相收發(fā)以太幣、部署智能合約的賬戶就是 EOA 賬戶,而部署智能合約時(shí)自動(dòng)生成的賬戶則是合約賬戶。每一個(gè)智能合約都有其獨(dú)一無二的以太坊賬戶。
賬戶狀態(tài)反映了一個(gè)以太坊賬戶的各項(xiàng)信息。例如,它存儲(chǔ)了當(dāng)前賬戶以太幣的余額信息、當(dāng)前賬戶發(fā)送過的交易數(shù)量。..每一個(gè)賬戶都有賬戶狀態(tài)。
下面就來看看賬戶狀態(tài)中都包括什么:
nonce
從此地址發(fā)送出去的交易數(shù)量(如果當(dāng)前為 EOA 賬戶)或者此賬號(hào)產(chǎn)生的合約創(chuàng)建操作(現(xiàn)在先別管合約創(chuàng)建操作是什么)。
balance
此賬號(hào)所擁有的以太幣數(shù)量(以 Wei 計(jì)量)。
storageRoot
賬戶存儲(chǔ)樹的根節(jié)點(diǎn)哈希值(稍后介紹賬戶存儲(chǔ)是什么)。
codeHash
對(duì)于合約賬戶,就是此賬戶存儲(chǔ) EVM 代碼的哈希值。對(duì)于 EOA 賬戶,此處留空。
賬戶狀態(tài)中不容忽視的一個(gè)細(xì)節(jié)是,上述對(duì)象在內(nèi)的所有對(duì)象都可變(除了 codeHash)。舉例來說,當(dāng)一個(gè)賬戶向其他賬戶發(fā)送以太幣時(shí),除了 nonce 會(huì)增加,賬戶的余額也會(huì)相應(yīng)改變。
而 codeHash 的不可變性使得,如果部署了有漏洞的智能合約,也無法修復(fù)更新此合約。對(duì)應(yīng)的,只能部署一個(gè)新合約(而有漏洞的版本會(huì)一直存在于區(qū)塊鏈上)。這也是為什么使用 Truffle 進(jìn)行智能合約的開發(fā)和部署十分必要,并且用 Solidity 編程時(shí)要遵循 最佳實(shí)踐 的要求。
賬戶存儲(chǔ)樹是保存與賬戶相關(guān)聯(lián)數(shù)據(jù)的結(jié)構(gòu)。該項(xiàng)只有合約賬戶才有,而在 EOA 中, storageRoot 留空、 codeHash 則是一串空字符串的哈希值。所有智能合約的數(shù)據(jù)都以 32 字節(jié)映射的形式保存在賬戶存儲(chǔ)樹中。此處不再贅述賬戶狀態(tài)樹如何維持合約數(shù)據(jù)。賬戶狀態(tài)中的 storageRoot 區(qū)域負(fù)責(zé)維持賬戶存儲(chǔ)樹根節(jié)點(diǎn)哈希值。
-賬戶狀態(tài)與賬戶存儲(chǔ)樹-
交易
交易推動(dòng)當(dāng)前狀態(tài)到下一狀態(tài)的轉(zhuǎn)變。在以太坊中有三種交易:
1. EOA 之間傳輸值的交易(例如,改變發(fā)送方和接收方余額大小)。
2. 發(fā)送消息來調(diào)用合約的交易(例如,通過發(fā)送消息調(diào)用來觸發(fā) setter 方法,以設(shè)置合約中的值)。
3. 用于部署合約的交易(由此創(chuàng)建了合約賬戶)。
(從技術(shù)角度來講,前兩種交易是一樣的。..它們都是通過消息調(diào)用來改變賬戶狀態(tài)的交易,只不過一個(gè)是 EOA 賬戶,一個(gè)是合約賬戶。此處將交易分為三種是為了方便讀者的理解。)
交易由以下部分組成:
nonce
此賬戶發(fā)出的交易序號(hào)數(shù)(校對(duì)注:可以粗略理解為“這是該賬戶的第幾筆交易”)。
gasPrice
執(zhí)行此交易、進(jìn)行計(jì)算時(shí)為每單位 gas 所支付的費(fèi)用(以 Wei 計(jì)量)。
gasLimit
執(zhí)行此交易時(shí)可以使用的最大 gas 數(shù)量。
to
· 如果此交易用于傳送以太幣,此處為接收以太幣的 EOA 地址。
· 如果此交易用于向合約發(fā)送消息(例如,調(diào)用智能合約中的方法),此處為合約的地址。
· 如果此交易用于創(chuàng)建合約,此處值為空。
value
· 如果此交易用于收發(fā)以太幣,此處為接收賬戶以 Wei 計(jì)量的代幣數(shù)量。
· 如果此交易用于發(fā)送對(duì)合約的消息調(diào)用,此處為向接收此消息智能合約所給付的 Wei 數(shù)量。
· 如果此交易用于創(chuàng)建合約,此處為合約初始化時(shí)賬戶存放的以 Wei 計(jì)量的以太幣數(shù)量。
v, r, s
在交易的密碼學(xué)簽名中用到的值,可以用于確定交易的發(fā)送方。
data(只用于價(jià)值傳輸以及向智能合約發(fā)送消息調(diào)用)
發(fā)送消息調(diào)用時(shí)附帶的輸入數(shù)據(jù)(例如,假設(shè)你想要執(zhí)行智能合約中的 setter 方法,數(shù)據(jù)區(qū)就應(yīng)該包括 setter 方法的標(biāo)識(shí)符,以及你想要設(shè)定的參數(shù)值)。
init(只用于合約創(chuàng)建)
用于初始化合約的 EVM 代碼。
別想著一下子就把這些概念消化完。.. 必須對(duì)以太坊的內(nèi)部機(jī)理有更深的認(rèn)識(shí)才真正理解、使用像 data 區(qū)、init 區(qū)這樣的概念。
相信不出你的意料,區(qū)塊中所有的交易也是存儲(chǔ)在默克爾樹中的。并且這棵樹的根節(jié)點(diǎn)哈希值由區(qū)塊頭保存!下面我們就來剖析一下以太坊區(qū)塊結(jié)構(gòu)。
區(qū)塊
區(qū)塊分為兩部分,即區(qū)塊頭和區(qū)塊體。
區(qū)塊頭就是以太坊中的區(qū)塊鏈部分。它保存了前一個(gè)區(qū)塊(也可稱為父區(qū)塊)的哈希值,通過區(qū)塊頭的連接形成了一條由密碼學(xué)背書的鏈。
區(qū)塊體包含了此區(qū)塊中記錄的一系列交易,以及叔塊(ommer)區(qū)塊頭列表。
-以太坊區(qū)塊的抽象示意圖-
下面就來介紹區(qū)塊頭包括哪些部分。
parentHash
前一個(gè)區(qū)塊的區(qū)塊頭哈希值。每個(gè)區(qū)塊都包含前序區(qū)塊的哈希值,一路可回溯至鏈上的創(chuàng)世塊。這也就是維護(hù)數(shù)據(jù)不會(huì)被篡改的結(jié)構(gòu)設(shè)計(jì)(任何對(duì)前序區(qū)塊的篡改都會(huì)影響后續(xù)所有區(qū)塊的哈希值)。
ommersHash
叔塊頭以及部分區(qū)塊體的哈希值。
beneficiary
因?yàn)橥诘酱藚^(qū)塊而獲得收益的以太坊賬戶。
stateRoot
世界狀態(tài)樹的根節(jié)點(diǎn)哈希值(在所有交易被執(zhí)行后)。
transactionsRoot
交易樹根節(jié)點(diǎn)的哈希值。這棵樹包含了區(qū)塊體的所有交易。
receiptsRoot
每當(dāng)交易執(zhí)行時(shí),以太坊都會(huì)生成對(duì)應(yīng)結(jié)果的交易收據(jù)。此處就是這個(gè)交易收據(jù)樹的根節(jié)點(diǎn)哈希。
logsBloom
布隆過濾器,用于判斷某區(qū)塊的交易是否產(chǎn)生了某日志。這避免了在區(qū)塊中存儲(chǔ)日志信息(節(jié)省了大量空間)。
difficulty
此區(qū)塊的難度值。這是當(dāng)前區(qū)塊挖礦難度的度量值(此處不對(duì)此概念的細(xì)節(jié)和計(jì)算作介紹)。
number
前序區(qū)塊的總數(shù)。這標(biāo)示了區(qū)塊鏈的高度(即區(qū)塊鏈上有多少區(qū)塊)。創(chuàng)世區(qū)塊的 number 為 0 。
gasLimit
每一個(gè)交易都需要消耗 gas 。gas limit 標(biāo)示了該區(qū)塊所記錄的所有交易可以使用的 gas 總量。這是限制區(qū)塊內(nèi)交易數(shù)量的一種手段。
gasUsed
區(qū)塊中各條交易所實(shí)際消耗的 gas 總量。
timestamp
區(qū)塊創(chuàng)建時(shí)的 Unix 時(shí)間戳。謹(jǐn)記由于以太坊網(wǎng)絡(luò)去中心化的特性,我們不能信任這個(gè)值,特別是撰寫智能合約、涉及到時(shí)間相關(guān)的商業(yè)邏輯時(shí)不能依靠這個(gè)值。
extraData
能輸入任何東西的不定長(zhǎng)字節(jié)數(shù)組。當(dāng)?shù)V工創(chuàng)建區(qū)塊時(shí),可以在這個(gè)區(qū)域添加任何東西。
mixHash
用于驗(yàn)證一個(gè)區(qū)塊是否被真正記錄到鏈上的哈希值。
nonce
和 mixHash 一樣,用于驗(yàn)證區(qū)塊是否被真正記錄到鏈上的值。
哎呀。..真是講到我嘴都酸了。..建議你別著急,慢慢吸收!不過我要再次強(qiáng)調(diào),閱讀本文不應(yīng)以記住每一個(gè)名詞及其作用為目標(biāo)(在谷歌上這些都能搜到)。我的寫作初衷是想用一種簡(jiǎn)單的方式(至少比黃皮書簡(jiǎn)單)介紹以太坊對(duì)象的方方面面,來幫助新手理解那些專業(yè)名詞代表什么。把這篇文章當(dāng)作“笨方法學(xué)以太坊對(duì)象”就好了!
結(jié)論
讓我們簡(jiǎn)要回顧一下學(xué)到了什么!總體而言,以太坊有四種前綴樹:
1. 世界狀態(tài)樹包括了從地址到賬戶狀態(tài)之間的映射。 世界狀態(tài)樹的根節(jié)點(diǎn)哈希值由區(qū)塊保存(在 stateRoot 字段),它標(biāo)示了區(qū)塊創(chuàng)建時(shí)的當(dāng)前狀態(tài)。整個(gè)網(wǎng)絡(luò)中只有一個(gè)世界狀態(tài)樹。
2. 賬戶存儲(chǔ)樹保存了與某一智能合約相關(guān)的數(shù)據(jù)信息。由賬戶狀態(tài)保存賬戶存儲(chǔ)樹的根節(jié)點(diǎn)哈希值(在 storageRoot 字段)。每個(gè)賬戶都有一個(gè)賬戶存儲(chǔ)樹。
3. 交易樹包含了一個(gè)區(qū)塊中的所有交易信息。由區(qū)塊頭(在 transactionsRoot 區(qū)域)保存交易樹的根節(jié)點(diǎn)哈希值。每個(gè)區(qū)塊都有一棵交易樹。
4. 交易收據(jù)樹包含了一個(gè)區(qū)塊中所有交易的收據(jù)信息。同樣由區(qū)塊頭(在 receiptsRoot 區(qū)域)保存交易收據(jù)樹的根節(jié)點(diǎn)哈希值;每個(gè)區(qū)塊都有對(duì)應(yīng)的交易收據(jù)樹。
我們今天討論的對(duì)象有:
1. 世界狀態(tài): 以太坊這臺(tái)分布式計(jì)算機(jī)的硬盤。它是從地址到賬戶狀態(tài)的映射。
2. 賬戶狀態(tài): 保存著每個(gè)以太坊賬戶的狀態(tài)信息。賬戶狀態(tài)同樣保存著賬戶狀態(tài)樹的 storageRoot,后者包含了該賬戶的存儲(chǔ)數(shù)據(jù)。
3. 交易: 標(biāo)示了系統(tǒng)中的狀態(tài)轉(zhuǎn)移。它可以是資金的轉(zhuǎn)移、消息調(diào)用或是合約的部署。
4. 區(qū)塊: 包括對(duì)前序區(qū)塊(parentHash)的鏈接,并且保存了當(dāng)執(zhí)行時(shí)會(huì)在系統(tǒng)中產(chǎn)生新狀態(tài)的交易。區(qū)塊同時(shí)保存了 stateRoot 、transactionRoot 、 receiptsRoot 、 世界狀態(tài)樹的根節(jié)點(diǎn)哈希、交易樹以及對(duì)應(yīng)的交易收據(jù)樹。
我想用一張圖來表示文中提及的各種概念信息。
-區(qū)塊、交易、賬戶狀態(tài)對(duì)象以及以太坊的默克爾樹-
根據(jù)我的經(jīng)驗(yàn),直接從黃皮書中學(xué)習(xí)以太坊并不方便,且需要巨大的耐心。如前所述,本文的主要目標(biāo)就是用區(qū)塊鏈初學(xué)者能聽得懂的語言描述以太坊的主要對(duì)象。
評(píng)論
查看更多