From e2d11910b933acd6544cac993d68ffa5b5cc3a80 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Wed, 14 Sep 2022 14:24:39 -0600 Subject: [PATCH] Implement main changes for the Merge / Paris hark fork - Add ``Paris`` fork VM classes. - Supplant ``DIFFICULTY`` opcode at ``0x44`` with ``PREVRANDAO``. - Allow ``mix_hash`` to be retrieved from the execution context and add ``mixhash`` opcode logic function to be used in new ``PREVRANDAO`` opcode. - Some renaming of "Mining" nomenclature found within more general classes / methods - created an issue at #2079 to track more of these changes in a separate PR. - Minor cleanups along the way. --- eth/abc.py | 19 ++++++- eth/chains/mainnet/__init__.py | 9 +++- eth/chains/mainnet/constants.py | 5 ++ eth/constants.py | 16 +++++- eth/tools/builder/chain/builders.py | 4 +- eth/vm/base.py | 30 ++++++----- eth/vm/execution_context.py | 27 ++++++---- eth/vm/forks/__init__.py | 3 ++ eth/vm/forks/byzantium/headers.py | 8 ++- eth/vm/forks/frontier/state.py | 8 +-- eth/vm/forks/paris/__init__.py | 67 ++++++++++++++++++++++++ eth/vm/forks/paris/blocks.py | 42 +++++++++++++++ eth/vm/forks/paris/computation.py | 11 ++++ eth/vm/forks/paris/headers.py | 79 +++++++++++++++++++++++++++++ eth/vm/forks/paris/opcodes.py | 30 +++++++++++ eth/vm/forks/paris/state.py | 23 +++++++++ eth/vm/forks/paris/transactions.py | 41 +++++++++++++++ eth/vm/logic/block.py | 4 ++ eth/vm/mnemonics.py | 1 + eth/vm/opcode_values.py | 1 + eth/vm/state.py | 4 ++ 21 files changed, 398 insertions(+), 34 deletions(-) create mode 100644 eth/vm/forks/paris/__init__.py create mode 100644 eth/vm/forks/paris/blocks.py create mode 100644 eth/vm/forks/paris/computation.py create mode 100644 eth/vm/forks/paris/headers.py create mode 100644 eth/vm/forks/paris/opcodes.py create mode 100644 eth/vm/forks/paris/state.py create mode 100644 eth/vm/forks/paris/transactions.py diff --git a/eth/abc.py b/eth/abc.py index 1c22a029e1..93efea0914 100644 --- a/eth/abc.py +++ b/eth/abc.py @@ -91,6 +91,7 @@ def mining_hash(self) -> Hash32: """ @property + @abstractmethod def hex_hash(self) -> str: """ Return the hash as a hex string. @@ -1718,6 +1719,14 @@ def difficulty(self) -> int: """ ... + @property + @abstractmethod + def mix_hash(self) -> Hash32: + """ + Return the mix hash of the block + """ + ... + @property @abstractmethod def gas_limit(self) -> int: @@ -2662,6 +2671,14 @@ def difficulty(self) -> int: """ ... + @property + @abstractmethod + def mix_hash(self) -> Hash32: + """ + Return the current ``mix_hash`` from the current :attr:`~execution_context` + """ + ... + @property @abstractmethod def gas_limit(self) -> int: @@ -2686,7 +2703,7 @@ def get_gas_price(self, transaction: SignedTransactionAPI) -> int: """ Return the gas price of the given transaction. - Factor in the current block's base gase price, if appropriate. (See EIP-1559) + Factor in the current block's base gas price, if appropriate. (See EIP-1559) """ ... diff --git a/eth/chains/mainnet/__init__.py b/eth/chains/mainnet/__init__.py index f57a49a431..750965ee46 100644 --- a/eth/chains/mainnet/__init__.py +++ b/eth/chains/mainnet/__init__.py @@ -12,6 +12,7 @@ from .constants import ( MAINNET_CHAIN_ID, + PARIS_MAINNET_BLOCK, GRAY_GLACIER_MAINNET_BLOCK, ARROW_GLACIER_MAINNET_BLOCK, LONDON_MAINNET_BLOCK, @@ -46,6 +47,7 @@ IstanbulVM, LondonVM, MuirGlacierVM, + ParisVM, PetersburgVM, SpuriousDragonVM, TangerineWhistleVM, @@ -106,8 +108,9 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM): LONDON_MAINNET_BLOCK, ARROW_GLACIER_MAINNET_BLOCK, GRAY_GLACIER_MAINNET_BLOCK, + PARIS_MAINNET_BLOCK, ) -MAINNET_VMS = ( +MINING_MAINNET_VMS = ( FrontierVM, MainnetHomesteadVM, TangerineWhistleVM, @@ -121,7 +124,11 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM): ArrowGlacierVM, GrayGlacierVM, ) +POS_MAINNET_VMS = ( + ParisVM, +) +MAINNET_VMS = MINING_MAINNET_VMS + POS_MAINNET_VMS MAINNET_VM_CONFIGURATION = tuple(zip(MAINNET_FORK_BLOCKS, MAINNET_VMS)) diff --git a/eth/chains/mainnet/constants.py b/eth/chains/mainnet/constants.py index 615175a688..4a42c06c8e 100644 --- a/eth/chains/mainnet/constants.py +++ b/eth/chains/mainnet/constants.py @@ -72,3 +72,8 @@ # Gray Glacier Block # GRAY_GLACIER_MAINNET_BLOCK = BlockNumber(15050000) + +# +# Paris Block (block height at which TTD was reached) +# +PARIS_MAINNET_BLOCK = BlockNumber(15537394) diff --git a/eth/constants.py b/eth/constants.py index f3f4af224c..762a46353d 100644 --- a/eth/constants.py +++ b/eth/constants.py @@ -1,7 +1,11 @@ from eth_typing import ( Address, BlockNumber, - Hash32 + Hash32, +) +from typing import ( + List, + Optional, ) from eth._warnings import catch_and_ignore_import_warning @@ -184,3 +188,13 @@ DEFAULT_SPOOF_Y_PARITY = 1 DEFAULT_SPOOF_R = 1 DEFAULT_SPOOF_S = 1 + + +# +# Merge / EIP-3675 constants +# +POST_MERGE_OMMERS_HASH = EMPTY_UNCLE_HASH +POST_MERGE_DIFFICULTY = 0 +POST_MERGE_MIX_HASH = ZERO_HASH32 +POST_MERGE_NONCE = b"\x00\x00\x00\x00\x00\x00\x00\x00" +POST_MERGE_OMMERS: List[Optional[Hash32]] = [] diff --git a/eth/tools/builder/chain/builders.py b/eth/tools/builder/chain/builders.py index 2446d36c48..328ca8116b 100644 --- a/eth/tools/builder/chain/builders.py +++ b/eth/tools/builder/chain/builders.py @@ -76,6 +76,7 @@ LondonVM, ArrowGlacierVM, GrayGlacierVM, + ParisVM, ) @@ -248,8 +249,9 @@ def dao_fork_at(dao_fork_block_number: BlockNumber, london_at = fork_at(LondonVM) arrow_glacier_at = fork_at(ArrowGlacierVM) gray_glacier_at = fork_at(GrayGlacierVM) +paris_at = fork_at(ParisVM) -latest_mainnet_at = gray_glacier_at +latest_mainnet_at = paris_at GENESIS_DEFAULTS = cast( Tuple[Tuple[str, Union[BlockNumber, int, None, bytes, Address, Hash32]], ...], diff --git a/eth/vm/base.py b/eth/vm/base.py index d100769173..daea1149a8 100644 --- a/eth/vm/base.py +++ b/eth/vm/base.py @@ -97,11 +97,13 @@ class VM(Configurable, VirtualMachineAPI): cls_logger = logging.getLogger('eth.vm.base.VM') - def __init__(self, - header: BlockHeaderAPI, - chaindb: ChainDatabaseAPI, - chain_context: ChainContextAPI, - consensus_context: ConsensusContextAPI) -> None: + def __init__( + self, + header: BlockHeaderAPI, + chaindb: ChainDatabaseAPI, + chain_context: ChainContextAPI, + consensus_context: ConsensusContextAPI + ) -> None: self.chaindb = chaindb self.chain_context = chain_context self.consensus_context = consensus_context @@ -168,10 +170,12 @@ def apply_transaction(self, return receipt, computation @classmethod - def create_execution_context(cls, - header: BlockHeaderAPI, - prev_hashes: Iterable[Hash32], - chain_context: ChainContextAPI) -> ExecutionContextAPI: + def create_execution_context( + cls, + header: BlockHeaderAPI, + prev_hashes: Iterable[Hash32], + chain_context: ChainContextAPI + ) -> ExecutionContextAPI: fee_recipient = cls.consensus_class.get_fee_recipient(header) try: base_fee = header.base_fee_per_gas @@ -181,6 +185,7 @@ def create_execution_context(cls, timestamp=header.timestamp, block_number=header.block_number, difficulty=header.difficulty, + mix_hash=header.mix_hash, gas_limit=header.gas_limit, prev_hashes=prev_hashes, chain_id=chain_context.chain_id, @@ -191,6 +196,7 @@ def create_execution_context(cls, timestamp=header.timestamp, block_number=header.block_number, difficulty=header.difficulty, + mix_hash=header.mix_hash, gas_limit=header.gas_limit, prev_hashes=prev_hashes, chain_id=chain_context.chain_id, @@ -283,7 +289,7 @@ def apply_all_transactions( return result_header, receipts_tuple, computations_tuple # - # Mining + # Importing blocks # def import_block(self, block: BlockAPI) -> BlockAndMetaWitness: if self.get_block().number != block.number: @@ -307,7 +313,8 @@ def import_block(self, block: BlockAPI) -> BlockAndMetaWitness: ) execution_context = self.create_execution_context( - block.header, self.previous_hashes, self.chain_context) + block.header, self.previous_hashes, self.chain_context + ) # Zero out the gas_used before applying transactions. Each applied transaction will # increase the gas used in the final new_header. @@ -329,7 +336,6 @@ def import_block(self, block: BlockAPI) -> BlockAndMetaWitness: def mine_block(self, block: BlockAPI, *args: Any, **kwargs: Any) -> BlockAndMetaWitness: packed_block = self.pack_block(block, *args, **kwargs) - block_result = self.finalize_block(packed_block) # Perform validation diff --git a/eth/vm/execution_context.py b/eth/vm/execution_context.py index b059916fd6..85941f11ce 100644 --- a/eth/vm/execution_context.py +++ b/eth/vm/execution_context.py @@ -15,29 +15,32 @@ class ExecutionContext(ExecutionContextAPI): _coinbase = None - _timestamp = None _number = None _difficulty = None + _mix_hash = None _gas_limit = None _prev_hashes = None _chain_id = None _base_fee_per_gas = None def __init__( - self, - coinbase: Address, - timestamp: int, - block_number: BlockNumber, - difficulty: int, - gas_limit: int, - prev_hashes: Iterable[Hash32], - chain_id: int, - base_fee_per_gas: Optional[int] = None) -> None: + self, + coinbase: Address, + timestamp: int, + block_number: BlockNumber, + difficulty: int, + mix_hash: Hash32, + gas_limit: int, + prev_hashes: Iterable[Hash32], + chain_id: int, + base_fee_per_gas: Optional[int] = None + ) -> None: self._coinbase = coinbase self._timestamp = timestamp self._block_number = block_number self._difficulty = difficulty + self._mix_hash = mix_hash self._gas_limit = gas_limit self._prev_hashes = CachedIterable(prev_hashes) self._chain_id = chain_id @@ -59,6 +62,10 @@ def block_number(self) -> BlockNumber: def difficulty(self) -> int: return self._difficulty + @property + def mix_hash(self) -> Hash32: + return self._mix_hash + @property def gas_limit(self) -> int: return self._gas_limit diff --git a/eth/vm/forks/__init__.py b/eth/vm/forks/__init__.py index a5e2cf0a19..a046f67b4e 100644 --- a/eth/vm/forks/__init__.py +++ b/eth/vm/forks/__init__.py @@ -37,3 +37,6 @@ from .gray_glacier import ( # noqa: F401 GrayGlacierVM, ) +from .paris import ( # noqa: F401 + ParisVM, +) diff --git a/eth/vm/forks/byzantium/headers.py b/eth/vm/forks/byzantium/headers.py index f477854af4..a7202e7a7f 100644 --- a/eth/vm/forks/byzantium/headers.py +++ b/eth/vm/forks/byzantium/headers.py @@ -98,7 +98,13 @@ def configure_header(difficulty_fn: Callable[[BlockHeaderAPI, int], int], validate_header_params_for_configuration(header_params) with vm.get_header().build_changeset(**header_params) as changeset: - if 'timestamp' in header_params and changeset.block_number > 0: + if ( + 'timestamp' in header_params + and changeset.block_number > 0 + + # check that we are pre-PoS and not using a constant for the difficulty + and not isinstance(difficulty_fn, int) + ): parent_header = get_parent_header(changeset.build_rlp(), vm.chaindb) changeset.difficulty = difficulty_fn( parent_header, diff --git a/eth/vm/forks/frontier/state.py b/eth/vm/forks/frontier/state.py index 8a75a03c6d..4aa44700db 100644 --- a/eth/vm/forks/frontier/state.py +++ b/eth/vm/forks/frontier/state.py @@ -178,7 +178,7 @@ def finalize_computation(self, self.vm_state.delta_balance(computation.msg.sender, gas_refund_amount) - # Miner Fees + # Beneficiary Fees gas_used = transaction.gas - gas_remaining - gas_refund transaction_fee = gas_used * self.vm_state.get_tip(transaction) self.vm_state.logger.debug2( @@ -190,13 +190,7 @@ def finalize_computation(self, # Process Self Destructs for account, _ in computation.get_accounts_for_deletion(): - # TODO: need to figure out how we prevent multiple selfdestructs from - # the same account and if this is the right place to put this. self.vm_state.logger.debug2('DELETING ACCOUNT: %s', encode_hex(account)) - - # TODO: this balance setting is likely superflous and can be - # removed since `delete_account` does this. - self.vm_state.set_balance(account, 0) self.vm_state.delete_account(account) return computation diff --git a/eth/vm/forks/paris/__init__.py b/eth/vm/forks/paris/__init__.py new file mode 100644 index 0000000000..3c03f21bc8 --- /dev/null +++ b/eth/vm/forks/paris/__init__.py @@ -0,0 +1,67 @@ +from typing import ( + Type, +) + +from eth.abc import BlockAPI, BlockHeaderAPI +from eth.constants import ( + POST_MERGE_DIFFICULTY, + POST_MERGE_NONCE, + POST_MERGE_OMMERS_HASH, +) +from eth.rlp.blocks import BaseBlock +from eth.vm.forks.gray_glacier import GrayGlacierVM +from eth.vm.state import BaseState +from eth_utils import ( + ValidationError, +) + +from .blocks import ParisBlock +from .headers import ( + configure_paris_header, + create_paris_header_from_parent, +) +from .state import ParisState + + +class ParisVM(GrayGlacierVM): + # fork name + fork = 'paris' + + # classes + block_class: Type[BaseBlock] = ParisBlock + _state_class: Type[BaseState] = ParisState + + # Methods + create_header_from_parent = staticmethod( # type: ignore + create_paris_header_from_parent(POST_MERGE_DIFFICULTY) + ) + configure_header = configure_paris_header + + def _assign_block_rewards(self, block: BlockAPI) -> None: + # No block reward or uncles / uncle rewards in PoS + pass + + @classmethod + def validate_header( + cls, + header: BlockHeaderAPI, + parent_header: BlockHeaderAPI + ) -> None: + super().validate_header(header, parent_header) + + difficulty, nonce, uncles_hash = ( + header.difficulty, header.nonce, header.uncles_hash + ) + + if difficulty != POST_MERGE_DIFFICULTY: + raise ValidationError( + f"Difficulty must be {POST_MERGE_DIFFICULTY}, got {difficulty}." + ) + if nonce != POST_MERGE_NONCE: + raise ValidationError( + f"Nonce must be {POST_MERGE_NONCE !r}, got {nonce !r}." + ) + if uncles_hash != POST_MERGE_OMMERS_HASH: + raise ValidationError( + f"Uncles hash must be {POST_MERGE_OMMERS_HASH !r}, got {uncles_hash !r}." + ) diff --git a/eth/vm/forks/paris/blocks.py b/eth/vm/forks/paris/blocks.py new file mode 100644 index 0000000000..dc9f418915 --- /dev/null +++ b/eth/vm/forks/paris/blocks.py @@ -0,0 +1,42 @@ +from abc import ABC +from typing import Type + +from eth.abc import TransactionBuilderAPI + +from eth_utils import ( + encode_hex, +) + +from rlp.sedes import ( + CountableList, +) + +from .transactions import ( + ParisTransactionBuilder, +) +from eth.vm.forks.gray_glacier.blocks import ( + GrayGlacierBlock, + GrayGlacierBlockHeader, + GrayGlacierMiningHeader, +) +from ..london.blocks import ( + LondonBackwardsHeader, +) + + +class ParisMiningHeader(GrayGlacierMiningHeader, ABC): + pass + + +class ParisBlockHeader(GrayGlacierBlockHeader, ABC): + def __str__(self) -> str: + return f'' + + +class ParisBlock(GrayGlacierBlock): + transaction_builder: Type[TransactionBuilderAPI] = ParisTransactionBuilder + fields = [ + ('header', ParisBlockHeader), + ('transactions', CountableList(transaction_builder)), + ('uncles', CountableList(LondonBackwardsHeader)), + ] diff --git a/eth/vm/forks/paris/computation.py b/eth/vm/forks/paris/computation.py new file mode 100644 index 0000000000..21932300cd --- /dev/null +++ b/eth/vm/forks/paris/computation.py @@ -0,0 +1,11 @@ +from .opcodes import PARIS_OPCODES +from eth.vm.forks.gray_glacier.computation import GrayGlacierComputation + + +class ParisComputation(GrayGlacierComputation): + """ + A class for all execution computations in the ``Paris`` hard fork + (a.k.a. "The Merge"). + Inherits from :class:`~eth.vm.forks.gray_glacier.GrayGlacierComputation` + """ + opcodes = PARIS_OPCODES diff --git a/eth/vm/forks/paris/headers.py b/eth/vm/forks/paris/headers.py new file mode 100644 index 0000000000..4067ce8e4b --- /dev/null +++ b/eth/vm/forks/paris/headers.py @@ -0,0 +1,79 @@ +from typing import Any, Callable, Optional + +from toolz import curry + +from eth.abc import BlockHeaderAPI +from eth.constants import ( + POST_MERGE_DIFFICULTY, + POST_MERGE_MIX_HASH, + POST_MERGE_NONCE, +) +from eth.vm.forks.gray_glacier.headers import ( + compute_gray_glacier_difficulty, + create_gray_glacier_header_from_parent, +) +from eth.vm.forks.byzantium.headers import ( + configure_header, +) +from eth_utils import ValidationError +from .blocks import ParisBlockHeader + + +def _validate_and_return_paris_header_param( + header_param: str, + actual: Any, + constant_value: Any, +) -> Any: + if actual and actual != constant_value: + raise ValidationError( + f"Header param '{header_param}' must always be " + f"{constant_value}, got: {actual}" + ) + return constant_value + + +@curry +def create_paris_header_from_parent( + _difficulty_fn: Callable[[BlockHeaderAPI, int], int], + parent_header: Optional[BlockHeaderAPI], + **header_params: Any, +) -> BlockHeaderAPI: + if parent_header is None: + if "mix_hash" not in header_params: + header_params["mix_hash"] = POST_MERGE_MIX_HASH + if "nonce" not in header_params: + header_params["nonce"] = POST_MERGE_NONCE + if "difficulty" not in header_params: + header_params["difficulty"] = POST_MERGE_DIFFICULTY + + header_params["mix_hash"] = ( + header_params["mix_hash"] if "mix_hash" in header_params + else parent_header.mix_hash + ) + + if parent_header is not None: + if "difficulty" in header_params: + header_params["difficulty"] = _validate_and_return_paris_header_param( + "difficulty", header_params["difficulty"], POST_MERGE_DIFFICULTY + ) + else: + header_params["difficulty"] = POST_MERGE_DIFFICULTY + + if "nonce" in header_params: + header_params["nonce"] = _validate_and_return_paris_header_param( + "nonce", header_params["nonce"], POST_MERGE_NONCE + ) + else: + header_params["nonce"] = POST_MERGE_NONCE + + gray_glacier_validated_header = create_gray_glacier_header_from_parent( + compute_gray_glacier_difficulty, parent_header, **header_params + ) + + # extract params validated up to gray glacier (previous VM) + # and plug into a `ParisBlockHeader` class + all_fields = gray_glacier_validated_header.as_dict() + return ParisBlockHeader(**all_fields) + + +configure_paris_header = configure_header(POST_MERGE_DIFFICULTY) diff --git a/eth/vm/forks/paris/opcodes.py b/eth/vm/forks/paris/opcodes.py new file mode 100644 index 0000000000..a382909544 --- /dev/null +++ b/eth/vm/forks/paris/opcodes.py @@ -0,0 +1,30 @@ +import copy +from typing import Dict + +from eth.vm.opcode import as_opcode +from eth_utils.toolz import merge + +from eth import constants +from eth.abc import OpcodeAPI +from eth.vm import mnemonics +from eth.vm import opcode_values +from eth.vm.logic import ( + block, +) + +from eth.vm.forks.london.opcodes import LONDON_OPCODES + + +NEW_OPCODES = { + # EIP-4399: supplant DIFFICULTY with PREVRANDAO + opcode_values.PREVRANDAO: as_opcode( + logic_fn=block.mixhash, + mnemonic=mnemonics.PREVRANDAO, + gas_cost=constants.GAS_BASE, + ), +} + +PARIS_OPCODES: Dict[int, OpcodeAPI] = merge( + copy.deepcopy(LONDON_OPCODES), + NEW_OPCODES +) diff --git a/eth/vm/forks/paris/state.py b/eth/vm/forks/paris/state.py new file mode 100644 index 0000000000..b7c5104877 --- /dev/null +++ b/eth/vm/forks/paris/state.py @@ -0,0 +1,23 @@ +from typing import Type + +from eth.abc import ( + StateAPI, + TransactionExecutorAPI, +) +from eth_typing import Hash32 +from .computation import ParisComputation +from ..gray_glacier import GrayGlacierState +from ..gray_glacier.state import GrayGlacierTransactionExecutor + + +class ParisTransactionExecutor(GrayGlacierTransactionExecutor): + pass + + +class ParisState(GrayGlacierState): + computation_class = ParisComputation + transaction_executor_class: Type[TransactionExecutorAPI] = ParisTransactionExecutor + + @property + def mix_hash(self: StateAPI) -> Hash32: + return self.execution_context.mix_hash diff --git a/eth/vm/forks/paris/transactions.py b/eth/vm/forks/paris/transactions.py new file mode 100644 index 0000000000..ace279910a --- /dev/null +++ b/eth/vm/forks/paris/transactions.py @@ -0,0 +1,41 @@ +from abc import ABC + +from eth_keys.datatypes import PrivateKey + +from eth._utils.transactions import ( + create_transaction_signature, +) +from eth.vm.forks.gray_glacier.transactions import ( + GrayGlacierLegacyTransaction, + GrayGlacierTransactionBuilder, + GrayGlacierUnsignedLegacyTransaction, +) + + +class ParisLegacyTransaction(GrayGlacierLegacyTransaction, ABC): + pass + + +class ParisUnsignedLegacyTransaction(GrayGlacierUnsignedLegacyTransaction): + def as_signed_transaction( + self, + private_key: PrivateKey, + chain_id: int = None + ) -> ParisLegacyTransaction: + v, r, s = create_transaction_signature(self, private_key, chain_id=chain_id) + return ParisLegacyTransaction( + nonce=self.nonce, + gas_price=self.gas_price, + gas=self.gas, + to=self.to, + value=self.value, + data=self.data, + v=v, + r=r, + s=s, + ) + + +class ParisTransactionBuilder(GrayGlacierTransactionBuilder): + legacy_signed = ParisLegacyTransaction + legacy_unsigned = ParisUnsignedLegacyTransaction diff --git a/eth/vm/logic/block.py b/eth/vm/logic/block.py index db9b771048..be97c0a270 100644 --- a/eth/vm/logic/block.py +++ b/eth/vm/logic/block.py @@ -31,3 +31,7 @@ def gaslimit(computation: BaseComputation) -> None: def basefee(computation: BaseComputation) -> None: computation.stack_push_int(computation.state.base_fee) + + +def mixhash(computation: BaseComputation) -> None: + computation.stack_push_bytes(computation.state.mix_hash) diff --git a/eth/vm/mnemonics.py b/eth/vm/mnemonics.py index 55d41843ee..50f56b2257 100644 --- a/eth/vm/mnemonics.py +++ b/eth/vm/mnemonics.py @@ -63,6 +63,7 @@ TIMESTAMP = 'TIMESTAMP' NUMBER = 'NUMBER' DIFFICULTY = 'DIFFICULTY' +PREVRANDAO = "PREVRANDAO" GASLIMIT = 'GASLIMIT' BASEFEE = 'BASEFEE' # diff --git a/eth/vm/opcode_values.py b/eth/vm/opcode_values.py index 7956371aa4..bfddc5e83a 100644 --- a/eth/vm/opcode_values.py +++ b/eth/vm/opcode_values.py @@ -73,6 +73,7 @@ TIMESTAMP = 0x42 NUMBER = 0x43 DIFFICULTY = 0x44 +PREVRANDAO = 0x44 # EIP-4399: supplant DIFFICULTY with PREVRANDAO GASLIMIT = 0x45 diff --git a/eth/vm/state.py b/eth/vm/state.py index 4099eaf03d..a0286701b2 100644 --- a/eth/vm/state.py +++ b/eth/vm/state.py @@ -84,6 +84,10 @@ def block_number(self) -> BlockNumber: def difficulty(self) -> int: return self.execution_context.difficulty + @property + def mix_hash(self) -> Hash32: + return self.execution_context.mix_hash + @property def gas_limit(self) -> int: return self.execution_context.gas_limit