alloy_genesis/
lib.rs

1//! Alloy genesis types
2
3#![doc = include_str!(".././README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
6    html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14use alloc::{collections::BTreeMap, string::String};
15use alloy_eips::{eip7840::BlobParams, BlobScheduleBlobParams};
16use alloy_primitives::{keccak256, Address, Bytes, B256, U256};
17use alloy_serde::{storage::deserialize_storage_map, ttd::deserialize_json_ttd_opt, OtherFields};
18use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH, KECCAK_EMPTY};
19use core::str::FromStr;
20use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
21
22/// The genesis block specification.
23#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "camelCase", default)]
25pub struct Genesis {
26    /// The fork configuration for this network.
27    #[serde(default)]
28    pub config: ChainConfig,
29    /// The genesis header nonce.
30    #[serde(with = "alloy_serde::quantity")]
31    pub nonce: u64,
32    /// The genesis header timestamp.
33    #[serde(with = "alloy_serde::quantity")]
34    pub timestamp: u64,
35    /// The genesis header extra data.
36    pub extra_data: Bytes,
37    /// The genesis header gas limit.
38    #[serde(with = "alloy_serde::quantity")]
39    pub gas_limit: u64,
40    /// The genesis header difficulty.
41    pub difficulty: U256,
42    /// The genesis header mix hash.
43    pub mix_hash: B256,
44    /// The genesis header coinbase address.
45    pub coinbase: Address,
46    /// The initial state of accounts in the genesis block.
47    pub alloc: BTreeMap<Address, GenesisAccount>,
48    // NOTE: the following fields:
49    // * base_fee_per_gas
50    // * excess_blob_gas
51    // * blob_gas_used
52    // * number
53    // should NOT be set in a real genesis file, but are included here for compatibility with
54    // consensus tests, which have genesis files with these fields populated.
55    /// The genesis header base fee
56    #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")]
57    pub base_fee_per_gas: Option<u128>,
58    /// The genesis header excess blob gas
59    #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")]
60    pub excess_blob_gas: Option<u64>,
61    /// The genesis header blob gas used
62    #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")]
63    pub blob_gas_used: Option<u64>,
64    /// The genesis block number
65    #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")]
66    pub number: Option<u64>,
67}
68
69impl Genesis {
70    /// Creates a chain config for Clique using the given chain id and funds the given address with
71    /// max coins.
72    ///
73    /// Enables all hard forks up to London at genesis.
74    pub fn clique_genesis(chain_id: u64, signer_addr: Address) -> Self {
75        // set up a clique config with an instant sealing period and short (8 block) epoch
76        let clique_config = CliqueConfig { period: Some(0), epoch: Some(8) };
77
78        let config = ChainConfig {
79            chain_id,
80            eip155_block: Some(0),
81            eip150_block: Some(0),
82            eip158_block: Some(0),
83
84            homestead_block: Some(0),
85            byzantium_block: Some(0),
86            constantinople_block: Some(0),
87            petersburg_block: Some(0),
88            istanbul_block: Some(0),
89            muir_glacier_block: Some(0),
90            berlin_block: Some(0),
91            london_block: Some(0),
92            clique: Some(clique_config),
93            ..Default::default()
94        };
95
96        // fund account
97        let alloc = BTreeMap::from([(
98            signer_addr,
99            GenesisAccount { balance: U256::MAX, ..Default::default() },
100        )]);
101
102        // put signer address in the extra data, padded by the required amount of zeros
103        // Clique issue: https://github.com/ethereum/EIPs/issues/225
104        // Clique EIP: https://eips.ethereum.org/EIPS/eip-225
105        //
106        // The first 32 bytes are vanity data, so we will populate it with zeros
107        // This is followed by the signer address, which is 20 bytes
108        // There are 65 bytes of zeros after the signer address, which is usually populated with the
109        // proposer signature. Because the genesis does not have a proposer signature, it will be
110        // populated with zeros.
111        let extra_data_bytes = [&[0u8; 32][..], signer_addr.as_slice(), &[0u8; 65][..]].concat();
112        let extra_data = extra_data_bytes.into();
113
114        Self {
115            config,
116            alloc,
117            difficulty: U256::from(1),
118            gas_limit: 5_000_000,
119            extra_data,
120            ..Default::default()
121        }
122    }
123
124    /// Set the nonce.
125    pub const fn with_nonce(mut self, nonce: u64) -> Self {
126        self.nonce = nonce;
127        self
128    }
129
130    /// Set the timestamp.
131    pub const fn with_timestamp(mut self, timestamp: u64) -> Self {
132        self.timestamp = timestamp;
133        self
134    }
135
136    /// Set the extra data.
137    pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
138        self.extra_data = extra_data;
139        self
140    }
141
142    /// Set the gas limit.
143    pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self {
144        self.gas_limit = gas_limit;
145        self
146    }
147
148    /// Set the difficulty.
149    pub const fn with_difficulty(mut self, difficulty: U256) -> Self {
150        self.difficulty = difficulty;
151        self
152    }
153
154    /// Set the mix hash of the header.
155    pub const fn with_mix_hash(mut self, mix_hash: B256) -> Self {
156        self.mix_hash = mix_hash;
157        self
158    }
159
160    /// Set the coinbase address.
161    pub const fn with_coinbase(mut self, address: Address) -> Self {
162        self.coinbase = address;
163        self
164    }
165
166    /// Set the base fee.
167    pub const fn with_base_fee(mut self, base_fee: Option<u128>) -> Self {
168        self.base_fee_per_gas = base_fee;
169        self
170    }
171
172    /// Set the excess blob gas.
173    pub const fn with_excess_blob_gas(mut self, excess_blob_gas: Option<u64>) -> Self {
174        self.excess_blob_gas = excess_blob_gas;
175        self
176    }
177
178    /// Set the blob gas used.
179    pub const fn with_blob_gas_used(mut self, blob_gas_used: Option<u64>) -> Self {
180        self.blob_gas_used = blob_gas_used;
181        self
182    }
183
184    /// Add accounts to the genesis block. If the address is already present,
185    /// the account is updated.
186    pub fn extend_accounts(
187        mut self,
188        accounts: impl IntoIterator<Item = (Address, GenesisAccount)>,
189    ) -> Self {
190        self.alloc.extend(accounts);
191        self
192    }
193}
194
195/// An account in the state of the genesis block.
196#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
197#[serde(deny_unknown_fields)]
198pub struct GenesisAccount {
199    /// The nonce of the account at genesis.
200    #[serde(skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt", default)]
201    pub nonce: Option<u64>,
202    /// The balance of the account at genesis.
203    pub balance: U256,
204    /// The account's bytecode at genesis.
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub code: Option<Bytes>,
207    /// The account's storage at genesis.
208    #[serde(
209        default,
210        skip_serializing_if = "Option::is_none",
211        deserialize_with = "deserialize_storage_map"
212    )]
213    pub storage: Option<BTreeMap<B256, B256>>,
214    /// The account's private key. Should only be used for testing.
215    #[serde(
216        rename = "secretKey",
217        default,
218        skip_serializing_if = "Option::is_none",
219        deserialize_with = "deserialize_private_key"
220    )]
221    pub private_key: Option<B256>,
222}
223
224impl GenesisAccount {
225    /// Set the nonce.
226    pub const fn with_nonce(mut self, nonce: Option<u64>) -> Self {
227        self.nonce = nonce;
228        self
229    }
230
231    /// Set the balance.
232    pub const fn with_balance(mut self, balance: U256) -> Self {
233        self.balance = balance;
234        self
235    }
236
237    /// Set the code.
238    pub fn with_code(mut self, code: Option<Bytes>) -> Self {
239        self.code = code;
240        self
241    }
242
243    /// Set the storage.
244    pub fn with_storage(mut self, storage: Option<BTreeMap<B256, B256>>) -> Self {
245        self.storage = storage;
246        self
247    }
248
249    /// Returns an iterator over the storage slots in (`B256`, `U256`) format.
250    pub fn storage_slots(&self) -> impl Iterator<Item = (B256, U256)> + '_ {
251        self.storage.as_ref().into_iter().flat_map(|storage| storage.iter()).map(|(key, value)| {
252            let value = U256::from_be_bytes(value.0);
253            (*key, value)
254        })
255    }
256
257    /// Convert the genesis account into the [`TrieAccount`] format.
258    pub fn into_trie_account(self) -> TrieAccount {
259        self.into()
260    }
261}
262
263impl From<GenesisAccount> for TrieAccount {
264    fn from(account: GenesisAccount) -> Self {
265        let storage_root = account
266            .storage
267            .map(|storage| {
268                alloy_trie::root::storage_root_unhashed(
269                    storage
270                        .into_iter()
271                        .filter(|(_, value)| !value.is_zero())
272                        .map(|(slot, value)| (slot, U256::from_be_bytes(*value))),
273                )
274            })
275            .unwrap_or(EMPTY_ROOT_HASH);
276
277        Self {
278            nonce: account.nonce.unwrap_or_default(),
279            balance: account.balance,
280            storage_root,
281            code_hash: account.code.map_or(KECCAK_EMPTY, keccak256),
282        }
283    }
284}
285
286/// Custom deserialization function for the private key.
287///
288/// This function allows the private key to be deserialized from a string or a `null` value.
289///
290/// We need a custom function here especially to handle the case where the private key is `0x` and
291/// should be deserialized as `None`.
292fn deserialize_private_key<'de, D>(deserializer: D) -> Result<Option<B256>, D::Error>
293where
294    D: Deserializer<'de>,
295{
296    let opt_str: Option<String> = Option::deserialize(deserializer)?;
297
298    if let Some(ref s) = opt_str {
299        if s == "0x" {
300            return Ok(None);
301        }
302        B256::from_str(s).map(Some).map_err(D::Error::custom)
303    } else {
304        Ok(None)
305    }
306}
307
308/// Defines core blockchain settings per block.
309///
310/// Tailors unique settings for each network based on its genesis block.
311///
312/// Governs crucial blockchain behavior and adaptability.
313///
314/// Encapsulates parameters shaping network evolution and behavior.
315///
316/// See [geth's `ChainConfig`
317/// struct](https://github.com/ethereum/go-ethereum/blob/v1.14.0/params/config.go#L326)
318/// for the source of each field.
319#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
320#[serde(default, rename_all = "camelCase")]
321pub struct ChainConfig {
322    /// The network's chain ID.
323    pub chain_id: u64,
324
325    /// The homestead switch block (None = no fork, 0 = already homestead).
326    #[serde(
327        skip_serializing_if = "Option::is_none",
328        deserialize_with = "alloy_serde::quantity::opt::deserialize"
329    )]
330    pub homestead_block: Option<u64>,
331
332    /// The DAO fork switch block (None = no fork).
333    #[serde(
334        skip_serializing_if = "Option::is_none",
335        deserialize_with = "alloy_serde::quantity::opt::deserialize"
336    )]
337    pub dao_fork_block: Option<u64>,
338
339    /// Whether or not the node supports the DAO hard-fork.
340    pub dao_fork_support: bool,
341
342    /// The [EIP-150](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md) hard fork block (None = no fork).
343    #[serde(
344        skip_serializing_if = "Option::is_none",
345        deserialize_with = "alloy_serde::quantity::opt::deserialize"
346    )]
347    pub eip150_block: Option<u64>,
348
349    /// The [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) hard fork block.
350    #[serde(
351        skip_serializing_if = "Option::is_none",
352        deserialize_with = "alloy_serde::quantity::opt::deserialize"
353    )]
354    pub eip155_block: Option<u64>,
355
356    /// The [EIP-158](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-158.md) hard fork block.
357    #[serde(
358        skip_serializing_if = "Option::is_none",
359        deserialize_with = "alloy_serde::quantity::opt::deserialize"
360    )]
361    pub eip158_block: Option<u64>,
362
363    /// The Byzantium hard fork block (None = no fork, 0 = already on byzantium).
364    #[serde(
365        skip_serializing_if = "Option::is_none",
366        deserialize_with = "alloy_serde::quantity::opt::deserialize"
367    )]
368    pub byzantium_block: Option<u64>,
369
370    /// The Constantinople hard fork block (None = no fork, 0 = already on constantinople).
371    #[serde(
372        skip_serializing_if = "Option::is_none",
373        deserialize_with = "alloy_serde::quantity::opt::deserialize"
374    )]
375    pub constantinople_block: Option<u64>,
376
377    /// The Petersburg hard fork block (None = no fork, 0 = already on petersburg).
378    #[serde(
379        skip_serializing_if = "Option::is_none",
380        deserialize_with = "alloy_serde::quantity::opt::deserialize"
381    )]
382    pub petersburg_block: Option<u64>,
383
384    /// The Istanbul hard fork block (None = no fork, 0 = already on istanbul).
385    #[serde(
386        skip_serializing_if = "Option::is_none",
387        deserialize_with = "alloy_serde::quantity::opt::deserialize"
388    )]
389    pub istanbul_block: Option<u64>,
390
391    /// The Muir Glacier hard fork block (None = no fork, 0 = already on muir glacier).
392    #[serde(
393        skip_serializing_if = "Option::is_none",
394        deserialize_with = "alloy_serde::quantity::opt::deserialize"
395    )]
396    pub muir_glacier_block: Option<u64>,
397
398    /// The Berlin hard fork block (None = no fork, 0 = already on berlin).
399    #[serde(
400        skip_serializing_if = "Option::is_none",
401        deserialize_with = "alloy_serde::quantity::opt::deserialize"
402    )]
403    pub berlin_block: Option<u64>,
404
405    /// The London hard fork block (None = no fork, 0 = already on london).
406    #[serde(
407        skip_serializing_if = "Option::is_none",
408        deserialize_with = "alloy_serde::quantity::opt::deserialize"
409    )]
410    pub london_block: Option<u64>,
411
412    /// The Arrow Glacier hard fork block (None = no fork, 0 = already on arrow glacier).
413    #[serde(
414        skip_serializing_if = "Option::is_none",
415        deserialize_with = "alloy_serde::quantity::opt::deserialize"
416    )]
417    pub arrow_glacier_block: Option<u64>,
418
419    /// The Gray Glacier hard fork block (None = no fork, 0 = already on gray glacier).
420    #[serde(
421        skip_serializing_if = "Option::is_none",
422        deserialize_with = "alloy_serde::quantity::opt::deserialize"
423    )]
424    pub gray_glacier_block: Option<u64>,
425
426    /// Virtual fork after the merge to use as a network splitter.
427    #[serde(
428        skip_serializing_if = "Option::is_none",
429        deserialize_with = "alloy_serde::quantity::opt::deserialize"
430    )]
431    pub merge_netsplit_block: Option<u64>,
432
433    /// Shanghai switch time (None = no fork, 0 = already on shanghai).
434    #[serde(
435        skip_serializing_if = "Option::is_none",
436        deserialize_with = "alloy_serde::quantity::opt::deserialize"
437    )]
438    pub shanghai_time: Option<u64>,
439
440    /// Cancun switch time (None = no fork, 0 = already on cancun).
441    #[serde(
442        skip_serializing_if = "Option::is_none",
443        deserialize_with = "alloy_serde::quantity::opt::deserialize"
444    )]
445    pub cancun_time: Option<u64>,
446
447    /// Prague switch time (None = no fork, 0 = already on prague).
448    #[serde(
449        skip_serializing_if = "Option::is_none",
450        deserialize_with = "alloy_serde::quantity::opt::deserialize"
451    )]
452    pub prague_time: Option<u64>,
453
454    /// Osaka switch time (None = no fork, 0 = already on osaka).
455    #[serde(
456        skip_serializing_if = "Option::is_none",
457        deserialize_with = "alloy_serde::quantity::opt::deserialize"
458    )]
459    pub osaka_time: Option<u64>,
460
461    /// Total difficulty reached that triggers the merge consensus upgrade.
462    #[serde(
463        skip_serializing_if = "Option::is_none",
464        deserialize_with = "deserialize_json_ttd_opt"
465    )]
466    pub terminal_total_difficulty: Option<U256>,
467
468    /// A flag specifying that the network already passed the terminal total difficulty. Its
469    /// purpose is to disable legacy sync without having seen the TTD locally.
470    pub terminal_total_difficulty_passed: bool,
471
472    /// Ethash parameters.
473    #[serde(skip_serializing_if = "Option::is_none")]
474    pub ethash: Option<EthashConfig>,
475
476    /// Clique parameters.
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub clique: Option<CliqueConfig>,
479
480    /// Parlia parameters.
481    #[serde(skip_serializing_if = "Option::is_none")]
482    pub parlia: Option<ParliaConfig>,
483
484    /// Additional fields specific to each chain.
485    #[serde(flatten, default)]
486    pub extra_fields: OtherFields,
487
488    /// The deposit contract address
489    #[serde(default, skip_serializing_if = "Option::is_none")]
490    pub deposit_contract_address: Option<Address>,
491
492    /// The blob schedule for the chain, indexed by hardfork name.
493    ///
494    /// See [EIP-7840](https://github.com/ethereum/EIPs/tree/master/EIPS/eip-7840.md).
495    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
496    pub blob_schedule: BTreeMap<String, BlobParams>,
497}
498
499impl ChainConfig {
500    /// Returns the [`BlobScheduleBlobParams`] from the configured blob schedule values.
501    pub fn blob_schedule_blob_params(&self) -> BlobScheduleBlobParams {
502        BlobScheduleBlobParams::from_schedule(&self.blob_schedule)
503    }
504
505    /// Checks if the blockchain is active at or after the Homestead fork block.
506    pub fn is_homestead_active_at_block(&self, block: u64) -> bool {
507        self.is_active_at_block(self.homestead_block, block)
508    }
509
510    /// Checks if the blockchain is active at or after the EIP150 fork block.
511    pub fn is_eip150_active_at_block(&self, block: u64) -> bool {
512        self.is_active_at_block(self.eip150_block, block)
513    }
514
515    /// Checks if the blockchain is active at or after the EIP155 fork block.
516    pub fn is_eip155_active_at_block(&self, block: u64) -> bool {
517        self.is_active_at_block(self.eip155_block, block)
518    }
519
520    /// Checks if the blockchain is active at or after the EIP158 fork block.
521    pub fn is_eip158_active_at_block(&self, block: u64) -> bool {
522        self.is_active_at_block(self.eip158_block, block)
523    }
524
525    /// Checks if the blockchain is active at or after the Byzantium fork block.
526    pub fn is_byzantium_active_at_block(&self, block: u64) -> bool {
527        self.is_active_at_block(self.byzantium_block, block)
528    }
529
530    /// Checks if the blockchain is active at or after the Constantinople fork block.
531    pub fn is_constantinople_active_at_block(&self, block: u64) -> bool {
532        self.is_active_at_block(self.constantinople_block, block)
533    }
534
535    /// Checks if the blockchain is active at or after the Muir Glacier (EIP-2384) fork block.
536    pub fn is_muir_glacier_active_at_block(&self, block: u64) -> bool {
537        self.is_active_at_block(self.muir_glacier_block, block)
538    }
539
540    /// Checks if the blockchain is active at or after the Petersburg fork block.
541    pub fn is_petersburg_active_at_block(&self, block: u64) -> bool {
542        self.is_active_at_block(self.petersburg_block, block)
543    }
544
545    /// Checks if the blockchain is active at or after the Istanbul fork block.
546    pub fn is_istanbul_active_at_block(&self, block: u64) -> bool {
547        self.is_active_at_block(self.istanbul_block, block)
548    }
549
550    /// Checks if the blockchain is active at or after the Berlin fork block.
551    pub fn is_berlin_active_at_block(&self, block: u64) -> bool {
552        self.is_active_at_block(self.berlin_block, block)
553    }
554
555    /// Checks if the blockchain is active at or after the London fork block.
556    pub fn is_london_active_at_block(&self, block: u64) -> bool {
557        self.is_active_at_block(self.london_block, block)
558    }
559
560    /// Checks if the blockchain is active at or after the Arrow Glacier (EIP-4345) fork block.
561    pub fn is_arrow_glacier_active_at_block(&self, block: u64) -> bool {
562        self.is_active_at_block(self.arrow_glacier_block, block)
563    }
564
565    /// Checks if the blockchain is active at or after the Gray Glacier (EIP-5133) fork block.
566    pub fn is_gray_glacier_active_at_block(&self, block: u64) -> bool {
567        self.is_active_at_block(self.gray_glacier_block, block)
568    }
569
570    /// Checks if the blockchain is active at or after the Shanghai fork block and the specified
571    /// timestamp.
572    pub fn is_shanghai_active_at_block_and_timestamp(&self, block: u64, timestamp: u64) -> bool {
573        self.is_london_active_at_block(block)
574            && self.is_active_at_timestamp(self.shanghai_time, timestamp)
575    }
576
577    /// Checks if the blockchain is active at or after the Cancun fork block and the specified
578    /// timestamp.
579    pub fn is_cancun_active_at_block_and_timestamp(&self, block: u64, timestamp: u64) -> bool {
580        self.is_london_active_at_block(block)
581            && self.is_active_at_timestamp(self.cancun_time, timestamp)
582    }
583
584    // Private function handling the comparison logic for block numbers
585    fn is_active_at_block(&self, config_block: Option<u64>, block: u64) -> bool {
586        config_block.is_some_and(|cb| cb <= block)
587    }
588
589    // Private function handling the comparison logic for timestamps
590    fn is_active_at_timestamp(&self, config_timestamp: Option<u64>, timestamp: u64) -> bool {
591        config_timestamp.is_some_and(|cb| cb <= timestamp)
592    }
593}
594
595impl Default for ChainConfig {
596    fn default() -> Self {
597        Self {
598            // mainnet
599            chain_id: 1,
600            homestead_block: None,
601            dao_fork_block: None,
602            dao_fork_support: false,
603            eip150_block: None,
604            eip155_block: None,
605            eip158_block: None,
606            byzantium_block: None,
607            constantinople_block: None,
608            petersburg_block: None,
609            istanbul_block: None,
610            muir_glacier_block: None,
611            berlin_block: None,
612            london_block: None,
613            arrow_glacier_block: None,
614            gray_glacier_block: None,
615            merge_netsplit_block: None,
616            shanghai_time: None,
617            cancun_time: None,
618            prague_time: None,
619            osaka_time: None,
620            terminal_total_difficulty: None,
621            terminal_total_difficulty_passed: false,
622            ethash: None,
623            clique: None,
624            parlia: None,
625            extra_fields: Default::default(),
626            deposit_contract_address: None,
627            blob_schedule: Default::default(),
628        }
629    }
630}
631
632/// Empty consensus configuration for proof-of-work networks.
633#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
634pub struct EthashConfig {}
635
636/// Consensus configuration for Clique.
637#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
638pub struct CliqueConfig {
639    /// Number of seconds between blocks to enforce.
640    #[serde(default, skip_serializing_if = "Option::is_none")]
641    pub period: Option<u64>,
642
643    /// Epoch length to reset votes and checkpoints.
644    #[serde(default, skip_serializing_if = "Option::is_none")]
645    pub epoch: Option<u64>,
646}
647
648/// Consensus configuration for Parlia.
649///
650/// Parlia is the consensus engine for BNB Smart Chain.
651/// For the general introduction: <https://docs.bnbchain.org/docs/learn/consensus/>
652/// For the specification: <https://github.com/bnb-chain/bsc/blob/master/params/config.go#L558>
653#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
654pub struct ParliaConfig {
655    /// Number of seconds between blocks to enforce.
656    #[serde(default, skip_serializing_if = "Option::is_none")]
657    pub period: Option<u64>,
658
659    /// Epoch length to update validator set.
660    #[serde(default, skip_serializing_if = "Option::is_none")]
661    pub epoch: Option<u64>,
662}
663
664#[cfg(test)]
665mod tests {
666    use super::*;
667    use alloc::{collections::BTreeMap, vec};
668    use alloy_primitives::{hex, Bytes};
669    use alloy_trie::{root::storage_root_unhashed, TrieAccount};
670    use core::str::FromStr;
671    use serde_json::json;
672
673    #[test]
674    fn genesis_defaults_config() {
675        let s = r#"{}"#;
676        let genesis: Genesis = serde_json::from_str(s).unwrap();
677        assert_eq!(genesis.config.chain_id, 1);
678    }
679
680    #[test]
681    fn test_genesis() {
682        let default_genesis = Genesis::default();
683
684        let nonce = 999;
685        let timestamp = 12345;
686        let extra_data = Bytes::from(b"extra-data");
687        let gas_limit = 333333;
688        let difficulty = U256::from(9000);
689        let mix_hash =
690            hex!("74385b512f1e0e47100907efe2b00ac78df26acba6dd16b0772923068a5801a8").into();
691        let coinbase = hex!("265873b6faf3258b3ab0827805386a2a20ed040e").into();
692        // create dummy account
693        let first_address: Address = hex!("7618a8c597b89e01c66a1f662078992c52a30c9a").into();
694        let mut account = BTreeMap::default();
695        account.insert(first_address, GenesisAccount::default());
696
697        // check values updated
698        let custom_genesis = Genesis::default()
699            .with_nonce(nonce)
700            .with_timestamp(timestamp)
701            .with_extra_data(extra_data.clone())
702            .with_gas_limit(gas_limit)
703            .with_difficulty(difficulty)
704            .with_mix_hash(mix_hash)
705            .with_coinbase(coinbase)
706            .extend_accounts(account.clone());
707
708        assert_ne!(custom_genesis, default_genesis);
709        // check every field
710        assert_eq!(custom_genesis.nonce, nonce);
711        assert_eq!(custom_genesis.timestamp, timestamp);
712        assert_eq!(custom_genesis.extra_data, extra_data);
713        assert_eq!(custom_genesis.gas_limit, gas_limit);
714        assert_eq!(custom_genesis.difficulty, difficulty);
715        assert_eq!(custom_genesis.mix_hash, mix_hash);
716        assert_eq!(custom_genesis.coinbase, coinbase);
717        assert_eq!(custom_genesis.alloc, account.clone());
718
719        // update existing account
720        assert_eq!(custom_genesis.alloc.len(), 1);
721        let same_address = first_address;
722        let new_alloc_account = GenesisAccount {
723            nonce: Some(1),
724            balance: U256::from(1),
725            code: Some(b"code".into()),
726            storage: Some(BTreeMap::default()),
727            private_key: None,
728        };
729        let mut updated_account = BTreeMap::default();
730        updated_account.insert(same_address, new_alloc_account);
731        let custom_genesis = custom_genesis.extend_accounts(updated_account.clone());
732        assert_ne!(account, updated_account);
733        assert_eq!(custom_genesis.alloc.len(), 1);
734
735        // add second account
736        let different_address = hex!("94e0681e3073dd71cec54b53afe988f39078fd1a").into();
737        let more_accounts = BTreeMap::from([(different_address, GenesisAccount::default())]);
738        let custom_genesis = custom_genesis.extend_accounts(more_accounts);
739        assert_eq!(custom_genesis.alloc.len(), 2);
740
741        // ensure accounts are different
742        let first_account = custom_genesis.alloc.get(&first_address);
743        let second_account = custom_genesis.alloc.get(&different_address);
744        assert!(first_account.is_some());
745        assert!(second_account.is_some());
746        assert_ne!(first_account, second_account);
747    }
748
749    #[test]
750    fn test_genesis_account() {
751        let default_account = GenesisAccount::default();
752
753        let nonce = Some(1);
754        let balance = U256::from(33);
755        let code = Some(b"code".into());
756        let root = hex!("9474ddfcea39c5a690d2744103e39d1ff1b03d18db10fc147d970ad24699395a").into();
757        let value = hex!("58eb8294d9bb16832a9dabfcb270fff99ab8ee1d8764e4f3d9fdf59ec1dee469").into();
758        let mut map = BTreeMap::default();
759        map.insert(root, value);
760        let storage = Some(map);
761
762        let genesis_account = GenesisAccount::default()
763            .with_nonce(nonce)
764            .with_balance(balance)
765            .with_code(code.clone())
766            .with_storage(storage.clone());
767
768        assert_ne!(default_account, genesis_account);
769        // check every field
770        assert_eq!(genesis_account.nonce, nonce);
771        assert_eq!(genesis_account.balance, balance);
772        assert_eq!(genesis_account.code, code);
773        assert_eq!(genesis_account.storage, storage);
774    }
775
776    #[test]
777    fn parse_hive_genesis() {
778        let geth_genesis = r#"
779    {
780        "difficulty": "0x20000",
781        "gasLimit": "0x1",
782        "alloc": {},
783        "config": {
784          "ethash": {},
785          "chainId": 1
786        }
787    }
788    "#;
789
790        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
791    }
792
793    #[test]
794    fn parse_hive_clique_smoke_genesis() {
795        let geth_genesis = r#"
796    {
797      "difficulty": "0x1",
798      "gasLimit": "0x400000",
799      "extraData":
800    "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
801    ,   "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
802      "nonce": "0x0",
803      "timestamp": "0x5c51a607",
804      "alloc": {}
805    }
806    "#;
807
808        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
809    }
810
811    #[test]
812    fn parse_non_hex_prefixed_balance() {
813        // tests that we can parse balance / difficulty fields that are either hex or decimal
814        let example_balance_json = r#"
815    {
816        "nonce": "0x0000000000000042",
817        "difficulty": "34747478",
818        "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
819        "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
820        "timestamp": "0x123456",
821        "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
822        "extraData": "0xfafbfcfd",
823        "gasLimit": "0x2fefd8",
824        "alloc": {
825            "0x3E951C9f69a06Bc3AD71fF7358DbC56bEd94b9F2": {
826              "balance": "1000000000000000000000000000"
827            },
828            "0xe228C30d4e5245f967ac21726d5412dA27aD071C": {
829              "balance": "1000000000000000000000000000"
830            },
831            "0xD59Ce7Ccc6454a2D2C2e06bbcf71D0Beb33480eD": {
832              "balance": "1000000000000000000000000000"
833            },
834            "0x1CF4D54414eF51b41f9B2238c57102ab2e61D1F2": {
835              "balance": "1000000000000000000000000000"
836            },
837            "0x249bE3fDEd872338C733cF3975af9736bdCb9D4D": {
838              "balance": "1000000000000000000000000000"
839            },
840            "0x3fCd1bff94513712f8cD63d1eD66776A67D5F78e": {
841              "balance": "1000000000000000000000000000"
842            }
843        },
844        "config": {
845            "ethash": {},
846            "chainId": 10,
847            "homesteadBlock": 0,
848            "eip150Block": 0,
849            "eip155Block": 0,
850            "eip158Block": 0,
851            "byzantiumBlock": 0,
852            "constantinopleBlock": 0,
853            "petersburgBlock": 0,
854            "istanbulBlock": 0
855        }
856    }
857    "#;
858
859        let genesis: Genesis = serde_json::from_str(example_balance_json).unwrap();
860
861        // check difficulty against hex ground truth
862        let expected_difficulty = U256::from_str("0x2123456").unwrap();
863        assert_eq!(expected_difficulty, genesis.difficulty);
864
865        // check all alloc balances
866        let dec_balance = U256::from_str("1000000000000000000000000000").unwrap();
867        for alloc in &genesis.alloc {
868            assert_eq!(alloc.1.balance, dec_balance);
869        }
870    }
871
872    #[test]
873    fn parse_hive_rpc_genesis() {
874        let geth_genesis = r#"
875    {
876      "config": {
877        "chainId": 7,
878        "homesteadBlock": 0,
879        "eip150Block": 0,
880        "eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
881        "eip155Block": 0,
882        "eip158Block": 0
883      },
884      "coinbase": "0x0000000000000000000000000000000000000000",
885      "difficulty": "0x20000",
886      "extraData":
887    "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
888    ,   "gasLimit": "0x2fefd8",
889      "nonce": "0x0000000000000000",
890      "timestamp": "0x1234",
891      "alloc": {
892        "cf49fda3be353c69b41ed96333cd24302da4556f": {
893          "balance": "0x123450000000000000000"
894        },
895        "0161e041aad467a890839d5b08b138c1e6373072": {
896          "balance": "0x123450000000000000000"
897        },
898        "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
899          "balance": "0x123450000000000000000"
900        },
901        "b97de4b8c857e4f6bc354f226dc3249aaee49209": {
902          "balance": "0x123450000000000000000"
903        },
904        "c5065c9eeebe6df2c2284d046bfc906501846c51": {
905          "balance": "0x123450000000000000000"
906        },
907        "0000000000000000000000000000000000000314": {
908          "balance": "0x0",
909          "code":
910    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029"
911    ,       "storage": {
912            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
913            "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
914          }
915        },
916        "0000000000000000000000000000000000000315": {
917          "balance": "0x9999999999999999999999999999999",
918          "code":
919    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
920        }
921      }
922    }
923    "#;
924
925        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
926    }
927
928    #[test]
929    fn parse_hive_graphql_genesis() {
930        let geth_genesis = r#"
931    {
932        "config"     : {},
933        "coinbase"   : "0x8888f1f195afa192cfee860698584c030f4c9db1",
934        "difficulty" : "0x020000",
935        "extraData"  : "0x42",
936        "gasLimit"   : "0x2fefd8",
937        "mixHash"    : "0x2c85bcbce56429100b2108254bb56906257582aeafcbd682bc9af67a9f5aee46",
938        "nonce"      : "0x78cc16f7b4f65485",
939        "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
940        "timestamp"  : "0x54c98c81",
941        "alloc"      : {
942            "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
943                "balance" : "0x09184e72a000"
944            }
945        }
946    }
947    "#;
948
949        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
950    }
951
952    #[test]
953    fn parse_hive_engine_genesis() {
954        let geth_genesis = r#"
955    {
956      "config": {
957        "chainId": 7,
958        "homesteadBlock": 0,
959        "eip150Block": 0,
960        "eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
961        "eip155Block": 0,
962        "eip158Block": 0,
963        "byzantiumBlock": 0,
964        "constantinopleBlock": 0,
965        "petersburgBlock": 0,
966        "istanbulBlock": 0,
967        "muirGlacierBlock": 0,
968        "berlinBlock": 0,
969        "yolov2Block": 0,
970        "yolov3Block": 0,
971        "londonBlock": 0
972      },
973      "coinbase": "0x0000000000000000000000000000000000000000",
974      "difficulty": "0x30000",
975      "extraData":
976    "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
977    ,   "gasLimit": "0x2fefd8",
978      "nonce": "0x0000000000000000",
979      "timestamp": "0x1234",
980      "alloc": {
981        "cf49fda3be353c69b41ed96333cd24302da4556f": {
982          "balance": "0x123450000000000000000"
983        },
984        "0161e041aad467a890839d5b08b138c1e6373072": {
985          "balance": "0x123450000000000000000"
986        },
987        "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
988          "balance": "0x123450000000000000000"
989        },
990        "b97de4b8c857e4f6bc354f226dc3249aaee49209": {
991          "balance": "0x123450000000000000000"
992        },
993        "c5065c9eeebe6df2c2284d046bfc906501846c51": {
994          "balance": "0x123450000000000000000"
995        },
996        "0000000000000000000000000000000000000314": {
997          "balance": "0x0",
998          "code":
999    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029"
1000    ,       "storage": {
1001            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
1002            "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
1003          }
1004        },
1005        "0000000000000000000000000000000000000315": {
1006          "balance": "0x9999999999999999999999999999999",
1007          "code":
1008    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
1009        },
1010        "0000000000000000000000000000000000000316": {
1011          "balance": "0x0",
1012          "code": "0x444355"
1013        },
1014        "0000000000000000000000000000000000000317": {
1015          "balance": "0x0",
1016          "code": "0x600160003555"
1017        }
1018      }
1019    }
1020    "#;
1021
1022        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1023    }
1024
1025    #[test]
1026    fn parse_hive_devp2p_genesis() {
1027        let geth_genesis = r#"
1028    {
1029        "config": {
1030            "chainId": 19763,
1031            "homesteadBlock": 0,
1032            "eip150Block": 0,
1033            "eip155Block": 0,
1034            "eip158Block": 0,
1035            "byzantiumBlock": 0,
1036            "ethash": {}
1037        },
1038        "nonce": "0xdeadbeefdeadbeef",
1039        "timestamp": "0x0",
1040        "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
1041        "gasLimit": "0x80000000",
1042        "difficulty": "0x20000",
1043        "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1044        "coinbase": "0x0000000000000000000000000000000000000000",
1045        "alloc": {
1046            "71562b71999873db5b286df957af199ec94617f7": {
1047                "balance": "0xffffffffffffffffffffffffff"
1048            }
1049        },
1050        "number": "0x0",
1051        "gasUsed": "0x0",
1052        "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
1053    }
1054    "#;
1055
1056        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1057    }
1058
1059    #[test]
1060    fn parse_deposit_contract_address() {
1061        let genesis = r#"
1062    {
1063      "config": {
1064        "chainId": 1337,
1065        "homesteadBlock": 0,
1066        "eip150Block": 0,
1067        "eip155Block": 0,
1068        "eip158Block": 0,
1069        "byzantiumBlock": 0,
1070        "constantinopleBlock": 0,
1071        "petersburgBlock": 0,
1072        "istanbulBlock": 0,
1073        "muirGlacierBlock": 0,
1074        "berlinBlock": 0,
1075        "londonBlock": 0,
1076        "arrowGlacierBlock": 0,
1077        "grayGlacierBlock": 0,
1078        "shanghaiTime": 0,
1079        "cancunTime": 0,
1080        "pragueTime": 1,
1081        "osakaTime": 2,
1082        "terminalTotalDifficulty": 0,
1083        "depositContractAddress": "0x0000000000000000000000000000000000000000",
1084        "terminalTotalDifficultyPassed": true
1085      },
1086      "nonce": "0x0",
1087      "timestamp": "0x0",
1088      "extraData": "0x",
1089      "gasLimit": "0x4c4b40",
1090      "difficulty": "0x1",
1091      "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1092      "coinbase": "0x0000000000000000000000000000000000000000"
1093    }
1094    "#;
1095
1096        let got_genesis: Genesis = serde_json::from_str(genesis).unwrap();
1097        let expected_genesis = Genesis {
1098            config: ChainConfig {
1099                chain_id: 1337,
1100                homestead_block: Some(0),
1101                eip150_block: Some(0),
1102                eip155_block: Some(0),
1103                eip158_block: Some(0),
1104                byzantium_block: Some(0),
1105                constantinople_block: Some(0),
1106                petersburg_block: Some(0),
1107                istanbul_block: Some(0),
1108                muir_glacier_block: Some(0),
1109                berlin_block: Some(0),
1110                london_block: Some(0),
1111                arrow_glacier_block: Some(0),
1112                gray_glacier_block: Some(0),
1113                dao_fork_block: None,
1114                dao_fork_support: false,
1115                shanghai_time: Some(0),
1116                cancun_time: Some(0),
1117                prague_time: Some(1),
1118                osaka_time: Some(2),
1119                terminal_total_difficulty: Some(U256::ZERO),
1120                terminal_total_difficulty_passed: true,
1121                deposit_contract_address: Some(Address::ZERO),
1122                ..Default::default()
1123            },
1124            nonce: 0,
1125            timestamp: 0,
1126            extra_data: Bytes::new(),
1127            gas_limit: 0x4c4b40,
1128            difficulty: U256::from(1),
1129            ..Default::default()
1130        };
1131
1132        assert_eq!(expected_genesis, got_genesis);
1133    }
1134
1135    #[test]
1136    fn parse_prague_time() {
1137        let genesis = r#"
1138    {
1139      "config": {
1140        "chainId": 1337,
1141        "homesteadBlock": 0,
1142        "eip150Block": 0,
1143        "eip155Block": 0,
1144        "eip158Block": 0,
1145        "byzantiumBlock": 0,
1146        "constantinopleBlock": 0,
1147        "petersburgBlock": 0,
1148        "istanbulBlock": 0,
1149        "muirGlacierBlock": 0,
1150        "berlinBlock": 0,
1151        "londonBlock": 0,
1152        "arrowGlacierBlock": 0,
1153        "grayGlacierBlock": 0,
1154        "shanghaiTime": 0,
1155        "cancunTime": 0,
1156        "pragueTime": 1,
1157        "terminalTotalDifficulty": 0,
1158        "terminalTotalDifficultyPassed": true
1159      },
1160      "nonce": "0x0",
1161      "timestamp": "0x0",
1162      "extraData": "0x",
1163      "gasLimit": "0x4c4b40",
1164      "difficulty": "0x1",
1165      "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1166      "coinbase": "0x0000000000000000000000000000000000000000"
1167    }
1168    "#;
1169
1170        let got_genesis: Genesis = serde_json::from_str(genesis).unwrap();
1171        let expected_genesis = Genesis {
1172            config: ChainConfig {
1173                chain_id: 1337,
1174                homestead_block: Some(0),
1175                eip150_block: Some(0),
1176                eip155_block: Some(0),
1177                eip158_block: Some(0),
1178                byzantium_block: Some(0),
1179                constantinople_block: Some(0),
1180                petersburg_block: Some(0),
1181                istanbul_block: Some(0),
1182                muir_glacier_block: Some(0),
1183                berlin_block: Some(0),
1184                london_block: Some(0),
1185                arrow_glacier_block: Some(0),
1186                gray_glacier_block: Some(0),
1187                dao_fork_block: None,
1188                dao_fork_support: false,
1189                shanghai_time: Some(0),
1190                cancun_time: Some(0),
1191                prague_time: Some(1),
1192                terminal_total_difficulty: Some(U256::ZERO),
1193                terminal_total_difficulty_passed: true,
1194                ..Default::default()
1195            },
1196            nonce: 0,
1197            timestamp: 0,
1198            extra_data: Bytes::new(),
1199            gas_limit: 0x4c4b40,
1200            difficulty: U256::from(1),
1201            ..Default::default()
1202        };
1203
1204        assert_eq!(expected_genesis, got_genesis);
1205    }
1206
1207    #[test]
1208    fn parse_execution_apis_genesis() {
1209        let geth_genesis = r#"
1210    {
1211      "config": {
1212        "chainId": 1337,
1213        "homesteadBlock": 0,
1214        "eip150Block": 0,
1215        "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1216        "eip155Block": 0,
1217        "eip158Block": 0,
1218        "byzantiumBlock": 0,
1219        "constantinopleBlock": 0,
1220        "petersburgBlock": 0,
1221        "istanbulBlock": 0,
1222        "muirGlacierBlock": 0,
1223        "berlinBlock": 0,
1224        "londonBlock": 0,
1225        "arrowGlacierBlock": 0,
1226        "grayGlacierBlock": 0,
1227        "shanghaiTime": 0,
1228        "terminalTotalDifficulty": 0,
1229        "terminalTotalDifficultyPassed": true,
1230        "ethash": {}
1231      },
1232      "nonce": "0x0",
1233      "timestamp": "0x0",
1234      "extraData": "0x",
1235      "gasLimit": "0x4c4b40",
1236      "difficulty": "0x1",
1237      "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1238      "coinbase": "0x0000000000000000000000000000000000000000",
1239      "alloc": {
1240        "658bdf435d810c91414ec09147daa6db62406379": {
1241          "balance": "0x487a9a304539440000"
1242        },
1243        "aa00000000000000000000000000000000000000": {
1244          "code": "0x6042",
1245          "storage": {
1246            "0x0000000000000000000000000000000000000000000000000000000000000000":
1247    "0x0000000000000000000000000000000000000000000000000000000000000000",
1248            "0x0100000000000000000000000000000000000000000000000000000000000000":
1249    "0x0100000000000000000000000000000000000000000000000000000000000000",
1250            "0x0200000000000000000000000000000000000000000000000000000000000000":
1251    "0x0200000000000000000000000000000000000000000000000000000000000000",
1252            "0x0300000000000000000000000000000000000000000000000000000000000000":
1253    "0x0000000000000000000000000000000000000000000000000000000000000303"       },
1254          "balance": "0x1",
1255          "nonce": "0x1"
1256        },
1257        "bb00000000000000000000000000000000000000": {
1258          "code": "0x600154600354",
1259          "storage": {
1260            "0x0000000000000000000000000000000000000000000000000000000000000000":
1261    "0x0000000000000000000000000000000000000000000000000000000000000000",
1262            "0x0100000000000000000000000000000000000000000000000000000000000000":
1263    "0x0100000000000000000000000000000000000000000000000000000000000000",
1264            "0x0200000000000000000000000000000000000000000000000000000000000000":
1265    "0x0200000000000000000000000000000000000000000000000000000000000000",
1266            "0x0300000000000000000000000000000000000000000000000000000000000000":
1267    "0x0000000000000000000000000000000000000000000000000000000000000303"       },
1268          "balance": "0x2",
1269          "nonce": "0x1"
1270        }
1271      }
1272    }
1273    "#;
1274
1275        let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1276    }
1277
1278    #[test]
1279    fn parse_hive_rpc_genesis_full() {
1280        let geth_genesis = r#"
1281    {
1282      "config": {
1283        "clique": {
1284          "period": 1
1285        },
1286        "chainId": 7,
1287        "homesteadBlock": 0,
1288        "eip150Block": 0,
1289        "eip155Block": 0,
1290        "eip158Block": 0
1291      },
1292      "coinbase": "0x0000000000000000000000000000000000000000",
1293      "difficulty": "0x020000",
1294      "extraData":
1295    "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1296    ,   "gasLimit": "0x2fefd8",
1297      "nonce": "0x0000000000000000",
1298      "timestamp": "0x1234",
1299      "alloc": {
1300        "cf49fda3be353c69b41ed96333cd24302da4556f": {
1301          "balance": "0x123450000000000000000"
1302        },
1303        "0161e041aad467a890839d5b08b138c1e6373072": {
1304          "balance": "0x123450000000000000000"
1305        },
1306        "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
1307          "balance": "0x123450000000000000000"
1308        },
1309        "b97de4b8c857e4f6bc354f226dc3249aaee49209": {
1310          "balance": "0x123450000000000000000"
1311        },
1312        "c5065c9eeebe6df2c2284d046bfc906501846c51": {
1313          "balance": "0x123450000000000000000"
1314        },
1315        "0000000000000000000000000000000000000314": {
1316          "balance": "0x0",
1317          "code":
1318    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029"
1319    ,       "storage": {
1320            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
1321            "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
1322          }
1323        },
1324        "0000000000000000000000000000000000000315": {
1325          "balance": "0x9999999999999999999999999999999",
1326          "code":
1327    "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
1328        }
1329      },
1330      "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1331      "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
1332    }
1333    "#;
1334
1335        let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1336        let alloc_entry = genesis
1337            .alloc
1338            .get(&Address::from_str("0000000000000000000000000000000000000314").unwrap())
1339            .expect("missing account for parsed genesis");
1340        let storage = alloc_entry.storage.as_ref().expect("missing storage for parsed genesis");
1341        let expected_storage = BTreeMap::from_iter(vec![
1342            (
1343                B256::from_str(
1344                    "0x0000000000000000000000000000000000000000000000000000000000000000",
1345                )
1346                .unwrap(),
1347                B256::from_str(
1348                    "0x0000000000000000000000000000000000000000000000000000000000001234",
1349                )
1350                .unwrap(),
1351            ),
1352            (
1353                B256::from_str(
1354                    "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9",
1355                )
1356                .unwrap(),
1357                B256::from_str(
1358                    "0x0000000000000000000000000000000000000000000000000000000000000001",
1359                )
1360                .unwrap(),
1361            ),
1362        ]);
1363        assert_eq!(storage, &expected_storage);
1364
1365        let expected_code =
1366    Bytes::from_str("0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029"
1367    ).unwrap();
1368        let code = alloc_entry.code.as_ref().expect(
1369            "missing code for parsed
1370    genesis",
1371        );
1372        assert_eq!(code, &expected_code);
1373    }
1374
1375    #[test]
1376    fn test_hive_smoke_alloc_deserialize() {
1377        let hive_genesis = r#"
1378    {
1379        "nonce": "0x0000000000000042",
1380        "difficulty": "0x2123456",
1381        "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
1382        "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1383        "timestamp": "0x123456",
1384        "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1385        "extraData": "0xfafbfcfd",
1386        "gasLimit": "0x2fefd8",
1387        "alloc": {
1388            "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
1389                "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1390            },
1391            "e6716f9544a56c530d868e4bfbacb172315bdead": {
1392                "balance": "0x11",
1393                "code": "0x12"
1394            },
1395            "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
1396                "balance": "0x21",
1397                "storage": {
1398                    "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
1399                }
1400            },
1401            "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
1402                "balance": "0x31",
1403                "nonce": "0x32"
1404            },
1405            "0000000000000000000000000000000000000001": {
1406                "balance": "0x41"
1407            },
1408            "0000000000000000000000000000000000000002": {
1409                "balance": "0x51"
1410            },
1411            "0000000000000000000000000000000000000003": {
1412                "balance": "0x61"
1413            },
1414            "0000000000000000000000000000000000000004": {
1415                "balance": "0x71"
1416            }
1417        },
1418        "config": {
1419            "ethash": {},
1420            "chainId": 10,
1421            "homesteadBlock": 0,
1422            "eip150Block": 0,
1423            "eip155Block": 0,
1424            "eip158Block": 0,
1425            "byzantiumBlock": 0,
1426            "constantinopleBlock": 0,
1427            "petersburgBlock": 0,
1428            "istanbulBlock": 0
1429        }
1430    }
1431    "#;
1432
1433        let expected_genesis =
1434            Genesis {
1435                nonce: 0x0000000000000042,
1436                difficulty: U256::from(0x2123456),
1437                mix_hash: B256::from_str(
1438                    "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
1439                )
1440                .unwrap(),
1441                coinbase: Address::from_str("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(),
1442                timestamp: 0x123456,
1443                extra_data: Bytes::from_str("0xfafbfcfd").unwrap(),
1444                gas_limit: 0x2fefd8,
1445                base_fee_per_gas: None,
1446                excess_blob_gas: None,
1447                blob_gas_used: None,
1448                number: None,
1449                alloc: BTreeMap::from_iter(vec![
1450                (
1451                    Address::from_str("0xdbdbdb2cbd23b783741e8d7fcf51e459b497e4a6").unwrap(),
1452                    GenesisAccount {
1453                        balance:
1454    U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").
1455    unwrap(),                     nonce: None,
1456                        code: None,
1457                        storage: None,
1458                        private_key: None,
1459                    },
1460                ),
1461                (
1462                    Address::from_str("0xe6716f9544a56c530d868e4bfbacb172315bdead").unwrap(),
1463                    GenesisAccount {
1464                        balance: U256::from_str("0x11").unwrap(),
1465                        nonce: None,
1466                        code: Some(Bytes::from_str("0x12").unwrap()),
1467                        storage: None,
1468                        private_key: None,
1469                    },
1470                ),
1471                (
1472                    Address::from_str("0xb9c015918bdaba24b4ff057a92a3873d6eb201be").unwrap(),
1473                    GenesisAccount {
1474                        balance: U256::from_str("0x21").unwrap(),
1475                        nonce: None,
1476                        code: None,
1477                        storage: Some(BTreeMap::from_iter(vec![
1478                            (
1479
1480    B256::from_str("0x0000000000000000000000000000000000000000000000000000000000000001").
1481    unwrap(),
1482    B256::from_str("0x0000000000000000000000000000000000000000000000000000000000000022").
1483    unwrap(),                         ),
1484                        ])),
1485                        private_key: None,
1486                    },
1487                ),
1488                (
1489                    Address::from_str("0x1a26338f0d905e295fccb71fa9ea849ffa12aaf4").unwrap(),
1490                    GenesisAccount {
1491                        balance: U256::from_str("0x31").unwrap(),
1492                        nonce: Some(0x32u64),
1493                        code: None,
1494                        storage: None,
1495                        private_key: None,
1496                    },
1497                ),
1498                (
1499                    Address::from_str("0x0000000000000000000000000000000000000001").unwrap(),
1500                    GenesisAccount {
1501                        balance: U256::from_str("0x41").unwrap(),
1502                        nonce: None,
1503                        code: None,
1504                        storage: None,
1505                        private_key: None,
1506                    },
1507                ),
1508                (
1509                    Address::from_str("0x0000000000000000000000000000000000000002").unwrap(),
1510                    GenesisAccount {
1511                        balance: U256::from_str("0x51").unwrap(),
1512                        nonce: None,
1513                        code: None,
1514                        storage: None,
1515                        private_key: None,
1516                    },
1517                ),
1518                (
1519                    Address::from_str("0x0000000000000000000000000000000000000003").unwrap(),
1520                    GenesisAccount {
1521                        balance: U256::from_str("0x61").unwrap(),
1522                        nonce: None,
1523                        code: None,
1524                        storage: None,
1525                        private_key: None,
1526                    },
1527                ),
1528                (
1529                    Address::from_str("0x0000000000000000000000000000000000000004").unwrap(),
1530                    GenesisAccount {
1531                        balance: U256::from_str("0x71").unwrap(),
1532                        nonce: None,
1533                        code: None,
1534                        storage: None,
1535                        private_key: None,
1536                    },
1537                ),
1538            ]),
1539                config: ChainConfig {
1540                    ethash: Some(EthashConfig {}),
1541                    chain_id: 10,
1542                    homestead_block: Some(0),
1543                    eip150_block: Some(0),
1544                    eip155_block: Some(0),
1545                    eip158_block: Some(0),
1546                    byzantium_block: Some(0),
1547                    constantinople_block: Some(0),
1548                    petersburg_block: Some(0),
1549                    istanbul_block: Some(0),
1550                    deposit_contract_address: None,
1551                    ..Default::default()
1552                },
1553            };
1554
1555        let deserialized_genesis: Genesis = serde_json::from_str(hive_genesis).unwrap();
1556        assert_eq!(
1557            deserialized_genesis, expected_genesis,
1558            "deserialized genesis
1559    {deserialized_genesis:#?} does not match expected {expected_genesis:#?}"
1560        );
1561    }
1562
1563    #[test]
1564    fn parse_dump_genesis_mainnet() {
1565        let mainnet = include_str!("../dumpgenesis/mainnet.json");
1566        let gen = serde_json::from_str::<Genesis>(mainnet).unwrap();
1567        let s = serde_json::to_string_pretty(&gen).unwrap();
1568        let gen2 = serde_json::from_str::<Genesis>(&s).unwrap();
1569        assert_eq!(gen, gen2);
1570    }
1571
1572    #[test]
1573    fn parse_dump_genesis_sepolia() {
1574        let sepolia = include_str!("../dumpgenesis/sepolia.json");
1575        let gen = serde_json::from_str::<Genesis>(sepolia).unwrap();
1576        let s = serde_json::to_string_pretty(&gen).unwrap();
1577        let gen2 = serde_json::from_str::<Genesis>(&s).unwrap();
1578        assert_eq!(gen, gen2);
1579    }
1580
1581    #[test]
1582    fn parse_dump_genesis_holesky() {
1583        let holesky = include_str!("../dumpgenesis/holesky.json");
1584        let gen = serde_json::from_str::<Genesis>(holesky).unwrap();
1585        let s = serde_json::to_string_pretty(&gen).unwrap();
1586        let gen2 = serde_json::from_str::<Genesis>(&s).unwrap();
1587        assert_eq!(gen, gen2);
1588    }
1589
1590    #[test]
1591    fn parse_extra_fields() {
1592        let geth_genesis = r#"
1593    {
1594        "difficulty": "0x20000",
1595        "gasLimit": "0x1",
1596        "alloc": {},
1597        "config": {
1598          "ethash": {},
1599          "chainId": 1,
1600          "string_field": "string_value",
1601          "numeric_field": 7,
1602          "object_field": {
1603            "sub_field": "sub_value"
1604          }
1605        }
1606    }
1607    "#;
1608        let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1609        let actual_string_value = genesis.config.extra_fields.get("string_field").unwrap();
1610        assert_eq!(actual_string_value, "string_value");
1611        let actual_numeric_value = genesis.config.extra_fields.get("numeric_field").unwrap();
1612        assert_eq!(actual_numeric_value, 7);
1613        let actual_object_value = genesis.config.extra_fields.get("object_field").unwrap();
1614        assert_eq!(actual_object_value, &serde_json::json!({"sub_field": "sub_value"}));
1615    }
1616
1617    #[test]
1618    fn deserialize_private_key_as_none_when_0x() {
1619        // Test case where "secretKey" is "0x", expecting None
1620        let json_data = json!({
1621            "balance": "0x0",
1622            "secretKey": "0x"
1623        });
1624
1625        let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
1626        assert_eq!(account.private_key, None);
1627    }
1628
1629    #[test]
1630    fn deserialize_private_key_with_valid_hex() {
1631        // Test case where "secretKey" is a valid hex string
1632        let json_data = json!({
1633            "balance": "0x0",
1634            "secretKey": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"
1635        });
1636
1637        let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
1638        let expected_key =
1639            B256::from_str("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234")
1640                .unwrap();
1641        assert_eq!(account.private_key, Some(expected_key));
1642    }
1643
1644    #[test]
1645    fn deserialize_private_key_as_none_when_null() {
1646        // Test case where "secretKey" is null, expecting None
1647        let json_data = json!({
1648            "balance": "0x0",
1649            "secretKey": null
1650        });
1651
1652        let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
1653        assert_eq!(account.private_key, None);
1654    }
1655
1656    #[test]
1657    fn deserialize_private_key_with_invalid_hex_fails() {
1658        // Test case where "secretKey" is an invalid hex string, expecting an error
1659        let json_data = json!({
1660            "balance": "0x0",
1661            "secretKey": "0xINVALIDHEX"
1662        });
1663
1664        let result: Result<GenesisAccount, _> = serde_json::from_value(json_data);
1665        assert!(result.is_err()); // The deserialization should fail due to invalid hex
1666    }
1667
1668    #[test]
1669    fn deserialize_private_key_with_empty_string_fails() {
1670        // Test case where "secretKey" is an empty string, expecting an error
1671        let json_data = json!({
1672            "secretKey": ""
1673        });
1674
1675        let result: Result<GenesisAccount, _> = serde_json::from_value(json_data);
1676        assert!(result.is_err()); // The deserialization should fail due to an empty string
1677    }
1678
1679    #[test]
1680    fn test_from_genesis_account_with_default_values() {
1681        let genesis_account = GenesisAccount::default();
1682
1683        // Convert the GenesisAccount to a TrieAccount
1684        let trie_account: TrieAccount = genesis_account.into();
1685
1686        // Check the fields are properly set.
1687        assert_eq!(trie_account.nonce, 0);
1688        assert_eq!(trie_account.balance, U256::default());
1689        assert_eq!(trie_account.storage_root, EMPTY_ROOT_HASH);
1690        assert_eq!(trie_account.code_hash, KECCAK_EMPTY);
1691
1692        // Check that the default Account converts to the same TrieAccount
1693        assert_eq!(TrieAccount::default(), trie_account);
1694    }
1695
1696    #[test]
1697    fn test_from_genesis_account_with_values() {
1698        // Create a GenesisAccount with specific values
1699        let mut storage = BTreeMap::new();
1700        storage.insert(B256::from([0x01; 32]), B256::from([0x02; 32]));
1701
1702        let genesis_account = GenesisAccount {
1703            nonce: Some(10),
1704            balance: U256::from(1000),
1705            code: Some(Bytes::from(vec![0x60, 0x61])),
1706            storage: Some(storage),
1707            private_key: None,
1708        };
1709
1710        // Convert the GenesisAccount to a TrieAccount
1711        let trie_account: TrieAccount = genesis_account.into();
1712
1713        let expected_storage_root = storage_root_unhashed(BTreeMap::from([(
1714            B256::from([0x01; 32]),
1715            U256::from_be_bytes(*B256::from([0x02; 32])),
1716        )]));
1717
1718        // Check that the fields are properly set.
1719        assert_eq!(trie_account.nonce, 10);
1720        assert_eq!(trie_account.balance, U256::from(1000));
1721        assert_eq!(trie_account.storage_root, expected_storage_root);
1722        assert_eq!(trie_account.code_hash, keccak256([0x60, 0x61]));
1723    }
1724
1725    #[test]
1726    fn test_from_genesis_account_with_zeroed_storage_values() {
1727        // Create a GenesisAccount with storage containing zero values
1728        let storage = BTreeMap::from([(B256::from([0x01; 32]), B256::from([0x00; 32]))]);
1729
1730        let genesis_account = GenesisAccount {
1731            nonce: Some(3),
1732            balance: U256::from(300),
1733            code: None,
1734            storage: Some(storage),
1735            private_key: None,
1736        };
1737
1738        // Convert the GenesisAccount to a TrieAccount
1739        let trie_account: TrieAccount = genesis_account.into();
1740
1741        // Check the fields are properly set.
1742        assert_eq!(trie_account.nonce, 3);
1743        assert_eq!(trie_account.balance, U256::from(300));
1744        // Zero values in storage should result in EMPTY_ROOT_HASH
1745        assert_eq!(trie_account.storage_root, EMPTY_ROOT_HASH);
1746        // No code provided, so code hash should be KECCAK_EMPTY
1747        assert_eq!(trie_account.code_hash, KECCAK_EMPTY);
1748    }
1749}