Skip to main content

ark_vrf/
pedersen.rs

1//! # Pedersen VRF
2//!
3//! Key-hiding VRF based on the PedVRF construction from Section 4 of
4//! [BCHSV23](https://eprint.iacr.org/2023/002). Replaces the public key with a
5//! Pedersen commitment to the secret key, allowing verification without revealing
6//! which specific public key was used. Serves as a building block for anonymized
7//! ring signatures.
8//!
9//! ## Usage
10//!
11//! ```rust,ignore
12//! use ark_vrf::suites::bandersnatch::*;
13//! use ark_vrf::pedersen::{Prover, Verifier};
14//!
15//! let secret = Secret::from_seed([0; 32]);
16//! let public = secret.public();
17//! let input = Input::new(b"example input").unwrap();
18//! let io = secret.vrf_io(input);
19//!
20//! // Proving
21//! let (proof, blinding) = secret.prove(io, b"aux data");
22//!
23//! // Verification
24//! let result = Public::verify(io, b"aux data", &proof);
25//!
26//! // Unblinding: verify the proof was created using a specific public key
27//! let expected = (public.0 + BandersnatchSha512Ell2::BLINDING_BASE * blinding).into_affine();
28//! assert_eq!(proof.key_commitment(), expected);
29//! ```
30
31use crate::Suite;
32use crate::utils;
33use crate::utils::common::DomSep;
34use crate::utils::straus::short_msm;
35use crate::*;
36use ark_ec::VariableBaseMSM;
37
38/// Seed hashed to curve to produce [`PedersenSuite::BLINDING_BASE`] in built-in suites.
39pub const PEDERSEN_BLINDING_BASE_SEED: &[u8] = b"pedersen-blinding";
40
41/// Suite extension for Pedersen VRF support.
42///
43/// Provides the additional cryptographic parameters required by the Pedersen VRF scheme.
44pub trait PedersenSuite: Suite {
45    /// Blinding base.
46    const BLINDING_BASE: AffinePoint<Self>;
47
48    /// Pedersen blinding factor.
49    ///
50    /// Default implementation is deterministic. All parameters but `secret` are public.
51    fn blinding(secret: &ScalarField<Self>, mut transcript: Self::Transcript) -> ScalarField<Self> {
52        transcript.absorb_raw(&[DomSep::PedersenBlinding as u8]);
53        Self::nonce(secret, Some(transcript))
54    }
55}
56
57/// Pedersen VRF proof.
58///
59/// Zero-knowledge proof with key-hiding properties:
60/// - `pk_com`: Commitment to the public key (Y_b = x·G + b·B)
61/// - `r`: Nonce commitment for the generator (R = k·G + k_b·B)
62/// - `ok`: Nonce commitment for the input point (O_k = k·I)
63/// - `s`: Response scalar for the secret key
64/// - `sb`: Response scalar for the blinding factor
65///
66/// Deserialization via [`CanonicalDeserialize`] includes subgroup checks for
67/// curve points, so deserialized proofs are guaranteed to contain valid points.
68#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)]
69pub struct Proof<S: PedersenSuite> {
70    pk_com: AffinePoint<S>,
71    r: AffinePoint<S>,
72    ok: AffinePoint<S>,
73    s: ScalarField<S>,
74    sb: ScalarField<S>,
75}
76
77impl<S: PedersenSuite> Proof<S> {
78    /// Get public key commitment from proof.
79    pub fn key_commitment(&self) -> AffinePoint<S> {
80        self.pk_com
81    }
82}
83
84/// Trait for types that can generate Pedersen VRF proofs.
85pub trait Prover<S: PedersenSuite> {
86    /// Generate a proof for the given VRF I/O pairs and additional data.
87    ///
88    /// Multiple I/O pairs are delinearized into a single merged pair before proving.
89    ///
90    /// Returns the proof together with the associated blinding factor.
91    fn prove(
92        &self,
93        ios: impl AsRef<[VrfIo<S>]>,
94        ad: impl AsRef<[u8]>,
95    ) -> (Proof<S>, ScalarField<S>);
96}
97
98/// Trait for entities that can verify Pedersen VRF proofs.
99///
100/// Verifies that a VRF output is correctly derived from an input using a
101/// committed public key, without revealing which specific public key was used.
102///
103/// All curve points involved in verification (I/O pairs and proof points)
104/// are assumed to be in the prime-order subgroup. This is guaranteed when
105/// points are constructed through checked constructors ([`Input::from_affine`],
106/// [`Output::from_affine`]) or through trusted operations like [`Input::new`]
107/// (hash-to-curve) and [`Secret::vrf_io`]. Proof points are guaranteed valid
108/// when deserialized via [`CanonicalDeserialize`] (which includes subgroup
109/// checks) or produced by [`Prover::prove`].
110///
111/// Using unchecked constructors (e.g. [`Input::from_affine_unchecked`]) places
112/// the burden of subgroup validation on the caller. Passing points with
113/// cofactor components leads to undefined verification behavior.
114pub trait Verifier<S: PedersenSuite> {
115    /// Verify a proof for the given VRF I/O pairs and additional data.
116    ///
117    /// Multiple I/O pairs are delinearized into a single merged pair before verifying.
118    ///
119    /// Returns `Ok(())` if verification succeeds, `Err(Error::VerificationFailure)` otherwise.
120    fn verify(
121        ios: impl AsRef<[VrfIo<S>]>,
122        ad: impl AsRef<[u8]>,
123        proof: &Proof<S>,
124    ) -> Result<(), Error>;
125}
126
127impl<S: PedersenSuite> Prover<S> for Secret<S> {
128    fn prove(
129        &self,
130        ios: impl AsRef<[VrfIo<S>]>,
131        ad: impl AsRef<[u8]>,
132    ) -> (Proof<S>, ScalarField<S>) {
133        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
134
135        // Build blinding factor from T.fork()
136        let blinding = S::blinding(&self.scalar, t.clone());
137
138        // Yb = x*G + b*B = PK + b*B
139        let bb = smul!(S::BLINDING_BASE, blinding);
140        let pk_com = (self.public.0.into_group() + bb).into_affine();
141
142        // Absorb Yb into the transcript
143        t.absorb_serialize(&pk_com);
144
145        // Nonces from T.fork()
146        let k = S::nonce(&self.scalar, Some(t.clone()));
147        let kb = S::nonce(&blinding, Some(t.clone()));
148
149        // R = k*G + kb*B
150        let kg = smul!(S::generator(), k);
151        let kbb = smul!(S::BLINDING_BASE, kb);
152        let r = kg + kbb;
153
154        // Ok = k*I
155        let ok = smul!(io.input.0, k);
156
157        let norms = CurveGroup::normalize_batch(&[r, ok]);
158        let (r, ok) = (norms[0], norms[1]);
159
160        // c = challenge([R, Ok], T)
161        let c = S::challenge(&[&r, &ok], Some(t));
162
163        // s = k + c*x
164        let s = k + c * self.scalar;
165        // sb = kb + c*b
166        let sb = kb + c * blinding;
167
168        let proof = Proof {
169            pk_com,
170            r,
171            ok,
172            s,
173            sb,
174        };
175        (proof, blinding)
176    }
177}
178
179impl<S: PedersenSuite> Verifier<S> for Public<S> {
180    fn verify(
181        ios: impl AsRef<[VrfIo<S>]>,
182        ad: impl AsRef<[u8]>,
183        proof: &Proof<S>,
184    ) -> Result<(), Error> {
185        let Proof {
186            pk_com,
187            r,
188            ok,
189            s,
190            sb,
191        } = proof;
192
193        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
194
195        // Absorb Yb into the transcript
196        t.absorb_serialize(pk_com);
197
198        // c = challenge([R, Ok], T)
199        let c = S::challenge(&[r, ok], Some(t));
200
201        let neg_c = -c;
202
203        // Eq1: s*I - c*O == Ok
204        // Verifies that the VRF output O is correctly derived from the input I
205        // using the same secret scalar x committed in the proof. Expanding the
206        // response s = k + c*x gives s*I = k*I + c*x*I = Ok + c*O.
207        let lhs1 = short_msm(&[io.input.0, io.output.0], &[*s, neg_c], 2);
208        if lhs1 != ok.into_group() {
209            return Err(Error::VerificationFailure);
210        }
211
212        // Eq2: s*G + sb*B - c*Yb == R
213        // Verifies knowledge of both the secret key x and blinding factor b
214        // committed in the public key commitment Yb = x*G + b*B. Expanding
215        // s = k + c*x and sb = kb + c*b gives s*G + sb*B = R + c*Yb.
216        let lhs2 = short_msm(
217            &[S::generator(), S::BLINDING_BASE, *pk_com],
218            &[*s, *sb, neg_c],
219            1,
220        );
221        if lhs2 != r.into_group() {
222            return Err(Error::VerificationFailure);
223        }
224
225        Ok(())
226    }
227}
228
229/// Deferred Pedersen verification data for batch verification.
230///
231/// Captures all the information needed to verify a single Pedersen proof,
232/// allowing multiple proofs to be verified together via a single MSM.
233pub struct BatchItem<S: PedersenSuite> {
234    c: ScalarField<S>,
235    input: AffinePoint<S>,
236    output: AffinePoint<S>,
237    pk_com: AffinePoint<S>,
238    r: AffinePoint<S>,
239    ok: AffinePoint<S>,
240    s: ScalarField<S>,
241    sb: ScalarField<S>,
242}
243
244/// Batch verifier for Pedersen VRF proofs.
245///
246/// Collects multiple proofs and verifies them together via a single
247/// multi-scalar multiplication.
248///
249/// The same subgroup membership assumptions as [`Verifier`] apply to all
250/// points fed into the batch (I/O pairs and proof points).
251pub struct BatchVerifier<S: PedersenSuite> {
252    items: Vec<BatchItem<S>>,
253}
254
255impl<S: PedersenSuite> Default for BatchVerifier<S> {
256    fn default() -> Self {
257        Self { items: Vec::new() }
258    }
259}
260
261impl<S: PedersenSuite> BatchVerifier<S> {
262    /// Create a new empty batch verifier.
263    pub fn new() -> Self {
264        Self::default()
265    }
266
267    /// Prepare a proof for batch verification.
268    ///
269    /// Computes the challenge and packages all data needed for deferred
270    /// verification. This is cheap (one hash, no scalar multiplications)
271    /// and can be done in parallel.
272    pub fn prepare(
273        ios: impl AsRef<[VrfIo<S>]>,
274        ad: impl AsRef<[u8]>,
275        proof: &Proof<S>,
276    ) -> BatchItem<S> {
277        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
278        t.absorb_serialize(&proof.pk_com);
279        let c = S::challenge(&[&proof.r, &proof.ok], Some(t));
280        BatchItem {
281            c,
282            input: io.input.0,
283            output: io.output.0,
284            pk_com: proof.pk_com,
285            r: proof.r,
286            ok: proof.ok,
287            s: proof.s,
288            sb: proof.sb,
289        }
290    }
291
292    /// Push a previously prepared entry into the batch.
293    pub fn push_prepared(&mut self, entry: BatchItem<S>) {
294        self.items.push(entry);
295    }
296
297    /// Prepare and push a proof in one step.
298    pub fn push(&mut self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) {
299        let entry = Self::prepare(ios, ad, proof);
300        self.push_prepared(entry);
301    }
302
303    /// Batch-verify multiple Pedersen proofs using a single multi-scalar multiplication.
304    ///
305    /// For each proof i, two equations are checked with independent random scalars
306    /// t_i (eq1) and u_i (eq2):
307    ///   Eq1: O_i*c_i + Ok_i == I_i*s_i
308    ///   Eq2: Yb_i*c_i + R_i == G*s_i + B*sb_i
309    ///
310    /// The random linear combination yields a (5N + 2)-point MSM.
311    ///
312    /// Returns `Ok(())` if all proofs verify, `Err(VerificationFailure)` otherwise.
313    pub fn verify(&self) -> Result<(), Error> {
314        let items = &self.items;
315        if items.is_empty() {
316            return Ok(());
317        }
318
319        let n = items.len();
320
321        // Generate deterministic random scalars from entry data.
322        // Absorb (c, s, sb) per entry, then squeeze 2N random scalars.
323        // The challenge c already commits to (Yb, I, O, R, Ok, ad), so only the
324        // response scalars s and sb need to be included separately.
325        let mut t = S::Transcript::new(S::SUITE_ID);
326        t.absorb_raw(&[DomSep::PedersenBatch as u8]);
327        for e in items {
328            t.absorb_serialize(&e.c);
329            t.absorb_serialize(&e.s);
330            t.absorb_serialize(&e.sb);
331        }
332        // Sample 2N random 128-bit scalars (t_i for eq1, u_i for eq2).
333        // 128-bit scalars are sufficient for the Schwartz-Zippel soundness argument
334        // (error probability 2^{-128}) and roughly halve the MSM cost compared to
335        // full-width field elements, since fewer doublings are needed in the
336        // Pippenger/Straus window.
337        let random_scalars: Vec<(ScalarField<S>, ScalarField<S>)> = (0..n)
338            .map(|_| {
339                let mut buf = [0u8; 32];
340                t.squeeze_raw(&mut buf);
341                let t = ScalarField::<S>::from_le_bytes_mod_order(&buf[..16]);
342                let u = ScalarField::<S>::from_le_bytes_mod_order(&buf[16..]);
343                (t, u)
344            })
345            .collect();
346
347        // Build MSM: 5N per-proof points + 2 shared bases (G, B)
348        let mut bases = Vec::with_capacity(5 * n + 2);
349        let mut scalars = Vec::with_capacity(5 * n + 2);
350
351        let mut g_scalar = ScalarField::<S>::zero();
352        let mut b_scalar = ScalarField::<S>::zero();
353
354        for (e, (t, u)) in items.iter().zip(random_scalars.iter()) {
355            // Eq1: t_i*c_i*O_i + t_i*Ok_i - t_i*s_i*I_i = 0
356            bases.push(e.output);
357            scalars.push(*t * e.c);
358
359            bases.push(e.ok);
360            scalars.push(*t);
361
362            bases.push(e.input);
363            scalars.push(-(*t * e.s));
364
365            // Eq2: u_i*c_i*Yb_i + u_i*R_i - u_i*s_i*G - u_i*sb_i*B = 0
366            bases.push(e.pk_com);
367            scalars.push(*u * e.c);
368
369            bases.push(e.r);
370            scalars.push(*u);
371
372            // Accumulate shared base scalars
373            g_scalar += *u * e.s;
374            b_scalar += *u * e.sb;
375        }
376
377        // Shared bases: G and B
378        bases.push(S::generator());
379        scalars.push(-g_scalar);
380
381        bases.push(S::BLINDING_BASE);
382        scalars.push(-b_scalar);
383
384        let result = <S::Affine as AffineRepr>::Group::msm_unchecked(&bases, &scalars);
385        if !result.is_zero() {
386            return Err(Error::VerificationFailure);
387        }
388
389        Ok(())
390    }
391}
392
393#[cfg(test)]
394pub(crate) mod testing {
395    use super::*;
396    use crate::testing::{self as common, CheckPoint, SuiteExt, TEST_SEED, random_val};
397
398    pub fn prove_verify<S: PedersenSuite>() {
399        use pedersen::{Prover, Verifier};
400
401        let secret = Secret::<S>::from_seed(TEST_SEED);
402        let input = Input::from_affine_unchecked(random_val(None));
403        let io = secret.vrf_io(input);
404
405        let (proof, blinding) = secret.prove(io, b"foo");
406        let result = Public::verify(io, b"foo", &proof);
407        assert!(result.is_ok());
408
409        assert_eq!(
410            proof.key_commitment(),
411            (secret.public().0 + S::BLINDING_BASE * blinding).into()
412        );
413    }
414
415    pub fn batch_verify<S: PedersenSuite>() {
416        use pedersen::{BatchVerifier, Prover, Verifier};
417
418        let secret = Secret::<S>::from_seed(TEST_SEED);
419        let input = Input::from_affine_unchecked(random_val(None));
420        let io = secret.vrf_io(input);
421
422        let (proof1, _) = secret.prove(io, b"foo");
423        let (proof2, _) = secret.prove(io, b"bar");
424
425        // Single-proof verification still works.
426        assert!(Public::verify(io, b"foo", &proof1).is_ok());
427        assert!(Public::verify(io, b"bar", &proof2).is_ok());
428
429        // Batch using push.
430        let mut batch = BatchVerifier::new();
431        batch.push(io, b"foo", &proof1);
432        batch.push(io, b"bar", &proof2);
433        assert!(batch.verify().is_ok());
434
435        // Batch using prepare + push_prepared.
436        let mut batch = BatchVerifier::new();
437        let entry1 = BatchVerifier::prepare(io, b"foo", &proof1);
438        let entry2 = BatchVerifier::prepare(io, b"bar", &proof2);
439        batch.push_prepared(entry1);
440        batch.push_prepared(entry2);
441        assert!(batch.verify().is_ok());
442
443        // Empty batch is ok.
444        let batch = BatchVerifier::<S>::new();
445        assert!(batch.verify().is_ok());
446
447        // Bad additional data should fail.
448        let mut batch = BatchVerifier::new();
449        batch.push(io, b"foo", &proof1);
450        batch.push(io, b"wrong", &proof2);
451        assert!(batch.verify().is_err());
452    }
453
454    /// N=1 slice produces same proof as passing a single `VrfIo`.
455    pub fn prove_verify_multi_single<S: PedersenSuite>() {
456        use pedersen::{Prover, Verifier};
457
458        let secret = Secret::<S>::from_seed(TEST_SEED);
459        let input = Input::from_affine_unchecked(random_val(None));
460        let io = secret.vrf_io(input);
461
462        let (proof_single, blinding_single) = secret.prove(io, b"foo");
463        let (proof_slice, blinding_slice) = secret.prove([io], b"foo");
464
465        // Byte-identical proofs and blinding factors
466        let encode = |p: &pedersen::Proof<S>| {
467            let mut buf = Vec::new();
468            p.serialize_compressed(&mut buf).unwrap();
469            buf
470        };
471        assert_eq!(encode(&proof_single), encode(&proof_slice));
472        assert_eq!(blinding_single, blinding_slice);
473
474        // Cross-verification
475        assert!(Public::verify(io, b"foo", &proof_slice).is_ok());
476        assert!(Public::verify([io], b"foo", &proof_single).is_ok());
477    }
478
479    /// N=3 multi proof: verify succeeds; tampered output/input/ad fails.
480    pub fn prove_verify_multi<S: PedersenSuite>() {
481        use pedersen::{Prover, Verifier};
482
483        let secret = Secret::<S>::from_seed(TEST_SEED);
484
485        let mut ios: Vec<VrfIo<S>> = (0..3u8)
486            .map(|i| {
487                let input = Input::new(&[i + 1]).unwrap();
488                secret.vrf_io(input)
489            })
490            .collect();
491        ios.push(VrfIo {
492            input: Input(S::Affine::generator()),
493            output: Output(secret.public().0),
494        });
495
496        let (proof, _) = secret.prove(&ios[..], b"bar");
497        assert!(Public::verify(&ios[..], b"bar", &proof).is_ok());
498
499        // Tamper: wrong output on ios[1]
500        let mut bad_ios = ios.clone();
501        bad_ios[1].output = secret.output(ios[0].input);
502        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
503
504        // Tamper: wrong input on ios[0]
505        let mut bad_ios = ios.clone();
506        bad_ios[0].input = ios[1].input;
507        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
508
509        // Tamper: wrong ad
510        assert!(Public::verify(&ios[..], b"baz", &proof).is_err());
511    }
512
513    /// N=0 reduces to a Schnorr signature over the additional data.
514    pub fn prove_verify_multi_empty<S: PedersenSuite>() {
515        use pedersen::{Prover, Verifier};
516
517        let secret = Secret::<S>::from_seed(TEST_SEED);
518
519        let ios: [VrfIo<S>; 0] = [];
520        let (proof, _) = secret.prove(ios, b"bar");
521
522        assert!(Public::verify(ios, b"bar", &proof).is_ok());
523
524        // Wrong ad should fail
525        assert!(Public::verify(ios, b"baz", &proof).is_err());
526    }
527
528    pub fn blinding_base_check<S: PedersenSuite>()
529    where
530        AffinePoint<S>: CheckPoint,
531    {
532        // Check that point has been computed using the magic spell.
533        assert_eq!(
534            S::BLINDING_BASE,
535            S::data_to_point(PEDERSEN_BLINDING_BASE_SEED).unwrap()
536        );
537        // Check that the point is on curve.
538        assert!(S::BLINDING_BASE.check(true).is_ok());
539    }
540
541    #[macro_export]
542    macro_rules! pedersen_suite_tests {
543        ($suite:ty) => {
544            mod pedersen {
545                use super::*;
546
547                #[test]
548                fn prove_verify() {
549                    $crate::pedersen::testing::prove_verify::<$suite>();
550                }
551
552                #[test]
553                fn prove_verify_multi_single() {
554                    $crate::pedersen::testing::prove_verify_multi_single::<$suite>();
555                }
556
557                #[test]
558                fn prove_verify_multi() {
559                    $crate::pedersen::testing::prove_verify_multi::<$suite>();
560                }
561
562                #[test]
563                fn prove_verify_multi_empty() {
564                    $crate::pedersen::testing::prove_verify_multi_empty::<$suite>();
565                }
566
567                #[test]
568                fn batch_verify() {
569                    $crate::pedersen::testing::batch_verify::<$suite>();
570                }
571
572                #[test]
573                fn blinding_base_check() {
574                    $crate::pedersen::testing::blinding_base_check::<$suite>();
575                }
576
577                $crate::test_vectors!($crate::pedersen::testing::TestVector<$suite>);
578            }
579        };
580    }
581
582    pub struct TestVector<S: PedersenSuite> {
583        pub base: common::TestVector<S>,
584        pub blind: ScalarField<S>,
585        pub proof: Proof<S>,
586    }
587
588    impl<S: PedersenSuite> core::fmt::Debug for TestVector<S> {
589        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
590            f.debug_struct("TestVector")
591                .field("base", &self.base)
592                .field("blinding", &self.blind)
593                .field("proof_pk_com", &self.proof.pk_com)
594                .field("proof_r", &self.proof.r)
595                .field("proof_ok", &self.proof.ok)
596                .field("proof_s", &self.proof.s)
597                .field("proof_sb", &self.proof.sb)
598                .finish()
599        }
600    }
601
602    impl<S> common::TestVectorTrait for TestVector<S>
603    where
604        S: PedersenSuite + SuiteExt + std::fmt::Debug,
605    {
606        fn name() -> String {
607            S::SUITE_NAME.to_string() + "_pedersen"
608        }
609
610        fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
611            use super::Prover;
612            let base = common::TestVector::new(comment, seed, alpha, ad);
613            let io = VrfIo {
614                input: Input::<S>::from_affine_unchecked(base.h),
615                output: Output::from_affine_unchecked(base.gamma),
616            };
617            let secret = Secret::from_scalar(base.sk);
618            let (proof, blind) = secret.prove(io, ad);
619            Self { base, blind, proof }
620        }
621
622        fn from_map(map: &common::TestVectorMap) -> Self {
623            let base = common::TestVector::from_map(map);
624            let blind = common::scalar_decode::<S>(&map.get_bytes("blinding"));
625            let pk_com = common::point_decode::<S>(&map.get_bytes("proof_pk_com")).unwrap();
626            let r = common::point_decode::<S>(&map.get_bytes("proof_r")).unwrap();
627            let ok = common::point_decode::<S>(&map.get_bytes("proof_ok")).unwrap();
628            let s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
629            let sb = common::scalar_decode::<S>(&map.get_bytes("proof_sb"));
630            let proof = Proof {
631                pk_com,
632                r,
633                ok,
634                s,
635                sb,
636            };
637            Self { base, blind, proof }
638        }
639
640        fn to_map(&self) -> common::TestVectorMap {
641            let items = [
642                (
643                    "blinding",
644                    hex::encode(common::scalar_encode::<S>(&self.blind)),
645                ),
646                (
647                    "proof_pk_com",
648                    hex::encode(common::point_encode::<S>(&self.proof.pk_com)),
649                ),
650                (
651                    "proof_r",
652                    hex::encode(common::point_encode::<S>(&self.proof.r)),
653                ),
654                (
655                    "proof_ok",
656                    hex::encode(common::point_encode::<S>(&self.proof.ok)),
657                ),
658                (
659                    "proof_s",
660                    hex::encode(common::scalar_encode::<S>(&self.proof.s)),
661                ),
662                (
663                    "proof_sb",
664                    hex::encode(common::scalar_encode::<S>(&self.proof.sb)),
665                ),
666            ];
667            let mut map = self.base.to_map();
668            items.into_iter().for_each(|(name, value)| {
669                map.0.insert(name.to_string(), value);
670            });
671            map
672        }
673
674        fn run(&self) {
675            self.base.run();
676            let io = VrfIo {
677                input: Input::<S>::from_affine_unchecked(self.base.h),
678                output: Output::from_affine_unchecked(self.base.gamma),
679            };
680            let sk = Secret::from_scalar(self.base.sk);
681            let (proof, blind) = sk.prove(io, &self.base.ad);
682            assert_eq!(self.blind, blind, "Blinding factor mismatch");
683            assert_eq!(self.proof.pk_com, proof.pk_com, "Proof pkb mismatch");
684            assert_eq!(self.proof.r, proof.r, "Proof r mismatch");
685            assert_eq!(self.proof.ok, proof.ok, "Proof ok mismatch");
686            assert_eq!(self.proof.s, proof.s, "Proof s mismatch");
687            assert_eq!(self.proof.sb, proof.sb, "Proof sb mismatch");
688
689            assert!(Public::verify(io, &self.base.ad, &proof).is_ok());
690        }
691    }
692}