1use alloy::primitives::U256;
11use ark_std::{
12 fmt::Debug,
13 format,
14 marker::PhantomData,
15 rand::{CryptoRng, RngCore},
16 vec,
17 vec::Vec,
18};
19use bitvec::prelude::*;
20use generic_array::GenericArray;
21use jf_signature::{AggregateableSignatureSchemes, SignatureError};
22use serde::{Deserialize, Serialize};
23use typenum::U32;
24
25use crate::{
26 stake_table::StakeTableEntry,
27 traits::{qc::QuorumCertificateScheme, signature_key::SignatureKey},
28};
29
30#[derive(Serialize, Deserialize)]
32pub struct BitVectorQc<A: AggregateableSignatureSchemes + Serialize + for<'a> Deserialize<'a>>(
33 PhantomData<A>,
34);
35
36#[derive(PartialEq, Debug, Clone, Hash)]
38pub struct QcParams<'a, K: SignatureKey, P> {
39 pub stake_entries: &'a [StakeTableEntry<K>],
41 pub threshold: U256,
43 pub agg_sig_pp: P,
45}
46
47impl<A> QuorumCertificateScheme<A> for BitVectorQc<A>
48where
49 A: AggregateableSignatureSchemes,
50 A::VerificationKey: SignatureKey,
51{
52 type QcProverParams<'a> = QcParams<'a, A::VerificationKey, A::PublicParameter>;
53
54 type QcVerifierParams<'a> = QcParams<'a, A::VerificationKey, A::PublicParameter>;
56
57 type Qc = (A::Signature, BitVec);
58 type MessageLength = U32;
59 type QuorumSize = U256;
60
61 fn sign<R: CryptoRng + RngCore, M: AsRef<[A::MessageUnit]>>(
63 pp: &A::PublicParameter,
64 sk: &A::SigningKey,
65 msg: M,
66 prng: &mut R,
67 ) -> Result<A::Signature, SignatureError> {
68 A::sign(pp, sk, msg, prng)
69 }
70
71 fn assemble(
72 qc_pp: &Self::QcProverParams<'_>,
73 signers: &BitSlice,
74 sigs: &[A::Signature],
75 ) -> Result<Self::Qc, SignatureError> {
76 if signers.len() != qc_pp.stake_entries.len() {
77 return Err(SignatureError::ParameterError(format!(
78 "bit vector len {} != the number of stake entries {}",
79 signers.len(),
80 qc_pp.stake_entries.len(),
81 )));
82 }
83 let total_weight: U256 =
84 qc_pp
85 .stake_entries
86 .iter()
87 .zip(signers.iter())
88 .fold(
89 U256::ZERO,
90 |acc, (entry, b)| {
91 if *b { acc + entry.stake_amount } else { acc }
92 },
93 );
94 if total_weight < qc_pp.threshold {
95 return Err(SignatureError::ParameterError(format!(
96 "total_weight {} less than threshold {}",
97 total_weight, qc_pp.threshold,
98 )));
99 }
100 let mut ver_keys = vec![];
101 for (entry, b) in qc_pp.stake_entries.iter().zip(signers.iter()) {
102 if *b {
103 ver_keys.push(entry.stake_key.clone());
104 }
105 }
106 if ver_keys.len() != sigs.len() {
107 return Err(SignatureError::ParameterError(format!(
108 "the number of ver_keys {} != the number of partial signatures {}",
109 ver_keys.len(),
110 sigs.len(),
111 )));
112 }
113 let sig = A::aggregate(&qc_pp.agg_sig_pp, &ver_keys[..], sigs)?;
114
115 Ok((sig, signers.into()))
116 }
117
118 fn check(
119 qc_vp: &Self::QcVerifierParams<'_>,
120 message: &GenericArray<A::MessageUnit, Self::MessageLength>,
121 qc: &Self::Qc,
122 ) -> Result<Self::QuorumSize, SignatureError> {
123 let (sig, signers) = qc;
124 if signers.len() != qc_vp.stake_entries.len() {
125 return Err(SignatureError::ParameterError(format!(
126 "signers bit vector len {} != the number of stake entries {}",
127 signers.len(),
128 qc_vp.stake_entries.len(),
129 )));
130 }
131 let total_weight: U256 =
132 qc_vp
133 .stake_entries
134 .iter()
135 .zip(signers.iter())
136 .fold(
137 U256::ZERO,
138 |acc, (entry, b)| {
139 if *b { acc + entry.stake_amount } else { acc }
140 },
141 );
142 if total_weight < qc_vp.threshold {
143 return Err(SignatureError::ParameterError(format!(
144 "total_weight {} less than threshold {}",
145 total_weight, qc_vp.threshold,
146 )));
147 }
148 let mut ver_keys = vec![];
149 for (entry, b) in qc_vp.stake_entries.iter().zip(signers.iter()) {
150 if *b {
151 ver_keys.push(entry.stake_key.clone());
152 }
153 }
154 A::multi_sig_verify(&qc_vp.agg_sig_pp, &ver_keys[..], message, sig)?;
155
156 Ok(total_weight)
157 }
158
159 fn trace(
160 qc_vp: &Self::QcVerifierParams<'_>,
161 message: &GenericArray<<A>::MessageUnit, Self::MessageLength>,
162 qc: &Self::Qc,
163 ) -> Result<Vec<<A>::VerificationKey>, SignatureError> {
164 Self::check(qc_vp, message, qc)?;
165 Self::signers(qc_vp, qc)
166 }
167
168 fn signers(
169 qc_vp: &Self::QcVerifierParams<'_>,
170 qc: &Self::Qc,
171 ) -> Result<Vec<<A>::VerificationKey>, SignatureError> {
172 let (_sig, signers) = qc;
173 if signers.len() != qc_vp.stake_entries.len() {
174 return Err(SignatureError::ParameterError(format!(
175 "signers bit vector len {} != the number of stake entries {}",
176 signers.len(),
177 qc_vp.stake_entries.len(),
178 )));
179 }
180
181 let signer_pks: Vec<_> = qc_vp
182 .stake_entries
183 .iter()
184 .zip(signers.iter())
185 .filter(|(_, b)| **b)
186 .map(|(pk, _)| pk.stake_key.clone())
187 .collect();
188 Ok(signer_pks)
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use jf_signature::{
195 SignatureScheme,
196 bls_over_bn254::{BLSOverBN254CurveSignatureScheme, KeyPair},
197 };
198 use vbs::{BinarySerializer, Serializer, version::StaticVersion};
199
200 use super::*;
201 type Version = StaticVersion<0, 1>;
202
203 macro_rules! test_quorum_certificate {
204 ($aggsig:tt) => {
205 let mut rng = jf_utils::test_rng();
206 let agg_sig_pp = $aggsig::param_gen(Some(&mut rng)).unwrap();
207 let key_pair1 = KeyPair::generate(&mut rng);
208 let key_pair2 = KeyPair::generate(&mut rng);
209 let key_pair3 = KeyPair::generate(&mut rng);
210 let entry1 = StakeTableEntry {
211 stake_key: key_pair1.ver_key(),
212 stake_amount: U256::from(3u8),
213 };
214 let entry2 = StakeTableEntry {
215 stake_key: key_pair2.ver_key(),
216 stake_amount: U256::from(5u8),
217 };
218 let entry3 = StakeTableEntry {
219 stake_key: key_pair3.ver_key(),
220 stake_amount: U256::from(7u8),
221 };
222 let qc_pp = QcParams {
223 stake_entries: &[entry1, entry2, entry3],
224 threshold: U256::from(10u8),
225 agg_sig_pp,
226 };
227 let msg = [72u8; 32];
228 let sig1 =
229 BitVectorQc::<$aggsig>::sign(&agg_sig_pp, key_pair1.sign_key_ref(), &msg, &mut rng)
230 .unwrap();
231 let sig2 =
232 BitVectorQc::<$aggsig>::sign(&agg_sig_pp, key_pair2.sign_key_ref(), &msg, &mut rng)
233 .unwrap();
234 let sig3 =
235 BitVectorQc::<$aggsig>::sign(&agg_sig_pp, key_pair3.sign_key_ref(), &msg, &mut rng)
236 .unwrap();
237
238 let signers = bitvec![0, 1, 1];
240 let qc = BitVectorQc::<$aggsig>::assemble(
241 &qc_pp,
242 signers.as_bitslice(),
243 &[sig2.clone(), sig3.clone()],
244 )
245 .unwrap();
246 assert!(BitVectorQc::<$aggsig>::check(&qc_pp, &msg.into(), &qc).is_ok());
247 assert_eq!(
248 BitVectorQc::<$aggsig>::trace(&qc_pp, &msg.into(), &qc).unwrap(),
249 vec![key_pair2.ver_key(), key_pair3.ver_key()],
250 );
251
252 assert_eq!(
254 qc,
255 Serializer::<Version>::deserialize(&Serializer::<Version>::serialize(&qc).unwrap())
256 .unwrap()
257 );
258
259 assert!(BitVectorQc::<$aggsig>::assemble(
262 &qc_pp,
263 signers.as_bitslice(),
264 std::slice::from_ref(&sig2)
265 )
266 .is_err());
267 let active_bad = bitvec![1, 1, 0];
269 assert!(BitVectorQc::<$aggsig>::assemble(
270 &qc_pp,
271 active_bad.as_bitslice(),
272 &[sig1.clone(), sig2.clone()]
273 )
274 .is_err());
275 let active_bad_2 = bitvec![0, 1, 1, 0];
277 assert!(BitVectorQc::<$aggsig>::assemble(
278 &qc_pp,
279 active_bad_2.as_bitslice(),
280 &[sig2, sig3],
281 )
282 .is_err());
283
284 assert!(BitVectorQc::<$aggsig>::check(
285 &qc_pp,
286 &msg.into(),
287 &(qc.0.clone(), active_bad)
288 )
289 .is_err());
290 assert!(BitVectorQc::<$aggsig>::check(
291 &qc_pp,
292 &msg.into(),
293 &(qc.0.clone(), active_bad_2)
294 )
295 .is_err());
296 let bad_msg = [70u8; 32];
297 assert!(BitVectorQc::<$aggsig>::check(&qc_pp, &bad_msg.into(), &qc).is_err());
298
299 let bad_sig = &sig1;
300 assert!(
301 BitVectorQc::<$aggsig>::check(&qc_pp, &msg.into(), &(bad_sig.clone(), qc.1))
302 .is_err()
303 );
304 };
305 }
306 #[test]
307 fn test_quorum_certificate() {
308 test_quorum_certificate!(BLSOverBN254CurveSignatureScheme);
309 }
310
311 struct ThreeNodeSetup {
313 key_pair1: KeyPair,
314 key_pair2: KeyPair,
315 key_pair3: KeyPair,
316 entries: Vec<
317 StakeTableEntry<<BLSOverBN254CurveSignatureScheme as SignatureScheme>::VerificationKey>,
318 >,
319 sig1: <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature,
320 sig2: <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature,
321 sig3: <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature,
322 }
323
324 fn three_node_setup() -> ThreeNodeSetup {
329 let mut rng = jf_utils::test_rng();
330 let key_pair1 = KeyPair::generate(&mut rng);
331 let key_pair2 = KeyPair::generate(&mut rng);
332 let key_pair3 = KeyPair::generate(&mut rng);
333 let entries = vec![
334 StakeTableEntry {
335 stake_key: key_pair1.ver_key(),
336 stake_amount: U256::from(1u8),
337 },
338 StakeTableEntry {
339 stake_key: key_pair2.ver_key(),
340 stake_amount: U256::from(1u8),
341 },
342 StakeTableEntry {
343 stake_key: key_pair3.ver_key(),
344 stake_amount: U256::from(1u8),
345 },
346 ];
347 let msg = [42u8; 32];
348 let sig1 = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
349 &(),
350 key_pair1.sign_key_ref(),
351 msg,
352 &mut rng,
353 )
354 .unwrap();
355 let sig2 = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
356 &(),
357 key_pair2.sign_key_ref(),
358 msg,
359 &mut rng,
360 )
361 .unwrap();
362 let sig3 = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
363 &(),
364 key_pair3.sign_key_ref(),
365 msg,
366 &mut rng,
367 )
368 .unwrap();
369 ThreeNodeSetup {
370 key_pair1,
371 key_pair2,
372 key_pair3,
373 entries,
374 sig1,
375 sig2,
376 sig3,
377 }
378 }
379
380 #[test]
381 fn test_signers_extracts_correct_keys() {
382 let setup = three_node_setup();
383 let qc_pp = QcParams {
384 stake_entries: &setup.entries,
385 threshold: U256::from(2u8),
386 agg_sig_pp: (),
387 };
388 let signers_bv = bitvec![0, 1, 1];
390 let qc = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(
391 &qc_pp,
392 signers_bv.as_bitslice(),
393 &[setup.sig2, setup.sig3],
394 )
395 .unwrap();
396 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc).unwrap();
397 assert_eq!(
398 result,
399 vec![setup.key_pair2.ver_key(), setup.key_pair3.ver_key()]
400 );
401 }
402
403 #[test]
404 fn test_signers_different_subset() {
405 let setup = three_node_setup();
406 let qc_pp = QcParams {
407 stake_entries: &setup.entries,
408 threshold: U256::from(2u8),
409 agg_sig_pp: (),
410 };
411 let signers_bv = bitvec![1, 0, 1];
413 let qc = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(
414 &qc_pp,
415 signers_bv.as_bitslice(),
416 &[setup.sig1, setup.sig3],
417 )
418 .unwrap();
419 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc).unwrap();
420 assert_eq!(
421 result,
422 vec![setup.key_pair1.ver_key(), setup.key_pair3.ver_key()]
423 );
424 }
425
426 #[test]
427 fn test_signers_all_participants() {
428 let setup = three_node_setup();
429 let qc_pp = QcParams {
430 stake_entries: &setup.entries,
431 threshold: U256::from(2u8),
432 agg_sig_pp: (),
433 };
434 let signers_bv = bitvec![1, 1, 1];
435 let qc = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(
436 &qc_pp,
437 signers_bv.as_bitslice(),
438 &[setup.sig1, setup.sig2, setup.sig3],
439 )
440 .unwrap();
441 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc).unwrap();
442 assert_eq!(
443 result,
444 vec![
445 setup.key_pair1.ver_key(),
446 setup.key_pair2.ver_key(),
447 setup.key_pair3.ver_key()
448 ]
449 );
450 }
451
452 #[test]
453 fn test_signers_no_participants() {
454 let setup = three_node_setup();
457 let qc_pp = QcParams {
458 stake_entries: &setup.entries,
459 threshold: U256::from(2u8),
460 agg_sig_pp: (),
461 };
462 let empty_bv = bitvec![0, 0, 0];
464 let qc = (setup.sig1, empty_bv);
465 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc).unwrap();
466 assert!(result.is_empty());
467 }
468
469 #[test]
470 fn test_signers_does_not_check_message() {
471 let setup = three_node_setup();
474 let qc_pp = QcParams {
475 stake_entries: &setup.entries,
476 threshold: U256::from(2u8),
477 agg_sig_pp: (),
478 };
479 let signers_bv = bitvec![0, 1, 1];
480 let qc = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(
482 &qc_pp,
483 signers_bv.as_bitslice(),
484 &[setup.sig2, setup.sig3],
485 )
486 .unwrap();
487 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc).unwrap();
489 assert_eq!(
490 result,
491 vec![setup.key_pair2.ver_key(), setup.key_pair3.ver_key()]
492 );
493 }
494
495 #[test]
496 fn test_signers_bitvec_length_mismatch() {
497 let setup = three_node_setup();
498 let qc_pp = QcParams {
499 stake_entries: &setup.entries,
500 threshold: U256::from(2u8),
501 agg_sig_pp: (),
502 };
503 let wrong_bv = bitvec![0, 1, 1, 0];
505 let qc_bad = (setup.sig1, wrong_bv);
507 let result = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(&qc_pp, &qc_bad);
508 assert!(result.is_err());
509 }
510}