From 25c5810f0eb44ed4e8a16b8b50953973a4751b78 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 10 Sep 2023 17:26:08 +0800 Subject: [PATCH] feat: add inner-prove and chunk-prove to testool (#921) * Add inner-prove and chunk-prove to testool. * Make build successfully with feature `inner-prove` and `chunk-prove`. * Generate pk/vk by coinbase address. * Clear import. * Fix wrong cfg for mock-prove. * Fix chunk-prove bug. * Fix * Update coinbase in inner-prove. --- Cargo.lock | 1 + prover/src/common/prover/chunk.rs | 27 ++-- prover/src/common/prover/utils.rs | 4 + prover/src/common/verifier/utils.rs | 4 + prover/src/config.rs | 5 + prover/src/zkevm/prover.rs | 10 +- testool/Cargo.toml | 8 +- testool/Makefile | 6 + testool/download_setup.sh | 15 +++ testool/src/statetest/executor.rs | 185 +++++++++++++++++++++++++--- 10 files changed, 232 insertions(+), 33 deletions(-) create mode 100644 testool/Makefile create mode 100644 testool/download_setup.sh diff --git a/Cargo.lock b/Cargo.lock index 54bc2b807a..05c9058043 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4877,6 +4877,7 @@ dependencies = [ "mock", "once_cell", "prettytable-rs", + "prover", "rand", "rand_chacha", "rayon", diff --git a/prover/src/common/prover/chunk.rs b/prover/src/common/prover/chunk.rs index 81711f63e7..1000d2656e 100644 --- a/prover/src/common/prover/chunk.rs +++ b/prover/src/common/prover/chunk.rs @@ -1,8 +1,5 @@ use super::Prover; -use crate::{ - config::{LAYER1_DEGREE, LAYER2_DEGREE}, - utils::gen_rng, -}; +use crate::{config::LayerId, utils::gen_rng}; use aggregator::extract_proof_and_instances_with_pairing_check; use anyhow::{anyhow, Result}; use halo2_proofs::halo2curves::bn256::Fr; @@ -14,16 +11,18 @@ impl Prover { &mut self, name: &str, witness_block: &Block, + inner_id: Option<&str>, output_dir: Option<&str>, ) -> Result { - let layer1_snark = self.load_or_gen_last_chunk_snark(name, witness_block, output_dir)?; + let layer1_snark = + self.load_or_gen_last_chunk_snark(name, witness_block, inner_id, output_dir)?; // Load or generate compression thin snark (layer-2). let layer2_snark = self.load_or_gen_comp_snark( name, - "layer2", + LayerId::Layer2.id(), true, - *LAYER2_DEGREE, + LayerId::Layer2.degree(), layer1_snark, output_dir, )?; @@ -38,15 +37,21 @@ impl Prover { &mut self, name: &str, witness_block: &Block, + inner_id: Option<&str>, output_dir: Option<&str>, ) -> Result { // Load or generate inner snark. - let inner_snark = self.load_or_gen_inner_snark(name, "inner", witness_block, output_dir)?; + let inner_snark = self.load_or_gen_inner_snark( + name, + inner_id.unwrap_or(LayerId::Inner.id()), + witness_block, + output_dir, + )?; log::info!("Got inner snark: {name}"); // Check pairing for super circuit. extract_proof_and_instances_with_pairing_check( - self.params(*LAYER1_DEGREE), + self.params(LayerId::Layer1.degree()), &[inner_snark.clone()], gen_rng(), ) @@ -55,9 +60,9 @@ impl Prover { // Load or generate compression wide snark (layer-1). let layer1_snark = self.load_or_gen_comp_snark( name, - "layer1", + LayerId::Layer1.id(), false, - *LAYER1_DEGREE, + LayerId::Layer1.degree(), inner_snark, output_dir, )?; diff --git a/prover/src/common/prover/utils.rs b/prover/src/common/prover/utils.rs index 00d5ce5af7..d071654b8b 100644 --- a/prover/src/common/prover/utils.rs +++ b/prover/src/common/prover/utils.rs @@ -73,4 +73,8 @@ impl Prover { pub fn raw_vk(&self, id: &str) -> Option> { self.pk_map.get(id).map(|pk| serialize_vk(pk.get_vk())) } + + pub fn clear_pks(&mut self) { + self.pk_map.clear(); + } } diff --git a/prover/src/common/verifier/utils.rs b/prover/src/common/verifier/utils.rs index f932c4ae9a..a77ff2e3a4 100644 --- a/prover/src/common/verifier/utils.rs +++ b/prover/src/common/verifier/utils.rs @@ -14,4 +14,8 @@ impl> Verifier { pub fn vk(&self) -> &VerifyingKey { &self.vk } + + pub fn set_vk(&mut self, vk: VerifyingKey) { + self.vk = vk; + } } diff --git a/prover/src/config.rs b/prover/src/config.rs index 0a34c66f03..527bccecff 100644 --- a/prover/src/config.rs +++ b/prover/src/config.rs @@ -31,6 +31,8 @@ pub static AGG_DEGREES: Lazy> = #[derive(Clone, Copy, Debug)] pub enum LayerId { + /// Super (inner) circuit layer + Inner, /// Compression wide layer Layer1, /// Compression thin layer (to generate chunk-proof) @@ -50,6 +52,7 @@ impl fmt::Display for LayerId { impl LayerId { pub fn id(&self) -> &str { match self { + Self::Inner => "inner", Self::Layer1 => "layer1", Self::Layer2 => "layer2", Self::Layer3 => "layer3", @@ -59,6 +62,7 @@ impl LayerId { pub fn degree(&self) -> u32 { match self { + Self::Inner => *INNER_DEGREE, Self::Layer1 => *LAYER1_DEGREE, Self::Layer2 => *LAYER2_DEGREE, Self::Layer3 => *LAYER3_DEGREE, @@ -72,6 +76,7 @@ impl LayerId { Self::Layer2 => &LAYER2_CONFIG_PATH, Self::Layer3 => &LAYER3_CONFIG_PATH, Self::Layer4 => &LAYER4_CONFIG_PATH, + Self::Inner => unreachable!("No config file for super (inner) circuit"), } } } diff --git a/prover/src/zkevm/prover.rs b/prover/src/zkevm/prover.rs index f4f452b485..20a12b58cf 100644 --- a/prover/src/zkevm/prover.rs +++ b/prover/src/zkevm/prover.rs @@ -44,6 +44,7 @@ impl Prover { &mut self, chunk_trace: Vec, name: Option<&str>, + inner_id: Option<&str>, output_dir: Option<&str>, ) -> Result { assert!(!chunk_trace.is_empty()); @@ -64,9 +65,12 @@ impl Prover { |name| name.to_string(), ); - let snark = self - .inner - .load_or_gen_final_chunk_snark(&name, &witness_block, output_dir)?; + let snark = self.inner.load_or_gen_final_chunk_snark( + &name, + &witness_block, + inner_id, + output_dir, + )?; self.check_and_clear_raw_vk(); diff --git a/testool/Cargo.toml b/testool/Cargo.toml index c7031a30f8..861515e60c 100644 --- a/testool/Cargo.toml +++ b/testool/Cargo.toml @@ -22,6 +22,7 @@ itertools = "0.10" mock = { path = "../mock" } once_cell = "1.10" prettytable-rs = "0.10" +prover = { path = "../prover", optional = true } rayon = "1.5" regex = "1" serde = { version = "1.0", features = ["derive"] } @@ -43,5 +44,8 @@ default = ["ignore-test-docker", "skip-self-destruct", "shanghai"] onephase = ["zkevm-circuits/onephase"] ignore-test-docker = [] skip-self-destruct = [] -shanghai = ["bus-mapping/shanghai", "eth-types/shanghai", "mock/shanghai", "zkevm-circuits/shanghai"] -scroll = ["bus-mapping/scroll", "eth-types/scroll", "external-tracer/scroll", "mock/scroll", "zkevm-circuits/scroll"] +shanghai = ["bus-mapping/shanghai", "eth-types/shanghai", "mock/shanghai", "zkevm-circuits/shanghai", "prover?/shanghai"] +scroll = ["bus-mapping/scroll", "eth-types/scroll", "external-tracer/scroll", "mock/scroll", "zkevm-circuits/scroll", "prover?/scroll"] +parallel_syn = ["halo2_proofs/parallel_syn", "zkevm-circuits/parallel_syn", "prover?/parallel_syn"] +inner-prove = ["prover", "parallel_syn", "scroll", "shanghai"] +chunk-prove = ["prover", "parallel_syn", "scroll", "shanghai"] diff --git a/testool/Makefile b/testool/Makefile new file mode 100644 index 0000000000..54a8af9555 --- /dev/null +++ b/testool/Makefile @@ -0,0 +1,6 @@ +.PHONY: download-setup + +# Could be called as `make download-setup -e degree=DEGREE params_dir=PARAMS_DIR`. +# As default `degree=26` and `params_dir=./test_params`. +download-setup: + sh download_setup.sh ${degree} ${params_dir} diff --git a/testool/download_setup.sh b/testool/download_setup.sh new file mode 100644 index 0000000000..a8e0e815af --- /dev/null +++ b/testool/download_setup.sh @@ -0,0 +1,15 @@ +set -x +set -e + +# Set degree to env AGG_DEGREE, first input or default value 26. +degree="${AGG_DEGREE:-${1:-26}}" + +# Set the output dir to second input or default as `./test_params`. +params_dir="${2:-"./test_params"}" +mkdir -p "$params_dir" + +output_file="$params_dir"/params"${degree}" +rm -f "$output_file" + +# degree 1 - 26 +axel -ac https://circuit-release.s3.us-west-2.amazonaws.com/setup/params"$degree" -o "$output_file" diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 98fb38e7b7..b44d7b8b45 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -4,7 +4,6 @@ use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, CircuitsParams, PrecompileEcParams}, state_db::CodeDB, }; - #[cfg(feature = "scroll")] use eth_types::ToBigEndian; use eth_types::{ @@ -19,7 +18,15 @@ use external_tracer::{LoggerConfig, TraceConfig}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, plonk::Circuit}; use itertools::Itertools; use once_cell::sync::Lazy; -use std::{collections::HashMap, str::FromStr}; +#[cfg(any(feature = "inner-prove", feature = "chunk-prove"))] +use prover::{ + common::{Prover, Verifier}, + config::LayerId, + config::{INNER_DEGREE, ZKEVM_DEGREES}, +}; +#[cfg(feature = "inner-prove")] +use prover::{utils::gen_rng, zkevm::circuit}; +use std::{collections::HashMap, env, str::FromStr}; use thiserror::Error; use zkevm_circuits::{ bytecode_circuit::circuit::BytecodeCircuit, ecc_circuit::EccCircuit, @@ -29,13 +36,65 @@ use zkevm_circuits::{ /// Read env var with default value pub fn read_env_var(var_name: &'static str, default: T) -> T { - std::env::var(var_name) + env::var(var_name) .map(|s| s.parse::().unwrap_or_else(|_| default.clone())) .unwrap_or(default) } /// Which circuit to test. Default is evm + state. pub static CIRCUIT: Lazy = Lazy::new(|| read_env_var("CIRCUIT", "".to_string())); +#[cfg(any(feature = "inner-prove", feature = "chunk-prove"))] +static mut REAL_PROVER: Lazy = Lazy::new(|| { + let params_dir = "./test_params"; + + let degrees: Vec = if cfg!(feature = "inner-prove") { + vec![*INNER_DEGREE] + } else { + // for chunk-prove + (*ZKEVM_DEGREES).clone() + }; + + let prover = Prover::from_params_dir(params_dir, °rees); + log::info!("Constructed real-prover"); + + prover +}); + +#[cfg(feature = "inner-prove")] +static mut INNER_VERIFIER: Lazy< + Verifier<::Inner>, +> = Lazy::new(|| { + let prover = unsafe { &mut REAL_PROVER }; + let params = prover.params(*INNER_DEGREE).clone(); + + let id = read_env_var("COINBASE", LayerId::Inner.id().to_string()); + let pk = prover.pk(&id).expect("Failed to get inner-prove PK"); + let vk = pk.get_vk().clone(); + + let verifier = Verifier::new(params, vk); + log::info!("Constructed inner-verifier"); + + verifier +}); + +#[cfg(feature = "chunk-prove")] +static mut CHUNK_VERIFIER: Lazy> = Lazy::new(|| { + env::set_var("COMPRESSION_CONFIG", LayerId::Layer2.config_path()); + + let prover = unsafe { &mut REAL_PROVER }; + let params = prover.params(LayerId::Layer2.degree()).clone(); + + let pk = prover + .pk(LayerId::Layer2.id()) + .expect("Failed to get chunk-prove PK"); + let vk = pk.get_vk().clone(); + + let verifier = Verifier::new(params, vk); + log::info!("Constructed chunk-verifier"); + + verifier +}); + #[derive(PartialEq, Eq, Error, Debug)] pub enum StateTestError { #[cfg(not(feature = "scroll"))] @@ -307,13 +366,10 @@ fn trace_config_to_witness_block_l2( .collect::>(); check_geth_traces(&geth_traces, &suite, verbose)?; - std::env::set_var( - "COINBASE", - format!("0x{}", hex::encode(block_trace.coinbase.address.unwrap())), - ); - std::env::set_var("CHAIN_ID", format!("{}", block_trace.chain_id)); + set_env_coinbase(&block_trace.coinbase.address.unwrap()); + env::set_var("CHAIN_ID", format!("{}", block_trace.chain_id)); let difficulty_be_bytes = [0u8; 32]; - std::env::set_var("DIFFICULTY", hex::encode(difficulty_be_bytes)); + env::set_var("DIFFICULTY", hex::encode(difficulty_be_bytes)); let mut builder = CircuitInputBuilder::new_from_l2_trace(circuits_params, &block_trace, false, false) .expect("could not handle block tx"); @@ -549,8 +605,10 @@ pub fn run_test( suite: TestSuite, circuits_config: CircuitsConfig, ) -> Result<(), StateTestError> { - // get the geth traces + let test_id = st.id.clone(); + log::info!("{test_id}: run-test BEGIN - {circuits_config:?}"); + // get the geth traces let (_, trace_config, post) = into_traceconfig(st.clone()); #[cfg(feature = "scroll")] @@ -662,13 +720,12 @@ pub fn run_test( if (*CIRCUIT) == "ccc" { check_ccc(); } else { - // TODO: do we need to automatically adjust this k? - let k = 20; - // TODO: remove this MOCK_RANDOMNESS? - let circuit = ScrollSuperCircuit::new_from_block(&witness_block); - let instance = circuit.instance(); - let prover = MockProver::run(k, &circuit, instance).unwrap(); - prover.assert_satisfied_par(); + #[cfg(feature = "inner-prove")] + inner_prove(&test_id, &st.env.current_coinbase, &witness_block); + #[cfg(feature = "chunk-prove")] + chunk_prove(&test_id, &st.env.current_coinbase, &witness_block); + #[cfg(not(any(feature = "inner-prove", feature = "chunk-prove")))] + mock_prove(&test_id, &witness_block); } }; //#[cfg(feature = "scroll")] @@ -703,5 +760,99 @@ pub fn run_test( } check_post(&builder, &post)?; + log::info!("{test_id}: run-test END"); Ok(()) } + +#[cfg(feature = "scroll")] +fn set_env_coinbase(coinbase: &Address) -> String { + let coinbase = format!("0x{}", hex::encode(coinbase)); + env::set_var("COINBASE", &coinbase); + + coinbase +} + +#[cfg(not(any(feature = "inner-prove", feature = "chunk-prove")))] +fn mock_prove(test_id: &str, witness_block: &Block) { + log::info!("{test_id}: mock-prove BEGIN"); + // TODO: do we need to automatically adjust this k? + let k = 20; + // TODO: remove this MOCK_RANDOMNESS? + let circuit = ScrollSuperCircuit::new_from_block(&witness_block); + let instance = circuit.instance(); + let prover = MockProver::run(k, &circuit, instance).unwrap(); + prover.assert_satisfied_par(); + + log::info!("{test_id}: mock-prove END"); +} + +#[cfg(feature = "inner-prove")] +fn inner_prove(test_id: &str, coinbase: &Address, witness_block: &Block) { + let coinbase = set_env_coinbase(coinbase); + log::info!("{test_id}: inner-prove BEGIN, coinbase = {coinbase}"); + + let prover = unsafe { &mut REAL_PROVER }; + let coinbase_changed = prover.pk(&coinbase).is_none(); + + // Clear previous PKs if coinbase address changed. + if coinbase_changed { + prover.clear_pks(); + } + + let rng = gen_rng(); + let snark = prover + .gen_inner_snark::(&coinbase, rng, witness_block) + .unwrap_or_else(|err| panic!("{test_id}: failed to generate inner snark: {err}")); + log::info!("{test_id}: generated inner snark"); + + let verifier = unsafe { &mut INNER_VERIFIER }; + + // Reset VK if coinbase address changed. + if coinbase_changed { + let pk = prover.pk(&coinbase).unwrap_or_else(|| { + panic!("{test_id}: failed to get inner-prove PK, coinbase = {coinbase}") + }); + let vk = pk.get_vk().clone(); + verifier.set_vk(vk); + } + + let verified = verifier.verify_snark(snark); + assert!(verified, "{test_id}: failed to verify inner snark"); + + log::info!("{test_id}: inner-prove END, coinbase = {coinbase}"); +} + +#[cfg(feature = "chunk-prove")] +fn chunk_prove(test_id: &str, coinbase: &Address, witness_block: &Block) { + let coinbase = set_env_coinbase(coinbase); + log::info!("{test_id}: chunk-prove BEGIN, coinbase = {coinbase}"); + + let prover = unsafe { &mut REAL_PROVER }; + let coinbase_changed = prover.pk(&coinbase).is_none(); + + // Clear previous PKs if coinbase address changed. + if coinbase_changed { + prover.clear_pks(); + } + + let snark = prover + .load_or_gen_final_chunk_snark(test_id, witness_block, Some(&coinbase), None) + .unwrap_or_else(|err| panic!("{test_id}: failed to generate chunk snark: {err}")); + log::info!("{test_id}: generated chunk snark"); + + let verifier = unsafe { &mut CHUNK_VERIFIER }; + + // Reset VK if coinbase address changed. + if coinbase_changed { + let pk = prover.pk(LayerId::Layer2.id()).unwrap_or_else(|| { + panic!("{test_id}: failed to get inner-prove PK, coinbase = {coinbase}") + }); + let vk = pk.get_vk().clone(); + verifier.set_vk(vk); + } + + let verified = verifier.verify_snark(snark); + assert!(verified, "{test_id}: failed to verify chunk snark"); + + log::info!("{test_id}: chunk-prove END, coinbase = {coinbase}"); +}