From ef6d617b189e52dd1e0a41db1ba7499336920618 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Tue, 20 Jan 2026 15:37:24 +0100 Subject: [PATCH 01/11] a test revamped --- w3f-ring-proof/src/lib.rs | 46 ++++++++++++++++++---------------- w3f-ring-proof/src/piop/mod.rs | 1 + 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/w3f-ring-proof/src/lib.rs b/w3f-ring-proof/src/lib.rs index ca45992..b466ce0 100644 --- a/w3f-ring-proof/src/lib.rs +++ b/w3f-ring-proof/src/lib.rs @@ -67,30 +67,32 @@ mod tests { use super::*; - fn _test_ring_proof>(domain_size: usize) { + fn _test_ring_proof>(domain_size: usize, batch_size: usize) { 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 proofs: 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 +101,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()); + proofs + .into_iter() + .for_each(|(blinded_pk, proof)| assert!(ring_verifier.verify(proof, blinded_pk))); end_timer!(t_verify); - assert!(res); } #[test] @@ -145,12 +148,13 @@ 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)); + _test_ring_proof::>(2usize.pow(10), 1); } #[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, From c80853fe4a7173289856b4d533e11024b76ee7e2 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 21 Jan 2026 23:02:34 +0100 Subject: [PATCH 02/11] kzg batch test --- w3f-ring-proof/src/lib.rs | 22 ++++++++--- w3f-ring-proof/src/ring_verifier.rs | 59 ++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/w3f-ring-proof/src/lib.rs b/w3f-ring-proof/src/lib.rs index b466ce0..a639bc5 100644 --- a/w3f-ring-proof/src/lib.rs +++ b/w3f-ring-proof/src/lib.rs @@ -67,7 +67,13 @@ mod tests { use super::*; - fn _test_ring_proof>(domain_size: usize, batch_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); @@ -76,7 +82,7 @@ mod tests { let (prover_key, verifier_key) = index::<_, CS, _>(&pcs_params, &piop_params, &pks); let t_prove = start_timer!(|| "Prove"); - let proofs: Vec<(EdwardsAffine, RingProof)> = (0..batch_size) + let claims: Vec<(EdwardsAffine, RingProof)> = (0..batch_size) .map(|_| { let prover_idx = rng.gen_range(0..keyset_size); let prover = RingProver::init( @@ -101,10 +107,10 @@ mod tests { ArkTranscript::new(b"w3f-ring-proof-test"), ); let t_verify = start_timer!(|| "Verify"); - proofs - .into_iter() - .for_each(|(blinded_pk, proof)| assert!(ring_verifier.verify(proof, blinded_pk))); + let (blinded_pks, proofs) = claims.iter().cloned().unzip(); + assert!(ring_verifier.verify_batch(proofs, blinded_pks)); end_timer!(t_verify); + (ring_verifier, claims) } #[test] @@ -150,7 +156,11 @@ 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), 1); + let (verifier, claims) = _test_ring_proof::>(2usize.pow(10), 1); + 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] diff --git a/w3f-ring-proof/src/ring_verifier.rs b/w3f-ring-proof/src/ring_verifier.rs index 324adc2..6087e3e 100644 --- a/w3f-ring-proof/src/ring_verifier.rs +++ b/w3f-ring-proof/src/ring_verifier.rs @@ -1,8 +1,9 @@ +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::piop::VerifierPiop; use w3f_plonk_common::transcript::PlonkTranscript; use w3f_plonk_common::verifier::PlonkVerifier; @@ -71,4 +72,60 @@ 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 { + 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), + ); + let res = self + .plonk_verifier + .verify(piop, proof, challenges, &mut rng); + if !res { + return false; + } + } + true + } } From 9a2cb65a767624516f843987cb335bf5c0e28321 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 22 Jan 2026 00:40:35 +0100 Subject: [PATCH 03/11] kzg acc --- w3f-plonk-common/src/kzg_acc.rs | 109 ++++++++++++++++++++++++++++ w3f-plonk-common/src/lib.rs | 1 + w3f-plonk-common/src/verifier.rs | 2 +- w3f-ring-proof/src/lib.rs | 2 +- w3f-ring-proof/src/ring_verifier.rs | 11 +-- 5 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 w3f-plonk-common/src/kzg_acc.rs diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs new file mode 100644 index 0000000..d0bfb94 --- /dev/null +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -0,0 +1,109 @@ +use ark_ec::pairing::Pairing; +use ark_ec::{CurveGroup, VariableBaseMSM}; +use ark_ff::PrimeField; +use ark_std::rand::Rng; +use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG}; +use w3f_pcs::pcs::kzg::params::KzgVerifierKey; +use w3f_pcs::pcs::{Commitment, PCS}; +use crate::piop::VerifierPiop; +use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; +use crate::verifier::Challenges; + +// 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![], + acc_scalars: vec![], + 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 mut columns_at_zeta = proof.columns_at_zeta.to_vec(); + columns_at_zeta.push(q_zeta); + + let agg_comm = as PCS>::C::combine(&challenges.nus, &columns); + 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.push(agg_comm.0); + acc_scalars.push(F::one()); + acc_points.push(proof.agg_at_zeta_proof); + acc_scalars.push(zeta); + acc_points.push(self.kzg_vk.g1); + acc_scalars.push(-agg_at_zeta); + + let r = F::rand(rng); + // z.w openning + acc_points.push(lin_comm.0); + acc_scalars.push(r); + acc_points.push(proof.lin_at_zeta_omega_proof); + acc_scalars.push(zeta_omega * r); + acc_points.push(self.kzg_vk.g1); + acc_scalars.push(-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) + } +} \ No newline at end of file diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index 87208d9..f46c7c4 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -14,6 +14,7 @@ pub mod prover; pub mod test_helpers; pub mod transcript; pub mod verifier; +pub mod kzg_acc; pub trait Column { fn domain(&self) -> GeneralEvaluationDomain; diff --git a/w3f-plonk-common/src/verifier.rs b/w3f-plonk-common/src/verifier.rs index 184c01e..47b9e83 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, diff --git a/w3f-ring-proof/src/lib.rs b/w3f-ring-proof/src/lib.rs index a639bc5..239d20c 100644 --- a/w3f-ring-proof/src/lib.rs +++ b/w3f-ring-proof/src/lib.rs @@ -156,7 +156,7 @@ mod tests { #[test] // cargo test test_ring_proof_kzg --release --features="print-trace" -- --show-output fn test_ring_proof_kzg() { - let (verifier, claims) = _test_ring_proof::>(2usize.pow(10), 1); + 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)); diff --git a/w3f-ring-proof/src/ring_verifier.rs b/w3f-ring-proof/src/ring_verifier.rs index 6087e3e..4f38cac 100644 --- a/w3f-ring-proof/src/ring_verifier.rs +++ b/w3f-ring-proof/src/ring_verifier.rs @@ -4,6 +4,7 @@ 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; @@ -100,6 +101,7 @@ where 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, @@ -119,13 +121,8 @@ where (seed.x, seed.y), (seed_plus_result.x, seed_plus_result.y), ); - let res = self - .plonk_verifier - .verify(piop, proof, challenges, &mut rng); - if !res { - return false; - } + acc.accumulate(piop, proof, challenges, &mut rng); } - true + acc.verify() } } From c342132d98af3f741eeb33d65aed3d4044341444 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 22 Jan 2026 00:41:02 +0100 Subject: [PATCH 04/11] fmt --- w3f-plonk-common/src/kzg_acc.rs | 21 +++++++++++---------- w3f-plonk-common/src/lib.rs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index d0bfb94..4de390a 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -1,13 +1,13 @@ +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; use ark_std::rand::Rng; -use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG}; use w3f_pcs::pcs::kzg::params::KzgVerifierKey; +use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG}; use w3f_pcs::pcs::{Commitment, PCS}; -use crate::piop::VerifierPiop; -use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; -use crate::verifier::Challenges; // Aggregates opennings for KZG commitments. // Somewhat similar to https://eprint.iacr.org/2020/499.pdf, section 8. @@ -41,10 +41,9 @@ impl KzgAccumulator { proof: Proof, Commitments, Evaluations>, challenges: Challenges, rng: &mut R, - ) - where + ) where F: PrimeField, - E: Pairing, + E: Pairing, Piop: VerifierPiop as PCS>::C>, Commitments: ColumnsCommited as PCS>::C>, Evaluations: ColumnsEvaluated, @@ -55,7 +54,7 @@ impl KzgAccumulator { piop.precommitted_columns(), proof.column_commitments.to_vec(), ] - .concat(); + .concat(); columns.push(proof.quotient_commitment.clone()); let mut columns_at_zeta = proof.columns_at_zeta.to_vec(); @@ -103,7 +102,9 @@ impl KzgAccumulator { 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(); + let proof = E::G1::msm(&self.kzg_proofs, &self.randomizers) + .unwrap() + .into_affine(); KZG::::verify_accumulated(AccumulatedOpening { acc, proof }, &self.kzg_vk) } -} \ No newline at end of file +} diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index f46c7c4..44db952 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -9,12 +9,12 @@ 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; pub mod transcript; pub mod verifier; -pub mod kzg_acc; pub trait Column { fn domain(&self) -> GeneralEvaluationDomain; From f8a5278995462f6e696e7fd0f8124634e041cbe0 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 22 Jan 2026 00:49:31 +0100 Subject: [PATCH 05/11] agg_commitment computed via the final msm --- w3f-plonk-common/src/kzg_acc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index 4de390a..7fa8612 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -56,11 +56,11 @@ impl KzgAccumulator { ] .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_comm = as PCS>::C::combine(&challenges.nus, &columns); let agg_at_zeta: F = columns_at_zeta .into_iter() .zip(challenges.nus.iter()) @@ -75,8 +75,8 @@ impl KzgAccumulator { let mut acc_points = vec![]; let mut acc_scalars = vec![]; - acc_points.push(agg_comm.0); - acc_scalars.push(F::one()); + acc_points.extend(columns); + acc_scalars.extend(challenges.nus); acc_points.push(proof.agg_at_zeta_proof); acc_scalars.push(zeta); acc_points.push(self.kzg_vk.g1); From bbf51e33a8c4884ee27c072914447a8a5d3e8608 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 22 Jan 2026 01:01:05 +0100 Subject: [PATCH 06/11] lin_comm computed via the final msm --- w3f-plonk-common/src/kzg_acc.rs | 5 +++-- w3f-plonk-common/src/piop.rs | 2 +- w3f-plonk-common/src/verifier.rs | 1 + w3f-ring-proof/src/piop/verifier.rs | 13 +++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index 7fa8612..0fe25d0 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -4,6 +4,7 @@ use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; use ark_ec::pairing::Pairing; use ark_ec::{CurveGroup, VariableBaseMSM}; use ark_ff::PrimeField; +use ark_std::iterable::Iterable; use ark_std::rand::Rng; use w3f_pcs::pcs::kzg::params::KzgVerifierKey; use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG}; @@ -84,8 +85,8 @@ impl KzgAccumulator { let r = F::rand(rng); // z.w openning - acc_points.push(lin_comm.0); - acc_scalars.push(r); + 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); acc_points.push(self.kzg_vk.g1); 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 47b9e83..7df1928 100644 --- a/w3f-plonk-common/src/verifier.rs +++ b/w3f-plonk-common/src/verifier.rs @@ -64,6 +64,7 @@ impl, T: PlonkTranscript> PlonkVerifier, 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 { From 5eff6453545876ecce28b35332914012b6271c70 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 22 Jan 2026 01:07:59 +0100 Subject: [PATCH 07/11] muls by the generator aggregated --- w3f-plonk-common/src/kzg_acc.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index 0fe25d0..d49af79 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -3,7 +3,7 @@ use crate::verifier::Challenges; use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; use ark_ec::pairing::Pairing; use ark_ec::{CurveGroup, VariableBaseMSM}; -use ark_ff::PrimeField; +use ark_ff::{PrimeField, Zero}; use ark_std::iterable::Iterable; use ark_std::rand::Rng; use w3f_pcs::pcs::kzg::params::KzgVerifierKey; @@ -28,8 +28,8 @@ impl KzgAccumulator { pub fn new(kzg_vk: KzgVerifierKey) -> Self { //TODO: capacity Self { - acc_points: vec![], - acc_scalars: vec![], + acc_points: vec![kzg_vk.g1], + acc_scalars: vec![E::ScalarField::zero()], kzg_proofs: vec![], randomizers: vec![], kzg_vk, @@ -80,8 +80,7 @@ impl KzgAccumulator { acc_scalars.extend(challenges.nus); acc_points.push(proof.agg_at_zeta_proof); acc_scalars.push(zeta); - acc_points.push(self.kzg_vk.g1); - acc_scalars.push(-agg_at_zeta); + self.acc_scalars[0] -= agg_at_zeta; let r = F::rand(rng); // z.w openning @@ -89,8 +88,7 @@ impl KzgAccumulator { 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); - acc_points.push(self.kzg_vk.g1); - acc_scalars.push(-proof.lin_at_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]; From 683456584f68396c602887707fe514ef444c6ee5 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 11 Feb 2026 18:28:23 +0300 Subject: [PATCH 08/11] final rs fixed --- w3f-plonk-common/src/kzg_acc.rs | 93 +++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index d49af79..60c54c2 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -4,20 +4,22 @@ 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}; +use w3f_pcs::pcs::PCS; -// Aggregates opennings for KZG commitments. +// Accumulates KZG openning claims for Plonk proofs. // 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, +// and runs `2` MSMs of size `O(k)` at the final stage, // that gives an asymptotic saving (thanks to Pippenger) // at the cost of linear accumulator size. + pub struct KzgAccumulator { + // acc_points[0] = G1 acc_points: Vec, + // acc_scalars[0] = 0 acc_scalars: Vec, kzg_proofs: Vec, randomizers: Vec, @@ -36,6 +38,29 @@ impl KzgAccumulator { } } + // `p(z) = v <=> q(X) = p(X)-v / X-z <=> q(X)(X-z) = p(X)-v <=> q(X)X = p(X) + q(X)z - v`. + // Raising + // `p(X) -> p(tau)G1 =: C`, + // `q(X) -> q(tau)G1 =: pi`, + // `X -> tau.G2`, + // `v -> v.G1`, + // and taking the pairing gives: + // `e(pi, tau.G2) + e(C + z.pi - v.G1, -G2) = 0`. + + // Combining `k` such equations using random coefficients `ri` results in + // `e(agg_pi, tau.G2) + e(acc, -G2) = 0`, where + + // `agg_pi := r1.pi_1 + ... + rk.pi_k` and + + // `acc := sum[r1.(C1 + z1.pi_1 - v1.G1), i = 1,...,k] = + // = -(r1.v1 + ... + rk.vk).G1 + (r1.C1 + ... + rk.Ck) + (r1.z1.pi_1 + ... + rk.zk.pi_k)`. + + // `agg_pi` is a `k`-MSM. + // In a common case when a batch proof `pi_i` attests opennings of multiple (`ni`) commitments at the same point `zi`, + // `Ci = ai_1.Ci_1 + ... + ai_ni.Ci_ni, i = 1,...,k`, + // `acc` is a `1+k+(n1+...+nk)`-MSM. + + /// Accumulates pub fn accumulate( &mut self, piop: Piop, @@ -49,54 +74,46 @@ impl KzgAccumulator { 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 r = F::rand(rng); + let r2 = r.square(); + let zeta = challenges.zeta; + // TODO: it could be a method unless `to_vec(self)` + let q_zeta = piop.evaluate_q_at_zeta(&challenges.alphas, proof.lin_at_zeta_omega); 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 zeta_omega = zeta * piop.domain_evaluated().omega(); let lin_comm = piop.lin_poly_commitment(&challenges.alphas); - let zeta = challenges.zeta; - let zeta_omega = zeta * piop.domain_evaluated().omega(); + // Openning at `z` + // TODO: try to get rid of the commitment wrapper in flonk + self.acc_points.extend(piop.precommitted_columns().iter().map(|c| c.0).collect::>()); + self.acc_points.extend(proof.column_commitments.to_vec().iter().map(|c| c.0).collect::>()); + self.acc_points.push(proof.quotient_commitment.clone().0); + self.acc_scalars.extend(challenges.nus.iter().map(|nu| *nu * r).collect::>()); // numbers should match here - let mut acc_points = vec![]; - let mut acc_scalars = vec![]; + self.acc_points.push(proof.agg_at_zeta_proof); + self.acc_scalars.push(zeta * r); + self.acc_scalars[0] -= agg_at_zeta * r; - 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; + // Openning at `z.w` + // TODO: see above + self.acc_points.extend(lin_comm.1.iter().map(|c| c.0).collect::>()); + self.acc_scalars.extend(lin_comm.0.into_iter().map(|c| c * r2).collect::>()); + self.acc_points.push(proof.lin_at_zeta_omega_proof); + self.acc_scalars.push(zeta_omega * r2); + self.acc_scalars[0] -= proof.lin_at_zeta_omega * r2; - 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); + self.kzg_proofs.push(proof.agg_at_zeta_proof); + self.kzg_proofs.push(proof.lin_at_zeta_omega_proof); + self.randomizers.push(r); + self.randomizers.push(r2); } pub fn verify(&self) -> bool { From d64e792f3d0675bfc2487191e714aafc921a819f Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 12 Feb 2026 19:18:03 +0300 Subject: [PATCH 09/11] cleanup --- w3f-ring-proof/src/piop/verifier.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/w3f-ring-proof/src/piop/verifier.rs b/w3f-ring-proof/src/piop/verifier.rs index 93c2e1f..8eb663f 100644 --- a/w3f-ring-proof/src/piop/verifier.rs +++ b/w3f-ring-proof/src/piop/verifier.rs @@ -142,11 +142,6 @@ impl, Jubjub: TECurveConfig> Veri 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 { From ce33c64a7ec3b677eee26d21a964125c1d6b33ac Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 12 Feb 2026 19:18:41 +0300 Subject: [PATCH 10/11] fmt --- w3f-plonk-common/src/kzg_acc.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index 60c54c2..772917e 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -93,10 +93,23 @@ impl KzgAccumulator { // Openning at `z` // TODO: try to get rid of the commitment wrapper in flonk - self.acc_points.extend(piop.precommitted_columns().iter().map(|c| c.0).collect::>()); - self.acc_points.extend(proof.column_commitments.to_vec().iter().map(|c| c.0).collect::>()); + self.acc_points.extend( + piop.precommitted_columns() + .iter() + .map(|c| c.0) + .collect::>(), + ); + self.acc_points.extend( + proof + .column_commitments + .to_vec() + .iter() + .map(|c| c.0) + .collect::>(), + ); self.acc_points.push(proof.quotient_commitment.clone().0); - self.acc_scalars.extend(challenges.nus.iter().map(|nu| *nu * r).collect::>()); // numbers should match here + self.acc_scalars + .extend(challenges.nus.iter().map(|nu| *nu * r).collect::>()); // numbers should match here self.acc_points.push(proof.agg_at_zeta_proof); self.acc_scalars.push(zeta * r); @@ -104,8 +117,10 @@ impl KzgAccumulator { // Openning at `z.w` // TODO: see above - self.acc_points.extend(lin_comm.1.iter().map(|c| c.0).collect::>()); - self.acc_scalars.extend(lin_comm.0.into_iter().map(|c| c * r2).collect::>()); + self.acc_points + .extend(lin_comm.1.iter().map(|c| c.0).collect::>()); + self.acc_scalars + .extend(lin_comm.0.into_iter().map(|c| c * r2).collect::>()); self.acc_points.push(proof.lin_at_zeta_omega_proof); self.acc_scalars.push(zeta_omega * r2); self.acc_scalars[0] -= proof.lin_at_zeta_omega * r2; From aa033f1f0f1f5c8cf04a680202f597a801b9f6bf Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 12 Feb 2026 19:25:05 +0300 Subject: [PATCH 11/11] disabled evm stuff --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc402de..8a143bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] resolver = "2" members = [ - "evm-vrfier", +# "evm-vrfier", "w3f-plonk-common", "w3f-ring-proof", - "w3f-ring-vrf-snark", +# "w3f-ring-vrf-snark", ] [workspace.dependencies]