以太坊EVM是棧式虛擬機,字長為256位,用於在以太坊區塊鏈上運行智能合約。EVM採用單字節操作碼(Opcode),因此全部操作碼定義在00~ff區間。本文提供EVM操作碼的速查簡表和詳表,方便以太坊智能合約開發人員、安全研究人員在開發、優化或分析以太坊智能合約的漏洞時作為指令手冊使用。
用自己熟悉的語言學習 以太坊DApp開發 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、EVM操作碼簡表
## 2、EVM總覽
以太坊虛擬機(EVM)是一個基於棧的big-endian虛擬機,字長為256位,用於在以太坊區塊鏈上運行智能合約。
智能合約就像普通賬戶一樣,不同之處在於它們在接收交易時運行EVM字節碼,從而使其能夠執行
計算和進一步交易。交易可以攜帶0或更多字節數據的有效負載,該有效負載用於指定與以太坊合約
的交互類型以及任何其他信息。
合約執行從字節碼的開頭開始。
除PUSH操作碼採用立即數外,每個操作碼均編碼為一個字節。
所有操作碼都從堆棧頂部彈出其操作數,然後推入其結果。
## 3、合約創建
創建智能合約的交易的數據有效載荷本身就是字節碼,它運行合約構造函數,設置初始合約狀態並返回最終合約字節碼。
一旦部署完成,構造函數就不會出現在部署後的合約中。
## 4、合約交互
通常,合約提供公開的ABI,這是用戶可以與以太坊智能合約進行交互的可支持方法的列表。為了與合約進行交互,用戶需要提交一筆交易,該交易攜帶任何數量的wei(包括0)以及根據ABI格式化的數據有效負載,並指定交互的類型和任何其他參數。
合約運行時,有四種主要的數據處理方式:
調用數據/Call Data
這是與智能合約交易相關的數據。它通常包含一個4字節的方法標識符然後是序列化參數。
請參閱:CALLDATALOAD,CALLDATASIZE,CALLDATACOPY
棧 / Stack
EVM維護一個uint256棧,用於保存局部變量、函數調用參數和返回地址。在返回地址和其他變量之間進行區分是比較困難的。
在此頁面上,棧頂表示為stack [-1],後跟stack [-2],...:
請參閱:PUSH1,DUP1,SWAP1,POP
內存 / Memory
內存是uint8的數組,用於在執行合約時保存暫態數據。
它不會在不同交易之間持久化。
請參閱:MLOAD,MSTORE,MSTORE8
存儲 / Storage
存儲是一個持久的關聯映射,以uint256為鍵、uint256為值。
所有的合約字段和映射都保存在存儲器中。
可以使用web3.eth.getStorageAt(address,key)
查看合約的存儲字段。
請參閱:SLOAD,SSTORE
## 5、EVM操作碼詳表
uint8 |
助記符 |
棧輸入 |
棧輸出 |
表達式 |
備註說明 |
00
|
STOP |
- |
- |
STOP() |
停止合約執行 |
01
|
ADD |
|
|
a + b |
(u)int256加法取模2256 |
02
|
MUL |
|
|
a b |
(u)int256乘法取模2256 |
03
|
SUB |
|
|
a - b |
(u)int256減法取模2256 |
04
|
DIV |
|
|
a // b |
uint256除法 |
05
|
SDIV |
|
|
a // b |
int256除法 |
06
|
MOD |
|
|
a % b |
uint256取模 |
07
|
SMOD |
|
|
a % b |
int256取模 |
08
|
ADDMOD |
|
|
(a + b) % N |
(u)int256加法取模N |
09
|
MULMOD |
|
|
(a b) % N |
(u)int256乘法取模N |
0A
|
EXP |
|
|
a b
|
uint256指數結果取模2256 |
0B
|
SIGNEXTEND |
|
|
y = SIGNEXTEND(x, b) |
將x從 (b + 1) 8 位有符號擴展為 256位 |
0C
|
Invalid |
- |
- |
- |
- |
0D
|
Invalid |
- |
- |
- |
- |
0E
|
Invalid |
- |
- |
- |
- |
0F
|
Invalid |
- |
- |
- |
- |
10
|
LT |
|
|
a < b |
uint256比較 |
11
|
GT |
|
|
a > b |
uint256比較 |
12
|
SLT |
|
|
a < b |
int256比較 |
13
|
SGT |
|
|
a > b |
int256比較 |
14
|
EQ |
|
|
a == b |
(u)int256相等比較 |
15
|
ISZERO |
|
|
a == 0 |
(u)int256零比較 |
16
|
AND |
|
|
a & b |
256位的位與計算 |
17
|
OR |
|
|
a | b |
256位的位或計算 |
18
|
XOR |
|
|
a ^ b |
256位的異或計算 |
19
|
NOT |
|
|
~a |
256位的位取反計算 |
1A
|
BYTE |
|
|
y = (x >> (248 - i 8)) & 0xFF |
返回(u)int256 x從最高字節開始的第i字節 |
1B
|
SHL |
|
|
value << shift |
256位左移 |
1C
|
SHR |
|
|
value >> shift |
256位右移 |
1D
|
SAR |
|
|
value >> shift |
int256右移位 |
1E
|
Invalid |
- |
- |
- |
- |
1F
|
Invalid |
- |
- |
- |
- |
20
|
SHA3 |
|
|
hash = keccak256(memory[offset:offset+length]) |
keccak256哈希 |
21
|
Invalid |
- |
- |
- |
- |
22
|
Invalid |
- |
- |
- |
- |
23
|
Invalid |
- |
- |
- |
- |
24
|
Invalid |
- |
- |
- |
- |
25
|
Invalid |
- |
- |
- |
- |
26
|
Invalid |
- |
- |
- |
- |
27
|
Invalid |
- |
- |
- |
- |
28
|
Invalid |
- |
- |
- |
- |
29
|
Invalid |
- |
- |
- |
- |
2A
|
Invalid |
- |
- |
- |
- |
2B
|
Invalid |
- |
- |
- |
- |
2C
|
Invalid |
- |
- |
- |
- |
2D
|
Invalid |
- |
- |
- |
- |
2E
|
Invalid |
- |
- |
- |
- |
2F
|
Invalid |
- |
- |
- |
- |
30
|
ADDRESS |
- |
|
address(this) |
當前執行合約的地址 |
31
|
BALANCE |
|
|
address(addr).balance |
指定地址的餘額,單位wei |
32
|
ORIGIN |
- |
|
tx.origin |
交易發起方地址 |
33
|
CALLER |
- |
|
msg.caller |
消息調用方地址 |
34
|
CALLVALUE |
- |
|
msg.value |
以wei為單位的消息攜帶金額 |
35
|
CALLDATALOAD |
|
|
msg.data[i:i+32] |
從消息數據讀取一個(u)int256 |
36
|
CALLDATASIZE |
- |
|
msg.data.size |
以字節為單位的消息數據長度 |
37
|
CALLDATACOPY |
|
- |
memory[destOffset:destOffset+length] =
msg.data[offset:offset+length] |
拷貝消息數據 |
38
|
CODESIZE |
- |
|
address(this).code.size |
以字節為單位的當前執行合約的長度 |
39
|
CODECOPY |
|
- |
memory[destOffset:destOffset+length] =
address(this).code[offset:offset+length] |
拷貝當前執行合約的字節碼 |
3A
|
GASPRICE |
- |
|
tx.gasprice |
當前執行交易的單位gas價格,以wei為單位 |
3B
|
EXTCODESIZE |
|
|
address(addr).code.size |
指定地址處的合約字節碼長度,以字節為單位 |
3C
|
EXTCODECOPY |
addr |
destOffset |
offset |
length |
|
|
- |
memory[destOffset:destOffset+length] =
address(addr).code[offset:offset+length] |
拷貝合約字節碼 |
3D
|
RETURNDATASIZE |
- |
|
size = RETURNDATASIZE() |
最後一個外部調用的返回數據的長度,以字節為單位 |
3E
|
RETURNDATACOPY |
|
- |
memory[destOffset:destOffset+length] =
RETURNDATA[offset:offset+length] |
拷貝返回的數據 |
3F
|
EXTCODEHASH |
|
|
hash = address(addr).exists ?
keccak256(address(addr).code) : 0 |
指定地址的合約字節碼的哈希,請參考EIP-1052事件 |
40
|
BLOCKHASH |
|
|
hash = block.blockHash(blockNumber) |
指定區塊的哈希,僅適用於最近的256個區塊,不包括當前區塊 |
41
|
COINBASE |
- |
|
block.coinbase |
當前區塊礦工的地址 |
42
|
TIMESTAMP |
- |
|
block.timestamp |
當前區塊的UNIX時間戳,以秒為單位 |
43
|
NUMBER |
- |
|
block.number |
當前區塊號 |
44
|
DIFFICULTY |
- |
|
block.difficulty |
當前區塊難度 |
45
|
GASLIMIT |
- |
|
block.gaslimit |
當前區塊GAS上限 |
46
|
Invalid |
- |
- |
- |
- |
47
|
Invalid |
- |
- |
- |
- |
48
|
Invalid |
- |
- |
- |
- |
49
|
Invalid |
- |
- |
- |
- |
4A
|
Invalid |
- |
- |
- |
- |
4B
|
Invalid |
- |
- |
- |
- |
4C
|
Invalid |
- |
- |
- |
- |
4D
|
Invalid |
- |
- |
- |
- |
4E
|
Invalid |
- |
- |
- |
- |
4F
|
Invalid |
- |
- |
- |
- |
50
|
POP |
|
- |
POP() |
彈出棧頂(u)int256 並丟棄 |
51
|
MLOAD |
|
|
value = memory[offset:offset+32] |
從內存讀取一個(u)int256 |
52
|
MSTORE |
|
- |
memory[offset:offset+32] = value |
向內存寫入一個(u)int256 |
53
|
MSTORE8 |
|
- |
memory[offset] = value & 0xFF |
向內存寫入一個uint8 |
54
|
SLOAD |
|
|
value = storage[key] |
從存儲讀取一個(u)int256 |
55
|
SSTORE |
|
- |
storage[key] = value |
向存儲寫入一個(u)int256 |
56
|
JUMP |
|
- |
$pc = destination |
無條件跳轉 |
57
|
JUMPI |
|
- |
$pc = cond ? destination : $pc + 1 |
條件為真時跳轉 |
58
|
PC |
- |
|
$pc |
程序計數器 |
59
|
MSIZE |
- |
|
size = MSIZE() |
當前合約執行的內存大小,以字節為單位 |
5A
|
GAS |
- |
|
gasRemaining = GAS() |
剩餘的GAS |
5B
|
JUMPDEST |
- |
- |
|
用於註解可能的跳轉目標的元數據 |
5C
|
Invalid |
- |
- |
- |
- |
5D
|
Invalid |
- |
- |
- |
- |
5E
|
Invalid |
- |
- |
- |
- |
5F
|
Invalid |
- |
- |
- |
- |
60
|
PUSH1 |
- |
|
PUSH(uint8) |
將1字節數值壓入棧 |
61
|
PUSH2 |
- |
|
PUSH(uint16) |
將2字節數值壓入棧 |
62
|
PUSH3 |
- |
|
PUSH(uint24) |
將3字節數值壓入棧 |
63
|
PUSH4 |
- |
|
PUSH(uint32) |
將4字節數值壓入棧 |
64
|
PUSH5 |
- |
|
PUSH(uint40) |
將5字節數值壓入棧 |
65
|
PUSH6 |
- |
|
PUSH(uint48) |
將6字節數值壓入棧 |
66
|
PUSH7 |
- |
|
PUSH(uint56) |
將7字節數值壓入棧 |
67
|
PUSH8 |
- |
|
PUSH(uint64) |
將8字節數值壓入棧 |
68
|
PUSH9 |
- |
|
PUSH(uint72) |
將9字節數值壓入棧 |
69
|
PUSH10 |
- |
|
PUSH(uint80) |
將10字節數值壓入棧 |
6A
|
PUSH11 |
- |
|
PUSH(uint88) |
將11字節數值壓入棧 |
6B
|
PUSH12 |
- |
|
PUSH(uint96) |
將12字節數值壓入棧 |
6C
|
PUSH13 |
- |
|
PUSH(uint104) |
將13字節數值壓入棧 |
6D
|
PUSH14 |
- |
|
PUSH(uint112) |
將14字節數值壓入棧 |
6E
|
PUSH15 |
- |
|
PUSH(uint120) |
將15字節數值壓入棧 |
6F
|
PUSH16 |
- |
|
PUSH(uint128) |
將16字節數值壓入棧 |
70
|
PUSH17 |
- |
|
PUSH(uint136) |
將17字節數值壓入棧 |
71
|
PUSH18 |
- |
|
PUSH(uint144) |
將18字節數值壓入棧 |
72
|
PUSH19 |
- |
|
PUSH(uint152) |
將19字節數值壓入棧 |
73
|
PUSH20 |
- |
|
PUSH(uint160) |
將20字節數值壓入棧 |
74
|
PUSH21 |
- |
|
PUSH(uint168) |
將21字節數值壓入棧 |
75
|
PUSH22 |
- |
|
PUSH(uint176) |
將22字節數值壓入棧 |
76
|
PUSH23 |
- |
|
PUSH(uint184) |
將23字節數值壓入棧 |
77
|
PUSH24 |
- |
|
PUSH(uint192) |
將24字節數值壓入棧 |
78
|
PUSH25 |
- |
|
PUSH(uint200) |
將25字節數值壓入棧 |
79
|
PUSH26 |
- |
|
PUSH(uint208) |
將26字節數值壓入棧 |
7A
|
PUSH27 |
- |
|
PUSH(uint216) |
將27字節數值壓入棧 |
7B
|
PUSH28 |
- |
|
PUSH(uint224) |
將28字節數值壓入棧 |
7C
|
PUSH29 |
- |
|
PUSH(uint232) |
將29字節數值壓入棧 |
7D
|
PUSH30 |
- |
|
PUSH(uint240) |
將30字節數值壓入棧 |
7E
|
PUSH31 |
- |
|
PUSH(uint248) |
將31字節數值壓入棧 |
7F
|
PUSH32 |
- |
|
PUSH(uint256) |
將32字節數值壓入棧 |
80
|
DUP1 |
|
|
PUSH(value) |
克隆棧上最後一個值 |
81
|
DUP2 |
|
|
PUSH(value) |
克隆棧上倒數第二個值 |
82
|
DUP3 |
|
|
PUSH(value) |
克隆棧上倒數第三個值 |
83
|
DUP4 |
|
|
PUSH(value) |
克隆棧上倒數第四個值 |
84
|
DUP5 |
|
|
PUSH(value) |
克隆棧上倒數第五個值 |
85
|
DUP6 |
|
|
PUSH(value) |
克隆棧上倒數第六個值 |
86
|
DUP7 |
|
|
PUSH(value) |
克隆棧上倒數第七個值 |
87
|
DUP8 |
|
|
PUSH(value) |
克隆棧上倒數第八個值 |
88
|
DUP9 |
|
|
PUSH(value) |
克隆棧上倒數第九個值 |
89
|
DUP10 |
|
|
PUSH(value) |
克隆棧上倒數第10個值 |
8A
|
DUP11 |
|
|
PUSH(value) |
克隆棧上倒數第11個值 |
8B
|
DUP12 |
|
|
PUSH(value) |
克隆棧上倒數第12個值 |
8C
|
DUP13 |
|
|
PUSH(value) |
克隆棧上倒數第13個值 |
8D
|
DUP14 |
|
|
PUSH(value) |
克隆棧上倒數第14個值 |
8E
|
DUP15 |
|
|
PUSH(value) |
克隆棧上倒數第15個值 |
8F
|
DUP16 |
|
|
PUSH(value) |
克隆棧上倒數第16個值 |
90
|
SWAP1 |
|
|
a, b = b, a |
交換棧頂兩個成員 |
91
|
SWAP2 |
|
|
a, b = b, a |
交換棧頂與倒數第3個成員 |
92
|
SWAP3 |
|
|
a, b = b, a |
交換棧頂與倒數第4個成員 |
93
|
SWAP4 |
|
|
a, b = b, a |
交換棧頂與倒數第5個成員 |
94
|
SWAP5 |
|
|
a, b = b, a |
交換棧頂與倒數第6個成員 |
95
|
SWAP6 |
|
|
a, b = b, a |
交換棧頂與倒數第7個成員 |
96
|
SWAP7 |
|
|
a, b = b, a |
交換棧頂與倒數第8個成員 |
97
|
SWAP8 |
|
|
a, b = b, a |
交換棧頂與倒數第9個成員 |
98
|
SWAP9 |
|
|
a, b = b, a |
交換棧頂與倒數第10個成員 |
99
|
SWAP10 |
|
|
a, b = b, a |
交換棧頂與倒數第11個成員 |
9A
|
SWAP11 |
|
|
a, b = b, a |
交換棧頂與倒數第12個成員 |
9B
|
SWAP12 |
|
|
a, b = b, a |
交換棧頂與倒數第13個成員 |
9C
|
SWAP13 |
|
|
a, b = b, a |
交換棧頂與倒數第14個成員 |
9D
|
SWAP14 |
|
|
a, b = b, a |
交換棧頂與倒數第15個成員 |
9E
|
SWAP15 |
|
|
a, b = b, a |
交換棧頂與倒數第16個成員 |
9F
|
SWAP16 |
|
|
a, b = b, a |
交換棧頂與倒數第17個成員 |
A0
|
LOG0 |
|
- |
LOG0(memory[offset:offset+length]) |
觸發事件 |
A1
|
LOG1 |
|
- |
LOG1(memory[offset:offset+length], topic0) |
觸發事件 |
A2
|
LOG2 |
offset |
length |
topic0 |
topic1 |
|
|
- |
LOG2(memory[offset:offset+length], topic0, topic1) |
觸發事件 |
A3
|
LOG3 |
offset |
length |
topic0 |
topic1 |
topic2 |
|
|
- |
LOG3(memory[offset:offset+length], topic0, topic1,
topic2) |
觸發事件 |
A4
|
LOG4 |
offset |
length |
topic0 |
topic1 |
topic2 |
topic3 |
|
|
- |
LOG4(memory[offset:offset+length], topic0, topic1,
topic2, topic3) |
觸發事件 |
A5
|
Invalid |
- |
- |
- |
- |
A6
|
Invalid |
- |
- |
- |
- |
A7
|
Invalid |
- |
- |
- |
- |
A8
|
Invalid |
- |
- |
- |
- |
A9
|
Invalid |
- |
- |
- |
- |
AA
|
Invalid |
- |
- |
- |
- |
AB
|
Invalid |
- |
- |
- |
- |
AC
|
Invalid |
- |
- |
- |
- |
AD
|
Invalid |
- |
- |
- |
- |
AE
|
Invalid |
- |
- |
- |
- |
AF
|
Invalid |
- |
- |
- |
- |
B0
|
PUSH |
- |
- |
??? |
??? |
B1
|
DUP |
- |
- |
??? |
??? |
B2
|
SWAP |
- |
- |
??? |
??? |
B3
|
Invalid |
- |
- |
- |
- |
B4
|
Invalid |
- |
- |
- |
- |
B5
|
Invalid |
- |
- |
- |
- |
B6
|
Invalid |
- |
- |
- |
- |
B7
|
Invalid |
- |
- |
- |
- |
B8
|
Invalid |
- |
- |
- |
- |
B9
|
Invalid |
- |
- |
- |
- |
BA
|
Invalid |
- |
- |
- |
- |
BB
|
Invalid |
- |
- |
- |
- |
BC
|
Invalid |
- |
- |
- |
- |
BD
|
Invalid |
- |
- |
- |
- |
BE
|
Invalid |
- |
- |
- |
- |
BF
|
Invalid |
- |
- |
- |
- |
C0
|
Invalid |
- |
- |
- |
- |
C1
|
Invalid |
- |
- |
- |
- |
C2
|
Invalid |
- |
- |
- |
- |
C3
|
Invalid |
- |
- |
- |
- |
C4
|
Invalid |
- |
- |
- |
- |
C5
|
Invalid |
- |
- |
- |
- |
C6
|
Invalid |
- |
- |
- |
- |
C7
|
Invalid |
- |
- |
- |
- |
C8
|
Invalid |
- |
- |
- |
- |
C9
|
Invalid |
- |
- |
- |
- |
CA
|
Invalid |
- |
- |
- |
- |
CB
|
Invalid |
- |
- |
- |
- |
CC
|
Invalid |
- |
- |
- |
- |
CD
|
Invalid |
- |
- |
- |
- |
CE
|
Invalid |
- |
- |
- |
- |
CF
|
Invalid |
- |
- |
- |
- |
D0
|
Invalid |
- |
- |
- |
- |
D1
|
Invalid |
- |
- |
- |
- |
D2
|
Invalid |
- |
- |
- |
- |
D3
|
Invalid |
- |
- |
- |
- |
D4
|
Invalid |
- |
- |
- |
- |
D5
|
Invalid |
- |
- |
- |
- |
D6
|
Invalid |
- |
- |
- |
- |
D7
|
Invalid |
- |
- |
- |
- |
D8
|
Invalid |
- |
- |
- |
- |
D9
|
Invalid |
- |
- |
- |
- |
DA
|
Invalid |
- |
- |
- |
- |
DB
|
Invalid |
- |
- |
- |
- |
DC
|
Invalid |
- |
- |
- |
- |
DD
|
Invalid |
- |
- |
- |
- |
DE
|
Invalid |
- |
- |
- |
- |
DF
|
Invalid |
- |
- |
- |
- |
E0
|
Invalid |
- |
- |
- |
- |
E1
|
Invalid |
- |
- |
- |
- |
E2
|
Invalid |
- |
- |
- |
- |
E3
|
Invalid |
- |
- |
- |
- |
E4
|
Invalid |
- |
- |
- |
- |
E5
|
Invalid |
- |
- |
- |
- |
E6
|
Invalid |
- |
- |
- |
- |
E7
|
Invalid |
- |
- |
- |
- |
E8
|
Invalid |
- |
- |
- |
- |
E9
|
Invalid |
- |
- |
- |
- |
EA
|
Invalid |
- |
- |
- |
- |
EB
|
Invalid |
- |
- |
- |
- |
EC
|
Invalid |
- |
- |
- |
- |
ED
|
Invalid |
- |
- |
- |
- |
EE
|
Invalid |
- |
- |
- |
- |
EF
|
Invalid |
- |
- |
- |
- |
F0
|
CREATE |
|
|
addr = new memory[offset:offset+length].value(value) |
創建子合約 |
F1
|
CALL |
gas |
addr |
value |
argsOffset |
argsLength |
retOffset |
retLength |
|
|
|
success, memory[retOffset:retOffset+retLength] =
address(addr).call.gas(gas).value(value)
(memory[argsOffset:argsOffset+argsLength]) |
調用另一個合約中的方法 |
F2
|
CALLCODE |
gas |
addr |
value |
argsOffset |
argsLength |
retOffset |
retLength |
|
|
|
success, memory[retOffset:retOffset+retLength] =
address(addr).callcode.gas(gas).value(value)
(memory[argsOffset:argsOffset+argsLength]) |
??? |
F3
|
RETURN |
|
- |
return memory[offset:offset+length] |
從這個合約調用返回 |
F4
|
DELEGATECALL |
gas |
addr |
argsOffset |
argsLength |
retOffset |
retLength |
|
|
|
success, memory[retOffset:retOffset+retLength] =
address(addr).delegatecall.gas(gas)
(memory[argsOffset:argsOffset+argsLength]) |
使用當前合約的存儲調用另一個合約的方法 |
F5
|
CREATE2 |
|
|
addr = new memory[offset:offset+length].value(value) |
使用確定的地址創建子合約,參見see EIP-1014 |
F6
|
Invalid |
- |
- |
- |
- |
F7
|
Invalid |
- |
- |
- |
- |
F8
|
Invalid |
- |
- |
- |
- |
F9
|
Invalid |
- |
- |
- |
- |
FA
|
STATICCALL |
gas |
addr |
argsOffset |
argsLength |
retOffset |
retLength |
|
|
|
success, memory[retOffset:retOffset+retLength] =
address(addr).staticcall.gas(gas)
(memory[argsOffset:argsOffset+argsLength]) |
調用另一個合約的方法,不允許合約創建、事件發送、存儲修改和合約銷燬等引起狀態改變的方法,參見EIP-214 |
FB
|
Invalid |
- |
- |
- |
- |
FC
|
Invalid |
- |
- |
- |
- |
FD
|
REVERT |
|
- |
revert(memory[offset:offset+length]) |
回滾交易並返回數據 |
FE
|
Invalid |
- |
- |
- |
- |
FF
|
SELFDESTRUCT |
|
- |
selfdestruct(address(addr)) |
銷燬合約並將所有資金髮送給addr地址 |
原文鏈接:EVM操作碼速查 — 匯智網