An immutable program code ROM, loaded with the bytecode of the smart contract to be executed
A volatile memory, with every location explicitly initialized to zero
A permanent storage that is part of the Ethereum state, also zero-initialized
Arithmetic and bitwise logic operations
Execution context inquiries
Stack, memory, and storage access
Control flow operations
Logging, calling, and other operators
Arithmetic opcode instructions:
ADD //Add the top two stack items MUL //Multiply the top two stack items SUB //Subtract the top two stack items DIV //Integer division SDIV //Signed integer division MOD //Modulo (remainder) operation SMOD //Signed modulo operation ADDMOD //Addition modulo any number MULMOD //Multiplication modulo any number EXP //Exponential operation SIGNEXTEND //Extend the length of a two's complement signed integer SHA3 //Compute the Keccak-256 hash of a block of memory
Note that all arithmetic is performed modulo 2256 (unless otherwise noted), and that the zeroth power of zero, 00, is taken to be 1.
Stack, memory, and storage management instructions:
POP //Remove the top item from the stack
MLOAD //Load a word from memory
MSTORE //Save a word to memory
MSTORE8 //Save a byte to memory
SLOAD //Load a word from storage
SSTORE //Save a word to storage
MSIZE //Get the size of the active memory in bytes
PUSHx //Place x byte item on the stack, where x can be any integer from
// 1 to 32 (full word) inclusive
DUPx //Duplicate the x-th stack item, where x can be any integer from
// 1 to 16 inclusive
SWAPx //Exchange 1st and (x+1)-th stack items, where x can be any
// integer from 1 to 16 inclusive
Instructions for control flow:
STOP //Halt execution
JUMP //Set the program counter to any value
JUMPI //Conditionally alter the program counter
PC //Get the value of the program counter (prior to the increment
//corresponding to this instruction)
JUMPDEST //Mark a valid destination for jumps
Opcodes for the system executing the program:
LOGx //Append a log record with x topics, where x is any integer
//from 0 to 4 inclusive
CREATE //Create a new account with associated code
CALL //Message-call into another account, i.e. run another
//account's code
CALLCODE //Message-call into this account with another
//account's code
RETURN //Halt execution and return output data
DELEGATECALL //Message-call into this account with an alternative
//account's code, but persisting the current values for
//sender and value
STATICCALL //Static message-call into an account
REVERT //Halt execution, reverting state changes but returning
//data and remaining gas
INVALID //The designated invalid instruction
SELFDESTRUCT //Halt execution and register account for deletion
Opcodes for comparisons and bitwise logic:
LT //Less-than comparison GT //Greater-than comparison SLT //Signed less-than comparison SGT //Signed greater-than comparison EQ //Equality comparison ISZERO //Simple NOT operator AND //Bitwise AND operation OR //Bitwise OR operation XOR //Bitwise XOR operation NOT //Bitwise NOT operation BYTE //Retrieve a single byte from a full-width 256-bit word
Opcodes dealing with execution environment information:
GAS //Get the amount of available gas (after the reduction for
//this instruction)
ADDRESS //Get the address of the currently executing account
BALANCE //Get the account balance of any given account
ORIGIN //Get the address of the EOA that initiated this EVM
//execution
CALLER //Get the address of the caller immediately responsible
//for this execution
CALLVALUE //Get the ether amount deposited by the caller responsible
//for this execution
CALLDATALOAD //Get the input data sent by the caller responsible for
//this execution
CALLDATASIZE //Get the size of the input data
CALLDATACOPY //Copy the input data to memory
CODESIZE //Get the size of code running in the current environment
CODECOPY //Copy the code running in the current environment to
//memory
GASPRICE //Get the gas price specified by the originating
//transaction
EXTCODESIZE //Get the size of any account's code
EXTCODECOPY //Copy any account's code to memory
RETURNDATASIZE //Get the size of the output data from the previous call
//in the current environment
RETURNDATACOPY //Copy data output from the previous call to memory
Opcodes for accessing information on the current block:
BLOCKHASH //Get the hash of one of the 256 most recently completed
//blocks
COINBASE //Get the block's beneficiary address for the block reward
TIMESTAMP //Get the block's timestamp
NUMBER //Get the block's number
DIFFICULTY //Get the block's difficulty
GASLIMIT //Get the block's gas limit
Compiling a Solidity source file to EVM bytecode can be accomplished via several methods. In Chapter 2 we used the online Remix compiler. In this chapter, we will use the solc executable at the command line. For a list of options, run the following command:
$ solc --help
Generating the raw opcode stream of a Solidity source file is easily achieved with the --opcodes command-line option. This opcode stream leaves out some information (the --asm option produces the full information), but it is sufficient for this discussion. For example, compiling an example Solidity file, Example.sol, and sending the opcode output into a directory named BytecodeDir is accomplished with the following command:
$ solc -o BytecodeDir --opcodes Example.sol
$ solc -o BytecodeDir --asm Example.sol
The following command will produce the bytecode binary for our example program:
$ solc -o BytecodeDir --bin Example.sol
pragma solidity^0.4.19;contractexample{addresscontractOwner;functionexample(){contractOwner=msg.sender;}}
PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP PUSH1 0x35 DUP1 PUSH1 0x5B PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 JUMP 0xb9 SWAP14 0xcb 0x1e 0xdd RETURNDATACOPY 0xec 0xe0 0x1f 0x27 0xc9 PUSH5 0x9C5ABCC14A NUMBER 0x5e INVALID EXTCODESIZE 0xdb 0xcf EXTCODESIZE 0x27 EXTCODESIZE 0xe2 0xb8 SWAP10 0xed 0x
/* "Example.sol":26:132 contract example {... */mstore(0x40,0x60)/* "Example.sol":74:130 function example() {... */jumpi(tag_1,iszero(callvalue))0x0dup1reverttag_1:/* "Example.sol":115:125 msg.sender */caller/* "Example.sol":99:112 contractOwner */0x0dup1/* "Example.sol":99:125 contractOwner = msg.sender */0x100expdup2sloaddup20xffffffffffffffffffffffffffffffffffffffffmulnotandswap1dup40xffffffffffffffffffffffffffffffffffffffffandmulorswap1sstorepop/* "Example.sol":26:132 contract example {... */dataSize(sub_0)dup1dataOffset(sub_0)0x0codecopy0x0returnstopsub_0:assembly{/* "Example.sol":26:132 contract example {... */mstore(0x40,0x60)0x0dup1revertauxdata:0xa165627a7a7230582056b99dcb1edd3eece01f27c9649c5abcc14a435efe3b...}
60606040523415600e57600080fd5b336000806101000a81548173 ffffffffffffffffffffffffffffffffffffffff 021916908373 ffffffffffffffffffffffffffffffffffffffff 160217905550603580605b6000396000f3006060604052600080fd00a165627a7a7230582056b...
PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE
PUSH32 0x436f6e67726174756c6174696f6e732120536f6f6e20746f206d617374657221
// Version of Solidity compiler this program was written forpragma solidity^0.4.19;// Our first contract is a faucet!contractFaucet{// Give out ether to anyone who asksfunctionwithdraw(uintwithdraw_amount)public{// Limit withdrawal amountrequire(withdraw_amount<=100000000000000000);// Send the amount to the address that requested itmsg.sender.transfer(withdraw_amount);}// Accept any incoming amountfunction()publicpayable{}}
Porosity is a popular open source decompiler.
Ethersplay is an EVM plug-in for Binary Ninja, a disassembler.
IDA-Evm is an EVM plugin for IDA, another disassembler.
PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3f JUMPI
| Stack |
|---|
<length of calldata from tx> |
|
keccak256("withdraw(uint256)") = 0x2e1a7d4d...
| Stack |
|---|
|
|
jumpi(label, cond) // Jump to "label" if "cond" is true
PUSH1 0x0 CALLDATALOAD PUSH29 0x1000000... SWAP1 DIV PUSH4 0xffffffff AND DUP1 PUSH4 0x2e1a7d4d EQ PUSH1 0x41 JUMPI
calldataload(p) //load 32 bytes of calldata starting from byte position p
| Stack |
|---|
|
<32 bytes of calldata starting at byte 0> |
| Stack |
|---|
<32 bytes of calldata starting at byte 0> |
|
div(x, y) // integer division x / y
| Stack |
|---|
<function identifier sent in |
| Stack |
|---|
|
<function identifier sent in |
<function identifier sent in |
| Stack |
|---|
|
<function identifier sent in |
| Stack |
|---|
|
|
function identifier sent in |
Adding two numbers costs 3 gas
Calculating a Keccak-256 hash costs 30 gas + 6 gas for each 256 bits of data being hashed
Sending a transaction costs 21,000 gas
miner fee = gas cost * gas price
remaining gas = gas limit - gas cost refunded ether = remaining gas * gas price
transaction fee = total gas used * gas price paid (in ether)
Gas cost is the number of units of gas required to perform a particular operation.
Gas price is the amount of ether you are willing to pay per unit of gas when you send your transaction to the Ethereum network.
Deleting a contract (SELFDESTRUCT) is worth a refund of 24,000 gas.
Changing a storage address from a nonzero value to zero (SSTORE[x] = 0) is worth a refund of 15,000 gas.