以太坊EVM是栈式虚拟机,字长为256位,用于在以太坊区块链上运行智能合约。EVM采用单字节操作码(Opcode),因此全部操作码定义在00~ff区间。本文提供EVM操作码的速查简表和详表,方便以太坊智能合约开发人员、安全研究人员在开发、优化或分析以太坊智能合约的漏洞时作为指令手册使用。
用自己熟悉的语言学习 以太坊DApp开发 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、EVM操作码简表
000102030405060708090A0B--------101112131415161718岁191A1B1C一维----20------------------------------303132333435363738393A3B3C3D3E3楼404142434445--------------------505152535455565758595A5B--------606162636465666768696A6B6C6D6E6楼707172737475767778797A7B7C7D7E7楼808182838485868788898A8B8C8D8E8楼909192939495969798999A9B9C9D9E9楼A0A1A2A3A4----------------------B0B1B2--------------------------------------------------------------------------------------------------------------------------F0F1F2F3F4F5--------F A----FD--FF
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],…:
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助记符栈输入栈输出表达式备注说明
00STOP--STOP()停止合约执行 01ADD
ab
a + ba + b(u)int256加法取模2**256 02MUL
ab
a * ba * b(u)int256乘法取模2**256 03SUB
ab
a - ba - b(u)int256减法取模2**256 04DIV
ab
a // ba // buint256除法 05SDIV
ab
a // ba // bint256除法 06MOD
ab
a % ba % buint256取模 07SMOD
ab
a % ba % bint256取模 08ADDMOD
abN
(a + b) % N(a + b) % N(u)int256加法取模N 09MULMOD
abN
(a * b) % N(a * b) % N(u)int256乘法取模N 0AEXP
ab
a ** ba ** buint256指数结果取模2**256 0BSIGNEXTEND
bx
yy = SIGNEXTEND(x, b)将x从 (b + 1) * 8 位有符号扩展为 256位 0CInvalid---- 0DInvalid---- 0EInvalid---- 0FInvalid---- 10LT
ab
a < ba < buint256比较 11GT
ab
a > ba > buint256比较 12SLT
ab
a < ba < bint256比较 13SGT
ab
a > ba > bint256比较 14EQ
ab
a == ba == b(u)int256相等比较 15ISZERO
a
a == 0a == 0(u)int256零比较 16AND
ab
a & ba & b256位的位与计算 17OR
ab
a | ba | b256位的位或计算 18XOR
ab
a ^ ba ^ b256位的异或计算 19NOT
a
~a~a256位的位取反计算 1ABYTE
ix
yy = (x >> (248 - i * 8)) & 0xFF返回(u)int256 x从最高字节开始的第i字节 1BSHL
shiftvalue
value << shiftvalue << shift256位左移 1CSHR
shiftvalue
value >> shiftvalue >> shift256位右移 1DSAR
shiftvalue
value >> shiftvalue >> shiftint256右移位 1EInvalid---- 1FInvalid---- 20SHA3
offsetlength
hashhash = keccak256(memory[offset:offset+length])keccak256哈希 21Invalid---- 22Invalid---- 23Invalid---- 24Invalid---- 25Invalid---- 26Invalid---- 27Invalid---- 28Invalid---- 29Invalid---- 2AInvalid---- 2BInvalid---- 2CInvalid---- 2DInvalid---- 2EInvalid---- 2FInvalid---- 30ADDRESS-
address(this)address(this)当前执行合约的地址 31BALANCE
addr
address(addr).balanceaddress(addr).balance指定地址的余额,单位wei 32ORIGIN-
tx.origintx.origin交易发起方地址 33CALLER-
msg.callermsg.caller消息调用方地址 34CALLVALUE-
msg.valuemsg.value以wei为单位的消息携带金额 35CALLDATALOAD
i
msg.data[i:i+32]msg.data[i:i+32]从消息数据读取一个(u)int256 36CALLDATASIZE-
msg.data.sizemsg.data.size以字节为单位的消息数据长度 37CALLDATACOPY
destOffsetoffsetlength-memory[destOffset:destOffset+length] = msg.data[offset:offset+length]拷贝消息数据 38CODESIZE-
address(this).code.sizeaddress(this).code.size以字节为单位的当前执行合约的长度 39CODECOPY
destOffsetoffsetlength-memory[destOffset:destOffset+length] = address(this).code[offset:offset+length]拷贝当前执行合约的字节码 3AGASPRICE-
tx.gaspricetx.gasprice当前执行交易的单位gas价格,以wei为单位 3BEXTCODESIZE
addr
address(addr).code.sizeaddress(addr).code.size指定地址处的合约字节码长度,以字节为单位 3CEXTCODECOPY
addrdestOffsetoffsetlength-memory[destOffset:destOffset+length] = address(addr).code[offset:offset+length]拷贝合约字节码 3DRETURNDATASIZE-
sizesize = RETURNDATASIZE()最后一个外部调用的返回数据的长度,以字节为单位 3ERETURNDATACOPY
destOffsetoffsetlength-memory[destOffset:destOffset+length] = RETURNDATA[offset:offset+length]拷贝返回的数据 3FEXTCODEHASH
addr
hashhash = address(addr).exists ? keccak256(address(addr).code) : 0指定地址的合约字节码的哈希,请参考EIP-1052事件 40BLOCKHASH
blockNumber
hashhash = block.blockHash(blockNumber)指定区块的哈希,仅适用于最近的256个区块,不包括当前区块 41COINBASE-
block.coinbaseblock.coinbase当前区块矿工的地址 42TIMESTAMP-
block.timestampblock.timestamp当前区块的UNIX时间戳,以秒为单位 43NUMBER-
block.numberblock.number当前区块号 44DIFFICULTY-
block.difficultyblock.difficulty当前区块难度 45GASLIMIT-
block.gaslimitblock.gaslimit当前区块GAS上限 46Invalid---- 47Invalid---- 48Invalid---- 49Invalid---- 4AInvalid---- 4BInvalid---- 4CInvalid---- 4DInvalid---- 4EInvalid---- 4FInvalid---- 50POP
_-POP()弹出栈顶(u)int256 并丢弃 51MLOAD
offset
valuevalue = memory[offset:offset+32]从内存读取一个(u)int256 52MSTORE
offsetvalue-memory[offset:offset+32] = value向内存写入一个(u)int256 53MSTORE8
offsetvalue-memory[offset] = value & 0xFF向内存写入一个uint8 54SLOAD
key
valuevalue = storage[key]从存储读取一个(u)int256 55SSTORE
keyvalue-storage[key] = value向存储写入一个(u)int256 56JUMP
destination-$pc = destination无条件跳转 57JUMPI
destinationcondition-$pc = cond ? destination : $pc + 1条件为真时跳转 58PC-
$pc$pc程序计数器 59MSIZE-
sizesize = MSIZE()当前合约执行的内存大小,以字节为单位 5AGAS-
gasRemaininggasRemaining = GAS()剩余的GAS 5BJUMPDEST--用于注解可能的跳转目标的元数据 5CInvalid---- 5DInvalid---- 5EInvalid---- 5FInvalid---- 60PUSH1-
uint8PUSH(uint8)将1字节数值压入栈 61PUSH2-
uint16PUSH(uint16)将2字节数值压入栈 62PUSH3-
uint24PUSH(uint24)将3字节数值压入栈 63PUSH4-
uint32PUSH(uint32)将4字节数值压入栈 64PUSH5-
uint40PUSH(uint40)将5字节数值压入栈 65PUSH6-
uint48PUSH(uint48)将6字节数值压入栈 66PUSH7-
uint56PUSH(uint56)将7字节数值压入栈 67PUSH8-
uint64PUSH(uint64)将8字节数值压入栈 68PUSH9-
uint72PUSH(uint72)将9字节数值压入栈 69PUSH10-
uint80PUSH(uint80)将10字节数值压入栈 6APUSH11-
uint88PUSH(uint88)将11字节数值压入栈 6BPUSH12-
uint96PUSH(uint96)将12字节数值压入栈 6CPUSH13-
uint104PUSH(uint104)将13字节数值压入栈 6DPUSH14-
uint112PUSH(uint112)将14字节数值压入栈 6EPUSH15-
uint120PUSH(uint120)将15字节数值压入栈 6FPUSH16-
uint128PUSH(uint128)将16字节数值压入栈 70PUSH17-
uint136PUSH(uint136)将17字节数值压入栈 71PUSH18-
uint144PUSH(uint144)将18字节数值压入栈 72PUSH19-
uint152PUSH(uint152)将19字节数值压入栈 73PUSH20-
uint160PUSH(uint160)将20字节数值压入栈 74PUSH21-
uint168PUSH(uint168)将21字节数值压入栈 75PUSH22-
uint176PUSH(uint176)将22字节数值压入栈 76PUSH23-
uint184PUSH(uint184)将23字节数值压入栈 77PUSH24-
uint192PUSH(uint192)将24字节数值压入栈 78PUSH25-
uint200PUSH(uint200)将25字节数值压入栈 79PUSH26-
uint208PUSH(uint208)将26字节数值压入栈 7APUSH27-
uint216PUSH(uint216)将27字节数值压入栈 7BPUSH28-
uint224PUSH(uint224)将28字节数值压入栈 7CPUSH29-
uint232PUSH(uint232)将29字节数值压入栈 7DPUSH30-
uint240PUSH(uint240)将30字节数值压入栈 7EPUSH31-
uint248PUSH(uint248)将31字节数值压入栈 7FPUSH32-
uint256PUSH(uint256)将32字节数值压入栈 80DUP1
value
valuevaluePUSH(value)克隆栈上最后一个值 81DUP2
_value
value_valuePUSH(value)克隆栈上倒数第二个值 82DUP3
__value
value__valuePUSH(value)克隆栈上倒数第三个值 83DUP4
___value
value___valuePUSH(value)克隆栈上倒数第四个值 84DUP5
...value
value...valuePUSH(value)克隆栈上倒数第五个值 85DUP6
...value
value...valuePUSH(value)克隆栈上倒数第六个值 86DUP7
...value
value...valuePUSH(value)克隆栈上倒数第七个值 87DUP8
...value
value...valuePUSH(value)克隆栈上倒数第八个值 88DUP9
...value
value...valuePUSH(value)克隆栈上倒数第九个值 89DUP10
...value
value...valuePUSH(value)克隆栈上倒数第10个值 8ADUP11
...value
value...valuePUSH(value)克隆栈上倒数第11个值 8BDUP12
...value
value...valuePUSH(value)克隆栈上倒数第12个值 8CDUP13
...value
value...valuePUSH(value)克隆栈上倒数第13个值 8DDUP14
...value
value...valuePUSH(value)克隆栈上倒数第14个值 8EDUP15
...value
value...valuePUSH(value)克隆栈上倒数第15个值 8FDUP16
...value
value...valuePUSH(value)克隆栈上倒数第16个值 90SWAP1
ab
baa, b = b, a交换栈顶两个成员 91SWAP2
a_b
b_aa, b = b, a交换栈顶与倒数第3个成员 92SWAP3
a__b
b__aa, b = b, a交换栈顶与倒数第4个成员 93SWAP4
a...b
b...aa, b = b, a交换栈顶与倒数第5个成员 94SWAP5
a...b
b...aa, b = b, a交换栈顶与倒数第6个成员 95SWAP6
a...b
b...aa, b = b, a交换栈顶与倒数第7个成员 96SWAP7
a...b
b...aa, b = b, a交换栈顶与倒数第8个成员 97SWAP8
a...b
b...aa, b = b, a交换栈顶与倒数第9个成员 98SWAP9
a...b
b...aa, b = b, a交换栈顶与倒数第10个成员 99SWAP10
a...b
b...aa, b = b, a交换栈顶与倒数第11个成员 9ASWAP11
a...b
b...aa, b = b, a交换栈顶与倒数第12个成员 9BSWAP12
a...b
b...aa, b = b, a交换栈顶与倒数第13个成员 9CSWAP13
a...b
b...aa, b = b, a交换栈顶与倒数第14个成员 9DSWAP14
a...b
b...aa, b = b, a交换栈顶与倒数第15个成员 9ESWAP15
a...b
b...aa, b = b, a交换栈顶与倒数第16个成员 9FSWAP16
a...b
b...aa, b = b, a交换栈顶与倒数第17个成员 A0LOG0
offsetlength-LOG0(memory[offset:offset+length])触发事件 A1LOG1
offsetlengthtopic0-LOG1(memory[offset:offset+length], topic0)触发事件 A2LOG2
offsetlengthtopic0topic1-LOG2(memory[offset:offset+length], topic0, topic1)触发事件 A3LOG3
offsetlengthtopic0topic1topic2-LOG3(memory[offset:offset+length], topic0, topic1, topic2)触发事件 A4LOG4
offsetlengthtopic0topic1topic2topic3-LOG4(memory[offset:offset+length], topic0, topic1, topic2, topic3)触发事件 A5Invalid---- A6Invalid---- A7Invalid---- A8Invalid---- A9Invalid---- AAInvalid---- ABInvalid---- ACInvalid---- ADInvalid---- AEInvalid---- AFInvalid---- B0PUSH--?????? B1DUP--?????? B2SWAP--?????? B3Invalid---- B4Invalid---- B5Invalid---- B6Invalid---- B7Invalid---- B8Invalid---- B9Invalid---- BAInvalid---- BBInvalid---- BCInvalid---- BDInvalid---- BEInvalid---- BFInvalid---- C0Invalid---- C1Invalid---- C2Invalid---- C3Invalid---- C4Invalid---- C5Invalid---- C6Invalid---- C7Invalid---- C8Invalid---- C9Invalid---- CAInvalid---- CBInvalid---- CCInvalid---- CDInvalid---- CEInvalid---- CFInvalid---- D0Invalid---- D1Invalid---- D2Invalid---- D3Invalid---- D4Invalid---- D5Invalid---- D6Invalid---- D7Invalid---- D8Invalid---- D9Invalid---- DAInvalid---- DBInvalid---- DCInvalid---- DDInvalid---- DEInvalid---- DFInvalid---- E0Invalid---- E1Invalid---- E2Invalid---- E3Invalid---- E4Invalid---- E5Invalid---- E6Invalid---- E7Invalid---- E8Invalid---- E9Invalid---- EAInvalid---- EBInvalid---- ECInvalid---- EDInvalid---- EEInvalid---- EFInvalid---- F0CREATE
valueoffsetlength
addraddr = new memory[offset:offset+length].value(value)创建子合约 F1CALL
gasaddrvalueargsOffsetargsLengthretOffsetretLength
successsuccess, memory[retOffset:retOffset+retLength] = address(addr).call.gas(gas).value(value) (memory[argsOffset:argsOffset+argsLength])调用另一个合约中的方法 F2CALLCODE
gasaddrvalueargsOffsetargsLengthretOffsetretLength
successsuccess, memory[retOffset:retOffset+retLength] = address(addr).callcode.gas(gas).value(value) (memory[argsOffset:argsOffset+argsLength])??? F3RETURN
offsetlength-return memory[offset:offset+length]从这个合约调用返回 F4DELEGATECALL
gasaddrargsOffsetargsLengthretOffsetretLength
successsuccess, memory[retOffset:retOffset+retLength] = address(addr).delegatecall.gas(gas) (memory[argsOffset:argsOffset+argsLength])使用当前合约的存储调用另一个合约的方法 F5CREATE2
valueoffsetlengthsalt
addraddr = new memory[offset:offset+length].value(value)使用确定的地址创建子合约,参见see EIP-1014 F6Invalid---- F7Invalid---- F8Invalid---- F9Invalid---- FASTATICCALL
gasaddrargsOffsetargsLengthretOffsetretLength
successsuccess, memory[retOffset:retOffset+retLength] = address(addr).staticcall.gas(gas) (memory[argsOffset:argsOffset+argsLength])调用另一个合约的方法,不允许合约创建、事件发送、存储修改和合约销毁等引起状态改变的方法,参见EIP-214 FBInvalid---- FCInvalid---- FDREVERT
offsetlength-revert(memory[offset:offset+length])回滚交易并返回数据 FEInvalid---- FFSELFDESTRUCT
addr-selfdestruct(address(addr))销毁合约并将所有资金发送给addr地址
原文链接:EVM操作码速查 — 汇智网