以太坊完整工作原理和运行机制!
以太坊完整工作原理和運(yùn)行機(jī)制!
2018年04月28日 00:00:00閱讀數(shù):26作者 | Preethi Kasireddy
編譯 | 老曹、Aholiab
鏈圈的人提起「以太坊」三個字想必是如雷貫耳。無論是以太幣,還是其天才創(chuàng)始人Vitalik Buterin,還是關(guān)于它的各種新聞,想必閉著眼都能看看而談。
即使如此,你可能還是不知道以太坊到底是個什么東西?它包含了哪些部分?又是基于哪些原理運(yùn)作的?這些你真的都知道嗎?
本文對以太坊的原理進(jìn)行一次大起底,盡量深入淺出且全面的讓你理解以太坊的本質(zhì)到底是什么。讓你對以太坊有一個整體而深刻的認(rèn)識。
本質(zhì)上來說,以太坊就是一個保存了數(shù)字交易永久記錄的公共數(shù)據(jù)庫。重要的是,這個數(shù)據(jù)庫不需要任何中間方來維護(hù)和雙方的權(quán)益。相反,它可以作為一種「無需信任」交易系統(tǒng)來運(yùn)作,也就是你可以在不需要第三方的情況下進(jìn)行點對點交易。
說來說去,還是這些啊?別急,今天就從技術(shù)層面來更深入的看看以太坊的機(jī)制到底是什么。在解釋這一概念時,我們盡量不去用復(fù)雜或看著嚇人的數(shù)學(xué)公式,即使不是程序員,也能在閱讀后對以太坊的運(yùn)營原理有更清晰的認(rèn)識。在閱讀的時候,你沒必要去理解文中的每一個細(xì)節(jié),可以聚焦在寬泛的層面上來理解以太坊。
區(qū)塊鏈的定義
區(qū)塊鏈?zhǔn)蔷哂小腹蚕頎顟B(tài)的加密安全交易單機(jī)」。聽起來有點拗口,我們來分析一下。
「加密安全」是指,數(shù)字貨幣的創(chuàng)造是通過復(fù)雜的數(shù)學(xué)算法來保證的,而這些算法很難破解,類似于系統(tǒng)的防火墻,你無法在區(qū)塊鏈中創(chuàng)建虛假的交易或刪除交易等。
「交易單機(jī)」是指,有一個機(jī)器的單個實例,就可以負(fù)責(zé)系統(tǒng)中產(chǎn)生的所有交易。 換句話說,每個人都相信「一個單一的全局真相」。
「共享狀態(tài)」意思是,在這一系統(tǒng)中所存儲的狀態(tài)對每個人都是透明和開放的。
知道了區(qū)塊鏈的定義,我們就來看看以太坊區(qū)塊鏈到底是什么?
以太坊區(qū)塊鏈算法
以太坊區(qū)塊鏈本質(zhì)上是一個為交易服務(wù)的狀態(tài)機(jī)。在計算機(jī)科學(xué)中,一個狀態(tài)機(jī)指的是這樣一種東西,它可以讀取一系列的輸入,并基于這些輸入產(chǎn)生一個新的狀態(tài)。
以太坊狀態(tài)機(jī)的運(yùn)行從一個「元狀態(tài)」開始,這類似于在網(wǎng)絡(luò)上沒有發(fā)生任何交易之前的一塊空白石板。當(dāng)交易執(zhí)行時,這個元狀態(tài)就轉(zhuǎn)變?yōu)橐恍┳罱K狀態(tài)。在任何時候,這個最終狀態(tài)都代表著以太坊區(qū)塊鏈的現(xiàn)狀。
以太坊系統(tǒng)中運(yùn)行著數(shù)百萬筆交易,這些交易被分組歸類為「區(qū)塊」。一個區(qū)塊包含一系列交易,每個塊與其前面的區(qū)塊串聯(lián)在一起。
要從一個狀態(tài)轉(zhuǎn)到另一個狀態(tài),必須證明交易是有效的。如果一個交易被認(rèn)為是有效的,就必須通過一個驗證過程,這一過程稱為「挖礦」。挖礦是指一組節(jié)點(即計算機(jī))消耗它們的計算資源來創(chuàng)建一個有效交易的區(qū)塊。
網(wǎng)絡(luò)中任何聲明自己是「礦工」的節(jié)點都可以嘗試創(chuàng)建和驗證區(qū)塊,全世界有許多礦工試圖同時創(chuàng)建和驗證區(qū)塊。每個礦工在向區(qū)塊鏈提交一個區(qū)塊時同時,都要提供一個數(shù)學(xué)的「證明」,且把這個證明作為一個保證:如果這個數(shù)學(xué)證明存在,則該區(qū)塊必然是有效的。
如果要在主區(qū)塊鏈上添加一個區(qū)塊,礦工必須比其他競爭對手更快地對其證明。通過讓礦工提供數(shù)學(xué)證明來驗證每個區(qū)塊的過程被稱為「工作量證明」。
一個礦工如果驗證了一個新的區(qū)塊,這個驗證工作就會得到一定數(shù)額的價值回報。這個價值是多少呢?以太坊區(qū)塊鏈?zhǔn)褂昧艘环N內(nèi)部數(shù)字令牌,叫做「以太幣」。 每當(dāng)一個礦工證明了一個區(qū)塊,就會生成并得到一個新的以太幣。
你可能會想:什么每個節(jié)點都在一條鏈上?礦工如果想創(chuàng)造新的的區(qū)塊鏈怎么辦?
正如我們在上文給區(qū)塊鏈的定義,區(qū)塊鏈?zhǔn)且粋€具有共享狀態(tài)的交易單機(jī)。這個定義決定了,區(qū)塊鏈的當(dāng)前狀態(tài)是一個單一的全局狀態(tài),每個人都必須接受。如果擁有多個狀態(tài)(或鏈條)會破壞整個系統(tǒng),因為人們不可能就哪個狀態(tài)是正確的狀態(tài)達(dá)成一致意見。如果這些鏈條是分開的,就會出現(xiàn)一個人在一條鏈上有10個以太幣,在另一條鏈上有20個的情況。在這種情況下,我們沒有辦法確定哪一個鏈條最「有效」,無法確定哪個人有多少硬幣。
多條鏈的產(chǎn)生,被稱為「分叉」。因為分叉會破壞系統(tǒng),因此我們通常會避免分叉,迫使人們選擇他們「相信」的鏈條。
為了確定哪個路徑是最有效的,并防止分叉的發(fā)生,以太坊使用了一種叫做「GHOST協(xié)議」的機(jī)制。
GHOST = Greedy Heaviest Observed Subtree
簡單地說,GHOST協(xié)議讓我們必須選擇在鏈上做最多計算的路徑。確定該路徑的一種方法是使用最新區(qū)塊的數(shù)量,來表示當(dāng)前路徑中的區(qū)塊總數(shù)(不計算起源塊)。塊數(shù)越多,路徑越長,挖礦的難度越大,最終就一定會到達(dá)最新區(qū)塊。使用這個方式讓我們對當(dāng)前區(qū)塊鏈狀態(tài)的唯一版本達(dá)成一致。
?
到這里,我們就對以太坊區(qū)塊鏈就有了一個宏觀的認(rèn)識,接下來我們就更深入地看看以太坊系統(tǒng)的主要組成部分:
帳戶;
狀態(tài);
Gas與費(fèi)用;
交易;
區(qū)塊;
交易執(zhí)行;
挖礦;
工作量證明。
以太坊的帳戶
以太坊的全球「共享狀態(tài)」是由許多賬戶組成的,它們能夠通過一個消息傳遞框架相互通信。每個帳戶都有一個與它關(guān)聯(lián)的狀態(tài)和一個20字節(jié)的地址。以太坊的地址是一個160位比特的標(biāo)識符,用于識別帳戶。
以太坊有兩種賬戶類型:
外部帳戶由私人密鑰控制,沒有與之相關(guān)的代碼。
合約賬戶由其合約代碼控制,并具有與其相關(guān)的代碼。
?
外部賬戶與合約賬戶
外部賬戶可以通過創(chuàng)建和使用其私人密鑰簽署一項交易,向其他外部賬戶或其他合約賬戶發(fā)送消息。兩個外部賬戶之間的消息只是一種價值轉(zhuǎn)移。但從一個外部帳戶到一個合約賬戶的消息會激活合約賬戶的代碼,使它能夠執(zhí)行各種操作(例如轉(zhuǎn)移代幣、寫入內(nèi)存、生成新的代幣、執(zhí)行一些計算、創(chuàng)建新合約等)。
與外部賬戶不同,合約賬戶不能自行啟動新的交易。相反,合約賬戶只能根據(jù)它們收到的其他交易(從外部賬戶或從另一個合約賬戶)進(jìn)行交易,這點我們會在下文進(jìn)行探討。
?
因此我們可以得出結(jié)論:在以太坊區(qū)塊鏈上發(fā)生的任何操作都是由外部控制賬戶的交易引起的。
?
帳戶狀態(tài)
無論帳戶是哪種類型,帳戶狀態(tài)都由以下四個部分組成。
nonce:如果帳戶是一個外部帳戶,這個數(shù)字代表從帳戶地址發(fā)送的交易數(shù)量。如果帳戶是一個合約帳戶,nonce是帳戶創(chuàng)建的合約數(shù)量。
balance:這個地址擁有的Wei(以太坊貨幣單位)數(shù)量,每個以太幣有1e+18 Wei。
storageRoot?:一個Merkle Patricia樹根節(jié)點的哈希,它對帳戶的存儲內(nèi)容的哈希值進(jìn)行編碼,并默認(rèn)為空。
codeHash:EVM(以太坊虛擬機(jī))的哈希值代碼。 對于合約帳戶,這是一個被哈希后并存儲為codeHash的代碼。對于外部帳戶,codeHash字段是空字符串的哈希。
全局狀態(tài)
我們知道以太坊的全局狀態(tài)包括帳戶地址和帳戶狀態(tài)之間的映射,這個映射存儲在一個數(shù)據(jù)結(jié)構(gòu)中,這種結(jié)構(gòu)被稱為Merkle Patricia樹。
Merkle Patricia樹是一種由一組樹狀節(jié)點構(gòu)成的二進(jìn)制結(jié)構(gòu),它包括:
底層有大量的葉子節(jié)點,其中包含了潛在的數(shù)據(jù);
一組中間節(jié)點,其中每個節(jié)點是其兩個子節(jié)點的哈希;
一個單個的根節(jié)點,也是由它的兩個子節(jié)點的哈希形成的,代表樹的頂部。
?
樹的底部數(shù)據(jù)是通過把我們想要存儲的數(shù)據(jù)分割成塊后而生成的,然后將這些數(shù)據(jù)塊分成幾個桶,然后對每個桶的進(jìn)行哈希迭代,值到剩下的哈希總數(shù)變?yōu)橐粋€根哈希。 ?
此外,樹需要存儲在里面的每一個值的密鑰。從樹的根節(jié)點開始,密鑰告訴你要遵循哪個子節(jié)點來獲取相應(yīng)的值,這些值存儲在葉子節(jié)點中。在以太坊中,狀態(tài)樹的鍵值對是地址和相關(guān)帳戶之間的映射,包括每個帳戶的balance、nonce、codeHash和storageRoot(storageRoot本身就是一棵樹)。
?
同樣的樹結(jié)構(gòu)也用于存儲交易和收據(jù)。更具體地說,每個塊都有一個「header」,它存儲三個不同Merkle樹結(jié)構(gòu)根節(jié)點的哈希,包括:
狀態(tài)樹;
交易樹;
收據(jù)樹。
?
Merkle樹能夠高效存儲信息的特性在以太坊系統(tǒng)中十分被看重,我們可以稱之為「輕節(jié)點」或「輕客戶端」,其實區(qū)塊鏈的節(jié)點有兩種:完整節(jié)點和輕節(jié)點
一個完整的節(jié)點需要下載完整的鏈,從元區(qū)塊到當(dāng)前的頭部塊,執(zhí)行所有的交易也都包含其中。通常情況下,礦工儲存完整的檔案節(jié)點,因為他們必須這樣做才能完成挖礦的過程。當(dāng)然,也可以在不執(zhí)行交易的情況下下載完整的節(jié)點。無論如何,任何完整的節(jié)點都包含整條鏈。
輕節(jié)點的概念與之相對,除非一個節(jié)點需要執(zhí)行每個交易或查詢歷史數(shù)據(jù),否則就沒有必要存儲整個鏈。這就是輕節(jié)點的意義所在。輕節(jié)點并不下載和存儲完整鏈并執(zhí)行所有的交易,而是只下載從元區(qū)塊到當(dāng)前頭部區(qū)塊的信息,而不執(zhí)行任何交易或檢索任何關(guān)聯(lián)狀態(tài)。因為輕節(jié)點可以訪問包含三個樹的區(qū)塊頭部哈希,所以仍然可以很容易地生成和接收關(guān)于交易、事件、余額等可驗證的結(jié)果。
這樣做的原因是因為Merkle樹中的哈希會向上傳播ーー如果一個惡意用戶試圖將一個偽造的交易交換到Merkle樹的底部,這種變化將導(dǎo)致上面節(jié)點的哈希變化,也將改變上面節(jié)點的哈希值。
?
任何想要驗證一段數(shù)據(jù)的節(jié)點都可以使用所謂的「Merkle證明」來執(zhí)行。一個Merkle 證明包括:
需要驗證的大量數(shù)據(jù)及其哈希值;
樹的根哈希;
「分支」(所有的參與者的哈希沿著路徑上升,一直到「樹根」)。
?
任何讀取該證明的人都可以驗證樹上所有的分枝是否一致,因此給定的數(shù)據(jù)塊實際上位于樹中的某個位置。
總之,使用Merkle樹的好處是,該結(jié)構(gòu)的根節(jié)點依據(jù)樹中存儲的數(shù)據(jù)進(jìn)行加密,因此根節(jié)點的哈希可以作為該數(shù)據(jù)的安全證明。由于區(qū)塊頭包括狀態(tài)、交易和收據(jù)樹的根哈希。因此任何節(jié)點都可以在不需要存儲整個狀態(tài)的情況下,驗證以太坊的一小部分狀態(tài),而整個狀態(tài)的大小可能是無限的。
Gas和支付
在以太坊中,費(fèi)用的計算是一個非常重要的概念。在以太坊網(wǎng)絡(luò)上進(jìn)行的每一筆交易都會產(chǎn)生費(fèi)用ーー沒有免費(fèi)的午餐!這筆費(fèi)用被稱為「Gas」。
Gas Price是指:你愿意花在每一個單位Gas上的以太幣數(shù)量,是用「gwei」來計算的。Wei是以太幣中最小的單位,其中1018?Wei代表1個以太幣。一個gwei是1,000,000,000 Wei。
每次交易,發(fā)送方都要設(shè)置一個Gas Limit和Gas Price。Gas Limit和Gas Price代表發(fā)送方愿意為執(zhí)行交易支付的最大金額。
例如,發(fā)送方將Gas Limit設(shè)置為50,000,一個Gas Price設(shè)置為20 gwei。這意味著發(fā)送者愿意花費(fèi)最多50,000 x 20 gwei,也就是:1,000,000,000,000,000 Wei(0.001以太幣)來執(zhí)行這一交易。
?
這里需要留意的是,Gas限額是發(fā)送方愿意花錢的最大限度。如果他們的賬戶余額中以太幣的數(shù)量大于這個最大值,那么他就可以進(jìn)行交易。在交易結(jié)束時,發(fā)送方將被退還的那些未使用的Gas,按原來的價格進(jìn)行兌換。
?
如果發(fā)送方?jīng)]有提供執(zhí)行交易所必需的Gas,則該交易運(yùn)行的結(jié)果會是「余額不足」,并被認(rèn)為無效。在這種情況下,交易處理中止,其間的產(chǎn)生的任何狀態(tài)都會發(fā)生逆轉(zhuǎn),這樣就可以在交易發(fā)生之前返回到以太坊區(qū)塊鏈。此外,交易失敗的記錄會被記錄下來,顯示嘗試過哪些交易,失敗了哪些交易。由于系統(tǒng)已經(jīng)在Gas用光之前做完了運(yùn)算工作,所以從邏輯上看,Gas不會被退還給發(fā)送方。
那么,這些Gas的錢到底去哪了呢?發(fā)送方花在Gas上的所有錢都寄給了「受益人」地址,也就是礦工地址。由于礦工們正在努力運(yùn)行計算和驗證交易,所以收到了Gas作為獎勵。
?
通常情況下,發(fā)送方愿意支付的Gas價格越高,礦工從交易中獲得的價值就越大,礦工們也就越有可能選擇這個交易。通過這種方式,礦工可以自由地選擇交易。為了給發(fā)送者設(shè)置Gas Price做參考,礦工們可以直接提出他們執(zhí)行交易所需的最低Gas Price。
存儲費(fèi)用
Gas不僅用于支付計算的費(fèi)用,還用于支付存儲的使用費(fèi)用。存儲的總費(fèi)用與使用的32字節(jié)的最小倍數(shù)成正比。
存儲費(fèi)用與交易費(fèi)用有一些不同。由于增加的存儲量增加了所有節(jié)點上的以太坊狀態(tài)數(shù)據(jù)庫的大小,所以存儲數(shù)據(jù)的數(shù)量會變小。由于這個原因,如果一個交易有一個步驟可以清除存儲中的條目,則可以免除執(zhí)行該操作的存儲費(fèi)用,并且還能因此得到退款。
費(fèi)用的目的是什么?
以太坊工作方式的一個重要方面是,網(wǎng)絡(luò)執(zhí)行的每一個操作都同時受到每個完整節(jié)點的影響。然而,在以太坊虛擬機(jī)上的計算步驟非常昂貴。因此,以太坊智能合約更適合簡單的任務(wù),比如運(yùn)行簡單的業(yè)務(wù)邏輯或驗證簽名和加密其他對象,而不適合更復(fù)雜的用途,比如文件存儲、電子郵件或機(jī)器學(xué)習(xí),這些都會給網(wǎng)絡(luò)帶來壓力。收費(fèi)的目的就是使整個網(wǎng)絡(luò)不會因用戶的不當(dāng)使用而變得負(fù)擔(dān)過重。
除此之外,以太坊是一種圖靈完整語言(圖靈機(jī)是一種能夠模擬任何計算機(jī)算法的機(jī)器)。這就允許了循環(huán),使得以太坊區(qū)塊鏈容易受到暫停問題的影響,因為在這個問題中,無法確定一個程序是否會無限運(yùn)行。如果沒有費(fèi)用,意圖不良的人可以通過在交易中執(zhí)行一個無限循環(huán)來擾亂網(wǎng)絡(luò),從而產(chǎn)生不良的影響。因此,費(fèi)用保護(hù)了網(wǎng)絡(luò)免受蓄意攻擊。
那么,為什么我們還要支付存儲費(fèi)用呢?就像計算一樣,在以太坊網(wǎng)絡(luò)上的存儲也是整個網(wǎng)絡(luò)必須承擔(dān)的一個成本。
交易與消息
我們在上面說到,以太坊是一個基于交易的狀態(tài)機(jī)。換句話說,不同賬戶之間發(fā)生的交易正是以太坊從一個狀態(tài)轉(zhuǎn)移到另一個狀態(tài)的原因。
因此,交易可以看做是一個由外部擁有的帳戶生成的序列化加密簽名指令,然后提交給區(qū)塊鏈。
交易分為兩類:「消息調(diào)用」和「合約創(chuàng)建」(創(chuàng)建新的以太坊合約的交易)。不管哪一類,所有交易都包含以下組件:
Nonce:發(fā)送方發(fā)送的交易數(shù)量的計數(shù);
gasPrice:發(fā)送方愿意支付每單位Gas所需執(zhí)行交易的Wei數(shù)量;
gasLimit:發(fā)送方愿意支付的執(zhí)行這一交易的Gas最大數(shù)量。這個數(shù)額是預(yù)先設(shè)定和支付的;
to:接收方的地址,在創(chuàng)建合約的交易中,合約帳戶地址還不存在,因此使用了空值;
Value:從發(fā)送方轉(zhuǎn)移到收件方的金額,在創(chuàng)建合約的交易中,這個Value作為新創(chuàng)建合約賬戶內(nèi)的起始余額;
v, r, s:用于生成識別交易發(fā)送方的簽名;
Init(只存在于創(chuàng)建合同的交易中):用于初始化新合約帳戶的EVM代碼片段,它只運(yùn)行一次,然后被丟棄,當(dāng)init第一次運(yùn)行時,它會返回帳戶代碼的主體,這個代碼是與合約帳戶永久關(guān)聯(lián)的一段代碼;
data(只存在于消息調(diào)用中的可選字段):消息調(diào)用的輸入數(shù)據(jù)(即參數(shù))。例如,如果一個智能合約充當(dāng)域名注冊服務(wù),那么對該合約的調(diào)用可能會有諸如域名以及IP地址等輸入字段。
?
在說「賬戶」的時候,我們看到,交易(包括消息調(diào)用和合約創(chuàng)建的交易),總是由外部賬戶啟動并提交給區(qū)塊鏈的。另一種思考方式是,交易是連接外部世界與以太坊內(nèi)部狀態(tài)的橋梁。
?
但這并不意味著一個合約不能與其他合約對話。在全局范圍內(nèi)存在的合約,可以與同一范圍內(nèi)的其他合約進(jìn)行交流。它們是以通過「消息」或「內(nèi)部交易」的方式來實現(xiàn)的。我們可以認(rèn)為消息或內(nèi)部交易類似于交易,其主要區(qū)別在于它們不是由外部賬戶所產(chǎn)生的,相反,是由合約產(chǎn)生的,是虛擬對象。與交易不同,合約不是序列化的,而是只存在于以太坊的執(zhí)行環(huán)境中。
當(dāng)一個合約將一個內(nèi)部交易發(fā)送到另一個合約時,存在于接收方合約賬戶上的關(guān)聯(lián)代碼就會被執(zhí)行。
?
需要注意的一點是,內(nèi)部交易或消息不包含Gas Limit。這是因為Gas Limit是由原始交易的外部創(chuàng)建者(即部分外部帳戶)來決定的。外部賬戶集合的Gas Limit必須足夠高,以便進(jìn)行交易,這包括由這一交易而導(dǎo)致發(fā)生的任何次級處理運(yùn)行,例如合約對合約的消息。如果在交易和消息鏈中,特定的消息執(zhí)行耗盡了Gas,那么該消息的執(zhí)行將與執(zhí)行引發(fā)的所有后續(xù)消息一起恢復(fù)。不過,上一級的執(zhí)行不需要恢復(fù)。
以太坊的區(qū)塊
所有的交易都被組合成「區(qū)塊」,區(qū)塊鏈則包含一系列這樣被鏈接在一起的區(qū)塊。在以太坊中,一個區(qū)塊包括「區(qū)塊頭」、關(guān)于包含在此區(qū)塊中交易集的信息,與當(dāng)前塊的ommers相關(guān)的一系列其他區(qū)塊頭Ommer解釋
Ommer是什么?
比起比特幣之類的區(qū)塊鏈,以太坊的構(gòu)建方式使區(qū)塊生成時間要低很多。這樣可以更快地處理交易。然而,縮短區(qū)塊生成時間的一個缺點是,礦工們要找到更多相互競爭的區(qū)塊解決方案。這些相互競爭的區(qū)塊也被稱為「孤兒區(qū)塊」,不能進(jìn)入主鏈。
Ommer的目的是幫助獎勵礦工,也包括這些孤兒區(qū)塊。礦工的ommer必須是「有效的」,也就是說在目前區(qū)塊的第六代或更小的范圍內(nèi)。六代之后,陳舊的孤兒區(qū)塊就不能再被引用。比起完整的區(qū)塊,Ommer塊獲得的獎勵要小一些。盡管如此,礦工們?nèi)匀挥幸欢ǖ膭恿θネ诰蜻@些孤兒區(qū)塊并獲得回報。
區(qū)塊頭
回到區(qū)塊本身,之前提到每個區(qū)塊都有一個區(qū)塊頭,但到底什么什么是區(qū)塊頭?區(qū)塊頭是區(qū)塊的一部分,包括:
Parenthash:一個父區(qū)塊頭的哈希(這就是為什么區(qū)塊鏈被稱為區(qū)塊「鏈」);
Ommershash:當(dāng)前區(qū)塊ommer列表的哈希;
beneficiary:收取采礦費(fèi)用的帳戶地址;
Stateroot:狀態(tài)樹的根節(jié)點哈希;
transactionsRoot:包含在此區(qū)塊中列出的所有交易樹根節(jié)點的哈希值;
receiptsRoot?:包含本區(qū)塊中列出的所有交易樹根節(jié)點的哈希的收據(jù);
logsBloom:一個由log組成的Bloom過濾器(數(shù)據(jù)結(jié)構(gòu));
difficulty:這個區(qū)塊的難度水平;
編號:當(dāng)前區(qū)塊的記數(shù)(元區(qū)塊的編號為0;每個后續(xù)區(qū)塊的塊數(shù)增加1);
gasLimit:當(dāng)前每個區(qū)塊的Gas限制;
gasUsed:本區(qū)塊交易所使用的總Gas之和;
時間戳:這個區(qū)塊注入的unix時間戳;
extraData:與此區(qū)塊相關(guān)的其他數(shù)據(jù);
mixHash:當(dāng)與nonce結(jié)合時,證明這個區(qū)塊執(zhí)行了足夠計算的哈希值;
Nonce:當(dāng)與mixHash結(jié)合時,證明這個區(qū)塊已經(jīng)執(zhí)行了足夠計算的哈希值;
每個區(qū)塊頭包含三個樹結(jié)構(gòu):
狀態(tài)根(stateRoot);
交易根(transactionsRoot);
收據(jù)根(receiptsRoot)。
這些樹結(jié)構(gòu)只不過是之前討論過的Merkle樹而已,沒有什么特別的。不過,從上面的描述中可以看到,有一些術(shù)語還需要進(jìn)一步說說。
日志(Log)
以太坊允許log跟蹤各種交易和消息。合約也可以通過定義需要記錄的「事件」來顯式生成log。
一條log包含:
記錄器的帳戶地址;
一系列主題,它們表示此交易所進(jìn)行的各種事件,以及,
任何與這些事件有關(guān)的數(shù)據(jù)。
log存儲在一個bloom過濾器中,它以有效的方式存儲海量的日志數(shù)據(jù)。
交易收據(jù)
區(qū)塊頭中存儲的日志來自于交易收據(jù)中包含的日志信息。就像在商店買東西時收到收據(jù)一樣,以太坊會為每筆交易生成一張收據(jù)。不出所料,每張收據(jù)都包含有關(guān)交易的某些信息。 這樣的收據(jù)包括以下內(nèi)容:
區(qū)塊編號;
區(qū)塊哈希;
交易哈希;
當(dāng)前交易所使用的Gas;
在當(dāng)前交易執(zhí)行后,當(dāng)前區(qū)塊中使用的Gas;
執(zhí)行當(dāng)前交易時創(chuàng)建的日志。
區(qū)塊的難度
區(qū)塊的「難度」用于在驗證區(qū)塊的時間內(nèi)來加強(qiáng)一致性。元區(qū)塊的難度為131,072,并用一個特殊的公式來計算后面每個區(qū)塊的難度。如果某個區(qū)塊比前一個區(qū)塊更快地被驗證,那么以太坊協(xié)議會增加該區(qū)塊的難度。
該區(qū)塊的難度會影響nonce,這是一個哈希,必須在挖礦時使用工作量證明算法來計算。
區(qū)塊的難度與nonce之間的關(guān)系在數(shù)學(xué)上表示為:
這里Hd代表了難度。找到滿足難度閾值的nonce的唯一方法是使用工作量證明算法來枚舉所有的可能性。 尋找解決方案的預(yù)期時間與難度成正比,難度越大,找到nonce就越困難,因此驗證區(qū)塊的難度就越大,這反過來增加了驗證新區(qū)塊的時間。通過調(diào)整區(qū)塊的難度,協(xié)議可以調(diào)整驗證區(qū)塊的時長。
另一方面,如果驗證時間變慢,那么協(xié)議就會減少難度。通過這種方式,驗證時間可以自我調(diào)整從而保持一個常量ーー平均每15秒一個區(qū)塊。
交易的執(zhí)行
看到這,你已經(jīng)來到了以太坊協(xié)議中最復(fù)雜的部分之一。假設(shè)將一個交易發(fā)送到以太坊網(wǎng)絡(luò)進(jìn)行處理,如果以太坊狀態(tài)要將你的交易包括在內(nèi),會發(fā)生什么?
首先,所有交易都必須滿足初始的一組需求才能執(zhí)行。 其中包括以下幾個部分。
交易必須是正確的RLP格式(RLP是「遞歸長度前綴」的縮寫,是用于二進(jìn)制數(shù)據(jù)編碼嵌套數(shù)組的數(shù)據(jù)格式,RLP是以太坊使用的序列化對象的格式)。
有效的交易簽名。
有效的交易nonce,回想一下,一個帳戶的nonce是從該帳戶發(fā)送的交易的統(tǒng)計,為了有效,交易nonce必須與發(fā)送方帳戶的nonce相等。
交易的Gas限額必須等于或大于交易所使用的內(nèi)部Gas, 內(nèi)部Gas包括:1)為執(zhí)行交易預(yù)先確定的費(fèi)用為21,000 Gas;2)與該交易一起發(fā)送的數(shù)據(jù)Gas費(fèi)用(對于每一個等于零的數(shù)據(jù)或代碼的每個字節(jié)收取4個Gas,每個非零字節(jié)的數(shù)據(jù)或代碼為68個Gas);3)如果這筆交易是一筆合約創(chuàng)建交易,則額外收取32,000 Gas。
?
發(fā)送方的賬戶余額必須有足夠的以太幣來支付前期的Gas費(fèi)用。前期Gas成本的計算很簡單:首先,交易的Gas Limit乘以交易的Gas Price,以確定最大的Gas成本。 然后,這個最大的成本被算在從發(fā)送方轉(zhuǎn)移到接收方的總額中。
如果交易符合上述有效性的所有要求,那么,就可以進(jìn)入下一個步驟。
首先,從發(fā)送方的余額中扣除執(zhí)行的前期成本,并將發(fā)送方帳戶的nonce加1。我們可以計算剩余的Gas,因為交易的Gas Limit要減去所使用的內(nèi)在Gas。
然后,交易開始執(zhí)行。在交易的整個執(zhí)行過程中,以太坊都跟蹤「子狀態(tài)」。子狀態(tài)記錄交易中產(chǎn)生的信息,這些信息也是交易完成后所馬上需要用到的。具體來說,它包含:
自毀集合:交易完成后將丟棄的一組帳戶(如果有的話);
日志序列:虛擬機(jī)代碼執(zhí)行的存檔和可索引的檢查點;
退款余額:交易完成后退還給發(fā)送者賬戶的余額。
一旦處理完交易中的所有步驟,并假定沒有無效狀態(tài),則通過確定向發(fā)送方退還未使用的Gas數(shù)量,來最終判定最終狀態(tài)。除了未使用的Gas外,發(fā)送方還從上文所述的「退款余額」中退還了一些余額。
一旦發(fā)送者獲得退款:
Gas(以太幣)就會給到給礦工;
該交易所使用的Gas被添加到區(qū)塊的Gas計數(shù)器(該計數(shù)器記錄該區(qū)塊中所有交易使用的總Gas);
刪除自毀集合中的所有帳戶(如果有的話);
最后,只剩下了新的狀態(tài)和已創(chuàng)建交易的一組log。至此,我們就講完了交易執(zhí)行的基本原理,下面再來看看合約創(chuàng)建的交易和消息調(diào)用之間的一些差異。
合約創(chuàng)建
前面說過,以太坊的賬戶分為兩類:合約帳戶和外部賬戶。當(dāng)交易是「契約創(chuàng)建」(Contract Creating)時,意思是,交易的目的是創(chuàng)建一個新的合約賬戶。
為了創(chuàng)建一個新的合約帳戶,我們首先使用一個特殊的公式來聲明新賬戶的地址,然后通過以下方式初始化新帳戶:
將nonce設(shè)置為零;
如果發(fā)送方在交易中發(fā)送了一定數(shù)量的以太幣作為價值,則將帳戶余額設(shè)置為該價值;
從發(fā)送方的余額中扣除這個新賬戶余額的增加部分;
將存儲設(shè)置為空;
將合約的codeHash設(shè)置為空字符串的哈希值;
一旦帳戶完成了初始化就可以創(chuàng)建帳戶了,使用與交易一起發(fā)送的init代碼。在執(zhí)行這個init代碼的過程中,可能發(fā)生很多情況。根據(jù)合約的構(gòu)造函數(shù),它可能更新帳戶的存儲,創(chuàng)建其他的合約賬戶,或其他的消息調(diào)用,等等。
一旦初始化合約的代碼被執(zhí)行就將開始消耗Gas,交易使用的Gas不能超出賬戶的余額,一旦超出,將會出現(xiàn)「Gas耗光」的異常并且退出。如果交易由于Gas耗光的異常而退出。
但是,如果發(fā)送方在交易中發(fā)送了一些以太幣,這時合約創(chuàng)建失敗也會退還以太幣嗎?答案是,不會。
如果初始化代碼執(zhí)行成功,則支付最終的合約創(chuàng)建成本。這是一個存儲成本,并且與創(chuàng)建合約代碼的大小成正比。如果剩余的Gas不足以支付這筆最終成本,那么這筆交易將再次聲明為一個「Gas耗光」異常。
如果一切順利,而且沒有遇到任何異常,那么剩余的Gas都會退還給交易的原始發(fā)送方,并允許改變狀態(tài)繼續(xù)存在!
消息調(diào)用
消息調(diào)用的執(zhí)行類似于合約創(chuàng)建,但有一些不同之處。
消息調(diào)用的執(zhí)行不包含任何init代碼,因為沒有創(chuàng)建新的帳戶。但是,如果這些數(shù)據(jù)是由交易發(fā)送方提供的,它可以包含輸入數(shù)據(jù)。一旦執(zhí)行,消息調(diào)用也有一個額外的組件,其中包含輸出數(shù)據(jù),如果后續(xù)執(zhí)行需要此數(shù)據(jù),則使用這些數(shù)據(jù)。
如同合約創(chuàng)建一樣,如果由于Gas耗盡或交易無效(例如堆棧溢出、無效的跳轉(zhuǎn)目的地或無效指令),則所使用的任何Gas都不會退還給原來的調(diào)用者,取而代之的是,所有剩余的Gas都會被消耗掉,并且狀態(tài)被重置到余額轉(zhuǎn)移之前的情況。
執(zhí)行模式
現(xiàn)在,我們來看看在VM中,交易實際上是如何執(zhí)行的。
實際處理交易的部分是以太坊自己的虛擬機(jī),被稱為EVM。就像之前定義的那樣,EVM是一個「圖靈完備」的虛擬機(jī)。唯一的不同是EVM有內(nèi)在Gas的約束。因此,可以完成的計算總量本質(zhì)上受到所提供Gas數(shù)量的限制。
?
此外,EVM 有一個基于棧機(jī)器的架構(gòu)。棧機(jī)器是一種使用「后入先出」的堆棧來保存臨時值的計算機(jī)。EVM中每個棧條目的大小為256位,最大為1024位。
EVM具有內(nèi)存,其中存儲的條目是字地址字節(jié)數(shù)組(word-addressed byte arrays)。 內(nèi)存是易失性的,這意味著它不是永久性的。
EVM還有存儲空間。與內(nèi)存不同,內(nèi)存的存儲是非易失性的,并作為系統(tǒng)狀態(tài)的一部分來維護(hù)。EVM在一個虛擬ROM中獨(dú)立存儲程序代碼,只能通過特殊的指令訪問虛擬ROM。這就是EVM與典型的馮·諾伊曼結(jié)構(gòu)的不同,馮·諾伊曼結(jié)構(gòu)中程序代碼是在內(nèi)存或存儲中。
?
EVM也有自己的語言——EVM字節(jié)碼。當(dāng)程序員在以太坊上寫智能合約的時候,通常用高級語言寫代碼,比如Solidity。然后,可以編譯成EVM字節(jié)碼,以便EVM可以理解執(zhí)行。
接下來我們來看看EVM如何運(yùn)行。在執(zhí)行特定的計算之前,處理器要確保以下信息是可用且有效的:
系統(tǒng)狀態(tài);
用于計算的剩余Gas;
擁有執(zhí)行代碼的帳戶地址;
產(chǎn)生此執(zhí)行交易的發(fā)送方地址;
引發(fā)代碼執(zhí)行的帳戶地址(可能與原始發(fā)送方不同);
產(chǎn)生此次執(zhí)行交易的Gas Price;
此執(zhí)行的輸入數(shù)據(jù);
作為當(dāng)前執(zhí)行的一部分,價值(以Wei為單位)傳遞到這個帳戶;
要執(zhí)行的機(jī)器碼;
當(dāng)前塊的區(qū)塊頭;
當(dāng)前消息調(diào)用或合約創(chuàng)建的棧深度;
在執(zhí)行開始時,內(nèi)存和棧是空的,程序計數(shù)器歸零。
然后,EVM遞歸執(zhí)行交易,計算系統(tǒng)狀態(tài)和每個循環(huán)的機(jī)器狀態(tài)。簡單地說,這個系統(tǒng)狀態(tài)就是全局狀態(tài)。機(jī)器狀態(tài)包括:
可用的Gas;
程序計數(shù)器;
內(nèi)存的內(nèi)容;
內(nèi)存中的字活躍數(shù);
棧內(nèi)容。
棧中條目是從該系列最左邊的部分中添加或刪除。表現(xiàn)為,在每個循環(huán)中,從剩余的Gas中減少適當(dāng)?shù)腉as,并且程序計數(shù)器遞增。
在每個循環(huán)的結(jié)束時,有三種可能性:
機(jī)器達(dá)到一個特殊狀態(tài)(例如Gas不足、指令無效、棧條目不足、棧條目溢出超過1024、無效的JUMP/JUMPI等),因此必須停止,任何更改都將被丟棄;
這個序列繼續(xù)進(jìn)入下一個循環(huán);
系統(tǒng)被迫停止。
假設(shè)執(zhí)行沒有達(dá)到一個特殊狀態(tài),并達(dá)到一個「可控制的狀態(tài)」或正常停止,那么機(jī)器就會生成結(jié)果狀態(tài)、保留執(zhí)行后剩余的Gas。
說了這么多,終于結(jié)束了以太坊中最復(fù)雜的部分。即使沒有完全理解這部分,也沒關(guān)系,除非是從事底層的開發(fā)工作,否則并不需要真正理解這些細(xì)節(jié)。
一個區(qū)塊的最終完成
最后,來看看多個交易塊是如何最終完成的。
這里所說的「最終」可能是指兩種不同的東西,這取決于區(qū)塊是新的還是已經(jīng)存在的。如果是一個新區(qū)塊,「最終」指的是挖掘這個區(qū)塊所需要的過程。如果是一個現(xiàn)有的區(qū)塊,那么「最終」指的是驗證塊的過程。在這兩種情況下,對于要到「最終」?fàn)顟B(tài)的區(qū)塊有以下四個要求。
驗證(如果是挖礦,就是判定)ommer:每個區(qū)塊頭中的每個ommer區(qū)塊都必須是一個有效的區(qū)塊頭,并且在當(dāng)前區(qū)塊的六代以內(nèi)。
驗證(如果是挖礦,就是判定)交易:區(qū)塊上的gasUsed數(shù)字必須等于該區(qū)塊中所列交易所使用Gas的累積。
申請獎勵(只限于挖礦的情況):受益人地址被授予5以太幣,用于開采該區(qū)塊。 (根據(jù)以太坊EIP-649,這5個ETH的報酬將很快減少到3個)。此外,對于每一個 ommer,當(dāng)前區(qū)塊的受益者將額外獲得當(dāng)前區(qū)塊獎勵的1/32。最后,ommer區(qū)塊的受益人也可以得到一定數(shù)額的賠償。
驗證(如果挖礦,計算一個有效的)狀態(tài)和nonce:確保應(yīng)用所有交易和由此產(chǎn)生的狀態(tài)更改,在區(qū)塊獎勵應(yīng)用于最終交易的結(jié)果狀態(tài)之后,定義新區(qū)塊的狀態(tài)。通過檢查這個最終狀態(tài)來驗證存儲在區(qū)塊頭中的狀態(tài)。
挖礦的工作量證明
將區(qū)塊難度賦予意義的算法叫做「工作量證明」(PoW)。以太坊的工作量證明算法被稱為「Ethash」(之前被稱為Dagger-Hashimoto)。
該算法的公式如下:
這里m是mixHash、n是nonce、Hn是新區(qū)塊的頭(不包括nonce和mixHash組件)、Hn是塊頭的nonce、d是DAG(一個大數(shù)據(jù)集)。
還記得上面談到的在區(qū)塊頭中存在的mixHash和nonce兩個字段嗎?
mixHash是一個哈希,當(dāng)與nonce結(jié)合時,可證明這個區(qū)塊執(zhí)行了足夠的計算;
nonce是一個哈希,當(dāng)與mixHash結(jié)合時,可證明這個區(qū)塊已經(jīng)執(zhí)行了足夠的計算
PoW的功能就是評估這兩個字段。至于如何使用的PoW函數(shù)來精確計算mixHash和nonce說起來有點復(fù)雜,但從總體上看,它的工作原理是這樣的。
每個區(qū)塊都算出一個「seed」,每個「epoch」對應(yīng)的seed都不相同,一個epoch相當(dāng)于3萬個區(qū)塊的長度。在第一個epoch,seed是一系列32字節(jié)零的哈希。對于后來的每一個epoch來說,它都是以前seed哈希的哈希。使用seed,一個節(jié)點可以計算一個偽隨機(jī)的「緩存」。
這個緩存非常有用,因為它使之前討論過的「輕節(jié)點」成為可能。輕節(jié)點的目的是為了使某些節(jié)點能夠有效地驗證交易,而沒有存儲整個塊環(huán)鏈數(shù)據(jù)集的負(fù)擔(dān)。一個輕節(jié)點可以完全基于這個緩存來驗證交易的有效性,因為緩存可以重新生成需要驗證的特定區(qū)塊。
使用緩存,節(jié)點可以生成DAG數(shù)據(jù)集,數(shù)據(jù)集中的每個項都依賴于緩存中的少量偽隨機(jī)選擇(pseudo-randomly-selected)的項。為了成為一名礦工,必須生成這個完整的數(shù)據(jù)集;所有客戶和礦工都存儲這個數(shù)據(jù)集,并且數(shù)據(jù)集隨時間線性增長。
然后,礦工們可以隨機(jī)抽取數(shù)據(jù)集的片段,然后通過數(shù)學(xué)函數(shù)將它們混合成一個mixHash。礦工將反復(fù)生成mixHash,直到輸出低于預(yù)期目標(biāo)的nonce。當(dāng)輸出滿足這個要求時,這個nonce被認(rèn)為是有效的,并且塊可以添加到鏈上。
挖礦作為一種安全機(jī)制
總體而言,PoW的目的是以一種安全加密方式證明工作量,基于特定的計算量生成某些輸出(即nonce)。這是因為除了窮舉所有可能性之外,沒有更好的辦法來找到低于要求閾值的nonce。重復(fù)應(yīng)用哈希函數(shù)的輸出具有均勻分布,因此我們可以確信,找到這樣一個nonce所需的平均時間取決于難度閾值。難度越大,解決問題的時間就越長。這樣,PoW算法對難度概念賦予了真實的意義,這個概念被用來增強(qiáng)區(qū)塊鏈的安全。
那么,區(qū)塊鏈安全又是指什么?很簡單:就是要創(chuàng)建一個每個人都信任的區(qū)塊鏈。正如先前在本文中討論的那樣,如果存在一個以上的鏈,用戶將對其失去信任,因為他們無法合理地確定哪一個鏈?zhǔn)恰赣行У摹规湣榱俗屢唤M用戶接受存儲在塊環(huán)鏈上的基本狀態(tài),需要一個大家都相信的且單一規(guī)范的區(qū)塊鏈。
而這正是PoW的作用:它確保一個特定的區(qū)塊鏈可以保持規(guī)范,使攻擊者難以創(chuàng)建新的區(qū)塊,或者覆蓋歷史的某一部分(例如擦除交易或創(chuàng)建虛假的交易),或者對一個分叉進(jìn)行維護(hù)。為了驗證他們的區(qū)塊,攻擊者需要比網(wǎng)絡(luò)中的其他任何人都更快地解決nonce問題,這樣網(wǎng)絡(luò)就會相信他們的鏈條是最重鏈(基于之前提到的GHOST協(xié)議的原則)。這是基本上不可能的,除非攻擊者擁有超過一半的網(wǎng)絡(luò)挖掘能力,因此這種情況被稱為「51%攻擊」。
挖礦作為一種財富分配機(jī)制
除了確保一個安全的區(qū)塊鏈環(huán)境,對那些為了提供這種安全而消耗算力的人,Pow還是一種分配財富的方式。回想一下,礦工在開采一個區(qū)塊時會得到獎勵,其中包括:
「獲勝」區(qū)塊獲得的5以太幣(不久將改為3以太幣)的獎賞;
該區(qū)塊所包括的交易在區(qū)塊內(nèi)消耗的Gas成本;
將ommer作為區(qū)塊的一部分的額外獎勵。
從長遠(yuǎn)來看,為了確保PoW機(jī)制在安全和財富分配方面的使用是可持續(xù)的,以太坊努力培養(yǎng)它的兩個特性:
讓盡可能多的人能夠接觸到它,換句話說,人們不應(yīng)該需要專門的硬件來運(yùn)行算法,這樣做的目的是使財富分配模型盡可能開放,以便任何人都可以根據(jù)自身的情況提供計算能力,以換取以太幣。
減少單個節(jié)點(或小集)產(chǎn)生不成比例利潤的可能性,任何節(jié)點,如果能夠獲得不成比例的利潤,就意味著節(jié)點對規(guī)范區(qū)塊鏈的確定有很大的影響,這會降低網(wǎng)絡(luò)的安全性。
在比特幣區(qū)塊鏈網(wǎng)絡(luò)中,與上述兩個屬性有關(guān)的一個問題是,PoW算法是一個 SHA256哈希函數(shù)。這類函數(shù)的弱點在于,它可以通過使用專門的硬件更有效地解決問題,也就是所謂的ASIC。
為了解決這一問題,以太坊選擇了將PoW算法按順序存儲到內(nèi)存硬件中。這意味著這個算法是經(jīng)過設(shè)計的,所以計算nonce需要大量的內(nèi)存和帶寬。大量的內(nèi)存需求使得計算機(jī)很難同時使用它的內(nèi)存來同時發(fā)現(xiàn)多個nonce,而且高帶寬的要求使得即使是超級計算機(jī)也很難同時發(fā)現(xiàn)多個nonce。這減少了集中化的風(fēng)險,也為正在進(jìn)行驗證的節(jié)點創(chuàng)建了一個更公平的機(jī)制。
需要注意的是,以太坊正在從PoW機(jī)制過渡到PoS機(jī)制,這又是另一個話題了,希望可以在今后的文章中探討。
結(jié)束語
終于到底了,這篇文章是不是有很多東西需要消化?
如果真的對以太坊感興趣,建議可以多讀幾次。我也是親自閱讀了以太坊的白皮書和代碼,然后才搞清楚以太坊要做的究竟是什么。還是那句話,你無需理解文章的每一個細(xì)節(jié),只要力求對整理原理有把握就很不錯了。
總結(jié)
以上是生活随笔為你收集整理的以太坊完整工作原理和运行机制!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三天竟然爆发两起大漏洞事件!我们来教你如
- 下一篇: 区块链去中心化的生命之源:“DPOS(委