mithril_common/protocol/
signer_builder.rs

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/// Allow to build Single Or Multi signers to generate a single signature or aggregate them
22#[derive(Debug)]
23pub struct SignerBuilder {
24    protocol_parameters: ProtocolParameters,
25    closed_key_registration: ProtocolClosedKeyRegistration,
26}
27
28/// [SignerBuilder] specific errors
29#[derive(Debug, Error)]
30pub enum SignerBuilderError {
31    /// Error raised when the list of signers given to the builder is empty
32    #[error("The list of signers must not be empty to create a signer builder.")]
33    EmptySigners,
34}
35
36impl SignerBuilder {
37    /// [SignerBuilder] constructor.
38    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    /// Build a [MultiSigner] based on the registered parties
81    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    /// Compute aggregate verification key from stake distribution
92    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    /// Build deterministic [SingleSigner] and [ProtocolInitializer] based on the registered parties.
141    ///
142    /// Use for **TEST ONLY**.
143    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    /// Restore a [SingleSigner] based on the registered parties and the given
159    /// protocol_initializer.
160    ///
161    /// This is useful since each protocol initializer holds a unique secret key
162    /// that corresponds to a registration key sent to an aggregator.
163    ///
164    /// The actual signing of message is done at a later epoch.
165    ///
166    /// The [SignerBuilder] used must be tied to the key registration, stake distribution
167    /// and protocol parameters of the epoch during which the given protocol initializer
168    /// was created.
169    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        // To make this test fail we try to build a SignerBuilder with signers from two
213        // different stake distributions, this will pass the individual check but not the
214        // register check.
215        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}