From ae5935e6b2692cb06118eebe4fe03094592cb9f3 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 12 Oct 2022 17:43:01 +0200 Subject: [PATCH] feat: reth binary and parsing of eth chain tests (#38) * Scaffolding for reth test binary. wip for blockchain tests * wip models for chain json tests * reth binary and chain test * fmt,clippy * Update bin/reth/src/lib.rs Co-authored-by: Bjerg * Update bin/reth/src/lib.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/mod.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/mod.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/mod.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/models.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/models.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/mod.rs Co-authored-by: Bjerg * Update bin/reth/src/test_eth_chain/runner.rs Co-authored-by: Bjerg * Move JsonU256 to primitives * fmt * Use eyre * nits Co-authored-by: Bjerg --- Cargo.lock | 130 ++++++++- Cargo.toml | 13 +- bin/reth.rs | 3 - bin/reth/Cargo.toml | 19 ++ bin/reth/src/cli.rs | 36 +++ bin/reth/src/lib.rs | 17 ++ bin/reth/src/main.rs | 9 + bin/reth/src/test_eth_chain/mod.rs | 52 ++++ bin/reth/src/test_eth_chain/models.rs | 391 ++++++++++++++++++++++++++ bin/reth/src/test_eth_chain/runner.rs | 13 + bin/reth/src/util/mod.rs | 11 + crates/executor/Cargo.toml | 2 +- crates/primitives/Cargo.toml | 6 +- crates/primitives/src/block.rs | 9 +- crates/primitives/src/jsonu256.rs | 94 +++++++ crates/primitives/src/lib.rs | 2 + 16 files changed, 789 insertions(+), 18 deletions(-) delete mode 100644 bin/reth.rs create mode 100644 bin/reth/Cargo.toml create mode 100644 bin/reth/src/cli.rs create mode 100644 bin/reth/src/lib.rs create mode 100644 bin/reth/src/main.rs create mode 100644 bin/reth/src/test_eth_chain/mod.rs create mode 100644 bin/reth/src/test_eth_chain/models.rs create mode 100644 bin/reth/src/test_eth_chain/runner.rs create mode 100644 bin/reth/src/util/mod.rs create mode 100644 crates/primitives/src/jsonu256.rs diff --git a/Cargo.lock b/Cargo.lock index 4155af63a..038a13ca1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,17 @@ dependencies = [ "syn", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "auto_impl" version = "1.0.1" @@ -263,6 +274,43 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385007cbbed899260395a4107435fead4cad80684461b3cc78238bdcb0bad58f" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "const-oid" version = "0.9.0" @@ -526,6 +574,16 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -965,6 +1023,12 @@ dependencies = [ "syn", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.1" @@ -1457,6 +1521,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + [[package]] name = "page_size" version = "0.4.2" @@ -1716,6 +1786,18 @@ dependencies = [ [[package]] name = "reth" version = "0.1.0" +dependencies = [ + "clap", + "eyre", + "reth-primitives", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "walkdir", +] [[package]] name = "reth-crate-template" @@ -1751,8 +1833,8 @@ dependencies = [ name = "reth-executor" version = "0.1.0" dependencies = [ - "anyhow", "async-trait", + "eyre", "reth-interfaces", "reth-primitives", "revm", @@ -1792,6 +1874,8 @@ dependencies = [ "bytes", "ethers-core", "fastrlp", + "serde", + "serde_json", ] [[package]] @@ -2026,6 +2110,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.20" @@ -2263,6 +2356,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -2335,6 +2434,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.37" @@ -2544,6 +2652,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -2673,6 +2792,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 4a98481bb..88e6241b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,6 @@ -[package] -name = "reth" -version = "0.1.0" -edition = "2021" - [workspace] members = [ + "bin/reth", "crate-template", "crates/db", "crates/executor", @@ -19,9 +15,4 @@ members = [ "crates/transaction-pool", "crates/db" ] - -[dependencies] - -[[bin]] -path = "bin/reth.rs" -name = "reth" +default-members = ["bin/reth"] diff --git a/bin/reth.rs b/bin/reth.rs deleted file mode 100644 index e7a11a969..000000000 --- a/bin/reth.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml new file mode 100644 index 000000000..2a00ce0ce --- /dev/null +++ b/bin/reth/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "reth" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/foundry-rs/reth" +readme = "README.md" + +[dependencies] +eyre = "0.6.8" +clap = { version = "4.0", features = ["derive"] } +thiserror = "1.0" +tracing = "0.1" +tracing-futures = "0.2" +tokio = { version = "1.21", features = ["sync", "macros", "rt-multi-thread"] } +serde = "1.0" +serde_json = "1.0" +reth-primitives = { path = "../../crates/primitives" } +walkdir = "2.3" diff --git a/bin/reth/src/cli.rs b/bin/reth/src/cli.rs new file mode 100644 index 000000000..e94b2330c --- /dev/null +++ b/bin/reth/src/cli.rs @@ -0,0 +1,36 @@ +use clap::{ArgAction, Parser, Subcommand}; + +use crate::test_eth_chain; + +/// main function that parses cli and runs command +pub async fn run() -> eyre::Result<()> { + let opt = Cli::parse(); + + match opt.command { + Commands::TestEthChain(command) => command.execute().await, + } +} + +/// Commands to be executed +#[derive(Subcommand)] +pub enum Commands { + /// Runs Ethereum blockchain tests + #[command(name = "test-chain")] + TestEthChain(test_eth_chain::Command), +} + +#[derive(Parser)] +#[command(author, version="0.1", about="Reth binary", long_about = None)] +struct Cli { + /// The command to run + #[clap(subcommand)] + command: Commands, + + /// Use verbose output + #[clap(short, long, action = ArgAction::Count, global = true)] + verbose: u8, + + /// Silence all output + #[clap(short, long, global = true)] + silent: bool, +} diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs new file mode 100644 index 000000000..085d1bb33 --- /dev/null +++ b/bin/reth/src/lib.rs @@ -0,0 +1,17 @@ +#![warn(missing_docs, unreachable_pub)] +#![deny(unused_must_use, rust_2018_idioms)] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] + +//! Rust Ethereum (reth) binary executable. + +/// CLI definition and entrypoint +pub mod cli; + +/// Utility functions. +pub mod util; + +/// Command for executing Ethereum blockchain tests +pub mod test_eth_chain; diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs new file mode 100644 index 000000000..079f39258 --- /dev/null +++ b/bin/reth/src/main.rs @@ -0,0 +1,9 @@ +use tracing::error; + +#[tokio::main] +async fn main() { + if let Err(err) = reth::cli::run().await { + error!("Error: {:?}", err); + std::process::exit(1); + } +} diff --git a/bin/reth/src/test_eth_chain/mod.rs b/bin/reth/src/test_eth_chain/mod.rs new file mode 100644 index 000000000..41a21b129 --- /dev/null +++ b/bin/reth/src/test_eth_chain/mod.rs @@ -0,0 +1,52 @@ +use crate::util; +use clap::Parser; +use std::path::PathBuf; +/// Models for parsing JSON blockchain tests +pub mod models; +/// Ethereum blockhain test runner +pub mod runner; + +/// Execute Ethereum blockchain tests by specifying path to json files +#[derive(Debug, Parser)] +pub struct Command { + /// Path to Ethereum JSON test files + path: Vec, +} + +impl Command { + /// Execute the command + pub async fn execute(self) -> eyre::Result<()> { + // note the use of `into_iter()` to consume `items` + let task_group: Vec<_> = self + .path + .iter() + .map(|item| { + util::find_all_json_tests(item).into_iter().map(|file| { + let tfile = file.clone(); + let join = tokio::spawn(async move { runner::run_test(tfile.as_path()).await }); + (join, file) + }) + }) + .collect(); + // await the tasks for resolve's to complete and give back our test results + let mut num_of_failed = 0; + let mut num_of_passed = 0; + for tasks in task_group { + for (join, file) in tasks { + match join.await.unwrap() { + Ok(_) => { + num_of_passed += 1; + } + Err(error) => { + num_of_failed += 1; + println!("Test {:?} failed:\n {error}\n", file); + } + } + } + } + + println!("\nPASSED {num_of_passed}/{} tests\n", num_of_passed + num_of_failed); + + Ok(()) + } +} diff --git a/bin/reth/src/test_eth_chain/models.rs b/bin/reth/src/test_eth_chain/models.rs new file mode 100644 index 000000000..016b6b0dd --- /dev/null +++ b/bin/reth/src/test_eth_chain/models.rs @@ -0,0 +1,391 @@ +use reth_primitives::{Address, Bloom, Bytes, JsonU256, H160, H256, H64}; +use serde::{self, Deserialize}; +use std::collections::BTreeMap; + +/// Blockchain test deserializer. +#[derive(Debug, PartialEq, Eq, Deserialize)] +pub struct Test(pub BTreeMap); + +/// Ethereum blockchain test data +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BlockchainTestData { + /// Genesis block header. + pub genesis_block_header: Header, + /// Genesis rlp. + #[serde(rename = "genesisRLP")] + pub genesis_rlp: Option, + /// Blocks. + pub blocks: Vec, + /// Post state. + pub post_state: Option, + /// Pre state. + pub pre: State, + /// Hash of best block. + pub lastblockhash: H256, + /// Network. + pub network: ForkSpec, + #[serde(default)] + /// Engine + pub self_engine: SealEngine, +} + +/// Ethereum blockchain test data Header. +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Header { + /// Bloom filter. + pub bloom: Bloom, + /// Coinbase. + pub coinbase: Address, + /// Difficulty. + pub difficulty: JsonU256, + /// Extra data. + pub extra_data: Bytes, + /// Gas limit. + pub gas_limit: JsonU256, + /// Gas used. + pub gas_used: JsonU256, + /// Block Hash. + pub hash: H256, + /// Mix hash. + pub mix_hash: H256, + /// Seal nonce. + pub nonce: H64, + /// Block number. + pub number: JsonU256, + /// Parent hash. + pub parent_hash: H256, + /// Receipt trie. + pub receipt_trie: H256, + /// State root. + pub state_root: H256, + /// Timestamp. + pub timestamp: JsonU256, + /// Transactions trie. + pub transactions_trie: H256, + /// Uncle hash. + pub uncle_hash: H256, + /// Base fee per gas. + pub base_fee_per_gas: Option, +} + +/// Ethereum blockchain test data Block. +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub struct Block { + /// Block header. + pub block_header: Option
, + /// Rlp block bytes + pub rlp: Bytes, + /// Transactions + pub transactions: Option>, + /// Uncle/Omner headers. + pub uncle_headers: Option>, +} + +/// Ethereum blockchain test data State. +#[derive(Clone, Debug, Eq, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct State(pub RootOrState); + +/// Merkle root hash or storage accounts. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +#[serde(untagged)] +pub enum RootOrState { + /// If state is too big, only state root is present + Root(H256), + /// Staet + State(BTreeMap), +} + +/// Spec account +#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct Account { + /// Balance. + pub balance: JsonU256, + /// Code. + pub code: Bytes, + /// Nonce. + pub nonce: JsonU256, + /// Storage. + pub storage: BTreeMap, +} + +/// Ethereum blockchain test data State. +#[derive(Debug, PartialEq, Eq, Deserialize)] +pub enum ForkSpec { + /// Fork EIP150. + EIP150, + /// Fork EIP158. + EIP158, + /// Fork Frontier. + Frontier, + /// Fork Homestead. + Homestead, + /// Fork Byzantium. + Byzantium, + /// Fork Constantinople. + Constantinople, + /// Fork ConstantinopleFix. + ConstantinopleFix, + /// Fork Istanbul. + Istanbul, + /// Fork EIP158ToByzantiumAt5. + EIP158ToByzantiumAt5, + /// Fork FrontierToHomesteadAt5. + FrontierToHomesteadAt5, + /// Fork HomesteadToDaoAt5. + HomesteadToDaoAt5, + /// Fork HomesteadToEIP150At5. + HomesteadToEIP150At5, + /// Fork ByzantiumToConstantinopleAt5. + ByzantiumToConstantinopleAt5, + /// Fork ByzantiumToConstantinopleFixAt5. + ByzantiumToConstantinopleFixAt5, + /// Fork Berlin. + Berlin, + /// Fork London. + London, + /// Fork BerlinToLondonAt5. + BerlinToLondonAt5, + /// Fork Merge, + Merge, +} + +/// Json Block test possible engine kind. +#[derive(Debug, PartialEq, Eq, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum SealEngine { + /// No consensus checks. + #[default] + NoProof, +} + +/// Ethereum blockchain test transaction data. +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Transaction type + #[serde(rename = "type")] + pub transaction_type: Option, + /// Data. + pub data: Bytes, + /// Gas limit. + pub gas_limit: JsonU256, + /// Gas price. + pub gas_price: Option, + /// Nonce. + pub nonce: JsonU256, + /// Signature r part. + pub r: JsonU256, + /// Signature s part. + pub s: JsonU256, + /// Parity bit. + pub v: JsonU256, + /// Transaction value. + pub value: JsonU256, + /// Chain ID. + pub chain_id: Option, + /// Access list. + pub access_list: Option, + /// Max fee per gas. + pub max_fee_per_gas: Option, + /// Max priority fee per gas + pub max_priority_fee_per_gas: Option, + /// Transaction hash. + pub hash: Option, +} + +/// Access list item +#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AccessListItem { + /// Account address + pub address: H160, + /// Storage key. + pub storage_keys: Vec, +} + +/// Access list. +pub type AccessList = Vec; + +#[cfg(test)] +mod test { + use super::*; + use serde_json; + + #[test] + fn blockchain_test_deserialize() { + let test = r#"{ + "evmBytecode_d0g0v0_Berlin" : { + "_info" : { + "comment" : "", + "filling-rpc-server" : "evm version 1.10.18-unstable-53304ff6-20220503", + "filling-tool-version" : "retesteth-0.2.2-testinfo+commit.05e0b8ca.Linux.g++", + "generatedTestHash" : "0951de8d9e6b2a08e57234f57ef719a17aee9d7e9d7e852e454a641028b791a9", + "lllcversion" : "Version: 0.5.14-develop.2021.11.27+commit.401d5358.Linux.g++", + "solidity" : "Version: 0.8.5+commit.a4f2e591.Linux.g++", + "source" : "src/GeneralStateTestsFiller/stBugs/evmBytecodeFiller.json", + "sourceHash" : "6ced7b43100305d1cc5aee48344c0eab6002940358e2c126279ef8444c2dea5a" + }, + "blocks" : [ + { + "blockHeader" : { + "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x1000000000000000000000000000000000000000", + "difficulty" : "0x020000", + "extraData" : "0x00", + "gasLimit" : "0x54a60a4202e088", + "gasUsed" : "0x01d4c0", + "hash" : "0xb8152a06d2018ed9a1eb1eb6e1fe8c3478d8eae5b04d743bf4a1ec699510cfe5", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce" : "0x0000000000000000", + "number" : "0x01", + "parentHash" : "0xb835c89a42605cfcc542381145b83c826caf10823b81af0f45091040a67e6601", + "receiptTrie" : "0x0ef77336cf7bfbd2c500dcefe7b48d0ef7896d38f6373fbeb301ea4dac3746a7", + "stateRoot" : "0x27bf1aca92967ecd83e11c52887203bbdcab73a27fe07e814cf749fa50483a53", + "timestamp" : "0x03e8", + "transactionsTrie" : "0x9008a2d4af552fea9b45675cd2af6d4117303b57da25b28438ccd1f6bad6828d", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "rlp" : "0xf90264f901fca0b835c89a42605cfcc542381145b83c826caf10823b81af0f45091040a67e6601a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347941000000000000000000000000000000000000000a027bf1aca92967ecd83e11c52887203bbdcab73a27fe07e814cf749fa50483a53a09008a2d4af552fea9b45675cd2af6d4117303b57da25b28438ccd1f6bad6828da00ef77336cf7bfbd2c500dcefe7b48d0ef7896d38f6373fbeb301ea4dac3746a7b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018754a60a4202e0888301d4c08203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a8301d4c094b94f5374fce5edbc8e2a8697c15331677e6ebf0b80801ca0f3b41c283c02ed98318dc9cac3f0ddc3de2f2f7853a03299a46f22a7c6726c3aa0396ccb5968a532ea070924408625d3e36d4db21bfbd0cb070ba9e1fe9dba58abc0", + "transactions" : [ + { + "data" : "0x", + "gasLimit" : "0x01d4c0", + "gasPrice" : "0x0a", + "nonce" : "0x00", + "r" : "0xf3b41c283c02ed98318dc9cac3f0ddc3de2f2f7853a03299a46f22a7c6726c3a", + "s" : "0x396ccb5968a532ea070924408625d3e36d4db21bfbd0cb070ba9e1fe9dba58ab", + "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "to" : "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "v" : "0x1c", + "value" : "0x00" + } + ], + "uncleHeaders" : [ + ] + } + ], + "genesisBlockHeader" : { + "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x1000000000000000000000000000000000000000", + "difficulty" : "0x020000", + "extraData" : "0x00", + "gasLimit" : "0x54a60a4202e088", + "gasUsed" : "0x00", + "hash" : "0xb835c89a42605cfcc542381145b83c826caf10823b81af0f45091040a67e6601", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce" : "0x0000000000000000", + "number" : "0x00", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0x642a369a4a9dbf57d83ba05413910a5dd2cff93858c68e9e8293a8fffeae8660", + "timestamp" : "0x00", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347941000000000000000000000000000000000000000a0642a369a4a9dbf57d83ba05413910a5dd2cff93858c68e9e8293a8fffeae8660a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808754a60a4202e088808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0", + "lastblockhash" : "0xb8152a06d2018ed9a1eb1eb6e1fe8c3478d8eae5b04d743bf4a1ec699510cfe5", + "network" : "Berlin", + "postState" : { + "0x1000000000000000000000000000000000000000" : { + "balance" : "0x1bc16d674eda4f80", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x38beec8feeb7d618", + "code" : "0x", + "nonce" : "0x01", + "storage" : { + } + }, + "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x00", + "code" : "0x67ffffffffffffffff600160006000fb", + "nonce" : "0x3f", + "storage" : { + } + } + }, + "pre" : { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x38beec8feeca2598", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + } + }, + "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x00", + "code" : "0x67ffffffffffffffff600160006000fb", + "nonce" : "0x3f", + "storage" : { + } + } + }, + "sealEngine" : "NoProof" + } + }"#; + + let res = serde_json::from_str::(test); + assert!(res.is_ok(), "Failed to deserialize BlockchainTestData with error: {:?}", res); + } + + #[test] + fn header_deserialize() { + let test = r#"{ + "baseFeePerGas" : "0x0a", + "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "difficulty" : "0x020000", + "extraData" : "0x00", + "gasLimit" : "0x10000000000000", + "gasUsed" : "0x10000000000000", + "hash" : "0x7ebfee2a2c785fef181b8ffd92d4a48a0660ec000f465f309757e3f092d13882", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce" : "0x0000000000000000", + "number" : "0x01", + "parentHash" : "0xa8f2eb2ea9dccbf725801eef5a31ce59bada431e888dfd5501677cc4365dc3be", + "receiptTrie" : "0xbdd943f5c62ae0299324244a0f65524337ada9817e18e1764631cc1424f3a293", + "stateRoot" : "0xc9c6306ee3e5acbaabe8e2fa28a10c12e27bad1d1aacc271665149f70519f8b0", + "timestamp" : "0x03e8", + "transactionsTrie" : "0xf5893b055ca05e4f14d1792745586a1376e218180bd56bd96b2b024e1dc78300", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }"#; + let res = serde_json::from_str::
(test); + assert!(res.is_ok(), "Failed to deserialize Header with error: {:?}", res); + } + + #[test] + fn transaction_deserialize() { + let test = r#"[ + { + "accessList" : [ + ], + "chainId" : "0x01", + "data" : "0x693c61390000000000000000000000000000000000000000000000000000000000000000", + "gasLimit" : "0x10000000000000", + "maxFeePerGas" : "0x07d0", + "maxPriorityFeePerGas" : "0x00", + "nonce" : "0x01", + "r" : "0x5fecc3972a35c9e341b41b0c269d9a7325e13269fb01c2f64cbce1046b3441c8", + "s" : "0x7d4d0eda0e4ebd53c5d0b6fc35c600b317f8fa873b3963ab623ec9cec7d969bd", + "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "to" : "0xcccccccccccccccccccccccccccccccccccccccc", + "type" : "0x02", + "v" : "0x01", + "value" : "0x00" + } + ]"#; + + let res = serde_json::from_str::>(test); + assert!(res.is_ok(), "Failed to deserialize transactin with error: {:?}", res); + } +} diff --git a/bin/reth/src/test_eth_chain/runner.rs b/bin/reth/src/test_eth_chain/runner.rs new file mode 100644 index 000000000..8cf03ef01 --- /dev/null +++ b/bin/reth/src/test_eth_chain/runner.rs @@ -0,0 +1,13 @@ +use super::models::Test; +use std::path::Path; + +/// Run one JSON-encoded Ethereum blockchain test at the specified path. +pub async fn run_test(path: &Path) -> eyre::Result<()> { + let json_file = std::fs::read(path)?; + let suits: Test = serde_json::from_reader(&*json_file)?; + + for suit in suits.0 { + println!("TODO:{:?}", suit.0); + } + Ok(()) +} diff --git a/bin/reth/src/util/mod.rs b/bin/reth/src/util/mod.rs new file mode 100644 index 000000000..231e29337 --- /dev/null +++ b/bin/reth/src/util/mod.rs @@ -0,0 +1,11 @@ +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; + +pub(crate) fn find_all_json_tests(path: &Path) -> Vec { + WalkDir::new(path) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_name().to_string_lossy().ends_with(".json")) + .map(DirEntry::into_path) + .collect::>() +} diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 5d829609d..6444fdaee 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -12,4 +12,4 @@ reth-primitives = { path = "../primitives" } reth-interfaces = { path = "../interfaces" } async-trait = "0.1.57" thiserror = "1.0.37" -anyhow = "1.0" +eyre = "0.6.8" diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index c8b411d6e..d1b7da9ff 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -10,4 +10,8 @@ description = "Commonly used types in reth." [dependencies] fastrlp = { version = "0.1.3" } ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false } -bytes = "1.2" \ No newline at end of file +bytes = "1.2" +serde = "1.0" + +[dev-dependencies] +serde_json = "1.0" \ No newline at end of file diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 3a87796be..e1fa100da 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,4 +1,4 @@ -use crate::{Header, HeaderLocked, Receipt, Transaction, TransactionSigned}; +use crate::{Header, HeaderLocked, Receipt, Transaction, TransactionSigned, H256}; use std::ops::Deref; /// Ethereum full block. @@ -30,6 +30,13 @@ pub struct BlockLocked { pub receipts: Vec, } +impl BlockLocked { + /// Header hash. + pub fn hash(&self) -> H256 { + self.header.hash() + } +} + impl Deref for BlockLocked { type Target = Header; fn deref(&self) -> &Self::Target { diff --git a/crates/primitives/src/jsonu256.rs b/crates/primitives/src/jsonu256.rs new file mode 100644 index 000000000..f01be550d --- /dev/null +++ b/crates/primitives/src/jsonu256.rs @@ -0,0 +1,94 @@ +use crate::U256; +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, str::FromStr}; + +/// Wrapper around primitive U256 type to handle edge cases of json parser +#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct JsonU256(pub U256); + +impl Serialize for JsonU256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.to_string().serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for JsonU256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(JsonU256Visitor) + } +} + +struct JsonU256Visitor; + +impl<'a> Visitor<'a> for JsonU256Visitor { + type Value = JsonU256; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a hex encoding or decimal number") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + Ok(JsonU256(U256::from(value))) + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + let value = match value.len() { + 0 => U256::from(0), + 2 if value.starts_with("0x") => U256::zero(), + _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|e| { + Error::custom(format!("Parsin JsonU256 as hex failed {}: {}", value, e)) + })?, + _ => U256::from_dec_str(value).map_err(|e| { + Error::custom(format!("Parsin JsonU256 as decimal failed {}: {:?}", value, e)) + })?, + }; + + Ok(JsonU256(value)) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } +} + +#[cfg(test)] +mod test { + use super::JsonU256; + use crate::U256; + use serde_json; + + #[test] + fn jsonu256_deserialize() { + let deserialized: Vec = + serde_json::from_str(r#"["","0", "0x","10",10,"0x10"]"#).unwrap(); + assert_eq!( + deserialized, + vec![ + JsonU256(U256::from(0)), + JsonU256(U256::from(0)), + JsonU256(U256::from(0)), + JsonU256(U256::from(10)), + JsonU256(U256::from(10)), + JsonU256(U256::from(16)), + ] + ); + } +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 6405977cc..37c5f83a2 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -11,6 +11,7 @@ mod account; mod block; mod chain; mod header; +mod jsonu256; mod log; mod receipt; mod transaction; @@ -19,6 +20,7 @@ pub use account::Account; pub use block::{Block, BlockLocked}; pub use chain::Chain; pub use header::{Header, HeaderLocked}; +pub use jsonu256::JsonU256; pub use log::Log; pub use receipt::Receipt; pub use transaction::{AccessList, AccessListItem, Transaction, TransactionSigned, TxType};