Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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 = ArkTranscript>
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