想了解以太坊 Ethereum Virtual Machine EVM opcode 推薦這個 evm.codes 的練習網站(https://www.evm.codes/)。網站的目的是為了幫助大家進一步認識背後這些 EVM opcodes 在忙些什麼。第一次玩可以進行一個基本的「ADD」操作。
EVM 是 stack-based 的資料結構。堆疊 stack 基本上是想像美國人洗碗機旁邊放著一疊盤子(a stack of plates),last-in, first-out (LIFO) data structure。最晚放上去的盤子,或是最後放上去的盤子,最先拿起來。維基百科 Stack (abstract data type) 的介紹就有放一張很高的一疊盤子的照片。(https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
接著想執行一個 ADD opcode 的運算。Opcode 運算涉及十六進位的轉換。先了解十六進位和十進位的轉換怎麼做。為了方便溝通,只要是十六進位的數字 14
就會像這樣用程式碼的方式標起來,十進位的數字就使用正常的字體。
EVM opcode 是十六進位。EVM opcode 需要使用十六進位的理由,主要是電腦底層都是用 binary 二進位操作。binary 寫下來就會是 010101 長這個樣子。一個 byte 有 8 個 bit。因此會有 8 個 0 或 1,例如 01010101。人眼看起來冗長,換成十六進位 Hexadecimal 只需要 2 個位數就能表達一個 byte。例如 ff
。大幅縮減人眼閱讀理解二進位資料的困難度。
0 1 2 3 4 5 6 7 8 9 A B C D E F
10 11 12 13 14 15
十六進位用符號 0 到 9 表示數值 0 到 9,A 到 F 代表數值 10 到 15。代表十六種組合。
14
這張照片是表現大半夜算 Hexadecimal 到 Decimal 進位轉換。待會 opcode 運算範例中會轉換的數字是 Hexadecimal 14
。所以就拿它當舉例。 14
腦袋裡讀「一、四」因為第二個位數的 1 並不是 10,所以中文若讀成「十四」容易造成混淆。
現在來進位轉換,第一位數是底數 16 的 0 次方,乘上 4,第二個位數是底數 16 的 1 次方,乘上 1。乘完後相加,4x1 = 4, 1x16 = 16,相加後 4 + 16 = 20。 Hexadecimal 14
轉換後是 decimal 20。
0a
完整的一個 byte 要有二個 hex,所以在介面上看到 PUSH1 寫「0a
」。十六進位的 0a
就是十進位的「10」。所以他並沒有寫錯。因為是十六進位的緣故。
0a
的 0 是補上的零,十六進位的 a 是十進位的 10。基本上看到 0a
就是 10。
點開「ADD」展開後找到裡面一行有底線的「Reproduce in playground.」,點進去之後往上捲,接著就開始操作 EVM opcode ADD。
比如說 EVM Playground 他的 ADD 的範例是:
// Example 1
PUSH1 10
PUSH1 10
ADD
這段程式的意圖是想做一個 10 + 10 的加法運算。要做這樣的加法,需要先把兩個 10 推到 stack 盤子裡,然後才能進行 add opcode 的運算操作。
「PUSH1」就是一個 opcode。 他的意思是把一個 byte 的資料推到 stack。
PUSH1 10 (這裡的 10 是十進位的 10)加上 PUSH1 10 ,相加 ADD 的時候,第一個盤子 stack 會放 10,第二個盤子 stack 就會放 10。按下右方的迷你轉彎箭頭符號 step into 進到下一步。
下方的 stack 欄位會顯示:
第一個盤子 stack 放了 a
。也就是 Push 10 onto the stack。
第二個盤子 stack 放了 a
。也就是 Push 10 onto the stack。
最後 ADD 加起來。
Stack 會是 14
。這個 14
就是 Pop 盤子拿起來,再推回來,相加後的值。回想一次,兩個放有 a
的盤子先放入,之後,拿出來的盤子放有 14
,再推回去放 14
的盤子。十六進位 Hexadecimal 是 14
,轉換 decimal 十進位是 20。
[00] 是 program counter,也就是 bytecode 位置 position 的第 0 個 byte。
60 是 PUSH1,第一個 byte。opcode 要算 gas。
0a 第二個 byte。是前面 PUSH1 的 argument,不用算 gas。
60 是 PUSH1,第三個 byte。opcode 要算 gas。
0a 第四個 byte。是前面 PUSH1 的 argument,不用算 gas。
01 是 ADD。第五個 byte。opcode 要算 gas。ADD 花了 3 gas。
整個 bytecode 來看會是 [60, 0a, 60, 0a, 01]
EVM opcode 每個指令的 gas cost 就像餐館的菜單,菜單寫品項和價格,就像 EVM opcode 每一步的運算的成本,都給一個定價。每個 opcode 都會有一個 gas cost,不同的 opcode 有不同的 gas cost。Total: 21000 是 gas cost 燃料手續費跳表起跳價,類似搭計程車時,一搭車就會有一個起跳價。每個 transaction 交易開始,gas cost 起跳從 21000 gas 開始。