diff --git a/specs/error_state/ErrorDepth.md b/specs/error_state/ErrorDepth.md new file mode 100644 index 000000000..c3b3aeb27 --- /dev/null +++ b/specs/error_state/ErrorDepth.md @@ -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 +``` \ No newline at end of file diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index f44ba2965..4d8d3aea0 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -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] = { @@ -95,4 +96,5 @@ ExecutionState.RETURN: return_, ExecutionState.ErrorOutOfGasConstant: oog_constant, ExecutionState.ErrorInvalidJump: invalid_jump, + ExecutionState.ErrorDepth: error_depth, } diff --git a/src/zkevm_specs/evm/execution/error_depth.py b/src/zkevm_specs/evm/execution/error_depth.py new file mode 100644 index 000000000..14f5ff5a7 --- /dev/null +++ b/src/zkevm_specs/evm/execution/error_depth.py @@ -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( + 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)) + + instruction.step_state_transition_in_same_context( + opcode, + rw_counter=Transition.delta(2), + program_counter=Transition.delta(1), + stack_pointer=Transition.delta(stack_delta) + ) diff --git a/tests/evm/test_depth.py b/tests/evm/test_depth.py new file mode 100644 index 000000000..3c5e80c08 --- /dev/null +++ b/tests/evm/test_depth.py @@ -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, + ) + ], + )