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 = ProtocolClerk::new_clerk_from_closed_key_registration(
77 &stm_parameters,
78 &self.closed_key_registration,
79 );
80
81 MultiSigner::new(clerk, stm_parameters)
82 }
83
84 pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
86 let stm_parameters = self.protocol_parameters.clone().into();
87 let clerk = ProtocolClerk::new_clerk_from_closed_key_registration(
88 &stm_parameters,
89 &self.closed_key_registration,
90 );
91
92 clerk.compute_aggregate_verification_key().into()
93 }
94
95 fn build_single_signer_with_rng<R: RngCore + CryptoRng>(
96 &self,
97 signer_with_stake: SignerWithStake,
98 kes_signer: Option<Arc<dyn KesSigner>>,
99 rng: &mut R,
100 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
101 let protocol_initializer = ProtocolInitializer::setup(
102 self.protocol_parameters.clone().into(),
103 kes_signer,
104 signer_with_stake.kes_period,
105 signer_with_stake.stake,
106 rng,
107 )
108 .with_context(|| {
109 format!(
110 "Could not create a protocol initializer for party: '{}'",
111 signer_with_stake.party_id
112 )
113 })?;
114
115 let protocol_signer = protocol_initializer
116 .clone()
117 .new_signer(self.closed_key_registration.clone())
118 .with_context(|| {
119 format!(
120 "Could not create a protocol signer for party: '{}'",
121 signer_with_stake.party_id
122 )
123 })?;
124
125 Ok((
126 SingleSigner::new(signer_with_stake.party_id, protocol_signer),
127 protocol_initializer,
128 ))
129 }
130
131 pub fn build_test_single_signer(
135 &self,
136 signer_with_stake: SignerWithStake,
137 kes_signer: Option<Arc<dyn KesSigner>>,
138 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
139 let protocol_initializer_seed: [u8; 32] =
140 signer_with_stake.party_id.as_bytes()[..32].try_into().unwrap();
141
142 self.build_single_signer_with_rng(
143 signer_with_stake,
144 kes_signer,
145 &mut ChaCha20Rng::from_seed(protocol_initializer_seed),
146 )
147 }
148
149 pub fn restore_signer_from_initializer(
161 &self,
162 party_id: PartyId,
163 protocol_initializer: ProtocolInitializer,
164 ) -> StdResult<SingleSigner> {
165 let single_signer = protocol_initializer
166 .new_signer(self.closed_key_registration.clone())
167 .with_context(|| {
168 "Could not create a single signer from protocol initializer".to_string()
169 })?;
170
171 Ok(SingleSigner::new(party_id, single_signer))
172 }
173}
174
175#[cfg(test)]
176mod test {
177 use mithril_stm::RegisterError;
178
179 use crate::{
180 crypto_helper::{KesSignerStandard, ProtocolRegistrationErrorWrapper},
181 test_utils::{MithrilFixtureBuilder, fake_data},
182 };
183
184 use super::*;
185
186 #[test]
187 fn cant_construct_signer_builder_with_an_empty_signers_list() {
188 let signers = vec![];
189 let protocol_parameters = fake_data::protocol_parameters();
190
191 let error = SignerBuilder::new(&signers, &protocol_parameters).expect_err(
192 "We should not be able to construct a signer builder with an empty signers list",
193 );
194
195 match error.downcast_ref::<SignerBuilderError>() {
196 Some(SignerBuilderError::EmptySigners) => (),
197 _ => panic!("Expected an EmptySigners error, got: {error:?}"),
198 }
199 }
200
201 #[test]
202 fn cant_construct_signer_builder_if_a_signer_registration_fail() {
203 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
207 let fixture_with_another_stake_distribution = MithrilFixtureBuilder::default()
208 .with_signers(1)
209 .with_stake_distribution(
210 crate::test_utils::StakeDistributionGenerationMethod::RandomDistribution {
211 seed: [4u8; 32],
212 min_stake: 1,
213 },
214 )
215 .build();
216 let mut signers = fixture.signers_with_stake();
217 signers.append(&mut fixture_with_another_stake_distribution.signers_with_stake());
218
219 let error = SignerBuilder::new(&signers, &fixture.protocol_parameters()).expect_err(
220 "We should not be able to construct a signer builder if a signer registration fail",
221 );
222
223 match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
224 Some(ProtocolRegistrationErrorWrapper::CoreRegister(_)) => (),
225 _ => panic!("Expected an CoreRegister error, got: {error:?}"),
226 }
227 }
228
229 #[test]
230 fn can_construct_signer_builder_with_valid_signers() {
231 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
232
233 SignerBuilder::new(
234 &fixture.signers_with_stake(),
235 &fixture.protocol_parameters(),
236 )
237 .expect("We should be able to construct a signer builder with valid signers");
238 }
239
240 #[test]
241 fn cant_build_single_signer_for_unregistered_party() {
242 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
243 let signers_from_another_fixture = MithrilFixtureBuilder::default()
244 .with_signers(1)
245 .with_party_id_seed([4u8; 32])
246 .build()
247 .signers_fixture();
248 let non_registered_signer = signers_from_another_fixture.first().unwrap();
249 let kes_signer = Some(Arc::new(KesSignerStandard::new(
250 non_registered_signer.kes_secret_key_path().unwrap().to_path_buf(),
251 non_registered_signer
252 .operational_certificate_path()
253 .unwrap()
254 .to_path_buf(),
255 )) as Arc<dyn KesSigner>);
256
257 let error = SignerBuilder::new(
258 &fixture.signers_with_stake(),
259 &fixture.protocol_parameters(),
260 )
261 .unwrap()
262 .build_test_single_signer(non_registered_signer.signer_with_stake.clone(), kes_signer)
263 .expect_err(
264 "We should not be able to construct a single signer from a not registered party",
265 );
266
267 match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
268 Some(ProtocolRegistrationErrorWrapper::CoreRegister(
269 RegisterError::UnregisteredInitializer,
270 )) => (),
271 _ => panic!(
272 "Expected an ProtocolRegistrationErrorWrapper::CoreRegister error, got: {error:?}"
273 ),
274 }
275 }
276
277 #[test]
278 fn should_build_single_signer_for_registered_party() {
279 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
280 let signers = fixture.signers_fixture();
281 let signer = signers.first().unwrap();
282 let kes_signer = Some(Arc::new(KesSignerStandard::new(
283 signer.kes_secret_key_path().unwrap().to_path_buf(),
284 signer.operational_certificate_path().unwrap().to_path_buf(),
285 )) as Arc<dyn KesSigner>);
286
287 let builder = SignerBuilder::new(
288 &fixture.signers_with_stake(),
289 &fixture.protocol_parameters(),
290 )
291 .unwrap();
292
293 builder
294 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
295 .expect("Should be able to build test single signer for a registered party");
296 }
297
298 #[test]
299 fn should_restore_single_signer_from_previous_initializer() {
300 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
301 let signers = fixture.signers_fixture();
302 let signer = signers.first().unwrap();
303 let kes_signer = Some(Arc::new(KesSignerStandard::new(
304 signer.kes_secret_key_path().unwrap().to_path_buf(),
305 signer.operational_certificate_path().unwrap().to_path_buf(),
306 )) as Arc<dyn KesSigner>);
307
308 let first_builder = SignerBuilder::new(
309 &fixture.signers_with_stake(),
310 &fixture.protocol_parameters(),
311 )
312 .unwrap();
313
314 let (_, initializer) = first_builder
315 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
316 .unwrap();
317
318 let second_builder = SignerBuilder::new(
319 &fixture.signers_with_stake(),
320 &fixture.protocol_parameters(),
321 )
322 .unwrap();
323
324 second_builder
325 .restore_signer_from_initializer(signer.party_id(), initializer)
326 .expect("Should be able to restore a single signer from a protocol initialized");
327 }
328}