開發與維運

以太坊虛擬機操作碼大全

以太坊EVM是棧式虛擬機,字長為256位,用於在以太坊區塊鏈上運行智能合約。EVM採用單字節操作碼(Opcode),因此全部操作碼定義在00~ff區間。本文提供EVM操作碼的速查簡表和詳表,方便以太坊智能合約開發人員、安全研究人員在開發、優化或分析以太坊智能合約的漏洞時作為指令手冊使用。

用自己熟悉的語言學習 以太坊DApp開發Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

1、EVM操作碼簡表

00 01 02 03 04 05 06 07 08 09 0A 0B -- -- -- --
10 11 12 13 14 15 16 17 18歲 19 1A 1B 1C 一維 -- --
20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3樓
40 41 42 43 44 45 -- -- -- -- -- -- -- -- -- --
50 51 52 53 54 55 56 57 58 59 5A 5B -- -- -- --
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6樓
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7樓
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8樓
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9樓
A0 A1 A2 A3 A4 -- -- -- -- -- -- -- -- -- -- --
B0 B1 B2 -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
F0 F1 F2 F3 F4 F5 -- -- -- -- F A -- -- FD -- FF

## 2、EVM總覽

以太坊虛擬機(EVM)是一個基於棧的big-endian虛擬機,字長為256位,用於在以太坊區塊鏈上運行智能合約。
智能合約就像普通賬戶一樣,不同之處在於它們在接收交易時運行EVM字節碼,從而使其能夠執行
計算和進一步交易。交易可以攜帶0或更多字節數據的有效負載,該有效負載用於指定與以太坊合約
的交互類型以及任何其他信息。

合約執行從字節碼的開頭開始。
除PUSH操作碼採用立即數外,每個操作碼均編碼為一個字節。
所有操作碼都從堆棧頂部彈出其操作數,然後推入其結果。

## 3、合約創建

創建智能合約的交易的數據有效載荷本身就是字節碼,它運行合約構造函數,設置初始合約狀態並返回最終合約字節碼。

一旦部署完成,構造函數就不會出現在部署後的合約中。

## 4、合約交互

通常,合約提供公開的ABI,這是用戶可以與以太坊智能合約進行交互的可支持方法的列表。為了與合約進行交互,用戶需要提交一筆交易,該交易攜帶任何數量的wei(包括0)以及根據ABI格式化的數據有效負載,並指定交互的類型和任何其他參數。

合約運行時,有四種主要的數據處理方式:

調用數據/Call Data

這是與智能合約交易相關的數據。它通常包含一個4字節的方法標識符然後是序列化參數。

請參閱:CALLDATALOADCALLDATASIZECALLDATACOPY
棧 / Stack

EVM維護一個uint256棧,用於保存局部變量、函數調用參數和返回地址。在返回地址和其他變量之間進行區分是比較困難的。

在此頁面上,棧頂表示為stack [-1],後跟stack [-2],...:

stack[-1] stack[-2] ...

請參閱:PUSH1DUP1SWAP1POP
內存 / Memory

內存是uint8的數組,用於在執行合約時保存暫態數據。

它不會在不同交易之間持久化。

請參閱:MLOAD,MSTORE,MSTORE8

存儲 / Storage

存儲是一個持久的關聯映射,以uint256為鍵、uint256為值。

所有的合約字段和映射都保存在存儲器中。

可以使用web3.eth.getStorageAt(address,key)查看合約的存儲字段。

請參閱:SLOADSSTORE

## 5、EVM操作碼詳表

uint8 助記符 棧輸入 棧輸出 表達式 備註說明
00 STOP - - STOP() 停止合約執行
01 ADD
a b
a + b
a + b (u)int256加法取模2256
02 MUL
a b
a b
a b (u)int256乘法取模2256
03 SUB
a b
a - b
a - b (u)int256減法取模2256
04 DIV
a b
a // b
a // b uint256除法
05 SDIV
a b
a // b
a // b int256除法
06 MOD
a b
a % b
a % b uint256取模
07 SMOD
a b
a % b
a % b int256取模
08 ADDMOD
a b N
(a + b) % N
(a + b) % N (u)int256加法取模N
09 MULMOD
a b N
(a b) % N
(a b) % N (u)int256乘法取模N
0A EXP
a b
a b
a b uint256指數結果取模2256
0B SIGNEXTEND
b x
y
y = SIGNEXTEND(x, b) 將x從 (b + 1) 8 位有符號擴展為 256位
0C Invalid - - - -
0D Invalid - - - -
0E Invalid - - - -
0F Invalid - - - -
10 LT
a b
a < b
a < b uint256比較
11 GT
a b
a > b
a > b uint256比較
12 SLT
a b
a < b
a < b int256比較
13 SGT
a b
a > b
a > b int256比較
14 EQ
a b
a == b
a == b (u)int256相等比較
15 ISZERO
a
a == 0
a == 0 (u)int256零比較
16 AND
a b
a & b
a & b 256位的位與計算
17 OR
a b
a | b
a | b 256位的位或計算
18 XOR
a b
a ^ b
a ^ b 256位的異或計算
19 NOT
a
~a
~a 256位的位取反計算
1A BYTE
i x
y
y = (x >> (248 - i 8)) & 0xFF 返回(u)int256 x從最高字節開始的第i字節
1B SHL
shift value
value << shift
value << shift 256位左移
1C SHR
shift value
value >> shift
value >> shift 256位右移
1D SAR
shift value
value >> shift
value >> shift int256右移位
1E Invalid - - - -
1F Invalid - - - -
20 SHA3
offset length
hash
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)
address(this) 當前執行合約的地址
31 BALANCE
addr
address(addr).balance
address(addr).balance 指定地址的餘額,單位wei
32 ORIGIN -
tx.origin
tx.origin 交易發起方地址
33 CALLER -
msg.caller
msg.caller 消息調用方地址
34 CALLVALUE -
msg.value
msg.value 以wei為單位的消息攜帶金額
35 CALLDATALOAD
i
msg.data[i:i+32]
msg.data[i:i+32] 從消息數據讀取一個(u)int256
36 CALLDATASIZE -
msg.data.size
msg.data.size 以字節為單位的消息數據長度
37 CALLDATACOPY
destOffset offset length
- memory[destOffset:destOffset+length] =
msg.data[offset:offset+length]
拷貝消息數據
38 CODESIZE -
address(this).code.size
address(this).code.size 以字節為單位的當前執行合約的長度
39 CODECOPY
destOffset offset length
- memory[destOffset:destOffset+length] =
address(this).code[offset:offset+length]
拷貝當前執行合約的字節碼
3A GASPRICE -
tx.gasprice
tx.gasprice 當前執行交易的單位gas價格,以wei為單位
3B EXTCODESIZE
addr
address(addr).code.size
address(addr).code.size 指定地址處的合約字節碼長度,以字節為單位
3C EXTCODECOPY
addr destOffset offset length
- memory[destOffset:destOffset+length] =
address(addr).code[offset:offset+length]
拷貝合約字節碼
3D RETURNDATASIZE -
size
size = RETURNDATASIZE() 最後一個外部調用的返回數據的長度,以字節為單位
3E RETURNDATACOPY
destOffset offset length
- memory[destOffset:destOffset+length] =
RETURNDATA[offset:offset+length]
拷貝返回的數據
3F EXTCODEHASH
addr
hash
hash = address(addr).exists ?
keccak256(address(addr).code) : 0
指定地址的合約字節碼的哈希,請參考EIP-1052事件
40 BLOCKHASH
blockNumber
hash
hash = block.blockHash(blockNumber) 指定區塊的哈希,僅適用於最近的256個區塊,不包括當前區塊
41 COINBASE -
block.coinbase
block.coinbase 當前區塊礦工的地址
42 TIMESTAMP -
block.timestamp
block.timestamp 當前區塊的UNIX時間戳,以秒為單位
43 NUMBER -
block.number
block.number 當前區塊號
44 DIFFICULTY -
block.difficulty
block.difficulty 當前區塊難度
45 GASLIMIT -
block.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
offset
value
value = memory[offset:offset+32] 從內存讀取一個(u)int256
52 MSTORE
offset value
- memory[offset:offset+32] = value 向內存寫入一個(u)int256
53 MSTORE8
offset value
- memory[offset] = value & 0xFF 向內存寫入一個uint8
54 SLOAD
key
value
value = storage[key] 從存儲讀取一個(u)int256
55 SSTORE
key value
- storage[key] = value 向存儲寫入一個(u)int256
56 JUMP
destination
- $pc = destination 無條件跳轉
57 JUMPI
destination condition
- $pc = cond ? destination : $pc + 1 條件為真時跳轉
58 PC -
$pc
$pc 程序計數器
59 MSIZE -
size
size = MSIZE() 當前合約執行的內存大小,以字節為單位
5A GAS -
gasRemaining
gasRemaining = GAS() 剩餘的GAS
5B JUMPDEST - - 用於註解可能的跳轉目標的元數據
5C Invalid - - - -
5D Invalid - - - -
5E Invalid - - - -
5F Invalid - - - -
60 PUSH1 -
uint8
PUSH(uint8) 將1字節數值壓入棧
61 PUSH2 -
uint16
PUSH(uint16) 將2字節數值壓入棧
62 PUSH3 -
uint24
PUSH(uint24) 將3字節數值壓入棧
63 PUSH4 -
uint32
PUSH(uint32) 將4字節數值壓入棧
64 PUSH5 -
uint40
PUSH(uint40) 將5字節數值壓入棧
65 PUSH6 -
uint48
PUSH(uint48) 將6字節數值壓入棧
66 PUSH7 -
uint56
PUSH(uint56) 將7字節數值壓入棧
67 PUSH8 -
uint64
PUSH(uint64) 將8字節數值壓入棧
68 PUSH9 -
uint72
PUSH(uint72) 將9字節數值壓入棧
69 PUSH10 -
uint80
PUSH(uint80) 將10字節數值壓入棧
6A PUSH11 -
uint88
PUSH(uint88) 將11字節數值壓入棧
6B PUSH12 -
uint96
PUSH(uint96) 將12字節數值壓入棧
6C PUSH13 -
uint104
PUSH(uint104) 將13字節數值壓入棧
6D PUSH14 -
uint112
PUSH(uint112) 將14字節數值壓入棧
6E PUSH15 -
uint120
PUSH(uint120) 將15字節數值壓入棧
6F PUSH16 -
uint128
PUSH(uint128) 將16字節數值壓入棧
70 PUSH17 -
uint136
PUSH(uint136) 將17字節數值壓入棧
71 PUSH18 -
uint144
PUSH(uint144) 將18字節數值壓入棧
72 PUSH19 -
uint152
PUSH(uint152) 將19字節數值壓入棧
73 PUSH20 -
uint160
PUSH(uint160) 將20字節數值壓入棧
74 PUSH21 -
uint168
PUSH(uint168) 將21字節數值壓入棧
75 PUSH22 -
uint176
PUSH(uint176) 將22字節數值壓入棧
76 PUSH23 -
uint184
PUSH(uint184) 將23字節數值壓入棧
77 PUSH24 -
uint192
PUSH(uint192) 將24字節數值壓入棧
78 PUSH25 -
uint200
PUSH(uint200) 將25字節數值壓入棧
79 PUSH26 -
uint208
PUSH(uint208) 將26字節數值壓入棧
7A PUSH27 -
uint216
PUSH(uint216) 將27字節數值壓入棧
7B PUSH28 -
uint224
PUSH(uint224) 將28字節數值壓入棧
7C PUSH29 -
uint232
PUSH(uint232) 將29字節數值壓入棧
7D PUSH30 -
uint240
PUSH(uint240) 將30字節數值壓入棧
7E PUSH31 -
uint248
PUSH(uint248) 將31字節數值壓入棧
7F PUSH32 -
uint256
PUSH(uint256) 將32字節數值壓入棧
80 DUP1
value
value value
PUSH(value) 克隆棧上最後一個值
81 DUP2
_ value
value _ value
PUSH(value) 克隆棧上倒數第二個值
82 DUP3
_ _ value
value _ _ value
PUSH(value) 克隆棧上倒數第三個值
83 DUP4
_ _ _ value
value _ _ _ value
PUSH(value) 克隆棧上倒數第四個值
84 DUP5
... value
value ... value
PUSH(value) 克隆棧上倒數第五個值
85 DUP6
... value
value ... value
PUSH(value) 克隆棧上倒數第六個值
86 DUP7
... value
value ... value
PUSH(value) 克隆棧上倒數第七個值
87 DUP8
... value
value ... value
PUSH(value) 克隆棧上倒數第八個值
88 DUP9
... value
value ... value
PUSH(value) 克隆棧上倒數第九個值
89 DUP10
... value
value ... value
PUSH(value) 克隆棧上倒數第10個值
8A DUP11
... value
value ... value
PUSH(value) 克隆棧上倒數第11個值
8B DUP12
... value
value ... value
PUSH(value) 克隆棧上倒數第12個值
8C DUP13
... value
value ... value
PUSH(value) 克隆棧上倒數第13個值
8D DUP14
... value
value ... value
PUSH(value) 克隆棧上倒數第14個值
8E DUP15
... value
value ... value
PUSH(value) 克隆棧上倒數第15個值
8F DUP16
... value
value ... value
PUSH(value) 克隆棧上倒數第16個值
90 SWAP1
a b
b a
a, b = b, a 交換棧頂兩個成員
91 SWAP2
a _ b
b _ a
a, b = b, a 交換棧頂與倒數第3個成員
92 SWAP3
a _ _ b
b _ _ a
a, b = b, a 交換棧頂與倒數第4個成員
93 SWAP4
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第5個成員
94 SWAP5
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第6個成員
95 SWAP6
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第7個成員
96 SWAP7
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第8個成員
97 SWAP8
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第9個成員
98 SWAP9
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第10個成員
99 SWAP10
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第11個成員
9A SWAP11
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第12個成員
9B SWAP12
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第13個成員
9C SWAP13
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第14個成員
9D SWAP14
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第15個成員
9E SWAP15
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第16個成員
9F SWAP16
a ... b
b ... a
a, b = b, a 交換棧頂與倒數第17個成員
A0 LOG0
offset length
- LOG0(memory[offset:offset+length]) 觸發事件
A1 LOG1
offset length topic0
- 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
value offset length
addr
addr = new memory[offset:offset+length].value(value) 創建子合約
F1 CALL
gas addr value argsOffset argsLength retOffset retLength
success
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
success, memory[retOffset:retOffset+retLength] =
address(addr).callcode.gas(gas).value(value)
(memory[argsOffset:argsOffset+argsLength])
???
F3 RETURN
offset length
- return memory[offset:offset+length] 從這個合約調用返回
F4 DELEGATECALL
gas addr argsOffset argsLength retOffset retLength
success
success, memory[retOffset:retOffset+retLength] =
address(addr).delegatecall.gas(gas)
(memory[argsOffset:argsOffset+argsLength])
使用當前合約的存儲調用另一個合約的方法
F5 CREATE2
value offset length salt
addr
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
success, memory[retOffset:retOffset+retLength] =
address(addr).staticcall.gas(gas)
(memory[argsOffset:argsOffset+argsLength])
調用另一個合約的方法,不允許合約創建、事件發送、存儲修改和合約銷燬等引起狀態改變的方法,參見EIP-214
FB Invalid - - - -
FC Invalid - - - -
FD REVERT
offset length
- revert(memory[offset:offset+length]) 回滾交易並返回數據
FE Invalid - - - -
FF SELFDESTRUCT
addr
- selfdestruct(address(addr)) 銷燬合約並將所有資金髮送給addr地址

原文鏈接:EVM操作碼速查 — 匯智網

Leave a Reply

Your email address will not be published. Required fields are marked *