詳解以太坊的工作原理

原文地址:https://feed.baidu.com/feed/data/box/landingreact?cmd=157&pageType=1&context=%7B%22nid%22%3A%22news_9065362251403519816%22%2C%22sourceFrom%22%3A%22bjh%22%7D

不管你們知不知道以太坊(Ethereum blockchain)是什麼,但是你們大概都聽說過以太坊。最近在新聞裏出現過很多次,包括一些專業雜誌的封面,但是如果你們對以太坊到底是什麼沒有一個基本的瞭解的話,看這些文章就會感覺跟看天書一樣。 所以,什麼是以太坊?本質上,就是一個保存數字交易永久記錄的公共數據庫。重要的是,這個數據庫不需要任何中央權威機構來維持和保護它。相反的它以一個“無信任”的交易系統來運行—一個個體在不需要信任任何第三方或對方的情況下進行點對點交易的架構。

依然感到很困惑?這就是這篇文章存在的理由。我的目標是在技術層面來解釋以太坊的工作原理,但是不會出現很複雜的數學問題或看起來很可怕的公式。即使你不是一個程序員,我希望你看完之後最起碼對技術有個更好的認識。如果有些部分技術性太強不好理解,這是非常正常的,真的沒有必要完全理解每一個小細節。我建議只要宏觀的理解一下事物就行了。

這篇文章中的很多議點都是以太坊黃皮書中討論過的概念的細分。我添加了我自己的解釋和圖表使理解以太坊更加簡單一點。那些足夠勇敢的人可以挑戰一下技術,去閱讀一下以太坊的黃皮書。

好了, 讓我們開始吧!

區塊鏈定義

區塊鏈就是一個具有共享狀態的密碼性安全交易的單機(cryptographically secure transactional singleton machine with shared-state)。[1]這有點長,是吧?讓我們將它分開來看:

“密碼性安全(Cryptographically secure)”是指用一個很難被解開的複雜數學機制算法來保證數字貨幣生產的安全性。將它想象成類似於防火牆的這種。它們使得欺騙系統近乎是一個不可能的事情(比如:構造一筆假的交易,消除一筆交易等等)。

“交易的單機(Transactional singleton machine)”是指只有一個權威的機器實例爲系統中產生的交易負責任。換句話說,只有一個全球真相是大家所相信的。

“具有共享狀態(With shared-state)”是指在這臺機器上存儲的狀態是共享的,對每個人都是開放的。

以太坊實現了區塊鏈的這個範例。

以太坊模型說明

以太坊的本質就是一個基於交易的狀態機(transaction-based state machine)。在計算機科學中,一個 狀態機 是指可以讀取一系列的輸入,然後根據這些輸入,會轉換成一個新的狀態出來的東西。

根據以太坊的狀態機,我們從創世紀狀態(genesis state)開始。這差不多類似於一片空白的石板,在網絡中還沒有任何交易的產生狀態。當交易被執行後,這個創世紀狀態就會轉變成最終狀態。在任何時刻,這個最終狀態都代表着以太坊當前的狀態。

以太坊的狀態有百萬個交易。這些交易都被“組團”到一個區塊中。一個區塊包含了一系列的交易,每個區塊都與它的前一個區塊鏈接起來。

爲了讓一個狀態轉換成下一個狀態,交易必須是有效的。爲了讓一個交易被認爲是有效的,它必須要經過一個驗證過程,此過程也就是挖礦。挖礦就是一組節點(即電腦)用它們的計算資源來創建一個包含有效交易的區塊出來。

任何在網絡上宣稱自己是礦工的節點都可以嘗試創建和驗證區塊。世界各地的很多礦工都在同一時間創建和驗證區塊。每個礦工在提交一個區塊到區塊鏈上的時候都會提供一個數學機制的“證明”,這個證明就像一個保證:如果這個證明存在,那麼這個區塊一定是有效的。

爲了讓一個區塊添加到主鏈上,一個礦工必須要比其他礦工更快的提供出這個“證明”。通過礦工提供的一個數學機制的“證明”來證實每個區塊的過程稱之爲工作量證明(proof of work)。

證實了一個新區塊的礦工都會被獎勵一定價值的獎賞。獎賞是什麼?以太坊使用一種內在數字代幣—以太幣(Ether)作爲獎賞。每次礦工證明了一個新區塊,那麼就會產生一個新的以太幣並被獎勵給礦工。

你也許會在想:什麼能確保每個人都只在區塊的同一條鏈上呢?我們怎麼能確定不會存在一部分礦工創建一個他們自己的鏈呢?

前面,我們定義了區塊鏈就是一個具有共享狀態的交易單機。使用這個定義,我們可以知道正確的當前狀態是一個全球真相,所有人都必須要接受它。擁有多個狀態(或多個鏈)會摧毀這個系統,因爲它在哪個是正確狀態的問題上不可能得到統一結果。如果鏈分叉了,你有可能在一條鏈上擁有10個幣,一條鏈上擁有20個幣,另一條鏈上擁有40個幣。在這種場景下,是沒有辦法確定哪個鏈纔是最”有效的“。

不論什麼時候只要多個路徑產生了,一個”分叉“就會出現。我們通常都想避免分叉,因爲它們會破壞系統,強制人們去選擇哪條鏈是他們相信的鏈。

爲了確定哪個路徑纔是最有效的以及防止多條鏈的產生,以太坊使用了一個叫做“GHOST協議(GHOST protocol.)”的數學機制。

GHOST = Greedy Heaviest Observed Subtree

簡單來說,GHOST協議就是讓我們必須選擇一個在其上完成計算最多的路徑。一個方法確定路徑就是使用最近一個區塊(葉子區塊)的區塊號,區塊號代表着當前路徑上總的區塊數(不包含創世紀區塊)。區塊號越大,路徑就會越長,就說明越多的挖礦算力被消耗在此路徑上以達到葉子區塊。使用這種推理就可以允許我們贊同當前狀態的權威版本。

現在你大概對區塊鏈是什麼有個理性的認識,讓我們在再深入了地解一下以太坊系統主要組成部分:

賬戶(accounts)

狀態(state)

損耗和費用(gas and fees)

交易(transactions)

區塊(blocks)

交易執行(transaction execution)

挖礦(mining)

工作量證明(proof of work)

在開始之前需要注意的是:每當我說某某的hash, 我指的都是KECCAK-256 hash, 以太坊就是使用這個hash算法。

賬戶

以太坊的全局“共享狀態”是有很多小對象(賬戶)來組成的,這些賬戶可以通過消息傳遞架構來與對方進行交互。每個賬戶都有一個與之關聯的狀態(state)和一個20字節的地址(address)。在以太坊中一個地址是160位的標識符,用來識別賬戶的。

這是兩種類型的賬戶:

外部擁有的賬戶,被私鑰控制且沒有任何代碼與之關聯

合約賬戶,被它們的合約代碼控制且有代碼與之關聯

外部擁有賬戶與合約賬戶的比較

理解外部擁有賬戶和合約賬戶的基本區別是很重要的。一個外部擁有賬戶可以通過創建和用自己的私鑰來對交易進行簽名,來發送消息給另一個外部擁有賬戶或合約賬戶。在兩個外部擁有賬戶之間傳送的消息只是一個簡單的價值轉移。但是從外部擁有賬戶到合約賬戶的消息會激活合約賬戶的代碼,允許它執行各種動作。(比如轉移代幣,寫入內部存儲,挖出一個新代幣,執行一些運算,創建一個新的合約等等)。

不像外部擁有賬戶,合約賬戶不可以自己發起一個交易。相反,合約賬戶只有在接收到一個交易之後(從一個外部擁有賬戶或另一個合約賬戶接),爲了響應此交易而觸發一個交易。我們將會在“交易和消息”部分來了解關於合約與合約之間的通信。

因此,在以太坊上任何的動作,總是被外部控制賬戶觸發的交易所發動的。

賬戶狀態

賬戶狀態有四個組成部分,不論賬戶類型是什麼,都存在這四個組成部分:

nonce:如果賬戶是一個外部擁有賬戶,nonce代表從此賬戶地址發送的交易序號。如果賬戶是一個合約賬戶,nonce代表此賬戶創建的合約序號

balance: 此地址擁有Wei的數量。1Ether=10^18Wei

storageRoot: Merkle Patricia樹的根節點Hash值(我們後面在解釋Merkle樹)。Merkle樹會將此賬戶存儲內容的Hash值進行編碼,默認是空值

codeHash:此賬戶EVM(以太坊虛擬機,後面細說)代碼的hash值。對於合約賬戶,就是被Hash的代碼並作爲codeHash保存。對於外部擁有賬戶,codeHash域是一個空字符串的Hash值

世界狀態

好了,我們知道了以太坊的全局狀態就是由賬戶地址和賬戶狀態的一個映射組成。這個映射被保存在一個叫做Merkle Patricia樹的數據結構中

Merkle Tree(也被叫做Merkle trie)是一種由一系列節點組成的二叉樹,這些節點包括:

在樹底的包含了源數據的大量葉子節點

一系列的中間的節點,這些節點是兩個子節點的Hash值

一個根節點,同樣是兩個子節點的Hash值,代表着整棵樹

樹底的數據是通過分開我們想要保存到chunks的數據產生的,然後將chunks分成buckets,再然後再獲取每個bucket的hash值並一直重複直到最後只剩下一個Hash:根Hash。

這棵樹要求存在裏面的值(value)都有一個對應的key。從樹的根節點開始,key會告訴你順着哪個子節點可以獲得對應的值,這個值存在葉子節點。在以太坊中,key/value是地址和與地址相關聯的賬戶之間狀態的映射,包括每個賬戶的balance, nonce, codeHash和storageRoot(storageRoot自己就是一顆樹)。

同樣的樹結構也用來存儲交易和收據。更具體的說,每個塊都有一個頭(header),保存了三個不同Merkle trie結構的根節點的Hash,包括:

狀態樹

交易樹

收據樹

在Merkle tries中存儲所有信息的高效性在以太坊中的“輕客戶端”和“輕節點”相當的有用。記住區塊鏈就是一羣節點來維持的。廣泛的說,有兩種節點類型:全節點和輕節點。

全節點通過下載整條鏈來進行同步,從創世紀塊到當前塊,執行其中包含的所有交易。通常,礦工會存儲全節點,因爲他們在挖礦過程中需要全節點。也有可能下載一個全節點而不用執行所有的交易。無論如何,一個全節點包含了整個鏈。

不過除非一個節點需要執行所有的交易或輕鬆訪問歷史數據,不然沒必要保存整條鏈。這就是輕節點概念的來源。比起下載和存儲整個鏈以及執行其中所有的交易,輕節點僅僅下載鏈的頭,從創世紀塊到當前塊的頭,不執行任何的交易或檢索任何相關聯的狀態。由於輕節點可以訪問塊的頭,而頭中包含了3個tries的Hash,所有輕節點依然可以很容易生成和接收關於交易、事件、餘額等可驗證的答案。

這個可以行的通是因爲在Merkle樹中hash值是向上傳播的—如果一個惡意用戶試圖用一個假交易來交換Merkle樹底的交易,這個會改變它上面節點的hash值,而它上面節點的值的改變也會導致上上一個節點Hash值的改變,以此類推,一直到樹的根節點。

任何節點想要驗證一些數據都可以通過Merkle證明來進行驗證,Merkle 證明的組成:

一塊需要驗證的數據

樹的根節點Hash

一個“分支”(從 chunk到根這個路徑上所有的hash值)

任何可以讀取證明的人都可以驗證分支的hash是連貫的,因此給出的塊在樹中實際的位置就是在此處。

總之,使用Merkle Patricia樹的好處就是該結構的根節點加密取決於存儲在樹中的數據,而且根據點的hash還可以作爲該數據的安全標識。由於塊的頭包含了狀態、交易、收據樹的根hash,所有任何節點都可以驗證以太坊的一小部分狀態而不用保存整個狀態,這整個狀態的的大小可能是非常大的。

Gas和費用

在以太坊中一個比較重要的概念就是費用(fees),由以太坊網絡上的交易而產生的每一次計算,都會產生費用—沒有免費的午餐。這個費用是以稱之爲”gas”的來支付。

gas就是用來衡量在一個具體計算中要求的費用單位。gas price就是你願意在每個gas上花費Ether的數量,以“gwei”進行衡量。“Wei”是Ether的最小單位,1Ether表示10^18Wei. 1gwei是1,000,000,000 Wei。

對每個交易,發送者設置gas limit和gas price。gas limit和gas price就代表着發送者願意爲執行交易支付的Wei的最大值。

例如,假設發送者設置gas limit爲50,000,gas price爲20gwei。這就表示發送者願意最多支付50,000*20gwei = 1,000,000,000,000,000 Wei = 0.001 Ether來執行此交易。

記住gas limit代表用戶願意花費在gas上的錢的最大值。如果在他們的賬戶餘額中有足夠的Ether來支付這個最大值費用,那麼就沒問題。在交易結束時任何未使用的gas都會被返回給發送者,以原始費率兌換。

在發送者沒有提供足夠的gas來執行交易,那麼交易執行就會出現“gas不足”然後被認爲是無效的。在這種情況下,交易處理就會被終止以及所有已改變的狀態將會被恢復,最後我們就又回到了交易之前的狀態—完完全全的之前狀態就像這筆交易從來沒有發生。因爲機器在耗盡gas之前還是爲計算做出了努力,

所以理論上,將不會有任何的gas被返回給發送者。

這些gas的錢到底去了哪裏?發送者在gas上花費的所有錢都發送給了“受益人”地址,通常情況下就是礦工的地址。因爲礦工爲了計算和驗證交易做出了努力,所以礦工接收gas的費用作爲獎勵。

通常,發送者願意支付更高的gas price,礦工從這筆交易總就能獲得更多的價值。因此,礦工也就更加願意選擇這筆交易。這樣的話,礦工可以自由的選擇一筆交易自己願意驗證或忽略。爲了引導發送者應該設置gas price爲多少,礦工可以選擇建議一個最小的gas值他們願意執行一個交易。

存儲也有費用

gas不僅僅是用來支付計算這一步的費用,而且也用來支付存儲的費用。存儲的總費用與所使用的32位字節的最小倍數成比例。

存儲費用有一些比較細微的方面。比如,由於增加了的存儲增加了所有節點上的以太坊狀態數據庫的大小,所以激勵保持數據存儲量小。爲了這個原因,如果一個交易的執行有一步是清除一個存儲實體,那麼爲執行這個操作的費用就會被放棄,並且由於釋放存儲空間的退款就會被返回給發送者。

費用的作用是什麼?

以太坊可以運作的一個重要方面就是每個網絡執行的操作同時也被全節點所影響。然而,計算的操作在以太坊虛擬機上是非常昂貴的。因此,以太坊智能合約最好是用來執行最簡單的任務,比如運行一個簡單的業務邏輯或者驗證簽名和其他密碼對象,而不是用於複雜的操作,比如文件存儲,電子郵件,或機器學習,這些會給網絡造成壓力。施加費用防止用戶使網絡超負荷。

以太坊是一個圖靈完備語言(短而言之,圖靈機器就是一個可以模擬任何電腦算法的機器。對於圖靈機器不太熟悉的人可以看看這個 和這個 )。這就允許有循環,並使以太坊受到停機問題 的影響,這個問題讓你無法確定程序是否無限制的運行。如果沒有費用的話,惡意的執行者通過執行一個包含無限循環的交易就可以很容易的讓網絡癱瘓而不會產生任何反響。因此,費用保護網絡不受蓄意攻擊。

你也許會想,“爲什麼我們還需要爲存儲付費?”其實就像計算一樣,以太坊網絡上的存儲是整個網絡都必須要負擔的成本。

交易和消息

之前說過以太坊是一個基於交易的狀態機。換句話說,在兩個不同賬戶之間發生的交易才讓以太坊全球狀態從一個狀態轉換成另一個狀態。

最基本的概念,一個交易就是被外部擁有賬戶生成的加密簽名的一段指令,序列化,然後提交給區塊鏈。

有兩種類型的交易:消息通信和合約創建(也就是交易產生一個新的以太坊合約)。

不管什麼類型的交易,都包含:

nonce:發送者發送交易數的計數

gasPrice:發送者願意支付執行交易所需的每個gas的Wei數量

gasLimit:發送者願意爲執行交易支付gas數量的最大值。這個數量被設置之後在任何計算完成之前就會被提前扣掉

to:接收者的地址。在合約創建交易中,合約賬戶的地址還沒有存在,所以值先空着

value:從發送者轉移到接收者的Wei數量。在合約創建交易中,value作爲新建合約賬戶的開始餘額

v,r,s:用於產生標識交易發生着的簽名

init(只有在合約創建交易中存在):用來初始化新合約賬戶的EVM代碼片段。init值會執行一次,然後就會被丟棄。當init第一次執行的時候,它返回一個賬戶代碼體,也就是永久與合約賬戶關聯的一段代碼。

data(可選域,只有在消息通信中存在):消息通話中的輸入數據(也就是參數)。例如,如果智能合約就是一個域名註冊服務,那麼調用合約可能就會期待輸入域例如域名和IP地址

在“賬戶”這個章節中我們學到交易—消息通信和合約創建交易兩者都總是被外部擁有賬戶觸發並提交到區塊鏈的。換種思維思考就是,交易是外部世界和以太坊內部狀態的橋樑。

但是這也並不代表一個合約與另一個合約無法通信。在以太坊狀態全局範圍內的合約可以與在相同範圍內的合約進行通信。他們是通過“消息”或者“內部交易”進行通信的。我們可以認爲消息或內部交易類似於交易,不過與交易有着最大的不同點—它們不是由外部擁有賬戶產生的。相反,他們是被合約產生的。它們是虛擬對象,與交易不同,沒有被序列化而且只存在與以太坊執行環境。

當一個合約發送一個內部交易給另一個合約,存在於接收者合約賬戶相關聯的代碼就會被執行。

一個重要需要注意的事情是內部交易或者消息不包含gasLimit。因爲gas limit是由原始交易的外部創建者決定的(也就是外部擁有賬戶)。外部擁有賬戶設置的gas limit必須要高到足夠將交易完成,包括由於此交易而長生的任何”子執行”,例如合約到合約的消息。如果,在一個交易或者信息鏈中,其中一個消息執行使gas已不足,那麼這個消息的執行會被還原,包括任何被此執行觸發的子消息。不過,父執行沒必要被還原。

區塊

所有的交易都被組成一個”塊”。一個區塊鏈包含了一系列這樣的鏈在一起區塊。

在以太坊中,一個區塊包含:

區塊頭

關於包含在此區塊中交易集的信息

與當前塊的ommers相關的一系列其他區塊頭

Ommers解釋

“ommer”到底是什麼? ommer就是一個區塊的父區塊與當前區塊父區塊的父區塊是相同的。讓我們快速瞭解一下ommers是用來幹嘛的,並且爲什麼一個區塊需要爲ommers包含區塊頭。

由於以太坊的構造,它的區塊生產時間(大概15秒左右)比其他的區塊鏈例如Bitcoin(大概10分鐘左右)要快很多。這使得交易的處理更快。但是,更短的區塊生產時間的一個缺點就是:更多的競爭區塊會被礦工發現。這些競爭區塊同樣也被稱爲“孤區塊”(也就是被挖出來但是不會被添加到主鏈上的區塊)。

Ommers的目的就是爲了幫助獎勵礦工納入這些孤區塊。礦工包含的ommers必須是有效的,也就是ommers必須在父區塊的第6個子區塊之內或更小範圍內。在第6個子區塊之後,陳舊的孤區塊將不會再被引用(因爲包含老舊的交易會使事情變得複雜一點)。

Ommer區塊會收到比全區塊少一點的獎勵。不管怎樣,依然存在激勵來讓礦工們納入孤區塊並能從中獲得一些報酬。

讓我們再回到區塊的問題上。我們前面提到每個區塊都有一個“區塊頭”,但這究竟是什麼?

區塊頭是一個區塊的一部分,包含了:

parentHash:父區塊頭的Hash值(這也是使得區塊變成區塊鏈的原因)

ommerHash:當前區塊ommers列表的Hash值

beneficiary:接收挖此區塊費用的賬戶地址

stateRoot:狀態樹根節點的Hash值(回憶一下我們之前所說的保存在頭中的狀態樹以及它使得輕客戶端認證任何關於狀態的事情都變得非常簡單)

transactionsRoot:包含此區塊所列的所有交易的樹的根節點Hash值

receiptsRoot:包含此區塊所列的所有交易收據的樹的根節點Hash值

logsBloom:由日誌信息組成的一個Bloom過濾器 (數據結構)

difficulty: 此區塊的難度級別

number:當前區塊的計數(創世紀塊的區塊序號爲0,對於每個後續區塊,區塊序號都增加1)

gasLimit:每個區塊的當前gas limit

gasUsed: 此區塊中交易所用的總gas量

timestamp:此區塊成立時的unix的時間戳

extraData:與此區塊相關的附加數據

mixHash:一個Hash值,當與nonce組合時,證明此區塊已經執行了足夠的計算

nonce:一個Hash值,當與mixHash組合時,證明此區塊已經執行了足夠的計算

注意每個區塊是如何包含三個樹結構的,三個樹結構分別對應:

狀態(stateRoot)

交易(transactionsRoot)

收據(receiptsRoot)

這三個樹結構就是我們前面討論的Merkle Patricia樹。

另外,上面描述的有幾個術語值得說明一下,下面來看一下。

日誌

以太坊允許日誌可以跟蹤各種交易和信息。一個合約可以通過定義“事件”來顯示的生成日誌。

一個日誌的實體包含:

記錄器的賬戶地址

代表本次交易執行的各種事件的一系列主題以及與這些事件相關的任何數據

日誌被保存在bloom過濾器 中,過濾器高效的保存了無盡的日誌數據。

交易收據

自於被包含在交易收據中的日誌信息存儲在頭中。就像你在商店買東西時收到的收據一樣,以太坊爲每筆交易都產生一個收據。像你期望的那樣,每個收據包含關於交易的特定信息。這些收據包含着:

區塊序號

區塊Hash

交易Hash

當前交易使用了的gas

在當前交易執行完之後當前塊使用的累計gas

執行當前交易時創建的日誌

等等

區塊難度

區塊的難度是被用來在驗證區塊時加強一致性。創世紀區塊的難度是131,072,有一個特殊的公式用來計算之後的每個塊的難度。如果某個區塊比前一個區塊驗證的更快,以太坊協議就會增加區塊的難度。

區塊的難度影響nonce,它是在挖礦時必須要使用proof-of-work算法來計算的一個hash值。

區塊難度和nonce之間的關係用數學形式表達就是:

Hd代表的是難度。

找到符合難度閾值的nonce唯一方法就是使用proof-of-work算法來列舉所有的可能性。找到解決方案預期時間與難度成正比—難度越高,找到nonce就越困難,因此驗證一個區塊也就越難,這又相應地增加了驗證新塊所需的時間。所以,通過調整區塊難度,協議可以調整驗證區塊所需的時間。

另一方面,如果驗證時間變的越來越慢,協議就會降低難度。這樣的話,驗證時間自我調節以保持恆定的速率—平均每15s一個塊。

交易執行

我們已經到了以太坊協議最複雜的部分:交易的執行。假設你發送了一筆交易給以太坊網絡處理,將以太坊狀態轉換成包含你的交易這個過程到底發生了什麼?

首先,爲了可以被執行所有的交易必須都要符合最基礎的一系列要求,包括:

交易必須是正確格式化的RLP。”RLP”代表Recursive Length Prefix,它是一種數據格式,用來編碼二進制數據嵌套數組。以太坊就是使用RLP格式序列化對象。

有效的交易簽名。

有效的交易序號。回憶一下賬戶中的nonce就是從此賬戶發送出去交易的計數。如果有效,那麼交易序號一定等於發送賬戶中的nonce。

交易的gas limit 一定要等於或者大於交易使用的intrinsic gas,intrinsic gas包括:

——-1.執行交易預訂費用爲21,000gas

——-2.隨交易發送的數據的gas費用(每字節數據或代碼爲0的費用爲4gas,每個非零字節的數據或代碼費用爲68gas)

——-3.如果交易是合約創建交易,還需要額外的32,000gas

發送賬戶餘額必須有足夠的Ether來支付”前期”gas費用。前期gas費用的計算比較簡單:首先,交易的gas limit乘以交易的gas價格得到最大的gas費用。然後,這個最大gas費用被加到從發送方傳送給接收方的總值。

如何交易符合上面所說的所有要求,那麼我們進行下面步驟。

第一步,我們從發送者的餘額中扣除執行的前期費用,併爲當前交易將發送者賬戶中的nonce增加1。此時,我們可以計算剩餘的gas,將交易的總gas減去使用的intrinsic gas。

第二步,開始執行交易。在交易執行的整個過程中,以太坊保持跟蹤“子狀態”。子狀態是記錄在交易中生成的信息的一種方式,當交易完成時會立即需要這些信息。具體來說,它包含:

自毀集:在交易完成之後會被丟棄的賬戶集(如果存在的話)

日誌系列:虛擬機的代碼執行的歸檔和可檢索的檢查點

退款餘額:交易完成之後需要退還給發送賬戶的總額。回憶一下我們之前提到的以太坊中的存儲需要付費,發送者要是清理了內存就會有退款。以太坊使用退款計數進行跟蹤退款餘額。退款計數從0開始並且每當合約刪除了一些存儲中的東西都會進行增加。

第三步,交易所需的各種計算開始被處理。

當交易所需的步驟全部處理完成,並假設沒有無效狀態,通過確定退還給發送者的未使用的gas量,最終的狀態也被確定。除了未使用的gas,發送者還會得到上面所說的“退款餘額”中退還的一些津貼。

一旦發送者得到退款之後:

gas的Ether就會礦工

交易使用的gas會被添加到區塊的gas計數中(計數一直記錄當前區塊中所有交易使用的gas總量,這對於驗證區塊時是非常有用的)

所有在自毀集中的賬戶(如果存在的話)都會被刪除

最後,我們就有了一個新的狀態以及交易創建的一系列日誌。

現在我們已經介紹了交易執行的基本知識,讓我們再看看合約創建交易和消息通信的一些區別。

合約創建(Contract creation)

回憶一下在以太坊中,有兩種賬戶類型:合約賬戶和外部擁有賬戶。當我們說一個交易是“合約創建”,是指交易的目的是創建一個新的合約賬戶。

爲了創建一個新的合約賬戶,我們使用一個特殊的公式來聲明新賬戶的地址。然後我們使用下面的方法來初始化一個賬戶:

設置nonce爲0

如果發送者通過交易發送了一定量的Ether作爲value,那麼設置賬戶的餘額爲value

將存儲設置爲0

設置合約的codeHash爲一個空字符串的Hash值

一旦我們完成了賬戶的初始化,使用交易發送過來的init code(查看”交易和信息”章節來複習一下init code),實際上就創造了一個賬戶。init code的執行過程是各種各樣的。取決於合約的構造器,可能是更新賬戶的存儲,也可能是創建另一個合約賬戶,或者發起另一個消息通信等等。

當初始化合約的代碼被執行之後,會使用gas。交易不允許使用的gas超過剩餘gas。如果它使用的gas超過剩餘gas,那麼就會發生gas不足異(OOG)常並退出。如果一個交易由於gas不足異常而退出,那麼狀態會立刻恢復到交易前的一個點。發送者也不會獲得在gas用完之前所花費的gas。

不過,如果發送者隨着交易發送了Ether,即使合約創建失敗Ether也會被退回來。

如果初始化代碼成功的執行完成,最後的合約創建的花費會被支付。這些是存儲成本,與創建的合約代碼大小成正比(再一次,沒有免費的午餐)。如果沒有足夠的剩餘gas來支付最後的花費,那麼交易就會再次宣佈gas不足異常並中斷退出。

如果所有的都正常進行沒有任何異常出現,那麼任何剩餘的未使用gas都會被退回給原始的交易發送者,現在改變的狀態才被允許永久保存。

消息通信(Message calls)

消息通信的執行與合約創建比較類似,只不過有一點點區別。

由於沒有新賬戶被創建,所以消息通信的執行不包含任何的init code。不過,它可以包含輸入數據,如果交易發送者提供了此數據的話。一旦執行,消息通信同樣會有一個額外的組件來包含輸出數據,如果後續執行需要此數據的話就組件就會被使用。

就像合約創建一樣,如果消息通信執行退出是因爲gas不足或交易無效(例如棧溢出,無效跳轉目的地或無效指令),那麼已使用的gas是不會被退回給原始觸發者的。相反,所有剩餘的未使用gas也會被消耗掉,並且狀態會被立刻重置爲餘額轉移之前的那個點。

沒有任何方法停止或恢復交易的執行而不讓系統消耗你提供的所有gas,直到最新的以太坊更新。例如,假設你編寫了一個合約,當調用者沒有授權來執行這些交易的時候拋出一個錯誤。在以太坊的前一個版本中,剩餘的gas也會被消耗掉,並且沒有任何gas退回給發送者。但是拜占庭更新包括了一個新的“恢復”代碼,允許合約停止執行並且恢復狀態改變而不消耗剩餘的gas,此代碼還擁有返回交易失敗原因的能力。如果一個交易是由於恢復而退出,那麼未使用的gas就會被返回給發送者。

執行模式

到目前爲止,我們瞭解了從開始到結束執行的交易必須經歷的一系列的步驟。現在,我們來看看交易究竟是如何在虛擬機(VM)中執行的。

協議實際操作交易處理的部分是以太坊自己的虛擬機,稱之爲以太坊虛擬機(EVM)。

像之前定義的那樣,EVM是圖靈完備虛擬機器。EVM存在而典型圖靈完備機器不存在的唯一限制就是EVM本質上是被gas束縛。因此,可以完成的計算總量本質上是被提供的gas總量限制的。

此外,EVM具有基於堆棧的架構。堆棧機器 就是使用後進先出來保存臨時值的計算機。

EVM中每個堆棧項的大小爲256位,堆棧有一個最大的大小,爲1024位。

EVM有內存,項目按照可尋址字節數組來存儲。內存是易失性的,也就是數據是不持久的。

EVM也有一個存儲器。不像內存,存儲器是非易失性的,並作爲系統狀態的一部分進行維護。EVM分開保存程序代碼,在虛擬ROM 中只能通過特殊指令來訪問。這樣的話,EVM就與典型的馮·諾依曼架構 不同,此架構將程序的代碼存儲在內存或存儲器中。

EVM同樣有屬於它自己的語言:“EVM字節碼”,當一個程序員比如你或我寫一個在以太坊上運行的智能合約時,我們通常都是用高級語言例如Solidity來編寫代碼。然後我們可以將它編譯成EVM可以理解的EVM字節碼。

好了,現在來說執行。

在執行特定的計算之前,處理器會確定下面所說的信息是有效和是否可獲取:

系統狀態

用於計算的剩餘gas

擁有執行代碼的賬戶地址

原始觸發此次執行的交易發送者的地址

觸發代碼執行的賬戶地址(可能與原始發送者不同)

觸發此次執行的交易gas價格

此次執行的輸入數據

Value(單位爲Wei)作爲當前執行的一部分傳遞給該賬戶

待執行的機器碼

當前區塊的區塊頭

當前消息通信或合約創建堆棧的深度

執行剛開始時,內存和堆棧都是空的,程序計數器爲0。

1

PC: 0 STACK: [] MEM: [], STORAGE: {}

然後EVM開始遞歸的執行交易,爲每個循環計算系統狀態和機器狀態。系統狀態也就是以太坊的全局狀態(global state)。機器狀態包含:

可獲取的gas

程序計數器

內存的內容

內存中字的活躍數

堆棧的內容

堆棧中的項從系列的最左邊被刪除或者添加。

每個循環,剩餘的gas都會被減少相應的量,程序計數器也會增加。

在每個循環的結束,都有三種可能性:

機器到達異常狀態(例如 gas不足,無效指令,堆棧項不足,堆棧項會溢出1024,無效的JUMP/JUMPI目的地等等)因此停止,並丟棄任何的更改

進入後續處理下一個循環

機器到達了受控停止(到達執行過程的終點)

假設執行沒有遇到異常狀態,達到一個“可控的”或正常的停止,機器就會產生一個合成狀態,執行之後的剩餘gas、產生的子狀態、以及組合輸出。

呼。我們終於過了一遍以太坊最難的部分了。如果你不能完全理解這個部分,也沒關係。除非你在理解非常深層次的東西,否則你真的沒有必要去理解執行的每個細節。

一個塊是如何完成的?

最後,讓我們看看一個包含許多交易的塊是如何完成的。

當我們說“完成”,取決於此塊是新的還是已存在的,可以指兩個不同的事情。如果是個新塊,就是指挖這個塊所需的處理。如果是已存在的塊,就是指驗證此塊的處理。不論哪種情況,一個塊的“完成”都有4個要求:

1)驗證(或者,如果是挖礦的話,就是確定)ommers

在區塊頭中的每個ommer都必須是有效的頭並且必須在當前塊的6代之內

2)驗證(或者,如果是挖礦的話,就是確定)交易

區塊中的gasUsed數量必須與區塊中所列交易使用的累積gas量相等。(回憶一下,當執行一個交易的時候,我們會跟蹤區塊的gas計數器,也就跟蹤了區塊中所有交易使用的gas總數量)

3)申請獎勵(只有挖礦時)

受益人的地址會因爲挖礦而獲得5Ether(在以太坊EIP-649 提案中,5ETH很快將會被減少爲3ETH)。另外,對於每個ommer,當前塊的受益人會獲得額外的1/32當前塊獎勵金的獎勵。最近,每個ommer區塊的受益人能夠得到一定量的獎勵(有個特殊公式可以進行計算)。

4)校驗(或者,如果是挖礦的話,就是計算一個有效的)狀態和nonce

確保所有的交易和改變的結果狀態都被應用了,然後在區塊獎勵被應用於最終交易結果狀態之後定義一個新塊爲狀態。通過檢查最終狀態與存儲在頭中的狀態樹來進行驗證。

工作量證明挖礦

在“區塊”這個章節簡短的說明了一下區塊難度這個概念。給予區塊難度意義的算法叫做工作量證明(PoW)。

以太坊的工作量證明算法稱之爲“Ethash” (之前叫做Dagger-Hashimoto)。

算法正式定義爲:

m代表的是mixHash,n代表的是nonce,Hn代表的是新區塊的頭(不包含需要計算的nonce和mixHash),Hn是區塊頭的nonce,d是DAG ,就是一個大數據集。

在”區塊”章節,我們討論了存在於區塊頭中的多項。其中兩項叫做mixHash和nonce。也許你會回憶起:

PoW函數就是用來估算這兩項的。

mixHash和nonce到底是如何使用PoW函數來計算出來的有點複雜,如果深入瞭解的話,我們可以另寫一篇文章來講解了。但是在一個高層面上,它大致就是這樣計算的:

會爲每個區塊計算一個”種子”。每個“時期”的種子都不一樣,每個時期是30,000個區塊長度。對於第一時期,種子就是32位0的hash值。對於後續的每個時期,種子就是前一個種子hash值的hash值。使用這個種子,節點可以計算一個僞隨機“緩存”。

這個緩存是非常有用的,因爲它可以使“輕節點”的概念變成現實,輕節點概念在這篇文章的前面討論過。輕節點的目的就是讓某個節點有能力高效的校驗交易而用不着存儲整個區塊鏈的數據集。一個輕節點可以僅基於緩存來校驗一個交易的有效性,因爲緩存可以重新生成需要校驗的特定塊。

使用這個緩存,節點可以生成DAG“數據集”,數據集中的每項取決於緩存中少量僞隨機選擇項。爲了成爲礦工,你需要要生成全數據集,所有全客戶端和礦工都保存這個數據集,並且這個數據集隨着時間線性增長。

然後礦工可以隨機抽取數據集中的部分並將它們放入一個數學函數中Hash出一個”mixHash”。礦工會重複生成mixHash直到輸出的值小於想要的目標值nonce。當輸出的值符合這個條件的時候,nonce就被認爲是有效的,然後區塊就被添加到鏈中。

挖礦作爲安全機制

總的來說,PoW的目的就是以加密安全的方式證明生成的一些輸出(也就是nonce)是經過了一定量的計算的。因爲除了列舉所有的可能性,沒有更好的其他方法來找到一個低於要求閾值的nonce。重複應用Hash函數的輸出均勻分佈,所以我們可以確保,在平均值上,找到滿足要求的nonce所需時間取決於難度閾值。難度係數越大,所需時間越長。這樣的話,PoW算法就給予難度這個概念的意義了:用來加強區塊鏈的安全。

我們所說的區塊鏈的安全又是什麼意思?這非常簡單:我們想要創造一個每個人都信任的區塊鏈。像我們之前在這篇文章中討論的那樣,如果存在超過1條以上的鏈,用戶的信任就會消失,因爲他們沒有能力合理的確認哪條鏈纔是“有效的”。爲了讓一羣用戶接受存儲在區塊鏈中的潛在狀態,我們需要有一羣人信任的一個權威區塊鏈。

這完完全全就是Pow算法所做的事情:它確保特定的區塊鏈直到未來都一直保持着權威性,讓攻擊者創造一個新區塊來重寫某個歷史部分(例如清除一個交易或者創建一個假的交易)或者保持一個分叉變得非常困難。爲了首先讓他們的區塊被驗證,攻擊者需要總是比網絡上的其他人要更快的解決掉nonce問題,這樣網絡就會相信他們的鏈是最重的鏈(基於我們之前提到的GHOST協議原則)。除非攻擊者擁有超過一半的網絡挖礦能力(這種場景也被稱爲大多數51%攻擊 ),要不然這基本上是不可能的。

挖礦作爲財富分配機制

除了提供一個安全的區塊鏈,PoW同樣也是分配財富給那些爲提供這個安全而花費自己計算力的人的一種方法。回憶一下,一個礦工挖出一個區塊的時候會獲得獎勵,包括:

爲“獲勝”區塊提供的5 ether靜態區塊獎勵(馬上就會變成3 ether )

區塊中的交易在區塊內所消耗的gas

納入ommers作爲區塊的一部分的額外獎勵

爲了保證PoW共識算法機制對安全和財富分配的使用是長期可持續的,以太坊努力灌輸這兩個特性:

儘可能的讓更多的人可訪問。換句話說,人們不需要特殊的或者與衆不同的硬件來運行這個算法。這樣做的目的是爲了讓財富分配模式變的儘可能的開放,以便任何人都可以提供一些算力而獲得Ether作爲回報。

降低任何單個節點(或小組)能夠創造與其不成比例的利潤可能性。任何可以創造不成比例的利潤的節點擁有比較大的影響力來決定權威區塊鏈。這是件麻煩的事情,因爲這降低了網絡的安全性。

在區塊鏈網絡中,一個與上面兩個特性有關的一個問題是PoW算法是一個SHA256哈希函數。這種函數的缺點就是它使用特殊的硬件(也被稱之爲ASCIs)可以更加快速高效的解決nonce問題。

爲了減輕這個問題,以太坊選擇讓PoW算法(Ethhash) 提高內存級別難度。意思是此算法被設計爲計算出要求的nonce需要大量的內存和帶寬。大量內存的需求讓電腦平行的使用內存同時計算多個nonce變得極其困難,高帶寬的需求讓即使是超級電腦同時計算多個nonce也變得十分艱難。這種方式降低了中心化的風險,併爲正在進行驗證的幾點提供了更加公平的競爭環境。

有一件值得注意的事情是以太坊正在從PoW共識機制漸漸轉換爲一個叫做“權益證明(PoS)”的共識算法。這就是一個比較野心的話題了,我們希望可以在未來的文章中探索這個話題。

總結

呼! 你終於堅持到最後了。我希望如此?

這篇文章中有很多的地方需要消化。如果需要你閱讀好幾遍才能理解怎麼回事,這完全正常。我個人重複閱讀了好幾次以太坊黃皮書,白皮書,以及代碼的不同部分才漸漸明白是怎麼回事。

無論如何,我希望你覺得這篇文章對你有幫助。如果你發現了任何的錯誤或失誤,我很樂意你給我寫個私人消息或者直接在評論區評論(我保證我會查看所有評論)。

記住,我是個人類(對,這是真的),我會犯錯誤。爲了社區的利益,我花時間免費寫了這篇文章。所以請你在反饋時不要帶着沒必要的攻擊性,儘量是建設性的反饋。


發佈了24 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章