mithril_common/protocol/
multi_signer.rs1use anyhow::{Context, anyhow};
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 .ok_or_else(|| {
67 anyhow!(format!(
68 "Unregistered party: '{}'",
69 single_signature.party_id
70 ))
71 })?;
72
73 protocol_signature
74 .verify(
75 &self.protocol_parameters,
76 &vk,
77 &stake,
78 &avk,
79 message.to_message().as_bytes(),
80 )
81 .with_context(|| {
82 format!(
83 "Invalid signature for party: '{}'",
84 single_signature.party_id
85 )
86 })?;
87
88 Ok(())
89 }
90}
91
92#[cfg(test)]
93mod test {
94 use mithril_stm::MultiSignatureError;
95
96 use crate::{
97 crypto_helper::ProtocolAggregationError,
98 entities::{ProtocolMessage, ProtocolMessagePartKey, ProtocolParameters},
99 protocol::SignerBuilder,
100 test::{
101 builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod},
102 double::fake_keys,
103 },
104 };
105
106 use super::*;
107
108 fn build_multi_signer(fixture: &MithrilFixture) -> MultiSigner {
109 SignerBuilder::new(
110 &fixture.signers_with_stake(),
111 &fixture.protocol_parameters(),
112 )
113 .unwrap()
114 .build_multi_signer()
115 }
116
117 #[test]
118 fn cant_aggregate_if_signatures_list_empty() {
119 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
120 let multi_signer = build_multi_signer(&fixture);
121 let message = ProtocolMessage::default();
122
123 let error = multi_signer
124 .aggregate_single_signatures(&[], &message, AggregateSignatureType::default())
125 .expect_err(
126 "Multi-signature should not be created with an empty single signatures list",
127 );
128
129 assert!(
130 matches!(
131 error.downcast_ref::<ProtocolAggregationError>(),
132 Some(ProtocolAggregationError::NotEnoughSignatures(_, _))
133 ),
134 "Expected ProtocolAggregationError::NotEnoughSignatures, got: {error:?}"
135 )
136 }
137
138 #[test]
139 fn can_aggregate_if_valid_signatures_and_quorum_reached() {
140 let fixture = MithrilFixtureBuilder::default().with_signers(10).build();
141 let multi_signer = build_multi_signer(&fixture);
142 let message = ProtocolMessage::default();
143 let signatures: Vec<SingleSignature> = fixture
144 .signers_fixture()
145 .iter()
146 .map(|s| s.sign(&message).unwrap())
147 .collect();
148
149 multi_signer
150 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
151 .expect("Multi-signature should be created");
152 }
153
154 #[test]
155 fn can_aggregate_even_with_one_invalid_signature_if_the_other_are_enough_for_the_quorum() {
156 let fixture = MithrilFixtureBuilder::default()
157 .with_signers(10)
158 .with_stake_distribution(StakeDistributionGenerationMethod::Uniform(20))
159 .with_protocol_parameters(ProtocolParameters::new(6, 200, 1.0))
160 .build();
161 let multi_signer = build_multi_signer(&fixture);
162 let message = ProtocolMessage::default();
163 let mut signatures: Vec<SingleSignature> = fixture
164 .signers_fixture()
165 .iter()
166 .map(|s| s.sign(&message).unwrap())
167 .collect();
168 signatures[4].signature = fake_keys::single_signature()[3].try_into().unwrap();
169
170 multi_signer
171 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
172 .expect("Multi-signature should be created even with one invalid signature");
173 }
174
175 #[test]
176 fn verify_single_signature_fail_if_signature_signer_isnt_in_the_registered_parties() {
177 let multi_signer = build_multi_signer(
178 &MithrilFixtureBuilder::default()
179 .with_signers(1)
180 .with_stake_distribution(StakeDistributionGenerationMethod::RandomDistribution {
181 seed: [3u8; 32],
182 min_stake: 1,
183 })
184 .build(),
185 );
186 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
187 let message = ProtocolMessage::default();
188 let single_signature = fixture.signers_fixture().last().unwrap().sign(&message).unwrap();
189
190 let error = multi_signer
193 .verify_single_signature(&message, &single_signature)
194 .expect_err(
195 "Verify single signature should fail if the signer isn't in the registered parties",
196 );
197
198 match error.downcast_ref::<MultiSignatureError>() {
199 Some(MultiSignatureError::SignatureInvalid(_)) => (),
200 _ => panic!("Expected an SignatureInvalid error, got: {error:?}"),
201 }
202 }
203
204 #[test]
205 fn verify_single_signature_fail_if_signature_signed_message_isnt_the_given_one() {
206 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
207 let multi_signer = build_multi_signer(&fixture);
208 let mut signed_message = ProtocolMessage::default();
209 signed_message.set_message_part(
210 ProtocolMessagePartKey::SnapshotDigest,
211 "a_digest".to_string(),
212 );
213 let single_signature = fixture
214 .signers_fixture()
215 .first()
216 .unwrap()
217 .sign(&signed_message)
218 .unwrap();
219
220 let error = multi_signer
221 .verify_single_signature(&ProtocolMessage::default(), &single_signature)
222 .expect_err("Verify single signature should fail");
223
224 match error.downcast_ref::<MultiSignatureError>() {
225 Some(MultiSignatureError::SignatureInvalid(_)) => (),
226 _ => panic!("Expected an SignatureInvalid error, got: {error:?}"),
227 }
228 }
229
230 #[test]
231 fn can_verify_valid_single_signature() {
232 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
233 let multi_signer = build_multi_signer(&fixture);
234 let message = ProtocolMessage::default();
235 let single_signature = fixture.signers_fixture().first().unwrap().sign(&message).unwrap();
236
237 multi_signer
238 .verify_single_signature(&message, &single_signature)
239 .expect("Verify single signature should succeed");
240 }
241}