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',