Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions w3f-plonk-common/src/kzg_acc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ 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.
// Somewhat similar to https://eprint.iacr.org/2020/499.pdf, section 8.
Expand Down
24 changes: 21 additions & 3 deletions w3f-ring-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,29 @@ mod tests {
(pcs_params, piop_params)
}

#[test]
// cargo test test_ring_proof_kzg --release --features="print-trace" -- --show-output
//
// Batch vs sequential verification times (ms):
//
// | proofs | batch | sequential | speedup |
// |--------|--------|------------|---------|
// | 1 | 2.790 | 3.032 | 1.09x |
// | 2 | 3.218 | 6.425 | 2.00x |
// | 4 | 5.122 | 11.968 | 2.34x |
// | 8 | 6.487 | 23.922 | 3.69x |
// | 16 | 10.002 | 47.773 | 4.78x |
// | 32 | 16.601 | 95.570 | 5.76x |
// | 64 | 29.484 | 210.959 | 7.15x |
// | 128 | 52.170 | 422.217 | 8.09x |
// | 256 | 85.164 | 762.874 | 8.96x |
//
// Sequential verification scales linearly with proof count. Batch verification
// scales sub-linearly.
#[test]
fn test_ring_proof_kzg() {
let (verifier, claims) = _test_ring_proof::<KZG<Bls12_381>>(2usize.pow(10), 10);
let t_verify_batch = start_timer!(|| "Verify Batch KZG");
let batch_size: usize = 16;
let (verifier, claims) = _test_ring_proof::<KZG<Bls12_381>>(2usize.pow(10), batch_size);
let t_verify_batch = start_timer!(|| format!("Verify Batch KZG (batch={batch_size})"));
let (blinded_pks, proofs) = claims.into_iter().unzip();
assert!(verifier.verify_batch_kzg(proofs, blinded_pks));
end_timer!(t_verify_batch);
Expand Down
86 changes: 61 additions & 25 deletions w3f-ring-proof/src/ring_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,40 +89,76 @@ where
}
}

impl<E, Jubjub, T> RingVerifier<E::ScalarField, KZG<E>, Jubjub, T>
/// Batch verifier for KZG PCS
pub struct KzgBatchVerifier<E, J, T>
where
E: Pairing,
Jubjub: TECurveConfig<BaseField = E::ScalarField>,
J: TECurveConfig<BaseField = E::ScalarField>,
T: PlonkTranscript<E::ScalarField, KZG<E>>,
{
pub acc: KzgAccumulator<E>,
pub verifier: RingVerifier<E::ScalarField, KZG<E>, J, T>,
}

impl<E, J, T> KzgBatchVerifier<E, J, T>
where
E: Pairing,
J: TECurveConfig<BaseField = E::ScalarField>,
T: PlonkTranscript<E::ScalarField, KZG<E>>,
{
/// Push a proof in the batch
pub fn push(&mut self, proof: RingProof<E::ScalarField, KZG<E>>, result: Affine<J>) {
let (challenges, mut rng) = self.verifier.plonk_verifier.restore_challenges(
&result,
&proof,
// '1' accounts for the quotient polynomial that is aggregated together with the columns
PiopVerifier::<E::ScalarField, <KZG<E> as PCS<_>>::C, Affine<J>>::N_COLUMNS + 1,
PiopVerifier::<E::ScalarField, <KZG<E> as PCS<_>>::C, Affine<J>>::N_CONSTRAINTS,
);
let seed = self.verifier.piop_params.seed;
let seed_plus_result = (seed + result).into_affine();
let domain_at_zeta = self.verifier.piop_params.domain.evaluate(challenges.zeta);
let piop = PiopVerifier::<_, _, Affine<J>>::init(
domain_at_zeta,
self.verifier.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),
);
self.acc.accumulate(piop, proof, challenges, &mut rng);
}

/// Batch verify
pub fn verify(&self) -> bool {
self.acc.verify()
}
}

impl<E, J, T> RingVerifier<E::ScalarField, KZG<E>, J, T>
where
E: Pairing,
J: TECurveConfig<BaseField = E::ScalarField>,
T: PlonkTranscript<E::ScalarField, KZG<E>>,
{
/// Build a new batch verifier.
pub fn kzg_batch_verifier(self) -> KzgBatchVerifier<E, J, T> {
KzgBatchVerifier {
acc: KzgAccumulator::<E>::new(self.plonk_verifier.pcs_vk.clone()),
verifier: self,
}
}

// Verifies a batch of proofs against the same ring.
pub fn verify_batch_kzg(
&self,
self,
proofs: Vec<RingProof<E::ScalarField, KZG<E>>>,
results: Vec<Affine<Jubjub>>,
results: Vec<Affine<J>>,
) -> bool {
let mut acc = KzgAccumulator::<E>::new(self.plonk_verifier.pcs_vk.clone());
let mut batch = self.kzg_batch_verifier();
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::<E::ScalarField, <KZG<E> as PCS<_>>::C, Affine<Jubjub>>::N_COLUMNS + 1,
PiopVerifier::<E::ScalarField, <KZG<E> as PCS<_>>::C, Affine<Jubjub>>::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<Jubjub>>::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);
batch.push(proof, result);
}
acc.verify()
batch.verify()
}
}
Loading