mithril_aggregator/tools/
genesis.rs

1use anyhow::{anyhow, Context};
2use std::{
3    fs::File,
4    io::{prelude::*, Write},
5    path::{Path, PathBuf},
6    sync::Arc,
7};
8
9use mithril_common::{
10    certificate_chain::{CertificateGenesisProducer, CertificateVerifier},
11    crypto_helper::{
12        ProtocolAggregateVerificationKey, ProtocolGenesisSecretKey, ProtocolGenesisSignature,
13        ProtocolGenesisSigner, ProtocolGenesisVerifier,
14    },
15    entities::{ProtocolParameters, TimePoint},
16    protocol::SignerBuilder,
17    CardanoNetwork, StdResult, TickerService,
18};
19
20use crate::database::repository::CertificateRepository;
21use crate::{EpochSettingsStorer, VerificationKeyStorer};
22
23pub struct GenesisToolsDependency {
24    /// Cardano network
25    pub network: CardanoNetwork,
26
27    /// Verification key store
28    pub verification_key_store: Arc<dyn VerificationKeyStorer>,
29
30    /// Ticker service.
31    pub ticker_service: Arc<dyn TickerService>,
32
33    /// Genesis signature verifier service.
34    pub genesis_verifier: Arc<ProtocolGenesisVerifier>,
35
36    /// Certificate verifier service.
37    pub certificate_verifier: Arc<dyn CertificateVerifier>,
38
39    /// Epoch settings storer.
40    pub epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
41
42    /// Certificate store.
43    pub certificate_repository: Arc<CertificateRepository>,
44}
45
46pub struct GenesisTools {
47    network: CardanoNetwork,
48    time_point: TimePoint,
49    genesis_avk: ProtocolAggregateVerificationKey,
50    genesis_protocol_parameters: ProtocolParameters,
51    genesis_verifier: Arc<ProtocolGenesisVerifier>,
52    certificate_verifier: Arc<dyn CertificateVerifier>,
53    certificate_repository: Arc<CertificateRepository>,
54}
55
56impl GenesisTools {
57    pub fn new(
58        network: CardanoNetwork,
59        time_point: TimePoint,
60        genesis_avk: ProtocolAggregateVerificationKey,
61        genesis_protocol_parameters: ProtocolParameters,
62        genesis_verifier: Arc<ProtocolGenesisVerifier>,
63        certificate_verifier: Arc<dyn CertificateVerifier>,
64        certificate_repository: Arc<CertificateRepository>,
65    ) -> Self {
66        Self {
67            network,
68            time_point,
69            genesis_avk,
70            genesis_protocol_parameters,
71            genesis_verifier,
72            certificate_verifier,
73            certificate_repository,
74        }
75    }
76
77    pub async fn from_dependencies(dependencies: GenesisToolsDependency) -> StdResult<Self> {
78        let ticker_service = dependencies.ticker_service.clone();
79        let time_point = ticker_service.get_current_time_point().await?;
80        let genesis_verifier = dependencies.genesis_verifier.clone();
81        let certificate_verifier = dependencies.certificate_verifier.clone();
82        let certificate_repository = dependencies.certificate_repository.clone();
83        let epoch_settings_storer = dependencies.epoch_settings_storer.clone();
84        let genesis_avk_epoch = time_point.epoch.offset_to_next_signer_retrieval_epoch();
85        let genesis_protocol_parameters = epoch_settings_storer
86            .get_protocol_parameters(time_point.epoch.offset_to_signer_retrieval_epoch()?)
87            .await?
88            .ok_or_else(|| anyhow!("Missing protocol parameters for epoch {genesis_avk_epoch}"))?;
89        let genesis_signers = dependencies
90            .verification_key_store
91            .get_signers(genesis_avk_epoch)
92            .await?
93            .ok_or_else(|| anyhow!("Missing signers for epoch {genesis_avk_epoch}"))?;
94
95        let protocol_multi_signer =
96            SignerBuilder::new(&genesis_signers, &genesis_protocol_parameters)
97                .with_context(|| "Could not build a multi signer to compute the genesis avk")?
98                .build_multi_signer();
99        let genesis_avk = protocol_multi_signer.compute_aggregate_verification_key();
100
101        Ok(Self::new(
102            dependencies.network,
103            time_point,
104            genesis_avk,
105            genesis_protocol_parameters,
106            genesis_verifier,
107            certificate_verifier,
108            certificate_repository,
109        ))
110    }
111
112    /// Export AVK of the genesis stake distribution to a payload file
113    pub fn export_payload_to_sign(&self, target_path: &Path) -> StdResult<()> {
114        let mut target_file = File::create(target_path)?;
115        let protocol_message = CertificateGenesisProducer::create_genesis_protocol_message(
116            &self.genesis_protocol_parameters,
117            &self.genesis_avk,
118            &self.time_point.epoch,
119        )?;
120        target_file.write_all(protocol_message.compute_hash().as_bytes())?;
121        Ok(())
122    }
123
124    /// Import signature of the AVK of the genesis stake distribution from a file
125    pub async fn import_payload_signature(&self, signed_payload_path: &Path) -> StdResult<()> {
126        let mut signed_payload_file = File::open(signed_payload_path).unwrap();
127        let mut signed_payload_buffer = Vec::new();
128        signed_payload_file.read_to_end(&mut signed_payload_buffer)?;
129        let genesis_signature = ProtocolGenesisSignature::from_bytes(&signed_payload_buffer)?;
130
131        self.create_and_save_genesis_certificate(genesis_signature)
132            .await
133    }
134
135    /// Automatic bootstrap of the genesis certificate (test only)
136    pub async fn bootstrap_test_genesis_certificate(
137        &self,
138        genesis_signer: ProtocolGenesisSigner,
139    ) -> StdResult<()> {
140        let genesis_producer = CertificateGenesisProducer::new(Some(Arc::new(genesis_signer)));
141        let genesis_protocol_message = CertificateGenesisProducer::create_genesis_protocol_message(
142            &self.genesis_protocol_parameters,
143            &self.genesis_avk,
144            &self.time_point.epoch,
145        )?;
146        let genesis_signature =
147            genesis_producer.sign_genesis_protocol_message(genesis_protocol_message)?;
148        self.create_and_save_genesis_certificate(genesis_signature)
149            .await
150    }
151
152    /// Sign the genesis certificate
153    pub async fn sign_genesis_certificate(
154        to_sign_payload_path: &Path,
155        target_signed_payload_path: &Path,
156        genesis_secret_key_path: &Path,
157    ) -> StdResult<()> {
158        let genesis_secret_key =
159            ProtocolGenesisSecretKey::read_json_hex_from_file(genesis_secret_key_path)?;
160        let genesis_signer = ProtocolGenesisSigner::from_secret_key(genesis_secret_key);
161
162        let mut to_sign_payload_file = File::open(to_sign_payload_path).unwrap();
163        let mut to_sign_payload_buffer = Vec::new();
164        to_sign_payload_file.read_to_end(&mut to_sign_payload_buffer)?;
165
166        let genesis_signature = genesis_signer.sign(&to_sign_payload_buffer);
167        let signed_payload = genesis_signature.to_bytes();
168
169        let mut target_signed_payload_file = File::create(target_signed_payload_path)?;
170        target_signed_payload_file.write_all(&signed_payload)?;
171
172        Ok(())
173    }
174
175    async fn create_and_save_genesis_certificate(
176        &self,
177        genesis_signature: ProtocolGenesisSignature,
178    ) -> StdResult<()> {
179        let genesis_certificate = CertificateGenesisProducer::create_genesis_certificate(
180            self.genesis_protocol_parameters.clone(),
181            self.network,
182            self.time_point.epoch,
183            self.genesis_avk.clone(),
184            genesis_signature,
185        )?;
186        self.certificate_verifier
187            .verify_genesis_certificate(
188                &genesis_certificate,
189                &self.genesis_verifier.to_verification_key(),
190            )
191            .await?;
192        self.certificate_repository
193            .create_certificate(genesis_certificate.clone())
194            .await
195            .with_context(|| {
196                format!(
197                    "Genesis tool can not create certificate with genesis signature: '{:?}'",
198                    genesis_signature
199                )
200            })?;
201        Ok(())
202    }
203
204    /// Export the genesis keypair to a folder and returns the paths to the files (secret key, verification_key)
205    pub fn create_and_save_genesis_keypair(keypair_path: &Path) -> StdResult<(PathBuf, PathBuf)> {
206        let genesis_signer = ProtocolGenesisSigner::create_non_deterministic_signer();
207        let genesis_secret_key_path = keypair_path.join("genesis.sk");
208        genesis_signer
209            .secret_key()
210            .write_json_hex_to_file(&genesis_secret_key_path)?;
211        let genesis_verification_key_path = keypair_path.join("genesis.vk");
212        genesis_signer
213            .verification_key()
214            .write_json_hex_to_file(&genesis_verification_key_path)?;
215
216        Ok((genesis_secret_key_path, genesis_verification_key_path))
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use mithril_common::{
223        certificate_chain::MithrilCertificateVerifier,
224        crypto_helper::{
225            ProtocolGenesisSecretKey, ProtocolGenesisSigner, ProtocolGenesisVerificationKey,
226        },
227        test_utils::{fake_data, MithrilFixtureBuilder, TempDir},
228    };
229    use std::{fs::read_to_string, path::PathBuf};
230
231    use crate::database::test_helper::main_db_connection;
232    use crate::test_tools::TestLogger;
233
234    use super::*;
235
236    fn get_temp_dir(dir_name: &str) -> PathBuf {
237        TempDir::create("genesis", dir_name)
238    }
239
240    fn create_fake_genesis_avk() -> ProtocolAggregateVerificationKey {
241        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
242
243        fixture.compute_avk()
244    }
245
246    fn build_tools(
247        genesis_signer: &ProtocolGenesisSigner,
248    ) -> (
249        GenesisTools,
250        Arc<CertificateRepository>,
251        Arc<ProtocolGenesisVerifier>,
252        Arc<dyn CertificateVerifier>,
253    ) {
254        let connection = main_db_connection().unwrap();
255        let certificate_store = Arc::new(CertificateRepository::new(Arc::new(connection)));
256        let certificate_verifier = Arc::new(MithrilCertificateVerifier::new(
257            TestLogger::stdout(),
258            certificate_store.clone(),
259        ));
260        let genesis_avk = create_fake_genesis_avk();
261        let genesis_verifier = Arc::new(genesis_signer.create_verifier());
262        let genesis_tools = GenesisTools::new(
263            fake_data::network(),
264            TimePoint::dummy(),
265            genesis_avk,
266            fake_data::protocol_parameters(),
267            genesis_verifier.clone(),
268            certificate_verifier.clone(),
269            certificate_store.clone(),
270        );
271
272        (
273            genesis_tools,
274            certificate_store,
275            genesis_verifier,
276            certificate_verifier,
277        )
278    }
279
280    #[tokio::test]
281    async fn export_sign_then_import_genesis_payload() {
282        let test_dir = get_temp_dir("export_payload_to_sign");
283        let payload_path = test_dir.join("payload.txt");
284        let signed_payload_path = test_dir.join("payload-signed.txt");
285        let (genesis_secret_key_path, _) = GenesisTools::create_and_save_genesis_keypair(&test_dir)
286            .expect("exporting the keypair should not fail");
287        let genesis_secret_key = ProtocolGenesisSecretKey::from_json_hex(
288            &read_to_string(&genesis_secret_key_path)
289                .expect("reading genesis secret key file should not fail"),
290        )
291        .expect("parsing genesis secret key should not fail");
292        let genesis_signer = ProtocolGenesisSigner::from_secret_key(genesis_secret_key);
293        let (genesis_tools, certificate_store, genesis_verifier, certificate_verifier) =
294            build_tools(&genesis_signer);
295
296        genesis_tools
297            .export_payload_to_sign(&payload_path)
298            .expect("export_payload_to_sign should not fail");
299        GenesisTools::sign_genesis_certificate(
300            &payload_path,
301            &signed_payload_path,
302            &genesis_secret_key_path,
303        )
304        .await
305        .expect("sign_genesis_certificate should not fail");
306        genesis_tools
307            .import_payload_signature(&signed_payload_path)
308            .await
309            .expect("import_payload_signature should not fail");
310
311        let last_certificates = certificate_store.get_latest_certificates(10).await.unwrap();
312
313        assert_eq!(1, last_certificates.len());
314        certificate_verifier
315            .verify_genesis_certificate(
316                &last_certificates[0],
317                &genesis_verifier.to_verification_key(),
318            )
319            .await
320            .expect(
321                "verify_genesis_certificate should successfully validate the genesis certificate",
322            );
323    }
324
325    #[tokio::test]
326    async fn bootstrap_test_genesis_certificate_works() {
327        let genesis_signer = ProtocolGenesisSigner::create_deterministic_signer();
328        let (genesis_tools, certificate_store, genesis_verifier, certificate_verifier) =
329            build_tools(&genesis_signer);
330
331        genesis_tools
332            .bootstrap_test_genesis_certificate(genesis_signer)
333            .await
334            .expect("bootstrap test genesis certificate should not fail");
335
336        let last_certificates = certificate_store.get_latest_certificates(10).await.unwrap();
337
338        assert_eq!(1, last_certificates.len());
339        certificate_verifier
340            .verify_genesis_certificate(
341                &last_certificates[0],
342                &genesis_verifier.to_verification_key(),
343            )
344            .await
345            .expect(
346                "verify_genesis_certificate should successfully validate the genesis certificate",
347            );
348    }
349
350    #[test]
351    fn test_create_and_save_genesis_keypair() {
352        let temp_dir = get_temp_dir("test_create_and_save_genesis_keypair");
353        let (genesis_secret_key_path, genesis_verification_key_path) =
354            GenesisTools::create_and_save_genesis_keypair(&temp_dir)
355                .expect("Failed to create and save genesis keypair");
356        let genesis_secret_key = ProtocolGenesisSecretKey::from_json_hex(
357            &read_to_string(&genesis_secret_key_path)
358                .expect("Failed to read genesis secret key file"),
359        )
360        .expect("Failed to parse genesis secret key");
361        let genesis_verification_key = ProtocolGenesisVerificationKey::from_json_hex(
362            &read_to_string(&genesis_verification_key_path)
363                .expect("Failed to read genesis verification key file"),
364        )
365        .expect("Failed to parse genesis verification key");
366        let genesis_verifier =
367            ProtocolGenesisSigner::from_secret_key(genesis_secret_key).create_verifier();
368
369        let expected_genesis_verification_key = genesis_verifier.to_verification_key();
370        assert_eq!(expected_genesis_verification_key, genesis_verification_key);
371    }
372}