diff --git a/Cargo.lock b/Cargo.lock index b498cd6ae42..46cec2f706d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3578,7 +3578,7 @@ dependencies = [ [[package]] name = "mithril-aggregator" -version = "0.6.26" +version = "0.6.27" dependencies = [ "anyhow", "async-trait", @@ -3735,7 +3735,7 @@ dependencies = [ [[package]] name = "mithril-common" -version = "0.4.112" +version = "0.4.113" dependencies = [ "anyhow", "async-trait", diff --git a/docs/website/root/manual/develop/nodes/mithril-aggregator.md b/docs/website/root/manual/develop/nodes/mithril-aggregator.md index 629039c8a3c..01dfbb45c3d 100644 --- a/docs/website/root/manual/develop/nodes/mithril-aggregator.md +++ b/docs/website/root/manual/develop/nodes/mithril-aggregator.md @@ -259,6 +259,7 @@ Usage: mithril-aggregator era Commands: list Era list command generate-tx-datum Era tx datum generate command + generate-keypair Era keypair generation command help Print this message or the help of the given subcommand(s) Options: @@ -401,6 +402,7 @@ Here are the available subcommands: | **genesis generate-keypair** | Generates a genesis keypair | | **era list** | Lists the supported eras | | **era generate-tx-datum** | Generates the era markers transaction datum to be stored on-chain | +| **era generate-keypair** | Generates an era keypair | | **tools recompute-certificates-hash** | Loads all certificates in the database, recomputing their hash, and updating all related entities | ## Configuration parameters @@ -506,4 +508,10 @@ Here is a list of the available parameters: | `era_markers_secret_key` | `--era-markers-secret-key` | - | `ERA_MARKERS_SECRET_KEY` | Era markers secret key that is used to verify the authenticity of the era markers on the chain. | - | - | :heavy_check_mark: | | `target_path` | `--target-path` | - | - | Path of the file to export the payload to. | - | - | - | +`era generate-keypair` command: + +| Parameter | Command line (long) | Command line (short) | Environment variable | Description | Default value | Example | Mandatory | +| ------------- | ------------------- | :------------------: | -------------------- | ------------------------------------- | ------------- | ------- | :----------------: | +| `target_path` | `--target-path` | - | - | Target path for the generated keypair | - | - | :heavy_check_mark: | + The `tools recompute-certificates-hash` command has no dedicated parameters. diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index 07213b2ae63..374d2d8a4ae 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-aggregator" -version = "0.6.26" +version = "0.6.27" description = "A Mithril Aggregator server" authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-aggregator/src/commands/era_command.rs b/mithril-aggregator/src/commands/era_command.rs index ba677943287..02994e4b9b0 100644 --- a/mithril-aggregator/src/commands/era_command.rs +++ b/mithril-aggregator/src/commands/era_command.rs @@ -40,6 +40,9 @@ pub enum EraSubCommand { /// Era tx datum generate command. GenerateTxDatum(GenerateTxDatumEraSubCommand), + + /// Era keypair generation command. + GenerateKeypair(GenerateKeypairEraSubCommand), } impl EraSubCommand { @@ -51,6 +54,7 @@ impl EraSubCommand { match self { Self::List(cmd) => cmd.execute(root_logger, config_builder).await, Self::GenerateTxDatum(cmd) => cmd.execute(root_logger, config_builder).await, + Self::GenerateKeypair(cmd) => cmd.execute(root_logger, config_builder).await, } } } @@ -129,3 +133,30 @@ impl GenerateTxDatumEraSubCommand { Ok(()) } } + +/// Era keypair generation command. +#[derive(Parser, Debug, Clone)] +pub struct GenerateKeypairEraSubCommand { + /// Target path for the generated keypair + #[clap(long)] + target_path: PathBuf, +} + +impl GenerateKeypairEraSubCommand { + pub async fn execute( + &self, + root_logger: Logger, + _config_builder: ConfigBuilder, + ) -> StdResult<()> { + debug!(root_logger, "GENERATE KEYPAIR ERA command"); + println!( + "Era generate keypair to {}", + self.target_path.to_string_lossy() + ); + + EraTools::create_and_save_era_keypair(&self.target_path) + .with_context(|| "era-tools: keypair generation error")?; + + Ok(()) + } +} diff --git a/mithril-aggregator/src/tools/era.rs b/mithril-aggregator/src/tools/era.rs index a45ec283437..8c4a23ff0fe 100644 --- a/mithril-aggregator/src/tools/era.rs +++ b/mithril-aggregator/src/tools/era.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use anyhow::anyhow; use mithril_common::{ chain_observer::{TxDatumBuilder, TxDatumFieldValue}, @@ -56,16 +58,41 @@ impl EraTools { .build()?; Ok(tx_datum.0) } + + /// Export the era keypair to a folder and returns the paths to the files (secret key, verification_key) + pub fn create_and_save_era_keypair(keypair_path: &Path) -> StdResult<(PathBuf, PathBuf)> { + let era_signer = EraMarkersSigner::create_non_deterministic_signer(); + let era_secret_key_path = keypair_path.join("era.sk"); + era_signer + .secret_key() + .write_json_hex_to_file(&era_secret_key_path)?; + let era_verification_key_path = keypair_path.join("era.vk"); + era_signer + .verification_key() + .write_json_hex_to_file(&era_verification_key_path)?; + + Ok((era_secret_key_path, era_verification_key_path)) + } } #[cfg(test)] mod tests { + use mithril_common::{ + crypto_helper::{EraMarkersVerifierSecretKey, EraMarkersVerifierVerificationKey}, + test_utils::TempDir, + }; + use std::fs::read_to_string; + use super::*; fn build_tools() -> EraTools { EraTools {} } + fn get_temp_dir(dir_name: &str) -> PathBuf { + TempDir::create("era", dir_name) + } + #[test] fn get_supported_eras_list() { let era_tools = build_tools(); @@ -92,4 +119,25 @@ mod tests { .generate_tx_datum(Epoch(3), Some(Epoch(2)), &era_markers_signer) .expect_err("generate_tx_datum should have failed"); } + + #[test] + fn test_create_and_save_era_keypair() { + let temp_dir = get_temp_dir("test_create_and_save_era_keypair"); + let (era_secret_key_path, era_verification_key_path) = + EraTools::create_and_save_era_keypair(&temp_dir) + .expect("Failed to create and save era keypair"); + let era_secret_key = EraMarkersVerifierSecretKey::from_json_hex( + &read_to_string(&era_secret_key_path).expect("Failed to read era secret key file"), + ) + .expect("Failed to parse era secret key"); + let era_verification_key = EraMarkersVerifierVerificationKey::from_json_hex( + &read_to_string(&era_verification_key_path) + .expect("Failed to read era verification key file"), + ) + .expect("Failed to parse era verification key"); + let era_verifier = EraMarkersSigner::from_secret_key(era_secret_key).create_verifier(); + + let expected_era_verification_key = era_verifier.to_verification_key(); + assert_eq!(expected_era_verification_key, era_verification_key); + } } diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index 2e29f78f91d..f08885bdc40 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-common" -version = "0.4.112" +version = "0.4.113" description = "Common types, interfaces, and utilities for Mithril nodes." authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-common/src/crypto_helper/era.rs b/mithril-common/src/crypto_helper/era.rs index d3dc2973d65..1e51d1244a8 100644 --- a/mithril-common/src/crypto_helper/era.rs +++ b/mithril-common/src/crypto_helper/era.rs @@ -47,13 +47,22 @@ impl EraMarkersSigner { Self::create_test_signer(rng) } - #[cfg(test)] /// [EraMarkersSigner] non deterministic pub fn create_non_deterministic_signer() -> Self { let rng = rand_core::OsRng; Self::create_test_signer(rng) } + /// Get the [EraMarkersVerifierSecretKey] + pub fn secret_key(&self) -> EraMarkersVerifierSecretKey { + self.secret_key.clone() + } + + /// Get the [EraMarkersVerifierVerificationKey] + pub fn verification_key(&self) -> EraMarkersVerifierVerificationKey { + self.secret_key.verifying_key().into() + } + /// [EraMarkersSigner] from [EraMarkersVerifierSecretKey] pub fn from_secret_key(secret_key: EraMarkersVerifierSecretKey) -> Self { Self { secret_key }