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        KesSigner, ProtocolAggregateVerificationKey, ProtocolClerk, ProtocolClosedKeyRegistration,
12        ProtocolInitializer, ProtocolKeyRegistration, ProtocolStakeDistribution,
13    },
14    entities::{PartyId, ProtocolParameters, SignerWithStake},
15    protocol::MultiSigner,
16};
17
18use super::SingleSigner;
19
20/// Allow to build Single Or Multi signers to generate a single signature or aggregate them
21#[derive(Debug)]
22pub struct SignerBuilder {
23    protocol_parameters: ProtocolParameters,
24    closed_key_registration: ProtocolClosedKeyRegistration,
25}
26
27/// [SignerBuilder] specific errors
28#[derive(Debug, Error)]
29pub enum SignerBuilderError {
30    /// Error raised when the list of signers given to the builder is empty
31    #[error("The list of signers must not be empty to create a signer builder.")]
32    EmptySigners,
33}
34
35impl SignerBuilder {
36    /// [SignerBuilder] constructor.
37    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    /// Build a [MultiSigner] based on the registered parties
74    pub fn build_multi_signer(&self) -> MultiSigner {
75        let stm_parameters = self.protocol_parameters.clone().into();
76        let clerk =
77            ProtocolClerk::from_registration(&stm_parameters, &self.closed_key_registration);
78
79        MultiSigner::new(clerk, stm_parameters)
80    }
81
82    /// Compute aggregate verification key from stake distribution
83    pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
84        let stm_parameters = self.protocol_parameters.clone().into();
85        let clerk =
86            ProtocolClerk::from_registration(&stm_parameters, &self.closed_key_registration);
87
88        clerk.compute_avk().into()
89    }
90
91    fn build_single_signer_with_rng<R: RngCore + CryptoRng>(
92        &self,
93        signer_with_stake: SignerWithStake,
94        kes_signer: Option<Arc<dyn KesSigner>>,
95        rng: &mut R,
96    ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
97        let protocol_initializer = ProtocolInitializer::setup(
98            self.protocol_parameters.clone().into(),
99            kes_signer,
100            signer_with_stake.kes_period,
101            signer_with_stake.stake,
102            rng,
103        )
104        .with_context(|| {
105            format!(
106                "Could not create a protocol initializer for party: '{}'",
107                signer_with_stake.party_id
108            )
109        })?;
110
111        let protocol_signer = protocol_initializer
112            .clone()
113            .new_signer(self.closed_key_registration.clone())
114            .with_context(|| {
115                format!(
116                    "Could not create a protocol signer for party: '{}'",
117                    signer_with_stake.party_id
118                )
119            })?;
120
121        Ok((
122            SingleSigner::new(signer_with_stake.party_id, protocol_signer),
123            protocol_initializer,
124        ))
125    }
126
127    /// Build deterministic [SingleSigner] and [ProtocolInitializer] based on the registered parties.
128    ///
129    /// Use for **TEST ONLY**.
130    pub fn build_test_single_signer(
131        &self,
132        signer_with_stake: SignerWithStake,
133        kes_signer: Option<Arc<dyn KesSigner>>,
134    ) -> StdResult<(SingleSigner, ProtocolInitializer)> {
135        let protocol_initializer_seed: [u8; 32] =
136            signer_with_stake.party_id.as_bytes()[..32].try_into().unwrap();
137
138        self.build_single_signer_with_rng(
139            signer_with_stake,
140            kes_signer,
141            &mut ChaCha20Rng::from_seed(protocol_initializer_seed),
142        )
143    }
144
145    /// Restore a [SingleSigner] based on the registered parties and the given
146    /// protocol_initializer.
147    ///
148    /// This is useful since each protocol initializer holds a unique secret key
149    /// that corresponds to a registration key sent to an aggregator.
150    ///
151    /// The actual signing of message is done at a later epoch.
152    ///
153    /// The [SignerBuilder] used must be tied to the key registration, stake distribution
154    /// and protocol parameters of the epoch during which the given protocol initializer
155    /// was created.
156    pub fn restore_signer_from_initializer(
157        &self,
158        party_id: PartyId,
159        protocol_initializer: ProtocolInitializer,
160    ) -> StdResult<SingleSigner> {
161        let single_signer = protocol_initializer
162            .new_signer(self.closed_key_registration.clone())
163            .with_context(|| {
164                "Could not create a single signer from protocol initializer".to_string()
165            })?;
166
167        Ok(SingleSigner::new(party_id, single_signer))
168    }
169}
170
171#[cfg(test)]
172mod test {
173    use mithril_stm::RegisterError;
174
175    use crate::{
176        crypto_helper::{KesSignerStandard, ProtocolRegistrationErrorWrapper},
177        test_utils::{MithrilFixtureBuilder, fake_data},
178    };
179
180    use super::*;
181
182    #[test]
183    fn cant_construct_signer_builder_with_an_empty_signers_list() {
184        let signers = vec![];
185        let protocol_parameters = fake_data::protocol_parameters();
186
187        let error = SignerBuilder::new(&signers, &protocol_parameters).expect_err(
188            "We should not be able to construct a signer builder with an empty signers list",
189        );
190
191        match error.downcast_ref::<SignerBuilderError>() {
192            Some(SignerBuilderError::EmptySigners) => (),
193            _ => panic!("Expected an EmptySigners error, got: {error:?}"),
194        }
195    }
196
197    #[test]
198    fn cant_construct_signer_builder_if_a_signer_registration_fail() {
199        // To make this test fail we try to build a SignerBuilder with signers from two
200        // different stake distributions, this will pass the individual check but not the
201        // register check.
202        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
203        let fixture_with_another_stake_distribution = MithrilFixtureBuilder::default()
204            .with_signers(1)
205            .with_stake_distribution(
206                crate::test_utils::StakeDistributionGenerationMethod::RandomDistribution {
207                    seed: [4u8; 32],
208                    min_stake: 1,
209                },
210            )
211            .build();
212        let mut signers = fixture.signers_with_stake();
213        signers.append(&mut fixture_with_another_stake_distribution.signers_with_stake());
214
215        let error = SignerBuilder::new(&signers, &fixture.protocol_parameters()).expect_err(
216            "We should not be able to construct a signer builder if a signer registration fail",
217        );
218
219        match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
220            Some(ProtocolRegistrationErrorWrapper::CoreRegister(_)) => (),
221            _ => panic!("Expected an CoreRegister error, got: {error:?}"),
222        }
223    }
224
225    #[test]
226    fn can_construct_signer_builder_with_valid_signers() {
227        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
228
229        SignerBuilder::new(
230            &fixture.signers_with_stake(),
231            &fixture.protocol_parameters(),
232        )
233        .expect("We should be able to construct a signer builder with valid signers");
234    }
235
236    #[test]
237    fn cant_build_single_signer_for_unregistered_party() {
238        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
239        let signers_from_another_fixture = MithrilFixtureBuilder::default()
240            .with_signers(1)
241            .with_party_id_seed([4u8; 32])
242            .build()
243            .signers_fixture();
244        let non_registered_signer = signers_from_another_fixture.first().unwrap();
245        let kes_signer = Some(Arc::new(KesSignerStandard::new(
246            non_registered_signer.kes_secret_key_path().unwrap().to_path_buf(),
247            non_registered_signer
248                .operational_certificate_path()
249                .unwrap()
250                .to_path_buf(),
251        )) as Arc<dyn KesSigner>);
252
253        let error = SignerBuilder::new(
254            &fixture.signers_with_stake(),
255            &fixture.protocol_parameters(),
256        )
257        .unwrap()
258        .build_test_single_signer(non_registered_signer.signer_with_stake.clone(), kes_signer)
259        .expect_err(
260            "We should not be able to construct a single signer from a not registered party",
261        );
262
263        match error.downcast_ref::<ProtocolRegistrationErrorWrapper>() {
264            Some(ProtocolRegistrationErrorWrapper::CoreRegister(
265                RegisterError::UnregisteredInitializer,
266            )) => (),
267            _ => panic!(
268                "Expected an ProtocolRegistrationErrorWrapper::CoreRegister error, got: {error:?}"
269            ),
270        }
271    }
272
273    #[test]
274    fn should_build_single_signer_for_registered_party() {
275        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
276        let signers = fixture.signers_fixture();
277        let signer = signers.first().unwrap();
278        let kes_signer = Some(Arc::new(KesSignerStandard::new(
279            signer.kes_secret_key_path().unwrap().to_path_buf(),
280            signer.operational_certificate_path().unwrap().to_path_buf(),
281        )) as Arc<dyn KesSigner>);
282
283        let builder = SignerBuilder::new(
284            &fixture.signers_with_stake(),
285            &fixture.protocol_parameters(),
286        )
287        .unwrap();
288
289        builder
290            .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
291            .expect("Should be able to build test single signer for a registered party");
292    }
293
294    #[test]
295    fn should_restore_single_signer_from_previous_initializer() {
296        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
297        let signers = fixture.signers_fixture();
298        let signer = signers.first().unwrap();
299        let kes_signer = Some(Arc::new(KesSignerStandard::new(
300            signer.kes_secret_key_path().unwrap().to_path_buf(),
301            signer.operational_certificate_path().unwrap().to_path_buf(),
302        )) as Arc<dyn KesSigner>);
303
304        let first_builder = SignerBuilder::new(
305            &fixture.signers_with_stake(),
306            &fixture.protocol_parameters(),
307        )
308        .unwrap();
309
310        let (_, initializer) = first_builder
311            .build_test_single_signer(signer.signer_with_stake.clone(), kes_signer)
312            .unwrap();
313
314        let second_builder = SignerBuilder::new(
315            &fixture.signers_with_stake(),
316            &fixture.protocol_parameters(),
317        )
318        .unwrap();
319
320        second_builder
321            .restore_signer_from_initializer(signer.party_id(), initializer)
322            .expect("Should be able to restore a single signer from a protocol initialized");
323    }
324}