use super::config::{KeyConfig, SERVICE};
use crate::framework::{Context, Module};
use crate::support::signer::SigningKeyExt;
use anyhow::{Context as _, Ok, Result};
use clap::Subcommand;
use cosmrs::bip32;
use cosmrs::bip32::secp256k1::elliptic_curve::rand_core::OsRng;
use cosmrs::crypto::secp256k1::SigningKey;
use dialoguer::Confirm;
use keyring::Entry;
#[derive(Subcommand, Debug)]
pub enum KeyCmd {
Set {
name: String,
mnemonic: String,
#[clap(short, long)]
yes: bool,
},
#[clap(alias = "del")]
Delete {
name: String,
#[clap(short, long)]
yes: bool,
},
#[clap(alias = "addr")]
Address {
name: String,
},
#[clap(alias = "gen")]
Generate {
name: String,
#[clap(long)]
show: bool,
#[clap(short, long)]
yes: bool,
},
}
pub struct KeyModule {}
impl<'a> Module<'a, KeyConfig, KeyCmd, anyhow::Error> for KeyModule {
fn execute<Ctx: Context<'a, KeyConfig>>(ctx: Ctx, cmd: &KeyCmd) -> Result<(), anyhow::Error> {
match cmd {
KeyCmd::Set {
name,
mnemonic,
yes,
} => {
let entry = keyring::Entry::new(SERVICE, name);
let global_config = ctx.global_config()?;
let derivation_path = global_config.derivation_path();
SigningKey::from_mnemonic(mnemonic, derivation_path)
.with_context(|| "Invalid phrase, if word length is not 24, please consider using 24-words mnemonic")?;
confirm_override(SERVICE, name, *yes)?;
entry
.set_password(mnemonic)
.with_context(|| "Unable to set key")
}
KeyCmd::Delete { name, yes } => {
let entry = keyring::Entry::new(SERVICE, name);
confirm_deletion(SERVICE, name, *yes)?;
entry
.delete_password()
.with_context(|| "Unable to delete key")
}
KeyCmd::Address { name } => {
let entry = keyring::Entry::new(SERVICE, name);
let global_config = ctx.global_config()?;
let mnemonic = entry.get_password()?;
let derivation_path = global_config.derivation_path();
let address = SigningKey::from_mnemonic(&mnemonic, derivation_path)?
.public_key()
.account_id(global_config.account_prefix())
.unwrap()
.to_string();
println!("{}", address);
Ok(())
}
KeyCmd::Generate { name, show, yes } => {
let mnemonic = bip32::Mnemonic::random(OsRng, bip32::Language::English);
let mnemonic = mnemonic.phrase();
let entry = keyring::Entry::new(SERVICE, name);
confirm_override(SERVICE, name, *yes)?;
if *show {
println!("{}", mnemonic);
}
entry
.set_password(mnemonic)
.with_context(|| "Unable to set key")
}
}
}
}
fn confirm_override(service: &str, name: &str, yes: bool) -> Result<bool, std::io::Error> {
let entry = Entry::new(service, name);
let exists = entry.get_password().is_ok();
if yes || !exists {
return core::result::Result::Ok(true);
}
Confirm::new()
.with_prompt(format!(
" > Key with name `{}` already exists. Do you want to override?",
name
))
.interact()
}
fn confirm_deletion(service: &str, name: &str, yes: bool) -> Result<bool, std::io::Error> {
let entry = Entry::new(service, name);
let exists = entry.get_password().is_ok();
if yes || !exists {
return core::result::Result::Ok(true);
}
Confirm::new()
.with_prompt(format!(" > Do you want to delete `{}`?", name))
.interact()
}