2025-09-05 22:50:10 -03:00
|
|
|
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
2025-10-03 01:30:40 -03:00
|
|
|
use nssa_core::account::Nonce;
|
|
|
|
|
use rand::{RngCore, rngs::OsRng};
|
2025-08-07 14:07:34 +03:00
|
|
|
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
|
|
|
|
|
|
2025-08-13 13:42:00 +03:00
|
|
|
use anyhow::Result;
|
2025-08-20 17:16:51 +03:00
|
|
|
use key_protocol::key_protocol_core::NSSAUserData;
|
2025-10-03 17:07:15 -03:00
|
|
|
use nssa::Account;
|
2025-09-05 22:50:10 -03:00
|
|
|
use serde::Serialize;
|
2025-08-07 14:07:34 +03:00
|
|
|
|
2025-08-19 14:14:09 +03:00
|
|
|
use crate::{
|
|
|
|
|
HOME_DIR_ENV_VAR,
|
2025-09-11 18:32:46 +03:00
|
|
|
config::{
|
|
|
|
|
PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic,
|
|
|
|
|
WalletConfig,
|
|
|
|
|
},
|
2025-08-19 14:14:09 +03:00
|
|
|
};
|
2025-08-07 14:07:34 +03:00
|
|
|
|
2025-09-05 22:50:10 -03:00
|
|
|
/// Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed.
|
2025-08-07 14:07:34 +03:00
|
|
|
pub fn get_home() -> Result<PathBuf> {
|
|
|
|
|
Ok(PathBuf::from_str(&std::env::var(HOME_DIR_ENV_VAR)?)?)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 22:50:10 -03:00
|
|
|
/// Fetch config from `NSSA_WALLET_HOME_DIR`
|
2025-08-11 08:55:08 +03:00
|
|
|
pub fn fetch_config() -> Result<WalletConfig> {
|
2025-08-07 14:07:34 +03:00
|
|
|
let config_home = get_home()?;
|
2025-08-11 08:55:08 +03:00
|
|
|
let file = File::open(config_home.join("wallet_config.json"))?;
|
2025-08-07 14:07:34 +03:00
|
|
|
let reader = BufReader::new(file);
|
|
|
|
|
|
|
|
|
|
Ok(serde_json::from_reader(reader)?)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 22:50:10 -03:00
|
|
|
/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json`
|
2025-08-08 15:22:04 +03:00
|
|
|
///
|
|
|
|
|
/// If file not present, it is considered as empty list of persistent accounts
|
2025-09-02 09:01:33 +03:00
|
|
|
pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
|
2025-08-08 15:22:04 +03:00
|
|
|
let home = get_home()?;
|
|
|
|
|
let accs_path = home.join("curr_accounts.json");
|
|
|
|
|
|
|
|
|
|
match File::open(accs_path) {
|
|
|
|
|
Ok(file) => {
|
|
|
|
|
let reader = BufReader::new(file);
|
|
|
|
|
Ok(serde_json::from_reader(reader)?)
|
|
|
|
|
}
|
|
|
|
|
Err(err) => match err.kind() {
|
|
|
|
|
std::io::ErrorKind::NotFound => Ok(vec![]),
|
|
|
|
|
_ => {
|
|
|
|
|
anyhow::bail!("IO error {err:#?}");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-20 17:16:51 +03:00
|
|
|
|
2025-09-05 22:50:10 -03:00
|
|
|
/// Produces a list of accounts for storage
|
2025-09-02 09:01:33 +03:00
|
|
|
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> {
|
2025-08-20 17:16:51 +03:00
|
|
|
let mut vec_for_storage = vec![];
|
|
|
|
|
|
2025-09-08 15:03:02 +03:00
|
|
|
for (addr, key) in &user_data.pub_account_signing_keys {
|
2025-09-11 18:32:46 +03:00
|
|
|
vec_for_storage.push(
|
|
|
|
|
PersistentAccountDataPublic {
|
|
|
|
|
address: *addr,
|
|
|
|
|
pub_sign_key: key.clone(),
|
|
|
|
|
}
|
|
|
|
|
.into(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (addr, (key, acc)) in &user_data.user_private_accounts {
|
|
|
|
|
vec_for_storage.push(
|
|
|
|
|
PersistentAccountDataPrivate {
|
|
|
|
|
address: *addr,
|
|
|
|
|
account: acc.clone(),
|
|
|
|
|
key_chain: key.clone(),
|
|
|
|
|
}
|
|
|
|
|
.into(),
|
|
|
|
|
);
|
2025-08-20 17:16:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec_for_storage
|
|
|
|
|
}
|
2025-08-26 09:16:46 +03:00
|
|
|
|
2025-10-03 01:30:40 -03:00
|
|
|
pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
|
|
|
|
|
let mut result = vec![[0; 16]; size];
|
|
|
|
|
result.iter_mut().for_each(|bytes| OsRng.fill_bytes(bytes));
|
2025-10-03 08:08:54 -03:00
|
|
|
result.into_iter().map(Nonce::from_le_bytes).collect()
|
2025-10-03 01:30:40 -03:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 22:50:10 -03:00
|
|
|
/// Human-readable representation of an account.
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
|
pub(crate) struct HumanReadableAccount {
|
|
|
|
|
balance: u128,
|
|
|
|
|
program_owner_b64: String,
|
|
|
|
|
data_b64: String,
|
|
|
|
|
nonce: u128,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Account> for HumanReadableAccount {
|
|
|
|
|
fn from(account: Account) -> Self {
|
|
|
|
|
let program_owner_b64 = BASE64.encode(bytemuck::cast_slice(&account.program_owner));
|
|
|
|
|
let data_b64 = BASE64.encode(account.data);
|
|
|
|
|
Self {
|
|
|
|
|
balance: account.balance,
|
|
|
|
|
program_owner_b64,
|
|
|
|
|
data_b64,
|
|
|
|
|
nonce: account.nonce,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 09:16:46 +03:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_get_home_get_env_var() {
|
2025-09-04 14:38:41 +03:00
|
|
|
unsafe {
|
|
|
|
|
std::env::set_var(HOME_DIR_ENV_VAR, "/path/to/configs");
|
|
|
|
|
}
|
2025-08-26 09:16:46 +03:00
|
|
|
|
|
|
|
|
let home = get_home().unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(PathBuf::from_str("/path/to/configs").unwrap(), home);
|
|
|
|
|
|
2025-09-04 14:38:41 +03:00
|
|
|
unsafe {
|
|
|
|
|
std::env::remove_var(HOME_DIR_ENV_VAR);
|
|
|
|
|
}
|
2025-08-26 09:16:46 +03:00
|
|
|
}
|
|
|
|
|
}
|