Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.
Merged
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
6 changes: 3 additions & 3 deletions specs/opcode/33CALLER.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ 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
2. Do busmapping lookup for call context caller read operation
3. Do busmapping lookup for stack write operation

## Constraints

Expand All @@ -24,8 +24,8 @@ then pushes the `address` to the stack.
- pc + 1
- gas + 2
3. Lookups: 2
- `address` is on top of stack
- `address` is in the rw table {call context, call ID, caller}
- `address` is on top of stack

## Exceptions

Expand Down
37 changes: 37 additions & 0 deletions specs/opcode/34CALLVALUE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CALLVALUE opcode

## Procedure

The `CALLVALUE` opcode gets the call value (msg.value) from the current call.

## EVM behaviour

The `CALLVALUE` opcode loads a `word` (32 bytes of data) from call context ->
call value, then pushes the `word` to the stack.

## Circuit behaviour

1. Construct call context table in rw table
2. Do busmapping lookup for call context call value read operation
3. Do busmapping lookup for stack write operation

## Constraints

1. opId = 0x34
2. State transition:
- gc + 2 (1 stack write, 1 call context read)
- stack_pointer - 1
- pc + 1
- gas + 2
3. Lookups: 2
- `word` is in the rw table {call context, call ID, call value}
- `word` 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/callvalue.py`.
37 changes: 37 additions & 0 deletions specs/opcode/36CALLDATASIZE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CALLDATASIZE opcode

## Procedure

The `CALLDATASIZE` opcode gets the call data size (msg.data.size) from the current call.

## EVM behaviour

The `CALLDATASIZE` opcode loads a `u64` (5 bytes of data) from call context ->
call data length, then pushes the `u64` to the stack.

## Circuit behaviour

1. Construct call context table in rw table
2. Do busmapping lookup for call context call data length read operation
3. Do busmapping lookup for stack write operation

## Constraints

1. opId = 0x36
2. State transition:
- gc + 2 (1 stack write, 1 call context read)
- stack_pointer - 1
- pc + 1
- gas + 2
3. Lookups: 2
- `u64` is in the rw table {call context, call ID, call data length}
- `u64` 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/calldatasize.py`.
4 changes: 4 additions & 0 deletions src/zkevm_specs/evm/execution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from .block_coinbase import *
from .caller import *
from .slt_sgt import *
from .callvalue import *
from .calldatasize import *


EXECUTION_STATE_IMPL: Dict[ExecutionState, Callable] = {
Expand All @@ -22,6 +24,8 @@
ExecutionState.EndBlock: end_block,
ExecutionState.ADD: add,
ExecutionState.CALLER: caller,
ExecutionState.CALLVALUE: callvalue,
ExecutionState.CALLDATASIZE: calldatasize,
ExecutionState.COINBASE: coinbase,
ExecutionState.JUMP: jump,
ExecutionState.JUMPI: jumpi,
Expand Down
3 changes: 3 additions & 0 deletions src/zkevm_specs/evm/execution/block_coinbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ def coinbase(instruction: Instruction):
address,
instruction.int_to_rlc(
instruction.block_context_lookup(BlockContextFieldTag.Coinbase),
# NOTE: We can replace this with N_BYTES_WORD if we reuse the 32
# byte RLC constraint in all places. See:
# https://github.com/appliedzkp/zkevm-specs/issues/101
20,
),
)
Expand Down
29 changes: 29 additions & 0 deletions src/zkevm_specs/evm/execution/calldatasize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from ..instruction import Instruction, Transition
from ..table import CallContextFieldTag
from ..opcode import Opcode
from ...util.param import N_BYTES_MEMORY_ADDRESS


def calldatasize(instruction: Instruction):
opcode = instruction.opcode_lookup(True)
instruction.constrain_equal(opcode, Opcode.CALLDATASIZE)

# check [rw_table, call_context] table for call data length and compare
# against stack top after push.
instruction.constrain_equal(
instruction.int_to_rlc(
instruction.call_context_lookup(CallContextFieldTag.CallDataLength),
# NOTE: We can replace this with N_BYTES_WORD if we reuse the 32
# byte RLC constraint in all places. See:
# https://github.com/appliedzkp/zkevm-specs/issues/101
N_BYTES_MEMORY_ADDRESS,
),
instruction.stack_push(),
)

instruction.step_state_transition_in_same_context(
opcode,
rw_counter=Transition.delta(2),
program_counter=Transition.delta(1),
stack_pointer=Transition.delta(-1),
)
12 changes: 8 additions & 4 deletions src/zkevm_specs/evm/execution/caller.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
from ..instruction import Instruction, Transition
from ..table import CallContextFieldTag
from ..opcode import Opcode
from ...util.param import N_BYTES_ACCOUNT_ADDRESS


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
# check [rw_table, call_context] table for caller address and compare with
# stack top after push
instruction.constrain_equal(
address,
instruction.int_to_rlc(
instruction.call_context_lookup(CallContextFieldTag.CallerAddress),
20,
# NOTE: We can replace this with N_BYTES_WORD if we reuse the 32
# byte RLC constraint in all places. See:
# https://github.com/appliedzkp/zkevm-specs/issues/101
N_BYTES_ACCOUNT_ADDRESS,
),
instruction.stack_push(),
)

instruction.step_state_transition_in_same_context(
Expand Down
22 changes: 22 additions & 0 deletions src/zkevm_specs/evm/execution/callvalue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from ..instruction import Instruction, Transition
from ..table import CallContextFieldTag
from ..opcode import Opcode


def callvalue(instruction: Instruction):
opcode = instruction.opcode_lookup(True)
instruction.constrain_equal(opcode, Opcode.CALLVALUE)

# check [rw_table, call_context] table for call value and compare against
# stack top after push.
instruction.constrain_equal(
instruction.call_context_lookup(CallContextFieldTag.Value),
instruction.stack_push(),
)

instruction.step_state_transition_in_same_context(
opcode,
rw_counter=Transition.delta(2),
program_counter=Transition.delta(1),
stack_pointer=Transition.delta(-1),
)
4 changes: 4 additions & 0 deletions src/zkevm_specs/util/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
N_BYTES_GAS = 8
# Number of bytes of program counter
N_BYTES_PROGRAM_COUNTER = 8
# Number of bytes of u64
N_BYTES_U64 = 8
# Number of bytes of an EVM word (u256)
N_BYTES_WORD = 32

# Gas cost of non-creation transaction
GAS_COST_TX = 21000
Expand Down
71 changes: 71 additions & 0 deletions tests/evm/test_calldatasize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import pytest

from zkevm_specs.evm import (
ExecutionState,
StepState,
Opcode,
verify_steps,
Tables,
RWTableTag,
RW,
CallContextFieldTag,
Bytecode,
)
from zkevm_specs.util import rand_fp, RLC, U64
from zkevm_specs.util.param import N_BYTES_U64


TESTING_DATA = (
0x00,
0x10,
0x302010,
)


@pytest.mark.parametrize("calldatasize", TESTING_DATA)
def test_calldatasize(calldatasize: U64):
randomness = rand_fp()

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

tables = Tables(
block_table=set(),
tx_table=set(),
bytecode_table=set(bytecode.table_assignments(randomness)),
rw_table=set(
[
(9, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.CallDataLength, calldatasize, 0, 0),
(10, RW.Write, RWTableTag.Stack, 1, 1023, RLC(calldatasize, randomness, N_BYTES_U64), 0, 0),
]
),
)

verify_steps(
randomness=randomness,
tables=tables,
steps=[
StepState(
execution_state=ExecutionState.CALLDATASIZE,
rw_counter=9,
call_id=1,
is_root=True,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@ed255 Should we also test the scenario where this is not the root (is_root=False) so that we test CALLDATASIZE for internal calls as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think that's a very interesting case to test. Nevertheless I think we'll have to wait for this #82 to be merged so that we have CALLs in order to achieve a test situation where is_root=False.

So I'd say that we can delay this for the future.

is_create=False,
code_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,
code_source=bytecode_hash,
program_counter=1,
stack_pointer=1023,
gas_left=0,
),
],
)
13 changes: 10 additions & 3 deletions tests/evm/test_caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@
Bytecode,
)
from zkevm_specs.util import rand_address, rand_fp, RLC, U160
from zkevm_specs.util.param import N_BYTES_ACCOUNT_ADDRESS


TESTING_DATA = (0x030201, rand_address())
TESTING_DATA = (
0x00,
0x10,
0x030201,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
rand_address(),
)


@pytest.mark.parametrize("caller", TESTING_DATA)
Expand All @@ -29,8 +36,8 @@ def test_caller(caller: U160):
bytecode_table=set(bytecode.table_assignments(randomness)),
rw_table=set(
[
(9, RW.Write, RWTableTag.Stack, 1, 1023, RLC(caller, randomness, 20), 0, 0),
(10, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.CallerAddress, caller, 0, 0),
(9, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.CallerAddress, caller, 0, 0),
(10, RW.Write, RWTableTag.Stack, 1, 1023, RLC(caller, randomness, N_BYTES_ACCOUNT_ADDRESS), 0, 0),
]
),
)
Expand Down
74 changes: 74 additions & 0 deletions tests/evm/test_callvalue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest

from zkevm_specs.evm import (
ExecutionState,
StepState,
Opcode,
verify_steps,
Tables,
RWTableTag,
RW,
CallContextFieldTag,
Bytecode,
)
from zkevm_specs.util import rand_fp, RLC, U256
from zkevm_specs.util.param import N_BYTES_WORD


TESTING_DATA = (
0x00,
0x10,
0x302010,
0xF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F,
)


@pytest.mark.parametrize("callvalue", TESTING_DATA)
def test_callvalue(callvalue: U256):
randomness = rand_fp()

callvalue_rlc = RLC(callvalue, randomness, N_BYTES_WORD)

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

tables = Tables(
block_table=set(),
tx_table=set(),
bytecode_table=set(bytecode.table_assignments(randomness)),
rw_table=set(
[
(9, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.Value, callvalue_rlc, 0, 0),
(10, RW.Write, RWTableTag.Stack, 1, 1023, callvalue_rlc, 0, 0),
]
),
)

verify_steps(
randomness=randomness,
tables=tables,
steps=[
StepState(
execution_state=ExecutionState.CALLVALUE,
rw_counter=9,
call_id=1,
is_root=True,
is_create=False,
code_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,
code_source=bytecode_hash,
program_counter=1,
stack_pointer=1023,
gas_left=0,
),
],
)