diff --git a/Cargo.lock b/Cargo.lock index 78db2818cf..4cfe037ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2172,10 +2172,13 @@ dependencies = [ "eth-types", "ethers", "ff 0.11.1", + "group 0.11.0", "halo2_proofs 0.1.0-beta.1", "lazy_static", "log", + "paste", "pretty_assertions", + "rand_chacha", "serde", "serde_json", "tokio", diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 9832a2bb82..e04f9b8a84 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -245,6 +245,7 @@ impl BuilderClient

{ ) -> Result<(EthBlock, Vec), Error> { let eth_block = self.cli.get_block_by_number(block_num.into()).await?; let geth_traces = self.cli.trace_block_by_number(block_num.into()).await?; + Ok((eth_block, geth_traces)) } @@ -352,12 +353,21 @@ impl BuilderClient

{ } /// Perform all the steps to generate the circuit inputs - pub async fn gen_inputs(&self, block_num: u64) -> Result { + pub async fn gen_inputs( + &self, + block_num: u64, + ) -> Result< + ( + CircuitInputBuilder, + eth_types::Block, + ), + Error, + > { let (eth_block, geth_traces) = self.get_block(block_num).await?; let access_set = self.get_state_accesses(ð_block, &geth_traces)?; let (proofs, codes) = self.get_state(block_num, access_set).await?; let (state_db, code_db) = self.build_state_code_db(proofs, codes); let builder = self.gen_inputs_from_state(state_db, code_db, ð_block, &geth_traces)?; - Ok(builder) + Ok((builder, eth_block)) } } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index c186d05154..12fe12acb4 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -13,7 +13,7 @@ serde_json = "1.0.66" serde = {version = "1.0.130", features = ["derive"] } bus-mapping = { path = "../bus-mapping"} eth-types = { path = "../eth-types"} -zkevm-circuits = { path = "../zkevm-circuits", features = ["test"] } +zkevm-circuits = { path = "../zkevm-circuits", features = ["test", "dev"] } tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] } url = "2.2.2" pretty_assertions = "1.0.0" @@ -21,6 +21,9 @@ log = "0.4.14" env_logger = "0.9" halo2_proofs = { version = "0.1.0-beta.1" } ff = "0.11" +rand_chacha = "0.3" +group = "0.11" +paste = "1.0" [dev-dependencies] pretty_assertions = "1.0.0" diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index 3e5c953202..4425f99020 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -2,13 +2,29 @@ use bus_mapping::circuit_input_builder::BuilderClient; use bus_mapping::operation::OperationContainer; -use halo2_proofs::dev::MockProver; -use integration_tests::{get_client, log_init, GenDataOutput}; +use eth_types::geth_types; +use group::{Curve, Group}; +use halo2_proofs::arithmetic::BaseExt; +use halo2_proofs::{ + arithmetic::{CurveAffine, Field}, + dev::MockProver, + pairing::bn256::Fr, +}; +use integration_tests::{get_client, log_init, GenDataOutput, CHAIN_ID}; use lazy_static::lazy_static; use log::trace; +use paste::paste; +use rand_chacha::rand_core::SeedableRng; +use rand_chacha::ChaCha20Rng; +use std::marker::PhantomData; +use zkevm_circuits::bytecode_circuit::dev::test_bytecode_circuit; +use zkevm_circuits::copy_circuit::dev::test_copy_circuit; use zkevm_circuits::evm_circuit::witness::RwMap; use zkevm_circuits::evm_circuit::{test::run_test_circuit, witness::block_convert}; use zkevm_circuits::state_circuit::StateCircuit; +use zkevm_circuits::tx_circuit::{ + sign_verify::SignVerifyChip, Secp256k1Affine, TxCircuit, POW_RAND_SIZE, VERIF_HEIGHT, +}; lazy_static! { pub static ref GEN_DATA: GenDataOutput = GenDataOutput::load(); @@ -18,19 +34,17 @@ async fn test_evm_circuit_block(block_num: u64) { log::info!("test evm circuit, block number: {}", block_num); let cli = get_client(); let cli = BuilderClient::new(cli).await.unwrap(); - let builder = cli.gen_inputs(block_num).await.unwrap(); + let (builder, _) = cli.gen_inputs(block_num).await.unwrap(); let block = block_convert(&builder.block, &builder.code_db); run_test_circuit(block).expect("evm_circuit verification failed"); } async fn test_state_circuit_block(block_num: u64) { - use halo2_proofs::pairing::bn256::Fr; - log::info!("test state circuit, block number: {}", block_num); let cli = get_client(); let cli = BuilderClient::new(cli).await.unwrap(); - let builder = cli.gen_inputs(block_num).await.unwrap(); + let (builder, _) = cli.gen_inputs(block_num).await.unwrap(); // Generate state proof let stack_ops = builder.block.container.sorted_stack(); @@ -58,20 +72,108 @@ async fn test_state_circuit_block(block_num: u64) { prover.verify().expect("state_circuit verification failed"); } +async fn test_tx_circuit_block(block_num: u64) { + const DEGREE: u32 = 20; + + log::info!("test tx circuit, block number: {}", block_num); + let cli = get_client(); + let cli = BuilderClient::new(cli).await.unwrap(); + + let (_, eth_block) = cli.gen_inputs(block_num).await.unwrap(); + let txs: Vec<_> = eth_block + .transactions + .iter() + .map(geth_types::Transaction::from_eth_tx) + .collect(); + + let mut rng = ChaCha20Rng::seed_from_u64(2); + let aux_generator = ::CurveExt::random(&mut rng).to_affine(); + + let randomness = Fr::random(&mut rng); + let mut instance: Vec> = (1..POW_RAND_SIZE + 1) + .map(|exp| vec![randomness.pow(&[exp as u64, 0, 0, 0]); txs.len() * VERIF_HEIGHT]) + .collect(); + + instance.push(vec![]); + let circuit = TxCircuit:: { + sign_verify: SignVerifyChip { + aux_generator, + window_size: 2, + _marker: PhantomData, + }, + randomness, + txs, + chain_id: CHAIN_ID, + }; + + let prover = MockProver::run(DEGREE, &circuit, instance).unwrap(); + + prover.verify().expect("tx_circuit verification failed"); +} + +pub async fn test_bytecode_circuit_block(block_num: u64) { + const DEGREE: u32 = 16; + let randomness = Fr::from(123456); + + log::info!("test bytecode circuit, block number: {}", block_num); + let cli = get_client(); + let cli = BuilderClient::new(cli).await.unwrap(); + let (builder, _) = cli.gen_inputs(block_num).await.unwrap(); + let bytecodes: Vec> = builder.code_db.0.values().cloned().collect(); + + test_bytecode_circuit(DEGREE, bytecodes, randomness); +} + +pub async fn test_copy_circuit_block(block_num: u64) { + const DEGREE: u32 = 16; + + log::info!("test copy circuit, block number: {}", block_num); + let cli = get_client(); + let cli = BuilderClient::new(cli).await.unwrap(); + let (builder, _) = cli.gen_inputs(block_num).await.unwrap(); + let block = block_convert(&builder.block, &builder.code_db); + + assert!(test_copy_circuit(DEGREE, block).is_ok()); +} + macro_rules! declare_tests { - ($test_evm_name:ident, $test_state_name:ident, $block_tag:expr) => { - #[tokio::test] - async fn $test_evm_name() { - log_init(); - let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_evm_circuit_block(*block_num).await; - } + ($name:ident, $block_tag:expr) => { + paste! { + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + test_evm_circuit_block(*block_num).await; + } + + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + test_state_circuit_block(*block_num).await; + } + + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + test_tx_circuit_block(*block_num).await; + } + + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + test_bytecode_circuit_block(*block_num).await; + } + + #[tokio::test] + async fn []() { + log_init(); + let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); + test_copy_circuit_block(*block_num).await; + } - #[tokio::test] - async fn $test_state_name() { - log_init(); - let block_num = GEN_DATA.blocks.get($block_tag).unwrap(); - test_state_circuit_block(*block_num).await; } }; } @@ -94,17 +196,14 @@ declare_tests!( ); */ declare_tests!( - test_evm_circuit_erc20_openzeppelin_transfer_fail, - test_state_circuit_erc20_openzeppelin_transfer_fail, + circuit_erc20_openzeppelin_transfer_fail, "ERC20 OpenZeppelin transfer failed" ); declare_tests!( - test_evm_circuit_erc20_openzeppelin_transfer_succeed, - test_state_circuit_erc20_openzeppelin_transfer_succeed, + circuit_erc20_openzeppelin_transfer_succeed, "ERC20 OpenZeppelin transfer successful" ); declare_tests!( - test_evm_circuit_multiple_erc20_openzeppelin_transfers, - test_state_circuit_multiple_erc20_openzeppelin_transfers, + circuit_multiple_erc20_openzeppelin_transfers, "Multiple ERC20 OpenZeppelin transfers" ); diff --git a/prover/src/compute_proof.rs b/prover/src/compute_proof.rs index 87db9a8086..b2b0ea8474 100644 --- a/prover/src/compute_proof.rs +++ b/prover/src/compute_proof.rs @@ -34,7 +34,7 @@ pub async fn compute_proof( let url = Http::from_str(rpc_url)?; let geth_client = GethClient::new(url); let builder = BuilderClient::new(geth_client).await?; - let builder = builder.gen_inputs(*block_num).await?; + let (builder, _) = builder.gen_inputs(*block_num).await?; // TODO: only {evm,state}_proof are implemented right now let evm_proof; diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index bf785214b7..c24d864ce9 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -59,4 +59,5 @@ harness = false [features] default = [] -test = [] +test = [ "dev" ] +dev = [] diff --git a/zkevm-circuits/src/bytecode_circuit.rs b/zkevm-circuits/src/bytecode_circuit.rs index 8fbf9d2da2..c14dc6c1ac 100644 --- a/zkevm-circuits/src/bytecode_circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit.rs @@ -1,4 +1,6 @@ //! The bytecode circuit implementation. pub(crate) mod bytecode_unroller; +/// Bytecode circuit tester +pub mod dev; pub(crate) mod param; diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs index a7a814827b..fb34f2f1c4 100644 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs @@ -17,7 +17,6 @@ use keccak256::plain::Keccak; use std::vec; use super::param::PUSH_TABLE_WIDTH; - /// Public data for the bytecode #[derive(Clone, Debug, PartialEq)] pub(crate) struct BytecodeRow { @@ -31,11 +30,12 @@ pub(crate) struct BytecodeRow { /// Unrolled bytecode #[derive(Clone, Debug, PartialEq)] pub struct UnrolledBytecode { - bytes: Vec, + pub(crate) bytes: Vec, rows: Vec>, } #[derive(Clone, Debug)] +/// Bytecode circuit configuration pub struct Config { randomness: Expression, minimum_rows: usize, @@ -54,7 +54,7 @@ pub struct Config { length_inv: Column, length_is_zero: IsZeroConfig, push_table: [Column; PUSH_TABLE_WIDTH], - keccak_table: KeccakTable, + pub(crate) keccak_table: KeccakTable, } impl Config { @@ -583,6 +583,7 @@ impl Config { } } +/// Get unrolled bytecode from raw bytes pub fn unroll(bytes: Vec, randomness: F) -> UnrolledBytecode { let code_hash = keccak(&bytes[..], randomness); let mut rows = vec![BytecodeRow:: { @@ -651,79 +652,14 @@ fn into_words(message: &[u8]) -> Vec { #[cfg(test)] mod tests { use super::*; - use crate::util::power_of_randomness_from_instance; - use eth_types::{Bytecode, Word}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - pairing::bn256::Fr, - plonk::{Circuit, ConstraintSystem, Error}, - }; - - #[derive(Default)] - struct MyCircuit { - bytecodes: Vec>, - size: usize, - randomness: F, - } + use crate::bytecode_circuit::dev::test_bytecode_circuit_unrolled; + use eth_types::Bytecode; + use halo2_proofs::pairing::bn256::Fr; fn get_randomness() -> F { F::from(123456) } - impl Circuit for MyCircuit { - type Config = Config; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let bytecode_table = BytecodeTable::construct(meta); - - let randomness = power_of_randomness_from_instance::<_, 1>(meta); - let keccak_table = KeccakTable::construct(meta); - - Config::configure(meta, randomness[0].clone(), bytecode_table, keccak_table) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.load(&mut layouter)?; - config.keccak_table.load( - &mut layouter, - self.bytecodes.iter().map(|b| b.bytes.clone()), - self.randomness, - )?; - config.assign(&mut layouter, self.size, &self.bytecodes, self.randomness)?; - Ok(()) - } - } - - fn verify(k: u32, bytecodes: Vec>, randomness: F, success: bool) { - let circuit = MyCircuit:: { - bytecodes, - size: 2usize.pow(k), - randomness, - }; - - let num_rows = 1 << k; - const NUM_BLINDING_ROWS: usize = 7 - 1; - let instance = vec![vec![randomness; num_rows - NUM_BLINDING_ROWS]]; - let prover = MockProver::::run(k, &circuit, instance).unwrap(); - let result = prover.verify(); - if let Err(failures) = &result { - for failure in failures.iter() { - println!("{}", failure); - } - } - assert_eq!(result.is_ok(), success); - } - /// Verify unrolling code #[test] fn bytecode_unrolling() { @@ -791,35 +727,35 @@ mod tests { unrolled, ); // Verify the unrolling in the circuit - verify::(k, vec![unrolled], randomness, true); + test_bytecode_circuit_unrolled(k, vec![unrolled], randomness, true); } /// Tests a fully empty circuit #[test] fn bytecode_empty() { let k = 9; - let randomness = get_randomness(); - verify::(k, vec![unroll(vec![], randomness)], randomness, true); + let randomness: Fr = get_randomness(); + test_bytecode_circuit_unrolled(k, vec![unroll(vec![], randomness)], randomness, true); } #[test] fn bytecode_simple() { let k = 9; - let randomness = get_randomness(); + let randomness: Fr = get_randomness(); let bytecodes = vec![ unroll(vec![7u8], randomness), unroll(vec![6u8], randomness), unroll(vec![5u8], randomness), ]; - verify::(k, bytecodes, randomness, true); + test_bytecode_circuit_unrolled(k, bytecodes, randomness, true); } /// Tests a fully full circuit #[test] fn bytecode_full() { let k = 9; - let randomness = get_randomness(); - verify::( + let randomness: Fr = get_randomness(); + test_bytecode_circuit_unrolled( k, vec![unroll(vec![7u8; 2usize.pow(k) - 7], randomness)], randomness, @@ -831,8 +767,8 @@ mod tests { #[test] fn bytecode_incomplete() { let k = 9; - let randomness = get_randomness(); - verify::( + let randomness: Fr = get_randomness(); + test_bytecode_circuit_unrolled( k, vec![unroll(vec![7u8; 2usize.pow(k) + 1], randomness)], randomness, @@ -844,8 +780,8 @@ mod tests { #[test] fn bytecode_push() { let k = 9; - let randomness = get_randomness(); - verify::( + let randomness: Fr = get_randomness(); + test_bytecode_circuit_unrolled( k, vec![ unroll(vec![], randomness), @@ -879,18 +815,18 @@ mod tests { let randomness = get_randomness(); let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; let unrolled = unroll(bytecode, randomness); - verify::(k, vec![unrolled.clone()], randomness, true); + test_bytecode_circuit_unrolled(k, vec![unrolled.clone()], randomness, true); // Change the code_hash on the first position { let mut invalid = unrolled.clone(); invalid.rows[0].code_hash += Fr::from(1u64); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Change the code_hash on another position { let mut invalid = unrolled.clone(); invalid.rows[4].code_hash += Fr::from(1u64); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Change all the hashes so it doesn't match the keccak lookup code_hash { @@ -898,7 +834,7 @@ mod tests { for row in invalid.rows.iter_mut() { row.code_hash = Fr::one(); } - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } } @@ -907,23 +843,23 @@ mod tests { #[ignore] fn bytecode_invalid_index() { let k = 9; - let randomness = get_randomness(); + let randomness: Fr = get_randomness(); let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; let unrolled = unroll(bytecode, randomness); - verify::(k, vec![unrolled.clone()], randomness, true); + test_bytecode_circuit_unrolled(k, vec![unrolled.clone()], randomness, true); // Start the index at 1 { let mut invalid = unrolled.clone(); for row in invalid.rows.iter_mut() { row.index += Fr::one(); } - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Don't increment an index once { let mut invalid = unrolled; invalid.rows.last_mut().unwrap().index -= Fr::one(); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } } @@ -934,24 +870,24 @@ mod tests { let randomness = get_randomness(); let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; let unrolled = unroll(bytecode, randomness); - verify::(k, vec![unrolled.clone()], randomness, true); + test_bytecode_circuit_unrolled(k, vec![unrolled.clone()], randomness, true); // Change the first byte { let mut invalid = unrolled.clone(); invalid.rows[1].value = Fr::from(9u64); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Change a byte on another position { let mut invalid = unrolled.clone(); invalid.rows[5].value = Fr::from(6u64); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Set a byte value out of range { let mut invalid = unrolled; invalid.rows[3].value = Fr::from(256u64); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } } @@ -970,24 +906,24 @@ mod tests { OpcodeId::PUSH6.as_u8(), ]; let unrolled = unroll(bytecode, randomness); - verify::(k, vec![unrolled.clone()], randomness, true); + test_bytecode_circuit_unrolled(k, vec![unrolled.clone()], randomness, true); // Mark the 3rd byte as code (is push data from the first PUSH1) { let mut invalid = unrolled.clone(); invalid.rows[3].is_code = Fr::one(); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Mark the 4rd byte as data (is code) { let mut invalid = unrolled.clone(); invalid.rows[4].is_code = Fr::zero(); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } // Mark the 7th byte as code (is data for the PUSH7) { let mut invalid = unrolled; invalid.rows[7].is_code = Fr::one(); - verify::(k, vec![invalid], randomness, false); + test_bytecode_circuit_unrolled(k, vec![invalid], randomness, false); } } } diff --git a/zkevm-circuits/src/bytecode_circuit/dev.rs b/zkevm-circuits/src/bytecode_circuit/dev.rs new file mode 100644 index 0000000000..032f02106b --- /dev/null +++ b/zkevm-circuits/src/bytecode_circuit/dev.rs @@ -0,0 +1,125 @@ +use super::bytecode_unroller::{unroll, Config, UnrolledBytecode}; +use crate::table::{BytecodeTable, KeccakTable}; +use crate::util::power_of_randomness_from_instance; +use eth_types::Field; +use halo2_proofs::{ + circuit::Layouter, + plonk::{ConstraintSystem, Error}, +}; +use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; +use std::vec; + +#[derive(Default)] +pub(crate) struct BytecodeCircuitTester { + bytecodes: Vec>, + size: usize, + randomness: F, +} + +fn get_randomness() -> F { + F::from(123456) +} + +impl Circuit for BytecodeCircuitTester { + type Config = Config; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let bytecode_table = BytecodeTable::construct(meta); + + let randomness = power_of_randomness_from_instance::<_, 1>(meta); + let keccak_table = KeccakTable::construct(meta); + + Config::configure(meta, randomness[0].clone(), bytecode_table, keccak_table) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + config.load(&mut layouter)?; + config.keccak_table.load( + &mut layouter, + self.bytecodes.iter().map(|b| b.bytes.clone()), + self.randomness, + )?; + config.assign(&mut layouter, self.size, &self.bytecodes, self.randomness)?; + Ok(()) + } +} + +impl BytecodeCircuitTester { + /// Verify that the selected bytecode fulfills the circuit + pub fn verify_raw(k: u32, bytecodes: Vec>, randomness: F) { + let unrolled: Vec<_> = bytecodes + .iter() + .map(|b| unroll(b.clone(), randomness)) + .collect(); + Self::verify(k, unrolled, randomness, true); + } + + pub(crate) fn verify( + k: u32, + bytecodes: Vec>, + randomness: F, + success: bool, + ) { + let circuit = BytecodeCircuitTester:: { + bytecodes, + size: 2usize.pow(k), + randomness, + }; + + let num_rows = 1 << k; + const NUM_BLINDING_ROWS: usize = 7 - 1; + let instance = vec![vec![randomness; num_rows - NUM_BLINDING_ROWS]]; + let prover = MockProver::::run(k, &circuit, instance).unwrap(); + let result = prover.verify(); + if let Err(failures) = &result { + for failure in failures.iter() { + println!("{}", failure); + } + } + assert_eq!(result.is_ok(), success); + } +} + +/// Test bytecode circuit with raw bytecode +pub fn test_bytecode_circuit(k: u32, bytecodes: Vec>, randomness: F) { + let unrolled: Vec<_> = bytecodes + .iter() + .map(|b| unroll(b.clone(), randomness)) + .collect(); + test_bytecode_circuit_unrolled(k, unrolled, randomness, true); +} + +/// Test bytecode circuit with unrolled bytecode +pub fn test_bytecode_circuit_unrolled( + k: u32, + bytecodes: Vec>, + randomness: F, + success: bool, +) { + let circuit = BytecodeCircuitTester:: { + bytecodes, + size: 2usize.pow(k), + randomness, + }; + + let num_rows = 1 << k; + const NUM_BLINDING_ROWS: usize = 7 - 1; + let instance = vec![vec![randomness; num_rows - NUM_BLINDING_ROWS]]; + let prover = MockProver::::run(k, &circuit, instance).unwrap(); + let result = prover.verify(); + if let Err(failures) = &result { + for failure in failures.iter() { + println!("{}", failure); + } + } + assert_eq!(result.is_ok(), success); +} diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 806542bd78..1174660746 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -680,32 +680,25 @@ impl CopyCircuit { } } -#[cfg(test)] -mod tests { +#[cfg(feature = "dev")] +/// Dev helpers +pub mod dev { use super::*; - use bus_mapping::{ - circuit_input_builder::{CircuitInputBuilder, CopyDataType}, - evm::{gen_sha3_code, MemoryKind}, - mock::BlockData, - operation::RWCounter, - }; - use eth_types::{bytecode, geth_types::GethData, Field, Word}; + use eth_types::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner}, dev::{MockProver, VerifyFailure}, plonk::{Circuit, ConstraintSystem}, }; - use mock::TestContext; - use rand::{prelude::SliceRandom, Rng}; use crate::{ - evm_circuit::witness::{block_convert, Block}, + evm_circuit::witness::Block, table::{BytecodeTable, RwTable, TxTable}, util::power_of_randomness_from_instance, }; #[derive(Clone)] - struct MyConfig { + struct CopyCircuitTesterConfig { tx_table: TxTable, rw_table: RwTable, bytecode_table: BytecodeTable, @@ -713,23 +706,26 @@ mod tests { } #[derive(Default)] - struct MyCircuit { + struct CopyCircuitTester { block: Block, randomness: F, } - fn get_randomness() -> F { - F::random(rand::thread_rng()) - } + impl CopyCircuitTester { + fn get_randomness() -> F { + F::random(rand::thread_rng()) + } - impl MyCircuit { pub fn new(block: Block, randomness: F) -> Self { Self { block, randomness } } + pub fn r() -> Expression { + 123456u64.expr() + } } - impl Circuit for MyCircuit { - type Config = MyConfig; + impl Circuit for CopyCircuitTester { + type Config = CopyCircuitTesterConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -754,7 +750,7 @@ mod tests { randomness[0].clone(), ); - MyConfig { + CopyCircuitTesterConfig { tx_table, rw_table, bytecode_table, @@ -787,18 +783,32 @@ mod tests { } } - fn run_circuit( - k: u32, - block: Block, - randomness: F, - ) -> Result<(), Vec> { - let circuit = MyCircuit::::new(block, randomness); + /// Test copy circuit with the provided block witness + pub fn test_copy_circuit(k: u32, block: Block) -> Result<(), Vec> { + let randomness = CopyCircuitTester::::get_randomness(); + let circuit = CopyCircuitTester::::new(block, randomness); let num_rows = 1 << k; const NUM_BLINDING_ROWS: usize = 7 - 1; let instance = vec![vec![randomness; num_rows - NUM_BLINDING_ROWS]]; let prover = MockProver::::run(k, &circuit, instance).unwrap(); prover.verify() } +} + +#[cfg(test)] +mod tests { + use super::dev::test_copy_circuit; + use bus_mapping::evm::{gen_sha3_code, MemoryKind}; + use bus_mapping::{ + circuit_input_builder::{CircuitInputBuilder, CopyDataType}, + mock::BlockData, + operation::RWCounter, + }; + use eth_types::{bytecode, geth_types::GethData, Word}; + use mock::TestContext; + use rand::{prelude::SliceRandom, Rng}; + + use crate::evm_circuit::witness::block_convert; fn gen_calldatacopy_data() -> CircuitInputBuilder { let code = bytecode! { @@ -849,21 +859,21 @@ mod tests { fn copy_circuit_valid_calldatacopy() { let builder = gen_calldatacopy_data(); let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(10, block, get_randomness()).is_ok()); + assert!(test_copy_circuit(10, block).is_ok()); } #[test] fn copy_circuit_valid_codecopy() { let builder = gen_codecopy_data(); let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(10, block, get_randomness()).is_ok()); + assert!(test_copy_circuit(10, block).is_ok()); } #[test] fn copy_circuit_valid_sha3() { let builder = gen_sha3_data(); let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(20, block, get_randomness()).is_ok()); + assert!(test_copy_circuit(20, block).is_ok()); } fn perturb_tag(block: &mut bus_mapping::circuit_input_builder::Block, tag: CopyDataType) { @@ -895,7 +905,7 @@ mod tests { false => perturb_tag(&mut builder.block, CopyDataType::TxCalldata), } let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(10, block, get_randomness()).is_err()); + assert!(test_copy_circuit(10, block).is_err()); } #[test] @@ -906,7 +916,7 @@ mod tests { false => perturb_tag(&mut builder.block, CopyDataType::Bytecode), } let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(10, block, get_randomness()).is_err()); + assert!(test_copy_circuit(10, block).is_err()); } #[test] @@ -917,6 +927,6 @@ mod tests { false => perturb_tag(&mut builder.block, CopyDataType::RlcAcc), } let block = block_convert(&builder.block, &builder.code_db); - assert!(run_circuit(20, block, get_randomness()).is_err()); + assert!(test_copy_circuit(20, block).is_err()); } } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 8485386c58..1d047624e7 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -25,7 +25,7 @@ use num::Integer; use num_bigint::BigUint; // use rand_core::RngCore; use rlp::RlpStream; -use secp256k1::Secp256k1Affine; +pub use secp256k1::Secp256k1Affine; use sha3::{Digest, Keccak256}; use sign_verify::{pk_bytes_swap_endianness, SignData, SignVerifyChip, SignVerifyConfig}; pub use sign_verify::{POW_RAND_SIZE, VERIF_HEIGHT};