From 8c5fd8d6962f80584dfa0100d38275d62f23a35c Mon Sep 17 00:00:00 2001 From: georgepisaltu Date: Fri, 31 Oct 2025 18:54:34 +0200 Subject: [PATCH] Batching proofs Signed-off-by: georgepisaltu --- src/demo_impls.rs | 20 ++++++++++++++++++ src/lib.rs | 26 ++++++++++++++++++++++++ src/ring_vrf_impl.rs | 48 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/demo_impls.rs b/src/demo_impls.rs index 89fc538..00cfe47 100644 --- a/src/demo_impls.rs +++ b/src/demo_impls.rs @@ -17,6 +17,7 @@ impl GenerateVerifiable for Trivial { type Commitment = (Self::Member, Vec); type Proof = [u8; 32]; type Signature = [u8; 32]; + type AccStep = (); type StaticChunk = (); fn is_member_valid(_member: &Self::Member) -> bool { @@ -49,6 +50,15 @@ impl GenerateVerifiable for Trivial { secret.clone() } + fn batch_step( + _proof: &Self::Proof, + _members: &Self::Members, + _context: &[u8], + _message: &[u8], + ) -> Self::AccStep { + () + } + fn open( member: &Self::Member, members: impl Iterator, @@ -117,6 +127,7 @@ impl GenerateVerifiable for Simple { type Commitment = (Self::Member, Vec); type Proof = ([u8; 64], Alias); type Signature = [u8; 64]; + type AccStep = (); type StaticChunk = (); fn is_member_valid(_member: &Self::Member) -> bool { @@ -151,6 +162,15 @@ impl GenerateVerifiable for Simple { pair.public.to_bytes() } + fn batch_step( + _proof: &Self::Proof, + _members: &Self::Members, + _context: &[u8], + _message: &[u8], + ) -> Self::AccStep { + () + } + fn open( member: &Self::Member, members: impl Iterator, diff --git a/src/lib.rs b/src/lib.rs index e6aec8f..7b00fe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,10 @@ pub trait GenerateVerifiable { /// A signature, creatable from a `Secret` for a message and which can be verified as valid /// with respect to the corresponding `Member`. type Signature: Clone + Eq + PartialEq + FullCodec + Debug + TypeInfo; + /// A multi-proof fragment which can be verified in a batch with other such fragments. It is + /// generated using a proof along with the data necessary to verify it in a context (the root of + /// the ring in which it was created, the context and the message). + type AccStep: Clone + Eq + PartialEq + FullCodec + Debug + TypeInfo; type StaticChunk: Clone + Eq + PartialEq + FullCodec + Debug + TypeInfo + MaxEncodedLen; @@ -137,6 +141,16 @@ pub trait GenerateVerifiable { message: &[u8], ) -> Result<(Self::Proof, Alias), ()>; + /// Generate a batch verification step out of a proof and the information needed to verify it + /// independently. The result of this operation should be used in a batch verification operation + /// along with other such steps. + fn batch_step( + _proof: &Self::Proof, + _members: &Self::Members, + _context: &[u8], + _message: &[u8], + ) -> Self::AccStep; + /// Make a non-anonymous signature of `message` using `secret`. fn sign(_secret: &Self::Secret, _message: &[u8]) -> Result { Err(()) @@ -171,6 +185,18 @@ pub trait GenerateVerifiable { Err(()) } + /// Check whether all of the proofs in this batch are valid, returning the `Alias` for each one, + /// in order of input. + fn batch_validate(_steps: Vec) -> Result, ()> { + Err(()) + } + + /// Check whether all of the proofs in this batch are valid and that the resulting `Alias` is + /// the same as the one provided as input. + fn batch_is_valid(_steps: Vec<(Self::AccStep, Alias)>) -> bool { + false + } + fn verify_signature( _signature: &Self::Signature, _message: &[u8], diff --git a/src/ring_vrf_impl.rs b/src/ring_vrf_impl.rs index 02179f9..e6440c1 100644 --- a/src/ring_vrf_impl.rs +++ b/src/ring_vrf_impl.rs @@ -174,11 +174,8 @@ fn make_alias(output: &bandersnatch::Output) -> Alias { pub struct BandersnatchVrfVerifiable; impl BandersnatchVrfVerifiable { - fn to_public_key( - value: &EncodedPublicKey, - ) -> Result { - let pt = - bandersnatch::AffinePoint::deserialize_compressed(&value.0[..]).map_err(|_| ())?; + fn to_public_key(value: &EncodedPublicKey) -> Result { + let pt = bandersnatch::AffinePoint::deserialize_compressed(&value.0[..]).map_err(|_| ())?; Ok(PublicKey(pt.into())) } @@ -199,6 +196,7 @@ impl GenerateVerifiable for BandersnatchVrfVerifiable { type Commitment = (u32, ArkScale); type Proof = [u8; RING_VRF_SIGNATURE_SIZE]; type Signature = [u8; PLAIN_VRF_SIGNATURE_SIZE]; + type AccStep = (Self::Proof, Self::Members, Vec, Vec); type StaticChunk = StaticChunk; fn start_members() -> Self::Intermediate { @@ -243,6 +241,20 @@ impl GenerateVerifiable for BandersnatchVrfVerifiable { Self::to_encoded_public_key(&PublicKey(secret.public().0)) } + fn batch_step( + proof: &Self::Proof, + members: &Self::Members, + context: &[u8], + message: &[u8], + ) -> Self::AccStep { + ( + proof.clone(), + members.clone(), + context.to_vec(), + message.to_vec(), + ) + } + fn validate( proof: &Self::Proof, members: &Self::Members, @@ -272,6 +284,32 @@ impl GenerateVerifiable for BandersnatchVrfVerifiable { Ok(make_alias(&signature.output)) } + fn batch_validate(steps: Vec) -> Result, ()> { + let mut aliases = Vec::with_capacity(steps.len()); + for (proof, members, context, message) in steps.into_iter() { + aliases.push(Self::validate( + &proof, + &members, + &context[..], + &message[..], + )?); + } + Ok(aliases) + } + + fn batch_is_valid(steps: Vec<(Self::AccStep, Alias)>) -> bool { + let proofs = steps.iter().map(|x| x.0.clone()).collect(); + let Ok(aliases) = Self::batch_validate(proofs) else { + return false; + }; + for (actual, expected) in aliases.iter().zip(steps.iter().map(|x| x.1)) { + if *actual != expected { + return false; + } + } + true + } + fn sign(secret: &Self::Secret, message: &[u8]) -> Result { use ark_vrf::ietf::Prover; let input_msg = [VRF_INPUT_DOMAIN, message].concat();