今天 EVM 的篇幅可能會蠻長的,不過有一部份的內容我已經在 Assembly 和 Optimal Gas Comsumption 的部分提過,大家可以先回去複習這兩篇文章然後回來深入 EVM!
EVM 作為一個處理智能合約編譯之後 Bytecode 的虛擬機,藉由不同的 Opcodes 可以達到圖靈完備性,平常我們在呼叫合約函式時真正去做運算和處理狀態更新的就是 EVM。
與比特幣系統最大的不同,以太坊從分散式帳本昇華成分散式狀態機,藉由儲存「狀態」與交易紀錄來讓單純的支付系統變成能夠運行智能合約的圖靈完備網路。而以太坊的 block header 跟比特幣的單純 tx merkle root 不同,以 stateRoot、txRoot 以及 receiptsRoot 組成,詳細可見 EIP-2718: Typed Transaction Envelope 以及 Ethereum Merkle Patricia Trie Explained。
以下圖片皆來自於:ETHEREUM VIRTUAL MACHINE (EVM)
由之前的圖我們可以知道 EVM 中主要有三個重點部分:Volatile(Machine State: Stack, Memory, Available Gas)、Global Variables(Storage: BLOCK, MESSAGE, TX)、Persistent(Virtual ROM: Program Code)。其中:
在執行交易的時候,EVM 會:
那執行交易有分成三種:單純支付轉帳、創建合約、呼叫合約函式,只有後兩者會讓 EVM 運作,更深入一點來講,當你的 transaction 物件中的 data
有帶東西的時候,才會啟動 EVM 去運算。
這部分我直接取上述的 Source 來作為範例,每一個 opcodes 都會被 encoded 成一個 bytecode(例如 STOP 式 0x00
),這個 bytecode 就是我們把一個合約進行編譯之後會產生的一個產物(另外一個則是 ABI)。文中舉的例子為 0x6001600101
這組 bytecode,在執行時 bytecode 會根據其 bytes 長度切開處理(1 byte 等於 2 hexadecimal characters)。
0x60
這個 bytecode,意思是 PUSH1,也就是將這個 1 byte 長度的 data push 到 stack。0x60
之後出現的 0x01
是屬於這個 data 的 value。0x60
也就是 PUSH1 以及他的 value 0x01
。0x01
是 ADD 這個 opcodes。則會將 stack 中的兩個 item(value 皆為 0x01
)拿出來相加並且 push 回 stack。0x02
。以太坊中有四種記憶體結構,分別為 calldata, stack, memory 和 storage(由最便宜到最貴):
external
function 中的 reference types 才能使用(例如 array, string),calldata
的參數是唯讀的。memory
的變數屬於一種揮發性記憶體,也就是說在 EVM 運算結束時這個變數記憶體就會被刪掉,如果確保只會在 function 使用或者想要暫時宣告一個變數,就可以使用 memory
。EVM 就像是一個程式一樣跑在各種各樣不同的設備上,其中有一個標準化的 Protocol 被稱為 Geth(GO-Ethereum),也有其他不同的語言實作版本。EVM 負責執行所有的 bytecode(轉為 opcodes),只要是 EVM 能夠處理的 bytecode 並在其上運作的鏈便是 EVM-compatible blockchain。
舉例來說在 Ethereum 中使用 Solidity 撰寫的程式碼也能夠在其他 EVM-compatible blockchain 使用,這也是為什麼在 L2 開發時於 OP-Rollups 會比 ZK-Rollups 舒適一點(ZK-Rollups 也有 EVM-Compatible 的 Solutions,例如 zkSync)。
最後歡迎大家拍打餵食大學生
0x2b83c71A59b926137D3E1f37EF20394d0495d72d