mithril_common/certificate_chain/
certificate_genesis.rs

1//! A module used to create a Genesis Certificate
2//!
3use std::sync::Arc;
4
5use chrono::prelude::*;
6use thiserror::Error;
7
8use crate::{
9    crypto_helper::{
10        ProtocolAggregateVerificationKey, ProtocolGenesisSignature, ProtocolGenesisSigner,
11        PROTOCOL_VERSION,
12    },
13    entities::{
14        Certificate, CertificateMetadata, CertificateSignature, Epoch, ProtocolMessage,
15        ProtocolMessagePartKey, ProtocolParameters,
16    },
17    protocol::ToMessage,
18    StdResult,
19};
20
21/// [CertificateGenesisProducer] related errors.
22#[derive(Error, Debug)]
23pub enum CertificateGenesisProducerError {
24    /// Error raised when there is no genesis signer available
25    #[error("missing genesis signer error")]
26    MissingGenesisSigner(),
27}
28
29/// CertificateGenesisProducer is in charge of producing a Genesis Certificate
30#[derive(Debug)]
31pub struct CertificateGenesisProducer {
32    genesis_signer: Option<Arc<ProtocolGenesisSigner>>,
33}
34
35impl CertificateGenesisProducer {
36    /// CertificateGenesisProducer factory
37    pub fn new(genesis_signer: Option<Arc<ProtocolGenesisSigner>>) -> Self {
38        Self { genesis_signer }
39    }
40
41    /// Create the Genesis protocol message
42    pub fn create_genesis_protocol_message(
43        genesis_protocol_parameters: &ProtocolParameters,
44        genesis_avk: &ProtocolAggregateVerificationKey,
45        genesis_epoch: &Epoch,
46    ) -> StdResult<ProtocolMessage> {
47        let genesis_avk = genesis_avk.to_json_hex()?;
48        let mut protocol_message = ProtocolMessage::new();
49        protocol_message.set_message_part(
50            ProtocolMessagePartKey::NextAggregateVerificationKey,
51            genesis_avk,
52        );
53        protocol_message.set_message_part(
54            ProtocolMessagePartKey::NextProtocolParameters,
55            genesis_protocol_parameters.compute_hash(),
56        );
57        protocol_message.set_message_part(
58            ProtocolMessagePartKey::CurrentEpoch,
59            genesis_epoch.to_string(),
60        );
61        Ok(protocol_message)
62    }
63
64    /// Sign the Genesis protocol message (test only)
65    pub fn sign_genesis_protocol_message<T: ToMessage>(
66        &self,
67        genesis_message: T,
68    ) -> Result<ProtocolGenesisSignature, CertificateGenesisProducerError> {
69        Ok(self
70            .genesis_signer
71            .as_ref()
72            .ok_or_else(CertificateGenesisProducerError::MissingGenesisSigner)?
73            .sign(genesis_message.to_message().as_bytes()))
74    }
75
76    /// Create a Genesis Certificate
77    pub fn create_genesis_certificate<T: Into<String>>(
78        protocol_parameters: ProtocolParameters,
79        network: T,
80        epoch: Epoch,
81        genesis_avk: ProtocolAggregateVerificationKey,
82        genesis_signature: ProtocolGenesisSignature,
83    ) -> StdResult<Certificate> {
84        let protocol_version = PROTOCOL_VERSION.to_string();
85        let initiated_at = Utc::now();
86        let sealed_at = Utc::now();
87        let signers = vec![];
88        let metadata = CertificateMetadata::new(
89            network,
90            protocol_version,
91            protocol_parameters.clone(),
92            initiated_at,
93            sealed_at,
94            signers,
95        );
96        let previous_hash = "".to_string();
97        let genesis_protocol_message =
98            Self::create_genesis_protocol_message(&protocol_parameters, &genesis_avk, &epoch)?;
99        Ok(Certificate::new(
100            previous_hash,
101            epoch,
102            metadata,
103            genesis_protocol_message,
104            genesis_avk,
105            CertificateSignature::GenesisSignature(genesis_signature),
106        ))
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    use crate::{entities::ProtocolMessagePartKey, test_utils::MithrilFixtureBuilder};
115
116    #[test]
117    fn test_create_genesis_protocol_message_has_expected_keys_and_values() {
118        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
119        let genesis_protocol_parameters = fixture.protocol_parameters();
120        let genesis_avk = fixture.compute_avk();
121        let genesis_epoch = Epoch(123);
122        let protocol_message = CertificateGenesisProducer::create_genesis_protocol_message(
123            &genesis_protocol_parameters,
124            &genesis_avk,
125            &genesis_epoch,
126        )
127        .unwrap();
128
129        let expected_genesis_avk_value = fixture.compute_and_encode_avk();
130        assert_eq!(
131            protocol_message
132                .get_message_part(&ProtocolMessagePartKey::NextAggregateVerificationKey),
133            Some(&expected_genesis_avk_value)
134        );
135
136        let expected_genesis_protocol_parameters_value = genesis_protocol_parameters.compute_hash();
137        assert_eq!(
138            protocol_message.get_message_part(&ProtocolMessagePartKey::NextProtocolParameters),
139            Some(&expected_genesis_protocol_parameters_value)
140        );
141
142        let expected_genesis_epoch = genesis_epoch.to_string();
143        assert_eq!(
144            protocol_message.get_message_part(&ProtocolMessagePartKey::CurrentEpoch),
145            Some(&expected_genesis_epoch)
146        );
147    }
148}