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