From ffcc9295442bccd7bf23646ee9eb2565aef9ff82 Mon Sep 17 00:00:00 2001 From: Eduard S Date: Fri, 14 Jan 2022 15:26:30 +0100 Subject: [PATCH] Add spec for CALLER opcode (#90) --- specs/opcode/33CALLER.md | 37 +++++++++++++ src/zkevm_specs/evm/execution/__init__.py | 1 + src/zkevm_specs/evm/execution/caller.py | 27 +++++++++ src/zkevm_specs/evm/main.py | 3 + tests/evm/test_caller.py | 67 +++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 specs/opcode/33CALLER.md create mode 100644 src/zkevm_specs/evm/execution/caller.py create mode 100644 tests/evm/test_caller.py diff --git a/specs/opcode/33CALLER.md b/specs/opcode/33CALLER.md new file mode 100644 index 000000000..8f2f67eab --- /dev/null +++ b/specs/opcode/33CALLER.md @@ -0,0 +1,37 @@ +# CALLER opcode + +## Procedure + +The `CALLER` opcode gets the caller address (msg.caller) from the current call. + +## EVM behaviour + +The `CALLER` opcode loads an `address` (20 bytes of data) from call context, +then pushes the `address` to the stack. + +## Circuit behaviour + +1. Construct call context table in rw table +2. Do busmapping lookup for stack write operation +3. Do busmapping lookup for call context caller read operation + +## Constraints + +1. opId = 0x33 +2. State transition: + - gc + 2 (1 stack write, 1 call context read) + - stack_pointer - 1 + - pc + 1 + - gas + 2 +3. Lookups: 2 + - `address` is on top of stack + - `address` is in the rw table {call context, call ID, caller} + +## Exceptions + +1. stack overflow: stack is full, stack pointer = 0 +2. out of gas: remaining gas is not enough + +## Code + +Please refer to `src/zkevm_specs/evm/execution/caller.py`. diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index 977878b7c..0306dd1dd 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -6,5 +6,6 @@ from .jump import * from .jumpi import * from .block_coinbase import * +from .caller import * # Error cases diff --git a/src/zkevm_specs/evm/execution/caller.py b/src/zkevm_specs/evm/execution/caller.py new file mode 100644 index 000000000..1750fb905 --- /dev/null +++ b/src/zkevm_specs/evm/execution/caller.py @@ -0,0 +1,27 @@ +from ..instruction import Instruction, Transition +from ..table import CallContextFieldTag +from ..opcode import Opcode + + +def caller(instruction: Instruction): + opcode = instruction.opcode_lookup(True) + instruction.constrain_equal(opcode, Opcode.CALLER) + address = instruction.stack_push() + + # check [rw_table, call_context] table for caller address + instruction.constrain_equal( + address, + instruction.bytes_to_rlc( + instruction.int_to_bytes( + instruction.call_context_lookup(CallContextFieldTag.CallerAddress), + 20, + ) + ), + ) + + instruction.constrain_same_context_state_transition( + opcode, + rw_counter=Transition.delta(2), + program_counter=Transition.delta(1), + stack_pointer=Transition.delta(-1), + ) diff --git a/src/zkevm_specs/evm/main.py b/src/zkevm_specs/evm/main.py index 381771de8..0564289dc 100644 --- a/src/zkevm_specs/evm/main.py +++ b/src/zkevm_specs/evm/main.py @@ -8,6 +8,7 @@ jump, jumpi, coinbase, + caller, ) from .execution_state import ExecutionState from .instruction import Instruction @@ -51,6 +52,8 @@ def verify_step( jumpi(instruction) elif instruction.curr.execution_state == ExecutionState.COINBASE: coinbase(instruction) + elif instruction.curr.execution_state == ExecutionState.CALLER: + caller(instruction) # Error cases else: raise NotImplementedError diff --git a/tests/evm/test_caller.py b/tests/evm/test_caller.py new file mode 100644 index 000000000..9668f3af0 --- /dev/null +++ b/tests/evm/test_caller.py @@ -0,0 +1,67 @@ +import pytest + +from zkevm_specs.evm import ( + ExecutionState, + StepState, + Opcode, + verify_steps, + Tables, + RWTableTag, + RW, + CallContextFieldTag, + Bytecode, +) +from zkevm_specs.util import RLCStore, U160 + + +TESTING_DATA = ((Opcode.CALLER, 0x030201),) + + +@pytest.mark.parametrize("opcode, address", TESTING_DATA) +def test_caller(opcode: Opcode, address: U160): + rlc_store = RLCStore() + + caller_rlc = rlc_store.to_rlc(address.to_bytes(20, "little")) + + bytecode = Bytecode(f"{opcode.hex()}00") + bytecode_hash = rlc_store.to_rlc(bytecode.hash, 32) + tables = Tables( + block_table=set(), + tx_table=set(), + bytecode_table=set(bytecode.table_assignments(rlc_store)), + rw_table=set( + [ + (9, RW.Write, RWTableTag.Stack, 1, 1023, caller_rlc, 0, 0), + (10, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.CallerAddress, address, 0, 0), + ] + ), + ) + + verify_steps( + rlc_store=rlc_store, + tables=tables, + steps=[ + StepState( + execution_state=ExecutionState.CALLER, + rw_counter=9, + call_id=1, + is_root=True, + is_create=False, + opcode_source=bytecode_hash, + program_counter=0, + stack_pointer=1024, + gas_left=2, + ), + StepState( + execution_state=ExecutionState.STOP, + rw_counter=11, + call_id=1, + is_root=True, + is_create=False, + opcode_source=bytecode_hash, + program_counter=1, + stack_pointer=1023, + gas_left=0, + ), + ], + )