Skip to content

Commit

Permalink
chore: fix cli wallet error management
Browse files Browse the repository at this point in the history
  • Loading branch information
grumbach committed Jan 6, 2025
1 parent 924daff commit ce9e9a7
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 109 deletions.
2 changes: 1 addition & 1 deletion ant-bootstrap/src/initial_peers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub struct PeersArgs {
/// a bootstrap cache JSON file.
#[clap(long, conflicts_with = "first", value_delimiter = ',')]
pub network_contacts_url: Vec<String>,
/// Set to indicate this is a local network. You could also set the `local` feature flag to set this to true.
/// Set to indicate this is a local network.
///
/// This would use mDNS for peer discovery.
#[clap(long, conflicts_with = "network_contacts_url", default_value = "false")]
Expand Down
7 changes: 3 additions & 4 deletions ant-cli/src/access/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use crate::wallet::load_wallet_private_key;
use autonomi::client::registers::RegisterSecretKey;
use autonomi::client::vault::VaultSecretKey;
use autonomi::{get_evm_network_from_env, Wallet};
use autonomi::{Network, Wallet};
use color_eyre::eyre::{eyre, Context, Result};
use color_eyre::Section;
use std::env;
Expand All @@ -22,11 +22,10 @@ const REGISTER_SIGNING_KEY_ENV: &str = "REGISTER_SIGNING_KEY";
const REGISTER_SIGNING_KEY_FILE: &str = "register_signing_key";

/// EVM wallet
pub fn load_evm_wallet_from_env() -> Result<Wallet> {
pub fn load_evm_wallet_from_env(evm_network: &Network) -> Result<Wallet> {
let secret_key =
get_secret_key_from_env().wrap_err("The secret key is required to perform this action")?;
let network = get_evm_network_from_env()?;
let wallet = Wallet::new_from_private_key(network, &secret_key)
let wallet = Wallet::new_from_private_key(evm_network.clone(), &secret_key)
.wrap_err("Failed to load EVM wallet from key")?;
Ok(wallet)
}
Expand Down
2 changes: 1 addition & 1 deletion ant-cli/src/commands/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pub async fn cost(file: &str, peers: NetworkPeers) -> Result<()> {
}

pub async fn upload(file: &str, public: bool, peers: NetworkPeers) -> Result<()> {
let wallet = load_wallet()?;
let mut client = crate::actions::connect_to_network(peers).await?;
let wallet = load_wallet(&client.evm_network)?;
let event_receiver = client.enable_client_events();
let (upload_summary_thread, upload_completed_tx) = collect_upload_summary(event_receiver);

Expand Down
2 changes: 1 addition & 1 deletion ant-cli/src/commands/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ pub async fn cost(name: &str, peers: NetworkPeers) -> Result<()> {
}

pub async fn create(name: &str, value: &str, public: bool, peers: NetworkPeers) -> Result<()> {
let wallet = load_wallet()?;
let register_key = crate::keys::get_register_signing_key()
.wrap_err("The register key is required to perform this action")?;
let mut client = crate::actions::connect_to_network(peers).await?;
let wallet = load_wallet(&client.evm_network)?;
let event_receiver = client.enable_client_events();
let (upload_summary_thread, upload_completed_tx) = collect_upload_summary(event_receiver);

Expand Down
4 changes: 2 additions & 2 deletions ant-cli/src/commands/vault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub async fn cost(peers: NetworkPeers) -> Result<()> {

pub async fn create(peers: NetworkPeers) -> Result<()> {
let client = crate::actions::connect_to_network(peers).await?;
let wallet = load_wallet()?;
let wallet = load_wallet(&client.evm_network)?;
let vault_sk = crate::keys::get_vault_secret_key()?;

println!("Retrieving local user data...");
Expand Down Expand Up @@ -58,7 +58,7 @@ pub async fn create(peers: NetworkPeers) -> Result<()> {
pub async fn sync(force: bool, peers: NetworkPeers) -> Result<()> {
let client = crate::actions::connect_to_network(peers).await?;
let vault_sk = crate::keys::get_vault_secret_key()?;
let wallet = load_wallet()?;
let wallet = load_wallet(&client.evm_network)?;

println!("Fetching vault from network...");
let net_user_data = client
Expand Down
7 changes: 4 additions & 3 deletions ant-cli/src/commands/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
// 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, select_wallet_private_key, store_private_key};
use crate::wallet::fs::{select_wallet_private_key, store_private_key};
use crate::wallet::input::request_password;
use crate::wallet::DUMMY_NETWORK;
use autonomi::Wallet;
use autonomi::{get_evm_network_from_env, Wallet};
use color_eyre::eyre::eyre;
use color_eyre::Result;
use prettytable::{Cell, Row, Table};
Expand Down Expand Up @@ -81,7 +81,8 @@ pub fn export() -> Result<()> {
}

pub async fn balance() -> Result<()> {
let wallet = select_wallet()?;
let network = get_evm_network_from_env().unwrap_or_default();
let wallet = crate::wallet::load_wallet(&network)?;

let token_balance = wallet.balance_of_tokens().await?;
let gas_balance = wallet.balance_of_gas_tokens().await?;
Expand Down
23 changes: 11 additions & 12 deletions ant-cli/src/wallet/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
// 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::error::Error;
use color_eyre::eyre::eyre;
use color_eyre::Result;
use rand::Rng;
use ring::aead::{BoundKey, Nonce, NonceSequence};
use ring::error::Unspecified;
Expand All @@ -28,7 +29,7 @@ impl NonceSequence for NonceSeq {
}
}

pub fn encrypt_private_key(private_key: &str, password: &str) -> Result<String, Error> {
pub fn encrypt_private_key(private_key: &str, password: &str) -> Result<String> {
// Generate a random salt
// Salt is used to ensure unique derived keys even for identical passwords
let mut salt = [0u8; SALT_LENGTH];
Expand All @@ -55,7 +56,7 @@ pub fn encrypt_private_key(private_key: &str, password: &str) -> Result<String,
// Create an unbound key using CHACHA20_POLY1305 algorithm
// CHACHA20_POLY1305 is a fast and secure AEAD (Authenticated Encryption with Associated Data) algorithm
let unbound_key = ring::aead::UnboundKey::new(&ring::aead::CHACHA20_POLY1305, &key)
.map_err(|_| Error::FailedToEncryptKey(String::from("Could not create unbound key")))?;
.map_err(|_| eyre!("Failed to encrypt key: Could not create unbound key"))?;

// Create a sealing key with the unbound key and nonce
let mut sealing_key = ring::aead::SealingKey::new(unbound_key, NonceSeq(nonce));
Expand All @@ -68,7 +69,7 @@ pub fn encrypt_private_key(private_key: &str, password: &str) -> Result<String,
// seal_in_place_append_tag encrypts the data and appends an authentication tag to ensure data integrity
sealing_key
.seal_in_place_append_tag(aad, &mut encrypted_private_key)
.map_err(|_| Error::FailedToEncryptKey(String::from("Could not seal sealing key")))?;
.map_err(|_| eyre!("Failed to encrypt key: Could not seal sealing key"))?;

let mut encrypted_data = Vec::new();
encrypted_data.extend_from_slice(&salt);
Expand All @@ -79,17 +80,17 @@ pub fn encrypt_private_key(private_key: &str, password: &str) -> Result<String,
Ok(hex::encode(encrypted_data))
}

pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result<String, Error> {
pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result<String> {
let encrypted_data = hex::decode(encrypted_data)
.map_err(|_| Error::FailedToDecryptKey(String::from("Encrypted data is invalid")))?;
.map_err(|_| eyre!("Failed to decrypt key: Encrypted data is invalid"))?;

let salt: [u8; SALT_LENGTH] = encrypted_data[..SALT_LENGTH]
.try_into()
.map_err(|_| Error::FailedToDecryptKey(String::from("Could not find salt")))?;
.map_err(|_| eyre!("Failed to decrypt key: Could not find salt"))?;

let nonce: [u8; NONCE_LENGTH] = encrypted_data[SALT_LENGTH..SALT_LENGTH + NONCE_LENGTH]
.try_into()
.map_err(|_| Error::FailedToDecryptKey(String::from("Could not find nonce")))?;
.map_err(|_| eyre!("Failed to decrypt key: Could not find nonce"))?;

let encrypted_private_key = &encrypted_data[SALT_LENGTH + NONCE_LENGTH..];

Expand All @@ -106,7 +107,7 @@ pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result<Strin

// Create an unbound key from the previously reconstructed key
let unbound_key = ring::aead::UnboundKey::new(&ring::aead::CHACHA20_POLY1305, &key)
.map_err(|_| Error::FailedToDecryptKey(String::from("Could not create unbound key")))?;
.map_err(|_| eyre!("Failed to decrypt key: Could not create unbound key"))?;

// Create an opening key using the unbound key and original nonce
let mut opening_key = ring::aead::OpeningKey::new(unbound_key, NonceSeq(nonce));
Expand All @@ -118,9 +119,7 @@ pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result<Strin
let decrypted_data = opening_key
.open_in_place(aad, &mut encrypted_private_key)
.map_err(|_| {
Error::FailedToDecryptKey(String::from(
"Could not open encrypted key, please check the password",
))
eyre!("Failed to decrypt key: Could not open encrypted key, please check the password")
})?;

// Create secret key from decrypted byte
Expand Down
31 changes: 0 additions & 31 deletions ant-cli/src/wallet/error.rs

This file was deleted.

70 changes: 32 additions & 38 deletions ant-cli/src/wallet/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
// 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::keys::load_evm_wallet_from_env;
use crate::wallet::encryption::{decrypt_private_key, encrypt_private_key};
use crate::wallet::error::Error;
use crate::wallet::input::{get_password_input, get_wallet_selection_input};
use crate::wallet::DUMMY_NETWORK;
use autonomi::{get_evm_network_from_env, RewardsAddress, Wallet};
use autonomi::{Network, RewardsAddress, Wallet};
use color_eyre::eyre::{bail, eyre, Context};
use color_eyre::Result;
use const_hex::traits::FromHex;
use prettytable::{Cell, Row, Table};
use std::ffi::OsString;
Expand All @@ -24,13 +24,13 @@ const ENCRYPTED_PRIVATE_KEY_EXT: &str = ".encrypted";
pub static SELECTED_WALLET_ADDRESS: OnceLock<String> = OnceLock::new();

/// Creates the wallets folder if it is missing and returns the folder path.
pub(crate) fn get_client_wallet_dir_path() -> Result<PathBuf, Error> {
let mut home_dirs = dirs_next::data_dir().ok_or(Error::WalletsFolderNotFound)?;
home_dirs.push("autonomi");
home_dirs.push("client");
pub(crate) fn get_client_wallet_dir_path() -> Result<PathBuf> {
let mut home_dirs = crate::access::data_dir::get_client_data_dir_path()
.wrap_err("Failed to get wallet directory")?;
home_dirs.push("wallets");

std::fs::create_dir_all(home_dirs.as_path()).map_err(|_| Error::FailedToCreateWalletsFolder)?;
std::fs::create_dir_all(home_dirs.as_path())
.wrap_err("Failed to create wallets folder, make sure you have the correct permissions")?;

Ok(home_dirs)
}
Expand All @@ -41,9 +41,9 @@ pub(crate) fn get_client_wallet_dir_path() -> Result<PathBuf, Error> {
pub(crate) fn store_private_key(
private_key: &str,
encryption_password: Option<String>,
) -> Result<OsString, Error> {
) -> Result<OsString> {
let wallet = Wallet::new_from_private_key(DUMMY_NETWORK, private_key)
.map_err(|_| Error::InvalidPrivateKey)?;
.map_err(|_| eyre!("Private key is invalid"))?;

// Wallet address
let wallet_address = wallet.address().to_string();
Expand All @@ -56,15 +56,13 @@ pub(crate) fn store_private_key(
let file_name = format!("{wallet_address}{ENCRYPTED_PRIVATE_KEY_EXT}");
let file_path = wallets_folder.join(file_name);

std::fs::write(file_path.clone(), encrypted_key)
.map_err(|err| Error::FailedToStorePrivateKey(err.to_string()))?;
std::fs::write(file_path.clone(), encrypted_key).wrap_err("Failed to store private key")?;

Ok(file_path.into_os_string())
} else {
let file_path = wallets_folder.join(wallet_address);

std::fs::write(file_path.clone(), private_key)
.map_err(|err| Error::FailedToStorePrivateKey(err.to_string()))?;
std::fs::write(file_path.clone(), private_key).wrap_err("Failed to store private key")?;

Ok(file_path.into_os_string())
}
Expand All @@ -73,7 +71,7 @@ pub(crate) fn store_private_key(
/// Loads the private key (hex-encoded) from disk.
///
/// If the private key file is encrypted, the function will prompt for the decryption password in the CLI.
pub(crate) fn load_private_key(wallet_address: &str) -> Result<String, Error> {
pub(crate) fn load_private_key(wallet_address: &str) -> Result<String> {
let wallets_folder = get_client_wallet_dir_path()?;

let mut file_name = wallet_address.to_string();
Expand All @@ -93,46 +91,42 @@ pub(crate) fn load_private_key(wallet_address: &str) -> Result<String, Error> {

let file_path = wallets_folder.join(file_name);

let mut file = std::fs::File::open(&file_path).map_err(|_| Error::PrivateKeyFileNotFound)?;
let mut file =
std::fs::File::open(&file_path).map_err(|e| eyre!("Private key file not found: {e}"))?;

let mut buffer = String::new();
file.read_to_string(&mut buffer)
.map_err(|_| Error::InvalidPrivateKeyFile)?;
.map_err(|_| eyre!("Invalid private key file"))?;

// If the file is encrypted, prompt for the password and decrypt the key.
if is_encrypted {
let password = get_password_input("Enter password to decrypt wallet:");

decrypt_private_key(&buffer, &password)
.map_err(|e| eyre!("Failed to decrypt private key: {e}"))
} else {
Ok(buffer)
}
}

pub(crate) fn load_wallet_from_address(wallet_address: &str) -> Result<Wallet, Error> {
let network = get_evm_network_from_env().expect("Could not load EVM network from environment");
pub(crate) fn load_wallet_from_address(wallet_address: &str, network: &Network) -> Result<Wallet> {
let private_key = load_private_key(wallet_address)?;
let wallet =
Wallet::new_from_private_key(network, &private_key).expect("Could not initialize wallet");
let wallet = Wallet::new_from_private_key(network.clone(), &private_key)
.map_err(|e| eyre!("Could not initialize wallet: {e}"))?;
Ok(wallet)
}

pub(crate) fn select_wallet() -> Result<Wallet, Error> {
// try if there is a wallet set in the ENV first
if let Ok(env_wallet) = load_evm_wallet_from_env() {
return Ok(env_wallet);
}

let wallet_address = select_wallet_address()?;
load_wallet_from_address(&wallet_address)
pub(crate) fn select_wallet_from_disk(network: &Network) -> Result<Wallet> {
let wallet_address = select_local_wallet_address()?;
load_wallet_from_address(&wallet_address, network)
}

pub(crate) fn select_wallet_private_key() -> Result<String, Error> {
let wallet_address = select_wallet_address()?;
pub(crate) fn select_wallet_private_key() -> Result<String> {
let wallet_address = select_local_wallet_address()?;
load_private_key(&wallet_address)
}

pub(crate) fn select_wallet_address() -> Result<String, Error> {
pub(crate) fn select_local_wallet_address() -> Result<String> {
// Try if a wallet address was already selected this session
if let Some(wallet_address) = SELECTED_WALLET_ADDRESS.get() {
return Ok(wallet_address.clone());
Expand All @@ -142,7 +136,7 @@ pub(crate) fn select_wallet_address() -> Result<String, Error> {
let wallet_files = get_wallet_files(&wallets_folder)?;

let wallet_address = match wallet_files.len() {
0 => Err(Error::NoWalletsFound),
0 => bail!("No local wallets found."),
1 => Ok(filter_wallet_file_extension(&wallet_files[0])),
_ => get_wallet_selection(wallet_files),
}?;
Expand All @@ -152,15 +146,15 @@ pub(crate) fn select_wallet_address() -> Result<String, Error> {
.to_string())
}

fn get_wallet_selection(wallet_files: Vec<String>) -> Result<String, Error> {
fn get_wallet_selection(wallet_files: Vec<String>) -> Result<String> {
list_wallets(&wallet_files);

let selected_index = get_wallet_selection_input("Select by index:")
.parse::<usize>()
.map_err(|_| Error::InvalidSelection)?;
.map_err(|_| eyre!("Invalid wallet selection input"))?;

if selected_index < 1 || selected_index > wallet_files.len() {
return Err(Error::InvalidSelection);
bail!("Invalid wallet selection input");
}

Ok(filter_wallet_file_extension(
Expand Down Expand Up @@ -192,9 +186,9 @@ fn list_wallets(wallet_files: &[String]) {
table.printstd();
}

fn get_wallet_files(wallets_folder: &PathBuf) -> Result<Vec<String>, Error> {
fn get_wallet_files(wallets_folder: &PathBuf) -> Result<Vec<String>> {
let wallet_files = std::fs::read_dir(wallets_folder)
.map_err(|_| Error::WalletsFolderNotFound)?
.map_err(|e| eyre!("Failed to read wallets folder: {e}"))?
.filter_map(Result::ok)
.filter_map(|dir_entry| dir_entry.file_name().into_string().ok())
.filter(|file_name| {
Expand Down
Loading

0 comments on commit ce9e9a7

Please sign in to comment.