diff --git a/Cargo.toml b/Cargo.toml index 0207849..b631cd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ # "evm-vrfier", -# "pasta-tree", + "pasta-tree", "w3f-plonk-common", "w3f-ring-proof", # "w3f-ring-vrf-snark", @@ -20,7 +20,7 @@ ark-ff = { version = "0.6", default-features = false } ark-ec = { version = "0.6", default-features = false } ark-poly = { version = "0.6", default-features = false } ark-serialize = { version = "0.6", default-features = false, features = ["derive"] } -w3f-pcs = { version = "0.0.7", default-features = false } +w3f-pcs = { version = "0.0.6", git = "https://github.com/paritytech/fflonk/", branch = "ipa-pcs", default-features = false } w3f-plonk-common = { version = "0.0.10", path = "w3f-plonk-common", default-features = false } rayon = { version = "1", default-features = false } ark-transcript = { version = "0.0.6", default-features = false } diff --git a/pasta-tree/src/auth_path/node.rs b/pasta-tree/src/auth_path/node.rs index e0f1564..ea09136 100644 --- a/pasta-tree/src/auth_path/node.rs +++ b/pasta-tree/src/auth_path/node.rs @@ -77,7 +77,7 @@ impl LevelWitness> { } /// NB! It is not "blinded", meaning that the blinding factor hasn't been applied. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct LevelWitnessWithBlinding { pub(crate) level_witness: LevelWitness, /// the verifier gets `Ci' = siblings[i] + bf.H` diff --git a/pasta-tree/src/auth_path/path.rs b/pasta-tree/src/auth_path/path.rs index 5793fa8..b1d8619 100644 --- a/pasta-tree/src/auth_path/path.rs +++ b/pasta-tree/src/auth_path/path.rs @@ -13,6 +13,7 @@ use ark_std::rand::Rng; /// `path_0[0]` contains the leaf (with its siblings). /// `commit(path_0[k].siblings) = path_1[k].siblings[path_1[k].i]`, if `path_1[k]` exists, /// otherwise it's the root. +#[derive(Clone, Debug)] pub struct AuthenticationPath { /// Nodes on the `C0` curve. pub c0_path: Vec>, diff --git a/pasta-tree/src/circuit_fat/params.rs b/pasta-tree/src/circuit_fat/params.rs index 879c559..59d41ab 100644 --- a/pasta-tree/src/circuit_fat/params.rs +++ b/pasta-tree/src/circuit_fat/params.rs @@ -9,6 +9,7 @@ use ark_ff::{AdditiveGroup, BigInteger, PrimeField, Zero}; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::cond_select::CondSelect; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::booleanity::BitColumn; use w3f_plonk_common::gadgets::ec::AffineColumn; @@ -29,6 +30,8 @@ pub struct PiopParams> { impl> CircuitParams for PiopParams> +where + G::BaseField: CondSelect, { type Commitments = crate::circuit_fat::ProofComms; type Evaluations = crate::circuit_fat::ProofEvals; @@ -86,7 +89,10 @@ impl> CircuitParams> PiopParams { +impl> PiopParams +where + G::BaseField: CondSelect, +{ pub fn setup(domain: Domain, h: G) -> Self { let scalar_bitlen = G::ScalarField::MODULUS_BIT_SIZE as usize; Self { diff --git a/pasta-tree/src/circuit_fat/prover.rs b/pasta-tree/src/circuit_fat/prover.rs index f5d89f0..591172b 100644 --- a/pasta-tree/src/circuit_fat/prover.rs +++ b/pasta-tree/src/circuit_fat/prover.rs @@ -11,6 +11,7 @@ use ark_poly::univariate::DensePolynomial; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::cond_select::CondSelect; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ProverGadget; use w3f_plonk_common::gadgets::booleanity::{BitColumn, Booleanity}; @@ -41,7 +42,10 @@ pub struct PiopProver> { result: G, } -impl> PiopProver> { +impl> PiopProver> +where + G::BaseField: CondSelect, +{ pub fn build( params: &PiopParams>, level: LevelWitnessWithBlinding>, @@ -195,6 +199,8 @@ impl> PiopProver> { impl> ProverPiop> for PiopProver> +where + G::BaseField: CondSelect, { const N_COLUMNS: usize = 9; const N_CONSTRAINTS: usize = 13; diff --git a/pasta-tree/src/circuit_fat/verifier.rs b/pasta-tree/src/circuit_fat/verifier.rs index 31a5c8b..d473ddf 100644 --- a/pasta-tree/src/circuit_fat/verifier.rs +++ b/pasta-tree/src/circuit_fat/verifier.rs @@ -99,6 +99,7 @@ impl> { const N_COLUMNS: usize = 9; const N_CONSTRAINTS: usize = 13; + type Instance = AffinePoint; fn precommitted_columns(&self) -> Vec> { vec![ diff --git a/pasta-tree/src/circuit_tall/params.rs b/pasta-tree/src/circuit_tall/params.rs index ac28082..7b888e3 100644 --- a/pasta-tree/src/circuit_tall/params.rs +++ b/pasta-tree/src/circuit_tall/params.rs @@ -9,6 +9,7 @@ use ark_ff::{FftField, One}; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::cond_select::CondSelect; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::booleanity::BitColumn; use w3f_plonk_common::gadgets::ec::AffineColumn; @@ -35,6 +36,8 @@ pub struct PiopParams> { impl> CircuitParams for PiopParams> +where + G::BaseField: CondSelect, { type Commitments = crate::circuit_tall::ProofComms; type Evaluations = crate::circuit_tall::ProofEvals; @@ -106,7 +109,10 @@ impl> CircuitParams> PiopParams { +impl> PiopParams +where + G::BaseField: CondSelect, +{ pub fn setup(domain: Domain, h: G, seed: G) -> Self { assert!(domain.domain_size() > 256); let actual_capacity = domain.capacity - 1; diff --git a/pasta-tree/src/circuit_tall/prover.rs b/pasta-tree/src/circuit_tall/prover.rs index a9a33b3..24687c9 100644 --- a/pasta-tree/src/circuit_tall/prover.rs +++ b/pasta-tree/src/circuit_tall/prover.rs @@ -10,6 +10,7 @@ use ark_poly::univariate::DensePolynomial; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::cond_select::CondSelect; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ProverGadget; use w3f_plonk_common::gadgets::booleanity::{BitColumn, Booleanity}; @@ -35,7 +36,10 @@ pub struct PiopProver> { result: G, } -impl> PiopProver> { +impl> PiopProver> +where + G::BaseField: CondSelect, +{ pub fn build( params: &PiopParams>, level: LevelWitnessWithBlinding>, diff --git a/pasta-tree/src/circuit_tall/verifier.rs b/pasta-tree/src/circuit_tall/verifier.rs index cefcdf1..fdac640 100644 --- a/pasta-tree/src/circuit_tall/verifier.rs +++ b/pasta-tree/src/circuit_tall/verifier.rs @@ -1,8 +1,8 @@ use crate::circuit_tall::{ProofComms, ProofEvals}; +use crate::{AffinePoint, CurveModel}; use ark_ec::AffineRepr; use ark_ec::CurveGroup; // use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; -use crate::{AffinePoint, CurveModel}; use ark_ff::{One, Zero}; use ark_std::marker::PhantomData; use ark_std::{vec, vec::Vec}; @@ -99,6 +99,8 @@ impl> { const N_COLUMNS: usize = 7; const N_CONSTRAINTS: usize = 7; + type Instance = AffinePoint; + // type Instance = > as ProverPiop>>::Instance; fn precommitted_columns(&self) -> Vec> { vec![self.points_x.clone(), self.select_part.clone()] diff --git a/pasta-tree/src/lib.rs b/pasta-tree/src/lib.rs index c86a00f..8543bb3 100644 --- a/pasta-tree/src/lib.rs +++ b/pasta-tree/src/lib.rs @@ -38,7 +38,7 @@ pub trait CircuitParams Commitments = Self::Commitments, Evaluations = Self::Evaluations, >; - type VerifierCircuit: VerifierPiop>; + type VerifierCircuit: VerifierPiop, Instance = AffinePoint>; fn prover_circuit( &self, @@ -95,6 +95,25 @@ type LevelProof = w3f_plonk_common::PiopProof<

>::Evaluations, >; +type BatchLevelProof = w3f_plonk_common::PiopProof< + ::ScalarField, + WrappedAffine, + [

>::Commitments; L], + [

>::Evaluations; L], +>; + +#[derive(Clone, Debug)] +pub struct BatchSideProof< + C: CurveGroup, + G: CurveModel, + P: CircuitParams, + const L: usize, +> { + piop_proof: BatchLevelProof, + pcs_proof: AggregateProof>, + todo: Coeffs, +} + #[derive(Clone)] pub struct CycleSideProof< C: CurveGroup, @@ -117,6 +136,18 @@ pub struct CurveTreeProof< c1_proof: CycleSideProof, C0, P1>, } +#[derive(Clone, Debug)] +pub struct CurveTreeProof2< + C0: CurveModel, + C1: CurveModel, + P0: CircuitParams, C1>, + P1: CircuitParams, C0>, + const L: usize, +> { + c0_proof: BatchSideProof, C1, P0, L>, + c1_proof: BatchSideProof, C0, P1, L>, +} + impl, P: CircuitParams> CycleSideParams { @@ -176,7 +207,7 @@ impl ArkTranscript { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Coeffs(F, F); impl> ShplonkTranscript for Coeffs { fn get_gamma(&mut self) -> F { @@ -261,6 +292,8 @@ mod tests { >(9, 2); } + // cargo test test_circuit_fat --release --features="print-trace" -- --show-output + // cargo test test_circuit_fat --release --features="print-trace parallel" -- --show-output #[test] fn test_circuit_fat() { _test_proof::< @@ -268,7 +301,7 @@ mod tests { VestaConfig, CircuitParamsFat, CircuitParamsFat, - >(8, 2); + >(8, 4); } // cargo test test_bench_curve_tree --release --features="print-trace" -- --show-output --ignored @@ -349,17 +382,30 @@ mod tests { .pow(height as u32) .to_formatted_string(&Locale::en) ); - let t_prove = start_timer!(|| format!( - "Proving CurveTree membership, height={height}, domain={domain_size}, arity={max_nodes}, capacity={}", - max_nodes.pow(height as u32) - )); - let (auth_path, proof) = params.prove(path, rng); + let t_prove = + start_timer!(|| format!("Proving membership, height={height}, domain={domain_size}")); + let (auth_path, proof) = params.prove(path.clone(), rng); end_timer!(t_prove); - let t_verify = start_timer!(|| "Verifying CurveTree membership"); + let t_verify = start_timer!(|| "Verifying membership"); let valid = params.verify(auth_path, proof, root); end_timer!(t_verify); assert!(valid); + + // number of columns for the FAT scheme is hardcoded in batch.rs + if height == 4 && log_n == 8 { + println!("\n\n"); + let t_prove = start_timer!(|| format!( + "Batch-proving membership, height={height}, domain={domain_size}" + )); + let (auth_path, proof) = params.batch_prove::<_, 2>(path, rng); + end_timer!(t_prove); + + let t_verify = start_timer!(|| "Verifying membership batch-proof"); + let valid = params.batch_verify::<2>(auth_path, proof, root); + end_timer!(t_verify); + assert!(valid); + } } pub fn random_witness, R: Rng>( diff --git a/pasta-tree/src/prover.rs b/pasta-tree/src/prover.rs index c36174d..24630f2 100644 --- a/pasta-tree/src/prover.rs +++ b/pasta-tree/src/prover.rs @@ -1,10 +1,10 @@ -use crate::ArkTranscript; use crate::auth_path::blinded::BlindedAuthenticationPath; use crate::auth_path::node::LevelWitnessWithBlinding; use crate::auth_path::path::AuthenticationPath; use crate::{ AffinePoint, CircuitParams, CurveModel, CycleParams, CycleSideParams, ProjectivePoint, }; +use crate::{ArkTranscript, BatchSideProof, CurveTreeProof2}; use crate::{Coeffs, CurveTreeProof, CycleSideProof}; use ark_ec::CurveGroup; use ark_ff::{PrimeField, Zero}; @@ -12,10 +12,12 @@ use ark_poly::Polynomial; use ark_std::rand::Rng; use ark_std::{UniformRand, end_timer, start_timer}; use std::collections::BTreeSet; +use std::marker::PhantomData; use w3f_pcs::pcs::PcsParams; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::Shplonk; +use w3f_plonk_common::batch::BatchProver; use w3f_plonk_common::piop::{ProverPiop, VerifierPiop}; use w3f_plonk_common::prover::{PcsOpeningAt2Points, PlonkProver}; @@ -46,6 +48,29 @@ where .prove_side(blinded_auth_path.c0_path, auth_path_with_bf.c0_path, rng); (auth_path, CurveTreeProof { c0_proof, c1_proof }) } + + pub fn batch_prove( + &self, + auth_path: AuthenticationPath, ProjectivePoint>, + rng: &mut R, + ) -> ( + BlindedAuthenticationPath, ProjectivePoint>, + CurveTreeProof2, + ) { + let auth_path_with_bf = auth_path.with_blinding(rng); + let blinded_auth_path = + auth_path_with_bf.apply_bfs(&self.c0_params.pcs_params, &self.c1_params.pcs_params); + let auth_path = blinded_auth_path.clone(); + let c1_path: [_; L] = auth_path_with_bf.c1_path.try_into().unwrap(); + let c0_path: [_; L] = auth_path_with_bf.c0_path.try_into().unwrap(); + let c0_proof = self + .c0_params + .batch_prove_side(blinded_auth_path.c1_path, c1_path, rng); + let c1_proof = self + .c1_params + .batch_prove_side(blinded_auth_path.c0_path, c0_path, rng); + (auth_path, CurveTreeProof2 { c0_proof, c1_proof }) + } } impl, P: CircuitParams> @@ -65,7 +90,7 @@ impl, P: CircuitParams< // per tree level let n_columns = P::VerifierCircuit::N_COLUMNS; - let n_to_commit = n_columns + 4; // plus the quotient chunks + let n_to_commit = n_columns + 3; // plus the quotient chunks let n_to_open = n_columns + 2; // plus the (folded) quotient (chunks) and the linearization polynomial // per side @@ -140,4 +165,90 @@ impl, P: CircuitParams< }; proof } + + pub fn batch_prove_side( + &self, + _blinded_path: Vec>, // TODO: probably not required + witness: [LevelWitnessWithBlinding>; L], + rng: &mut R, + ) -> BatchSideProof { + let curve_name = &std::any::type_name::()[53..]; + // println!("\n\nprover {curve_name}\nchildren={blinded_path:?}\n"); + + let n_columns = P::VerifierCircuit::N_COLUMNS; + let n_to_commit = L * n_columns + 3; // columns for multiple levels + the shared quotient chunks + let n_to_open = L * n_columns + 2; // --//-- + the (folded) quotient + the linearization polynomial + + let plonk_prover = PlonkProver::, _>::init( + self.pcs_params.ck(), + (), // TODO: + ArkTranscript::new(b"pasta-tree-level-proof"), + ); + + let parent_bfs: Vec<_> = witness.iter().map(|level| level.parent_bf).collect(); + let batch_piop = witness.map(|level| self.piop_params.prover_circuit(level)); + let batch_piop = BatchProver(batch_piop, PhantomData, PhantomData); + + let t_commit_side = start_timer!(|| format!( + "Committing {L}x{n_columns}+3 = {n_to_commit} polynomials to {curve_name}" + )); + let (pcs_openings, piop_proof, _transcript) = + plonk_prover.reduce_to_pcs_opening(batch_piop); + end_timer!(t_commit_side); + + let PcsOpeningAt2Points { + polys_at_zeta, + polys_at_zeta_omega, + zeta, + zeta_omega, + } = pcs_openings; + // println!("zeta = {zeta}\nq(zeta) = {}\n", polys_at_zeta[polys_at_zeta.len() - 1].evaluate(&zeta)); + + let mut at_coords = vec![BTreeSet::from([zeta]); polys_at_zeta.len()]; + let mut polys_to_open = polys_at_zeta; + at_coords.extend(vec![ + BTreeSet::from([zeta_omega]); + polys_at_zeta_omega.len() + ]); + polys_to_open.extend(polys_at_zeta_omega.clone()); + assert_eq!(polys_to_open.len(), n_to_open); + + let mut with_bfs: Vec<_> = parent_bfs + .into_iter() + .flat_map(|bf| vec![bf, C::ScalarField::zero(), C::ScalarField::zero()]) + .collect(); + with_bfs.resize(n_to_open, C::ScalarField::zero()); + + // use ark_ec::AffineRepr; + // for (i, ((p, z), bf)) in polys_to_open.iter() + // .zip(at_coords.iter().map(|z| z.first().unwrap())) + // .zip(with_bfs.iter()) + // .enumerate() { + // let v = p.evaluate(z); + // let c = HidingIpa::::commit(&self.pcs_params, &p).unwrap().0; + // println!("{i}: z={:.5}, v={:.5}, c={:.5}, bf={:.5}", z.to_string(), v.to_string(), c.x().unwrap().to_string(), bf.to_string()); + // } + + let t_open = start_timer!(|| format!( + "Opening {L}x{n_columns}+2 = {n_to_open} polynomials, max_degree = {}", + polys_to_open.iter().map(|p| p.degree()).max().unwrap() + )); + let todo = Coeffs(C::ScalarField::rand(rng), C::ScalarField::rand(rng)); + let pcs_proof = Shplonk::>::open_many_hiding( + &self.pcs_params, + &polys_to_open, + &with_bfs, + &at_coords, + &mut todo.clone(), + rng, + ); + end_timer!(t_open); + + let proof = BatchSideProof { + piop_proof, + pcs_proof, + todo, + }; + proof + } } diff --git a/pasta-tree/src/verifier.rs b/pasta-tree/src/verifier.rs index 6be1aa0..d073897 100644 --- a/pasta-tree/src/verifier.rs +++ b/pasta-tree/src/verifier.rs @@ -1,14 +1,17 @@ -use crate::ArkTranscript; use crate::auth_path::blinded::BlindedAuthenticationPath; use crate::{ AffinePoint, CircuitParams, CurveModel, CycleParams, CycleSideParams, ProjectivePoint, }; +use crate::{ArkTranscript, BatchSideProof, CurveTreeProof2}; use crate::{CurveTreeProof, CycleSideProof}; use ark_ec::CurveGroup; use ark_ff::PrimeField; +use std::marker::PhantomData; use w3f_pcs::pcs::PcsParams; +use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::Shplonk; +use w3f_plonk_common::batch::BatchVerifier; use w3f_plonk_common::piop::VerifierPiop; use w3f_plonk_common::verifier::{PcsOpeningAt2Points, PlonkVerifier}; @@ -36,6 +39,26 @@ where assert!(c1_proof); c0_proof && c1_proof } + + pub fn batch_verify( + &self, + auth_path: BlindedAuthenticationPath, ProjectivePoint>, + proof: CurveTreeProof2, + root: AffinePoint, + ) -> bool { + let BlindedAuthenticationPath { c0_path, c1_path } = auth_path; + let mut c0_parents = c0_path[1..].to_vec(); + c0_parents.push(root); + let c0_proof = self + .c0_params + .verify_batch(c1_path.clone(), c0_parents, proof.c0_proof); + assert!(c0_proof); + let c1_proof = self + .c1_params + .verify_batch(c0_path, c1_path, proof.c1_proof); + assert!(c1_proof); + c0_proof && c1_proof + } } impl, P: CircuitParams> @@ -77,13 +100,8 @@ impl, P: CircuitParams< .zip(parents.into_iter()) .zip(side_proof.piop_proofs.into_iter()) { - let (challenges, _rng) = plonk_verifier.restore_challenges( - &child, - &level_proof, - // '1' accounts for the quotient polynomial that is aggregated together with the columns - P::VerifierCircuit::N_COLUMNS + 1, - P::VerifierCircuit::N_CONSTRAINTS, - ); + let challenges = plonk_verifier + .restore_fs_challenges::(&child, &level_proof); let piop = self.piop_params.verifier_circuit( (child, parent), &fixed_cols, @@ -124,4 +142,94 @@ impl, P: CircuitParams< ); valid } + + pub fn verify_batch( + &self, + // selected re-randomized children + children: Vec>, + // parents, re-randomized at the previous step + parents: Vec, + side_proof: BatchSideProof, + ) -> bool { + // let curve_name = &std::any::type_name::()[53..]; + // println!("\n\nverifier {curve_name}\nchildren={children:?}\nparents={parents:?}\n"); + + let fixed_cols = self.commit_fixed_columns(); // TODO: precompute + let piop_proof = side_proof.piop_proof.clone(); + let instance: [AffinePoint; L] = children.clone().try_into().unwrap(); + + let plonk_verifier: PlonkVerifier, _> = PlonkVerifier::init( + self.pcs_params.vk(), + &(), // TODO + ArkTranscript::new(b"pasta-tree-level-proof"), + ); + + let challenges = plonk_verifier.restore_fs_challenges::, + P::VerifierCircuit, + L, + >, _, _>(&instance, &piop_proof); + let zeta_ = challenges.zeta; + // println!("zeta = {zeta_}"); + + let batch_piop: [_; L] = children + .into_iter() + .zip(parents.into_iter()) + .zip(piop_proof.column_commitments.into_iter()) + .zip(piop_proof.columns_at_zeta.into_iter()) + .map(|(((child, parent), cols), evals)| { + self.piop_params.verifier_circuit( + (child, parent), + &fixed_cols, + cols, + evals, + challenges.zeta, + ) + }) + .collect::>() + .try_into() + .unwrap_or_else(|_| panic!("wtf")); + let batch_piop = BatchVerifier(batch_piop, PhantomData, PhantomData); + + let PcsOpeningAt2Points { + open_at_zeta, + open_at_zeta_omega, + zeta, + zeta_omega, + vals_at_zeta, + vals_at_zeta_omega, + } = plonk_verifier.evaluate_piop(batch_piop, side_proof.piop_proof, challenges); + debug_assert_eq!(zeta, zeta_); + // println!("q(zeta) = {}", vals_at_zeta[vals_at_zeta.len() - 1]); + + let mut at_coords = vec![vec![zeta]; open_at_zeta.len()]; + let mut polys_to_open = open_at_zeta; + at_coords.extend(vec![vec![zeta_omega]; open_at_zeta_omega.len()]); + polys_to_open.extend(open_at_zeta_omega.clone()); + let to_values: Vec> = vals_at_zeta + .into_iter() + .chain(vals_at_zeta_omega.into_iter()) + .map(|v| vec![v]) + .collect(); + + // use ark_ec::AffineRepr; + // for (i, ((c, z), v)) in polys_to_open.iter() + // .zip(at_coords.iter().map(|z| z.first().unwrap())) + // .zip(to_values.iter().map(|v| v.first().unwrap())) + // .enumerate() { + // println!("{i}: z={:.5}, v={:.5}, c = {:.5}", z.to_string(), v.to_string(), c.0.x().unwrap().to_string()); + // } + + let mut todo = side_proof.todo; + let valid = Shplonk::>::verify_many( + &self.pcs_params.vk(), + &polys_to_open, + side_proof.pcs_proof, + &at_coords, + &to_values, + &mut todo, + ); + valid + } } diff --git a/w3f-plonk-common/src/batch.rs b/w3f-plonk-common/src/batch.rs new file mode 100644 index 0000000..cae0453 --- /dev/null +++ b/w3f-plonk-common/src/batch.rs @@ -0,0 +1,164 @@ +use crate::domain::{Domain, EvaluatedDomain}; +use crate::piop::{ProverPiop, VerifierPiop}; +use crate::{ColumnsCommited, ColumnsEvaluated}; +use ark_ff::PrimeField; +use ark_poly::univariate::DensePolynomial; +use ark_poly::Evaluations; +use ark_std::marker::PhantomData; +use ark_std::vec::Vec; +use w3f_pcs::pcs::Commitment; + +impl, CC: ColumnsCommited, const K: usize> + ColumnsCommited for [CC; K] +{ + fn to_vec(self) -> Vec { + self.into_iter().flat_map(|p| p.to_vec()).collect() + } +} + +// TODO: remove +// TODO: fixed columns that are fixed (selectors) don't have to be re-evaluated in the same point. +// TODO: other columns (like root layer) can be just witnessed in the proof. +// Flattens the vec while moving the first 3 elements of each inner vec to the beginning of the vec. +fn excerpt_fixed_columns(column_vecs: Vec>) -> Vec { + let k = column_vecs.len(); + let total_cols = 9; + let fixed_cols = 3; + let advice_cols = total_cols - fixed_cols; + let mut fixed = Vec::with_capacity(fixed_cols * k); + let mut advice = Vec::with_capacity(advice_cols * k); + for mut vec in column_vecs { + assert_eq!(vec.len(), total_cols); + advice.extend(vec.split_off(3)); + fixed.extend(vec); + } + [fixed, advice].concat() +} + +impl, const K: usize> ColumnsEvaluated for [CE; K] { + fn to_vec(self) -> Vec { + let column_vecs = self + .iter() + .map(|evals| evals.clone().to_vec()) + .collect::>(); + let res = excerpt_fixed_columns(column_vecs); + let total_cols = 9; + debug_assert_eq!(res.len(), total_cols * K); + res + } +} + +pub struct BatchProver, P: ProverPiop, const K: usize>( + pub [P; K], + pub PhantomData, + pub PhantomData, +); +pub struct BatchVerifier, V: VerifierPiop, const K: usize>( + pub [V; K], + pub PhantomData, + pub PhantomData, +); + +impl, P: ProverPiop, const K: usize> ProverPiop + for BatchProver +{ + const N_COLUMNS: usize = P::N_COLUMNS * K; + const N_CONSTRAINTS: usize = P::N_CONSTRAINTS * K; + const N_QUOTIENT_CHUNKS: usize = P::N_QUOTIENT_CHUNKS; + type Commitments = [P::Commitments; K]; + type Evaluations = [P::Evaluations; K]; + type Instance = [P::Instance; K]; + + fn committed_columns) -> C + Clone>( + &self, + commit: Fun, + ) -> Self::Commitments { + self.0 + .iter() + .map(|p| p.committed_columns(commit.clone())) + .collect::>() + .try_into() + .unwrap() + } + + fn columns(&self) -> Vec> { + let column_vecs = self.0.iter().map(|p| p.columns()).collect::>(); + excerpt_fixed_columns(column_vecs) + } + + fn columns_evaluated(&self, zeta: &F) -> Self::Evaluations { + self.0 + .iter() + .map(|p| p.columns_evaluated(zeta)) + .collect::>() + .try_into() + .unwrap() + } + + fn constraints(&self) -> Vec> { + self.0.iter().flat_map(|p| p.constraints()).collect() + } + + fn constraints_lin(&self, zeta: &F) -> Vec> { + self.0 + .iter() + .flat_map(|p| p.constraints_lin(zeta)) + .collect() + } + + fn quotient(&self, alphas: &[F]) -> Option>> { + self._quotient_chunks(alphas) + } + + fn domain(&self) -> &Domain { + self.0[0].domain() + } + + fn result(&self) -> Self::Instance { + self.0 + .iter() + .map(|p| p.result()) + .collect::>() // Requires allocation if you use .collect() + .try_into() + .unwrap() + } +} + +impl, V: VerifierPiop, const K: usize> VerifierPiop + for BatchVerifier +{ + const N_COLUMNS: usize = V::N_COLUMNS * K; + const N_CONSTRAINTS: usize = V::N_CONSTRAINTS * K; + type Instance = [V::Instance; K]; + + fn precommitted_columns(&self) -> Vec { + self.0 + .iter() + .flat_map(|p| p.precommitted_columns()) + .collect() + } + + fn evaluate_constraints_main(&self) -> Vec { + self.0 + .iter() + .flat_map(|p| p.evaluate_constraints_main()) + .collect() + } + + fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> (Vec, Vec) { + self.0 + .iter() + .zip(agg_coeffs.chunks(V::N_CONSTRAINTS)) + .map(|(p, alphas)| p.lin_poly_commitment(alphas)) + .reduce(|(mut acc_f, mut acc_c), (f, c)| { + acc_f.extend(f); + acc_c.extend(c); + (acc_f, acc_c) + }) + .unwrap() + } + + fn domain_evaluated(&self) -> &EvaluatedDomain { + self.0[0].domain_evaluated() + } +} diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index 2e97683..6174265 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -9,6 +9,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::{Commitment, PCS}; +pub mod batch; pub mod cond_select; pub mod domain; pub mod gadgets; @@ -75,13 +76,13 @@ pub fn const_evals(c: F, domain: GeneralEvaluationDomain) -> Eva } pub trait ColumnsEvaluated: - Clone + CanonicalSerialize + CanonicalDeserialize + Clone + ark_std::fmt::Debug + CanonicalSerialize + CanonicalDeserialize { fn to_vec(self) -> Vec; } pub trait ColumnsCommited>: - Clone + CanonicalSerialize + CanonicalDeserialize + Clone + ark_std::fmt::Debug + CanonicalSerialize + CanonicalDeserialize { fn to_vec(self) -> Vec; } @@ -112,7 +113,7 @@ where } /// Same as `Proof` but excluding the PCS opening. -#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct PiopProof where F: PrimeField, diff --git a/w3f-plonk-common/src/piop.rs b/w3f-plonk-common/src/piop.rs index 3406a2a..792a118 100644 --- a/w3f-plonk-common/src/piop.rs +++ b/w3f-plonk-common/src/piop.rs @@ -16,10 +16,10 @@ pub trait ProverPiop> { type Commitments: ColumnsCommited; type Evaluations: ColumnsEvaluated; - type Instance: CanonicalSerialize + CanonicalDeserialize; + type Instance: Clone + ark_std::fmt::Debug + CanonicalSerialize + CanonicalDeserialize; // Commitments to the column polynomials excluding the precommitted columns. - fn committed_columns) -> C>( + fn committed_columns) -> C + Clone>( &self, commit: Fun, ) -> Self::Commitments; diff --git a/w3f-ring-proof/src/piop/mod.rs b/w3f-ring-proof/src/piop/mod.rs index 6fd12e1..8bc5263 100644 --- a/w3f-ring-proof/src/piop/mod.rs +++ b/w3f-ring-proof/src/piop/mod.rs @@ -21,7 +21,7 @@ pub mod params; pub mod prover; pub mod verifier; -#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct RingCommitments> { pub(crate) bits: C, pub(crate) inn_prod_acc: C, @@ -40,7 +40,7 @@ impl> ColumnsCommited for RingCommitments< } } -#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct RingEvaluations { pub(crate) points: [F; 2], pub(crate) ring_selector: F, @@ -208,3 +208,58 @@ pub fn index, G: AffineRepr>( }; (prover_key, verifier_key) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::index; + use crate::tests::setup; + use ark_ed_on_bls12_381_bandersnatch::{EdwardsAffine, Fq, Fr}; + use ark_std::{test_rng, UniformRand}; + use w3f_pcs::pcs::id::WrappedPolynomial; + use w3f_pcs::pcs::IdentityCommitment; + use w3f_pcs::Polynomial; + use w3f_plonk_common::piop::ProverPiop; + use w3f_plonk_common::test_helpers::random_vec; + + #[test] + fn test_ring_piop() { + let rng = &mut test_rng(); + + let log_n = 9; + let n = 1 << log_n; + + let (pcs_params, piop_params) = setup::<_, IdentityCommitment>(rng, n); + let pks = random_vec::(piop_params.keyset_part_size, rng); + let (prover_key, verifier_key) = + index::<_, IdentityCommitment, _>(&pcs_params, &piop_params, &pks); + let fixed_columns = prover_key.fixed_columns.clone(); + let prover: PiopProver = + PiopProver::build(&piop_params, fixed_columns, 1, Fr::rand(rng)); + assert!(ProverPiop::>::constraints_satisfied(&prover)); + + let zeta = Fq::rand(rng); + let columns = ProverPiop::>::columns(&prover); + let evals = ProverPiop::>::columns_evaluated(&prover, &zeta); + let evals = evals.to_vec(); + assert_eq!(columns.len(), evals.len()); + for (p, v) in columns.iter().zip(evals) { + assert_eq!(p.evaluate(&zeta), v); + } + + let fixed_columns = verifier_key.fixed_columns_committed.as_vec(); + let advice_columns = + ProverPiop::>::committed_columns(&prover, |p| { + IdentityCommitment::commit(&prover_key.pcs_ck, p).unwrap() + }); + let advice_columns = advice_columns.to_vec(); + let commitments = [fixed_columns, advice_columns].concat(); + assert_eq!(columns.len(), commitments.len()); + for (p, c) in columns.iter().zip(commitments) { + assert_eq!( + IdentityCommitment::commit(&prover_key.pcs_ck, p).unwrap(), + c + ); + } + } +} diff --git a/w3f-ring-proof/src/piop/prover.rs b/w3f-ring-proof/src/piop/prover.rs index eed0bf3..6244395 100644 --- a/w3f-ring-proof/src/piop/prover.rs +++ b/w3f-ring-proof/src/piop/prover.rs @@ -275,32 +275,3 @@ where self.cond_add.result() } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::index; - use crate::tests::setup; - use ark_ed_on_bls12_381_bandersnatch::{EdwardsAffine, Fq, Fr}; - use ark_std::{test_rng, UniformRand}; - use w3f_pcs::pcs::id::WrappedPolynomial; - use w3f_pcs::pcs::IdentityCommitment; - use w3f_plonk_common::test_helpers::random_vec; - - #[test] - fn test_constraints() { - let rng = &mut test_rng(); - - let log_n = 9; - let n = 1 << log_n; - - let (pcs_params, piop_params) = setup::<_, IdentityCommitment>(rng, n); - let pks = random_vec::(piop_params.keyset_part_size, rng); - let (prover_key, _verifier_key) = - index::<_, IdentityCommitment, _>(&pcs_params, &piop_params, &pks); - let fixed_columns = prover_key.fixed_columns.clone(); - let piop: PiopProver = - PiopProver::build(&piop_params, fixed_columns, 1, Fr::rand(rng)); - assert!(ProverPiop::>::constraints_satisfied(&piop)); - } -}