Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ant-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ criterion = "0.5.1"
eyre = "0.6.8"
rand = { version = "~0.8.5", features = ["small_rng"] }
rayon = "1.8.0"
serial_test = "3.0"
tempfile = "3.6.0"

[lints]
Expand Down
2 changes: 1 addition & 1 deletion ant-cli/src/access/cached_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
const PAYMENT_EXPIRATION_SECS: u64 = 3600 * 24 * 30;

pub fn get_payments_dir() -> Result<PathBuf> {
let dir = super::data_dir::get_client_data_dir_path()?;
let dir = super::data_dir::get_client_data_dir_base()?;
let payments_dir = dir.join("payments");
std::fs::create_dir_all(&payments_dir)
.wrap_err("Could not create cached payments directory")?;
Expand Down
111 changes: 107 additions & 4 deletions ant-cli/src/access/data_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,126 @@
// 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 autonomi::Wallet;
use color_eyre::{
Section,
eyre::{Context, Result, eyre},
};
use std::path::PathBuf;
use thiserror::Error;

pub fn get_client_data_dir_path() -> Result<PathBuf> {
#[derive(Debug, Clone, Error)]
pub enum DataDirError {
#[error(
"Multiple accounts found: {0:?}. Please specify which account to use or provide the SECRET_KEY for the account you want to use"
)]
MultipleAccounts(Vec<String>),
#[error(
"No existing user data directories found. Please provide the SECRET_KEY for the account you want to use"
)]
NoExistingUserDirFound,
}

/// Get the client data directory path. This is the directory that contains the user data for ALL users.
pub fn get_client_data_dir_base() -> Result<PathBuf> {
let mut home_dirs = dirs_next::data_dir()
.ok_or_else(|| eyre!("Failed to obtain data dir, your OS might not be supported."))?;
home_dirs.push("autonomi");
home_dirs.push("client");
std::fs::create_dir_all(home_dirs.as_path())
Ok(home_dirs)
}

/// Get the client data directory path. This is the directory that contains the user data for a SINGLE user.
/// For the general data directory case, use [`get_client_data_dir_base`] instead.
/// Automatically detects the wallet directory to use:
/// - if the SECRET_KEY is available, uses it to get the wallet address
/// - if only one user directory exists, uses it
/// - if multiple user directories exist, returns error
/// - if no user directories exist, returns error
pub fn get_client_user_data_dir() -> Result<PathBuf> {
let base_dir = get_client_data_dir_base()?;

// Get the wallet address to use
let wallet_addr = match get_wallet_pk() {
Ok(pk) => pk,
Err(_) => get_wallet_addr_from_existing_user_dirs()?,
};

// Migrate legacy data if needed (user data stored directly under client/ without wallet address)
super::data_dir_migration::migrate_legacy_data_if_needed(&wallet_addr)?;

// Create the wallet directory
let mut wallet_dir = base_dir;
wallet_dir.push(&wallet_addr);
std::fs::create_dir_all(wallet_dir.as_path())
.wrap_err("Failed to create data dir")
.with_suggestion(|| {
format!(
"make sure you have the correct permissions to access the data dir: {home_dirs:?}"
"make sure you have the correct permissions to access the data dir: {wallet_dir:?}"
)
})?;
Ok(home_dirs)

Ok(wallet_dir)
}

fn get_wallet_addr_from_existing_user_dirs() -> Result<String> {
// Check if there are any existing accounts user data directories
let existing_users = get_existing_user_dirs()?;

match &existing_users[..] {
// Exactly one account exists, use it
[one] => Ok(one.clone()),
// No accounts exist yet, try to get address from current environment
// First try from SECRET_KEY env var
[] => Err(DataDirError::NoExistingUserDirFound.into()),
// Multiple wallets exist, try SECRET_KEY env var else return error
[_, ..] => Err(DataDirError::MultipleAccounts(existing_users).into()),
}
}

/// Get existing wallet directories under the client data dir
fn get_existing_user_dirs() -> Result<Vec<String>> {
let base_dir = get_client_data_dir_base()?;

if !base_dir.exists() {
return Ok(Vec::new());
}

let mut wallet_dirs = Vec::new();

if let Ok(entries) = std::fs::read_dir(&base_dir) {
for entry in entries.flatten() {
if entry.path().is_dir() {
let dir_name = entry.file_name().to_string_lossy().to_string();
// Check if it looks like a wallet address (starts with 0x and has the right length)
if dir_name.starts_with("0x") && dir_name.len() == 42 {
wallet_dirs.push(dir_name);
}
}
}
}

Ok(wallet_dirs)
}

/// Get all existing account data directory paths
/// Returns a vector of (wallet_address, path) tuples
pub fn get_all_client_data_dir_paths() -> Result<Vec<(String, PathBuf)>> {
let base_dir = get_client_data_dir_base()?;
let existing_users = get_existing_user_dirs()?;

let mut paths = Vec::new();
for user in existing_users {
let path = base_dir.join(&user);
paths.push((user, path));
}

Ok(paths)
}

fn get_wallet_pk() -> Result<String> {
let secret_key = crate::wallet::load_wallet_private_key()?;
let wallet = Wallet::new_from_private_key(crate::wallet::DUMMY_NETWORK, &secret_key)
.map_err(|_| eyre!("Invalid SECRET_KEY provided"))?;
Ok(wallet.address().to_string())
}
Loading
Loading