mithril_aggregator/tools/
genesis.rs1use 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 pub network: CardanoNetwork,
26
27 pub verification_key_store: Arc<dyn VerificationKeyStorer>,
29
30 pub ticker_service: Arc<dyn TickerService>,
32
33 pub genesis_verifier: Arc<ProtocolGenesisVerifier>,
35
36 pub certificate_verifier: Arc<dyn CertificateVerifier>,
38
39 pub epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
41
42 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 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 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 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 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 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}