Skip to content

Commit b5cb6e2

Browse files
committed
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 ethereum#2079 to track more of these changes in a separate PR. - Minor cleanups along the way.
1 parent 3ce11fa commit b5cb6e2

21 files changed

+406
-34
lines changed

eth/abc.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def mining_hash(self) -> Hash32:
9191
"""
9292

9393
@property
94+
@abstractmethod
9495
def hex_hash(self) -> str:
9596
"""
9697
Return the hash as a hex string.
@@ -1718,6 +1719,14 @@ def difficulty(self) -> int:
17181719
"""
17191720
...
17201721

1722+
@property
1723+
@abstractmethod
1724+
def mix_hash(self) -> Hash32:
1725+
"""
1726+
Return the mix hash of the block
1727+
"""
1728+
...
1729+
17211730
@property
17221731
@abstractmethod
17231732
def gas_limit(self) -> int:
@@ -2662,6 +2671,14 @@ def difficulty(self) -> int:
26622671
"""
26632672
...
26642673

2674+
@property
2675+
@abstractmethod
2676+
def mix_hash(self) -> Hash32:
2677+
"""
2678+
Return the current ``mix_hash`` from the current :attr:`~execution_context`
2679+
"""
2680+
...
2681+
26652682
@property
26662683
@abstractmethod
26672684
def gas_limit(self) -> int:
@@ -2686,7 +2703,7 @@ def get_gas_price(self, transaction: SignedTransactionAPI) -> int:
26862703
"""
26872704
Return the gas price of the given transaction.
26882705
2689-
Factor in the current block's base gase price, if appropriate. (See EIP-1559)
2706+
Factor in the current block's base gas price, if appropriate. (See EIP-1559)
26902707
"""
26912708
...
26922709

eth/chains/mainnet/__init__.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from .constants import (
1414
MAINNET_CHAIN_ID,
15+
PARIS_MAINNET_BLOCK,
1516
GRAY_GLACIER_MAINNET_BLOCK,
1617
ARROW_GLACIER_MAINNET_BLOCK,
1718
LONDON_MAINNET_BLOCK,
@@ -46,6 +47,7 @@
4647
IstanbulVM,
4748
LondonVM,
4849
MuirGlacierVM,
50+
ParisVM,
4951
PetersburgVM,
5052
SpuriousDragonVM,
5153
TangerineWhistleVM,
@@ -106,8 +108,9 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM):
106108
LONDON_MAINNET_BLOCK,
107109
ARROW_GLACIER_MAINNET_BLOCK,
108110
GRAY_GLACIER_MAINNET_BLOCK,
111+
PARIS_MAINNET_BLOCK,
109112
)
110-
MAINNET_VMS = (
113+
MINING_MAINNET_VMS = (
111114
FrontierVM,
112115
MainnetHomesteadVM,
113116
TangerineWhistleVM,
@@ -121,7 +124,11 @@ class MainnetHomesteadVM(MainnetDAOValidatorVM):
121124
ArrowGlacierVM,
122125
GrayGlacierVM,
123126
)
127+
POS_MAINNET_VMS = (
128+
ParisVM,
129+
)
124130

131+
MAINNET_VMS = MINING_MAINNET_VMS + POS_MAINNET_VMS
125132
MAINNET_VM_CONFIGURATION = tuple(zip(MAINNET_FORK_BLOCKS, MAINNET_VMS))
126133

127134

eth/chains/mainnet/constants.py

+5
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,8 @@
7272
# Gray Glacier Block
7373
#
7474
GRAY_GLACIER_MAINNET_BLOCK = BlockNumber(15050000)
75+
76+
#
77+
# Paris Block (block height at which TTD was reached)
78+
#
79+
PARIS_MAINNET_BLOCK = BlockNumber(15537394)

eth/constants.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from eth_typing import (
22
Address,
33
BlockNumber,
4-
Hash32
4+
Hash32,
5+
)
6+
from typing import (
7+
List,
8+
Optional,
59
)
610

711
from eth._warnings import catch_and_ignore_import_warning
@@ -184,3 +188,13 @@
184188
DEFAULT_SPOOF_Y_PARITY = 1
185189
DEFAULT_SPOOF_R = 1
186190
DEFAULT_SPOOF_S = 1
191+
192+
193+
#
194+
# Merge / EIP-3675 constants
195+
#
196+
POST_MERGE_OMMERS_HASH = EMPTY_UNCLE_HASH
197+
POST_MERGE_DIFFICULTY = 0
198+
POST_MERGE_MIX_HASH = ZERO_HASH32
199+
POST_MERGE_NONCE = b"\x00\x00\x00\x00\x00\x00\x00\x00"
200+
POST_MERGE_OMMERS: List[Optional[Hash32]] = []

eth/tools/builder/chain/builders.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
LondonVM,
7777
ArrowGlacierVM,
7878
GrayGlacierVM,
79+
ParisVM,
7980
)
8081

8182

@@ -248,8 +249,9 @@ def dao_fork_at(dao_fork_block_number: BlockNumber,
248249
london_at = fork_at(LondonVM)
249250
arrow_glacier_at = fork_at(ArrowGlacierVM)
250251
gray_glacier_at = fork_at(GrayGlacierVM)
252+
paris_at = fork_at(ParisVM)
251253

252-
latest_mainnet_at = gray_glacier_at
254+
latest_mainnet_at = paris_at
253255

254256
GENESIS_DEFAULTS = cast(
255257
Tuple[Tuple[str, Union[BlockNumber, int, None, bytes, Address, Hash32]], ...],

eth/vm/base.py

+18-12
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,13 @@ class VM(Configurable, VirtualMachineAPI):
9797

9898
cls_logger = logging.getLogger('eth.vm.base.VM')
9999

100-
def __init__(self,
101-
header: BlockHeaderAPI,
102-
chaindb: ChainDatabaseAPI,
103-
chain_context: ChainContextAPI,
104-
consensus_context: ConsensusContextAPI) -> None:
100+
def __init__(
101+
self,
102+
header: BlockHeaderAPI,
103+
chaindb: ChainDatabaseAPI,
104+
chain_context: ChainContextAPI,
105+
consensus_context: ConsensusContextAPI
106+
) -> None:
105107
self.chaindb = chaindb
106108
self.chain_context = chain_context
107109
self.consensus_context = consensus_context
@@ -168,10 +170,12 @@ def apply_transaction(self,
168170
return receipt, computation
169171

170172
@classmethod
171-
def create_execution_context(cls,
172-
header: BlockHeaderAPI,
173-
prev_hashes: Iterable[Hash32],
174-
chain_context: ChainContextAPI) -> ExecutionContextAPI:
173+
def create_execution_context(
174+
cls,
175+
header: BlockHeaderAPI,
176+
prev_hashes: Iterable[Hash32],
177+
chain_context: ChainContextAPI
178+
) -> ExecutionContextAPI:
175179
fee_recipient = cls.consensus_class.get_fee_recipient(header)
176180
try:
177181
base_fee = header.base_fee_per_gas
@@ -181,6 +185,7 @@ def create_execution_context(cls,
181185
timestamp=header.timestamp,
182186
block_number=header.block_number,
183187
difficulty=header.difficulty,
188+
mix_hash=header.mix_hash,
184189
gas_limit=header.gas_limit,
185190
prev_hashes=prev_hashes,
186191
chain_id=chain_context.chain_id,
@@ -191,6 +196,7 @@ def create_execution_context(cls,
191196
timestamp=header.timestamp,
192197
block_number=header.block_number,
193198
difficulty=header.difficulty,
199+
mix_hash=header.mix_hash,
194200
gas_limit=header.gas_limit,
195201
prev_hashes=prev_hashes,
196202
chain_id=chain_context.chain_id,
@@ -283,7 +289,7 @@ def apply_all_transactions(
283289
return result_header, receipts_tuple, computations_tuple
284290

285291
#
286-
# Mining
292+
# Importing blocks
287293
#
288294
def import_block(self, block: BlockAPI) -> BlockAndMetaWitness:
289295
if self.get_block().number != block.number:
@@ -307,7 +313,8 @@ def import_block(self, block: BlockAPI) -> BlockAndMetaWitness:
307313
)
308314

309315
execution_context = self.create_execution_context(
310-
block.header, self.previous_hashes, self.chain_context)
316+
block.header, self.previous_hashes, self.chain_context
317+
)
311318

312319
# Zero out the gas_used before applying transactions. Each applied transaction will
313320
# increase the gas used in the final new_header.
@@ -329,7 +336,6 @@ def import_block(self, block: BlockAPI) -> BlockAndMetaWitness:
329336

330337
def mine_block(self, block: BlockAPI, *args: Any, **kwargs: Any) -> BlockAndMetaWitness:
331338
packed_block = self.pack_block(block, *args, **kwargs)
332-
333339
block_result = self.finalize_block(packed_block)
334340

335341
# Perform validation

eth/vm/execution_context.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,32 @@
1515

1616
class ExecutionContext(ExecutionContextAPI):
1717
_coinbase = None
18-
1918
_timestamp = None
2019
_number = None
2120
_difficulty = None
21+
_mix_hash = None
2222
_gas_limit = None
2323
_prev_hashes = None
2424
_chain_id = None
2525
_base_fee_per_gas = None
2626

2727
def __init__(
28-
self,
29-
coinbase: Address,
30-
timestamp: int,
31-
block_number: BlockNumber,
32-
difficulty: int,
33-
gas_limit: int,
34-
prev_hashes: Iterable[Hash32],
35-
chain_id: int,
36-
base_fee_per_gas: Optional[int] = None) -> None:
28+
self,
29+
coinbase: Address,
30+
timestamp: int,
31+
block_number: BlockNumber,
32+
difficulty: int,
33+
mix_hash: Hash32,
34+
gas_limit: int,
35+
prev_hashes: Iterable[Hash32],
36+
chain_id: int,
37+
base_fee_per_gas: Optional[int] = None
38+
) -> None:
3739
self._coinbase = coinbase
3840
self._timestamp = timestamp
3941
self._block_number = block_number
4042
self._difficulty = difficulty
43+
self._mix_hash = mix_hash
4144
self._gas_limit = gas_limit
4245
self._prev_hashes = CachedIterable(prev_hashes)
4346
self._chain_id = chain_id
@@ -59,6 +62,10 @@ def block_number(self) -> BlockNumber:
5962
def difficulty(self) -> int:
6063
return self._difficulty
6164

65+
@property
66+
def mix_hash(self) -> Hash32:
67+
return self._mix_hash
68+
6269
@property
6370
def gas_limit(self) -> int:
6471
return self._gas_limit

eth/vm/forks/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@
3737
from .gray_glacier import ( # noqa: F401
3838
GrayGlacierVM,
3939
)
40+
from .paris import ( # noqa: F401
41+
ParisVM,
42+
)

eth/vm/forks/byzantium/headers.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ def configure_header(difficulty_fn: Callable[[BlockHeaderAPI, int], int],
9898
validate_header_params_for_configuration(header_params)
9999

100100
with vm.get_header().build_changeset(**header_params) as changeset:
101-
if 'timestamp' in header_params and changeset.block_number > 0:
101+
if (
102+
'timestamp' in header_params
103+
and changeset.block_number > 0
104+
105+
# check that we are pre-PoS and not using a constant for the difficulty
106+
and not isinstance(difficulty_fn, int)
107+
):
102108
parent_header = get_parent_header(changeset.build_rlp(), vm.chaindb)
103109
changeset.difficulty = difficulty_fn(
104110
parent_header,

eth/vm/forks/frontier/state.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def finalize_computation(self,
178178

179179
self.vm_state.delta_balance(computation.msg.sender, gas_refund_amount)
180180

181-
# Miner Fees
181+
# Beneficiary Fees
182182
gas_used = transaction.gas - gas_remaining - gas_refund
183183
transaction_fee = gas_used * self.vm_state.get_tip(transaction)
184184
self.vm_state.logger.debug2(
@@ -190,13 +190,7 @@ def finalize_computation(self,
190190

191191
# Process Self Destructs
192192
for account, _ in computation.get_accounts_for_deletion():
193-
# TODO: need to figure out how we prevent multiple selfdestructs from
194-
# the same account and if this is the right place to put this.
195193
self.vm_state.logger.debug2('DELETING ACCOUNT: %s', encode_hex(account))
196-
197-
# TODO: this balance setting is likely superflous and can be
198-
# removed since `delete_account` does this.
199-
self.vm_state.set_balance(account, 0)
200194
self.vm_state.delete_account(account)
201195

202196
return computation

eth/vm/forks/paris/__init__.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import (
2+
Type,
3+
)
4+
5+
from eth.abc import BlockAPI, BlockHeaderAPI
6+
from eth.constants import (
7+
POST_MERGE_DIFFICULTY,
8+
POST_MERGE_NONCE,
9+
POST_MERGE_OMMERS_HASH,
10+
)
11+
from eth.rlp.blocks import BaseBlock
12+
from eth.vm.forks.gray_glacier import GrayGlacierVM
13+
from eth.vm.state import BaseState
14+
from eth_utils import (
15+
ValidationError,
16+
)
17+
18+
from .blocks import ParisBlock
19+
from .headers import (
20+
configure_paris_header,
21+
create_paris_header_from_parent,
22+
)
23+
from .state import ParisState
24+
25+
26+
class ParisVM(GrayGlacierVM):
27+
# fork name
28+
fork = 'paris'
29+
30+
# classes
31+
block_class: Type[BaseBlock] = ParisBlock
32+
_state_class: Type[BaseState] = ParisState
33+
34+
# Methods
35+
create_header_from_parent = staticmethod( # type: ignore
36+
create_paris_header_from_parent(POST_MERGE_DIFFICULTY)
37+
)
38+
configure_header = configure_paris_header
39+
40+
@staticmethod
41+
def get_block_reward() -> int:
42+
return 0
43+
44+
@classmethod
45+
def get_nephew_reward(cls) -> int:
46+
return 0
47+
48+
def _assign_block_rewards(self, block: BlockAPI) -> None:
49+
# No block reward or uncles / uncle rewards in PoS
50+
pass
51+
52+
@classmethod
53+
def validate_header(
54+
cls,
55+
header: BlockHeaderAPI,
56+
parent_header: BlockHeaderAPI
57+
) -> None:
58+
super().validate_header(header, parent_header)
59+
60+
difficulty, nonce, uncles_hash = (
61+
header.difficulty, header.nonce, header.uncles_hash
62+
)
63+
64+
if difficulty != POST_MERGE_DIFFICULTY:
65+
raise ValidationError(
66+
f"Difficulty must be {POST_MERGE_DIFFICULTY}, got {difficulty}."
67+
)
68+
if nonce != POST_MERGE_NONCE:
69+
raise ValidationError(
70+
f"Nonce must be {POST_MERGE_NONCE !r}, got {nonce !r}."
71+
)
72+
if uncles_hash != POST_MERGE_OMMERS_HASH:
73+
raise ValidationError(
74+
f"Uncles hash must be {POST_MERGE_OMMERS_HASH !r}, got {uncles_hash !r}."
75+
)

0 commit comments

Comments
 (0)