1. 程式人生 > >以太坊完整工作原理和執行機制!

以太坊完整工作原理和執行機制!

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

作者 | Preethi Kasireddy

編譯 | 老曹、Aholiab

鏈圈的人提起「以太坊」三個字想必是如雷貫耳。無論是以太幣,還是其天才創始人Vitalik Buterin,還是關於它的各種新聞,想必閉著眼都能看看而談。

即使如此,你可能還是不知道以太坊到底是個什麼東西?它包含了哪些部分?又是基於哪些原理運作的?這些你真的都知道嗎?

本文對以太坊的原理進行一次大起底,儘量深入淺出且全面的讓你理解以太坊的本質到底是什麼。讓你對以太坊有一個整體而深刻的認識。

本質上來說,以太坊就是一個儲存了數字交易永久記錄的公共資料庫。重要的是,這個資料庫不需要任何中間方來維護和雙方的權益。相反,它可以作為一種「無需信任」交易系統來運作,也就是你可以在不需要第三方的情況下進行點對點交易。

說來說去,還是這些啊?別急,今天就從技術層面來更深入的看看以太坊的機制到底是什麼。在解釋這一概念時,我們儘量不去用複雜或看著嚇人的數學公式,即使不是程式設計師,也能在閱讀後對以太坊的運營原理有更清晰的認識。在閱讀的時候,你沒必要去理解文中的每一個細節,可以聚焦在寬泛的層面上來理解以太坊。

區塊鏈的定義

區塊鏈是具有「共享狀態的加密安全交易單機」。聽起來有點拗口,我們來分析一下。

  • 加密安全」是指,數字貨幣的創造是通過複雜的數學演算法來保證的,而這些演算法很難破解,類似於系統的防火牆,你無法在區塊鏈中建立虛假的交易或刪除交易等。

  • 交易單機」是指,有一個機器的單個例項,就可以負責系統中產生的所有交易。 換句話說,每個人都相信「一個單一的全域性真相」。

  • 共享狀態」意思是,在這一系統中所儲存的狀態對每個人都是透明和開放的。

知道了區塊鏈的定義,我們就來看看以太坊區塊鏈到底是什麼?

以太坊區塊鏈演算法

以太坊區塊鏈本質上是一個為交易服務的狀態機。在電腦科學中,一個狀態機指的是這樣一種東西,它可以讀取一系列的輸入,並基於這些輸入產生一個新的狀態。

640?wx_fmt=png

以太坊狀態機的執行從一個「元狀態」開始,這類似於在網路上沒有發生任何交易之前的一塊空白石板。當交易執行時,這個元狀態就轉變為一些最終狀態。在任何時候,這個最終狀態都代表著以太坊區塊鏈的現狀。

640?wx_fmt=png

以太坊系統中執行著數百萬筆交易,這些交易被分組歸類為「區塊」。一個區塊包含一系列交易,每個塊與其前面的區塊串聯在一起。

640?wx_fmt=png

要從一個狀態轉到另一個狀態,必須證明交易是有效的。如果一個交易被認為是有效的,就必須通過一個驗證過程,這一過程稱為「挖礦」。挖礦是指一組節點(即計算機)消耗它們的計算資源來建立一個有效交易的區塊。

網路中任何宣告自己是「礦工」的節點都可以嘗試建立和驗證區塊,全世界有許多礦工試圖同時建立和驗證區塊。每個礦工在向區塊鏈提交一個區塊時同時,都要提供一個數學的「證明」,且把這個證明作為一個保證:如果這個數學證明存在,則該區塊必然是有效的。

如果要在主區塊鏈上新增一個區塊,礦工必須比其他競爭對手更快地對其證明。通過讓礦工提供數學證明來驗證每個區塊的過程被稱為「工作量證明」。

一個礦工如果驗證了一個新的區塊,這個驗證工作就會得到一定數額的價值回報。這個價值是多少呢?以太坊區塊鏈使用了一種內部數字令牌,叫做「以太幣」。 每當一個礦工證明了一個區塊,就會生成並得到一個新的以太幣。

你可能會想:什麼每個節點都在一條鏈上?礦工如果想創造新的的區塊鏈怎麼辦?

正如我們在上文給區塊鏈的定義,區塊鏈是一個具有共享狀態的交易單機。這個定義決定了,區塊鏈的當前狀態是一個單一的全域性狀態,每個人都必須接受。如果擁有多個狀態(或鏈條)會破壞整個系統,因為人們不可能就哪個狀態是正確的狀態達成一致意見。如果這些鏈條是分開的,就會出現一個人在一條鏈上有10個以太幣,在另一條鏈上有20個的情況。在這種情況下,我們沒有辦法確定哪一個鏈條最「有效」,無法確定哪個人有多少硬幣。

多條鏈的產生,被稱為「分叉」。因為分叉會破壞系統,因此我們通常會避免分叉,迫使人們選擇他們「相信」的鏈條。

640?wx_fmt=png

為了確定哪個路徑是最有效的,並防止分叉的發生,以太坊使用了一種叫做「GHOST協議」的機制。

GHOST = Greedy Heaviest Observed Subtree

簡單地說,GHOST協議讓我們必須選擇在鏈上做最多計算的路徑。確定該路徑的一種方法是使用最新區塊的數量,來表示當前路徑中的區塊總數(不計算起源塊)。塊數越多,路徑越長,挖礦的難度越大,最終就一定會到達最新區塊。使用這個方式讓我們對當前區塊鏈狀態的唯一版本達成一致。

640?wx_fmt=png

到這裡,我們就對以太坊區塊鏈就有了一個巨集觀的認識,接下來我們就更深入地看看以太坊系統的主要組成部分:

  • 帳戶;

  • 狀態;

  • Gas與費用;

  • 交易;

  • 區塊;

  • 交易執行;

  • 挖礦;

  • 工作量證明。

以太坊的帳戶

以太坊的全球「共享狀態」是由許多賬戶組成的,它們能夠通過一個訊息傳遞框架相互通訊。每個帳戶都有一個與它關聯的狀態和一個20位元組的地址。以太坊的地址是一個160位位元的識別符號,用於識別帳戶。

以太坊有兩種賬戶型別:

  • 外部帳戶由私人金鑰控制,沒有與之相關的程式碼。

  • 合約賬戶由其合約程式碼控制,並具有與其相關的程式碼。

外部賬戶與合約賬戶

外部賬戶可以通過建立和使用其私人金鑰簽署一項交易,向其他外部賬戶或其他合約賬戶傳送訊息。兩個外部賬戶之間的訊息只是一種價值轉移。但從一個外部帳戶到一個合約賬戶的訊息會啟用合約賬戶的程式碼,使它能夠執行各種操作(例如轉移代幣、寫入記憶體、生成新的代幣、執行一些計算、建立新合約等)。

與外部賬戶不同,合約賬戶不能自行啟動新的交易。相反,合約賬戶只能根據它們收到的其他交易(從外部賬戶或從另一個合約賬戶)進行交易,這點我們會在下文進行探討。

640?wx_fmt=png

因此我們可以得出結論:在以太坊區塊鏈上發生的任何操作都是由外部控制賬戶的交易引起的。

640?wx_fmt=png

帳戶狀態

無論帳戶是哪種型別,帳戶狀態都由以下四個部分組成。

  • nonce:如果帳戶是一個外部帳戶,這個數字代表從帳戶地址傳送的交易數量。如果帳戶是一個合約帳戶,nonce是帳戶建立的合約數量。

  • balance:這個地址擁有的Wei(以太坊貨幣單位)數量,每個以太幣有1e+18 Wei。

  • storageRoot :一個Merkle Patricia樹根節點的雜湊,它對帳戶的儲存內容的雜湊值進行編碼,並預設為空。

  • codeHash:EVM(以太坊虛擬機器)的雜湊值程式碼。 對於合約帳戶,這是一個被雜湊後並存儲為codeHash的程式碼。對於外部帳戶,codeHash欄位是空字串的雜湊。

640?wx_fmt=png

全域性狀態

我們知道以太坊的全域性狀態包括帳戶地址和帳戶狀態之間的對映,這個對映儲存在一個數據結構中,這種結構被稱為Merkle Patricia樹。

Merkle Patricia樹是一種由一組樹狀節點構成的二進位制結構,它包括:

  • 底層有大量的葉子節點,其中包含了潛在的資料;

  • 一組中間節點,其中每個節點是其兩個子節點的雜湊;

  • 一個單個的根節點,也是由它的兩個子節點的雜湊形成的,代表樹的頂部。

640?wx_fmt=png

樹的底部資料是通過把我們想要儲存的資料分割成塊後而生成的,然後將這些資料塊分成幾個桶,然後對每個桶的進行雜湊迭代,值到剩下的雜湊總數變為一個根雜湊。  

640?wx_fmt=png

此外,樹需要儲存在裡面的每一個值的金鑰。從樹的根節點開始,金鑰告訴你要遵循哪個子節點來獲取相應的值,這些值儲存在葉子節點中。在以太坊中,狀態樹的鍵值對是地址和相關帳戶之間的對映,包括每個帳戶的balance、nonce、codeHash和storageRoot(storageRoot本身就是一棵樹)。

640?wx_fmt=png

同樣的樹結構也用於儲存交易和收據。更具體地說,每個塊都有一個「header」,它儲存三個不同Merkle樹結構根節點的雜湊,包括:

  1. 狀態樹;

  2. 交易樹;

  3. 收據樹。

640?wx_fmt=png

Merkle樹能夠高效儲存資訊的特性在以太坊系統中十分被看重,我們可以稱之為「輕節點」或「輕客戶端」,其實區塊鏈的節點有兩種:完整節點和輕節點

一個完整的節點需要下載完整的鏈,從元區塊到當前的頭部塊,執行所有的交易也都包含其中。通常情況下,礦工儲存完整的檔案節點,因為他們必須這樣做才能完成挖礦的過程。當然,也可以在不執行交易的情況下下載完整的節點。無論如何,任何完整的節點都包含整條鏈。

輕節點的概念與之相對,除非一個節點需要執行每個交易或查詢歷史資料,否則就沒有必要儲存整個鏈。這就是輕節點的意義所在。輕節點並不下載和儲存完整鏈並執行所有的交易,而是隻下載從元區塊到當前頭部區塊的資訊,而不執行任何交易或檢索任何關聯狀態。因為輕節點可以訪問包含三個樹的區塊頭部雜湊,所以仍然可以很容易地生成和接收關於交易、事件、餘額等可驗證的結果。

這樣做的原因是因為Merkle樹中的雜湊會向上傳播ーー如果一個惡意使用者試圖將一個偽造的交易交換到Merkle樹的底部,這種變化將導致上面節點的雜湊變化,也將改變上面節點的雜湊值。

640?wx_fmt=png

任何想要驗證一段資料的節點都可以使用所謂的「Merkle證明」來執行。一個Merkle 證明包括:

  1. 需要驗證的大量資料及其雜湊值;

  2. 樹的根雜湊;

  3. 「分支」(所有的參與者的雜湊沿著路徑上升,一直到「樹根」)。

640?wx_fmt=png

任何讀取該證明的人都可以驗證樹上所有的分枝是否一致,因此給定的資料塊實際上位於樹中的某個位置。

總之,使用Merkle樹的好處是,該結構的根節點依據樹中儲存的資料進行加密,因此根節點的雜湊可以作為該資料的安全證明。由於區塊頭包括狀態、交易和收據樹的根雜湊。因此任何節點都可以在不需要儲存整個狀態的情況下,驗證以太坊的一小部分狀態,而整個狀態的大小可能是無限的。

Gas和支付

在以太坊中,費用的計算是一個非常重要的概念。在以太坊網路上進行的每一筆交易都會產生費用ーー沒有免費的午餐!這筆費用被稱為「Gas」。

Gas Price是指:你願意花在每一個單位Gas上的以太幣數量,是用「gwei」來計算的。Wei是以太幣中最小的單位,其中1018 Wei代表1個以太幣。一個gwei是1,000,000,000 Wei。

每次交易,傳送方都要設定一個Gas Limit和Gas Price。Gas Limit和Gas Price代表傳送方願意為執行交易支付的最大金額。

例如,傳送方將Gas Limit設定為50,000,一個Gas Price設定為20 gwei。這意味著傳送者願意花費最多50,000 x 20 gwei,也就是:1,000,000,000,000,000 Wei(0.001以太幣)來執行這一交易。

640?wx_fmt=png

這裡需要留意的是,Gas限額是傳送方願意花錢的最大限度。如果他們的賬戶餘額中以太幣的數量大於這個最大值,那麼他就可以進行交易。在交易結束時,傳送方將被退還的那些未使用的Gas,按原來的價格進行兌換。

640?wx_fmt=png

如果傳送方沒有提供執行交易所必需的Gas,則該交易執行的結果會是「餘額不足」,並被認為無效。在這種情況下,交易處理中止,其間的產生的任何狀態都會發生逆轉,這樣就可以在交易發生之前返回到以太坊區塊鏈。此外,交易失敗的記錄會被記錄下來,顯示嘗試過哪些交易,失敗了哪些交易。由於系統已經在Gas用光之前做完了運算工作,所以從邏輯上看,Gas不會被退還給傳送方。

640?wx_fmt=png

那麼,這些Gas的錢到底去哪了呢?傳送方花在Gas上的所有錢都寄給了「受益人」地址,也就是礦工地址。由於礦工們正在努力執行計算和驗證交易,所以收到了Gas作為獎勵。

640?wx_fmt=png

通常情況下,傳送方願意支付的Gas價格越高,礦工從交易中獲得的價值就越大,礦工們也就越有可能選擇這個交易。通過這種方式,礦工可以自由地選擇交易。為了給傳送者設定Gas Price做參考,礦工們可以直接提出他們執行交易所需的最低Gas Price。

儲存費用

Gas不僅用於支付計算的費用,還用於支付儲存的使用費用。儲存的總費用與使用的32位元組的最小倍數成正比。

儲存費用與交易費用有一些不同。由於增加的儲存量增加了所有節點上的以太坊狀態資料庫的大小,所以儲存資料的數量會變小。由於這個原因,如果一個交易有一個步驟可以清除儲存中的條目,則可以免除執行該操作的儲存費用,並且還能因此得到退款。

費用的目的是什麼?

以太坊工作方式的一個重要方面是,網路執行的每一個操作都同時受到每個完整節點的影響。然而,在以太坊虛擬機器上的計算步驟非常昂貴。因此,以太坊智慧合約更適合簡單的任務,比如執行簡單的業務邏輯或驗證簽名和加密其他物件,而不適合更復雜的用途,比如檔案儲存、電子郵件或機器學習,這些都會給網路帶來壓力。收費的目的就是使整個網路不會因使用者的不當使用而變得負擔過重。

除此之外,以太坊是一種圖靈完整語言(圖靈機是一種能夠模擬任何計算機演算法的機器)。這就允許了迴圈,使得以太坊區塊鏈容易受到暫停問題的影響,因為在這個問題中,無法確定一個程式是否會無限執行。如果沒有費用,意圖不良的人可以通過在交易中執行一個無限迴圈來擾亂網路,從而產生不良的影響。因此,費用保護了網路免受蓄意攻擊。

那麼,為什麼我們還要支付儲存費用呢?就像計算一樣,在以太坊網路上的儲存也是整個網路必須承擔的一個成本。

交易與訊息

我們在上面說到,以太坊是一個基於交易的狀態機。換句話說,不同賬戶之間發生的交易正是以太坊從一個狀態轉移到另一個狀態的原因。

因此,交易可以看做是一個由外部擁有的帳戶生成的序列化加密簽名指令,然後提交給區塊鏈。

交易分為兩類:「訊息呼叫」和「合約建立」(建立新的以太坊合約的交易)。不管哪一類,所有交易都包含以下元件:

  • Nonce:傳送方傳送的交易數量的計數;

  • gasPrice:傳送方願意支付每單位Gas所需執行交易的Wei數量;

  • gasLimit:傳送方願意支付的執行這一交易的Gas最大數量。這個數額是預先設定和支付的;

  • to:接收方的地址,在建立合約的交易中,合約帳戶地址還不存在,因此使用了空值;

  • Value:從傳送方轉移到收件方的金額,在建立合約的交易中,這個Value作為新建立合約賬戶內的起始餘額;

  • v, r, s:用於生成識別交易傳送方的簽名;

  • Init(只存在於建立合同的交易中):用於初始化新合約帳戶的EVM程式碼片段,它只執行一次,然後被丟棄,當init第一次執行時,它會返回帳戶程式碼的主體,這個程式碼是與合約帳戶永久關聯的一段程式碼;

  • data(只存在於訊息呼叫中的可選欄位):訊息呼叫的輸入資料(即引數)。例如,如果一個智慧合約充當域名註冊服務,那麼對該合約的呼叫可能會有諸如域名以及IP地址等輸入欄位。

640?wx_fmt=png

在說「賬戶」的時候,我們看到,交易(包括訊息呼叫和合約建立的交易),總是由外部賬戶啟動並提交給區塊鏈的。另一種思考方式是,交易是連線外部世界與以太坊內部狀態的橋樑。

640?wx_fmt=png

但這並不意味著一個合約不能與其他合約對話。在全域性範圍內存在的合約,可以與同一範圍內的其他合約進行交流。它們是以通過「訊息」或「內部交易」的方式來實現的。我們可以認為訊息或內部交易類似於交易,其主要區別在於它們不是由外部賬戶所產生的,相反,是由合約產生的,是虛擬物件。與交易不同,合約不是序列化的,而是隻存在於以太坊的執行環境中。

當一個合約將一個內部交易傳送到另一個合約時,存在於接收方合約賬戶上的關聯程式碼就會被執行。

640?wx_fmt=png

需要注意的一點是,內部交易或訊息不包含Gas Limit。這是因為Gas Limit是由原始交易的外部建立者(即部分外部帳戶)來決定的。外部賬戶集合的Gas Limit必須足夠高,以便進行交易,這包括由這一交易而導致發生的任何次級處理執行,例如合約對合約的訊息。如果在交易和訊息鏈中,特定的訊息執行耗盡了Gas,那麼該訊息的執行將與執行引發的所有後續訊息一起恢復。不過,上一級的執行不需要恢復。

以太坊的區塊

所有的交易都被組合成「區塊」,區塊鏈則包含一系列這樣被連結在一起的區塊。在以太坊中,一個區塊包括「區塊頭」、關於包含在此區塊中交易集的資訊,與當前塊的ommers相關的一系列其他區塊頭Ommer解釋

Ommer是什麼?

比起比特幣之類的區塊鏈,以太坊的構建方式使區塊生成時間要低很多。這樣可以更快地處理交易。然而,縮短區塊生成時間的一個缺點是,礦工們要找到更多相互競爭的區塊解決方案。這些相互競爭的區塊也被稱為「孤兒區塊」,不能進入主鏈。

Ommer的目的是幫助獎勵礦工,也包括這些孤兒區塊。礦工的ommer必須是「有效的」,也就是說在目前區塊的第六代或更小的範圍內。六代之後,陳舊的孤兒區塊就不能再被引用。比起完整的區塊,Ommer塊獲得的獎勵要小一些。儘管如此,礦工們仍然有一定的動力去挖掘這些孤兒區塊並獲得回報。

區塊頭

回到區塊本身,之前提到每個區塊都有一個區塊頭,但到底什麼什麼是區塊頭?區塊頭是區塊的一部分,包括:

  • Parenthash:一個父區塊頭的雜湊(這就是為什麼區塊鏈被稱為區塊「鏈」);

  • Ommershash:當前區塊ommer列表的雜湊;

  • beneficiary:收取採礦費用的帳戶地址;

  • Stateroot:狀態樹的根節點雜湊;

  • transactionsRoot:包含在此區塊中列出的所有交易樹根節點的雜湊值;

  • receiptsRoot :包含本區塊中列出的所有交易樹根節點的雜湊的收據;

  • logsBloom:一個由log組成的Bloom過濾器(資料結構);

  • difficulty:這個區塊的難度水平;

  • 編號:當前區塊的記數(元區塊的編號為0;每個後續區塊的塊數增加1);

  • gasLimit:當前每個區塊的Gas限制;

  • gasUsed:本區塊交易所使用的總Gas之和;

  • 時間戳:這個區塊注入的unix時間戳;

  • extraData:與此區塊相關的其他資料;

  • mixHash:當與nonce結合時,證明這個區塊執行了足夠計算的雜湊值;

  • Nonce:當與mixHash結合時,證明這個區塊已經執行了足夠計算的雜湊值;

640?wx_fmt=png

每個區塊頭包含三個樹結構:

  • 狀態根(stateRoot);

  • 交易根(transactionsRoot);

  • 收據根(receiptsRoot)。

這些樹結構只不過是之前討論過的Merkle樹而已,沒有什麼特別的。不過,從上面的描述中可以看到,有一些術語還需要進一步說說。

日誌(Log)

以太坊允許log跟蹤各種交易和訊息。合約也可以通過定義需要記錄的「事件」來顯式生成log。

一條log包含:

  • 記錄器的帳戶地址;

  • 一系列主題,它們表示此交易所進行的各種事件,以及,

  • 任何與這些事件有關的資料。

log儲存在一個bloom過濾器中,它以有效的方式儲存海量的日誌資料。

交易收據

區塊頭中儲存的日誌來自於交易收據中包含的日誌資訊。就像在商店買東西時收到收據一樣,以太坊會為每筆交易生成一張收據。不出所料,每張收據都包含有關交易的某些資訊。 這樣的收據包括以下內容:

  • 區塊編號;

  • 區塊雜湊;

  • 交易雜湊;

  • 當前交易所使用的Gas;

  • 在當前交易執行後,當前區塊中使用的Gas;

  • 執行當前交易時建立的日誌。

區塊的難度

區塊的「難度」用於在驗證區塊的時間內來加強一致性。元區塊的難度為131,072,並用一個特殊的公式來計算後面每個區塊的難度。如果某個區塊比前一個區塊更快地被驗證,那麼以太坊協議會增加該區塊的難度。

該區塊的難度會影響nonce,這是一個雜湊,必須在挖礦時使用工作量證明演算法來計算。

區塊的難度與nonce之間的關係在數學上表示為:

640?wx_fmt=png

這裡Hd代表了難度。找到滿足難度閾值的nonce的唯一方法是使用工作量證明演算法來列舉所有的可能性。 尋找解決方案的預期時間與難度成正比,難度越大,找到nonce就越困難,因此驗證區塊的難度就越大,這反過來增加了驗證新區塊的時間。通過調整區塊的難度,協議可以調整驗證區塊的時長。

另一方面,如果驗證時間變慢,那麼協議就會減少難度。通過這種方式,驗證時間可以自我調整從而保持一個常量ーー平均每15秒一個區塊。

交易的執行

看到這,你已經來到了以太坊協議中最複雜的部分之一。假設將一個交易傳送到以太坊網路進行處理,如果以太坊狀態要將你的交易包括在內,會發生什麼?

640?wx_fmt=png

首先,所有交易都必須滿足初始的一組需求才能執行。 其中包括以下幾個部分。

  • 交易必須是正確的RLP格式(RLP是「遞迴長度字首」的縮寫,是用於二進位制資料編碼巢狀陣列的資料格式,RLP是以太坊使用的序列化物件的格式)。

  • 有效的交易簽名。

  • 有效的交易nonce,回想一下,一個帳戶的nonce是從該帳戶傳送的交易的統計,為了有效,交易nonce必須與傳送方帳戶的nonce相等。

  • 交易的Gas限額必須等於或大於交易所使用的內部Gas, 內部Gas包括:1)為執行交易預先確定的費用為21,000 Gas;2)與該交易一起傳送的資料Gas費用(對於每一個等於零的資料或程式碼的每個位元組收取4個Gas,每個非零位元組的資料或程式碼為68個Gas);3)如果這筆交易是一筆合約建立交易,則額外收取32,000 Gas。

640?wx_fmt=png

  • 傳送方的賬戶餘額必須有足夠的以太幣來支付前期的Gas費用。前期Gas成本的計算很簡單:首先,交易的Gas Limit乘以交易的Gas Price,以確定最大的Gas成本。 然後,這個最大的成本被算在從傳送方轉移到接收方的總額中。

640?wx_fmt=png

如果交易符合上述有效性的所有要求,那麼,就可以進入下一個步驟。

首先,從傳送方的餘額中扣除執行的前期成本,並將傳送方帳戶的nonce加1。我們可以計算剩餘的Gas,因為交易的Gas Limit要減去所使用的內在Gas。

640?wx_fmt=png

然後,交易開始執行。在交易的整個執行過程中,以太坊都跟蹤「子狀態」。子狀態記錄交易中產生的資訊,這些資訊也是交易完成後所馬上需要用到的。具體來說,它包含:

  • 自毀集合:交易完成後將丟棄的一組帳戶(如果有的話);

  • 日誌序列:虛擬機器程式碼執行的存檔和可索引的檢查點;

  • 退款餘額:交易完成後退還給傳送者賬戶的餘額。

一旦處理完交易中的所有步驟,並假定沒有無效狀態,則通過確定向傳送方退還未使用的Gas數量,來最終判定最終狀態。除了未使用的Gas外,傳送方還從上文所述的「退款餘額」中退還了一些餘額。

一旦傳送者獲得退款:

  • Gas(以太幣)就會給到給礦工;

  • 該交易所使用的Gas被新增到區塊的Gas計數器(該計數器記錄該區塊中所有交易使用的總Gas);

  • 刪除自毀集合中的所有帳戶(如果有的話);

最後,只剩下了新的狀態和已建立交易的一組log。至此,我們就講完了交易執行的基本原理,下面再來看看合約建立的交易和訊息呼叫之間的一些差異。

合約建立

前面說過,以太坊的賬戶分為兩類:合約帳戶和外部賬戶。當交易是「契約建立」(Contract Creating)時,意思是,交易的目的是建立一個新的合約賬戶

為了建立一個新的合約帳戶,我們首先使用一個特殊的公式來宣告新賬戶的地址,然後通過以下方式初始化新帳戶:

  • 將nonce設定為零;

  • 如果傳送方在交易中傳送了一定數量的以太幣作為價值,則將帳戶餘額設定為該價值;

  • 從傳送方的餘額中扣除這個新賬戶餘額的增加部分;

  • 將儲存設定為空;

  • 將合約的codeHash設定為空字串的雜湊值;

一旦帳戶完成了初始化就可以建立帳戶了,使用與交易一起傳送的init程式碼。在執行這個init程式碼的過程中,可能發生很多情況。根據合約的建構函式,它可能更新帳戶的儲存,建立其他的合約賬戶,或其他的訊息呼叫,等等。

一旦初始化合約的程式碼被執行就將開始消耗Gas,交易使用的Gas不能超出賬戶的餘額,一旦超出,將會出現「Gas耗光」的異常並且退出。如果交易由於Gas耗光的異常而退出。

但是,如果傳送方在交易中傳送了一些以太幣,這時合約建立失敗也會退還以太幣嗎?答案是,不會。

如果初始化程式碼執行成功,則支付最終的合約建立成本。這是一個儲存成本,並且與建立合約程式碼的大小成正比。如果剩餘的Gas不足以支付這筆最終成本,那麼這筆交易將再次宣告為一個「Gas耗光」異常。

如果一切順利,而且沒有遇到任何異常,那麼剩餘的Gas都會退還給交易的原始傳送方,並允許改變狀態繼續存在!

訊息呼叫

訊息呼叫的執行類似於合約建立,但有一些不同之處。

訊息呼叫的執行不包含任何init程式碼,因為沒有建立新的帳戶。但是,如果這些資料是由交易傳送方提供的,它可以包含輸入資料。一旦執行,訊息呼叫也有一個額外的元件,其中包含輸出資料,如果後續執行需要此資料,則使用這些資料。

如同合約建立一樣,如果由於Gas耗盡或交易無效(例如堆疊溢位、無效的跳轉目的地或無效指令),則所使用的任何Gas都不會退還給原來的呼叫者,取而代之的是,所有