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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
141 changes: 141 additions & 0 deletions w3f-plonk-common/src/kzg_acc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
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::rand::Rng;
use w3f_pcs::pcs::kzg::params::KzgVerifierKey;
use w3f_pcs::pcs::kzg::{AccumulatedOpening, KZG};
use w3f_pcs::pcs::PCS;

// 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(k)` at the final stage,
// that gives an asymptotic saving (thanks to Pippenger)
// at the cost of linear accumulator size.

pub struct KzgAccumulator<E: Pairing> {
// acc_points[0] = G1
acc_points: Vec<E::G1Affine>,
// acc_scalars[0] = 0
acc_scalars: Vec<E::ScalarField>,
kzg_proofs: Vec<E::G1Affine>,
randomizers: Vec<E::ScalarField>,
kzg_vk: KzgVerifierKey<E>,
}

impl<E: Pairing> KzgAccumulator<E> {
pub fn new(kzg_vk: KzgVerifierKey<E>) -> Self {
//TODO: capacity
Self {
acc_points: vec![kzg_vk.g1],
acc_scalars: vec![E::ScalarField::zero()],
kzg_proofs: vec![],
randomizers: vec![],
kzg_vk,
}
}

// `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<F, Piop, Commitments, Evaluations, R: Rng>(
&mut self,
piop: Piop,
proof: Proof<F, KZG<E>, Commitments, Evaluations>,
challenges: Challenges<F>,
rng: &mut R,
) where
F: PrimeField,
E: Pairing<ScalarField = F>,
Piop: VerifierPiop<F, <KZG<E> as PCS<F>>::C>,
Commitments: ColumnsCommited<F, <KZG<E> as PCS<F>>::C>,
Evaluations: ColumnsEvaluated<F>,
{
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);

// 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::<Vec<_>>(),
);
self.acc_points.extend(
proof
.column_commitments
.to_vec()
.iter()
.map(|c| c.0)
.collect::<Vec<_>>(),
);
self.acc_points.push(proof.quotient_commitment.clone().0);
self.acc_scalars
.extend(challenges.nus.iter().map(|nu| *nu * r).collect::<Vec<_>>()); // numbers should match here

self.acc_points.push(proof.agg_at_zeta_proof);
self.acc_scalars.push(zeta * r);
self.acc_scalars[0] -= agg_at_zeta * r;

// Openning at `z.w`
// TODO: see above
self.acc_points
.extend(lin_comm.1.iter().map(|c| c.0).collect::<Vec<_>>());
self.acc_scalars
.extend(lin_comm.0.into_iter().map(|c| c * r2).collect::<Vec<_>>());
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;

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 {
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::<E>::verify_accumulated(AccumulatedOpening { acc, proof }, &self.kzg_vk)
}
}
1 change: 1 addition & 0 deletions w3f-plonk-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion w3f-plonk-common/src/piop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub trait VerifierPiop<F: PrimeField, C: Commitment<F>> {
}

// 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<F>, Vec<C>);

fn domain_evaluated(&self) -> &EvaluatedDomain<F>;
}
3 changes: 2 additions & 1 deletion w3f-plonk-common/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{ColumnsCommited, ColumnsEvaluated, Proof};

pub struct PlonkVerifier<F: PrimeField, CS: PCS<F>, T: PlonkTranscript<F, CS>> {
// 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,
Expand Down Expand Up @@ -64,6 +64,7 @@ impl<F: PrimeField, CS: PCS<F>, T: PlonkTranscript<F, CS>> PlonkVerifier<F, CS,
.sum();

let lin_comm = piop.lin_poly_commitment(&challenges.alphas);
let lin_comm = CS::C::combine(&lin_comm.0, &lin_comm.1);

let zeta = challenges.zeta;
let zeta_omega = zeta * piop.domain_evaluated().omega();
Expand Down
56 changes: 35 additions & 21 deletions w3f-ring-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,38 @@ mod tests {

use super::*;

fn _test_ring_proof<CS: PCS<Fq>>(domain_size: usize) {
fn _test_ring_proof<CS: PCS<Fq>>(
domain_size: usize,
batch_size: usize,
) -> (
RingVerifier<Fq, CS, BandersnatchConfig>,
Vec<(EdwardsAffine, RingProof<Fq, CS>)>,
) {
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::<EdwardsAffine, _>(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<Fq, CS>)> = (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(
Expand All @@ -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]
Expand Down Expand Up @@ -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::<KZG<Bls12_381>>(2usize.pow(10));
let (verifier, claims) = _test_ring_proof::<KZG<Bls12_381>>(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::<w3f_pcs::pcs::IdentityCommitment>(2usize.pow(10));
_test_ring_proof::<w3f_pcs::pcs::IdentityCommitment>(2usize.pow(10), 1);
}
}
1 change: 1 addition & 0 deletions w3f-ring-proof/src/piop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ impl<F: PrimeField, G: AffineRepr<BaseField = F>> FixedColumns<F, G> {
}

// #[derive(CanonicalSerialize, CanonicalDeserialize)]
#[derive(Clone)]
pub struct ProverKey<F: PrimeField, CS: PCS<F>, G: AffineRepr<BaseField = F>> {
pub(crate) pcs_ck: CS::CK,
pub(crate) fixed_columns: FixedColumns<F, G>,
Expand Down
8 changes: 4 additions & 4 deletions w3f-ring-proof/src/piop/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl<F: PrimeField, C: Commitment<F>, Jubjub: TECurveConfig<BaseField = F>> Veri
.concat()
}

fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> C {
fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> (Vec<F>, Vec<C>) {
assert_eq!(agg_coeffs.len(), Self::N_CONSTRAINTS);

let inner_prod_acc = self.witness_columns_committed.inn_prod_acc.clone();
Expand All @@ -138,9 +138,9 @@ impl<F: PrimeField, C: Commitment<F>, Jubjub: TECurveConfig<BaseField = F>> 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],
)
}

Expand Down
56 changes: 55 additions & 1 deletion w3f-ring-proof/src/ring_verifier.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -71,4 +73,56 @@ where
pub fn piop_params(&self) -> &PiopParams<F, Jubjub> {
&self.piop_params
}

pub fn verify_batch(
&self,
proofs: Vec<RingProof<F, CS>>,
results: Vec<Affine<Jubjub>>,
) -> bool {
for (proof, result) in proofs.into_iter().zip(results) {
let res = self.verify(proof, result);
if !res {
return false;
}
}
true
}
}

impl<E, Jubjub, T> RingVerifier<E::ScalarField, KZG<E>, Jubjub, T>
where
E: Pairing,
Jubjub: TECurveConfig<BaseField = E::ScalarField>,
T: PlonkTranscript<E::ScalarField, KZG<E>>,
{
// Verifies a batch of proofs against the same ring.
pub fn verify_batch_kzg(
&self,
proofs: Vec<RingProof<E::ScalarField, KZG<E>>>,
results: Vec<Affine<Jubjub>>,
) -> bool {
let mut acc = KzgAccumulator::<E>::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::<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);
}
acc.verify()
}
}
Loading