Skip to content

Commit 0f4914a

Browse files
authored
feat: use SealedHeader in ChainSpec (#14514)
1 parent 8123d6b commit 0f4914a

File tree

18 files changed

+185
-190
lines changed

18 files changed

+185
-190
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/chainspec/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ pub use info::ChainInfo;
3030
#[cfg(any(test, feature = "test-utils"))]
3131
pub use spec::test_fork_ids;
3232
pub use spec::{
33-
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, ChainSpecProvider,
34-
DepositContract, ForkBaseFeeParams, HardforkBlobParams, DEV, HOLESKY, MAINNET, SEPOLIA,
33+
make_genesis_header, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,
34+
ChainSpecProvider, DepositContract, ForkBaseFeeParams, HardforkBlobParams, DEV, HOLESKY,
35+
MAINNET, SEPOLIA,
3536
};
3637

3738
use reth_primitives_traits::sync::OnceLock;

crates/chainspec/src/spec.rs

Lines changed: 103 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub use alloy_eips::eip1559::BaseFeeParams;
22

3-
use crate::{constants::MAINNET_DEPOSIT_CONTRACT, once_cell_set, EthChainSpec};
3+
use crate::{constants::MAINNET_DEPOSIT_CONTRACT, EthChainSpec};
44
use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec};
55
use alloy_chains::{Chain, NamedChain};
66
use alloy_consensus::{
@@ -26,25 +26,79 @@ use reth_network_peers::{
2626
base_nodes, base_testnet_nodes, holesky_nodes, mainnet_nodes, op_nodes, op_testnet_nodes,
2727
sepolia_nodes, NodeRecord,
2828
};
29-
use reth_primitives_traits::{
30-
sync::{LazyLock, OnceLock},
31-
SealedHeader,
32-
};
29+
use reth_primitives_traits::{sync::LazyLock, SealedHeader};
30+
31+
/// Helper method building a [`Header`] given [`Genesis`] and [`ChainHardforks`].
32+
pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Header {
33+
// If London is activated at genesis, we set the initial base fee as per EIP-1559.
34+
let base_fee_per_gas = hardforks
35+
.fork(EthereumHardfork::London)
36+
.active_at_block(0)
37+
.then(|| genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE));
38+
39+
// If shanghai is activated, initialize the header with an empty withdrawals hash, and
40+
// empty withdrawals list.
41+
let withdrawals_root = hardforks
42+
.fork(EthereumHardfork::Shanghai)
43+
.active_at_timestamp(genesis.timestamp)
44+
.then_some(EMPTY_WITHDRAWALS);
45+
46+
// If Cancun is activated at genesis, we set:
47+
// * parent beacon block root to 0x0
48+
// * blob gas used to provided genesis or 0x0
49+
// * excess blob gas to provided genesis or 0x0
50+
let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
51+
if hardforks.fork(EthereumHardfork::Cancun).active_at_timestamp(genesis.timestamp) {
52+
let blob_gas_used = genesis.blob_gas_used.unwrap_or(0);
53+
let excess_blob_gas = genesis.excess_blob_gas.unwrap_or(0);
54+
(Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
55+
} else {
56+
(None, None, None)
57+
};
58+
59+
// If Prague is activated at genesis we set requests root to an empty trie root.
60+
let requests_hash = hardforks
61+
.fork(EthereumHardfork::Prague)
62+
.active_at_timestamp(genesis.timestamp)
63+
.then_some(EMPTY_REQUESTS_HASH);
64+
65+
Header {
66+
gas_limit: genesis.gas_limit,
67+
difficulty: genesis.difficulty,
68+
nonce: genesis.nonce.into(),
69+
extra_data: genesis.extra_data.clone(),
70+
state_root: state_root_ref_unhashed(&genesis.alloc),
71+
timestamp: genesis.timestamp,
72+
mix_hash: genesis.mix_hash,
73+
beneficiary: genesis.coinbase,
74+
base_fee_per_gas,
75+
withdrawals_root,
76+
parent_beacon_block_root,
77+
blob_gas_used,
78+
excess_blob_gas,
79+
requests_hash,
80+
..Default::default()
81+
}
82+
}
3383

3484
/// The Ethereum mainnet spec
3585
pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
86+
let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
87+
.expect("Can't deserialize Mainnet genesis json");
88+
let hardforks = EthereumHardfork::mainnet().into();
3689
let mut spec = ChainSpec {
3790
chain: Chain::mainnet(),
38-
genesis: serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
39-
.expect("Can't deserialize Mainnet genesis json"),
40-
genesis_hash: once_cell_set(MAINNET_GENESIS_HASH),
41-
genesis_header: Default::default(),
91+
genesis_header: SealedHeader::new(
92+
make_genesis_header(&genesis, &hardforks),
93+
MAINNET_GENESIS_HASH,
94+
),
95+
genesis,
4296
// <https://etherscan.io/block/15537394>
4397
paris_block_and_final_difficulty: Some((
4498
15537394,
4599
U256::from(58_750_003_716_598_352_816_469u128),
46100
)),
47-
hardforks: EthereumHardfork::mainnet().into(),
101+
hardforks,
48102
// https://etherscan.io/tx/0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0
49103
deposit_contract: Some(DepositContract::new(
50104
MAINNET_DEPOSIT_CONTRACT_ADDRESS,
@@ -61,15 +115,19 @@ pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
61115

62116
/// The Sepolia spec
63117
pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
118+
let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
119+
.expect("Can't deserialize Sepolia genesis json");
120+
let hardforks = EthereumHardfork::sepolia().into();
64121
let mut spec = ChainSpec {
65122
chain: Chain::sepolia(),
66-
genesis: serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
67-
.expect("Can't deserialize Sepolia genesis json"),
68-
genesis_hash: once_cell_set(SEPOLIA_GENESIS_HASH),
69-
genesis_header: Default::default(),
123+
genesis_header: SealedHeader::new(
124+
make_genesis_header(&genesis, &hardforks),
125+
SEPOLIA_GENESIS_HASH,
126+
),
127+
genesis,
70128
// <https://sepolia.etherscan.io/block/1450409>
71129
paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))),
72-
hardforks: EthereumHardfork::sepolia().into(),
130+
hardforks,
73131
// https://sepolia.etherscan.io/tx/0x025ecbf81a2f1220da6285d1701dc89fb5a956b62562ee922e1a9efd73eb4b14
74132
deposit_contract: Some(DepositContract::new(
75133
address!("7f02c3e3c98b133055b8b348b2ac625669ed295d"),
@@ -86,14 +144,18 @@ pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
86144

87145
/// The Holesky spec
88146
pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
147+
let genesis = serde_json::from_str(include_str!("../res/genesis/holesky.json"))
148+
.expect("Can't deserialize Holesky genesis json");
149+
let hardforks = EthereumHardfork::holesky().into();
89150
let mut spec = ChainSpec {
90151
chain: Chain::holesky(),
91-
genesis: serde_json::from_str(include_str!("../res/genesis/holesky.json"))
92-
.expect("Can't deserialize Holesky genesis json"),
93-
genesis_hash: once_cell_set(HOLESKY_GENESIS_HASH),
94-
genesis_header: Default::default(),
152+
genesis_header: SealedHeader::new(
153+
make_genesis_header(&genesis, &hardforks),
154+
HOLESKY_GENESIS_HASH,
155+
),
156+
genesis,
95157
paris_block_and_final_difficulty: Some((0, U256::from(1))),
96-
hardforks: EthereumHardfork::holesky().into(),
158+
hardforks,
97159
deposit_contract: Some(DepositContract::new(
98160
address!("4242424242424242424242424242424242424242"),
99161
0,
@@ -112,11 +174,16 @@ pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
112174
/// Includes 20 prefunded accounts with `10_000` ETH each derived from mnemonic "test test test test
113175
/// test test test test test test test junk".
114176
pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
177+
let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json"))
178+
.expect("Can't deserialize Dev testnet genesis json");
179+
let hardforks = DEV_HARDFORKS.clone();
115180
ChainSpec {
116181
chain: Chain::dev(),
117-
genesis: serde_json::from_str(include_str!("../res/genesis/dev.json"))
118-
.expect("Can't deserialize Dev testnet genesis json"),
119-
genesis_hash: once_cell_set(DEV_GENESIS_HASH),
182+
genesis_header: SealedHeader::new(
183+
make_genesis_header(&genesis, &hardforks),
184+
DEV_GENESIS_HASH,
185+
),
186+
genesis,
120187
paris_block_and_final_difficulty: Some((0, U256::from(0))),
121188
hardforks: DEV_HARDFORKS.clone(),
122189
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
@@ -220,17 +287,8 @@ pub struct ChainSpec {
220287
/// The genesis block.
221288
pub genesis: Genesis,
222289

223-
/// The hash of the genesis block.
224-
///
225-
/// This is either stored at construction time if it is known using [`once_cell_set`], or
226-
/// computed once on the first access.
227-
pub genesis_hash: OnceLock<B256>,
228-
229290
/// The header corresponding to the genesis block.
230-
///
231-
/// This is either stored at construction time if it is known using [`once_cell_set`], or
232-
/// computed once on the first access.
233-
pub genesis_header: OnceLock<Header>,
291+
pub genesis_header: SealedHeader,
234292

235293
/// The block at which [`EthereumHardfork::Paris`] was activated and the final difficulty at
236294
/// this block.
@@ -256,7 +314,6 @@ impl Default for ChainSpec {
256314
fn default() -> Self {
257315
Self {
258316
chain: Default::default(),
259-
genesis_hash: Default::default(),
260317
genesis: Default::default(),
261318
genesis_header: Default::default(),
262319
paris_block_and_final_difficulty: Default::default(),
@@ -307,55 +364,7 @@ impl ChainSpec {
307364

308365
/// Get the header for the genesis block.
309366
pub fn genesis_header(&self) -> &Header {
310-
self.genesis_header.get_or_init(|| self.make_genesis_header())
311-
}
312-
313-
fn make_genesis_header(&self) -> Header {
314-
// If London is activated at genesis, we set the initial base fee as per EIP-1559.
315-
let base_fee_per_gas = self.initial_base_fee();
316-
317-
// If shanghai is activated, initialize the header with an empty withdrawals hash, and
318-
// empty withdrawals list.
319-
let withdrawals_root = self
320-
.fork(EthereumHardfork::Shanghai)
321-
.active_at_timestamp(self.genesis.timestamp)
322-
.then_some(EMPTY_WITHDRAWALS);
323-
324-
// If Cancun is activated at genesis, we set:
325-
// * parent beacon block root to 0x0
326-
// * blob gas used to provided genesis or 0x0
327-
// * excess blob gas to provided genesis or 0x0
328-
let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
329-
if self.is_cancun_active_at_timestamp(self.genesis.timestamp) {
330-
let blob_gas_used = self.genesis.blob_gas_used.unwrap_or(0);
331-
let excess_blob_gas = self.genesis.excess_blob_gas.unwrap_or(0);
332-
(Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
333-
} else {
334-
(None, None, None)
335-
};
336-
337-
// If Prague is activated at genesis we set requests root to an empty trie root.
338-
let requests_hash = self
339-
.is_prague_active_at_timestamp(self.genesis.timestamp)
340-
.then_some(EMPTY_REQUESTS_HASH);
341-
342-
Header {
343-
gas_limit: self.genesis.gas_limit,
344-
difficulty: self.genesis.difficulty,
345-
nonce: self.genesis.nonce.into(),
346-
extra_data: self.genesis.extra_data.clone(),
347-
state_root: state_root_ref_unhashed(&self.genesis.alloc),
348-
timestamp: self.genesis.timestamp,
349-
mix_hash: self.genesis.mix_hash,
350-
beneficiary: self.genesis.coinbase,
351-
base_fee_per_gas,
352-
withdrawals_root,
353-
parent_beacon_block_root,
354-
blob_gas_used,
355-
excess_blob_gas,
356-
requests_hash,
357-
..Default::default()
358-
}
367+
&self.genesis_header
359368
}
360369

361370
/// Get the sealed header for the genesis block.
@@ -413,7 +422,7 @@ impl ChainSpec {
413422

414423
/// Get the hash of the genesis block.
415424
pub fn genesis_hash(&self) -> B256 {
416-
*self.genesis_hash.get_or_init(|| self.genesis_header().hash_slow())
425+
self.genesis_header.hash()
417426
}
418427

419428
/// Get the timestamp of the genesis block.
@@ -728,11 +737,13 @@ impl From<Genesis> for ChainSpec {
728737
DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
729738
});
730739

740+
let hardforks = ChainHardforks::new(ordered_hardforks);
741+
731742
Self {
732743
chain: genesis.config.chain_id.into(),
744+
genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis, &hardforks)),
733745
genesis,
734-
genesis_hash: OnceLock::new(),
735-
hardforks: ChainHardforks::new(ordered_hardforks),
746+
hardforks,
736747
paris_block_and_final_difficulty,
737748
deposit_contract,
738749
blob_params,
@@ -974,10 +985,14 @@ impl ChainSpecBuilder {
974985
}
975986
})
976987
};
988+
let genesis = self.genesis.expect("The genesis is required");
977989
ChainSpec {
978990
chain: self.chain.expect("The chain is required"),
979-
genesis: self.genesis.expect("The genesis is required"),
980-
genesis_hash: OnceLock::new(),
991+
genesis_header: SealedHeader::new_unhashed(make_genesis_header(
992+
&genesis,
993+
&self.hardforks,
994+
)),
995+
genesis,
981996
hardforks: self.hardforks,
982997
paris_block_and_final_difficulty,
983998
deposit_contract: None,
@@ -1930,7 +1945,6 @@ Post-merge hard forks (timestamp based):
19301945
assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
19311946
}
19321947

1933-
assert_eq!(chainspec.genesis_hash.get(), None);
19341948
let expected_state_root: B256 =
19351949
hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into();
19361950
assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
@@ -2003,7 +2017,6 @@ Post-merge hard forks (timestamp based):
20032017

20042018
let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
20052019
let chainspec: ChainSpec = genesis.into();
2006-
assert_eq!(chainspec.genesis_hash.get(), None);
20072020
assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
20082021
let expected_state_root: B256 =
20092022
hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
@@ -2318,7 +2331,6 @@ Post-merge hard forks (timestamp based):
23182331
let spec = ChainSpec {
23192332
chain: Chain::mainnet(),
23202333
genesis: Genesis::default(),
2321-
genesis_hash: OnceLock::new(),
23222334
hardforks: ChainHardforks::new(vec![(
23232335
EthereumHardfork::Frontier.boxed(),
23242336
ForkCondition::Never,
@@ -2336,7 +2348,6 @@ Post-merge hard forks (timestamp based):
23362348
let spec = ChainSpec {
23372349
chain: Chain::mainnet(),
23382350
genesis: Genesis::default(),
2339-
genesis_hash: OnceLock::new(),
23402351
hardforks: ChainHardforks::new(vec![(
23412352
EthereumHardfork::Shanghai.boxed(),
23422353
ForkCondition::Never,

crates/ethereum/cli/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ mod test {
2323
let cmd: NodeCommand = NodeCommand::parse_from(["reth", "--dev"]);
2424
let chain = DEV.clone();
2525
assert_eq!(cmd.chain.chain, chain.chain);
26-
assert_eq!(cmd.chain.genesis_hash, chain.genesis_hash);
26+
assert_eq!(cmd.chain.genesis_hash(), chain.genesis_hash());
2727
assert_eq!(
2828
cmd.chain.paris_block_and_final_difficulty,
2929
chain.paris_block_and_final_difficulty

crates/optimism/chainspec/src/base.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,28 @@ use alloc::{sync::Arc, vec};
44

55
use alloy_chains::Chain;
66
use alloy_primitives::{b256, U256};
7-
use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec};
7+
use reth_chainspec::{make_genesis_header, BaseFeeParams, BaseFeeParamsKind, ChainSpec};
88
use reth_ethereum_forks::{EthereumHardfork, Hardfork};
99
use reth_optimism_forks::OpHardfork;
10+
use reth_primitives_traits::SealedHeader;
1011

1112
use crate::{LazyLock, OpChainSpec};
1213

1314
/// The Base mainnet spec
1415
pub static BASE_MAINNET: LazyLock<Arc<OpChainSpec>> = LazyLock::new(|| {
16+
let genesis = serde_json::from_str(include_str!("../res/genesis/base.json"))
17+
.expect("Can't deserialize Base genesis json");
18+
let hardforks = OpHardfork::base_mainnet();
1519
OpChainSpec {
1620
inner: ChainSpec {
1721
chain: Chain::base_mainnet(),
18-
genesis: serde_json::from_str(include_str!("../res/genesis/base.json"))
19-
.expect("Can't deserialize Base genesis json"),
20-
genesis_hash: once_cell_set(b256!(
21-
"f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"
22-
)),
22+
genesis_header: SealedHeader::new(
23+
make_genesis_header(&genesis, &hardforks),
24+
b256!("f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"),
25+
),
26+
genesis,
2327
paris_block_and_final_difficulty: Some((0, U256::from(0))),
24-
hardforks: OpHardfork::base_mainnet(),
28+
hardforks,
2529
base_fee_params: BaseFeeParamsKind::Variable(
2630
vec![
2731
(EthereumHardfork::London.boxed(), BaseFeeParams::optimism()),

0 commit comments

Comments
 (0)