1mod banderwagon;
2
3use crate::{
4 bls12381::primitives::group::{Scalar, ScalarReadCfg, G1},
5 transcript::{Summary, Transcript},
6 zk::{
7 bulletproofs::circuit::{self, prove, verify},
8 pedersen_to_plain,
9 },
10 Secret,
11};
12use banderwagon::{vrf_batch_checked, vrf_batch_checked_circuit, vrf_recv, F, G};
13use bytes::{Buf, BufMut, Bytes};
14use commonware_codec::{
15 Encode, EncodeFixed, EncodeSize, Error as CodecError, FixedArray, FixedSize, Read, ReadExt,
16 Write,
17};
18use commonware_formatting::hex;
19use commonware_math::algebra::{Additive as _, CryptoGroup, Random};
20use commonware_parallel::Strategy;
21use commonware_utils::{
22 ordered::{Map, Set},
23 Array, Span, TryCollect, TryFromIterator,
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);
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 let outputs = Map::try_from_iter(
259 receivers
260 .into_iter()
261 .zip(witness.values())
262 .map(|((receiver, _), output)| (receiver, output.clone())),
263 )
264 .expect("receivers was already deduplicated");
265 let commitments = Map::try_from_iter(outputs.keys().iter().cloned().zip(claim.commitments))
266 .expect("receivers was already deduplicated");
267 let pedersen_to_plain = {
268 let setup = pedersen_to_plain::Setup {
269 value_generator: *setup.inner().value_generator(),
270 blinding_generator: *setup.inner().blinding_generator(),
271 };
272 let mut out = Vec::new();
273 for (receiver, output) in outputs.iter_pairs() {
274 let commitment = *commitments
275 .get_value(receiver)
276 .expect("output should have commitment");
277 let proof = pedersen_to_plain::prove(
278 &mut *rng,
279 transcript,
280 &setup,
281 &pedersen_to_plain::Claim {
282 plain: commitment,
283 pedersen: commitment,
284 },
285 &pedersen_to_plain::Witness {
286 value: output.clone(),
287 blinding: Scalar::zero(),
288 },
289 );
290 out.push(proof);
291 }
292 out
293 };
294 let proof = Proof {
295 circuit_proof,
296 pedersen_to_plain,
297 };
298 (outputs, VrfCommitments { proof, commitments })
299 }
300}
301
302impl Write for PrivateKey {
303 fn write(&self, buf: &mut impl BufMut) {
304 self.inner
305 .expose(|x| buf.put_slice(&x.encode_fixed::<{ F::SIZE }>()));
306 }
307}
308
309impl Read for PrivateKey {
310 type Cfg = ();
311
312 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
313 let raw = Zeroizing::new(<[u8; Self::SIZE]>::read(buf)?);
314 let x: F = ReadExt::read(&mut raw.as_slice())?;
315 Ok(Self {
316 inner: Secret::new(x),
317 })
318 }
319}
320
321impl FixedSize for PrivateKey {
322 const SIZE: usize = F::SIZE;
323}
324
325#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, FixedArray)]
329pub struct Signature {
330 raw: [u8; G::SIZE + F::SIZE],
331}
332
333impl Write for Signature {
334 fn write(&self, buf: &mut impl BufMut) {
335 self.raw.write(buf);
336 }
337}
338
339impl Read for Signature {
340 type Cfg = ();
341
342 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
343 let raw = <[u8; Self::SIZE]>::read(buf)?;
344 Ok(Self { raw })
345 }
346}
347
348impl FixedSize for Signature {
349 const SIZE: usize = G::SIZE + F::SIZE;
350}
351
352impl crate::Signature for Signature {}
353
354impl Span for Signature {}
355
356impl Array for Signature {}
357
358impl AsRef<[u8]> for Signature {
359 fn as_ref(&self) -> &[u8] {
360 &self.raw
361 }
362}
363
364impl Deref for Signature {
365 type Target = [u8];
366 fn deref(&self) -> &[u8] {
367 &self.raw
368 }
369}
370
371impl Debug for Signature {
372 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
373 write!(f, "{}", hex(&self.raw))
374 }
375}
376
377impl Display for Signature {
378 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
379 write!(f, "{}", hex(&self.raw))
380 }
381}
382
383#[derive(Clone, FixedArray)]
387pub struct PublicKey {
388 raw: [u8; G::SIZE],
389 point: G,
390}
391
392impl PublicKey {
393 fn from_point(point: G) -> Self {
394 let raw: [u8; G::SIZE] = point.encode_fixed();
395 Self { raw, point }
396 }
397}
398
399impl crate::Verifier for PublicKey {
400 type Signature = Signature;
401
402 fn verify(&self, namespace: &[u8], msg: &[u8], sig: &Signature) -> bool {
403 let k_big: G = match ReadExt::read(&mut &sig.raw[..G::SIZE]) {
404 Ok(p) => p,
405 Err(_) => return false,
406 };
407 let s: F = match ReadExt::read(&mut &sig.raw[G::SIZE..]) {
408 Ok(s) => s,
409 Err(_) => return false,
410 };
411
412 let mut t = Transcript::new(SCHNORR_NS);
414 t.commit(namespace)
415 .commit(msg)
416 .commit(self.raw.as_slice())
417 .commit(sig.raw[..G::SIZE].as_ref());
418 let e = F::random(t.noise(b"challenge"));
419
420 let lhs = G::generator() * &s;
422 let rhs = k_big + &(self.point.clone() * &e);
423 lhs == rhs
424 }
425}
426
427impl crate::PublicKey for PublicKey {}
428
429impl Write for PublicKey {
430 fn write(&self, buf: &mut impl BufMut) {
431 self.raw.write(buf);
432 }
433}
434
435impl Read for PublicKey {
436 type Cfg = ();
437
438 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
439 let raw = <[u8; Self::SIZE]>::read(buf)?;
440 let point: G = ReadExt::read(&mut raw.as_slice())?;
441 Ok(Self { raw, point })
442 }
443}
444
445impl FixedSize for PublicKey {
446 const SIZE: usize = G::SIZE;
447}
448
449impl Span for PublicKey {}
450
451impl Array for PublicKey {}
452
453impl AsRef<[u8]> for PublicKey {
454 fn as_ref(&self) -> &[u8] {
455 &self.raw
456 }
457}
458
459impl Deref for PublicKey {
460 type Target = [u8];
461 fn deref(&self) -> &[u8] {
462 &self.raw
463 }
464}
465
466impl Eq for PublicKey {}
467
468impl PartialEq for PublicKey {
469 fn eq(&self, other: &Self) -> bool {
470 self.raw == other.raw
471 }
472}
473
474impl Ord for PublicKey {
475 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
476 self.raw.cmp(&other.raw)
477 }
478}
479
480impl PartialOrd for PublicKey {
481 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
482 Some(self.cmp(other))
483 }
484}
485
486impl Hash for PublicKey {
487 fn hash<H: Hasher>(&self, state: &mut H) {
488 self.raw.hash(state);
489 }
490}
491
492impl Debug for PublicKey {
493 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
494 write!(f, "{}", hex(self))
495 }
496}
497
498impl Display for PublicKey {
499 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
500 write!(f, "{}", hex(self))
501 }
502}
503
504#[derive(Clone)]
507struct Proof {
508 circuit_proof: circuit::Proof<Scalar, G1>,
509 pedersen_to_plain: Vec<pedersen_to_plain::Proof<Scalar, G1>>,
510}
511
512impl Write for Proof {
513 fn write(&self, buf: &mut impl BufMut) {
514 self.circuit_proof.write(buf);
515 self.pedersen_to_plain.write(buf);
516 }
517}
518
519impl EncodeSize for Proof {
520 fn encode_size(&self) -> usize {
521 self.circuit_proof.encode_size() + self.pedersen_to_plain.encode_size()
522 }
523}
524
525impl Read for Proof {
526 type Cfg = NonZeroU32;
531
532 fn read_cfg(buf: &mut impl Buf, max_players: &Self::Cfg) -> Result<Self, CodecError> {
533 let max_proof_len = 1usize << lg_len_for_players(max_players.get());
534 let circuit_proof = circuit::Proof::<Scalar, G1>::read_cfg(
535 buf,
536 &(max_proof_len, ((), ScalarReadCfg::AllowZero)),
537 )?;
538 let range = commonware_codec::RangeCfg::new(0..=max_players.get() as usize);
539 let pedersen_to_plain = Vec::<pedersen_to_plain::Proof<Scalar, G1>>::read_cfg(
540 buf,
541 &(range, ((), ScalarReadCfg::AllowZero)),
542 )?;
543 Ok(Self {
544 circuit_proof,
545 pedersen_to_plain,
546 })
547 }
548}
549
550impl Write for VrfCommitments {
551 fn write(&self, buf: &mut impl BufMut) {
552 self.proof.write(buf);
553 self.commitments.write(buf);
554 }
555}
556
557impl EncodeSize for VrfCommitments {
558 fn encode_size(&self) -> usize {
559 self.proof.encode_size() + self.commitments.encode_size()
560 }
561}
562
563impl Read for VrfCommitments {
564 type Cfg = NonZeroU32;
565
566 fn read_cfg(buf: &mut impl Buf, max_players: &Self::Cfg) -> Result<Self, CodecError> {
567 let proof = Proof::read_cfg(buf, max_players)?;
568 let range = commonware_codec::RangeCfg::new(0..=max_players.get() as usize);
569 let commitments = Read::read_cfg(buf, &(range, (), ()))?;
570 Ok(Self { proof, commitments })
571 }
572}
573
574#[derive(Clone)]
579pub struct VrfCommitments {
580 proof: Proof,
581 commitments: Map<PublicKey, G1>,
582}
583
584impl VrfCommitments {
585 #[cfg(any(feature = "arbitrary", test))]
588 pub(super) fn perturb(&mut self, receiver: &PublicKey, delta: &G1) {
589 if let Some(c) = self.commitments.get_value_mut(receiver) {
590 *c += delta;
591 }
592 }
593
594 pub fn check_batch(
619 rng: &mut impl CryptoRng,
620 setup: &Setup,
621 transcript: &Transcript,
622 players: &Set<PublicKey>,
623 outputs: impl IntoIterator<Item = (PublicKey, Bytes, Self)>,
624 strategy: &impl Strategy,
625 ) -> Map<PublicKey, Map<PublicKey, G1>> {
626 let outputs: Vec<(PublicKey, Bytes, Self)> = outputs
637 .into_iter()
638 .filter_map(|(sender, msg, commitments)| {
639 let mut buf: &[u8] = msg.as_ref();
640 let _: Summary = ReadExt::read(&mut buf).ok()?;
641 if commitments.proof.pedersen_to_plain.len() != commitments.commitments.len() {
642 return None;
643 }
644 if commitments
645 .commitments
646 .keys()
647 .iter()
648 .any(|pk| players.position(pk).is_none())
649 {
650 return None;
651 }
652 Some((sender, msg, commitments))
653 })
654 .collect();
655
656 let per_sender = setup.inner().eval_check_batched(
660 rng,
661 |vs, rng| {
662 let pp_setup = pedersen_to_plain::Setup {
666 value_generator: vs.value_generator().clone(),
667 blinding_generator: vs.blinding_generator().clone(),
668 };
669
670 let mut per_sender = Vec::with_capacity(outputs.len());
671 for (sender, msg, commitments) in &outputs {
672 let receivers: Vec<G> = commitments
676 .commitments
677 .keys()
678 .iter()
679 .map(|pk| pk.point.clone())
680 .collect();
681 let circuit =
682 vrf_batch_checked_circuit(msg.as_ref(), &sender.point, &receivers);
683 let claim = circuit::Claim {
684 commitments: commitments.commitments.values().to_vec(),
685 };
686
687 let mut t = transcript.fork(b"dealer vrf");
691 t.commit(sender.encode());
692
693 let Some(circuit_synth) = verify(
694 &mut *rng,
695 &mut t,
696 vs,
697 &circuit,
698 &claim,
699 commitments.proof.circuit_proof.clone(),
700 strategy,
701 ) else {
702 per_sender.push(None);
706 continue;
707 };
708 let mut sender_acc = circuit_synth * &Scalar::random(&mut *rng);
709
710 for ((_, comm), pp_proof) in commitments
713 .commitments
714 .iter_pairs()
715 .zip(commitments.proof.pedersen_to_plain.iter().cloned())
716 {
717 let pp_claim = pedersen_to_plain::Claim {
718 plain: *comm,
719 pedersen: *comm,
720 };
721 let pp_synth = pedersen_to_plain::verify(
722 &mut *rng, &mut t, &pp_setup, &pp_claim, pp_proof,
723 );
724 sender_acc += &(pp_synth * &Scalar::random(&mut *rng));
725 }
726 per_sender.push(Some(sender_acc));
727 }
728 Some(per_sender)
729 },
730 strategy,
731 );
732
733 let Some(per_sender) = per_sender else {
734 return Map::default();
735 };
736
737 outputs
738 .into_iter()
739 .zip(per_sender)
740 .filter_map(|((sender, _, commitments), valid)| {
741 valid.then_some((sender, commitments.commitments))
742 })
743 .try_collect()
744 .expect("senders must be unique")
745 }
746}
747
748#[cfg(test)]
749mod tests {
750 use super::*;
751 use commonware_macros::test_group;
752 use commonware_parallel::Sequential;
753 use commonware_utils::test_rng;
754 use std::sync::LazyLock;
755
756 static TEST_SETUP: LazyLock<Setup> = LazyLock::new(|| Setup::new(NonZeroU32::new(3).unwrap()));
759
760 #[test_group("slow")]
761 #[test]
762 fn vrf_batch_checked_roundtrips_through_check_batch() {
763 let mut rng = test_rng();
764
765 let sender_sk = PrivateKey::random(&mut rng);
766 let sender_pk = sender_sk.public();
767 let receiver_pks: Vec<PublicKey> = (0..3)
768 .map(|_| PrivateKey::random(&mut rng).public())
769 .collect();
770
771 let nonce = Summary::random(&mut rng);
772 let msg = Bytes::copy_from_slice(nonce.as_ref());
773
774 let outer_transcript = Transcript::new(b"vrf-batch-checked-test");
778
779 let mut prover_t = outer_transcript.fork(b"dealer vrf");
780 prover_t.commit(sender_pk.encode());
781 let (_outputs, commitments) = sender_sk.vrf_batch_checked(
782 &mut rng,
783 &TEST_SETUP,
784 &mut prover_t,
785 &nonce,
786 receiver_pks.iter().cloned(),
787 &Sequential,
788 );
789
790 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
791 let result = VrfCommitments::check_batch(
792 &mut rng,
793 &TEST_SETUP,
794 &outer_transcript,
795 &players,
796 std::iter::once((sender_pk.clone(), msg, commitments.clone())),
797 &Sequential,
798 );
799
800 assert_eq!(result.len(), 1);
801 let checked = result
802 .get_value(&sender_pk)
803 .expect("sender should appear in batch result");
804 assert_eq!(checked, &commitments.commitments);
805 }
806
807 #[test_group("slow")]
808 #[test]
809 fn check_batch_rejects_perturbed_commitments() {
810 let mut rng = test_rng();
811
812 let sender_sk = PrivateKey::random(&mut rng);
813 let sender_pk = sender_sk.public();
814 let receiver_pks: Vec<PublicKey> = (0..3)
815 .map(|_| PrivateKey::random(&mut rng).public())
816 .collect();
817
818 let nonce = Summary::random(&mut rng);
819 let msg = Bytes::copy_from_slice(nonce.as_ref());
820
821 let outer_transcript = Transcript::new(b"vrf-batch-checked-test");
822
823 let mut prover_t = outer_transcript.fork(b"dealer vrf");
824 prover_t.commit(sender_pk.encode());
825 let (_outputs, mut commitments) = sender_sk.vrf_batch_checked(
826 &mut rng,
827 &TEST_SETUP,
828 &mut prover_t,
829 &nonce,
830 receiver_pks.iter().cloned(),
831 &Sequential,
832 );
833
834 commitments.perturb(&receiver_pks[0], &G1::generator());
836
837 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
838 let result = VrfCommitments::check_batch(
839 &mut rng,
840 &TEST_SETUP,
841 &outer_transcript,
842 &players,
843 std::iter::once((sender_pk, msg, commitments)),
844 &Sequential,
845 );
846 assert!(result.is_empty());
847 }
848
849 #[test]
850 fn check_batch_rejects_short_pedersen_to_plain_vector() {
851 let mut rng = test_rng();
852
853 let sender_sk = PrivateKey::random(&mut rng);
854 let sender_pk = sender_sk.public();
855 let receiver_pks: Vec<PublicKey> = (0..3)
856 .map(|_| PrivateKey::random(&mut rng).public())
857 .collect();
858
859 let nonce = Summary::random(&mut rng);
860 let msg = Bytes::copy_from_slice(nonce.as_ref());
861
862 let outer_transcript = Transcript::new(b"vrf-batch-checked-test");
863
864 let mut prover_t = outer_transcript.fork(b"dealer vrf");
865 prover_t.commit(sender_pk.encode());
866 let (_outputs, mut commitments) = sender_sk.vrf_batch_checked(
867 &mut rng,
868 &TEST_SETUP,
869 &mut prover_t,
870 &nonce,
871 receiver_pks.iter().cloned(),
872 &Sequential,
873 );
874
875 commitments.proof.pedersen_to_plain.pop().unwrap();
879 assert!(
880 commitments.proof.pedersen_to_plain.len() < commitments.commitments.len(),
881 "test setup expects fewer proofs than commitments",
882 );
883
884 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
885 let result = VrfCommitments::check_batch(
886 &mut rng,
887 &TEST_SETUP,
888 &outer_transcript,
889 &players,
890 std::iter::once((sender_pk, msg, commitments)),
891 &Sequential,
892 );
893 assert!(result.is_empty());
894 }
895
896 #[test_group("slow")]
897 #[test]
898 fn check_batch_falls_back_to_per_sender_on_failure() {
899 let mut rng = test_rng();
900
901 let senders: Vec<(PrivateKey, PublicKey)> = (0..2)
903 .map(|_| {
904 let sk = PrivateKey::random(&mut rng);
905 let pk = sk.public();
906 (sk, pk)
907 })
908 .collect();
909 let receiver_pks: Vec<PublicKey> = (0..3)
910 .map(|_| PrivateKey::random(&mut rng).public())
911 .collect();
912
913 let outer_transcript = Transcript::new(b"vrf-batch-checked-test");
914
915 let mut prepared = Vec::new();
916 for (sk, pk) in &senders {
917 let nonce = Summary::random(&mut rng);
918 let msg = Bytes::copy_from_slice(nonce.as_ref());
919 let mut prover_t = outer_transcript.fork(b"dealer vrf");
920 prover_t.commit(pk.encode());
921 let (_outputs, commitments) = sk.vrf_batch_checked(
922 &mut rng,
923 &TEST_SETUP,
924 &mut prover_t,
925 &nonce,
926 receiver_pks.iter().cloned(),
927 &Sequential,
928 );
929 prepared.push((pk.clone(), msg, commitments));
930 }
931
932 prepared[1].2.perturb(&receiver_pks[0], &G1::generator());
935
936 let players: Set<PublicKey> = receiver_pks.iter().cloned().try_collect().unwrap();
937 let result = VrfCommitments::check_batch(
938 &mut rng,
939 &TEST_SETUP,
940 &outer_transcript,
941 &players,
942 prepared.iter().cloned(),
943 &Sequential,
944 );
945
946 assert_eq!(result.len(), 1);
948 let good_pk = &senders[0].1;
949 let bad_pk = &senders[1].1;
950 assert_eq!(result.get_value(good_pk), Some(&prepared[0].2.commitments));
951 assert!(result.get_value(bad_pk).is_none());
952 }
953
954 #[test]
955 fn setup_codec_roundtrip() {
956 let s = Setup::new(NonZeroU32::new(3).unwrap());
957 let bytes = s.encode();
958 let decoded = Setup::read_cfg(&mut bytes.as_ref(), &NonZeroU32::new(3).unwrap()).unwrap();
959 assert_eq!(decoded.max_players(), s.max_players());
960 assert_eq!(decoded.encode(), bytes);
962 }
963}