From 8e80bfa5ff4295cb4b9bb31620b3e70f1e1fcf3f Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:35:34 +0100 Subject: [PATCH 1/9] mark: 0xaatif/trace-decoder-tests From b4d219ea05a748bb4e0f76cd101efb6805d69e72 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:42:19 +0100 Subject: [PATCH 2/9] chore: remove old tests --- trace_decoder/tests/trace_decoder_tests.rs | 281 --------------------- 1 file changed, 281 deletions(-) delete mode 100644 trace_decoder/tests/trace_decoder_tests.rs diff --git a/trace_decoder/tests/trace_decoder_tests.rs b/trace_decoder/tests/trace_decoder_tests.rs deleted file mode 100644 index 6db98749f..000000000 --- a/trace_decoder/tests/trace_decoder_tests.rs +++ /dev/null @@ -1,281 +0,0 @@ -//! Tests to check the parsing/decoding and `GenerationInputs` validity. -//! They rely on the jerigon and cdk erigon witness files as input. - -use std::time::Duration; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use alloy::rpc::types::eth::Header; -use anyhow::Context as _; -use evm_arithmetization::prover::testing::simulate_execution_all_segments; -use evm_arithmetization::GenerationInputs; -use itertools::Itertools; -use log::info; -use mpt_trie::partial_trie::PartialTrie; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::util::timing::TimingTree; -use plonky2_maybe_rayon::*; -use pretty_env_logger::env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use prover::BlockProverInput; -use rstest::rstest; -use trace_decoder::OtherBlockData; - -type F = GoldilocksField; - -const JERIGON_WITNESS_DIR: &str = "tests/data/witnesses/zero_jerigon"; -///TODO Add CDK Erigon witness test data. -/// Local [cdk erigon](https://github.com/0xPolygonHermez/cdk-erigon?tab=readme-ov-file#running-cdk-erigon) dev network -/// could be used for basic witness generation. -/// Related work for type2 prover is on the [type2_cancun](https://github.com/0xPolygonZero/zk_evm/pull/319) branch at the moment. -/// When the cdk erigon witness data is added, enable test execution for -/// `CDK_ERIGON_WITNESS_DIR` -//const CDK_ERIGON_WITNESS_DIR: &str = -// "tests/data/witnesses/hermez_cdk_erigon"; - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} - -fn find_witness_data_files(dir: &str) -> anyhow::Result> { - let read_dir = fs::read_dir(dir)?; - read_dir - .into_iter() - .map(|dir_entry| dir_entry.map(|it| it.path())) - .filter_ok(|path| { - !path - .to_str() - .expect("valid str file path") - .contains("header") - }) - .collect::, _>>() - .context(format!("Failed to find witness files in dir {dir}")) -} - -fn read_witness_file(file_path: &Path) -> anyhow::Result> { - let witness = fs::File::open(file_path).context("Unable to read file")?; - let mut reader = std::io::BufReader::new(witness); - let jd = &mut serde_json::Deserializer::from_reader(&mut reader); - serde_path_to_error::deserialize(jd).context(format!( - "Failed to deserialize json file {}", - file_path.display() - )) -} - -fn derive_header_file_path(witness_file_path: &Path) -> Result { - let mut header_file_path = witness_file_path.to_path_buf(); - header_file_path.set_extension(""); - let mut block_header_file_name = header_file_path - .file_name() - .context("Invalid header file name")? - .to_os_string(); - block_header_file_name.push("_header.json"); - header_file_path.set_file_name(block_header_file_name); - Ok(header_file_path) -} - -fn decode_generation_inputs( - block_prover_input: BlockProverInput, - use_burn_addr: bool, -) -> anyhow::Result> { - let block_num = block_prover_input.other_data.b_data.b_meta.block_number; - let trace_decoder_output = trace_decoder::entrypoint( - block_prover_input.block_trace, - block_prover_input.other_data.clone(), - 3, - use_burn_addr, - ) - .context(format!( - "Failed to execute trace decoder on block {}", - block_num - ))? - .into_iter() - .collect::>(); - Ok(trace_decoder_output) -} - -fn verify_generation_inputs( - header: &Header, - other: &OtherBlockData, - generation_inputs: Vec, -) -> anyhow::Result<()> { - assert!(generation_inputs.len() >= 2); - assert_eq!( - other.checkpoint_state_trie_root, - generation_inputs - .first() - .expect("generation inputs should have first element") - .tries - .state_trie - .hash() - ); - assert!(generation_inputs - .windows(2) - .map(|inputs| { - inputs[0].trie_roots_after.state_root == inputs[1].tries.state_trie.hash() - && inputs[0].trie_roots_after.receipts_root == inputs[1].tries.receipts_trie.hash() - && inputs[0].trie_roots_after.transactions_root - == inputs[1].tries.transactions_trie.hash() - }) - .all(|it| it)); - let last_generation_input = generation_inputs - .last() - .expect("generation inputs should have last element"); - assert_eq!( - last_generation_input.trie_roots_after.state_root.0, - header.state_root.0 - ); - // Some block metadata sanity checks - assert_eq!( - last_generation_input - .block_metadata - .block_timestamp - .as_u64(), - header.timestamp - ); - // Block hash check - assert_eq!( - last_generation_input.block_hashes.cur_hash.as_bytes(), - &header.hash.to_vec() - ); - // Previous block hash check - assert_eq!( - last_generation_input - .block_hashes - .prev_hashes - .last() - .expect("Valid last hash") - .as_bytes(), - &header.parent_hash.to_vec() - ); - info!( - "Block {} GenerationInputs valid", - other.b_data.b_meta.block_number - ); - Ok(()) -} - -/// This test aims at ensuring that the decoder can properly parse a block trace -/// received from Jerigon and CDK Erigon into zkEVM `GenerationInputs`, which -/// the prover can then pick to prove each transaction in the block -/// independently. -/// -/// This test only `simulates` the zkEVM CPU, i.e. does not generate STARK -/// traces nor generates proofs, as its purpose is to be runnable easily in the -/// CI even in `debug` mode. -#[rstest] -#[case(JERIGON_WITNESS_DIR)] -//#[case(CDK_ERIGON_WITNESS_DIR)] -fn test_parsing_decoding_proving(#[case] test_witness_directory: &str) { - init_logger(); - - // TODO: https://github.com/0xPolygonZero/zk_evm/issues/565 - // Once CDK_ERIGON_WITNESS_DIR is available, change this so - // `use_burn_addr` is only true in that case. - let use_burn_addr = test_witness_directory != JERIGON_WITNESS_DIR; - let results = find_witness_data_files(test_witness_directory) - .expect("valid json data files found") - .into_iter() - .map(|file_path| { - { - // Read one json witness file for this block and get list of BlockProverInputs - read_witness_file(&file_path) - } - }) - .map_ok(|block_prover_inputs| { - block_prover_inputs.into_iter().map(|block_prover_input| { - // Run trace decoder, create list of generation inputs - let block_generation_inputs = - decode_generation_inputs(block_prover_input, use_burn_addr)?; - block_generation_inputs - .into_par_iter() - .map(|generation_inputs| { - // For every generation input, simulate execution. - // Execution will be simulated in parallel. - // If system runs out of memory, limit the rayon - // with setting env variable RAYON_NUM_THREADS=. - let timing = TimingTree::new( - &format!( - "simulate zkEVM CPU for block {}, txns {:?}..{:?}.", - generation_inputs.block_metadata.block_number, - generation_inputs.txn_number_before, - generation_inputs.txn_number_before - + generation_inputs.signed_txns.len() - ), - log::Level::Info, - ); - simulate_execution_all_segments::(generation_inputs, 19)?; - timing.filter(Duration::from_millis(100)).print(); - Ok::<(), anyhow::Error>(()) - }) - .collect::, anyhow::Error>>() - }) - }) - .flatten_ok() - .map(|it| it?) - .collect::>>(); - - results.iter().for_each(|it| { - if let Err(e) = it { - panic!("Failed to run parsing decoding proving test: {e:?}"); - } - }); -} - -/// This test checks for the parsing and decoding of the block witness -/// received from Jerigon and CDK Erigon into zkEVM `GenerationInputs`, and -/// checks if trace decoder output generation inputs are valid and consistent. -#[rstest] -#[case(JERIGON_WITNESS_DIR)] -//#[case(CDK_ERIGON_WITNESS_DIR)] -fn test_generation_inputs_consistency(#[case] test_witness_directory: &str) { - init_logger(); - - // TODO: https://github.com/0xPolygonZero/zk_evm/issues/565 - // Once CDK_ERIGON_WITNESS_DIR is available, change this so - // `use_burn_addr` is only true in that case. - let use_burn_addr = test_witness_directory != JERIGON_WITNESS_DIR; - let result: Vec> = find_witness_data_files(test_witness_directory) - .expect("valid json data files found") - .into_iter() - .map(|file_path| { - { - // Read json header file of the block. We need it to check tracer output - // consistency - let header_file_path = derive_header_file_path(&file_path)?; - let header_file = fs::File::open(header_file_path.as_path()).context(format!( - "Unable to open header file {}", - header_file_path.display() - ))?; - let mut header_reader = std::io::BufReader::new(header_file); - let block_headers = serde_json::from_reader::<_, Vec
>(&mut header_reader) - .context(format!( - "Failed to deserialize header json file {}", - header_file_path.display() - ))?; - // Read one json witness file and get list of BlockProverInputs - let block_prover_inputs = read_witness_file(&file_path)?; - Ok(block_headers - .into_iter() - .zip(block_prover_inputs.into_iter())) - } - }) - .flatten_ok() - .map_ok(|(block_header, block_prover_input)| { - let other_block_data = block_prover_input.other_data.clone(); - // Run trace decoder, create generation inputs for this block - let block_generation_inputs = - decode_generation_inputs(block_prover_input, use_burn_addr)?; - // Verify generation inputs for this block - verify_generation_inputs(&block_header, &other_block_data, block_generation_inputs) - }) - .map(|it: Result, anyhow::Error>| it?) - .collect(); - - result.iter().for_each(|it| { - if let Err(e) = it { - panic!("Failed to verify generation inputs consistency: {e:?}"); - } - }); -} From 54e9a1d9a1a3abb265fe38c13bad0cae2e0de9a7 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:42:30 +0100 Subject: [PATCH 3/9] test: add new tests --- Cargo.lock | 112 +++++++++++------- trace_decoder/Cargo.toml | 14 ++- trace_decoder/tests/common/mod.rs | 82 +++++++++++++ trace_decoder/tests/consistent-with-header.rs | 66 +++++++++++ trace_decoder/tests/simulate-execution.rs | 34 ++++++ 5 files changed, 264 insertions(+), 44 deletions(-) create mode 100644 trace_decoder/tests/common/mod.rs create mode 100644 trace_decoder/tests/consistent-with-header.rs create mode 100644 trace_decoder/tests/simulate-execution.rs diff --git a/Cargo.lock b/Cargo.lock index 11feda934..65163cc93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,16 @@ dependencies = [ "strum", ] +[[package]] +name = "alloy-compat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6216c5ab5502a6824ec87a7bcd5c52ca14137f00b37dea3ab321a1656ff46383" +dependencies = [ + "alloy-primitives", + "ethereum-types", +] + [[package]] name = "alloy-consensus" version = "0.3.0" @@ -735,6 +745,30 @@ dependencies = [ "syn 2.0.76", ] +[[package]] +name = "assert2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31fea2b6e18dfe892863c3a0a68f9e005b0195565f3d55b8612946ebca789cc" +dependencies = [ + "assert2-macros", + "diff", + "is-terminal", + "yansi", +] + +[[package]] +name = "assert2-macros" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1ac052c642f6d94e4be0b33028b346b7ab809ea5432b584eb8859f12f7ad2c" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.76", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -1737,6 +1771,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.9.0" @@ -1923,6 +1963,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "eth_trie" version = "0.4.0" @@ -2249,12 +2295,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" - [[package]] name = "futures-util" version = "0.3.30" @@ -2859,6 +2899,18 @@ dependencies = [ "libc", ] +[[package]] +name = "libtest-mimic" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58" +dependencies = [ + "clap", + "escape8259", + "termcolor", + "threadpool", +] + [[package]] name = "linkme" version = "0.3.28" @@ -4006,12 +4058,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - [[package]] name = "reqwest" version = "0.12.7" @@ -4134,36 +4180,6 @@ dependencies = [ "zero_bin_common", ] -[[package]] -name = "rstest" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros", - "rustc_version 0.4.1", -] - -[[package]] -name = "rstest_macros" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d" -dependencies = [ - "cfg-if", - "glob", - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "relative-path", - "rustc_version 0.4.1", - "syn 2.0.76", - "unicode-ident", -] - [[package]] name = "ruint" version = "1.12.3" @@ -5092,10 +5108,13 @@ name = "trace_decoder" version = "0.6.0" dependencies = [ "alloy", + "alloy-compat", "anyhow", + "assert2", "bitflags 2.6.0", "bitvec", "bytes", + "camino", "ciborium", "ciborium-io", "copyvec", @@ -5104,10 +5123,12 @@ dependencies = [ "enum-as-inner", "ethereum-types", "evm_arithmetization", + "glob", "hex", "hex-literal", "itertools 0.13.0", "keccak-hash 0.10.0", + "libtest-mimic", "log", "mpt_trie", "nunny", @@ -5116,7 +5137,6 @@ dependencies = [ "pretty_env_logger", "prover", "rlp", - "rstest", "serde", "serde_json", "serde_path_to_error", @@ -5776,6 +5796,12 @@ dependencies = [ "time", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zero_bin_common" version = "0.1.0" diff --git a/trace_decoder/Cargo.toml b/trace_decoder/Cargo.toml index 4308a0ebe..78e0aa3ef 100644 --- a/trace_decoder/Cargo.toml +++ b/trace_decoder/Cargo.toml @@ -41,14 +41,26 @@ zk_evm_common = { workspace = true } [dev-dependencies] alloy = { workspace = true } +alloy-compat = "0.1.0" +assert2 = "0.3.15" +camino = "1.1.9" criterion = { workspace = true } +glob = "0.3.1" +libtest-mimic = "0.7.3" plonky2_maybe_rayon = { workspace = true } pretty_env_logger = { workspace = true } prover = { workspace = true } -rstest = "0.21.0" serde_json = { workspace = true } serde_path_to_error = { workspace = true } [[bench]] name = "block_processing" harness = false + +[[test]] +name = "consistent-with-header" +harness = false + +[[test]] +name = "simulate-execution" +harness = false diff --git a/trace_decoder/tests/common/mod.rs b/trace_decoder/tests/common/mod.rs new file mode 100644 index 000000000..a6369d86d --- /dev/null +++ b/trace_decoder/tests/common/mod.rs @@ -0,0 +1,82 @@ +use std::{fs::File, path::Path}; + +use alloy::rpc::types::Header; +use anyhow::{ensure, Context as _}; +use camino::Utf8Path; +use prover::BlockProverInput; +use serde::de::DeserializeOwned; +use trace_decoder::{BlockTrace, OtherBlockData}; + +pub fn cases() -> anyhow::Result> { + print!("loading test vectors..."); + let ret = glob::glob(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/data/witnesses/zero_jerigon/*_header.json" + )) + .expect("valid glob pattern") + .map(|res| { + let header_path = res.context("filesystem error discovering test vectors")?; + Case::load(&header_path).context(format!( + "couldn't load case for header {}", + header_path.display() + )) + }) + .collect(); + println!("done"); + ret +} + +/// Test cases consist of [`BlockProverInput`] collected from `zero_bin`'s `rpc` +/// command, and the corresponding block header, fetched directly over RPC. +/// +/// In directory above, the files are stored alongside one another, as, for +/// example: +/// - `b4_dev.json` +/// - `b4_dev_header.json` +pub struct Case { + /// `b4_dev`, in the above example. + /// + /// Used as a test identifier. + pub name: String, + pub header: Header, + pub trace: BlockTrace, + pub other: OtherBlockData, +} + +impl Case { + fn load(header_path: &Path) -> anyhow::Result { + let header_path = Utf8Path::from_path(header_path).context("non-UTF-8 path")?; + let base = Utf8Path::new( + header_path + .as_str() + .strip_suffix("_header.json") + .context("inconsistent header name")?, // sync with glob call + ); + // for some reason these are lists... + let mut headers = json::>(header_path)?; + let mut bpis = json::>(base.with_extension("json"))?; + ensure!(headers.len() == 1, "bad header file"); + ensure!(bpis.len() == 1, "bad bpi file"); + let BlockProverInput { + block_trace, + other_data, + } = bpis.remove(0); + anyhow::Ok(Case { + name: base.file_name().context("inconsistent base name")?.into(), + header: headers.remove(0), + trace: block_trace, + other: other_data, + }) + } +} + +fn json(path: impl AsRef) -> anyhow::Result { + fn _imp(path: impl AsRef) -> anyhow::Result { + let file = File::open(path)?; + Ok(serde_path_to_error::deserialize( + &mut serde_json::Deserializer::from_reader(file), + )?) + } + + _imp(&path).context(format!("couldn't load {}", path.as_ref().display())) +} diff --git a/trace_decoder/tests/consistent-with-header.rs b/trace_decoder/tests/consistent-with-header.rs new file mode 100644 index 000000000..2d1aeca24 --- /dev/null +++ b/trace_decoder/tests/consistent-with-header.rs @@ -0,0 +1,66 @@ +//! Check that the [`evm_arithmetization::GenerationInputs`] produced by +//! [`trace_decoder`] are consistent between each other, and with the block +//! header obtained over RPC. + +mod common; + +use alloy_compat::Compat as _; +use assert2::check; +use common::{cases, Case}; +use itertools::Itertools; +use libtest_mimic::{Arguments, Trial}; +use mpt_trie::partial_trie::PartialTrie as _; + +fn main() -> anyhow::Result<()> { + let mut trials = vec![]; + for Case { + name, + header, + trace, + other, + } in cases()? + { + trials.push(Trial::test(name, move || { + let gis = trace_decoder::entrypoint(trace.clone(), other.clone(), 3, false) + .map_err(|e| format!("{e:?}"))?; // get the full cause chain + check!(gis.len() >= 2); + check!(other.checkpoint_state_trie_root == gis[0].tries.state_trie.hash()); + let pairs = || gis.iter().tuple_windows::<(_, _)>(); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.state_root != after.tries.state_trie.hash() + }) == None + ); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.receipts_root != after.tries.receipts_trie.hash() + }) == None + ); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.transactions_root + != after.tries.transactions_trie.hash() + }) == None + ); + check!( + gis.last().map(|it| it.trie_roots_after.state_root.compat()) + == Some(header.state_root) + ); + check!( + gis.iter() + .position(|it| it.block_metadata.block_timestamp != header.timestamp.into()) + == None + ); + check!( + gis.iter().position(|it| it + .block_hashes + .prev_hashes + .last() + .is_some_and(|it| *it != header.parent_hash.compat())) + == None + ); + Ok(()) + })); + } + libtest_mimic::run(&Arguments::from_args(), trials).exit() +} diff --git a/trace_decoder/tests/simulate-execution.rs b/trace_decoder/tests/simulate-execution.rs new file mode 100644 index 000000000..21baeaf91 --- /dev/null +++ b/trace_decoder/tests/simulate-execution.rs @@ -0,0 +1,34 @@ +//! Check that the [`evm_arithmetization::GenerationInputs`] produced by +//! [`trace_decoder`] are consistent between each other, and with the block +//! header obtained over RPC. + +mod common; + +use anyhow::Context as _; +use common::{cases, Case}; +use libtest_mimic::{Arguments, Trial}; +use plonky2::field::goldilocks_field::GoldilocksField; + +fn main() -> anyhow::Result<()> { + let mut trials = vec![]; + for Case { + name, + header: _, + trace, + other, + } in cases()? + { + let gis = trace_decoder::entrypoint(trace, other, 3, false) + .context(format!("error in `trace_decoder` for {name}"))?; + for (ix, gi) in gis.into_iter().enumerate() { + trials.push(Trial::test(format!("{name}/{ix}"), move || { + evm_arithmetization::prover::testing::simulate_execution_all_segments::< + GoldilocksField, + >(gi, 19) + .map_err(|e| format!("{e:?}"))?; // get the full error chain + Ok(()) + })) + } + } + libtest_mimic::run(&Arguments::from_args(), trials).exit() +} From cd336636bddc5685767d55ed549a588c7241ed56 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:44:26 +0100 Subject: [PATCH 4/9] chore: move test cases to cases directory --- trace_decoder/tests/{data => cases}/.gitattributes | 0 .../{data/witnesses/zero_jerigon => cases}/b19807080_main.json | 0 .../witnesses/zero_jerigon => cases}/b19807080_main_header.json | 0 .../{data/witnesses/zero_jerigon => cases}/b19840104_main.json | 0 .../witnesses/zero_jerigon => cases}/b19840104_main_header.json | 0 .../{data/witnesses/zero_jerigon => cases}/b20240052_main.json | 0 .../witnesses/zero_jerigon => cases}/b20240052_main_header.json | 0 .../{data/witnesses/zero_jerigon => cases}/b20240058_main.json | 0 .../witnesses/zero_jerigon => cases}/b20240058_main_header.json | 0 .../{data/witnesses/zero_jerigon => cases}/b20472570_main.json | 0 .../witnesses/zero_jerigon => cases}/b20472570_main_header.json | 0 .../tests/{data/witnesses/zero_jerigon => cases}/b28_dev.json | 0 .../{data/witnesses/zero_jerigon => cases}/b28_dev_header.json | 0 .../tests/{data/witnesses/zero_jerigon => cases}/b4_dev.json | 0 .../{data/witnesses/zero_jerigon => cases}/b4_dev_header.json | 0 trace_decoder/tests/common/mod.rs | 2 +- 16 files changed, 1 insertion(+), 1 deletion(-) rename trace_decoder/tests/{data => cases}/.gitattributes (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b19807080_main.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b19807080_main_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b19840104_main.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b19840104_main_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20240052_main.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20240052_main_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20240058_main.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20240058_main_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20472570_main.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b20472570_main_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b28_dev.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b28_dev_header.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b4_dev.json (100%) rename trace_decoder/tests/{data/witnesses/zero_jerigon => cases}/b4_dev_header.json (100%) diff --git a/trace_decoder/tests/data/.gitattributes b/trace_decoder/tests/cases/.gitattributes similarity index 100% rename from trace_decoder/tests/data/.gitattributes rename to trace_decoder/tests/cases/.gitattributes diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b19807080_main.json b/trace_decoder/tests/cases/b19807080_main.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b19807080_main.json rename to trace_decoder/tests/cases/b19807080_main.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b19807080_main_header.json b/trace_decoder/tests/cases/b19807080_main_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b19807080_main_header.json rename to trace_decoder/tests/cases/b19807080_main_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b19840104_main.json b/trace_decoder/tests/cases/b19840104_main.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b19840104_main.json rename to trace_decoder/tests/cases/b19840104_main.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b19840104_main_header.json b/trace_decoder/tests/cases/b19840104_main_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b19840104_main_header.json rename to trace_decoder/tests/cases/b19840104_main_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20240052_main.json b/trace_decoder/tests/cases/b20240052_main.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20240052_main.json rename to trace_decoder/tests/cases/b20240052_main.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20240052_main_header.json b/trace_decoder/tests/cases/b20240052_main_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20240052_main_header.json rename to trace_decoder/tests/cases/b20240052_main_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20240058_main.json b/trace_decoder/tests/cases/b20240058_main.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20240058_main.json rename to trace_decoder/tests/cases/b20240058_main.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20240058_main_header.json b/trace_decoder/tests/cases/b20240058_main_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20240058_main_header.json rename to trace_decoder/tests/cases/b20240058_main_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20472570_main.json b/trace_decoder/tests/cases/b20472570_main.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20472570_main.json rename to trace_decoder/tests/cases/b20472570_main.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b20472570_main_header.json b/trace_decoder/tests/cases/b20472570_main_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b20472570_main_header.json rename to trace_decoder/tests/cases/b20472570_main_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json b/trace_decoder/tests/cases/b28_dev.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json rename to trace_decoder/tests/cases/b28_dev.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json b/trace_decoder/tests/cases/b28_dev_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json rename to trace_decoder/tests/cases/b28_dev_header.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b4_dev.json b/trace_decoder/tests/cases/b4_dev.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b4_dev.json rename to trace_decoder/tests/cases/b4_dev.json diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b4_dev_header.json b/trace_decoder/tests/cases/b4_dev_header.json similarity index 100% rename from trace_decoder/tests/data/witnesses/zero_jerigon/b4_dev_header.json rename to trace_decoder/tests/cases/b4_dev_header.json diff --git a/trace_decoder/tests/common/mod.rs b/trace_decoder/tests/common/mod.rs index a6369d86d..77c3ead77 100644 --- a/trace_decoder/tests/common/mod.rs +++ b/trace_decoder/tests/common/mod.rs @@ -11,7 +11,7 @@ pub fn cases() -> anyhow::Result> { print!("loading test vectors..."); let ret = glob::glob(concat!( env!("CARGO_MANIFEST_DIR"), - "/tests/data/witnesses/zero_jerigon/*_header.json" + "/tests/cases/*_header.json" )) .expect("valid glob pattern") .map(|res| { From 83064de560e117cffb77fe3294ef838f84e47334 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:46:33 +0100 Subject: [PATCH 5/9] chore: move library test cases --- trace_decoder/src/cases/.gitattributes | 1 + trace_decoder/src/cases/README.md | 1 + .../data/tries => src/cases}/hermez_cdk_erigon.json | 0 .../{tests/data/tries => src/cases}/zero_jerigon.json | 0 trace_decoder/src/type1.rs | 11 +++++------ trace_decoder/src/type2.rs | 11 +++++------ 6 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 trace_decoder/src/cases/.gitattributes create mode 100644 trace_decoder/src/cases/README.md rename trace_decoder/{tests/data/tries => src/cases}/hermez_cdk_erigon.json (100%) rename trace_decoder/{tests/data/tries => src/cases}/zero_jerigon.json (100%) diff --git a/trace_decoder/src/cases/.gitattributes b/trace_decoder/src/cases/.gitattributes new file mode 100644 index 000000000..f53fb93bf --- /dev/null +++ b/trace_decoder/src/cases/.gitattributes @@ -0,0 +1 @@ +*.json linguist-generated=true diff --git a/trace_decoder/src/cases/README.md b/trace_decoder/src/cases/README.md new file mode 100644 index 000000000..a37883839 --- /dev/null +++ b/trace_decoder/src/cases/README.md @@ -0,0 +1 @@ +Test vectors for unit tests in [../wire](../wire.rs). diff --git a/trace_decoder/tests/data/tries/hermez_cdk_erigon.json b/trace_decoder/src/cases/hermez_cdk_erigon.json similarity index 100% rename from trace_decoder/tests/data/tries/hermez_cdk_erigon.json rename to trace_decoder/src/cases/hermez_cdk_erigon.json diff --git a/trace_decoder/tests/data/tries/zero_jerigon.json b/trace_decoder/src/cases/zero_jerigon.json similarity index 100% rename from trace_decoder/tests/data/tries/zero_jerigon.json rename to trace_decoder/src/cases/zero_jerigon.json diff --git a/trace_decoder/src/type1.rs b/trace_decoder/src/type1.rs index c073c2a13..019a75c95 100644 --- a/trace_decoder/src/type1.rs +++ b/trace_decoder/src/type1.rs @@ -380,12 +380,11 @@ fn finish_stack(v: &mut Vec) -> anyhow::Result { #[test] fn test_tries() { - for (ix, case) in serde_json::from_str::>(include_str!( - "../tests/data/tries/zero_jerigon.json" - )) - .unwrap() - .into_iter() - .enumerate() + for (ix, case) in + serde_json::from_str::>(include_str!("cases/zero_jerigon.json")) + .unwrap() + .into_iter() + .enumerate() { println!("case {}", ix); let instructions = crate::wire::parse(&case.bytes).unwrap(); diff --git a/trace_decoder/src/type2.rs b/trace_decoder/src/type2.rs index 2d10edf40..dd3e45c4b 100644 --- a/trace_decoder/src/type2.rs +++ b/trace_decoder/src/type2.rs @@ -226,12 +226,11 @@ fn iter_leaves(node: Node) -> Box>(include_str!( - "../tests/data/tries/hermez_cdk_erigon.json" - )) - .unwrap() - .into_iter() - .enumerate() + for (ix, case) in + serde_json::from_str::>(include_str!("cases/hermez_cdk_erigon.json")) + .unwrap() + .into_iter() + .enumerate() { println!("case {}", ix); let instructions = crate::wire::parse(&case.bytes).unwrap(); From f4612a9c4ac67877214340dadfc51e5fbbc1f145 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 17:57:59 +0100 Subject: [PATCH 6/9] chore: unused --- trace_decoder/tests/common/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/trace_decoder/tests/common/mod.rs b/trace_decoder/tests/common/mod.rs index 77c3ead77..51c74f75c 100644 --- a/trace_decoder/tests/common/mod.rs +++ b/trace_decoder/tests/common/mod.rs @@ -38,6 +38,7 @@ pub struct Case { /// /// Used as a test identifier. pub name: String, + #[allow(unused)] // only used by one of the test binaries pub header: Header, pub trace: BlockTrace, pub other: OtherBlockData, From f7608830bccfcb57c5bf6d2e8c6c0f7c88ce88c7 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 18:11:51 +0100 Subject: [PATCH 7/9] test: more checks --- trace_decoder/tests/consistent-with-header.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/trace_decoder/tests/consistent-with-header.rs b/trace_decoder/tests/consistent-with-header.rs index 2d1aeca24..735616716 100644 --- a/trace_decoder/tests/consistent-with-header.rs +++ b/trace_decoder/tests/consistent-with-header.rs @@ -24,7 +24,10 @@ fn main() -> anyhow::Result<()> { let gis = trace_decoder::entrypoint(trace.clone(), other.clone(), 3, false) .map_err(|e| format!("{e:?}"))?; // get the full cause chain check!(gis.len() >= 2); - check!(other.checkpoint_state_trie_root == gis[0].tries.state_trie.hash()); + check!( + Some(other.checkpoint_state_trie_root) + == gis.first().map(|it| it.tries.state_trie.hash()) + ); let pairs = || gis.iter().tuple_windows::<(_, _)>(); check!( pairs().position(|(before, after)| { @@ -51,6 +54,7 @@ fn main() -> anyhow::Result<()> { .position(|it| it.block_metadata.block_timestamp != header.timestamp.into()) == None ); + check!(gis.last().map(|it| it.block_hashes.cur_hash.compat()) == Some(header.hash)); check!( gis.iter().position(|it| it .block_hashes From 8af3db5a4f25196bbf24066d16a64cf884a8efc9 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 18:38:58 +0100 Subject: [PATCH 8/9] refactor: gi -> gen_input --- trace_decoder/tests/consistent-with-header.rs | 24 ++++++++++++------- trace_decoder/tests/simulate-execution.rs | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/trace_decoder/tests/consistent-with-header.rs b/trace_decoder/tests/consistent-with-header.rs index 735616716..68d4b2fee 100644 --- a/trace_decoder/tests/consistent-with-header.rs +++ b/trace_decoder/tests/consistent-with-header.rs @@ -21,14 +21,14 @@ fn main() -> anyhow::Result<()> { } in cases()? { trials.push(Trial::test(name, move || { - let gis = trace_decoder::entrypoint(trace.clone(), other.clone(), 3, false) + let gen_inputs = trace_decoder::entrypoint(trace.clone(), other.clone(), 3, false) .map_err(|e| format!("{e:?}"))?; // get the full cause chain - check!(gis.len() >= 2); + check!(gen_inputs.len() >= 2); check!( Some(other.checkpoint_state_trie_root) - == gis.first().map(|it| it.tries.state_trie.hash()) + == gen_inputs.first().map(|it| it.tries.state_trie.hash()) ); - let pairs = || gis.iter().tuple_windows::<(_, _)>(); + let pairs = || gen_inputs.iter().tuple_windows::<(_, _)>(); check!( pairs().position(|(before, after)| { before.trie_roots_after.state_root != after.tries.state_trie.hash() @@ -46,17 +46,25 @@ fn main() -> anyhow::Result<()> { }) == None ); check!( - gis.last().map(|it| it.trie_roots_after.state_root.compat()) + gen_inputs + .last() + .map(|it| it.trie_roots_after.state_root.compat()) == Some(header.state_root) ); check!( - gis.iter() + gen_inputs + .iter() .position(|it| it.block_metadata.block_timestamp != header.timestamp.into()) == None ); - check!(gis.last().map(|it| it.block_hashes.cur_hash.compat()) == Some(header.hash)); check!( - gis.iter().position(|it| it + gen_inputs + .last() + .map(|it| it.block_hashes.cur_hash.compat()) + == Some(header.hash) + ); + check!( + gen_inputs.iter().position(|it| it .block_hashes .prev_hashes .last() diff --git a/trace_decoder/tests/simulate-execution.rs b/trace_decoder/tests/simulate-execution.rs index 21baeaf91..4a1691ca5 100644 --- a/trace_decoder/tests/simulate-execution.rs +++ b/trace_decoder/tests/simulate-execution.rs @@ -18,9 +18,9 @@ fn main() -> anyhow::Result<()> { other, } in cases()? { - let gis = trace_decoder::entrypoint(trace, other, 3, false) + let gen_inputs = trace_decoder::entrypoint(trace, other, 3, false) .context(format!("error in `trace_decoder` for {name}"))?; - for (ix, gi) in gis.into_iter().enumerate() { + for (ix, gi) in gen_inputs.into_iter().enumerate() { trials.push(Trial::test(format!("{name}/{ix}"), move || { evm_arithmetization::prover::testing::simulate_execution_all_segments::< GoldilocksField, From a9602c87af62e989579a745d2a6bd78fc31799a9 Mon Sep 17 00:00:00 2001 From: 0xaatif Date: Thu, 5 Sep 2024 18:42:47 +0100 Subject: [PATCH 9/9] test: different batch sizes --- trace_decoder/tests/consistent-with-header.rs | 123 +++++++++--------- trace_decoder/tests/simulate-execution.rs | 40 +++--- 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/trace_decoder/tests/consistent-with-header.rs b/trace_decoder/tests/consistent-with-header.rs index 68d4b2fee..0cd477680 100644 --- a/trace_decoder/tests/consistent-with-header.rs +++ b/trace_decoder/tests/consistent-with-header.rs @@ -13,66 +13,69 @@ use mpt_trie::partial_trie::PartialTrie as _; fn main() -> anyhow::Result<()> { let mut trials = vec![]; - for Case { - name, - header, - trace, - other, - } in cases()? - { - trials.push(Trial::test(name, move || { - let gen_inputs = trace_decoder::entrypoint(trace.clone(), other.clone(), 3, false) - .map_err(|e| format!("{e:?}"))?; // get the full cause chain - check!(gen_inputs.len() >= 2); - check!( - Some(other.checkpoint_state_trie_root) - == gen_inputs.first().map(|it| it.tries.state_trie.hash()) - ); - let pairs = || gen_inputs.iter().tuple_windows::<(_, _)>(); - check!( - pairs().position(|(before, after)| { - before.trie_roots_after.state_root != after.tries.state_trie.hash() - }) == None - ); - check!( - pairs().position(|(before, after)| { - before.trie_roots_after.receipts_root != after.tries.receipts_trie.hash() - }) == None - ); - check!( - pairs().position(|(before, after)| { - before.trie_roots_after.transactions_root - != after.tries.transactions_trie.hash() - }) == None - ); - check!( - gen_inputs - .last() - .map(|it| it.trie_roots_after.state_root.compat()) - == Some(header.state_root) - ); - check!( - gen_inputs - .iter() - .position(|it| it.block_metadata.block_timestamp != header.timestamp.into()) - == None - ); - check!( - gen_inputs - .last() - .map(|it| it.block_hashes.cur_hash.compat()) - == Some(header.hash) - ); - check!( - gen_inputs.iter().position(|it| it - .block_hashes - .prev_hashes - .last() - .is_some_and(|it| *it != header.parent_hash.compat())) - == None - ); - Ok(()) - })); + + for batch_size in [1, 3] { + for Case { + name, + header, + trace, + other, + } in cases()? + { + trials.push(Trial::test(format!("{name}@{batch_size}"), move || { + let gen_inputs = trace_decoder::entrypoint(trace, other.clone(), batch_size, false) + .map_err(|e| format!("{e:?}"))?; // get the full cause chain + check!(gen_inputs.len() >= 2); + check!( + Some(other.checkpoint_state_trie_root) + == gen_inputs.first().map(|it| it.tries.state_trie.hash()) + ); + let pairs = || gen_inputs.iter().tuple_windows::<(_, _)>(); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.state_root != after.tries.state_trie.hash() + }) == None + ); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.receipts_root != after.tries.receipts_trie.hash() + }) == None + ); + check!( + pairs().position(|(before, after)| { + before.trie_roots_after.transactions_root + != after.tries.transactions_trie.hash() + }) == None + ); + check!( + gen_inputs + .last() + .map(|it| it.trie_roots_after.state_root.compat()) + == Some(header.state_root) + ); + check!( + gen_inputs + .iter() + .position(|it| it.block_metadata.block_timestamp != header.timestamp.into()) + == None + ); + check!( + gen_inputs + .last() + .map(|it| it.block_hashes.cur_hash.compat()) + == Some(header.hash) + ); + check!( + gen_inputs.iter().position(|it| it + .block_hashes + .prev_hashes + .last() + .is_some_and(|it| *it != header.parent_hash.compat())) + == None + ); + Ok(()) + })); + } } libtest_mimic::run(&Arguments::from_args(), trials).exit() } diff --git a/trace_decoder/tests/simulate-execution.rs b/trace_decoder/tests/simulate-execution.rs index 4a1691ca5..080a02c62 100644 --- a/trace_decoder/tests/simulate-execution.rs +++ b/trace_decoder/tests/simulate-execution.rs @@ -11,23 +11,29 @@ use plonky2::field::goldilocks_field::GoldilocksField; fn main() -> anyhow::Result<()> { let mut trials = vec![]; - for Case { - name, - header: _, - trace, - other, - } in cases()? - { - let gen_inputs = trace_decoder::entrypoint(trace, other, 3, false) - .context(format!("error in `trace_decoder` for {name}"))?; - for (ix, gi) in gen_inputs.into_iter().enumerate() { - trials.push(Trial::test(format!("{name}/{ix}"), move || { - evm_arithmetization::prover::testing::simulate_execution_all_segments::< - GoldilocksField, - >(gi, 19) - .map_err(|e| format!("{e:?}"))?; // get the full error chain - Ok(()) - })) + for batch_size in [1, 3] { + for Case { + name, + header: _, + trace, + other, + } in cases()? + { + let gen_inputs = trace_decoder::entrypoint(trace, other, batch_size, false).context( + format!("error in `trace_decoder` for {name} at batch size {batch_size}"), + )?; + for (ix, gi) in gen_inputs.into_iter().enumerate() { + trials.push(Trial::test( + format!("{name}@{batch_size}/{ix}"), + move || { + evm_arithmetization::prover::testing::simulate_execution_all_segments::< + GoldilocksField, + >(gi, 19) + .map_err(|e| format!("{e:?}"))?; // get the full error chain + Ok(()) + }, + )) + } } } libtest_mimic::run(&Arguments::from_args(), trials).exit()