From 7a2d655c9975ab67d8a96eac36d22e4c671aae8a Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Tue, 3 Oct 2023 23:29:07 +0800 Subject: [PATCH] permutation circuit to support permutation fingerprint on rw_table --- zkevm-circuits/src/copy_circuit/dev.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 2 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/permutation_circuit.rs | 266 ++++++++++++++++++++++ zkevm-circuits/src/state_circuit.rs | 2 +- zkevm-circuits/src/super_circuit.rs | 23 +- zkevm-circuits/src/util.rs | 2 + zkevm-circuits/src/witness/rw.rs | 28 +-- 8 files changed, 306 insertions(+), 20 deletions(-) create mode 100644 zkevm-circuits/src/permutation_circuit.rs diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index cd5b4c2a5c..6fa6a2fca0 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -61,7 +61,7 @@ impl Circuit for CopyCircuit { config.0.rw_table.load( &mut layouter, - &self.external_data.rws.table_assignments(), + &self.external_data.rws.table_assignments(true), self.external_data.max_rws, )?; diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index e330550eb7..cc1927768d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -415,7 +415,7 @@ impl Circuit for EvmCircuit { block.rws.check_rw_counter_sanity(); config.rw_table.load( &mut layouter, - &block.rws.table_assignments(), + &block.rws.table_assignments(true), block.circuits_params.max_rws, )?; config diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index fe25729425..769923cd1e 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -26,6 +26,7 @@ pub mod exp_circuit; pub mod keccak_circuit; #[allow(dead_code, reason = "under active development")] pub mod mpt_circuit; +pub mod permutation_circuit; pub mod pi_circuit; pub mod root_circuit; pub mod state_circuit; diff --git a/zkevm-circuits/src/permutation_circuit.rs b/zkevm-circuits/src/permutation_circuit.rs new file mode 100644 index 0000000000..f6cb5bfb98 --- /dev/null +++ b/zkevm-circuits/src/permutation_circuit.rs @@ -0,0 +1,266 @@ +use std::marker::PhantomData; + +use eth_types::Field; +use gadgets::util::Expr; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Instance, Selector}, + poly::Rotation, +}; +use itertools::Itertools; + +use crate::{ + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness, +}; + +/// Config for PermutationCircuit +#[derive(Clone, Debug)] +pub struct PermutationCircuitConfig { + // column + fingerprints: Column, + alpha: Column, + gamma: Column, + permutation_instance: Column, + + // selector + q_row_non_first: Selector, // 1 between (first, last], exclude first + + _phantom: PhantomData, +} + +/// Circuit configuration arguments +pub struct PermutationCircuitConfigArgs { + pub cols: Vec>, +} + +impl SubCircuitConfig for PermutationCircuitConfig { + type ConfigArgs = PermutationCircuitConfigArgs; + + /// Return a new TxCircuitConfig + fn new(meta: &mut ConstraintSystem, Self::ConfigArgs { cols }: Self::ConfigArgs) -> Self { + let fingerprints = meta.advice_column(); + let permutation_instance = meta.instance_column(); + let alpha = meta.advice_column(); + let gamma = meta.advice_column(); + let q_row_non_first = meta.selector(); + + meta.create_gate("permutation fingerprint update logic", |meta| { + let alpha = meta.query_advice(alpha, Rotation::cur()); + let gamma = meta.query_advice(gamma, Rotation::cur()); + let q_row_non_first = meta.query_selector(q_row_non_first); + let cols_cur_exprs = cols + .iter() + .map(|col| meta.query_advice(*col, Rotation::cur())) + .collect::>>(); + let fingerprint_prev = meta.query_advice(fingerprints, Rotation::prev()); + let fingerprint_cur = meta.query_advice(fingerprints, Rotation::cur()); + + let power_of_gamma = std::iter::successors(1.expr().into(), |prev: &Expression| { + (prev.clone() * gamma.clone()).into() + }) + .take(cols.len()) + .collect::>>(); + + let perf_term = cols_cur_exprs + .iter() + .zip(power_of_gamma.iter()) + .map(|(a, b)| a.clone() * b.clone()) + .fold(0.expr(), |a, b| a + b); + + [q_row_non_first * (fingerprint_cur - fingerprint_prev * (alpha - perf_term))] + }); + + meta.create_gate("challenges consistency", |meta| { + let q_row_non_first = meta.query_selector(q_row_non_first); + let alpha_prev = meta.query_advice(alpha, Rotation::prev()); + let alpha_cur = meta.query_advice(alpha, Rotation::cur()); + let gamma_prev = meta.query_advice(gamma, Rotation::prev()); + let gamma_cur = meta.query_advice(gamma, Rotation::cur()); + [ + q_row_non_first * (alpha_prev - alpha_cur), + q_row_non_first * (gamma_prev - gamma_cur), + ] + }); + Self { + fingerprints, + permutation_instance, + q_row_non_first, + alpha, + gamma, + _phantom: PhantomData:: {}, + } + } +} + +impl PermutationCircuitConfig { + pub fn assign( + &self, + region: &mut Region<'_, F>, + alpha: Value, + gamma: Value, + prev_continuous_fingerprint: Value, + col_values: &Vec>>, + ) -> Result< + ( + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, + ), + Error, + > { + let mut offset = 0; + // Constrain raw_public_input cells to public inputs + let alpha_first_cell = region.assign_advice( + || format!("alpha at index {}", offset), + self.alpha, + offset, + || alpha, + )?; + let gamma_first_cell = region.assign_advice( + || format!("gamma at index {}", offset), + self.gamma, + offset, + || gamma, + )?; + let prev_continuous_fingerprint_cell = region.assign_advice( + || format!("fingerprint at index {}", offset), + self.fingerprints, + offset, + || prev_continuous_fingerprint, + )?; + + let power_of_gamma = { + let num_of_col = col_values.get(0).map(|x| x.len()).unwrap_or_default(); + std::iter::successors(Some(Value::known(F::ONE)), |prev| { + (prev.clone() * gamma.clone()).into() + }) + .take(num_of_col) + .collect::>>() + }; + + offset += 1; + + let mut last_fingerprint_cell = None; + for (_, row) in col_values.iter().enumerate() { + self.q_row_non_first.enable(region, offset); + + let perf_term = { + let tmp = row + .iter() + .zip_eq(power_of_gamma.iter()) + .map(|(a, b)| a.zip(*b).map(|(a, b)| a * b)) + .fold(Value::known(F::ZERO), |prev, cur| { + prev.zip(cur).map(|(a, b)| a + b) + }); + alpha.zip(tmp).map(|(alpha, perf_term)| alpha - perf_term) + }; + let fingerprint_cell = region.assign_advice( + || format!("fingerprint at index {}", offset), + self.fingerprints, + offset, + || perf_term, + )?; + if offset == col_values.len() { + last_fingerprint_cell = Some(fingerprint_cell); + } + region.assign_advice( + || format!("alpha at index {}", offset), + self.alpha, + offset, + || alpha, + )?; + region.assign_advice( + || format!("gamma at index {}", offset), + self.gamma, + offset, + || gamma, + )?; + + offset += 1; + } + + Ok(( + alpha_first_cell, + gamma_first_cell, + prev_continuous_fingerprint_cell, + last_fingerprint_cell.unwrap(), + )) + } + + /// Get number of rows required. + pub fn get_num_rows_required(num_rw: usize) -> usize { + unimplemented!() + } +} + +/// permutation fingerprint gadget +#[derive(Debug, Clone)] +pub struct PermutationCircuit { + alpha: Value, + gamma: Value, + prev_continuous_fingerprint: Value, + col_values: Vec>>, +} + +impl SubCircuit for PermutationCircuit { + type Config = PermutationCircuitConfig; + + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + + fn new_from_block(_block: &witness::Block) -> Self { + unimplemented!() + } + + /// Return the minimum number of rows required to prove the block + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + unimplemented!() + } + + /// Make the assignments to the TxCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + let (alpha_cell, gamma_cell, prev_continuous_fingerprint_cell, new_fingerprint_cell) = + layouter.assign_region( + || "permutation circuit", + |mut region| { + let cells = config.assign( + &mut region, + self.alpha, + self.gamma, + self.prev_continuous_fingerprint, + &self.col_values, + )?; + + Ok(cells) + }, + )?; + layouter.constrain_instance(alpha_cell.cell(), config.permutation_instance, 0)?; + layouter.constrain_instance(gamma_cell.cell(), config.permutation_instance, 1)?; + layouter.constrain_instance( + prev_continuous_fingerprint_cell.cell(), + config.permutation_instance, + 2, + )?; + layouter.constrain_instance(new_fingerprint_cell.cell(), config.permutation_instance, 2)?; + + Ok(()) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + // TODO + unimplemented!() + } +} + +impl PermutationCircuit {} diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index d48d836b77..7a7b80db81 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -433,7 +433,7 @@ pub struct StateCircuit { impl StateCircuit { /// make a new state circuit from an RwMap pub fn new(rw_map: RwMap, n_rows: usize) -> Self { - let rows = rw_map.table_assignments(); + let rows = rw_map.table_assignments(false); // address sorted let updates = MptUpdates::mock_from(&rows); Self { rows, diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 1ca6c45c20..9945e8fbee 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -59,11 +59,12 @@ use crate::{ evm_circuit::{EvmCircuit, EvmCircuitConfig, EvmCircuitConfigArgs}, exp_circuit::{ExpCircuit, ExpCircuitConfig}, keccak_circuit::{KeccakCircuit, KeccakCircuitConfig, KeccakCircuitConfigArgs}, + permutation_circuit::{PermutationCircuitConfig, PermutationCircuitConfigArgs}, pi_circuit::{PiCircuit, PiCircuitConfig, PiCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, TxTable, - UXTable, + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, MptTable, + RwTable, TxTable, UXTable, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -97,6 +98,7 @@ pub struct SuperCircuitConfig { keccak_circuit: KeccakCircuitConfig, pi_circuit: PiCircuitConfig, exp_circuit: ExpCircuitConfig, + permutation_circuit: PermutationCircuitConfig, } /// Circuit configuration arguments @@ -122,7 +124,11 @@ impl SubCircuitConfig for SuperCircuitConfig { }: Self::ConfigArgs, ) -> Self { let tx_table = TxTable::construct(meta); + + // TODO reserve offset 0 to adapt column from previous supercircuit chunk + let chronological_rw_table = RwTable::construct(meta); let rw_table = RwTable::construct(meta); + let mpt_table = MptTable::construct(meta); let bytecode_table = BytecodeTable::construct(meta); let block_table = BlockTable::construct(meta); @@ -144,6 +150,14 @@ impl SubCircuitConfig for SuperCircuitConfig { power_of_randomness[0].clone(), ); + // TODO support passing arbitrary columns set for permutation fingerprints + let permutation_circuit = PermutationCircuitConfig::new( + meta, + PermutationCircuitConfigArgs { + cols: >::advice_columns(&chronological_rw_table), + }, + ); + let keccak_circuit = KeccakCircuitConfig::new( meta, KeccakCircuitConfigArgs { @@ -183,7 +197,7 @@ impl SubCircuitConfig for SuperCircuitConfig { meta, CopyCircuitConfigArgs { tx_table: tx_table.clone(), - rw_table, + rw_table: chronological_rw_table.clone(), bytecode_table: bytecode_table.clone(), copy_table, q_enable: q_copy_table, @@ -207,7 +221,7 @@ impl SubCircuitConfig for SuperCircuitConfig { EvmCircuitConfigArgs { challenges, tx_table, - rw_table, + rw_table: chronological_rw_table, bytecode_table, block_table: block_table.clone(), copy_table, @@ -232,6 +246,7 @@ impl SubCircuitConfig for SuperCircuitConfig { keccak_circuit, pi_circuit, exp_circuit, + permutation_circuit, } } } diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index e8da067cff..ff3eba5e39 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -19,6 +19,8 @@ pub use gadgets::util::Expr; pub mod cell_manager; /// Cell Placement strategies pub mod cell_placement_strategy; +/// Permutation fingerprints gadget +pub mod permutation_fingerprints; /// Steal the expression from gate pub fn query_expression( diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 6399c3baef..c7eb42dff5 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -55,7 +55,7 @@ impl RwMap { pub fn check_value(&self) { let err_msg_first = "first access reads don't change value"; let err_msg_non_first = "non-first access reads don't change value"; - let rows = self.table_assignments(); + let rows = self.table_assignments(false); let updates = MptUpdates::mock_from(&rows); let mut errs = Vec::new(); for idx in 1..rows.len() { @@ -123,7 +123,7 @@ impl RwMap { 1 } } - /// Prepad Rw::Start rows to target length + /// padding Rw::Start ahead rows to target length pub fn table_assignments_prepad(rows: &[Rw], target_len: usize) -> (Vec, usize) { // Remove Start rows as we will add them from scratch. let rows: Vec = rows @@ -136,18 +136,20 @@ impl RwMap { (padding.chain(rows.into_iter()).collect(), padding_length) } /// Build Rws for assignment - pub fn table_assignments(&self) -> Vec { + pub fn table_assignments(&self, keep_chronological_order: bool) -> Vec { let mut rows: Vec = self.0.values().flatten().cloned().collect(); - rows.sort_by_key(|row| { - ( - row.tag() as u64, - row.id().unwrap_or_default(), - row.address().unwrap_or_default(), - row.field_tag().unwrap_or_default(), - row.storage_key().unwrap_or_default(), - row.rw_counter(), - ) - }); + if !keep_chronological_order { + rows.sort_by_key(|row| { + ( + row.tag() as u64, + row.id().unwrap_or_default(), + row.address().unwrap_or_default(), + row.field_tag().unwrap_or_default(), + row.storage_key().unwrap_or_default(), + row.rw_counter(), + ) + }); + } rows } }