From fd6a65abd72ef4b21d32fd6fc43b5a5e8cf116f3 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Thu, 14 Dec 2023 07:46:04 +0800 Subject: [PATCH] WIP: rw_table witness commitment --- zkevm-circuits/src/root_circuit.rs | 31 ++- zkevm-circuits/src/super_circuit/test.rs | 71 ++++- zkevm-circuits/src/table/rw_table.rs | 317 ++++++++++++++++++++++- 3 files changed, 409 insertions(+), 10 deletions(-) diff --git a/zkevm-circuits/src/root_circuit.rs b/zkevm-circuits/src/root_circuit.rs index ce528087ad..ba26f80638 100644 --- a/zkevm-circuits/src/root_circuit.rs +++ b/zkevm-circuits/src/root_circuit.rs @@ -280,7 +280,7 @@ where config.aggregate::(ctx, &key.clone(), &self.snark_witnesses)?; // aggregate user challenge for rwtable permutation challenge - let (alpha, gamma) = { + let (_alpha, _gamma) = { let mut challenges = config.aggregate_user_challenges::( loader.clone(), self.user_challenges, @@ -320,9 +320,24 @@ where .scalar_chip() .assign_constant(&mut loader.ctx_mut(), M::Scalar::from(1)) .unwrap(); + (zero_const, one_const, total_chunk_const) }; + // TODO remove me + let (_hardcode_alpha, _hardcode_gamma) = { + ( + loader + .scalar_chip() + .assign_constant(&mut loader.ctx_mut(), M::Scalar::from(101)) + .unwrap(), + loader + .scalar_chip() + .assign_constant(&mut loader.ctx_mut(), M::Scalar::from(103)) + .unwrap(), + ) + }; + // `first.sc_rwtable_row_prev_fingerprint == // first.ec_rwtable_row_prev_fingerprint` will be checked inside circuit vec![ @@ -333,11 +348,17 @@ where (first_chunk.initial_rwc.assigned(), &one_const), // constraint permutation fingerprint // challenge: alpha - (first_chunk.sc_permu_alpha.assigned(), &alpha.assigned()), - (first_chunk.ec_permu_alpha.assigned(), &alpha.assigned()), + // TODO remove hardcode + (first_chunk.sc_permu_alpha.assigned(), &_hardcode_alpha), + (first_chunk.ec_permu_alpha.assigned(), &_hardcode_alpha), + // (first_chunk.sc_permu_alpha.assigned(), &alpha.assigned()), + // (first_chunk.ec_permu_alpha.assigned(), &alpha.assigned()), // challenge: gamma - (first_chunk.sc_permu_gamma.assigned(), &gamma.assigned()), - (first_chunk.ec_permu_gamma.assigned(), &gamma.assigned()), + // TODO remove hardcode + (first_chunk.sc_permu_gamma.assigned(), &_hardcode_gamma), + (first_chunk.ec_permu_gamma.assigned(), &_hardcode_gamma), + // (first_chunk.sc_permu_gamma.assigned(), &gamma.assigned()), + // (first_chunk.ec_permu_gamma.assigned(), &gamma.assigned()), // fingerprint ( first_chunk.ec_rwtable_prev_fingerprint.assigned(), diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index fa003f040b..fd37bb752a 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -1,14 +1,26 @@ +use crate::{table::rw_table::get_rwtable_cols_commitment, witness::RwMap}; + pub use super::*; +use bus_mapping::operation::OperationContainer; +use eth_types::{address, bytecode, geth_types::GethData, Word}; use ethers_signers::{LocalWallet, Signer}; -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use halo2_proofs::{ + dev::MockProver, + halo2curves::{ + bn256::{Bn256, Fr}, + ff::WithSmallOrderMulGroup, + }, + poly::{ + commitment::CommitmentScheme, + kzg::commitment::{KZGCommitmentScheme, ParamsKZG}, + }, +}; use log::error; use mock::{TestContext, MOCK_CHAIN_ID}; use rand::SeedableRng; -use rand_chacha::ChaCha20Rng; +use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; use std::collections::HashMap; -use eth_types::{address, bytecode, geth_types::GethData, Word}; - #[test] fn super_circuit_degree() { let mut cs = ConstraintSystem::::default(); @@ -172,3 +184,54 @@ fn serial_test_super_circuit_2tx_2max_tx() { }; test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } + +#[ignore] +#[test] +fn test_rw_table_commitment() { + let k = 18; + let params = ParamsKZG::::setup(k, OsRng); + rw_table_commitment::>(¶ms); +} + +fn rw_table_commitment<'params, Scheme: CommitmentScheme>(params: &'params Scheme::ParamsProver) +where + ::Scalar: WithSmallOrderMulGroup<3> + eth_types::Field, +{ + let circuits_params = FixedCParams { + max_txs: 1, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + }; + let rw_map = RwMap::from(&OperationContainer { + ..Default::default() + }); + let rows = rw_map.table_assignments(false); + + const TEST_MOCK_RANDOMNESS: u64 = 0x100; + + // synthesize to get degree + let mut cs = ConstraintSystem::<::Scalar>::default(); + let config = SuperCircuit::configure_with_params( + &mut cs, + SuperCircuitParams { + max_txs: circuits_params.max_txs, + max_calldata: circuits_params.max_calldata, + mock_randomness: TEST_MOCK_RANDOMNESS.into(), + }, + ); + let degree = cs.degree(); + + let advice_commitments = get_rwtable_cols_commitment::( + degree, + &rows, + circuits_params.max_rws, + params, + false, + ); + println!("advice_commitments {:?}", advice_commitments[0]); +} diff --git a/zkevm-circuits/src/table/rw_table.rs b/zkevm-circuits/src/table/rw_table.rs index d82dec5fc7..81e65b51f3 100644 --- a/zkevm-circuits/src/table/rw_table.rs +++ b/zkevm-circuits/src/table/rw_table.rs @@ -1,4 +1,19 @@ -use halo2_proofs::circuit::AssignedCell; +use bus_mapping::operation::OperationContainer; +use halo2_proofs::{ + self, + circuit::{AssignedCell, SimpleFloorPlanner}, + halo2curves::ff::{BatchInvert, WithSmallOrderMulGroup}, +}; + +use halo2_proofs::{ + halo2curves::{bn256::Fr, group::Curve, CurveAffine}, + plonk::{Advice, Assigned, Assignment, Challenge, Fixed, FloorPlanner, Instance, Selector}, + poly::{ + commitment::{Blind, CommitmentScheme, Params}, + EvaluationDomain, LagrangeCoeff, Polynomial, + }, +}; +use snark_verifier::util::arithmetic::PrimeCurveAffine; use super::*; @@ -72,6 +87,20 @@ impl RwTable { /// Construct a new RwTable pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { + // TODO upgrade halo2 to use `unblinded_advice_column` + // https://github.com/privacy-scaling-explorations/halo2/blob/main/halo2_proofs/examples/vector-ops-unblinded.rs + // rw_counter: meta.unblinded_advice_column(), + // is_write: meta.unblinded_advice_column(), + // tag: meta.unblinded_advice_column(), + // id: meta.unblinded_advice_column(), + // address: meta.unblinded_advice_column(), + // field_tag: meta.unblinded_advice_column(), + // storage_key: word::Word::new([meta.unblinded_advice_column(), + // meta.unblinded_advice_column()]), value: word::Word::new([meta. + // unblinded_advice_column(), meta.unblinded_advice_column()]), value_prev: + // word::Word::new([meta.unblinded_advice_column(), meta.unblinded_advice_column()]), + // init_val: word::Word::new([meta.unblinded_advice_column(), + // meta.unblinded_advice_column()]), rw_counter: meta.advice_column(), is_write: meta.advice_column(), tag: meta.advice_column(), @@ -152,3 +181,289 @@ impl RwTable { Ok(()) } } + +/// get rw table column commitment +/// implementation snippet from halo2 `create_proof` https://github.com/privacy-scaling-explorations/halo2/blob/9b33f9ce524dbb9133fc8b9638b2afd0571659a8/halo2_proofs/src/plonk/prover.rs#L37 +pub fn get_rwtable_cols_commitment<'params, Scheme: CommitmentScheme>( + degree: usize, + rws: &[Rw], + n_rows: usize, + params_prover: &'params Scheme::ParamsProver, + is_first_row_padding: bool, +) -> Vec<::Curve> +where + ::Scalar: WithSmallOrderMulGroup<3> + Field, +{ + struct WitnessCollection { + k: u32, + advice: Vec, LagrangeCoeff>>, + _marker: std::marker::PhantomData, + } + + impl<'a, F: Field> Assignment for WitnessCollection { + fn enter_region(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about regions in this context. + } + + fn exit_region(&mut self) { + // Do nothing; we don't care about regions in this context. + } + + fn enable_selector(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error> + where + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn annotate_column(&mut self, _annotation: A, _column: Column) + where + A: FnOnce() -> AR, + AR: Into, + { + // Do nothing + } + + fn query_instance(&self, column: Column, row: usize) -> Result, Error> { + Err(Error::BoundsFailure) + } + + fn assign_advice( + &mut self, + _: A, + column: Column, + row: usize, + to: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + to().into_field().map(|v| { + *self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure) + .unwrap() = v; + }); + Ok(()) + } + + fn assign_fixed( + &mut self, + _: A, + _: Column, + _: usize, + _: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn copy( + &mut self, + _: Column, + _: usize, + _: Column, + _: usize, + ) -> Result<(), Error> { + // We only care about advice columns here + + Ok(()) + } + + fn fill_from_row( + &mut self, + _: Column, + _: usize, + _: Value>, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_challenge(&self, challenge: Challenge) -> Value { + Value::unknown() + } + + fn push_namespace(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self, _: Option) { + // Do nothing; we don't care about namespaces in this context. + } + } + + let rw_map = RwMap::from(&OperationContainer { + ..Default::default() + }); + let rows = rw_map.table_assignments(false); + let rwtable_circuit = RwTableCircuit::new(&rows, 1, false); + + let domain = EvaluationDomain::<::Scalar>::new( + degree as u32, + params_prover.k(), + ); + + let mut cs = ConstraintSystem::default(); + let rwtable_circuit_config = RwTableCircuit::configure(&mut cs); + // TODO adjust domain.empty_lagrange_assigned() visibility in halo2 library to public + let mut witness = WitnessCollection { + k: params_prover.k(), + advice: vec![ + domain.empty_lagrange_assigned(); + rwtable_circuit_config.rw_table.advice_columns().len() + ], + _marker: std::marker::PhantomData, + }; + + // Synthesize the circuit to obtain the witness and other information. + as Circuit>::FloorPlanner::synthesize( + &mut witness, + &rwtable_circuit, + rwtable_circuit_config, + cs.constants().clone(), + ) + .unwrap(); + + let mut advice_values = + batch_invert_assigned::(domain, witness.advice.into_iter().collect()); + + // Compute commitments to advice column polynomials + let blinds = vec![Blind::default(); witness.advice.len()]; + let advice_commitments_projective: Vec<_> = advice_values + .iter() + .zip(blinds.iter()) + .map(|(poly, blind)| params_prover.commit_lagrange(poly, *blind)) + .collect(); + let mut advice_commitments = + vec![Scheme::Curve::identity(); advice_commitments_projective.len()]; + + ::CurveExt::batch_normalize( + &advice_commitments_projective, + &mut advice_commitments, + ); + + advice_commitments +} + +struct RwTableCircuit<'a> { + rws: &'a [Rw], + n_rows: usize, + is_first_row_padding: bool, +} + +impl<'a> RwTableCircuit<'a> { + #[allow(dead_code)] + pub(crate) fn new(rws: &'a [Rw], n_rows: usize, is_first_row_padding: bool) -> Self { + Self { + rws, + n_rows, + is_first_row_padding, + } + } +} + +#[derive(Clone)] +struct RwTableCircuitConfig { + pub rw_table: RwTable, +} + +impl<'a> RwTableCircuitConfig {} + +impl<'a, F: Field> Circuit for RwTableCircuit<'a> { + type Config = RwTableCircuitConfig; + + type FloorPlanner = SimpleFloorPlanner; + + type Params = (); + + fn without_witnesses(&self) -> Self { + todo!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + RwTableCircuitConfig { + rw_table: RwTable::construct(meta), + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + layouter.assign_region( + || "XXXX", + |mut region| { + config.rw_table.load_with_region( + &mut region, + self.rws, + self.n_rows, + self.is_first_row_padding, + ) + }, + )?; + Ok(()) + } +} + +// migrate from halo2 library +fn batch_invert_assigned>( + domain: EvaluationDomain, + assigned: Vec, LagrangeCoeff>>, +) -> Vec> { + let mut assigned_denominators: Vec<_> = assigned + .iter() + .map(|f| { + f.iter() + .map(|value| value.denominator()) + .collect::>() + }) + .collect(); + + assigned_denominators + .iter_mut() + .flat_map(|f| { + f.iter_mut() + // If the denominator is trivial, we can skip it, reducing the + // size of the batch inversion. + .filter_map(|d| d.as_mut()) + }) + .batch_invert(); + + assigned + .iter() + .zip(assigned_denominators.into_iter()) + .map(|(poly, inv_denoms)| { + let inv_denoms = inv_denoms.into_iter().map(|d| d.unwrap_or(F::ONE)); + domain.lagrange_from_vec( + poly.iter() + .zip(inv_denoms.into_iter()) + .map(|(a, inv_den)| a.numerator() * inv_den) + .collect(), + ) + }) + .collect() +}