ark_vrf/
ring.rs

1//! # Ring VRF
2//!
3//! Implementation of a zero-knowledge VRF scheme providing signer anonymity within a set of
4//! public keys, based on [BCHSV23](https://eprint.iacr.org/2023/002).
5//!
6//! This module is gated by the `ring` feature.
7//!
8//! ## Usage Example
9//!
10//! ```rust,ignore
11//! // Ring setup
12//! const RING_SIZE: usize = 100;
13//! let prover_key_index = 3;
14//!
15//! // Create a ring of public keys
16//! let mut ring = (0..RING_SIZE)
17//!     .map(|i| Secret::from_seed(&i.to_le_bytes()).public().0)
18//!     .collect::<Vec<_>>();
19//! ring[prover_key_index] = public.0;
20//!
21//! // Initialize ring parameters
22//! let params = RingProofParams::from_seed(RING_SIZE, b"example seed");
23//!
24//! // Proving
25//! use ark_vrf::ring::Prover;
26//! let prover_key = params.prover_key(&ring);
27//! let prover = params.prover(prover_key, prover_key_index);
28//! let proof = secret.prove(input, output, aux_data, &prover);
29//!
30//! // Verification
31//! use ark_vrf::ring::Verifier;
32//! let verifier_key = params.verifier_key(&ring);
33//! let verifier = params.verifier(verifier_key);
34//! let result = Public::verify(input, output, aux_data, &proof, &verifier);
35//!
36//! // Efficient verification with commitment
37//! let ring_commitment = verifier_key.commitment();
38//! let reconstructed_key = params.verifier_key_from_commitment(ring_commitment);
39//! ```
40
41use crate::*;
42use ark_ec::{
43    pairing::Pairing,
44    twisted_edwards::{Affine as TEAffine, TECurveConfig},
45};
46use ark_std::ops::Range;
47use pedersen::{PedersenSuite, Proof as PedersenProof};
48use utils::te_sw_map::TEMapping;
49use w3f_ring_proof as ring_proof;
50
51/// Magic spell for [RingSuite::ACCUMULATOR_BASE] generation in built-in implementations.
52///
53/// (en) *"The foundation of the accumulator which in the silence of time guards the hidden secret"*
54pub const ACCUMULATOR_BASE_SEED: &[u8] =
55    b"substratum accumulatoris quod in silentio temporis arcanum absconditum custodit";
56
57/// Magic spell for [RingSuite::PADDING] generation in built-in implementations.
58///
59/// (en) *"A shadow that fills the void left by lost souls echoing among the darkness"*
60pub const PADDING_SEED: &[u8] =
61    b"umbra quae vacuum implet ab animabus perditis relictum inter tenebras resonans";
62
63/// Max ring size that can be managed with the given PCS domain size.
64pub const fn max_ring_size_from_pcs_domain_size<S: Suite>(pcs_domain_size: usize) -> usize {
65    let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_domain_size);
66    max_ring_size_from_piop_domain_size::<S>(piop_domain_size)
67}
68
69/// PCS domain size required to manage the given ring size.
70// This is the size required by the prover, verifier can just use piop_domain_size
71pub const fn pcs_domain_size<S: Suite>(ring_size: usize) -> usize {
72    3 * piop_domain_size::<S>(ring_size) + 1
73}
74
75/// Max ring size that can be managed with the given PIOP domain size.
76const fn max_ring_size_from_piop_domain_size<S: Suite>(piop_domain_size: usize) -> usize {
77    piop_domain_size - (4 + ScalarField::<S>::MODULUS_BIT_SIZE as usize)
78}
79
80/// PIOP domain size required to manage the given ring size.
81///
82/// Next power of two after accouting for 3 ZK + 1 extra point used internally.
83const fn piop_domain_size<S: Suite>(ring_size: usize) -> usize {
84    (ring_size + 4 + ScalarField::<S>::MODULUS_BIT_SIZE as usize).next_power_of_two()
85}
86
87/// A properly constructed PCS domain has size equal to 3*piop_domain_size+1,
88/// with piop_domain_size a power of 2.
89const fn piop_domain_size_from_pcs_domain_size(pcs_domain_size: usize) -> usize {
90    1 << ((pcs_domain_size - 1) / 3).ilog2()
91}
92
93/// Ring suite.
94///
95/// This trait provides the cryptographic primitives needed for ring VRF signatures.
96/// All required bounds are expressed directly on the associated type for better ergonomics.
97pub trait RingSuite:
98    PedersenSuite<
99    Affine: AffineRepr<BaseField: ark_ff::PrimeField, Config: TECurveConfig + Clone>
100                + TEMapping<<Self::Affine as AffineRepr>::Config>,
101>
102{
103    /// Pairing type.
104    type Pairing: ark_ec::pairing::Pairing<ScalarField = BaseField<Self>>;
105
106    /// Accumulator base.
107    ///
108    /// In order for the ring-proof backend to work correctly, this is required to be
109    /// in the prime order subgroup.
110    const ACCUMULATOR_BASE: AffinePoint<Self>;
111
112    /// Padding point with unknown discrete log.
113    const PADDING: AffinePoint<Self>;
114}
115
116/// KZG Polinomial Commitment Scheme.
117pub type Pcs<S> = ring_proof::pcs::kzg::KZG<<S as RingSuite>::Pairing>;
118
119/// KZG commitment.
120pub type PcsCommitment<S> =
121    ring_proof::pcs::kzg::commitment::KzgCommitment<<S as RingSuite>::Pairing>;
122
123/// KZG Polynomial Commitment Scheme parameters.
124///
125/// Basically powers of tau SRS.
126pub type PcsParams<S> = ring_proof::pcs::kzg::urs::URS<<S as RingSuite>::Pairing>;
127
128/// Polynomial Interactive Oracle Proof (IOP) parameters.
129///
130/// Basically all the application specific parameters required to construct and
131/// verify the ring proof.
132pub type PiopParams<S> = ring_proof::PiopParams<BaseField<S>, CurveConfig<S>>;
133
134/// Ring keys commitment.
135pub type RingCommitment<S> = ring_proof::FixedColumnsCommitted<BaseField<S>, PcsCommitment<S>>;
136
137/// Ring prover key.
138pub type RingProverKey<S> = ring_proof::ProverKey<BaseField<S>, Pcs<S>, TEAffine<CurveConfig<S>>>;
139
140/// Ring verifier key.
141pub type RingVerifierKey<S> = ring_proof::VerifierKey<BaseField<S>, Pcs<S>>;
142
143/// Ring prover.
144pub type RingProver<S> = ring_proof::ring_prover::RingProver<BaseField<S>, Pcs<S>, CurveConfig<S>>;
145
146/// Ring verifier.
147pub type RingVerifier<S> =
148    ring_proof::ring_verifier::RingVerifier<BaseField<S>, Pcs<S>, CurveConfig<S>>;
149
150/// Raw ring proof.
151///
152/// This is the primitive ring proof used in conjunction with Pedersen proof to
153/// construct the actual ring vrf proof [`Proof`].
154pub type RingBareProof<S> = ring_proof::RingProof<BaseField<S>, Pcs<S>>;
155
156/// Ring VRF proof.
157///
158/// Two-part zero-knowledge proof with signer anonymity:
159/// - `pedersen_proof`: Key commitment and VRF correctness proof
160/// - `ring_proof`: Membership proof binding the commitment to the ring
161#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
162pub struct Proof<S: RingSuite> {
163    pub pedersen_proof: PedersenProof<S>,
164    pub ring_proof: RingBareProof<S>,
165}
166
167/// Trait for types that can generate Ring VRF proofs.
168///
169/// Implementors can create anonymous proofs that a VRF output
170/// is correctly derived using a secret key from a ring of public keys.
171pub trait Prover<S: RingSuite> {
172    /// Generate a proof for the given input/output and additional data.
173    ///
174    /// Creates a zero-knowledge proof that:
175    /// 1. The prover knows a secret key for one of the ring's public keys
176    /// 2. That secret key was used to compute the VRF output
177    ///
178    /// * `input` - VRF input point
179    /// * `output` - VRF output point
180    /// * `ad` - Additional data to bind to the proof
181    /// * `prover` - Ring prover instance for the specific ring position
182    fn prove(
183        &self,
184        input: Input<S>,
185        output: Output<S>,
186        ad: impl AsRef<[u8]>,
187        prover: &RingProver<S>,
188    ) -> Proof<S>;
189}
190
191/// Trait for entities that can verify Ring VRF proofs.
192///
193/// Implementors can verify anonymous proofs that a VRF output
194/// was derived using a secret key from a ring of public keys.
195pub trait Verifier<S: RingSuite> {
196    /// Verify a proof for the given input/output and additional data.
197    ///
198    /// Verifies that:
199    /// 1. The proof was created by a member of the ring
200    /// 2. The VRF output is correct for the given input
201    /// 3. The additional data matches what was used during proving
202    ///
203    /// * `input` - VRF input point
204    /// * `output` - Claimed VRF output point
205    /// * `ad` - Additional data bound to the proof
206    /// * `sig` - The proof to verify
207    /// * `verifier` - Ring verifier instance for the specific ring
208    ///
209    /// Returns `Ok(())` if verification succeeds, `Err(Error::VerificationFailure)` otherwise.
210    fn verify(
211        input: Input<S>,
212        output: Output<S>,
213        ad: impl AsRef<[u8]>,
214        sig: &Proof<S>,
215        verifier: &RingVerifier<S>,
216    ) -> Result<(), Error>;
217}
218
219impl<S: RingSuite> Prover<S> for Secret<S> {
220    fn prove(
221        &self,
222        input: Input<S>,
223        output: Output<S>,
224        ad: impl AsRef<[u8]>,
225        ring_prover: &RingProver<S>,
226    ) -> Proof<S> {
227        use pedersen::Prover as PedersenProver;
228        let (pedersen_proof, secret_blinding) =
229            <Self as PedersenProver<S>>::prove(self, input, output, ad);
230        let ring_proof = ring_prover.prove(secret_blinding);
231        Proof {
232            pedersen_proof,
233            ring_proof,
234        }
235    }
236}
237
238impl<S: RingSuite> Verifier<S> for Public<S> {
239    fn verify(
240        input: Input<S>,
241        output: Output<S>,
242        ad: impl AsRef<[u8]>,
243        sig: &Proof<S>,
244        verifier: &RingVerifier<S>,
245    ) -> Result<(), Error> {
246        use pedersen::Verifier as PedersenVerifier;
247        <Self as PedersenVerifier<S>>::verify(input, output, ad, &sig.pedersen_proof)?;
248        let key_commitment = sig.pedersen_proof.key_commitment().into_te();
249        if !verifier.verify(sig.ring_proof.clone(), key_commitment) {
250            return Err(Error::VerificationFailure);
251        }
252        Ok(())
253    }
254}
255
256/// Ring proof parameters.
257///
258/// Contains the cryptographic parameters needed for ring proof generation and verification:
259/// - `pcs`: Polynomial Commitment Scheme parameters (KZG setup)
260/// - `piop`: Polynomial Interactive Oracle Proof parameters
261#[derive(Clone)]
262pub struct RingProofParams<S: RingSuite> {
263    /// PCS parameters.
264    pub pcs: PcsParams<S>,
265    /// PIOP parameters.
266    pub piop: PiopParams<S>,
267}
268
269pub(crate) fn piop_params<S: RingSuite>(domain_size: usize) -> PiopParams<S> {
270    PiopParams::<S>::setup(
271        ring_proof::Domain::new(domain_size, true),
272        S::BLINDING_BASE.into_te(),
273        S::ACCUMULATOR_BASE.into_te(),
274        S::PADDING.into_te(),
275    )
276}
277
278impl<S: RingSuite> RingProofParams<S> {
279    /// Construct deterministic ring proof params for the given ring size.
280    ///
281    /// Creates parameters using a deterministic `ChaCha20Rng` seeded with `seed`.
282    pub fn from_seed(ring_size: usize, seed: [u8; 32]) -> Self {
283        use ark_std::rand::SeedableRng;
284        let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed);
285        Self::from_rand(ring_size, &mut rng)
286    }
287
288    /// Construct random ring proof params for the given ring size.
289    ///
290    /// Generates a new KZG setup with sufficient degree to support the specified ring size.
291    pub fn from_rand(ring_size: usize, rng: &mut impl ark_std::rand::RngCore) -> Self {
292        use ring_proof::pcs::PCS;
293        let max_degree = pcs_domain_size::<S>(ring_size) - 1;
294        let pcs_params = Pcs::<S>::setup(max_degree, rng);
295        Self::from_pcs_params(ring_size, pcs_params).expect("PCS params is correct")
296    }
297
298    /// Construct ring proof params from existing KZG setup.
299    ///
300    /// Creates parameters using an existing KZG setup, truncating if larger than needed
301    /// or returning an error if the setup is insufficient for the specified ring size.
302    ///
303    /// * `ring_size` - Maximum number of keys in the ring
304    /// * `pcs_params` - KZG setup parameters
305    pub fn from_pcs_params(ring_size: usize, mut pcs_params: PcsParams<S>) -> Result<Self, Error> {
306        let pcs_domain_size = pcs_domain_size::<S>(ring_size);
307        if pcs_params.powers_in_g1.len() < pcs_domain_size || pcs_params.powers_in_g2.len() < 2 {
308            return Err(Error::InvalidData);
309        }
310        // Keep only the required powers of tau
311        pcs_params.powers_in_g1.truncate(pcs_domain_size);
312        pcs_params.powers_in_g2.truncate(2);
313        let piop_domain_size = piop_domain_size::<S>(ring_size);
314        Ok(Self {
315            pcs: pcs_params,
316            piop: piop_params::<S>(piop_domain_size),
317        })
318    }
319
320    /// The max ring size these parameters are able to handle.
321    #[inline(always)]
322    pub fn max_ring_size(&self) -> usize {
323        self.piop.keyset_part_size
324    }
325
326    /// Create a prover key for the given ring of public keys.
327    ///
328    /// Indexes the ring and prepares the cryptographic material needed for proving.
329    /// If the ring exceeds the maximum supported size, excess keys are ignored.
330    ///
331    /// * `pks` - Array of public keys forming the ring
332    pub fn prover_key(&self, pks: &[AffinePoint<S>]) -> RingProverKey<S> {
333        let pks = TEMapping::to_te_slice(&pks[..pks.len().min(self.max_ring_size())]);
334        ring_proof::index(&self.pcs, &self.piop, &pks).0
335    }
336
337    /// Create a prover instance for a specific position in the ring.
338    ///
339    /// * `prover_key` - Ring prover key created with `prover_key()`
340    /// * `key_index` - Position of the prover's public key in the original ring
341    pub fn prover(&self, prover_key: RingProverKey<S>, key_index: usize) -> RingProver<S> {
342        RingProver::<S>::init(
343            prover_key,
344            self.piop.clone(),
345            key_index,
346            ring_proof::ArkTranscript::new(S::SUITE_ID),
347        )
348    }
349
350    /// Create a verifier key for the given ring of public keys.
351    ///
352    /// Indexes the ring and prepares the cryptographic material needed for verification.
353    /// If the ring exceeds the maximum supported size, excess keys are ignored.
354    ///
355    /// * `pks` - Array of public keys forming the ring
356    pub fn verifier_key(&self, pks: &[AffinePoint<S>]) -> RingVerifierKey<S> {
357        let pks = TEMapping::to_te_slice(&pks[..pks.len().min(self.max_ring_size())]);
358        ring_proof::index(&self.pcs, &self.piop, &pks).1
359    }
360
361    /// Create a verifier key from a precomputed ring commitment.
362    ///
363    /// Allows efficient reconstruction of a verifier key without needing the full ring.
364    /// The commitment can be obtained from an existing verifier key via `commitment()`.
365    ///
366    /// * `commitment` - Precomputed commitment to the ring of public keys
367    pub fn verifier_key_from_commitment(
368        &self,
369        commitment: RingCommitment<S>,
370    ) -> RingVerifierKey<S> {
371        use ring_proof::pcs::PcsParams;
372        RingVerifierKey::<S>::from_commitment_and_kzg_vk(commitment, self.pcs.raw_vk())
373    }
374
375    /// Create a builder for incremental construction of the verifier key.
376    ///
377    /// Returns a builder and associated PCS parameters that can be used to
378    /// construct a verifier key by adding public keys in batches.
379    pub fn verifier_key_builder(&self) -> (RingVerifierKeyBuilder<S>, RingBuilderPcsParams<S>) {
380        type RingBuilderKey<S> =
381            ring_proof::ring::RingBuilderKey<BaseField<S>, <S as RingSuite>::Pairing>;
382        let piop_domain_size = piop_domain_size::<S>(self.piop.keyset_part_size);
383        let builder_key = RingBuilderKey::<S>::from_srs(&self.pcs, piop_domain_size);
384        let builder_pcs_params = RingBuilderPcsParams(builder_key.lis_in_g1);
385        let builder = RingVerifierKeyBuilder::new(self, &builder_pcs_params);
386        (builder, builder_pcs_params)
387    }
388
389    /// Create a verifier instance from a verifier key.
390    ///
391    /// * `verifier_key` - Ring verifier key created with `verifier_key()`
392    pub fn verifier(&self, verifier_key: RingVerifierKey<S>) -> RingVerifier<S> {
393        RingVerifier::<S>::init(
394            verifier_key,
395            self.piop.clone(),
396            ring_proof::ArkTranscript::new(S::SUITE_ID),
397        )
398    }
399
400    /// Create a verifier instance without requiring the full parameters.
401    ///
402    /// Creates a verifier using only the verifier key and ring size, computing
403    /// necessary parameters on-the-fly. This is more memory efficient but slightly
404    /// less computationally efficient than using the full parameters.
405    ///
406    /// * `verifier_key` - Ring verifier key
407    /// * `ring_size` - Size of the ring used to create the verifier key
408    pub fn verifier_no_context(
409        verifier_key: RingVerifierKey<S>,
410        ring_size: usize,
411    ) -> RingVerifier<S> {
412        RingVerifier::<S>::init(
413            verifier_key,
414            piop_params::<S>(piop_domain_size::<S>(ring_size)),
415            ring_proof::ArkTranscript::new(S::SUITE_ID),
416        )
417    }
418
419    /// Get the padding point.
420    ///
421    /// This is a point of unknown dlog that can be used in place of any key during
422    /// ring construciton.
423    #[inline(always)]
424    pub const fn padding_point() -> AffinePoint<S> {
425        S::PADDING
426    }
427}
428
429impl<S: RingSuite> CanonicalSerialize for RingProofParams<S> {
430    fn serialize_with_mode<W: ark_serialize::Write>(
431        &self,
432        mut writer: W,
433        compress: ark_serialize::Compress,
434    ) -> Result<(), ark_serialize::SerializationError> {
435        self.pcs.serialize_with_mode(&mut writer, compress)
436    }
437
438    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
439        self.pcs.serialized_size(compress)
440    }
441}
442
443impl<S: RingSuite> CanonicalDeserialize for RingProofParams<S> {
444    fn deserialize_with_mode<R: ark_serialize::Read>(
445        mut reader: R,
446        compress: ark_serialize::Compress,
447        validate: ark_serialize::Validate,
448    ) -> Result<Self, ark_serialize::SerializationError> {
449        let pcs_params = <PcsParams<S> as CanonicalDeserialize>::deserialize_with_mode(
450            &mut reader,
451            compress,
452            validate,
453        )?;
454        let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_params.powers_in_g1.len());
455        Ok(Self {
456            pcs: pcs_params,
457            piop: piop_params::<S>(piop_domain_size),
458        })
459    }
460}
461
462impl<S: RingSuite> ark_serialize::Valid for RingProofParams<S> {
463    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
464        self.pcs.check()
465    }
466}
467
468/// Information required for incremental ring construction.
469///
470/// Basically the SRS in Lagrangian form.
471/// Can be constructed via the `PcsParams::ck_with_lagrangian()` method.
472#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
473pub struct RingBuilderPcsParams<S: RingSuite>(pub Vec<G1Affine<S>>);
474
475// Under construction ring commitment.
476type PartialRingCommitment<S> =
477    ring_proof::ring::Ring<BaseField<S>, <S as RingSuite>::Pairing, CurveConfig<S>>;
478
479type RawVerifierKey<S> = <PcsParams<S> as ring_proof::pcs::PcsParams>::RVK;
480
481/// Builder for incremental construction of ring verifier keys.
482///
483/// Allows constructing a verifier key by adding public keys in batches,
484/// which is useful for large rings or memory-constrained environments.
485#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
486pub struct RingVerifierKeyBuilder<S: RingSuite> {
487    partial: PartialRingCommitment<S>,
488    raw_vk: RawVerifierKey<S>,
489}
490
491pub type G1Affine<S> = <<S as RingSuite>::Pairing as Pairing>::G1Affine;
492pub type G2Affine<S> = <<S as RingSuite>::Pairing as Pairing>::G2Affine;
493
494/// Trait for accessing Structured Reference String entries in Lagrangian basis.
495///
496/// Provides access to precomputed SRS elements needed for efficient ring operations.
497pub trait SrsLookup<S: RingSuite> {
498    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>>;
499}
500
501impl<S: RingSuite, F> SrsLookup<S> for F
502where
503    F: Fn(Range<usize>) -> Option<Vec<G1Affine<S>>>,
504{
505    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>> {
506        self(range)
507    }
508}
509
510impl<S: RingSuite> SrsLookup<S> for &RingBuilderPcsParams<S> {
511    fn lookup(&self, range: Range<usize>) -> Option<Vec<G1Affine<S>>> {
512        if range.end > self.0.len() {
513            return None;
514        }
515        Some(self.0[range].to_vec())
516    }
517}
518
519impl<S: RingSuite> RingVerifierKeyBuilder<S> {
520    /// Create a new empty ring verifier key builder.
521    ///
522    /// * `params` - Ring proof parameters
523    /// * `lookup` - SRS lookup implementation for accessing precomputed values
524    pub fn new(params: &RingProofParams<S>, lookup: impl SrsLookup<S>) -> Self {
525        use ring_proof::pcs::PcsParams;
526        let lookup = |range: Range<usize>| lookup.lookup(range).ok_or(());
527        let raw_vk = params.pcs.raw_vk();
528        let partial =
529            PartialRingCommitment::<S>::empty(&params.piop, lookup, raw_vk.g1.into_group());
530        RingVerifierKeyBuilder { partial, raw_vk }
531    }
532
533    /// Get the number of remaining slots available in the ring.
534    #[inline(always)]
535    pub fn free_slots(&self) -> usize {
536        self.partial.max_keys - self.partial.curr_keys
537    }
538
539    /// Add public keys to the ring being built.
540    ///
541    /// * `pks` - Public keys to add to the ring
542    /// * `lookup` - SRS lookup implementation for accessing precomputed values
543    ///
544    /// Returns `Ok(())` if keys were added successfully, or `Err(available_slots)`
545    /// if there's not enough space. Returns `Err(usize::MAX)` if SRS lookup fails.
546    pub fn append(
547        &mut self,
548        pks: &[AffinePoint<S>],
549        lookup: impl SrsLookup<S>,
550    ) -> Result<(), usize> {
551        let avail_slots = self.free_slots();
552        if avail_slots < pks.len() {
553            return Err(avail_slots);
554        }
555        // Currently `ring-proof` backend panics if lookup fails.
556        // This workaround makes lookup failures a bit less harsh.
557        let segment = lookup
558            .lookup(self.partial.curr_keys..self.partial.curr_keys + pks.len())
559            .ok_or(usize::MAX)?;
560        let lookup = |range: Range<usize>| {
561            debug_assert_eq!(segment.len(), range.len());
562            Ok(segment.clone())
563        };
564        let pks = TEMapping::to_te_slice(pks);
565        self.partial.append(&pks, lookup);
566        Ok(())
567    }
568
569    /// Complete the building process and create the verifier key.
570    pub fn finalize(self) -> RingVerifierKey<S> {
571        RingVerifierKey::<S>::from_ring_and_kzg_vk(&self.partial, self.raw_vk)
572    }
573}
574
575/// Type aliases for the given ring suite.
576#[macro_export]
577macro_rules! ring_suite_types {
578    ($suite:ident) => {
579        #[allow(dead_code)]
580        pub type PcsParams = $crate::ring::PcsParams<$suite>;
581        #[allow(dead_code)]
582        pub type PiopParams = $crate::ring::PiopParams<$suite>;
583        #[allow(dead_code)]
584        pub type RingProofParams = $crate::ring::RingProofParams<$suite>;
585        #[allow(dead_code)]
586        pub type RingProverKey = $crate::ring::RingProverKey<$suite>;
587        #[allow(dead_code)]
588        pub type RingVerifierKey = $crate::ring::RingVerifierKey<$suite>;
589        #[allow(dead_code)]
590        pub type RingCommitment = $crate::ring::RingCommitment<$suite>;
591        #[allow(dead_code)]
592        pub type RingProver = $crate::ring::RingProver<$suite>;
593        #[allow(dead_code)]
594        pub type RingVerifier = $crate::ring::RingVerifier<$suite>;
595        #[allow(dead_code)]
596        pub type RingProof = $crate::ring::Proof<$suite>;
597        #[allow(dead_code)]
598        pub type RingVerifierKeyBuilder = $crate::ring::RingVerifierKeyBuilder<$suite>;
599    };
600}
601
602#[cfg(test)]
603pub(crate) mod testing {
604    use super::*;
605    use crate::pedersen;
606    use crate::testing::{self as common, CheckPoint, TEST_SEED};
607    use ark_ec::{
608        short_weierstrass::{Affine as SWAffine, SWCurveConfig},
609        twisted_edwards::{Affine as TEAffine, TECurveConfig},
610    };
611
612    pub const TEST_RING_SIZE: usize = 8;
613
614    fn find_complement_point<C: SWCurveConfig>() -> SWAffine<C> {
615        use ark_ff::{One, Zero};
616        assert!(!C::cofactor_is_one());
617        let mut x = C::BaseField::zero();
618        loop {
619            if let Some(p) = SWAffine::get_point_from_x_unchecked(x, false)
620                .filter(|p| !p.is_in_correct_subgroup_assuming_on_curve())
621            {
622                return p;
623            }
624            x += C::BaseField::one();
625        }
626    }
627
628    pub trait FindAccumulatorBase<S: Suite>: Sized {
629        const IN_PRIME_ORDER_SUBGROUP: bool;
630        fn find_accumulator_base(data: &[u8]) -> Option<Self>;
631    }
632
633    impl<S, C> FindAccumulatorBase<S> for SWAffine<C>
634    where
635        C: SWCurveConfig,
636        S: Suite<Affine = Self>,
637    {
638        const IN_PRIME_ORDER_SUBGROUP: bool = false;
639
640        fn find_accumulator_base(data: &[u8]) -> Option<Self> {
641            let p = S::data_to_point(data)?;
642            let c = find_complement_point();
643            let res = (p + c).into_affine();
644            debug_assert!(!res.is_in_correct_subgroup_assuming_on_curve());
645            Some(res)
646        }
647    }
648
649    impl<S, C> FindAccumulatorBase<S> for TEAffine<C>
650    where
651        C: TECurveConfig,
652        S: Suite<Affine = Self>,
653    {
654        const IN_PRIME_ORDER_SUBGROUP: bool = true;
655
656        fn find_accumulator_base(data: &[u8]) -> Option<Self> {
657            let res = S::data_to_point(data)?;
658            debug_assert!(res.is_in_correct_subgroup_assuming_on_curve());
659            Some(res)
660        }
661    }
662
663    #[allow(unused)]
664    pub fn prove_verify<S: RingSuite>() {
665        let rng = &mut ark_std::test_rng();
666        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
667
668        let secret = Secret::<S>::from_seed(TEST_SEED);
669        let public = secret.public();
670        let input = Input::from(common::random_val(Some(rng)));
671        let output = secret.output(input);
672
673        let ring_size = params.max_ring_size();
674        let pcs_dom_size = pcs_domain_size::<S>(ring_size);
675        assert_eq!(pcs_dom_size, params.pcs.powers_in_g1.len());
676        assert_eq!(pcs_dom_size / 3, piop_domain_size::<S>(ring_size));
677
678        assert_eq!(
679            max_ring_size_from_pcs_domain_size::<S>(pcs_dom_size),
680            ring_size
681        );
682
683        let prover_idx = 3;
684        let mut pks = common::random_vec::<AffinePoint<S>>(ring_size, Some(rng));
685        pks[prover_idx] = public.0;
686
687        let prover_key = params.prover_key(&pks);
688        let prover = params.prover(prover_key, prover_idx);
689        let proof = secret.prove(input, output, b"foo", &prover);
690
691        let verifier_key = params.verifier_key(&pks);
692        let verifier = params.verifier(verifier_key);
693        let result = Public::verify(input, output, b"foo", &proof, &verifier);
694        assert!(result.is_ok());
695    }
696
697    #[allow(unused)]
698    pub fn padding_check<S: RingSuite>()
699    where
700        AffinePoint<S>: CheckPoint,
701    {
702        // Check that point has been computed using the magic spell.
703        assert_eq!(S::PADDING, S::data_to_point(PADDING_SEED).unwrap());
704
705        // Check that the point is on curve.
706        assert!(S::PADDING.check(true).is_ok());
707    }
708
709    #[allow(unused)]
710    pub fn accumulator_base_check<S: RingSuite>()
711    where
712        AffinePoint<S>: FindAccumulatorBase<S> + CheckPoint,
713    {
714        // Check that point has been computed using the magic spell.
715        assert_eq!(
716            S::ACCUMULATOR_BASE,
717            AffinePoint::<S>::find_accumulator_base(ACCUMULATOR_BASE_SEED).unwrap()
718        );
719
720        // SW form requires accumulator seed to be outside prime order subgroup.
721        // TE form requires accumulator seed to be in prime order subgroup.
722        let in_prime_subgroup = <AffinePoint<S> as FindAccumulatorBase<S>>::IN_PRIME_ORDER_SUBGROUP;
723        assert!(S::ACCUMULATOR_BASE.check(in_prime_subgroup).is_ok());
724    }
725
726    #[allow(unused)]
727    pub fn verifier_key_builder<S: RingSuite>() {
728        use crate::testing::{random_val, random_vec};
729
730        let rng = &mut ark_std::test_rng();
731        let params = RingProofParams::<S>::from_rand(TEST_RING_SIZE, rng);
732
733        let secret = Secret::<S>::from_seed(TEST_SEED);
734        let public = secret.public();
735        let input = Input::from(common::random_val(Some(rng)));
736        let output = secret.output(input);
737
738        let ring_size = params.max_ring_size();
739        let prover_idx = random_val::<usize>(Some(rng)) % ring_size;
740        let mut pks = random_vec::<AffinePoint<S>>(ring_size, Some(rng));
741        pks[prover_idx] = public.0;
742
743        let prover_key = params.prover_key(&pks);
744        let prover = params.prover(prover_key, prover_idx);
745        let proof = secret.prove(input, output, b"foo", &prover);
746
747        // Incremental ring verifier key construction
748        let (mut vk_builder, lookup) = params.verifier_key_builder();
749        assert_eq!(vk_builder.free_slots(), pks.len());
750
751        let extra_pk = random_val::<AffinePoint<S>>(Some(rng));
752        assert_eq!(
753            vk_builder.append(&[extra_pk], |_| None).unwrap_err(),
754            usize::MAX
755        );
756
757        while !pks.is_empty() {
758            let chunk_len = 1 + random_val::<usize>(Some(rng)) % 5;
759            let chunk = pks.drain(..pks.len().min(chunk_len)).collect::<Vec<_>>();
760            vk_builder.append(&chunk[..], &lookup).unwrap();
761            assert_eq!(vk_builder.free_slots(), pks.len());
762        }
763        // No more space left
764        let extra_pk = random_val::<AffinePoint<S>>(Some(rng));
765        assert_eq!(vk_builder.append(&[extra_pk], &lookup).unwrap_err(), 0);
766        let verifier_key = vk_builder.finalize();
767        let verifier = params.verifier(verifier_key);
768        let result = Public::verify(input, output, b"foo", &proof, &verifier);
769        assert!(result.is_ok());
770    }
771
772    #[macro_export]
773    macro_rules! ring_suite_tests {
774        ($suite:ty) => {
775            mod ring {
776                use super::*;
777
778                #[test]
779                fn prove_verify() {
780                    $crate::ring::testing::prove_verify::<$suite>()
781                }
782
783                #[test]
784                fn padding_check() {
785                    $crate::ring::testing::padding_check::<$suite>()
786                }
787
788                #[test]
789                fn accumulator_base_check() {
790                    $crate::ring::testing::accumulator_base_check::<$suite>()
791                }
792
793                #[test]
794                fn verifier_key_builder() {
795                    $crate::ring::testing::verifier_key_builder::<$suite>()
796                }
797
798                $crate::test_vectors!($crate::ring::testing::TestVector<$suite>);
799            }
800        };
801    }
802
803    pub trait RingSuiteExt: RingSuite + crate::testing::SuiteExt {
804        const SRS_FILE: &str;
805
806        fn params() -> &'static RingProofParams<Self>;
807
808        #[allow(unused)]
809        fn load_context() -> RingProofParams<Self> {
810            use ark_serialize::CanonicalDeserialize;
811            use std::{fs::File, io::Read};
812            let mut file = File::open(Self::SRS_FILE).unwrap();
813            let mut buf = Vec::new();
814            file.read_to_end(&mut buf).unwrap();
815            let pcs_params =
816                PcsParams::<Self>::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap();
817            RingProofParams::from_pcs_params(crate::ring::testing::TEST_RING_SIZE, pcs_params)
818                .unwrap()
819        }
820
821        #[allow(unused)]
822        fn write_context(params: &RingProofParams<Self>) {
823            use ark_serialize::CanonicalSerialize;
824            use std::{fs::File, io::Write};
825            let mut file = File::create(Self::SRS_FILE).unwrap();
826            let mut buf = Vec::new();
827            params.pcs.serialize_uncompressed(&mut buf).unwrap();
828            file.write_all(&buf).unwrap();
829        }
830    }
831
832    pub struct TestVector<S: RingSuite> {
833        pub pedersen: pedersen::testing::TestVector<S>,
834        pub ring_pks: [AffinePoint<S>; TEST_RING_SIZE],
835        pub ring_pks_com: RingCommitment<S>,
836        pub ring_proof: RingBareProof<S>,
837    }
838
839    impl<S: RingSuite> core::fmt::Debug for TestVector<S> {
840        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
841            f.debug_struct("TestVector")
842                .field("pedersen", &self.pedersen)
843                .field("ring_proof", &"...")
844                .finish()
845        }
846    }
847
848    impl<S> common::TestVectorTrait for TestVector<S>
849    where
850        S: RingSuiteExt + std::fmt::Debug + 'static,
851    {
852        fn name() -> String {
853            S::suite_name() + "_ring"
854        }
855
856        fn new(comment: &str, seed: &[u8], alpha: &[u8], salt: &[u8], ad: &[u8]) -> Self {
857            use super::Prover;
858            let pedersen = pedersen::testing::TestVector::new(comment, seed, alpha, salt, ad);
859
860            let secret = Secret::<S>::from_scalar(pedersen.base.sk);
861            let public = secret.public();
862
863            let input = Input::<S>::from(pedersen.base.h);
864            let output = Output::from(pedersen.base.gamma);
865
866            let params = <S as RingSuiteExt>::params();
867
868            use ark_std::rand::SeedableRng;
869            let rng = &mut rand_chacha::ChaCha20Rng::from_seed([0x11; 32]);
870            let prover_idx = 3;
871            let mut ring_pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
872            ring_pks[prover_idx] = public.0;
873
874            let prover_key = params.prover_key(&ring_pks);
875            let prover = params.prover(prover_key, prover_idx);
876            let proof = secret.prove(input, output, ad, &prover);
877
878            let verifier_key = params.verifier_key(&ring_pks);
879            let ring_pks_com = verifier_key.commitment();
880
881            {
882                // Just in case...
883                let mut p = (Vec::new(), Vec::new());
884                pedersen.proof.serialize_compressed(&mut p.0).unwrap();
885                proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
886                assert_eq!(p.0, p.1);
887            }
888
889            // TODO: also dump the verifier pks commitment
890            Self {
891                pedersen,
892                ring_pks: ring_pks.try_into().unwrap(),
893                ring_pks_com,
894                ring_proof: proof.ring_proof,
895            }
896        }
897
898        fn from_map(map: &common::TestVectorMap) -> Self {
899            let pedersen = pedersen::testing::TestVector::from_map(map);
900
901            let ring_pks = map.get::<[AffinePoint<S>; TEST_RING_SIZE]>("ring_pks");
902            let ring_pks_com = map.get::<RingCommitment<S>>("ring_pks_com");
903            let ring_proof = map.get::<RingBareProof<S>>("ring_proof");
904
905            Self {
906                pedersen,
907                ring_pks,
908                ring_pks_com,
909                ring_proof,
910            }
911        }
912
913        fn to_map(&self) -> common::TestVectorMap {
914            let mut map = self.pedersen.to_map();
915            map.set("ring_pks", &self.ring_pks);
916            map.set("ring_pks_com", &self.ring_pks_com);
917            map.set("ring_proof", &self.ring_proof);
918            map
919        }
920
921        fn run(&self) {
922            self.pedersen.run();
923
924            let input = Input::<S>::from(self.pedersen.base.h);
925            let output = Output::from(self.pedersen.base.gamma);
926            let secret = Secret::from_scalar(self.pedersen.base.sk);
927            let public = secret.public();
928            assert_eq!(public.0, self.pedersen.base.pk);
929
930            let params = <S as RingSuiteExt>::params();
931
932            let prover_idx = self.ring_pks.iter().position(|&pk| pk == public.0).unwrap();
933
934            let prover_key = params.prover_key(&self.ring_pks);
935            let prover = params.prover(prover_key, prover_idx);
936
937            let verifier_key = params.verifier_key(&self.ring_pks);
938            let verifier = params.verifier(verifier_key);
939
940            let proof = secret.prove(input, output, &self.pedersen.base.ad, &prover);
941
942            {
943                // Check if Pedersen proof matches
944                let mut p = (Vec::new(), Vec::new());
945                self.pedersen.proof.serialize_compressed(&mut p.0).unwrap();
946                proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
947                assert_eq!(p.0, p.1);
948            }
949
950            #[cfg(feature = "test-vectors")]
951            {
952                // Verify if the ring-proof matches. This check is performed only when
953                // deterministic proof generation is required for test vectors.
954                let mut p = (Vec::new(), Vec::new());
955                self.ring_proof.serialize_compressed(&mut p.0).unwrap();
956                proof.ring_proof.serialize_compressed(&mut p.1).unwrap();
957                assert_eq!(p.0, p.1);
958            }
959
960            assert!(
961                Public::verify(input, output, &self.pedersen.base.ad, &proof, &verifier).is_ok()
962            );
963        }
964    }
965}