1mod banderwagon;
2
3use crate::{
4 Secret,
5 bls12381::primitives::group::{G1, Scalar, ScalarReadCfg},
6 transcript::{Summary, Transcript, Version},
7 zk::{
8 bulletproofs::circuit::{self, prove, verify},
9 pedersen_to_plain,
10 },
11};
12use banderwagon::{F, G, vrf_batch_checked, vrf_batch_checked_circuit, vrf_recv};
13use bytes::{BufMut, Bytes};
14use commonware_codec::{
15 Buf, Copying, Encode, EncodeFixed, EncodeSize, Error as CodecError, FixedArray, FixedSize,
16 Read, ReadExt, Write,
17};
18use commonware_formatting::hex;
19use commonware_math::algebra::{Additive as _, CryptoGroup, Random};
20use commonware_parallel::Strategy;
21use commonware_utils::{
22 Array, Span, TryCollect,
23 ordered::{Map, Set},
24};
25use core::{
26 fmt::{Debug, Display},
27 hash::{Hash, Hasher},
28 ops::Deref,
29};
30use rand_core::CryptoRng;
31use std::num::NonZeroU32;
32use zeroize::Zeroizing;
33
34const SCHNORR_NS: &[u8] = b"_COMMONWARE_CRYPTOGRAPHY_BANDERSNATCH_SCHNORR";
35
36const BULLETPROOFS_DST: &[u8] = b"_COMMONWARE_CRYPTOGRAPHY_GOLDEN_DKG_BULLETPROOFS";
37
38const WIRES_PER_PLAYER: usize = 2247;
49const WIRES_BASE: usize = 1327;
50
51const fn lg_len_for_players(num_players: u32) -> u8 {
57 let internal = WIRES_PER_PLAYER * (num_players as usize) + WIRES_BASE;
58 let mut padded: usize = 1;
60 let mut lg: u8 = 0;
61 while padded < internal {
62 padded <<= 1;
63 lg += 1;
64 }
65 lg
66}
67
68pub struct Setup {
85 inner: circuit::Setup<G1>,
86 max_players: NonZeroU32,
87}
88
89impl Setup {
90 pub fn new(max_players: NonZeroU32) -> Self {
96 let lg_len = lg_len_for_players(max_players.get());
97 let inner = circuit::Setup::hashed(BULLETPROOFS_DST, lg_len, G1::generator());
101 Self { inner, max_players }
102 }
103
104 #[must_use]
107 pub const fn supports(&self, num_players: u32) -> bool {
108 num_players <= self.max_players.get()
109 }
110
111 pub(super) const fn max_players(&self) -> NonZeroU32 {
113 self.max_players
114 }
115
116 pub(super) const fn inner(&self) -> &circuit::Setup<G1> {
117 &self.inner
118 }
119}
120
121impl Write for Setup {
122 fn write(&self, buf: &mut impl BufMut) {
123 self.max_players.get().write(buf);
124 self.inner.write(buf);
125 }
126}
127
128impl EncodeSize for Setup {
129 fn encode_size(&self) -> usize {
130 self.max_players.get().encode_size() + self.inner.encode_size()
131 }
132}
133
134impl Read for Setup {
135 type Cfg = NonZeroU32;
138
139 fn read_cfg(buf: &mut impl Buf, expected_max_players: &Self::Cfg) -> Result<Self, CodecError> {
140 let max_players_raw = u32::read(buf)?;
141 let max_players = NonZeroU32::new(max_players_raw)
142 .ok_or(CodecError::Invalid("Setup", "max_players must be nonzero"))?;
143 if max_players != *expected_max_players {
144 return Err(CodecError::Invalid("Setup", "max_players mismatch"));
145 }
146 let lg_len = lg_len_for_players(max_players.get());
147 let max_len = 1usize << lg_len;
148 let inner = circuit::Setup::<G1>::read_cfg(buf, &(max_len, ()))?;
149 if !inner.supports(lg_len) {
150 return Err(CodecError::Invalid("Setup", "inner setup too small"));
151 }
152 Ok(Self { inner, max_players })
153 }
154}
155
156#[derive(Clone, Debug)]
157pub struct PrivateKey {
158 inner: Secret<F>,
159}
160
161impl Random for PrivateKey {
162 fn random(rng: impl CryptoRng) -> Self {
163 Self {
164 inner: Secret::new(F::random(rng)),
165 }
166 }
167}
168
169impl crate::Signer for PrivateKey {
170 type Signature = Signature;
171 type PublicKey = PublicKey;
172
173 fn public_key(&self) -> Self::PublicKey {
174 self.inner
175 .expose(|x| PublicKey::from_point(G::generator() * x))
176 }
177
178 fn sign(&self, namespace: &[u8], msg: &[u8]) -> Signature {
179 let pk = self.public();
180 let mut t = Transcript::new(SCHNORR_NS, Version::V1);
181 t.commit(namespace).commit(msg).commit(pk.raw.as_slice());
182
183 let k = self.inner.expose(|x| {
185 let mut nonce_t = t.fork(b"nonce");
186 let x_bytes = Zeroizing::new(x.encode_fixed::<{ F::SIZE }>());
187 nonce_t.commit(x_bytes.as_slice());
188 F::random(nonce_t.noise(b"k"))
189 });
190
191 let k_big = G::generator() * &k;
192 let k_big_bytes: [u8; G::SIZE] = k_big.encode_fixed();
193 t.commit(k_big_bytes.as_slice());
194 let e = F::random(t.noise(b"challenge"));
195
196 let s = self.inner.expose(|x| e * x + &k);
198
199 let mut raw = [0u8; Signature::SIZE];
200 raw[..G::SIZE].copy_from_slice(&k_big_bytes);
201 raw[G::SIZE..].copy_from_slice(&s.encode_fixed::<{ F::SIZE }>());
202 Signature { raw }
203 }
204}
205
206impl PrivateKey {
207 pub fn public(&self) -> PublicKey {
209 crate::Signer::public_key(self)
210 }
211
212 pub(super) fn vrf_recv(&self, msg: &Summary, sender: &PublicKey) -> Scalar {
221 self.inner
222 .expose(|inner| vrf_recv(msg, &sender.point, inner))
223 }
224
225 pub(super) fn vrf_batch_checked(
232 &self,
233 rng: &mut impl CryptoRng,
234 setup: &Setup,
235 transcript: &mut Transcript,
236 msg: &Summary,
237 receivers: impl IntoIterator<Item = PublicKey>,
238 strategy: &impl Strategy,
239 ) -> (Map<PublicKey, Scalar>, VrfCommitments) {
240 let receivers = Map::from_iter_dedup(receivers.into_iter().map(|x| {
241 let point = x.point.clone();
242 (x, point)
243 }));
244 let (circuit, witness) = self
245 .inner
246 .expose(|x| vrf_batch_checked(msg, x, receivers.values()));
247 let claim = witness.claim(setup.inner());
248 let circuit_proof = prove(
249 &mut *rng,
250 transcript,
251 setup.inner(),
252 &circuit,
253 &claim,
254 &witness,
255 strategy,
256 )
257 .expect("proving should succeed");
258
259 let keys = receivers.into_keys();
261 let outputs = Map::from_parts(keys.clone(), witness.values().to_vec())
262 .expect("witness should have one output per receiver");
263 let commitments = Map::from_parts(keys, claim.commitments)
264 .expect("claim should have one commitment per receiver");
265 let pedersen_to_plain = {
266 let setup = pedersen_to_plain::Setup {
267 value_generator: *setup.inner().value_generator(),
268 blinding_generator: *setup.inner().blinding_generator(),
269 };
270 let mut out = Vec::new();
271 for (receiver, output) in outputs.iter_pairs() {
272 let commitment = *commitments
273 .get_value(receiver)
274 .expect("output should have commitment");
275 let proof = pedersen_to_plain::prove(
276 &mut *rng,
277 transcript,
278 &setup,
279 &pedersen_to_plain::Claim {
280 plain: commitment,
281 pedersen: commitment,
282 },
283 &pedersen_to_plain::Witness {
284 value: output.clone(),
285 blinding: Scalar::zero(),
286 },
287 );
288 out.push(proof);
289 }
290 out
291 };
292 let proof = Proof {
293 circuit_proof,
294 pedersen_to_plain,
295 };
296 (outputs, VrfCommitments { proof, commitments })
297 }
298}
299
300impl Write for PrivateKey {
301 fn write(&self, buf: &mut impl BufMut) {
302 self.inner
303 .expose(|x| buf.put_slice(&x.encode_fixed::<{ F::SIZE }>()));
304 }
305}
306
307impl Read for PrivateKey {
308 type Cfg = ();
309
310 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
311 let raw = Zeroizing::new(<[u8; Self::SIZE]>::read(buf)?);
312 let x: F = ReadExt::read(&mut Copying(raw.as_slice()))?;
313 Ok(Self {
314 inner: Secret::new(x),
315 })
316 }
317}
318
319impl FixedSize for PrivateKey {
320 const SIZE: usize = F::SIZE;
321}
322
323#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, FixedArray)]
327pub struct Signature {
328 raw: [u8; G::SIZE + F::SIZE],
329}
330
331impl Write for Signature {
332 fn write(&self, buf: &mut impl BufMut) {
333 self.raw.write(buf);
334 }
335}
336
337impl Read for Signature {
338 type Cfg = ();
339
340 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
341 let raw = <[u8; Self::SIZE]>::read(buf)?;
342 Ok(Self { raw })
343 }
344}
345
346impl FixedSize for Signature {
347 const SIZE: usize = G::SIZE + F::SIZE;
348}
349
350impl crate::Signature for Signature {}
351
352impl Span for Signature {}
353
354impl Array for Signature {}
355
356impl AsRef<[u8]> for Signature {
357 fn as_ref(&self) -> &[u8] {
358 &self.raw
359 }
360}
361
362impl Deref for Signature {
363 type Target = [u8];
364 fn deref(&self) -> &[u8] {
365 &self.raw
366 }
367}
368
369impl Debug for Signature {
370 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
371 write!(f, "{}", hex(&self.raw))
372 }
373}
374
375impl Display for Signature {
376 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
377 write!(f, "{}", hex(&self.raw))
378 }
379}
380
381#[derive(Clone, FixedArray)]
385pub struct PublicKey {
386 raw: [u8; G::SIZE],
387 point: G,
388}
389
390impl PublicKey {
391 fn from_point(point: G) -> Self {
392 let raw: [u8; G::SIZE] = point.encode_fixed();
393 Self { raw, point }
394 }
395}
396
397impl crate::Verifier for PublicKey {
398 type Signature = Signature;
399
400 fn verify(&self, namespace: &[u8], msg: &[u8], sig: &Signature) -> bool {
401 let k_big: G = match ReadExt::read(&mut Copying(&sig.raw[..G::SIZE])) {
402 Ok(p) => p,
403 Err(_) => return false,
404 };
405 let s: F = match ReadExt::read(&mut Copying(&sig.raw[G::SIZE..])) {
406 Ok(s) => s,
407 Err(_) => return false,
408 };
409
410 let mut t = Transcript::new(SCHNORR_NS, Version::V1);
412 t.commit(namespace)
413 .commit(msg)
414 .commit(self.raw.as_slice())
415 .commit(sig.raw[..G::SIZE].as_ref());
416 let e = F::random(t.noise(b"challenge"));
417
418 let lhs = G::generator() * &s;
420 let rhs = k_big + &(self.point.clone() * &e);
421 lhs == rhs
422 }
423}
424
425impl crate::PublicKey for PublicKey {}
426
427impl Write for PublicKey {
428 fn write(&self, buf: &mut impl BufMut) {
429 self.raw.write(buf);
430 }
431}
432
433impl Read for PublicKey {
434 type Cfg = ();
435
436 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
437 let raw = <[u8; Self::SIZE]>::read(buf)?;
438 let point: G = ReadExt::read(&mut Copying(&raw))?;
439 Ok(Self { raw, point })
440 }
441}
442
443impl FixedSize for PublicKey {
444 const SIZE: usize = G::SIZE;
445}
446
447impl Span for PublicKey {}
448
449impl Array for PublicKey {}
450
451impl AsRef<[u8]> for PublicKey {
452 fn as_ref(&self) -> &[u8] {
453 &self.raw
454 }
455}
456
457impl Deref for PublicKey {
458 type Target = [u8];
459 fn deref(&self) -> &[u8] {
460 &self.raw
461 }
462}
463
464impl Eq for PublicKey {}
465
466impl PartialEq for PublicKey {
467 fn eq(&self, other: &Self) -> bool {
468 self.raw == other.raw
469 }
470}
471
472impl Ord for PublicKey {
473 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
474 self.raw.cmp(&other.raw)
475 }
476}
477
478impl PartialOrd for PublicKey {
479 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
480 Some(self.cmp(other))
481 }
482}
483
484impl Hash for PublicKey {
485 fn hash<H: Hasher>(&self, state: &mut H) {
486 self.raw.hash(state);
487 }
488}
489
490impl Debug for PublicKey {
491 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
492 write!(f, "{}", hex(self))
493 }
494}
495
496impl Display for PublicKey {
497 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
498 write!(f, "{}", hex(self))
499 }
500}
501
502#[derive(Clone)]
505struct Proof {
506 circuit_proof: circuit::Proof<Scalar, G1>,
507 pedersen_to_plain: Vec<pedersen_to_plain::Proof<Scalar, G1>>,
508}
509
510impl Write for Proof {
511 fn write(&self, buf: &mut impl BufMut) {
512 self.circuit_proof.write(buf);
513 self.pedersen_to_plain.write(buf);
514 }
515}
516
517impl EncodeSize for Proof {
518 fn encode_size(&self) -> usize {
519 self.circuit_proof.encode_size() + self.pedersen_to_plain.encode_size()
520 }
521}
522
523impl Read for Proof {
524 type Cfg = NonZeroU32;
529
530 fn read_cfg(buf: &mut impl Buf, max_players: &Self::Cfg) -> Result<Self, CodecError> {
531 let max_proof_len = 1usize << lg_len_for_players(max_players.get());
532 let circuit_proof = circuit::Proof::<Scalar, G1>::read_cfg(
533 buf,
534 &(max_proof_len, ((), ScalarReadCfg::AllowZero)),
535 )?;
536 let range = commonware_codec::RangeCfg::new(0..=max_players.get() as usize);
537 let pedersen_to_plain = Vec::<pedersen_to_plain::Proof<Scalar, G1>>::read_cfg(
538 buf,
539 &(range, ((), ScalarReadCfg::AllowZero)),
540 )?;
541 Ok(Self {
542 circuit_proof,
543 pedersen_to_plain,
544 })
545 }
546}
547
548impl Write for VrfCommitments {
549 fn write(&self, buf: &mut impl BufMut) {
550 self.proof.write(buf);
551 self.commitments.write(buf);
552 }
553}
554
555impl EncodeSize for VrfCommitments {
556 fn encode_size(&self) -> usize {
557 self.proof.encode_size() + self.commitments.encode_size()
558 }
559}
560
561impl Read for VrfCommitments {
562 type Cfg = NonZeroU32;
563
564 fn read_cfg(buf: &mut impl Buf, max_players: &Self::Cfg) -> Result<Self, CodecError> {
565 let proof = Proof::read_cfg(buf, max_players)?;
566 let range = commonware_codec::RangeCfg::new(0..=max_players.get() as usize);
567 let commitments = Read::read_cfg(buf, &(range, (), ()))?;
568 Ok(Self { proof, commitments })
569 }
570}
571
572#[derive(Clone)]
577pub struct VrfCommitments {
578 proof: Proof,
579 commitments: Map<PublicKey, G1>,
580}
581
582impl VrfCommitments {
583 #[cfg(any(feature = "arbitrary", test))]
586 pub(super) fn perturb(&mut self, receiver: &PublicKey, delta: &G1) {
587 if let Some(c) = self.commitments.get_value_mut(receiver) {
588 *c += delta;
589 }
590 }
591
592 pub fn check_batch(
617 rng: &mut impl CryptoRng,
618 setup: &Setup,
619 transcript: &Transcript,
620 players: &Set<PublicKey>,
621 outputs: impl IntoIterator<Item = (PublicKey, Bytes, Self)>,
622 strategy: &impl Strategy,
623 ) -> Map<PublicKey, Map<PublicKey, G1>> {
624 let outputs: Vec<(PublicKey, Bytes, Self)> = outputs
635 .into_iter()
636 .filter_map(|(sender, msg, commitments)| {
637 let mut buf = msg.clone();
638 let _: Summary = ReadExt::read(&mut buf).ok()?;
639 if commitments.proof.pedersen_to_plain.len() != commitments.commitments.len() {
640 return None;
641 }
642 if commitments
643 .commitments
644 .keys()
645 .iter()
646 .any(|pk| players.position(pk).is_none())
647 {
648 return None;
649 }
650 Some((sender, msg, commitments))
651 })
652 .collect();
653
654 let per_sender = setup.inner().eval_check_batched(
658 rng,
659 |vs, rng| {
660 let pp_setup = pedersen_to_plain::Setup {
664 value_generator: vs.value_generator().clone(),
665 blinding_generator: vs.blinding_generator().clone(),
666 };
667
668 let mut per_sender = Vec::with_capacity(outputs.len());
669 for (sender, msg, commitments) in &outputs {
670 let receivers: Vec<G> = commitments
674 .commitments
675 .keys()
676 .iter()
677 .map(|pk| pk.point.clone())
678 .collect();
679 let circuit =
680 vrf_batch_checked_circuit(msg.as_ref(), &sender.point, &receivers);
681 let claim = circuit::Claim {
682 commitments: commitments.commitments.values().to_vec(),
683 };
684
685 let mut t = transcript.fork(b"dealer vrf");
689 t.commit(sender.encode());
690
691 let Some(circuit_synth) = verify(
692 &mut *rng,
693 &mut t,
694 vs,
695 &circuit,
696 &claim,
697 commitments.proof.circuit_proof.clone(),
698 strategy,
699 ) else {
700 per_sender.push(None);
704 continue;
705 };
706 let mut sender_acc = circuit_synth * &Scalar::random(&mut *rng);
707
708 for ((_, comm), pp_proof) in commitments
711 .commitments
712 .iter_pairs()
713 .zip(commitments.proof.pedersen_to_plain.iter().cloned())
714 {
715 let pp_claim = pedersen_to_plain::Claim {
716 plain: *comm,
717 pedersen: *comm,
718 };
719 let pp_synth = pedersen_to_plain::verify(
720 &mut *rng, &mut t, &pp_setup, &pp_claim, pp_proof,
721 );
722 sender_acc += &(pp_synth * &Scalar::random(&mut *rng));
723 }
724 per_sender.push(Some(sender_acc));
725 }
726 Some(per_sender)
727 },
728 strategy,
729 );
730
731 let Some(per_sender) = per_sender else {
732 return Map::default();
733 };
734
735 outputs
736 .into_iter()
737 .zip(per_sender)
738 .filter_map(|((sender, _, commitments), valid)| {
739 valid.then_some((sender, commitments.commitments))
740 })
741 .try_collect()
742 .expect("senders must be unique")
743 }
744}
745
746#[cfg(test)]
747mod tests {
748 use super::*;
749 use commonware_macros::test_group;
750 use commonware_parallel::Sequential;
751 use commonware_utils::test_rng;
752 use std::sync::LazyLock;
753
754 static TEST_SETUP: LazyLock<Setup> = LazyLock::new(|| Setup::new(NonZeroU32::new(3).unwrap()));
757
758 #[test_group("slow")]
759 #[test]
760 fn vrf_batch_checked_roundtrips_through_check_batch() {
761 let mut rng = test_rng();
762
763 let sender_sk = PrivateKey::random(&mut rng);
764 let sender_pk = sender_sk.public();
765 let mut receivers: Vec<_> = (0..3).map(|_| PrivateKey::random(&mut rng)).collect();
766 receivers.sort_by_key(PrivateKey::public);
767 let receiver_pks: Vec<_> = receivers.iter().rev().map(PrivateKey::public).collect();
768
769 let nonce = Summary::random(&mut rng);
770 let msg = Bytes::copy_from_slice(nonce.as_ref());
771
772 let outer_transcript = Transcript::new(b"vrf-batch-checked-test", Version::V1);
776
777 let mut prover_t = outer_transcript.fork(b"dealer vrf");
778 prover_t.commit(sender_pk.encode());
779 let (outputs, commitments) = sender_sk.vrf_batch_checked(
780 &mut rng,
781 &TEST_SETUP,
782 &mut prover_t,
783 &nonce,
784 receiver_pks.iter().cloned(),
785 &Sequential,
786 );
787
788 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
789 assert_eq!(outputs.keys(), &players);
790 assert_eq!(commitments.commitments.keys(), &players);
791 for receiver in &receivers {
792 assert_eq!(
793 outputs.get_value(&receiver.public()),
794 Some(&receiver.vrf_recv(&nonce, &sender_pk)),
795 );
796 }
797 let result = VrfCommitments::check_batch(
798 &mut rng,
799 &TEST_SETUP,
800 &outer_transcript,
801 &players,
802 std::iter::once((sender_pk.clone(), msg, commitments.clone())),
803 &Sequential,
804 );
805
806 assert_eq!(result.len(), 1);
807 let checked = result
808 .get_value(&sender_pk)
809 .expect("sender should appear in batch result");
810 assert_eq!(checked, &commitments.commitments);
811 }
812
813 #[test_group("slow")]
814 #[test]
815 fn check_batch_rejects_perturbed_commitments() {
816 let mut rng = test_rng();
817
818 let sender_sk = PrivateKey::random(&mut rng);
819 let sender_pk = sender_sk.public();
820 let receiver_pks: Vec<PublicKey> = (0..3)
821 .map(|_| PrivateKey::random(&mut rng).public())
822 .collect();
823
824 let nonce = Summary::random(&mut rng);
825 let msg = Bytes::copy_from_slice(nonce.as_ref());
826
827 let outer_transcript = Transcript::new(b"vrf-batch-checked-test", Version::V1);
828
829 let mut prover_t = outer_transcript.fork(b"dealer vrf");
830 prover_t.commit(sender_pk.encode());
831 let (_outputs, mut commitments) = sender_sk.vrf_batch_checked(
832 &mut rng,
833 &TEST_SETUP,
834 &mut prover_t,
835 &nonce,
836 receiver_pks.iter().cloned(),
837 &Sequential,
838 );
839
840 commitments.perturb(&receiver_pks[0], &G1::generator());
842
843 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
844 let result = VrfCommitments::check_batch(
845 &mut rng,
846 &TEST_SETUP,
847 &outer_transcript,
848 &players,
849 std::iter::once((sender_pk, msg, commitments)),
850 &Sequential,
851 );
852 assert!(result.is_empty());
853 }
854
855 #[test]
856 fn check_batch_rejects_short_pedersen_to_plain_vector() {
857 let mut rng = test_rng();
858
859 let sender_sk = PrivateKey::random(&mut rng);
860 let sender_pk = sender_sk.public();
861 let receiver_pks: Vec<PublicKey> = (0..3)
862 .map(|_| PrivateKey::random(&mut rng).public())
863 .collect();
864
865 let nonce = Summary::random(&mut rng);
866 let msg = Bytes::copy_from_slice(nonce.as_ref());
867
868 let outer_transcript = Transcript::new(b"vrf-batch-checked-test", Version::V1);
869
870 let mut prover_t = outer_transcript.fork(b"dealer vrf");
871 prover_t.commit(sender_pk.encode());
872 let (_outputs, mut commitments) = sender_sk.vrf_batch_checked(
873 &mut rng,
874 &TEST_SETUP,
875 &mut prover_t,
876 &nonce,
877 receiver_pks.iter().cloned(),
878 &Sequential,
879 );
880
881 commitments.proof.pedersen_to_plain.pop().unwrap();
885 assert!(
886 commitments.proof.pedersen_to_plain.len() < commitments.commitments.len(),
887 "test setup expects fewer proofs than commitments",
888 );
889
890 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
891 let result = VrfCommitments::check_batch(
892 &mut rng,
893 &TEST_SETUP,
894 &outer_transcript,
895 &players,
896 std::iter::once((sender_pk, msg, commitments)),
897 &Sequential,
898 );
899 assert!(result.is_empty());
900 }
901
902 #[test_group("slow")]
903 #[test]
904 fn check_batch_falls_back_to_per_sender_on_failure() {
905 let mut rng = test_rng();
906
907 let senders: Vec<(PrivateKey, PublicKey)> = (0..2)
909 .map(|_| {
910 let sk = PrivateKey::random(&mut rng);
911 let pk = sk.public();
912 (sk, pk)
913 })
914 .collect();
915 let receiver_pks: Vec<PublicKey> = (0..3)
916 .map(|_| PrivateKey::random(&mut rng).public())
917 .collect();
918
919 let outer_transcript = Transcript::new(b"vrf-batch-checked-test", Version::V1);
920
921 let mut prepared = Vec::new();
922 for (sk, pk) in &senders {
923 let nonce = Summary::random(&mut rng);
924 let msg = Bytes::copy_from_slice(nonce.as_ref());
925 let mut prover_t = outer_transcript.fork(b"dealer vrf");
926 prover_t.commit(pk.encode());
927 let (_outputs, commitments) = sk.vrf_batch_checked(
928 &mut rng,
929 &TEST_SETUP,
930 &mut prover_t,
931 &nonce,
932 receiver_pks.iter().cloned(),
933 &Sequential,
934 );
935 prepared.push((pk.clone(), msg, commitments));
936 }
937
938 prepared[1].2.perturb(&receiver_pks[0], &G1::generator());
941
942 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
943 let result = VrfCommitments::check_batch(
944 &mut rng,
945 &TEST_SETUP,
946 &outer_transcript,
947 &players,
948 prepared.iter().cloned(),
949 &Sequential,
950 );
951
952 assert_eq!(result.len(), 1);
954 let good_pk = &senders[0].1;
955 let bad_pk = &senders[1].1;
956 assert_eq!(result.get_value(good_pk), Some(&prepared[0].2.commitments));
957 assert!(result.get_value(bad_pk).is_none());
958 }
959
960 #[test]
961 fn setup_codec_roundtrip() {
962 let s = Setup::new(NonZeroU32::new(3).unwrap());
963 let bytes = s.encode();
964 let decoded = Setup::read_cfg(&mut bytes.clone(), &NonZeroU32::new(3).unwrap()).unwrap();
965 assert_eq!(decoded.max_players(), s.max_players());
966 assert_eq!(decoded.encode(), bytes);
968 }
969}