Skip to content

Commit

Permalink
Implement main changes for the Merge / Paris hark fork
Browse files Browse the repository at this point in the history
- 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 ethereum#2079 to track more of these changes in a separate PR.
- Minor cleanups along the way.
  • Loading branch information
fselmo committed Sep 16, 2022
1 parent dd3e658 commit e2d1191
Show file tree
Hide file tree
Showing 21 changed files with 398 additions and 34 deletions.
19 changes: 18 additions & 1 deletion eth/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def mining_hash(self) -> Hash32:
"""

@property
@abstractmethod
def hex_hash(self) -> str:
"""
Return the hash as a hex string.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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)
"""
...

Expand Down
9 changes: 8 additions & 1 deletion eth/chains/mainnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from .constants import (
MAINNET_CHAIN_ID,
PARIS_MAINNET_BLOCK,
GRAY_GLACIER_MAINNET_BLOCK,
ARROW_GLACIER_MAINNET_BLOCK,
LONDON_MAINNET_BLOCK,
Expand Down Expand Up @@ -46,6 +47,7 @@
IstanbulVM,
LondonVM,
MuirGlacierVM,
ParisVM,
PetersburgVM,
SpuriousDragonVM,
TangerineWhistleVM,
Expand Down Expand Up @@ -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,
Expand All @@ -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))


Expand Down
5 changes: 5 additions & 0 deletions eth/chains/mainnet/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
16 changes: 15 additions & 1 deletion eth/constants.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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]] = []
4 changes: 3 additions & 1 deletion eth/tools/builder/chain/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
LondonVM,
ArrowGlacierVM,
GrayGlacierVM,
ParisVM,
)


Expand Down Expand Up @@ -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]], ...],
Expand Down
30 changes: 18 additions & 12 deletions eth/vm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand All @@ -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
Expand Down
27 changes: 17 additions & 10 deletions eth/vm/execution_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions eth/vm/forks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@
from .gray_glacier import ( # noqa: F401
GrayGlacierVM,
)
from .paris import ( # noqa: F401
ParisVM,
)
8 changes: 7 additions & 1 deletion eth/vm/forks/byzantium/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 1 addition & 7 deletions eth/vm/forks/frontier/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down
67 changes: 67 additions & 0 deletions eth/vm/forks/paris/__init__.py
Original file line number Diff line number Diff line change
@@ -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}."
)
Loading

0 comments on commit e2d1191

Please sign in to comment.