Skip to content

Commit 1bd03a0

Browse files
authored
feat: implement cmd/createenr (#115)
* feat: implement cmd/create/enr * fix: run fmt * fix: address comments
1 parent b8b3105 commit 1bd03a0

7 files changed

Lines changed: 119 additions & 15 deletions

File tree

crates/charon-cli/src/cli.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use clap::{Parser, Subcommand};
44

5-
use crate::commands::{enr::EnrArgs, version::VersionArgs};
5+
use crate::commands::{create_enr::CreateEnrArgs, enr::EnrArgs, version::VersionArgs};
66

77
/// Pluto - Proof of Stake Ethereum Distributed Validator Client
88
#[derive(Parser)]
@@ -27,9 +27,29 @@ pub enum Commands {
2727
)]
2828
Enr(EnrArgs),
2929

30+
#[command(
31+
about = "Create artifacts for a distributed validator cluster",
32+
long_about = "Create artifacts for a distributed validator cluster. These commands can be used to facilitate the creation of a distributed validator cluster between a group of operators by performing a distributed key generation ceremony, or they can be used to create a local cluster for single operator use cases."
33+
)]
34+
Create(CreateArgs),
35+
3036
#[command(about = "Print version and exit", long_about = "Output version info")]
3137
Version(VersionArgs),
3238
// Future commands will be added here:
3339
// Run(RunArgs),
34-
// Create(CreateArgs),
40+
}
41+
42+
/// Arguments for the create command
43+
#[derive(clap::Args)]
44+
pub struct CreateArgs {
45+
#[command(subcommand)]
46+
pub command: CreateCommands,
47+
}
48+
49+
/// Create subcommands
50+
#[derive(Subcommand)]
51+
pub enum CreateCommands {
52+
/// Create an Ethereum Node Record (ENR) private key to identify this charon
53+
/// client
54+
Enr(CreateEnrArgs),
3555
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! Create ENR command implementation.
2+
//!
3+
//! This module implements the `pluto create enr` command, which generates a new
4+
//! Ethereum Node Record (ENR) private key and stores it securely on disk.
5+
6+
use std::{
7+
io::{self, Write},
8+
path::{Path, PathBuf},
9+
};
10+
11+
use charon_eth2::enr::Record;
12+
use charon_p2p::k1;
13+
14+
use crate::error::{CliError, Result};
15+
16+
/// Arguments for the create enr command
17+
#[derive(clap::Args)]
18+
pub struct CreateEnrArgs {
19+
#[arg(
20+
long = "data-dir",
21+
env = "CHARON_DATA_DIR",
22+
default_value = ".charon",
23+
help = "The directory where pluto will store all its internal data."
24+
)]
25+
pub data_dir: PathBuf,
26+
}
27+
28+
/// Runs the create enr command
29+
///
30+
/// Stores a new charon-enr-private-key to disk and prints the ENR for the
31+
/// provided config. It returns an error if the key already exists.
32+
pub fn run(args: CreateEnrArgs) -> Result<()> {
33+
if k1::load_priv_key(&args.data_dir).is_ok() {
34+
let enr_path = k1::key_path(&args.data_dir);
35+
return Err(CliError::PrivateKeyAlreadyExists { enr_path });
36+
}
37+
38+
let key = k1::new_saved_priv_key(&args.data_dir)?;
39+
40+
let record = Record::new(key, Vec::new())?;
41+
let key_path = k1::key_path(&args.data_dir);
42+
43+
let mut writer = io::stdout();
44+
writeln!(writer, "Created ENR private key: {}", key_path.display())?;
45+
writeln!(writer, "{}", record)?;
46+
write_enr_warning(&mut writer, &key_path)?;
47+
48+
Ok(())
49+
}
50+
51+
/// Writes backup key warning to the terminal
52+
fn write_enr_warning(w: &mut dyn Write, key_path: &Path) -> Result<()> {
53+
writeln!(w)?;
54+
writeln!(
55+
w,
56+
"***************** WARNING: Backup key **********************"
57+
)?;
58+
writeln!(
59+
w,
60+
" PLEASE BACKUP YOUR KEY IMMEDIATELY! IF YOU LOSE YOUR KEY,"
61+
)?;
62+
writeln!(
63+
w,
64+
" YOU WON'T BE ABLE TO PARTICIPATE IN RUNNING A CHARON CLUSTER.\n"
65+
)?;
66+
writeln!(w, " YOU CAN FIND YOUR KEY IN {}", key_path.display())?;
67+
writeln!(
68+
w,
69+
"****************************************************************"
70+
)?;
71+
writeln!(w)?;
72+
Ok(())
73+
}

crates/charon-cli/src/commands/enr.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ use crate::error::{CliError, Result};
1818
/// Arguments for the ENR command.
1919
#[derive(clap::Args)]
2020
pub struct EnrArgs {
21-
/// The directory where pluto will store all its internal data.
22-
#[arg(long = "data-dir", env = "CHARON_DATA_DIR", default_value = ".charon")]
21+
#[arg(
22+
long = "data-dir",
23+
env = "CHARON_DATA_DIR",
24+
default_value = ".charon",
25+
help = "The directory where pluto will store all its internal data."
26+
)]
2327
pub data_dir: PathBuf,
2428

25-
/// Prints the expanded form of ENR.
26-
#[arg(long, short = 'v')]
29+
#[arg(long, help = "Prints the expanded form of ENR.")]
2730
pub verbose: bool,
2831
}
2932

30-
/// loads the p2pkey from disk and prints the ENR for the provided config.
33+
/// Loads the p2pkey from disk and prints the ENR for the provided config.
3134
pub fn run(args: EnrArgs) -> Result<()> {
3235
let mut writer = io::stdout();
3336

@@ -40,11 +43,8 @@ pub fn run(args: EnrArgs) -> Result<()> {
4043
let enr_path = k1::key_path(&args.data_dir);
4144
return Err(CliError::PrivateKeyNotFound { enr_path });
4245
}
43-
Err(k1::K1Error::K1UtilError(e)) => {
44-
return Err(CliError::KeyLoadError(e.to_string()));
45-
}
46-
Err(k1::K1Error::IoError(e)) => {
47-
return Err(CliError::IoError(e));
46+
Err(e) => {
47+
return Err(CliError::KeyLoadError(e));
4848
}
4949
};
5050

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
pub mod create_enr;
12
pub mod enr;
23
pub mod version;

crates/charon-cli/src/commands/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::error::Result;
66
#[derive(clap::Args)]
77
pub struct VersionArgs {
88
#[arg(
9-
long = "verbose",
9+
long,
1010
help = "Includes detailed module version info and supported protocols."
1111
)]
1212
pub verbose: bool,

crates/charon-cli/src/error.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@ pub(crate) enum CliError {
1818
enr_path: PathBuf,
1919
},
2020

21+
/// Private key already exists.
22+
#[error("charon-enr-private-key already exists")]
23+
PrivateKeyAlreadyExists {
24+
/// Path where the ENR private key exists.
25+
enr_path: PathBuf,
26+
},
27+
2128
/// Failed to load private key.
2229
#[error("Failed to load private key: {0}")]
23-
KeyLoadError(String),
30+
KeyLoadError(#[from] charon_p2p::k1::K1Error),
2431

2532
/// ENR generation failed.
2633
#[error("ENR generation failed: {0}")]

crates/charon-cli/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ mod cli;
1010
mod commands;
1111
mod error;
1212

13-
use cli::{Cli, Commands};
13+
use cli::{Cli, Commands, CreateCommands};
1414
use error::Result;
1515

1616
fn main() -> Result<()> {
1717
let cli = Cli::parse();
1818

1919
match cli.command {
20+
Commands::Create(args) => match args.command {
21+
CreateCommands::Enr(args) => commands::create_enr::run(args),
22+
},
2023
Commands::Enr(args) => commands::enr::run(args),
2124
Commands::Version(args) => commands::version::run(args),
2225
}

0 commit comments

Comments
 (0)