fix(core): properly parse genesis alloc storage (#2205)
* fix(core): properly parse genesis alloc storage * Previously we were not properly parsing the storage field of GenesisAlloc. The first issue was in using serde(flatten), which is just incorrect because storage is not encoded flattened. The second issue was in the parsing of H256 keys and values. Some of the storage values in hive genesis examples were encoded in less than 64 hex characters, such as the string `0x12`. This would not succeed normal H256 parsing. * Introduce from_unformatted_hex_map to properly deserialize the storage map. * Modify existing genesis parsing tests to check parsed storage and code values against expected values. * Introduce new genesis parsing test from the hive smoke tests, checking full struct equality. * remove unused from_unformatted hex * make clippy happy
This commit is contained in:
parent
319b86a643
commit
537d0a9deb
|
@ -2,13 +2,13 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{Address, Bytes, H256, U256, U64},
|
types::{Address, Bytes, H256, U256, U64},
|
||||||
utils::{from_int_or_hex, from_int_or_hex_opt, from_u64_or_hex_opt},
|
utils::{from_int_or_hex, from_int_or_hex_opt, from_u64_or_hex_opt, from_unformatted_hex_map},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// This represents the chain configuration, specifying the genesis block, header fields, and hard
|
/// This represents the chain configuration, specifying the genesis block, header fields, and hard
|
||||||
/// fork switch blocks.
|
/// fork switch blocks.
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Genesis {
|
pub struct Genesis {
|
||||||
/// The fork configuration for this network.
|
/// The fork configuration for this network.
|
||||||
|
@ -133,7 +133,11 @@ pub struct GenesisAccount {
|
||||||
pub balance: U256,
|
pub balance: U256,
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub code: Option<Bytes>,
|
pub code: Option<Bytes>,
|
||||||
#[serde(flatten, skip_serializing_if = "Option::is_none", default)]
|
#[serde(
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "from_unformatted_hex_map",
|
||||||
|
default
|
||||||
|
)]
|
||||||
pub storage: Option<HashMap<H256, H256>>,
|
pub storage: Option<HashMap<H256, H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +146,7 @@ pub struct GenesisAccount {
|
||||||
/// See [geth's `ChainConfig`
|
/// See [geth's `ChainConfig`
|
||||||
/// struct](https://github.com/ethereum/go-ethereum/blob/64dccf7aa411c5c7cd36090c3d9b9892945ae813/params/config.go#L349)
|
/// struct](https://github.com/ethereum/go-ethereum/blob/64dccf7aa411c5c7cd36090c3d9b9892945ae813/params/config.go#L349)
|
||||||
/// for the source of each field.
|
/// for the source of each field.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
pub struct ChainConfig {
|
pub struct ChainConfig {
|
||||||
/// The network's chain ID.
|
/// The network's chain ID.
|
||||||
|
@ -248,11 +252,11 @@ const fn one() -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Empty consensus configuration for proof-of-work networks.
|
/// Empty consensus configuration for proof-of-work networks.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct EthashConfig {}
|
pub struct EthashConfig {}
|
||||||
|
|
||||||
/// Consensus configuration for Clique.
|
/// Consensus configuration for Clique.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct CliqueConfig {
|
pub struct CliqueConfig {
|
||||||
/// Number of seconds between blocks to enforce.
|
/// Number of seconds between blocks to enforce.
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -265,7 +269,12 @@ pub struct CliqueConfig {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Genesis, H256};
|
use super::{ChainConfig, Genesis, GenesisAccount, H256};
|
||||||
|
use crate::{
|
||||||
|
types::{Address, Bytes, H160, U256},
|
||||||
|
utils::EthashConfig,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_hive_genesis() {
|
fn parse_hive_genesis() {
|
||||||
|
@ -611,6 +620,204 @@ mod tests {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let _genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
|
let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
|
||||||
|
let alloc_entry = genesis
|
||||||
|
.alloc
|
||||||
|
.get(&H160::from_str("0000000000000000000000000000000000000314").unwrap())
|
||||||
|
.expect("missing account for parsed genesis");
|
||||||
|
let storage = alloc_entry.storage.as_ref().expect("missing storage for parsed genesis");
|
||||||
|
let expected_storage = HashMap::from_iter(vec![
|
||||||
|
(
|
||||||
|
H256::from_str(
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
H256::from_str(
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000001234",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
H256::from_str(
|
||||||
|
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
H256::from_str(
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
assert_eq!(storage, &expected_storage);
|
||||||
|
|
||||||
|
let expected_code = Bytes::from_str("0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029").unwrap();
|
||||||
|
let code = alloc_entry.code.as_ref().expect("missing code for parsed genesis");
|
||||||
|
assert_eq!(code, &expected_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hive_smoke_alloc_deserialize() {
|
||||||
|
let hive_genesis = r#"
|
||||||
|
{
|
||||||
|
"nonce": "0x0000000000000042",
|
||||||
|
"difficulty": "0x2123456",
|
||||||
|
"mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
|
||||||
|
"coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"timestamp": "0x123456",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"extraData": "0xfafbfcfd",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"alloc": {
|
||||||
|
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
|
||||||
|
"balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
},
|
||||||
|
"e6716f9544a56c530d868e4bfbacb172315bdead": {
|
||||||
|
"balance": "0x11",
|
||||||
|
"code": "0x12"
|
||||||
|
},
|
||||||
|
"b9c015918bdaba24b4ff057a92a3873d6eb201be": {
|
||||||
|
"balance": "0x21",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
|
||||||
|
"balance": "0x31",
|
||||||
|
"nonce": "0x32"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000001": {
|
||||||
|
"balance": "0x41"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000002": {
|
||||||
|
"balance": "0x51"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000003": {
|
||||||
|
"balance": "0x61"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000004": {
|
||||||
|
"balance": "0x71"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"ethash": {},
|
||||||
|
"chainId": 10,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let expected_genesis = Genesis {
|
||||||
|
nonce: 0x0000000000000042.into(),
|
||||||
|
difficulty: 0x2123456.into(),
|
||||||
|
mix_hash: H256::from_str("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234").unwrap(),
|
||||||
|
coinbase: Address::from_str("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(),
|
||||||
|
timestamp: 0x123456.into(),
|
||||||
|
parent_hash: Some(H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000000").unwrap()),
|
||||||
|
extra_data: Bytes::from_str("0xfafbfcfd").unwrap(),
|
||||||
|
gas_limit: 0x2fefd8.into(),
|
||||||
|
alloc: HashMap::from_iter(vec![
|
||||||
|
(
|
||||||
|
Address::from_str("0xdbdbdb2cbd23b783741e8d7fcf51e459b497e4a6").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0xe6716f9544a56c530d868e4bfbacb172315bdead").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x11").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: Some(Bytes::from_str("0x12").unwrap()),
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0xb9c015918bdaba24b4ff057a92a3873d6eb201be").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x21").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: Some(HashMap::from_iter(vec![
|
||||||
|
(
|
||||||
|
H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000001").unwrap(),
|
||||||
|
H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000022").unwrap(),
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0x1a26338f0d905e295fccb71fa9ea849ffa12aaf4").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x31").unwrap(),
|
||||||
|
nonce: Some(0x32u64),
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0x0000000000000000000000000000000000000001").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x41").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0x0000000000000000000000000000000000000002").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x51").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0x0000000000000000000000000000000000000003").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x61").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Address::from_str("0x0000000000000000000000000000000000000004").unwrap(),
|
||||||
|
GenesisAccount {
|
||||||
|
balance: U256::from_str("0x71").unwrap(),
|
||||||
|
nonce: None,
|
||||||
|
code: None,
|
||||||
|
storage: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
config: ChainConfig {
|
||||||
|
ethash: Some(EthashConfig{}),
|
||||||
|
chain_id: 10,
|
||||||
|
homestead_block: Some(0),
|
||||||
|
eip150_block: Some(0),
|
||||||
|
eip155_block: Some(0),
|
||||||
|
eip158_block: Some(0),
|
||||||
|
byzantium_block: Some(0),
|
||||||
|
constantinople_block: Some(0),
|
||||||
|
petersburg_block: Some(0),
|
||||||
|
istanbul_block: Some(0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let deserialized_genesis: Genesis = serde_json::from_str(hive_genesis).unwrap();
|
||||||
|
assert_eq!(deserialized_genesis, expected_genesis, "deserialized genesis {deserialized_genesis:#?} does not match expected {expected_genesis:#?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,12 @@ pub use rlp;
|
||||||
/// Re-export hex
|
/// Re-export hex
|
||||||
pub use hex;
|
pub use hex;
|
||||||
|
|
||||||
use crate::types::{Address, ParseI256Error, I256, U256, U64};
|
use crate::types::{Address, Bytes, ParseI256Error, H256, I256, U256, U64};
|
||||||
use elliptic_curve::sec1::ToEncodedPoint;
|
use elliptic_curve::sec1::ToEncodedPoint;
|
||||||
use ethabi::ethereum_types::FromDecStrErr;
|
use ethabi::ethereum_types::FromDecStrErr;
|
||||||
use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
|
use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt,
|
fmt,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
@ -468,6 +469,47 @@ pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: Vec<Vec<U256>>
|
||||||
(max_fee_per_gas, max_priority_fee_per_gas)
|
(max_fee_per_gas, max_priority_fee_per_gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a Bytes value into a H256, accepting inputs that are less than 32 bytes long. These
|
||||||
|
/// inputs will be left padded with zeros.
|
||||||
|
pub fn from_bytes_to_h256<'de, D>(bytes: Bytes) -> Result<H256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if bytes.0.len() > 32 {
|
||||||
|
return Err(serde::de::Error::custom("input too long to be a H256"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// left pad with zeros to 32 bytes
|
||||||
|
let mut padded = [0u8; 32];
|
||||||
|
padded[32 - bytes.0.len()..].copy_from_slice(&bytes.0);
|
||||||
|
|
||||||
|
// then convert to H256 without a panic
|
||||||
|
Ok(H256::from_slice(&padded))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes the input into an Option<HashMap<H256, H256>>, using from_unformatted_hex to
|
||||||
|
/// deserialize the keys and values.
|
||||||
|
pub fn from_unformatted_hex_map<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<Option<HashMap<H256, H256>>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let map = Option::<HashMap<Bytes, Bytes>>::deserialize(deserializer)?;
|
||||||
|
match map {
|
||||||
|
Some(mut map) => {
|
||||||
|
let mut res_map = HashMap::new();
|
||||||
|
for (k, v) in map.drain() {
|
||||||
|
let k_deserialized = from_bytes_to_h256::<'de, D>(k)?;
|
||||||
|
let v_deserialized = from_bytes_to_h256::<'de, D>(v)?;
|
||||||
|
res_map.insert(k_deserialized, v_deserialized);
|
||||||
|
}
|
||||||
|
Ok(Some(res_map))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with
|
/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with
|
||||||
/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number).
|
/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number).
|
||||||
pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
|
Loading…
Reference in New Issue