diff --git a/specs/opcode/30ADDRESS.md b/specs/opcode/30ADDRESS.md new file mode 100644 index 000000000..ec5a03773 --- /dev/null +++ b/specs/opcode/30ADDRESS.md @@ -0,0 +1,37 @@ +# ADDRESS opcode + +## Procedure + +The `ADDRESS` opcode gets the address of currently executing account. + +## EVM behaviour + +The `ADDRESS` opcode loads the callee address (20 bytes of data) from call +context, then pushes this address to the stack. + +## Circuit behaviour + +1. Construct call context table in rw table +2. Do busmapping lookup for call context callee read operation +3. Do busmapping lookup for stack write operation + +## Constraints + +1. opId = 0x30 +2. State transition: + - gc + 2 (1 stack write, 1 call context read) + - stack_pointer - 1 + - pc + 1 + - gas - 2 +3. Lookups: 2 + - `address` is in the rw table {call context, call ID, callee} + - `address` is on top of stack + +## 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/address.py`. diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index d1b4db432..005aa02e6 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -9,6 +9,7 @@ # Opcode's successful cases from .add_sub import * from .addmod import * +from .address import * from .mulmod import * from .block_ctx import * from .call import * @@ -47,6 +48,7 @@ ExecutionState.EndBlock: end_block, ExecutionState.ADD: add_sub, ExecutionState.ADDMOD: addmod, + ExecutionState.ADDRESS: address, ExecutionState.MULMOD: mulmod, ExecutionState.MUL: mul_div_mod, ExecutionState.NOT: not_opcode, diff --git a/src/zkevm_specs/evm/execution/address.py b/src/zkevm_specs/evm/execution/address.py new file mode 100644 index 000000000..63437de5c --- /dev/null +++ b/src/zkevm_specs/evm/execution/address.py @@ -0,0 +1,24 @@ +from ...util import N_BYTES_ACCOUNT_ADDRESS +from ..instruction import Instruction, Transition +from ..table import CallContextFieldTag +from ..opcode import Opcode + + +def address(instruction: Instruction): + opcode = instruction.opcode_lookup(True) + instruction.constrain_equal(opcode, Opcode.ADDRESS) + + # Get callee address from call context and compare with stack top after push. + instruction.constrain_equal( + instruction.call_context_lookup(CallContextFieldTag.CalleeAddress), + # NOTE: We can replace this with N_BYTES_WORD if we reuse the 32 byte RLC constraint in + # all places. See: https://github.com/privacy-scaling-explorations/zkevm-specs/issues/101 + instruction.rlc_to_fq(instruction.stack_push(), N_BYTES_ACCOUNT_ADDRESS), + ) + + instruction.step_state_transition_in_same_context( + opcode, + rw_counter=Transition.delta(2), + program_counter=Transition.delta(1), + stack_pointer=Transition.delta(-1), + ) diff --git a/tests/evm/test_address.py b/tests/evm/test_address.py new file mode 100644 index 000000000..8f4b7b1b0 --- /dev/null +++ b/tests/evm/test_address.py @@ -0,0 +1,70 @@ +import pytest + +from zkevm_specs.evm import ( + Bytecode, + CallContextFieldTag, + ExecutionState, + RWDictionary, + StepState, + Tables, + verify_steps, +) +from zkevm_specs.util import RLC, U160, rand_address, rand_fq + + +TESTING_DATA = ( + 0x00, + 0x10, + 0x030201, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + rand_address(), +) + + +@pytest.mark.parametrize("address", TESTING_DATA) +def test_address(address: U160): + randomness = rand_fq() + + bytecode = Bytecode().address() + bytecode_hash = RLC(bytecode.hash(), randomness) + + tables = Tables( + block_table=set(), + tx_table=set(), + bytecode_table=set(bytecode.table_assignments(randomness)), + rw_table=set( + RWDictionary(9) + .call_context_read(1, CallContextFieldTag.CalleeAddress, address) + .stack_write(1, 1023, RLC(address, randomness)) + .rws + ), + ) + + verify_steps( + randomness=randomness, + tables=tables, + steps=[ + StepState( + execution_state=ExecutionState.ADDRESS, + rw_counter=9, + call_id=1, + is_root=True, + is_create=False, + code_hash=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, + code_hash=bytecode_hash, + program_counter=1, + stack_pointer=1023, + gas_left=0, + ), + ], + )