diff --git a/specs/opcode/38CODESIZE.md b/specs/opcode/38CODESIZE.md new file mode 100644 index 000000000..146932262 --- /dev/null +++ b/specs/opcode/38CODESIZE.md @@ -0,0 +1,35 @@ +# CODESIZE opcode + +## Procedure + +The `CODESIZE` opcode gets the size of code running in current environment. + +## EVM behaviour + +The `CODESIZE` opcode pushes the size of code running in the current environment to the top of the stack. + +## Circuit behaviour + +1. Lookup the code size from the bytecode table +2. Do busmapping lookup for stack write operation + +## Constraints + +1. opId = 0x38 +2. State transition: + - gc + 1 (1 stack write) + - stack_pointer - 1 + - pc + 1 + - gas + 2 +3. Lookups: 2 + - `codesize` (bytecode_length) from the bytecode table + - `codesize` 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 [`codesize`](src/zkevm_specs/evm/execution/codesize.py). diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index 287dc13a2..7a22349ce 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -19,6 +19,7 @@ from .calldatacopy import * from .calldataload import * from .codecopy import * +from .codesize import * from .gas import * from .iszero import * from .jump import * @@ -50,6 +51,7 @@ ExecutionState.CALLDATALOAD: calldataload, ExecutionState.CALLDATASIZE: calldatasize, ExecutionState.CODECOPY: codecopy, + ExecutionState.CODESIZE: codesize, ExecutionState.BlockCtx: blockctx, ExecutionState.JUMP: jump, ExecutionState.JUMPI: jumpi, diff --git a/src/zkevm_specs/evm/execution/codesize.py b/src/zkevm_specs/evm/execution/codesize.py new file mode 100644 index 000000000..93cc69bcb --- /dev/null +++ b/src/zkevm_specs/evm/execution/codesize.py @@ -0,0 +1,22 @@ +from ...util import N_BYTES_U64 +from ..instruction import Instruction, Transition +from ..opcode import Opcode + + +def codesize(instruction: Instruction): + opcode = instruction.opcode_lookup(True) + instruction.constrain_equal(opcode, Opcode.CODESIZE) + + code_size = instruction.bytecode_length(instruction.curr.code_hash) + + instruction.constrain_equal( + instruction.rlc_to_fq(instruction.stack_push(), N_BYTES_U64), + code_size.expr(), + ) + + instruction.step_state_transition_in_same_context( + opcode, + rw_counter=Transition.delta(1), + program_counter=Transition.delta(1), + stack_pointer=Transition.delta(-1), + ) diff --git a/tests/evm/test_codesize.py b/tests/evm/test_codesize.py new file mode 100644 index 000000000..1fd5fe1b2 --- /dev/null +++ b/tests/evm/test_codesize.py @@ -0,0 +1,55 @@ +import pytest + +from zkevm_specs.evm import ( + Bytecode, + ExecutionState, + RWDictionary, + StepState, + Tables, + verify_steps, +) +from zkevm_specs.util import rand_fq, RLC + + +def test_codesize(): + randomness = rand_fq() + + bytecode = Bytecode().codesize().stop() + codesize = len(bytecode.code) + 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).stack_write(1, 1023, RLC(codesize, randomness)).rws), + ) + + verify_steps( + randomness=randomness, + tables=tables, + steps=[ + StepState( + execution_state=ExecutionState.CODESIZE, + 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=10, + call_id=1, + is_root=True, + is_create=False, + code_hash=bytecode_hash, + program_counter=1, + stack_pointer=1023, + gas_left=0, + ), + ], + )