---
tags: zkevm
---
<style>
table {
white-space: nowrap;
}
table ul {
padding-left: 0.75em !important;
margin-bottom: 0 !important;
}
</style>
# ZKEVM - Random Thoughts
## EVM Caveat
### Memory Expansion
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:
```python
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.
### Access List (EIP2929)
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.
### Precompiles
| address | name | execution result | gas cost |
| ------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `0x01` | ECRecover | <ul><li>`ECRECOVER`</li><li>`ERROR_OOG_CONSTANT`</li></ul> | `3000` |
| `0x02` | Sha256 | <ul><li>`SHA256`</li><li>`ERROR_OOG_SHA256`</li></ul> | `60 + 12*ceil(len(input)/32)` |
| `0x03` | Ripemd160 | <ul><li>`RIPEMD160`</li><li>`ERROR_OOG_RIPEMD160`</li></ul> | `600 + 120*ceil(len(input)/32)` |
| `0x04` | DataCopy | <ul><li>`DATA_COPY`</li><li>`ERROR_OOG_DATA_COPY`</li></ul> | `15 + 3*ceil(len(input)/32)` |
| `0x05` | BigModExp | <ul><li>`BIG_MOD_EXP`</li><li>`ERROR_OOG_BIG_MOD_EXP`</li></ul> | see [EIP2565](https://eips.ethereum.org/EIPS/eip-2565#specification) |
| `0x06` | BN254Add | <ul><li>`BN254_ADD`</li><li>`ERROR_OOG_CONSTANT`</li><li>`ERROR_INVALID_INPUT_BN254_ADD (Invalid G1)`</li></ul> | `150` |
| `0x07` | BN254Mul | <ul><li>`BN254_MUL`</li><li>`ERROR_OOG_CONSTANT`</li><li>`ERROR_INVALID_INPUT_BN254_MUL (Invalid G1)`</li></ul> | `6000` |
| `0x08` | BN254Pairing | <ul><li>`BN254_PAIRING`</li><li>`ERROR_OOG_BN254_PAIRING`</li><li>`ERROR_INVALID_INPUT_BN254_PAIRING (Invalid length, G1, G2)`</li></ul> | `45000 + 34000*(len(input)/192)` |
| `0x09` | Blake2F | <ul><li>`BLAKE2F`</li><li>`ERROR_OOG_BLAKE2F`</li><li>`ERROR_INVALID_INPUT_BLAKE2F (Invalid length)`</li></ul> | `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.
## Possible Upgrade of EVM
### `SELFDESTRUCT` Neutralization
> The `SELFDESTRUCT` opcode is renamed to `SENDALL`, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce
> [name=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.
#### Reference
- [Statelessness gas cost changes EIP draft](https://notes.ethereum.org/-fJSOrnYQl-mqoWKpaTIsQ)
### EOF - EVM Object Format
After [`EIP3670` - EOF - Code Validation](https://eips.ethereum.org/EIPS/eip-3670):
- Create transaction, `CREATE*` becomes more complicated, we need to verify creation code and also returned runtime code.
- Opcode lookup becomes easier since no any implict behavior (implict `PUSH*` data padding, implicit `STOP` in the end).
After [`EIP4200` - Static relative jumps](https://eips.ethereum.org/EIPS/eip-4200):
- `JUMP*` becomes easier since no any invalid jump at runtime.
#### Reference
- [Everything about the EVM Object Format (EOF)](https://notes.ethereum.org/@ipsilon/evm-object-format-overview)
- [The future of EVM - EVM Object Format](https://axic.github.io/notes/liscon_eof/#/)
### Statelessness
- Merkle (Hexary) Patricia Trie becomes Verkle Trie
- Trie-access-aware gas cost calculation
- Chunkified contract bytecode
#### Reference
- [Ethereum statelessness roadmap](https://notes.ethereum.org/Yn_mwNa2SeeQHnKsRgekKg)
### `EIP4488` - Transaction calldata gas cost reduction with total calldata limit
[`EIP4488`](https://github.com/ethereum/EIPs/pull/4488) 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`.
## Misc
- [Sparse Representation Trick on Keccak](/4gOBP_loQ7GXXWsaCqcovA)
- [Multi-Limbs Multiplication](/HL0QhGUeQoSgIBt2el6fHA)
- [ZKEVM - Call and Call State](/GT5-oa__SbedW1W2o4YJBw)
- [ZKEVM - EVM Circuit Layout and Branching](/QmmGsuVpRUmPDT0Pvr8VIw)
- [ZKEVM - EVM Circuit Design Rationale](/N82vVLWjSp6fZewlOTj1WQ)
- [ZKEVM - Consideration on Transaction](/MmXpxgW2TbiMa2EUDeRbaQ)
- [ZKEVM - RLP Encoding](/yTirDRdATVmB4wjECHMebg)
- [ZKEVM - State Circuit Extension - `StateDB`](/G48BKqdPScyoFDHPNzgOYQ)