Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions specs/error_state/ErrorDepth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# ErrorDepth state

## Procedure
For CALL-family codes this type of error occurs when causing a callstack overflow.

### EVM behavior
1. Fail the call attempt resulting in pushing `0` into stack
2. Continue the execution of current context

### Constraints
1. Current opcode must be CALL-family codes.
2. `depth == 1025`.
3. The next step with `0` on stack top.

### Lookups
- 1 Call Context lookup `CallContextFieldTag.Depth`
- 1 Stack push lookup

## Code

Please refer to `src/zkevm_specs/evm/execution/error_depth.py`.

### Solidity Code to trigger ErrorDepth
```solidity
PUSH32 0x7f602060006000376000600060206000600060003561ffff5a03f10000000000
PUSH1 0x0
MSTORE
PUSH32 0x0060005260206000F30000000000000000000000000000000000000000000000
PUSH1 0x20
MSTORE

PUSH1 0x40
PUSH1 0x0
PUSH1 0x0
CREATE

DUP1
PUSH1 0x40
MSTORE

PUSH1 0x0 // retSize
PUSH1 0x0 // retOffset
PUSH1 0x20 // argSize
PUSH1 0x40 // argOffset
PUSH1 0x0 // Value
DUP6
PUSH2 0xFF
GAS
SUB
CALL
```
2 changes: 2 additions & 0 deletions src/zkevm_specs/evm/execution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from .extcodecopy import *
from .oog_constant import *
from .error_Invalid_jump import *
from .error_depth import *


EXECUTION_STATE_IMPL: Dict[ExecutionState, Callable] = {
Expand Down Expand Up @@ -95,4 +96,5 @@
ExecutionState.RETURN: return_,
ExecutionState.ErrorOutOfGasConstant: oog_constant,
ExecutionState.ErrorInvalidJump: invalid_jump,
ExecutionState.ErrorDepth: error_depth,
}
39 changes: 39 additions & 0 deletions src/zkevm_specs/evm/execution/error_depth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from ...util import FQ
from ..instruction import Instruction, Transition
from ..opcode import Opcode
from ..table import CallContextFieldTag


def error_depth(instruction: Instruction):
opcode = instruction.opcode_lookup(True)
# current executing op code must be CALL-family
instruction.constrain_in(
opcode,
[FQ(Opcode.CALL), FQ(Opcode.CALLCODE), FQ(Opcode.DELEGATECALL), FQ(Opcode.STATICCALL)]
)

is_call, is_callcode, is_delegatecall, is_staticcall = instruction.multiple_select(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need to check it is call or call code etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but CALL and CALLCODE will consume 7 stack elements and DELEGATECALL and STATICCALL will consume 6 elements

opcode, (Opcode.CALL, Opcode.CALLCODE, Opcode.DELEGATECALL, Opcode.STATICCALL)
)

stack_delta = 0
if is_call or is_callcode:
stack_delta -= 7
elif is_delegatecall or is_delegatecall:
stack_delta -= 6

# next step must have zero on stack top
is_zero = instruction.stack_push()
stack_delta += 1
instruction.constrain_equal(is_zero, FQ(0))

# check the call depth
depth = instruction.call_context_lookup(CallContextFieldTag.Depth)
instruction.constrain_equal(depth, FQ(1025))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be 1024 because current look up is for caller's depth


instruction.step_state_transition_in_same_context(
opcode,
rw_counter=Transition.delta(2),
program_counter=Transition.delta(1),
stack_pointer=Transition.delta(stack_delta)
)
64 changes: 64 additions & 0 deletions tests/evm/test_depth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import pytest
from collections import namedtuple
from itertools import chain

from zkevm_specs.evm import (
Block,
Bytecode,
CallContextFieldTag,
ExecutionState,
Opcode,
RWDictionary,
StepState,
Tables,
Transaction,
verify_steps,
)
from zkevm_specs.util import rand_fq, RLC

BYTECODE = Bytecode().call()
TESTING_DATA_IS_ROOT = ((Transaction(), BYTECODE),)


@pytest.mark.parametrize("tx, bytecode", TESTING_DATA_IS_ROOT)
def test_depth(tx: Transaction, bytecode: Bytecode):
randomness = rand_fq()

block = Block()

bytecode_hash = RLC(bytecode.hash(), randomness)

tables = Tables(
block_table=set(block.table_assignments(randomness)),
tx_table=set(
chain(
tx.table_assignments(randomness),
Transaction(id=tx.id + 1).table_assignments(randomness),
)
),
bytecode_table=set(bytecode.table_assignments(randomness)),
rw_table=set(
RWDictionary(24)
.call_context_read(1, CallContextFieldTag.Depth, 1025)
.rws
),
)

verify_steps(
randomness=randomness,
tables=tables,
steps=[
StepState(
execution_state=ExecutionState.ErrorDepth,
rw_counter=24,
call_id=1,
is_root=True,
is_create=False,
code_hash=bytecode_hash,
program_counter=0,
stack_pointer=1023,
gas_left=2,
reversible_write_counter=2,
)
],
)