diff --git a/ethers-core/src/utils/genesis.rs b/ethers-core/src/utils/genesis.rs index a19f8354..c6885631 100644 --- a/ethers-core/src/utils/genesis.rs +++ b/ethers-core/src/utils/genesis.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::{ types::{Address, Bytes, H256, U256, U64}, - utils::{from_int_or_hex, from_int_or_hex_opt}, + utils::{from_int_or_hex, from_int_or_hex_opt, from_u64_or_hex_opt}, }; use serde::{Deserialize, Serialize}; @@ -12,15 +12,19 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "camelCase")] pub struct Genesis { /// The fork configuration for this network. + #[serde(default)] pub config: ChainConfig, /// The genesis header nonce. + #[serde(default)] pub nonce: U64, /// The genesis header timestamp. + #[serde(default)] pub timestamp: U64, /// The genesis header extra data. + #[serde(default)] pub extra_data: Bytes, /// The genesis header gas limit. @@ -31,13 +35,33 @@ pub struct Genesis { pub difficulty: U256, /// The genesis header mix hash. + #[serde(default)] pub mix_hash: H256, /// The genesis header coinbase address. + #[serde(default)] pub coinbase: Address, /// The initial state of the genesis block. pub alloc: HashMap, + + // The following fields are only included for tests, and should not be used in real genesis + // blocks. + /// The block number + #[serde(skip_serializing_if = "Option::is_none", default)] + pub number: Option, + + /// The block gas gasUsed + #[serde(skip_serializing_if = "Option::is_none", default)] + pub gas_used: Option, + + /// The block parent hash + #[serde(skip_serializing_if = "Option::is_none", default)] + pub parent_hash: Option, + + /// The base fee + #[serde(skip_serializing_if = "Option::is_none", default)] + pub base_fee_per_gas: Option, } impl Genesis { @@ -100,12 +124,16 @@ impl Genesis { /// An account in the state of the genesis block. #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct GenesisAccount { - #[serde(skip_serializing_if = "Option::is_none")] + #[serde( + skip_serializing_if = "Option::is_none", + deserialize_with = "from_u64_or_hex_opt", + default + )] pub nonce: Option, pub balance: U256, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub code: Option, - #[serde(flatten, skip_serializing_if = "Option::is_none")] + #[serde(flatten, skip_serializing_if = "Option::is_none", default)] pub storage: Option>, } @@ -118,6 +146,7 @@ pub struct GenesisAccount { #[serde(default, rename_all = "camelCase")] pub struct ChainConfig { /// The network's chain ID. + #[serde(default = "one")] pub chain_id: u64, /// The homestead switch block (None = no fork, 0 = already homestead). @@ -191,11 +220,9 @@ pub struct ChainConfig { #[serde(skip_serializing_if = "Option::is_none")] pub shanghai_time: Option, - // TODO: change to cancunTime when is - // merged in geth - /// Cancun hard fork block. + /// Cancun switch time. #[serde(skip_serializing_if = "Option::is_none")] - pub cancun_block: Option, + pub cancun_time: Option, /// Total difficulty reached that triggers the merge consensus upgrade. #[serde(skip_serializing_if = "Option::is_none", deserialize_with = "from_int_or_hex_opt")] @@ -214,6 +241,12 @@ pub struct ChainConfig { pub clique: Option, } +// used only for serde +#[inline] +const fn one() -> u64 { + 1 +} + /// Empty consensus configuration for proof-of-work networks. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct EthashConfig {} @@ -227,3 +260,298 @@ pub struct CliqueConfig { /// Epoch length to reset votes and checkpoints. pub epoch: u64, } + +#[cfg(test)] +mod tests { + use super::{Genesis, H256}; + + #[test] + fn parse_hive_genesis() { + let geth_genesis = r#" + { + "difficulty": "0x20000", + "gasLimit": "0x1", + "alloc": {}, + "config": { + "ethash": {}, + "chainId": 1 + } + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_hive_clique_smoke_genesis() { + let geth_genesis = r#" + { + "difficulty": "0x1", + "gasLimit": "0x400000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "timestamp": "0x5c51a607", + "alloc": {} + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_hive_rpc_genesis() { + let geth_genesis = r#" + { + "config": { + "chainId": 7, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689", + "eip155Block": 0, + "eip158Block": 0 + }, + "coinbase": "0x0000000000000000000000000000000000000000", + "difficulty": "0x20000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x2fefd8", + "nonce": "0x0000000000000000", + "timestamp": "0x1234", + "alloc": { + "cf49fda3be353c69b41ed96333cd24302da4556f": { + "balance": "0x123450000000000000000" + }, + "0161e041aad467a890839d5b08b138c1e6373072": { + "balance": "0x123450000000000000000" + }, + "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": { + "balance": "0x123450000000000000000" + }, + "b97de4b8c857e4f6bc354f226dc3249aaee49209": { + "balance": "0x123450000000000000000" + }, + "c5065c9eeebe6df2c2284d046bfc906501846c51": { + "balance": "0x123450000000000000000" + }, + "0000000000000000000000000000000000000314": { + "balance": "0x0", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234", + "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01" + } + }, + "0000000000000000000000000000000000000315": { + "balance": "0x9999999999999999999999999999999", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029" + } + } + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_hive_graphql_genesis() { + let geth_genesis = r#" + { + "config" : {}, + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "0x020000", + "extraData" : "0x42", + "gasLimit" : "0x2fefd8", + "mixHash" : "0x2c85bcbce56429100b2108254bb56906257582aeafcbd682bc9af67a9f5aee46", + "nonce" : "0x78cc16f7b4f65485", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x54c98c81", + "alloc" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance" : "0x09184e72a000" + } + } + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_hive_engine_genesis() { + let geth_genesis = r#" + { + "config": { + "chainId": 7, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "yolov2Block": 0, + "yolov3Block": 0, + "londonBlock": 0 + }, + "coinbase": "0x0000000000000000000000000000000000000000", + "difficulty": "0x30000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x2fefd8", + "nonce": "0x0000000000000000", + "timestamp": "0x1234", + "alloc": { + "cf49fda3be353c69b41ed96333cd24302da4556f": { + "balance": "0x123450000000000000000" + }, + "0161e041aad467a890839d5b08b138c1e6373072": { + "balance": "0x123450000000000000000" + }, + "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": { + "balance": "0x123450000000000000000" + }, + "b97de4b8c857e4f6bc354f226dc3249aaee49209": { + "balance": "0x123450000000000000000" + }, + "c5065c9eeebe6df2c2284d046bfc906501846c51": { + "balance": "0x123450000000000000000" + }, + "0000000000000000000000000000000000000314": { + "balance": "0x0", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234", + "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01" + } + }, + "0000000000000000000000000000000000000315": { + "balance": "0x9999999999999999999999999999999", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029" + }, + "0000000000000000000000000000000000000316": { + "balance": "0x0", + "code": "0x444355" + }, + "0000000000000000000000000000000000000317": { + "balance": "0x0", + "code": "0x600160003555" + } + } + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_hive_devp2p_genesis() { + let geth_genesis = r#" + { + "config": { + "chainId": 19763, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "ethash": {} + }, + "nonce": "0xdeadbeefdeadbeef", + "timestamp": "0x0", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x80000000", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xffffffffffffffffffffffffff" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + "#; + + let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + } + + #[test] + fn parse_execution_apis_genesis() { + let geth_genesis = r#" + { + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "ethash": {} + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x", + "gasLimit": "0x4c4b40", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "658bdf435d810c91414ec09147daa6db62406379": { + "balance": "0x487a9a304539440000" + }, + "aa00000000000000000000000000000000000000": { + "code": "0x6042", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", + "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" + }, + "balance": "0x1", + "nonce": "0x1" + }, + "bb00000000000000000000000000000000000000": { + "code": "0x600154600354", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", + "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" + }, + "balance": "0x2", + "nonce": "0x1" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": "0x3b9aca00" + } + "#; + + let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + + // ensure the test fields are parsed correctly + assert_eq!(genesis.base_fee_per_gas, Some(1000000000.into())); + assert_eq!(genesis.number, Some(0.into())); + assert_eq!(genesis.gas_used, Some(0.into())); + assert_eq!(genesis.parent_hash, Some(H256::zero())); + } +} diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs index 9528fa01..6d29d9d0 100644 --- a/ethers-core/src/utils/mod.rs +++ b/ethers-core/src/utils/mod.rs @@ -36,7 +36,7 @@ pub use rlp; /// Re-export hex pub use hex; -use crate::types::{Address, ParseI256Error, I256, U256}; +use crate::types::{Address, ParseI256Error, I256, U256, U64}; use elliptic_curve::sec1::ToEncodedPoint; use ethabi::ethereum_types::FromDecStrErr; use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey}; @@ -487,6 +487,25 @@ where } } +/// Deserializes the input into a U64, accepting both 0x-prefixed hex and decimal strings with +/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). +pub fn from_u64_or_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum IntOrHex { + Int(serde_json::Number), + Hex(String), + } + + match IntOrHex::deserialize(deserializer)? { + IntOrHex::Hex(s) => U64::from_str(s.as_str()).map_err(serde::de::Error::custom), + IntOrHex::Int(n) => U64::from_dec_str(&n.to_string()).map_err(serde::de::Error::custom), + } +} + /// Deserializes the input into an `Option`, using [`from_int_or_hex`] to deserialize the /// inner value. pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> @@ -496,6 +515,15 @@ where Ok(Some(from_int_or_hex(deserializer)?)) } +/// Deserializes the input into an `Option`, using [`from_u64_or_hex`] to deserialize the +/// inner value. +pub fn from_u64_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + Ok(Some(from_u64_or_hex(deserializer)?.as_u64())) +} + fn estimate_priority_fee(rewards: Vec>) -> U256 { let mut rewards: Vec = rewards.iter().map(|r| r[0]).filter(|r| *r > U256::zero()).collect();