From 8e31d88c3c06bfca141ceaff9be0ba31e340b9e9 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 7 May 2026 15:02:34 +0200 Subject: [PATCH 1/4] Rename GenerateVerifiable::Capacity to Config --- src/lib.rs | 51 +++++++++++++++++++++++-------------------------- src/mock.rs | 8 ++++---- src/ring/mod.rs | 20 +++++++++---------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f679eb2..2bec6d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,17 +93,21 @@ pub trait GenerateVerifiable { /// For ring VRF implementations, this is typically a G1 affine point from the SRS. type StaticChunk: Clone + Eq + PartialEq + FullCodec + Debug + TypeInfo + MaxEncodedLen; - /// The capacity type used to parametrize ring operations. - type Capacity: Clone + Copy; - /// A signature attributable to a specific `Member`, verifiable against that member's /// public key. /// /// Created via `sign`, verified via `verify_signature`. type Signature: Clone + Eq + PartialEq + FullCodec + Debug + TypeInfo; + /// Per-call configuration for the operations below. + /// + /// Used by implementations that support multiple parameter sets to select + /// which one applies at each call (for ring VRF: which domain/ring size). + /// Set to `()` when there is nothing to configure. + type Config: Clone + Copy; + /// Begin building a `Members` value. - fn start_members(capacity: Self::Capacity) -> Self::Intermediate; + fn start_members(config: Self::Config) -> Self::Intermediate; /// Introduce a set of new `Member`s into the intermediate value used to build a new `Members` /// value. @@ -144,7 +148,7 @@ pub trait GenerateVerifiable { /// implementing the functionality. #[cfg(feature = "prover")] fn open( - capacity: Self::Capacity, + config: Self::Config, member: &Self::Member, members_iter: impl Iterator, ) -> Result; @@ -193,14 +197,14 @@ pub trait GenerateVerifiable { /// if so, ensure that the member is necessarily associated with `alias` in this `context` and /// that they elected to opine `message`. fn is_valid( - capacity: Self::Capacity, + config: Self::Config, proof: &Self::Proof, members: &Self::Members, context: &[u8], alias: &Alias, message: &[u8], ) -> bool { - match Self::validate(capacity, proof, members, context, message) { + match Self::validate(config, proof, members, context, message) { Ok(a) => &a == alias, Err(()) => false, } @@ -210,14 +214,14 @@ pub trait GenerateVerifiable { /// if so, ensure that the member is necessarily associated with corresponding `alias` from the given `aliases` in this `context` and /// that they elected to opine `message`. fn is_valid_multi_context( - capacity: Self::Capacity, + config: Self::Config, proof: &Self::Proof, members: &Self::Members, contexts: &[&[u8]], aliases: &[Alias], message: &[u8], ) -> bool { - match Self::validate_multi_context(capacity, proof, members, contexts, message) { + match Self::validate_multi_context(config, proof, members, contexts, message) { Ok(a) => a == aliases, Err(()) => false, } @@ -228,19 +232,19 @@ pub trait GenerateVerifiable { /// Like `is_valid`, but `alias` is returned, not provided. fn validate( - capacity: Self::Capacity, + config: Self::Config, proof: &Self::Proof, members: &Self::Members, context: &[u8], message: &[u8], ) -> Result { - let result = Self::validate_multi_context(capacity, proof, members, &[context], message)?; + let result = Self::validate_multi_context(config, proof, members, &[context], message)?; Ok(result[0]) } /// Like `is_valid_multi_context`, but aliases are returned, not provided. fn validate_multi_context( - capacity: Self::Capacity, + config: Self::Config, proof: &Self::Proof, members: &Self::Members, contexts: &[&[u8]], @@ -253,15 +257,13 @@ pub trait GenerateVerifiable { /// Currently only supports single-context proofs. Multi-context proofs should be /// validated individually via [`Self::validate_multi_context`]. fn batch_validate( - capacity: Self::Capacity, + config: Self::Config, members: &Self::Members, proofs: &[BatchProofItem], ) -> Result, ()> { proofs .iter() - .map(|item| { - Self::validate(capacity, &item.proof, members, &item.context, &item.message) - }) + .map(|item| Self::validate(config, &item.proof, members, &item.context, &item.message)) .collect() } @@ -293,7 +295,7 @@ impl Receipt { /// Combines [`GenerateVerifiable::open`] and [`GenerateVerifiable::create`]. #[cfg(feature = "prover")] pub fn create<'a>( - capacity: Gen::Capacity, + config: Gen::Config, secret: &Gen::Secret, members: impl Iterator, context: &[u8], @@ -302,7 +304,7 @@ impl Receipt { where Gen::Member: 'a, { - let commitment = Gen::open(capacity, &Gen::member_from_secret(secret), members)?; + let commitment = Gen::open(config, &Gen::member_from_secret(secret), members)?; let (proof, alias) = Gen::create(commitment, secret, context, &message)?; Ok(Self { proof, @@ -328,24 +330,19 @@ impl Receipt { /// the receipt back so it can be inspected or retried. pub fn verify( self, - capacity: Gen::Capacity, + config: Gen::Config, members: &Gen::Members, context: &[u8], ) -> Result<(Alias, Vec), Self> { - match Gen::validate(capacity, &self.proof, members, context, &self.message) { + match Gen::validate(config, &self.proof, members, context, &self.message) { Ok(alias) => Ok((alias, self.message)), Err(()) => Err(self), } } /// Check whether this receipt contains a valid proof for the given `members` and `context`. - pub fn is_valid( - &self, - capacity: Gen::Capacity, - members: &Gen::Members, - context: &[u8], - ) -> bool { + pub fn is_valid(&self, config: Gen::Config, members: &Gen::Members, context: &[u8]) -> bool { Gen::is_valid( - capacity, + config, &self.proof, members, context, diff --git a/src/mock.rs b/src/mock.rs index 54a8191..90f32dd 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -85,9 +85,9 @@ impl GenerateVerifiable for Mock { type Proof = MockProof; type Signature = [u8; 32]; type StaticChunk = (); - type Capacity = (); + type Config = (); - fn start_members(_capacity: Self::Capacity) -> Self::Intermediate { + fn start_members(_config: Self::Config) -> Self::Intermediate { BoundedVec::new() } @@ -119,7 +119,7 @@ impl GenerateVerifiable for Mock { #[cfg(feature = "prover")] fn open( - _capacity: Self::Capacity, + _config: Self::Config, member: &Self::Member, members: impl Iterator, ) -> Result { @@ -155,7 +155,7 @@ impl GenerateVerifiable for Mock { } fn validate_multi_context( - _capacity: Self::Capacity, + _config: Self::Config, proof: &Self::Proof, members: &Self::Members, contexts: &[&[u8]], diff --git a/src/ring/mod.rs b/src/ring/mod.rs index 0e5e88a..bc55bc1 100644 --- a/src/ring/mod.rs +++ b/src/ring/mod.rs @@ -557,11 +557,11 @@ impl GenerateVerifiable for RingVrfVerifiable { type Proof = BoundedVec>; type Signature = S::SignatureBytes; type StaticChunk = StaticChunk; - type Capacity = RingDomainSize; + type Config = RingDomainSize; - fn start_members(capacity: Self::Capacity) -> Self::Intermediate { + fn start_members(config: Self::Config) -> Self::Intermediate { // TODO: Optimize by caching the deserialized value; must be compatible with the WASM runtime environment. - let data = S::CurveParams::empty_ring_commitment(capacity); + let data = S::CurveParams::empty_ring_commitment(config); MembersSet::deserialize_uncompressed_unchecked(data).unwrap() } @@ -605,13 +605,13 @@ impl GenerateVerifiable for RingVrfVerifiable { } fn validate_multi_context( - capacity: Self::Capacity, + config: Self::Config, proof: &Self::Proof, members: &Self::Members, contexts: &[&[u8]], message: &[u8], ) -> Result, ()> { - let verifier_params = S::VerifierCache::get(capacity); + let verifier_params = S::VerifierCache::get(config); let ring_verifier = verifier_params.ring_verifier(members.0.clone()); let signature = RingVrfSignature::::deserialize_canonical(proof.as_slice())?; @@ -647,11 +647,11 @@ impl GenerateVerifiable for RingVrfVerifiable { // 2. maintain a list of caches (one for each verifier key) // - the loop below needs to build the `verifier` using the appropriate verifier key fn batch_validate( - capacity: Self::Capacity, + config: Self::Config, members: &Self::Members, proofs: &[BatchProofItem], ) -> Result, ()> { - let verifier_params = S::VerifierCache::get(capacity); + let verifier_params = S::VerifierCache::get(config); let verifier = verifier_params.ring_verifier(members.0.clone()); let mut aliases = Vec::with_capacity(proofs.len()); @@ -686,7 +686,7 @@ impl GenerateVerifiable for RingVrfVerifiable { #[cfg(feature = "prover")] fn open( - capacity: Self::Capacity, + config: Self::Config, member: &Self::Member, members: impl Iterator, ) -> Result { @@ -695,11 +695,11 @@ impl GenerateVerifiable for RingVrfVerifiable { .collect::, _>>()?; let member = PublicKey::::deserialize_canonical(member.as_ref())?; let prover_idx = pks.iter().position(|&m| m == member.0).ok_or(())? as u32; - let prover_key = S::ProverCache::get(capacity) + let prover_key = S::ProverCache::get(config) .prover_key(&pks) .map_err(|_| ())?; Ok(ProverState { - domain_size: capacity.value(), + domain_size: config.value(), prover_idx, prover_key, }) From 6a8a99f3f23ee318e8c4f47b3f8aa79d79f4c13c Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 7 May 2026 15:16:27 +0200 Subject: [PATCH 2/4] Simplify --- src/ring/bandersnatch.rs | 24 ++++++------------------ src/ring/mod.rs | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/ring/bandersnatch.rs b/src/ring/bandersnatch.rs index fd992a7..e76cdd2 100644 --- a/src/ring/bandersnatch.rs +++ b/src/ring/bandersnatch.rs @@ -30,15 +30,9 @@ impl VerifierCache for BandersnatchVerifierCache { fn get(domain_size: RingDomainSize) -> Self::Handle { use spin::Once; type P = ark_vrf::ring::RingContext; - static D11: Once

= Once::new(); - static D12: Once

= Once::new(); - static D16: Once

= Once::new(); - let init = || make_ring_context(domain_size); - match domain_size { - RingDomainSize::Domain11 => D11.call_once(init), - RingDomainSize::Domain12 => D12.call_once(init), - RingDomainSize::Domain16 => D16.call_once(init), - } + static CELLS: [Once

; RingDomainSize::COUNT] = + [const { Once::new() }; RingDomainSize::COUNT]; + CELLS[domain_size.index()].call_once(|| make_ring_context(domain_size)) } } @@ -55,15 +49,9 @@ impl ProverCache for BandersnatchProverCache { fn get(domain_size: RingDomainSize) -> Self::Handle { use spin::Once; type P = ark_vrf::ring::RingSetup; - static D11: Once

= Once::new(); - static D12: Once

= Once::new(); - static D16: Once

= Once::new(); - let init = || make_ring_setup(domain_size); - match domain_size { - RingDomainSize::Domain11 => D11.call_once(init), - RingDomainSize::Domain12 => D12.call_once(init), - RingDomainSize::Domain16 => D16.call_once(init), - } + static CELLS: [Once

; RingDomainSize::COUNT] = + [const { Once::new() }; RingDomainSize::COUNT]; + CELLS[domain_size.index()].call_once(|| make_ring_setup(domain_size)) } } diff --git a/src/ring/mod.rs b/src/ring/mod.rs index bc55bc1..18a9fc1 100644 --- a/src/ring/mod.rs +++ b/src/ring/mod.rs @@ -5,8 +5,8 @@ pub use ark_vrf; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Valid}; use ark_vrf::{ - VrfIo, ring::{RingSuite, Verifier}, + VrfIo, }; use bounded_collections::{BoundedVec, Get}; use parity_scale_codec::{Decode, Encode}; @@ -47,13 +47,10 @@ impl TryFrom for RingDomainSize { } } -impl From for u32 { - fn from(value: RingDomainSize) -> Self { - value.value() - } -} - impl RingDomainSize { + /// Number of variants. Used to size per-domain caches. + pub(crate) const COUNT: usize = 3; + /// Returns the domain size as a power of 2. pub const fn as_power(self) -> u32 { match self { @@ -72,6 +69,15 @@ impl RingDomainSize { pub const fn max_ring_size(self) -> usize { ark_vrf::ring::max_ring_size_from_pcs_domain_size::(self.value() as usize) } + + /// Stable dense index in `[0, COUNT)`. Used to key per-domain caches. + pub(crate) const fn index(self) -> usize { + match self { + RingDomainSize::Domain11 => 0, + RingDomainSize::Domain12 => 1, + RingDomainSize::Domain16 => 2, + } + } } /// Trait for providing pairing-curve-specific ring data. From 63247612e5c0d90f7505c95ec7cbd1b7442879cd Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 7 May 2026 15:25:33 +0200 Subject: [PATCH 3/4] Simplify --- src/ring/bandersnatch.rs | 57 ++++++++++++++++++---------------------- src/ring/mod.rs | 26 +++++++----------- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/src/ring/bandersnatch.rs b/src/ring/bandersnatch.rs index e76cdd2..3a1f96c 100644 --- a/src/ring/bandersnatch.rs +++ b/src/ring/bandersnatch.rs @@ -6,7 +6,7 @@ use crate::ring::{ pub use ark_vrf::suites::bandersnatch::BandersnatchSha512Ell2; #[cfg(feature = "prover")] -use crate::ring::{ProverCache, make_ring_setup}; +use crate::ring::{make_ring_setup, ProverCache}; /// Bandersnatch ring VRF Verifiable (BandersnatchSha512Ell2 suite). pub type BandersnatchVrfVerifiable = RingVrfVerifiable; @@ -30,9 +30,9 @@ impl VerifierCache for BandersnatchVerifierCache { fn get(domain_size: RingDomainSize) -> Self::Handle { use spin::Once; type P = ark_vrf::ring::RingContext; - static CELLS: [Once

; RingDomainSize::COUNT] = - [const { Once::new() }; RingDomainSize::COUNT]; - CELLS[domain_size.index()].call_once(|| make_ring_context(domain_size)) + static CELLS: [Once

; RingDomainSize::VARIANTS.len()] = + [const { Once::new() }; RingDomainSize::VARIANTS.len()]; + CELLS[domain_size as usize].call_once(|| make_ring_context(domain_size)) } } @@ -49,9 +49,9 @@ impl ProverCache for BandersnatchProverCache { fn get(domain_size: RingDomainSize) -> Self::Handle { use spin::Once; type P = ark_vrf::ring::RingSetup; - static CELLS: [Once

; RingDomainSize::COUNT] = - [const { Once::new() }; RingDomainSize::COUNT]; - CELLS[domain_size.index()].call_once(|| make_ring_setup(domain_size)) + static CELLS: [Once

; RingDomainSize::VARIANTS.len()] = + [const { Once::new() }; RingDomainSize::VARIANTS.len()]; + CELLS[domain_size as usize].call_once(|| make_ring_setup(domain_size)) } } @@ -85,7 +85,7 @@ mod tests { use ark_vrf::ring::SrsLookup; use super::*; - use crate::{GenerateVerifiable, ring::ring_signature_size}; + use crate::{ring::ring_signature_size, GenerateVerifiable}; // Type aliases for Bandersnatch-specific generic types pub type MembersSet = crate::ring::MembersSet; @@ -233,7 +233,7 @@ mod tests { #[cfg(test)] mod builder_tests { - use crate::{GenerateVerifiable, ring::ring_verifier_builder_params}; + use crate::{ring::ring_verifier_builder_params, GenerateVerifiable}; use super::*; use ark_scale::MaxEncodedLen; @@ -242,7 +242,7 @@ mod builder_tests { use parity_scale_codec::{Decode, Encode}; use tests::{ - MembersCommitment, MembersSet, bandersnatch_ring_setup, start_members_from_params, + bandersnatch_ring_setup, start_members_from_params, MembersCommitment, MembersSet, }; /// Macro to generate test functions for all implemented domain sizes. @@ -324,14 +324,7 @@ mod builder_tests { fn generate_empty_ring_builders() { use std::io::Write; - /// All available domain sizes. - const ALL: [RingDomainSize; 3] = [ - RingDomainSize::Domain11, - RingDomainSize::Domain12, - RingDomainSize::Domain16, - ]; - - for domain_size in ALL { + for domain_size in RingDomainSize::VARIANTS { let (builder, builder_params) = start_members_from_params(domain_size); let builder_file = format!( @@ -785,10 +778,12 @@ mod builder_tests { let mut wrong_message_items = batch_items.clone(); wrong_message_items[2].message = b"tampered message".to_vec(); - assert!( - BandersnatchVrfVerifiable::batch_validate(domain_size, &members, &wrong_message_items,) - .is_err() - ); + assert!(BandersnatchVrfVerifiable::batch_validate( + domain_size, + &members, + &wrong_message_items, + ) + .is_err()); // Test with a proof from a non-member key. // Generate a new key that is NOT in the ring, create a proof using a ring @@ -1054,16 +1049,14 @@ mod builder_tests { let proof_malleated: ::Proof = BoundedVec::try_from(bytes).unwrap(); - assert!( - BandersnatchVrfVerifiable::validate( - domain_size, - &proof_malleated, - &members, - context, - message, - ) - .is_err() - ); + assert!(BandersnatchVrfVerifiable::validate( + domain_size, + &proof_malleated, + &members, + context, + message, + ) + .is_err()); // Same check via batch_validate. let batch_items = vec![BatchProofItem { diff --git a/src/ring/mod.rs b/src/ring/mod.rs index 18a9fc1..dc992f5 100644 --- a/src/ring/mod.rs +++ b/src/ring/mod.rs @@ -38,18 +38,19 @@ pub enum RingDomainSize { impl TryFrom for RingDomainSize { type Error = (); fn try_from(value: u32) -> Result { - const ALL: [RingDomainSize; 3] = [ - RingDomainSize::Domain11, - RingDomainSize::Domain12, - RingDomainSize::Domain16, - ]; - ALL.iter().copied().find(|d| d.value() == value).ok_or(()) + Self::VARIANTS + .iter() + .copied() + .find(|d| d.value() == value) + .ok_or(()) } } impl RingDomainSize { - /// Number of variants. Used to size per-domain caches. - pub(crate) const COUNT: usize = 3; + /// All variants, in declaration order. Reuse this instead of redefining + /// the array at call sites. + pub(crate) const VARIANTS: [Self; 3] = + [Self::Domain11, Self::Domain12, Self::Domain16]; /// Returns the domain size as a power of 2. pub const fn as_power(self) -> u32 { @@ -69,15 +70,6 @@ impl RingDomainSize { pub const fn max_ring_size(self) -> usize { ark_vrf::ring::max_ring_size_from_pcs_domain_size::(self.value() as usize) } - - /// Stable dense index in `[0, COUNT)`. Used to key per-domain caches. - pub(crate) const fn index(self) -> usize { - match self { - RingDomainSize::Domain11 => 0, - RingDomainSize::Domain12 => 1, - RingDomainSize::Domain16 => 2, - } - } } /// Trait for providing pairing-curve-specific ring data. From fa003a4ffd52c2d79d7d6a99e3afc5b06674482c Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 7 May 2026 15:25:42 +0200 Subject: [PATCH 4/4] fmt --- src/ring/bandersnatch.rs | 36 ++++++++++++++++++------------------ src/ring/mod.rs | 5 ++--- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ring/bandersnatch.rs b/src/ring/bandersnatch.rs index 3a1f96c..12654dd 100644 --- a/src/ring/bandersnatch.rs +++ b/src/ring/bandersnatch.rs @@ -6,7 +6,7 @@ use crate::ring::{ pub use ark_vrf::suites::bandersnatch::BandersnatchSha512Ell2; #[cfg(feature = "prover")] -use crate::ring::{make_ring_setup, ProverCache}; +use crate::ring::{ProverCache, make_ring_setup}; /// Bandersnatch ring VRF Verifiable (BandersnatchSha512Ell2 suite). pub type BandersnatchVrfVerifiable = RingVrfVerifiable; @@ -85,7 +85,7 @@ mod tests { use ark_vrf::ring::SrsLookup; use super::*; - use crate::{ring::ring_signature_size, GenerateVerifiable}; + use crate::{GenerateVerifiable, ring::ring_signature_size}; // Type aliases for Bandersnatch-specific generic types pub type MembersSet = crate::ring::MembersSet; @@ -233,7 +233,7 @@ mod tests { #[cfg(test)] mod builder_tests { - use crate::{ring::ring_verifier_builder_params, GenerateVerifiable}; + use crate::{GenerateVerifiable, ring::ring_verifier_builder_params}; use super::*; use ark_scale::MaxEncodedLen; @@ -242,7 +242,7 @@ mod builder_tests { use parity_scale_codec::{Decode, Encode}; use tests::{ - bandersnatch_ring_setup, start_members_from_params, MembersCommitment, MembersSet, + MembersCommitment, MembersSet, bandersnatch_ring_setup, start_members_from_params, }; /// Macro to generate test functions for all implemented domain sizes. @@ -778,12 +778,10 @@ mod builder_tests { let mut wrong_message_items = batch_items.clone(); wrong_message_items[2].message = b"tampered message".to_vec(); - assert!(BandersnatchVrfVerifiable::batch_validate( - domain_size, - &members, - &wrong_message_items, - ) - .is_err()); + assert!( + BandersnatchVrfVerifiable::batch_validate(domain_size, &members, &wrong_message_items,) + .is_err() + ); // Test with a proof from a non-member key. // Generate a new key that is NOT in the ring, create a proof using a ring @@ -1049,14 +1047,16 @@ mod builder_tests { let proof_malleated: ::Proof = BoundedVec::try_from(bytes).unwrap(); - assert!(BandersnatchVrfVerifiable::validate( - domain_size, - &proof_malleated, - &members, - context, - message, - ) - .is_err()); + assert!( + BandersnatchVrfVerifiable::validate( + domain_size, + &proof_malleated, + &members, + context, + message, + ) + .is_err() + ); // Same check via batch_validate. let batch_items = vec![BatchProofItem { diff --git a/src/ring/mod.rs b/src/ring/mod.rs index dc992f5..0e9aeb2 100644 --- a/src/ring/mod.rs +++ b/src/ring/mod.rs @@ -5,8 +5,8 @@ pub use ark_vrf; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Valid}; use ark_vrf::{ - ring::{RingSuite, Verifier}, VrfIo, + ring::{RingSuite, Verifier}, }; use bounded_collections::{BoundedVec, Get}; use parity_scale_codec::{Decode, Encode}; @@ -49,8 +49,7 @@ impl TryFrom for RingDomainSize { impl RingDomainSize { /// All variants, in declaration order. Reuse this instead of redefining /// the array at call sites. - pub(crate) const VARIANTS: [Self; 3] = - [Self::Domain11, Self::Domain12, Self::Domain16]; + pub(crate) const VARIANTS: [Self; 3] = [Self::Domain11, Self::Domain12, Self::Domain16]; /// Returns the domain size as a power of 2. pub const fn as_power(self) -> u32 {