1use clap::{builder::RangedU64ValueParser, Parser};
37use hopr_crypto_types::keypairs::Keypair;
38use hopr_primitive_types::primitives::Address;
39use hoprd_keypair::key_pair::HoprKeys;
40use tracing::{debug, info};
41
42use crate::key_pair::{
43 create_identity, read_identities, read_identity, update_identity_password, ArgEnvReader, IdentityFileArgs,
44 NewPasswordArgs,
45};
46use crate::utils::HelperErrors::UnableToParseAddress;
47use crate::utils::{Cmd, HelperErrors};
48use hopr_crypto_types::prelude::{OffchainPublicKey, PeerId};
49use hopr_primitive_types::prelude::ToHex;
50use std::collections::HashMap;
51use std::str::FromStr;
52
53#[derive(Clone, Debug, Parser)]
55pub enum IdentitySubcommands {
56 #[command(visible_alias = "cr")]
58 Create {
59 #[command(flatten)]
61 local_identity: IdentityFileArgs,
62
63 #[clap(
65 help = "Number of identities to be generated, e.g. 1",
66 long,
67 short,
68 value_parser = RangedU64ValueParser::<u32>::new().range(1..),
69 default_value_t = 1
70 )]
71 number: u32,
72 },
73
74 #[command(visible_alias = "rd")]
76 Read {
77 #[command(flatten)]
79 local_identity: IdentityFileArgs,
80 },
81
82 #[command(visible_alias = "up")]
84 Update {
85 #[command(flatten)]
87 local_identity: IdentityFileArgs,
88
89 #[command(flatten)]
91 new_password: NewPasswordArgs,
92 },
93
94 #[command(visible_alias = "conv")]
96 ConvertPeer {
97 #[command(flatten)]
99 peer_or_key: ConvertPeerArgs,
100 },
101}
102
103#[derive(Debug, Clone, Parser, Default)]
105pub struct ConvertPeerArgs {
106 #[clap(
108 short,
109 long,
110 help = "PeerID in Base58 or public key as hex starting with 0x",
111 name = "peer_or_key",
112 value_name = "PEER_ID_OR_PUBLIC_KEY"
113 )]
114 pub peer_or_key: String,
115}
116
117impl IdentitySubcommands {
118 fn execute_identity_creation_loop(local_identity: IdentityFileArgs, number: u32) -> Result<(), HelperErrors> {
120 let pwd = local_identity.clone().password.read_default()?;
122
123 let mut node_identities: HashMap<String, HoprKeys> = HashMap::new();
124
125 match local_identity.identity_from_directory {
126 Some(local_id) => {
127 let id_dir = local_id
128 .identity_directory
129 .ok_or(HelperErrors::MissingIdentityDirectory)?;
130 for index in 0..=number - 1 {
131 let file_prefix = local_id
133 .identity_prefix
134 .as_ref()
135 .map(|provided_name| provided_name.to_owned() + &index.to_string());
136
137 let (id_filename, identity) = create_identity(&id_dir, &pwd, &file_prefix)
138 .map_err(|_| HelperErrors::UnableToCreateIdentity)?;
139 node_identities.insert(id_filename, identity);
140 }
141
142 info!("Identities: {:?}", node_identities);
143 Ok(())
144 }
145 None => Err(HelperErrors::MissingParameter(
146 "Missing identity_from_directory when creating identites".into(),
147 )),
148 }
149 }
150
151 fn execute_identity_read_loop(local_identity: IdentityFileArgs) -> Result<(), HelperErrors> {
153 let pwd = local_identity.clone().password.read_default()?;
155
156 let files = local_identity.get_files()?;
158 debug!("Identities read {:?}", files.len());
159
160 let node_identities: HashMap<String, HoprKeys> =
161 read_identities(files, &pwd).map_err(|_| HelperErrors::UnableToReadIdentity)?;
162
163 let node_addresses: Vec<Address> = node_identities
164 .values()
165 .map(|n| n.chain_key.public().to_address())
166 .collect();
167
168 let peer_ids: Vec<String> = node_identities
169 .values()
170 .map(|n| n.packet_key.public().to_peerid_str())
171 .collect();
172
173 info!("Identities: {:?}", node_identities);
174 println!("Identity addresses: {:?}", node_addresses);
175 println!("Identity peerids: [{}]", peer_ids.join(", "));
176 Ok(())
177 }
178
179 fn execute_identity_update(
181 local_identity: IdentityFileArgs,
182 new_password: NewPasswordArgs,
183 ) -> Result<(), HelperErrors> {
184 let pwd = local_identity.clone().password.read_default()?;
186 let new_pwd = new_password.read_default()?;
188
189 let files = local_identity.get_files()?;
191 debug!("Identities read {:?}", files.len());
192
193 let _ = files
194 .iter()
195 .map(|file| {
196 read_identity(file, &pwd)
197 .map_err(|_| HelperErrors::UnableToUpdateIdentityPassword)
198 .and_then(|(_, keys)| update_identity_password(keys, file, &new_pwd))
199 })
200 .collect::<Result<Vec<_>, _>>()?;
201
202 info!("Updated password for {:?} identity files", files.len());
203 Ok(())
204 }
205}
206
207impl Cmd for IdentitySubcommands {
208 fn run(self) -> Result<(), HelperErrors> {
210 match self {
211 IdentitySubcommands::Create { local_identity, number } => {
212 IdentitySubcommands::execute_identity_creation_loop(local_identity, number)
213 }
214 IdentitySubcommands::Read { local_identity } => {
215 IdentitySubcommands::execute_identity_read_loop(local_identity)
216 }
217 IdentitySubcommands::Update {
218 local_identity,
219 new_password,
220 } => IdentitySubcommands::execute_identity_update(local_identity, new_password),
221 IdentitySubcommands::ConvertPeer { peer_or_key } => {
222 if peer_or_key.peer_or_key.to_lowercase().starts_with("0x") {
223 let pk = OffchainPublicKey::from_hex(&peer_or_key.peer_or_key)
224 .map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key))?;
225 println!("{}", pk.to_peerid_str());
226 Ok(())
227 } else {
228 let pk = PeerId::from_str(&peer_or_key.peer_or_key)
229 .map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key.clone()))
230 .and_then(|p| {
231 OffchainPublicKey::try_from(p).map_err(|_| UnableToParseAddress(peer_or_key.peer_or_key))
232 })?;
233 println!("{}", pk.to_hex());
234 Ok(())
235 }
236 }
237 }
238 }
239
240 async fn async_run(self) -> Result<(), HelperErrors> {
241 Ok(())
242 }
243}