diff --git a/cairo/ethereum/cancun/vm.cairo b/cairo/ethereum/cancun/vm.cairo index 5893626a..3edc3ed2 100644 --- a/cairo/ethereum/cancun/vm.cairo +++ b/cairo/ethereum/cancun/vm.cairo @@ -87,3 +87,468 @@ struct MessageStruct { accessed_storage_keys: SetTupleAddressBytes32, parent_evm: OptionalEvm, } + +namespace EvmImpl { + func set_pc{evm: Evm}(new_pc: Uint) { + tempvar evm = Evm( + new EvmStruct( + pc=new_pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_stack{evm: Evm}(new_stack: Stack) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=new_stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_memory{evm: Evm}(new_memory: Memory) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=new_memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_code{evm: Evm}(new_code: Bytes) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=new_code, + gas_left=evm.value.gas_left, + env=evm.value.env, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_gas_left{evm: Evm}(new_gas_left: Uint) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=new_gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_env{evm: Evm}(new_env: Environment) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=new_env, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_valid_jump_destinations{evm: Evm}(new_valid_jump_destinations: SetUint) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=new_valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_logs{evm: Evm}(new_logs: TupleLog) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + logs=new_logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_refund_counter{evm: Evm}(new_refund_counter: Uint) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=new_refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_running{evm: Evm}(new_running: bool) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=new_running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_message{evm: Evm}(new_message: Message) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=new_message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_output{evm: Evm}(new_output: Bytes) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_accounts_to_delete{evm: Evm}(new_accounts_to_delete: SetAddress) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=new_accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_touched_accounts{evm: Evm}(new_touched_accounts: SetAddress) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=new_touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_return_data{evm: Evm}(new_return_data: Bytes) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=new_return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_error{evm: Evm}(new_error: OptionalEthereumException) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=new_error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_accessed_addresses{evm: Evm}(new_accessed_addresses: SetAddress) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=new_accessed_addresses, + accessed_storage_keys=evm.value.accessed_storage_keys, + ), + ); + return (); + } + + func set_accessed_storage_keys{evm: Evm}(new_accessed_storage_keys: SetTupleAddressBytes32) { + tempvar evm = Evm( + new EvmStruct( + pc=evm.value.pc, + stack=evm.value.stack, + memory=evm.value.memory, + code=evm.value.code, + gas_left=evm.value.gas_left, + env=evm.value.env, + valid_jump_destinations=evm.value.valid_jump_destinations, + logs=evm.value.logs, + refund_counter=evm.value.refund_counter, + running=evm.value.running, + message=evm.value.message, + output=evm.value.output, + accounts_to_delete=evm.value.accounts_to_delete, + touched_accounts=evm.value.touched_accounts, + return_data=evm.value.return_data, + error=evm.value.error, + accessed_addresses=evm.value.accessed_addresses, + accessed_storage_keys=new_accessed_storage_keys, + ), + ); + return (); + } +} diff --git a/cairo/ethereum/cancun/vm/exceptions.cairo b/cairo/ethereum/cancun/vm/exceptions.cairo index 08851a03..cdfd34bf 100644 --- a/cairo/ethereum/cancun/vm/exceptions.cairo +++ b/cairo/ethereum/cancun/vm/exceptions.cairo @@ -7,3 +7,43 @@ struct StackUnderflowError { struct StackOverflowError { value: BytesStruct*, } + +struct OutOfGasError { + value: BytesStruct*, +} + +struct InvalidOpcodeError { + value: BytesStruct*, +} + +struct InvalidJumpDestError { + value: BytesStruct*, +} + +struct StackDepthLimitError { + value: BytesStruct*, +} + +struct WriteInStaticContextError { + value: BytesStruct*, +} + +struct OutOfBoundsReadError { + value: BytesStruct*, +} + +struct InvalidParameterError { + value: BytesStruct*, +} + +struct InvalidContractPrefixError { + value: BytesStruct*, +} + +struct AddressCollisionError { + value: BytesStruct*, +} + +struct KZGProofError { + value: BytesStruct*, +} diff --git a/cairo/ethereum/cancun/vm/gas.cairo b/cairo/ethereum/cancun/vm/gas.cairo index 1ee3d9ed..2eb520c9 100644 --- a/cairo/ethereum/cancun/vm/gas.cairo +++ b/cairo/ethereum/cancun/vm/gas.cairo @@ -1,8 +1,13 @@ from ethereum_types.numeric import U256, Uint, U64 from ethereum.utils.numeric import is_zero, divmod, taylor_exponential, min, ceil32 +from ethereum_types.bytes import BytesStruct from ethereum.cancun.blocks import Header from ethereum.cancun.transactions import Transaction -from starkware.cairo.common.math_cmp import is_le, is_not_zero +from ethereum.cancun.vm import Evm, EvmStruct, EvmImpl +from ethereum.cancun.vm.exceptions import OutOfGasError + +from starkware.cairo.common.math_cmp import is_le, is_not_zero, RC_BOUND +from starkware.cairo.common.math import assert_le_felt const TARGET_BLOB_GAS_PER_BLOCK = 393216; const GAS_INIT_CODE_WORD_COST = 2; @@ -20,6 +25,56 @@ struct MessageCallGas { value: MessageCallGasStruct*, } +// @notice Subtracts `amount` from `evm.gas_left`. +// @dev The gas left is decremented by the given amount. +// Use code adapted from is_nn. +// Assumption: gas_left < 2 ** 128 +// @param evm The pointer to the current execution context. +// @param amount The amount of gas the current operation requires. +// @return EVM The pointer to the updated execution context. +func charge_gas{range_check_ptr, evm: Evm}(amount: Uint) -> OutOfGasError { + // This is equivalent to is_nn(evm.value.gas_left - amount) + with_attr error_message("charge_gas: gas_left > 2**128") { + assert [range_check_ptr] = evm.value.gas_left.value; + tempvar range_check_ptr = range_check_ptr + 1; + } + + tempvar a = evm.value.gas_left.value - amount.value; // a is necessary for using the whitelisted hint + %{ memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1 %} + jmp out_of_range if [ap] != 0, ap++; + [range_check_ptr] = a; + ap += 20; + tempvar range_check_ptr = range_check_ptr + 1; + jmp enough_gas; + + out_of_range: + %{ memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1 %} + jmp need_felt_comparison if [ap] != 0, ap++; + assert [range_check_ptr] = (-a) - 1; + ap += 17; + tempvar range_check_ptr = range_check_ptr + 1; + jmp not_enough_gas; + + need_felt_comparison: + assert_le_felt(RC_BOUND, a); + jmp not_enough_gas; + + enough_gas: + let range_check_ptr = [ap - 1]; + let evm_struct = cast([fp - 4], EvmStruct*); + tempvar evm = Evm(evm_struct); + EvmImpl.set_gas_left(Uint(a)); + tempvar ok = OutOfGasError(cast(0, BytesStruct*)); + return ok; + + not_enough_gas: + let range_check_ptr = [ap - 1]; + let evm_struct = cast([fp - 4], EvmStruct*); + tempvar evm = Evm(evm_struct); + tempvar err = OutOfGasError(new BytesStruct(cast(0, felt*), 0)); + return err; +} + func calculate_memory_gas_cost{range_check_ptr}(size_in_bytes: Uint) -> Uint { let size = ceil32(size_in_bytes); let (size_in_words, _) = divmod(size.value, 32); diff --git a/cairo/tests/ethereum/cancun/vm/test_gas.py b/cairo/tests/ethereum/cancun/vm/test_gas.py index d9e82cb0..ce7d9fd8 100644 --- a/cairo/tests/ethereum/cancun/vm/test_gas.py +++ b/cairo/tests/ethereum/cancun/vm/test_gas.py @@ -1,3 +1,4 @@ +import pytest from ethereum_types.numeric import U256, Uint from hypothesis import assume, given from hypothesis import strategies as st @@ -12,12 +13,26 @@ calculate_memory_gas_cost, calculate_message_call_gas, calculate_total_blob_gas, + charge_gas, init_code_cost, max_message_call_gas, ) +from tests.utils.args_gen import Evm class TestGas: + @given(evm=..., amount=...) + def test_charge_gas(self, cairo_run, evm: Evm, amount: Uint): + try: + cairo_result = cairo_run("charge_gas", evm, amount) + except Exception as cairo_error: + with pytest.raises(type(cairo_error)): + charge_gas(evm, amount) + return + + charge_gas(evm, amount) + assert evm == cairo_result + @given(size_in_bytes=...) def test_calculate_memory_gas_cost(self, cairo_run, size_in_bytes: Uint): assert calculate_memory_gas_cost(size_in_bytes) == cairo_run( diff --git a/cairo/tests/utils/args_gen.py b/cairo/tests/utils/args_gen.py index 2dd13609..bb660f71 100644 --- a/cairo/tests/utils/args_gen.py +++ b/cairo/tests/utils/args_gen.py @@ -49,6 +49,8 @@ - Add the test generation strategy to strategies.py if it's a new type (not required when only doing composition of existing types, e.g. `Union[U256, bool]`) """ +import inspect +import sys from collections import ChainMap, abc, defaultdict from dataclasses import dataclass, fields, is_dataclass, make_dataclass from functools import partial @@ -119,7 +121,7 @@ from ethereum.cancun.vm import Environment as EnvironmentBase from ethereum.cancun.vm import Evm as EvmBase from ethereum.cancun.vm import Message as MessageBase -from ethereum.cancun.vm.exceptions import StackOverflowError, StackUnderflowError +from ethereum.cancun.vm.exceptions import ExceptionalHalt from ethereum.cancun.vm.gas import MessageCallGas from ethereum.crypto.hash import Hash32 from ethereum.exceptions import EthereumException @@ -214,6 +216,23 @@ def __eq__(self, other): ) +vm_exception_classes = inspect.getmembers( + sys.modules["ethereum.cancun.vm.exceptions"], + lambda x: inspect.isclass(x) and issubclass(x, ExceptionalHalt), +) + +vm_exception_mappings = { + ( + "ethereum", + "cancun", + "vm", + "exceptions", + f"{name}", + ): cls + for name, cls in vm_exception_classes + if cls is not ExceptionalHalt +} + _cairo_struct_to_python_type: Dict[Tuple[str, ...], Any] = { ("ethereum_types", "others", "None"): type(None), ("ethereum_types", "numeric", "bool"): bool, @@ -301,20 +320,6 @@ def __eq__(self, other): ("ethereum", "exceptions", "EthereumException"): EthereumException, ("ethereum", "cancun", "vm", "memory", "Memory"): Memory, ("ethereum", "cancun", "vm", "stack", "Stack"): Stack[U256], - ( - "ethereum", - "cancun", - "vm", - "exceptions", - "StackUnderflowError", - ): StackUnderflowError, - ( - "ethereum", - "cancun", - "vm", - "exceptions", - "StackOverflowError", - ): StackOverflowError, ("ethereum", "cancun", "trie", "Subnodes"): Annotated[Tuple[Extended, ...], 16], ("ethereum", "cancun", "state", "TransientStorage"): TransientStorage, ("ethereum", "cancun", "state", "MappingAddressTrieBytesU256"): Mapping[ @@ -344,6 +349,7 @@ def __eq__(self, other): ("ethereum", "cancun", "vm", "Evm"): Evm, ("ethereum", "cancun", "vm", "Memory"): Memory, ("ethereum", "cancun", "vm", "Stack"): Stack[U256], + **vm_exception_mappings, } # In the EELS, some functions are annotated with Sequence while it's actually just Bytes. diff --git a/cairo/tests/utils/strategies.py b/cairo/tests/utils/strategies.py index d1a545bf..3d2430b2 100644 --- a/cairo/tests/utils/strategies.py +++ b/cairo/tests/utils/strategies.py @@ -11,7 +11,8 @@ from starkware.cairo.lang.cairo_constants import DEFAULT_PRIME from ethereum.crypto.elliptic_curve import SECP256K1N -from tests.utils.args_gen import Memory, Stack +from ethereum.exceptions import EthereumException +from tests.utils.args_gen import Environment, Evm, Memory, Message, Stack # Mock the Extended type because hypothesis cannot handle the RLP Protocol # Needs to be done before importing the types from ethereum.cancun.trie @@ -59,6 +60,8 @@ st.one_of(st.binary(), uint, st.text(), st.booleans()), st.lists ) +small_bytes = st.binary(min_size=0, max_size=256) + # See https://github.com/ethereum/execution-specs/issues/1036 # It's currently not possible to generate strategies using `st.builds` because the dataclasses @@ -89,13 +92,56 @@ def stack_strategy(thing): ) -def memory_strategy(): - """ - Generating up to 2**13 bytes of memory is enough for most tests as more would take too long - in the test runner. - 2**32 bytes would be the value at which the memory expansion would trigger an OOG - """ - return st.binary(min_size=0, max_size=2**13).map(bytearray) +# Generating up to 2**13 bytes of memory is enough for most tests as more would take too long +# in the test runner. +# 2**32 bytes would be the value at which the memory expansion would trigger an OOG +memory = st.binary(min_size=0, max_size=2**13).map(Memory) + + +evm = st.fixed_dictionaries( + { + "pc": st.from_type(Uint), + "stack": stack_strategy(Stack[U256]), + "memory": memory, + "code": small_bytes, + "gas_left": uint, + "env": st.from_type(Environment), + "valid_jump_destinations": st.sets(st.from_type(Uint)), + "logs": st.tuples(st.from_type(Log)), + "refund_counter": st.integers(min_value=0), + "running": st.booleans(), + "message": st.from_type(Message), + "output": small_bytes, + "accounts_to_delete": st.sets(st.from_type(Address)), + "touched_accounts": st.sets(st.from_type(Address)), + "return_data": small_bytes, + "error": st.none() | st.from_type(EthereumException), + "accessed_addresses": st.sets(st.from_type(Address)), + "accessed_storage_keys": st.sets( + st.tuples(st.from_type(Address), st.from_type(Bytes32)) + ), + } +).map(lambda x: Evm(**x)) + + +message = st.fixed_dictionaries( + { + "caller": address, + "target": st.one_of(bytes0, address), + "current_target": address, + "gas": uint, + "value": uint256, + "data": small_bytes, + "code_address": st.none() | address, + "code": small_bytes, + "depth": uint, + "should_transfer_value": st.booleans(), + "is_static": st.booleans(), + "accessed_addresses": st.sets(address), + "accessed_storage_keys": st.sets(st.tuples(address, bytes32)), + "parent_evm": st.none() | evm, + } +).map(lambda x: Message(**x)) # Fork @@ -188,4 +234,5 @@ def register_type_strategies(): st.register_type_strategy(PrivateKey, private_key) st.register_type_strategy(Trie, trie_strategy) st.register_type_strategy(Stack, stack_strategy) - st.register_type_strategy(Memory, memory_strategy) + st.register_type_strategy(Memory, memory) + st.register_type_strategy(Evm, evm)