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
244impl<S: PedersenSuite> BatchItem<S> {
245    /// Prepare a proof for batch verification.
246    ///
247    /// Computes the challenge and packages all data needed for deferred
248    /// verification. This is cheap (one hash, no scalar multiplications)
249    /// and can be done in parallel.
250    pub fn new(ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) -> Self {
251        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
252        t.absorb_serialize(&proof.pk_com);
253        let c = S::challenge(&[&proof.r, &proof.ok], Some(t));
254        Self {
255            c,
256            input: io.input.0,
257            output: io.output.0,
258            pk_com: proof.pk_com,
259            r: proof.r,
260            ok: proof.ok,
261            s: proof.s,
262            sb: proof.sb,
263        }
264    }
265}
266
267/// Batch verifier for Pedersen VRF proofs.
268///
269/// Collects multiple proofs and verifies them together via a single
270/// multi-scalar multiplication.
271///
272/// The same subgroup membership assumptions as [`Verifier`] apply to all
273/// points fed into the batch (I/O pairs and proof points).
274pub struct BatchVerifier<S: PedersenSuite> {
275    items: Vec<BatchItem<S>>,
276}
277
278impl<S: PedersenSuite> Default for BatchVerifier<S> {
279    fn default() -> Self {
280        Self { items: Vec::new() }
281    }
282}
283
284impl<S: PedersenSuite> BatchVerifier<S> {
285    /// Create a new empty batch verifier.
286    pub fn new() -> Self {
287        Self::default()
288    }
289
290    /// Push a previously prepared entry into the batch.
291    pub fn push_prepared(&mut self, entry: BatchItem<S>) {
292        self.items.push(entry);
293    }
294
295    /// Prepare and push a proof in one step.
296    pub fn push(&mut self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) {
297        self.push_prepared(BatchItem::new(ios, ad, proof));
298    }
299
300    /// Batch-verify multiple Pedersen proofs using a single multi-scalar multiplication.
301    ///
302    /// For each proof i, two equations are checked with independent random scalars
303    /// t_i (eq1) and u_i (eq2):
304    ///   Eq1: O_i*c_i + Ok_i == I_i*s_i
305    ///   Eq2: Yb_i*c_i + R_i == G*s_i + B*sb_i
306    ///
307    /// The random linear combination yields a (5N + 2)-point MSM.
308    ///
309    /// Returns `Ok(())` if all proofs verify, `Err(VerificationFailure)` otherwise.
310    pub fn verify(&self) -> Result<(), Error> {
311        let items = &self.items;
312        if items.is_empty() {
313            return Ok(());
314        }
315
316        let n = items.len();
317
318        // Generate deterministic random scalars from entry data.
319        // Absorb (c, s, sb) per entry, then squeeze 2N random scalars.
320        // The challenge c already commits to (Yb, I, O, R, Ok, ad), so only the
321        // response scalars s and sb need to be included separately.
322        let mut t = S::Transcript::new(S::SUITE_ID);
323        t.absorb_raw(&[DomSep::BatchVerify as u8]);
324        for e in items {
325            t.absorb_serialize(&e.c);
326            t.absorb_serialize(&e.s);
327            t.absorb_serialize(&e.sb);
328        }
329        // Sample 2N random 128-bit scalars (t_i for eq1, u_i for eq2).
330        // 128-bit scalars are sufficient for the Schwartz-Zippel soundness argument
331        // (error probability 2^{-128}) and roughly halve the MSM cost compared to
332        // full-width field elements, since fewer doublings are needed in the
333        // Pippenger/Straus window.
334        let random_scalars: Vec<(ScalarField<S>, ScalarField<S>)> = (0..n)
335            .map(|_| {
336                let mut buf = [0u8; 32];
337                t.squeeze_raw(&mut buf);
338                let t = ScalarField::<S>::from_le_bytes_mod_order(&buf[..16]);
339                let u = ScalarField::<S>::from_le_bytes_mod_order(&buf[16..]);
340                (t, u)
341            })
342            .collect();
343
344        // Build MSM: 5N per-proof points + 2 shared bases (G, B)
345        let mut bases = Vec::with_capacity(5 * n + 2);
346        let mut scalars = Vec::with_capacity(5 * n + 2);
347
348        let mut g_scalar = ScalarField::<S>::zero();
349        let mut b_scalar = ScalarField::<S>::zero();
350
351        for (e, (t, u)) in items.iter().zip(random_scalars.iter()) {
352            // Eq1: t_i*c_i*O_i + t_i*Ok_i - t_i*s_i*I_i = 0
353            bases.push(e.output);
354            scalars.push(*t * e.c);
355
356            bases.push(e.ok);
357            scalars.push(*t);
358
359            bases.push(e.input);
360            scalars.push(-(*t * e.s));
361
362            // Eq2: u_i*c_i*Yb_i + u_i*R_i - u_i*s_i*G - u_i*sb_i*B = 0
363            bases.push(e.pk_com);
364            scalars.push(*u * e.c);
365
366            bases.push(e.r);
367            scalars.push(*u);
368
369            // Accumulate shared base scalars
370            g_scalar += *u * e.s;
371            b_scalar += *u * e.sb;
372        }
373
374        // Shared bases: G and B
375        bases.push(S::generator());
376        scalars.push(-g_scalar);
377
378        bases.push(S::BLINDING_BASE);
379        scalars.push(-b_scalar);
380
381        let result = <S::Affine as AffineRepr>::Group::msm_unchecked(&bases, &scalars);
382        if !result.is_zero() {
383            return Err(Error::VerificationFailure);
384        }
385
386        Ok(())
387    }
388}
389
390#[cfg(test)]
391pub(crate) mod testing {
392    use super::*;
393    use crate::testing::{self as common, CheckPoint, SuiteExt, TEST_SEED, random_val};
394
395    pub fn prove_verify<S: PedersenSuite>() {
396        use pedersen::{Prover, Verifier};
397
398        let secret = Secret::<S>::from_seed(TEST_SEED);
399        let input = Input::from_affine_unchecked(random_val(None));
400        let io = secret.vrf_io(input);
401
402        let (proof, blinding) = secret.prove(io, b"foo");
403        let result = Public::verify(io, b"foo", &proof);
404        assert!(result.is_ok());
405
406        assert_eq!(
407            proof.key_commitment(),
408            (secret.public().0 + S::BLINDING_BASE * blinding).into()
409        );
410    }
411
412    pub fn batch_verify<S: PedersenSuite>() {
413        use pedersen::{BatchItem, BatchVerifier, Prover, Verifier};
414
415        let secret = Secret::<S>::from_seed(TEST_SEED);
416        let input = Input::from_affine_unchecked(random_val(None));
417        let io = secret.vrf_io(input);
418
419        let (proof1, _) = secret.prove(io, b"foo");
420        let (proof2, _) = secret.prove(io, b"bar");
421
422        // Single-proof verification still works.
423        assert!(Public::verify(io, b"foo", &proof1).is_ok());
424        assert!(Public::verify(io, b"bar", &proof2).is_ok());
425
426        // Batch using push.
427        let mut batch = BatchVerifier::new();
428        batch.push(io, b"foo", &proof1);
429        batch.push(io, b"bar", &proof2);
430        assert!(batch.verify().is_ok());
431
432        // Batch using BatchItem::new + push_prepared.
433        let mut batch = BatchVerifier::new();
434        let entry1 = BatchItem::new(io, b"foo", &proof1);
435        let entry2 = BatchItem::new(io, b"bar", &proof2);
436        batch.push_prepared(entry1);
437        batch.push_prepared(entry2);
438        assert!(batch.verify().is_ok());
439
440        // Empty batch is ok.
441        let batch = BatchVerifier::<S>::new();
442        assert!(batch.verify().is_ok());
443
444        // Bad additional data should fail.
445        let mut batch = BatchVerifier::new();
446        batch.push(io, b"foo", &proof1);
447        batch.push(io, b"wrong", &proof2);
448        assert!(batch.verify().is_err());
449    }
450
451    /// N=1 slice produces same proof as passing a single `VrfIo`.
452    pub fn prove_verify_multi_single<S: PedersenSuite>() {
453        use pedersen::{Prover, Verifier};
454
455        let secret = Secret::<S>::from_seed(TEST_SEED);
456        let input = Input::from_affine_unchecked(random_val(None));
457        let io = secret.vrf_io(input);
458
459        let (proof_single, blinding_single) = secret.prove(io, b"foo");
460        let (proof_slice, blinding_slice) = secret.prove([io], b"foo");
461
462        // Byte-identical proofs and blinding factors
463        let encode = |p: &pedersen::Proof<S>| {
464            let mut buf = Vec::new();
465            p.serialize_compressed(&mut buf).unwrap();
466            buf
467        };
468        assert_eq!(encode(&proof_single), encode(&proof_slice));
469        assert_eq!(blinding_single, blinding_slice);
470
471        // Cross-verification
472        assert!(Public::verify(io, b"foo", &proof_slice).is_ok());
473        assert!(Public::verify([io], b"foo", &proof_single).is_ok());
474    }
475
476    /// N=3 multi proof: verify succeeds; tampered output/input/ad fails.
477    pub fn prove_verify_multi<S: PedersenSuite>() {
478        use pedersen::{Prover, Verifier};
479
480        let secret = Secret::<S>::from_seed(TEST_SEED);
481
482        let mut ios: Vec<VrfIo<S>> = (0..3u8)
483            .map(|i| {
484                let input = Input::new(&[i + 1]).unwrap();
485                secret.vrf_io(input)
486            })
487            .collect();
488        ios.push(VrfIo {
489            input: Input(S::Affine::generator()),
490            output: Output(secret.public().0),
491        });
492
493        let (proof, _) = secret.prove(&ios[..], b"bar");
494        assert!(Public::verify(&ios[..], b"bar", &proof).is_ok());
495
496        // Tamper: wrong output on ios[1]
497        let mut bad_ios = ios.clone();
498        bad_ios[1].output = secret.output(ios[0].input);
499        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
500
501        // Tamper: wrong input on ios[0]
502        let mut bad_ios = ios.clone();
503        bad_ios[0].input = ios[1].input;
504        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
505
506        // Tamper: wrong ad
507        assert!(Public::verify(&ios[..], b"baz", &proof).is_err());
508    }
509
510    /// N=0 reduces to a Schnorr signature over the additional data.
511    pub fn prove_verify_multi_empty<S: PedersenSuite>() {
512        use pedersen::{Prover, Verifier};
513
514        let secret = Secret::<S>::from_seed(TEST_SEED);
515
516        let ios: [VrfIo<S>; 0] = [];
517        let (proof, _) = secret.prove(ios, b"bar");
518
519        assert!(Public::verify(ios, b"bar", &proof).is_ok());
520
521        // Wrong ad should fail
522        assert!(Public::verify(ios, b"baz", &proof).is_err());
523    }
524
525    pub fn blinding_base_check<S: PedersenSuite>()
526    where
527        AffinePoint<S>: CheckPoint,
528    {
529        // Check that point has been computed using the magic spell.
530        assert_eq!(
531            S::BLINDING_BASE,
532            S::data_to_point(PEDERSEN_BLINDING_BASE_SEED).unwrap()
533        );
534        // Check that the point is on curve.
535        assert!(S::BLINDING_BASE.check(true).is_ok());
536    }
537
538    #[macro_export]
539    macro_rules! pedersen_suite_tests {
540        ($suite:ty) => {
541            mod pedersen {
542                use super::*;
543
544                #[test]
545                fn prove_verify() {
546                    $crate::pedersen::testing::prove_verify::<$suite>();
547                }
548
549                #[test]
550                fn prove_verify_multi_single() {
551                    $crate::pedersen::testing::prove_verify_multi_single::<$suite>();
552                }
553
554                #[test]
555                fn prove_verify_multi() {
556                    $crate::pedersen::testing::prove_verify_multi::<$suite>();
557                }
558
559                #[test]
560                fn prove_verify_multi_empty() {
561                    $crate::pedersen::testing::prove_verify_multi_empty::<$suite>();
562                }
563
564                #[test]
565                fn batch_verify() {
566                    $crate::pedersen::testing::batch_verify::<$suite>();
567                }
568
569                #[test]
570                fn blinding_base_check() {
571                    $crate::pedersen::testing::blinding_base_check::<$suite>();
572                }
573
574                $crate::test_vectors!($crate::pedersen::testing::TestVector<$suite>);
575            }
576        };
577    }
578
579    pub struct TestVector<S: PedersenSuite> {
580        pub base: common::TestVector<S>,
581        pub blind: ScalarField<S>,
582        pub proof: Proof<S>,
583    }
584
585    impl<S: PedersenSuite> core::fmt::Debug for TestVector<S> {
586        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
587            f.debug_struct("TestVector")
588                .field("base", &self.base)
589                .field("blinding", &self.blind)
590                .field("proof_pk_com", &self.proof.pk_com)
591                .field("proof_r", &self.proof.r)
592                .field("proof_ok", &self.proof.ok)
593                .field("proof_s", &self.proof.s)
594                .field("proof_sb", &self.proof.sb)
595                .finish()
596        }
597    }
598
599    impl<S> common::TestVectorTrait for TestVector<S>
600    where
601        S: PedersenSuite + SuiteExt + std::fmt::Debug,
602    {
603        fn name() -> String {
604            S::SUITE_NAME.to_string() + "_pedersen"
605        }
606
607        fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
608            use super::Prover;
609            let base = common::TestVector::new(comment, seed, alpha, ad);
610            let io = VrfIo {
611                input: Input::<S>::from_affine_unchecked(base.h),
612                output: Output::from_affine_unchecked(base.gamma),
613            };
614            let secret = Secret::from_scalar(base.sk);
615            let (proof, blind) = secret.prove(io, ad);
616            Self { base, blind, proof }
617        }
618
619        fn from_map(map: &common::TestVectorMap) -> Self {
620            let base = common::TestVector::from_map(map);
621            let blind = common::scalar_decode::<S>(&map.get_bytes("blinding"));
622            let pk_com = common::point_decode::<S>(&map.get_bytes("proof_pk_com")).unwrap();
623            let r = common::point_decode::<S>(&map.get_bytes("proof_r")).unwrap();
624            let ok = common::point_decode::<S>(&map.get_bytes("proof_ok")).unwrap();
625            let s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
626            let sb = common::scalar_decode::<S>(&map.get_bytes("proof_sb"));
627            let proof = Proof {
628                pk_com,
629                r,
630                ok,
631                s,
632                sb,
633            };
634            Self { base, blind, proof }
635        }
636
637        fn to_map(&self) -> common::TestVectorMap {
638            let items = [
639                (
640                    "blinding",
641                    hex::encode(common::scalar_encode::<S>(&self.blind)),
642                ),
643                (
644                    "proof_pk_com",
645                    hex::encode(common::point_encode::<S>(&self.proof.pk_com)),
646                ),
647                (
648                    "proof_r",
649                    hex::encode(common::point_encode::<S>(&self.proof.r)),
650                ),
651                (
652                    "proof_ok",
653                    hex::encode(common::point_encode::<S>(&self.proof.ok)),
654                ),
655                (
656                    "proof_s",
657                    hex::encode(common::scalar_encode::<S>(&self.proof.s)),
658                ),
659                (
660                    "proof_sb",
661                    hex::encode(common::scalar_encode::<S>(&self.proof.sb)),
662                ),
663            ];
664            let mut map = self.base.to_map();
665            items.into_iter().for_each(|(name, value)| {
666                map.0.insert(name.to_string(), value);
667            });
668            map
669        }
670
671        fn run(&self) {
672            self.base.run();
673            let io = VrfIo {
674                input: Input::<S>::from_affine_unchecked(self.base.h),
675                output: Output::from_affine_unchecked(self.base.gamma),
676            };
677            let sk = Secret::from_scalar(self.base.sk);
678            let (proof, blind) = sk.prove(io, &self.base.ad);
679            assert_eq!(self.blind, blind, "Blinding factor mismatch");
680            assert_eq!(self.proof.pk_com, proof.pk_com, "Proof pkb mismatch");
681            assert_eq!(self.proof.r, proof.r, "Proof r mismatch");
682            assert_eq!(self.proof.ok, proof.ok, "Proof ok mismatch");
683            assert_eq!(self.proof.s, proof.s, "Proof s mismatch");
684            assert_eq!(self.proof.sb, proof.sb, "Proof sb mismatch");
685
686            assert!(Public::verify(io, &self.base.ad, &proof).is_ok());
687        }
688    }
689}