mithril_aggregator/commands/
genesis_command.rs

1use anyhow::Context;
2use clap::{Parser, Subcommand};
3use config::{builder::DefaultState, ConfigBuilder};
4use mithril_common::{
5    crypto_helper::{ProtocolGenesisSecretKey, ProtocolGenesisSigner},
6    entities::HexEncodedGenesisSecretKey,
7    StdResult,
8};
9use slog::{debug, Logger};
10use std::path::PathBuf;
11
12use crate::{dependency_injection::DependenciesBuilder, tools::GenesisTools, Configuration};
13
14/// Genesis tools
15#[derive(Parser, Debug, Clone)]
16pub struct GenesisCommand {
17    /// commands
18    #[clap(subcommand)]
19    pub genesis_subcommand: GenesisSubCommand,
20}
21
22impl GenesisCommand {
23    pub async fn execute(
24        &self,
25        root_logger: Logger,
26        config_builder: ConfigBuilder<DefaultState>,
27    ) -> StdResult<()> {
28        self.genesis_subcommand
29            .execute(root_logger, config_builder)
30            .await
31    }
32}
33
34/// Genesis tools commands.
35#[derive(Debug, Clone, Subcommand)]
36pub enum GenesisSubCommand {
37    /// Genesis certificate export command.
38    Export(ExportGenesisSubCommand),
39
40    /// Genesis certificate import command.
41    Import(ImportGenesisSubCommand),
42
43    /// Genesis certificate sign command.
44    Sign(SignGenesisSubCommand),
45
46    /// Genesis certificate bootstrap command.
47    Bootstrap(BootstrapGenesisSubCommand),
48
49    /// Genesis keypair generation command.
50    GenerateKeypair(GenerateKeypairGenesisSubCommand),
51}
52
53impl GenesisSubCommand {
54    pub async fn execute(
55        &self,
56        root_logger: Logger,
57        config_builder: ConfigBuilder<DefaultState>,
58    ) -> StdResult<()> {
59        match self {
60            Self::Bootstrap(cmd) => cmd.execute(root_logger, config_builder).await,
61            Self::Export(cmd) => cmd.execute(root_logger, config_builder).await,
62            Self::Import(cmd) => cmd.execute(root_logger, config_builder).await,
63            Self::Sign(cmd) => cmd.execute(root_logger, config_builder).await,
64            Self::GenerateKeypair(cmd) => cmd.execute(root_logger, config_builder).await,
65        }
66    }
67}
68
69/// Genesis certificate export command
70#[derive(Parser, Debug, Clone)]
71pub struct ExportGenesisSubCommand {
72    /// Target path
73    #[clap(long)]
74    target_path: PathBuf,
75}
76
77impl ExportGenesisSubCommand {
78    pub async fn execute(
79        &self,
80        root_logger: Logger,
81        config_builder: ConfigBuilder<DefaultState>,
82    ) -> StdResult<()> {
83        let config: Configuration = config_builder
84            .build()
85            .with_context(|| "configuration build error")?
86            .try_deserialize()
87            .with_context(|| "configuration deserialize error")?;
88        debug!(root_logger, "EXPORT GENESIS command"; "config" => format!("{config:?}"));
89        println!(
90            "Genesis export payload to sign to {}",
91            self.target_path.display()
92        );
93        let mut dependencies_builder =
94            DependenciesBuilder::new(root_logger.clone(), config.clone());
95        let dependencies = dependencies_builder
96            .create_genesis_container()
97            .await
98            .with_context(|| {
99                "Dependencies Builder can not create genesis command dependencies container"
100            })?;
101
102        let genesis_tools = GenesisTools::from_dependencies(dependencies)
103            .await
104            .with_context(|| "genesis-tools: initialization error")?;
105        genesis_tools
106            .export_payload_to_sign(&self.target_path)
107            .with_context(|| "genesis-tools: export error")?;
108        Ok(())
109    }
110}
111
112#[derive(Parser, Debug, Clone)]
113pub struct ImportGenesisSubCommand {
114    /// Signed Payload Path
115    #[clap(long)]
116    signed_payload_path: PathBuf,
117}
118
119impl ImportGenesisSubCommand {
120    pub async fn execute(
121        &self,
122        root_logger: Logger,
123        config_builder: ConfigBuilder<DefaultState>,
124    ) -> StdResult<()> {
125        let config: Configuration = config_builder
126            .build()
127            .with_context(|| "configuration build error")?
128            .try_deserialize()
129            .with_context(|| "configuration deserialize error")?;
130        debug!(root_logger, "IMPORT GENESIS command"; "config" => format!("{config:?}"));
131        println!(
132            "Genesis import signed payload from {}",
133            self.signed_payload_path.to_string_lossy()
134        );
135        let mut dependencies_builder =
136            DependenciesBuilder::new(root_logger.clone(), config.clone());
137        let dependencies = dependencies_builder
138            .create_genesis_container()
139            .await
140            .with_context(|| {
141                "Dependencies Builder can not create genesis command dependencies container"
142            })?;
143
144        let genesis_tools = GenesisTools::from_dependencies(dependencies)
145            .await
146            .with_context(|| "genesis-tools: initialization error")?;
147        genesis_tools
148            .import_payload_signature(&self.signed_payload_path)
149            .await
150            .with_context(|| "genesis-tools: import error")?;
151        Ok(())
152    }
153}
154
155#[derive(Parser, Debug, Clone)]
156pub struct SignGenesisSubCommand {
157    /// To Sign Payload Path
158    #[clap(long)]
159    to_sign_payload_path: PathBuf,
160
161    /// Target Signed Payload Path
162    #[clap(long)]
163    target_signed_payload_path: PathBuf,
164
165    /// Genesis Secret Key Path
166    #[clap(long)]
167    genesis_secret_key_path: PathBuf,
168}
169
170impl SignGenesisSubCommand {
171    pub async fn execute(
172        &self,
173        root_logger: Logger,
174        _config_builder: ConfigBuilder<DefaultState>,
175    ) -> StdResult<()> {
176        debug!(root_logger, "SIGN GENESIS command");
177        println!(
178            "Genesis sign payload from {} to {}",
179            self.to_sign_payload_path.to_string_lossy(),
180            self.target_signed_payload_path.to_string_lossy()
181        );
182
183        GenesisTools::sign_genesis_certificate(
184            &self.to_sign_payload_path,
185            &self.target_signed_payload_path,
186            &self.genesis_secret_key_path,
187        )
188        .await
189        .with_context(|| "genesis-tools: sign error")?;
190
191        Ok(())
192    }
193}
194#[derive(Parser, Debug, Clone)]
195pub struct BootstrapGenesisSubCommand {
196    /// Genesis Secret Key (test only)
197    #[clap(long, env = "GENESIS_SECRET_KEY")]
198    genesis_secret_key: HexEncodedGenesisSecretKey,
199}
200
201impl BootstrapGenesisSubCommand {
202    pub async fn execute(
203        &self,
204        root_logger: Logger,
205        config_builder: ConfigBuilder<DefaultState>,
206    ) -> StdResult<()> {
207        let config: Configuration = config_builder
208            .build()
209            .with_context(|| "configuration build error")?
210            .try_deserialize()
211            .with_context(|| "configuration deserialize error")?;
212        debug!(root_logger, "BOOTSTRAP GENESIS command"; "config" => format!("{config:?}"));
213        println!("Genesis bootstrap for test only!");
214        let mut dependencies_builder =
215            DependenciesBuilder::new(root_logger.clone(), config.clone());
216        let dependencies = dependencies_builder
217            .create_genesis_container()
218            .await
219            .with_context(|| {
220                "Dependencies Builder can not create genesis command dependencies container"
221            })?;
222
223        let genesis_tools = GenesisTools::from_dependencies(dependencies)
224            .await
225            .with_context(|| "genesis-tools: initialization error")?;
226        let genesis_secret_key = ProtocolGenesisSecretKey::from_json_hex(&self.genesis_secret_key)
227            .with_context(|| "json hex decode of genesis secret key failure")?;
228        let genesis_signer = ProtocolGenesisSigner::from_secret_key(genesis_secret_key);
229        genesis_tools
230            .bootstrap_test_genesis_certificate(genesis_signer)
231            .await
232            .with_context(|| "genesis-tools: bootstrap error")?;
233        Ok(())
234    }
235}
236
237/// Genesis keypair generation command.
238#[derive(Parser, Debug, Clone)]
239pub struct GenerateKeypairGenesisSubCommand {
240    /// Target path for the generated keypair
241    #[clap(long)]
242    target_path: PathBuf,
243}
244
245impl GenerateKeypairGenesisSubCommand {
246    pub async fn execute(
247        &self,
248        root_logger: Logger,
249        _config_builder: ConfigBuilder<DefaultState>,
250    ) -> StdResult<()> {
251        debug!(root_logger, "GENERATE KEYPAIR GENESIS command");
252        println!(
253            "Genesis generate keypair to {}",
254            self.target_path.to_string_lossy()
255        );
256
257        GenesisTools::create_and_save_genesis_keypair(&self.target_path)
258            .with_context(|| "genesis-tools: keypair generation error")?;
259
260        Ok(())
261    }
262}