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