There are several opcodes that expand memory:
opcode |
off1 |
len1 |
off2 |
len2 |
Pure Memory Gas Cost |
---|---|---|---|---|---|
32 SHA3 |
stack[sp] |
stack[sp+1] |
|||
55 CALLDATACOPY |
stack[sp] |
stack[sp+2] |
|||
57 CODECOPY |
stack[sp] |
stack[sp+2] |
|||
60 EXTCODECOPY |
stack[sp+1] |
stack[sp+3] |
|||
62 RETURNDATACOPY |
stack[sp] |
stack[sp+2] |
|||
81 MLOAD |
stack[sp] |
32 |
Yes | ||
82 MSTORE |
stack[sp] |
32 |
Yes | ||
83 MSTORE8 |
stack[sp] |
1 |
Yes | ||
160 LOG0 |
stack[sp] |
stack[sp+1] |
|||
161 LOG1 |
stack[sp] |
stack[sp+1] |
|||
162 LOG2 |
stack[sp] |
stack[sp+1] |
|||
163 LOG3 |
stack[sp] |
stack[sp+1] |
|||
164 LOG4 |
stack[sp] |
stack[sp+1] |
|||
240 CREATE |
stack[sp+1] |
stack[sp+2] |
Yes | ||
241 CALL |
stack[sp+3] |
stack[sp+4] |
stack[sp+5] |
stack[sp+6] |
|
242 CALLCODE |
stack[sp+3] |
stack[sp+4] |
stack[sp+5] |
stack[sp+6] |
|
243 RETURN |
stack[sp] |
stack[sp+1] |
Yes | ||
244 DELEGATECALL |
stack[sp+2] |
stack[sp+3] |
stack[sp+4] |
stack[sp+5] |
|
245 CREATE2 |
stack[sp+1] |
stack[sp+2] |
|||
250 STATICCALL |
stack[sp+2] |
stack[sp+3] |
stack[sp+4] |
stack[sp+5] |
|
253 REVERT |
stack[sp] |
stack[sp+1] |
Yes |
The psuedo code of memory expansion:
from math import ceil
MEMORY_SIZE_MAX = 32 * (2**32 - 1) # 0x1FFFFFFFE0
# returns (new_memory_size, is_overflow)
def expand_memory(memory_size: int, off1: int, len1: int, off2: int, len2: int) -> int, bool:
def m(off: int, len: int) -> int, bool:
if len is None or len == 0:
return 0, False
new_memory_size = 32 * ceil(off + len / 32)
if new_memory_size > MEMORY_SIZE_MAX:
return 0, True
return new_memory_size, False
new_memory_size1, is_overflow1 = m(off1, len1)
new_memory_size2, is_overflow2 = m(off2, len2)
if is_overflow1 or is_overflow2:
return 0, True
return max(memory_size, new_memory_size1, new_memory_size2), False
Ideally we can verify the OOG caused only by memory expansion in a single branch, by lookup the offset and length in stack position with the opcode, and verify the new memory size indeed exceeds the max value.
To support EIP2929, we add 2 extra revertable targets tx_access_list_account
and tx_access_list_storage_slot
in State circuit to maintain, their value is constraint to only be 0
or 1
, and lazily initialied to be 0
. Then in EVM circuit, we will always write it to 1
, and see the different between current and previous value to see if it's a warm or cold access.
address | name | execution result | gas cost |
---|---|---|---|
0x01 |
ECRecover |
|
3000 |
0x02 |
Sha256 |
|
60 + 12*ceil(len(input)/32) |
0x03 |
Ripemd160 |
|
600 + 120*ceil(len(input)/32) |
0x04 |
DataCopy |
|
15 + 3*ceil(len(input)/32) |
0x05 |
BigModExp |
|
see EIP2565 |
0x06 |
BN254Add |
|
150 |
0x07 |
BN254Mul |
|
6000 |
0x08 |
BN254Pairing |
|
45000 + 34000*(len(input)/192) |
0x09 |
Blake2F |
|
be_to_int(input[:4]) |
Note: If we are reading returndata directly from last callee's memory, we need to be careful when handling precompile 0x04
DataCopy since the last callee would be caller itself. We should explicitly copy calldata to precompile's memory and treat it as a call.
SELFDESTRUCT
NeutralizationThe
SELFDESTRUCT
opcode is renamed toSENDALL
, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce
quotes from <a href="https://notes.ethereum.org/-fJSOrnYQl-mqoWKpaTIsQ#Miscellaneous">Statelessness gas cost changes EIP draft #Miscellaneous</a>
If so, we can get rid of a bunch of consideration of the destroy of code and storage.
After EIP3670
- EOF - Code Validation:
CREATE*
becomes more complicated, we need to verify creation code and also returned runtime code.PUSH*
data padding, implicit STOP
in the end).After EIP4200
- Static relative jumps:
JUMP*
becomes easier since no any invalid jump at runtime.EIP4488
- Transaction calldata gas cost reduction with total calldata limitEIP4488
specification:
Parameter | Value |
---|---|
NEW_CALLDATA_GAS_COST |
3 |
BASE_MAX_CALLDATA_PER_BLOCK |
1,048,576 |
CALLDATA_PER_TX_STIPEND |
300 |
Reduce the gas cost of transaction calldata to NEW_CALLDATA_GAS_COST
per byte, regardless of whether the byte is zero or nonzero.
Add a rule that a block is only valid if sum(len(tx.calldata) for tx in block.txs) <= BASE_MAX_CALLDATA_PER_BLOCK + len(block.txs) * CALLDATA_PER_TX_STIPEND
.