mithril_common/protocol/
multi_signer.rs1use anyhow::Context;
2use mithril_stm::{AggregateSignatureType, Parameters};
3
4use crate::{
5 StdResult,
6 crypto_helper::{ProtocolAggregateVerificationKey, ProtocolClerk, ProtocolMultiSignature},
7 entities::SingleSignature,
8 protocol::ToMessage,
9};
10
11pub struct MultiSigner {
13 protocol_clerk: ProtocolClerk,
14 protocol_parameters: Parameters,
15}
16
17impl MultiSigner {
18 pub(super) fn new(protocol_clerk: ProtocolClerk, protocol_parameters: Parameters) -> Self {
19 Self {
20 protocol_clerk,
21 protocol_parameters,
22 }
23 }
24
25 pub fn aggregate_single_signatures<T: ToMessage>(
27 &self,
28 single_signatures: &[SingleSignature],
29 message: &T,
30 aggregate_signature_type: AggregateSignatureType,
31 ) -> StdResult<ProtocolMultiSignature> {
32 let protocol_signatures: Vec<_> = single_signatures
33 .iter()
34 .map(|single_signature| single_signature.to_protocol_signature())
35 .collect();
36
37 self.protocol_clerk
38 .aggregate_signatures_with_type(
39 &protocol_signatures,
40 message.to_message().as_bytes(),
41 aggregate_signature_type,
42 )
43 .map(|multi_sig| multi_sig.into())
44 }
45
46 pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
48 self.protocol_clerk.compute_aggregate_verification_key().into()
49 }
50
51 pub fn verify_single_signature<T: ToMessage>(
53 &self,
54 message: &T,
55 single_signature: &SingleSignature,
56 ) -> StdResult<()> {
57 let protocol_signature = single_signature.to_protocol_signature();
58
59 let avk = self.compute_aggregate_verification_key();
60
61 let (vk, stake) = self
64 .protocol_clerk
65 .get_registered_party_for_index(&protocol_signature.signer_index)
66 .with_context(|| format!("Unregistered party: '{}'", single_signature.party_id))?;
67
68 protocol_signature
69 .verify(
70 &self.protocol_parameters,
71 &vk,
72 &stake,
73 &avk,
74 message.to_message().as_bytes(),
75 )
76 .with_context(|| {
77 format!(
78 "Invalid signature for party: '{}'",
79 single_signature.party_id
80 )
81 })?;
82
83 Ok(())
84 }
85}
86
87#[cfg(test)]
88mod test {
89 use mithril_stm::BlsSignatureError;
90
91 use crate::{
92 crypto_helper::ProtocolAggregationError,
93 entities::{ProtocolMessage, ProtocolMessagePartKey, ProtocolParameters},
94 protocol::SignerBuilder,
95 test::{
96 builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod},
97 double::fake_keys,
98 },
99 };
100
101 use super::*;
102
103 fn build_multi_signer(fixture: &MithrilFixture) -> MultiSigner {
104 SignerBuilder::new(
105 &fixture.signers_with_stake(),
106 &fixture.protocol_parameters(),
107 )
108 .unwrap()
109 .build_multi_signer()
110 }
111
112 #[test]
113 fn cant_aggregate_if_signatures_list_empty() {
114 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
115 let multi_signer = build_multi_signer(&fixture);
116 let message = ProtocolMessage::default();
117
118 let error = multi_signer
119 .aggregate_single_signatures(&[], &message, AggregateSignatureType::default())
120 .expect_err(
121 "Multi-signature should not be created with an empty single signatures list",
122 );
123
124 assert!(
125 matches!(
126 error.downcast_ref::<ProtocolAggregationError>(),
127 Some(ProtocolAggregationError::NotEnoughSignatures(_, _))
128 ),
129 "Expected ProtocolAggregationError::NotEnoughSignatures, got: {error:?}"
130 )
131 }
132
133 #[test]
134 fn can_aggregate_if_valid_signatures_and_quorum_reached() {
135 let fixture = MithrilFixtureBuilder::default().with_signers(10).build();
136 let multi_signer = build_multi_signer(&fixture);
137 let message = ProtocolMessage::default();
138 let signatures: Vec<SingleSignature> = fixture
139 .signers_fixture()
140 .iter()
141 .map(|s| s.sign(&message).unwrap())
142 .collect();
143
144 multi_signer
145 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
146 .expect("Multi-signature should be created");
147 }
148
149 #[test]
150 fn can_aggregate_even_with_one_invalid_signature_if_the_other_are_enough_for_the_quorum() {
151 let fixture = MithrilFixtureBuilder::default()
152 .with_signers(10)
153 .with_stake_distribution(StakeDistributionGenerationMethod::Uniform(20))
154 .with_protocol_parameters(ProtocolParameters::new(6, 200, 1.0))
155 .build();
156 let multi_signer = build_multi_signer(&fixture);
157 let message = ProtocolMessage::default();
158 let mut signatures: Vec<SingleSignature> = fixture
159 .signers_fixture()
160 .iter()
161 .map(|s| s.sign(&message).unwrap())
162 .collect();
163 signatures[4].signature = fake_keys::single_signature()[3].try_into().unwrap();
164
165 multi_signer
166 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
167 .expect("Multi-signature should be created even with one invalid signature");
168 }
169
170 #[test]
171 fn verify_single_signature_fail_if_signature_signer_isnt_in_the_registered_parties() {
172 let multi_signer = build_multi_signer(
173 &MithrilFixtureBuilder::default()
174 .with_signers(1)
175 .with_stake_distribution(StakeDistributionGenerationMethod::RandomDistribution {
176 seed: [3u8; 32],
177 min_stake: 1,
178 })
179 .build(),
180 );
181 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
182 let message = ProtocolMessage::default();
183 let single_signature = fixture.signers_fixture().last().unwrap().sign(&message).unwrap();
184
185 let error = multi_signer
188 .verify_single_signature(&message, &single_signature)
189 .expect_err(
190 "Verify single signature should fail if the signer isn't in the registered parties",
191 );
192
193 match error.downcast_ref::<BlsSignatureError>() {
194 Some(BlsSignatureError::SignatureInvalid(_)) => (),
195 _ => panic!("Expected an SignatureInvalid error, got: {error:?}"),
196 }
197 }
198
199 #[test]
200 fn verify_single_signature_fail_if_signature_signed_message_isnt_the_given_one() {
201 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
202 let multi_signer = build_multi_signer(&fixture);
203 let mut signed_message = ProtocolMessage::default();
204 signed_message.set_message_part(
205 ProtocolMessagePartKey::SnapshotDigest,
206 "a_digest".to_string(),
207 );
208 let single_signature = fixture
209 .signers_fixture()
210 .first()
211 .unwrap()
212 .sign(&signed_message)
213 .unwrap();
214
215 let error = multi_signer
216 .verify_single_signature(&ProtocolMessage::default(), &single_signature)
217 .expect_err("Verify single signature should fail");
218
219 match error.downcast_ref::<BlsSignatureError>() {
220 Some(BlsSignatureError::SignatureInvalid(_)) => (),
221 _ => panic!("Expected an SignatureInvalid error, got: {error:?}"),
222 }
223 }
224
225 #[test]
226 fn can_verify_valid_single_signature() {
227 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
228 let multi_signer = build_multi_signer(&fixture);
229 let message = ProtocolMessage::default();
230 let single_signature = fixture.signers_fixture().first().unwrap().sign(&message).unwrap();
231
232 multi_signer
233 .verify_single_signature(&message, &single_signature)
234 .expect("Verify single signature should succeed");
235 }
236}