From a2dd3d386fe4f40701e3dbd3685fe2ba2ba4551c Mon Sep 17 00:00:00 2001 From: Warm Beer Date: Fri, 13 Dec 2024 12:38:40 +0100 Subject: [PATCH 1/5] feat: add CLI `wallet import ` command --- ant-cli/src/commands.rs | 21 +++++++++-- ant-cli/src/commands/wallet.rs | 68 +++++++++++++++++++++------------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/ant-cli/src/commands.rs b/ant-cli/src/commands.rs index a1d1fd487a..694942493d 100644 --- a/ant-cli/src/commands.rs +++ b/ant-cli/src/commands.rs @@ -158,9 +158,18 @@ pub enum WalletCmd { /// Optional flag to not add a password. #[clap(long, action)] no_password: bool, - /// Optional hex-encoded private key. - #[clap(long)] - private_key: Option, + /// Optional password to encrypt the wallet with. + #[clap(long, short)] + password: Option, + }, + + /// Import an existing wallet. + Import { + /// Hex-encoded private key. + private_key: String, + /// Optional flag to not add a password. + #[clap(long, action)] + no_password: bool, /// Optional password to encrypt the wallet with. #[clap(long, short)] password: Option, @@ -208,9 +217,13 @@ pub async fn handle_subcommand(opt: Opt) -> Result<()> { SubCmd::Wallet { command } => match command { WalletCmd::Create { no_password, + password, + } => wallet::create(no_password, password), + WalletCmd::Import { private_key, + no_password, password, - } => wallet::create(no_password, private_key, password), + } => wallet::import(private_key, no_password, password), WalletCmd::Balance => Ok(wallet::balance().await?), }, } diff --git a/ant-cli/src/commands/wallet.rs b/ant-cli/src/commands/wallet.rs index 3b31a873b2..de4c3c7eee 100644 --- a/ant-cli/src/commands/wallet.rs +++ b/ant-cli/src/commands/wallet.rs @@ -16,34 +16,35 @@ use prettytable::{Cell, Row, Table}; const WALLET_PASSWORD_REQUIRED: bool = false; -pub fn create( +pub fn create(no_password: bool, password: Option) -> Result<()> { + let maybe_encryption_password = maybe_request_password(no_password, password)?; + + let wallet_private_key = Wallet::random_private_key(); + + let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .expect("Infallible") + .address() + .to_string(); + + // Save the private key file + let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; + + println!("Wallet address: {wallet_address}"); + println!("Stored wallet in: {file_path:?}"); + + Ok(()) +} + +pub fn import( + wallet_private_key: String, no_password: bool, - private_key: Option, password: Option, ) -> Result<()> { - if no_password && password.is_some() { - return Err(eyre!( - "Only one of `--no-password` or `--password` may be specified" - )); - } - - // Set a password for encryption or not - let encryption_password: Option = match (no_password, password) { - (true, _) => None, - (false, Some(pass)) => Some(pass.to_owned()), - (false, None) => request_password(WALLET_PASSWORD_REQUIRED), - }; + // Validate imported key + Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .map_err(|_| eyre!("Please provide a valid private key in hex format"))?; - let wallet_private_key = if let Some(private_key) = private_key { - // Validate imported key - Wallet::new_from_private_key(DUMMY_NETWORK, &private_key) - .map_err(|_| eyre!("Please provide a valid secret key in hex format"))?; - - private_key - } else { - // Create a new key - Wallet::random_private_key() - }; + let maybe_encryption_password = maybe_request_password(no_password, password)?; let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) .expect("Infallible") @@ -51,7 +52,7 @@ pub fn create( .to_string(); // Save the private key file - let file_path = store_private_key(&wallet_private_key, encryption_password)?; + let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; println!("Wallet address: {wallet_address}"); println!("Stored wallet in: {file_path:?}"); @@ -83,3 +84,20 @@ pub async fn balance() -> Result<()> { Ok(()) } + +fn maybe_request_password(no_password: bool, password: Option) -> Result> { + if no_password && password.is_some() { + return Err(eyre!( + "Only one of `--no-password` or `--password` may be specified" + )); + } + + // Set a password for encryption or not + let maybe_password = match (no_password, password) { + (true, _) => None, + (false, Some(pass)) => Some(pass.to_owned()), + (false, None) => request_password(WALLET_PASSWORD_REQUIRED), + }; + + Ok(maybe_password) +} From c025b97205ee49eff7c207f24024affdde1bbe0c Mon Sep 17 00:00:00 2001 From: Warm Beer Date: Fri, 13 Dec 2024 13:09:00 +0100 Subject: [PATCH 2/5] feat: print wallet private key on CLI `wallet create` command --- ant-cli/src/commands/wallet.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ant-cli/src/commands/wallet.rs b/ant-cli/src/commands/wallet.rs index de4c3c7eee..46819bc19d 100644 --- a/ant-cli/src/commands/wallet.rs +++ b/ant-cli/src/commands/wallet.rs @@ -30,6 +30,7 @@ pub fn create(no_password: bool, password: Option) -> Result<()> { let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; println!("Wallet address: {wallet_address}"); + println!("Wallet private key: {wallet_private_key}"); println!("Stored wallet in: {file_path:?}"); Ok(()) From 7a08e279bd066a1eb5b48de68f7b55324bc0c070 Mon Sep 17 00:00:00 2001 From: Warm Beer Date: Fri, 13 Dec 2024 13:25:38 +0100 Subject: [PATCH 3/5] feat: add CLI `wallet export` command --- ant-cli/src/commands.rs | 6 +++++- ant-cli/src/commands/wallet.rs | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ant-cli/src/commands.rs b/ant-cli/src/commands.rs index 694942493d..6c6316d3cd 100644 --- a/ant-cli/src/commands.rs +++ b/ant-cli/src/commands.rs @@ -175,6 +175,9 @@ pub enum WalletCmd { password: Option, }, + /// Print the private key of a wallet. + Export, + /// Check the balance of the wallet. Balance, } @@ -224,7 +227,8 @@ pub async fn handle_subcommand(opt: Opt) -> Result<()> { no_password, password, } => wallet::import(private_key, no_password, password), - WalletCmd::Balance => Ok(wallet::balance().await?), + WalletCmd::Export => wallet::export(), + WalletCmd::Balance => wallet::balance().await, }, } } diff --git a/ant-cli/src/commands/wallet.rs b/ant-cli/src/commands/wallet.rs index 46819bc19d..de97c77fc9 100644 --- a/ant-cli/src/commands/wallet.rs +++ b/ant-cli/src/commands/wallet.rs @@ -6,7 +6,7 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use crate::wallet::fs::{select_wallet, store_private_key}; +use crate::wallet::fs::{select_wallet, select_wallet_private_key, store_private_key}; use crate::wallet::input::request_password; use crate::wallet::DUMMY_NETWORK; use autonomi::Wallet; @@ -61,6 +61,20 @@ pub fn import( Ok(()) } +pub fn export() -> Result<()> { + let wallet_private_key = select_wallet_private_key()?; + + let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .expect("Infallible") + .address() + .to_string(); + + println!("Wallet address: {wallet_address}"); + println!("Wallet private key: {wallet_private_key}"); + + Ok(()) +} + pub async fn balance() -> Result<()> { let wallet = select_wallet()?; From 123f5929253883afe4bc313d4da40c71e7c20887 Mon Sep 17 00:00:00 2001 From: Warm Beer Date: Fri, 13 Dec 2024 15:25:28 +0100 Subject: [PATCH 4/5] fix: error when decrypting wallet without 0x prefix --- ant-cli/src/commands/wallet.rs | 7 ++++++- ant-cli/src/wallet/encryption.rs | 5 +---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ant-cli/src/commands/wallet.rs b/ant-cli/src/commands/wallet.rs index de97c77fc9..b1a2caf70b 100644 --- a/ant-cli/src/commands/wallet.rs +++ b/ant-cli/src/commands/wallet.rs @@ -37,7 +37,7 @@ pub fn create(no_password: bool, password: Option) -> Result<()> { } pub fn import( - wallet_private_key: String, + mut wallet_private_key: String, no_password: bool, password: Option, ) -> Result<()> { @@ -52,6 +52,11 @@ pub fn import( .address() .to_string(); + // Prepend with 0x if it isn't already + if !wallet_private_key.starts_with("0x") { + wallet_private_key = format!("0x{wallet_private_key}"); + } + // Save the private key file let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; diff --git a/ant-cli/src/wallet/encryption.rs b/ant-cli/src/wallet/encryption.rs index bc673574ce..88f53afa15 100644 --- a/ant-cli/src/wallet/encryption.rs +++ b/ant-cli/src/wallet/encryption.rs @@ -123,11 +123,8 @@ pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result Date: Fri, 13 Dec 2024 16:04:42 +0100 Subject: [PATCH 5/5] fix: on duplicate wallet files (encrypted & plain), favour the plain one --- ant-cli/src/wallet/fs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ant-cli/src/wallet/fs.rs b/ant-cli/src/wallet/fs.rs index 136ddf5c4f..1cf4bb3284 100644 --- a/ant-cli/src/wallet/fs.rs +++ b/ant-cli/src/wallet/fs.rs @@ -82,7 +82,10 @@ pub(crate) fn load_private_key(wallet_address: &str) -> Result { let encrypted_file_path = wallets_folder.join(format!("{wallet_address}{ENCRYPTED_PRIVATE_KEY_EXT}")); - let is_encrypted = encrypted_file_path.exists(); + let is_plain = wallets_folder.join(&file_name).exists(); + + // Trick to favour the plain file in case they both exist + let is_encrypted = encrypted_file_path.exists() && !is_plain; if is_encrypted { file_name.push_str(ENCRYPTED_PRIVATE_KEY_EXT);