Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Feat/#448 implement ErrorOutOfGasLog error state #469

Merged
merged 7 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion specs/error_state/ErrorOutOfGasLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ For this gadget, the core is to calculate gas required as following:
- if it is not root call, it restores caller's context by reading to `rw_table`, then does step state transition to it.

## Code
TODO: add python code after circuit merges!

Please refer to `src/zkevm_specs/evm/execution/error_oog_log.py`.
2 changes: 2 additions & 0 deletions src/zkevm_specs/evm_circuit/execution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from .error_invalid_jump import *
from .error_invalid_opcode import *
from .error_gas_uint_overflow import *
from .error_oog_log import *


EXECUTION_STATE_IMPL: Dict[ExecutionState, Callable] = {
Expand Down Expand Up @@ -125,6 +126,7 @@
ExecutionState.ErrorInvalidOpcode: error_invalid_opcode,
ExecutionState.ErrorOutOfGasConstant: error_oog_constant,
ExecutionState.ErrorStack: error_stack,
ExecutionState.ErrorOutOfGasLOG: error_oog_log,
# ExecutionState.ECRECOVER: ,
# ExecutionState.SHA256: ,
# ExecutionState.RIPEMD160: ,
Expand Down
31 changes: 31 additions & 0 deletions src/zkevm_specs/evm_circuit/execution/error_oog_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from zkevm_specs.util.param import GAS_COST_LOG, GAS_COST_LOGDATA, N_BYTES_MEMORY_ADDRESS
from ...util import FQ
from ..instruction import Instruction
from ..opcode import Opcode
from ...util import N_BYTES_GAS


def error_oog_log(instruction: Instruction):
# retrieve op code associated to oog constant error
opcode = instruction.opcode_lookup(True)
# constrain op in [log0, log4] range
instruction.range_lookup(opcode - Opcode.LOG0, 5)

# pop `mstart`, `msize` from stack
mstart = instruction.word_to_fq(instruction.stack_pop(), N_BYTES_MEMORY_ADDRESS)
msize = instruction.word_to_fq(instruction.stack_pop(), N_BYTES_MEMORY_ADDRESS)

# get total gas cost
_, memory_expansion_gas = instruction.memory_expansion_dynamic_length(mstart, msize)
gas_cost = (
GAS_COST_LOG
+ GAS_COST_LOG * (opcode - Opcode.LOG0)
+ GAS_COST_LOGDATA * msize
+ memory_expansion_gas
)

# check gas left is less than total gas required
gas_not_enough, _ = instruction.compare(instruction.curr.gas_left, gas_cost, N_BYTES_GAS)
KimiWu123 marked this conversation as resolved.
Show resolved Hide resolved
instruction.constrain_equal(gas_not_enough, FQ(1))

instruction.constrain_error_state(instruction.rw_counter_offset + 1)
106 changes: 106 additions & 0 deletions tests/evm/test_error_oog_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import pytest

from zkevm_specs.evm_circuit import (
ExecutionState,
StepState,
verify_steps,
Tables,
CallContextFieldTag,
Block,
Bytecode,
RWDictionary,
Opcode,
)
from zkevm_specs.util import Word


TESTING_DATA = (
(Opcode.LOG0, 32, 32, 636),
(Opcode.LOG1, 32, 32, 1011),
(Opcode.LOG2, 32, 32, 1386),
(Opcode.LOG3, 32, 32, 1761),
(Opcode.LOG4, 32, 32, 2136),
)


@pytest.mark.parametrize("opcode, offset, size, gas_left", TESTING_DATA)
def test_error_oog_log(opcode: Opcode, offset: int, size: int, gas_left: int):
if opcode == Opcode.LOG0:
bytecode = Bytecode().log0()
elif opcode == Opcode.LOG1:
bytecode = Bytecode().log1()
elif opcode == Opcode.LOG2:
bytecode = Bytecode().log2()
elif opcode == Opcode.LOG3:
bytecode = Bytecode().log3()
else:
bytecode = Bytecode().log4()
bytecode_hash = Word(bytecode.hash())

current_call_id = 2
rw_counter = 14
rw_table = (
RWDictionary(rw_counter)
.stack_read(current_call_id, 1022, Word(offset))
.stack_read(current_call_id, 1023, Word(size))
)
stack_pointer = 1022
pc = (Opcode.LOG4 - Opcode.LOG0 + 1) * 33 + 1

memory_word_size = 0 if size == 0 else (offset + size + 31) // 32

rw_table.call_context_read(current_call_id, CallContextFieldTag.IsSuccess, 0)

# fmt: off
rw_table \
.call_context_read(current_call_id, CallContextFieldTag.CallerId, 1) \
.call_context_read(1, CallContextFieldTag.IsRoot, False) \
.call_context_read(1, CallContextFieldTag.IsCreate, False) \
.call_context_read(1, CallContextFieldTag.CodeHash, bytecode_hash) \
.call_context_read(1, CallContextFieldTag.ProgramCounter, pc) \
.call_context_read(1, CallContextFieldTag.StackPointer, stack_pointer) \
.call_context_read(1, CallContextFieldTag.GasLeft, gas_left) \
.call_context_read(1, CallContextFieldTag.MemorySize, memory_word_size) \
.call_context_read(1, CallContextFieldTag.ReversibleWriteCounter, 0) \
.call_context_write(1, CallContextFieldTag.LastCalleeId, 2) \
.call_context_write(1, CallContextFieldTag.LastCalleeReturnDataOffset, 0) \
.call_context_write(1, CallContextFieldTag.LastCalleeReturnDataLength, 0)
# fmt: on

tables = Tables(
block_table=set(Block().table_assignments()),
tx_table=set(),
bytecode_table=set(bytecode.table_assignments()),
rw_table=set(rw_table.rws),
)

verify_steps(
tables=tables,
steps=[
StepState(
execution_state=ExecutionState.ErrorOutOfGasLOG,
rw_counter=rw_counter,
call_id=current_call_id,
is_root=False,
is_create=False,
code_hash=bytecode_hash,
program_counter=0,
stack_pointer=stack_pointer,
gas_left=gas_left,
reversible_write_counter=0,
),
StepState(
execution_state=ExecutionState.STOP,
rw_counter=rw_table.rw_counter,
call_id=1,
is_root=False,
is_create=False,
code_hash=bytecode_hash,
program_counter=pc,
stack_pointer=stack_pointer,
gas_left=gas_left,
memory_word_size=memory_word_size,
reversible_write_counter=0,
),
],
)
Loading