關於以太坊虛擬機 EVM


一、什么是EVM

EVM是以太坊協議的一部分,它用來處理智能合約的部署和執行。除了在EOA(由用戶私鑰控制的所謂“外部賬戶”)之間的簡單轉賬交易以外,其他所有涉及狀態更新的操作都是通過EVM來計算的。從高層抽象的角度,運行在以太坊區塊鏈上的EVM可以想象成一個包含了數百萬可執行對象的全球化去中心化計算機,這些可執行對象都擁有它們各自的永久數據存儲。

EVM是一個“准”圖靈完備的狀態機,因為在其中進行的任意智能合約的執行都必須限定在一個由可用的gas總量所限制的、有限的計算步驟數量之內。

EVM是一個基於棧的架構,在一個棧中保存了所有的內存數值。EVM的數據處理單位被定義為256位的“字”,並且它還有以下數據組件:

一個不可變的程序代碼存儲區ROM,加載了要執行的智能合約字節碼。

一個內容可變的內存,它被嚴格初始化為全0的數值。

一個永久存儲,它是作為以太坊狀態的一部分而存在的,也會被初始化為0

 

二、與現有技術的比較

所謂“虛擬機”的概念通常用於對真實計算機的虛擬化,一般是通過一個像virtualbox或者QEMU這樣的“管理程序”,或者像Linux上的KVM這樣的完整操作系統實例來實現的。這些方案中都必須分別對實際的硬件、系統調用和其他內核功能提供一個軟件抽象。

而EVM則運行在一個更局限的領域,它僅僅是一個計算引擎,僅僅提供對計算和存儲的抽象。

就像Java虛擬機(JVM)那樣。從高級視角來看,JVM的設計提供了一個無須知曉底層宿主OS或硬件的運行環境,從而提供了跨不同系統平台的兼容性。像Java、Scala(基於JVM)或者C#(基於.NET)這樣的高級程序設計語言會被編譯為與它們對應的虛擬機字節碼指令集。 同樣的,像LLL、SerPent、Mutan或solidity這樣的高級智能合約開發語言,也會被編譯為由EVM執行的字節碼指令集

EVM沒有可調度性,因為執行順序是由外部所組織好的,也就是由以太坊客戶端通過驗證區塊中的交易來決定哪些智能合約應該運行,以及他們的執行順序應該是什么。從這個角度講,以太坊世界計算機就像JavaScript引擎那樣是“單線程”的。EVM既沒有任何"系統接口”,也沒有“硬件支持”,因為並沒有任何物理機器需要與之交互。以太坊世界計算機是完全虛擬化的。

EVM指令集(字節碼)

EVM指令有很多標准機器碼指令組成,包含:算術和位運算邏輯操作;執行上下文查詢;棧、內存和存儲訪問;處理流程操作;日志、跳轉和其他操作

 

三、以太坊的狀態

EVM的任務是基於以太坊協議、根據智能合約的代碼的執行來計算合法的狀態轉換,用以更新以太坊的狀態。 

這也就是將以太坊作為基於交易的狀態機所描述的層面,同時也是外部用戶(即賬戶持有人和礦工)通過創建、接受交易和對交易進行排序打包來引發狀態轉換所反映的層面。

在最高級,我們有以太坊世界狀態的概念。世界狀態是一個以太坊地址到賬戶數據的映射。

具體來說,每個以太坊賬戶地址所對應的賬戶數據都由以下幾個部分組成:一個以太幣余額,一個nonce, 賬戶的存儲(僅供智能合約使用的永久數據存儲)以及賬戶的程序代碼(如果賬戶是智能合約賬戶),一個EOA永遠不會有代碼,且只有全空的存儲。

當一個交易最終反映為一次智能合約代碼的執行時,一個EVM實例會基於當前正在創建的區塊信息和這個交易的信息被初始化出來。具體的說,就是會將調用的合約賬戶所對應的代碼加載到EVM的ROM中,程序計數器置0,從調用的合約賬戶所對應的存儲中加載存儲數據,將內存清零並將於區塊和其他環境變量相關的信息設置好。這個執行中的關鍵變量就是提供給這次執行的gas,這個gas被設定為原始交易開始時交易發送者支付的gas總量。在執行的過程中,gas的供給會基於操作執行的消耗相應減少。

無論何時,只要gas的供給減小到0,我們就會得到一個“out of gas”的異常,執行會立即終止,相應的交易也即失敗。

把EVM的運行想象為將以太坊世界狀態復制到一個沙盒中,如果執行因為任何原因沒有完成,那么這個沙盒中的狀態就會被丟棄。不過,如果執行成功結束,那么真正的世界狀態就會被更新為這個沙盒的狀態,包含所有對調用過的合約的存儲數據的修改、新創建的合約以及其間所有以太幣余額的轉移。

請注意,因為智能合約可以自己產生交易,所以代碼執行時一個遞歸的過程。一個合約可以調用其他合約,每個調用都會最終都會反映為另一個基於新目標的EVM執行。

每次EVM的實例化都會基於之前的EVM沙盒來構造。每次實例化也會獲得一個指定數量的gas供給,它自己可能因為獲得的gas過少而無法完成它的執行。在這種情況下,沙盒的狀態就會被丟棄,執行會返回到上一層調用。

四、合約部署代碼

 我們創建和部署一個新合約到以太坊平台上所使用的代碼和合約代碼本身有一個很重要但又很微妙的區別。

為了創建一個新合約,我們需要一個特殊的交易,其目標地址to字段需要被指定為0x0,並且它的data字段需要被設置為合約的初始化代碼。

當這樣一個合約創建交易被處理的時候,新合約賬戶的代碼其實並不是交易數據的data字段所附帶的代碼。一個EVM實例會將這個交易所附帶的data字段作為程序代碼加載到其ROM中,並將這個部署代碼的執行輸出結果作為新合約賬戶的關聯代碼,通過這樣的方式,新合約就可以使用部署時的以太坊世界狀態,以編程方式被初始化,並更改合約的存儲數據甚至發送以太幣或者創建更多的新合約。

當我們編譯一個合約,比如在命令行使用solc,就會獲得部署字節碼,也會獲得運行時字節碼。

使用部署字節碼來執行初始化新合約賬戶的所有操作,包含實際交易調用這個新合約時需要執行的字節碼(即運行時字節碼)以及在合約的構造函數中進行初始化處理的代碼。

換句話說,運行時字節碼就是當新合約被調用時,所執行的所有字節碼,僅此而已,其中並不包含需要在部署中使用的用來初始化合約的字節碼。

 

五、圖靈完備和gas

用簡單的術語來解釋,如果一個系統或者編程語言能夠解決你交給它的所有問題,它就是圖靈完備的。然而這種能力伴隨着一個非常重要的警示:有些問題需要無限的資源去解決。

由於停機問題,以太坊世界計算機就有了一個被程序要求永遠執行下去的風險。這可能是由於某些意外情況或者惡意的目的。我們曾討論過以太坊就像是一個單線程的計算機,沒有調度程序,所以它會被無限循環卡住,而這將會使得它變得不可用。

在有了gas之后,也就有了一個解決方案:如果在一個預先指定的最大計算量被用盡的時候計算還沒有結束,那么所有的處理都會無條件地停止。這就使得EVM成為一個准圖靈完備的機器:它可以解決你交給它的所有問題,但前提是這個問題可以在一定量的計算量內被解決。

 

六、區塊的gas限制

區塊的gas限制指的是一個區塊中的所有交易總共能消耗的最大gas數量,它也限定了一個區塊中能包含多少交易。

例如,我們假定有5個交易,它們的gas上限分別為30000、30000、40000、50000和50000。如果區塊的gas限制是180000,那么任意四個交易都可以包含到一個區塊中,第5個區塊則需要等待后續的區塊。如果是一個礦工嘗試包含一個gas消耗超過區塊gas限制的交易,那么這個區塊將會被網絡解決。

 


免責聲明!

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



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