use clap::{builder::RangedU64ValueParser, Parser};
use hopr_crypto_types::keypairs::Keypair;
use hopr_primitive_types::primitives::Address;
use hoprd_keypair::key_pair::HoprKeys;
use tracing::{debug, info};
use crate::key_pair::{
create_identity, read_identities, read_identity, update_identity_password, ArgEnvReader, IdentityFileArgs,
NewPasswordArgs,
};
use crate::utils::HelperErrors::UnableToParseAddress;
use crate::utils::{Cmd, HelperErrors};
use hopr_crypto_types::prelude::{OffchainPublicKey, PeerId};
use hopr_primitive_types::prelude::ToHex;
use std::collections::HashMap;
use std::str::FromStr;
#[derive(Clone, Debug, Parser)]
pub enum IdentitySubcommands {
#[command(visible_alias = "cr")]
Create {
#[command(flatten)]
local_identity: IdentityFileArgs,
#[clap(
help = "Number of identities to be generated, e.g. 1",
long,
short,
value_parser = RangedU64ValueParser::<u32>::new().range(1..),
default_value_t = 1
)]
number: u32,
},
#[command(visible_alias = "rd")]
Read {
#[command(flatten)]
local_identity: IdentityFileArgs,
},
#[command(visible_alias = "up")]
Update {
#[command(flatten)]
local_identity: IdentityFileArgs,
#[command(flatten)]
new_password: NewPasswordArgs,
},
#[command(visible_alias = "conv")]
ConvertPeer {
#[command(flatten)]
peer_or_key: ConvertPeerArgs,
},
}
#[derive(Debug, Clone, Parser, Default)]
pub struct ConvertPeerArgs {
#[clap(
short,
long,
help = "PeerID in Base58 or public key as hex starting with 0x",
name = "peer_or_key",
value_name = "PEER_ID_OR_PUBLIC_KEY"
)]
pub peer_or_key: String,
}
impl IdentitySubcommands {
fn execute_identity_creation_loop(local_identity: IdentityFileArgs, number: u32) -> Result<(), HelperErrors> {
let pwd = local_identity.clone().password.read_default()?;
let mut node_identities: HashMap<String, HoprKeys> = HashMap::new();
match local_identity.identity_from_directory {
Some(local_id) => {
let id_dir = local_id
.identity_directory
.ok_or(HelperErrors::MissingIdentityDirectory)?;
for index in 0..=number - 1 {
let file_prefix = local_id
.identity_prefix
.as_ref()
.map(|provided_name| provided_name.to_owned() + &index.to_string());
let (id_filename, identity) = create_identity(&id_dir, &pwd, &file_prefix)
.map_err(|_| HelperErrors::UnableToCreateIdentity)?;
node_identities.insert(id_filename, identity);
}
info!("Identities: {:?}", node_identities);
Ok(())
}
None => Err(HelperErrors::MissingParameter(
"Missing identity_from_directory when creating identites".into(),
)),
}
}
fn execute_identity_read_loop(local_identity: IdentityFileArgs) -> Result<(), HelperErrors> {
let pwd = local_identity.clone().password.read_default()?;
let files = local_identity.get_files()?;
debug!("Identities read {:?}", files.len());
let node_identities: HashMap<String, HoprKeys> =
read_identities(files, &pwd).map_err(|_| HelperErrors::UnableToReadIdentity)?;
let node_addresses: Vec<Address> = node_identities
.values()
.map(|n| n.chain_key.public().to_address())
.collect();
let peer_ids: Vec<String> = node_identities
.values()
.map(|n| n.packet_key.public().to_peerid_str())
.collect();
info!("Identities: {:?}", node_identities);
println!("Identity addresses: {:?}", node_addresses);
println!("Identity peerids: [{}]", peer_ids.join(", "));
Ok(())
}
fn execute_identity_update(
local_identity: IdentityFileArgs,
new_password: NewPasswordArgs,
) -> Result<(), HelperErrors> {
let pwd = local_identity.clone().password.read_default()?;
let new_pwd = new_password.read_default()?;
let files = local_identity.get_files()?;
debug!("Identities read {:?}", files.len());
let _ = files
.iter()
.map(|file| {
read_identity(file, &pwd)
.map_err(|_| HelperErrors::UnableToUpdateIdentityPassword)
.and_then(|(_, keys)| update_identity_password(keys, file, &new_pwd))
})
.collect::<Result<Vec<_>, _>>()?;
info!("Updated password for {:?} identity files", files.len());
Ok(())
}
}
impl Cmd for IdentitySubcommands {
fn run(self) -> Result<(), HelperErrors> {
match self {
IdentitySubcommands::Create { local_identity, number } => {
IdentitySubcommands::execute_identity_creation_loop(local_identity, number)
}
IdentitySubcommands::Read { local_identity } => {
IdentitySubcommands::execute_identity_read_loop(local_identity)
}
IdentitySubcommands::Update {
local_identity,
new_password,
} => IdentitySubcommands::execute_identity_update(local_identity, new_password),
IdentitySubcommands::ConvertPeer { peer_or_key } => {
if peer_or_key.peer_or_key.to_lowercase().starts_with("0x") {
let pk = OffchainPublicKey::from_hex(&peer_or_key.peer_or_key)
.map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key))?;
println!("{}", pk.to_peerid_str());
Ok(())
} else {
let pk = PeerId::from_str(&peer_or_key.peer_or_key)
.map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key.clone()))
.and_then(|p| {
OffchainPublicKey::try_from(p).map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key))
})?;
println!("{}", pk.to_hex());
Ok(())
}
}
}
}
async fn async_run(self) -> Result<(), HelperErrors> {
Ok(())
}
}