diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 6839d3cb76..e2183badad 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -9,16 +9,18 @@ on: push: branches: - main - workflow_dispatch: + workflow_dispatch: inputs: provertype: - description: 'invoke real vs mock prover' + description: 'invoke real vs mock prover (with or without root)' required: true - default: 'mock_prover' + default: 'sub_mock_prover' type: choice options: - - real_prover - - mock_prover + - sub_real_prover + - sub_mock_prover + - root_mock_prover + - root_real_prover concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -36,12 +38,18 @@ jobs: - id: set-outputs name: Select instance and prover type run: | - if [ "${{ github.event.inputs.provertype }}" = "real_prover" ] || [ "${{ github.event_name }}" = "schedule" ]; then + if [ "${{ github.event.inputs.provertype }}" = "root_real_prover" ] || [ "${{ github.event_name }}" = "schedule" ]; then echo "instancetype=r6i.32xlarge" >> "$GITHUB_OUTPUT" - echo "provertype=real_prover" >> "$GITHUB_OUTPUT" - elif [ "${{ github.event.inputs.provertype }}" = "mock_prover" ] || [ -z ${{ github.event.inputs.provertype }} ]; then + echo "provertype=root_real_prover" >> "$GITHUB_OUTPUT" + elif [ "${{ github.event.inputs.provertype }}" = "root_mock_prover" ]; then + echo "instancetype=r6i.32xlarge" >> "$GITHUB_OUTPUT" + echo "provertype=root_mock_prover" >> "$GITHUB_OUTPUT" + elif [ "${{ github.event.inputs.provertype }}" = "sub_real_prover" ]; then + echo "instancetype=r6i.32xlarge" >> "$GITHUB_OUTPUT" + echo "provertype=sub_real_prover" >> "$GITHUB_OUTPUT" + elif [ "${{ github.event.inputs.provertype }}" = "sub_mock_prover" ] || [ -z ${{ github.event.inputs.provertype }} ]; then echo "instancetype=c5.9xlarge" >> "$GITHUB_OUTPUT" - echo "provertype=mock_prover" >> "$GITHUB_OUTPUT" + echo "provertype=sub_mock_prover" >> "$GITHUB_OUTPUT" else exit 1 fi @@ -73,7 +81,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Cargo cache - uses: actions/cache@v3 + uses: actions/cache@v3 with: path: | ~/.cargo/bin/ @@ -82,7 +90,7 @@ jobs: ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - # Run an initial build in a separate step to split the build time from execution time + # Run an initial build in a separate step to split the build time from execution time - name: Build bins run: cargo build --bin gen_blockchain_data - name: Build tests diff --git a/Cargo.lock b/Cargo.lock index 54417fccd6..a9516c3df2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1496,6 +1496,8 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ + "byteorder", + "rand", "rustc-hex", "static_assertions", ] @@ -2508,6 +2510,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.17.0" @@ -2914,6 +2937,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", "uint", ] @@ -3168,6 +3193,23 @@ dependencies = [ "winreg", ] +[[package]] +name = "revm" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73d84c8f9836efb0f5f5f8de4700a953c4e1f3119e5cfcb0aad8e5be73daf991" +dependencies = [ + "arrayref", + "auto_impl", + "bytes", + "hashbrown 0.13.2", + "num_enum", + "primitive-types 0.12.1", + "revm_precompiles", + "rlp", + "sha3 0.10.7", +] + [[package]] name = "revm-precompile" version = "2.0.2" @@ -3179,7 +3221,7 @@ dependencies = [ "once_cell", "revm-primitives", "ripemd", - "secp256k1", + "secp256k1 0.27.0", "sha2 0.10.6", "sha3 0.10.7", "substrate-bn", @@ -3206,6 +3248,24 @@ dependencies = [ "sha3 0.10.7", ] +[[package]] +name = "revm_precompiles" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0353d456ef3e989dc9190f42c6020f09bc2025930c37895826029304413204b5" +dependencies = [ + "bytes", + "hashbrown 0.13.2", + "num", + "once_cell", + "primitive-types 0.12.1", + "ripemd", + "secp256k1 0.24.3", + "sha2 0.10.6", + "sha3 0.10.7", + "substrate-bn", +] + [[package]] name = "rfc6979" version = "0.3.1" @@ -3477,13 +3537,31 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys 0.6.1", +] + [[package]] name = "secp256k1" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.8.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", ] [[package]] @@ -3712,6 +3790,7 @@ name = "snark-verifier" version = "0.1.0" source = "git+https://github.com/privacy-scaling-explorations/snark-verifier?tag=v2023_04_20#e5d5e4a6ccff2bba71baf77ab7a12b124d6364a1" dependencies = [ + "bytes", "ecc", "halo2_proofs", "halo2curves", @@ -3722,7 +3801,11 @@ dependencies = [ "num-integer", "num-traits", "poseidon", + "primitive-types 0.12.1", "rand", + "revm", + "rlp", + "sha3 0.10.7", ] [[package]] diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 11e676d561..b1e470763d 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -80,7 +80,7 @@ impl Default for CircuitsParams { // TODO: Check whether this value is correct or we should increase/decrease based on // this lib tests max_copy_rows: 1000, - max_exp_steps: 1000, + max_exp_steps: 1000 / 7, // exp_circuit::OFFSET_INCREMENT = 7 max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, diff --git a/integration-tests/run.sh b/integration-tests/run.sh index a67bdb4902..a1f1a8dda9 100755 --- a/integration-tests/run.sh +++ b/integration-tests/run.sh @@ -3,7 +3,7 @@ set -e ARG_DEFAULT_SUDO= ARG_DEFAULT_STEPS="setup gendata tests cleanup" -ARG_DEFAULT_TESTS="rpc circuit_input_builder circuits::mock_prover" +ARG_DEFAULT_TESTS="rpc circuit_input_builder circuits::sub_mock_prover" usage() { cat >&2 << EOF diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index c9bfd37a09..c43d2026bf 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -5,11 +5,13 @@ use bus_mapping::{ }; use eth_types::geth_types::GethData; use halo2_proofs::{ + self, + circuit::Value, dev::{CellValue, MockProver}, halo2curves::bn256::{Bn256, Fr, G1Affine}, plonk::{ create_proof, keygen_pk, keygen_vk, permutation::Assembly, verify_proof, Circuit, - ProvingKey, VerifyingKey, + ProvingKey, }, poly::{ commitment::ParamsProver, @@ -19,14 +21,10 @@ use halo2_proofs::{ strategy::SingleStrategy, }, }, - transcript::{ - Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, - }, }; use lazy_static::lazy_static; use mock::TestContext; use rand_chacha::rand_core::SeedableRng; -use rand_core::RngCore; use rand_xorshift::XorShiftRng; use std::{collections::HashMap, marker::PhantomData, sync::Mutex}; use tokio::sync::Mutex as TokioMutex; @@ -37,6 +35,9 @@ use zkevm_circuits::{ exp_circuit::TestExpCircuit, keccak_circuit::TestKeccakCircuit, pi_circuit::TestPiCircuit, + root_circuit::{ + compile, Config, EvmTranscript, NativeLoader, PoseidonTranscript, RootCircuit, Shplonk, + }, state_circuit::TestStateCircuit, super_circuit::SuperCircuit, tx_circuit::TestTxCircuit, @@ -84,6 +85,9 @@ const KECCAK_CIRCUIT_DEGREE: u32 = 16; const SUPER_CIRCUIT_DEGREE: u32 = 20; const EXP_CIRCUIT_DEGREE: u32 = 16; const PI_CIRCUIT_DEGREE: u32 = 17; +const ROOT_CIRCUIT_SMALL_DEGREE: u32 = 24; +// Big is for SuperCircuit only +const ROOT_CIRCUIT_BIG_DEGREE: u32 = 26; lazy_static! { /// Data generation. @@ -101,63 +105,177 @@ lazy_static! { lazy_static! { /// Integration test for EVM circuit pub static ref EVM_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("EVM", EVM_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("EVM", EVM_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for State circuit pub static ref STATE_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("State", STATE_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("State", STATE_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for State circuit pub static ref TX_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Tx", TX_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("Tx", TX_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for Bytecode circuit pub static ref BYTECODE_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Bytecode", BYTECODE_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("Bytecode", BYTECODE_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for Copy circuit pub static ref COPY_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Copy", COPY_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("Copy", COPY_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for Keccak circuit pub static ref KECCAK_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); /// Integration test for Copy circuit pub static ref SUPER_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Super", SUPER_CIRCUIT_DEGREE)); + TokioMutex::new(IntegrationTest::new("Super", SUPER_CIRCUIT_DEGREE, ROOT_CIRCUIT_BIG_DEGREE)); + + /// Integration test for Exp circuit + pub static ref EXP_CIRCUIT_TEST: TokioMutex>> = + TokioMutex::new(IntegrationTest::new("Exp", EXP_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); + + /// Integration test for Pi circuit + pub static ref PI_CIRCUIT_TEST: TokioMutex>> = + TokioMutex::new(IntegrationTest::new("Pi", PI_CIRCUIT_DEGREE, ROOT_CIRCUIT_SMALL_DEGREE)); +} + +lazy_static! { + /// Cache of real proofs from each block to be reused with the Root circuit tests + static ref PROOF_CACHE: TokioMutex>> = TokioMutex::new(HashMap::new()); +} - /// Integration test for Exp circuit - pub static ref EXP_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Exp", EXP_CIRCUIT_DEGREE)); +/// Generate a real proof of a Circuit with Poseidon transcript and Shplonk accumulation scheme. +/// Verify the proof and return it. The proof is suitable to be verified by the Root Circuit. +fn test_actual_circuit>( + circuit: C, + degree: u32, + instance: Vec>, + proving_key: ProvingKey, +) -> Vec { + let general_params = get_general_params(degree); + let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); + + let mut transcript = PoseidonTranscript::new(Vec::new()); + + // change instace to slice + let instance: Vec<&[Fr]> = instance.iter().map(|v| v.as_slice()).collect(); + + log::info!("gen circuit proof"); + create_proof::, ProverSHPLONK<'_, Bn256>, _, _, _, _>( + &general_params, + &proving_key, + &[circuit], + &[&instance], + RNG.clone(), + &mut transcript, + ) + .expect("proof generation should not fail"); + let proof = transcript.finalize(); + + log::info!("verify circuit proof"); + let verifying_key = proving_key.get_vk(); + let mut verifier_transcript = PoseidonTranscript::new(proof.as_slice()); + let strategy = SingleStrategy::new(&general_params); + + verify_proof::, VerifierSHPLONK<'_, Bn256>, _, _, _>( + &verifier_params, + verifying_key, + strategy, + &[&instance], + &mut verifier_transcript, + ) + .expect("failed to verify circuit"); + + proof +} - /// Integration test for Pi circuit - pub static ref PI_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Pi", PI_CIRCUIT_DEGREE)); +/// Generate a real proof of the RootCircuit with Keccak transcript and Shplonk accumulation +/// scheme. Verify the proof and return it. By using the Keccak transcript (via EvmTranscript) +/// the resulting proof is suitable for verification by the EVM. +/// +/// NOTE: MockProver Root Circuit with 64 GiB RAM (2023-06-12): +/// - degree=26 -> OOM +/// - degree=25 -> OK (peak ~35 GiB) +fn test_actual_root_circuit>( + circuit: C, + degree: u32, + instance: Vec>, + proving_key: ProvingKey, +) -> Vec { + let general_params = get_general_params(degree); + let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); + + let mut transcript = EvmTranscript::<_, NativeLoader, _, _>::new(vec![]); + + // change instace to slice + let instance: Vec<&[Fr]> = instance.iter().map(|v| v.as_slice()).collect(); + + log::info!("gen root circuit proof"); + create_proof::, ProverSHPLONK<'_, Bn256>, _, _, _, _>( + &general_params, + &proving_key, + &[circuit], + &[&instance], + RNG.clone(), + &mut transcript, + ) + .expect("proof generation should not fail"); + let proof = transcript.finalize(); + + log::info!("verify root circuit proof"); + let verifying_key = proving_key.get_vk(); + let mut verifier_transcript = EvmTranscript::<_, NativeLoader, _, _>::new(proof.as_slice()); + let strategy = SingleStrategy::new(&general_params); + + verify_proof::, VerifierSHPLONK<'_, Bn256>, _, _, _>( + &verifier_params, + verifying_key, + strategy, + &[&instance], + &mut verifier_transcript, + ) + .expect("failed to verify circuit"); + + proof } /// Generic implementation for integration tests pub struct IntegrationTest + Circuit> { name: &'static str, degree: u32, + root_degree: u32, key: Option>, + root_key: Option>, fixed: Option>>>, permutation: Option, + // The RootCircuit changes depending on the underlying circuit, so we keep a copy of its fixed + // columns and permutation here to have a unique version for each SubCircuit. + root_fixed: Option>>>, + root_permutation: Option, _marker: PhantomData, } impl + Circuit> IntegrationTest { - fn new(name: &'static str, degree: u32) -> Self { + fn new(name: &'static str, degree: u32, root_degree: u32) -> Self { Self { name, degree, + root_degree, key: None, + root_key: None, fixed: None, permutation: None, + root_fixed: None, + root_permutation: None, _marker: PhantomData, } } + fn proof_name(&self, block_tag: &str) -> String { + format!("{}_{}", self.name, block_tag) + } + fn get_key(&mut self) -> ProvingKey { match self.key.clone() { Some(key) => key, @@ -176,86 +294,41 @@ impl + Circuit> IntegrationTest { } } - fn test_actual(&self, circuit: C, instance: Vec>, proving_key: ProvingKey) { - fn test_gen_proof, R: RngCore>( - rng: R, - circuit: C, - general_params: &ParamsKZG, - proving_key: &ProvingKey, - mut transcript: Blake2bWrite, G1Affine, Challenge255>, - instances: &[&[Fr]], - ) -> Vec { - create_proof::< - KZGCommitmentScheme, - ProverSHPLONK<'_, Bn256>, - Challenge255, - R, - Blake2bWrite, G1Affine, Challenge255>, - C, - >( - general_params, - proving_key, - &[circuit], - &[instances], - rng, - &mut transcript, - ) - .expect("proof generation should not fail"); - - transcript.finalize() - } + fn get_root_key(&mut self) -> ProvingKey { + match self.root_key.clone() { + Some(key) => key, + None => { + let params = get_general_params(self.degree); + let pk = self.get_key(); - fn test_verify( - general_params: &ParamsKZG, - verifier_params: &ParamsKZG, - verifying_key: &VerifyingKey, - proof: &[u8], - instances: &[&[Fr]], - ) { - let mut verifier_transcript = Blake2bRead::<_, G1Affine, Challenge255<_>>::init(proof); - let strategy = SingleStrategy::new(general_params); - - verify_proof::< - KZGCommitmentScheme, - VerifierSHPLONK<'_, Bn256>, - Challenge255, - Blake2bRead<&[u8], G1Affine, Challenge255>, - SingleStrategy<'_, Bn256>, - >( - verifier_params, - verifying_key, - strategy, - &[instances], - &mut verifier_transcript, - ) - .expect("failed to verify circuit"); + let block = new_empty_block(); + let circuit = C::new_from_block(&block); + let instance = circuit.instance(); + + let protocol = compile( + ¶ms, + pk.get_vk(), + Config::kzg().with_num_instance( + instance.iter().map(|instance| instance.len()).collect(), + ), + ); + let circuit = RootCircuit::>::new( + ¶ms, + &protocol, + Value::unknown(), + Value::unknown(), + ) + .unwrap(); + + let general_params = get_general_params(self.root_degree); + let verifying_key = + keygen_vk(&general_params, &circuit).expect("keygen_vk should not fail"); + let key = keygen_pk(&general_params, verifying_key, &circuit) + .expect("keygen_pk should not fail"); + self.root_key = Some(key.clone()); + key + } } - - let general_params = get_general_params(self.degree); - let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); - - let transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]); - - // change instace to slice - let instance: Vec<&[Fr]> = instance.iter().map(|v| v.as_slice()).collect(); - - let proof = test_gen_proof( - RNG.clone(), - circuit, - &general_params, - &proving_key, - transcript, - &instance, - ); - - let verifying_key = proving_key.get_vk(); - test_verify( - &general_params, - &verifier_params, - verifying_key, - &proof, - &instance, - ); } fn test_mock(&mut self, circuit: &C, instance: Vec>) { @@ -292,27 +365,117 @@ impl + Circuit> IntegrationTest { } } + fn test_root_variadic(&mut self, mock_prover: &MockProver) { + let fixed = mock_prover.fixed(); + + match self.root_fixed.clone() { + Some(prev_fixed) => { + assert!( + fixed.eq(&prev_fixed), + "root circuit fixed columns are not constant for different witnesses" + ); + } + None => { + self.root_fixed = Some(fixed.clone()); + } + }; + + let permutation = mock_prover.permutation(); + + if let Some(prev_permutation) = self.root_permutation.clone() { + assert!( + permutation.eq(&prev_permutation), + "root circuit permutations are not constant for different witnesses" + ); + } else { + self.root_permutation = Some(permutation.clone()); + } + } + /// Run integration test at a block identified by a tag. - pub async fn test_at_block_tag(&mut self, block_tag: &str, actual: bool) { + pub async fn test_at_block_tag(&mut self, block_tag: &str, root: bool, actual: bool) { let block_num = *GEN_DATA.blocks.get(block_tag).unwrap(); + let proof_name = self.proof_name(block_tag); let (builder, _) = gen_inputs(block_num).await; log::info!( - "test {} circuit, block: #{} - {}", + "test {} circuit{}, {} prover, block: #{} - {}", self.name, + if root { + " with aggregation (root circuit)" + } else { + "" + }, + if actual { "real" } else { "mock" }, block_num, - block_tag + block_tag, ); let mut block = block_convert(&builder.block, &builder.code_db).unwrap(); block.randomness = Fr::from(TEST_MOCK_RANDOMNESS); let circuit = C::new_from_block(&block); let instance = circuit.instance(); - if actual { - let key = self.get_key(); - self.test_actual(circuit, instance, key); + #[allow(clippy::collapsible_else_if)] + if root { + let params = get_general_params(self.degree); + let pk = self.get_key(); + let protocol = compile( + ¶ms, + pk.get_vk(), + Config::kzg() + .with_num_instance(instance.iter().map(|instance| instance.len()).collect()), + ); + + let proof = { + let mut proof_cache = PROOF_CACHE.lock().await; + if let Some(proof) = proof_cache.get(&proof_name) { + log::info!("using circuit cached proof"); + proof.clone() + } else { + let key = self.get_key(); + log::info!("circuit proof generation (no proof in the cache)"); + let proof = test_actual_circuit(circuit, self.degree, instance.clone(), key); + proof_cache.insert(proof_name, proof.clone()); + proof + } + }; + + log::info!("root circuit new"); + let root_circuit = RootCircuit::>::new( + ¶ms, + &protocol, + Value::known(&instance), + Value::known(&proof), + ) + .unwrap(); + + if actual { + let root_key = self.get_root_key(); + let instance = root_circuit.instance(); + log::info!("root circuit proof generation"); + test_actual_root_circuit(root_circuit, self.root_degree, instance, root_key); + } else { + log::info!("root circuit mock prover verification"); + // Mock + let mock_prover = + MockProver::::run(self.root_degree, &root_circuit, root_circuit.instance()) + .unwrap(); + self.test_root_variadic(&mock_prover); + mock_prover + .verify_par() + .expect("mock prover verification failed"); + } } else { - self.test_mock(&circuit, instance); + if actual { + let key = self.get_key(); + log::info!("circuit proof generation"); + let proof = test_actual_circuit(circuit, self.degree, instance, key); + let mut proof_cache = PROOF_CACHE.lock().await; + proof_cache.insert(proof_name, proof); + } else { + log::info!("circuit mock prover verification"); + self.test_mock(&circuit, instance); + } } } } diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index 1582bd196b..5169b4977d 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -1,58 +1,59 @@ macro_rules! run_test { - ($test_instance:expr, $block_tag:expr, $real_prover:expr) => { + ($test_instance:expr, $block_tag:expr, $root:expr, $real_prover:expr) => { log_init(); let mut test = $test_instance.lock().await; - test.test_at_block_tag($block_tag, $real_prover).await; + test.test_at_block_tag($block_tag, $root, $real_prover) + .await; }; } macro_rules! declare_tests { - (($name:ident, $block_tag:expr),$real_prover:expr) => { + (($name:ident, $block_tag:expr),$root:expr,$real_prover:expr) => { paste! { #[tokio::test] async fn []() { - run_test! (EVM_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (EVM_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (STATE_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (STATE_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (TX_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (TX_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (BYTECODE_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (BYTECODE_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (COPY_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (COPY_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (KECCAK_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (KECCAK_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (SUPER_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (SUPER_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (EXP_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (EXP_CIRCUIT_TEST, $block_tag, $root, $real_prover); } #[tokio::test] async fn []() { - run_test! (PI_CIRCUIT_TEST, $block_tag, $real_prover); + run_test! (PI_CIRCUIT_TEST, $block_tag, $root, $real_prover); } } }; @@ -73,17 +74,37 @@ macro_rules! unroll_tests { PI_CIRCUIT_TEST, }; use integration_tests::log_init; - mod real_prover { + // NOTE: The SubCircuits include all well known SubCircuits and the SuperCircuit. + + // SubCircuit tests with real prover + mod sub_real_prover { + use super::*; + $( + declare_tests! ($arg, false, true) ; + )* + } + + // SubCircuit tests with mock prover + mod sub_mock_prover { + use super::*; + $( + declare_tests! ($arg, false, false) ; + )* + } + + // Root Circuit (aggregation) tests with real prover. Needs real proof of each SubCircuit. + mod root_real_prover { use super::*; $( - declare_tests! ($arg, true) ; + declare_tests! ($arg, true, true) ; )* } - mod mock_prover { + // Root Circuit (aggregation) tests with mock prover. Needs real proof of each SubCircuit. + mod root_mock_prover { use super::*; $( - declare_tests! ($arg, false) ; + declare_tests! ($arg, true, false) ; )* } } diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index b20410557a..4941c9ace5 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -33,7 +33,7 @@ integer = { git = "https://github.com/privacy-scaling-explorations/halo2wrong" libsecp256k1 = "0.7" num-bigint = { version = "0.4" } rand_chacha = "0.3" -snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_04_20", default-features = false, features = ["loader_halo2", "system_halo2"] } +snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_04_20", default-features = false, features = ["loader_halo2", "system_halo2", "loader_evm"] } cli-table = { version = "0.4", optional = true } [dev-dependencies] diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index 0b3eaee2a4..4ed697017e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -133,7 +133,7 @@ impl ExecutionGadget for EndBlockGadget { let max_txs_assigned = self.max_txs.assign(region, offset, Value::known(max_txs))?; // When rw_indices is not empty, we're at the last row (at a fixed offset), // where we need to access the max_rws and max_txs constant. - if !step.rw_indices_len() == 0 { + if step.rw_indices_len() != 0 { region.constrain_constant(max_rws_assigned, max_rws)?; region.constrain_constant(max_txs_assigned, max_txs)?; } diff --git a/zkevm-circuits/src/exp_circuit/test.rs b/zkevm-circuits/src/exp_circuit/test.rs index 2b5012458d..a4a8e0da57 100644 --- a/zkevm-circuits/src/exp_circuit/test.rs +++ b/zkevm-circuits/src/exp_circuit/test.rs @@ -54,7 +54,14 @@ fn gen_code_multiple(args: Vec<(Word, Word)>) -> Bytecode { fn gen_data(code: Bytecode) -> CircuitInputBuilder { let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + let mut builder = BlockData::new_from_geth_data_with_params( + block.clone(), + CircuitsParams { + max_exp_steps: 1000, + ..CircuitsParams::default() + }, + ) + .new_circuit_input_builder(); builder .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); @@ -111,14 +118,19 @@ fn exp_circuit_multiple() { #[test] fn variadic_size_check() { - let k = 20; + let k = 13; // Empty let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) .unwrap() .into(); - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default()) - .new_circuit_input_builder(); + let mut builder = BlockData::new_from_geth_data_with_params( + block.clone(), + CircuitsParams { + max_exp_steps: 1000, + ..CircuitsParams::default() + }, + ) + .new_circuit_input_builder(); builder .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); diff --git a/zkevm-circuits/src/root_circuit.rs b/zkevm-circuits/src/root_circuit.rs index c5c38feb50..ec51a01ccd 100644 --- a/zkevm-circuits/src/root_circuit.rs +++ b/zkevm-circuits/src/root_circuit.rs @@ -9,8 +9,8 @@ use halo2_proofs::{ }; use itertools::Itertools; use maingate::MainGateInstructions; + use snark_verifier::{ - loader::native::NativeLoader, pcs::{ kzg::{KzgAccumulator, KzgAsProvingKey, KzgAsVerifyingKey, KzgDecidingKey}, AccumulationDecider, AccumulationScheme, AccumulationSchemeProver, @@ -32,7 +32,10 @@ pub use aggregation::{ aggregate, AggregationConfig, EccChip, Gwc, Halo2Loader, KzgDk, KzgSvk, PlonkSuccinctVerifier, PlonkVerifier, PoseidonTranscript, Shplonk, Snark, SnarkWitness, BITS, LIMBS, }; -pub use snark_verifier::system::halo2::{compile, Config}; +pub use snark_verifier::{ + loader::native::NativeLoader, + system::halo2::{compile, transcript::evm::EvmTranscript, Config}, +}; #[cfg(any(feature = "test", test))] pub use aggregation::TestAggregationCircuit; diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs index a48761229d..a51d48a146 100644 --- a/zkevm-circuits/src/table/copy_table.rs +++ b/zkevm-circuits/src/table/copy_table.rs @@ -32,6 +32,8 @@ pub struct CopyTable { pub rw_counter: Column, /// Decrementing counter denoting reverse read-write counter. pub rwc_inc_left: Column, + /// Selector for the tag BinaryNumberChip + pub q_enable: Column, /// Binary chip to constrain the copy table conditionally depending on the /// current row's tag, whether it is Bytecode, Memory, TxCalldata or /// TxLog. @@ -44,6 +46,7 @@ impl CopyTable { Self { is_first: meta.advice_column(), id: meta.advice_column_in(SecondPhase), + q_enable, tag: BinaryNumberChip::configure(meta, q_enable, None), addr: meta.advice_column(), src_addr_end: meta.advice_column(), @@ -235,6 +238,17 @@ impl CopyTable { } } + // Enable selector at all rows + let max_copy_rows = block.circuits_params.max_copy_rows; + for offset in 0..max_copy_rows { + region.assign_fixed( + || "q_enable", + self.q_enable, + offset, + || Value::known(F::ONE), + )?; + } + Ok(()) }, ) diff --git a/zkevm-circuits/src/table/exp_table.rs b/zkevm-circuits/src/table/exp_table.rs index c2ee645e53..628a4c30ff 100644 --- a/zkevm-circuits/src/table/exp_table.rs +++ b/zkevm-circuits/src/table/exp_table.rs @@ -134,17 +134,6 @@ impl ExpTable { || Value::known(value), )?; } - let is_step = if offset % OFFSET_INCREMENT == 0 { - F::ONE - } else { - F::ZERO - }; - region.assign_fixed( - || format!("exponentiation table row {}", offset), - self.is_step, - offset, - || Value::known(is_step), - )?; offset += 1; } } @@ -160,6 +149,22 @@ impl ExpTable { )?; } + // Enable selector at all rows + let max_exp_steps = block.circuits_params.max_exp_steps; + for offset in 0..max_exp_steps * OFFSET_INCREMENT { + let is_step = if offset % OFFSET_INCREMENT == 0 { + F::ONE + } else { + F::ZERO + }; + region.assign_fixed( + || format!("exponentiation table row {}", offset), + self.is_step, + offset, + || Value::known(is_step), + )?; + } + Ok(()) }, )