EVM (Ethereum Virtual Machine)
Table of Contents
1. EVM
EVM (Ethereum Virtual Machine) 是基于栈的虚拟机。在 EVM 虚拟环境中没有寄存器。比如,指令 ADD,它会从栈上弹出两个元素作为参数,计算完它们的和后把结果保存到栈顶中。
2. 虚拟机指令
EVM 中所有指令都是一个字节,所以最多有 256 个指令。目前,已经定义了约 140 多个指令,如图 1(摘自:The Ethereum Virtual Machine — How does it work?)所示。
Figure 1: EVM Opcodes
2.1. 指令一览
为了防止 DoS 攻击,几乎所有指令都会消耗 Gas,指令及相关说明如表 1 所示。
Opcode | Mnemonic | Description | Ins | Outs | Gas | Extra Info |
---|---|---|---|---|---|---|
0x00 | STOP | Halts execution | 0 | 0 | 0 | - |
0x01 | ADD | Addition operation | 2 | 1 | 3 | - |
0x02 | MUL | Multiplication operation | 2 | 1 | 5 | - |
0x03 | SUB | Subtraction operation | 2 | 1 | 3 | - |
0x04 | DIV | Integer division operation | 2 | 1 | 5 | - |
0x05 | SDIV | Signed integer division operation (truncated) | 2 | 1 | 5 | - |
0x06 | MOD | Modulo remainder operation | 2 | 1 | 5 | - |
0x07 | SMOD | Signed modulo remainder operation | 2 | 1 | 5 | - |
0x08 | ADDMOD | Modulo addition operation | 3 | 1 | 8 | - |
0x09 | MULMOD | Modulo multiplication operation | 3 | 1 | 8 | - |
0x0a | EXP | Exponential operation | 2 | 1 | 10* | - |
0x0b | SIGNEXTEND | Extend length of two's complement signed integer | 2 | 1 | 5 | - |
0x0c - 0x0f | Unused | Unused | - | |||
0x10 | LT | Less-than comparison | 2 | 1 | 3 | - |
0x11 | GT | Greater-than comparison | 2 | 1 | 3 | - |
0x12 | SLT | Signed less-than comparison | 2 | 1 | 3 | - |
0x13 | SGT | Signed greater-than comparison | 2 | 1 | 3 | - |
0x14 | EQ | Equality comparison | 2 | 1 | 3 | - |
0x15 | ISZERO | Simple not operator | 2 | 1 | 3 | - |
0x16 | AND | Bitwise AND operation | 2 | 1 | 3 | - |
0x17 | OR | Bitwise OR operation | 2 | 1 | 3 | - |
0x18 | XOR | Bitwise XOR operation | 2 | 1 | 3 | - |
0x19 | NOT | Bitwise NOT operation | 2 | 1 | 3 | - |
0x1a | BYTE | Retrieve single byte from word | 2 | 1 | 3 | - |
0x1b | SHL | Shift Left | 3 | EIP145 | ||
0x1c | SHR | Logical Shift Right | 3 | EIP145 | ||
0x1d | SAR | Arithmetic Shift Right | 3 | EIP145 | ||
0x20 | SHA3 | Compute Keccak-256 hash | 2 | 1 | 30* | - |
0x21 - 0x2f | Unused | Unused | ||||
0x30 | ADDRESS | Get address of currently executing account | 0 | 1 | 2 | - |
0x31 | BALANCE | Get balance of the given account | 1 | 1 | 400 | - |
0x32 | ORIGIN | Get execution origination address | 0 | 1 | 2 | - |
0x33 | CALLER | Get caller address | 0 | 1 | 2 | - |
0x34 | CALLVALUE | Get deposited value by the instruction/transaction responsible for this execution | 0 | 1 | 2 | - |
0x35 | CALLDATALOAD | Get input data of current environment | 1 | 1 | 3 | - |
0x36 | CALLDATASIZE | Get size of input data in current environment | 0 | 1 | 2* | - |
0x37 | CALLDATACOPY | Copy input data in current environment to memory | 3 | 0 | 3 | - |
0x38 | CODESIZE | Get size of code running in current environment | 0 | 1 | 2 | - |
0x39 | CODECOPY | Copy code running in current environment to memory | 3 | 0 | 3* | - |
0x3a | GASPRICE | Get price of gas in current environment | 0 | 1 | 2 | - |
0x3b | EXTCODESIZE | Get size of an account's code | 1 | 1 | 700 | - |
0x3c | EXTCODECOPY | Copy an account's code to memory | 4 | 0 | 700* | - |
0x3d | RETURNDATASIZE | Pushes the size of the return data buffer onto the stack | 0 | 1 | 2 | EIP211 |
0x3e | RETURNDATACOPY | Copies data from the return data buffer to memory | 3 | 0 | 3 | EIP211 |
0x3f | Unused | - | ||||
0x40 | BLOCKHASH | Get the hash of one of the 256 most recent complete blocks | 1 | 1 | 20 | - |
0x41 | COINBASE | Get the block's beneficiary address | 0 | 1 | 2 | - |
0x42 | TIMESTAMP | Get the block's timestamp | 0 | 1 | 2 | - |
0x43 | NUMBER | Get the block's number | 0 | 1 | 2 | - |
0x44 | DIFFICULTY | Get the block's difficulty | 0 | 1 | 2 | - |
0x45 | GASLIMIT | Get the block's gas limit | 0 | 1 | 2 | - |
0x46 - 0x4f | Unused | - | ||||
0x50 | POP | Remove word from stack | 1 | 0 | 2 | - |
0x51 | MLOAD | Load word from memory | 1 | 1 | 3* | - |
0x52 | MSTORE | Save word to memory | 2 | 0 | 3* | - |
0x53 | MSTORE8 | Save byte to memory | 2 | 0 | 3 | - |
0x54 | SLOAD | Load word from storage | 1 | 1 | 200 | - |
0x55 | SSTORE | Save word to storage | 2 | 0 | 20000* | - |
0x56 | JUMP | Alter the program counter | 1 | 0 | 8 | - |
0x57 | JUMPI | Conditionally alter the program counter | 2 | 0 | 10 | - |
0x58 | GETPC | Get the value of the program counter prior to the increment | 0 | 1 | 2 | - |
0x59 | MSIZE | Get the size of active memory in bytes | 0 | 1 | 2 | - |
0x5a | GAS | Get the amount of available gas, including the corresponding reduction the amount of available gas | 0 | 1 | 2 | - |
0x5b | JUMPDEST | Mark a valid destination for jumps | 0 | 0 | 1 | - |
0x5c - 0x5f | Unused | - | ||||
0x60 | PUSH1 | Place 1 byte item on stack | 0 | 1 | 3 | - |
0x61 | PUSH2 | Place 2-byte item on stack | 0 | 1 | 3 | - |
0x62 | PUSH3 | Place 3-byte item on stack | 0 | 1 | 3 | - |
0x63 | PUSH4 | Place 4-byte item on stack | 0 | 1 | 3 | - |
0x64 | PUSH5 | Place 5-byte item on stack | 0 | 1 | 3 | - |
0x65 | PUSH6 | Place 6-byte item on stack | 0 | 1 | 3 | - |
0x66 | PUSH7 | Place 7-byte item on stack | 0 | 1 | 3 | - |
0x67 | PUSH8 | Place 8-byte item on stack | 0 | 1 | 3 | - |
0x68 | PUSH9 | Place 9-byte item on stack | 0 | 1 | 3 | - |
0x69 | PUSH10 | Place 10-byte item on stack | 0 | 1 | 3 | - |
0x6a | PUSH11 | Place 11-byte item on stack | 0 | 1 | 3 | - |
0x6b | PUSH12 | Place 12-byte item on stack | 0 | 1 | 3 | - |
0x6c | PUSH13 | Place 13-byte item on stack | 0 | 1 | 3 | - |
0x6d | PUSH14 | Place 14-byte item on stack | 0 | 1 | 3 | - |
0x6e | PUSH15 | Place 15-byte item on stack | 0 | 1 | 3 | - |
0x6f | PUSH16 | Place 16-byte item on stack | 0 | 1 | 3 | - |
0x70 | PUSH17 | Place 17-byte item on stack | 0 | 1 | 3 | - |
0x71 | PUSH18 | Place 18-byte item on stack | 0 | 1 | 3 | - |
0x72 | PUSH19 | Place 19-byte item on stack | 0 | 1 | 3 | - |
0x73 | PUSH20 | Place 20-byte item on stack | 0 | 1 | 3 | - |
0x74 | PUSH21 | Place 21-byte item on stack | 0 | 1 | 3 | - |
0x75 | PUSH22 | Place 22-byte item on stack | 0 | 1 | 3 | - |
0x76 | PUSH23 | Place 23-byte item on stack | 0 | 1 | 3 | - |
0x77 | PUSH24 | Place 24-byte item on stack | 0 | 1 | 3 | - |
0x78 | PUSH25 | Place 25-byte item on stack | 0 | 1 | 3 | - |
0x79 | PUSH26 | Place 26-byte item on stack | 0 | 1 | 3 | - |
0x7a | PUSH27 | Place 27-byte item on stack | 0 | 1 | 3 | - |
0x7b | PUSH28 | Place 28-byte item on stack | 0 | 1 | 3 | - |
0x7c | PUSH29 | Place 29-byte item on stack | 0 | 1 | 3 | - |
0x7d | PUSH30 | Place 30-byte item on stack | 0 | 1 | 3 | - |
0x7e | PUSH31 | Place 31-byte item on stack | 0 | 1 | 3 | - |
0x7f | PUSH32 | Place 32-byte (full word) item on stack | 0 | 1 | 3 | - |
0x80 | DUP1 | Duplicate 1st stack item | 1 | 2 | 3 | - |
0x81 | DUP2 | Duplicate 2nd stack item | 2 | 3 | 3 | - |
0x82 | DUP3 | Duplicate 3rd stack item | 3 | 4 | 3 | - |
0x83 | DUP4 | Duplicate 4th stack item | 4 | 5 | 3 | - |
0x84 | DUP5 | Duplicate 5th stack item | 5 | 6 | 3 | - |
0x85 | DUP6 | Duplicate 6th stack item | 6 | 7 | 3 | - |
0x86 | DUP7 | Duplicate 7th stack item | 7 | 8 | 3 | - |
0x87 | DUP8 | Duplicate 8th stack item | 8 | 9 | 3 | - |
0x88 | DUP9 | Duplicate 9th stack item | 9 | 10 | 3 | - |
0x89 | DUP10 | Duplicate 10th stack item | 10 | 11 | 3 | - |
0x8a | DUP11 | Duplicate 11th stack item | 11 | 12 | 3 | - |
0x8b | DUP12 | Duplicate 12th stack item | 12 | 13 | 3 | - |
0x8c | DUP13 | Duplicate 13th stack item | 13 | 14 | 3 | - |
0x8d | DUP14 | Duplicate 14th stack item | 14 | 15 | 3 | - |
0x8e | DUP15 | Duplicate 15th stack item | 15 | 16 | 3 | - |
0x8f | DUP16 | Duplicate 16th stack item | 16 | 17 | 3 | - |
0x90 | SWAP1 | Exchange 1st and 2nd stack items | 2 | 2 | 3 | - |
0x91 | SWAP2 | Exchange 1st and 3rd stack items | 3 | 3 | 3 | - |
0x92 | SWAP3 | Exchange 1st and 4th stack items | 4 | 4 | 3 | - |
0x93 | SWAP4 | Exchange 1st and 5th stack items | 5 | 5 | 3 | - |
0x94 | SWAP5 | Exchange 1st and 6th stack items | 6 | 6 | 3 | - |
0x95 | SWAP6 | Exchange 1st and 7th stack items | 7 | 7 | 3 | - |
0x96 | SWAP7 | Exchange 1st and 8th stack items | 8 | 8 | 3 | - |
0x97 | SWAP8 | Exchange 1st and 9th stack items | 9 | 9 | 3 | - |
0x98 | SWAP9 | Exchange 1st and 10th stack items | 10 | 10 | 3 | - |
0x99 | SWAP10 | Exchange 1st and 11th stack items | 11 | 11 | 3 | - |
0x9a | SWAP11 | Exchange 1st and 12th stack items | 12 | 12 | 3 | - |
0x9b | SWAP12 | Exchange 1st and 13th stack items | 13 | 13 | 3 | - |
0x9c | SWAP13 | Exchange 1st and 14th stack items | 14 | 14 | 3 | - |
0x9d | SWAP14 | Exchange 1st and 15th stack items | 15 | 15 | 3 | - |
0x9e | SWAP15 | Exchange 1st and 16th stack items | 16 | 16 | 3 | - |
0x9f | SWAP16 | Exchange 1st and 17th stack items | 17 | 17 | 3 | - |
0xa0 | LOG0 | Append log record with no topics | 2 | 0 | 375 | - |
0xa1 | LOG1 | Append log record with one topic | 3 | 0 | 750 | - |
0xa2 | LOG2 | Append log record with two topics | 4 | 0 | 1125 | - |
0xa3 | LOG3 | Append log record with three topics | 5 | 0 | 1500 | - |
0xa4 | LOG4 | Append log record with four topics | 6 | 0 | 1875 | - |
0xa5 - 0xaf | Unused | - | ||||
0xb0 | JUMPTO | Tentative libevmasm has different numbers | EIP615 | |||
0xb1 | JUMPIF | Tentative | EIP615 | |||
0xb2 | JUMPSUB | Tentative | EIP615 | |||
0xb4 | JUMPSUBV | Tentative | EIP615 | |||
0xb5 | BEGINSUB | Tentative | EIP615 | |||
0xb6 | BEGINDATA | Tentative | EIP615 | |||
0xb8 | RETURNSUB | Tentative | EIP615 | |||
0xb9 | PUTLOCAL | Tentative | EIP615 | |||
0xba | GETLOCAL | Tentative | EIP615 | |||
0xbb - 0xe0 | Unused | - | ||||
0xe1 | SLOADBYTES | Only referenced in pyethereum | 3 | 0 | - | - |
0xe2 | SSTOREBYTES | Only referenced in pyethereum | 3 | 0 | - | - |
0xe3 | SSIZE | Only referenced in pyethereum | 1 | 1 | - | - |
0xe4 - 0xef | Unused | - | ||||
0xf0 | CREATE | Create a new account with associated code | 3 | 1 | 32000 | - |
0xf1 | CALL | Message-call into an account | 7 | 1 | Complicated | - |
0xf2 | CALLCODE | Message-call into this account with alternative account's code | 7 | 1 | Complicated | - |
0xf3 | RETURN | Halt execution returning output data | 2 | 0 | 0 | - |
0xf4 | DELEGATECALL | Message-call into this account with an alternative account's code, but persisting into this account with an alternative account's code | 6 | 1 | Complicated | - |
0xf5 | CREATE2 | Create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160 | - | |||
0xf6 - 0xf9 | Unused | - | - | |||
0xfa | STATICCALL | Similar to CALL, but does not modify state | 6 | 1 | 40 | - |
0xfb - 0xfc | Unused | - | - | - | ||
0xfd | REVERT | Stop execution and revert state changes, without consuming all provided gas and providing a reason | 2 | 0 | 0 | - |
0xfe | INVALID | Designated invalid instruction | 0 | - | ||
0xff | SELFDESTRUCT | Halt execution and register account for later deletion | 1 | 0 | 5000* | - |
表 1 中,Ins 列表示当前指令会从栈中读取多少个参数;Outs 列表示当前指令会向栈中写入多少个参数;Gas 列表示其消耗的 Gas,如果 Gas 数旁边标记有星号则表示 Gas 消耗根据参数的不同而不同。
2.2. 退还 Gas 的操作
下面两个操作会“退还” Gas:
- 把 storage 的值从“非零值”设置为“零”(SSTORE 指令);
- 销毁合约的 SELFDESTRUCT 指令(注:在伦敦硬分叉之后,SELFDESTRUCT 指令不再退还 Gas,详情可参考 EIP-3529)。
2.3. 反编译实例
使用 evm 的 disasm
子命令可以反编译二进制文件,如:
$ echo "6005600401" > code.txt $ evm disasm code.txt 6005600401 00000: PUSH1 0x05 00002: PUSH1 0x04 00004: ADD
使用 evm 的 run
子命令可以执行二进制,指定 --debug
选项的话会输出栈中的内容:
$ evm --code 6005600401 --debug run 0x #### TRACE #### PUSH1 pc=00000000 gas=10000000000 cost=3 PUSH1 pc=00000002 gas=9999999997 cost=3 Stack: 00000000 0000000000000000000000000000000000000000000000000000000000000005 ADD pc=00000004 gas=9999999994 cost=3 Stack: 00000000 0000000000000000000000000000000000000000000000000000000000000004 00000001 0000000000000000000000000000000000000000000000000000000000000005 STOP pc=00000005 gas=9999999991 cost=0 Stack: 00000000 0000000000000000000000000000000000000000000000000000000000000009 #### LOGS ####
从上面输出中,可以看到栈顶内容为 9 。
3. 参考
https://ethervm.io/ (形象地介绍了每个指令的含义)
https://github.com/ethereumbook/ethereumbook/blob/develop/13evm.asciidoc (《精通以太坊》一书中关于虚拟机的章节)