Skip to main content

ark_vrf/
ring.rs

1//! # Ring VRF
2//!
3//! Anonymized ring VRF combining Pedersen VRF with the ring proof scheme derived
4//! from [CSSV22](https://eprint.iacr.org/2022/1362). Proves that a single blinded
5//! key is a member of a committed ring without revealing which one.
6//!
7//! This module is gated by the `ring` feature.
8//!
9//! ## Usage
10//!
11//! ```rust,ignore
12//! use ark_vrf::suites::bandersnatch::*;
13//! use ark_vrf::ring::Prover;
14//!
15//! const RING_SIZE: usize = 100;
16//! let prover_key_index = 3;
17//!
18//! // Create a ring of public keys
19//! let mut ring = (0..RING_SIZE)
20//!     .map(|i| {
21//!         let mut seed = [0u8; 32];
22//!         seed[..8].copy_from_slice(&i.to_le_bytes());
23//!         Secret::from_seed(seed).public().0
24//!     })
25//!     .collect::<Vec<_>>();
26//! ring[prover_key_index] = public.0;
27//!
28//! // Initialize ring parameters
29//! let params = RingProofParams::from_seed(RING_SIZE, [0x42; 32]);
30//!
31//! // Proving
32//! let prover_key = params.prover_key(&ring).unwrap();
33//! let prover = params.prover(prover_key, prover_key_index);
34//! let io = secret.vrf_io(input);
35//! let proof = secret.prove(io, b"aux data", &prover);
36//!
37//! // Verification
38//! use ark_vrf::ring::Verifier;
39//! let verifier_key = params.verifier_key(&ring).unwrap();
40//! let verifier = params.verifier(verifier_key);
41//! let result = Public::verify(io, b"aux data", &proof, &verifier);
42//!
43//! // Efficient verification with commitment
44//! let ring_commitment = verifier_key.commitment();
45//! let reconstructed_key = params.verifier_key_from_commitment(ring_commitment);
46//! ```
47
48use crate::*;
49use ark_ec::{
50    pairing::Pairing,
51    twisted_edwards::{Affine as TEAffine, TECurveConfig},
52};
53use ark_std::ops::Range;
54use pedersen::{PedersenSuite, Proof as PedersenProof};
55use utils::te_sw_map::TEMapping;
56use w3f_ring_proof as ring_proof;
57
58/// Seed hashed to curve to produce [`RingSuite::ACCUMULATOR_BASE`] in built-in suites.
59pub const ACCUMULATOR_BASE_SEED: &[u8] = b"ring-accumulator";
60
61/// Seed hashed to curve to produce [`RingSuite::PADDING`] in built-in suites.
62pub const PADDING_SEED: &[u8] = b"ring-padding";
63
64/// Ring suite.
65///
66/// This trait provides the cryptographic primitives needed for ring VRF signatures.
67/// All required bounds are expressed directly on the associated type for better ergonomics.
68pub trait RingSuite:
69    PedersenSuite<
70    Affine: AffineRepr<BaseField: ark_ff::PrimeField, Config: TECurveConfig + Clone>
71                + TEMapping<<Self::Affine as AffineRepr>::Config>,
72>
73{
74    /// Pairing type.
75    type Pairing: ark_ec::pairing::Pairing<ScalarField = BaseField<Self>>;
76
77    /// Accumulator base.
78    ///
79    /// In order for the ring-proof backend to work correctly, this is required to be
80    /// in the prime order subgroup.
81    const ACCUMULATOR_BASE: AffinePoint<Self>;
82
83    /// Padding point with unknown discrete log.
84    const PADDING: AffinePoint<Self>;
85}
86
87/// KZG Polynomial Commitment Scheme.
88pub type Kzg<S> = ring_proof::pcs::kzg::KZG<<S as RingSuite>::Pairing>;
89
90/// KZG commitment.
91pub type PcsCommitment<S> =
92    ring_proof::pcs::kzg::commitment::KzgCommitment<<S as RingSuite>::Pairing>;
93
94/// KZG Polynomial Commitment Scheme parameters.
95///
96/// Basically powers of tau SRS.
97pub type PcsParams<S> = ring_proof::pcs::kzg::urs::URS<<S as RingSuite>::Pairing>;
98
99/// Polynomial Interactive Oracle Proof (IOP) parameters.
100///
101/// Basically all the application specific parameters required to construct and
102/// verify the ring proof.
103pub type PiopParams<S> = ring_proof::PiopParams<BaseField<S>, CurveConfig<S>>;
104
105/// Ring keys commitment.
106pub type RingCommitment<S> = ring_proof::FixedColumnsCommitted<BaseField<S>, PcsCommitment<S>>;
107
108/// Ring prover key.
109pub type RingProverKey<S> = ring_proof::ProverKey<BaseField<S>, Kzg<S>, TEAffine<CurveConfig<S>>>;
110
111/// Ring verifier key.
112pub type RingVerifierKey<S> = ring_proof::VerifierKey<BaseField<S>, Kzg<S>>;
113
114/// Ring prover.
115pub type RingProver<S> = ring_proof::ring_prover::RingProver<BaseField<S>, Kzg<S>, CurveConfig<S>>;
116
117/// Ring verifier.
118pub type RingVerifier<S> =
119    ring_proof::ring_verifier::RingVerifier<BaseField<S>, Kzg<S>, CurveConfig<S>>;
120
121/// Ring proof batch verifier (KZG-based).
122pub type RingBatchVerifier<S> = ring_proof::ring_verifier::KzgBatchVerifier<
123    <S as RingSuite>::Pairing,
124    CurveConfig<S>,
125    ring_proof::ArkTranscript,
126>;
127
128/// Raw ring proof.
129///
130/// This is the primitive ring proof used in conjunction with Pedersen proof to
131/// construct the actual ring vrf proof [`Proof`].
132pub type RingBareProof<S> = ring_proof::RingProof<BaseField<S>, Kzg<S>>;
133
134/// Ring VRF proof.
135///
136/// Two-part zero-knowledge proof with signer anonymity:
137/// - `pedersen_proof`: Key commitment and VRF correctness proof
138/// - `ring_proof`: Membership proof binding the commitment to the ring
139///
140/// Deserialization via [`CanonicalDeserialize`] includes subgroup checks for
141/// curve points, so deserialized proofs are guaranteed to contain valid points.
142#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
143pub struct Proof<S: RingSuite> {
144    /// Pedersen VRF proof (key commitment and VRF correctness).
145    pub pedersen_proof: PedersenProof<S>,
146    /// Ring membership proof binding the key commitment to the ring.
147    pub ring_proof: RingBareProof<S>,
148}
149
150/// Trait for types that can generate Ring VRF proofs.
151pub trait Prover<S: RingSuite> {
152    /// Generate a proof for the given VRF I/O pairs and additional data.
153    ///
154    /// Multiple I/O pairs are delinearized into a single merged pair before proving.
155    fn prove(
156        &self,
157        ios: impl AsRef<[VrfIo<S>]>,
158        ad: impl AsRef<[u8]>,
159        prover: &RingProver<S>,
160    ) -> Proof<S>;
161}
162
163/// Trait for entities that can verify Ring VRF proofs.
164///
165/// Verifies that a VRF output was correctly derived using a secret key
166/// belonging to one of the ring's public keys, without revealing which one.
167///
168/// All curve points involved in verification (I/O pairs and proof points)
169/// are assumed to be in the prime-order subgroup. This is guaranteed when
170/// points are constructed through checked constructors ([`Input::from_affine`],
171/// [`Output::from_affine`]) or through trusted operations like [`Input::new`]
172/// (hash-to-curve) and [`Secret::vrf_io`]. Proof points are guaranteed valid
173/// when deserialized via [`CanonicalDeserialize`] (which includes subgroup
174/// checks) or produced by [`Prover::prove`].
175///
176/// Using unchecked constructors (e.g. [`Input::from_affine_unchecked`]) places
177/// the burden of subgroup validation on the caller. Passing points with
178/// cofactor components leads to undefined verification behavior.
179pub trait Verifier<S: RingSuite> {
180    /// Verify a proof for the given VRF I/O pairs and additional data.
181    ///
182    /// Multiple I/O pairs are delinearized into a single merged pair before verifying.
183    ///
184    /// Returns `Ok(())` if verification succeeds, `Err(Error::VerificationFailure)` otherwise.
185    fn verify(
186        ios: impl AsRef<[VrfIo<S>]>,
187        ad: impl AsRef<[u8]>,
188        sig: &Proof<S>,
189        verifier: &RingVerifier<S>,
190    ) -> Result<(), Error>;
191}
192
193impl<S: RingSuite> Prover<S> for Secret<S> {
194    fn prove(
195        &self,
196        ios: impl AsRef<[VrfIo<S>]>,
197        ad: impl AsRef<[u8]>,
198        ring_prover: &RingProver<S>,
199    ) -> Proof<S> {
200        use pedersen::Prover as PedersenProver;
201        let (pedersen_proof, secret_blinding) = <Self as PedersenProver<S>>::prove(self, ios, ad);
202        let ring_proof = ring_prover.prove(secret_blinding);
203        Proof {
204            pedersen_proof,
205            ring_proof,
206        }
207    }
208}
209
210impl<S: RingSuite> Verifier<S> for Public<S> {
211    fn verify(
212        ios: impl AsRef<[VrfIo<S>]>,
213        ad: impl AsRef<[u8]>,
214        proof: &Proof<S>,
215        verifier: &RingVerifier<S>,
216    ) -> Result<(), Error> {
217        use pedersen::Verifier as PedersenVerifier;
218        <Self as PedersenVerifier<S>>::verify(ios, ad, &proof.pedersen_proof)?;
219        let key_commitment = proof.pedersen_proof.key_commitment().into_te();
220        if !verifier.verify(proof.ring_proof.clone(), key_commitment) {
221            return Err(Error::VerificationFailure);
222        }
223        Ok(())
224    }
225}
226
227/// Ring proof parameters.
228///
229/// Contains the cryptographic parameters needed for ring proof generation and verification:
230/// - `pcs`: Polynomial Commitment Scheme parameters (KZG setup)
231/// - `piop`: Polynomial Interactive Oracle Proof parameters
232#[derive(Clone)]
233pub struct RingProofParams<S: RingSuite> {
234    /// PCS parameters.
235    pub pcs: PcsParams<S>,
236    /// PIOP parameters.
237    pub piop: PiopParams<S>,
238}
239
240pub(crate) fn piop_params<S: RingSuite>(domain_size: usize) -> PiopParams<S> {
241    PiopParams::<S>::setup(
242        ring_proof::Domain::new(domain_size, true),
243        S::BLINDING_BASE.into_te(),
244        S::ACCUMULATOR_BASE.into_te(),
245        S::PADDING.into_te(),
246    )
247}
248
249impl<S: RingSuite> RingProofParams<S> {
250    /// Construct deterministic ring proof params for the given ring size.
251    ///
252    /// Creates parameters using a transcript-based RNG seeded with `seed`.
253    pub fn from_seed(ring_size: usize, seed: [u8; 32]) -> Self {
254        let mut t = S::Transcript::new(S::SUITE_ID);
255        t.absorb_raw(&seed);
256        let mut rng = t.to_rng();
257        Self::from_rand(ring_size, &mut rng)
258    }
259
260    /// Construct random ring proof params for the given ring size.
261    ///
262    /// Generates a new KZG setup with sufficient degree to support the specified ring size.
263    pub fn from_rand(ring_size: usize, rng: &mut impl ark_std::rand::RngCore) -> Self {
264        use ring_proof::pcs::PCS;
265        let max_degree = pcs_domain_size::<S>(ring_size) - 1;
266        let pcs_params = Kzg::<S>::setup(max_degree, rng);
267        Self::from_pcs_params(ring_size, pcs_params).expect("PCS params is correct")
268    }
269
270    /// Construct ring proof params from existing KZG setup.
271    ///
272    /// Truncates the setup if larger than needed, or returns an error if it is
273    /// insufficient for the specified ring size.
274    pub fn from_pcs_params(ring_size: usize, mut pcs_params: PcsParams<S>) -> Result<Self, Error> {
275        let pcs_domain_size = pcs_domain_size::<S>(ring_size);
276        if pcs_params.powers_in_g1.len() < pcs_domain_size || pcs_params.powers_in_g2.len() < 2 {
277            return Err(Error::InvalidData);
278        }
279        // Keep only the required powers of tau
280        pcs_params.powers_in_g1.truncate(pcs_domain_size);
281        pcs_params.powers_in_g2.truncate(2);
282        let piop_domain_size = piop_domain_size::<S>(ring_size);
283        Ok(Self {
284            pcs: pcs_params,
285            piop: piop_params::<S>(piop_domain_size),
286        })
287    }
288
289    /// The max ring size these parameters are able to handle.
290    #[inline(always)]
291    pub fn max_ring_size(&self) -> usize {
292        self.piop.keyset_part_size
293    }
294
295    /// Create a prover key for the given ring of public keys.
296    ///
297    /// Returns `Error::InvalidData` if `pks` exceeds [`Self::max_ring_size`].
298    pub fn prover_key(&self, pks: &[AffinePoint<S>]) -> Result<RingProverKey<S>, Error> {
299        if pks.len() > self.max_ring_size() {
300            return Err(Error::InvalidData);
301        }
302        let pks = TEMapping::to_te_slice(pks);
303        Ok(ring_proof::index(&self.pcs, &self.piop, &pks).0)
304    }
305
306    /// Create a prover instance for a specific position in the ring.
307    pub fn prover(&self, prover_key: RingProverKey<S>, key_index: usize) -> RingProver<S> {
308        RingProver::<S>::init(
309            prover_key,
310            self.piop.clone(),
311            key_index,
312            ring_proof::ArkTranscript::new(const { &S::SUITE_ID.to_bytes() }),
313        )
314    }
315
316    /// Create a verifier key for the given ring of public keys.
317    ///
318    /// Returns `Error::InvalidData` if `pks` exceeds [`Self::max_ring_size`].
319    pub fn verifier_key(&self, pks: &[AffinePoint<S>]) -> Result<RingVerifierKey<S>, Error> {
320        if pks.len() > self.max_ring_size() {
321            return Err(Error::InvalidData);
322        }
323        let pks = TEMapping::to_te_slice(pks);
324        Ok(ring_proof::index(&self.pcs, &self.piop, &pks).1)
325    }
326
327    /// Create a verifier key from a precomputed ring commitment.
328    ///
329    /// The commitment can be obtained from an existing verifier key via
330    /// [`RingVerifierKey::commitment`].
331    pub fn verifier_key_from_commitment(
332        &self,
333        commitment: RingCommitment<S>,
334    ) -> RingVerifierKey<S> {
335        use ring_proof::pcs::PcsParams;
336        RingVerifierKey::<S>::from_commitment_and_kzg_vk(commitment, self.pcs.raw_vk())
337    }
338
339    /// Create a builder for incremental construction of the verifier key.
340    pub fn verifier_key_builder(&self) -> (VerifierKeyBuilder<S>, RingBuilderPcsParams<S>) {
341        type RingBuilderKey<S> =
342            ring_proof::ring::RingBuilderKey<BaseField<S>, <S as RingSuite>::Pairing>;
343        let piop_domain_size = piop_domain_size::<S>(self.piop.keyset_part_size);
344        let builder_key = RingBuilderKey::<S>::from_srs(&self.pcs, piop_domain_size);
345        let builder_pcs_params = RingBuilderPcsParams(builder_key.lis_in_g1);
346        let builder = VerifierKeyBuilder::new(self, &builder_pcs_params);
347        (builder, builder_pcs_params)
348    }
349
350    /// Create a verifier instance from a verifier key.
351    pub fn verifier(&self, verifier_key: RingVerifierKey<S>) -> RingVerifier<S> {
352        RingVerifier::<S>::init(
353            verifier_key,
354            self.piop.clone(),
355            ring_proof::ArkTranscript::new(const { &S::SUITE_ID.to_bytes() }),
356        )
357    }
358
359    /// Create a verifier instance without requiring the full parameters.
360    ///
361    /// Computes necessary PIOP parameters on-the-fly from the ring size rather
362    /// than reusing the ones stored in `self`.
363    pub fn verifier_no_context(
364        verifier_key: RingVerifierKey<S>,
365        ring_size: usize,
366    ) -> RingVerifier<S> {
367        RingVerifier::<S>::init(
368            verifier_key,
369            piop_params::<S>(piop_domain_size::<S>(ring_size)),
370            ring_proof::ArkTranscript::new(const { &S::SUITE_ID.to_bytes() }),
371        )
372    }
373
374    /// Get the padding point.
375    ///
376    /// This is a point of unknown dlog that can be used in place of any key during
377    /// ring construction.
378    #[inline(always)]
379    pub const fn padding_point() -> AffinePoint<S> {
380        S::PADDING
381    }
382}
383
384impl<S: RingSuite> CanonicalSerialize for RingProofParams<S> {
385    fn serialize_with_mode<W: ark_serialize::Write>(
386        &self,
387        mut writer: W,
388        compress: ark_serialize::Compress,
389    ) -> Result<(), ark_serialize::SerializationError> {
390        self.pcs.serialize_with_mode(&mut writer, compress)
391    }
392
393    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
394        self.pcs.serialized_size(compress)
395    }
396}
397
398impl<S: RingSuite> CanonicalDeserialize for RingProofParams<S> {
399    fn deserialize_with_mode<R: ark_serialize::Read>(
400        mut reader: R,
401        compress: ark_serialize::Compress,
402        validate: ark_serialize::Validate,
403    ) -> Result<Self, ark_serialize::SerializationError> {
404        let pcs_params = <PcsParams<S> as CanonicalDeserialize>::deserialize_with_mode(
405            &mut reader,
406            compress,
407            validate,
408        )?;
409        let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_params.powers_in_g1.len());
410        Ok(Self {
411            pcs: pcs_params,
412            piop: piop_params::<S>(piop_domain_size),
413        })
414    }
415}
416
417impl<S: RingSuite> ark_serialize::Valid for RingProofParams<S> {
418    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
419        self.pcs.check()
420    }
421}
422
423/// Information required for incremental ring construction.
424///
425/// Basically the SRS in Lagrangian form.
426/// Can be constructed via the `PcsParams::ck_with_lagrangian()` method.
427#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
428pub struct RingBuilderPcsParams<S: RingSuite>(pub Vec<G1Affine<S>>);
429
430// Under construction ring commitment.
431type PartialRingCommitment<S> =
432    ring_proof::ring::Ring<BaseField<S>, <S as RingSuite>::Pairing, CurveConfig<S>>;
433
434type RawVerifierKey<S> = <PcsParams<S> as ring_proof::pcs::PcsParams>::RVK;
435
436/// Builder for incremental construction of ring verifier keys.
437///
438/// Allows constructing a verifier key by adding public keys in batches,
439/// which is useful for large rings or memory-constrained environments.
440#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
441pub struct VerifierKeyBuilder<S: RingSuite> {
442    partial: PartialRingCommitment<S>,
443    raw_vk: RawVerifierKey<S>,
444}
445
446/// Pairing G1 affine point type.
447pub type G1Affine<S> = <<S as RingSuite>::Pairing as Pairing>::G1Affine;
448/// Pairing G2 affine point type.
449pub type G2Affine<S> = <<S as RingSuite>::Pairing as Pairing>::G2Affine;
450
451/// Trait for accessing Structured Reference String entries in Lagrangian basis.
452///
453/// Provides access to precomputed SRS elements needed for efficient ring operations.
454pub trait SrsLookup<S: RingSuite> {
455    /// Look up a range of SRS elements. Returns `None` if the range is out of bounds.
456    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>>;
457}
458
459impl<S: RingSuite, F> SrsLookup<S> for F
460where
461    F: Fn(Range<usize>) -> Option<Vec<G1Affine<S>>>,
462{
463    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>> {
464        self(range)
465    }
466}
467
468impl<S: RingSuite> SrsLookup<S> for &RingBuilderPcsParams<S> {
469    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>> {
470        if range.end > self.0.len() {
471            return None;
472        }
473        Some(self.0[range].to_vec())
474    }
475}
476
477impl<S: RingSuite> VerifierKeyBuilder<S> {
478    /// Create a new empty ring verifier key builder.
479    pub fn new(params: &RingProofParams<S>, lookup: impl SrsLookup<S>) -> Self {
480        use ring_proof::pcs::PcsParams;
481        let lookup = |range: Range<usize>| lookup.lookup(range).ok_or(());
482        let raw_vk = params.pcs.raw_vk();
483        let partial =
484            PartialRingCommitment::<S>::empty(&params.piop, lookup, raw_vk.g1.into_group());
485        VerifierKeyBuilder { partial, raw_vk }
486    }
487
488    /// Get the number of remaining slots available in the ring.
489    #[inline(always)]
490    pub fn free_slots(&self) -> usize {
491        self.partial.max_keys - self.partial.curr_keys
492    }
493
494    /// Add public keys to the ring being built.
495    ///
496    /// Returns `Err(available_slots)` if there's not enough space, or
497    /// `Err(usize::MAX)` if the SRS lookup fails.
498    pub fn append(
499        &mut self,
500        pks: &[AffinePoint<S>],
501        lookup: impl SrsLookup<S>,
502    ) -> Result<(), usize> {
503        let avail_slots = self.free_slots();
504        if avail_slots < pks.len() {
505            return Err(avail_slots);
506        }
507        // Currently `ring-proof` backend panics if lookup fails.
508        // This workaround makes lookup failures a bit less harsh.
509        let segment = lookup
510            .lookup(self.partial.curr_keys..self.partial.curr_keys + pks.len())
511            .ok_or(usize::MAX)?;
512        let lookup = |range: Range<usize>| {
513            debug_assert_eq!(segment.len(), range.len());
514            Ok(segment.clone())
515        };
516        let pks = TEMapping::to_te_slice(pks);
517        self.partial.append(&pks, lookup);
518        Ok(())
519    }
520
521    /// Complete the building process and create the verifier key.
522    pub fn finalize(self) -> RingVerifierKey<S> {
523        RingVerifierKey::<S>::from_ring_and_kzg_vk(&self.partial, self.raw_vk)
524    }
525}
526
527type RingPreparedBatchItem<S> =
528    ring_proof::ring_verifier::PreparedBatchItem<<S as RingSuite>::Pairing, CurveConfig<S>>;
529
530/// Pre-processed data for a single ring proof awaiting batch verification.
531pub struct BatchItem<S: RingSuite> {
532    ring: RingPreparedBatchItem<S>,
533    pedersen: pedersen::BatchItem<S>,
534}
535
536/// Batch verifier for ring VRF proofs.
537///
538/// Collects multiple ring proofs and verifies them together, amortizing the
539/// cost of pairing checks and multi-scalar multiplications.
540///
541/// The same subgroup membership assumptions as [`Verifier`] apply to all
542/// points fed into the batch (I/O pairs and proof points).
543pub struct BatchVerifier<S: RingSuite> {
544    ring_batch: RingBatchVerifier<S>,
545    pedersen_batch: pedersen::BatchVerifier<S>,
546}
547
548impl<S: RingSuite> BatchVerifier<S> {
549    /// Create a new batch verifier from a ring verifier instance.
550    pub fn new(ring_verifier: RingVerifier<S>) -> Self {
551        Self {
552            ring_batch: ring_verifier.kzg_batch_verifier(),
553            pedersen_batch: pedersen::BatchVerifier::new(),
554        }
555    }
556
557    /// Prepare a proof for deferred batch verification.
558    ///
559    /// Performs the cheap per-proof work (hashing, transcript setup) without
560    /// the expensive pairing and MSM checks.
561    pub fn prepare(
562        &self,
563        ios: impl AsRef<[VrfIo<S>]>,
564        ad: impl AsRef<[u8]>,
565        proof: &Proof<S>,
566    ) -> BatchItem<S> {
567        let pedersen = pedersen::BatchVerifier::prepare(ios, ad, &proof.pedersen_proof);
568        let key_commitment = proof.pedersen_proof.key_commitment().into_te();
569        let ring = self
570            .ring_batch
571            .prepare(proof.ring_proof.clone(), key_commitment);
572        BatchItem { ring, pedersen }
573    }
574
575    /// Push a previously prepared item into the batch.
576    pub fn push_prepared(&mut self, item: BatchItem<S>) {
577        self.pedersen_batch.push_prepared(item.pedersen);
578        self.ring_batch.push_prepared(item.ring);
579    }
580
581    /// Prepare and push a proof in one step.
582    pub fn push(&mut self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) {
583        let prepared = self.prepare(ios, ad, proof);
584        self.push_prepared(prepared);
585    }
586
587    /// Verify all collected proofs in a single batch.
588    ///
589    /// Checks both the Pedersen proofs (via MSM) and the ring proofs (via pairing).
590    /// Returns `Ok(())` if all proofs verify, `Err(VerificationFailure)` otherwise.
591    pub fn verify(&self) -> Result<(), Error> {
592        self.pedersen_batch.verify()?;
593        self.ring_batch
594            .verify()
595            .then_some(())
596            .ok_or(Error::VerificationFailure)
597    }
598}
599
600/// Type aliases for the given ring suite.
601#[macro_export]
602macro_rules! ring_suite_types {
603    ($suite:ident) => {
604        #[allow(dead_code)]
605        pub type PcsParams = $crate::ring::PcsParams<$suite>;
606        #[allow(dead_code)]
607        pub type PiopParams = $crate::ring::PiopParams<$suite>;
608        #[allow(dead_code)]
609        pub type RingProofParams = $crate::ring::RingProofParams<$suite>;
610        #[allow(dead_code)]
611        pub type RingProverKey = $crate::ring::RingProverKey<$suite>;
612        #[allow(dead_code)]
613        pub type RingVerifierKey = $crate::ring::RingVerifierKey<$suite>;
614        #[allow(dead_code)]
615        pub type RingCommitment = $crate::ring::RingCommitment<$suite>;
616        #[allow(dead_code)]
617        pub type RingProver = $crate::ring::RingProver<$suite>;
618        #[allow(dead_code)]
619        pub type RingVerifier = $crate::ring::RingVerifier<$suite>;
620        #[allow(dead_code)]
621        pub type RingProof = $crate::ring::Proof<$suite>;
622        #[allow(dead_code)]
623        pub type RingVerifierKeyBuilder = $crate::ring::VerifierKeyBuilder<$suite>;
624        #[allow(dead_code)]
625        pub type RingBatchItem = $crate::ring::BatchItem<$suite>;
626        #[allow(dead_code)]
627        pub type RingBatchVerifier = $crate::ring::BatchVerifier<$suite>;
628    };
629}
630
631/// Domain size conversion utilities
632///
633/// The ring proof system operates with three related size parameters:
634///
635/// 1. `min_ring_size`: Number of keys that the ring should accomodate (user-facing parameter)
636/// 2. `max_ring_size`: Max number of keys that the ring can accomodate
637/// 3. `piop_domain_size`: Size of the PIOP (Polynomial IOP) domain
638/// 4. `pcs_domain_size`: Size of the PCS (Polynomial Commitment Scheme) domain
639///
640/// Relationships:
641///   piop_domain_size = (ring_size + PIOP_OVERHEAD).next_power_of_two()
642///   pcs_domain_size  = 3 * piop_domain_size + 1
643///   max_ring_size    = piop_domain_size - PIOP_OVERHEAD
644///
645/// where PIOP_OVERHEAD = 4 + MODULUS_BIT_SIZE accounts for:
646///   - 3 points for zero-knowledge blinding
647///   - 1 extra point used internally by the PIOP
648///   - MODULUS_BIT_SIZE bits for blinding factor
649///
650/// Note: Multiple ring sizes map to the same domain sizes due to power-of-2 rounding.
651/// For example, ring sizes 1-254 (with 254-bit scalar) all map to piop_domain_size=512
652/// and pcs_domain_size=1537.
653pub mod dom_utils {
654    use super::*;
655
656    /// Returns the actual ring capacity for a given minimum size requirement.
657    ///
658    /// Because domain sizes round up to powers of 2, allocating for `min_ring_size`
659    /// keys typically provides capacity for more. This function returns that actual
660    /// capacity: the largest ring size that uses the same domain as `min_ring_size`.
661    ///
662    /// Always returns a value `>= min_ring_size`.
663    pub const fn max_ring_size<S: Suite>(min_ring_size: usize) -> usize {
664        max_ring_size_from_piop_domain_size::<S>(piop_domain_size::<S>(min_ring_size))
665    }
666
667    /// PIOP overhead: accounts for 3 ZK blinding points + 1 internal point + scalar field bits.
668    pub const fn piop_overhead<S: Suite>() -> usize {
669        4 + ScalarField::<S>::MODULUS_BIT_SIZE as usize
670    }
671
672    /// PIOP domain size required to support the given ring size.
673    ///
674    /// Returns the smallest power of 2 that can accommodate `min_ring_capactity` members.
675    /// This is the domain size used for polynomial operations in the ring proof and
676    /// already accounts for the PIOP overhead.
677    pub const fn piop_domain_size<S: Suite>(min_ring_capacity: usize) -> usize {
678        (min_ring_capacity + piop_overhead::<S>()).next_power_of_two()
679    }
680
681    /// Maximum ring size supported by a given PIOP domain size.
682    ///
683    /// Returns the largest ring that fits in the domain.
684    pub const fn max_ring_size_from_piop_domain_size<S: Suite>(piop_domain_size: usize) -> usize {
685        piop_domain_size - piop_overhead::<S>()
686    }
687
688    /// PCS domain size required to support the given ring size.
689    ///
690    /// Returns `3 * piop_domain_size + 1`. This is the number of G1 elements required
691    /// in the SRS (powers of tau) for the prover. The verifier only needs the PIOP domain size.
692    pub const fn pcs_domain_size<S: Suite>(min_ring_size: usize) -> usize {
693        pcs_domain_size_from_piop_domain_size(piop_domain_size::<S>(min_ring_size))
694    }
695
696    /// PCS domain size for a given PIOP domain size.
697    ///
698    /// Returns `3 * piop_domain_size + 1`.
699    pub const fn pcs_domain_size_from_piop_domain_size(piop_domain_size: usize) -> usize {
700        3 * piop_domain_size + 1
701    }
702
703    /// PIOP domain size extracted from a PCS domain size.
704    ///
705    /// Recovers the PIOP domain size from a PCS domain size. The ilog2 ensures we get
706    /// a valid power of 2 even if the input wasn't properly constructed.
707    pub const fn piop_domain_size_from_pcs_domain_size(pcs_domain_size: usize) -> usize {
708        1 << ((pcs_domain_size - 1) / 3).ilog2()
709    }
710
711    /// Maximum ring size supported by a given PCS domain size.
712    ///
713    /// Composes `piop_domain_size_from_pcs_domain_size` and `max_ring_size_from_piop_domain_size`.
714    pub const fn max_ring_size_from_pcs_domain_size<S: Suite>(pcs_domain_size: usize) -> usize {
715        let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_domain_size);
716        max_ring_size_from_piop_domain_size::<S>(piop_domain_size)
717    }
718}
719pub use dom_utils::*;
720
721#[cfg(test)]
722pub(crate) mod testing {
723    use super::*;
724    use crate::pedersen;
725    use crate::testing::{self as common, CheckPoint, TEST_SEED};
726    use ark_ec::{
727        short_weierstrass::{Affine as SWAffine, SWCurveConfig},
728        twisted_edwards::{Affine as TEAffine, TECurveConfig},
729    };
730
731    pub const TEST_RING_SIZE: usize = 8;
732
733    const MAX_AD_LEN: usize = 100;
734
735    fn find_complement_point<C: SWCurveConfig>() -> SWAffine<C> {
736        use ark_ff::{One, Zero};
737        assert!(!C::cofactor_is_one());
738        let mut x = C::BaseField::zero();
739        loop {
740            if let Some(p) = SWAffine::get_point_from_x_unchecked(x, false)
741                .filter(|p| !p.is_in_correct_subgroup_assuming_on_curve())
742            {
743                return p;
744            }
745            x += C::BaseField::one();
746        }
747    }
748
749    pub trait FindAccumulatorBase<S: Suite>: Sized {
750        const IN_PRIME_ORDER_SUBGROUP: bool;
751        fn find_accumulator_base(data: &[u8]) -> Option<Self>;
752    }
753
754    impl<S, C> FindAccumulatorBase<S> for SWAffine<C>
755    where
756        C: SWCurveConfig,
757        S: Suite<Affine = Self>,
758    {
759        const IN_PRIME_ORDER_SUBGROUP: bool = false;
760
761        fn find_accumulator_base(data: &[u8]) -> Option<Self> {
762            let p = S::data_to_point(data)?;
763            let c = find_complement_point();
764            let res = (p + c).into_affine();
765            debug_assert!(!res.is_in_correct_subgroup_assuming_on_curve());
766            Some(res)
767        }
768    }
769
770    impl<S, C> FindAccumulatorBase<S> for TEAffine<C>
771    where
772        C: TECurveConfig,
773        S: Suite<Affine = Self>,
774    {
775        const IN_PRIME_ORDER_SUBGROUP: bool = true;
776
777        fn find_accumulator_base(data: &[u8]) -> Option<Self> {
778            let res = S::data_to_point(data)?;
779            debug_assert!(res.is_in_correct_subgroup_assuming_on_curve());
780            Some(res)
781        }
782    }
783
784    struct BatchItem<S: RingSuite> {
785        io: VrfIo<S>,
786        ad: Vec<u8>,
787        proof: Proof<S>,
788    }
789
790    impl<S: RingSuite> BatchItem<S> {
791        fn new(
792            secret: &Secret<S>,
793            prover: &RingProver<S>,
794            rng: &mut dyn ark_std::rand::RngCore,
795        ) -> Self {
796            let input = Input::from_affine_unchecked(common::random_val(Some(rng)));
797            let io = secret.vrf_io(input);
798            let ad_len = common::random_val::<usize>(Some(rng)) % (MAX_AD_LEN + 1);
799            let ad = common::random_vec(ad_len, Some(rng));
800            let proof = secret.prove(io, &ad, prover);
801            Self { io, ad, proof }
802        }
803    }
804
805    #[allow(unused)]
806    pub fn prove_verify<S: RingSuite>() {
807        let rng = &mut ark_std::test_rng();
808        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
809
810        let secret = Secret::<S>::from_seed(TEST_SEED);
811        let public = secret.public();
812
813        let mut pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
814        let prover_idx = 3;
815        pks[prover_idx] = public.0;
816
817        let prover_key = params.prover_key(&pks).unwrap();
818        let prover = params.prover(prover_key, prover_idx);
819
820        let item = BatchItem::<S>::new(&secret, &prover, rng);
821
822        let verifier_key = params.verifier_key(&pks).unwrap();
823        let verifier = params.verifier(verifier_key);
824        let result = Public::verify(item.io, &item.ad, &item.proof, &verifier);
825        assert!(result.is_ok());
826    }
827
828    /// N=3 multi proof via ring prove/verify.
829    #[allow(unused)]
830    pub fn prove_verify_multi<S: RingSuite>() {
831        use ring::{Prover, Verifier};
832
833        let rng = &mut ark_std::test_rng();
834        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
835
836        let secret = Secret::<S>::from_seed(TEST_SEED);
837        let public = secret.public();
838
839        let mut pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
840        let prover_idx = 3;
841        pks[prover_idx] = public.0;
842
843        let prover_key = params.prover_key(&pks).unwrap();
844        let prover = params.prover(prover_key, prover_idx);
845
846        let verifier_key = params.verifier_key(&pks).unwrap();
847        let verifier = params.verifier(verifier_key);
848
849        let mut ios: Vec<VrfIo<S>> = (0..3u8)
850            .map(|i| {
851                let input = Input::new(&[i + 1]).unwrap();
852                secret.vrf_io(input)
853            })
854            .collect();
855        ios.push(VrfIo {
856            input: Input(S::Affine::generator()),
857            output: Output(public.0),
858        });
859
860        let proof = secret.prove(&ios[..], b"bar", &prover);
861        assert!(Public::verify(&ios[..], b"bar", &proof, &verifier).is_ok());
862
863        // Tamper: wrong output on ios[1]
864        let mut bad_ios = ios.clone();
865        bad_ios[1].output = secret.output(ios[0].input);
866        assert!(Public::verify(&bad_ios[..], b"bar", &proof, &verifier).is_err());
867
868        // Tamper: wrong ad
869        assert!(Public::verify(&ios[..], b"baz", &proof, &verifier).is_err());
870    }
871
872    #[allow(unused)]
873    pub fn prove_verify_batch<S: RingSuite>() {
874        use rayon::prelude::*;
875
876        const BATCH_SIZE: usize = 3 * TEST_RING_SIZE;
877
878        let rng = &mut ark_std::test_rng();
879        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
880
881        let secret = Secret::<S>::from_seed(TEST_SEED);
882        let public = secret.public();
883
884        let mut pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
885        let prover_idx = 3;
886        pks[prover_idx] = public.0;
887
888        let prover_key = params.prover_key(&pks).unwrap();
889        let prover = params.prover(prover_key, prover_idx);
890
891        // Generate proofs in parallel
892        let batch: Vec<_> = (0..BATCH_SIZE)
893            .into_par_iter()
894            .map_init(ark_std::test_rng, |rng, _| {
895                BatchItem::<S>::new(&secret, &prover, rng)
896            })
897            .collect();
898
899        let verifier_key = params.verifier_key(&pks).unwrap();
900        let verifier = params.verifier(verifier_key);
901
902        // Batch verify all proofs
903        let mut batch_verifier = BatchVerifier::<S>::new(verifier);
904        let res = batch_verifier.verify();
905        assert!(res.is_ok());
906
907        // Prove incrementally constructed batches
908        for item in batch.iter() {
909            batch_verifier.push(item.io, &item.ad, &item.proof);
910            let res = batch_verifier.verify();
911            assert!(res.is_ok());
912        }
913
914        println!("Batch size = {BATCH_SIZE}");
915
916        println!("============================================================");
917
918        let verifier_key = params.verifier_key(&pks).unwrap();
919        let verifier = params.verifier(verifier_key);
920        let mut batch_verifier = BatchVerifier::<S>::new(verifier);
921        let start = std::time::Instant::now();
922        common::timed("Proofs push", || {
923            for item in batch.iter() {
924                batch_verifier.push(item.io, &item.ad, &item.proof);
925            }
926        });
927        common::timed("Unprepared batch verification", || batch_verifier.verify());
928        println!("Total time: {:?}", start.elapsed());
929
930        println!("============================================================");
931
932        let verifier_key = params.verifier_key(&pks).unwrap();
933        let verifier = params.verifier(verifier_key);
934        let mut batch_verifier = BatchVerifier::<S>::new(verifier);
935        let start = std::time::Instant::now();
936        let prepared = common::timed("Proofs prepare", || {
937            batch
938                .par_iter()
939                .map(|item| batch_verifier.prepare(item.io, &item.ad, &item.proof))
940                .collect::<Vec<_>>()
941        });
942        common::timed("Proofs push prepared", || {
943            prepared
944                .into_iter()
945                .for_each(|p| batch_verifier.push_prepared(p))
946        });
947        common::timed("Prepared batch verification", || batch_verifier.verify());
948        println!("Total time: {:?}", start.elapsed());
949    }
950
951    #[allow(unused)]
952    pub fn padding_check<S: RingSuite>()
953    where
954        AffinePoint<S>: CheckPoint,
955    {
956        // Check that point has been computed using the magic spell.
957        assert_eq!(S::PADDING, S::data_to_point(PADDING_SEED).unwrap());
958
959        // Check that the point is on curve.
960        assert!(S::PADDING.check(true).is_ok());
961    }
962
963    #[allow(unused)]
964    pub fn accumulator_base_check<S: RingSuite>()
965    where
966        AffinePoint<S>: FindAccumulatorBase<S> + CheckPoint,
967    {
968        // Check that point has been computed using the magic spell.
969        assert_eq!(
970            S::ACCUMULATOR_BASE,
971            AffinePoint::<S>::find_accumulator_base(ACCUMULATOR_BASE_SEED).unwrap()
972        );
973
974        // SW form requires accumulator seed to be outside prime order subgroup.
975        // TE form requires accumulator seed to be in prime order subgroup.
976        let in_prime_subgroup = <AffinePoint<S> as FindAccumulatorBase<S>>::IN_PRIME_ORDER_SUBGROUP;
977        assert!(S::ACCUMULATOR_BASE.check(in_prime_subgroup).is_ok());
978    }
979
980    #[allow(unused)]
981    pub fn verifier_key_builder<S: RingSuite>() {
982        use crate::testing::{random_val, random_vec};
983
984        let rng = &mut ark_std::test_rng();
985        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
986
987        let secret = Secret::<S>::from_seed(TEST_SEED);
988        let public = secret.public();
989        let input = Input::from_affine_unchecked(common::random_val(Some(rng)));
990        let io = secret.vrf_io(input);
991
992        let ring_size = params.max_ring_size();
993        let prover_idx = random_val::<usize>(Some(rng)) % ring_size;
994        let mut pks = random_vec::<AffinePoint<S>>(ring_size, Some(rng));
995        pks[prover_idx] = public.0;
996
997        let prover_key = params.prover_key(&pks).unwrap();
998        let prover = params.prover(prover_key, prover_idx);
999        let proof = secret.prove(io, b"foo", &prover);
1000
1001        // Incremental ring verifier key construction
1002        let (mut vk_builder, lookup) = params.verifier_key_builder();
1003        assert_eq!(vk_builder.free_slots(), pks.len());
1004
1005        let extra_pk = random_val::<AffinePoint<S>>(Some(rng));
1006        assert_eq!(
1007            vk_builder.append(&[extra_pk], |_| None).unwrap_err(),
1008            usize::MAX
1009        );
1010
1011        while !pks.is_empty() {
1012            let chunk_len = 1 + random_val::<usize>(Some(rng)) % 5;
1013            let chunk = pks.drain(..pks.len().min(chunk_len)).collect::<Vec<_>>();
1014            vk_builder.append(&chunk[..], &lookup).unwrap();
1015            assert_eq!(vk_builder.free_slots(), pks.len());
1016        }
1017        // No more space left
1018        let extra_pk = random_val::<AffinePoint<S>>(Some(rng));
1019        assert_eq!(vk_builder.append(&[extra_pk], &lookup).unwrap_err(), 0);
1020        let verifier_key = vk_builder.finalize();
1021        let verifier = params.verifier(verifier_key);
1022        let result = Public::verify(io, b"foo", &proof, &verifier);
1023        assert!(result.is_ok());
1024    }
1025
1026    pub fn domain_size_conversions<S: RingSuite>() {
1027        let overhead = piop_overhead::<S>();
1028
1029        for ring_size in [1, 10, 200, 300, 500, 1000, 2000, 10000] {
1030            let piop_dom_size = piop_domain_size::<S>(ring_size);
1031            let pcs_dom_size = pcs_domain_size::<S>(ring_size);
1032            let max_ring_size = max_ring_size_from_piop_domain_size::<S>(piop_dom_size);
1033
1034            assert!(piop_dom_size.is_power_of_two());
1035            assert_eq!(pcs_dom_size, 3 * piop_dom_size + 1);
1036
1037            // piop_domain_size must fit ring_size + overhead
1038            assert!(piop_dom_size >= ring_size + overhead);
1039            // piop_domain_size is the smallest power of 2 that fits
1040            assert!(piop_dom_size / 2 < ring_size + overhead);
1041            // piop_dom_size is sufficient for max_ring_size
1042            assert_eq!(piop_dom_size, piop_domain_size::<S>(max_ring_size));
1043            // ring_size <= max_ring_size for the computed domain
1044            assert!(ring_size <= max_ring_size);
1045
1046            // max_ring_size() helper equivalence
1047            assert_eq!(dom_utils::max_ring_size::<S>(ring_size), max_ring_size);
1048            // max_ring_size() is idempotent
1049            assert_eq!(dom_utils::max_ring_size::<S>(max_ring_size), max_ring_size);
1050
1051            // Round-trip
1052            let piop_dom_rt = piop_domain_size_from_pcs_domain_size(pcs_dom_size);
1053            assert_eq!(piop_dom_size, piop_dom_rt);
1054            let pcs_dom_rt = pcs_domain_size_from_piop_domain_size(piop_dom_rt);
1055            assert_eq!(pcs_dom_size, pcs_dom_rt);
1056
1057            let max_ring_from_pcs = max_ring_size_from_pcs_domain_size::<S>(pcs_dom_size);
1058            assert_eq!(max_ring_size, max_ring_from_pcs);
1059
1060            // max_ring + 1 should require a larger piop domain
1061            let next_piop = piop_domain_size::<S>(max_ring_size + 1);
1062            assert!(next_piop > piop_dom_size,);
1063            assert!(next_piop.is_power_of_two());
1064        }
1065
1066        // Test inverse with arbitrary PCS values (not necessarily properly constructed)
1067        // The inverse function should recover the largest valid piop that fits
1068        for pcs_dom_size in [1 << 11, 1 << 12, 1 << 14, 1 << 16] {
1069            let piop_dom = piop_domain_size_from_pcs_domain_size(pcs_dom_size);
1070            let max_ring = max_ring_size_from_pcs_domain_size::<S>(pcs_dom_size);
1071
1072            assert!(piop_dom.is_power_of_two());
1073            // piop should satisfy: 3 * piop + 1 <= pcs
1074            assert!(3 * piop_dom < pcs_dom_size);
1075            // but 3 * (2 * piop) + 1 > pcs (piop is maximal)
1076            assert!(3 * (2 * piop_dom) + 1 > pcs_dom_size);
1077            // max_ring should map back to this piop
1078            assert_eq!(piop_domain_size::<S>(max_ring), piop_dom);
1079            // max_ring + 1 should require larger piop
1080            assert!(piop_domain_size::<S>(max_ring + 1) > piop_dom);
1081        }
1082
1083        // Edge case: ring_size = 0 (degenerate but shouldn't panic)
1084        let piop_zero = piop_domain_size::<S>(0);
1085        assert!(piop_zero.is_power_of_two());
1086        assert_eq!(piop_zero, overhead.next_power_of_two());
1087    }
1088
1089    #[macro_export]
1090    macro_rules! ring_suite_tests {
1091        ($suite:ty) => {
1092            mod ring {
1093                use super::*;
1094
1095                #[test]
1096                fn prove_verify() {
1097                    $crate::ring::testing::prove_verify::<$suite>()
1098                }
1099
1100                #[test]
1101                fn prove_verify_multi() {
1102                    $crate::ring::testing::prove_verify_multi::<$suite>()
1103                }
1104
1105                #[test]
1106                fn prove_verify_batch() {
1107                    $crate::ring::testing::prove_verify_batch::<$suite>()
1108                }
1109
1110                #[test]
1111                fn padding_check() {
1112                    $crate::ring::testing::padding_check::<$suite>()
1113                }
1114
1115                #[test]
1116                fn accumulator_base_check() {
1117                    $crate::ring::testing::accumulator_base_check::<$suite>()
1118                }
1119
1120                #[test]
1121                fn verifier_key_builder() {
1122                    $crate::ring::testing::verifier_key_builder::<$suite>()
1123                }
1124
1125                #[test]
1126                fn domain_size_conversions() {
1127                    $crate::ring::testing::domain_size_conversions::<$suite>()
1128                }
1129
1130                $crate::test_vectors!($crate::ring::testing::TestVector<$suite>);
1131            }
1132        };
1133    }
1134
1135    pub trait RingSuiteExt: RingSuite + crate::testing::SuiteExt {
1136        const SRS_FILE: &str;
1137
1138        fn params() -> &'static RingProofParams<Self>;
1139
1140        #[allow(unused)]
1141        fn load_context() -> RingProofParams<Self> {
1142            use ark_serialize::CanonicalDeserialize;
1143            use std::{fs::File, io::Read};
1144            let mut file = File::open(Self::SRS_FILE).unwrap();
1145            let mut buf = Vec::new();
1146            file.read_to_end(&mut buf).unwrap();
1147            let pcs_params =
1148                PcsParams::<Self>::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap();
1149            RingProofParams::from_pcs_params(crate::ring::testing::TEST_RING_SIZE, pcs_params)
1150                .unwrap()
1151        }
1152
1153        #[allow(unused)]
1154        fn write_context(params: &RingProofParams<Self>) {
1155            use ark_serialize::CanonicalSerialize;
1156            use std::{fs::File, io::Write};
1157            let mut file = File::create(Self::SRS_FILE).unwrap();
1158            let mut buf = Vec::new();
1159            params.pcs.serialize_uncompressed(&mut buf).unwrap();
1160            file.write_all(&buf).unwrap();
1161        }
1162    }
1163
1164    pub struct TestVector<S: RingSuite> {
1165        pub pedersen: pedersen::testing::TestVector<S>,
1166        pub ring_pks: [AffinePoint<S>; TEST_RING_SIZE],
1167        pub ring_pks_com: RingCommitment<S>,
1168        pub ring_proof: RingBareProof<S>,
1169    }
1170
1171    impl<S: RingSuite> core::fmt::Debug for TestVector<S> {
1172        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1173            f.debug_struct("TestVector")
1174                .field("pedersen", &self.pedersen)
1175                .field("ring_proof", &"...")
1176                .finish()
1177        }
1178    }
1179
1180    impl<S> common::TestVectorTrait for TestVector<S>
1181    where
1182        S: RingSuiteExt + std::fmt::Debug + 'static,
1183    {
1184        fn name() -> String {
1185            S::SUITE_NAME.to_string() + "_ring"
1186        }
1187
1188        fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
1189            use super::Prover;
1190            let pedersen = pedersen::testing::TestVector::new(comment, seed, alpha, ad);
1191
1192            let secret = Secret::<S>::from_scalar(pedersen.base.sk);
1193            let public = secret.public();
1194
1195            let io = VrfIo {
1196                input: Input::<S>::from_affine_unchecked(pedersen.base.h),
1197                output: Output::from_affine_unchecked(pedersen.base.gamma),
1198            };
1199
1200            let params = <S as RingSuiteExt>::params();
1201
1202            use ark_std::rand::SeedableRng;
1203            let rng = &mut ark_std::rand::rngs::StdRng::from_seed([42; 32]);
1204            let prover_idx = 3;
1205            let mut ring_pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
1206            ring_pks[prover_idx] = public.0;
1207
1208            let prover_key = params.prover_key(&ring_pks).unwrap();
1209            let prover = params.prover(prover_key, prover_idx);
1210            let proof = secret.prove(io, ad, &prover);
1211
1212            let verifier_key = params.verifier_key(&ring_pks).unwrap();
1213            let ring_pks_com = verifier_key.commitment();
1214
1215            {
1216                // Just in case...
1217                let mut p = (Vec::new(), Vec::new());
1218                pedersen.proof.serialize_compressed(&mut p.0).unwrap();
1219                proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
1220                assert_eq!(p.0, p.1);
1221            }
1222
1223            Self {
1224                pedersen,
1225                ring_pks: ring_pks.try_into().unwrap(),
1226                ring_pks_com,
1227                ring_proof: proof.ring_proof,
1228            }
1229        }
1230
1231        fn from_map(map: &common::TestVectorMap) -> Self {
1232            let pedersen = pedersen::testing::TestVector::from_map(map);
1233
1234            let ring_pks = map.get::<[AffinePoint<S>; TEST_RING_SIZE]>("ring_pks");
1235            let ring_pks_com = map.get::<RingCommitment<S>>("ring_pks_com");
1236            let ring_proof = map.get::<RingBareProof<S>>("ring_proof");
1237
1238            Self {
1239                pedersen,
1240                ring_pks,
1241                ring_pks_com,
1242                ring_proof,
1243            }
1244        }
1245
1246        fn to_map(&self) -> common::TestVectorMap {
1247            let mut map = self.pedersen.to_map();
1248            map.set("ring_pks", &self.ring_pks);
1249            map.set("ring_pks_com", &self.ring_pks_com);
1250            map.set("ring_proof", &self.ring_proof);
1251            map
1252        }
1253
1254        fn run(&self) {
1255            self.pedersen.run();
1256
1257            let io = VrfIo {
1258                input: Input::<S>::from_affine_unchecked(self.pedersen.base.h),
1259                output: Output::from_affine_unchecked(self.pedersen.base.gamma),
1260            };
1261            let secret = Secret::from_scalar(self.pedersen.base.sk);
1262            let public = secret.public();
1263            assert_eq!(public.0, self.pedersen.base.pk);
1264
1265            let params = <S as RingSuiteExt>::params();
1266
1267            let prover_idx = self.ring_pks.iter().position(|&pk| pk == public.0).unwrap();
1268
1269            let prover_key = params.prover_key(&self.ring_pks).unwrap();
1270            let prover = params.prover(prover_key, prover_idx);
1271
1272            let verifier_key = params.verifier_key(&self.ring_pks).unwrap();
1273            let verifier = params.verifier(verifier_key);
1274
1275            let proof = secret.prove(io, &self.pedersen.base.ad, &prover);
1276
1277            {
1278                // Check if Pedersen proof matches
1279                let mut p = (Vec::new(), Vec::new());
1280                self.pedersen.proof.serialize_compressed(&mut p.0).unwrap();
1281                proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
1282                assert_eq!(p.0, p.1);
1283            }
1284
1285            #[cfg(feature = "test-vectors")]
1286            {
1287                // Verify if the ring-proof matches. This check is performed only when
1288                // deterministic proof generation is required for test vectors.
1289                let mut p = (Vec::new(), Vec::new());
1290                self.ring_proof.serialize_compressed(&mut p.0).unwrap();
1291                proof.ring_proof.serialize_compressed(&mut p.1).unwrap();
1292                assert_eq!(p.0, p.1);
1293            }
1294
1295            assert!(Public::verify(io, &self.pedersen.base.ad, &proof, &verifier).is_ok());
1296        }
1297    }
1298}