diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs new file mode 100644 index 0000000..d49af79 --- /dev/null +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -0,0 +1,109 @@ +use crate::piop::VerifierPiop; +use crate::verifier::Challenges; +use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; +use ark_ec::pairing::Pairing; +use ark_ec::{CurveGroup, VariableBaseMSM}; +use ark_ff::{PrimeField, Zero}; +use ark_std::iterable::Iterable; +use ark_std::rand::Rng; +use w3f_pcs::pcs::kzg::params::KzgVerifierKey; +use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG}; +use w3f_pcs::pcs::{Commitment, PCS}; + +// Aggregates opennings for KZG commitments. +// Somewhat similar to https://eprint.iacr.org/2020/499.pdf, section 8. +// With a difference that this accumulates opennings lazily, +// and runs `2` MSMs of size `O(n)` at the final stage, +// that gives an asymptotic saving (thanks to Pippenger) +// at the cost of linear accumulator size. +pub struct KzgAccumulator { + acc_points: Vec, + acc_scalars: Vec, + kzg_proofs: Vec, + randomizers: Vec, + kzg_vk: KzgVerifierKey, +} + +impl KzgAccumulator { + pub fn new(kzg_vk: KzgVerifierKey) -> Self { + //TODO: capacity + Self { + acc_points: vec![kzg_vk.g1], + acc_scalars: vec![E::ScalarField::zero()], + kzg_proofs: vec![], + randomizers: vec![], + kzg_vk, + } + } + + pub fn accumulate( + &mut self, + piop: Piop, + proof: Proof, Commitments, Evaluations>, + challenges: Challenges, + rng: &mut R, + ) where + F: PrimeField, + E: Pairing, + Piop: VerifierPiop as PCS>::C>, + Commitments: ColumnsCommited as PCS>::C>, + Evaluations: ColumnsEvaluated, + { + let q_zeta = piop.evaluate_q_at_zeta(&challenges.alphas, proof.lin_at_zeta_omega); + + let mut columns = [ + piop.precommitted_columns(), + proof.column_commitments.to_vec(), + ] + .concat(); + columns.push(proof.quotient_commitment.clone()); + let columns = columns.iter().map(|c| c.0).collect::>(); + + let mut columns_at_zeta = proof.columns_at_zeta.to_vec(); + columns_at_zeta.push(q_zeta); + + let agg_at_zeta: F = columns_at_zeta + .into_iter() + .zip(challenges.nus.iter()) + .map(|(y, r)| y * r) + .sum(); + + let lin_comm = piop.lin_poly_commitment(&challenges.alphas); + + let zeta = challenges.zeta; + let zeta_omega = zeta * piop.domain_evaluated().omega(); + + let mut acc_points = vec![]; + let mut acc_scalars = vec![]; + + acc_points.extend(columns); + acc_scalars.extend(challenges.nus); + acc_points.push(proof.agg_at_zeta_proof); + acc_scalars.push(zeta); + self.acc_scalars[0] -= agg_at_zeta; + + let r = F::rand(rng); + // z.w openning + acc_points.extend(lin_comm.1.iter().map(|c| c.0).collect::>()); + acc_scalars.extend(lin_comm.0.into_iter().map(|c| c * r).collect::>()); + acc_points.push(proof.lin_at_zeta_omega_proof); + acc_scalars.push(zeta_omega * r); + self.acc_scalars[0] -= proof.lin_at_zeta_omega * r; + + let kzg_proofs = vec![proof.agg_at_zeta_proof, proof.lin_at_zeta_omega_proof]; + let randomizers = vec![F::one(), r]; + + self.acc_points.extend(acc_points); + self.acc_scalars.extend(acc_scalars); + self.kzg_proofs.extend(kzg_proofs); + self.randomizers.extend(randomizers); + } + + pub fn verify(&self) -> bool { + let acc = (-E::G1::msm(&self.acc_points, &self.acc_scalars).unwrap()).into_affine(); + let proof = E::G1::msm(&self.kzg_proofs, &self.randomizers) + .unwrap() + .into_affine(); + KZG::::verify_accumulated(AccumulatedOpening { acc, proof }, &self.kzg_vk) + } +} diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index 87208d9..44db952 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -9,6 +9,7 @@ use w3f_pcs::pcs::{Commitment, PCS}; pub mod domain; pub mod gadgets; +pub mod kzg_acc; pub mod piop; pub mod prover; pub mod test_helpers; diff --git a/w3f-plonk-common/src/piop.rs b/w3f-plonk-common/src/piop.rs index df37e9b..81e5333 100644 --- a/w3f-plonk-common/src/piop.rs +++ b/w3f-plonk-common/src/piop.rs @@ -69,7 +69,7 @@ pub trait VerifierPiop> { } // Commitment to the aggregated linearization polynomial without the constant term. - fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> C; + fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> (Vec, Vec); fn domain_evaluated(&self) -> &EvaluatedDomain; } diff --git a/w3f-plonk-common/src/verifier.rs b/w3f-plonk-common/src/verifier.rs index 184c01e..7df1928 100644 --- a/w3f-plonk-common/src/verifier.rs +++ b/w3f-plonk-common/src/verifier.rs @@ -11,7 +11,7 @@ use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; pub struct PlonkVerifier, T: PlonkTranscript> { // Polynomial commitment scheme verifier's key. - pcs_vk: CS::VK, + pub pcs_vk: CS::VK, // Transcript, // initialized with the public parameters and the commitments to the precommitted columns. transcript_prelude: T, @@ -64,6 +64,7 @@ impl, T: PlonkTranscript> PlonkVerifier>(domain_size: usize) { + fn _test_ring_proof>( + domain_size: usize, + batch_size: usize, + ) -> ( + RingVerifier, + Vec<(EdwardsAffine, RingProof)>, + ) { let rng = &mut test_rng(); let (pcs_params, piop_params) = setup::<_, CS>(rng, domain_size); - - let max_keyset_size = piop_params.keyset_part_size; - let keyset_size: usize = rng.gen_range(0..max_keyset_size); + let keyset_size = piop_params.keyset_part_size; let pks = random_vec::(keyset_size, rng); - let k = rng.gen_range(0..keyset_size); // prover's secret index - let pk = pks[k].clone(); - let (prover_key, verifier_key) = index::<_, CS, _>(&pcs_params, &piop_params, &pks); - // PROOF generation - let secret = Fr::rand(rng); // prover's secret scalar - let result = piop_params.h.mul(secret) + pk; - let ring_prover = RingProver::init( - prover_key, - piop_params.clone(), - k, - ArkTranscript::new(b"w3f-ring-proof-test"), - ); let t_prove = start_timer!(|| "Prove"); - let proof = ring_prover.prove(secret); + let claims: Vec<(EdwardsAffine, RingProof)> = (0..batch_size) + .map(|_| { + let prover_idx = rng.gen_range(0..keyset_size); + let prover = RingProver::init( + prover_key.clone(), + piop_params.clone(), + prover_idx, + ArkTranscript::new(b"w3f-ring-proof-test"), + ); + let prover_pk = pks[prover_idx].clone(); + let blinding_factor = Fr::rand(rng); + let blinded_pk = prover_pk + piop_params.h.mul(blinding_factor); + let blinded_pk = blinded_pk.into_affine(); + let proof = prover.prove(blinding_factor); + (blinded_pk, proof) + }) + .collect(); end_timer!(t_prove); let ring_verifier = RingVerifier::init( @@ -99,9 +107,10 @@ mod tests { ArkTranscript::new(b"w3f-ring-proof-test"), ); let t_verify = start_timer!(|| "Verify"); - let res = ring_verifier.verify(proof, result.into_affine()); + let (blinded_pks, proofs) = claims.iter().cloned().unzip(); + assert!(ring_verifier.verify_batch(proofs, blinded_pks)); end_timer!(t_verify); - assert!(res); + (ring_verifier, claims) } #[test] @@ -145,12 +154,17 @@ mod tests { } #[test] + // cargo test test_ring_proof_kzg --release --features="print-trace" -- --show-output fn test_ring_proof_kzg() { - _test_ring_proof::>(2usize.pow(10)); + let (verifier, claims) = _test_ring_proof::>(2usize.pow(10), 10); + let t_verify_batch = start_timer!(|| "Verify Batch KZG"); + let (blinded_pks, proofs) = claims.into_iter().unzip(); + assert!(verifier.verify_batch_kzg(proofs, blinded_pks)); + end_timer!(t_verify_batch); } #[test] fn test_ring_proof_id() { - _test_ring_proof::(2usize.pow(10)); + _test_ring_proof::(2usize.pow(10), 1); } } diff --git a/w3f-ring-proof/src/piop/mod.rs b/w3f-ring-proof/src/piop/mod.rs index a4d2c71..29cd41a 100644 --- a/w3f-ring-proof/src/piop/mod.rs +++ b/w3f-ring-proof/src/piop/mod.rs @@ -127,6 +127,7 @@ impl> FixedColumns { } // #[derive(CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone)] pub struct ProverKey, G: AffineRepr> { pub(crate) pcs_ck: CS::CK, pub(crate) fixed_columns: FixedColumns, diff --git a/w3f-ring-proof/src/piop/verifier.rs b/w3f-ring-proof/src/piop/verifier.rs index ea6ea24..93c2e1f 100644 --- a/w3f-ring-proof/src/piop/verifier.rs +++ b/w3f-ring-proof/src/piop/verifier.rs @@ -123,7 +123,7 @@ impl, Jubjub: TECurveConfig> Veri .concat() } - fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> C { + fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> (Vec, Vec) { assert_eq!(agg_coeffs.len(), Self::N_CONSTRAINTS); let inner_prod_acc = self.witness_columns_committed.inn_prod_acc.clone(); @@ -138,10 +138,15 @@ impl, Jubjub: TECurveConfig> Veri cond_add_x_coeff += agg_coeffs[2] * c_acc_x; cond_add_y_coeff += agg_coeffs[2] * c_acc_y; - C::combine( - &[inner_prod_coeff, cond_add_x_coeff, cond_add_y_coeff], - &[inner_prod_acc.clone(), cond_add_acc_x, cond_add_acc_y], + ( + vec![inner_prod_coeff, cond_add_x_coeff, cond_add_y_coeff], + vec![inner_prod_acc.clone(), cond_add_acc_x, cond_add_acc_y], ) + + // C::combine( + // &[inner_prod_coeff, cond_add_x_coeff, cond_add_y_coeff], + // &[inner_prod_acc.clone(), cond_add_acc_x, cond_add_acc_y], + // ) } fn domain_evaluated(&self) -> &EvaluatedDomain { diff --git a/w3f-ring-proof/src/ring_verifier.rs b/w3f-ring-proof/src/ring_verifier.rs index 324adc2..4f38cac 100644 --- a/w3f-ring-proof/src/ring_verifier.rs +++ b/w3f-ring-proof/src/ring_verifier.rs @@ -1,8 +1,10 @@ +use ark_ec::pairing::Pairing; use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ec::CurveGroup; use ark_ff::PrimeField; +use w3f_pcs::pcs::kzg::KZG; use w3f_pcs::pcs::{RawVerifierKey, PCS}; - +use w3f_plonk_common::kzg_acc::KzgAccumulator; use w3f_plonk_common::piop::VerifierPiop; use w3f_plonk_common::transcript::PlonkTranscript; use w3f_plonk_common::verifier::PlonkVerifier; @@ -71,4 +73,56 @@ where pub fn piop_params(&self) -> &PiopParams { &self.piop_params } + + pub fn verify_batch( + &self, + proofs: Vec>, + results: Vec>, + ) -> bool { + for (proof, result) in proofs.into_iter().zip(results) { + let res = self.verify(proof, result); + if !res { + return false; + } + } + true + } +} + +impl RingVerifier, Jubjub, T> +where + E: Pairing, + Jubjub: TECurveConfig, + T: PlonkTranscript>, +{ + // Verifies a batch of proofs against the same ring. + pub fn verify_batch_kzg( + &self, + proofs: Vec>>, + results: Vec>, + ) -> bool { + let mut acc = KzgAccumulator::::new(self.plonk_verifier.pcs_vk.clone()); + for (proof, result) in proofs.into_iter().zip(results) { + let (challenges, mut rng) = self.plonk_verifier.restore_challenges( + &result, + &proof, + // '1' accounts for the quotient polynomial that is aggregated together with the columns + PiopVerifier:: as PCS<_>>::C, Affine>::N_COLUMNS + 1, + PiopVerifier:: as PCS<_>>::C, Affine>::N_CONSTRAINTS, + ); + let seed = self.piop_params.seed; + let seed_plus_result = (seed + result).into_affine(); + let domain_at_zeta = self.piop_params.domain.evaluate(challenges.zeta); + let piop = PiopVerifier::<_, _, Affine>::init( + domain_at_zeta, + self.fixed_columns_committed.clone(), + proof.column_commitments.clone(), + proof.columns_at_zeta.clone(), + (seed.x, seed.y), + (seed_plus_result.x, seed_plus_result.y), + ); + acc.accumulate(piop, proof, challenges, &mut rng); + } + acc.verify() + } }