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 14, 2022
1 parent 0350ced commit e16b798
Show file tree
Hide file tree
Showing 20 changed files with 406 additions and 65 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
4 changes: 4 additions & 0 deletions 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,6 +108,7 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM):
LONDON_MAINNET_BLOCK,
ARROW_GLACIER_MAINNET_BLOCK,
GRAY_GLACIER_MAINNET_BLOCK,
PARIS_MAINNET_BLOCK,
)
MAINNET_VMS = (
FrontierVM,
Expand All @@ -120,6 +123,7 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM):
LondonVM,
ArrowGlacierVM,
GrayGlacierVM,
ParisVM,
)

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(50000000)
10 changes: 10 additions & 0 deletions eth/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,13 @@
DEFAULT_SPOOF_Y_PARITY = 1
DEFAULT_SPOOF_R = 1
DEFAULT_SPOOF_S = 1


#
# Merge / EIP-3675 constants
#
POST_MERGE_OMMERS_HASH = b"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G"
POST_MERGE_DIFFICULTY = 0
POST_MERGE_MIX_HASH = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
POST_MERGE_NONCE = b"\x00\x00\x00\x00\x00\x00\x00\x00"
POST_MERGE_OMMERS = []
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
89 changes: 44 additions & 45 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,34 +170,29 @@ 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
except AttributeError:
return ExecutionContext(
coinbase=fee_recipient,
timestamp=header.timestamp,
block_number=header.block_number,
difficulty=header.difficulty,
gas_limit=header.gas_limit,
prev_hashes=prev_hashes,
chain_id=chain_context.chain_id,
)
else:
return ExecutionContext(
coinbase=fee_recipient,
timestamp=header.timestamp,
block_number=header.block_number,
difficulty=header.difficulty,
gas_limit=header.gas_limit,
prev_hashes=prev_hashes,
chain_id=chain_context.chain_id,
base_fee_per_gas=base_fee,
)

fields = {
"coinbase": fee_recipient,
"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,
}

if hasattr(header, "base_fee_per_gas"):
fields["base_fee_per_gas"] = header.base_fee_per_gas

return ExecutionContext(**fields)

def execute_bytecode(self,
origin: Address,
Expand Down Expand Up @@ -283,7 +280,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 @@ -292,22 +289,25 @@ def import_block(self, block: BlockAPI) -> BlockAndMetaWitness:
f" the attempted block was #{block.number}"
)

header_fields = {
"coinbase": block.header.coinbase,
"difficulty": block.header.difficulty,
"gas_limit": block.header.gas_limit,
"timestamp": block.header.timestamp,
"extra_data": block.header.extra_data,
"mix_hash": block.header.mix_hash,
"nonce": block.header.nonce,
"uncles_hash": keccak(rlp.encode(block.uncles)),
}

self._block = self.get_block().copy(
header=self.configure_header(
coinbase=block.header.coinbase,
difficulty=block.header.difficulty,
gas_limit=block.header.gas_limit,
timestamp=block.header.timestamp,
extra_data=block.header.extra_data,
mix_hash=block.header.mix_hash,
nonce=block.header.nonce,
uncles_hash=keccak(rlp.encode(block.uncles)),
),
header=self.configure_header(**header_fields),
uncles=block.uncles,
)

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 +329,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
Loading

0 comments on commit e16b798

Please sign in to comment.