1use std::sync::Arc;
2
3use anyhow::Context;
4use rand_chacha::ChaCha20Rng;
5use rand_core::{CryptoRng, RngCore, SeedableRng};
6use thiserror::Error;
7
8use crate::{
9 StdResult,
10 crypto_helper::{
11 KesSigner, ProtocolAggregateVerificationKey, ProtocolClerk, ProtocolClosedKeyRegistration,
12 ProtocolInitializer, ProtocolKeyRegistration, ProtocolStakeDistribution,
13 },
14 entities::{PartyId, ProtocolParameters, SignerWithStake},
15 protocol::MultiSigner,
16};
17
18use super::SingleSigner;
19
20#[derive(Debug)]
22pub struct SignerBuilder {
23 protocol_parameters: ProtocolParameters,
24 closed_key_registration: ProtocolClosedKeyRegistration,
25}
26
27#[derive(Debug, Error)]
29pub enum SignerBuilderError {
30 #[error("The list of signers must not be empty to create a signer builder.")]
32 EmptySigners,
33}
34
35impl SignerBuilder {
36 pub fn new(
38 registered_signers: &[SignerWithStake],
39 protocol_parameters: &ProtocolParameters,
40 ) -> StdResult<Self> {
41 if registered_signers.is_empty() {
42 return Err(SignerBuilderError::EmptySigners.into());
43 }
44
45 let stake_distribution = registered_signers
46 .iter()
47 .map(|s| s.into())
48 .collect::<ProtocolStakeDistribution>();
49 let mut key_registration = ProtocolKeyRegistration::init(&stake_distribution);
50
51 for signer in registered_signers {
52 key_registration
53 .register(
54 Some(signer.party_id.to_owned()),
55 signer.operational_certificate.clone(),
56 signer.verification_key_signature,
57 signer.kes_period,
58 signer.verification_key,
59 )
60 .with_context(|| {
61 format!("Registration failed for signer: '{}'", signer.party_id)
62 })?;
63 }
64
65 let closed_registration = key_registration.close();
66
67 Ok(Self {
68 protocol_parameters: protocol_parameters.clone(),
69 closed_key_registration: closed_registration,
70 })
71 }
72
73 pub fn build_multi_signer(&self) -> MultiSigner {
75 let stm_parameters = self.protocol_parameters.clone().into();
76 let clerk =
77 ProtocolClerk::from_registration(&stm_parameters, &self.closed_key_registration);
78
79 MultiSigner::new(clerk, stm_parameters)
80 }
81
82 pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
84 let stm_parameters = self.protocol_parameters.clone().into();
85 let clerk =
86 ProtocolClerk::from_registration(&stm_parameters, &self.closed_key_registration);
87
88 clerk.compute_avk().into()
89 }
90
91 fn build_single_signer_with_rng<R: RngCore + CryptoRng>(
92 &self,
93 signer_with_stake: SignerWithStake,
94 kes_signer: Option<Arc<dyn KesSigner>>,
95 rng: &mut R,
96 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
97 let protocol_initializer = ProtocolInitializer::setup(
98 self.protocol_parameters.clone().into(),
99 kes_signer,
100 signer_with_stake.kes_period,
101 signer_with_stake.stake,
102 rng,
103 )
104 .with_context(|| {
105 format!(
106 "Could not create a protocol initializer for party: '{}'",
107 signer_with_stake.party_id
108 )
109 })?;
110
111 let protocol_signer = protocol_initializer
112 .clone()
113 .new_signer(self.closed_key_registration.clone())
114 .with_context(|| {
115 format!(
116 "Could not create a protocol signer for party: '{}'",
117 signer_with_stake.party_id
118 )
119 })?;
120
121 Ok((
122 SingleSigner::new(signer_with_stake.party_id, protocol_signer),
123 protocol_initializer,
124 ))
125 }
126
127 pub fn build_test_single_signer(
131 &self,
132 signer_with_stake: SignerWithStake,
133 kes_signer: Option<Arc<dyn KesSigner>>,
134 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
135 let protocol_initializer_seed: [u8; 32] =
136 signer_with_stake.party_id.as_bytes()[..32].try_into().unwrap();
137
138 self.build_single_signer_with_rng(
139 signer_with_stake,
140 kes_signer,
141 &mut ChaCha20Rng::from_seed(protocol_initializer_seed),
142 )
143 }
144
145 pub fn restore_signer_from_initializer(
157 &self,
158 party_id: PartyId,
159 protocol_initializer: ProtocolInitializer,
160 ) -> StdResult<SingleSigner> {
161 let single_signer = protocol_initializer
162 .new_signer(self.closed_key_registration.clone())
163 .with_context(|| {
164 "Could not create a single signer from protocol initializer".to_string()
165 })?;
166
167 Ok(SingleSigner::new(party_id, single_signer))
168 }
169}
170
171#[cfg(test)]
172mod test {
173 use mithril_stm::RegisterError;
174
175 use crate::{
176 crypto_helper::{KesSignerStandard, ProtocolRegistrationErrorWrapper},
177 test_utils::{MithrilFixtureBuilder, fake_data},
178 };
179
180 use super::*;
181
182 #[test]
183 fn cant_construct_signer_builder_with_an_empty_signers_list() {
184 let signers = vec![];
185 let protocol_parameters = fake_data::protocol_parameters();
186
187 let error = SignerBuilder::new(&signers, &protocol_parameters).expect_err(
188 "We should not be able to construct a signer builder with an empty signers list",
189 );
190
191 match error.downcast_ref::<SignerBuilderError>() {
192 Some(SignerBuilderError::EmptySigners) => (),
193 _ => panic!("Expected an EmptySigners error, got: {error:?}"),
194 }
195 }
196
197 #[test]
198 fn cant_construct_signer_builder_if_a_signer_registration_fail() {
199 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
203 let fixture_with_another_stake_distribution = MithrilFixtureBuilder::default()
204 .with_signers(1)
205 .with_stake_distribution(
206 crate::test_utils::StakeDistributionGenerationMethod::RandomDistribution {
207 seed: [4u8; 32],
208 min_stake: 1,
209 },
210 )
211 .build();
212 let mut signers = fixture.signers_with_stake();
213 signers.append(&mut fixture_with_another_stake_distribution.signers_with_stake());
214
215 let error = SignerBuilder::new(&signers, &fixture.protocol_parameters()).expect_err(
216 "We should not be able to construct a signer builder if a signer registration fail",
217 );
218
219 match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
220 Some(ProtocolRegistrationErrorWrapper::CoreRegister(_)) => (),
221 _ => panic!("Expected an CoreRegister error, got: {error:?}"),
222 }
223 }
224
225 #[test]
226 fn can_construct_signer_builder_with_valid_signers() {
227 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
228
229 SignerBuilder::new(
230 &fixture.signers_with_stake(),
231 &fixture.protocol_parameters(),
232 )
233 .expect("We should be able to construct a signer builder with valid signers");
234 }
235
236 #[test]
237 fn cant_build_single_signer_for_unregistered_party() {
238 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
239 let signers_from_another_fixture = MithrilFixtureBuilder::default()
240 .with_signers(1)
241 .with_party_id_seed([4u8; 32])
242 .build()
243 .signers_fixture();
244 let non_registered_signer = signers_from_another_fixture.first().unwrap();
245 let kes_signer = Some(Arc::new(KesSignerStandard::new(
246 non_registered_signer.kes_secret_key_path().unwrap().to_path_buf(),
247 non_registered_signer
248 .operational_certificate_path()
249 .unwrap()
250 .to_path_buf(),
251 )) as Arc<dyn KesSigner>);
252
253 let error = SignerBuilder::new(
254 &fixture.signers_with_stake(),
255 &fixture.protocol_parameters(),
256 )
257 .unwrap()
258 .build_test_single_signer(non_registered_signer.signer_with_stake.clone(), kes_signer)
259 .expect_err(
260 "We should not be able to construct a single signer from a not registered party",
261 );
262
263 match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
264 Some(ProtocolRegistrationErrorWrapper::CoreRegister(
265 RegisterError::UnregisteredInitializer,
266 )) => (),
267 _ => panic!(
268 "Expected an ProtocolRegistrationErrorWrapper::CoreRegister error, got: {error:?}"
269 ),
270 }
271 }
272
273 #[test]
274 fn should_build_single_signer_for_registered_party() {
275 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
276 let signers = fixture.signers_fixture();
277 let signer = signers.first().unwrap();
278 let kes_signer = Some(Arc::new(KesSignerStandard::new(
279 signer.kes_secret_key_path().unwrap().to_path_buf(),
280 signer.operational_certificate_path().unwrap().to_path_buf(),
281 )) as Arc<dyn KesSigner>);
282
283 let builder = SignerBuilder::new(
284 &fixture.signers_with_stake(),
285 &fixture.protocol_parameters(),
286 )
287 .unwrap();
288
289 builder
290 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
291 .expect("Should be able to build test single signer for a registered party");
292 }
293
294 #[test]
295 fn should_restore_single_signer_from_previous_initializer() {
296 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
297 let signers = fixture.signers_fixture();
298 let signer = signers.first().unwrap();
299 let kes_signer = Some(Arc::new(KesSignerStandard::new(
300 signer.kes_secret_key_path().unwrap().to_path_buf(),
301 signer.operational_certificate_path().unwrap().to_path_buf(),
302 )) as Arc<dyn KesSigner>);
303
304 let first_builder = SignerBuilder::new(
305 &fixture.signers_with_stake(),
306 &fixture.protocol_parameters(),
307 )
308 .unwrap();
309
310 let (_, initializer) = first_builder
311 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
312 .unwrap();
313
314 let second_builder = SignerBuilder::new(
315 &fixture.signers_with_stake(),
316 &fixture.protocol_parameters(),
317 )
318 .unwrap();
319
320 second_builder
321 .restore_signer_from_initializer(signer.party_id(), initializer)
322 .expect("Should be able to restore a single signer from a protocol initialized");
323 }
324}