From aa82dfb2fb393212961d0093c30b8f7fc971451d Mon Sep 17 00:00:00 2001 From: fselmo Date: Mon, 17 Apr 2023 17:41:41 -0600 Subject: [PATCH 1/4] Refactor ``BaseComputation`` to be simpler and subclass ``MessageComputation``: - The current implementation of the computation classes (and the base computation class) will become stale when EOF is introduced because it always assumes a "message" which has transaction fields. We should refactor the actual base computation class to have only the inherent properties of base computations. - The ``MessageComputation`` class then inherits from a simpler, stripped-down ``BaseComputation`` class. Existing VM computation classes are now ``MessageComputation`` classes since they inherently require a ``message`` and ``transaction_context`` starting at the ``__init__()`` method and stretching across some of its methods and properties. - This hierarchy paves the way for EOF computations, and possibly other computations, to exist within a more flexible model. EOF computations are likely to be properties of message computations since an EOF computation cannot be executed without a message / transaction. --- docs/api/api.abc.rst | 4 +- docs/api/vm/api.vm.computation.rst | 4 +- docs/guides/creating_opcodes.rst | 4 +- docs/release_notes.rst | 4 +- eth/abc.py | 273 ++++---- eth/chains/base.py | 8 +- eth/precompiles/blake2.py | 4 +- eth/precompiles/ecadd.py | 6 +- eth/precompiles/ecmul.py | 6 +- eth/precompiles/ecpairing.py | 6 +- eth/precompiles/ecrecover.py | 4 +- eth/precompiles/identity.py | 4 +- eth/precompiles/modexp.py | 6 +- eth/precompiles/ripemd160.py | 4 +- eth/precompiles/sha256.py | 4 +- eth/rlp/transactions.py | 4 +- eth/vm/base.py | 10 +- eth/vm/computation.py | 595 ------------------ eth/vm/computation/__init__.py | 4 + eth/vm/computation/base_computation.py | 346 ++++++++++ eth/vm/computation/message_computation.py | 357 +++++++++++ eth/vm/forks/arrow_glacier/computation.py | 8 +- eth/vm/forks/arrow_glacier/state.py | 4 +- eth/vm/forks/berlin/computation.py | 8 +- eth/vm/forks/berlin/logic.py | 35 +- eth/vm/forks/berlin/state.py | 8 +- eth/vm/forks/byzantium/__init__.py | 4 +- eth/vm/forks/byzantium/computation.py | 9 +- eth/vm/forks/byzantium/opcodes.py | 4 +- eth/vm/forks/byzantium/state.py | 4 +- eth/vm/forks/constantinople/computation.py | 11 +- eth/vm/forks/constantinople/state.py | 4 +- eth/vm/forks/frontier/__init__.py | 8 +- eth/vm/forks/frontier/computation.py | 14 +- eth/vm/forks/frontier/state.py | 20 +- eth/vm/forks/gray_glacier/computation.py | 8 +- eth/vm/forks/gray_glacier/state.py | 4 +- eth/vm/forks/homestead/computation.py | 12 +- eth/vm/forks/homestead/state.py | 6 +- eth/vm/forks/istanbul/computation.py | 11 +- eth/vm/forks/istanbul/state.py | 4 +- eth/vm/forks/istanbul/storage.py | 7 +- eth/vm/forks/london/computation.py | 8 +- eth/vm/forks/london/state.py | 8 +- eth/vm/forks/muir_glacier/computation.py | 9 +- eth/vm/forks/muir_glacier/state.py | 4 +- eth/vm/forks/paris/computation.py | 9 +- eth/vm/forks/paris/state.py | 4 +- eth/vm/forks/petersburg/computation.py | 9 +- eth/vm/forks/petersburg/state.py | 4 +- eth/vm/forks/shanghai/computation.py | 10 +- eth/vm/forks/shanghai/state.py | 2 +- eth/vm/forks/spurious_dragon/_utils.py | 4 +- eth/vm/forks/spurious_dragon/computation.py | 13 +- eth/vm/forks/spurious_dragon/state.py | 8 +- eth/vm/forks/tangerine_whistle/computation.py | 9 +- eth/vm/forks/tangerine_whistle/state.py | 2 +- eth/vm/logic/arithmetic.py | 30 +- eth/vm/logic/block.py | 18 +- eth/vm/logic/call.py | 46 +- eth/vm/logic/comparison.py | 24 +- eth/vm/logic/context.py | 46 +- eth/vm/logic/duplication.py | 4 +- eth/vm/logic/flow.py | 14 +- eth/vm/logic/invalid.py | 4 +- eth/vm/logic/logging.py | 4 +- eth/vm/logic/memory.py | 10 +- eth/vm/logic/sha3.py | 4 +- eth/vm/logic/stack.py | 6 +- eth/vm/logic/storage.py | 8 +- eth/vm/logic/swap.py | 4 +- eth/vm/logic/system.py | 47 +- eth/vm/opcode.py | 4 +- eth/vm/state.py | 18 +- tests/core/vm/test_base_computation.py | 6 +- tests/core/vm/test_frontier_computation.py | 6 +- tests/json-fixtures/test_virtual_machine.py | 6 +- 77 files changed, 1221 insertions(+), 1041 deletions(-) delete mode 100644 eth/vm/computation.py create mode 100644 eth/vm/computation/__init__.py create mode 100644 eth/vm/computation/base_computation.py create mode 100644 eth/vm/computation/message_computation.py diff --git a/docs/api/api.abc.rst b/docs/api/api.abc.rst index 0d878e2ca3..c94bc4db18 100644 --- a/docs/api/api.abc.rst +++ b/docs/api/api.abc.rst @@ -157,10 +157,10 @@ ExecutionContextAPI :members: -ComputationAPI +MessageComputationAPI -------------- -.. autoclass:: eth.abc.ComputationAPI +.. autoclass:: eth.abc.MessageComputationAPI :members: diff --git a/docs/api/vm/api.vm.computation.rst b/docs/api/vm/api.vm.computation.rst index b4e579ff50..f7f391d46b 100644 --- a/docs/api/vm/api.vm.computation.rst +++ b/docs/api/vm/api.vm.computation.rst @@ -1,9 +1,9 @@ Computation =========== -BaseComputation +MessageComputation --------------- -.. autoclass:: eth.vm.computation.BaseComputation +.. autoclass:: eth.vm.computation.MessageComputation :members: diff --git a/docs/guides/creating_opcodes.rst b/docs/guides/creating_opcodes.rst index f65585c1e6..6fc7b67875 100644 --- a/docs/guides/creating_opcodes.rst +++ b/docs/guides/creating_opcodes.rst @@ -1,7 +1,7 @@ Creating Opcodes ================ -An opcode is just a function which takes a :class:`~eth.vm.computation.BaseComputation` +An opcode is just a function which takes a :class:`~eth.vm.computation.MessageComputation` instance as it's sole argument. If an opcode function has a return value, this value will be discarded during normal VM execution. @@ -52,7 +52,7 @@ Usage of the :func:`~eth.vm.opcode.as_opcode` helper: def custom_op(computation): ... # opcode logic here - class ExampleComputation(BaseComputation): + class ExampleComputation(MessageComputation): opcodes = { b'\x01': as_opcode(custom_op, 'CUSTOM_OP', 10), } diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 6acc245d6b..b100336ebc 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -423,8 +423,8 @@ Internal Changes - for Contributors - Fix for creating a duplicate "ghost" Computation that was never used. It didn't break anything, but was inelegant and surprising to get extra objects created that were mostly useless. This was achieved by changing - :meth:`eth.abc.ComputationAPI.apply_message` and - :meth:`eth.abc.ComputationAPI.apply_create_message` to be class methods. (`#1921 `__) + :meth:`eth.abc.MessageComputationAPI.apply_message` and + :meth:`eth.abc.MessageComputationAPI.apply_create_message` to be class methods. (`#1921 `__) py-evm 0.3.0-alpha.14 (2020-02-10) diff --git a/eth/abc.py b/eth/abc.py index ea0071d6fc..b688133697 100644 --- a/eth/abc.py +++ b/eth/abc.py @@ -347,7 +347,7 @@ def get_intrinsic_gas(self) -> int: ... @abstractmethod - def gas_used_by(self, computation: 'ComputationAPI') -> int: + def gas_used_by(self, computation: 'MessageComputationAPI') -> int: """ Return the gas used by the given computation. In Frontier, for example, this is sum of the intrinsic cost and the gas used @@ -1351,6 +1351,7 @@ class GasMeterAPI(ABC): """ A class to define a gas meter. """ + start_gas: int gas_refunded: int gas_remaining: int @@ -1439,7 +1440,7 @@ class OpcodeAPI(ABC): mnemonic: str @abstractmethod - def __call__(self, computation: 'ComputationAPI') -> None: + def __call__(self, computation: 'MessageComputationAPI') -> None: """ Execute the logic of the opcode. """ @@ -1448,7 +1449,7 @@ def __call__(self, computation: 'ComputationAPI') -> None: @classmethod @abstractmethod def as_opcode(cls: Type[T], - logic_fn: Callable[['ComputationAPI'], None], + logic_fn: Callable[['MessageComputationAPI'], None], mnemonic: str, gas_cost: int) -> T: """ @@ -1860,41 +1861,36 @@ def base_fee_per_gas(self) -> Optional[int]: ... -class ComputationAPI(ContextManager['ComputationAPI'], StackManipulationAPI): +class ComputationAPI( + ContextManager['ComputationAPI'], + StackManipulationAPI, +): """ - The base class for all execution computations. + The base abstract class for all execution computations. """ - msg: MessageAPI + logger: ExtendedDebugLogger + + state: "StateAPI" code: CodeStreamAPI - opcodes: Dict[int, OpcodeAPI] = None - state: 'StateAPI' return_data: bytes + # VM configuration + opcodes: Dict[int, OpcodeAPI] + _precompiles: Dict[Address, Callable[["ComputationAPI"], "ComputationAPI"]] + @abstractmethod - def __init__(self, - state: 'StateAPI', - message: MessageAPI, - transaction_context: TransactionContextAPI) -> None: - """ - Instantiate the computation. - """ + def __init__(self, state: "StateAPI") -> None: ... - # - # Convenience - # - @property @abstractmethod - def is_origin_computation(self) -> bool: + def _configure_gas_meter(self) -> GasMeterAPI: """ - Return ``True`` if this computation is the outermost computation at ``depth == 0``. + Configure the gas meter for the computation at class initialization. """ ... - # - # Error handling - # + # -- error handling -- # @property @abstractmethod def is_success(self) -> bool: @@ -1963,9 +1959,7 @@ def should_erase_return_data(self) -> bool: """ ... - # - # Memory Management - # + # -- memory management -- # @abstractmethod def extend_memory(self, start_position: int, size: int) -> None: """ @@ -1978,14 +1972,16 @@ def extend_memory(self, start_position: int, size: int) -> None: @abstractmethod def memory_write(self, start_position: int, size: int, value: bytes) -> None: """ - Write ``value`` to memory at ``start_position``. Require that ``len(value) == size``. + Write ``value`` to memory at ``start_position``. Require that + ``len(value) == size``. """ ... @abstractmethod def memory_read(self, start_position: int, size: int) -> memoryview: """ - Read and return a view of ``size`` bytes from memory starting at ``start_position``. + Read and return a view of ``size`` bytes from memory starting at + ``start_position``. """ ... @@ -1996,13 +1992,11 @@ def memory_read_bytes(self, start_position: int, size: int) -> bytes: """ ... - # - # Gas Consumption - # + # -- gas consumption -- # @abstractmethod def get_gas_meter(self) -> GasMeterAPI: """ - Return the :class:`~eth.abc.GasMeterAPI` of the computation. + Return the gas meter for the computation. """ ... @@ -2028,13 +2022,6 @@ def refund_gas(self, amount: int) -> None: """ ... - @abstractmethod - def get_gas_refund(self) -> int: - """ - Return the number of refunded gas. - """ - ... - @abstractmethod def get_gas_used(self) -> int: """ @@ -2049,9 +2036,7 @@ def get_gas_remaining(self) -> int: """ ... - # - # Stack management - # + # -- stack management -- # @abstractmethod def stack_swap(self, position: int) -> None: """ @@ -2066,9 +2051,7 @@ def stack_dup(self, position: int) -> None: """ ... - # - # Computation result - # + # -- computation result -- # @property @abstractmethod def output(self) -> bytes: @@ -2087,9 +2070,73 @@ def output(self, value: bytes) -> None: # sure that the setter doesn't actually get used as a noop. raise NotImplementedError - # - # Runtime operations - # + # -- opcode API -- # + @property + @abstractmethod + def precompiles(self) -> Dict[ + Address, Callable[["ComputationAPI"], None] + ]: + """ + Return a dictionary where the keys are the addresses of precompiles and the + values are the precompile functions. + """ + ... + + @classmethod + @abstractmethod + def get_precompiles(cls) -> Dict[ + Address, Callable[["ComputationAPI"], None] + ]: + """ + Return a dictionary where the keys are the addresses of precompiles and the + values are the precompile functions. + """ + ... + + @abstractmethod + def get_opcode_fn(self, opcode: int) -> OpcodeAPI: + """ + Return the function for the given ``opcode``. + """ + ... + + +class MessageComputationAPI( + ComputationAPI, + ContextManager['MessageComputationAPI'], +): + """ + The base abstract class for all execution *message* computations. + """ + + msg: MessageAPI + transaction_context: TransactionContextAPI + children: List["MessageComputationAPI"] + + @abstractmethod + def __init__( + self, + state: 'StateAPI', + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> None: + """ + Instantiate the message computation. + """ + ... + + # -- convenience -- # + @property + @abstractmethod + def is_origin_computation(self) -> bool: + """ + Return ``True`` if this message computation is the outermost computation at + ``depth == 0``. Since EOF computations cannot exist without a message, this + is solely an inherent property of message computations. + """ + ... + + # -- runtime operations -- # @abstractmethod def prepare_child_message(self, gas: int, @@ -2099,34 +2146,49 @@ def prepare_child_message(self, code: bytes, **kwargs: Any) -> MessageAPI: """ - Helper method for creating a child computation. + Helper method for creating a child message computation. """ ... @abstractmethod - def apply_child_computation(self, child_msg: MessageAPI) -> 'ComputationAPI': + def apply_child_message_computation( + self, + child_msg: MessageAPI, + ) -> 'MessageComputationAPI': """ - Apply the vm message ``child_msg`` as a child computation. + Apply the vm message ``child_msg`` as a child message computation. """ ... @abstractmethod - def generate_child_computation(self, child_msg: MessageAPI) -> 'ComputationAPI': + def generate_child_message_computation( + self, + child_msg: MessageAPI, + ) -> 'MessageComputationAPI': """ - Generate a child computation from the given ``child_msg``. + Generate a child message computation from the given ``child_msg``. """ ... @abstractmethod - def add_child_computation(self, child_computation: 'ComputationAPI') -> None: + def add_child_message_computation( + self, + child_message_computation: 'MessageComputationAPI', + ) -> None: """ Add the given ``child_computation``. """ ... - # - # Account management - # + # -- gas consumption -- # + @abstractmethod + def get_gas_refund(self) -> int: + """ + Return the number of refunded gas. + """ + ... + + # -- account management -- # @abstractmethod def register_account_for_deletion(self, beneficiary: Address) -> None: """ @@ -2141,9 +2203,7 @@ def get_accounts_for_deletion(self) -> Tuple[Tuple[Address, Address], ...]: """ ... - # - # EVM logging - # + # -- EVM logging -- # @abstractmethod def add_log_entry(self, account: Address, topics: Tuple[int, ...], data: bytes) -> None: """ @@ -2168,16 +2228,14 @@ def get_log_entries(self) -> Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]: """ ... - # - # State Transition - # + # -- state transition -- # @classmethod @abstractmethod def apply_message( cls, state: 'StateAPI', message: MessageAPI, - transaction_context: TransactionContextAPI) -> 'ComputationAPI': + transaction_context: TransactionContextAPI) -> 'MessageComputationAPI': """ Execute a VM message. This is where the VM-specific call logic exists. """ @@ -2189,7 +2247,7 @@ def apply_create_message( cls, state: 'StateAPI', message: MessageAPI, - transaction_context: TransactionContextAPI) -> 'ComputationAPI': + transaction_context: TransactionContextAPI) -> 'MessageComputationAPI': """ Execute a VM message to create a new contract. This is where the VM-specific create logic exists. @@ -2198,10 +2256,12 @@ def apply_create_message( @classmethod @abstractmethod - def apply_computation(cls, - state: 'StateAPI', - message: MessageAPI, - transaction_context: TransactionContextAPI) -> 'ComputationAPI': + def apply_computation( + cls, + state: 'StateAPI', + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> 'MessageComputationAPI': """ Execute the logic within the message: Either run the precompile, or step through each opcode. Generally, the only VM-specific logic is for @@ -2214,34 +2274,6 @@ def apply_computation(cls, """ ... - # - # Opcode API - # - @property - @abstractmethod - def precompiles(self) -> Dict[Address, Callable[['ComputationAPI'], None]]: - """ - Return a dictionary where the keys are the addresses of precompiles and the values are - the precompile functions. - """ - ... - - @classmethod - @abstractmethod - def get_precompiles(cls) -> Dict[Address, Callable[['ComputationAPI'], None]]: - """ - Return a dictionary where the keys are the addresses of precompiles and the values are - the precompile functions. - """ - ... - - @abstractmethod - def get_opcode_fn(self, opcode: int) -> OpcodeAPI: - """ - Return the function for the given ``opcode``. - """ - ... - class AccountStorageDatabaseAPI(ABC): """ @@ -2637,9 +2669,9 @@ def __init__(self, vm_state: 'StateAPI') -> None: ... @abstractmethod - def __call__(self, transaction: SignedTransactionAPI) -> 'ComputationAPI': + def __call__(self, transaction: SignedTransactionAPI) -> 'MessageComputationAPI': """ - Execute the ``transaction`` and return a :class:`eth.abc.ComputationAPI`. + Execute the ``transaction`` and return a :class:`eth.abc.MessageComputationAPI`. """ ... @@ -2661,7 +2693,7 @@ def build_evm_message(self, transaction: SignedTransactionAPI) -> MessageAPI: @abstractmethod def build_computation(self, message: MessageAPI, - transaction: SignedTransactionAPI) -> 'ComputationAPI': + transaction: SignedTransactionAPI) -> 'MessageComputationAPI': """ Apply the ``message`` to the VM and use the given ``transaction`` to retrieve the context from. @@ -2672,7 +2704,7 @@ def build_computation(self, @abstractmethod def finalize_computation(self, transaction: SignedTransactionAPI, - computation: 'ComputationAPI') -> 'ComputationAPI': + computation: 'MessageComputationAPI') -> 'MessageComputationAPI': """ Finalize the ``transaction``. """ @@ -2702,7 +2734,7 @@ class StateAPI(ConfigurableAPI): Each :class:`~eth.abc.StateAPI` class must be configured with: - - ``computation_class``: The :class:`~eth.abc.ComputationAPI` class for + - ``message_computation_class``: The :class:`~eth.abc.MessageComputationAPI` class for vm execution. - ``transaction_context_class``: The :class:`~eth.abc.TransactionContextAPI` class for vm execution. @@ -2712,7 +2744,7 @@ class for vm execution. # execution_context: ExecutionContextAPI - computation_class: Type[ComputationAPI] + message_computation_class: Type[MessageComputationAPI] transaction_context_class: Type[TransactionContextAPI] account_db_class: Type[AccountDatabaseAPI] transaction_executor_class: Type[TransactionExecutorAPI] = None @@ -3071,7 +3103,7 @@ def get_ancestor_hash(self, block_number: BlockNumber) -> Hash32: @abstractmethod def get_computation(self, message: MessageAPI, - transaction_context: TransactionContextAPI) -> ComputationAPI: + transaction_context: TransactionContextAPI) -> MessageComputationAPI: """ Return a computation instance for the given `message` and `transaction_context` """ @@ -3093,7 +3125,10 @@ def get_transaction_context_class(cls) -> Type[TransactionContextAPI]: # Execution # @abstractmethod - def apply_transaction(self, transaction: SignedTransactionAPI) -> ComputationAPI: + def apply_transaction( + self, + transaction: SignedTransactionAPI, + ) -> MessageComputationAPI: """ Apply transaction to the vm state @@ -3110,8 +3145,10 @@ def get_transaction_executor(self) -> TransactionExecutorAPI: ... @abstractmethod - def costless_execute_transaction(self, - transaction: SignedTransactionAPI) -> ComputationAPI: + def costless_execute_transaction( + self, + transaction: SignedTransactionAPI, + ) -> MessageComputationAPI: """ Execute the given ``transaction`` with a gas price of ``0``. """ @@ -3281,7 +3318,7 @@ def transaction_applied_hook( transactions: Sequence[SignedTransactionAPI], base_header: BlockHeaderAPI, partial_header: BlockHeaderAPI, - computation: ComputationAPI, + computation: MessageComputationAPI, receipt: ReceiptAPI) -> None: """ A hook for a subclass to use as a way to note that a transaction was applied. @@ -3297,7 +3334,7 @@ def transaction_applied_hook( def apply_transaction(self, header: BlockHeaderAPI, transaction: SignedTransactionAPI - ) -> Tuple[ReceiptAPI, ComputationAPI]: + ) -> Tuple[ReceiptAPI, MessageComputationAPI]: """ Apply the transaction to the current block. This is a wrapper around :func:`~eth.vm.state.State.apply_transaction` with some extra orchestration logic. @@ -3328,7 +3365,7 @@ def execute_bytecode(self, value: int, data: bytes, code: bytes, - code_address: Address = None) -> ComputationAPI: + code_address: Address = None) -> MessageComputationAPI: """ Execute raw bytecode in the context of the current state of the virtual machine. Note that this skips over some of the logic @@ -3340,8 +3377,8 @@ def execute_bytecode(self, - others... For other potential surprises, check the implementation differences - between :meth:`ComputationAPI.apply_computation` and - :meth:`ComputationAPI.apply_message`. (depending on the VM fork) + between :meth:`MessageComputationAPI.apply_computation` and + :meth:`MessageComputationAPI.apply_message`. (depending on the VM fork) """ ... @@ -3350,7 +3387,7 @@ def apply_all_transactions( self, transactions: Sequence[SignedTransactionAPI], base_header: BlockHeaderAPI - ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: """ Determine the results of applying all transactions to the base header. This does *not* update the current block or header of the VM. @@ -3374,7 +3411,7 @@ def apply_all_withdrawals(self, withdrawals: Sequence[WithdrawalAPI]) -> None: def make_receipt(self, base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, - computation: ComputationAPI, + computation: MessageComputationAPI, state: StateAPI) -> ReceiptAPI: """ Generate the receipt resulting from applying the transaction. @@ -4008,7 +4045,7 @@ def build_block_with_transactions_and_withdrawals( transactions: Tuple[SignedTransactionAPI, ...], parent_header: BlockHeaderAPI = None, withdrawals: Tuple[WithdrawalAPI, ...] = None, - ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: """ Generate a block with the provided transactions. This does *not* import that block into your chain. If you want this new block in your chain, @@ -4234,7 +4271,7 @@ def mine_all( *args: Any, parent_header: BlockHeaderAPI = None, **kwargs: Any, - ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: """ Build a block with the given transactions, and mine it. @@ -4248,7 +4285,7 @@ def mine_all( @abstractmethod def apply_transaction(self, transaction: SignedTransactionAPI - ) -> Tuple[BlockAPI, ReceiptAPI, ComputationAPI]: + ) -> Tuple[BlockAPI, ReceiptAPI, MessageComputationAPI]: """ Apply the transaction to the current tip block. diff --git a/eth/chains/base.py b/eth/chains/base.py index 04673793b2..67e9e88d14 100644 --- a/eth/chains/base.py +++ b/eth/chains/base.py @@ -47,7 +47,7 @@ ConsensusContextAPI, VirtualMachineAPI, ReceiptAPI, - ComputationAPI, + MessageComputationAPI, StateAPI, SignedTransactionAPI, UnsignedTransactionAPI, @@ -352,7 +352,7 @@ def build_block_with_transactions_and_withdrawals( transactions: Sequence[SignedTransactionAPI], parent_header: BlockHeaderAPI = None, withdrawals: Sequence[WithdrawalAPI] = None, - ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: base_header = self.ensure_header(parent_header) vm = self.get_vm(base_header) @@ -645,7 +645,7 @@ def __init__(self, base_db: AtomicDatabaseAPI, header: BlockHeaderAPI = None) -> def apply_transaction( self, transaction: SignedTransactionAPI, - ) -> Tuple[BlockAPI, ReceiptAPI, ComputationAPI]: + ) -> Tuple[BlockAPI, ReceiptAPI, MessageComputationAPI]: vm = self.get_vm(self.header) base_block = vm.get_block() @@ -696,7 +696,7 @@ def mine_all( parent_header: BlockHeaderAPI = None, withdrawals: Sequence[WithdrawalAPI] = None, **kwargs: Any, - ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: if parent_header is None: base_header = self.header diff --git a/eth/precompiles/blake2.py b/eth/precompiles/blake2.py index 22c9a9accd..ac6a16cd7e 100644 --- a/eth/precompiles/blake2.py +++ b/eth/precompiles/blake2.py @@ -7,7 +7,7 @@ VMError, ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) try: @@ -18,7 +18,7 @@ GAS_COST_PER_ROUND = 1 -def blake2b_fcompress(computation: BaseComputation) -> BaseComputation: +def blake2b_fcompress(computation: MessageComputation) -> MessageComputation: try: parameters = extract_blake2b_parameters(computation.msg.data_as_bytes) except ValidationError as exc: diff --git a/eth/precompiles/ecadd.py b/eth/precompiles/ecadd.py index c17331e0df..eecf18813c 100644 --- a/eth/precompiles/ecadd.py +++ b/eth/precompiles/ecadd.py @@ -27,14 +27,14 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) @curry def ecadd( - computation: BaseComputation, - gas_cost: int = constants.GAS_ECADD) -> BaseComputation: + computation: MessageComputation, + gas_cost: int = constants.GAS_ECADD) -> MessageComputation: computation.consume_gas(gas_cost, reason='ECADD Precompile') diff --git a/eth/precompiles/ecmul.py b/eth/precompiles/ecmul.py index aaf0ef9d52..506815dfec 100644 --- a/eth/precompiles/ecmul.py +++ b/eth/precompiles/ecmul.py @@ -27,14 +27,14 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) @curry def ecmul( - computation: BaseComputation, - gas_cost: int = constants.GAS_ECMUL) -> BaseComputation: + computation: MessageComputation, + gas_cost: int = constants.GAS_ECMUL) -> MessageComputation: computation.consume_gas(gas_cost, reason='ECMUL Precompile') diff --git a/eth/precompiles/ecpairing.py b/eth/precompiles/ecpairing.py index 6d435e6562..971c86694d 100644 --- a/eth/precompiles/ecpairing.py +++ b/eth/precompiles/ecpairing.py @@ -31,7 +31,7 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) @@ -41,9 +41,9 @@ @curry def ecpairing( - computation: BaseComputation, + computation: MessageComputation, gas_cost_base: int = constants.GAS_ECPAIRING_BASE, - gas_cost_per_point: int = constants.GAS_ECPAIRING_PER_POINT) -> BaseComputation: + gas_cost_per_point: int = constants.GAS_ECPAIRING_PER_POINT) -> MessageComputation: if len(computation.msg.data) % 192: # data length must be an exact multiple of 192 diff --git a/eth/precompiles/ecrecover.py b/eth/precompiles/ecrecover.py index 4774d58133..2a1b0e83bd 100644 --- a/eth/precompiles/ecrecover.py +++ b/eth/precompiles/ecrecover.py @@ -21,11 +21,11 @@ validate_lte, ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) -def ecrecover(computation: BaseComputation) -> BaseComputation: +def ecrecover(computation: MessageComputation) -> MessageComputation: computation.consume_gas(constants.GAS_ECRECOVER, reason="ECRecover Precompile") data = computation.msg.data_as_bytes raw_message_hash = data[:32] diff --git a/eth/precompiles/identity.py b/eth/precompiles/identity.py index f45c085cfb..22b76cbe05 100644 --- a/eth/precompiles/identity.py +++ b/eth/precompiles/identity.py @@ -4,11 +4,11 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) -def identity(computation: BaseComputation) -> BaseComputation: +def identity(computation: MessageComputation) -> MessageComputation: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_IDENTITY + word_count * constants.GAS_IDENTITYWORD diff --git a/eth/precompiles/modexp.py b/eth/precompiles/modexp.py index d1f4942789..9cad85a7ff 100644 --- a/eth/precompiles/modexp.py +++ b/eth/precompiles/modexp.py @@ -23,7 +23,7 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) @@ -127,9 +127,9 @@ def _modexp(data: bytes) -> int: @curry def modexp( - computation: BaseComputation, + computation: MessageComputation, gas_calculator: Callable[[bytes], int] = _compute_modexp_gas_fee_eip_198 -) -> BaseComputation: +) -> MessageComputation: """ https://github.com/ethereum/EIPs/pull/198 """ diff --git a/eth/precompiles/ripemd160.py b/eth/precompiles/ripemd160.py index 603167d03b..573c0d550a 100644 --- a/eth/precompiles/ripemd160.py +++ b/eth/precompiles/ripemd160.py @@ -9,11 +9,11 @@ pad32, ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) -def ripemd160(computation: BaseComputation) -> BaseComputation: +def ripemd160(computation: MessageComputation) -> MessageComputation: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_RIPEMD160 + word_count * constants.GAS_RIPEMD160WORD diff --git a/eth/precompiles/sha256.py b/eth/precompiles/sha256.py index 68231d204a..73960e9440 100644 --- a/eth/precompiles/sha256.py +++ b/eth/precompiles/sha256.py @@ -7,11 +7,11 @@ ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) -def sha256(computation: BaseComputation) -> BaseComputation: +def sha256(computation: MessageComputation) -> MessageComputation: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_SHA256 + word_count * constants.GAS_SHA256WORD diff --git a/eth/rlp/transactions.py b/eth/rlp/transactions.py index 7781a7813a..14f6780a19 100644 --- a/eth/rlp/transactions.py +++ b/eth/rlp/transactions.py @@ -24,7 +24,7 @@ from eth.abc import ( BaseTransactionAPI, - ComputationAPI, + MessageComputationAPI, LegacyTransactionFieldsAPI, SignedTransactionAPI, TransactionBuilderAPI, @@ -44,7 +44,7 @@ def validate(self) -> None: def intrinsic_gas(self) -> int: return self.get_intrinsic_gas() - def gas_used_by(self, computation: ComputationAPI) -> int: + def gas_used_by(self, computation: MessageComputationAPI) -> int: return self.get_intrinsic_gas() + computation.get_gas_used() @property diff --git a/eth/vm/base.py b/eth/vm/base.py index d2fde2cfc6..cc7dfc5f1a 100644 --- a/eth/vm/base.py +++ b/eth/vm/base.py @@ -35,7 +35,7 @@ BlockHeaderAPI, ChainContextAPI, ChainDatabaseAPI, - ComputationAPI, + MessageComputationAPI, ConsensusAPI, ConsensusContextAPI, ExecutionContextAPI, @@ -166,7 +166,7 @@ def logger(self) -> logging.Logger: def apply_transaction(self, header: BlockHeaderAPI, transaction: SignedTransactionAPI - ) -> Tuple[ReceiptAPI, ComputationAPI]: + ) -> Tuple[ReceiptAPI, MessageComputationAPI]: self.validate_transaction_against_header(header, transaction) # Mark current state as un-revertable, since new transaction is starting... @@ -222,7 +222,7 @@ def execute_bytecode(self, data: bytes, code: bytes, code_address: Address = None, - ) -> ComputationAPI: + ) -> MessageComputationAPI: if origin is None: origin = sender @@ -244,7 +244,7 @@ def execute_bytecode(self, ) # Execute it in the VM - return self.state.computation_class.apply_computation( + return self.state.message_computation_class.apply_computation( self.state, message, transaction_context, @@ -254,7 +254,7 @@ def apply_all_transactions( self, transactions: Sequence[SignedTransactionAPI], base_header: BlockHeaderAPI - ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: + ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: vm_header = self.get_header() if base_header.block_number != vm_header.block_number: raise ValidationError( diff --git a/eth/vm/computation.py b/eth/vm/computation.py deleted file mode 100644 index 30ebb29bbb..0000000000 --- a/eth/vm/computation.py +++ /dev/null @@ -1,595 +0,0 @@ -import itertools -from types import TracebackType -from typing import ( - Any, - Callable, - Dict, - List, - Optional, - Tuple, - Type, - Union, - cast, -) - -from cached_property import cached_property -from eth_typing import ( - Address, -) -from eth_utils import ( - encode_hex, - get_extended_debug_logger, -) - -from eth.abc import ( - MemoryAPI, - StackAPI, - GasMeterAPI, - MessageAPI, - OpcodeAPI, - CodeStreamAPI, - ComputationAPI, - StateAPI, - TransactionContextAPI, -) -from eth.constants import ( - GAS_MEMORY, - GAS_MEMORY_QUADRATIC_DENOMINATOR, -) -from eth.exceptions import ( - Halt, - VMError, -) -from eth.typing import ( - BytesOrView, -) -from eth._utils.datatypes import ( - Configurable, -) -from eth._utils.numeric import ( - ceil32, -) -from eth.validation import ( - validate_canonical_address, - validate_is_bytes, - validate_uint256, -) -from eth.vm.code_stream import ( - CodeStream, -) -from eth.vm.gas_meter import ( - GasMeter, -) -from eth.vm.logic.invalid import ( - InvalidOpcode, -) -from eth.vm.memory import ( - Memory, -) -from eth.vm.message import ( - Message, -) -from eth.vm.stack import ( - Stack, -) - - -def NO_RESULT(computation: ComputationAPI) -> None: - """ - This is a special method intended for usage as the "no precompile found" result. - The type signature is designed to match the other precompiles. - """ - raise Exception("This method is never intended to be executed") - - -def memory_gas_cost(size_in_bytes: int) -> int: - size_in_words = ceil32(size_in_bytes) // 32 - linear_cost = size_in_words * GAS_MEMORY - quadratic_cost = size_in_words ** 2 // GAS_MEMORY_QUADRATIC_DENOMINATOR - - total_cost = linear_cost + quadratic_cost - return total_cost - - -class BaseComputation(Configurable, ComputationAPI): - """ - The base class for all execution computations. - - .. note:: - - Each :class:`~eth.vm.computation.BaseComputation` class must be configured with: - - ``opcodes``: A mapping from the opcode integer value to the logic function for the opcode. - - ``_precompiles``: A mapping of contract address to the precompile function for execution - of precompiled contracts. - """ - state: StateAPI = None - msg: MessageAPI = None - transaction_context: TransactionContextAPI = None - - _memory: MemoryAPI = None - _stack: StackAPI = None - _gas_meter: GasMeterAPI = None - - code: CodeStreamAPI = None - - children: List[ComputationAPI] = None - - _output: bytes = b'' - return_data: bytes = b'' - _error: VMError = None - - # TODO: use a NamedTuple for log entries - _log_entries: List[Tuple[int, Address, Tuple[int, ...], bytes]] = None - accounts_to_delete: Dict[Address, Address] = None - - # VM configuration - opcodes: Dict[int, OpcodeAPI] = None - _precompiles: Dict[Address, Callable[[ComputationAPI], ComputationAPI]] = None - - logger = get_extended_debug_logger('eth.vm.computation.Computation') - - def __init__(self, - state: StateAPI, - message: MessageAPI, - transaction_context: TransactionContextAPI) -> None: - - self.state = state - self.msg = message - self.transaction_context = transaction_context - - self._memory = Memory() - self._stack = Stack() - self._gas_meter = self.get_gas_meter() - - self.children = [] - self.accounts_to_delete = {} - self._log_entries = [] - - code = message.code - self.code = CodeStream(code) - - # - # Convenience - # - @property - def is_origin_computation(self) -> bool: - return self.msg.sender == self.transaction_context.origin - - # - # Error handling - # - @property - def is_success(self) -> bool: - return self._error is None - - @property - def is_error(self) -> bool: - return not self.is_success - - @property - def error(self) -> VMError: - if self._error is not None: - return self._error - raise AttributeError("Computation does not have an error") - - @error.setter - def error(self, value: VMError) -> None: - if self._error is not None: - raise AttributeError(f"Computation already has an error set: {self._error}") - self._error = value - - def raise_if_error(self) -> None: - if self._error is not None: - raise self._error - - @property - def should_burn_gas(self) -> bool: - return self.is_error and self._error.burns_gas - - @property - def should_return_gas(self) -> bool: - return not self.should_burn_gas - - @property - def should_erase_return_data(self) -> bool: - return self.is_error and self._error.erases_return_data - - # - # Memory Management - # - def extend_memory(self, start_position: int, size: int) -> None: - validate_uint256(start_position, title="Memory start position") - validate_uint256(size, title="Memory size") - - before_size = ceil32(len(self._memory)) - after_size = ceil32(start_position + size) - - before_cost = memory_gas_cost(before_size) - after_cost = memory_gas_cost(after_size) - - if self.logger.show_debug2: - self.logger.debug2( - "MEMORY: size (%s -> %s) | cost (%s -> %s)", - before_size, - after_size, - before_cost, - after_cost, - ) - - if size: - if before_cost < after_cost: - gas_fee = after_cost - before_cost - self._gas_meter.consume_gas( - gas_fee, - reason=" ".join(( - "Expanding memory", - str(before_size), - "->", - str(after_size), - )) - ) - - self._memory.extend(start_position, size) - - def memory_write(self, start_position: int, size: int, value: bytes) -> None: - return self._memory.write(start_position, size, value) - - def memory_read(self, start_position: int, size: int) -> memoryview: - return self._memory.read(start_position, size) - - def memory_read_bytes(self, start_position: int, size: int) -> bytes: - return self._memory.read_bytes(start_position, size) - - # - # Gas Consumption - # - def get_gas_meter(self) -> GasMeterAPI: - return GasMeter(self.msg.gas) - - def consume_gas(self, amount: int, reason: str) -> None: - return self._gas_meter.consume_gas(amount, reason) - - def return_gas(self, amount: int) -> None: - return self._gas_meter.return_gas(amount) - - def refund_gas(self, amount: int) -> None: - return self._gas_meter.refund_gas(amount) - - def get_gas_refund(self) -> int: - if self.is_error: - return 0 - else: - return self._gas_meter.gas_refunded + sum(c.get_gas_refund() for c in self.children) - - def get_gas_used(self) -> int: - if self.should_burn_gas: - return self.msg.gas - else: - return max( - 0, - self.msg.gas - self._gas_meter.gas_remaining, - ) - - def get_gas_remaining(self) -> int: - if self.should_burn_gas: - return 0 - else: - return self._gas_meter.gas_remaining - - @classmethod - def consume_initcode_gas_cost(cls, computation: ComputationAPI) -> None: - # this method does not become relevant until the Shanghai hard fork - """ - Before starting the computation, consume initcode gas cost. - """ - pass - - # - # Stack management - # - def stack_swap(self, position: int) -> None: - return self._stack.swap(position) - - def stack_dup(self, position: int) -> None: - return self._stack.dup(position) - - # Stack manipulation is performance-sensitive code. - # Avoid method call overhead by proxying stack method directly to stack object - - @cached_property - def stack_pop_ints(self) -> Callable[[int], Tuple[int, ...]]: - return self._stack.pop_ints - - @cached_property - def stack_pop_bytes(self) -> Callable[[int], Tuple[bytes, ...]]: - return self._stack.pop_bytes - - @cached_property - def stack_pop_any(self) -> Callable[[int], Tuple[Union[int, bytes], ...]]: - return self._stack.pop_any - - @cached_property - def stack_pop1_int(self) -> Callable[[], int]: - return self._stack.pop1_int - - @cached_property - def stack_pop1_bytes(self) -> Callable[[], bytes]: - return self._stack.pop1_bytes - - @cached_property - def stack_pop1_any(self) -> Callable[[], Union[int, bytes]]: - return self._stack.pop1_any - - @cached_property - def stack_push_int(self) -> Callable[[int], None]: - return self._stack.push_int - - @cached_property - def stack_push_bytes(self) -> Callable[[bytes], None]: - return self._stack.push_bytes - - # - # Computation result - # - @property - def output(self) -> bytes: - if self.should_erase_return_data: - return b'' - else: - return self._output - - @output.setter - def output(self, value: bytes) -> None: - validate_is_bytes(value) - self._output = value - - # - # Runtime operations - # - def prepare_child_message(self, - gas: int, - to: Address, - value: int, - data: BytesOrView, - code: bytes, - **kwargs: Any) -> MessageAPI: - kwargs.setdefault('sender', self.msg.storage_address) - - child_message = Message( - gas=gas, - to=to, - value=value, - data=data, - code=code, - depth=self.msg.depth + 1, - **kwargs - ) - return child_message - - def apply_child_computation(self, child_msg: MessageAPI) -> ComputationAPI: - child_computation = self.generate_child_computation(child_msg) - self.add_child_computation(child_computation) - return child_computation - - def generate_child_computation(self, child_msg: MessageAPI) -> ComputationAPI: - if child_msg.is_create: - child_computation = self.apply_create_message( - self.state, - child_msg, - self.transaction_context, - ) - else: - child_computation = self.apply_message( - self.state, - child_msg, - self.transaction_context, - ) - return child_computation - - def add_child_computation(self, child_computation: ComputationAPI) -> None: - if child_computation.is_error: - if child_computation.msg.is_create: - self.return_data = child_computation.output - elif child_computation.should_burn_gas: - self.return_data = b'' - else: - self.return_data = child_computation.output - else: - if child_computation.msg.is_create: - self.return_data = b'' - else: - self.return_data = child_computation.output - self.children.append(child_computation) - - # - # Account management - # - def register_account_for_deletion(self, beneficiary: Address) -> None: - validate_canonical_address(beneficiary, title="Self destruct beneficiary address") - - if self.msg.storage_address in self.accounts_to_delete: - raise ValueError( - "Invariant. Should be impossible for an account to be " - "registered for deletion multiple times" - ) - self.accounts_to_delete[self.msg.storage_address] = beneficiary - - def get_accounts_for_deletion(self) -> Tuple[Tuple[Address, Address], ...]: - if self.is_error: - return () - else: - return tuple(dict(itertools.chain( - self.accounts_to_delete.items(), - *(child.get_accounts_for_deletion() for child in self.children) - )).items()) - - # - # EVM logging - # - def add_log_entry(self, account: Address, topics: Tuple[int, ...], data: bytes) -> None: - validate_canonical_address(account, title="Log entry address") - for topic in topics: - validate_uint256(topic, title="Log entry topic") - validate_is_bytes(data, title="Log entry data") - self._log_entries.append( - (self.transaction_context.get_next_log_counter(), account, topics, data)) - - def get_raw_log_entries(self) -> Tuple[Tuple[int, bytes, Tuple[int, ...], bytes], ...]: - if self.is_error: - return () - else: - return tuple(sorted(itertools.chain( - self._log_entries, - *(child.get_raw_log_entries() for child in self.children) - ))) - - def get_log_entries(self) -> Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]: - return tuple(log[1:] for log in self.get_raw_log_entries()) - - # - # Context Manager API - # - def __enter__(self) -> ComputationAPI: - if self.logger.show_debug2: - self.logger.debug2( - ( - "COMPUTATION STARTING: gas: %s | from: %s | to: %s | value: %s " - "| depth %s | static: %s" - ), - self.msg.gas, - encode_hex(self.msg.sender), - encode_hex(self.msg.to), - self.msg.value, - self.msg.depth, - "y" if self.msg.is_static else "n", - ) - - return self - - def __exit__(self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType]) -> Union[None, bool]: - if exc_value and isinstance(exc_value, VMError): - if self.logger.show_debug2: - self.logger.debug2( - ( - "COMPUTATION ERROR: gas: %s | from: %s | to: %s | value: %s | " - "depth: %s | static: %s | error: %s" - ), - self.msg.gas, - encode_hex(self.msg.sender), - encode_hex(self.msg.to), - self.msg.value, - self.msg.depth, - "y" if self.msg.is_static else "n", - exc_value, - ) - self._error = exc_value - if self.should_burn_gas: - self.consume_gas( - self._gas_meter.gas_remaining, - reason=" ".join(( - "Zeroing gas due to VM Exception:", - str(exc_value), - )), - ) - - # When we raise an exception that erases return data, erase the return data. - if self.should_erase_return_data: - self.return_data = b'' - - # suppress VM exceptions - return True - elif exc_type is None and self.logger.show_debug2: - self.logger.debug2( - ( - "COMPUTATION SUCCESS: from: %s | to: %s | value: %s | " - "depth: %s | static: %s | gas-used: %s | gas-remaining: %s" - ), - encode_hex(self.msg.sender), - encode_hex(self.msg.to), - self.msg.value, - self.msg.depth, - "y" if self.msg.is_static else "n", - self.get_gas_used(), - self._gas_meter.gas_remaining, - ) - - return None - - # - # State Transition - # - @classmethod - def apply_computation( - cls, - state: StateAPI, - message: MessageAPI, - transaction_context: TransactionContextAPI - ) -> ComputationAPI: - - with cls(state, message, transaction_context) as computation: - if message.is_create and computation.is_origin_computation: - # If computation is from a create transaction, consume initcode gas if - # >= Shanghai. CREATE and CREATE2 are handled in the opcode - # implementations. - cls.consume_initcode_gas_cost(computation) - - # Early exit on pre-compiles - precompile = computation.precompiles.get(message.code_address, NO_RESULT) - if precompile is not NO_RESULT: - precompile(computation) - return computation - - show_debug2 = computation.logger.show_debug2 - - opcode_lookup = computation.opcodes - for opcode in computation.code: - try: - opcode_fn = opcode_lookup[opcode] - except KeyError: - opcode_fn = InvalidOpcode(opcode) - - if show_debug2: - # We dig into some internals for debug logs - base_comp = cast(BaseComputation, computation) - computation.logger.debug2( - "OPCODE: 0x%x (%s) | pc: %s | stack: %s", - opcode, - opcode_fn.mnemonic, - max(0, computation.code.program_counter - 1), - base_comp._stack, - ) - - try: - opcode_fn(computation=computation) - except Halt: - break - - return computation - - # - # Opcode API - # - @property - def precompiles(self) -> Dict[Address, Callable[[ComputationAPI], Any]]: - if self._precompiles is None: - return {} - else: - return self._precompiles - - @classmethod - def get_precompiles(cls) -> Dict[Address, Callable[[ComputationAPI], Any]]: - if cls._precompiles is None: - return {} - else: - return cls._precompiles - - def get_opcode_fn(self, opcode: int) -> OpcodeAPI: - try: - return self.opcodes[opcode] - except KeyError: - return InvalidOpcode(opcode) diff --git a/eth/vm/computation/__init__.py b/eth/vm/computation/__init__.py new file mode 100644 index 0000000000..b42a0f5efa --- /dev/null +++ b/eth/vm/computation/__init__.py @@ -0,0 +1,4 @@ +from .message_computation import ( # noqa: F401 + BaseComputation, + MessageComputation, +) diff --git a/eth/vm/computation/base_computation.py b/eth/vm/computation/base_computation.py new file mode 100644 index 0000000000..0eaf17b9f1 --- /dev/null +++ b/eth/vm/computation/base_computation.py @@ -0,0 +1,346 @@ +from types import TracebackType +from typing import ( + Any, + Callable, + Dict, + Optional, + Tuple, + Type, + Union, +) + +from cached_property import cached_property + +from eth_typing import ( + Address, +) +from eth_utils import ( + get_extended_debug_logger, +) + +from eth._utils.datatypes import ( + Configurable, +) +from eth._utils.numeric import ( + ceil32, +) +from eth.abc import ( + ComputationAPI, + MemoryAPI, + StackAPI, + GasMeterAPI, + OpcodeAPI, + CodeStreamAPI, + MessageComputationAPI, + StateAPI, +) +from eth.constants import ( + GAS_MEMORY, + GAS_MEMORY_QUADRATIC_DENOMINATOR, +) +from eth.exceptions import ( + VMError, +) +from eth.validation import ( + validate_is_bytes, + validate_uint256, +) +from eth.vm.logic.invalid import ( + InvalidOpcode, +) +from eth.vm.memory import ( + Memory, +) +from eth.vm.stack import ( + Stack, +) + + +def NO_RESULT(computation: ComputationAPI) -> None: + """ + This is a special method intended for usage as the "no precompile found" result. + The type signature is designed to match the other precompiles. + """ + raise Exception("This method is never intended to be executed") + + +def memory_gas_cost(size_in_bytes: int) -> int: + size_in_words = ceil32(size_in_bytes) // 32 + linear_cost = size_in_words * GAS_MEMORY + quadratic_cost = size_in_words ** 2 // GAS_MEMORY_QUADRATIC_DENOMINATOR + + total_cost = linear_cost + quadratic_cost + return total_cost + + +class BaseComputation(Configurable, ComputationAPI): + """ + The base class for all execution computations. + + .. note:: + + Each :class:`~eth.vm.computation.BaseComputation` class must be configured with: + + ``opcodes``: A mapping from the opcode integer value to the logic + function for the opcode. + + ``_precompiles``: A mapping of contract address to the precompile function + for execution of precompiled contracts. + """ + + logger = get_extended_debug_logger("eth.vm.computation.BaseComputation") + + state: StateAPI = None + code: CodeStreamAPI = None + return_data: bytes = b'' + accounts_to_delete: Dict[Address, Address] = None + + _memory: MemoryAPI = None + _stack: StackAPI = None + _gas_meter: GasMeterAPI = None + _error: VMError = None + _output: bytes = b'' + + # VM configuration + opcodes: Dict[int, OpcodeAPI] = None + _precompiles: Dict[Address, Callable[[ComputationAPI], ComputationAPI]] = None + + def __init__(self, state: StateAPI) -> None: + self.state = state + self._memory = Memory() + self._stack = Stack() + + def _configure_gas_meter(self) -> GasMeterAPI: + raise NotImplementedError("Must be implemented by subclasses") + + # -- error handling -- # + @property + def is_success(self) -> bool: + return self._error is None + + @property + def is_error(self) -> bool: + return not self.is_success + + @property + def error(self) -> VMError: + if self._error is not None: + return self._error + raise AttributeError("Computation does not have an error") + + @error.setter + def error(self, value: VMError) -> None: + if self._error is not None: + raise AttributeError(f"Computation already has an error set: {self._error}") + self._error = value + + def raise_if_error(self) -> None: + if self._error is not None: + raise self._error + + @property + def should_burn_gas(self) -> bool: + return self.is_error and self._error.burns_gas + + @property + def should_return_gas(self) -> bool: + return not self.should_burn_gas + + @property + def should_erase_return_data(self) -> bool: + return self.is_error and self._error.erases_return_data + + # -- memory management -- # + def extend_memory(self, start_position: int, size: int) -> None: + validate_uint256(start_position, title="Memory start position") + validate_uint256(size, title="Memory size") + + before_size = ceil32(len(self._memory)) + after_size = ceil32(start_position + size) + + before_cost = memory_gas_cost(before_size) + after_cost = memory_gas_cost(after_size) + + if self.logger.show_debug2: + self.logger.debug2( + "MEMORY: size (%s -> %s) | cost (%s -> %s)", + before_size, + after_size, + before_cost, + after_cost, + ) + + if size: + if before_cost < after_cost: + gas_fee = after_cost - before_cost + self._gas_meter.consume_gas( + gas_fee, + reason=" ".join( + ( + "Expanding memory", + str(before_size), + "->", + str(after_size), + ) + ) + ) + + self._memory.extend(start_position, size) + + def memory_write(self, start_position: int, size: int, value: bytes) -> None: + return self._memory.write(start_position, size, value) + + def memory_read(self, start_position: int, size: int) -> memoryview: + return self._memory.read(start_position, size) + + def memory_read_bytes(self, start_position: int, size: int) -> bytes: + return self._memory.read_bytes(start_position, size) + + # -- gas consumption -- # + def get_gas_meter(self) -> GasMeterAPI: + return self._gas_meter + + def consume_gas(self, amount: int, reason: str) -> None: + return self._gas_meter.consume_gas(amount, reason) + + def return_gas(self, amount: int) -> None: + return self._gas_meter.return_gas(amount) + + def refund_gas(self, amount: int) -> None: + return self._gas_meter.refund_gas(amount) + + def get_gas_used(self) -> int: + if self.should_burn_gas: + return self._gas_meter.start_gas + else: + return max( + 0, + self._gas_meter.start_gas - self._gas_meter.gas_remaining, + ) + + def get_gas_remaining(self) -> int: + if self.should_burn_gas: + return 0 + else: + return self._gas_meter.gas_remaining + + @classmethod + def consume_initcode_gas_cost(cls, computation: MessageComputationAPI) -> None: + # this method does not become relevant until the Shanghai hard fork + """ + Before starting the computation, consume initcode gas cost. + """ + pass + + # -- stack management -- # + def stack_swap(self, position: int) -> None: + return self._stack.swap(position) + + def stack_dup(self, position: int) -> None: + return self._stack.dup(position) + + # Stack manipulation is performance-sensitive code. + # Avoid method call overhead by proxying stack method directly to stack object + + @cached_property + def stack_pop_ints(self) -> Callable[[int], Tuple[int, ...]]: + return self._stack.pop_ints + + @cached_property + def stack_pop_bytes(self) -> Callable[[int], Tuple[bytes, ...]]: + return self._stack.pop_bytes + + @cached_property + def stack_pop_any(self) -> Callable[[int], Tuple[Union[int, bytes], ...]]: + return self._stack.pop_any + + @cached_property + def stack_pop1_int(self) -> Callable[[], int]: + return self._stack.pop1_int + + @cached_property + def stack_pop1_bytes(self) -> Callable[[], bytes]: + return self._stack.pop1_bytes + + @cached_property + def stack_pop1_any(self) -> Callable[[], Union[int, bytes]]: + return self._stack.pop1_any + + @cached_property + def stack_push_int(self) -> Callable[[int], None]: + return self._stack.push_int + + @cached_property + def stack_push_bytes(self) -> Callable[[bytes], None]: + return self._stack.push_bytes + + # -- computation result -- # + @property + def output(self) -> bytes: + if self.should_erase_return_data: + return b'' + else: + return self._output + + @output.setter + def output(self, value: bytes) -> None: + validate_is_bytes(value) + self._output = value + + # -- opcode API -- # + @property + def precompiles(self) -> Dict[Address, Callable[[ComputationAPI], Any]]: + if self._precompiles is None: + return {} + else: + return self._precompiles + + @classmethod + def get_precompiles(cls) -> Dict[Address, Callable[[ComputationAPI], Any]]: + if cls._precompiles is None: + return {} + else: + return cls._precompiles + + def get_opcode_fn(self, opcode: int) -> OpcodeAPI: + try: + return self.opcodes[opcode] + except KeyError: + return InvalidOpcode(opcode) + + # -- context manager API -- # + def __enter__(self) -> ComputationAPI: + if self.logger.show_debug2: + self.logger.debug2("COMPUTATION STARTING") + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> Union[None, bool]: + if exc_value and isinstance(exc_value, VMError): + # Exception handling logic for computations is done here in the base class. + # Subclass-specific logging can be done in each subclass by overriding + # `__exit__` and calling `super().__exit__(exc_type, exc_value, traceback)`. + self._error = exc_value + if self.should_burn_gas: + self.consume_gas( + self._gas_meter.gas_remaining, + reason=" ".join( + ( + "Zeroing gas due to VM Exception:", + str(exc_value), + ) + ), + ) + + # when we raise an exception that erases return data, erase the return data + if self.should_erase_return_data: + self.return_data = b'' + + # suppress VM exceptions + return True + + return None diff --git a/eth/vm/computation/message_computation.py b/eth/vm/computation/message_computation.py new file mode 100644 index 0000000000..08235b188e --- /dev/null +++ b/eth/vm/computation/message_computation.py @@ -0,0 +1,357 @@ +import itertools +from types import TracebackType +from typing import ( + Any, + Dict, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +from eth.vm.computation.base_computation import ( + BaseComputation, NO_RESULT, +) +from eth_typing import ( + Address, +) +from eth_utils import ( + encode_hex, + get_extended_debug_logger, +) + +from eth.abc import ( + MessageAPI, + MessageComputationAPI, + StateAPI, + TransactionContextAPI, +) +from eth.exceptions import ( + Halt, + VMError, +) +from eth.typing import ( + BytesOrView, +) +from eth.validation import ( + validate_canonical_address, + validate_is_bytes, + validate_uint256, +) +from eth.vm.code_stream import ( + CodeStream, +) +from eth.vm.gas_meter import ( + GasMeter, +) +from eth.vm.logic.invalid import ( + InvalidOpcode, +) +from eth.vm.message import ( + Message, +) + + +class MessageComputation(BaseComputation, MessageComputationAPI): + """ + A class for executing message computations. + """ + + logger = get_extended_debug_logger("eth.vm.computation.MessageComputation") + + msg: MessageAPI = None + transaction_context: TransactionContextAPI = None + children: List[MessageComputationAPI] = None + accounts_to_delete: Dict[Address, Address] = None + + _log_entries: List[Tuple[int, Address, Tuple[int, ...], bytes]] = None + + def __init__( + self, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> None: + super().__init__(state) + + self.msg = message + self.transaction_context = transaction_context + self.code = CodeStream(message.code) + self._gas_meter = self._configure_gas_meter() + + self.children = [] + self.accounts_to_delete = {} + self._log_entries = [] + + def _configure_gas_meter(self) -> GasMeter: + return GasMeter(self.msg.gas) + + # -- class methods -- # + @classmethod + def apply_message( + cls, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> MessageComputationAPI: + raise NotImplementedError("Must be implemented by subclasses") + + @classmethod + def apply_create_message( + cls, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> MessageComputationAPI: + raise NotImplementedError("Must be implemented by subclasses") + + # -- convenience -- # + @property + def is_origin_computation(self) -> bool: + return self.msg.sender == self.transaction_context.origin + + # -- runtime operations -- # + def prepare_child_message( + self, + gas: int, + to: Address, + value: int, + data: BytesOrView, + code: bytes, + **kwargs: Any, + ) -> MessageAPI: + kwargs.setdefault('sender', self.msg.storage_address) + + child_message = Message( + gas=gas, + to=to, + value=value, + data=data, + code=code, + depth=self.msg.depth + 1, + **kwargs + ) + return child_message + + def apply_child_message_computation( + self, + child_msg: MessageAPI, + ) -> MessageComputationAPI: + child_computation = self.generate_child_message_computation(child_msg) + self.add_child_message_computation(child_computation) + return child_computation + + def generate_child_message_computation( + self, + child_msg: MessageAPI, + ) -> MessageComputationAPI: + if child_msg.is_create: + child_computation = self.apply_create_message( + self.state, + child_msg, + self.transaction_context, + ) + else: + child_computation = self.apply_message( + self.state, + child_msg, + self.transaction_context, + ) + return child_computation + + def add_child_message_computation( + self, + child_message_computation: MessageComputationAPI, + ) -> None: + if child_message_computation.is_error: + if child_message_computation.msg.is_create: + self.return_data = child_message_computation.output + elif child_message_computation.should_burn_gas: + self.return_data = b'' + else: + self.return_data = child_message_computation.output + else: + if child_message_computation.msg.is_create: + self.return_data = b'' + else: + self.return_data = child_message_computation.output + self.children.append(child_message_computation) + + # -- gas consumption -- # + def get_gas_refund(self) -> int: + if self.is_error: + return 0 + else: + return ( + self._gas_meter.gas_refunded + + sum(c.get_gas_refund() for c in self.children) + ) + + # -- account management -- # + def register_account_for_deletion(self, beneficiary: Address) -> None: + # SELFDESTRUCT + + validate_canonical_address( + beneficiary, + title="Self destruct beneficiary address", + ) + + if self.msg.storage_address in self.accounts_to_delete: + raise ValueError( + "Invariant. Should be impossible for an account to be " + "registered for deletion multiple times" + ) + self.accounts_to_delete[self.msg.storage_address] = beneficiary + + def get_accounts_for_deletion(self) -> Tuple[Tuple[Address, Address], ...]: + # SELFDESTRUCT + + if self.is_error: + return () + else: + return tuple(dict(itertools.chain( + self.accounts_to_delete.items(), + *(child.get_accounts_for_deletion() for child in self.children) + )).items()) + + # -- EVM logging -- # + def add_log_entry( + self, + account: Address, + topics: Tuple[int, ...], + data: bytes, + ) -> None: + validate_canonical_address(account, title="Log entry address") + for topic in topics: + validate_uint256(topic, title="Log entry topic") + validate_is_bytes(data, title="Log entry data") + self._log_entries.append( + (self.transaction_context.get_next_log_counter(), account, topics, data)) + + def get_raw_log_entries(self) -> Tuple[ + Tuple[int, bytes, Tuple[int, ...], bytes], ... + ]: + if self.is_error: + return () + else: + return tuple(sorted(itertools.chain( + self._log_entries, + *(child.get_raw_log_entries() for child in self.children) + ))) + + def get_log_entries(self) -> Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]: + return tuple(log[1:] for log in self.get_raw_log_entries()) + + # -- state transition -- # + @classmethod + def apply_computation( + cls, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> MessageComputationAPI: + + with cls(state, message, transaction_context) as computation: + if message.is_create and computation.is_origin_computation: + # If computation is from a create transaction, consume initcode gas if + # >= Shanghai. CREATE and CREATE2 are handled in the opcode + # implementations. + cls.consume_initcode_gas_cost(computation) + + # Early exit on pre-compiles + precompile = computation.precompiles.get( + message.code_address, NO_RESULT + ) + if precompile is not NO_RESULT: + precompile(computation) + return computation + + show_debug2 = computation.logger.show_debug2 + + opcode_lookup = computation.opcodes + for opcode in computation.code: + try: + opcode_fn = opcode_lookup[opcode] + except KeyError: + opcode_fn = InvalidOpcode(opcode) + + if show_debug2: + # We dig into some internals for debug logs + base_comp = cast(MessageComputation, computation) + computation.logger.debug2( + "OPCODE: 0x%x (%s) | pc: %s | stack: %s", + opcode, + opcode_fn.mnemonic, + max(0, computation.code.program_counter - 1), + base_comp._stack, + ) + + try: + opcode_fn(computation=computation) + except Halt: + break + + return computation + + # -- context manager API -- # + def __enter__(self) -> MessageComputationAPI: + super().__enter__() + if self.logger.show_debug2: + self.logger.debug2( + ( + "MESSAGE COMPUTATION: " + "from: %s | to: %s | value: %s | depth %s | static: %s | gas: %s" + ), + encode_hex(self.msg.sender), + encode_hex(self.msg.to), + self.msg.value, + self.msg.depth, + "y" if self.msg.is_static else "n", + self.msg.gas, + ) + + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> Union[None, bool]: + if exc_value and isinstance(exc_value, VMError): + if self.logger.show_debug2: + self.logger.debug2( + ( + "MESSAGE COMPUTATION ERROR: " + "gas: %s | from: %s | to: %s | value: %s | " + "depth: %s | static: %s | error: %s" + ), + self.msg.gas, + encode_hex(self.msg.sender), + encode_hex(self.msg.to), + self.msg.value, + self.msg.depth, + "y" if self.msg.is_static else "n", + exc_value, + ) + return super().__exit__(exc_type, exc_value, traceback) + elif exc_type is None and self.logger.show_debug2: + super().__exit__(exc_type, exc_value, traceback) + self.logger.debug2( + ( + "MESSAGE COMPUTATION SUCCESS: " + "from: %s | to: %s | value: %s | depth: %s | static: %s " + "| gas-used: %s | gas-remaining: %s" + ), + encode_hex(self.msg.sender), + encode_hex(self.msg.to), + self.msg.value, + self.msg.depth, + "y" if self.msg.is_static else "n", + self.get_gas_used(), + self._gas_meter.gas_remaining, + ) + + return None diff --git a/eth/vm/forks/arrow_glacier/computation.py b/eth/vm/forks/arrow_glacier/computation.py index 3779e0976a..b61816c076 100644 --- a/eth/vm/forks/arrow_glacier/computation.py +++ b/eth/vm/forks/arrow_glacier/computation.py @@ -1,9 +1,9 @@ -from ..london.computation import LondonComputation +from ..london.computation import LondonMessageComputation -class ArrowGlacierComputation(LondonComputation): +class ArrowGlacierMessageComputation(LondonMessageComputation): """ - A class for all execution computations in the ``ArrowGlacier`` fork. - Inherits from :class:`~eth.vm.forks.london.LondonComputation` + A class for all execution *message* computations in the ``ArrowGlacier`` fork. + Inherits from :class:`~eth.vm.forks.london.LondonMessageComputation` """ pass diff --git a/eth/vm/forks/arrow_glacier/state.py b/eth/vm/forks/arrow_glacier/state.py index 76882c3ff8..ccc820a370 100644 --- a/eth/vm/forks/arrow_glacier/state.py +++ b/eth/vm/forks/arrow_glacier/state.py @@ -1,7 +1,7 @@ from typing import Type from eth.abc import TransactionExecutorAPI -from .computation import ArrowGlacierComputation +from .computation import ArrowGlacierMessageComputation from ..london import LondonState from ..london.state import LondonTransactionExecutor @@ -11,5 +11,5 @@ class ArrowGlacierTransactionExecutor(LondonTransactionExecutor): class ArrowGlacierState(LondonState): - computation_class = ArrowGlacierComputation + message_computation_class = ArrowGlacierMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = ArrowGlacierTransactionExecutor diff --git a/eth/vm/forks/berlin/computation.py b/eth/vm/forks/berlin/computation.py index be112fb4f6..6046cf6146 100644 --- a/eth/vm/forks/berlin/computation.py +++ b/eth/vm/forks/berlin/computation.py @@ -27,7 +27,7 @@ MUIR_GLACIER_PRECOMPILES ) from eth.vm.forks.muir_glacier.computation import ( - MuirGlacierComputation, + MuirGlacierMessageComputation, ) from .opcodes import BERLIN_OPCODES @@ -79,10 +79,10 @@ def _compute_modexp_gas_fee_eip_2565(data: bytes) -> int: ) -class BerlinComputation(MuirGlacierComputation): +class BerlinMessageComputation(MuirGlacierMessageComputation): """ - A class for all execution computations in the ``Berlin`` fork. - Inherits from :class:`~eth.vm.forks.muir_glacier.MuirGlacierComputation` + A class for all execution *message* computations in the ``Berlin`` fork. + Inherits from :class:`~eth.vm.forks.muir_glacier.MuirGlacierMessageComputation` """ # Override opcodes = BERLIN_OPCODES diff --git a/eth/vm/forks/berlin/logic.py b/eth/vm/forks/berlin/logic.py index ed910357a0..b0bbf30939 100644 --- a/eth/vm/forks/berlin/logic.py +++ b/eth/vm/forks/berlin/logic.py @@ -8,7 +8,7 @@ force_bytes_to_address, ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, ) from eth.vm import mnemonics from eth.vm.forks.istanbul.storage import ( @@ -39,7 +39,7 @@ from . import constants as berlin_constants -def _mark_address_warm(computation: ComputationAPI, address: Address) -> bool: +def _mark_address_warm(computation: MessageComputationAPI, address: Address) -> bool: """ Mark the given address as warm if it was not previously. @@ -62,7 +62,7 @@ def _account_load_cost(was_cold: bool) -> int: def _consume_gas_for_account_load( - computation: ComputationAPI, + computation: MessageComputationAPI, address: Address, reason: str) -> None: was_cold = _mark_address_warm(computation, address) @@ -70,7 +70,7 @@ def _consume_gas_for_account_load( computation.consume_gas(gas_cost, reason=reason) -def _mark_storage_warm(computation: ComputationAPI, slot: int) -> bool: +def _mark_storage_warm(computation: MessageComputationAPI, slot: int) -> bool: """ :return was_cold: True if the storage slot was not previously accessed during this transaction @@ -83,13 +83,13 @@ def _mark_storage_warm(computation: ComputationAPI, slot: int) -> bool: return True -def balance_eip2929(computation: ComputationAPI) -> None: +def balance_eip2929(computation: MessageComputationAPI) -> None: address = force_bytes_to_address(computation.stack_pop1_bytes()) _consume_gas_for_account_load(computation, address, mnemonics.BALANCE) push_balance_of_address(address, computation) -def extcodesize_eip2929(computation: ComputationAPI) -> None: +def extcodesize_eip2929(computation: MessageComputationAPI) -> None: address = force_bytes_to_address(computation.stack_pop1_bytes()) _consume_gas_for_account_load(computation, address, mnemonics.EXTCODEHASH) @@ -97,13 +97,13 @@ def extcodesize_eip2929(computation: ComputationAPI) -> None: computation.stack_push_int(code_size) -def extcodecopy_eip2929(computation: ComputationAPI) -> None: +def extcodecopy_eip2929(computation: MessageComputationAPI) -> None: address, size = extcodecopy_execute(computation) consume_extcodecopy_word_cost(computation, size) _consume_gas_for_account_load(computation, address, mnemonics.EXTCODECOPY) -def extcodehash_eip2929(computation: ComputationAPI) -> None: +def extcodehash_eip2929(computation: MessageComputationAPI) -> None: """ Return the code hash for a given address. EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1052.md @@ -119,7 +119,7 @@ def extcodehash_eip2929(computation: ComputationAPI) -> None: computation.stack_push_bytes(state.get_code_hash(address)) -def sload_eip2929(computation: ComputationAPI) -> None: +def sload_eip2929(computation: MessageComputationAPI) -> None: slot = computation.stack_pop1_int() if _mark_storage_warm(computation, slot): @@ -142,7 +142,10 @@ def sload_eip2929(computation: ComputationAPI) -> None: @curry -def sstore_eip2929_generic(gas_schedule: NetSStoreGasSchedule, computation: ComputationAPI) -> int: +def sstore_eip2929_generic( + gas_schedule: NetSStoreGasSchedule, + computation: MessageComputationAPI, +) -> int: slot = sstore_eip2200_generic(gas_schedule, computation) if _mark_storage_warm(computation, slot): @@ -156,7 +159,11 @@ def sstore_eip2929_generic(gas_schedule: NetSStoreGasSchedule, computation: Comp class LoadFeeByCacheWarmth: - def get_account_load_fee(self, computation: ComputationAPI, code_address: Address) -> int: + def get_account_load_fee( + self, + computation: MessageComputationAPI, + code_address: Address, + ) -> int: was_cold = _mark_address_warm(computation, code_address) return _account_load_cost(was_cold) @@ -177,7 +184,7 @@ class StaticCallEIP2929(LoadFeeByCacheWarmth, StaticCall): pass -def selfdestruct_eip2929(computation: ComputationAPI) -> None: +def selfdestruct_eip2929(computation: MessageComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) if _mark_address_warm(computation, beneficiary): @@ -194,7 +201,7 @@ class CreateEIP2929(CreateByzantium): def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: ComputationAPI) -> Address: + computation: MessageComputationAPI) -> Address: address = super().generate_contract_address(stack_data, call_data, computation) computation.state.mark_address_warm(address) return address @@ -204,7 +211,7 @@ class Create2EIP2929(Create2): def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: ComputationAPI) -> Address: + computation: MessageComputationAPI) -> Address: address = super().generate_contract_address(stack_data, call_data, computation) computation.state.mark_address_warm(address) return address diff --git a/eth/vm/forks/berlin/state.py b/eth/vm/forks/berlin/state.py index b047d5f7f1..cc66db771b 100644 --- a/eth/vm/forks/berlin/state.py +++ b/eth/vm/forks/berlin/state.py @@ -1,7 +1,7 @@ from typing import Type from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, SignedTransactionAPI, TransactionExecutorAPI, @@ -13,14 +13,14 @@ SpuriousDragonTransactionExecutor, ) -from .computation import BerlinComputation +from .computation import BerlinMessageComputation class BerlinTransactionExecutor(SpuriousDragonTransactionExecutor): def build_computation( self, message: MessageAPI, - transaction: SignedTransactionAPI) -> ComputationAPI: + transaction: SignedTransactionAPI) -> MessageComputationAPI: self.vm_state.mark_address_warm(transaction.sender) @@ -36,5 +36,5 @@ def build_computation( class BerlinState(MuirGlacierState): - computation_class = BerlinComputation + message_computation_class = BerlinMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = BerlinTransactionExecutor diff --git a/eth/vm/forks/byzantium/__init__.py b/eth/vm/forks/byzantium/__init__.py index 7246f50337..235e16ad8b 100644 --- a/eth/vm/forks/byzantium/__init__.py +++ b/eth/vm/forks/byzantium/__init__.py @@ -16,7 +16,7 @@ from eth.abc import ( BlockAPI, BlockHeaderAPI, - ComputationAPI, + MessageComputationAPI, ReceiptAPI, SignedTransactionAPI, StateAPI, @@ -104,7 +104,7 @@ def make_receipt( cls, base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, - computation: ComputationAPI, + computation: MessageComputationAPI, state: StateAPI) -> ReceiptAPI: gas_used = base_header.gas_used + cls.finalize_gas_used(transaction, computation) diff --git a/eth/vm/forks/byzantium/computation.py b/eth/vm/forks/byzantium/computation.py index f591c3f3ca..30becc1e5d 100644 --- a/eth/vm/forks/byzantium/computation.py +++ b/eth/vm/forks/byzantium/computation.py @@ -7,7 +7,7 @@ force_bytes_to_address, ) from eth.vm.forks.frontier.computation import FRONTIER_PRECOMPILES -from eth.vm.forks.spurious_dragon.computation import SpuriousDragonComputation +from eth.vm.forks.spurious_dragon.computation import SpuriousDragonMessageComputation from .opcodes import BYZANTIUM_OPCODES @@ -22,10 +22,11 @@ ) -class ByzantiumComputation(SpuriousDragonComputation): +class ByzantiumMessageComputation(SpuriousDragonMessageComputation): """ - A class for all execution computations in the ``Byzantium`` fork. - Inherits from :class:`~eth.vm.forks.spurious_dragon.computation.SpuriousDragonComputation` + A class for all execution *message* computations in the ``Byzantium`` fork. + Inherits from + :class:`~eth.vm.forks.spurious_dragon.computation.SpuriousDragonMessageComputation` """ # Override opcodes = BYZANTIUM_OPCODES diff --git a/eth/vm/forks/byzantium/opcodes.py b/eth/vm/forks/byzantium/opcodes.py index 2760b29e9e..3e614f195f 100644 --- a/eth/vm/forks/byzantium/opcodes.py +++ b/eth/vm/forks/byzantium/opcodes.py @@ -17,7 +17,7 @@ ) from eth.vm import mnemonics from eth.vm import opcode_values -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation from eth.vm.forks.tangerine_whistle.constants import ( GAS_CALL_EIP150, GAS_SELFDESTRUCT_EIP150 @@ -36,7 +36,7 @@ def ensure_no_static(opcode_fn: Callable[..., Any]) -> Callable[..., Any]: @functools.wraps(opcode_fn) - def inner(computation: BaseComputation) -> Callable[..., Any]: + def inner(computation: MessageComputation) -> Callable[..., Any]: if computation.msg.is_static: raise WriteProtection("Cannot modify state while inside of a STATICCALL context") return opcode_fn(computation) diff --git a/eth/vm/forks/byzantium/state.py b/eth/vm/forks/byzantium/state.py index d588c62ebb..0ff9c3164f 100644 --- a/eth/vm/forks/byzantium/state.py +++ b/eth/vm/forks/byzantium/state.py @@ -1,7 +1,7 @@ from eth.vm.forks.spurious_dragon.state import SpuriousDragonState -from .computation import ByzantiumComputation +from .computation import ByzantiumMessageComputation class ByzantiumState(SpuriousDragonState): - computation_class = ByzantiumComputation + message_computation_class = ByzantiumMessageComputation diff --git a/eth/vm/forks/constantinople/computation.py b/eth/vm/forks/constantinople/computation.py index 6938282470..2fbf5dd107 100644 --- a/eth/vm/forks/constantinople/computation.py +++ b/eth/vm/forks/constantinople/computation.py @@ -2,7 +2,7 @@ BYZANTIUM_PRECOMPILES ) from eth.vm.forks.byzantium.computation import ( - ByzantiumComputation + ByzantiumMessageComputation ) from eth.vm.gas_meter import ( allow_negative_refund_strategy, @@ -14,16 +14,17 @@ CONSTANTINOPLE_PRECOMPILES = BYZANTIUM_PRECOMPILES -class ConstantinopleComputation(ByzantiumComputation): +class ConstantinopleMessageComputation(ByzantiumMessageComputation): """ - A class for all execution computations in the ``Constantinople`` fork. - Inherits from :class:`~eth.vm.forks.byzantium.computation.ByzantiumComputation` + A class for all execution *message* computations in the ``Constantinople`` fork. + Inherits from + :class:`~eth.vm.forks.byzantium.computation.ByzantiumMessageComputation` """ # Override opcodes = CONSTANTINOPLE_OPCODES _precompiles = CONSTANTINOPLE_PRECOMPILES - def get_gas_meter(self) -> GasMeter: + def _configure_gas_meter(self) -> GasMeter: return GasMeter( self.msg.gas, allow_negative_refund_strategy diff --git a/eth/vm/forks/constantinople/state.py b/eth/vm/forks/constantinople/state.py index 2ea1e877ca..ca41f928d5 100644 --- a/eth/vm/forks/constantinople/state.py +++ b/eth/vm/forks/constantinople/state.py @@ -2,8 +2,8 @@ ByzantiumState ) -from .computation import ConstantinopleComputation +from .computation import ConstantinopleMessageComputation class ConstantinopleState(ByzantiumState): - computation_class = ConstantinopleComputation + message_computation_class = ConstantinopleMessageComputation diff --git a/eth/vm/forks/frontier/__init__.py b/eth/vm/forks/frontier/__init__.py index 0f2441b9b2..174f939160 100644 --- a/eth/vm/forks/frontier/__init__.py +++ b/eth/vm/forks/frontier/__init__.py @@ -12,7 +12,7 @@ ReceiptAPI, StateAPI, SignedTransactionAPI, - ComputationAPI, + MessageComputationAPI, ) from eth.constants import ( BLOCK_REWARD, @@ -35,7 +35,7 @@ from .validation import validate_frontier_transaction_against_header -def make_frontier_receipt(computation: ComputationAPI, +def make_frontier_receipt(computation: MessageComputationAPI, new_cumulative_gas_used: int) -> ReceiptAPI: # Reusable for other forks # This skips setting the state root (set to 0 instead). The logic for making a state root @@ -101,7 +101,7 @@ def calculate_net_gas_refund(cls, consumed_gas: int, gross_refund: int) -> int: @classmethod def finalize_gas_used(cls, transaction: SignedTransactionAPI, - computation: ComputationAPI) -> int: + computation: MessageComputationAPI) -> int: gas_remaining = computation.get_gas_remaining() consumed_gas = transaction.gas - gas_remaining @@ -116,7 +116,7 @@ def make_receipt( cls, base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, - computation: ComputationAPI, + computation: MessageComputationAPI, state: StateAPI) -> ReceiptAPI: gas_used = base_header.gas_used + cls.finalize_gas_used(transaction, computation) diff --git a/eth/vm/forks/frontier/computation.py b/eth/vm/forks/frontier/computation.py index cf0b6ae537..693ece056e 100644 --- a/eth/vm/forks/frontier/computation.py +++ b/eth/vm/forks/frontier/computation.py @@ -14,7 +14,7 @@ force_bytes_to_address, ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, @@ -25,7 +25,7 @@ StackDepthLimit, ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) from .opcodes import FRONTIER_OPCODES @@ -39,10 +39,10 @@ } -class FrontierComputation(BaseComputation): +class FrontierMessageComputation(MessageComputation): """ - A class for all execution computations in the ``Frontier`` fork. - Inherits from :class:`~eth.vm.computation.BaseComputation` + A class for all execution message computations in the ``Frontier`` fork. + Inherits from :class:`~eth.vm.computation.MessageComputation` """ # Override opcodes = FRONTIER_OPCODES @@ -54,7 +54,7 @@ def apply_message( state: StateAPI, message: MessageAPI, transaction_context: TransactionContextAPI, - ) -> ComputationAPI: + ) -> MessageComputationAPI: snapshot = state.snapshot() @@ -99,7 +99,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> ComputationAPI: + transaction_context: TransactionContextAPI) -> MessageComputationAPI: computation = cls.apply_message(state, message, transaction_context) diff --git a/eth/vm/forks/frontier/state.py b/eth/vm/forks/frontier/state.py index d5ae8878d1..ee8da8b623 100644 --- a/eth/vm/forks/frontier/state.py +++ b/eth/vm/forks/frontier/state.py @@ -7,7 +7,7 @@ from eth.abc import ( AccountDatabaseAPI, - ComputationAPI, + MessageComputationAPI, SignedTransactionAPI, MessageAPI, TransactionContextAPI, @@ -34,7 +34,7 @@ ) -from .computation import FrontierComputation +from .computation import FrontierMessageComputation from .constants import ( REFUND_SELFDESTRUCT, MAX_REFUND_QUOTIENT, @@ -111,7 +111,7 @@ def build_evm_message(self, transaction: SignedTransactionAPI) -> MessageAPI: def build_computation(self, message: MessageAPI, - transaction: SignedTransactionAPI) -> ComputationAPI: + transaction: SignedTransactionAPI) -> MessageComputationAPI: transaction_context = self.vm_state.get_transaction_context(transaction) if message.is_create: is_collision = self.vm_state.has_code_or_nonce( @@ -131,13 +131,13 @@ def build_computation(self, encode_hex(message.storage_address), ) else: - computation = self.vm_state.computation_class.apply_create_message( + computation = self.vm_state.message_computation_class.apply_create_message( self.vm_state, message, transaction_context, ) else: - computation = self.vm_state.computation_class.apply_message( + computation = self.vm_state.message_computation_class.apply_message( self.vm_state, message, transaction_context, @@ -147,7 +147,7 @@ def build_computation(self, @classmethod def calculate_gas_refund(cls, - computation: ComputationAPI, + computation: MessageComputationAPI, gas_used: int) -> int: # Self Destruct Refunds num_deletions = len(computation.get_accounts_for_deletion()) @@ -162,8 +162,8 @@ def calculate_gas_refund(cls, def finalize_computation( self, transaction: SignedTransactionAPI, - computation: ComputationAPI - ) -> ComputationAPI: + computation: MessageComputationAPI + ) -> MessageComputationAPI: transaction_context = self.vm_state.get_transaction_context(transaction) gas_remaining = computation.get_gas_remaining() @@ -204,12 +204,12 @@ def finalize_computation( class FrontierState(BaseState): - computation_class: Type[ComputationAPI] = FrontierComputation + message_computation_class: Type[MessageComputationAPI] = FrontierMessageComputation transaction_context_class: Type[TransactionContextAPI] = FrontierTransactionContext account_db_class: Type[AccountDatabaseAPI] = AccountDB transaction_executor_class: Type[TransactionExecutorAPI] = FrontierTransactionExecutor - def apply_transaction(self, transaction: SignedTransactionAPI) -> ComputationAPI: + def apply_transaction(self, transaction: SignedTransactionAPI) -> MessageComputationAPI: executor = self.get_transaction_executor() return executor(transaction) diff --git a/eth/vm/forks/gray_glacier/computation.py b/eth/vm/forks/gray_glacier/computation.py index 00428e6857..378434304d 100644 --- a/eth/vm/forks/gray_glacier/computation.py +++ b/eth/vm/forks/gray_glacier/computation.py @@ -1,9 +1,9 @@ -from ..arrow_glacier.computation import ArrowGlacierComputation +from ..arrow_glacier.computation import ArrowGlacierMessageComputation -class GrayGlacierComputation(ArrowGlacierComputation): +class GrayGlacierMessageComputation(ArrowGlacierMessageComputation): """ - A class for all execution computations in the ``GrayGlacier`` fork. - Inherits from :class:`~eth.vm.forks.arrow_glacier.ArrowGlacierComputation` + A class for all execution *message* computations in the ``GrayGlacier`` fork. + Inherits from :class:`~eth.vm.forks.arrow_glacier.ArrowGlacierMessageComputation` """ pass diff --git a/eth/vm/forks/gray_glacier/state.py b/eth/vm/forks/gray_glacier/state.py index 68e7a99385..629ccd65e4 100644 --- a/eth/vm/forks/gray_glacier/state.py +++ b/eth/vm/forks/gray_glacier/state.py @@ -1,7 +1,7 @@ from typing import Type from eth.abc import TransactionExecutorAPI -from .computation import GrayGlacierComputation +from .computation import GrayGlacierMessageComputation from ..arrow_glacier import ArrowGlacierState from ..arrow_glacier.state import ArrowGlacierTransactionExecutor @@ -11,5 +11,5 @@ class GrayGlacierTransactionExecutor(ArrowGlacierTransactionExecutor): class GrayGlacierState(ArrowGlacierState): - computation_class = GrayGlacierComputation + message_computation_class = GrayGlacierMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = GrayGlacierTransactionExecutor diff --git a/eth/vm/forks/homestead/computation.py b/eth/vm/forks/homestead/computation.py index bafa9d9d27..e2926cecb4 100644 --- a/eth/vm/forks/homestead/computation.py +++ b/eth/vm/forks/homestead/computation.py @@ -9,22 +9,22 @@ ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, ) from eth.vm.forks.frontier.computation import ( - FrontierComputation, + FrontierMessageComputation, ) from .opcodes import HOMESTEAD_OPCODES -class HomesteadComputation(FrontierComputation): +class HomesteadMessageComputation(FrontierMessageComputation): """ - A class for all execution computations in the ``Frontier`` fork. - Inherits from :class:`~eth.vm.forks.frontier.computation.FrontierComputation` + A class for all execution *message* computations in the ``Frontier`` fork. + Inherits from :class:`~eth.vm.forks.frontier.computation.FrontierMessageComputation` """ # Override opcodes = HOMESTEAD_OPCODES @@ -34,7 +34,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> ComputationAPI: + transaction_context: TransactionContextAPI) -> MessageComputationAPI: snapshot = state.snapshot() computation = cls.apply_message(state, message, transaction_context) diff --git a/eth/vm/forks/homestead/state.py b/eth/vm/forks/homestead/state.py index e828bf1b55..c994ee74c3 100644 --- a/eth/vm/forks/homestead/state.py +++ b/eth/vm/forks/homestead/state.py @@ -1,7 +1,7 @@ from typing import Type from eth.abc import ( - ComputationAPI, + MessageComputationAPI, SignedTransactionAPI, ) from eth.vm.forks.frontier.state import ( @@ -9,12 +9,12 @@ FrontierTransactionExecutor, ) -from .computation import HomesteadComputation +from .computation import HomesteadMessageComputation from .validation import validate_homestead_transaction class HomesteadState(FrontierState): - computation_class: Type[ComputationAPI] = HomesteadComputation + message_computation_class: Type[MessageComputationAPI] = HomesteadMessageComputation def validate_transaction(self, transaction: SignedTransactionAPI) -> None: validate_homestead_transaction(self, transaction) diff --git a/eth/vm/forks/istanbul/computation.py b/eth/vm/forks/istanbul/computation.py index 564064c25d..d548a113d8 100644 --- a/eth/vm/forks/istanbul/computation.py +++ b/eth/vm/forks/istanbul/computation.py @@ -10,7 +10,7 @@ PETERSBURG_PRECOMPILES ) from eth.vm.forks.petersburg.computation import ( - PetersburgComputation, + PetersburgMessageComputation, ) from eth.vm.gas_meter import ( allow_negative_refund_strategy, @@ -39,16 +39,17 @@ ) -class IstanbulComputation(PetersburgComputation): +class IstanbulMessageComputation(PetersburgMessageComputation): """ - A class for all execution computations in the ``Istanbul`` fork. - Inherits from :class:`~eth.vm.forks.constantinople.petersburg.PetersburgComputation` + A class for all execution *message* computations in the ``Istanbul`` fork. + Inherits from + :class:`~eth.vm.forks.constantinople.petersburg.PetersburgMessageComputation` """ # Override opcodes = ISTANBUL_OPCODES _precompiles = ISTANBUL_PRECOMPILES - def get_gas_meter(self) -> GasMeter: + def _configure_gas_meter(self) -> GasMeter: return GasMeter( self.msg.gas, allow_negative_refund_strategy diff --git a/eth/vm/forks/istanbul/state.py b/eth/vm/forks/istanbul/state.py index a2f2f10c5d..92969a76ae 100644 --- a/eth/vm/forks/istanbul/state.py +++ b/eth/vm/forks/istanbul/state.py @@ -2,8 +2,8 @@ PetersburgState ) -from .computation import IstanbulComputation +from .computation import IstanbulMessageComputation class IstanbulState(PetersburgState): - computation_class = IstanbulComputation + message_computation_class = IstanbulMessageComputation diff --git a/eth/vm/forks/istanbul/storage.py b/eth/vm/forks/istanbul/storage.py index e0fab3a250..773ba9da77 100644 --- a/eth/vm/forks/istanbul/storage.py +++ b/eth/vm/forks/istanbul/storage.py @@ -1,7 +1,7 @@ from eth_utils.toolz import curry from eth.exceptions import OutOfGas -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation from eth.vm.forks.constantinople.storage import ( GAS_SCHEDULE_EIP1283, ) @@ -19,7 +19,10 @@ @curry -def sstore_eip2200_generic(gas_schedule: NetSStoreGasSchedule, computation: BaseComputation) -> int: +def sstore_eip2200_generic( + gas_schedule: NetSStoreGasSchedule, + computation: MessageComputation, +) -> int: gas_remaining = computation.get_gas_remaining() if gas_remaining <= 2300: raise OutOfGas( diff --git a/eth/vm/forks/london/computation.py b/eth/vm/forks/london/computation.py index ae72e8d627..7e56152073 100644 --- a/eth/vm/forks/london/computation.py +++ b/eth/vm/forks/london/computation.py @@ -1,16 +1,16 @@ from eth.exceptions import ReservedBytesInCode from eth.vm.forks.berlin.computation import ( - BerlinComputation, + BerlinMessageComputation, ) from .opcodes import LONDON_OPCODES from ..london.constants import EIP3541_RESERVED_STARTING_BYTE -class LondonComputation(BerlinComputation): +class LondonMessageComputation(BerlinMessageComputation): """ - A class for all execution computations in the ``London`` fork. - Inherits from :class:`~eth.vm.forks.berlin.BerlinComputation` + A class for all execution *message* computations in the ``London`` fork. + Inherits from :class:`~eth.vm.forks.berlin.BerlinMessageComputation` """ opcodes = LONDON_OPCODES diff --git a/eth/vm/forks/london/state.py b/eth/vm/forks/london/state.py index 51c58be21f..39ea04d7a0 100644 --- a/eth/vm/forks/london/state.py +++ b/eth/vm/forks/london/state.py @@ -6,7 +6,7 @@ ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, SignedTransactionAPI, StateAPI, @@ -27,7 +27,7 @@ generate_contract_address, ) -from .computation import LondonComputation +from .computation import LondonMessageComputation from .validation import validate_london_normalized_transaction from .constants import EIP3529_MAX_REFUND_QUOTIENT @@ -86,7 +86,7 @@ def build_evm_message(self, transaction: SignedTransactionAPI) -> MessageAPI: @classmethod def calculate_gas_refund(cls, - computation: ComputationAPI, + computation: MessageComputationAPI, gas_used: int) -> int: # Self destruct refunds were added in Frontier # London removes them in EIP-3529 @@ -96,7 +96,7 @@ def calculate_gas_refund(cls, class LondonState(BerlinState): - computation_class = LondonComputation + message_computation_class = LondonMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = LondonTransactionExecutor def get_tip(self, transaction: SignedTransactionAPI) -> int: diff --git a/eth/vm/forks/muir_glacier/computation.py b/eth/vm/forks/muir_glacier/computation.py index 722273f5c8..50d0a863af 100644 --- a/eth/vm/forks/muir_glacier/computation.py +++ b/eth/vm/forks/muir_glacier/computation.py @@ -2,7 +2,7 @@ ISTANBUL_PRECOMPILES ) from eth.vm.forks.istanbul.computation import ( - IstanbulComputation, + IstanbulMessageComputation, ) from .opcodes import MUIR_GLACIER_OPCODES @@ -10,10 +10,11 @@ MUIR_GLACIER_PRECOMPILES = ISTANBUL_PRECOMPILES -class MuirGlacierComputation(IstanbulComputation): +class MuirGlacierMessageComputation(IstanbulMessageComputation): """ - A class for all execution computations in the ``MuirGlacier`` fork. - Inherits from :class:`~eth.vm.forks.constantinople.istanbul.IstanbulComputation` + A class for all execution *message* computations in the ``MuirGlacier`` fork. + Inherits from + :class:`~eth.vm.forks.constantinople.istanbul.IstanbulMessageComputation` """ # Override opcodes = MUIR_GLACIER_OPCODES diff --git a/eth/vm/forks/muir_glacier/state.py b/eth/vm/forks/muir_glacier/state.py index a3a25cb764..de97ee944a 100644 --- a/eth/vm/forks/muir_glacier/state.py +++ b/eth/vm/forks/muir_glacier/state.py @@ -2,8 +2,8 @@ IstanbulState ) -from .computation import MuirGlacierComputation +from .computation import MuirGlacierMessageComputation class MuirGlacierState(IstanbulState): - computation_class = MuirGlacierComputation + message_computation_class = MuirGlacierMessageComputation diff --git a/eth/vm/forks/paris/computation.py b/eth/vm/forks/paris/computation.py index 21932300cd..e308f5416c 100644 --- a/eth/vm/forks/paris/computation.py +++ b/eth/vm/forks/paris/computation.py @@ -1,11 +1,10 @@ from .opcodes import PARIS_OPCODES -from eth.vm.forks.gray_glacier.computation import GrayGlacierComputation +from eth.vm.forks.gray_glacier.computation import GrayGlacierMessageComputation -class ParisComputation(GrayGlacierComputation): +class ParisMessageComputation(GrayGlacierMessageComputation): """ - A class for all execution computations in the ``Paris`` hard fork - (a.k.a. "The Merge"). - Inherits from :class:`~eth.vm.forks.gray_glacier.GrayGlacierComputation` + A class for all execution *message* computations in the ``Paris`` hard fork + Inherits from :class:`~eth.vm.forks.gray_glacier.GrayGlacierMessageComputation` """ opcodes = PARIS_OPCODES diff --git a/eth/vm/forks/paris/state.py b/eth/vm/forks/paris/state.py index b7c5104877..321480ce26 100644 --- a/eth/vm/forks/paris/state.py +++ b/eth/vm/forks/paris/state.py @@ -5,7 +5,7 @@ TransactionExecutorAPI, ) from eth_typing import Hash32 -from .computation import ParisComputation +from .computation import ParisMessageComputation from ..gray_glacier import GrayGlacierState from ..gray_glacier.state import GrayGlacierTransactionExecutor @@ -15,7 +15,7 @@ class ParisTransactionExecutor(GrayGlacierTransactionExecutor): class ParisState(GrayGlacierState): - computation_class = ParisComputation + message_computation_class = ParisMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = ParisTransactionExecutor @property diff --git a/eth/vm/forks/petersburg/computation.py b/eth/vm/forks/petersburg/computation.py index f555cfc8d6..6d08ce25c4 100644 --- a/eth/vm/forks/petersburg/computation.py +++ b/eth/vm/forks/petersburg/computation.py @@ -2,7 +2,7 @@ BYZANTIUM_PRECOMPILES ) from eth.vm.forks.byzantium.computation import ( - ByzantiumComputation + ByzantiumMessageComputation ) from .opcodes import PETERSBURG_OPCODES @@ -10,10 +10,11 @@ PETERSBURG_PRECOMPILES = BYZANTIUM_PRECOMPILES -class PetersburgComputation(ByzantiumComputation): +class PetersburgMessageComputation(ByzantiumMessageComputation): """ - A class for all execution computations in the ``Petersburg`` fork. - Inherits from :class:`~eth.vm.forks.byzantium.computation.ByzantiumComputation` + A class for all execution *message* computations in the ``Petersburg`` fork. + Inherits from + :class:`~eth.vm.forks.byzantium.computation.ByzantiumMessageComputation` """ # Override opcodes = PETERSBURG_OPCODES diff --git a/eth/vm/forks/petersburg/state.py b/eth/vm/forks/petersburg/state.py index 476e457083..60b23d12fb 100644 --- a/eth/vm/forks/petersburg/state.py +++ b/eth/vm/forks/petersburg/state.py @@ -2,8 +2,8 @@ ByzantiumState ) -from .computation import PetersburgComputation +from .computation import PetersburgMessageComputation class PetersburgState(ByzantiumState): - computation_class = PetersburgComputation + message_computation_class = PetersburgMessageComputation diff --git a/eth/vm/forks/shanghai/computation.py b/eth/vm/forks/shanghai/computation.py index 0937afcb7e..4f049cde8a 100644 --- a/eth/vm/forks/shanghai/computation.py +++ b/eth/vm/forks/shanghai/computation.py @@ -1,5 +1,5 @@ from eth._utils.numeric import ceil32 -from eth.abc import ComputationAPI, MessageAPI, StateAPI, TransactionContextAPI +from eth.abc import MessageComputationAPI, MessageAPI, StateAPI, TransactionContextAPI from .constants import ( INITCODE_WORD_COST, MAX_INITCODE_SIZE, @@ -8,12 +8,12 @@ from eth.exceptions import ( OutOfGas, ) -from eth.vm.forks.paris.computation import ParisComputation +from eth.vm.forks.paris.computation import ParisMessageComputation -class ShanghaiComputation(ParisComputation): +class ShanghaiComputation(ParisMessageComputation): """ - A class for all execution computations in the ``Shanghai`` hard fork + A class for all execution *message* computations in the ``Shanghai`` hard fork """ opcodes = SHANGHAI_OPCODES @@ -40,7 +40,7 @@ def validate_create_message(cls, message: MessageAPI) -> None: ) @classmethod - def consume_initcode_gas_cost(cls, computation: ComputationAPI) -> None: + def consume_initcode_gas_cost(cls, computation: MessageComputationAPI) -> None: # EIP-3860: initcode gas cost initcode_length = len(computation.msg.code) diff --git a/eth/vm/forks/shanghai/state.py b/eth/vm/forks/shanghai/state.py index 05e728f7ce..b67c5aa7de 100644 --- a/eth/vm/forks/shanghai/state.py +++ b/eth/vm/forks/shanghai/state.py @@ -14,7 +14,7 @@ class ShanghaiTransactionExecutor(ParisTransactionExecutor): class ShanghaiState(ParisState): - computation_class = ShanghaiComputation + message_computation_class = ShanghaiComputation transaction_executor_class: Type[TransactionExecutorAPI] = ShanghaiTransactionExecutor # noqa: E501 def apply_withdrawal(self, withdrawal: WithdrawalAPI) -> None: diff --git a/eth/vm/forks/spurious_dragon/_utils.py b/eth/vm/forks/spurious_dragon/_utils.py index 35fd999ffa..a55bce7f66 100644 --- a/eth/vm/forks/spurious_dragon/_utils.py +++ b/eth/vm/forks/spurious_dragon/_utils.py @@ -10,7 +10,7 @@ force_bytes_to_address, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation THREE = force_bytes_to_address(b'\x03') @@ -18,7 +18,7 @@ @to_set def collect_touched_accounts( - computation: BaseComputation, + computation: MessageComputation, ancestor_had_error: bool = False ) -> Iterable[Address]: """ diff --git a/eth/vm/forks/spurious_dragon/computation.py b/eth/vm/forks/spurious_dragon/computation.py index bfa863e08b..a9848a611a 100644 --- a/eth/vm/forks/spurious_dragon/computation.py +++ b/eth/vm/forks/spurious_dragon/computation.py @@ -6,7 +6,7 @@ from eth import constants from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, @@ -16,17 +16,18 @@ VMError, ) from eth.vm.forks.homestead.computation import ( - HomesteadComputation, + HomesteadMessageComputation, ) from ..spurious_dragon.constants import EIP170_CODE_SIZE_LIMIT from .opcodes import SPURIOUS_DRAGON_OPCODES -class SpuriousDragonComputation(HomesteadComputation): +class SpuriousDragonMessageComputation(HomesteadMessageComputation): """ - A class for all execution computations in the ``SpuriousDragon`` fork. - Inherits from :class:`~eth.vm.forks.homestead.computation.HomesteadComputation` + A class for all execution *message* computations in the ``SpuriousDragon`` fork. + Inherits from + :class:`~eth.vm.forks.homestead.computation.HomesteadMessageComputation` """ # Override opcodes = SPURIOUS_DRAGON_OPCODES @@ -36,7 +37,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> ComputationAPI: + transaction_context: TransactionContextAPI) -> MessageComputationAPI: snapshot = state.snapshot() diff --git a/eth/vm/forks/spurious_dragon/state.py b/eth/vm/forks/spurious_dragon/state.py index 802278b595..67e0e98096 100644 --- a/eth/vm/forks/spurious_dragon/state.py +++ b/eth/vm/forks/spurious_dragon/state.py @@ -5,7 +5,7 @@ ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, SignedTransactionAPI, TransactionExecutorAPI, ) @@ -14,14 +14,14 @@ HomesteadTransactionExecutor, ) -from .computation import SpuriousDragonComputation +from .computation import SpuriousDragonMessageComputation from ._utils import collect_touched_accounts class SpuriousDragonTransactionExecutor(HomesteadTransactionExecutor): def finalize_computation(self, transaction: SignedTransactionAPI, - computation: ComputationAPI) -> ComputationAPI: + computation: MessageComputationAPI) -> MessageComputationAPI: computation = super().finalize_computation(transaction, computation) # @@ -45,5 +45,5 @@ def finalize_computation(self, class SpuriousDragonState(HomesteadState): - computation_class: Type[ComputationAPI] = SpuriousDragonComputation + message_computation_class: Type[MessageComputationAPI] = SpuriousDragonMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = SpuriousDragonTransactionExecutor diff --git a/eth/vm/forks/tangerine_whistle/computation.py b/eth/vm/forks/tangerine_whistle/computation.py index 22916d3202..9612d80dbc 100644 --- a/eth/vm/forks/tangerine_whistle/computation.py +++ b/eth/vm/forks/tangerine_whistle/computation.py @@ -1,12 +1,13 @@ -from ..homestead.computation import HomesteadComputation +from ..homestead.computation import HomesteadMessageComputation from .opcodes import TANGERINE_WHISTLE_OPCODES -class TangerineWhistleComputation(HomesteadComputation): +class TangerineWhistleComputation(HomesteadMessageComputation): """ - A class for all execution computations in the ``TangerineWhistle`` fork. - Inherits from :class:`~eth.vm.forks.homestead.computation.HomesteadComputation` + A class for all execution *message* computations in the ``TangerineWhistle`` fork. + Inherits from + :class:`~eth.vm.forks.homestead.computation.HomesteadMessageComputation` """ # Override opcodes = TANGERINE_WHISTLE_OPCODES diff --git a/eth/vm/forks/tangerine_whistle/state.py b/eth/vm/forks/tangerine_whistle/state.py index 4c8715d27a..b815630563 100644 --- a/eth/vm/forks/tangerine_whistle/state.py +++ b/eth/vm/forks/tangerine_whistle/state.py @@ -4,4 +4,4 @@ class TangerineWhistleState(HomesteadState): - computation_class = TangerineWhistleComputation + message_computation_class = TangerineWhistleComputation diff --git a/eth/vm/logic/arithmetic.py b/eth/vm/logic/arithmetic.py index 07d46b80ec..8be2c52380 100644 --- a/eth/vm/logic/arithmetic.py +++ b/eth/vm/logic/arithmetic.py @@ -10,10 +10,10 @@ ceil8, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def add(computation: BaseComputation) -> None: +def add(computation: MessageComputation) -> None: """ Addition """ @@ -24,7 +24,7 @@ def add(computation: BaseComputation) -> None: computation.stack_push_int(result) -def addmod(computation: BaseComputation) -> None: +def addmod(computation: MessageComputation) -> None: """ Modulo Addition """ @@ -38,7 +38,7 @@ def addmod(computation: BaseComputation) -> None: computation.stack_push_int(result) -def sub(computation: BaseComputation) -> None: +def sub(computation: MessageComputation) -> None: """ Subtraction """ @@ -49,7 +49,7 @@ def sub(computation: BaseComputation) -> None: computation.stack_push_int(result) -def mod(computation: BaseComputation) -> None: +def mod(computation: MessageComputation) -> None: """ Modulo """ @@ -63,7 +63,7 @@ def mod(computation: BaseComputation) -> None: computation.stack_push_int(result) -def smod(computation: BaseComputation) -> None: +def smod(computation: MessageComputation) -> None: """ Signed Modulo """ @@ -82,7 +82,7 @@ def smod(computation: BaseComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def mul(computation: BaseComputation) -> None: +def mul(computation: MessageComputation) -> None: """ Multiplication """ @@ -93,7 +93,7 @@ def mul(computation: BaseComputation) -> None: computation.stack_push_int(result) -def mulmod(computation: BaseComputation) -> None: +def mulmod(computation: MessageComputation) -> None: """ Modulo Multiplication """ @@ -106,7 +106,7 @@ def mulmod(computation: BaseComputation) -> None: computation.stack_push_int(result) -def div(computation: BaseComputation) -> None: +def div(computation: MessageComputation) -> None: """ Division """ @@ -120,7 +120,7 @@ def div(computation: BaseComputation) -> None: computation.stack_push_int(result) -def sdiv(computation: BaseComputation) -> None: +def sdiv(computation: MessageComputation) -> None: """ Signed Division """ @@ -140,7 +140,7 @@ def sdiv(computation: BaseComputation) -> None: @curry -def exp(computation: BaseComputation, gas_per_byte: int) -> None: +def exp(computation: MessageComputation, gas_per_byte: int) -> None: """ Exponentiation """ @@ -164,7 +164,7 @@ def exp(computation: BaseComputation, gas_per_byte: int) -> None: computation.stack_push_int(result) -def signextend(computation: BaseComputation) -> None: +def signextend(computation: MessageComputation) -> None: """ Signed Extend """ @@ -183,7 +183,7 @@ def signextend(computation: BaseComputation) -> None: computation.stack_push_int(result) -def shl(computation: BaseComputation) -> None: +def shl(computation: MessageComputation) -> None: """ Bitwise left shift """ @@ -197,7 +197,7 @@ def shl(computation: BaseComputation) -> None: computation.stack_push_int(result) -def shr(computation: BaseComputation) -> None: +def shr(computation: MessageComputation) -> None: """ Bitwise right shift """ @@ -211,7 +211,7 @@ def shr(computation: BaseComputation) -> None: computation.stack_push_int(result) -def sar(computation: BaseComputation) -> None: +def sar(computation: MessageComputation) -> None: """ Arithmetic bitwise right shift """ diff --git a/eth/vm/logic/block.py b/eth/vm/logic/block.py index be97c0a270..fe75890b12 100644 --- a/eth/vm/logic/block.py +++ b/eth/vm/logic/block.py @@ -1,7 +1,7 @@ -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def blockhash(computation: BaseComputation) -> None: +def blockhash(computation: MessageComputation) -> None: block_number = computation.stack_pop1_int() block_hash = computation.state.get_ancestor_hash(block_number) @@ -9,29 +9,29 @@ def blockhash(computation: BaseComputation) -> None: computation.stack_push_bytes(block_hash) -def coinbase(computation: BaseComputation) -> None: +def coinbase(computation: MessageComputation) -> None: computation.stack_push_bytes(computation.state.coinbase) -def timestamp(computation: BaseComputation) -> None: +def timestamp(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.timestamp) -def number(computation: BaseComputation) -> None: +def number(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.block_number) -def difficulty(computation: BaseComputation) -> None: +def difficulty(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.difficulty) -def gaslimit(computation: BaseComputation) -> None: +def gaslimit(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.gas_limit) -def basefee(computation: BaseComputation) -> None: +def basefee(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.base_fee) -def mixhash(computation: BaseComputation) -> None: +def mixhash(computation: MessageComputation) -> None: computation.stack_push_bytes(computation.state.mix_hash) diff --git a/eth/vm/logic/call.py b/eth/vm/logic/call.py index 5d8c4294a9..f704ca7e6d 100644 --- a/eth/vm/logic/call.py +++ b/eth/vm/logic/call.py @@ -14,7 +14,7 @@ ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, ) from eth.exceptions import ( OutOfGas, @@ -35,18 +35,18 @@ class BaseCall(Opcode, ABC): @abstractmethod def compute_msg_extra_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> int: raise NotImplementedError("Must be implemented by subclasses") @abstractmethod - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: raise NotImplementedError("Must be implemented by subclasses") def compute_msg_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -55,14 +55,18 @@ def compute_msg_gas(self, child_msg_gas = gas + (constants.GAS_CALLSTIPEND if value else 0) return child_msg_gas, total_fee - def get_account_load_fee(self, computation: ComputationAPI, code_address: Address) -> int: + def get_account_load_fee( + self, + computation: MessageComputationAPI, + code_address: Address, + ) -> int: """ Return the gas cost for implicitly loading the account needed to access the bytecode. """ return 0 - def __call__(self, computation: ComputationAPI) -> None: + def __call__(self, computation: MessageComputationAPI) -> None: computation.consume_gas( self.gas_cost, reason=self.mnemonic, @@ -159,7 +163,7 @@ def __call__(self, computation: ComputationAPI) -> None: # TODO: after upgrade to py3.6, use a TypedDict and try again child_msg = computation.prepare_child_message(**child_msg_kwargs) # type: ignore - child_computation = computation.apply_child_computation(child_msg) + child_computation = computation.apply_child_message_computation(child_msg) if child_computation.is_error: computation.stack_push_int(0) @@ -180,7 +184,7 @@ def __call__(self, computation: ComputationAPI) -> None: class Call(BaseCall): def compute_msg_extra_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> int: @@ -190,7 +194,7 @@ def compute_msg_extra_gas(self, create_gas_fee = constants.GAS_NEWACCOUNT if not account_exists else 0 return transfer_gas_fee + create_gas_fee - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: gas = computation.stack_pop1_int() to = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -219,13 +223,13 @@ def get_call_params(self, computation: ComputationAPI) -> CallParams: class CallCode(BaseCall): def compute_msg_extra_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> int: return constants.GAS_CALLVALUE if value else 0 - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: gas = computation.stack_pop1_int() code_address = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -257,20 +261,20 @@ def get_call_params(self, computation: ComputationAPI) -> CallParams: class DelegateCall(BaseCall): def compute_msg_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: return gas, gas def compute_msg_extra_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> int: return 0 - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: gas = computation.stack_pop1_int() code_address = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -305,7 +309,7 @@ def get_call_params(self, computation: ComputationAPI) -> CallParams: # class CallEIP150(Call): def compute_msg_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -322,7 +326,7 @@ def compute_msg_gas(self, class CallCodeEIP150(CallCode): def compute_msg_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -339,7 +343,7 @@ def compute_msg_gas(self, class DelegateCallEIP150(DelegateCall): def compute_msg_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -360,7 +364,7 @@ def max_child_gas_eip150(gas: int) -> int: def compute_eip150_msg_gas(*, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, extra_gas: int, value: int, @@ -387,7 +391,7 @@ def compute_eip150_msg_gas(*, # class CallEIP161(CallEIP150): def compute_msg_extra_gas(self, - computation: ComputationAPI, + computation: MessageComputationAPI, gas: int, to: Address, value: int) -> int: @@ -405,7 +409,7 @@ def compute_msg_extra_gas(self, # Byzantium # class StaticCall(CallEIP161): - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: gas = computation.stack_pop1_int() to = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -432,7 +436,7 @@ def get_call_params(self, computation: ComputationAPI) -> CallParams: class CallByzantium(CallEIP161): - def get_call_params(self, computation: ComputationAPI) -> CallParams: + def get_call_params(self, computation: MessageComputationAPI) -> CallParams: call_params = super().get_call_params(computation) value = call_params[1] if computation.msg.is_static and value != 0: diff --git a/eth/vm/logic/comparison.py b/eth/vm/logic/comparison.py index 19eafa2548..3f89ca2884 100644 --- a/eth/vm/logic/comparison.py +++ b/eth/vm/logic/comparison.py @@ -5,10 +5,10 @@ unsigned_to_signed, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def lt(computation: BaseComputation) -> None: +def lt(computation: MessageComputation) -> None: """ Lesser Comparison """ @@ -22,7 +22,7 @@ def lt(computation: BaseComputation) -> None: computation.stack_push_int(result) -def gt(computation: BaseComputation) -> None: +def gt(computation: MessageComputation) -> None: """ Greater Comparison """ @@ -36,7 +36,7 @@ def gt(computation: BaseComputation) -> None: computation.stack_push_int(result) -def slt(computation: BaseComputation) -> None: +def slt(computation: MessageComputation) -> None: """ Signed Lesser Comparison """ @@ -53,7 +53,7 @@ def slt(computation: BaseComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def sgt(computation: BaseComputation) -> None: +def sgt(computation: MessageComputation) -> None: """ Signed Greater Comparison """ @@ -70,7 +70,7 @@ def sgt(computation: BaseComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def eq(computation: BaseComputation) -> None: +def eq(computation: MessageComputation) -> None: """ Equality """ @@ -84,7 +84,7 @@ def eq(computation: BaseComputation) -> None: computation.stack_push_int(result) -def iszero(computation: BaseComputation) -> None: +def iszero(computation: MessageComputation) -> None: """ Not """ @@ -98,7 +98,7 @@ def iszero(computation: BaseComputation) -> None: computation.stack_push_int(result) -def and_op(computation: BaseComputation) -> None: +def and_op(computation: MessageComputation) -> None: """ Bitwise And """ @@ -109,7 +109,7 @@ def and_op(computation: BaseComputation) -> None: computation.stack_push_int(result) -def or_op(computation: BaseComputation) -> None: +def or_op(computation: MessageComputation) -> None: """ Bitwise Or """ @@ -120,7 +120,7 @@ def or_op(computation: BaseComputation) -> None: computation.stack_push_int(result) -def xor(computation: BaseComputation) -> None: +def xor(computation: MessageComputation) -> None: """ Bitwise XOr """ @@ -131,7 +131,7 @@ def xor(computation: BaseComputation) -> None: computation.stack_push_int(result) -def not_op(computation: BaseComputation) -> None: +def not_op(computation: MessageComputation) -> None: """ Not """ @@ -142,7 +142,7 @@ def not_op(computation: BaseComputation) -> None: computation.stack_push_int(result) -def byte_op(computation: BaseComputation) -> None: +def byte_op(computation: MessageComputation) -> None: """ Bitwise And """ diff --git a/eth/vm/logic/context.py b/eth/vm/logic/context.py index a0ebee7830..74a89b16a9 100644 --- a/eth/vm/logic/context.py +++ b/eth/vm/logic/context.py @@ -7,7 +7,7 @@ from eth import constants from eth.abc import ( - ComputationAPI, + MessageComputationAPI, ) from eth.exceptions import ( OutOfBoundsRead, @@ -20,40 +20,40 @@ ceil32, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def balance(computation: BaseComputation) -> None: +def balance(computation: MessageComputation) -> None: addr = force_bytes_to_address(computation.stack_pop1_bytes()) push_balance_of_address(addr, computation) -def selfbalance(computation: BaseComputation) -> None: +def selfbalance(computation: MessageComputation) -> None: push_balance_of_address(computation.msg.storage_address, computation) -def push_balance_of_address(address: Address, computation: ComputationAPI) -> None: +def push_balance_of_address(address: Address, computation: MessageComputationAPI) -> None: balance = computation.state.get_balance(address) computation.stack_push_int(balance) -def origin(computation: BaseComputation) -> None: +def origin(computation: MessageComputation) -> None: computation.stack_push_bytes(computation.transaction_context.origin) -def address(computation: BaseComputation) -> None: +def address(computation: MessageComputation) -> None: computation.stack_push_bytes(computation.msg.storage_address) -def caller(computation: BaseComputation) -> None: +def caller(computation: MessageComputation) -> None: computation.stack_push_bytes(computation.msg.sender) -def callvalue(computation: BaseComputation) -> None: +def callvalue(computation: MessageComputation) -> None: computation.stack_push_int(computation.msg.value) -def calldataload(computation: BaseComputation) -> None: +def calldataload(computation: MessageComputation) -> None: """ Load call data into memory. """ @@ -66,12 +66,12 @@ def calldataload(computation: BaseComputation) -> None: computation.stack_push_bytes(normalized_value) -def calldatasize(computation: BaseComputation) -> None: +def calldatasize(computation: MessageComputation) -> None: size = len(computation.msg.data) computation.stack_push_int(size) -def calldatacopy(computation: BaseComputation) -> None: +def calldatacopy(computation: MessageComputation) -> None: ( mem_start_position, calldata_start_position, @@ -93,16 +93,16 @@ def calldatacopy(computation: BaseComputation) -> None: computation.memory_write(mem_start_position, size, padded_value) -def chain_id(computation: BaseComputation) -> None: +def chain_id(computation: MessageComputation) -> None: computation.stack_push_int(computation.state.execution_context.chain_id) -def codesize(computation: BaseComputation) -> None: +def codesize(computation: MessageComputation) -> None: size = len(computation.code) computation.stack_push_int(size) -def codecopy(computation: BaseComputation) -> None: +def codecopy(computation: MessageComputation) -> None: ( mem_start_position, code_start_position, @@ -127,18 +127,18 @@ def codecopy(computation: BaseComputation) -> None: computation.memory_write(mem_start_position, size, padded_code_bytes) -def gasprice(computation: BaseComputation) -> None: +def gasprice(computation: MessageComputation) -> None: computation.stack_push_int(computation.transaction_context.gas_price) -def extcodesize(computation: BaseComputation) -> None: +def extcodesize(computation: MessageComputation) -> None: account = force_bytes_to_address(computation.stack_pop1_bytes()) code_size = len(computation.state.get_code(account)) computation.stack_push_int(code_size) -def extcodecopy_execute(computation: ComputationAPI) -> Tuple[Address, int]: +def extcodecopy_execute(computation: MessageComputationAPI) -> Tuple[Address, int]: """ Runs the logical component of extcodecopy, without charging gas. @@ -163,7 +163,7 @@ def extcodecopy_execute(computation: ComputationAPI) -> Tuple[Address, int]: return account, size -def consume_extcodecopy_word_cost(computation: ComputationAPI, size: int) -> None: +def consume_extcodecopy_word_cost(computation: MessageComputationAPI, size: int) -> None: word_count = ceil32(size) // 32 copy_gas_cost = constants.GAS_COPY * word_count computation.consume_gas( @@ -172,12 +172,12 @@ def consume_extcodecopy_word_cost(computation: ComputationAPI, size: int) -> Non ) -def extcodecopy(computation: BaseComputation) -> None: +def extcodecopy(computation: MessageComputation) -> None: _address, size = extcodecopy_execute(computation) consume_extcodecopy_word_cost(computation, size) -def extcodehash(computation: BaseComputation) -> None: +def extcodehash(computation: MessageComputation) -> None: """ Return the code hash for a given address. EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1052.md @@ -191,12 +191,12 @@ def extcodehash(computation: BaseComputation) -> None: computation.stack_push_bytes(state.get_code_hash(account)) -def returndatasize(computation: BaseComputation) -> None: +def returndatasize(computation: MessageComputation) -> None: size = len(computation.return_data) computation.stack_push_int(size) -def returndatacopy(computation: BaseComputation) -> None: +def returndatacopy(computation: MessageComputation) -> None: ( mem_start_position, returndata_start_position, diff --git a/eth/vm/logic/duplication.py b/eth/vm/logic/duplication.py index 4d4b75eb27..a2e89904be 100644 --- a/eth/vm/logic/duplication.py +++ b/eth/vm/logic/duplication.py @@ -1,9 +1,9 @@ import functools -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def dup_XX(computation: BaseComputation, position: int) -> None: +def dup_XX(computation: MessageComputation, position: int) -> None: """ Stack item duplication. """ diff --git a/eth/vm/logic/flow.py b/eth/vm/logic/flow.py index 47fc7a15ad..b8533f454a 100644 --- a/eth/vm/logic/flow.py +++ b/eth/vm/logic/flow.py @@ -4,17 +4,17 @@ Halt, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation from eth.vm.opcode_values import ( JUMPDEST, ) -def stop(computation: BaseComputation) -> None: +def stop(computation: MessageComputation) -> None: raise Halt('STOP') -def jump(computation: BaseComputation) -> None: +def jump(computation: MessageComputation) -> None: jump_dest = computation.stack_pop1_int() computation.code.program_counter = jump_dest @@ -28,7 +28,7 @@ def jump(computation: BaseComputation) -> None: raise InvalidInstruction("Jump resulted in invalid instruction") -def jumpi(computation: BaseComputation) -> None: +def jumpi(computation: MessageComputation) -> None: jump_dest, check_value = computation.stack_pop_ints(2) if check_value: @@ -43,17 +43,17 @@ def jumpi(computation: BaseComputation) -> None: raise InvalidInstruction("Jump resulted in invalid instruction") -def jumpdest(computation: BaseComputation) -> None: +def jumpdest(computation: MessageComputation) -> None: pass -def program_counter(computation: BaseComputation) -> None: +def program_counter(computation: MessageComputation) -> None: pc = max(computation.code.program_counter - 1, 0) computation.stack_push_int(pc) -def gas(computation: BaseComputation) -> None: +def gas(computation: MessageComputation) -> None: gas_remaining = computation.get_gas_remaining() computation.stack_push_int(gas_remaining) diff --git a/eth/vm/logic/invalid.py b/eth/vm/logic/invalid.py index 462b6ac8e7..4566f22b2d 100644 --- a/eth/vm/logic/invalid.py +++ b/eth/vm/logic/invalid.py @@ -1,4 +1,4 @@ -from eth.abc import ComputationAPI +from eth.abc import MessageComputationAPI from eth.exceptions import InvalidInstruction from eth.vm.opcode import Opcode @@ -11,7 +11,7 @@ def __init__(self, value: int) -> None: self.value = value super().__init__() - def __call__(self, computation: ComputationAPI) -> None: + def __call__(self, computation: MessageComputationAPI) -> None: raise InvalidInstruction( f"Invalid opcode 0x{self.value:x} @ {computation.code.program_counter - 1}" ) diff --git a/eth/vm/logic/logging.py b/eth/vm/logic/logging.py index cfd881d437..8be33e94a8 100644 --- a/eth/vm/logic/logging.py +++ b/eth/vm/logic/logging.py @@ -2,10 +2,10 @@ from typing import Tuple from eth import constants -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def log_XX(computation: BaseComputation, topic_count: int) -> None: +def log_XX(computation: MessageComputation, topic_count: int) -> None: if topic_count < 0 or topic_count > 4: raise TypeError("Invalid log topic size. Must be 0, 1, 2, 3, or 4") diff --git a/eth/vm/logic/memory.py b/eth/vm/logic/memory.py index 696693d449..55790f3a70 100644 --- a/eth/vm/logic/memory.py +++ b/eth/vm/logic/memory.py @@ -1,7 +1,7 @@ -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def mstore(computation: BaseComputation) -> None: +def mstore(computation: MessageComputation) -> None: start_position = computation.stack_pop1_int() value = computation.stack_pop1_bytes() @@ -13,7 +13,7 @@ def mstore(computation: BaseComputation) -> None: computation.memory_write(start_position, 32, normalized_value) -def mstore8(computation: BaseComputation) -> None: +def mstore8(computation: MessageComputation) -> None: start_position = computation.stack_pop1_int() value = computation.stack_pop1_bytes() @@ -25,7 +25,7 @@ def mstore8(computation: BaseComputation) -> None: computation.memory_write(start_position, 1, normalized_value) -def mload(computation: BaseComputation) -> None: +def mload(computation: MessageComputation) -> None: start_position = computation.stack_pop1_int() computation.extend_memory(start_position, 32) @@ -34,5 +34,5 @@ def mload(computation: BaseComputation) -> None: computation.stack_push_bytes(value) -def msize(computation: BaseComputation) -> None: +def msize(computation: MessageComputation) -> None: computation.stack_push_int(len(computation._memory)) diff --git a/eth/vm/logic/sha3.py b/eth/vm/logic/sha3.py index 6c79fad934..dbf488fd99 100644 --- a/eth/vm/logic/sha3.py +++ b/eth/vm/logic/sha3.py @@ -4,10 +4,10 @@ from eth._utils.numeric import ( ceil32, ) -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def sha3(computation: BaseComputation) -> None: +def sha3(computation: MessageComputation) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) diff --git a/eth/vm/logic/stack.py b/eth/vm/logic/stack.py index 2a995c12cf..35bd1b7def 100644 --- a/eth/vm/logic/stack.py +++ b/eth/vm/logic/stack.py @@ -1,13 +1,13 @@ import functools -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def pop(computation: BaseComputation) -> None: +def pop(computation: MessageComputation) -> None: computation.stack_pop1_any() -def push_XX(computation: BaseComputation, size: int) -> None: +def push_XX(computation: MessageComputation, size: int) -> None: raw_value = computation.code.read(size) # This is a performance-sensitive area. diff --git a/eth/vm/logic/storage.py b/eth/vm/logic/storage.py index 47bf2cb74d..b13dde35ab 100644 --- a/eth/vm/logic/storage.py +++ b/eth/vm/logic/storage.py @@ -5,10 +5,10 @@ ) from eth import constants -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def sstore(computation: BaseComputation) -> None: +def sstore(computation: MessageComputation) -> None: slot, value = computation.stack_pop_ints(2) current_value = computation.state.get_storage( @@ -52,7 +52,7 @@ def sstore(computation: BaseComputation) -> None: ) -def sload(computation: BaseComputation) -> None: +def sload(computation: MessageComputation) -> None: slot = computation.stack_pop1_int() value = computation.state.get_storage( @@ -76,7 +76,7 @@ class NetSStoreGasSchedule(NamedTuple): sstore_clears_schedule: int -def net_sstore(gas_schedule: NetSStoreGasSchedule, computation: BaseComputation) -> int: +def net_sstore(gas_schedule: NetSStoreGasSchedule, computation: MessageComputation) -> int: """ :return slot: where the new value was stored """ diff --git a/eth/vm/logic/swap.py b/eth/vm/logic/swap.py index b2b75d0c1a..c2709a82a6 100644 --- a/eth/vm/logic/swap.py +++ b/eth/vm/logic/swap.py @@ -1,9 +1,9 @@ import functools -from eth.vm.computation import BaseComputation +from eth.vm.computation import MessageComputation -def swap_XX(computation: BaseComputation, position: int) -> None: +def swap_XX(computation: MessageComputation, position: int) -> None: """ Stack item swapping """ diff --git a/eth/vm/logic/system.py b/eth/vm/logic/system.py index 214346f49b..5f57aa6f11 100644 --- a/eth/vm/logic/system.py +++ b/eth/vm/logic/system.py @@ -20,7 +20,7 @@ ceil32, ) from eth.abc import ( - ComputationAPI, + MessageComputationAPI, MessageAPI, ) from eth.vm import mnemonics @@ -29,7 +29,7 @@ from .call import max_child_gas_eip150 -def return_op(computation: ComputationAPI) -> None: +def return_op(computation: MessageComputationAPI) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) @@ -38,7 +38,7 @@ def return_op(computation: ComputationAPI) -> None: raise Halt('RETURN') -def revert(computation: ComputationAPI) -> None: +def revert(computation: MessageComputationAPI) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) @@ -47,12 +47,12 @@ def revert(computation: ComputationAPI) -> None: raise Revert(computation.output) -def selfdestruct(computation: ComputationAPI) -> None: +def selfdestruct(computation: MessageComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) _selfdestruct(computation, beneficiary) -def selfdestruct_eip150(computation: ComputationAPI) -> None: +def selfdestruct_eip150(computation: MessageComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) if not computation.state.account_exists(beneficiary): computation.consume_gas( @@ -62,7 +62,10 @@ def selfdestruct_eip150(computation: ComputationAPI) -> None: _selfdestruct(computation, beneficiary) -def selfdestruct_eip161_on_address(computation: ComputationAPI, beneficiary: Address) -> None: +def selfdestruct_eip161_on_address( + computation: MessageComputationAPI, + beneficiary: Address, +) -> None: is_dead = ( not computation.state.account_exists(beneficiary) or computation.state.account_is_empty(beneficiary) @@ -75,12 +78,12 @@ def selfdestruct_eip161_on_address(computation: ComputationAPI, beneficiary: Add _selfdestruct(computation, beneficiary) -def selfdestruct_eip161(computation: ComputationAPI) -> None: +def selfdestruct_eip161(computation: MessageComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) selfdestruct_eip161_on_address(computation, beneficiary) -def _selfdestruct(computation: ComputationAPI, beneficiary: Address) -> None: +def _selfdestruct(computation: MessageComputationAPI, beneficiary: Address) -> None: local_balance = computation.state.get_balance(computation.msg.storage_address) beneficiary_balance = computation.state.get_balance(beneficiary) @@ -128,7 +131,7 @@ def get_gas_cost(self, data: CreateOpcodeStackData) -> int: def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: ComputationAPI) -> Address: + computation: MessageComputationAPI) -> Address: creation_nonce = computation.state.get_nonce(computation.msg.storage_address) computation.state.increment_nonce(computation.msg.storage_address) @@ -140,12 +143,12 @@ def generate_contract_address(self, return contract_address - def get_stack_data(self, computation: ComputationAPI) -> CreateOpcodeStackData: + def get_stack_data(self, computation: MessageComputationAPI) -> CreateOpcodeStackData: endowment, memory_start, memory_length = computation.stack_pop_ints(3) return CreateOpcodeStackData(endowment, memory_start, memory_length) - def __call__(self, computation: ComputationAPI) -> None: + def __call__(self, computation: MessageComputationAPI) -> None: stack_data = self.get_stack_data(computation) @@ -206,8 +209,12 @@ def __call__(self, computation: ComputationAPI) -> None: ) self.apply_create_message(computation, child_msg) - def apply_create_message(self, computation: ComputationAPI, child_msg: MessageAPI) -> None: - child_computation = computation.apply_child_computation(child_msg) + def apply_create_message( + self, + computation: MessageComputationAPI, + child_msg: MessageAPI, + ) -> None: + child_computation = computation.apply_child_message_computation(child_msg) if child_computation.is_error: computation.stack_push_int(0) @@ -223,7 +230,7 @@ def max_child_gas_modifier(self, gas: int) -> int: class CreateByzantium(CreateEIP150): - def __call__(self, computation: ComputationAPI) -> None: + def __call__(self, computation: MessageComputationAPI) -> None: if computation.msg.is_static: raise WriteProtection("Cannot modify state while inside of a STATICCALL context") return super().__call__(computation) @@ -231,7 +238,7 @@ def __call__(self, computation: ComputationAPI) -> None: class Create2(CreateByzantium): - def get_stack_data(self, computation: ComputationAPI) -> CreateOpcodeStackData: + def get_stack_data(self, computation: MessageComputationAPI) -> CreateOpcodeStackData: endowment, memory_start, memory_length, salt = computation.stack_pop_ints(4) return CreateOpcodeStackData(endowment, memory_start, memory_length, salt) @@ -242,7 +249,7 @@ def get_gas_cost(self, data: CreateOpcodeStackData) -> int: def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: ComputationAPI) -> Address: + computation: MessageComputationAPI) -> Address: computation.state.increment_nonce(computation.msg.storage_address) return generate_safe_contract_address( @@ -251,7 +258,11 @@ def generate_contract_address(self, call_data ) - def apply_create_message(self, computation: ComputationAPI, child_msg: MessageAPI) -> None: + def apply_create_message( + self, + computation: MessageComputationAPI, + child_msg: MessageAPI, + ) -> None: # We need to ensure that creation operates on empty storage **and** # that if the initialization code fails that we revert the account back # to its original state root. @@ -259,7 +270,7 @@ def apply_create_message(self, computation: ComputationAPI, child_msg: MessageAP computation.state.delete_storage(child_msg.storage_address) - child_computation = computation.apply_child_computation(child_msg) + child_computation = computation.apply_child_message_computation(child_msg) if child_computation.is_error: computation.state.revert(snapshot) diff --git a/eth/vm/opcode.py b/eth/vm/opcode.py index 5232f45555..3da0487410 100644 --- a/eth/vm/opcode.py +++ b/eth/vm/opcode.py @@ -14,7 +14,7 @@ from eth._utils.datatypes import Configurable from eth.abc import ( - ComputationAPI, + MessageComputationAPI, OpcodeAPI, ) @@ -43,7 +43,7 @@ def as_opcode(cls: Type[T], gas_cost: int) -> T: if gas_cost: @functools.wraps(logic_fn) - def wrapped_logic_fn(computation: ComputationAPI) -> Any: + def wrapped_logic_fn(computation: MessageComputationAPI) -> Any: """ Wrapper function for the logic function which consumes the base opcode gas cost prior to execution. diff --git a/eth/vm/state.py b/eth/vm/state.py index e3f987fbb8..311fd75d2e 100644 --- a/eth/vm/state.py +++ b/eth/vm/state.py @@ -20,7 +20,7 @@ from eth.abc import ( AccountDatabaseAPI, AtomicDatabaseAPI, - ComputationAPI, + MessageComputationAPI, ExecutionContextAPI, MessageAPI, SignedTransactionAPI, @@ -45,7 +45,7 @@ class BaseState(Configurable, StateAPI): # __slots__ = ['_db', 'execution_context', '_account_db'] - computation_class: Type[ComputationAPI] = None + message_computation_class: Type[MessageComputationAPI] = None transaction_context_class: Type[TransactionContextAPI] = None account_db_class: Type[AccountDatabaseAPI] = None transaction_executor_class: Type[TransactionExecutorAPI] = None @@ -188,7 +188,7 @@ def is_address_warm(self, address: Address) -> bool: """ return ( self._account_db.is_address_warm(address) - or address in self.computation_class.get_precompiles() + or address in self.message_computation_class.get_precompiles() ) def mark_address_warm(self, address: Address) -> None: @@ -242,11 +242,11 @@ def get_ancestor_hash(self, block_number: int) -> Hash32: # def get_computation(self, message: MessageAPI, - transaction_context: TransactionContextAPI) -> ComputationAPI: - if self.computation_class is None: - raise AttributeError("No `computation_class` has been set for this State") + transaction_context: TransactionContextAPI) -> MessageComputationAPI: + if self.message_computation_class is None: + raise AttributeError("No `message_computation_class` has been set for this State") else: - computation = self.computation_class(self, message, transaction_context) + computation = self.message_computation_class(self, message, transaction_context) return computation # @@ -265,7 +265,7 @@ def get_transaction_executor(self) -> TransactionExecutorAPI: return self.transaction_executor_class(self) def costless_execute_transaction(self, - transaction: SignedTransactionAPI) -> ComputationAPI: + transaction: SignedTransactionAPI) -> MessageComputationAPI: with self.override_transaction_context(gas_price=transaction.gas_price): free_transaction = transaction.copy(gas_price=0) return self.apply_transaction(free_transaction) @@ -309,7 +309,7 @@ class BaseTransactionExecutor(TransactionExecutorAPI): def __init__(self, vm_state: StateAPI) -> None: self.vm_state = vm_state - def __call__(self, transaction: SignedTransactionAPI) -> ComputationAPI: + def __call__(self, transaction: SignedTransactionAPI) -> MessageComputationAPI: self.validate_transaction(transaction) message = self.build_evm_message(transaction) computation = self.build_computation(message, transaction) diff --git a/tests/core/vm/test_base_computation.py b/tests/core/vm/test_base_computation.py index 5447ba588a..e60f4aeaa0 100644 --- a/tests/core/vm/test_base_computation.py +++ b/tests/core/vm/test_base_computation.py @@ -12,14 +12,14 @@ Message, ) from eth.vm.computation import ( - BaseComputation, + MessageComputation, ) from eth.vm.transaction_context import ( BaseTransactionContext, ) -class DummyComputation(BaseComputation): +class DummyComputation(MessageComputation): @classmethod def apply_message(cls, *args): return cls(*args) @@ -217,7 +217,7 @@ def test_get_log_entries_order_with_children(computation, child_message, canonic parent_log2 = (canonical_address_a, [4, 5, 6], b'2') child_log = (canonical_address_a, [1, 2, 3], b'child') computation.add_log_entry(*parent_log) - child_computation = computation.apply_child_computation(child_message) + child_computation = computation.apply_child_message_computation(child_message) # Pretend the child computation logged something. child_computation.add_log_entry(*child_log) computation.add_log_entry(*parent_log2) diff --git a/tests/core/vm/test_frontier_computation.py b/tests/core/vm/test_frontier_computation.py index 7c7f522ff6..af890472ba 100644 --- a/tests/core/vm/test_frontier_computation.py +++ b/tests/core/vm/test_frontier_computation.py @@ -4,7 +4,7 @@ Message, ) from eth.vm.forks.frontier.computation import ( - FrontierComputation, + FrontierMessageComputation, ) @@ -30,7 +30,7 @@ def message(canonical_address_a, canonical_address_b): @pytest.fixture def computation(message, transaction_context, state): - computation = FrontierComputation( + computation = FrontierMessageComputation( state=state, message=message, transaction_context=transaction_context, @@ -52,7 +52,7 @@ def child_message(computation, canonical_address_b): @pytest.fixture def child_computation(computation, child_message): - child_computation = computation.generate_child_computation(child_message) + child_computation = computation.generate_child_message_computation(child_message) return child_computation diff --git a/tests/json-fixtures/test_virtual_machine.py b/tests/json-fixtures/test_virtual_machine.py index a57699f6b3..8ea42a1caa 100644 --- a/tests/json-fixtures/test_virtual_machine.py +++ b/tests/json-fixtures/test_virtual_machine.py @@ -38,7 +38,7 @@ HomesteadVM, ) from eth.vm.forks.homestead.computation import ( - HomesteadComputation, + HomesteadMessageComputation, ) from eth.vm.forks.homestead.state import HomesteadState from eth.vm.message import ( @@ -116,7 +116,7 @@ def get_block_hash_for_testing(self, block_number): return keccak(to_bytes(text=f"{block_number}")) -HomesteadComputationForTesting = HomesteadComputation.configure( +HomesteadComputationForTesting = HomesteadMessageComputation.configure( __name__='HomesteadComputationForTesting', apply_message=apply_message_for_testing, apply_create_message=apply_create_message_for_testing, @@ -124,7 +124,7 @@ def get_block_hash_for_testing(self, block_number): HomesteadStateForTesting = HomesteadState.configure( __name__='HomesteadStateForTesting', get_ancestor_hash=get_block_hash_for_testing, - computation_class=HomesteadComputationForTesting, + message_computation_class=HomesteadComputationForTesting, ) HomesteadVMForTesting = HomesteadVM.configure( __name__='HomesteadVMForTesting', From 0c4c4da5f164f8be2c7b1f74c9594d22ab3f902a Mon Sep 17 00:00:00 2001 From: fselmo Date: Tue, 18 Apr 2023 16:50:16 -0600 Subject: [PATCH 2/4] Fix docs related to message computation refactoring --- docs/api/api.abc.rst | 9 +++- docs/api/vm/api.vm.computation.rst | 9 +++- docs/api/vm/api.vm.forks.rst | 50 +++++++++---------- docs/guides/creating_opcodes.rst | 4 +- docs/release_notes.rst | 4 +- eth/vm/forks/tangerine_whistle/computation.py | 2 +- eth/vm/forks/tangerine_whistle/state.py | 4 +- 7 files changed, 47 insertions(+), 35 deletions(-) diff --git a/docs/api/api.abc.rst b/docs/api/api.abc.rst index c94bc4db18..9be76d34a3 100644 --- a/docs/api/api.abc.rst +++ b/docs/api/api.abc.rst @@ -157,9 +157,16 @@ ExecutionContextAPI :members: -MessageComputationAPI +ComputationAPI -------------- +.. autoclass:: eth.abc.ComputationAPI + :members: + + +MessageComputationAPI +--------------------- + .. autoclass:: eth.abc.MessageComputationAPI :members: diff --git a/docs/api/vm/api.vm.computation.rst b/docs/api/vm/api.vm.computation.rst index f7f391d46b..7287f13eaf 100644 --- a/docs/api/vm/api.vm.computation.rst +++ b/docs/api/vm/api.vm.computation.rst @@ -1,9 +1,14 @@ Computation =========== -MessageComputation +BaseComputation --------------- -.. autoclass:: eth.vm.computation.MessageComputation +.. autoclass:: eth.vm.computation.BaseComputation :members: +MessageComputation +------------------ + +.. autoclass:: eth.vm.computation.MessageComputation + :members: diff --git a/docs/api/vm/api.vm.forks.rst b/docs/api/vm/api.vm.forks.rst index e15d0cf3b2..80835264be 100644 --- a/docs/api/vm/api.vm.forks.rst +++ b/docs/api/vm/api.vm.forks.rst @@ -17,10 +17,10 @@ FrontierState .. autoclass:: eth.vm.forks.frontier.state.FrontierState :members: -FrontierComputation -~~~~~~~~~~~~~~~~~~~ +FrontierMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.frontier.computation.FrontierComputation +.. autoclass:: eth.vm.forks.frontier.computation.FrontierMessageComputation :members: @@ -39,10 +39,10 @@ HomesteadState .. autoclass:: eth.vm.forks.homestead.state.HomesteadState :members: -HomesteadComputation -~~~~~~~~~~~~~~~~~~~~ +HomesteadMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.homestead.computation.HomesteadComputation +.. autoclass:: eth.vm.forks.homestead.computation.HomesteadMessageComputation :members: @@ -61,10 +61,10 @@ TangerineWhistleState .. autoclass:: eth.vm.forks.tangerine_whistle.state.TangerineWhistleState :members: -TangerineWhistleComputation -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TangerineWhistleMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.tangerine_whistle.computation.TangerineWhistleComputation +.. autoclass:: eth.vm.forks.tangerine_whistle.computation.TangerineWhistleMessageComputation :members: @@ -83,10 +83,10 @@ SpuriousDragonState .. autoclass:: eth.vm.forks.spurious_dragon.state.SpuriousDragonState :members: -SpuriousDragonComputation -~~~~~~~~~~~~~~~~~~~~~~~~~ +SpuriousDragonMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.spurious_dragon.computation.SpuriousDragonComputation +.. autoclass:: eth.vm.forks.spurious_dragon.computation.SpuriousDragonMessageComputation :members: @@ -94,7 +94,7 @@ Byzantium --------- ByzantiumVM -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~ .. autoclass:: eth.vm.forks.byzantium.ByzantiumVM :members: @@ -105,10 +105,10 @@ ByzantiumState .. autoclass:: eth.vm.forks.byzantium.state.ByzantiumState :members: -ByzantiumComputation -~~~~~~~~~~~~~~~~~~~~ +ByzantiumMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.byzantium.computation.ByzantiumComputation +.. autoclass:: eth.vm.forks.byzantium.computation.ByzantiumMessageComputation :members: @@ -127,10 +127,10 @@ ConstantinopleState .. autoclass:: eth.vm.forks.constantinople.state.ConstantinopleState :members: -ConstantinopleComputation -~~~~~~~~~~~~~~~~~~~~~~~~~ +ConstantinopleMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.constantinople.computation.ConstantinopleComputation +.. autoclass:: eth.vm.forks.constantinople.computation.ConstantinopleMessageComputation :members: @@ -149,10 +149,10 @@ PetersburgState .. autoclass:: eth.vm.forks.petersburg.state.PetersburgState :members: -PetersburgComputation -~~~~~~~~~~~~~~~~~~~~~ +PetersburgMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.petersburg.computation.PetersburgComputation +.. autoclass:: eth.vm.forks.petersburg.computation.PetersburgMessageComputation :members: @@ -171,10 +171,10 @@ IstanbulState .. autoclass:: eth.vm.forks.istanbul.state.IstanbulState :members: -IstanbulComputation -~~~~~~~~~~~~~~~~~~~~~ +IstanbulMessageComputation +~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: eth.vm.forks.istanbul.computation.IstanbulComputation +.. autoclass:: eth.vm.forks.istanbul.computation.IstanbulMessageComputation :members: diff --git a/docs/guides/creating_opcodes.rst b/docs/guides/creating_opcodes.rst index 6fc7b67875..f65585c1e6 100644 --- a/docs/guides/creating_opcodes.rst +++ b/docs/guides/creating_opcodes.rst @@ -1,7 +1,7 @@ Creating Opcodes ================ -An opcode is just a function which takes a :class:`~eth.vm.computation.MessageComputation` +An opcode is just a function which takes a :class:`~eth.vm.computation.BaseComputation` instance as it's sole argument. If an opcode function has a return value, this value will be discarded during normal VM execution. @@ -52,7 +52,7 @@ Usage of the :func:`~eth.vm.opcode.as_opcode` helper: def custom_op(computation): ... # opcode logic here - class ExampleComputation(MessageComputation): + class ExampleComputation(BaseComputation): opcodes = { b'\x01': as_opcode(custom_op, 'CUSTOM_OP', 10), } diff --git a/docs/release_notes.rst b/docs/release_notes.rst index b100336ebc..6acc245d6b 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -423,8 +423,8 @@ Internal Changes - for Contributors - Fix for creating a duplicate "ghost" Computation that was never used. It didn't break anything, but was inelegant and surprising to get extra objects created that were mostly useless. This was achieved by changing - :meth:`eth.abc.MessageComputationAPI.apply_message` and - :meth:`eth.abc.MessageComputationAPI.apply_create_message` to be class methods. (`#1921 `__) + :meth:`eth.abc.ComputationAPI.apply_message` and + :meth:`eth.abc.ComputationAPI.apply_create_message` to be class methods. (`#1921 `__) py-evm 0.3.0-alpha.14 (2020-02-10) diff --git a/eth/vm/forks/tangerine_whistle/computation.py b/eth/vm/forks/tangerine_whistle/computation.py index 9612d80dbc..cec0fd2c42 100644 --- a/eth/vm/forks/tangerine_whistle/computation.py +++ b/eth/vm/forks/tangerine_whistle/computation.py @@ -3,7 +3,7 @@ from .opcodes import TANGERINE_WHISTLE_OPCODES -class TangerineWhistleComputation(HomesteadMessageComputation): +class TangerineWhistleMessageComputation(HomesteadMessageComputation): """ A class for all execution *message* computations in the ``TangerineWhistle`` fork. Inherits from diff --git a/eth/vm/forks/tangerine_whistle/state.py b/eth/vm/forks/tangerine_whistle/state.py index b815630563..ca61cab5c7 100644 --- a/eth/vm/forks/tangerine_whistle/state.py +++ b/eth/vm/forks/tangerine_whistle/state.py @@ -1,7 +1,7 @@ from eth.vm.forks.homestead.state import HomesteadState -from .computation import TangerineWhistleComputation +from .computation import TangerineWhistleMessageComputation class TangerineWhistleState(HomesteadState): - message_computation_class = TangerineWhistleComputation + message_computation_class = TangerineWhistleMessageComputation From c7bc4cf5410e8a4b80dc7b7a0a60c4a021828fc3 Mon Sep 17 00:00:00 2001 From: fselmo Date: Wed, 19 Apr 2023 12:13:57 -0600 Subject: [PATCH 3/4] Refactor computation children to BaseComputation class - Define computation children in the ``BaseComputation`` class and narrow the type as needed for subclasses - e.g. for ``MessageComputation`` narrow the children to be ``MessageComputationAPI``. - Some minor cleanup along the way. - Add newsfragment --- eth/abc.py | 20 ++++++++++---------- eth/vm/computation/base_computation.py | 12 ++++++++++-- eth/vm/computation/message_computation.py | 11 +++++++---- newsfragments/2097.breaking.rst | 1 + 4 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 newsfragments/2097.breaking.rst diff --git a/eth/abc.py b/eth/abc.py index b688133697..cca3901c3d 100644 --- a/eth/abc.py +++ b/eth/abc.py @@ -1862,7 +1862,7 @@ def base_fee_per_gas(self) -> Optional[int]: class ComputationAPI( - ContextManager['ComputationAPI'], + ContextManager["ComputationAPI"], StackManipulationAPI, ): """ @@ -2111,12 +2111,11 @@ class MessageComputationAPI( msg: MessageAPI transaction_context: TransactionContextAPI - children: List["MessageComputationAPI"] @abstractmethod def __init__( self, - state: 'StateAPI', + state: "StateAPI", message: MessageAPI, transaction_context: TransactionContextAPI, ) -> None: @@ -2154,7 +2153,7 @@ def prepare_child_message(self, def apply_child_message_computation( self, child_msg: MessageAPI, - ) -> 'MessageComputationAPI': + ) -> "MessageComputationAPI": """ Apply the vm message ``child_msg`` as a child message computation. """ @@ -2164,7 +2163,7 @@ def apply_child_message_computation( def generate_child_message_computation( self, child_msg: MessageAPI, - ) -> 'MessageComputationAPI': + ) -> "MessageComputationAPI": """ Generate a child message computation from the given ``child_msg``. """ @@ -2173,7 +2172,7 @@ def generate_child_message_computation( @abstractmethod def add_child_message_computation( self, - child_message_computation: 'MessageComputationAPI', + child_message_computation: "MessageComputationAPI", ) -> None: """ Add the given ``child_computation``. @@ -2232,10 +2231,11 @@ def get_log_entries(self) -> Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]: @classmethod @abstractmethod def apply_message( - cls, - state: 'StateAPI', - message: MessageAPI, - transaction_context: TransactionContextAPI) -> 'MessageComputationAPI': + cls, + state: "StateAPI", + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> "MessageComputationAPI": """ Execute a VM message. This is where the VM-specific call logic exists. """ diff --git a/eth/vm/computation/base_computation.py b/eth/vm/computation/base_computation.py index 0eaf17b9f1..f7ba78f128 100644 --- a/eth/vm/computation/base_computation.py +++ b/eth/vm/computation/base_computation.py @@ -3,9 +3,12 @@ Any, Callable, Dict, + Generic, + List, Optional, Tuple, Type, + TypeVar, Union, ) @@ -73,7 +76,10 @@ def memory_gas_cost(size_in_bytes: int) -> int: return total_cost -class BaseComputation(Configurable, ComputationAPI): +C = TypeVar("C", bound="ComputationAPI") + + +class BaseComputation(ComputationAPI, Configurable, Generic[C]): """ The base class for all execution computations. @@ -92,8 +98,8 @@ class BaseComputation(Configurable, ComputationAPI): state: StateAPI = None code: CodeStreamAPI = None + children: List[C] = None return_data: bytes = b'' - accounts_to_delete: Dict[Address, Address] = None _memory: MemoryAPI = None _stack: StackAPI = None @@ -107,6 +113,8 @@ class BaseComputation(Configurable, ComputationAPI): def __init__(self, state: StateAPI) -> None: self.state = state + self.children = [] + self._memory = Memory() self._stack = Stack() diff --git a/eth/vm/computation/message_computation.py b/eth/vm/computation/message_computation.py index 08235b188e..7fdebf33c5 100644 --- a/eth/vm/computation/message_computation.py +++ b/eth/vm/computation/message_computation.py @@ -12,7 +12,8 @@ ) from eth.vm.computation.base_computation import ( - BaseComputation, NO_RESULT, + BaseComputation, + NO_RESULT, ) from eth_typing import ( Address, @@ -54,7 +55,10 @@ ) -class MessageComputation(BaseComputation, MessageComputationAPI): +class MessageComputation( + MessageComputationAPI, + BaseComputation[MessageComputationAPI], +): """ A class for executing message computations. """ @@ -74,14 +78,13 @@ def __init__( message: MessageAPI, transaction_context: TransactionContextAPI, ) -> None: - super().__init__(state) + BaseComputation.__init__(self, state) self.msg = message self.transaction_context = transaction_context self.code = CodeStream(message.code) self._gas_meter = self._configure_gas_meter() - self.children = [] self.accounts_to_delete = {} self._log_entries = [] diff --git a/newsfragments/2097.breaking.rst b/newsfragments/2097.breaking.rst new file mode 100644 index 0000000000..fbf9701d45 --- /dev/null +++ b/newsfragments/2097.breaking.rst @@ -0,0 +1 @@ +Refactor ``BaseComputation`` as a simpler base class and subclass ``MessageComputation`` building on it, allowing flexibility for other types of computations such as EOF computations. From 0c301935dede155e1930172c8c8dcf08b4a1bcf6 Mon Sep 17 00:00:00 2001 From: fselmo Date: Thu, 20 Apr 2023 12:19:17 -0600 Subject: [PATCH 4/4] Changes from comments on pr #2097 --- eth/vm/forks/shanghai/computation.py | 9 +++++++-- eth/vm/forks/shanghai/state.py | 4 ++-- tests/core/opcodes/test_opcodes.py | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/eth/vm/forks/shanghai/computation.py b/eth/vm/forks/shanghai/computation.py index 4f049cde8a..afd00e9183 100644 --- a/eth/vm/forks/shanghai/computation.py +++ b/eth/vm/forks/shanghai/computation.py @@ -1,5 +1,10 @@ from eth._utils.numeric import ceil32 -from eth.abc import MessageComputationAPI, MessageAPI, StateAPI, TransactionContextAPI +from eth.abc import ( + MessageComputationAPI, + MessageAPI, + StateAPI, + TransactionContextAPI, +) from .constants import ( INITCODE_WORD_COST, MAX_INITCODE_SIZE, @@ -11,7 +16,7 @@ from eth.vm.forks.paris.computation import ParisMessageComputation -class ShanghaiComputation(ParisMessageComputation): +class ShanghaiMessageComputation(ParisMessageComputation): """ A class for all execution *message* computations in the ``Shanghai`` hard fork """ diff --git a/eth/vm/forks/shanghai/state.py b/eth/vm/forks/shanghai/state.py index b67c5aa7de..5dbd97eda2 100644 --- a/eth/vm/forks/shanghai/state.py +++ b/eth/vm/forks/shanghai/state.py @@ -4,7 +4,7 @@ TransactionExecutorAPI, WithdrawalAPI, ) -from .computation import ShanghaiComputation +from .computation import ShanghaiMessageComputation from ..paris import ParisState from ..paris.state import ParisTransactionExecutor @@ -14,7 +14,7 @@ class ShanghaiTransactionExecutor(ParisTransactionExecutor): class ShanghaiState(ParisState): - message_computation_class = ShanghaiComputation + message_computation_class = ShanghaiMessageComputation transaction_executor_class: Type[TransactionExecutorAPI] = ShanghaiTransactionExecutor # noqa: E501 def apply_withdrawal(self, withdrawal: WithdrawalAPI) -> None: diff --git a/tests/core/opcodes/test_opcodes.py b/tests/core/opcodes/test_opcodes.py index 260575eecc..6b7941ecdf 100644 --- a/tests/core/opcodes/test_opcodes.py +++ b/tests/core/opcodes/test_opcodes.py @@ -7,7 +7,7 @@ POS_MAINNET_VMS, ) from eth.vm.forks.shanghai.computation import ( - ShanghaiComputation, + ShanghaiMessageComputation, ) from eth_utils import ( decode_hex, @@ -1599,7 +1599,7 @@ def test_selfdestruct_does_not_issue_deprecation_warning_pre_shanghai(vm_class): def test_selfdestruct_issues_deprecation_warning_for_shanghai(): - available_vm_opcodes = ShanghaiComputation.opcodes + available_vm_opcodes = ShanghaiMessageComputation.opcodes vm_opcodes_without_selfdestruct = { k: available_vm_opcodes[k] for k in available_vm_opcodes.keys()