mithril_stm/
stm.rs

1//! Top-level API for Mithril Stake-based Threshold Multisignature scheme.
2//! See figure 6 of [the paper](https://eprint.iacr.org/2021/916) for most of the
3//! protocol.
4//!
5//! What follows is a simple example showing the usage of STM.
6//!
7//! ```rust
8//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
9//! use blake2::{Blake2b, digest::consts::U32};
10//! use mithril_stm::{StmClerk, StmParameters, StmSig, KeyReg, StmInitializer, StmSigner};
11//! use mithril_stm::AggregationError;
12//! use rayon::prelude::*; // We use par_iter to speed things up
13//!
14//! use rand_chacha::ChaCha20Rng;
15//! use rand_core::{RngCore, SeedableRng};
16//!
17//! let nparties = 4; // Use a small number of parties for this example
18//! type D = Blake2b<U32>; // Setting the hash function for convenience
19//!
20//! let mut rng = ChaCha20Rng::from_seed([0u8; 32]); // create and initialize rng
21//! let mut msg = [0u8; 16]; // setting an arbitrary message
22//! rng.fill_bytes(&mut msg);
23//!
24//! // In the following, we will have 4 parties try to sign `msg`, then aggregate and
25//! // verify those signatures.
26//!
27//! //////////////////////////
28//! // initialization phase //
29//! //////////////////////////
30//!
31//! // Set low parameters for testing
32//! // XXX: not for production
33//! let params = StmParameters {
34//!     m: 100, // Security parameter XXX: not for production
35//!     k: 2, // Quorum parameter XXX: not for production
36//!     phi_f: 0.2, // Lottery parameter XXX: not for production
37//! };
38//!
39//! // Generate some arbitrary stake for each party
40//! // Stake is an integer.
41//! // Total stake of all parties is total stake in the system.
42//! let stakes = (0..nparties)
43//!     .into_iter()
44//!     .map(|_| 1 + (rng.next_u64() % 9999))
45//!     .collect::<Vec<_>>();
46//!
47//! // Create a new key registry from the parties and their stake
48//! let mut key_reg = KeyReg::init();
49//!
50//! // For each party, crate a StmInitializer.
51//! // This struct can create keys for the party.
52//! let mut ps: Vec<StmInitializer> = Vec::with_capacity(nparties);
53//! for stake in stakes {
54//!     // Create keys for this party
55//!     let p = StmInitializer::setup(params, stake, &mut rng);
56//!     // Register keys with the KeyReg service
57//!     key_reg
58//!         .register(p.stake, p.verification_key())
59//!         .unwrap();
60//!     ps.push(p);
61//! }
62//!
63//! // Close the key registration.
64//! let closed_reg = key_reg.close();
65//!
66//! // Finalize the StmInitializer and turn it into a StmSigner, which can execute the
67//! // rest of the protocol.
68//! let ps = ps
69//!     .into_par_iter()
70//!     .map(|p| p.new_signer(closed_reg.clone()).unwrap())
71//!     .collect::<Vec<StmSigner<D>>>();
72//!
73//! /////////////////////
74//! // operation phase //
75//! /////////////////////
76//!
77//! // Next, each party tries to sign the message for each index available.
78//! // We collect the successful signatures into a vec.
79//! let sigs = ps
80//!     .par_iter()
81//!     .filter_map(|p| {
82//!         return p.sign(&msg);
83//!     })
84//!     .collect::<Vec<StmSig>>();
85//!
86//! // StmClerk can aggregate and verify signatures.
87//! let clerk = StmClerk::from_signer(&ps[0]);
88//!
89//! // Aggregate and verify the signatures
90//! let msig = clerk.aggregate(&sigs, &msg);
91//! match msig {
92//!     Ok(aggr) => {
93//!         println!("Aggregate ok");
94//!         assert!(aggr
95//!             .verify(&msg, &clerk.compute_avk(), &params)
96//!             .is_ok());
97//!     }
98//!     Err(AggregationError::NotEnoughSignatures(n, k)) => {
99//!         println!("Not enough signatures");
100//!         assert!(n < params.k && k == params.k)
101//!     }
102//!     Err(_) => unreachable!(),
103//! }
104//! # Ok(())
105//! # }
106//! ```
107
108use crate::bls_multi_signature::{Signature, VerificationKey};
109use crate::eligibility_check::ev_lt_phi;
110use crate::error::{
111    AggregationError, CoreVerifierError, RegisterError, StmAggregateSignatureError,
112    StmSignatureError,
113};
114use crate::key_reg::{ClosedKeyReg, RegParty};
115use crate::merkle_tree::{BatchPath, MTLeaf, MerkleTreeCommitmentBatchCompat};
116use crate::participant::{StmSigner, StmVerificationKey};
117use blake2::digest::{Digest, FixedOutput};
118use serde::ser::SerializeTuple;
119use serde::{Deserialize, Serialize, Serializer};
120use std::cmp::Ordering;
121use std::collections::{BTreeMap, HashMap, HashSet};
122use std::convert::{From, TryFrom, TryInto};
123use std::hash::{Hash, Hasher};
124
125/// The quantity of stake held by a party, represented as a `u64`.
126pub type Stake = u64;
127
128/// Quorum index for signatures.
129/// An aggregate signature (`StmMultiSig`) must have at least `k` unique indices.
130pub type Index = u64;
131
132/// Used to set protocol parameters.
133// todo: this is the criteria to consider parameters valid:
134// Let A = max assumed adversarial stake
135// Let a = A / max_stake
136// Let p = φ(a)  // f needs tuning, something close to 0.2 is reasonable
137// Then, we're secure if SUM[from i=k to i=m] Binomial(i successes, m experiments, p chance of success) <= 2^-100 or thereabouts.
138// The latter turns to 1 - BinomialCDF(k-1,m,p)
139#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
140pub struct StmParameters {
141    /// Security parameter, upper bound on indices.
142    pub m: u64,
143    /// Quorum parameter.
144    pub k: u64,
145    /// `f` in phi(w) = 1 - (1 - f)^w, where w is the stake of a participant..
146    pub phi_f: f64,
147}
148
149impl StmParameters {
150    /// Convert to bytes
151    /// # Layout
152    /// * Security parameter, `m` (as u64)
153    /// * Quorum parameter, `k` (as u64)
154    /// * Phi f, as (f64)
155    pub fn to_bytes(&self) -> [u8; 24] {
156        let mut out = [0; 24];
157        out[..8].copy_from_slice(&self.m.to_be_bytes());
158        out[8..16].copy_from_slice(&self.k.to_be_bytes());
159        out[16..].copy_from_slice(&self.phi_f.to_be_bytes());
160        out
161    }
162
163    /// Extract the `StmParameters` from a byte slice.
164    /// # Error
165    /// The function fails if the given string of bytes is not of required size.
166    pub fn from_bytes(bytes: &[u8]) -> Result<Self, RegisterError> {
167        if bytes.len() != 24 {
168            return Err(RegisterError::SerializationError);
169        }
170
171        let mut u64_bytes = [0u8; 8];
172        u64_bytes.copy_from_slice(&bytes[..8]);
173        let m = u64::from_be_bytes(u64_bytes);
174        u64_bytes.copy_from_slice(&bytes[8..16]);
175        let k = u64::from_be_bytes(u64_bytes);
176        u64_bytes.copy_from_slice(&bytes[16..]);
177        let phi_f = f64::from_be_bytes(u64_bytes);
178
179        Ok(Self { m, k, phi_f })
180    }
181}
182
183/// Stm aggregate key (batch compatible), which contains the merkle tree commitment and the total stake of the system.
184/// Batch Compat Merkle tree commitment includes the number of leaves in the tree in order to obtain batch path.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186#[serde(bound(
187    serialize = "BatchPath<D>: Serialize",
188    deserialize = "BatchPath<D>: Deserialize<'de>"
189))]
190pub struct StmAggrVerificationKey<D: Clone + Digest + FixedOutput> {
191    mt_commitment: MerkleTreeCommitmentBatchCompat<D>,
192    total_stake: Stake,
193}
194
195impl<D: Digest + Clone + FixedOutput> PartialEq for StmAggrVerificationKey<D> {
196    fn eq(&self, other: &Self) -> bool {
197        self.mt_commitment == other.mt_commitment && self.total_stake == other.total_stake
198    }
199}
200
201impl<D: Digest + Clone + FixedOutput> Eq for StmAggrVerificationKey<D> {}
202
203impl<D: Clone + Digest + FixedOutput> From<&ClosedKeyReg<D>> for StmAggrVerificationKey<D> {
204    fn from(reg: &ClosedKeyReg<D>) -> Self {
205        Self {
206            mt_commitment: reg.merkle_tree.to_commitment_batch_compat(),
207            total_stake: reg.total_stake,
208        }
209    }
210}
211
212/// Signature created by a single party who has won the lottery.
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct StmSig {
215    /// The signature from the underlying MSP scheme.
216    pub sigma: Signature,
217    /// The index(es) for which the signature is valid
218    pub indexes: Vec<Index>,
219    /// Merkle tree index of the signer.
220    pub signer_index: Index,
221}
222
223impl StmSig {
224    /// Verify an stm signature by checking that the lottery was won, the merkle path is correct,
225    /// the indexes are in the desired range and the underlying multi signature validates.
226    pub fn verify<D: Clone + Digest + FixedOutput>(
227        &self,
228        params: &StmParameters,
229        pk: &StmVerificationKey,
230        stake: &Stake,
231        avk: &StmAggrVerificationKey<D>,
232        msg: &[u8],
233    ) -> Result<(), StmSignatureError> {
234        let msgp = avk.mt_commitment.concat_with_msg(msg);
235        self.verify_core(params, pk, stake, &msgp, &avk.total_stake)?;
236        Ok(())
237    }
238
239    /// Verify that all indices of a signature are valid.
240    pub(crate) fn check_indices(
241        &self,
242        params: &StmParameters,
243        stake: &Stake,
244        msg: &[u8],
245        total_stake: &Stake,
246    ) -> Result<(), StmSignatureError> {
247        for &index in &self.indexes {
248            if index > params.m {
249                return Err(StmSignatureError::IndexBoundFailed(index, params.m));
250            }
251
252            let ev = self.sigma.eval(msg, index);
253
254            if !ev_lt_phi(params.phi_f, ev, *stake, *total_stake) {
255                return Err(StmSignatureError::LotteryLost);
256            }
257        }
258
259        Ok(())
260    }
261
262    /// Convert an `StmSig` into bytes
263    ///
264    /// # Layout
265    /// * Stake
266    /// * Number of valid indexes (as u64)
267    /// * Indexes of the signature
268    /// * Public Key
269    /// * Signature
270    /// * Merkle index of the signer.
271    pub fn to_bytes(&self) -> Vec<u8> {
272        let mut output = Vec::new();
273        output.extend_from_slice(&(self.indexes.len() as u64).to_be_bytes());
274
275        for index in &self.indexes {
276            output.extend_from_slice(&index.to_be_bytes());
277        }
278
279        output.extend_from_slice(&self.sigma.to_bytes());
280
281        output.extend_from_slice(&self.signer_index.to_be_bytes());
282        output
283    }
284
285    /// Extract a batch compatible `StmSig` from a byte slice.
286    pub fn from_bytes<D: Clone + Digest + FixedOutput>(
287        bytes: &[u8],
288    ) -> Result<StmSig, StmSignatureError> {
289        let mut u64_bytes = [0u8; 8];
290
291        u64_bytes.copy_from_slice(&bytes[0..8]);
292        let nr_indexes = u64::from_be_bytes(u64_bytes) as usize;
293
294        let mut indexes = Vec::new();
295        for i in 0..nr_indexes {
296            u64_bytes.copy_from_slice(&bytes[8 + i * 8..16 + i * 8]);
297            indexes.push(u64::from_be_bytes(u64_bytes));
298        }
299
300        let offset = 8 + nr_indexes * 8;
301        let sigma = Signature::from_bytes(&bytes[offset..offset + 48])?;
302
303        u64_bytes.copy_from_slice(&bytes[offset + 48..offset + 56]);
304        let signer_index = u64::from_be_bytes(u64_bytes);
305
306        Ok(StmSig {
307            sigma,
308            indexes,
309            signer_index,
310        })
311    }
312
313    /// Compare two `StmSig` by their signers' merkle tree indexes.
314    pub fn cmp_stm_sig(&self, other: &Self) -> Ordering {
315        self.signer_index.cmp(&other.signer_index)
316    }
317
318    /// Verify a core signature by checking that the lottery was won,
319    /// the indexes are in the desired range and the underlying multi signature validates.
320    pub fn verify_core(
321        &self,
322        params: &StmParameters,
323        pk: &StmVerificationKey,
324        stake: &Stake,
325        msg: &[u8],
326        total_stake: &Stake,
327    ) -> Result<(), StmSignatureError> {
328        self.sigma.verify(msg, pk)?;
329        self.check_indices(params, stake, msg, total_stake)?;
330
331        Ok(())
332    }
333}
334
335impl Hash for StmSig {
336    fn hash<H: Hasher>(&self, state: &mut H) {
337        Hash::hash_slice(&self.sigma.to_bytes(), state)
338    }
339}
340
341impl PartialEq for StmSig {
342    fn eq(&self, other: &Self) -> bool {
343        self.sigma == other.sigma
344    }
345}
346
347impl Eq for StmSig {}
348
349impl PartialOrd for StmSig {
350    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
351        Some(std::cmp::Ord::cmp(self, other))
352    }
353}
354
355impl Ord for StmSig {
356    fn cmp(&self, other: &Self) -> Ordering {
357        self.cmp_stm_sig(other)
358    }
359}
360
361/// Signature with its registered party.
362#[derive(Debug, Clone, Hash, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
363pub struct StmSigRegParty {
364    /// Stm signature
365    pub sig: StmSig,
366    /// Registered party
367    pub reg_party: RegParty,
368}
369
370impl StmSigRegParty {
371    /// Convert StmSigRegParty to bytes
372    /// # Layout
373    /// * RegParty
374    /// * Signature
375    pub fn to_bytes(&self) -> Vec<u8> {
376        let mut out = Vec::new();
377        out.extend_from_slice(&self.reg_party.to_bytes());
378        out.extend_from_slice(&self.sig.to_bytes());
379
380        out
381    }
382    ///Extract a `StmSigRegParty` from a byte slice.
383    pub fn from_bytes<D: Digest + Clone + FixedOutput>(
384        bytes: &[u8],
385    ) -> Result<StmSigRegParty, StmSignatureError> {
386        let reg_party = RegParty::from_bytes(&bytes[0..104])?;
387        let sig = StmSig::from_bytes::<D>(&bytes[104..])?;
388
389        Ok(StmSigRegParty { sig, reg_party })
390    }
391}
392
393impl Serialize for StmSigRegParty {
394    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
395    where
396        S: Serializer,
397    {
398        let mut tuple = serializer.serialize_tuple(2)?;
399        tuple.serialize_element(&self.sig)?;
400        tuple.serialize_element(&self.reg_party)?;
401        tuple.end()
402    }
403}
404
405/// `StmClerk` can verify and aggregate `StmSig`s and verify `StmMultiSig`s.
406/// Clerks can only be generated with the registration closed.
407/// This avoids that a Merkle Tree is computed before all parties have registered.
408#[derive(Debug, Clone)]
409pub struct StmClerk<D: Clone + Digest> {
410    pub(crate) closed_reg: ClosedKeyReg<D>,
411    pub(crate) params: StmParameters,
412}
413
414impl<D: Digest + Clone + FixedOutput> StmClerk<D> {
415    /// Create a new `Clerk` from a closed registration instance.
416    pub fn from_registration(params: &StmParameters, closed_reg: &ClosedKeyReg<D>) -> Self {
417        Self {
418            params: *params,
419            closed_reg: closed_reg.clone(),
420        }
421    }
422
423    /// Create a Clerk from a signer.
424    pub fn from_signer(signer: &StmSigner<D>) -> Self {
425        let closed_reg = signer
426            .get_closed_reg()
427            .clone()
428            .expect("Core signer does not include closed registration. StmClerk, and so, the Stm certificate cannot be built without closed registration!")
429            ;
430
431        Self {
432            params: signer.get_params(),
433            closed_reg,
434        }
435    }
436
437    /// Aggregate a set of signatures for their corresponding indices.
438    ///
439    /// This function first deduplicates the repeated signatures, and if there are enough signatures, it collects the merkle tree indexes of unique signatures.
440    /// The list of merkle tree indexes is used to create a batch proof, to prove that all signatures are from eligible signers.
441    ///
442    /// It returns an instance of `StmAggrSig`.
443    pub fn aggregate(
444        &self,
445        sigs: &[StmSig],
446        msg: &[u8],
447    ) -> Result<StmAggrSig<D>, AggregationError> {
448        let sig_reg_list = sigs
449            .iter()
450            .map(|sig| StmSigRegParty {
451                sig: sig.clone(),
452                reg_party: self.closed_reg.reg_parties[sig.signer_index as usize],
453            })
454            .collect::<Vec<StmSigRegParty>>();
455
456        let avk = StmAggrVerificationKey::from(&self.closed_reg);
457        let msgp = avk.mt_commitment.concat_with_msg(msg);
458        let mut unique_sigs = CoreVerifier::dedup_sigs_for_indices(
459            &self.closed_reg.total_stake,
460            &self.params,
461            &msgp,
462            &sig_reg_list,
463        )?;
464
465        unique_sigs.sort_unstable();
466
467        let mt_index_list = unique_sigs
468            .iter()
469            .map(|sig_reg| sig_reg.sig.signer_index as usize)
470            .collect::<Vec<usize>>();
471
472        let batch_proof = self.closed_reg.merkle_tree.get_batched_path(mt_index_list);
473
474        Ok(StmAggrSig {
475            signatures: unique_sigs,
476            batch_proof,
477        })
478    }
479
480    /// Compute the `StmAggrVerificationKey` related to the used registration.
481    pub fn compute_avk(&self) -> StmAggrVerificationKey<D> {
482        StmAggrVerificationKey::from(&self.closed_reg)
483    }
484
485    /// Get the (VK, stake) of a party given its index.
486    pub fn get_reg_party(&self, party_index: &Index) -> Option<(StmVerificationKey, Stake)> {
487        self.closed_reg
488            .reg_parties
489            .get(*party_index as usize)
490            .map(|&r| r.into())
491    }
492}
493
494/// `StmMultiSig` uses the "concatenation" proving system (as described in Section 4.3 of the original paper.)
495/// This means that the aggregated signature contains a vector with all individual signatures.
496/// BatchPath is also a part of the aggregate signature which covers path for all signatures.
497#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(bound(
499    serialize = "BatchPath<D>: Serialize",
500    deserialize = "BatchPath<D>: Deserialize<'de>"
501))]
502pub struct StmAggrSig<D: Clone + Digest + FixedOutput> {
503    pub(crate) signatures: Vec<StmSigRegParty>,
504    /// The list of unique merkle tree nodes that covers path for all signatures.
505    pub batch_proof: BatchPath<D>,
506}
507
508impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSig<D> {
509    /// Verify all checks from signatures, except for the signature verification itself.
510    ///
511    /// Indices and quorum are checked by `CoreVerifier::preliminary_verify` with `msgp`.
512    /// It collects leaves from signatures and checks the batch proof.
513    /// After batch proof is checked, it collects and returns the signatures and
514    /// verification keys to be used by aggregate verification.
515    fn preliminary_verify(
516        &self,
517        msg: &[u8],
518        avk: &StmAggrVerificationKey<D>,
519        parameters: &StmParameters,
520    ) -> Result<(Vec<Signature>, Vec<VerificationKey>), StmAggregateSignatureError<D>> {
521        let msgp = avk.mt_commitment.concat_with_msg(msg);
522        CoreVerifier::preliminary_verify(&avk.total_stake, &self.signatures, parameters, &msgp)?;
523
524        let leaves = self
525            .signatures
526            .iter()
527            .map(|r| r.reg_party)
528            .collect::<Vec<RegParty>>();
529
530        avk.mt_commitment.check(&leaves, &self.batch_proof)?;
531
532        Ok(CoreVerifier::collect_sigs_vks(&self.signatures))
533    }
534
535    /// Verify aggregate signature, by checking that
536    /// * each signature contains only valid indices,
537    /// * the lottery is indeed won by each one of them,
538    /// * the merkle tree path is valid,
539    /// * the aggregate signature validates with respect to the aggregate verification key
540    ///   (aggregation is computed using functions `MSP.BKey` and `MSP.BSig` as described in Section 2.4 of the paper).
541    pub fn verify(
542        &self,
543        msg: &[u8],
544        avk: &StmAggrVerificationKey<D>,
545        parameters: &StmParameters,
546    ) -> Result<(), StmAggregateSignatureError<D>> {
547        let msgp = avk.mt_commitment.concat_with_msg(msg);
548        let (sigs, vks) = self.preliminary_verify(msg, avk, parameters)?;
549
550        Signature::verify_aggregate(msgp.as_slice(), &vks, &sigs)?;
551        Ok(())
552    }
553
554    /// Batch verify a set of signatures, with different messages and avks.
555    #[cfg(feature = "batch-verify-aggregates")]
556    pub fn batch_verify(
557        stm_signatures: &[Self],
558        msgs: &[Vec<u8>],
559        avks: &[StmAggrVerificationKey<D>],
560        parameters: &[StmParameters],
561    ) -> Result<(), StmAggregateSignatureError<D>> {
562        let batch_size = stm_signatures.len();
563        assert_eq!(
564            batch_size,
565            msgs.len(),
566            "Number of messages should correspond to size of the batch"
567        );
568        assert_eq!(
569            batch_size,
570            avks.len(),
571            "Number of avks should correspond to size of the batch"
572        );
573        assert_eq!(
574            batch_size,
575            parameters.len(),
576            "Number of parameters should correspond to size of the batch"
577        );
578
579        let mut aggr_sigs = Vec::with_capacity(batch_size);
580        let mut aggr_vks = Vec::with_capacity(batch_size);
581        for (idx, sig_group) in stm_signatures.iter().enumerate() {
582            sig_group.preliminary_verify(&msgs[idx], &avks[idx], &parameters[idx])?;
583            let grouped_sigs: Vec<Signature> = sig_group
584                .signatures
585                .iter()
586                .map(|sig_reg| sig_reg.sig.sigma)
587                .collect();
588            let grouped_vks: Vec<VerificationKey> = sig_group
589                .signatures
590                .iter()
591                .map(|sig_reg| sig_reg.reg_party.0)
592                .collect();
593
594            let (aggr_vk, aggr_sig) = Signature::aggregate(&grouped_vks, &grouped_sigs).unwrap();
595            aggr_sigs.push(aggr_sig);
596            aggr_vks.push(aggr_vk);
597        }
598
599        let concat_msgs: Vec<Vec<u8>> = msgs
600            .iter()
601            .zip(avks.iter())
602            .map(|(msg, avk)| avk.mt_commitment.concat_with_msg(msg))
603            .collect();
604
605        Signature::batch_verify_aggregates(&concat_msgs, &aggr_vks, &aggr_sigs)?;
606        Ok(())
607    }
608
609    /// Convert multi signature to bytes
610    /// # Layout
611    /// * Number of the pairs of Signatures and Registered Parties (SigRegParty) (as u64)
612    /// * Size of a pair of Signature and Registered Party
613    /// * Pairs of Signatures and Registered Parties
614    /// * Batch proof
615    pub fn to_bytes(&self) -> Vec<u8> {
616        let mut out = Vec::new();
617        out.extend_from_slice(&u64::try_from(self.signatures.len()).unwrap().to_be_bytes());
618        out.extend_from_slice(
619            &u64::try_from(self.signatures[0].to_bytes().len())
620                .unwrap()
621                .to_be_bytes(),
622        );
623        for sig_reg in &self.signatures {
624            out.extend_from_slice(&sig_reg.to_bytes());
625        }
626        let proof = &self.batch_proof;
627        out.extend_from_slice(&proof.to_bytes());
628
629        out
630    }
631
632    ///Extract a `StmAggrSig` from a byte slice.
633    pub fn from_bytes(bytes: &[u8]) -> Result<StmAggrSig<D>, StmAggregateSignatureError<D>> {
634        let mut u64_bytes = [0u8; 8];
635
636        u64_bytes.copy_from_slice(&bytes[..8]);
637        let size = usize::try_from(u64::from_be_bytes(u64_bytes))
638            .map_err(|_| StmAggregateSignatureError::SerializationError)?;
639
640        u64_bytes.copy_from_slice(&bytes[8..16]);
641        let sig_reg_size = usize::try_from(u64::from_be_bytes(u64_bytes))
642            .map_err(|_| StmAggregateSignatureError::SerializationError)?;
643
644        let mut sig_reg_list = Vec::with_capacity(size);
645        for i in 0..size {
646            let sig_reg = StmSigRegParty::from_bytes::<D>(
647                &bytes[16 + (sig_reg_size * i)..16 + (sig_reg_size * (i + 1))],
648            )?;
649            sig_reg_list.push(sig_reg);
650        }
651
652        let offset = 16 + sig_reg_size * size;
653        let batch_proof = BatchPath::from_bytes(&bytes[offset..])?;
654
655        Ok(StmAggrSig {
656            signatures: sig_reg_list,
657            batch_proof,
658        })
659    }
660}
661
662/// Full node verifier including the list of eligible signers and the total stake of the system.
663pub struct CoreVerifier {
664    /// List of registered parties.
665    pub eligible_parties: Vec<RegParty>,
666    /// Total stake of registered parties.
667    pub total_stake: Stake,
668}
669
670impl CoreVerifier {
671    /// Setup a core verifier for given list of signers.
672    ///     * Collect the unique signers in a hash set,
673    ///     * Calculate the total stake of the eligible signers,
674    ///     * Sort the eligible signers.
675    pub fn setup(public_signers: &[(VerificationKey, Stake)]) -> Self {
676        let mut total_stake: Stake = 0;
677        let mut unique_parties = HashSet::new();
678        for signer in public_signers.iter() {
679            let (res, overflow) = total_stake.overflowing_add(signer.1);
680            if overflow {
681                panic!("Total stake overflow");
682            }
683            total_stake = res;
684            unique_parties.insert(MTLeaf(signer.0, signer.1));
685        }
686
687        let mut eligible_parties: Vec<_> = unique_parties.into_iter().collect();
688        eligible_parties.sort_unstable();
689        CoreVerifier {
690            eligible_parties,
691            total_stake,
692        }
693    }
694
695    /// Preliminary verification that checks whether indices are unique and the quorum is achieved.
696    fn preliminary_verify(
697        total_stake: &Stake,
698        signatures: &[StmSigRegParty],
699        parameters: &StmParameters,
700        msg: &[u8],
701    ) -> Result<(), CoreVerifierError> {
702        let mut nr_indices = 0;
703        let mut unique_indices = HashSet::new();
704
705        for sig_reg in signatures {
706            sig_reg
707                .sig
708                .check_indices(parameters, &sig_reg.reg_party.1, msg, total_stake)?;
709            for &index in &sig_reg.sig.indexes {
710                unique_indices.insert(index);
711                nr_indices += 1;
712            }
713        }
714
715        if nr_indices != unique_indices.len() {
716            return Err(CoreVerifierError::IndexNotUnique);
717        }
718        if (nr_indices as u64) < parameters.k {
719            return Err(CoreVerifierError::NoQuorum(nr_indices as u64, parameters.k));
720        }
721
722        Ok(())
723    }
724
725    /// Given a slice of `sig_reg_list`, this function returns a new list of `sig_reg_list` with only valid indices.
726    /// In case of conflict (having several signatures for the same index)
727    /// it selects the smallest signature (i.e. takes the signature with the smallest scalar).
728    /// The function selects at least `self.k` indexes.
729    ///  # Error
730    /// If there is no sufficient signatures, then the function fails.
731    // todo: We need to agree on a criteria to dedup (by default we use a BTreeMap that guarantees keys order)
732    // todo: not good, because it only removes index if there is a conflict (see benches)
733    pub fn dedup_sigs_for_indices(
734        total_stake: &Stake,
735        params: &StmParameters,
736        msg: &[u8],
737        sigs: &[StmSigRegParty],
738    ) -> Result<Vec<StmSigRegParty>, AggregationError> {
739        let mut sig_by_index: BTreeMap<Index, &StmSigRegParty> = BTreeMap::new();
740        let mut removal_idx_by_vk: HashMap<&StmSigRegParty, Vec<Index>> = HashMap::new();
741
742        for sig_reg in sigs.iter() {
743            if sig_reg
744                .sig
745                .verify_core(
746                    params,
747                    &sig_reg.reg_party.0,
748                    &sig_reg.reg_party.1,
749                    msg,
750                    total_stake,
751                )
752                .is_err()
753            {
754                continue;
755            }
756            for index in sig_reg.sig.indexes.iter() {
757                let mut insert_this_sig = false;
758                if let Some(&previous_sig) = sig_by_index.get(index) {
759                    let sig_to_remove_index = if sig_reg.sig.sigma < previous_sig.sig.sigma {
760                        insert_this_sig = true;
761                        previous_sig
762                    } else {
763                        sig_reg
764                    };
765
766                    if let Some(indexes) = removal_idx_by_vk.get_mut(sig_to_remove_index) {
767                        indexes.push(*index);
768                    } else {
769                        removal_idx_by_vk.insert(sig_to_remove_index, vec![*index]);
770                    }
771                } else {
772                    insert_this_sig = true;
773                }
774
775                if insert_this_sig {
776                    sig_by_index.insert(*index, sig_reg);
777                }
778            }
779        }
780
781        let mut dedup_sigs: HashSet<StmSigRegParty> = HashSet::new();
782        let mut count: u64 = 0;
783
784        for (_, &sig_reg) in sig_by_index.iter() {
785            if dedup_sigs.contains(sig_reg) {
786                continue;
787            }
788            let mut deduped_sig = sig_reg.clone();
789            if let Some(indexes) = removal_idx_by_vk.get(sig_reg) {
790                deduped_sig.sig.indexes = deduped_sig
791                    .sig
792                    .indexes
793                    .clone()
794                    .into_iter()
795                    .filter(|i| !indexes.contains(i))
796                    .collect();
797            }
798
799            let size: Result<u64, _> = deduped_sig.sig.indexes.len().try_into();
800            if let Ok(size) = size {
801                if dedup_sigs.contains(&deduped_sig) {
802                    panic!("Should not reach!");
803                }
804                dedup_sigs.insert(deduped_sig);
805                count += size;
806
807                if count >= params.k {
808                    return Ok(dedup_sigs.into_iter().collect());
809                }
810            }
811        }
812
813        Err(AggregationError::NotEnoughSignatures(count, params.k))
814    }
815
816    /// Collect and return `Vec<Signature>, Vec<VerificationKey>` which will be used
817    /// by the aggregate verification.
818    fn collect_sigs_vks(sig_reg_list: &[StmSigRegParty]) -> (Vec<Signature>, Vec<VerificationKey>) {
819        let sigs = sig_reg_list
820            .iter()
821            .map(|sig_reg| sig_reg.sig.sigma)
822            .collect::<Vec<Signature>>();
823        let vks = sig_reg_list
824            .iter()
825            .map(|sig_reg| sig_reg.reg_party.0)
826            .collect::<Vec<VerificationKey>>();
827
828        (sigs, vks)
829    }
830
831    /// Core verification
832    ///
833    /// Verify a list of signatures with respect to given message with given parameters.
834    pub fn verify(
835        &self,
836        signatures: &[StmSig],
837        parameters: &StmParameters,
838        msg: &[u8],
839    ) -> Result<(), CoreVerifierError> {
840        let sig_reg_list = signatures
841            .iter()
842            .map(|sig| StmSigRegParty {
843                sig: sig.clone(),
844                reg_party: self.eligible_parties[sig.signer_index as usize],
845            })
846            .collect::<Vec<StmSigRegParty>>();
847
848        let unique_sigs =
849            Self::dedup_sigs_for_indices(&self.total_stake, parameters, msg, &sig_reg_list)?;
850
851        Self::preliminary_verify(&self.total_stake, &unique_sigs, parameters, msg)?;
852
853        let (sigs, vks) = Self::collect_sigs_vks(&unique_sigs);
854
855        Signature::verify_aggregate(msg.to_vec().as_slice(), &vks, &sigs)?;
856
857        Ok(())
858    }
859}
860
861#[cfg(test)]
862mod tests {
863    use super::*;
864    use crate::key_reg::*;
865    use blake2::{digest::consts::U32, Blake2b};
866    use proptest::collection::{hash_map, vec};
867    use proptest::prelude::*;
868    use proptest::test_runner::{RngAlgorithm::ChaCha, TestRng};
869    use std::collections::{HashMap, HashSet};
870
871    use crate::participant::{StmInitializer, StmSigner};
872    use rand_chacha::ChaCha20Rng;
873    use rand_core::SeedableRng;
874
875    type Sig = StmAggrSig<D>;
876    type D = Blake2b<U32>;
877
878    fn setup_equal_parties(params: StmParameters, nparties: usize) -> Vec<StmSigner<D>> {
879        let stake = vec![1; nparties];
880        setup_parties(params, stake)
881    }
882
883    fn setup_parties(params: StmParameters, stake: Vec<Stake>) -> Vec<StmSigner<D>> {
884        let mut kr = KeyReg::init();
885        let mut trng = TestRng::deterministic_rng(ChaCha);
886        let mut rng = ChaCha20Rng::from_seed(trng.gen());
887
888        #[allow(clippy::needless_collect)]
889        let ps = stake
890            .into_iter()
891            .map(|stake| {
892                let p = StmInitializer::setup(params, stake, &mut rng);
893                kr.register(stake, p.pk).unwrap();
894                p
895            })
896            .collect::<Vec<_>>();
897        let closed_reg = kr.close();
898        ps.into_iter()
899            .map(|p| p.new_signer(closed_reg.clone()).unwrap())
900            .collect()
901    }
902
903    /// Generate a vector of stakes that should sum to `honest_stake`
904    /// when ignoring the indices in `adversaries`
905    fn arb_honest_for_adversaries(
906        num_parties: usize,
907        honest_stake: Stake,
908        adversaries: HashMap<usize, Stake>,
909    ) -> impl Strategy<Value = Vec<Stake>> {
910        vec(1..honest_stake, num_parties).prop_map(move |parties| {
911            let honest_sum = parties.iter().enumerate().fold(0, |acc, (i, s)| {
912                if !adversaries.contains_key(&i) {
913                    acc + s
914                } else {
915                    acc
916                }
917            });
918
919            parties
920                .iter()
921                .enumerate()
922                .map(|(i, s)| {
923                    if let Some(a) = adversaries.get(&i) {
924                        *a
925                    } else {
926                        (*s * honest_stake) / honest_sum
927                    }
928                })
929                .collect()
930        })
931    }
932
933    /// Generate a vector of `num_parties` stakes summing to `num_parties * total_stake`,
934    /// plus a subset S of 0..num_parties such that the sum of the stakes at indices
935    /// in S is adversary_stake * N
936    fn arb_parties_with_adversaries(
937        num_parties: usize,
938        num_adversaries: usize,
939        total_stake: Stake,
940        adversary_stake: Stake,
941    ) -> impl Strategy<Value = (HashSet<usize>, Vec<Stake>)> {
942        hash_map(0..num_parties, 1..total_stake, num_adversaries).prop_flat_map(
943            move |adversaries| {
944                let adversary_sum: Stake = adversaries.values().sum();
945                let adversaries_normed = adversaries
946                    .iter()
947                    .map(|(a, stake)| (*a, (stake * adversary_stake) / adversary_sum))
948                    .collect();
949
950                let adversaries = adversaries.into_keys().collect();
951                (
952                    Just(adversaries),
953                    arb_honest_for_adversaries(
954                        num_parties,
955                        total_stake - adversary_stake,
956                        adversaries_normed,
957                    ),
958                )
959            },
960        )
961    }
962
963    fn find_signatures(msg: &[u8], ps: &[StmSigner<D>], is: &[usize]) -> Vec<StmSig> {
964        let mut sigs = Vec::new();
965        for i in is {
966            if let Some(sig) = ps[*i].sign(msg) {
967                sigs.push(sig);
968            }
969        }
970        sigs
971    }
972
973    /// Pick N between min and max, and then
974    /// generate a vector of N stakes summing to N * tstake,
975    /// plus a subset S of 0..N such that the sum of the stakes at indices
976    /// in S is astake * N
977    fn arb_parties_adversary_stake(
978        min: usize,
979        max: usize,
980        tstake: Stake,
981        astake: Stake,
982    ) -> impl Strategy<Value = (HashSet<usize>, Vec<Stake>)> {
983        (min..max)
984            .prop_flat_map(|n| (Just(n), 1..=n / 2))
985            .prop_flat_map(move |(n, nadv)| {
986                arb_parties_with_adversaries(n, nadv, tstake * n as Stake, astake * n as Stake)
987            })
988    }
989
990    #[derive(Debug)]
991    struct ProofTest {
992        msig: Result<Sig, AggregationError>,
993        clerk: StmClerk<D>,
994        msg: [u8; 16],
995    }
996    /// Run the protocol up to aggregation. This will produce a valid aggregation of signatures.
997    /// The following tests mutate this aggregation so that the proof is no longer valid.
998    fn arb_proof_setup(max_parties: usize) -> impl Strategy<Value = ProofTest> {
999        any::<[u8; 16]>().prop_flat_map(move |msg| {
1000            (2..max_parties).prop_map(move |n| {
1001                let params = StmParameters {
1002                    m: 5,
1003                    k: 5,
1004                    phi_f: 1.0,
1005                };
1006                let ps = setup_equal_parties(params, n);
1007                let clerk = StmClerk::from_signer(&ps[0]);
1008
1009                let all_ps: Vec<usize> = (0..n).collect();
1010                let sigs = find_signatures(&msg, &ps, &all_ps);
1011
1012                let msig = clerk.aggregate(&sigs, &msg);
1013                ProofTest { msig, clerk, msg }
1014            })
1015        })
1016    }
1017
1018    fn with_proof_mod<F>(mut tc: ProofTest, f: F)
1019    where
1020        F: Fn(&mut Sig, &mut StmClerk<D>, &mut [u8; 16]),
1021    {
1022        match tc.msig {
1023            Ok(mut aggr) => {
1024                f(&mut aggr, &mut tc.clerk, &mut tc.msg);
1025                assert!(aggr
1026                    .verify(&tc.msg, &tc.clerk.compute_avk(), &tc.clerk.params)
1027                    .is_err())
1028            }
1029            Err(e) => unreachable!("Reached an unexpected error: {:?}", e),
1030        }
1031    }
1032
1033    proptest! {
1034        #![proptest_config(ProptestConfig::with_cases(50))]
1035
1036        #[test]
1037        /// Test that `dedup_sigs_for_indices` only takes valid signatures.
1038        fn test_dedup(msg in any::<[u8; 16]>()) {
1039            let false_msg = [1u8; 20];
1040            let params = StmParameters { m: 1, k: 1, phi_f: 1.0 };
1041            let ps = setup_equal_parties(params, 1);
1042            let clerk = StmClerk::from_signer(&ps[0]);
1043            let avk = clerk.compute_avk();
1044            let mut sigs = Vec::with_capacity(2);
1045
1046            if let Some(sig) = ps[0].sign(&false_msg) {
1047                sigs.push(sig);
1048            }
1049
1050            if let Some(sig) = ps[0].sign(&msg) {
1051                sigs.push(sig);
1052            }
1053
1054            let sig_reg_list = sigs
1055            .iter()
1056            .map(|sig| StmSigRegParty {
1057                sig: sig.clone(),
1058                reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize],
1059            })
1060            .collect::<Vec<StmSigRegParty>>();
1061
1062            let msgp = avk.mt_commitment.concat_with_msg(&msg);
1063            let dedup_result = CoreVerifier::dedup_sigs_for_indices(
1064                &clerk.closed_reg.total_stake,
1065                &params,
1066                &msgp,
1067                &sig_reg_list,
1068            );
1069            assert!(dedup_result.is_ok(), "dedup failure {dedup_result:?}");
1070            for passed_sigs in dedup_result.unwrap() {
1071                let verify_result = passed_sigs.sig.verify(&params, &ps[0].get_vk(), &ps[0].get_stake(), &avk, &msg);
1072                assert!(verify_result.is_ok(), "verify {verify_result:?}");
1073            }
1074        }
1075    }
1076
1077    proptest! {
1078        #![proptest_config(ProptestConfig::with_cases(50))]
1079
1080        #[test]
1081        /// Test that when a quorum is found, the aggregate signature can be verified by anyone with
1082        /// access to the avk and the parameters.
1083        fn test_aggregate_sig(nparties in 2_usize..30,
1084                              m in 10_u64..20,
1085                              k in 1_u64..5,
1086                              msg in any::<[u8;16]>()) {
1087            let params = StmParameters { m, k, phi_f: 0.2 };
1088            let ps = setup_equal_parties(params, nparties);
1089            let clerk = StmClerk::from_signer(&ps[0]);
1090
1091            let all_ps: Vec<usize> = (0..nparties).collect();
1092            let sigs = find_signatures(&msg, &ps, &all_ps);
1093            let msig = clerk.aggregate(&sigs, &msg);
1094
1095            match msig {
1096                Ok(aggr) => {
1097                    let verify_result = aggr.verify(&msg, &clerk.compute_avk(), &params);
1098                    assert!(verify_result.is_ok(), "Verification failed: {verify_result:?}");
1099                }
1100                Err(AggregationError::NotEnoughSignatures(n, k)) =>
1101                    assert!(n < params.k || k == params.k),
1102                Err(AggregationError::UsizeConversionInvalid) =>
1103                    unreachable!()
1104            }
1105        }
1106
1107        #[test]
1108        /// Test that batch verification of certificates works
1109        fn batch_verify(nparties in 2_usize..30,
1110                              m in 10_u64..20,
1111                              k in 1_u64..4,
1112                              seed in any::<[u8;32]>(),
1113                              batch_size in 2..10,
1114        ) {
1115            let mut rng = ChaCha20Rng::from_seed(seed);
1116            let mut aggr_avks = Vec::new();
1117            let mut aggr_stms = Vec::new();
1118            let mut batch_msgs = Vec::new();
1119            let mut batch_params = Vec::new();
1120            for _ in 0..batch_size {
1121                let mut msg = [0u8; 32];
1122                rng.fill_bytes(&mut msg);
1123                let params = StmParameters { m, k, phi_f: 0.95 };
1124                let ps = setup_equal_parties(params, nparties);
1125                let clerk = StmClerk::from_signer(&ps[0]);
1126
1127                let all_ps: Vec<usize> = (0..nparties).collect();
1128                let sigs = find_signatures(&msg, &ps, &all_ps);
1129                let msig = clerk.aggregate(&sigs, &msg);
1130
1131                match msig {
1132                    Ok(aggr) => {
1133                        aggr_avks.push(clerk.compute_avk());
1134                        aggr_stms.push(aggr);
1135                        batch_msgs.push(msg.to_vec());
1136                        batch_params.push(params);
1137                    }
1138                    Err(AggregationError::NotEnoughSignatures(_n, _k)) => {
1139                        assert!(sigs.len() < params.k as usize)
1140                    }
1141                    Err(AggregationError::UsizeConversionInvalid) => unreachable!(),
1142                }
1143            }
1144
1145            assert!(StmAggrSig::batch_verify(&aggr_stms, &batch_msgs, &aggr_avks, &batch_params).is_ok());
1146
1147            let mut msg = [0u8; 32];
1148            rng.fill_bytes(&mut msg);
1149            let params = StmParameters { m, k, phi_f: 0.8 };
1150            let ps = setup_equal_parties(params, nparties);
1151            let clerk = StmClerk::from_signer(&ps[0]);
1152
1153            let all_ps: Vec<usize> = (0..nparties).collect();
1154            let sigs = find_signatures(&msg, &ps, &all_ps);
1155            let fake_msig = clerk.aggregate(&sigs, &msg);
1156
1157            aggr_stms[0] = fake_msig.unwrap();
1158            assert!(StmAggrSig::batch_verify(&aggr_stms, &batch_msgs, &aggr_avks, &batch_params).is_err());
1159        }
1160    }
1161
1162    proptest! {
1163        #[test]
1164        /// Test that when a party creates a signature it can be verified
1165        fn test_sig(msg in any::<[u8;16]>()) {
1166            let params = StmParameters { m: 1, k: 1, phi_f: 0.2 };
1167            let ps = setup_equal_parties(params, 1);
1168            let clerk = StmClerk::from_signer(&ps[0]);
1169            let avk = clerk.compute_avk();
1170
1171            if let Some(sig) = ps[0].sign(&msg) {
1172                assert!(sig.verify(&params, &ps[0].get_vk(), &ps[0].get_stake(), &avk, &msg).is_ok());
1173            }
1174        }
1175    }
1176
1177    proptest! {
1178        #![proptest_config(ProptestConfig::with_cases(10))]
1179        #[test]
1180        fn test_parameters_serialize_deserialize(m in any::<u64>(), k in any::<u64>(), phi_f in any::<f64>()) {
1181            let params = StmParameters { m, k, phi_f };
1182
1183            let bytes = params.to_bytes();
1184            let deserialised = StmParameters::from_bytes(&bytes);
1185            assert!(deserialised.is_ok())
1186        }
1187
1188        #[test]
1189        fn test_initializer_serialize_deserialize(seed in any::<[u8;32]>()) {
1190            let mut rng = ChaCha20Rng::from_seed(seed);
1191            let params = StmParameters { m: 1, k: 1, phi_f: 1.0 };
1192            let stake = rng.next_u64();
1193            let initializer = StmInitializer::setup(params, stake, &mut rng);
1194
1195            let bytes = initializer.to_bytes();
1196            assert!(StmInitializer::from_bytes(&bytes).is_ok());
1197
1198            let bytes = bincode::serialize(&initializer).unwrap();
1199            assert!(bincode::deserialize::<StmInitializer>(&bytes).is_ok())
1200        }
1201
1202        #[test]
1203        fn test_sig_serialize_deserialize(msg in any::<[u8;16]>()) {
1204            let params = StmParameters { m: 1, k: 1, phi_f: 0.2 };
1205            let ps = setup_equal_parties(params, 1);
1206            let clerk = StmClerk::from_signer(&ps[0]);
1207            let avk = clerk.compute_avk();
1208
1209            if let Some(sig) = ps[0].sign(&msg) {
1210                let bytes = sig.to_bytes();
1211                let sig_deser = StmSig::from_bytes::<D>(&bytes).unwrap();
1212                assert!(sig_deser.verify(&params, &ps[0].get_vk(), &ps[0].get_stake(), &avk, &msg).is_ok());
1213
1214                let encoded = bincode::serialize(&sig).unwrap();
1215                let decoded: StmSig = bincode::deserialize(&encoded).unwrap();
1216                assert!(decoded.verify(&params, &ps[0].get_vk(), &ps[0].get_stake(), &avk, &msg).is_ok());
1217            }
1218        }
1219
1220        #[test]
1221        fn test_multisig_serialize_deserialize(nparties in 2_usize..10,
1222                                          msg in any::<[u8;16]>()) {
1223            let params = StmParameters { m: 10, k: 5, phi_f: 1.0 };
1224            let ps = setup_equal_parties(params, nparties);
1225            let clerk = StmClerk::from_signer(&ps[0]);
1226
1227            let all_ps: Vec<usize> = (0..nparties).collect();
1228            let sigs = find_signatures(&msg, &ps, &all_ps);
1229            let msig = clerk.aggregate(&sigs, &msg);
1230            if let Ok(aggr) = msig {
1231                    let bytes: Vec<u8> = aggr.to_bytes();
1232                    let aggr2 = StmAggrSig::from_bytes(&bytes).unwrap();
1233                    assert!(aggr2.verify(&msg, &clerk.compute_avk(), &params).is_ok());
1234
1235                    let encoded = bincode::serialize(&aggr).unwrap();
1236                    let decoded: StmAggrSig::<D> = bincode::deserialize(&encoded).unwrap();
1237                    assert!(decoded.verify(&msg, &clerk.compute_avk(), &params).is_ok());
1238            }
1239        }
1240    }
1241
1242    proptest! {
1243        #![proptest_config(ProptestConfig::with_cases(10))]
1244
1245        #[test]
1246        /// Test that when the adversaries do not hold sufficient stake, they can not form a quorum
1247        fn test_adversary_quorum(
1248            (adversaries, parties) in arb_parties_adversary_stake(8, 30, 16, 4),
1249            msg in any::<[u8;16]>(),
1250        ) {
1251            // Test sanity check:
1252            // Check that the adversarial party has less than 40% of the total stake.
1253            let (good, bad) = parties.iter().enumerate().fold((0,0), |(acc1, acc2), (i, st)| {
1254                if adversaries.contains(&i) {
1255                    (acc1, acc2 + *st)
1256                } else {
1257                    (acc1 + *st, acc2)
1258                }
1259            });
1260            assert!(bad as f64 / ((good + bad) as f64) < 0.4);
1261
1262            let params = StmParameters { m: 2642, k: 357, phi_f: 0.2 }; // From Table 1
1263            let ps = setup_parties(params, parties);
1264
1265            let sigs =  find_signatures(&msg, &ps, &adversaries.into_iter().collect::<Vec<_>>());
1266
1267            assert!(sigs.len() < params.k as usize);
1268
1269            let clerk = StmClerk::from_signer(&ps[0]);
1270
1271            let msig = clerk.aggregate(&sigs, &msg);
1272            match msig {
1273                Err(AggregationError::NotEnoughSignatures(n, k)) =>
1274                    assert!(n < params.k && params.k == k),
1275                _ =>
1276                    unreachable!(),
1277            }
1278        }
1279    }
1280
1281    proptest! {
1282        // Each of the tests below corresponds to falsifying a conjunct in the
1283        // definition of a valid signature
1284        #[test]
1285        fn test_invalid_proof_quorum(tc in arb_proof_setup(10)) {
1286            with_proof_mod(tc, |_aggr, clerk, _msg| {
1287                clerk.params.k += 7;
1288            })
1289        }
1290        // todo: fn test_invalid_proof_individual_sig
1291        #[test]
1292        fn test_invalid_proof_index_bound(tc in arb_proof_setup(10)) {
1293            with_proof_mod(tc, |_aggr, clerk, _msg| {
1294                clerk.params.m = 1;
1295            })
1296        }
1297        #[test]
1298        fn test_invalid_proof_index_unique(tc in arb_proof_setup(10)) {
1299            with_proof_mod(tc, |aggr, clerk, _msg| {
1300                for sig_reg in aggr.signatures.iter_mut() {
1301                    for index in sig_reg.sig.indexes.iter_mut() {
1302                       *index %= clerk.params.k - 1
1303                    }
1304                }
1305            })
1306        }
1307        #[test]
1308        fn test_invalid_proof_path(tc in arb_proof_setup(10)) {
1309            with_proof_mod(tc, |aggr, _, _msg| {
1310                let p = aggr.batch_proof.clone();
1311                let mut index_list = p.indices.clone();
1312                let values = p.values;
1313                let batch_proof = {
1314                    index_list[0] += 1;
1315                    BatchPath {
1316                        values,
1317                        indices: index_list,
1318                        hasher: Default::default()
1319                    }
1320                };
1321                aggr.batch_proof = batch_proof;
1322            })
1323        }
1324    }
1325
1326    // ---------------------------------------------------------------------
1327    // Core verifier
1328    // ---------------------------------------------------------------------
1329    fn setup_equal_core_parties(
1330        params: StmParameters,
1331        nparties: usize,
1332    ) -> (Vec<StmInitializer>, Vec<(VerificationKey, Stake)>) {
1333        let stake = vec![1; nparties];
1334        setup_core_parties(params, stake)
1335    }
1336
1337    fn setup_core_parties(
1338        params: StmParameters,
1339        stake: Vec<Stake>,
1340    ) -> (Vec<StmInitializer>, Vec<(VerificationKey, Stake)>) {
1341        let mut trng = TestRng::deterministic_rng(ChaCha);
1342        let mut rng = ChaCha20Rng::from_seed(trng.gen());
1343
1344        let ps = stake
1345            .into_iter()
1346            .map(|stake| StmInitializer::setup(params, stake, &mut rng))
1347            .collect::<Vec<StmInitializer>>();
1348
1349        let public_signers = ps
1350            .iter()
1351            .map(|s| (s.pk.vk, s.stake))
1352            .collect::<Vec<(VerificationKey, Stake)>>();
1353
1354        (ps, public_signers)
1355    }
1356
1357    fn find_core_signatures(
1358        msg: &[u8],
1359        ps: &[StmSigner<D>],
1360        total_stake: Stake,
1361        is: &[usize],
1362    ) -> Vec<StmSig> {
1363        let mut sigs = Vec::new();
1364        for i in is {
1365            if let Some(sig) = ps[*i].core_sign(msg, total_stake) {
1366                sigs.push(sig);
1367            }
1368        }
1369        sigs
1370    }
1371
1372    proptest! {
1373        #![proptest_config(ProptestConfig::with_cases(50))]
1374
1375        #[test]
1376        fn test_core_verifier(nparties in 2_usize..30,
1377                              m in 10_u64..20,
1378                              k in 1_u64..5,
1379                              msg in any::<[u8;16]>()) {
1380
1381            let params = StmParameters { m, k, phi_f: 0.2 };
1382            let (initializers, public_signers) = setup_equal_core_parties(params, nparties);
1383            let all_ps: Vec<usize> = (0..nparties).collect();
1384
1385            let core_verifier = CoreVerifier::setup(&public_signers);
1386
1387            let signers = initializers
1388                .into_iter()
1389                .filter_map(|s| s.new_core_signer(&core_verifier.eligible_parties))
1390                .collect::<Vec<StmSigner<D>>>();
1391
1392            let signatures = find_core_signatures(&msg, &signers, core_verifier.total_stake, &all_ps);
1393
1394            let verify_result = core_verifier.verify(&signatures, &params, &msg);
1395
1396            match verify_result{
1397                Ok(_) => {
1398                    assert!(verify_result.is_ok(), "Verification failed: {verify_result:?}");
1399                }
1400                Err(CoreVerifierError::NoQuorum(nr_indices, _k)) => {
1401                    assert!((nr_indices) < params.k);
1402                }
1403                Err(CoreVerifierError::IndexNotUnique) => unreachable!(),
1404                _ => unreachable!(),
1405            }
1406        }
1407
1408        #[test]
1409        fn test_total_stake_core_verifier(nparties in 2_usize..30,
1410                              m in 10_u64..20,
1411                              k in 1_u64..5,) {
1412            let params = StmParameters { m, k, phi_f: 0.2 };
1413            let (_initializers, public_signers) = setup_equal_core_parties(params, nparties);
1414            let core_verifier = CoreVerifier::setup(&public_signers);
1415            assert_eq!(nparties as u64, core_verifier.total_stake, "Total stake expected: {}, got: {}.", nparties, core_verifier.total_stake);
1416        }
1417    }
1418}