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, SignerRegistrationParameters,
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(SignerRegistrationParameters {
55 party_id: Some(signer.party_id.to_owned()),
56 operational_certificate: signer.operational_certificate.clone(),
57 verification_key_signature_for_concatenation: signer
58 .verification_key_signature_for_concatenation,
59 kes_evolutions: signer.kes_evolutions,
60 verification_key_for_concatenation: signer.verification_key_for_concatenation,
61 #[cfg(feature = "future_snark")]
62 verification_key_for_snark: signer.verification_key_for_snark,
63 #[cfg(feature = "future_snark")]
64 verification_key_signature_for_snark: signer
65 .verification_key_signature_for_snark,
66 })
67 .with_context(|| {
68 format!("Registration failed for signer: '{}'", signer.party_id)
69 })?;
70 }
71
72 let closed_registration = key_registration.close();
73
74 Ok(Self {
75 protocol_parameters: protocol_parameters.clone(),
76 closed_key_registration: closed_registration,
77 })
78 }
79
80 pub fn build_multi_signer(&self) -> MultiSigner {
82 let stm_parameters = self.protocol_parameters.clone().into();
83 let clerk = ProtocolClerk::new_clerk_from_closed_key_registration(
84 &stm_parameters,
85 &self.closed_key_registration,
86 );
87
88 MultiSigner::new(clerk, stm_parameters)
89 }
90
91 pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
93 let stm_parameters = self.protocol_parameters.clone().into();
94 let clerk = ProtocolClerk::new_clerk_from_closed_key_registration(
95 &stm_parameters,
96 &self.closed_key_registration,
97 );
98
99 clerk.compute_aggregate_verification_key()
100 }
101
102 fn build_single_signer_with_rng<R: RngCore + CryptoRng>(
103 &self,
104 signer_with_stake: SignerWithStake,
105 kes_signer: Option<Arc<dyn KesSigner>>,
106 rng: &mut R,
107 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
108 let protocol_initializer = ProtocolInitializer::setup(
109 self.protocol_parameters.clone().into(),
110 kes_signer,
111 signer_with_stake
112 .kes_evolutions
113 .map(|kes_evolutions| KesPeriod(0) + kes_evolutions),
114 signer_with_stake.stake,
115 rng,
116 )
117 .with_context(|| {
118 format!(
119 "Could not create a protocol initializer for party: '{}'",
120 signer_with_stake.party_id
121 )
122 })?;
123
124 let protocol_signer = protocol_initializer
125 .clone()
126 .new_signer(self.closed_key_registration.clone())
127 .with_context(|| {
128 format!(
129 "Could not create a protocol signer for party: '{}'",
130 signer_with_stake.party_id
131 )
132 })?;
133
134 Ok((
135 SingleSigner::new(signer_with_stake.party_id, protocol_signer),
136 protocol_initializer,
137 ))
138 }
139
140 pub fn build_test_single_signer(
144 &self,
145 signer_with_stake: SignerWithStake,
146 kes_signer: Option<Arc<dyn KesSigner>>,
147 ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
148 let protocol_initializer_seed: [u8; 32] =
149 signer_with_stake.party_id.as_bytes()[..32].try_into().unwrap();
150
151 self.build_single_signer_with_rng(
152 signer_with_stake,
153 kes_signer,
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::KesSignerStandard,
190 test::{builder::MithrilFixtureBuilder, double::fake_data},
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::builder::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::<RegisterError>() {
233 Some(RegisterError::EntryAlreadyRegistered { .. }) => (),
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 let kes_signer = Some(Arc::new(KesSignerStandard::new(
259 non_registered_signer.kes_secret_key_path().unwrap().to_path_buf(),
260 non_registered_signer
261 .operational_certificate_path()
262 .unwrap()
263 .to_path_buf(),
264 )) as Arc<dyn KesSigner>);
265
266 let error = SignerBuilder::new(
267 &fixture.signers_with_stake(),
268 &fixture.protocol_parameters(),
269 )
270 .unwrap()
271 .build_test_single_signer(non_registered_signer.signer_with_stake.clone(), kes_signer)
272 .expect_err(
273 "We should not be able to construct a single signer from a not registered party",
274 );
275
276 match error.downcast_ref::<RegisterError>() {
277 Some(RegisterError::UnregisteredInitializer) => (),
278 _ => panic!(
279 "Expected an ProtocolRegistrationErrorWrapper::CoreRegister error, got: {error:?}"
280 ),
281 }
282 }
283
284 #[test]
285 fn should_build_single_signer_for_registered_party() {
286 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
287 let signers = fixture.signers_fixture();
288 let signer = signers.first().unwrap();
289 let kes_signer = Some(Arc::new(KesSignerStandard::new(
290 signer.kes_secret_key_path().unwrap().to_path_buf(),
291 signer.operational_certificate_path().unwrap().to_path_buf(),
292 )) as Arc<dyn KesSigner>);
293
294 let builder = SignerBuilder::new(
295 &fixture.signers_with_stake(),
296 &fixture.protocol_parameters(),
297 )
298 .unwrap();
299
300 builder
301 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
302 .expect("Should be able to build test single signer for a registered party");
303 }
304
305 #[test]
306 fn should_restore_single_signer_from_previous_initializer() {
307 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
308 let signers = fixture.signers_fixture();
309 let signer = signers.first().unwrap();
310 let kes_signer = Some(Arc::new(KesSignerStandard::new(
311 signer.kes_secret_key_path().unwrap().to_path_buf(),
312 signer.operational_certificate_path().unwrap().to_path_buf(),
313 )) as Arc<dyn KesSigner>);
314
315 let first_builder = SignerBuilder::new(
316 &fixture.signers_with_stake(),
317 &fixture.protocol_parameters(),
318 )
319 .unwrap();
320
321 let (_, initializer) = first_builder
322 .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
323 .unwrap();
324
325 let second_builder = SignerBuilder::new(
326 &fixture.signers_with_stake(),
327 &fixture.protocol_parameters(),
328 )
329 .unwrap();
330
331 second_builder
332 .restore_signer_from_initializer(signer.party_id(), initializer)
333 .expect("Should be able to restore a single signer from a protocol initialized");
334 }
335}