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