From 5106f879a4408589a911d95e46baa5f857172b87 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Thu, 29 Feb 2024 11:48:07 +0100 Subject: [PATCH] MSM: use constant size witness and rename old witness in ProofInputs --- msm/src/constraint.rs | 19 +++--- msm/src/lib.rs | 48 +++++++++------ msm/src/main.rs | 6 +- msm/src/proof.rs | 112 +++++----------------------------- msm/src/prover.rs | 36 ++++++----- msm/src/serialization/main.rs | 44 ++++++++----- msm/src/verifier.rs | 26 ++++---- 7 files changed, 118 insertions(+), 173 deletions(-) diff --git a/msm/src/constraint.rs b/msm/src/constraint.rs index 3d77c12b2a..f9cb4f277e 100644 --- a/msm/src/constraint.rs +++ b/msm/src/constraint.rs @@ -9,8 +9,9 @@ use o1_utils::field_helpers::FieldHelpers; use o1_utils::foreign_field::ForeignElement; use crate::columns::{Column, ColumnIndexer, MSMColumnIndexer}; -use crate::proof::{Witness, WitnessColumns}; -use crate::{BN254G1Affine, Ff1, Fp, LIMBS_NUM}; +use crate::proof::ProofInputs; +use crate::witness::Witness; +use crate::{BN254G1Affine, Ff1, Fp, LIMBS_NUM, N}; /// Used to represent constraints as multi variate polynomials. The variables /// are over the columns. @@ -83,8 +84,8 @@ impl BuilderEnv { /// Each WitnessColumn stands for both one row and multirow. This /// function converts from a vector of one-row instantiation to a /// single multi-row form (which is a `Witness`). - pub fn get_witness(&self) -> Witness { - let mut x: Vec> = vec![vec![]; 3 * LIMBS_NUM]; + pub fn get_witness(&self) -> ProofInputs { + let mut cols: [Vec; N] = std::array::from_fn(|_| vec![]); for wc in &self.witness_raw { let WitnessColumnsIndexer { @@ -93,14 +94,14 @@ impl BuilderEnv { c: wc_c, } = wc; for i in 0..LIMBS_NUM { - x[i].push(wc_a[i]); - x[LIMBS_NUM + i].push(wc_b[i]); - x[2 * LIMBS_NUM + i].push(wc_c[i]); + cols[i].push(wc_a[i]); + cols[LIMBS_NUM + i].push(wc_b[i]); + cols[2 * LIMBS_NUM + i].push(wc_c[i]); } } - Witness { - evaluations: WitnessColumns { x }, + ProofInputs { + evaluations: Witness { cols }, mvlookups: vec![], } } diff --git a/msm/src/lib.rs b/msm/src/lib.rs index f773de97c4..cef9272356 100644 --- a/msm/src/lib.rs +++ b/msm/src/lib.rs @@ -29,6 +29,11 @@ pub type BN254 = ark_ec::bn::Bn; pub type BN254G1Affine = ::G1Affine; pub type BN254G2Affine = ::G2Affine; +/// Number of columns +/// FIXME: we must move it into the subdirectory of the +/// foreign field addition circuit +pub const N: usize = 3 * LIMBS_NUM; + /// The native field we are working with. pub type Fp = ark_bn254::Fr; @@ -48,10 +53,13 @@ mod tests { use poly_commitment::pairing_proof::PairingSRS; use crate::{ - columns::Column, mvlookup::Lookup, proof::Witness, prover::prove, verifier::verify, + columns::Column, mvlookup::Lookup, proof::ProofInputs, prover::prove, verifier::verify, BaseSponge, Fp, OpeningProof, ScalarSponge, BN254, }; + // Number of columns + const N: usize = 10; + #[test] fn test_completeness() { let mut rng = o1_utils::tests::make_test_rng(); @@ -67,20 +75,20 @@ mod tests { let mut srs: PairingSRS = PairingSRS::create(x, domain.d1.size as usize); srs.full_srs.add_lagrange_basis(domain.d1); - let witness = Witness::random(domain); + let inputs = ProofInputs::random(domain); let constraints: Vec<_> = vec![]; // generate the proof - let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( + let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( domain, &srs, - witness, + inputs, constraints, &mut rng, ); // verify the proof - let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof); + let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof); assert!(verifies); } @@ -98,22 +106,22 @@ mod tests { let mut srs: PairingSRS = PairingSRS::create(x, domain.d1.size as usize); srs.full_srs.add_lagrange_basis(domain.d1); - let witness = Witness::random(domain); + let inputs = ProofInputs::random(domain); let constraints = vec![]; // generate the proof - let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( + let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( domain, &srs, - witness, + inputs, constraints.clone(), &mut rng, ); - let witness_prime = Witness::random(domain); - let proof_prime = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( + let inputs_prime = ProofInputs::random(domain); + let proof_prime = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( domain, &srs, - witness_prime, + inputs_prime, constraints, &mut rng, ); @@ -123,7 +131,7 @@ mod tests { let mut proof_clone = proof.clone(); proof_clone.opening_proof = proof_prime.opening_proof; let verifies = - verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof_clone); + verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof_clone); assert!(!verifies); } @@ -134,7 +142,7 @@ mod tests { let mut proof_clone = proof.clone(); proof_clone.commitments = proof_prime.commitments; let verifies = - verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof_clone); + verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof_clone); assert!(!verifies); } @@ -146,7 +154,7 @@ mod tests { let mut proof_clone = proof.clone(); proof_clone.zeta_evaluations = proof_prime.zeta_evaluations; let verifies = - verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof_clone); + verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof_clone); assert!(!verifies); } } @@ -166,10 +174,10 @@ mod tests { let mut srs: PairingSRS = PairingSRS::create(x, domain.d1.size as usize); srs.full_srs.add_lagrange_basis(domain.d1); - let mut witness = Witness::random(domain); + let mut inputs = ProofInputs::random(domain); let constraints = vec![]; // Take one random f_i (FIXME: taking first one for now) - let looked_up_values = witness.mvlookups[0].f[0].clone(); + let looked_up_values = inputs.mvlookups[0].f[0].clone(); // We change a random looked up element (FIXME: first one for now) let wrong_looked_up_value = Lookup { table_id: looked_up_values[0].table_id, @@ -177,16 +185,16 @@ mod tests { value: vec![Fp::rand(&mut rng)], }; // Overwriting the first looked up value - witness.mvlookups[0].f[0][0] = wrong_looked_up_value; + inputs.mvlookups[0].f[0][0] = wrong_looked_up_value; // generate the proof - let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( + let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( domain, &srs, - witness, + inputs, constraints, &mut rng, ); - let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof); + let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof); // FIXME: At the moment, it does verify. It should not. We are missing constraints. assert!(!verifies); } diff --git a/msm/src/main.rs b/msm/src/main.rs index fa8cba0c70..ae9f769f1d 100644 --- a/msm/src/main.rs +++ b/msm/src/main.rs @@ -10,7 +10,7 @@ use kimchi_msm::precomputed_srs::get_bn254_srs; use kimchi_msm::prover::prove; use kimchi_msm::verifier::verify; use kimchi_msm::{ - BN254G1Affine, BaseSponge, Ff1, Fp, OpeningProof, ScalarSponge, BN254, DOMAIN_SIZE, + BN254G1Affine, BaseSponge, Ff1, Fp, OpeningProof, ScalarSponge, BN254, DOMAIN_SIZE, N, }; pub fn generate_random_msm_witness() -> BuilderEnv { @@ -43,7 +43,7 @@ pub fn main() { println!("Generating the proof"); let constraints = vec![]; - let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( + let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( domain, &srs, witness, @@ -52,6 +52,6 @@ pub fn main() { ); println!("Verifying the proof"); - let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof); + let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof); println!("Proof verification result: {verifies}") } diff --git a/msm/src/proof.rs b/msm/src/proof.rs index c83f20a7d8..b9ae8369a7 100644 --- a/msm/src/proof.rs +++ b/msm/src/proof.rs @@ -1,90 +1,14 @@ +use crate::witness::Witness; use ark_ff::UniformRand; use kimchi::{circuits::domains::EvaluationDomains, curve::KimchiCurve}; use poly_commitment::{commitment::PolyComm, OpenProof}; -use rand::{prelude::*, thread_rng}; -use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator}; +use rand::thread_rng; use crate::mvlookup::{LookupProof, LookupWitness}; -/// List all columns of the circuit. -/// It is parametrized by a type `T` which can be either: -/// - `Vec` for the evaluations -/// - `PolyComm` for the commitments -#[derive(Debug, Clone)] -pub struct WitnessColumns { - pub x: Vec, -} - -impl<'lt, G> IntoIterator for &'lt WitnessColumns { - type Item = &'lt G; - type IntoIter = std::vec::IntoIter<&'lt G>; - - fn into_iter(self) -> Self::IntoIter { - let n = self.x.len(); - let mut iter_contents = Vec::with_capacity(n); - iter_contents.extend(&self.x); - iter_contents.into_iter() - } -} - -impl IntoParallelIterator for WitnessColumns -where - Vec: IntoParallelIterator, -{ - type Iter = as IntoParallelIterator>::Iter; - type Item = as IntoParallelIterator>::Item; - - fn into_par_iter(self) -> Self::Iter { - let n = self.x.len(); - let mut iter_contents = Vec::with_capacity(n); - iter_contents.extend(self.x); - iter_contents.into_par_iter() - } -} - -impl FromParallelIterator for WitnessColumns { - fn from_par_iter(par_iter: I) -> Self - where - I: IntoParallelIterator, - { - let iter_contents = par_iter.into_par_iter().collect::>(); - WitnessColumns { x: iter_contents } - } -} - -impl<'data, G> IntoParallelIterator for &'data WitnessColumns -where - Vec<&'data G>: IntoParallelIterator, -{ - type Iter = as IntoParallelIterator>::Iter; - type Item = as IntoParallelIterator>::Item; - - fn into_par_iter(self) -> Self::Iter { - let n = self.x.len(); - let mut iter_contents = Vec::with_capacity(n); - iter_contents.extend(self.x.iter()); - iter_contents.into_par_iter() - } -} - -impl<'data, G> IntoParallelIterator for &'data mut WitnessColumns -where - Vec<&'data mut G>: IntoParallelIterator, -{ - type Iter = as IntoParallelIterator>::Iter; - type Item = as IntoParallelIterator>::Item; - - fn into_par_iter(self) -> Self::Iter { - let n = self.x.len(); - let mut iter_contents = Vec::with_capacity(n); - iter_contents.extend(self.x.iter_mut()); - iter_contents.into_par_iter() - } -} - #[derive(Debug)] -pub struct Witness { - pub evaluations: WitnessColumns>, +pub struct ProofInputs { + pub evaluations: Witness>, pub mvlookups: Vec>, } @@ -92,31 +16,27 @@ pub struct Witness { // It is not only in the test API because it is used at the moment in the // main.rs. It should be moved to the test API when main.rs is replaced with // real production code. -impl Witness { +impl ProofInputs { pub fn random(domain: EvaluationDomains) -> Self { let mut rng = thread_rng(); - let random_n = rng.gen_range(1..1000); - Witness { - evaluations: WitnessColumns { - x: (0..random_n) - .map(|_| { - (0..domain.d1.size as usize) - .map(|_| G::ScalarField::rand(&mut rng)) - .collect::>() - }) - .collect::>(), - }, + let cols: [Vec; N] = std::array::from_fn(|_| { + (0..domain.d1.size as usize) + .map(|_| G::ScalarField::rand(&mut rng)) + .collect::>() + }); + ProofInputs { + evaluations: Witness { cols }, mvlookups: vec![LookupWitness::::random(domain)], } } } #[derive(Debug, Clone)] -pub struct Proof> { +pub struct Proof> { // Columns/PlonK argument - pub(crate) commitments: WitnessColumns>, - pub(crate) zeta_evaluations: WitnessColumns, - pub(crate) zeta_omega_evaluations: WitnessColumns, + pub(crate) commitments: Witness>, + pub(crate) zeta_evaluations: Witness, + pub(crate) zeta_omega_evaluations: Witness, // MVLookup argument pub(crate) mvlookup_commitments: Option>>, pub(crate) mvlookup_zeta_evaluations: Option>, diff --git a/msm/src/prover.rs b/msm/src/prover.rs index 16c8387d46..457a7074e5 100644 --- a/msm/src/prover.rs +++ b/msm/src/prover.rs @@ -17,7 +17,8 @@ use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; use crate::mvlookup::{self, LookupProof}; -use crate::proof::{Proof, Witness, WitnessColumns}; +use crate::proof::{Proof, ProofInputs}; +use crate::witness::Witness; pub fn prove< G: KimchiCurve, @@ -26,47 +27,48 @@ pub fn prove< EFrSponge: FrSponge, Column, RNG, + const N: usize, >( domain: EvaluationDomains, srs: &OpeningProof::SRS, - inputs: Witness, + inputs: ProofInputs, _constraints: Vec, Column>>, rng: &mut RNG, -) -> Proof +) -> Proof where OpeningProof::SRS: Sync, RNG: RngCore + CryptoRng, { // Interpolate all columns on d1, using trait Into. - let evaluations: WitnessColumns>> = inputs + let evaluations: Witness>> = inputs .evaluations .into_par_iter() .map(|evals| { Evaluations::>::from_vec_and_domain(evals, domain.d1) }) - .collect::>>>(); + .collect::>>>(); - let polys: WitnessColumns> = { + let polys: Witness> = { let interpolate = |evals: Evaluations>| evals.interpolate(); evaluations .into_par_iter() .map(interpolate) - .collect::>() + .collect::>>() }; - let commitments: WitnessColumns> = { + let commitments: Witness> = { let comm = |poly: &DensePolynomial| srs.commit_non_hiding(poly, 1); (&polys) .into_par_iter() .map(comm) - .collect::>() + .collect::>>() }; let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params()); // Do not use parallelism - commitments + (&commitments) .into_iter() .for_each(|comm| absorb_commitment(&mut fq_sponge, comm)); @@ -97,10 +99,12 @@ where // TODO: Parallelize let (zeta_evaluations, zeta_omega_evaluations) = { let evals = |point| { - let WitnessColumns { x } = &polys; - let comm = |poly: &DensePolynomial| poly.evaluate(point); - let x = x.iter().map(comm).collect::>(); - WitnessColumns { x } + let Witness { cols: polys } = &polys; + let mut evals: [G::ScalarField; N] = [G::ScalarField::zero(); N]; + for (i, poly) in polys.iter().enumerate() { + evals[i] = poly.evaluate(point) + } + Witness { cols: evals } }; (evals(&zeta), evals(&zeta_omega)) }; @@ -185,9 +189,9 @@ where let mut fr_sponge = EFrSponge::new(G::sponge_params()); fr_sponge.absorb(&fq_sponge.digest()); - for (zeta_eval, zeta_omega_eval) in zeta_evaluations + for (zeta_eval, zeta_omega_eval) in (&zeta_evaluations) .into_iter() - .zip(zeta_omega_evaluations.into_iter()) + .zip((&zeta_omega_evaluations).into_iter()) { fr_sponge.absorb(zeta_eval); fr_sponge.absorb(zeta_omega_eval); diff --git a/msm/src/serialization/main.rs b/msm/src/serialization/main.rs index e891810d1b..d8b91eaa5f 100644 --- a/msm/src/serialization/main.rs +++ b/msm/src/serialization/main.rs @@ -3,21 +3,27 @@ use kimchi_msm::serialization::witness; use kimchi_msm::witness::Witness; use poly_commitment::pairing_proof::PairingSRS; +use kimchi_msm::columns::Column; use kimchi_msm::precomputed_srs::get_bn254_srs; +use kimchi_msm::proof::ProofInputs; +use kimchi_msm::prover::prove; use kimchi_msm::serialization::witness::deserialize_field_element; -use kimchi_msm::{Fp, BN254, DOMAIN_SIZE, LIMBS_NUM}; +use kimchi_msm::verifier::verify; +use kimchi_msm::{BaseSponge, Fp, OpeningProof, ScalarSponge, BN254, DOMAIN_SIZE, LIMBS_NUM}; + +const N: usize = 3 + 19 + LIMBS_NUM; pub fn main() { // FIXME: use a proper RNG - let mut _rng = o1_utils::tests::make_test_rng(); + let mut rng = o1_utils::tests::make_test_rng(); println!("Creating the domain and SRS"); let domain = EvaluationDomains::::create(DOMAIN_SIZE).unwrap(); - let _srs: PairingSRS = get_bn254_srs(domain); + let srs: PairingSRS = get_bn254_srs(domain); let mut env = witness::Env::::create(); - let mut witness: Witness> = Witness { + let mut witness: Witness> = Witness { cols: std::array::from_fn(|_| Vec::with_capacity(DOMAIN_SIZE)), }; @@ -36,16 +42,22 @@ pub fn main() { } } - // println!("Generating the proof"); - // let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _>( - // domain, - // &srs, - // witness, - // constraints, - // &mut rng, - // ); - - // println!("Verifying the proof"); - // let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge>(domain, &srs, &proof); - // println!("Proof verification result: {verifies}") + let _constraints = vec![]; + let proof_inputs = ProofInputs { + evaluations: witness, + mvlookups: vec![], + }; + + println!("Generating the proof"); + let proof = prove::<_, OpeningProof, BaseSponge, ScalarSponge, Column, _, N>( + domain, + &srs, + proof_inputs, + _constraints, + &mut rng, + ); + + println!("Verifying the proof"); + let verifies = verify::<_, OpeningProof, BaseSponge, ScalarSponge, N>(domain, &srs, &proof); + println!("Proof verification result: {verifies}") } diff --git a/msm/src/verifier.rs b/msm/src/verifier.rs index 5d77b62fc9..f79ad88fc7 100644 --- a/msm/src/verifier.rs +++ b/msm/src/verifier.rs @@ -16,10 +16,11 @@ pub fn verify< OpeningProof: OpenProof, EFqSponge: Clone + FqSponge, EFrSponge: FrSponge, + const N: usize, >( domain: EvaluationDomains, srs: &OpeningProof::SRS, - proof: &Proof, + proof: &Proof, ) -> bool { let Proof { commitments, @@ -50,25 +51,24 @@ pub fn verify< let omega = domain.d1.group_gen; let zeta_omega = zeta * omega; - let mut es: Vec<_> = zeta_evaluations + let mut es: Vec>> = zeta_evaluations .into_iter() .zip(zeta_omega_evaluations) .map(|(zeta, zeta_omega)| vec![vec![*zeta], vec![*zeta_omega]]) - .collect(); + .collect::>>>(); if mvlookup_commitments.is_some() { - es.extend( - mvlookup_zeta_evaluations - .as_ref() - .unwrap() - .into_iter() - .zip(mvlookup_zeta_omega_evaluations.as_ref().unwrap()) - .map(|(zeta, zeta_omega)| vec![vec![*zeta], vec![*zeta_omega]]) - .collect::>(), - ); + let mvlookup_evals = mvlookup_zeta_evaluations + .as_ref() + .unwrap() + .into_iter() + .zip(mvlookup_zeta_omega_evaluations.as_ref().unwrap()) + .map(|(zeta, zeta_omega)| vec![vec![*zeta], vec![*zeta_omega]]) + .collect::>>>(); + es.extend(mvlookup_evals); } - let mut evaluations: Vec<_> = commitments + let mut evaluations: Vec> = commitments .into_iter() .zip(zeta_evaluations.into_iter().zip(zeta_omega_evaluations)) .map(|(commitment, (zeta_eval, zeta_omega_eval))| Evaluation {