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