diff --git a/specs/opcode/41COINBASE.md b/specs/opcode/41COINBASE.md index 7ec0cade6..aa229c1ea 100644 --- a/specs/opcode/41COINBASE.md +++ b/specs/opcode/41COINBASE.md @@ -36,4 +36,4 @@ then push the `address` to the stack. ## Code -Please refer to `src/zkevm_specs/evm/execution/coinbase.py`. +Please refer to `src/zkevm_specs/evm/execution/block_coinbase.py`. diff --git a/specs/opcode/42TIMESTAMP.md b/specs/opcode/42TIMESTAMP.md new file mode 100644 index 000000000..d18550e30 --- /dev/null +++ b/specs/opcode/42TIMESTAMP.md @@ -0,0 +1,39 @@ +# Timestamp op code + +## Procedure + +The `TIMESTAMP` opcode pushes the timestamp of the current block onto the stack. + +## EVM behavior + +The `TIMESTAMP` opcode loads a `timestamp` (8 bytes of data) from the block context and then +pushes it onto the stack. + +## Circuit behavior + +1. construct block context table +2. do busmapping lookup for stack write operation +3. other implicit check: bytes length + +## Constraints + +1. opId = 0x42 +2. State transition: + - gc + 1 (1 stack write) + - stack_pointer - 1 + - pc + 1 + - gas + 2 +3. Lookups: 2 + - `timestamp` is on the top of stack + - `timestamp` is in the block context table +4. Others: + - `timestamp` fits into 8 bytes + +## 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/block_timestamp.py`. diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index 9a8e3deab..e290a43fe 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -12,6 +12,7 @@ from .jumpi import * from .push import * from .block_coinbase import * +from .block_timestamp import * from .caller import * from .slt_sgt import * from .callvalue import * @@ -27,6 +28,7 @@ ExecutionState.CALLVALUE: callvalue, ExecutionState.CALLDATASIZE: calldatasize, ExecutionState.COINBASE: coinbase, + ExecutionState.TIMESTAMP: timestamp, ExecutionState.JUMP: jump, ExecutionState.JUMPI: jumpi, ExecutionState.PUSH: push, diff --git a/src/zkevm_specs/evm/execution/block_timestamp.py b/src/zkevm_specs/evm/execution/block_timestamp.py new file mode 100644 index 000000000..16c95bfe6 --- /dev/null +++ b/src/zkevm_specs/evm/execution/block_timestamp.py @@ -0,0 +1,22 @@ +from ..instruction import Instruction, Transition +from ..table import BlockContextFieldTag +from ..opcode import Opcode + + +def timestamp(instruction: Instruction): + opcode = instruction.opcode_lookup(True) + instruction.constrain_equal(opcode, Opcode.TIMESTAMP) + timestamp = instruction.stack_push() + # in real circuit also check timestamp raw data is 64 bit length (8 bytes) + # check block table for timestamp + instruction.constrain_equal( + timestamp, + instruction.block_context_lookup(BlockContextFieldTag.Timestamp), + ) + + 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_timestamp.py b/tests/evm/test_timestamp.py new file mode 100644 index 000000000..e44979050 --- /dev/null +++ b/tests/evm/test_timestamp.py @@ -0,0 +1,65 @@ +import pytest + +from zkevm_specs.evm import ( + ExecutionState, + StepState, + verify_steps, + Tables, + RWTableTag, + RW, + Block, + Bytecode, +) +from zkevm_specs.util import rand_range, rand_fp, RLC, U64 + +TESTING_DATA = (0, 1, 2 ** 64 - 1, rand_range(2 ** 64)) + + +@pytest.mark.parametrize("timestamp", TESTING_DATA) +def test_timestamp(timestamp: U64): + randomness = rand_fp() + + block = Block(timestamp=timestamp) + + bytecode = Bytecode().timestamp() + bytecode_hash = RLC(bytecode.hash(), randomness) + + tables = Tables( + block_table=set(block.table_assignments(randomness)), + tx_table=set(), + bytecode_table=set(bytecode.table_assignments(randomness)), + rw_table=set( + [ + (9, RW.Write, RWTableTag.Stack, 1, 1023, RLC(timestamp, randomness, 8), 0, 0), + ] + ), + ) + + verify_steps( + randomness=randomness, + tables=tables, + steps=[ + StepState( + execution_state=ExecutionState.TIMESTAMP, + 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=10, + call_id=1, + is_root=True, + is_create=False, + code_source=bytecode_hash, + program_counter=1, + stack_pointer=1023, + gas_left=0, + ), + ], + )