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