Skip to main content

w3f_ring_proof/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3use ark_ff::PrimeField;
4use ark_serialize::CanonicalSerialize;
5use ark_std::rand::RngCore;
6use w3f_pcs::pcs::PCS;
7
8pub use piop::index;
9pub use w3f_plonk_common::domain::Domain;
10use w3f_plonk_common::Proof;
11
12pub use crate::piop::{params::PiopParams, FixedColumnsCommitted, ProverKey, VerifierKey};
13use crate::piop::{RingCommitments, RingEvaluations};
14
15pub mod multi_ring_batch_verifier;
16mod piop;
17pub mod ring;
18pub mod ring_prover;
19pub mod ring_verifier;
20
21pub type RingProof<F, CS> = Proof<F, CS, RingCommitments<F, <CS as PCS<F>>::C>, RingEvaluations<F>>;
22
23/// Polynomial Commitment Schemes.
24pub use w3f_pcs::pcs;
25
26#[derive(Clone)]
27pub struct ArkTranscript(ark_transcript::Transcript);
28
29impl<F: PrimeField, CS: PCS<F>> w3f_plonk_common::transcript::PlonkTranscript<F, CS>
30    for ArkTranscript
31{
32    fn _128_bit_point(&mut self, label: &'static [u8]) -> F {
33        self.0.challenge(label).read_reduce()
34    }
35
36    fn _add_serializable(&mut self, label: &'static [u8], message: &impl CanonicalSerialize) {
37        self.0.label(label);
38        self.0.append(message);
39    }
40
41    fn to_rng(mut self) -> impl RngCore {
42        self.0.challenge(b"transcript_rng")
43    }
44}
45
46impl ArkTranscript {
47    pub fn new(label: &'static [u8]) -> Self {
48        Self(ark_transcript::Transcript::new_labeled(label))
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use ark_bls12_381::Bls12_381;
55    use ark_ec::CurveGroup;
56    use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr};
57    use ark_std::ops::Mul;
58    use ark_std::rand::Rng;
59    use ark_std::{end_timer, start_timer, test_rng, UniformRand};
60    use w3f_pcs::pcs::kzg::KZG;
61
62    use w3f_plonk_common::test_helpers::random_vec;
63
64    use crate::piop::FixedColumnsCommitted;
65    use crate::ring::{Ring, RingBuilderKey};
66    use crate::ring_prover::RingProver;
67    use crate::ring_verifier::RingVerifier;
68
69    use super::*;
70
71    fn _test_ring_proof<CS: PCS<Fq> + Clone>(
72        domain_size: usize,
73        batch_size: usize,
74    ) -> (
75        RingVerifier<Fq, CS, BandersnatchConfig>,
76        Vec<(EdwardsAffine, RingProof<Fq, CS>)>,
77    ) {
78        let rng = &mut test_rng();
79
80        let (pcs_params, piop_params) = setup::<_, CS>(rng, domain_size);
81        let keyset_size = piop_params.keyset_part_size;
82        let pks = random_vec::<EdwardsAffine, _>(keyset_size, rng);
83        let (prover_key, verifier_key) = index::<_, CS, _>(&pcs_params, &piop_params, &pks);
84
85        let prover = RingProver::init(
86            prover_key.clone(),
87            piop_params.clone(),
88            0,
89            ArkTranscript::new(b"w3f-ring-proof-test"),
90        );
91
92        let ring_verifier = RingVerifier::init(
93            verifier_key,
94            piop_params.clone(),
95            ArkTranscript::new(b"w3f-ring-proof-test"),
96        );
97        let t_prove = start_timer!(|| {
98            format!("Proving {batch_size} KZG ring-proofs with plonk, domain={domain_size}, max_keys={keyset_size}")
99        });
100        let claims: Vec<(EdwardsAffine, RingProof<Fq, CS>)> = (0..batch_size)
101            .map(|_| {
102                let pk_idx = rng.gen_range(0..keyset_size);
103                let r = Fr::rand(rng);
104                let (blinded_pk, mem_proof) = prover.rerandomize_pk(pk_idx, r);
105                assert_eq!(blinded_pk, piop_params.blind_pk(pks[pk_idx], r));
106                (blinded_pk, mem_proof)
107            })
108            .collect();
109        end_timer!(t_prove);
110
111        let t_verify =
112            start_timer!(|| format!("Verifying {batch_size} KZG ring-proofs with plonk"));
113        let (blinded_pks, proofs) = claims.iter().cloned().unzip();
114        assert!(ring_verifier.verify_batch(proofs, blinded_pks));
115        end_timer!(t_verify);
116        (ring_verifier, claims)
117    }
118
119    #[test]
120    // cargo test test_ring_proof_kzg --release --features="print-trace" -- --show-output
121    fn test_ring_proof_kzg() {
122        _test_ring_proof::<KZG<Bls12_381>>(2usize.pow(9), 1);
123    }
124
125    #[test]
126    fn test_ring_proof_id() {
127        _test_ring_proof::<pcs::IdentityCommitment>(2usize.pow(10), 1);
128    }
129
130    #[test]
131    fn test_lagrangian_commitment() {
132        let rng = &mut test_rng();
133
134        let domain_size = 2usize.pow(9);
135
136        let (pcs_params, piop_params) = setup::<_, KZG<Bls12_381>>(rng, domain_size);
137        let ring_builder_key = RingBuilderKey::from_srs(&pcs_params, domain_size);
138
139        let max_keyset_size = piop_params.keyset_part_size;
140        let keyset_size: usize = rng.gen_range(0..max_keyset_size);
141        let pks = random_vec::<EdwardsAffine, _>(keyset_size, rng);
142
143        let (_, verifier_key) = index::<_, KZG<Bls12_381>, _>(&pcs_params, &piop_params, &pks);
144
145        let ring = Ring::<_, Bls12_381, _>::with_keys(&piop_params, &pks, &ring_builder_key);
146
147        let fixed_columns_committed = FixedColumnsCommitted::from_ring(&ring);
148        assert_eq!(
149            fixed_columns_committed,
150            verifier_key.fixed_columns_committed
151        );
152    }
153
154    fn setup<R: Rng, CS: PCS<Fq>>(
155        rng: &mut R,
156        domain_size: usize,
157    ) -> (CS::Params, PiopParams<Fq, BandersnatchConfig>) {
158        let setup_degree = 3 * domain_size;
159        let pcs_params = CS::setup(setup_degree, rng);
160
161        let domain = Domain::new(domain_size, true);
162        let h = EdwardsAffine::rand(rng);
163        let seed = EdwardsAffine::rand(rng);
164        let padding = EdwardsAffine::rand(rng);
165        let piop_params = PiopParams::setup(domain, h, seed, padding);
166
167        (pcs_params, piop_params)
168    }
169
170    // cargo test test_ring_proof_batch_kzg_verification --release --features="print-trace" -- --show-output
171    //
172    // Batch vs sequential verification times (ms):
173    //
174    // | proofs | sequential | batch  | speedup |
175    // |--------|------------|--------|---------|
176    // | 1      | 3.032      | 2.790  | 1.09x   |
177    // | 2      | 6.425      | 3.218  | 2.00x   |
178    // | 4      | 11.968     | 5.122  | 2.34x   |
179    // | 8      | 23.922     | 6.487  | 3.69x   |
180    // | 16     | 47.773     | 10.002 | 4.78x   |
181    // | 32     | 95.570     | 16.601 | 5.76x   |
182    // | 64     | 210.959    | 29.484 | 7.15x   |
183    // | 128    | 422.217    | 52.170 | 8.09x   |
184    // | 256    | 762.874    | 85.164 | 8.96x   |
185    //
186    // Sequential verification scales linearly with proof count.
187    // Batch verification scales sub-linearly.
188    #[test]
189    fn test_ring_proof_batch_kzg_verification() {
190        let batch_size: usize = 2;
191        let domain_size = 2usize.pow(9);
192        let (verifier, claims) = _test_ring_proof::<KZG<Bls12_381>>(domain_size, batch_size);
193        let (blinded_pks, proofs) = claims.into_iter().unzip();
194        let t_batch_verify =
195            start_timer!(|| format!("Batch-verifying {batch_size} KZG ring-proofs with plonk"));
196        assert!(verifier.verify_batch_kzg(proofs, blinded_pks));
197        end_timer!(t_batch_verify);
198    }
199
200    #[test]
201    fn test_multi_ring_batch_verify_kzg() {
202        let rng = &mut test_rng();
203        let domain_size = 2usize.pow(9);
204        let proofs_per_ring = 4;
205
206        let (pcs_params, piop_params) = setup::<_, KZG<Bls12_381>>(rng, domain_size);
207
208        // Ring A
209        let keyset_size_a = piop_params.keyset_part_size;
210        let pks_a = random_vec::<EdwardsAffine, _>(keyset_size_a, rng);
211        let (prover_key_a, verifier_key_a) =
212            index::<_, KZG<Bls12_381>, _>(&pcs_params, &piop_params, &pks_a);
213
214        // Ring B (smaller keyset)
215        let keyset_size_b = piop_params.keyset_part_size / 2;
216        let pks_b = random_vec::<EdwardsAffine, _>(keyset_size_b, rng);
217        let (prover_key_b, verifier_key_b) =
218            index::<_, KZG<Bls12_381>, _>(&pcs_params, &piop_params, &pks_b);
219
220        let mut generate_claims = |prover_key: &ProverKey<Fq, KZG<Bls12_381>, EdwardsAffine>,
221                                   pks: &[EdwardsAffine],
222                                   keyset_size: usize| {
223            (0..proofs_per_ring)
224                .map(|_| {
225                    let prover_idx = rng.gen_range(0..keyset_size);
226                    let prover = RingProver::init(
227                        prover_key.clone(),
228                        piop_params.clone(),
229                        prover_idx,
230                        ArkTranscript::new(b"w3f-ring-proof-test"),
231                    );
232                    let blinding_factor = Fr::rand(rng);
233                    let blinded_pk =
234                        (pks[prover_idx] + piop_params.h.mul(blinding_factor)).into_affine();
235                    let proof = prover.prove(blinding_factor);
236                    (blinded_pk, proof)
237                })
238                .collect::<Vec<_>>()
239        };
240
241        let claims_a = generate_claims(&prover_key_a, &pks_a, keyset_size_a);
242        let claims_b = generate_claims(&prover_key_b, &pks_b, keyset_size_b);
243
244        let verifier_a = RingVerifier::init(
245            verifier_key_a,
246            piop_params.clone(),
247            ArkTranscript::new(b"w3f-ring-proof-test"),
248        );
249        let verifier_b = RingVerifier::init(
250            verifier_key_b,
251            piop_params,
252            ArkTranscript::new(b"w3f-ring-proof-test"),
253        );
254
255        // Sanity: individual verification
256        for (result, proof) in &claims_a {
257            assert!(verifier_a.verify(proof.clone(), *result));
258        }
259        for (result, proof) in &claims_b {
260            assert!(verifier_b.verify(proof.clone(), *result));
261        }
262
263        // Multi-ring batch verification
264        use crate::multi_ring_batch_verifier::BatchVerifier;
265        let mut batch = BatchVerifier::new(
266            verifier_a.pcs_vk().clone(),
267            verifier_a.plonk_verifier.transcript_prelude.clone(),
268        );
269        for (result, proof) in claims_a {
270            batch.push(&verifier_a, proof, result);
271        }
272        for (result, proof) in claims_b {
273            batch.push(&verifier_b, proof, result);
274        }
275        assert!(batch.verify());
276    }
277}