fix: cli full refactor

This commit is contained in:
Oleksandr Pravdyvyi 2025-10-28 16:02:30 +02:00
parent b3e4b9a8ca
commit 5840f9b779
No known key found for this signature in database
GPG Key ID: 9F8955C63C443871
11 changed files with 324 additions and 316 deletions

View File

@ -8,3 +8,5 @@ pub mod transaction;
//TODO: Compile only for tests
pub mod test_utils;
pub type HashType = [u8; 32];
pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7";

View File

@ -43,11 +43,11 @@ pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin");
fn make_public_account_input_from_str(addr: &str) -> String {
format!("Public/{addr:?}")
format!("Public/{addr}")
}
fn make_private_account_input_from_str(addr: &str) -> String {
format!("Private/{addr:?}")
format!("Private/{addr}")
}
#[allow(clippy::type_complexity)]
@ -92,7 +92,7 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
seq_http_server_handle.stop(true).await;
let wallet_home = wallet::helperfunctions::get_home().unwrap();
let persistent_data_home = wallet_home.join("curr_accounts.json");
let persistent_data_home = wallet_home.join("storage.json");
//Removing persistent accounts after run to not affect other executions
//Not necessary an error, if fails as there is tests for failure scenario
@ -163,3 +163,20 @@ async fn verify_commitment_is_in_state(
Ok(Some(_))
)
}
#[cfg(test)]
mod tests {
use crate::{make_private_account_input_from_str, make_public_account_input_from_str};
#[test]
fn correct_addr_from_prefix() {
let addr1 = "cafecafe";
let addr2 = "deadbeaf";
let addr1_pub = make_public_account_input_from_str(addr1);
let addr2_priv = make_private_account_input_from_str(addr2);
assert_eq!(addr1_pub, "Public/cafecafe".to_string());
assert_eq!(addr2_priv, "Private/deadbeaf".to_string());
}
}

View File

@ -1,25 +1,21 @@
use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration};
use common::sequencer_client::SequencerClient;
use common::{PINATA_BASE58, sequencer_client::SequencerClient};
use log::info;
use nssa::{Address, ProgramDeploymentTransaction, program::Program};
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
use wallet::{
Command, SubcommandReturnValue, WalletCore,
cli::{
account::{AccountSubcommand, FetchSubcommand, NewSubcommand},
account::{AccountSubcommand, NewSubcommand},
native_token_transfer_program::AuthTransferSubcommand,
pinata_program::{
PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic,
},
pinata_program::PinataProgramAgnosticSubcommand,
token_program::TokenProgramAgnosticSubcommand,
},
config::PersistentAccountData,
helperfunctions::{fetch_config, fetch_persistent_accounts},
config::{PersistentAccountData, PersistentStorage},
helperfunctions::{fetch_config, fetch_persistent_storage},
};
use sequencer_core::sequencer_store::PINATA_BASE58;
use crate::{
ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE,
NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS,
@ -83,7 +79,10 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
wallet::execute_subcommand(command).await.unwrap();
let persistent_accounts = fetch_persistent_accounts().await.unwrap();
let PersistentStorage {
accounts: persistent_accounts,
last_synced_block: _,
} = fetch_persistent_storage().await.unwrap();
let mut new_persistent_account_addr = String::new();
@ -290,7 +289,10 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
.await
.unwrap();
let persistent_accounts = fetch_persistent_accounts().await.unwrap();
let PersistentStorage {
accounts: persistent_accounts,
last_synced_block: _,
} = fetch_persistent_storage().await.unwrap();
let mut new_persistent_accounts_addr = Vec::new();
@ -668,7 +670,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
amount: 7,
};
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } =
wallet::execute_subcommand(Command::Token(subcommand))
.await
.unwrap()
@ -679,11 +681,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr: recipient_addr.to_string(),
output_id: 1,
}));
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::execute_subcommand(command).await.unwrap();
@ -1096,11 +1094,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await;
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr: to_addr.to_string(),
output_id: 1,
}));
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
@ -1335,13 +1329,10 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
let pinata_addr = PINATA_BASE58;
let pinata_prize = 150;
let solution = 989106;
let command = Command::Pinata(PinataProgramSubcommand::Public(
PinataProgramSubcommandPublic::Claim {
pinata_addr: pinata_addr.to_string(),
winner_addr: ACC_SENDER.to_string(),
solution,
},
));
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_addr: make_public_account_input_from_str(ACC_SENDER),
solution,
});
let wallet_config = fetch_config().await.unwrap();
@ -1427,14 +1418,18 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
#[nssa_integration_test]
pub async fn test_authenticated_transfer_initialize_function() {
info!("test initialize account for authenticated transfer");
let command = Command::AuthenticatedTransferInitializePublicAccount {};
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {}));
let SubcommandReturnValue::RegisterAccount { addr } =
wallet::execute_subcommand(command).await.unwrap()
else {
panic!("Error creating account");
};
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
addr: addr.to_string(),
});
wallet::execute_subcommand(command).await.unwrap();
info!("Checking correct execution");
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -1463,13 +1458,10 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
let pinata_prize = 150;
let solution = 989106;
let command = Command::Pinata(PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr: pinata_addr.to_string(),
winner_addr: ACC_SENDER_PRIVATE.to_string(),
solution,
},
));
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_addr: make_private_account_input_from_str(ACC_SENDER_PRIVATE),
solution,
});
let wallet_config = fetch_config().await.unwrap();
@ -1481,7 +1473,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
.unwrap()
.balance;
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } =
wallet::execute_subcommand(command).await.unwrap()
else {
panic!("invalid subcommand return value");
@ -1497,11 +1489,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
.unwrap()
.balance;
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash: tx_hash.clone(),
acc_addr: ACC_SENDER_PRIVATE.to_string(),
output_id: 0,
}));
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::execute_subcommand(command).await.unwrap();
let wallet_config = fetch_config().await.unwrap();
@ -1538,13 +1526,10 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
panic!("invalid subcommand return value");
};
let command = Command::Pinata(PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr: pinata_addr.to_string(),
winner_addr: winner_addr.to_string(),
solution,
},
));
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_addr: make_private_account_input_from_str(&winner_addr.to_string()),
solution,
});
let wallet_config = fetch_config().await.unwrap();

View File

@ -9,8 +9,6 @@ use crate::config::AccountInitialData;
pub mod block_store;
pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7";
pub struct SequecerChainStore {
pub state: nssa::V02State,
pub block_store: SequecerBlockStore,
@ -35,6 +33,8 @@ impl SequecerChainStore {
#[cfg(feature = "testnet")]
let state = {
use common::PINATA_BASE58;
let mut this =
nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments);
this.add_pinata_program(PINATA_BASE58.parse().unwrap());

View File

@ -1,7 +1,6 @@
use anyhow::Result;
use base58::ToBase58;
use clap::Subcommand;
use common::transaction::NSSATransaction;
use nssa::{Address, program::Program};
use serde::Serialize;
@ -9,6 +8,7 @@ use crate::{
SubcommandReturnValue, WalletCore,
cli::WalletSubcommand,
helperfunctions::{AddressPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix},
parse_block_range,
};
const TOKEN_DEFINITION_TYPE: u8 = 0;
@ -76,9 +76,6 @@ pub enum AccountSubcommand {
#[arg(short, long)]
addr: String,
},
///Fetch
#[command(subcommand)]
Fetch(FetchSubcommand),
///New
#[command(subcommand)]
New(NewSubcommand),
@ -86,28 +83,6 @@ pub enum AccountSubcommand {
SyncPrivate {},
}
///Represents generic getter CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum FetchSubcommand {
///Fetch transaction by `hash`
Tx {
#[arg(short, long)]
tx_hash: String,
},
///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id`
PrivateAccount {
///tx_hash - valid 32 byte hex string
#[arg(long)]
tx_hash: String,
///acc_addr - valid 32 byte hex string
#[arg(long)]
acc_addr: String,
///output_id - id of the output in the transaction
#[arg(long)]
output_id: usize,
},
}
///Represents generic register CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum NewSubcommand {
@ -117,74 +92,6 @@ pub enum NewSubcommand {
Private {},
}
impl WalletSubcommand for FetchSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
FetchSubcommand::Tx { tx_hash } => {
let tx_obj = wallet_core
.sequencer_client
.get_transaction_by_hash(tx_hash)
.await?;
println!("Transaction object {tx_obj:#?}");
Ok(SubcommandReturnValue::Empty)
}
FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr,
output_id: ciph_id,
} => {
let acc_addr: Address = acc_addr.parse().unwrap();
let account_key_chain = wallet_core
.storage
.user_data
.user_private_accounts
.get(&acc_addr);
let Some((account_key_chain, _)) = account_key_chain else {
anyhow::bail!("Account not found");
};
let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone();
let to_comm = tx.message.new_commitments[ciph_id].clone();
let shared_secret =
account_key_chain.calculate_shared_secret_receiver(to_ebc.epk);
let res_acc_to = nssa_core::EncryptionScheme::decrypt(
&to_ebc.ciphertext,
&shared_secret,
&to_comm,
ciph_id as u32,
)
.unwrap();
println!("RES acc to {res_acc_to:#?}");
println!("Transaction data is {:?}", tx.message);
wallet_core
.storage
.insert_private_account_data(acc_addr, res_acc_to);
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::Empty)
}
}
}
}
impl WalletSubcommand for NewSubcommand {
async fn handle_subcommand(
self,
@ -196,7 +103,7 @@ impl WalletSubcommand for NewSubcommand {
println!("Generated new account with addr {addr}");
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -221,7 +128,7 @@ impl WalletSubcommand for NewSubcommand {
hex::encode(key.incoming_viewing_public_key.to_bytes())
);
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -287,6 +194,8 @@ impl WalletSubcommand for AccountSubcommand {
AccountSubcommand::Get { raw, addr } => {
let (addr, addr_kind) = parse_addr_with_privacy_prefix(&addr)?;
let addr = addr.parse()?;
let account = match addr_kind {
AddressPrivacyKind::Public => wallet_core.get_account_public(addr).await?,
AddressPrivacyKind::Private => wallet_core
@ -333,14 +242,39 @@ impl WalletSubcommand for AccountSubcommand {
Ok(SubcommandReturnValue::Empty)
}
AccountSubcommand::Fetch(fetch_subcommand) => {
fetch_subcommand.handle_subcommand(wallet_core).await
}
AccountSubcommand::New(new_subcommand) => {
new_subcommand.handle_subcommand(wallet_core).await
}
AccountSubcommand::SyncPrivate {} => {
todo!();
let last_synced_block = wallet_core.last_synced_block;
let curr_last_block = wallet_core
.sequencer_client
.get_last_block()
.await?
.last_block;
if !wallet_core
.storage
.user_data
.user_private_accounts
.is_empty()
{
parse_block_range(
last_synced_block,
curr_last_block,
wallet_core.sequencer_client.clone(),
wallet_core,
)
.await?;
} else {
wallet_core.last_synced_block = curr_last_block;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent data at {path:#?}");
}
Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block))
}
}
}

View File

@ -60,11 +60,13 @@ impl WalletSubcommand for AuthTransferSubcommand {
println!("Transaction data is {transfer_tx:?}");
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
}
AddressPrivacyKind::Private => {
let addr = addr.parse()?;
let (res, [secret]) = wallet_core
.register_account_under_authenticated_transfers_programs_private(addr)
.await?;
@ -85,7 +87,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
}
@ -120,33 +122,29 @@ impl WalletSubcommand for AuthTransferSubcommand {
match (from_privacy, to_privacy) {
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
NativeTokenTransferProgramSubcommand::Public {
from: from.to_string(),
to: to.to_string(),
amount,
}
NativeTokenTransferProgramSubcommand::Public { from, to, amount }
}
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned {
from: from.to_string(),
to: to.to_string(),
from,
to,
amount,
},
)
}
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
NativeTokenTransferProgramSubcommand::Deshielded {
from: from.to_string(),
to: to.to_string(),
from,
to,
amount,
}
}
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
NativeTokenTransferProgramSubcommand::Shielded(
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned {
from: from.to_string(),
to: to.to_string(),
from,
to,
amount,
},
)
@ -160,7 +158,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
AddressPrivacyKind::Private => {
NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
from: from.to_string(),
from,
to_npk,
to_ipk,
amount,
@ -170,7 +168,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
AddressPrivacyKind::Public => {
NativeTokenTransferProgramSubcommand::Shielded(
NativeTokenTransferProgramSubcommandShielded::ShieldedForeign {
from: from.to_string(),
from,
to_npk,
to_ipk,
amount,
@ -340,7 +338,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -384,7 +382,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -434,7 +432,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -467,7 +465,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
let tx_hash = res.tx_hash;
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -513,7 +511,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -533,7 +531,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
println!("Transaction data is {transfer_tx:?}");
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");

View File

@ -1,9 +1,59 @@
use anyhow::Result;
use clap::Subcommand;
use common::transaction::NSSATransaction;
use common::{PINATA_BASE58, transaction::NSSATransaction};
use log::info;
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
use crate::{
SubcommandReturnValue, WalletCore,
cli::WalletSubcommand,
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
};
///Represents generic CLI subcommand for a wallet working with pinata program
#[derive(Subcommand, Debug, Clone)]
pub enum PinataProgramAgnosticSubcommand {
///Claim
Claim {
///to_addr - valid 32 byte base58 string
#[arg(long)]
to_addr: String,
///solution - solution to pinata challenge
#[arg(long)]
solution: u128,
},
}
impl WalletSubcommand for PinataProgramAgnosticSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
let underlying_subcommand = match self {
PinataProgramAgnosticSubcommand::Claim { to_addr, solution } => {
let (to_addr, to_addr_privacy) = parse_addr_with_privacy_prefix(&to_addr)?;
match to_addr_privacy {
AddressPrivacyKind::Public => {
PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim {
pinata_addr: PINATA_BASE58.to_string(),
winner_addr: to_addr,
solution,
})
}
AddressPrivacyKind::Private => PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr: PINATA_BASE58.to_string(),
winner_addr: to_addr,
solution,
},
),
}
}
};
underlying_subcommand.handle_subcommand(wallet_core).await
}
}
///Represents generic CLI subcommand for a wallet working with pinata program
#[derive(Subcommand, Debug, Clone)]
@ -131,7 +181,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");

View File

@ -64,8 +64,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
TokenProgramSubcommand::Public(
TokenProgramSubcommandPublic::CreateNewToken {
definition_addr: definition_addr.to_string(),
supply_addr: supply_addr.to_string(),
definition_addr,
supply_addr,
name,
total_supply,
},
@ -74,18 +74,20 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
TokenProgramSubcommand::Private(
TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned {
definition_addr: definition_addr.to_string(),
supply_addr: supply_addr.to_string(),
definition_addr,
supply_addr,
name,
total_supply,
},
)
}
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
todo!();
//ToDo: maybe implement this one. It is not immediately clear why definition should be private.
anyhow::bail!("Unavailable privacy pairing")
}
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
todo!();
//Probably valid. If definition is not public, but supply is it is very suspicious.
anyhow::bail!("Unavailable privacy pairing")
}
};
@ -120,8 +122,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
TokenProgramSubcommand::Public(
TokenProgramSubcommandPublic::TransferToken {
sender_addr: from.to_string(),
recipient_addr: to.to_string(),
sender_addr: from,
recipient_addr: to,
balance_to_move: amount,
},
)
@ -129,8 +131,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
TokenProgramSubcommand::Private(
TokenProgramSubcommandPrivate::TransferTokenPrivateOwned {
sender_addr: from.to_string(),
recipient_addr: to.to_string(),
sender_addr: from,
recipient_addr: to,
balance_to_move: amount,
},
)
@ -138,8 +140,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
TokenProgramSubcommand::Deshielded(
TokenProgramSubcommandDeshielded::TransferTokenDeshielded {
sender_addr: from.to_string(),
recipient_addr: to.to_string(),
sender_addr: from,
recipient_addr: to,
balance_to_move: amount,
},
)
@ -147,8 +149,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
TokenProgramSubcommand::Shielded(
TokenProgramSubcommandShielded::TransferTokenShieldedOwned {
sender_addr: from.to_string(),
recipient_addr: to.to_string(),
sender_addr: from,
recipient_addr: to,
balance_to_move: amount,
},
)
@ -161,7 +163,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
match from_privacy {
AddressPrivacyKind::Private => TokenProgramSubcommand::Private(
TokenProgramSubcommandPrivate::TransferTokenPrivateForeign {
sender_addr: from.to_string(),
sender_addr: from,
recipient_npk: to_npk,
recipient_ipk: to_ipk,
balance_to_move: amount,
@ -169,7 +171,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
),
AddressPrivacyKind::Public => TokenProgramSubcommand::Shielded(
TokenProgramSubcommandShielded::TransferTokenShieldedForeign {
sender_addr: from.to_string(),
sender_addr: from,
recipient_npk: to_npk,
recipient_ipk: to_ipk,
balance_to_move: amount,
@ -401,7 +403,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -458,7 +460,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -508,7 +510,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -556,7 +558,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -611,7 +613,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
println!("Transaction data is {:?}", tx.message);
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
@ -665,7 +667,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");

View File

@ -46,6 +46,12 @@ pub enum PersistentAccountData {
Private(PersistentAccountDataPrivate),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistentStorage {
pub accounts: Vec<PersistentAccountData>,
pub last_synced_block: u64,
}
impl InitialAccountData {
pub fn address(&self) -> nssa::Address {
match &self {

View File

@ -12,8 +12,7 @@ use serde::Serialize;
use crate::{
HOME_DIR_ENV_VAR,
config::{
PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic,
WalletConfig,
PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig,
},
};
@ -30,21 +29,24 @@ pub async fn fetch_config() -> Result<WalletConfig> {
Ok(serde_json::from_slice(&config_contents)?)
}
/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json`
/// Fetch data stored at `NSSA_WALLET_HOME_DIR/storage.json`
///
/// If file not present, it is considered as empty list of persistent accounts
pub async fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
pub async fn fetch_persistent_storage() -> Result<PersistentStorage> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
let mut persistent_accounts_content = vec![];
let accs_path = home.join("storage.json");
let mut storage_content = vec![];
match tokio::fs::File::open(accs_path).await {
Ok(mut file) => {
file.read_to_end(&mut persistent_accounts_content).await?;
Ok(serde_json::from_slice(&persistent_accounts_content)?)
file.read_to_end(&mut storage_content).await?;
Ok(serde_json::from_slice(&storage_content)?)
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => Ok(vec![]),
std::io::ErrorKind::NotFound => Ok(PersistentStorage {
accounts: vec![],
last_synced_block: 0,
}),
_ => {
anyhow::bail!("IO error {err:#?}");
}
@ -52,8 +54,11 @@ pub async fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
}
}
/// Produces a list of accounts for storage
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> {
/// Produces data for storage
pub fn produce_data_for_storage(
user_data: &NSSAUserData,
last_synced_block: u64,
) -> PersistentStorage {
let mut vec_for_storage = vec![];
for (addr, key) in &user_data.pub_account_signing_keys {
@ -77,7 +82,10 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccou
);
}
vec_for_storage
PersistentStorage {
accounts: vec_for_storage,
last_synced_block,
}
}
pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {

View File

@ -20,15 +20,18 @@ use clap::{Parser, Subcommand};
use nssa_core::{Commitment, MembershipProof};
use tokio::io::AsyncWriteExt;
use crate::cli::{
WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand,
native_token_transfer_program::AuthTransferSubcommand, pinata_program::PinataProgramSubcommand,
token_program::TokenProgramAgnosticSubcommand,
use crate::{
cli::{
WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand,
native_token_transfer_program::AuthTransferSubcommand,
pinata_program::PinataProgramAgnosticSubcommand,
token_program::TokenProgramAgnosticSubcommand,
},
config::PersistentStorage,
helperfunctions::fetch_persistent_storage,
};
use crate::{
helperfunctions::{
fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage,
},
helperfunctions::{fetch_config, get_home, produce_data_for_storage},
poller::TxPoller,
};
@ -48,6 +51,7 @@ pub struct WalletCore {
pub storage: WalletChainStore,
pub poller: TxPoller,
pub sequencer_client: Arc<SequencerClient>,
pub last_synced_block: u64,
}
impl WalletCore {
@ -57,7 +61,10 @@ impl WalletCore {
let mut storage = WalletChainStore::new(config)?;
let persistent_accounts = fetch_persistent_accounts().await?;
let PersistentStorage {
accounts: persistent_accounts,
last_synced_block,
} = fetch_persistent_storage().await?;
for pers_acc_data in persistent_accounts {
storage.insert_account_data(pers_acc_data);
}
@ -66,23 +73,24 @@ impl WalletCore {
storage,
poller: tx_poller,
sequencer_client: client.clone(),
last_synced_block,
})
}
///Store persistent accounts at home
pub async fn store_persistent_accounts(&self) -> Result<PathBuf> {
///Store persistent data at home
pub async fn store_persistent_data(&self) -> Result<PathBuf> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
let storage_path = home.join("storage.json");
let data = produce_data_for_storage(&self.storage.user_data);
let accs = serde_json::to_vec_pretty(&data)?;
let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block);
let storage = serde_json::to_vec_pretty(&data)?;
let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?;
accs_file.write_all(&accs).await?;
let mut storage_file = tokio::fs::File::create(storage_path.as_path()).await?;
storage_file.write_all(&storage).await?;
info!("Stored accounts data at {accs_path:#?}");
info!("Stored data at {storage_path:#?}");
Ok(accs_path)
Ok(storage_path)
}
pub fn create_new_account_public(&mut self) -> Address {
@ -201,11 +209,10 @@ pub enum Command {
Account(AccountSubcommand),
///Pinata command
#[command(subcommand)]
Pinata(PinataProgramSubcommand),
Pinata(PinataProgramAgnosticSubcommand),
///Token command
#[command(subcommand)]
Token(TokenProgramAgnosticSubcommand),
AuthenticatedTransferInitializePublicAccount {},
// Check the wallet can connect to the node and builtin local programs
// match the remote versions
CheckHealth {},
@ -229,6 +236,7 @@ pub enum SubcommandReturnValue {
RegisterAccount { addr: nssa::Address },
Account(nssa::Account),
Empty,
SyncedToBlock(u64),
}
pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValue> {
@ -284,25 +292,6 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty
}
Command::AuthenticatedTransferInitializePublicAccount {} => {
let addr = wallet_core.create_new_account_public();
println!("Generated new account with addr {addr}");
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
let res = wallet_core
.register_account_under_authenticated_transfers_programs(addr)
.await?;
println!("Results of tx send is {res:#?}");
let _transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
SubcommandReturnValue::RegisterAccount { addr }
}
Command::Token(token_subcommand) => {
token_subcommand.handle_subcommand(&mut wallet_core).await?
}
@ -311,6 +300,80 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
Ok(subcommand_ret)
}
pub async fn parse_block_range(
start: u64,
stop: u64,
seq_client: Arc<SequencerClient>,
wallet_core: &mut WalletCore,
) -> Result<()> {
for block_id in start..(stop + 1) {
let block =
borsh::from_slice::<HashableBlockData>(&seq_client.get_block(block_id).await?.block)?;
for tx in block.transactions {
let nssa_tx = NSSATransaction::try_from(&tx)?;
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
let mut affected_accounts = vec![];
for (acc_addr, (key_chain, _)) in
&wallet_core.storage.user_data.user_private_accounts
{
let view_tag = EncryptedAccountData::compute_view_tag(
key_chain.nullifer_public_key.clone(),
key_chain.incoming_viewing_public_key.clone(),
);
for (ciph_id, encrypted_data) in tx
.message()
.encrypted_private_post_states
.iter()
.enumerate()
{
if encrypted_data.view_tag == view_tag {
let ciphertext = &encrypted_data.ciphertext;
let commitment = &tx.message.new_commitments[ciph_id];
let shared_secret = key_chain
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
let res_acc = nssa_core::EncryptionScheme::decrypt(
ciphertext,
&shared_secret,
commitment,
ciph_id as u32,
);
if let Some(res_acc) = res_acc {
println!(
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
);
affected_accounts.push((*acc_addr, res_acc));
}
}
}
}
for (affected_addr, new_acc) in affected_accounts {
wallet_core
.storage
.insert_private_account_data(affected_addr, new_acc);
}
}
}
wallet_core.last_synced_block = block_id;
wallet_core.store_persistent_data().await?;
println!(
"Block at id {block_id} with timestamp {} parsed",
block.timestamp
);
}
Ok(())
}
pub async fn execute_continious_run() -> Result<()> {
let config = fetch_config().await?;
let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
@ -320,70 +383,13 @@ pub async fn execute_continious_run() -> Result<()> {
let mut curr_last_block = latest_block_num;
loop {
for block_id in curr_last_block..(latest_block_num + 1) {
let block = borsh::from_slice::<HashableBlockData>(
&seq_client.get_block(block_id).await?.block,
)?;
for tx in block.transactions {
let nssa_tx = NSSATransaction::try_from(&tx)?;
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
let mut affected_accounts = vec![];
for (acc_addr, (key_chain, _)) in
&wallet_core.storage.user_data.user_private_accounts
{
let view_tag = EncryptedAccountData::compute_view_tag(
key_chain.nullifer_public_key.clone(),
key_chain.incoming_viewing_public_key.clone(),
);
for (ciph_id, encrypted_data) in tx
.message()
.encrypted_private_post_states
.iter()
.enumerate()
{
if encrypted_data.view_tag == view_tag {
let ciphertext = &encrypted_data.ciphertext;
let commitment = &tx.message.new_commitments[ciph_id];
let shared_secret = key_chain
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
let res_acc = nssa_core::EncryptionScheme::decrypt(
ciphertext,
&shared_secret,
commitment,
ciph_id as u32,
);
if let Some(res_acc) = res_acc {
println!(
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
);
affected_accounts.push((*acc_addr, res_acc));
}
}
}
}
for (affected_addr, new_acc) in affected_accounts {
wallet_core
.storage
.insert_private_account_data(affected_addr, new_acc);
}
}
}
wallet_core.store_persistent_accounts().await?;
println!(
"Block at id {block_id} with timestamp {} parsed",
block.timestamp
);
}
parse_block_range(
curr_last_block,
latest_block_num,
seq_client.clone(),
&mut wallet_core,
)
.await?;
curr_last_block = latest_block_num + 1;