From 6efb9b87172a3ac252c1f15b8b653ed850094832 Mon Sep 17 00:00:00 2001 From: Eduard S Date: Mon, 30 Jan 2023 11:29:48 +0100 Subject: [PATCH] chore: unify circuits in integration tests (#1104) * chore: unify circuits in integration tests - unify circuit proving test in integration tests with a single generic function that takes a SubCircuit - implement SubCircuit for SuperCircuit - fix: Invalid `SubCircuit.instance()` implementation for TxCircuit - allow setting the mock_randomness for the SuperCircuit externally * feat(super): remove unnecessary const generics - Remove MAX_TXS, MAX_CALLDATA and MAX_RWS from SuperCircuitConfig - Remove MAX_RWS and MAX_CALLDATA from SuperCircuit --- circuit-benchmarks/src/super_circuit.rs | 16 +- .../src/integration_test_circuits.rs | 202 +++------ integration-tests/tests/circuits.rs | 55 ++- testool/src/statetest/executor.rs | 13 +- zkevm-circuits/src/super_circuit.rs | 399 ++++++++++-------- zkevm-circuits/src/tx_circuit.rs | 6 + 6 files changed, 351 insertions(+), 340 deletions(-) diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index 8d24b04403..ac9cddfcd5 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -3,6 +3,7 @@ #[cfg(test)] mod tests { use ark_std::{end_timer, start_timer}; + use bus_mapping::circuit_input_builder::CircuitsParams; use eth_types::geth_types::GethData; use eth_types::{address, bytecode, Word}; use ethers_signers::LocalWallet; @@ -71,7 +72,18 @@ mod tests { block.sign(&wallets); - let (_, circuit, instance, _) = SuperCircuit::<_, 1, 32, 512, 512>::build(block).unwrap(); + const MAX_TXS: usize = 1; + const MAX_CALLDATA: usize = 32; + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: 256, + max_copy_rows: 256, + max_bytecode: 512, + keccak_padding: None, + }; + let (_, circuit, instance, _) = + SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, 0x100>::build(block, circuits_params).unwrap(); let instance_refs: Vec<&[Fr]> = instance.iter().map(|v| &v[..]).collect(); // Bench setup generation @@ -96,7 +108,7 @@ mod tests { Challenge255, ChaChaRng, Blake2bWrite, G1Affine, Challenge255>, - SuperCircuit, + SuperCircuit, >( &general_params, &pk, diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index 1f17cc7e6a..b754061ce6 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -25,27 +25,47 @@ use std::collections::HashMap; use std::sync::Mutex; use zkevm_circuits::bytecode_circuit::bytecode_unroller::BytecodeCircuit; use zkevm_circuits::copy_circuit::CopyCircuit; -use zkevm_circuits::evm_circuit::test::get_test_degree; -use zkevm_circuits::evm_circuit::{test::get_test_cicuit_from_block, witness::block_convert}; +use zkevm_circuits::evm_circuit::witness::block_convert; use zkevm_circuits::state_circuit::StateCircuit; -use zkevm_circuits::super_circuit::SuperCircuit; use zkevm_circuits::tx_circuit::TxCircuit; use zkevm_circuits::util::SubCircuit; use zkevm_circuits::witness::Block; +/// TEST_MOCK_RANDOMNESS +pub const TEST_MOCK_RANDOMNESS: u64 = 0x100; + +/// MAX_TXS +pub const MAX_TXS: usize = 4; +/// MAX_CALLDATA +pub const MAX_CALLDATA: usize = 512; +/// MAX_RWS +pub const MAX_RWS: usize = 5888; +/// MAX_BYTECODE +pub const MAX_BYTECODE: usize = 5000; +/// MAX_COPY_ROWS +pub const MAX_COPY_ROWS: usize = 5888; + const CIRCUITS_PARAMS: CircuitsParams = CircuitsParams { - max_rws: 16384, - max_txs: 4, - max_calldata: 4000, - max_bytecode: 4000, - max_copy_rows: 16384, + max_rws: MAX_RWS, + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_bytecode: MAX_BYTECODE, + max_copy_rows: MAX_COPY_ROWS, keccak_padding: None, }; -const STATE_CIRCUIT_DEGREE: u32 = 17; -const TX_CIRCUIT_DEGREE: u32 = 20; -const BYTECODE_CIRCUIT_DEGREE: u32 = 16; -const COPY_CIRCUIT_DEGREE: u32 = 16; +/// EVM Circuit degree +pub const EVM_CIRCUIT_DEGREE: u32 = 18; +/// State Circuit degree +pub const STATE_CIRCUIT_DEGREE: u32 = 17; +/// Tx Circuit degree +pub const TX_CIRCUIT_DEGREE: u32 = 20; +/// Bytecode Circuit degree +pub const BYTECODE_CIRCUIT_DEGREE: u32 = 16; +/// Copy Circuit degree +pub const COPY_CIRCUIT_DEGREE: u32 = 16; +/// Super Circuit degree +pub const SUPER_CIRCUIT_DEGREE: u32 = 20; lazy_static! { /// Data generation. @@ -61,7 +81,8 @@ lazy_static! { } lazy_static! { - static ref STATE_CIRCUIT_KEY: ProvingKey = { + /// State Circuit proving key + pub static ref STATE_CIRCUIT_KEY: ProvingKey = { let block = new_empty_block(); let circuit = StateCircuit::::new_from_block(&block); let general_params = get_general_params(STATE_CIRCUIT_DEGREE); @@ -70,7 +91,8 @@ lazy_static! { keygen_vk(&general_params, &circuit).expect("keygen_vk should not fail"); keygen_pk(&general_params, verifying_key, &circuit).expect("keygen_pk should not fail") }; - static ref TX_CIRCUIT_KEY: ProvingKey = { + /// Tx Circuit proving key + pub static ref TX_CIRCUIT_KEY: ProvingKey = { let block = new_empty_block(); let circuit = TxCircuit::::new_from_block(&block); let general_params = get_general_params(TX_CIRCUIT_DEGREE); @@ -79,7 +101,8 @@ lazy_static! { keygen_vk(&general_params, &circuit).expect("keygen_vk should not fail"); keygen_pk(&general_params, verifying_key, &circuit).expect("keygen_pk should not fail") }; - static ref BYTECODE_CIRCUIT_KEY: ProvingKey = { + /// Bytecode Circuit proving key + pub static ref BYTECODE_CIRCUIT_KEY: ProvingKey = { let block = new_empty_block(); let circuit = BytecodeCircuit::::new_from_block(&block); let general_params = get_general_params(BYTECODE_CIRCUIT_DEGREE); @@ -88,7 +111,8 @@ lazy_static! { keygen_vk(&general_params, &circuit).expect("keygen_vk should not fail"); keygen_pk(&general_params, verifying_key, &circuit).expect("keygen_pk should not fail") }; - static ref COPY_CIRCUIT_KEY: ProvingKey = { + /// Copy Circuit proving key + pub static ref COPY_CIRCUIT_KEY: ProvingKey = { let block = new_empty_block(); let circuit = CopyCircuit::::new_from_block(&block); let general_params = get_general_params(COPY_CIRCUIT_DEGREE); @@ -238,142 +262,24 @@ fn test_mock>(degree: u32, circuit: &C, instance: Vec>) { .expect("mock prover verification failed"); } -/// Integration test for evm circuit. -pub async fn test_evm_circuit_block(block_num: u64, actual: bool) { - log::info!("test evm circuit, block number: {}", block_num); - let (builder, _) = gen_inputs(block_num).await; - - let block = block_convert(&builder.block, &builder.code_db).unwrap(); - - let degree = get_test_degree(&block); - let circuit = get_test_cicuit_from_block(block); - - if actual { - test_actual(degree, circuit, vec![], None); - } else { - test_mock(degree, &circuit, vec![]); - } -} - -/// Integration test for state circuit. -pub async fn test_state_circuit_block(block_num: u64, actual: bool) { - log::info!("test state circuit, block number: {}", block_num); - +/// Integration test generic function +pub async fn test_circuit_at_block + Circuit>( + circuit_name: &str, + degree: u32, + block_num: u64, + actual: bool, + proving_key: Option>, +) { + log::info!("test {} circuit, block number: {}", circuit_name, block_num); let (builder, _) = gen_inputs(block_num).await; - let block = block_convert(&builder.block, &builder.code_db).unwrap(); - - let circuit = StateCircuit::::new_from_block(&block); + 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 { - test_actual( - STATE_CIRCUIT_DEGREE, - circuit, - instance, - Some((*STATE_CIRCUIT_KEY).clone()), - ); + test_actual(degree, circuit, instance, proving_key); } else { - test_mock(STATE_CIRCUIT_DEGREE, &circuit, instance); - } -} - -/// Integration test for tx circuit. -pub async fn test_tx_circuit_block(block_num: u64, actual: bool) { - log::info!("test tx circuit, block number: {}", block_num); - - let (builder, _) = gen_inputs(block_num).await; - - let block = block_convert(&builder.block, &builder.code_db).unwrap(); - let circuit = TxCircuit::::new_from_block(&block); - - if actual { - test_actual( - TX_CIRCUIT_DEGREE, - circuit, - vec![vec![]], - Some((*TX_CIRCUIT_KEY).clone()), - ); - } else { - test_mock(TX_CIRCUIT_DEGREE, &circuit, vec![vec![]]); - } -} - -/// Integration test for bytecode circuit. -pub async fn test_bytecode_circuit_block(block_num: u64, actual: bool) { - log::info!("test bytecode circuit, block number: {}", block_num); - let (builder, _) = gen_inputs(block_num).await; - - let block = block_convert(&builder.block, &builder.code_db).unwrap(); - let circuit = - BytecodeCircuit::::new_from_block_sized(&block, 2usize.pow(BYTECODE_CIRCUIT_DEGREE)); - - if actual { - test_actual( - BYTECODE_CIRCUIT_DEGREE, - circuit, - Vec::new(), - Some((*BYTECODE_CIRCUIT_KEY).clone()), - ); - } else { - test_mock(BYTECODE_CIRCUIT_DEGREE, &circuit, Vec::new()); - } -} - -/// Integration test for copy circuit. -pub async fn test_copy_circuit_block(block_num: u64, actual: bool) { - log::info!("test copy circuit, block number: {}", block_num); - let (builder, _) = gen_inputs(block_num).await; - let block = block_convert(&builder.block, &builder.code_db).unwrap(); - - let circuit = CopyCircuit::::new_from_block(&block); - - if actual { - test_actual( - COPY_CIRCUIT_DEGREE, - circuit, - vec![], - Some((*COPY_CIRCUIT_KEY).clone()), - ); - } else { - test_mock(COPY_CIRCUIT_DEGREE, &circuit, vec![]); - } -} - -/// Integration test for super circuit. -pub async fn test_super_circuit_block(block_num: u64) { - const MAX_TXS: usize = 4; - const MAX_CALLDATA: usize = 512; - const MAX_RWS: usize = 5888; - const MAX_BYTECODE: usize = 5000; - const MAX_COPY_ROWS: usize = 5888; - - log::info!("test super circuit, block number: {}", block_num); - let cli = get_client(); - let cli = BuilderClient::new( - cli, - CircuitsParams { - max_rws: MAX_RWS, - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_bytecode: MAX_BYTECODE, - max_copy_rows: MAX_COPY_ROWS, - keccak_padding: None, - }, - ) - .await - .unwrap(); - let (builder, _) = cli.gen_inputs(block_num).await.unwrap(); - let (k, circuit, instance) = - SuperCircuit::::build_from_circuit_input_builder( - &builder, - ) - .unwrap(); - // TODO: add actual prover - let prover = MockProver::run(k, &circuit, instance).unwrap(); - let res = prover.verify_par(); - if let Err(err) = res { - eprintln!("Verification failures:"); - eprintln!("{:#?}", err); - panic!("Failed verification"); + test_mock(degree, &circuit, instance); } } diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index 6702ec46db..7ffd1a7801 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -5,35 +5,54 @@ macro_rules! declare_tests { async fn []() { log_init(); let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_evm_circuit_block(*block_num, $real_prover).await; + let pk = None; + test_circuit_at_block::>( + "evm", EVM_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; } #[tokio::test] async fn []() { log_init(); let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_state_circuit_block(*block_num, $real_prover).await; + let pk = if $real_prover { Some((*STATE_CIRCUIT_KEY).clone()) } else { None }; + test_circuit_at_block::> + ("state", STATE_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; } #[tokio::test] async fn []() { log_init(); let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_tx_circuit_block(*block_num, $real_prover).await; + let pk = if $real_prover { Some((*TX_CIRCUIT_KEY).clone()) } else { None }; + test_circuit_at_block::> + ("tx", TX_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; } #[tokio::test] async fn []() { log_init(); let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_bytecode_circuit_block(*block_num, $real_prover).await; + let pk = if $real_prover { Some((*BYTECODE_CIRCUIT_KEY).clone()) } else { None }; + test_circuit_at_block::> + ("bytecode", BYTECODE_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; } #[tokio::test] async fn []() { log_init(); let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_copy_circuit_block(*block_num, $real_prover).await; + let pk = if $real_prover { Some((*COPY_CIRCUIT_KEY).clone()) } else { None }; + test_circuit_at_block::> + ("copy", COPY_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; + } + + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + let pk = None; + test_circuit_at_block::> + ("super", SUPER_CIRCUIT_DEGREE, *block_num, $real_prover, pk).await; } } }; @@ -41,25 +60,27 @@ macro_rules! declare_tests { macro_rules! unroll_tests { ($($arg:tt),*) => { + use paste::paste; + use zkevm_circuits::{ + state_circuit::StateCircuit, + super_circuit::SuperCircuit, + tx_circuit::TxCircuit, + evm_circuit::EvmCircuit, + bytecode_circuit::bytecode_unroller::BytecodeCircuit, + copy_circuit::CopyCircuit + }; + use halo2_proofs::halo2curves::bn256::Fr; + use integration_tests::integration_test_circuits::*; + use integration_tests::log_init; mod real_prover { - use paste::paste; - use integration_tests::integration_test_circuits::{ - test_bytecode_circuit_block, test_copy_circuit_block, test_evm_circuit_block, - test_state_circuit_block, test_tx_circuit_block, GEN_DATA, - }; - use integration_tests::log_init; + use super::*; $( declare_tests! ($arg, true) ; )* } mod mock_prover { - use paste::paste; - use integration_tests::integration_test_circuits::{ - test_bytecode_circuit_block, test_copy_circuit_block, test_evm_circuit_block, - test_state_circuit_block, test_tx_circuit_block, GEN_DATA, - }; - use integration_tests::log_init; + use super::*; $( declare_tests! ($arg, false) ; )* diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index a5ee4e62ca..b9143311eb 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -275,8 +275,19 @@ pub fn run_test( } else { geth_data.sign(&wallets); + const MAX_TXS: usize = 1; + const MAX_CALLDATA: usize = 32; + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: 256, + max_copy_rows: 256, + max_bytecode: 512, + keccak_padding: None, + }; let (k, circuit, instance, _builder) = - SuperCircuit::::build(geth_data).unwrap(); + SuperCircuit::::build(geth_data, circuits_params) + .unwrap(); builder = _builder; let prover = MockProver::run(k, &circuit, instance).unwrap(); diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index c2c0538db2..635fee3523 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -79,19 +79,9 @@ use halo2_proofs::{ use std::array; -/// Mock randomness used for `SuperCircuit`. -pub const MOCK_RANDOMNESS: u64 = 0x100; -// TODO: Figure out if we can remove MAX_TXS, MAX_CALLDATA and MAX_RWS from the -// struct. - /// Configuration of the Super Circuit #[derive(Clone)] -pub struct SuperCircuitConfig< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MAX_RWS: usize, -> { +pub struct SuperCircuitConfig { block_table: BlockTable, mpt_table: MptTable, evm_circuit: EvmCircuitConfig, @@ -104,65 +94,28 @@ pub struct SuperCircuitConfig< exp_circuit: ExpCircuitConfig, } -/// The Super Circuit contains all the zkEVM circuits -#[derive(Clone, Default, Debug)] -pub struct SuperCircuit< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MAX_RWS: usize, - const MAX_COPY_ROWS: usize, -> { - /// EVM Circuit - pub evm_circuit: EvmCircuit, - /// State Circuit - pub state_circuit: StateCircuit, - /// The transaction circuit that will be used in the `synthesize` step. - pub tx_circuit: TxCircuit, - /// Public Input Circuit - pub pi_circuit: PiCircuit, - /// Bytecode Circuit - pub bytecode_circuit: BytecodeCircuit, - /// Copy Circuit - pub copy_circuit: CopyCircuit, - /// Exp Circuit - pub exp_circuit: ExpCircuit, - /// Keccak Circuit - pub keccak_circuit: KeccakCircuit, +/// Circuit configuration arguments +pub struct SuperCircuitConfigArgs { + /// Max txs + pub max_txs: usize, + /// Max calldata + pub max_calldata: usize, + /// Mock randomness + pub mock_randomness: u64, } -impl< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MAX_RWS: usize, - const MAX_COPY_ROWS: usize, - > SuperCircuit -{ - /// Return the number of rows required to verify a given block - pub fn get_num_rows_required(block: &Block) -> usize { - let num_rows_evm_circuit = EvmCircuit::::get_num_rows_required(block); - let num_rows_tx_circuit = TxCircuitConfig::::get_num_rows_required(MAX_TXS); - num_rows_evm_circuit.max(num_rows_tx_circuit) - } -} - -impl< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MAX_RWS: usize, - const MAX_COPY_ROWS: usize, - > Circuit for SuperCircuit -{ - type Config = SuperCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { +impl SubCircuitConfig for SuperCircuitConfig { + type ConfigArgs = SuperCircuitConfigArgs; + + /// Configure SuperCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + max_txs, + max_calldata, + mock_randomness, + }: Self::ConfigArgs, + ) -> Self { let tx_table = TxTable::construct(meta); let rw_table = RwTable::construct(meta); let mpt_table = MptTable::construct(meta); @@ -173,8 +126,10 @@ impl< let exp_table = ExpTable::construct(meta); let keccak_table = KeccakTable::construct(meta); + // Use a mock randomness instead of the randomness derived from the challange + // (either from mock or real prover) to help debugging assignments. let power_of_randomness: [Expression; 31] = array::from_fn(|i| { - Expression::Constant(F::from(MOCK_RANDOMNESS).pow(&[1 + i as u64, 0, 0, 0])) + Expression::Constant(F::from(mock_randomness).pow(&[1 + i as u64, 0, 0, 0])) }); let challenges = Challenges::mock( @@ -194,8 +149,8 @@ impl< let pi_circuit = PiCircuitConfig::new( meta, PiCircuitConfigArgs { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs, + max_calldata, block_table: block_table.clone(), tx_table: tx_table.clone(), }, @@ -250,7 +205,7 @@ impl< }, ); - Self::Config { + Self { block_table, mpt_table, evm_circuit, @@ -263,6 +218,159 @@ impl< exp_circuit, } } +} + +/// The Super Circuit contains all the zkEVM circuits +#[derive(Clone, Default, Debug)] +pub struct SuperCircuit< + F: Field, + const MAX_TXS: usize, + const MAX_CALLDATA: usize, + const MOCK_RANDOMNESS: u64, +> { + /// EVM Circuit + pub evm_circuit: EvmCircuit, + /// State Circuit + pub state_circuit: StateCircuit, + /// The transaction circuit that will be used in the `synthesize` step. + pub tx_circuit: TxCircuit, + /// Public Input Circuit + pub pi_circuit: PiCircuit, + /// Bytecode Circuit + pub bytecode_circuit: BytecodeCircuit, + /// Copy Circuit + pub copy_circuit: CopyCircuit, + /// Exp Circuit + pub exp_circuit: ExpCircuit, + /// Keccak Circuit + pub keccak_circuit: KeccakCircuit, +} + +impl + SuperCircuit +{ + /// Return the number of rows required to verify a given block + pub fn get_num_rows_required(block: &Block) -> usize { + let num_rows_evm_circuit = EvmCircuit::::get_num_rows_required(block); + assert_eq!(block.circuits_params.max_txs, MAX_TXS); + let num_rows_tx_circuit = + TxCircuitConfig::::get_num_rows_required(block.circuits_params.max_txs); + num_rows_evm_circuit.max(num_rows_tx_circuit) + } +} + +// Eventhough the SuperCircuit is not a subcircuit we implement the SubCircuit +// trait for it in order to get the `new_from_block` and `instance` methods that +// allow us to generalize integration tests. +impl + SubCircuit for SuperCircuit +{ + type Config = SuperCircuitConfig; + + fn new_from_block(block: &Block) -> Self { + let evm_circuit = EvmCircuit::new_from_block(block); + let state_circuit = StateCircuit::new_from_block(block); + let tx_circuit = TxCircuit::new_from_block(block); + let pi_circuit = PiCircuit::new_from_block(block); + let bytecode_circuit = BytecodeCircuit::new_from_block(block); + let copy_circuit = CopyCircuit::new_from_block_no_external(block); + let exp_circuit = ExpCircuit::new_from_block(block); + let keccak_circuit = KeccakCircuit::new_from_block(block); + + SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS> { + evm_circuit, + state_circuit, + tx_circuit, + pi_circuit, + bytecode_circuit, + copy_circuit, + exp_circuit, + keccak_circuit, + } + } + + /// Returns suitable inputs for the SuperCircuit. + fn instance(&self) -> Vec> { + let mut instance = Vec::new(); + instance.extend_from_slice(&self.keccak_circuit.instance()); + instance.extend_from_slice(&self.pi_circuit.instance()); + instance.extend_from_slice(&self.tx_circuit.instance()); + instance.extend_from_slice(&self.bytecode_circuit.instance()); + instance.extend_from_slice(&self.copy_circuit.instance()); + instance.extend_from_slice(&self.state_circuit.instance()); + instance.extend_from_slice(&self.exp_circuit.instance()); + instance.extend_from_slice(&self.evm_circuit.instance()); + + instance + } + + /// Return the minimum number of rows required to prove the block + fn min_num_rows_block(block: &Block) -> (usize, usize) { + let evm = EvmCircuit::min_num_rows_block(block); + let state = StateCircuit::min_num_rows_block(block); + let bytecode = BytecodeCircuit::min_num_rows_block(block); + let copy = CopyCircuit::min_num_rows_block(block); + let keccak = KeccakCircuit::min_num_rows_block(block); + let tx = TxCircuit::min_num_rows_block(block); + let exp = ExpCircuit::min_num_rows_block(block); + let pi = PiCircuit::min_num_rows_block(block); + + let rows: Vec<(usize, usize)> = vec![evm, state, bytecode, copy, keccak, tx, exp, pi]; + let (rows_without_padding, rows_with_padding): (Vec, Vec) = + rows.into_iter().unzip(); + ( + itertools::max(rows_without_padding).unwrap(), + itertools::max(rows_with_padding).unwrap(), + ) + } + + /// Make the assignments to the SuperCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + self.keccak_circuit + .synthesize_sub(&config.keccak_circuit, challenges, layouter)?; + self.bytecode_circuit + .synthesize_sub(&config.bytecode_circuit, challenges, layouter)?; + self.tx_circuit + .synthesize_sub(&config.tx_circuit, challenges, layouter)?; + self.state_circuit + .synthesize_sub(&config.state_circuit, challenges, layouter)?; + self.copy_circuit + .synthesize_sub(&config.copy_circuit, challenges, layouter)?; + self.exp_circuit + .synthesize_sub(&config.exp_circuit, challenges, layouter)?; + self.evm_circuit + .synthesize_sub(&config.evm_circuit, challenges, layouter)?; + self.pi_circuit + .synthesize_sub(&config.pi_circuit, challenges, layouter)?; + Ok(()) + } +} + +impl + Circuit for SuperCircuit +{ + type Config = SuperCircuitConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + Self::Config::new( + meta, + SuperCircuitConfigArgs { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + mock_randomness: MOCK_RANDOMNESS, + }, + ) + } fn synthesize( &self, @@ -289,36 +397,12 @@ impl< Value::known(block.randomness), )?; - self.keccak_circuit - .synthesize_sub(&config.keccak_circuit, &challenges, &mut layouter)?; - self.bytecode_circuit.synthesize_sub( - &config.bytecode_circuit, - &challenges, - &mut layouter, - )?; - self.tx_circuit - .synthesize_sub(&config.tx_circuit, &challenges, &mut layouter)?; - self.state_circuit - .synthesize_sub(&config.state_circuit, &challenges, &mut layouter)?; - self.copy_circuit - .synthesize_sub(&config.copy_circuit, &challenges, &mut layouter)?; - self.exp_circuit - .synthesize_sub(&config.exp_circuit, &challenges, &mut layouter)?; - self.evm_circuit - .synthesize_sub(&config.evm_circuit, &challenges, &mut layouter)?; - self.pi_circuit - .synthesize_sub(&config.pi_circuit, &challenges, &mut layouter)?; - Ok(()) + self.synthesize_sub(&config, &challenges, &mut layouter) } } -impl< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MAX_RWS: usize, - const MAX_COPY_ROWS: usize, - > SuperCircuit +impl + SuperCircuit { /// From the witness data, generate a SuperCircuit instance with all of the /// sub-circuits filled with their corresponding witnesses. @@ -328,18 +412,10 @@ impl< #[allow(clippy::type_complexity)] pub fn build( geth_data: GethData, + circuits_params: CircuitsParams, ) -> Result<(u32, Self, Vec>, CircuitInputBuilder), bus_mapping::Error> { - let block_data = BlockData::new_from_geth_data_with_params( - geth_data.clone(), - CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_rws: MAX_RWS, - max_copy_rows: MAX_COPY_ROWS, - max_bytecode: 512, - keccak_padding: None, - }, - ); + let block_data = + BlockData::new_from_geth_data_with_params(geth_data.clone(), circuits_params); let mut builder = block_data.new_circuit_input_builder(); builder .handle_block(&geth_data.eth_block, &geth_data.geth_traces) @@ -359,64 +435,20 @@ impl< ) -> Result<(u32, Self, Vec>), bus_mapping::Error> { let mut block = block_convert(&builder.block, &builder.code_db).unwrap(); block.randomness = F::from(MOCK_RANDOMNESS); + assert_eq!(block.circuits_params.max_txs, MAX_TXS); + assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); const NUM_BLINDING_ROWS: usize = 64; let (_, rows_needed) = Self::min_num_rows_block(&block); let k = log2_ceil(NUM_BLINDING_ROWS + rows_needed); log::debug!("super circuit uses k = {}", k); - let evm_circuit = EvmCircuit::new_from_block(&block); - let state_circuit = StateCircuit::new_from_block(&block); - let tx_circuit = TxCircuit::new_from_block(&block); - let pi_circuit = PiCircuit::new_from_block(&block); - let bytecode_circuit = BytecodeCircuit::new_from_block(&block); - let copy_circuit = CopyCircuit::new_from_block_no_external(&block); - let exp_circuit = ExpCircuit::new_from_block(&block); - let keccak_circuit = KeccakCircuit::new_from_block(&block); - - let circuit = SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MAX_RWS, MAX_COPY_ROWS> { - evm_circuit, - state_circuit, - tx_circuit, - pi_circuit, - bytecode_circuit, - copy_circuit, - exp_circuit, - keccak_circuit, - }; + let circuit = + SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS>::new_from_block(&block); let instance = circuit.instance(); Ok((k, circuit, instance)) } - - /// Returns suitable inputs for the SuperCircuit. - pub fn instance(&self) -> Vec> { - // SignVerifyChip -> ECDSAChip -> MainGate instance column - let pi_instance = self.pi_circuit.instance(); - let instance = vec![pi_instance[0].clone(), vec![]]; - - instance - } - - /// Return the minimum number of rows required to prove the block - pub fn min_num_rows_block(block: &Block) -> (usize, usize) { - let evm = EvmCircuit::min_num_rows_block(block); - let state = StateCircuit::min_num_rows_block(block); - let bytecode = BytecodeCircuit::min_num_rows_block(block); - let copy = CopyCircuit::min_num_rows_block(block); - let keccak = KeccakCircuit::min_num_rows_block(block); - let tx = TxCircuit::min_num_rows_block(block); - let exp = ExpCircuit::min_num_rows_block(block); - let pi = PiCircuit::min_num_rows_block(block); - - let rows: Vec<(usize, usize)> = vec![evm, state, bytecode, copy, keccak, tx, exp, pi]; - let (rows_without_padding, rows_with_padding): (Vec, Vec) = - rows.into_iter().unzip(); - ( - itertools::max(rows_without_padding).unwrap(), - itertools::max(rows_with_padding).unwrap(), - ) - } } #[cfg(test)] @@ -436,7 +468,7 @@ mod super_circuit_tests { #[test] fn super_circuit_degree() { let mut cs = ConstraintSystem::::default(); - SuperCircuit::<_, 1, 32, 256, 32>::configure(&mut cs); + SuperCircuit::<_, 1, 32, 0x100>::configure(&mut cs); log::info!("super circuit degree: {}", cs.degree()); log::info!("super circuit minimum_rows: {}", cs.minimum_rows()); assert!(cs.degree() <= 9); @@ -445,14 +477,17 @@ mod super_circuit_tests { fn test_super_circuit< const MAX_TXS: usize, const MAX_CALLDATA: usize, - const MAX_RWS: usize, - const MAX_COPY_ROWS: usize, + const MOCK_RANDOMNESS: u64, >( block: GethData, + circuits_params: CircuitsParams, ) { let (k, circuit, instance, _) = - SuperCircuit::::build(block) - .unwrap(); + SuperCircuit::::build( + block, + circuits_params, + ) + .unwrap(); let prover = MockProver::run(k, &circuit, instance).unwrap(); let res = prover.verify_par(); if let Err(err) = res { @@ -547,6 +582,8 @@ mod super_circuit_tests { block } + const TEST_MOCK_RANDOMNESS: u64 = 0x100; + // High memory usage test. Run in serial with: // `cargo test [...] serial_ -- --ignored --test-threads 1` #[ignore] @@ -555,9 +592,15 @@ mod super_circuit_tests { let block = block_1tx(); const MAX_TXS: usize = 1; const MAX_CALLDATA: usize = 32; - const MAX_RWS: usize = 256; - const MAX_COPY_ROWS: usize = 256; - test_super_circuit::(block); + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: 256, + max_copy_rows: 256, + max_bytecode: 512, + keccak_padding: None, + }; + test_super_circuit::(block, circuits_params); } #[ignore] #[test] @@ -565,9 +608,15 @@ mod super_circuit_tests { let block = block_1tx(); const MAX_TXS: usize = 2; const MAX_CALLDATA: usize = 32; - const MAX_RWS: usize = 256; - const MAX_COPY_ROWS: usize = 256; - test_super_circuit::(block); + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: 256, + max_copy_rows: 256, + max_bytecode: 512, + keccak_padding: None, + }; + test_super_circuit::(block, circuits_params); } #[ignore] #[test] @@ -575,8 +624,14 @@ mod super_circuit_tests { let block = block_2tx(); const MAX_TXS: usize = 2; const MAX_CALLDATA: usize = 32; - const MAX_RWS: usize = 256; - const MAX_COPY_ROWS: usize = 256; - test_super_circuit::(block); + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: 256, + max_copy_rows: 256, + max_bytecode: 512, + keccak_padding: None, + }; + test_super_circuit::(block, circuits_params); } } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index c8de88e68e..09e6d98f7d 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -374,6 +374,12 @@ impl SubCircuit for TxCircuit { self.assign_tx_table(config, challenges, layouter, assigned_sig_verifs)?; Ok(()) } + + fn instance(&self) -> Vec> { + // The maingate expects an instance column, but we don't use it, so we return an + // "empty" instance column + vec![vec![]] + } } #[cfg(any(feature = "test", test))]