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()
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_concatenation_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 #[cfg(feature = "future_snark")]
76 None,
77 )
78 .with_context(|| {
79 format!(
80 "Invalid signature for party: '{}'",
81 single_signature.party_id
82 )
83 })?;
84
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod test {
91 use mithril_stm::BlsSignatureError;
92
93 use crate::{
94 crypto_helper::ProtocolAggregationError,
95 entities::{ProtocolMessage, ProtocolMessagePartKey, ProtocolParameters},
96 protocol::SignerBuilder,
97 test::{
98 builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod},
99 double::fake_keys,
100 },
101 };
102
103 use super::*;
104
105 fn build_multi_signer(fixture: &MithrilFixture) -> MultiSigner {
106 SignerBuilder::new(
107 &fixture.signers_with_stake(),
108 &fixture.protocol_parameters(),
109 )
110 .unwrap()
111 .build_multi_signer()
112 }
113
114 #[test]
115 fn cant_aggregate_if_signatures_list_empty() {
116 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
117 let multi_signer = build_multi_signer(&fixture);
118 let message = ProtocolMessage::default();
119
120 let error = multi_signer
121 .aggregate_single_signatures(&[], &message, AggregateSignatureType::default())
122 .expect_err(
123 "Multi-signature should not be created with an empty single signatures list",
124 );
125
126 assert!(
127 matches!(
128 error.downcast_ref::<ProtocolAggregationError>(),
129 Some(ProtocolAggregationError::NotEnoughSignatures(_, _))
130 ),
131 "Expected ProtocolAggregationError::NotEnoughSignatures, got: {error:?}"
132 )
133 }
134
135 #[test]
136 fn can_aggregate_if_valid_signatures_and_quorum_reached() {
137 let fixture = MithrilFixtureBuilder::default().with_signers(10).build();
138 let multi_signer = build_multi_signer(&fixture);
139 let message = ProtocolMessage::default();
140 let signatures: Vec<SingleSignature> = fixture
141 .signers_fixture()
142 .iter()
143 .map(|s| s.sign(&message).unwrap())
144 .collect();
145
146 multi_signer
147 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
148 .expect("Multi-signature should be created");
149 }
150
151 #[test]
152 fn can_aggregate_even_with_one_invalid_signature_if_the_other_are_enough_for_the_quorum() {
153 let fixture = MithrilFixtureBuilder::default()
154 .with_signers(10)
155 .with_stake_distribution(StakeDistributionGenerationMethod::Uniform(20))
156 .with_protocol_parameters(ProtocolParameters::new(6, 200, 1.0))
157 .build();
158 let multi_signer = build_multi_signer(&fixture);
159 let message = ProtocolMessage::default();
160 let mut signatures: Vec<SingleSignature> = fixture
161 .signers_fixture()
162 .iter()
163 .map(|s| s.sign(&message).unwrap())
164 .collect();
165 signatures[4].signature = fake_keys::single_signature()[3].try_into().unwrap();
166
167 multi_signer
168 .aggregate_single_signatures(&signatures, &message, AggregateSignatureType::default())
169 .expect("Multi-signature should be created even with one invalid signature");
170 }
171
172 #[test]
173 fn verify_single_signature_fail_if_signature_signer_isnt_in_the_registered_parties() {
174 let multi_signer = build_multi_signer(
175 &MithrilFixtureBuilder::default()
176 .with_signers(1)
177 .with_stake_distribution(StakeDistributionGenerationMethod::RandomDistribution {
178 seed: [3u8; 32],
179 min_stake: 1,
180 })
181 .build(),
182 );
183 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
184 let message = ProtocolMessage::default();
185 let single_signature = fixture.signers_fixture().last().unwrap().sign(&message).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::<BlsSignatureError>() {
196 Some(BlsSignatureError::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::<BlsSignatureError>() {
222 Some(BlsSignatureError::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.signers_fixture().first().unwrap().sign(&message).unwrap();
233
234 multi_signer
235 .verify_single_signature(&message, &single_signature)
236 .expect("Verify single signature should succeed");
237 }
238}