mithril_stm/protocol/aggregate_signature/
clerk.rs

1use anyhow::{Context, anyhow};
2use std::collections::{BTreeMap, HashMap, HashSet};
3
4use crate::{
5    ClosedKeyRegistration, Index, MembershipDigest, Parameters, Signer, SingleSignature,
6    SingleSignatureWithRegisteredParty, Stake, StmResult, VerificationKey,
7    proof_system::ConcatenationProof,
8};
9
10use super::{
11    AggregateSignature, AggregateSignatureType, AggregateVerificationKey, AggregationError,
12};
13
14/// `Clerk` can verify and aggregate `SingleSignature`s and verify `AggregateSignature`s.
15/// Clerks can only be generated with the registration closed.
16/// This avoids that a Merkle Tree is computed before all parties have registered.
17#[derive(Debug, Clone)]
18pub struct Clerk<D: MembershipDigest> {
19    pub(crate) closed_reg: ClosedKeyRegistration<D>,
20    pub(crate) params: Parameters,
21}
22
23impl<D: MembershipDigest> Clerk<D> {
24    /// Create a new `Clerk` from a closed registration instance.
25    pub fn new_clerk_from_closed_key_registration(
26        params: &Parameters,
27        closed_reg: &ClosedKeyRegistration<D>,
28    ) -> Self {
29        Self {
30            params: *params,
31            closed_reg: closed_reg.clone(),
32        }
33    }
34
35    /// Create a Clerk from a signer.
36    pub fn new_clerk_from_signer(signer: &Signer<D>) -> Self {
37        let closed_reg = signer
38            .get_closed_key_registration()
39            .clone()
40            .expect("Core signer does not include closed registration. Clerk, and so, the Stm certificate cannot be built without closed registration!")
41            ;
42
43        Self {
44            params: signer.get_parameters(),
45            closed_reg,
46        }
47    }
48
49    /// Aggregate a set of signatures with a given proof type.
50    pub fn aggregate_signatures_with_type(
51        &self,
52        sigs: &[SingleSignature],
53        msg: &[u8],
54        aggregate_signature_type: AggregateSignatureType,
55    ) -> StmResult<AggregateSignature<D>> {
56        match aggregate_signature_type {
57            AggregateSignatureType::Concatenation => Ok(AggregateSignature::Concatenation(
58                ConcatenationProof::aggregate_signatures(self, sigs, msg).with_context(|| {
59                    format!(
60                        "Signatures failed to aggregate for type {}",
61                        AggregateSignatureType::Concatenation
62                    )
63                })?,
64            )),
65            #[cfg(feature = "future_snark")]
66            AggregateSignatureType::Future => Err(anyhow!(
67                AggregationError::UnsupportedProofSystem(aggregate_signature_type)
68            )),
69        }
70    }
71
72    /// Compute the `AggregateVerificationKey` related to the used registration.
73    pub fn compute_aggregate_verification_key(&self) -> AggregateVerificationKey<D> {
74        AggregateVerificationKey::from(&self.closed_reg)
75    }
76
77    /// Get the (VK, stake) of a party given its index.
78    pub fn get_registered_party_for_index(
79        &self,
80        party_index: &Index,
81    ) -> Option<(VerificationKey, Stake)> {
82        self.closed_reg
83            .reg_parties
84            .get(*party_index as usize)
85            .map(|&r| r.into())
86    }
87
88    /// Given a slice of `sig_reg_list`, this function returns a new list of `sig_reg_list` with only valid indices.
89    /// In case of conflict (having several signatures for the same index)
90    /// it selects the smallest signature (i.e. takes the signature with the smallest scalar).
91    /// The function selects at least `self.k` indexes.
92    ///  # Error
93    /// If there is no sufficient signatures, then the function fails.
94    // todo: We need to agree on a criteria to dedup (by default we use a BTreeMap that guarantees keys order)
95    // todo: not good, because it only removes index if there is a conflict (see benches)
96    pub fn select_valid_signatures_for_k_indices(
97        params: &Parameters,
98        msg: &[u8],
99        sigs: &[SingleSignatureWithRegisteredParty],
100        avk: &AggregateVerificationKey<D>,
101    ) -> StmResult<Vec<SingleSignatureWithRegisteredParty>> {
102        let mut sig_by_index: BTreeMap<Index, &SingleSignatureWithRegisteredParty> =
103            BTreeMap::new();
104        let mut removal_idx_by_vk: HashMap<&SingleSignatureWithRegisteredParty, Vec<Index>> =
105            HashMap::new();
106
107        for sig_reg in sigs.iter() {
108            if sig_reg
109                .sig
110                .verify(params, &sig_reg.reg_party.0, &sig_reg.reg_party.1, avk, msg)
111                .is_err()
112            {
113                continue;
114            }
115            for index in sig_reg.sig.get_concatenation_signature_indices().iter() {
116                let mut insert_this_sig = false;
117                if let Some(&previous_sig) = sig_by_index.get(index) {
118                    let sig_to_remove_index = if sig_reg.sig.get_concatenation_signature_sigma()
119                        < previous_sig.sig.get_concatenation_signature_sigma()
120                    {
121                        insert_this_sig = true;
122                        previous_sig
123                    } else {
124                        sig_reg
125                    };
126
127                    if let Some(indexes) = removal_idx_by_vk.get_mut(sig_to_remove_index) {
128                        indexes.push(*index);
129                    } else {
130                        removal_idx_by_vk.insert(sig_to_remove_index, vec![*index]);
131                    }
132                } else {
133                    insert_this_sig = true;
134                }
135
136                if insert_this_sig {
137                    sig_by_index.insert(*index, sig_reg);
138                }
139            }
140        }
141
142        let mut dedup_sigs: HashSet<SingleSignatureWithRegisteredParty> = HashSet::new();
143        let mut count: u64 = 0;
144
145        for (_, &sig_reg) in sig_by_index.iter() {
146            if dedup_sigs.contains(sig_reg) {
147                continue;
148            }
149            let mut deduped_sig = sig_reg.clone();
150            if let Some(indexes) = removal_idx_by_vk.get(sig_reg) {
151                let indices = deduped_sig
152                    .sig
153                    .get_concatenation_signature_indices()
154                    .into_iter()
155                    .filter(|i| !indexes.contains(i))
156                    .collect::<Vec<Index>>();
157                deduped_sig.sig.set_concatenation_signature_indices(&indices);
158            }
159
160            let size: Result<u64, _> =
161                deduped_sig.sig.get_concatenation_signature_indices().len().try_into();
162            if let Ok(size) = size {
163                if dedup_sigs.contains(&deduped_sig) {
164                    panic!("Should not reach!");
165                }
166                dedup_sigs.insert(deduped_sig);
167                count += size;
168
169                if count >= params.k {
170                    return Ok(dedup_sigs.into_iter().collect());
171                }
172            }
173        }
174        Err(anyhow!(AggregationError::NotEnoughSignatures(
175            count, params.k
176        )))
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use proptest::prelude::*;
183    use rand_chacha::ChaCha20Rng;
184    use rand_core::{RngCore, SeedableRng};
185
186    use crate::{
187        Clerk, ClosedKeyRegistration, Initializer, KeyRegistration, MithrilMembershipDigest,
188        Parameters, SingleSignatureWithRegisteredParty,
189    };
190
191    use super::AggregationError;
192
193    type D = MithrilMembershipDigest;
194
195    proptest! {
196        #![proptest_config(ProptestConfig::with_cases(50))]
197
198        #[test]
199        fn test_dedup(
200            seed in any::<[u8; 32]>(),
201            msg in any::<[u8;16]>(),
202            nparties in 1_usize..10,
203            m in 1_u64..20,
204            k in 1_u64..10,
205            phi_f in 0.1_f64..1.0,
206            num_invalid_sigs_per_party in 0_usize..3,
207        ) {
208            let params = Parameters { m, k, phi_f };
209            let mut rng = ChaCha20Rng::from_seed(seed);
210
211            // False messages
212            let mut false_msgs = Vec::new();
213            for _ in 0..num_invalid_sigs_per_party {
214                let mut false_msg = vec![0u8; 32];
215                rng.fill_bytes(&mut false_msg);
216                if false_msg == msg {
217                    false_msg[0] = msg[0].wrapping_add(1);
218                }
219                false_msgs.push(false_msg);
220            }
221
222            let mut key_registration = KeyRegistration::init();
223            let mut initializers = Vec::new();
224
225            for i in 0..nparties {
226                let stake = (i as u64 + 1) * 10;
227                let initializer = Initializer::new(params, stake, &mut rng);
228                key_registration.register(initializer.stake, initializer.pk).unwrap();
229                initializers.push(initializer);
230            }
231
232            let closed_registration: ClosedKeyRegistration<D> = key_registration.close();
233
234            let signers: Vec<_> = initializers
235                .into_iter()
236                .map(|init| init.create_signer(closed_registration.clone()).unwrap())
237                .collect();
238
239            let clerk = Clerk::new_clerk_from_signer(&signers[0]);
240            let avk = clerk.compute_aggregate_verification_key();
241
242            let mut all_sigs = Vec::new();
243            for signer in &signers {
244                // Add invalid signatures
245                for false_msg in &false_msgs {
246                    if let Some(sig) = signer.sign(false_msg) {
247                        all_sigs.push(sig);
248                    }
249                }
250                // Add valid signatures
251                if let Some(sig) = signer.sign(&msg) {
252                    all_sigs.push(sig);
253                }
254            }
255
256            let sig_reg_list = all_sigs
257                .iter()
258                .map(|sig| SingleSignatureWithRegisteredParty {
259                    sig: sig.clone(),
260                    reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize],
261                })
262                .collect::<Vec<SingleSignatureWithRegisteredParty>>();
263
264            let dedup_result =
265                Clerk::select_valid_signatures_for_k_indices(&params, &msg, &sig_reg_list, &avk);
266
267            match dedup_result {
268                Ok(valid_sigs) => {
269                    assert!(!valid_sigs.is_empty(), "Should have at least one valid signature");
270
271                    for passed_sigs in valid_sigs {
272                        let signer = &signers[passed_sigs.sig.signer_index as usize];
273                        let verify_result = passed_sigs.sig.verify(
274                            &params,
275                            &signer.get_verification_key(),
276                            &signer.get_stake(),
277                            &avk,
278                            &msg,
279                        );
280                        assert!(verify_result.is_ok(), "All returned signatures should verify: {:?}", verify_result);
281                    }
282                }
283                Err(error) => {
284                    assert!(
285                        matches!(
286                            error.downcast_ref::<AggregationError>(),
287                            Some(AggregationError::NotEnoughSignatures(..))
288                        ),
289                        "Expected NotEnoughSignatures, got: {:?}", error
290                    );
291                }
292            }
293        }
294    }
295}