From a1ef2b77ff0983a1fedde49f89b2da419dc7b107 Mon Sep 17 00:00:00 2001 From: fselmo Date: Fri, 28 Apr 2023 10:31:24 -0600 Subject: [PATCH] Re-refactor pre-EOF: Put the state of the Computation class hierarchy mostly back to where it was, keeping some good cleanup and updating the ComputationAPI to match properties implemented in the base computation implementation class. Some of the earlier `ethereum/tests` pointed to EOF code within the bytecode, not at the start. This led to some wrong class architecting to make it work with our current model, with the bigger refactor commit from last week. It would be best to put the state of the Computation hierarchy mostly back to how it was so as to not break too much of the existing structure. Massaging EOF into this model won't be too difficult with the (proper) way the EOF tests are written now. Since there won't be a release until all these changes are in, the most this does is muddy the commit history a bit. But at least we gained a touch of housekeeping along the way. --- docs/api/api.abc.rst | 7 - eth/abc.py | 143 ++-- eth/chains/base.py | 8 +- eth/precompiles/blake2.py | 6 +- eth/precompiles/ecadd.py | 11 +- eth/precompiles/ecmul.py | 11 +- eth/precompiles/ecpairing.py | 8 +- eth/precompiles/ecrecover.py | 8 +- eth/precompiles/identity.py | 6 +- eth/precompiles/modexp.py | 9 +- eth/precompiles/ripemd160.py | 6 +- eth/precompiles/sha256.py | 6 +- eth/rlp/transactions.py | 4 +- eth/vm/base.py | 10 +- eth/vm/computation.py | 634 ++++++++++++++++++ eth/vm/computation/__init__.py | 4 - eth/vm/computation/base_computation.py | 354 ---------- eth/vm/computation/message_computation.py | 360 ---------- eth/vm/forks/arrow_glacier/computation.py | 6 +- eth/vm/forks/arrow_glacier/state.py | 4 +- eth/vm/forks/berlin/computation.py | 6 +- eth/vm/forks/berlin/logic.py | 28 +- eth/vm/forks/berlin/state.py | 8 +- eth/vm/forks/byzantium/__init__.py | 4 +- eth/vm/forks/byzantium/computation.py | 6 +- eth/vm/forks/byzantium/opcodes.py | 5 +- eth/vm/forks/byzantium/state.py | 4 +- eth/vm/forks/constantinople/computation.py | 6 +- eth/vm/forks/constantinople/state.py | 4 +- eth/vm/forks/frontier/__init__.py | 8 +- eth/vm/forks/frontier/computation.py | 12 +- eth/vm/forks/frontier/state.py | 20 +- eth/vm/forks/gray_glacier/computation.py | 6 +- eth/vm/forks/gray_glacier/state.py | 4 +- eth/vm/forks/homestead/computation.py | 10 +- eth/vm/forks/homestead/state.py | 6 +- eth/vm/forks/istanbul/computation.py | 6 +- eth/vm/forks/istanbul/state.py | 4 +- eth/vm/forks/istanbul/storage.py | 4 +- eth/vm/forks/london/computation.py | 6 +- eth/vm/forks/london/state.py | 8 +- eth/vm/forks/muir_glacier/computation.py | 6 +- eth/vm/forks/muir_glacier/state.py | 4 +- eth/vm/forks/paris/computation.py | 6 +- eth/vm/forks/paris/state.py | 4 +- eth/vm/forks/petersburg/computation.py | 6 +- eth/vm/forks/petersburg/state.py | 4 +- eth/vm/forks/shanghai/computation.py | 8 +- eth/vm/forks/shanghai/state.py | 4 +- eth/vm/forks/spurious_dragon/_utils.py | 8 +- eth/vm/forks/spurious_dragon/computation.py | 10 +- eth/vm/forks/spurious_dragon/state.py | 8 +- eth/vm/forks/tangerine_whistle/computation.py | 6 +- eth/vm/forks/tangerine_whistle/state.py | 4 +- eth/vm/logic/arithmetic.py | 31 +- eth/vm/logic/block.py | 22 +- eth/vm/logic/call.py | 42 +- eth/vm/logic/comparison.py | 25 +- 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 | 5 +- eth/vm/logic/memory.py | 10 +- eth/vm/logic/sha3.py | 4 +- eth/vm/logic/stack.py | 6 +- eth/vm/logic/storage.py | 9 +- eth/vm/logic/swap.py | 4 +- eth/vm/logic/system.py | 36 +- eth/vm/opcode.py | 4 +- eth/vm/state.py | 18 +- newsfragments/2097.breaking.rst | 1 - newsfragments/2106.internal.rst | 1 + tests/core/opcodes/test_opcodes.py | 4 +- tests/core/vm/test_base_computation.py | 6 +- .../core/vm/test_contract_code_size_limit.py | 4 +- tests/core/vm/test_frontier_computation.py | 6 +- tests/json-fixtures/test_virtual_machine.py | 6 +- 78 files changed, 1028 insertions(+), 1132 deletions(-) create mode 100644 eth/vm/computation.py delete mode 100644 eth/vm/computation/__init__.py delete mode 100644 eth/vm/computation/base_computation.py delete mode 100644 eth/vm/computation/message_computation.py delete mode 100644 newsfragments/2097.breaking.rst create mode 100644 newsfragments/2106.internal.rst diff --git a/docs/api/api.abc.rst b/docs/api/api.abc.rst index 9be76d34a3..0d878e2ca3 100644 --- a/docs/api/api.abc.rst +++ b/docs/api/api.abc.rst @@ -164,13 +164,6 @@ ComputationAPI :members: -MessageComputationAPI ---------------------- - -.. autoclass:: eth.abc.MessageComputationAPI - :members: - - AccountStorageDatabaseAPI ------------------------- diff --git a/eth/abc.py b/eth/abc.py index cca3901c3d..7f1f2845a7 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: 'MessageComputationAPI') -> int: + def gas_used_by(self, computation: 'ComputationAPI') -> int: """ Return the gas used by the given computation. In Frontier, for example, this is sum of the intrinsic cost and the gas used @@ -1440,7 +1440,7 @@ class OpcodeAPI(ABC): mnemonic: str @abstractmethod - def __call__(self, computation: 'MessageComputationAPI') -> None: + def __call__(self, computation: 'ComputationAPI') -> None: """ Execute the logic of the opcode. """ @@ -1449,7 +1449,7 @@ def __call__(self, computation: 'MessageComputationAPI') -> None: @classmethod @abstractmethod def as_opcode(cls: Type[T], - logic_fn: Callable[['MessageComputationAPI'], None], + logic_fn: Callable[['ComputationAPI'], None], mnemonic: str, gas_cost: int) -> T: """ @@ -1872,15 +1872,34 @@ class ComputationAPI( logger: ExtendedDebugLogger state: "StateAPI" + msg: MessageAPI + transaction_context: TransactionContextAPI code: CodeStreamAPI - return_data: bytes + children: List["ComputationAPI"] + return_data: bytes = b'' + accounts_to_delete: Dict[Address, Address] + + _memory: MemoryAPI + _stack: StackAPI + _gas_meter: GasMeterAPI + _error: VMError + _output: bytes = b'' + _log_entries: List[Tuple[int, Address, Tuple[int, ...], bytes]] # VM configuration opcodes: Dict[int, OpcodeAPI] _precompiles: Dict[Address, Callable[["ComputationAPI"], "ComputationAPI"]] @abstractmethod - def __init__(self, state: "StateAPI") -> None: + def __init__( + self, + state: "StateAPI", + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> None: + """ + Instantiate the computation. + """ ... @abstractmethod @@ -1890,6 +1909,16 @@ def _configure_gas_meter(self) -> GasMeterAPI: """ ... + # -- convenience -- # + @property + @abstractmethod + def is_origin_computation(self) -> bool: + """ + Return ``True`` if this computation is the outermost computation at + ``depth == 0``. + """ + ... + # -- error handling -- # @property @abstractmethod @@ -2100,41 +2129,6 @@ def get_opcode_fn(self, opcode: int) -> OpcodeAPI: """ ... - -class MessageComputationAPI( - ComputationAPI, - ContextManager['MessageComputationAPI'], -): - """ - The base abstract class for all execution *message* computations. - """ - - msg: MessageAPI - transaction_context: TransactionContextAPI - - @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, @@ -2145,34 +2139,34 @@ def prepare_child_message(self, code: bytes, **kwargs: Any) -> MessageAPI: """ - Helper method for creating a child message computation. + Helper method for creating a child computation. """ ... @abstractmethod - def apply_child_message_computation( + def apply_child_computation( self, child_msg: MessageAPI, - ) -> "MessageComputationAPI": + ) -> "ComputationAPI": """ - Apply the vm message ``child_msg`` as a child message computation. + Apply the vm message ``child_msg`` as a child computation. """ ... @abstractmethod - def generate_child_message_computation( + def generate_child_computation( self, child_msg: MessageAPI, - ) -> "MessageComputationAPI": + ) -> "ComputationAPI": """ - Generate a child message computation from the given ``child_msg``. + Generate a child computation from the given ``child_msg``. """ ... @abstractmethod - def add_child_message_computation( + def add_child_computation( self, - child_message_computation: "MessageComputationAPI", + child_computation: "ComputationAPI", ) -> None: """ Add the given ``child_computation``. @@ -2235,7 +2229,7 @@ def apply_message( state: "StateAPI", message: MessageAPI, transaction_context: TransactionContextAPI, - ) -> "MessageComputationAPI": + ) -> "ComputationAPI": """ Execute a VM message. This is where the VM-specific call logic exists. """ @@ -2244,10 +2238,11 @@ def apply_message( @classmethod @abstractmethod def apply_create_message( - cls, - state: 'StateAPI', - message: MessageAPI, - transaction_context: TransactionContextAPI) -> 'MessageComputationAPI': + cls, + state: "StateAPI", + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> "ComputationAPI": """ Execute a VM message to create a new contract. This is where the VM-specific create logic exists. @@ -2261,7 +2256,7 @@ def apply_computation( state: 'StateAPI', message: MessageAPI, transaction_context: TransactionContextAPI, - ) -> 'MessageComputationAPI': + ) -> "ComputationAPI": """ Execute the logic within the message: Either run the precompile, or step through each opcode. Generally, the only VM-specific logic is for @@ -2669,9 +2664,9 @@ def __init__(self, vm_state: 'StateAPI') -> None: ... @abstractmethod - def __call__(self, transaction: SignedTransactionAPI) -> 'MessageComputationAPI': + def __call__(self, transaction: SignedTransactionAPI) -> "ComputationAPI": """ - Execute the ``transaction`` and return a :class:`eth.abc.MessageComputationAPI`. + Execute the ``transaction`` and return a :class:`eth.abc.ComputationAPI`. """ ... @@ -2693,7 +2688,7 @@ def build_evm_message(self, transaction: SignedTransactionAPI) -> MessageAPI: @abstractmethod def build_computation(self, message: MessageAPI, - transaction: SignedTransactionAPI) -> 'MessageComputationAPI': + transaction: SignedTransactionAPI) -> "ComputationAPI": """ Apply the ``message`` to the VM and use the given ``transaction`` to retrieve the context from. @@ -2704,7 +2699,7 @@ def build_computation(self, @abstractmethod def finalize_computation(self, transaction: SignedTransactionAPI, - computation: 'MessageComputationAPI') -> 'MessageComputationAPI': + computation: "ComputationAPI") -> "ComputationAPI": """ Finalize the ``transaction``. """ @@ -2734,7 +2729,7 @@ class StateAPI(ConfigurableAPI): Each :class:`~eth.abc.StateAPI` class must be configured with: - - ``message_computation_class``: The :class:`~eth.abc.MessageComputationAPI` class for + - ``computation_class``: The :class:`~eth.abc.ComputationAPI` class for vm execution. - ``transaction_context_class``: The :class:`~eth.abc.TransactionContextAPI` class for vm execution. @@ -2744,7 +2739,7 @@ class for vm execution. # execution_context: ExecutionContextAPI - message_computation_class: Type[MessageComputationAPI] + computation_class: Type[ComputationAPI] transaction_context_class: Type[TransactionContextAPI] account_db_class: Type[AccountDatabaseAPI] transaction_executor_class: Type[TransactionExecutorAPI] = None @@ -3103,7 +3098,7 @@ def get_ancestor_hash(self, block_number: BlockNumber) -> Hash32: @abstractmethod def get_computation(self, message: MessageAPI, - transaction_context: TransactionContextAPI) -> MessageComputationAPI: + transaction_context: TransactionContextAPI) -> ComputationAPI: """ Return a computation instance for the given `message` and `transaction_context` """ @@ -3128,7 +3123,7 @@ def get_transaction_context_class(cls) -> Type[TransactionContextAPI]: def apply_transaction( self, transaction: SignedTransactionAPI, - ) -> MessageComputationAPI: + ) -> ComputationAPI: """ Apply transaction to the vm state @@ -3148,7 +3143,7 @@ def get_transaction_executor(self) -> TransactionExecutorAPI: def costless_execute_transaction( self, transaction: SignedTransactionAPI, - ) -> MessageComputationAPI: + ) -> ComputationAPI: """ Execute the given ``transaction`` with a gas price of ``0``. """ @@ -3318,7 +3313,7 @@ def transaction_applied_hook( transactions: Sequence[SignedTransactionAPI], base_header: BlockHeaderAPI, partial_header: BlockHeaderAPI, - computation: MessageComputationAPI, + computation: ComputationAPI, receipt: ReceiptAPI) -> None: """ A hook for a subclass to use as a way to note that a transaction was applied. @@ -3334,7 +3329,7 @@ def transaction_applied_hook( def apply_transaction(self, header: BlockHeaderAPI, transaction: SignedTransactionAPI - ) -> Tuple[ReceiptAPI, MessageComputationAPI]: + ) -> Tuple[ReceiptAPI, ComputationAPI]: """ Apply the transaction to the current block. This is a wrapper around :func:`~eth.vm.state.State.apply_transaction` with some extra orchestration logic. @@ -3365,7 +3360,7 @@ def execute_bytecode(self, value: int, data: bytes, code: bytes, - code_address: Address = None) -> MessageComputationAPI: + code_address: Address = None) -> ComputationAPI: """ Execute raw bytecode in the context of the current state of the virtual machine. Note that this skips over some of the logic @@ -3377,8 +3372,8 @@ def execute_bytecode(self, - others... For other potential surprises, check the implementation differences - between :meth:`MessageComputationAPI.apply_computation` and - :meth:`MessageComputationAPI.apply_message`. (depending on the VM fork) + between :meth:`ComputationAPI.apply_computation` and + :meth:`ComputationAPI.apply_message`. (depending on the VM fork) """ ... @@ -3387,7 +3382,7 @@ def apply_all_transactions( self, transactions: Sequence[SignedTransactionAPI], base_header: BlockHeaderAPI - ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: + ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: """ Determine the results of applying all transactions to the base header. This does *not* update the current block or header of the VM. @@ -3411,7 +3406,7 @@ def apply_all_withdrawals(self, withdrawals: Sequence[WithdrawalAPI]) -> None: def make_receipt(self, base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, - computation: MessageComputationAPI, + computation: ComputationAPI, state: StateAPI) -> ReceiptAPI: """ Generate the receipt resulting from applying the transaction. @@ -4045,7 +4040,7 @@ def build_block_with_transactions_and_withdrawals( transactions: Tuple[SignedTransactionAPI, ...], parent_header: BlockHeaderAPI = None, withdrawals: Tuple[WithdrawalAPI, ...] = None, - ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: + ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: """ 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, @@ -4271,7 +4266,7 @@ def mine_all( *args: Any, parent_header: BlockHeaderAPI = None, **kwargs: Any, - ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[MessageComputationAPI, ...]]: + ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: """ Build a block with the given transactions, and mine it. @@ -4285,7 +4280,7 @@ def mine_all( @abstractmethod def apply_transaction(self, transaction: SignedTransactionAPI - ) -> Tuple[BlockAPI, ReceiptAPI, MessageComputationAPI]: + ) -> Tuple[BlockAPI, ReceiptAPI, ComputationAPI]: """ Apply the transaction to the current tip block. diff --git a/eth/chains/base.py b/eth/chains/base.py index 67e9e88d14..04673793b2 100644 --- a/eth/chains/base.py +++ b/eth/chains/base.py @@ -47,7 +47,7 @@ ConsensusContextAPI, VirtualMachineAPI, ReceiptAPI, - MessageComputationAPI, + ComputationAPI, 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[MessageComputationAPI, ...]]: + ) -> Tuple[BlockAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: 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, MessageComputationAPI]: + ) -> Tuple[BlockAPI, ReceiptAPI, ComputationAPI]: 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[MessageComputationAPI, ...]]: + ) -> Tuple[BlockImportResult, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: if parent_header is None: base_header = self.header diff --git a/eth/precompiles/blake2.py b/eth/precompiles/blake2.py index ac6a16cd7e..b104e6d727 100644 --- a/eth/precompiles/blake2.py +++ b/eth/precompiles/blake2.py @@ -6,8 +6,8 @@ from eth.exceptions import ( VMError, ) -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) try: @@ -18,7 +18,7 @@ GAS_COST_PER_ROUND = 1 -def blake2b_fcompress(computation: MessageComputation) -> MessageComputation: +def blake2b_fcompress(computation: ComputationAPI) -> ComputationAPI: 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 eecf18813c..5a064f3cbc 100644 --- a/eth/precompiles/ecadd.py +++ b/eth/precompiles/ecadd.py @@ -15,6 +15,9 @@ from eth import constants +from eth.abc import ( + ComputationAPI, +) from eth.exceptions import ( VMError, ) @@ -26,15 +29,11 @@ pad32r, ) -from eth.vm.computation import ( - MessageComputation, -) - @curry def ecadd( - computation: MessageComputation, - gas_cost: int = constants.GAS_ECADD) -> MessageComputation: + computation: ComputationAPI, + gas_cost: int = constants.GAS_ECADD) -> ComputationAPI: computation.consume_gas(gas_cost, reason='ECADD Precompile') diff --git a/eth/precompiles/ecmul.py b/eth/precompiles/ecmul.py index 506815dfec..456d0b0645 100644 --- a/eth/precompiles/ecmul.py +++ b/eth/precompiles/ecmul.py @@ -15,6 +15,9 @@ from eth import constants +from eth.abc import ( + ComputationAPI, +) from eth.exceptions import ( VMError, ) @@ -26,15 +29,11 @@ pad32r, ) -from eth.vm.computation import ( - MessageComputation, -) - @curry def ecmul( - computation: MessageComputation, - gas_cost: int = constants.GAS_ECMUL) -> MessageComputation: + computation: ComputationAPI, + gas_cost: int = constants.GAS_ECMUL) -> ComputationAPI: computation.consume_gas(gas_cost, reason='ECMUL Precompile') diff --git a/eth/precompiles/ecpairing.py b/eth/precompiles/ecpairing.py index 971c86694d..505ea25547 100644 --- a/eth/precompiles/ecpairing.py +++ b/eth/precompiles/ecpairing.py @@ -30,8 +30,8 @@ pad32, ) -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) @@ -41,9 +41,9 @@ @curry def ecpairing( - computation: MessageComputation, + computation: ComputationAPI, gas_cost_base: int = constants.GAS_ECPAIRING_BASE, - gas_cost_per_point: int = constants.GAS_ECPAIRING_PER_POINT) -> MessageComputation: + gas_cost_per_point: int = constants.GAS_ECPAIRING_PER_POINT) -> ComputationAPI: 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 2a1b0e83bd..84fc658f7a 100644 --- a/eth/precompiles/ecrecover.py +++ b/eth/precompiles/ecrecover.py @@ -15,17 +15,17 @@ pad32r, ) +from eth.abc import ( + ComputationAPI, +) from eth.validation import ( validate_lt_secpk1n, validate_gte, validate_lte, ) -from eth.vm.computation import ( - MessageComputation, -) -def ecrecover(computation: MessageComputation) -> MessageComputation: +def ecrecover(computation: ComputationAPI) -> ComputationAPI: 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 22b76cbe05..96df0efa48 100644 --- a/eth/precompiles/identity.py +++ b/eth/precompiles/identity.py @@ -3,12 +3,12 @@ ceil32, ) -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) -def identity(computation: MessageComputation) -> MessageComputation: +def identity(computation: ComputationAPI) -> ComputationAPI: 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 9cad85a7ff..0666381d37 100644 --- a/eth/precompiles/modexp.py +++ b/eth/precompiles/modexp.py @@ -21,9 +21,8 @@ zpad_right, zpad_left, ) - -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) @@ -127,9 +126,9 @@ def _modexp(data: bytes) -> int: @curry def modexp( - computation: MessageComputation, + computation: ComputationAPI, gas_calculator: Callable[[bytes], int] = _compute_modexp_gas_fee_eip_198 -) -> MessageComputation: +) -> ComputationAPI: """ https://github.com/ethereum/EIPs/pull/198 """ diff --git a/eth/precompiles/ripemd160.py b/eth/precompiles/ripemd160.py index 573c0d550a..1cdcbb32fc 100644 --- a/eth/precompiles/ripemd160.py +++ b/eth/precompiles/ripemd160.py @@ -8,12 +8,12 @@ from eth._utils.padding import ( pad32, ) -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) -def ripemd160(computation: MessageComputation) -> MessageComputation: +def ripemd160(computation: ComputationAPI) -> ComputationAPI: 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 73960e9440..426ffa7be0 100644 --- a/eth/precompiles/sha256.py +++ b/eth/precompiles/sha256.py @@ -6,12 +6,12 @@ ceil32, ) -from eth.vm.computation import ( - MessageComputation, +from eth.abc import ( + ComputationAPI, ) -def sha256(computation: MessageComputation) -> MessageComputation: +def sha256(computation: ComputationAPI) -> ComputationAPI: 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 14f6780a19..7781a7813a 100644 --- a/eth/rlp/transactions.py +++ b/eth/rlp/transactions.py @@ -24,7 +24,7 @@ from eth.abc import ( BaseTransactionAPI, - MessageComputationAPI, + ComputationAPI, 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: MessageComputationAPI) -> int: + def gas_used_by(self, computation: ComputationAPI) -> int: return self.get_intrinsic_gas() + computation.get_gas_used() @property diff --git a/eth/vm/base.py b/eth/vm/base.py index cc7dfc5f1a..d2fde2cfc6 100644 --- a/eth/vm/base.py +++ b/eth/vm/base.py @@ -35,7 +35,7 @@ BlockHeaderAPI, ChainContextAPI, ChainDatabaseAPI, - MessageComputationAPI, + ComputationAPI, ConsensusAPI, ConsensusContextAPI, ExecutionContextAPI, @@ -166,7 +166,7 @@ def logger(self) -> logging.Logger: def apply_transaction(self, header: BlockHeaderAPI, transaction: SignedTransactionAPI - ) -> Tuple[ReceiptAPI, MessageComputationAPI]: + ) -> Tuple[ReceiptAPI, ComputationAPI]: 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, - ) -> MessageComputationAPI: + ) -> ComputationAPI: if origin is None: origin = sender @@ -244,7 +244,7 @@ def execute_bytecode(self, ) # Execute it in the VM - return self.state.message_computation_class.apply_computation( + return self.state.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[MessageComputationAPI, ...]]: + ) -> Tuple[BlockHeaderAPI, Tuple[ReceiptAPI, ...], Tuple[ComputationAPI, ...]]: 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 new file mode 100644 index 0000000000..398d3aff68 --- /dev/null +++ b/eth/vm/computation.py @@ -0,0 +1,634 @@ +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 BytesOrView +from eth.vm.code_stream import CodeStream +from eth.vm.gas_meter import GasMeter +from eth.vm.message import Message +from eth_typing import ( + Address, +) +from eth_utils import ( + encode_hex, + get_extended_debug_logger, +) + +from eth._utils.datatypes import ( + Configurable, +) +from eth._utils.numeric import ( + ceil32, +) +from eth.abc import ( + CodeStreamAPI, + ComputationAPI, + MemoryAPI, + MessageAPI, + GasMeterAPI, + OpcodeAPI, + StackAPI, + StateAPI, + TransactionContextAPI, +) +from eth.constants import ( + GAS_MEMORY, + GAS_MEMORY_QUADRATIC_DENOMINATOR, +) +from eth.exceptions import ( + Halt, + VMError, +) +from eth.validation import ( + validate_canonical_address, + 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(ComputationAPI, Configurable): + """ + 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 + msg: MessageAPI = None + transaction_context: TransactionContextAPI = None + code: CodeStreamAPI = None + children: List[ComputationAPI] = 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'' + _log_entries: List[Tuple[int, Address, Tuple[int, ...], bytes]] = None + + # VM configuration + opcodes: Dict[int, OpcodeAPI] = None + _precompiles: Dict[Address, Callable[[ComputationAPI], ComputationAPI]] = None + + def __init__( + self, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> None: + self.state = 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._stack = Stack() + self._memory = Memory() + 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, + ) -> ComputationAPI: + raise NotImplementedError("Must be implemented by subclasses") + + @classmethod + def apply_create_message( + cls, + state: StateAPI, + message: MessageAPI, + transaction_context: TransactionContextAPI, + ) -> ComputationAPI: + 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_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) + + # -- 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, + ) -> 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 + + # -- 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: 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 + + # -- 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( + ( + "MESSAGE COMPUTATION STARTING: " + "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( + ( + "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 diff --git a/eth/vm/computation/__init__.py b/eth/vm/computation/__init__.py deleted file mode 100644 index b42a0f5efa..0000000000 --- a/eth/vm/computation/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .message_computation import ( # noqa: F401 - BaseComputation, - MessageComputation, -) diff --git a/eth/vm/computation/base_computation.py b/eth/vm/computation/base_computation.py deleted file mode 100644 index f7ba78f128..0000000000 --- a/eth/vm/computation/base_computation.py +++ /dev/null @@ -1,354 +0,0 @@ -from types import TracebackType -from typing import ( - Any, - Callable, - Dict, - Generic, - List, - Optional, - Tuple, - Type, - TypeVar, - 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 - - -C = TypeVar("C", bound="ComputationAPI") - - -class BaseComputation(ComputationAPI, Configurable, Generic[C]): - """ - 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 - children: List[C] = None - return_data: bytes = b'' - - _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.children = [] - - 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 deleted file mode 100644 index 7fdebf33c5..0000000000 --- a/eth/vm/computation/message_computation.py +++ /dev/null @@ -1,360 +0,0 @@ -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( - MessageComputationAPI, - 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: - 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.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 b61816c076..41d92eca89 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 LondonMessageComputation +from ..london.computation import LondonComputation -class ArrowGlacierMessageComputation(LondonMessageComputation): +class ArrowGlacierComputation(LondonComputation): """ A class for all execution *message* computations in the ``ArrowGlacier`` fork. - Inherits from :class:`~eth.vm.forks.london.LondonMessageComputation` + Inherits from :class:`~eth.vm.forks.london.LondonComputation` """ pass diff --git a/eth/vm/forks/arrow_glacier/state.py b/eth/vm/forks/arrow_glacier/state.py index ccc820a370..76882c3ff8 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 ArrowGlacierMessageComputation +from .computation import ArrowGlacierComputation from ..london import LondonState from ..london.state import LondonTransactionExecutor @@ -11,5 +11,5 @@ class ArrowGlacierTransactionExecutor(LondonTransactionExecutor): class ArrowGlacierState(LondonState): - message_computation_class = ArrowGlacierMessageComputation + computation_class = ArrowGlacierComputation transaction_executor_class: Type[TransactionExecutorAPI] = ArrowGlacierTransactionExecutor diff --git a/eth/vm/forks/berlin/computation.py b/eth/vm/forks/berlin/computation.py index 6046cf6146..ff41a0c05e 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 ( - MuirGlacierMessageComputation, + MuirGlacierComputation, ) from .opcodes import BERLIN_OPCODES @@ -79,10 +79,10 @@ def _compute_modexp_gas_fee_eip_2565(data: bytes) -> int: ) -class BerlinMessageComputation(MuirGlacierMessageComputation): +class BerlinComputation(MuirGlacierComputation): """ A class for all execution *message* computations in the ``Berlin`` fork. - Inherits from :class:`~eth.vm.forks.muir_glacier.MuirGlacierMessageComputation` + Inherits from :class:`~eth.vm.forks.muir_glacier.MuirGlacierComputation` """ # Override opcodes = BERLIN_OPCODES diff --git a/eth/vm/forks/berlin/logic.py b/eth/vm/forks/berlin/logic.py index b0bbf30939..07a6864e0b 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 ( - MessageComputationAPI, + ComputationAPI, ) 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: MessageComputationAPI, address: Address) -> bool: +def _mark_address_warm(computation: ComputationAPI, 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: MessageComputationAPI, + computation: ComputationAPI, 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: MessageComputationAPI, slot: int) -> bool: +def _mark_storage_warm(computation: ComputationAPI, 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: MessageComputationAPI, slot: int) -> bool: return True -def balance_eip2929(computation: MessageComputationAPI) -> None: +def balance_eip2929(computation: ComputationAPI) -> 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: MessageComputationAPI) -> None: +def extcodesize_eip2929(computation: ComputationAPI) -> 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: MessageComputationAPI) -> None: computation.stack_push_int(code_size) -def extcodecopy_eip2929(computation: MessageComputationAPI) -> None: +def extcodecopy_eip2929(computation: ComputationAPI) -> 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: MessageComputationAPI) -> None: +def extcodehash_eip2929(computation: ComputationAPI) -> 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: MessageComputationAPI) -> None: computation.stack_push_bytes(state.get_code_hash(address)) -def sload_eip2929(computation: MessageComputationAPI) -> None: +def sload_eip2929(computation: ComputationAPI) -> None: slot = computation.stack_pop1_int() if _mark_storage_warm(computation, slot): @@ -144,7 +144,7 @@ def sload_eip2929(computation: MessageComputationAPI) -> None: @curry def sstore_eip2929_generic( gas_schedule: NetSStoreGasSchedule, - computation: MessageComputationAPI, + computation: ComputationAPI, ) -> int: slot = sstore_eip2200_generic(gas_schedule, computation) @@ -161,7 +161,7 @@ def sstore_eip2929_generic( class LoadFeeByCacheWarmth: def get_account_load_fee( self, - computation: MessageComputationAPI, + computation: ComputationAPI, code_address: Address, ) -> int: was_cold = _mark_address_warm(computation, code_address) @@ -184,7 +184,7 @@ class StaticCallEIP2929(LoadFeeByCacheWarmth, StaticCall): pass -def selfdestruct_eip2929(computation: MessageComputationAPI) -> None: +def selfdestruct_eip2929(computation: ComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) if _mark_address_warm(computation, beneficiary): @@ -201,7 +201,7 @@ class CreateEIP2929(CreateByzantium): def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: MessageComputationAPI) -> Address: + computation: ComputationAPI) -> Address: address = super().generate_contract_address(stack_data, call_data, computation) computation.state.mark_address_warm(address) return address @@ -211,7 +211,7 @@ class Create2EIP2929(Create2): def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: MessageComputationAPI) -> Address: + computation: ComputationAPI) -> 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 cc66db771b..b047d5f7f1 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 ( - MessageComputationAPI, + ComputationAPI, MessageAPI, SignedTransactionAPI, TransactionExecutorAPI, @@ -13,14 +13,14 @@ SpuriousDragonTransactionExecutor, ) -from .computation import BerlinMessageComputation +from .computation import BerlinComputation class BerlinTransactionExecutor(SpuriousDragonTransactionExecutor): def build_computation( self, message: MessageAPI, - transaction: SignedTransactionAPI) -> MessageComputationAPI: + transaction: SignedTransactionAPI) -> ComputationAPI: self.vm_state.mark_address_warm(transaction.sender) @@ -36,5 +36,5 @@ def build_computation( class BerlinState(MuirGlacierState): - message_computation_class = BerlinMessageComputation + computation_class = BerlinComputation transaction_executor_class: Type[TransactionExecutorAPI] = BerlinTransactionExecutor diff --git a/eth/vm/forks/byzantium/__init__.py b/eth/vm/forks/byzantium/__init__.py index 235e16ad8b..7246f50337 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, - MessageComputationAPI, + ComputationAPI, ReceiptAPI, SignedTransactionAPI, StateAPI, @@ -104,7 +104,7 @@ def make_receipt( cls, base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, - computation: MessageComputationAPI, + computation: ComputationAPI, 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 30becc1e5d..83a3d0048a 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 SpuriousDragonMessageComputation +from eth.vm.forks.spurious_dragon.computation import SpuriousDragonComputation from .opcodes import BYZANTIUM_OPCODES @@ -22,11 +22,11 @@ ) -class ByzantiumMessageComputation(SpuriousDragonMessageComputation): +class ByzantiumComputation(SpuriousDragonComputation): """ A class for all execution *message* computations in the ``Byzantium`` fork. Inherits from - :class:`~eth.vm.forks.spurious_dragon.computation.SpuriousDragonMessageComputation` + :class:`~eth.vm.forks.spurious_dragon.computation.SpuriousDragonComputation` """ # Override opcodes = BYZANTIUM_OPCODES diff --git a/eth/vm/forks/byzantium/opcodes.py b/eth/vm/forks/byzantium/opcodes.py index 3e614f195f..aa8b63077a 100644 --- a/eth/vm/forks/byzantium/opcodes.py +++ b/eth/vm/forks/byzantium/opcodes.py @@ -11,13 +11,12 @@ from eth import constants -from eth.abc import OpcodeAPI +from eth.abc import ComputationAPI, OpcodeAPI from eth.exceptions import ( WriteProtection, ) from eth.vm import mnemonics from eth.vm import opcode_values -from eth.vm.computation import MessageComputation from eth.vm.forks.tangerine_whistle.constants import ( GAS_CALL_EIP150, GAS_SELFDESTRUCT_EIP150 @@ -36,7 +35,7 @@ def ensure_no_static(opcode_fn: Callable[..., Any]) -> Callable[..., Any]: @functools.wraps(opcode_fn) - def inner(computation: MessageComputation) -> Callable[..., Any]: + def inner(computation: ComputationAPI) -> 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 0ff9c3164f..d588c62ebb 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 ByzantiumMessageComputation +from .computation import ByzantiumComputation class ByzantiumState(SpuriousDragonState): - message_computation_class = ByzantiumMessageComputation + computation_class = ByzantiumComputation diff --git a/eth/vm/forks/constantinople/computation.py b/eth/vm/forks/constantinople/computation.py index 2fbf5dd107..e1686f8210 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 ( - ByzantiumMessageComputation + ByzantiumComputation ) from eth.vm.gas_meter import ( allow_negative_refund_strategy, @@ -14,11 +14,11 @@ CONSTANTINOPLE_PRECOMPILES = BYZANTIUM_PRECOMPILES -class ConstantinopleMessageComputation(ByzantiumMessageComputation): +class ConstantinopleComputation(ByzantiumComputation): """ A class for all execution *message* computations in the ``Constantinople`` fork. Inherits from - :class:`~eth.vm.forks.byzantium.computation.ByzantiumMessageComputation` + :class:`~eth.vm.forks.byzantium.computation.ByzantiumComputation` """ # Override opcodes = CONSTANTINOPLE_OPCODES diff --git a/eth/vm/forks/constantinople/state.py b/eth/vm/forks/constantinople/state.py index ca41f928d5..2ea1e877ca 100644 --- a/eth/vm/forks/constantinople/state.py +++ b/eth/vm/forks/constantinople/state.py @@ -2,8 +2,8 @@ ByzantiumState ) -from .computation import ConstantinopleMessageComputation +from .computation import ConstantinopleComputation class ConstantinopleState(ByzantiumState): - message_computation_class = ConstantinopleMessageComputation + computation_class = ConstantinopleComputation diff --git a/eth/vm/forks/frontier/__init__.py b/eth/vm/forks/frontier/__init__.py index 174f939160..0f2441b9b2 100644 --- a/eth/vm/forks/frontier/__init__.py +++ b/eth/vm/forks/frontier/__init__.py @@ -12,7 +12,7 @@ ReceiptAPI, StateAPI, SignedTransactionAPI, - MessageComputationAPI, + ComputationAPI, ) from eth.constants import ( BLOCK_REWARD, @@ -35,7 +35,7 @@ from .validation import validate_frontier_transaction_against_header -def make_frontier_receipt(computation: MessageComputationAPI, +def make_frontier_receipt(computation: ComputationAPI, 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: MessageComputationAPI) -> int: + computation: ComputationAPI) -> 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: MessageComputationAPI, + computation: ComputationAPI, 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 693ece056e..b0345dd66a 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 ( - MessageComputationAPI, + ComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, @@ -25,7 +25,7 @@ StackDepthLimit, ) from eth.vm.computation import ( - MessageComputation, + BaseComputation, ) from .opcodes import FRONTIER_OPCODES @@ -39,10 +39,10 @@ } -class FrontierMessageComputation(MessageComputation): +class FrontierComputation(BaseComputation): """ A class for all execution message computations in the ``Frontier`` fork. - Inherits from :class:`~eth.vm.computation.MessageComputation` + Inherits from :class:`~eth.vm.computation.BaseComputation` """ # Override opcodes = FRONTIER_OPCODES @@ -54,7 +54,7 @@ def apply_message( state: StateAPI, message: MessageAPI, transaction_context: TransactionContextAPI, - ) -> MessageComputationAPI: + ) -> ComputationAPI: snapshot = state.snapshot() @@ -99,7 +99,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> MessageComputationAPI: + transaction_context: TransactionContextAPI) -> ComputationAPI: 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 ee8da8b623..d5ae8878d1 100644 --- a/eth/vm/forks/frontier/state.py +++ b/eth/vm/forks/frontier/state.py @@ -7,7 +7,7 @@ from eth.abc import ( AccountDatabaseAPI, - MessageComputationAPI, + ComputationAPI, SignedTransactionAPI, MessageAPI, TransactionContextAPI, @@ -34,7 +34,7 @@ ) -from .computation import FrontierMessageComputation +from .computation import FrontierComputation 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) -> MessageComputationAPI: + transaction: SignedTransactionAPI) -> ComputationAPI: 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.message_computation_class.apply_create_message( + computation = self.vm_state.computation_class.apply_create_message( self.vm_state, message, transaction_context, ) else: - computation = self.vm_state.message_computation_class.apply_message( + computation = self.vm_state.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: MessageComputationAPI, + computation: ComputationAPI, 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: MessageComputationAPI - ) -> MessageComputationAPI: + computation: ComputationAPI + ) -> ComputationAPI: 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): - message_computation_class: Type[MessageComputationAPI] = FrontierMessageComputation + computation_class: Type[ComputationAPI] = FrontierComputation transaction_context_class: Type[TransactionContextAPI] = FrontierTransactionContext account_db_class: Type[AccountDatabaseAPI] = AccountDB transaction_executor_class: Type[TransactionExecutorAPI] = FrontierTransactionExecutor - def apply_transaction(self, transaction: SignedTransactionAPI) -> MessageComputationAPI: + def apply_transaction(self, transaction: SignedTransactionAPI) -> ComputationAPI: 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 378434304d..16b9b9d76e 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 ArrowGlacierMessageComputation +from ..arrow_glacier.computation import ArrowGlacierComputation -class GrayGlacierMessageComputation(ArrowGlacierMessageComputation): +class GrayGlacierComputation(ArrowGlacierComputation): """ A class for all execution *message* computations in the ``GrayGlacier`` fork. - Inherits from :class:`~eth.vm.forks.arrow_glacier.ArrowGlacierMessageComputation` + Inherits from :class:`~eth.vm.forks.arrow_glacier.ArrowGlacierComputation` """ pass diff --git a/eth/vm/forks/gray_glacier/state.py b/eth/vm/forks/gray_glacier/state.py index 629ccd65e4..68e7a99385 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 GrayGlacierMessageComputation +from .computation import GrayGlacierComputation from ..arrow_glacier import ArrowGlacierState from ..arrow_glacier.state import ArrowGlacierTransactionExecutor @@ -11,5 +11,5 @@ class GrayGlacierTransactionExecutor(ArrowGlacierTransactionExecutor): class GrayGlacierState(ArrowGlacierState): - message_computation_class = GrayGlacierMessageComputation + computation_class = GrayGlacierComputation transaction_executor_class: Type[TransactionExecutorAPI] = GrayGlacierTransactionExecutor diff --git a/eth/vm/forks/homestead/computation.py b/eth/vm/forks/homestead/computation.py index e2926cecb4..78c6843f45 100644 --- a/eth/vm/forks/homestead/computation.py +++ b/eth/vm/forks/homestead/computation.py @@ -9,22 +9,22 @@ ) from eth.abc import ( - MessageComputationAPI, + ComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, ) from eth.vm.forks.frontier.computation import ( - FrontierMessageComputation, + FrontierComputation, ) from .opcodes import HOMESTEAD_OPCODES -class HomesteadMessageComputation(FrontierMessageComputation): +class HomesteadComputation(FrontierComputation): """ A class for all execution *message* computations in the ``Frontier`` fork. - Inherits from :class:`~eth.vm.forks.frontier.computation.FrontierMessageComputation` + Inherits from :class:`~eth.vm.forks.frontier.computation.FrontierComputation` """ # Override opcodes = HOMESTEAD_OPCODES @@ -34,7 +34,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> MessageComputationAPI: + transaction_context: TransactionContextAPI) -> ComputationAPI: 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 c994ee74c3..e828bf1b55 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 ( - MessageComputationAPI, + ComputationAPI, SignedTransactionAPI, ) from eth.vm.forks.frontier.state import ( @@ -9,12 +9,12 @@ FrontierTransactionExecutor, ) -from .computation import HomesteadMessageComputation +from .computation import HomesteadComputation from .validation import validate_homestead_transaction class HomesteadState(FrontierState): - message_computation_class: Type[MessageComputationAPI] = HomesteadMessageComputation + computation_class: Type[ComputationAPI] = HomesteadComputation 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 d548a113d8..d077c6d143 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 ( - PetersburgMessageComputation, + PetersburgComputation, ) from eth.vm.gas_meter import ( allow_negative_refund_strategy, @@ -39,11 +39,11 @@ ) -class IstanbulMessageComputation(PetersburgMessageComputation): +class IstanbulComputation(PetersburgComputation): """ A class for all execution *message* computations in the ``Istanbul`` fork. Inherits from - :class:`~eth.vm.forks.constantinople.petersburg.PetersburgMessageComputation` + :class:`~eth.vm.forks.constantinople.petersburg.PetersburgComputation` """ # Override opcodes = ISTANBUL_OPCODES diff --git a/eth/vm/forks/istanbul/state.py b/eth/vm/forks/istanbul/state.py index 92969a76ae..a2f2f10c5d 100644 --- a/eth/vm/forks/istanbul/state.py +++ b/eth/vm/forks/istanbul/state.py @@ -2,8 +2,8 @@ PetersburgState ) -from .computation import IstanbulMessageComputation +from .computation import IstanbulComputation class IstanbulState(PetersburgState): - message_computation_class = IstanbulMessageComputation + computation_class = IstanbulComputation diff --git a/eth/vm/forks/istanbul/storage.py b/eth/vm/forks/istanbul/storage.py index 773ba9da77..68ec81450e 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.abc import ComputationAPI from eth.exceptions import OutOfGas -from eth.vm.computation import MessageComputation from eth.vm.forks.constantinople.storage import ( GAS_SCHEDULE_EIP1283, ) @@ -21,7 +21,7 @@ @curry def sstore_eip2200_generic( gas_schedule: NetSStoreGasSchedule, - computation: MessageComputation, + computation: ComputationAPI, ) -> int: gas_remaining = computation.get_gas_remaining() if gas_remaining <= 2300: diff --git a/eth/vm/forks/london/computation.py b/eth/vm/forks/london/computation.py index 7e56152073..97ce048ca1 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 ( - BerlinMessageComputation, + BerlinComputation, ) from .opcodes import LONDON_OPCODES from ..london.constants import EIP3541_RESERVED_STARTING_BYTE -class LondonMessageComputation(BerlinMessageComputation): +class LondonComputation(BerlinComputation): """ A class for all execution *message* computations in the ``London`` fork. - Inherits from :class:`~eth.vm.forks.berlin.BerlinMessageComputation` + Inherits from :class:`~eth.vm.forks.berlin.BerlinComputation` """ opcodes = LONDON_OPCODES diff --git a/eth/vm/forks/london/state.py b/eth/vm/forks/london/state.py index 39ea04d7a0..51c58be21f 100644 --- a/eth/vm/forks/london/state.py +++ b/eth/vm/forks/london/state.py @@ -6,7 +6,7 @@ ) from eth.abc import ( - MessageComputationAPI, + ComputationAPI, MessageAPI, SignedTransactionAPI, StateAPI, @@ -27,7 +27,7 @@ generate_contract_address, ) -from .computation import LondonMessageComputation +from .computation import LondonComputation 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: MessageComputationAPI, + computation: ComputationAPI, 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): - message_computation_class = LondonMessageComputation + computation_class = LondonComputation 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 50d0a863af..f4b406e572 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 ( - IstanbulMessageComputation, + IstanbulComputation, ) from .opcodes import MUIR_GLACIER_OPCODES @@ -10,11 +10,11 @@ MUIR_GLACIER_PRECOMPILES = ISTANBUL_PRECOMPILES -class MuirGlacierMessageComputation(IstanbulMessageComputation): +class MuirGlacierComputation(IstanbulComputation): """ A class for all execution *message* computations in the ``MuirGlacier`` fork. Inherits from - :class:`~eth.vm.forks.constantinople.istanbul.IstanbulMessageComputation` + :class:`~eth.vm.forks.constantinople.istanbul.IstanbulComputation` """ # Override opcodes = MUIR_GLACIER_OPCODES diff --git a/eth/vm/forks/muir_glacier/state.py b/eth/vm/forks/muir_glacier/state.py index de97ee944a..a3a25cb764 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 MuirGlacierMessageComputation +from .computation import MuirGlacierComputation class MuirGlacierState(IstanbulState): - message_computation_class = MuirGlacierMessageComputation + computation_class = MuirGlacierComputation diff --git a/eth/vm/forks/paris/computation.py b/eth/vm/forks/paris/computation.py index e308f5416c..64e6b42d02 100644 --- a/eth/vm/forks/paris/computation.py +++ b/eth/vm/forks/paris/computation.py @@ -1,10 +1,10 @@ from .opcodes import PARIS_OPCODES -from eth.vm.forks.gray_glacier.computation import GrayGlacierMessageComputation +from eth.vm.forks.gray_glacier.computation import GrayGlacierComputation -class ParisMessageComputation(GrayGlacierMessageComputation): +class ParisComputation(GrayGlacierComputation): """ A class for all execution *message* computations in the ``Paris`` hard fork - Inherits from :class:`~eth.vm.forks.gray_glacier.GrayGlacierMessageComputation` + Inherits from :class:`~eth.vm.forks.gray_glacier.GrayGlacierComputation` """ opcodes = PARIS_OPCODES diff --git a/eth/vm/forks/paris/state.py b/eth/vm/forks/paris/state.py index 321480ce26..b7c5104877 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 ParisMessageComputation +from .computation import ParisComputation from ..gray_glacier import GrayGlacierState from ..gray_glacier.state import GrayGlacierTransactionExecutor @@ -15,7 +15,7 @@ class ParisTransactionExecutor(GrayGlacierTransactionExecutor): class ParisState(GrayGlacierState): - message_computation_class = ParisMessageComputation + computation_class = ParisComputation transaction_executor_class: Type[TransactionExecutorAPI] = ParisTransactionExecutor @property diff --git a/eth/vm/forks/petersburg/computation.py b/eth/vm/forks/petersburg/computation.py index 6d08ce25c4..a812c2c48e 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 ( - ByzantiumMessageComputation + ByzantiumComputation ) from .opcodes import PETERSBURG_OPCODES @@ -10,11 +10,11 @@ PETERSBURG_PRECOMPILES = BYZANTIUM_PRECOMPILES -class PetersburgMessageComputation(ByzantiumMessageComputation): +class PetersburgComputation(ByzantiumComputation): """ A class for all execution *message* computations in the ``Petersburg`` fork. Inherits from - :class:`~eth.vm.forks.byzantium.computation.ByzantiumMessageComputation` + :class:`~eth.vm.forks.byzantium.computation.ByzantiumComputation` """ # Override opcodes = PETERSBURG_OPCODES diff --git a/eth/vm/forks/petersburg/state.py b/eth/vm/forks/petersburg/state.py index 60b23d12fb..476e457083 100644 --- a/eth/vm/forks/petersburg/state.py +++ b/eth/vm/forks/petersburg/state.py @@ -2,8 +2,8 @@ ByzantiumState ) -from .computation import PetersburgMessageComputation +from .computation import PetersburgComputation class PetersburgState(ByzantiumState): - message_computation_class = PetersburgMessageComputation + computation_class = PetersburgComputation diff --git a/eth/vm/forks/shanghai/computation.py b/eth/vm/forks/shanghai/computation.py index afd00e9183..bcfc1cb27e 100644 --- a/eth/vm/forks/shanghai/computation.py +++ b/eth/vm/forks/shanghai/computation.py @@ -1,6 +1,6 @@ from eth._utils.numeric import ceil32 from eth.abc import ( - MessageComputationAPI, + ComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, @@ -13,10 +13,10 @@ from eth.exceptions import ( OutOfGas, ) -from eth.vm.forks.paris.computation import ParisMessageComputation +from eth.vm.forks.paris.computation import ParisComputation -class ShanghaiMessageComputation(ParisMessageComputation): +class ShanghaiComputation(ParisComputation): """ A class for all execution *message* computations in the ``Shanghai`` hard fork """ @@ -45,7 +45,7 @@ def validate_create_message(cls, message: MessageAPI) -> None: ) @classmethod - def consume_initcode_gas_cost(cls, computation: MessageComputationAPI) -> None: + def consume_initcode_gas_cost(cls, computation: ComputationAPI) -> 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 5dbd97eda2..05e728f7ce 100644 --- a/eth/vm/forks/shanghai/state.py +++ b/eth/vm/forks/shanghai/state.py @@ -4,7 +4,7 @@ TransactionExecutorAPI, WithdrawalAPI, ) -from .computation import ShanghaiMessageComputation +from .computation import ShanghaiComputation from ..paris import ParisState from ..paris.state import ParisTransactionExecutor @@ -14,7 +14,7 @@ class ShanghaiTransactionExecutor(ParisTransactionExecutor): class ShanghaiState(ParisState): - message_computation_class = ShanghaiMessageComputation + 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 a55bce7f66..eddb6ac5c6 100644 --- a/eth/vm/forks/spurious_dragon/_utils.py +++ b/eth/vm/forks/spurious_dragon/_utils.py @@ -6,19 +6,19 @@ from eth import constants +from eth.abc import ( + ComputationAPI, +) from eth._utils.address import ( force_bytes_to_address, ) -from eth.vm.computation import MessageComputation - - THREE = force_bytes_to_address(b'\x03') @to_set def collect_touched_accounts( - computation: MessageComputation, + computation: ComputationAPI, 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 a9848a611a..615cbc2ea6 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 ( - MessageComputationAPI, + ComputationAPI, MessageAPI, StateAPI, TransactionContextAPI, @@ -16,18 +16,18 @@ VMError, ) from eth.vm.forks.homestead.computation import ( - HomesteadMessageComputation, + HomesteadComputation, ) from ..spurious_dragon.constants import EIP170_CODE_SIZE_LIMIT from .opcodes import SPURIOUS_DRAGON_OPCODES -class SpuriousDragonMessageComputation(HomesteadMessageComputation): +class SpuriousDragonComputation(HomesteadComputation): """ A class for all execution *message* computations in the ``SpuriousDragon`` fork. Inherits from - :class:`~eth.vm.forks.homestead.computation.HomesteadMessageComputation` + :class:`~eth.vm.forks.homestead.computation.HomesteadComputation` """ # Override opcodes = SPURIOUS_DRAGON_OPCODES @@ -37,7 +37,7 @@ def apply_create_message( cls, state: StateAPI, message: MessageAPI, - transaction_context: TransactionContextAPI) -> MessageComputationAPI: + transaction_context: TransactionContextAPI) -> ComputationAPI: snapshot = state.snapshot() diff --git a/eth/vm/forks/spurious_dragon/state.py b/eth/vm/forks/spurious_dragon/state.py index 67e0e98096..802278b595 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 ( - MessageComputationAPI, + ComputationAPI, SignedTransactionAPI, TransactionExecutorAPI, ) @@ -14,14 +14,14 @@ HomesteadTransactionExecutor, ) -from .computation import SpuriousDragonMessageComputation +from .computation import SpuriousDragonComputation from ._utils import collect_touched_accounts class SpuriousDragonTransactionExecutor(HomesteadTransactionExecutor): def finalize_computation(self, transaction: SignedTransactionAPI, - computation: MessageComputationAPI) -> MessageComputationAPI: + computation: ComputationAPI) -> ComputationAPI: computation = super().finalize_computation(transaction, computation) # @@ -45,5 +45,5 @@ def finalize_computation(self, class SpuriousDragonState(HomesteadState): - message_computation_class: Type[MessageComputationAPI] = SpuriousDragonMessageComputation + computation_class: Type[ComputationAPI] = SpuriousDragonComputation 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 cec0fd2c42..3b2c330486 100644 --- a/eth/vm/forks/tangerine_whistle/computation.py +++ b/eth/vm/forks/tangerine_whistle/computation.py @@ -1,13 +1,13 @@ -from ..homestead.computation import HomesteadMessageComputation +from ..homestead.computation import HomesteadComputation from .opcodes import TANGERINE_WHISTLE_OPCODES -class TangerineWhistleMessageComputation(HomesteadMessageComputation): +class TangerineWhistleComputation(HomesteadComputation): """ A class for all execution *message* computations in the ``TangerineWhistle`` fork. Inherits from - :class:`~eth.vm.forks.homestead.computation.HomesteadMessageComputation` + :class:`~eth.vm.forks.homestead.computation.HomesteadComputation` """ # Override opcodes = TANGERINE_WHISTLE_OPCODES diff --git a/eth/vm/forks/tangerine_whistle/state.py b/eth/vm/forks/tangerine_whistle/state.py index ca61cab5c7..4c8715d27a 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 TangerineWhistleMessageComputation +from .computation import TangerineWhistleComputation class TangerineWhistleState(HomesteadState): - message_computation_class = TangerineWhistleMessageComputation + computation_class = TangerineWhistleComputation diff --git a/eth/vm/logic/arithmetic.py b/eth/vm/logic/arithmetic.py index 8be2c52380..b6df153a77 100644 --- a/eth/vm/logic/arithmetic.py +++ b/eth/vm/logic/arithmetic.py @@ -1,3 +1,4 @@ +from eth.abc import ComputationAPI from eth_utils.toolz import ( curry, ) @@ -10,10 +11,8 @@ ceil8, ) -from eth.vm.computation import MessageComputation - -def add(computation: MessageComputation) -> None: +def add(computation: ComputationAPI) -> None: """ Addition """ @@ -24,7 +23,7 @@ def add(computation: MessageComputation) -> None: computation.stack_push_int(result) -def addmod(computation: MessageComputation) -> None: +def addmod(computation: ComputationAPI) -> None: """ Modulo Addition """ @@ -38,7 +37,7 @@ def addmod(computation: MessageComputation) -> None: computation.stack_push_int(result) -def sub(computation: MessageComputation) -> None: +def sub(computation: ComputationAPI) -> None: """ Subtraction """ @@ -49,7 +48,7 @@ def sub(computation: MessageComputation) -> None: computation.stack_push_int(result) -def mod(computation: MessageComputation) -> None: +def mod(computation: ComputationAPI) -> None: """ Modulo """ @@ -63,7 +62,7 @@ def mod(computation: MessageComputation) -> None: computation.stack_push_int(result) -def smod(computation: MessageComputation) -> None: +def smod(computation: ComputationAPI) -> None: """ Signed Modulo """ @@ -82,7 +81,7 @@ def smod(computation: MessageComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def mul(computation: MessageComputation) -> None: +def mul(computation: ComputationAPI) -> None: """ Multiplication """ @@ -93,7 +92,7 @@ def mul(computation: MessageComputation) -> None: computation.stack_push_int(result) -def mulmod(computation: MessageComputation) -> None: +def mulmod(computation: ComputationAPI) -> None: """ Modulo Multiplication """ @@ -106,7 +105,7 @@ def mulmod(computation: MessageComputation) -> None: computation.stack_push_int(result) -def div(computation: MessageComputation) -> None: +def div(computation: ComputationAPI) -> None: """ Division """ @@ -120,7 +119,7 @@ def div(computation: MessageComputation) -> None: computation.stack_push_int(result) -def sdiv(computation: MessageComputation) -> None: +def sdiv(computation: ComputationAPI) -> None: """ Signed Division """ @@ -140,7 +139,7 @@ def sdiv(computation: MessageComputation) -> None: @curry -def exp(computation: MessageComputation, gas_per_byte: int) -> None: +def exp(computation: ComputationAPI, gas_per_byte: int) -> None: """ Exponentiation """ @@ -164,7 +163,7 @@ def exp(computation: MessageComputation, gas_per_byte: int) -> None: computation.stack_push_int(result) -def signextend(computation: MessageComputation) -> None: +def signextend(computation: ComputationAPI) -> None: """ Signed Extend """ @@ -183,7 +182,7 @@ def signextend(computation: MessageComputation) -> None: computation.stack_push_int(result) -def shl(computation: MessageComputation) -> None: +def shl(computation: ComputationAPI) -> None: """ Bitwise left shift """ @@ -197,7 +196,7 @@ def shl(computation: MessageComputation) -> None: computation.stack_push_int(result) -def shr(computation: MessageComputation) -> None: +def shr(computation: ComputationAPI) -> None: """ Bitwise right shift """ @@ -211,7 +210,7 @@ def shr(computation: MessageComputation) -> None: computation.stack_push_int(result) -def sar(computation: MessageComputation) -> None: +def sar(computation: ComputationAPI) -> None: """ Arithmetic bitwise right shift """ diff --git a/eth/vm/logic/block.py b/eth/vm/logic/block.py index fe75890b12..85e9128eec 100644 --- a/eth/vm/logic/block.py +++ b/eth/vm/logic/block.py @@ -1,37 +1,39 @@ -from eth.vm.computation import MessageComputation +from eth_typing import BlockNumber +from eth.abc import ComputationAPI -def blockhash(computation: MessageComputation) -> None: + +def blockhash(computation: ComputationAPI) -> None: block_number = computation.stack_pop1_int() - block_hash = computation.state.get_ancestor_hash(block_number) + block_hash = computation.state.get_ancestor_hash(BlockNumber(block_number)) computation.stack_push_bytes(block_hash) -def coinbase(computation: MessageComputation) -> None: +def coinbase(computation: ComputationAPI) -> None: computation.stack_push_bytes(computation.state.coinbase) -def timestamp(computation: MessageComputation) -> None: +def timestamp(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.timestamp) -def number(computation: MessageComputation) -> None: +def number(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.block_number) -def difficulty(computation: MessageComputation) -> None: +def difficulty(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.difficulty) -def gaslimit(computation: MessageComputation) -> None: +def gaslimit(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.gas_limit) -def basefee(computation: MessageComputation) -> None: +def basefee(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.base_fee) -def mixhash(computation: MessageComputation) -> None: +def mixhash(computation: ComputationAPI) -> None: computation.stack_push_bytes(computation.state.mix_hash) diff --git a/eth/vm/logic/call.py b/eth/vm/logic/call.py index f704ca7e6d..0b74cd4402 100644 --- a/eth/vm/logic/call.py +++ b/eth/vm/logic/call.py @@ -14,7 +14,7 @@ ) from eth.abc import ( - MessageComputationAPI, + ComputationAPI, ) from eth.exceptions import ( OutOfGas, @@ -35,18 +35,18 @@ class BaseCall(Opcode, ABC): @abstractmethod def compute_msg_extra_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> int: raise NotImplementedError("Must be implemented by subclasses") @abstractmethod - def get_call_params(self, computation: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> CallParams: raise NotImplementedError("Must be implemented by subclasses") def compute_msg_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -57,7 +57,7 @@ def compute_msg_gas(self, def get_account_load_fee( self, - computation: MessageComputationAPI, + computation: ComputationAPI, code_address: Address, ) -> int: """ @@ -66,7 +66,7 @@ def get_account_load_fee( """ return 0 - def __call__(self, computation: MessageComputationAPI) -> None: + def __call__(self, computation: ComputationAPI) -> None: computation.consume_gas( self.gas_cost, reason=self.mnemonic, @@ -163,7 +163,7 @@ def __call__(self, computation: MessageComputationAPI) -> 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_message_computation(child_msg) + child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push_int(0) @@ -184,7 +184,7 @@ def __call__(self, computation: MessageComputationAPI) -> None: class Call(BaseCall): def compute_msg_extra_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> int: @@ -194,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: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> CallParams: gas = computation.stack_pop1_int() to = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -223,13 +223,13 @@ def get_call_params(self, computation: MessageComputationAPI) -> CallParams: class CallCode(BaseCall): def compute_msg_extra_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> int: return constants.GAS_CALLVALUE if value else 0 - def get_call_params(self, computation: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> CallParams: gas = computation.stack_pop1_int() code_address = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -261,20 +261,20 @@ def get_call_params(self, computation: MessageComputationAPI) -> CallParams: class DelegateCall(BaseCall): def compute_msg_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: return gas, gas def compute_msg_extra_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> int: return 0 - def get_call_params(self, computation: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> CallParams: gas = computation.stack_pop1_int() code_address = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -309,7 +309,7 @@ def get_call_params(self, computation: MessageComputationAPI) -> CallParams: # class CallEIP150(Call): def compute_msg_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -326,7 +326,7 @@ def compute_msg_gas(self, class CallCodeEIP150(CallCode): def compute_msg_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -343,7 +343,7 @@ def compute_msg_gas(self, class DelegateCallEIP150(DelegateCall): def compute_msg_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> Tuple[int, int]: @@ -364,7 +364,7 @@ def max_child_gas_eip150(gas: int) -> int: def compute_eip150_msg_gas(*, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, extra_gas: int, value: int, @@ -391,7 +391,7 @@ def compute_eip150_msg_gas(*, # class CallEIP161(CallEIP150): def compute_msg_extra_gas(self, - computation: MessageComputationAPI, + computation: ComputationAPI, gas: int, to: Address, value: int) -> int: @@ -409,7 +409,7 @@ def compute_msg_extra_gas(self, # Byzantium # class StaticCall(CallEIP161): - def get_call_params(self, computation: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> CallParams: gas = computation.stack_pop1_int() to = force_bytes_to_address(computation.stack_pop1_bytes()) @@ -436,7 +436,7 @@ def get_call_params(self, computation: MessageComputationAPI) -> CallParams: class CallByzantium(CallEIP161): - def get_call_params(self, computation: MessageComputationAPI) -> CallParams: + def get_call_params(self, computation: ComputationAPI) -> 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 3f89ca2884..aab3c9bfb1 100644 --- a/eth/vm/logic/comparison.py +++ b/eth/vm/logic/comparison.py @@ -4,11 +4,10 @@ signed_to_unsigned, unsigned_to_signed, ) +from eth.abc import ComputationAPI -from eth.vm.computation import MessageComputation - -def lt(computation: MessageComputation) -> None: +def lt(computation: ComputationAPI) -> None: """ Lesser Comparison """ @@ -22,7 +21,7 @@ def lt(computation: MessageComputation) -> None: computation.stack_push_int(result) -def gt(computation: MessageComputation) -> None: +def gt(computation: ComputationAPI) -> None: """ Greater Comparison """ @@ -36,7 +35,7 @@ def gt(computation: MessageComputation) -> None: computation.stack_push_int(result) -def slt(computation: MessageComputation) -> None: +def slt(computation: ComputationAPI) -> None: """ Signed Lesser Comparison """ @@ -53,7 +52,7 @@ def slt(computation: MessageComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def sgt(computation: MessageComputation) -> None: +def sgt(computation: ComputationAPI) -> None: """ Signed Greater Comparison """ @@ -70,7 +69,7 @@ def sgt(computation: MessageComputation) -> None: computation.stack_push_int(signed_to_unsigned(result)) -def eq(computation: MessageComputation) -> None: +def eq(computation: ComputationAPI) -> None: """ Equality """ @@ -84,7 +83,7 @@ def eq(computation: MessageComputation) -> None: computation.stack_push_int(result) -def iszero(computation: MessageComputation) -> None: +def iszero(computation: ComputationAPI) -> None: """ Not """ @@ -98,7 +97,7 @@ def iszero(computation: MessageComputation) -> None: computation.stack_push_int(result) -def and_op(computation: MessageComputation) -> None: +def and_op(computation: ComputationAPI) -> None: """ Bitwise And """ @@ -109,7 +108,7 @@ def and_op(computation: MessageComputation) -> None: computation.stack_push_int(result) -def or_op(computation: MessageComputation) -> None: +def or_op(computation: ComputationAPI) -> None: """ Bitwise Or """ @@ -120,7 +119,7 @@ def or_op(computation: MessageComputation) -> None: computation.stack_push_int(result) -def xor(computation: MessageComputation) -> None: +def xor(computation: ComputationAPI) -> None: """ Bitwise XOr """ @@ -131,7 +130,7 @@ def xor(computation: MessageComputation) -> None: computation.stack_push_int(result) -def not_op(computation: MessageComputation) -> None: +def not_op(computation: ComputationAPI) -> None: """ Not """ @@ -142,7 +141,7 @@ def not_op(computation: MessageComputation) -> None: computation.stack_push_int(result) -def byte_op(computation: MessageComputation) -> None: +def byte_op(computation: ComputationAPI) -> None: """ Bitwise And """ diff --git a/eth/vm/logic/context.py b/eth/vm/logic/context.py index 74a89b16a9..9d0cd8cec7 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 ( - MessageComputationAPI, + ComputationAPI, ) from eth.exceptions import ( OutOfBoundsRead, @@ -20,40 +20,38 @@ ceil32, ) -from eth.vm.computation import MessageComputation - -def balance(computation: MessageComputation) -> None: +def balance(computation: ComputationAPI) -> None: addr = force_bytes_to_address(computation.stack_pop1_bytes()) push_balance_of_address(addr, computation) -def selfbalance(computation: MessageComputation) -> None: +def selfbalance(computation: ComputationAPI) -> None: push_balance_of_address(computation.msg.storage_address, computation) -def push_balance_of_address(address: Address, computation: MessageComputationAPI) -> None: +def push_balance_of_address(address: Address, computation: ComputationAPI) -> None: balance = computation.state.get_balance(address) computation.stack_push_int(balance) -def origin(computation: MessageComputation) -> None: +def origin(computation: ComputationAPI) -> None: computation.stack_push_bytes(computation.transaction_context.origin) -def address(computation: MessageComputation) -> None: +def address(computation: ComputationAPI) -> None: computation.stack_push_bytes(computation.msg.storage_address) -def caller(computation: MessageComputation) -> None: +def caller(computation: ComputationAPI) -> None: computation.stack_push_bytes(computation.msg.sender) -def callvalue(computation: MessageComputation) -> None: +def callvalue(computation: ComputationAPI) -> None: computation.stack_push_int(computation.msg.value) -def calldataload(computation: MessageComputation) -> None: +def calldataload(computation: ComputationAPI) -> None: """ Load call data into memory. """ @@ -66,12 +64,12 @@ def calldataload(computation: MessageComputation) -> None: computation.stack_push_bytes(normalized_value) -def calldatasize(computation: MessageComputation) -> None: +def calldatasize(computation: ComputationAPI) -> None: size = len(computation.msg.data) computation.stack_push_int(size) -def calldatacopy(computation: MessageComputation) -> None: +def calldatacopy(computation: ComputationAPI) -> None: ( mem_start_position, calldata_start_position, @@ -93,16 +91,16 @@ def calldatacopy(computation: MessageComputation) -> None: computation.memory_write(mem_start_position, size, padded_value) -def chain_id(computation: MessageComputation) -> None: +def chain_id(computation: ComputationAPI) -> None: computation.stack_push_int(computation.state.execution_context.chain_id) -def codesize(computation: MessageComputation) -> None: +def codesize(computation: ComputationAPI) -> None: size = len(computation.code) computation.stack_push_int(size) -def codecopy(computation: MessageComputation) -> None: +def codecopy(computation: ComputationAPI) -> None: ( mem_start_position, code_start_position, @@ -127,18 +125,18 @@ def codecopy(computation: MessageComputation) -> None: computation.memory_write(mem_start_position, size, padded_code_bytes) -def gasprice(computation: MessageComputation) -> None: +def gasprice(computation: ComputationAPI) -> None: computation.stack_push_int(computation.transaction_context.gas_price) -def extcodesize(computation: MessageComputation) -> None: +def extcodesize(computation: ComputationAPI) -> 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: MessageComputationAPI) -> Tuple[Address, int]: +def extcodecopy_execute(computation: ComputationAPI) -> Tuple[Address, int]: """ Runs the logical component of extcodecopy, without charging gas. @@ -163,7 +161,7 @@ def extcodecopy_execute(computation: MessageComputationAPI) -> Tuple[Address, in return account, size -def consume_extcodecopy_word_cost(computation: MessageComputationAPI, size: int) -> None: +def consume_extcodecopy_word_cost(computation: ComputationAPI, size: int) -> None: word_count = ceil32(size) // 32 copy_gas_cost = constants.GAS_COPY * word_count computation.consume_gas( @@ -172,12 +170,12 @@ def consume_extcodecopy_word_cost(computation: MessageComputationAPI, size: int) ) -def extcodecopy(computation: MessageComputation) -> None: +def extcodecopy(computation: ComputationAPI) -> None: _address, size = extcodecopy_execute(computation) consume_extcodecopy_word_cost(computation, size) -def extcodehash(computation: MessageComputation) -> None: +def extcodehash(computation: ComputationAPI) -> None: """ Return the code hash for a given address. EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1052.md @@ -191,12 +189,12 @@ def extcodehash(computation: MessageComputation) -> None: computation.stack_push_bytes(state.get_code_hash(account)) -def returndatasize(computation: MessageComputation) -> None: +def returndatasize(computation: ComputationAPI) -> None: size = len(computation.return_data) computation.stack_push_int(size) -def returndatacopy(computation: MessageComputation) -> None: +def returndatacopy(computation: ComputationAPI) -> None: ( mem_start_position, returndata_start_position, diff --git a/eth/vm/logic/duplication.py b/eth/vm/logic/duplication.py index a2e89904be..b26f09089d 100644 --- a/eth/vm/logic/duplication.py +++ b/eth/vm/logic/duplication.py @@ -1,9 +1,9 @@ import functools -from eth.vm.computation import MessageComputation +from eth.abc import ComputationAPI -def dup_XX(computation: MessageComputation, position: int) -> None: +def dup_XX(computation: ComputationAPI, position: int) -> None: """ Stack item duplication. """ diff --git a/eth/vm/logic/flow.py b/eth/vm/logic/flow.py index b8533f454a..3115375c2e 100644 --- a/eth/vm/logic/flow.py +++ b/eth/vm/logic/flow.py @@ -1,20 +1,20 @@ +from eth.abc import ComputationAPI from eth.exceptions import ( InvalidJumpDestination, InvalidInstruction, Halt, ) -from eth.vm.computation import MessageComputation from eth.vm.opcode_values import ( JUMPDEST, ) -def stop(computation: MessageComputation) -> None: +def stop(computation: ComputationAPI) -> None: raise Halt('STOP') -def jump(computation: MessageComputation) -> None: +def jump(computation: ComputationAPI) -> None: jump_dest = computation.stack_pop1_int() computation.code.program_counter = jump_dest @@ -28,7 +28,7 @@ def jump(computation: MessageComputation) -> None: raise InvalidInstruction("Jump resulted in invalid instruction") -def jumpi(computation: MessageComputation) -> None: +def jumpi(computation: ComputationAPI) -> None: jump_dest, check_value = computation.stack_pop_ints(2) if check_value: @@ -43,17 +43,17 @@ def jumpi(computation: MessageComputation) -> None: raise InvalidInstruction("Jump resulted in invalid instruction") -def jumpdest(computation: MessageComputation) -> None: +def jumpdest(computation: ComputationAPI) -> None: pass -def program_counter(computation: MessageComputation) -> None: +def program_counter(computation: ComputationAPI) -> None: pc = max(computation.code.program_counter - 1, 0) computation.stack_push_int(pc) -def gas(computation: MessageComputation) -> None: +def gas(computation: ComputationAPI) -> 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 4566f22b2d..462b6ac8e7 100644 --- a/eth/vm/logic/invalid.py +++ b/eth/vm/logic/invalid.py @@ -1,4 +1,4 @@ -from eth.abc import MessageComputationAPI +from eth.abc import ComputationAPI 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: MessageComputationAPI) -> None: + def __call__(self, computation: ComputationAPI) -> 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 8be33e94a8..47ad0053dd 100644 --- a/eth/vm/logic/logging.py +++ b/eth/vm/logic/logging.py @@ -1,11 +1,10 @@ import functools from typing import Tuple from eth import constants +from eth.abc import ComputationAPI -from eth.vm.computation import MessageComputation - -def log_XX(computation: MessageComputation, topic_count: int) -> None: +def log_XX(computation: ComputationAPI, 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 55790f3a70..f159cb00a9 100644 --- a/eth/vm/logic/memory.py +++ b/eth/vm/logic/memory.py @@ -1,7 +1,7 @@ -from eth.vm.computation import MessageComputation +from eth.abc import ComputationAPI -def mstore(computation: MessageComputation) -> None: +def mstore(computation: ComputationAPI) -> None: start_position = computation.stack_pop1_int() value = computation.stack_pop1_bytes() @@ -13,7 +13,7 @@ def mstore(computation: MessageComputation) -> None: computation.memory_write(start_position, 32, normalized_value) -def mstore8(computation: MessageComputation) -> None: +def mstore8(computation: ComputationAPI) -> None: start_position = computation.stack_pop1_int() value = computation.stack_pop1_bytes() @@ -25,7 +25,7 @@ def mstore8(computation: MessageComputation) -> None: computation.memory_write(start_position, 1, normalized_value) -def mload(computation: MessageComputation) -> None: +def mload(computation: ComputationAPI) -> None: start_position = computation.stack_pop1_int() computation.extend_memory(start_position, 32) @@ -34,5 +34,5 @@ def mload(computation: MessageComputation) -> None: computation.stack_push_bytes(value) -def msize(computation: MessageComputation) -> None: +def msize(computation: ComputationAPI) -> None: computation.stack_push_int(len(computation._memory)) diff --git a/eth/vm/logic/sha3.py b/eth/vm/logic/sha3.py index dbf488fd99..3c3d61b96c 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 MessageComputation +from eth.abc import ComputationAPI -def sha3(computation: MessageComputation) -> None: +def sha3(computation: ComputationAPI) -> 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 35bd1b7def..c21302eecf 100644 --- a/eth/vm/logic/stack.py +++ b/eth/vm/logic/stack.py @@ -1,13 +1,13 @@ import functools -from eth.vm.computation import MessageComputation +from eth.abc import ComputationAPI -def pop(computation: MessageComputation) -> None: +def pop(computation: ComputationAPI) -> None: computation.stack_pop1_any() -def push_XX(computation: MessageComputation, size: int) -> None: +def push_XX(computation: ComputationAPI, 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 b13dde35ab..57588c4198 100644 --- a/eth/vm/logic/storage.py +++ b/eth/vm/logic/storage.py @@ -1,14 +1,13 @@ from typing import NamedTuple +from eth.abc import ComputationAPI from eth_utils import ( encode_hex, ) from eth import constants -from eth.vm.computation import MessageComputation - -def sstore(computation: MessageComputation) -> None: +def sstore(computation: ComputationAPI) -> None: slot, value = computation.stack_pop_ints(2) current_value = computation.state.get_storage( @@ -52,7 +51,7 @@ def sstore(computation: MessageComputation) -> None: ) -def sload(computation: MessageComputation) -> None: +def sload(computation: ComputationAPI) -> None: slot = computation.stack_pop1_int() value = computation.state.get_storage( @@ -76,7 +75,7 @@ class NetSStoreGasSchedule(NamedTuple): sstore_clears_schedule: int -def net_sstore(gas_schedule: NetSStoreGasSchedule, computation: MessageComputation) -> int: +def net_sstore(gas_schedule: NetSStoreGasSchedule, computation: ComputationAPI) -> int: """ :return slot: where the new value was stored """ diff --git a/eth/vm/logic/swap.py b/eth/vm/logic/swap.py index c2709a82a6..478aec6561 100644 --- a/eth/vm/logic/swap.py +++ b/eth/vm/logic/swap.py @@ -1,9 +1,9 @@ import functools -from eth.vm.computation import MessageComputation +from eth.abc import ComputationAPI -def swap_XX(computation: MessageComputation, position: int) -> None: +def swap_XX(computation: ComputationAPI, position: int) -> None: """ Stack item swapping """ diff --git a/eth/vm/logic/system.py b/eth/vm/logic/system.py index 5f57aa6f11..26725237bf 100644 --- a/eth/vm/logic/system.py +++ b/eth/vm/logic/system.py @@ -20,7 +20,7 @@ ceil32, ) from eth.abc import ( - MessageComputationAPI, + ComputationAPI, MessageAPI, ) from eth.vm import mnemonics @@ -29,7 +29,7 @@ from .call import max_child_gas_eip150 -def return_op(computation: MessageComputationAPI) -> None: +def return_op(computation: ComputationAPI) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) @@ -38,7 +38,7 @@ def return_op(computation: MessageComputationAPI) -> None: raise Halt('RETURN') -def revert(computation: MessageComputationAPI) -> None: +def revert(computation: ComputationAPI) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) @@ -47,12 +47,12 @@ def revert(computation: MessageComputationAPI) -> None: raise Revert(computation.output) -def selfdestruct(computation: MessageComputationAPI) -> None: +def selfdestruct(computation: ComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) _selfdestruct(computation, beneficiary) -def selfdestruct_eip150(computation: MessageComputationAPI) -> None: +def selfdestruct_eip150(computation: ComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) if not computation.state.account_exists(beneficiary): computation.consume_gas( @@ -63,7 +63,7 @@ def selfdestruct_eip150(computation: MessageComputationAPI) -> None: def selfdestruct_eip161_on_address( - computation: MessageComputationAPI, + computation: ComputationAPI, beneficiary: Address, ) -> None: is_dead = ( @@ -78,12 +78,12 @@ def selfdestruct_eip161_on_address( _selfdestruct(computation, beneficiary) -def selfdestruct_eip161(computation: MessageComputationAPI) -> None: +def selfdestruct_eip161(computation: ComputationAPI) -> None: beneficiary = force_bytes_to_address(computation.stack_pop1_bytes()) selfdestruct_eip161_on_address(computation, beneficiary) -def _selfdestruct(computation: MessageComputationAPI, beneficiary: Address) -> None: +def _selfdestruct(computation: ComputationAPI, beneficiary: Address) -> None: local_balance = computation.state.get_balance(computation.msg.storage_address) beneficiary_balance = computation.state.get_balance(beneficiary) @@ -131,7 +131,7 @@ def get_gas_cost(self, data: CreateOpcodeStackData) -> int: def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: MessageComputationAPI) -> Address: + computation: ComputationAPI) -> Address: creation_nonce = computation.state.get_nonce(computation.msg.storage_address) computation.state.increment_nonce(computation.msg.storage_address) @@ -143,12 +143,12 @@ def generate_contract_address(self, return contract_address - def get_stack_data(self, computation: MessageComputationAPI) -> CreateOpcodeStackData: + def get_stack_data(self, computation: ComputationAPI) -> CreateOpcodeStackData: endowment, memory_start, memory_length = computation.stack_pop_ints(3) return CreateOpcodeStackData(endowment, memory_start, memory_length) - def __call__(self, computation: MessageComputationAPI) -> None: + def __call__(self, computation: ComputationAPI) -> None: stack_data = self.get_stack_data(computation) @@ -211,10 +211,10 @@ def __call__(self, computation: MessageComputationAPI) -> None: def apply_create_message( self, - computation: MessageComputationAPI, + computation: ComputationAPI, child_msg: MessageAPI, ) -> None: - child_computation = computation.apply_child_message_computation(child_msg) + child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push_int(0) @@ -230,7 +230,7 @@ def max_child_gas_modifier(self, gas: int) -> int: class CreateByzantium(CreateEIP150): - def __call__(self, computation: MessageComputationAPI) -> None: + def __call__(self, computation: ComputationAPI) -> None: if computation.msg.is_static: raise WriteProtection("Cannot modify state while inside of a STATICCALL context") return super().__call__(computation) @@ -238,7 +238,7 @@ def __call__(self, computation: MessageComputationAPI) -> None: class Create2(CreateByzantium): - def get_stack_data(self, computation: MessageComputationAPI) -> CreateOpcodeStackData: + def get_stack_data(self, computation: ComputationAPI) -> CreateOpcodeStackData: endowment, memory_start, memory_length, salt = computation.stack_pop_ints(4) return CreateOpcodeStackData(endowment, memory_start, memory_length, salt) @@ -249,7 +249,7 @@ def get_gas_cost(self, data: CreateOpcodeStackData) -> int: def generate_contract_address(self, stack_data: CreateOpcodeStackData, call_data: bytes, - computation: MessageComputationAPI) -> Address: + computation: ComputationAPI) -> Address: computation.state.increment_nonce(computation.msg.storage_address) return generate_safe_contract_address( @@ -260,7 +260,7 @@ def generate_contract_address(self, def apply_create_message( self, - computation: MessageComputationAPI, + computation: ComputationAPI, child_msg: MessageAPI, ) -> None: # We need to ensure that creation operates on empty storage **and** @@ -270,7 +270,7 @@ def apply_create_message( computation.state.delete_storage(child_msg.storage_address) - child_computation = computation.apply_child_message_computation(child_msg) + child_computation = computation.apply_child_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 3da0487410..5232f45555 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 ( - MessageComputationAPI, + ComputationAPI, 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: MessageComputationAPI) -> Any: + def wrapped_logic_fn(computation: ComputationAPI) -> 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 311fd75d2e..e3f987fbb8 100644 --- a/eth/vm/state.py +++ b/eth/vm/state.py @@ -20,7 +20,7 @@ from eth.abc import ( AccountDatabaseAPI, AtomicDatabaseAPI, - MessageComputationAPI, + ComputationAPI, ExecutionContextAPI, MessageAPI, SignedTransactionAPI, @@ -45,7 +45,7 @@ class BaseState(Configurable, StateAPI): # __slots__ = ['_db', 'execution_context', '_account_db'] - message_computation_class: Type[MessageComputationAPI] = None + computation_class: Type[ComputationAPI] = 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.message_computation_class.get_precompiles() + or address in self.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) -> MessageComputationAPI: - if self.message_computation_class is None: - raise AttributeError("No `message_computation_class` has been set for this State") + transaction_context: TransactionContextAPI) -> ComputationAPI: + if self.computation_class is None: + raise AttributeError("No `computation_class` has been set for this State") else: - computation = self.message_computation_class(self, message, transaction_context) + computation = self.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) -> MessageComputationAPI: + transaction: SignedTransactionAPI) -> ComputationAPI: 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) -> MessageComputationAPI: + def __call__(self, transaction: SignedTransactionAPI) -> ComputationAPI: self.validate_transaction(transaction) message = self.build_evm_message(transaction) computation = self.build_computation(message, transaction) diff --git a/newsfragments/2097.breaking.rst b/newsfragments/2097.breaking.rst deleted file mode 100644 index fbf9701d45..0000000000 --- a/newsfragments/2097.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Refactor ``BaseComputation`` as a simpler base class and subclass ``MessageComputation`` building on it, allowing flexibility for other types of computations such as EOF computations. diff --git a/newsfragments/2106.internal.rst b/newsfragments/2106.internal.rst new file mode 100644 index 0000000000..793638c0e3 --- /dev/null +++ b/newsfragments/2106.internal.rst @@ -0,0 +1 @@ +Refactored the computation class hierarchy and cleaned up the code along the way. Some abstract API classes have more of the underlying properties that the subclasses implement. diff --git a/tests/core/opcodes/test_opcodes.py b/tests/core/opcodes/test_opcodes.py index 6b7941ecdf..260575eecc 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 ( - ShanghaiMessageComputation, + ShanghaiComputation, ) 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 = ShanghaiMessageComputation.opcodes + available_vm_opcodes = ShanghaiComputation.opcodes vm_opcodes_without_selfdestruct = { k: available_vm_opcodes[k] for k in available_vm_opcodes.keys() diff --git a/tests/core/vm/test_base_computation.py b/tests/core/vm/test_base_computation.py index e60f4aeaa0..5447ba588a 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 ( - MessageComputation, + BaseComputation, ) from eth.vm.transaction_context import ( BaseTransactionContext, ) -class DummyComputation(MessageComputation): +class DummyComputation(BaseComputation): @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_message_computation(child_message) + child_computation = computation.apply_child_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_contract_code_size_limit.py b/tests/core/vm/test_contract_code_size_limit.py index ddf0133bac..6c56159c8f 100644 --- a/tests/core/vm/test_contract_code_size_limit.py +++ b/tests/core/vm/test_contract_code_size_limit.py @@ -3,7 +3,7 @@ from eth_utils import hexstr_if_str, to_wei, to_bytes from eth.constants import CREATE_CONTRACT_ADDRESS from eth.vm import opcode_values -from eth.vm.forks.spurious_dragon.computation import SpuriousDragonMessageComputation +from eth.vm.forks.spurious_dragon.computation import SpuriousDragonComputation from eth.vm.forks.spurious_dragon.constants import EIP170_CODE_SIZE_LIMIT from eth.vm.message import Message from eth._utils.address import generate_contract_address @@ -77,7 +77,7 @@ def test_contract_code_size_limit( # EIP-170 apply after the SpuriousDragon fork. if ( - issubclass(computation.__class__, SpuriousDragonMessageComputation) + issubclass(computation.__class__, SpuriousDragonComputation) and code_len > EIP170_CODE_SIZE_LIMIT ): assert isinstance(computation.error, OutOfGas) diff --git a/tests/core/vm/test_frontier_computation.py b/tests/core/vm/test_frontier_computation.py index af890472ba..7c7f522ff6 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 ( - FrontierMessageComputation, + FrontierComputation, ) @@ -30,7 +30,7 @@ def message(canonical_address_a, canonical_address_b): @pytest.fixture def computation(message, transaction_context, state): - computation = FrontierMessageComputation( + computation = FrontierComputation( 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_message_computation(child_message) + child_computation = computation.generate_child_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 8ea42a1caa..a57699f6b3 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 ( - HomesteadMessageComputation, + HomesteadComputation, ) 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 = HomesteadMessageComputation.configure( +HomesteadComputationForTesting = HomesteadComputation.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, - message_computation_class=HomesteadComputationForTesting, + computation_class=HomesteadComputationForTesting, ) HomesteadVMForTesting = HomesteadVM.configure( __name__='HomesteadVMForTesting',