Skip to content

Commit

Permalink
Improvements for keygen cli and crates
Browse files Browse the repository at this point in the history
Import improvements from FuelLabs/sway#5153.

Once this PR is merged and a new release is created sway will reference
this crate instead of having its own copy.
  • Loading branch information
cr-fuel committed Oct 31, 2023
1 parent b02f16f commit a065b3f
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 82 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions bin/keygen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ description = "Command line utilities for fuel-core key management"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
serde_json = { workspace = true, features = ["raw_value"] }
fuel-core-keygen = { workspace = true }
atty = "0.2.14"
termion = "2.0.1"
54 changes: 49 additions & 5 deletions bin/keygen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
//! A simple keygen cli utility tool for configuring fuel-core

use atty::Stream;
use clap::Parser;
use fuel_core_keygen::keygen;
use std::io::{
stdin,
stdout,
Read,
Write,
};
use termion::screen::IntoAlternateScreen;

/// Key management utilities for configuring fuel-core
#[derive(Debug, Parser)]
Expand All @@ -11,15 +18,52 @@ pub(crate) enum Command {
}

impl Command {
pub(crate) fn exec(&self) -> anyhow::Result<()> {
pub(crate) fn exec(&self) -> anyhow::Result<(serde_json::Value, bool)> {
match self {
Command::New(cmd) => cmd.exec(),
Command::Parse(cmd) => cmd.exec(),
Command::New(cmd) => Ok((cmd.exec()?, cmd.pretty)),
Command::Parse(cmd) => Ok((cmd.exec()?, cmd.pretty)),
}
}
}

fn main() -> anyhow::Result<()> {
let cmd = Command::parse();
cmd.exec()
let (result, is_pretty) = cmd.exec()?;
print_value(result, is_pretty)
}

fn wait_for_keypress() {
let mut single_key = [0u8];
stdin().read_exact(&mut single_key).unwrap();
}

fn display_string_discreetly(
discreet_string: &str,
continue_message: &str,
) -> anyhow::Result<()> {
if atty::is(Stream::Stdout) {
let mut screen = stdout().into_alternate_screen()?;
writeln!(screen, "{discreet_string}")?;
screen.flush()?;
println!("{continue_message}");
wait_for_keypress();
} else {
println!("{discreet_string}");
}
Ok(())
}

fn print_value(output: serde_json::Value, pretty: bool) -> anyhow::Result<()> {
let output = if pretty {
serde_json::to_string_pretty(&output)
} else {
serde_json::to_string(&output)
}
.map_err(anyhow::Error::msg);

let _ = display_string_discreetly(
&output?,
"### Do not share or lose this private key! Press any key to complete. ###",
);
Ok(())
}
2 changes: 0 additions & 2 deletions crates/keygen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ description = "Create to create command line utilities for fuel-core key managem

[dependencies]
anyhow = { workspace = true }
atty = "0.2.14"
clap = { workspace = true, features = ["derive", "env"] }
fuel-core-types = { workspace = true, features = ["serde", "random"] }
libp2p-identity = { version = "0.2.4", features = ["secp256k1", "peerid"] }
serde_json = { workspace = true, features = ["raw_value"] }
termion = "2.0.1"
98 changes: 28 additions & 70 deletions crates/keygen/src/keygen.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
use crate::{
BLOCK_PRODUCTION,
P2P,
};
use atty::Stream;
use clap::ValueEnum;
use fuel_core_types::{
fuel_crypto::{
Expand All @@ -21,30 +16,23 @@ use libp2p_identity::{
};
use serde_json::json;
use std::{
io::{
stdin,
stdout,
Read,
Write,
},
ops::Deref,
str::FromStr,
};
use termion::screen::IntoAlternateScreen;

/// Generate a random new secret & public key in the format expected by fuel-core
#[derive(Debug, clap::Args)]
#[clap(author, version, about)]
pub struct NewKey {
#[clap(long = "pretty", short = 'p')]
pretty: bool,
pub pretty: bool,
#[clap(
long = "key-type",
short = 'k',
value_enum,
default_value = BLOCK_PRODUCTION,
default_value = <KeyType as std::convert::Into<&'static str>>::into(KeyType::BlockProduction),
)]
key_type: KeyType,
pub key_type: KeyType,
}

#[derive(Clone, Debug, Default, ValueEnum)]
Expand All @@ -54,20 +42,29 @@ pub enum KeyType {
Peering,
}

impl From<KeyType> for &'static str {
fn from(key_type: KeyType) -> Self {
match key_type {
KeyType::BlockProduction => "block-production",
KeyType::Peering => "p2p",
}
}
}

impl NewKey {
pub fn exec(&self) -> anyhow::Result<()> {
pub fn exec(&self) -> anyhow::Result<serde_json::Value> {
let mut rng = StdRng::from_entropy();
let secret = SecretKey::random(&mut rng);
let public_key = secret.public_key();
let secret_str = secret.to_string();

let output = match self.key_type {
Ok(match self.key_type {
KeyType::BlockProduction => {
let address = Input::owner(&public_key);
json!({
"secret": secret_str,
"address": address,
"type": BLOCK_PRODUCTION,
"type": <KeyType as std::convert::Into<&'static str>>::into(KeyType::BlockProduction),
})
}
KeyType::Peering => {
Expand All @@ -80,11 +77,10 @@ impl NewKey {
json!({
"secret": secret_str,
"peer_id": peer_id.to_string(),
"type": P2P
"type": <KeyType as std::convert::Into<&'static str>>::into(KeyType::Peering),
})
}
};
print_value(output, self.pretty)
})
}
}

Expand All @@ -94,28 +90,27 @@ impl NewKey {
pub struct ParseSecret {
secret: String,
#[clap(long = "pretty", short = 'p')]
pretty: bool,
pub pretty: bool,
#[clap(
long = "key-type",
short = 'k',
value_enum,
default_value = BLOCK_PRODUCTION,
default_value = <KeyType as std::convert::Into<&'static str>>::into(KeyType::BlockProduction),
)]
key_type: KeyType,
pub key_type: KeyType,
}

impl ParseSecret {
pub fn exec(&self) -> anyhow::Result<()> {
pub fn exec(&self) -> anyhow::Result<serde_json::Value> {
let secret = SecretKey::from_str(&self.secret)
.map_err(|_| anyhow::anyhow!("invalid secret key"))?;
match self.key_type {
Ok(match self.key_type {
KeyType::BlockProduction => {
let address = Input::owner(&secret.public_key());
let output = json!({
json!({
"address": address.to_string(),
"type": "block-production",
});
print_value(output, self.pretty)
})
}
KeyType::Peering => {
let mut bytes = *secret.deref();
Expand All @@ -124,48 +119,11 @@ impl ParseSecret {
let p2p_keypair = secp256k1::Keypair::from(p2p_secret);
let libp2p_keypair = Keypair::from(p2p_keypair);
let peer_id = PeerId::from_public_key(&libp2p_keypair.public());
let output = json!({
json!({
"peer_id": peer_id.to_string(),
"type": P2P
});
print_value(output, self.pretty)
"type": <KeyType as std::convert::Into<&'static str>>::into(KeyType::Peering),
})
}
}
}
}

fn wait_for_keypress() {
let mut single_key = [0u8];
stdin().read_exact(&mut single_key).unwrap();
}

fn display_string_discreetly(
discreet_string: &str,
continue_message: &str,
) -> anyhow::Result<()> {
if atty::is(Stream::Stdout) {
let mut screen = stdout().into_alternate_screen()?;
writeln!(screen, "{discreet_string}")?;
screen.flush()?;
println!("{continue_message}");
wait_for_keypress();
} else {
println!("{discreet_string}");
})
}
Ok(())
}

fn print_value(output: serde_json::Value, pretty: bool) -> anyhow::Result<()> {
let output = if pretty {
serde_json::to_string_pretty(&output)
} else {
serde_json::to_string(&output)
}
.map_err(anyhow::Error::msg);

let _ = display_string_discreetly(
&output?,
"### Do not share or lose this private key! Press any key to complete. ###",
);
Ok(())
}
3 changes: 0 additions & 3 deletions crates/keygen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//! Keygen crate

pub const BLOCK_PRODUCTION: &str = "block-production";
pub const P2P: &str = "p2p";

pub mod keygen;

0 comments on commit a065b3f

Please sign in to comment.