diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index dc7188b..31cd177 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -56,7 +56,7 @@ fn make_private_account_input_from_str(account_id: &str) -> String { pub async fn pre_test( home_dir: PathBuf, ) -> Result<(ServerHandle, JoinHandle>, TempDir)> { - wallet::execute_setup("test_pass".to_owned()).await?; + wallet::cli::execute_setup("test_pass".to_owned()).await?; let home_dir_sequencer = home_dir.join("sequencer"); diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index 55be519..9903345 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -16,13 +16,15 @@ use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ - Command, SubcommandReturnValue, WalletCore, + WalletCore, cli::{ + Command, SubcommandReturnValue, account::{AccountSubcommand, NewSubcommand}, config::ConfigSubcommand, - native_token_transfer_program::AuthTransferSubcommand, - pinata_program::PinataProgramAgnosticSubcommand, - token_program::TokenProgramAgnosticSubcommand, + programs::{ + native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand, + token::TokenProgramAgnosticSubcommand, + }, }, config::PersistentStorage, helperfunctions::{fetch_config, fetch_persistent_storage}, @@ -57,7 +59,7 @@ pub fn prepare_function_map() -> HashMap { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -92,7 +94,7 @@ pub fn prepare_function_map() -> HashMap { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); let PersistentStorage { accounts: persistent_accounts, @@ -123,7 +125,7 @@ pub fn prepare_function_map() -> HashMap { amount: 100, }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -162,7 +164,7 @@ pub fn prepare_function_map() -> HashMap { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let failed_send = wallet::execute_subcommand(command).await; + let failed_send = wallet::cli::execute_subcommand(command).await; assert!(failed_send.is_err()); @@ -203,7 +205,7 @@ pub fn prepare_function_map() -> HashMap { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -234,7 +236,7 @@ pub fn prepare_function_map() -> HashMap { amount: 100, }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -289,7 +291,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token definition let SubcommandReturnValue::RegisterAccount { account_id: definition_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -302,7 +304,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token supply holder let SubcommandReturnValue::RegisterAccount { account_id: supply_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -315,7 +317,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { account_id: recipient_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -335,7 +337,7 @@ pub fn prepare_function_map() -> HashMap { name: "A NAME".to_string(), total_supply: 37, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); info!("Waiting for next block creation"); @@ -394,7 +396,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); info!("Waiting for next block creation"); @@ -449,7 +451,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { account_id: definition_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -462,7 +464,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { account_id: supply_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -475,7 +477,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { account_id: recipient_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -496,7 +498,7 @@ pub fn prepare_function_map() -> HashMap { total_supply: 37, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -543,7 +545,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -577,7 +579,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -610,7 +612,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { account_id: definition_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -623,7 +625,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { account_id: supply_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -636,7 +638,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { account_id: recipient_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -657,7 +659,7 @@ pub fn prepare_function_map() -> HashMap { total_supply: 37, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -711,7 +713,7 @@ pub fn prepare_function_map() -> HashMap { }; let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap() else { @@ -723,7 +725,7 @@ pub fn prepare_function_map() -> HashMap { let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) @@ -752,7 +754,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { account_id: definition_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -765,7 +767,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token supply holder (public) let SubcommandReturnValue::RegisterAccount { account_id: supply_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -778,7 +780,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { account_id: recipient_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -799,7 +801,7 @@ pub fn prepare_function_map() -> HashMap { total_supply: 37, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -836,7 +838,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -865,7 +867,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -894,7 +896,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token definition (public) let SubcommandReturnValue::RegisterAccount { account_id: definition_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -907,7 +909,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { account_id: supply_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -920,7 +922,7 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { account_id: recipient_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Public { cci: ChainIndex::root(), }, @@ -941,7 +943,7 @@ pub fn prepare_function_map() -> HashMap { total_supply: 37, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -988,7 +990,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -1017,7 +1019,7 @@ pub fn prepare_function_map() -> HashMap { amount: 7, }; - wallet::execute_subcommand(Command::Token(subcommand)) + wallet::cli::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -1049,7 +1051,7 @@ pub fn prepare_function_map() -> HashMap { amount: 100, }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -1088,7 +1090,7 @@ pub fn prepare_function_map() -> HashMap { }); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = - wallet::execute_subcommand(command).await.unwrap() + wallet::cli::execute_subcommand(command).await.unwrap() else { panic!("invalid subcommand return value"); }; @@ -1128,7 +1130,7 @@ pub fn prepare_function_map() -> HashMap { cci: ChainIndex::root(), })); - let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { account_id: to_account_id, } = sub_ret @@ -1157,7 +1159,7 @@ pub fn prepare_function_map() -> HashMap { amount: 100, }); - let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { panic!("FAILED TO SEND TX"); }; @@ -1165,7 +1167,7 @@ pub fn prepare_function_map() -> HashMap { let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) .await .unwrap(); @@ -1192,13 +1194,13 @@ pub fn prepare_function_map() -> HashMap { // info!( // "########## test_success_private_transfer_to_another_owned_account_cont_run_path // ##########" ); - // let continious_run_handle = tokio::spawn(wallet::execute_continious_run()); + // let continious_run_handle = tokio::spawn(wallet::cli::execute_continious_run()); // let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); // let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {})); - // let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + // let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); // let SubcommandReturnValue::RegisterAccount { // account_id: to_account_id, // } = sub_ret @@ -1228,7 +1230,7 @@ pub fn prepare_function_map() -> HashMap { // amount: 100, // }); - // let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + // let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); // let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { // panic!("FAILED TO SEND TX"); // }; @@ -1279,7 +1281,7 @@ pub fn prepare_function_map() -> HashMap { let from_acc = wallet_storage.get_account_private(&from).unwrap(); assert_eq!(from_acc.balance, 10000); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -1322,7 +1324,7 @@ pub fn prepare_function_map() -> HashMap { let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -1368,7 +1370,7 @@ pub fn prepare_function_map() -> HashMap { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = - wallet::execute_subcommand(command).await.unwrap() + wallet::cli::execute_subcommand(command).await.unwrap() else { panic!("invalid subcommand return value"); }; @@ -1398,10 +1400,8 @@ pub fn prepare_function_map() -> HashMap { let pinata_account_id = PINATA_BASE58; let pinata_prize = 150; - let solution = 989106; let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to_account_id: make_public_account_input_from_str(ACC_SENDER), - solution, + to: make_public_account_input_from_str(ACC_SENDER), }); let wallet_config = fetch_config().await.unwrap(); @@ -1414,7 +1414,7 @@ pub fn prepare_function_map() -> HashMap { .unwrap() .balance; - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -1493,7 +1493,7 @@ pub fn prepare_function_map() -> HashMap { cci: ChainIndex::root(), })); let SubcommandReturnValue::RegisterAccount { account_id } = - wallet::execute_subcommand(command).await.unwrap() + wallet::cli::execute_subcommand(command).await.unwrap() else { panic!("Error creating account"); }; @@ -1501,7 +1501,7 @@ pub fn prepare_function_map() -> HashMap { let command = Command::AuthTransfer(AuthTransferSubcommand::Init { account_id: make_public_account_input_from_str(&account_id.to_string()), }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Checking correct execution"); let wallet_config = fetch_config().await.unwrap(); @@ -1529,11 +1529,9 @@ pub fn prepare_function_map() -> HashMap { info!("########## test_pinata_private_receiver ##########"); let pinata_account_id = PINATA_BASE58; let pinata_prize = 150; - let solution = 989106; let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to_account_id: make_private_account_input_from_str(ACC_SENDER_PRIVATE), - solution, + to: make_private_account_input_from_str(ACC_SENDER_PRIVATE), }); let wallet_config = fetch_config().await.unwrap(); @@ -1547,7 +1545,7 @@ pub fn prepare_function_map() -> HashMap { .balance; let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = - wallet::execute_subcommand(command).await.unwrap() + wallet::cli::execute_subcommand(command).await.unwrap() else { panic!("invalid subcommand return value"); }; @@ -1563,7 +1561,7 @@ pub fn prepare_function_map() -> HashMap { .balance; let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1586,12 +1584,11 @@ pub fn prepare_function_map() -> HashMap { info!("########## test_pinata_private_receiver_new_account ##########"); let pinata_account_id = PINATA_BASE58; let pinata_prize = 150; - let solution = 989106; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { account_id: winner_account_id, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( NewSubcommand::Private { cci: ChainIndex::root(), }, @@ -1603,8 +1600,7 @@ pub fn prepare_function_map() -> HashMap { }; let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to_account_id: make_private_account_input_from_str(&winner_account_id.to_string()), - solution, + to: make_private_account_input_from_str(&winner_account_id.to_string()), }); let wallet_config = fetch_config().await.unwrap(); @@ -1617,7 +1613,7 @@ pub fn prepare_function_map() -> HashMap { .unwrap() .balance; - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -1657,7 +1653,7 @@ pub fn prepare_function_map() -> HashMap { key: "seq_poll_retry_delay_millis".to_string(), value: "1000".to_string(), }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); @@ -1668,7 +1664,7 @@ pub fn prepare_function_map() -> HashMap { key: "seq_poll_retry_delay_millis".to_string(), value: old_seq_poll_retry_delay_millis.to_string(), }); - wallet::execute_subcommand(command).await.unwrap(); + wallet::cli::execute_subcommand(command).await.unwrap(); info!("Success!"); } diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 74eb5bc..3b12d8f 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -20,6 +20,7 @@ base58.workspace = true hex = "0.4.3" rand.workspace = true itertools = "0.14.0" +sha2.workspace = true [dependencies.key_protocol] path = "../key_protocol" diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage.rs similarity index 100% rename from wallet/src/chain_storage/mod.rs rename to wallet/src/chain_storage.rs diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index 6e831d1..5b23b2b 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -7,10 +7,11 @@ use nssa::{Account, AccountId, program::Program}; use serde::Serialize; use crate::{ - SubcommandReturnValue, WalletCore, - cli::WalletSubcommand, - helperfunctions::{AccountPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix}, - parse_block_range, + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, + helperfunctions::{ + AccountPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix, parse_block_range, + }, }; const TOKEN_DEFINITION_TYPE: u8 = 0; diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs index a606066..419fa5e 100644 --- a/wallet/src/cli/chain.rs +++ b/wallet/src/cli/chain.rs @@ -1,7 +1,10 @@ use anyhow::Result; use clap::Subcommand; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, +}; /// Represents generic chain CLI subcommand #[derive(Subcommand, Debug, Clone)] diff --git a/wallet/src/cli/config.rs b/wallet/src/cli/config.rs index c41aa32..68670af 100644 --- a/wallet/src/cli/config.rs +++ b/wallet/src/cli/config.rs @@ -1,7 +1,10 @@ use anyhow::Result; use clap::Subcommand; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, +}; /// Represents generic config CLI subcommand #[derive(Subcommand, Debug, Clone)] diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index fff14cb..c1def06 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -1,15 +1,200 @@ -use anyhow::Result; +use std::sync::Arc; -use crate::{SubcommandReturnValue, WalletCore}; +use anyhow::Result; +use clap::{Parser, Subcommand}; +use common::sequencer_client::SequencerClient; +use nssa::program::Program; + +use crate::{ + WalletCore, + cli::{ + account::AccountSubcommand, + chain::ChainSubcommand, + config::ConfigSubcommand, + programs::{ + native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand, + token::TokenProgramAgnosticSubcommand, + }, + }, + helperfunctions::{fetch_config, parse_block_range}, +}; pub mod account; pub mod chain; pub mod config; -pub mod native_token_transfer_program; -pub mod pinata_program; -pub mod token_program; +pub mod programs; pub(crate) trait WalletSubcommand { async fn handle_subcommand(self, wallet_core: &mut WalletCore) -> Result; } + +/// Represents CLI command for a wallet +#[derive(Subcommand, Debug, Clone)] +#[clap(about)] +pub enum Command { + /// Authenticated transfer subcommand + #[command(subcommand)] + AuthTransfer(AuthTransferSubcommand), + /// Generic chain info subcommand + #[command(subcommand)] + ChainInfo(ChainSubcommand), + /// Account view and sync subcommand + #[command(subcommand)] + Account(AccountSubcommand), + /// Pinata program interaction subcommand + #[command(subcommand)] + Pinata(PinataProgramAgnosticSubcommand), + /// Token program interaction subcommand + #[command(subcommand)] + Token(TokenProgramAgnosticSubcommand), + /// Check the wallet can connect to the node and builtin local programs + /// match the remote versions + CheckHealth {}, + /// Command to setup config, get and set config fields + #[command(subcommand)] + Config(ConfigSubcommand), +} + +/// Represents overarching CLI command for a wallet with setup included +#[derive(Debug, Subcommand, Clone)] +#[clap(about)] +pub enum OverCommand { + /// Represents CLI command for a wallet + #[command(subcommand)] + Command(Command), + /// Setup of a storage. Initializes rots for public and private trees from `password`. + Setup { + #[arg(short, long)] + password: String, + }, +} + +/// To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config +/// +/// All account adresses must be valid 32 byte base58 strings. +/// +/// All account account_ids must be provided as {privacy_prefix}/{account_id}, +/// where valid options for `privacy_prefix` is `Public` and `Private` +#[derive(Parser, Debug)] +#[clap(version, about)] +pub struct Args { + /// Continious run flag + #[arg(short, long)] + pub continuous_run: bool, + /// Wallet command + #[command(subcommand)] + pub command: Option, +} + +#[derive(Debug, Clone)] +pub enum SubcommandReturnValue { + PrivacyPreservingTransfer { tx_hash: String }, + RegisterAccount { account_id: nssa::AccountId }, + Account(nssa::Account), + Empty, + SyncedToBlock(u64), +} + +pub async fn execute_subcommand(command: Command) -> Result { + let wallet_config = fetch_config().await?; + let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?; + + let subcommand_ret = match command { + Command::AuthTransfer(transfer_subcommand) => { + transfer_subcommand + .handle_subcommand(&mut wallet_core) + .await? + } + Command::ChainInfo(chain_subcommand) => { + chain_subcommand.handle_subcommand(&mut wallet_core).await? + } + Command::Account(account_subcommand) => { + account_subcommand + .handle_subcommand(&mut wallet_core) + .await? + } + Command::Pinata(pinata_subcommand) => { + pinata_subcommand + .handle_subcommand(&mut wallet_core) + .await? + } + Command::CheckHealth {} => { + let remote_program_ids = wallet_core + .sequencer_client + .get_program_ids() + .await + .expect("Error fetching program ids"); + let Some(authenticated_transfer_id) = remote_program_ids.get("authenticated_transfer") + else { + panic!("Missing authenticated transfer ID from remote"); + }; + if authenticated_transfer_id != &Program::authenticated_transfer_program().id() { + panic!("Local ID for authenticated transfer program is different from remote"); + } + let Some(token_id) = remote_program_ids.get("token") else { + panic!("Missing token program ID from remote"); + }; + if token_id != &Program::token().id() { + panic!("Local ID for token program is different from remote"); + } + let Some(circuit_id) = remote_program_ids.get("privacy_preserving_circuit") else { + panic!("Missing privacy preserving circuit ID from remote"); + }; + if circuit_id != &nssa::PRIVACY_PRESERVING_CIRCUIT_ID { + panic!("Local ID for privacy preserving circuit is different from remote"); + } + + println!("✅All looks good!"); + + SubcommandReturnValue::Empty + } + Command::Token(token_subcommand) => { + token_subcommand.handle_subcommand(&mut wallet_core).await? + } + Command::Config(config_subcommand) => { + config_subcommand + .handle_subcommand(&mut wallet_core) + .await? + } + }; + + Ok(subcommand_ret) +} + +pub async fn execute_continuous_run() -> Result<()> { + let config = fetch_config().await?; + let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); + let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?; + + let mut latest_block_num = seq_client.get_last_block().await?.last_block; + let mut curr_last_block = latest_block_num; + + loop { + parse_block_range( + curr_last_block, + latest_block_num, + seq_client.clone(), + &mut wallet_core, + ) + .await?; + + curr_last_block = latest_block_num + 1; + + tokio::time::sleep(std::time::Duration::from_millis( + config.seq_poll_timeout_millis, + )) + .await; + + latest_block_num = seq_client.get_last_block().await?.last_block; + } +} + +pub async fn execute_setup(password: String) -> Result<()> { + let config = fetch_config().await?; + let wallet_core = WalletCore::start_from_config_new_storage(config.clone(), password).await?; + + wallet_core.store_persistent_data().await?; + + Ok(()) +} diff --git a/wallet/src/cli/programs/mod.rs b/wallet/src/cli/programs/mod.rs new file mode 100644 index 0000000..3ffb7bb --- /dev/null +++ b/wallet/src/cli/programs/mod.rs @@ -0,0 +1,3 @@ +pub mod native_token_transfer; +pub mod pinata; +pub mod token; diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/programs/native_token_transfer.rs similarity index 86% rename from wallet/src/cli/native_token_transfer_program.rs rename to wallet/src/cli/programs/native_token_transfer.rs index f5fdb9a..9dc72ae 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -4,9 +4,10 @@ use common::transaction::NSSATransaction; use nssa::AccountId; use crate::{ - SubcommandReturnValue, WalletCore, - cli::WalletSubcommand, + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, + program_facades::native_token_transfer::NativeTokenTransfer, }; /// Represents generic CLI subcommand for a wallet working with native token transfer program @@ -56,11 +57,11 @@ impl WalletSubcommand for AuthTransferSubcommand { AccountPrivacyKind::Public => { let account_id = account_id.parse()?; - let res = wallet_core - .register_account_under_authenticated_transfers_programs(account_id) + let res = NativeTokenTransfer(wallet_core) + .register_account(account_id) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; @@ -74,13 +75,11 @@ impl WalletSubcommand for AuthTransferSubcommand { AccountPrivacyKind::Private => { let account_id = account_id.parse()?; - let (res, [secret]) = wallet_core - .register_account_under_authenticated_transfers_programs_private( - account_id, - ) + let (res, secret) = NativeTokenTransfer(wallet_core) + .register_account_private(account_id) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -317,23 +316,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); - let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + let (res, [secret_from, secret_to]) = NativeTokenTransfer(wallet_core) + .send_private_transfer_to_owned_account(from, to, amount) + .await?; - let (res, [secret_from, secret_to]) = if let Some(to_proof) = to_initialization { - wallet_core - .send_private_native_token_transfer_owned_account_already_initialized( - from, to, amount, to_proof, - ) - .await? - } else { - wallet_core - .send_private_native_token_transfer_owned_account_not_initialized( - from, to, amount, - ) - .await? - }; - - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -373,11 +360,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { let to_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - let (res, [secret_from, _]) = wallet_core - .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + let (res, [secret_from, _]) = NativeTokenTransfer(wallet_core) + .send_private_transfer_to_outer_account(from, to_npk, to_ipk, amount) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -413,21 +400,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); - let to_initialization = wallet_core.check_private_account_initialized(&to).await?; + let (res, secret) = NativeTokenTransfer(wallet_core) + .send_shielded_transfer(from, to, amount) + .await?; - let (res, [secret]) = if let Some(to_proof) = to_initialization { - wallet_core - .send_shielded_native_token_transfer_already_initialized( - from, to, amount, to_proof, - ) - .await? - } else { - wallet_core - .send_shielded_native_token_transfer_not_initialized(from, to, amount) - .await? - }; - - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -468,11 +445,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let to_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); - let res = wallet_core - .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + let (res, _) = NativeTokenTransfer(wallet_core) + .send_shielded_transfer_to_outer_account(from, to_npk, to_ipk, amount) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; @@ -502,11 +479,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); - let (res, [secret]) = wallet_core - .send_deshielded_native_token_transfer(from, to, amount) + let (res, secret) = NativeTokenTransfer(wallet_core) + .send_deshielded_transfer(from, to, amount) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -532,11 +509,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); - let res = wallet_core - .send_public_native_token_transfer(from, to, amount) + let res = NativeTokenTransfer(wallet_core) + .send_public_transfer(from, to, amount) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/programs/pinata.rs similarity index 64% rename from wallet/src/cli/pinata_program.rs rename to wallet/src/cli/programs/pinata.rs index cc71a51..c0e2223 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/programs/pinata.rs @@ -1,12 +1,12 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Subcommand; use common::{PINATA_BASE58, transaction::NSSATransaction}; -use log::info; use crate::{ - SubcommandReturnValue, WalletCore, - cli::WalletSubcommand, + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, + program_facades::pinata::Pinata, }; /// Represents generic CLI subcommand for a wallet working with pinata program @@ -14,12 +14,9 @@ use crate::{ pub enum PinataProgramAgnosticSubcommand { /// Claim pinata Claim { - /// to_account_id - valid 32 byte base58 string with privacy prefix + /// to - valid 32 byte base58 string with privacy prefix #[arg(long)] - to_account_id: String, - /// solution - solution to pinata challenge - #[arg(long)] - solution: u128, + to: String, }, } @@ -29,26 +26,20 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand { wallet_core: &mut WalletCore, ) -> Result { let underlying_subcommand = match self { - PinataProgramAgnosticSubcommand::Claim { - to_account_id, - solution, - } => { - let (to_account_id, to_addr_privacy) = - parse_addr_with_privacy_prefix(&to_account_id)?; + PinataProgramAgnosticSubcommand::Claim { to } => { + let (to, to_addr_privacy) = parse_addr_with_privacy_prefix(&to)?; match to_addr_privacy { AccountPrivacyKind::Public => { PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim { pinata_account_id: PINATA_BASE58.to_string(), - winner_account_id: to_account_id, - solution, + winner_account_id: to, }) } AccountPrivacyKind::Private => PinataProgramSubcommand::Private( PinataProgramSubcommandPrivate::ClaimPrivateOwned { pinata_account_id: PINATA_BASE58.to_string(), - winner_account_id: to_account_id, - solution, + winner_account_id: to, }, ), } @@ -82,9 +73,6 @@ pub enum PinataProgramSubcommandPublic { /// winner_account_id - valid 32 byte hex string #[arg(long)] winner_account_id: String, - /// solution - solution to pinata challenge - #[arg(long)] - solution: u128, }, } @@ -100,9 +88,6 @@ pub enum PinataProgramSubcommandPrivate { /// winner_account_id - valid 32 byte hex string #[arg(long)] winner_account_id: String, - /// solution - solution to pinata challenge - #[arg(long)] - solution: u128, }, } @@ -115,16 +100,28 @@ impl WalletSubcommand for PinataProgramSubcommandPublic { PinataProgramSubcommandPublic::Claim { pinata_account_id, winner_account_id, - solution, } => { - let res = wallet_core - .claim_pinata( - pinata_account_id.parse().unwrap(), + let pinata_account_id = pinata_account_id.parse().unwrap(); + let solution = find_solution(wallet_core, pinata_account_id) + .await + .context("failed to compute solution")?; + + let res = Pinata(wallet_core) + .claim( + pinata_account_id, winner_account_id.parse().unwrap(), solution, ) .await?; - info!("Results of tx send is {res:#?}"); + + println!("Results of tx send are {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + println!("Transaction data is {transfer_tx:?}"); Ok(SubcommandReturnValue::Empty) } @@ -141,41 +138,26 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { PinataProgramSubcommandPrivate::ClaimPrivateOwned { pinata_account_id, winner_account_id, - solution, } => { let pinata_account_id = pinata_account_id.parse().unwrap(); let winner_account_id = winner_account_id.parse().unwrap(); + let solution = find_solution(wallet_core, pinata_account_id) + .await + .context("failed to compute solution")?; - let winner_initialization = wallet_core - .check_private_account_initialized(&winner_account_id) + let (res, secret_winner) = Pinata(wallet_core) + .claim_private_owned_account(pinata_account_id, winner_account_id, solution) .await?; - let (res, [secret_winner]) = if let Some(winner_proof) = winner_initialization { - wallet_core - .claim_pinata_private_owned_account_already_initialized( - pinata_account_id, - winner_account_id, - solution, - winner_proof, - ) - .await? - } else { - wallet_core - .claim_pinata_private_owned_account_not_initialized( - pinata_account_id, - winner_account_id, - solution, - ) - .await? - }; - - info!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core .poll_native_token_transfer(tx_hash.clone()) .await?; + println!("Transaction data is {transfer_tx:?}"); + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let acc_decode_data = vec![(secret_winner, winner_account_id)]; @@ -210,3 +192,46 @@ impl WalletSubcommand for PinataProgramSubcommand { } } } + +async fn find_solution(wallet: &WalletCore, pinata_account_id: nssa::AccountId) -> Result { + let account = wallet.get_account_public(pinata_account_id).await?; + let data: [u8; 33] = account + .data + .try_into() + .map_err(|_| anyhow::Error::msg("invalid pinata account data"))?; + + println!("Computing solution for pinata..."); + let now = std::time::Instant::now(); + + let solution = compute_solution(data); + + println!("Found solution {solution} in {:?}", now.elapsed()); + Ok(solution) +} + +fn compute_solution(data: [u8; 33]) -> u128 { + let difficulty = data[0]; + let seed = &data[1..]; + + let mut solution = 0u128; + while !validate_solution(difficulty, seed, solution) { + solution = solution.checked_add(1).expect("solution overflowed u128"); + } + + solution +} + +fn validate_solution(difficulty: u8, seed: &[u8], solution: u128) -> bool { + use sha2::{Digest as _, digest::FixedOutput as _}; + + let mut bytes = [0; 32 + 16]; + bytes[..32].copy_from_slice(seed); + bytes[32..].copy_from_slice(&solution.to_le_bytes()); + + let mut hasher = sha2::Sha256::new(); + hasher.update(bytes); + let digest: [u8; 32] = hasher.finalize_fixed().into(); + + let difficulty = difficulty as usize; + digest[..difficulty].iter().all(|&b| b == 0) +} diff --git a/wallet/src/cli/token_program.rs b/wallet/src/cli/programs/token.rs similarity index 88% rename from wallet/src/cli/token_program.rs rename to wallet/src/cli/programs/token.rs index b412e2f..d1a27dd 100644 --- a/wallet/src/cli/token_program.rs +++ b/wallet/src/cli/programs/token.rs @@ -4,9 +4,10 @@ use common::transaction::NSSATransaction; use nssa::AccountId; use crate::{ - SubcommandReturnValue, WalletCore, - cli::WalletSubcommand, + WalletCore, + cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, + program_facades::token::Token, }; /// Represents generic CLI subcommand for a wallet working with token program @@ -338,8 +339,8 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { } let mut name_bytes = [0; 6]; name_bytes[..name.len()].copy_from_slice(name); - wallet_core - .send_new_token_definition( + Token(wallet_core) + .send_new_definition( definition_account_id.parse().unwrap(), supply_account_id.parse().unwrap(), name_bytes, @@ -353,8 +354,8 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { recipient_account_id, balance_to_move, } => { - wallet_core - .send_transfer_token_transaction( + Token(wallet_core) + .send_transfer_transaction( sender_account_id.parse().unwrap(), recipient_account_id.parse().unwrap(), balance_to_move, @@ -389,8 +390,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { let definition_account_id: AccountId = definition_account_id.parse().unwrap(); let supply_account_id: AccountId = supply_account_id.parse().unwrap(); - let (res, [secret_supply]) = wallet_core - .send_new_token_definition_private_owned( + let (res, secret_supply) = Token(wallet_core) + .send_new_definition_private_owned( definition_account_id, supply_account_id, name_bytes, @@ -398,7 +399,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { ) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -428,31 +429,15 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { let sender_account_id: AccountId = sender_account_id.parse().unwrap(); let recipient_account_id: AccountId = recipient_account_id.parse().unwrap(); - let recipient_initialization = wallet_core - .check_private_account_initialized(&recipient_account_id) + let (res, [secret_sender, secret_recipient]) = Token(wallet_core) + .send_transfer_transaction_private_owned_account( + sender_account_id, + recipient_account_id, + balance_to_move, + ) .await?; - let (res, [secret_sender, secret_recipient]) = - if let Some(recipient_proof) = recipient_initialization { - wallet_core - .send_transfer_token_transaction_private_owned_account_already_initialized( - sender_account_id, - recipient_account_id, - balance_to_move, - recipient_proof, - ) - .await? - } else { - wallet_core - .send_transfer_token_transaction_private_owned_account_not_initialized( - sender_account_id, - recipient_account_id, - balance_to_move, - ) - .await? - }; - - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -496,8 +481,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { recipient_ipk.to_vec(), ); - let (res, [secret_sender, _]) = wallet_core - .send_transfer_token_transaction_private_foreign_account( + let (res, [secret_sender, _]) = Token(wallet_core) + .send_transfer_transaction_private_foreign_account( sender_account_id, recipient_npk, recipient_ipk, @@ -505,7 +490,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { ) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -545,15 +530,15 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { let sender_account_id: AccountId = sender_account_id.parse().unwrap(); let recipient_account_id: AccountId = recipient_account_id.parse().unwrap(); - let (res, [secret_sender]) = wallet_core - .send_transfer_token_transaction_deshielded( + let (res, secret_sender) = Token(wallet_core) + .send_transfer_transaction_deshielded( sender_account_id, recipient_account_id, balance_to_move, ) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -604,8 +589,8 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { recipient_ipk.to_vec(), ); - let res = wallet_core - .send_transfer_token_transaction_shielded_foreign_account( + let (res, _) = Token(wallet_core) + .send_transfer_transaction_shielded_foreign_account( sender_account_id, recipient_npk, recipient_ipk, @@ -613,7 +598,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { ) .await?; - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core @@ -638,31 +623,15 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { let sender_account_id: AccountId = sender_account_id.parse().unwrap(); let recipient_account_id: AccountId = recipient_account_id.parse().unwrap(); - let recipient_initialization = wallet_core - .check_private_account_initialized(&recipient_account_id) + let (res, secret_recipient) = Token(wallet_core) + .send_transfer_transaction_shielded_owned_account( + sender_account_id, + recipient_account_id, + balance_to_move, + ) .await?; - let (res, [secret_recipient]) = - if let Some(recipient_proof) = recipient_initialization { - wallet_core - .send_transfer_token_transaction_shielded_owned_account_already_initialized( - sender_account_id, - recipient_account_id, - balance_to_move, - recipient_proof, - ) - .await? - } else { - wallet_core - .send_transfer_token_transaction_shielded_owned_account_not_initialized( - sender_account_id, - recipient_account_id, - balance_to_move, - ) - .await? - }; - - println!("Results of tx send is {res:#?}"); + println!("Results of tx send are {res:#?}"); let tx_hash = res.tx_hash; let transfer_tx = wallet_core diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 770d2bb..19d2d56 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -1,16 +1,21 @@ -use std::{path::PathBuf, str::FromStr}; +use std::{path::PathBuf, str::FromStr, sync::Arc}; use anyhow::Result; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; -use key_protocol::key_protocol_core::NSSAUserData; -use nssa::Account; +use common::{ + block::HashableBlockData, sequencer_client::SequencerClient, transaction::NSSATransaction, +}; +use key_protocol::{ + key_management::key_tree::traits::KeyNode as _, key_protocol_core::NSSAUserData, +}; +use nssa::{Account, privacy_preserving_transaction::message::EncryptedAccountData}; use nssa_core::account::Nonce; use rand::{RngCore, rngs::OsRng}; use serde::Serialize; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use crate::{ - HOME_DIR_ENV_VAR, + HOME_DIR_ENV_VAR, WalletCore, config::{ InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic, PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig, @@ -225,6 +230,125 @@ impl From for HumanReadableAccount { } } +pub async fn parse_block_range( + start: u64, + stop: u64, + seq_client: Arc, + wallet_core: &mut WalletCore, +) -> Result<()> { + for block_id in start..(stop + 1) { + let block = + borsh::from_slice::(&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_account_id, (key_chain, _)) in + &wallet_core.storage.user_data.default_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 account_id {acc_account_id:#?} with account object {res_acc:#?}" + ); + + affected_accounts.push((*acc_account_id, res_acc)); + } + } + } + } + + for keys_node in wallet_core + .storage + .user_data + .private_key_tree + .key_map + .values() + { + let acc_account_id = keys_node.account_id(); + let key_chain = &keys_node.value.0; + + 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 account_id {acc_account_id:#?} with account object {res_acc:#?}" + ); + + affected_accounts.push((acc_account_id, res_acc)); + } + } + } + } + + for (affected_account_id, new_acc) in affected_accounts { + wallet_core + .storage + .insert_private_account_data(affected_account_id, 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(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 6cb52fe..f79d947 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -3,31 +3,24 @@ use std::{path::PathBuf, sync::Arc}; use anyhow::Result; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use chain_storage::WalletChainStore; -use clap::{Parser, Subcommand}; use common::{ - block::HashableBlockData, - sequencer_client::SequencerClient, + error::ExecutionFailureKind, + sequencer_client::{SequencerClient, json::SendTxResponse}, transaction::{EncodedTransaction, NSSATransaction}, }; use config::WalletConfig; -use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::KeyNode}; +use key_protocol::key_management::key_tree::chain_index::ChainIndex; use log::info; -use nssa::{ - Account, AccountId, privacy_preserving_transaction::message::EncryptedAccountData, - program::Program, -}; -use nssa_core::{Commitment, MembershipProof}; +use nssa::{Account, AccountId, PrivacyPreservingTransaction, program::Program}; +use nssa_core::{Commitment, MembershipProof, SharedSecretKey, program::InstructionData}; +pub use privacy_preserving_tx::PrivacyPreservingAccount; use tokio::io::AsyncWriteExt; use crate::{ - cli::{ - WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, - config::ConfigSubcommand, native_token_transfer_program::AuthTransferSubcommand, - pinata_program::PinataProgramAgnosticSubcommand, - token_program::TokenProgramAgnosticSubcommand, - }, config::PersistentStorage, - helperfunctions::{fetch_config, fetch_persistent_storage, get_home, produce_data_for_storage}, + helperfunctions::{ + fetch_persistent_storage, get_home, produce_data_for_storage, produce_random_nonces, + }, poller::TxPoller, }; @@ -37,11 +30,9 @@ pub mod chain_storage; pub mod cli; pub mod config; pub mod helperfunctions; -pub mod pinata_interactions; pub mod poller; -pub mod token_program_interactions; -pub mod token_transfers; -pub mod transaction_utils; +mod privacy_preserving_tx; +pub mod program_facades; pub struct WalletCore { pub storage: WalletChainStore, @@ -156,6 +147,15 @@ impl WalletCore { Ok(response.account) } + pub fn get_account_public_signing_key( + &self, + account_id: &AccountId, + ) -> Option<&nssa::PrivateKey> { + self.storage + .user_data + .get_pub_account_signing_key(account_id) + } + pub fn get_account_private(&self, account_id: &AccountId) -> Option { self.storage .user_data @@ -218,293 +218,79 @@ impl WalletCore { Ok(()) } -} -/// Represents CLI command for a wallet -#[derive(Subcommand, Debug, Clone)] -#[clap(about)] -pub enum Command { - /// Authenticated transfer subcommand - #[command(subcommand)] - AuthTransfer(AuthTransferSubcommand), - /// Generic chain info subcommand - #[command(subcommand)] - ChainInfo(ChainSubcommand), - /// Account view and sync subcommand - #[command(subcommand)] - Account(AccountSubcommand), - /// Pinata program interaction subcommand - #[command(subcommand)] - Pinata(PinataProgramAgnosticSubcommand), - /// Token program interaction subcommand - #[command(subcommand)] - Token(TokenProgramAgnosticSubcommand), - /// Check the wallet can connect to the node and builtin local programs - /// match the remote versions - CheckHealth {}, - /// Command to setup config, get and set config fields - #[command(subcommand)] - Config(ConfigSubcommand), -} - -/// Represents overarching CLI command for a wallet with setup included -#[derive(Debug, Subcommand, Clone)] -#[clap(about)] -pub enum OverCommand { - /// Represents CLI command for a wallet - #[command(subcommand)] - Command(Command), - /// Setup of a storage. Initializes rots for public and private trees from `password`. - Setup { - #[arg(short, long)] - password: String, - }, -} - -/// To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config -/// -/// All account adresses must be valid 32 byte base58 strings. -/// -/// All account account_ids must be provided as {privacy_prefix}/{account_id}, -/// where valid options for `privacy_prefix` is `Public` and `Private` -#[derive(Parser, Debug)] -#[clap(version, about)] -pub struct Args { - /// Continious run flag - #[arg(short, long)] - pub continious_run: bool, - /// Wallet command - #[command(subcommand)] - pub command: Option, -} - -#[derive(Debug, Clone)] -pub enum SubcommandReturnValue { - PrivacyPreservingTransfer { tx_hash: String }, - RegisterAccount { account_id: nssa::AccountId }, - Account(nssa::Account), - Empty, - SyncedToBlock(u64), -} - -pub async fn execute_subcommand(command: Command) -> Result { - let wallet_config = fetch_config().await?; - let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?; - - let subcommand_ret = match command { - Command::AuthTransfer(transfer_subcommand) => { - transfer_subcommand - .handle_subcommand(&mut wallet_core) - .await? - } - Command::ChainInfo(chain_subcommand) => { - chain_subcommand.handle_subcommand(&mut wallet_core).await? - } - Command::Account(account_subcommand) => { - account_subcommand - .handle_subcommand(&mut wallet_core) - .await? - } - Command::Pinata(pinata_subcommand) => { - pinata_subcommand - .handle_subcommand(&mut wallet_core) - .await? - } - Command::CheckHealth {} => { - let remote_program_ids = wallet_core - .sequencer_client - .get_program_ids() - .await - .expect("Error fetching program ids"); - let Some(authenticated_transfer_id) = remote_program_ids.get("authenticated_transfer") - else { - panic!("Missing authenticated transfer ID from remote"); - }; - if authenticated_transfer_id != &Program::authenticated_transfer_program().id() { - panic!("Local ID for authenticated transfer program is different from remote"); - } - let Some(token_id) = remote_program_ids.get("token") else { - panic!("Missing token program ID from remote"); - }; - if token_id != &Program::token().id() { - panic!("Local ID for token program is different from remote"); - } - let Some(circuit_id) = remote_program_ids.get("privacy_preserving_circuit") else { - panic!("Missing privacy preserving circuit ID from remote"); - }; - if circuit_id != &nssa::PRIVACY_PRESERVING_CIRCUIT_ID { - panic!("Local ID for privacy preserving circuit is different from remote"); - } - - println!("✅All looks good!"); - - SubcommandReturnValue::Empty - } - Command::Token(token_subcommand) => { - token_subcommand.handle_subcommand(&mut wallet_core).await? - } - Command::Config(config_subcommand) => { - config_subcommand - .handle_subcommand(&mut wallet_core) - .await? - } - }; - - Ok(subcommand_ret) -} - -pub async fn parse_block_range( - start: u64, - stop: u64, - seq_client: Arc, - wallet_core: &mut WalletCore, -) -> Result<()> { - for block_id in start..(stop + 1) { - let block = - borsh::from_slice::(&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_account_id, (key_chain, _)) in - &wallet_core.storage.user_data.default_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 account_id {acc_account_id:#?} with account object {res_acc:#?}" - ); - - affected_accounts.push((*acc_account_id, res_acc)); - } - } - } - } - - for keys_node in wallet_core - .storage - .user_data - .private_key_tree - .key_map - .values() - { - let acc_account_id = keys_node.account_id(); - let key_chain = &keys_node.value.0; - - 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 account_id {acc_account_id:#?} with account object {res_acc:#?}" - ); - - affected_accounts.push((acc_account_id, res_acc)); - } - } - } - } - - for (affected_account_id, new_acc) in affected_accounts { - wallet_core - .storage - .insert_private_account_data(affected_account_id, 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 - ); + pub async fn send_privacy_preserving_tx( + &self, + accounts: Vec, + instruction_data: &InstructionData, + program: &Program, + ) -> Result<(SendTxResponse, Vec), ExecutionFailureKind> { + self.send_privacy_preserving_tx_with_pre_check(accounts, instruction_data, program, |_| { + Ok(()) + }) + .await } - Ok(()) -} + pub async fn send_privacy_preserving_tx_with_pre_check( + &self, + accounts: Vec, + instruction_data: &InstructionData, + program: &Program, + tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, + ) -> Result<(SendTxResponse, Vec), ExecutionFailureKind> { + let acc_manager = privacy_preserving_tx::AccountManager::new(self, accounts).await?; -pub async fn execute_continious_run() -> Result<()> { - let config = fetch_config().await?; - let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); - let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?; + let pre_states = acc_manager.pre_states(); + tx_pre_check( + &pre_states + .iter() + .map(|pre| &pre.account) + .collect::>(), + )?; - let mut latest_block_num = seq_client.get_last_block().await?.last_block; - let mut curr_last_block = latest_block_num; - - loop { - parse_block_range( - curr_last_block, - latest_block_num, - seq_client.clone(), - &mut wallet_core, + let private_account_keys = acc_manager.private_account_keys(); + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &pre_states, + instruction_data, + acc_manager.visibility_mask(), + &produce_random_nonces(private_account_keys.len()), + &private_account_keys + .iter() + .map(|keys| (keys.npk.clone(), keys.ssk.clone())) + .collect::>(), + &acc_manager.private_account_auth(), + program, ) - .await?; + .unwrap(); - curr_last_block = latest_block_num + 1; + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + acc_manager.public_account_ids(), + Vec::from_iter(acc_manager.public_account_nonces()), + private_account_keys + .iter() + .map(|keys| (keys.npk.clone(), keys.ipk.clone(), keys.epk.clone())) + .collect(), + output, + ) + .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis( - config.seq_poll_timeout_millis, + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &acc_manager.witness_signing_keys(), + ); + let tx = PrivacyPreservingTransaction::new(message, witness_set); + + let shared_secrets = private_account_keys + .into_iter() + .map(|keys| keys.ssk) + .collect(); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secrets, )) - .await; - - latest_block_num = seq_client.get_last_block().await?.last_block; } } - -pub async fn execute_setup(password: String) -> Result<()> { - let config = fetch_config().await?; - let wallet_core = WalletCore::start_from_config_new_storage(config.clone(), password).await?; - - wallet_core.store_persistent_data().await?; - - Ok(()) -} diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 304d788..a8a4fbe 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -1,14 +1,16 @@ use anyhow::Result; -use clap::{CommandFactory, Parser}; +use clap::{CommandFactory as _, Parser as _}; use tokio::runtime::Builder; -use wallet::{Args, OverCommand, execute_continious_run, execute_setup, execute_subcommand}; +use wallet::cli::{Args, OverCommand, execute_continuous_run, execute_setup, execute_subcommand}; pub const NUM_THREADS: usize = 2; // TODO #169: We have sample configs for sequencer, but not for wallet // TODO #168: Why it requires config as a directory? Maybe better to deduce directory from config -// file path? TODO #172: Why it requires config as env var while sequencer_runner accepts as -// argument? TODO #171: Running pinata doesn't give output about transaction hash and etc. +// file path? +// TODO #172: Why it requires config as env var while sequencer_runner accepts as +// argument? +// TODO #171: Running pinata doesn't give output about transaction hash and etc. fn main() -> Result<()> { let runtime = Builder::new_multi_thread() .worker_threads(NUM_THREADS) @@ -21,22 +23,20 @@ fn main() -> Result<()> { env_logger::init(); runtime.block_on(async move { - if let Some(overcommand) = args.command { - match overcommand { + if let Some(over_command) = args.command { + match over_command { OverCommand::Command(command) => { - execute_subcommand(command).await.unwrap(); - } - OverCommand::Setup { password } => { - execute_setup(password).await.unwrap(); + let _output = execute_subcommand(command).await?; + Ok(()) } + OverCommand::Setup { password } => execute_setup(password).await, } - } else if args.continious_run { - execute_continious_run().await.unwrap(); + } else if args.continuous_run { + execute_continuous_run().await } else { let help = Args::command().render_long_help(); println!("{help}"); + Ok(()) } - }); - - Ok(()) + }) } diff --git a/wallet/src/pinata_interactions.rs b/wallet/src/pinata_interactions.rs deleted file mode 100644 index e5150c5..0000000 --- a/wallet/src/pinata_interactions.rs +++ /dev/null @@ -1,161 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; -use nssa::{AccountId, privacy_preserving_transaction::circuit}; -use nssa_core::{MembershipProof, SharedSecretKey, account::AccountWithMetadata}; - -use crate::{ - WalletCore, helperfunctions::produce_random_nonces, transaction_utils::AccountPreparedData, -}; - -impl WalletCore { - pub async fn claim_pinata( - &self, - pinata_account_id: AccountId, - winner_account_id: AccountId, - solution: u128, - ) -> Result { - let account_ids = vec![pinata_account_id, winner_account_id]; - let program_id = nssa::program::Program::pinata().id(); - let message = - nssa::public_transaction::Message::try_new(program_id, account_ids, vec![], solution) - .unwrap(); - - let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx_public(tx).await?) - } - - pub async fn claim_pinata_private_owned_account_already_initialized( - &self, - pinata_account_id: AccountId, - winner_account_id: AccountId, - solution: u128, - winner_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: winner_nsk, - npk: winner_npk, - ipk: winner_ipk, - auth_acc: winner_pre, - proof: _, - } = self - .private_acc_preparation(winner_account_id, true, false) - .await?; - - let pinata_acc = self.get_account_public(pinata_account_id).await.unwrap(); - - let program = nssa::program::Program::pinata(); - - let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_account_id); - - let eph_holder_winner = EphemeralKeyHolder::new(&winner_npk); - let shared_secret_winner = eph_holder_winner.calculate_shared_secret_sender(&winner_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[pinata_pre, winner_pre], - &nssa::program::Program::serialize_instruction(solution).unwrap(), - &[0, 1], - &produce_random_nonces(1), - &[(winner_npk.clone(), shared_secret_winner.clone())], - &[(winner_nsk.unwrap(), winner_proof)], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( - vec![pinata_account_id], - vec![], - vec![( - winner_npk.clone(), - winner_ipk.clone(), - eph_holder_winner.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let witness_set = - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - &message, - proof, - &[], - ); - let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( - message, - witness_set, - ); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_winner], - )) - } - - pub async fn claim_pinata_private_owned_account_not_initialized( - &self, - pinata_account_id: AccountId, - winner_account_id: AccountId, - solution: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: _, - npk: winner_npk, - ipk: winner_ipk, - auth_acc: winner_pre, - proof: _, - } = self - .private_acc_preparation(winner_account_id, false, false) - .await?; - - let pinata_acc = self.get_account_public(pinata_account_id).await.unwrap(); - - let program = nssa::program::Program::pinata(); - - let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_account_id); - - let eph_holder_winner = EphemeralKeyHolder::new(&winner_npk); - let shared_secret_winner = eph_holder_winner.calculate_shared_secret_sender(&winner_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[pinata_pre, winner_pre], - &nssa::program::Program::serialize_instruction(solution).unwrap(), - &[0, 2], - &produce_random_nonces(1), - &[(winner_npk.clone(), shared_secret_winner.clone())], - &[], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( - vec![pinata_account_id], - vec![], - vec![( - winner_npk.clone(), - winner_ipk.clone(), - eph_holder_winner.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let witness_set = - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - &message, - proof, - &[], - ); - let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( - message, - witness_set, - ); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_winner], - )) - } -} diff --git a/wallet/src/privacy_preserving_tx.rs b/wallet/src/privacy_preserving_tx.rs new file mode 100644 index 0000000..e8e14d9 --- /dev/null +++ b/wallet/src/privacy_preserving_tx.rs @@ -0,0 +1,212 @@ +use common::error::ExecutionFailureKind; +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::{AccountId, PrivateKey}; +use nssa_core::{ + MembershipProof, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, + account::{AccountWithMetadata, Nonce}, + encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, +}; + +use crate::WalletCore; + +pub enum PrivacyPreservingAccount { + Public(AccountId), + PrivateOwned(AccountId), + PrivateForeign { + npk: NullifierPublicKey, + ipk: IncomingViewingPublicKey, + }, +} + +pub struct PrivateAccountKeys { + pub npk: NullifierPublicKey, + pub ssk: SharedSecretKey, + pub ipk: IncomingViewingPublicKey, + pub epk: EphemeralPublicKey, +} + +enum State { + Public { + account: AccountWithMetadata, + sk: Option, + }, + Private(AccountPreparedData), +} + +pub struct AccountManager { + states: Vec, + visibility_mask: Vec, +} + +impl AccountManager { + pub async fn new( + wallet: &WalletCore, + accounts: Vec, + ) -> Result { + let mut pre_states = Vec::with_capacity(accounts.len()); + let mut visibility_mask = Vec::with_capacity(accounts.len()); + + for account in accounts { + let (state, mask) = match account { + PrivacyPreservingAccount::Public(account_id) => { + let acc = wallet + .get_account_public(account_id) + .await + .map_err(|_| ExecutionFailureKind::KeyNotFoundError)?; + + let sk = wallet.get_account_public_signing_key(&account_id).cloned(); + let account = AccountWithMetadata::new(acc.clone(), sk.is_some(), account_id); + + (State::Public { account, sk }, 0) + } + PrivacyPreservingAccount::PrivateOwned(account_id) => { + let pre = private_acc_preparation(wallet, account_id).await?; + let mask = if pre.auth_acc.is_authorized { 1 } else { 2 }; + + (State::Private(pre), mask) + } + PrivacyPreservingAccount::PrivateForeign { npk, ipk } => { + let acc = nssa_core::account::Account::default(); + let auth_acc = AccountWithMetadata::new(acc, false, &npk); + let pre = AccountPreparedData { + nsk: None, + npk, + ipk, + auth_acc, + proof: None, + }; + + (State::Private(pre), 2) + } + }; + + pre_states.push(state); + visibility_mask.push(mask); + } + + Ok(Self { + states: pre_states, + visibility_mask, + }) + } + + pub fn pre_states(&self) -> Vec { + self.states + .iter() + .map(|state| match state { + State::Public { account, .. } => account.clone(), + State::Private(pre) => pre.auth_acc.clone(), + }) + .collect() + } + + pub fn visibility_mask(&self) -> &[u8] { + &self.visibility_mask + } + + pub fn public_account_nonces(&self) -> Vec { + self.states + .iter() + .filter_map(|state| match state { + State::Public { account, sk } => sk.as_ref().map(|_| account.account.nonce), + _ => None, + }) + .collect() + } + + pub fn private_account_keys(&self) -> Vec { + self.states + .iter() + .filter_map(|state| match state { + State::Private(pre) => { + let eph_holder = EphemeralKeyHolder::new(&pre.npk); + + Some(PrivateAccountKeys { + npk: pre.npk.clone(), + ssk: eph_holder.calculate_shared_secret_sender(&pre.ipk), + ipk: pre.ipk.clone(), + epk: eph_holder.generate_ephemeral_public_key(), + }) + } + _ => None, + }) + .collect() + } + + pub fn private_account_auth(&self) -> Vec<(NullifierSecretKey, MembershipProof)> { + self.states + .iter() + .filter_map(|state| match state { + State::Private(pre) => Some((pre.nsk?, pre.proof.clone()?)), + _ => None, + }) + .collect() + } + + pub fn public_account_ids(&self) -> Vec { + self.states + .iter() + .filter_map(|state| match state { + State::Public { account, .. } => Some(account.account_id), + _ => None, + }) + .collect() + } + + pub fn witness_signing_keys(&self) -> Vec<&PrivateKey> { + self.states + .iter() + .filter_map(|state| match state { + State::Public { sk, .. } => sk.as_ref(), + _ => None, + }) + .collect() + } +} + +struct AccountPreparedData { + nsk: Option, + npk: NullifierPublicKey, + ipk: IncomingViewingPublicKey, + auth_acc: AccountWithMetadata, + proof: Option, +} + +async fn private_acc_preparation( + wallet: &WalletCore, + account_id: AccountId, +) -> Result { + let Some((from_keys, from_acc)) = wallet + .storage + .user_data + .get_private_account(&account_id) + .cloned() + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let mut nsk = Some(from_keys.private_key_holder.nullifier_secret_key); + + let from_npk = from_keys.nullifer_public_key; + let from_ipk = from_keys.incoming_viewing_public_key; + + // TODO: Remove this unwrap, error types must be compatible + let proof = wallet + .check_private_account_initialized(&account_id) + .await + .unwrap(); + + if proof.is_none() { + nsk = None; + } + + let sender_pre = AccountWithMetadata::new(from_acc.clone(), proof.is_some(), &from_npk); + + Ok(AccountPreparedData { + nsk, + npk: from_npk, + ipk: from_ipk, + auth_acc: sender_pre, + proof, + }) +} diff --git a/wallet/src/program_facades/mod.rs b/wallet/src/program_facades/mod.rs new file mode 100644 index 0000000..27d30ce --- /dev/null +++ b/wallet/src/program_facades/mod.rs @@ -0,0 +1,6 @@ +//! This module contains [`WalletCore`](crate::WalletCore) facades for interacting with various +//! on-chain programs. + +pub mod native_token_transfer; +pub mod pinata; +pub mod token; diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs new file mode 100644 index 0000000..a25be2c --- /dev/null +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -0,0 +1,35 @@ +use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::AccountId; + +use super::{NativeTokenTransfer, auth_transfer_preparation}; +use crate::PrivacyPreservingAccount; + +impl NativeTokenTransfer<'_> { + pub async fn send_deshielded_transfer( + &self, + from: AccountId, + to: AccountId, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![ + PrivacyPreservingAccount::PrivateOwned(from), + PrivacyPreservingAccount::Public(to), + ], + &instruction_data, + &program, + tx_pre_check, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected sender's secret"); + (resp, first) + }) + } +} diff --git a/wallet/src/program_facades/native_token_transfer/mod.rs b/wallet/src/program_facades/native_token_transfer/mod.rs new file mode 100644 index 0000000..693ef8d --- /dev/null +++ b/wallet/src/program_facades/native_token_transfer/mod.rs @@ -0,0 +1,33 @@ +use common::error::ExecutionFailureKind; +use nssa::{Account, program::Program}; +use nssa_core::program::InstructionData; + +use crate::WalletCore; + +pub mod deshielded; +pub mod private; +pub mod public; +pub mod shielded; + +pub struct NativeTokenTransfer<'w>(pub &'w WalletCore); + +fn auth_transfer_preparation( + balance_to_move: u128, +) -> ( + InstructionData, + Program, + impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, +) { + let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); + let program = Program::authenticated_transfer_program(); + let tx_pre_check = move |accounts: &[&Account]| { + let from = accounts[0]; + if from.balance >= balance_to_move { + Ok(()) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + }; + + (instruction_data, program, tx_pre_check) +} diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs new file mode 100644 index 0000000..fcf6eee --- /dev/null +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -0,0 +1,89 @@ +use std::vec; + +use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::{AccountId, program::Program}; +use nssa_core::{NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey}; + +use super::{NativeTokenTransfer, auth_transfer_preparation}; +use crate::PrivacyPreservingAccount; + +impl NativeTokenTransfer<'_> { + pub async fn register_account_private( + &self, + from: AccountId, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let instruction: u128 = 0; + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![PrivacyPreservingAccount::PrivateOwned(from)], + &Program::serialize_instruction(instruction).unwrap(), + &Program::authenticated_transfer_program(), + |_| Ok(()), + ) + .await + .map(|(resp, secrets)| { + let mut secrets_iter = secrets.into_iter(); + let first = secrets_iter.next().expect("expected sender's secret"); + (resp, first) + }) + } + + pub async fn send_private_transfer_to_outer_account( + &self, + from: AccountId, + to_npk: NullifierPublicKey, + to_ipk: IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![ + PrivacyPreservingAccount::PrivateOwned(from), + PrivacyPreservingAccount::PrivateForeign { + npk: to_npk, + ipk: to_ipk, + }, + ], + &instruction_data, + &program, + tx_pre_check, + ) + .await + .map(|(resp, secrets)| { + let mut secrets_iter = secrets.into_iter(); + let first = secrets_iter.next().expect("expected sender's secret"); + let second = secrets_iter.next().expect("expected receiver's secret"); + (resp, [first, second]) + }) + } + + pub async fn send_private_transfer_to_owned_account( + &self, + from: AccountId, + to: AccountId, + balance_to_move: u128, + ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![ + PrivacyPreservingAccount::PrivateOwned(from), + PrivacyPreservingAccount::PrivateOwned(to), + ], + &instruction_data, + &program, + tx_pre_check, + ) + .await + .map(|(resp, secrets)| { + let mut secrets_iter = secrets.into_iter(); + let first = secrets_iter.next().expect("expected sender's secret"); + let second = secrets_iter.next().expect("expected receiver's secret"); + (resp, [first, second]) + }) + } +} diff --git a/wallet/src/token_transfers/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs similarity index 73% rename from wallet/src/token_transfers/public.rs rename to wallet/src/program_facades/native_token_transfer/public.rs index a63d838..2edab15 100644 --- a/wallet/src/token_transfers/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -5,21 +5,21 @@ use nssa::{ public_transaction::{Message, WitnessSet}, }; -use crate::WalletCore; +use super::NativeTokenTransfer; -impl WalletCore { - pub async fn send_public_native_token_transfer( +impl NativeTokenTransfer<'_> { + pub async fn send_public_transfer( &self, from: AccountId, to: AccountId, balance_to_move: u128, ) -> Result { - let Ok(balance) = self.get_account_balance(from).await else { + let Ok(balance) = self.0.get_account_balance(from).await else { return Err(ExecutionFailureKind::SequencerError); }; if balance >= balance_to_move { - let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else { + let Ok(nonces) = self.0.get_accounts_nonces(vec![from]).await else { return Err(ExecutionFailureKind::SequencerError); }; @@ -28,7 +28,7 @@ impl WalletCore { let message = Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap(); - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + let signing_key = self.0.storage.user_data.get_pub_account_signing_key(&from); let Some(signing_key) = signing_key else { return Err(ExecutionFailureKind::KeyNotFoundError); @@ -38,17 +38,17 @@ impl WalletCore { let tx = PublicTransaction::new(message, witness_set); - Ok(self.sequencer_client.send_tx_public(tx).await?) + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } else { Err(ExecutionFailureKind::InsufficientFundsError) } } - pub async fn register_account_under_authenticated_transfers_programs( + pub async fn register_account( &self, from: AccountId, ) -> Result { - let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else { + let Ok(nonces) = self.0.get_accounts_nonces(vec![from]).await else { return Err(ExecutionFailureKind::SequencerError); }; @@ -57,7 +57,7 @@ impl WalletCore { let program_id = Program::authenticated_transfer_program().id(); let message = Message::try_new(program_id, account_ids, nonces, instruction).unwrap(); - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + let signing_key = self.0.storage.user_data.get_pub_account_signing_key(&from); let Some(signing_key) = signing_key else { return Err(ExecutionFailureKind::KeyNotFoundError); @@ -67,6 +67,6 @@ impl WalletCore { let tx = PublicTransaction::new(message, witness_set); - Ok(self.sequencer_client.send_tx_public(tx).await?) + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } } diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs new file mode 100644 index 0000000..c049b13 --- /dev/null +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -0,0 +1,68 @@ +use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::AccountId; +use nssa_core::{NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey}; + +use super::{NativeTokenTransfer, auth_transfer_preparation}; +use crate::PrivacyPreservingAccount; + +impl NativeTokenTransfer<'_> { + pub async fn send_shielded_transfer( + &self, + from: AccountId, + to: AccountId, + balance_to_move: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![ + PrivacyPreservingAccount::Public(from), + PrivacyPreservingAccount::PrivateOwned(to), + ], + &instruction_data, + &program, + tx_pre_check, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected sender's secret"); + (resp, first) + }) + } + + pub async fn send_shielded_transfer_to_outer_account( + &self, + from: AccountId, + to_npk: NullifierPublicKey, + to_ipk: IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + + self.0 + .send_privacy_preserving_tx_with_pre_check( + vec![ + PrivacyPreservingAccount::Public(from), + PrivacyPreservingAccount::PrivateForeign { + npk: to_npk, + ipk: to_ipk, + }, + ], + &instruction_data, + &program, + tx_pre_check, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected sender's secret"); + (resp, first) + }) + } +} diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs new file mode 100644 index 0000000..46bc7a1 --- /dev/null +++ b/wallet/src/program_facades/pinata.rs @@ -0,0 +1,52 @@ +use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::AccountId; +use nssa_core::SharedSecretKey; + +use crate::{PrivacyPreservingAccount, WalletCore}; + +pub struct Pinata<'w>(pub &'w WalletCore); + +impl Pinata<'_> { + pub async fn claim( + &self, + pinata_account_id: AccountId, + winner_account_id: AccountId, + solution: u128, + ) -> Result { + let account_ids = vec![pinata_account_id, winner_account_id]; + let program_id = nssa::program::Program::pinata().id(); + let message = + nssa::public_transaction::Message::try_new(program_id, account_ids, vec![], solution) + .unwrap(); + + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.0.sequencer_client.send_tx_public(tx).await?) + } + + pub async fn claim_private_owned_account( + &self, + pinata_account_id: AccountId, + winner_account_id: AccountId, + solution: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::Public(pinata_account_id), + PrivacyPreservingAccount::PrivateOwned(winner_account_id), + ], + &nssa::program::Program::serialize_instruction(solution).unwrap(), + &nssa::program::Program::pinata(), + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected recipient's secret"); + (resp, first) + }) + } +} diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs new file mode 100644 index 0000000..298c4f4 --- /dev/null +++ b/wallet/src/program_facades/token.rs @@ -0,0 +1,275 @@ +use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::{AccountId, program::Program}; +use nssa_core::{ + NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey, + program::InstructionData, +}; + +use crate::{PrivacyPreservingAccount, WalletCore}; + +pub struct Token<'w>(pub &'w WalletCore); + +impl Token<'_> { + pub async fn send_new_definition( + &self, + definition_account_id: AccountId, + supply_account_id: AccountId, + name: [u8; 6], + total_supply: u128, + ) -> Result { + let account_ids = vec![definition_account_id, supply_account_id]; + let program_id = nssa::program::Program::token().id(); + // Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] + let mut instruction = [0; 23]; + instruction[1..17].copy_from_slice(&total_supply.to_le_bytes()); + instruction[17..].copy_from_slice(&name); + let message = nssa::public_transaction::Message::try_new( + program_id, + account_ids, + vec![], + instruction, + ) + .unwrap(); + + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.0.sequencer_client.send_tx_public(tx).await?) + } + + pub async fn send_new_definition_private_owned( + &self, + definition_account_id: AccountId, + supply_account_id: AccountId, + name: [u8; 6], + total_supply: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_definition(name, total_supply); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::Public(definition_account_id), + PrivacyPreservingAccount::PrivateOwned(supply_account_id), + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected recipient's secret"); + (resp, first) + }) + } + + pub async fn send_transfer_transaction( + &self, + sender_account_id: AccountId, + recipient_account_id: AccountId, + amount: u128, + ) -> Result { + let account_ids = vec![sender_account_id, recipient_account_id]; + let program_id = nssa::program::Program::token().id(); + // Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || + // 0x00 || 0x00 || 0x00]. + let mut instruction = [0; 23]; + instruction[0] = 0x01; + instruction[1..17].copy_from_slice(&amount.to_le_bytes()); + let Ok(nonces) = self.0.get_accounts_nonces(vec![sender_account_id]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let message = nssa::public_transaction::Message::try_new( + program_id, + account_ids, + nonces, + instruction, + ) + .unwrap(); + + let Some(signing_key) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&sender_account_id) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.0.sequencer_client.send_tx_public(tx).await?) + } + + pub async fn send_transfer_transaction_private_owned_account( + &self, + sender_account_id: AccountId, + recipient_account_id: AccountId, + amount: u128, + ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_transfer(amount); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::PrivateOwned(sender_account_id), + PrivacyPreservingAccount::PrivateOwned(recipient_account_id), + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let mut iter = secrets.into_iter(); + let first = iter.next().expect("expected sender's secret"); + let second = iter.next().expect("expected recipient's secret"); + (resp, [first, second]) + }) + } + + pub async fn send_transfer_transaction_private_foreign_account( + &self, + sender_account_id: AccountId, + recipient_npk: NullifierPublicKey, + recipient_ipk: IncomingViewingPublicKey, + amount: u128, + ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_transfer(amount); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::PrivateOwned(sender_account_id), + PrivacyPreservingAccount::PrivateForeign { + npk: recipient_npk, + ipk: recipient_ipk, + }, + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let mut iter = secrets.into_iter(); + let first = iter.next().expect("expected sender's secret"); + let second = iter.next().expect("expected recipient's secret"); + (resp, [first, second]) + }) + } + + pub async fn send_transfer_transaction_deshielded( + &self, + sender_account_id: AccountId, + recipient_account_id: AccountId, + amount: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_transfer(amount); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::PrivateOwned(sender_account_id), + PrivacyPreservingAccount::Public(recipient_account_id), + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected sender's secret"); + (resp, first) + }) + } + + pub async fn send_transfer_transaction_shielded_owned_account( + &self, + sender_account_id: AccountId, + recipient_account_id: AccountId, + amount: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_transfer(amount); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::Public(sender_account_id), + PrivacyPreservingAccount::PrivateOwned(recipient_account_id), + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected recipient's secret"); + (resp, first) + }) + } + + pub async fn send_transfer_transaction_shielded_foreign_account( + &self, + sender_account_id: AccountId, + recipient_npk: NullifierPublicKey, + recipient_ipk: IncomingViewingPublicKey, + amount: u128, + ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> { + let (instruction_data, program) = token_program_preparation_transfer(amount); + + self.0 + .send_privacy_preserving_tx( + vec![ + PrivacyPreservingAccount::Public(sender_account_id), + PrivacyPreservingAccount::PrivateForeign { + npk: recipient_npk, + ipk: recipient_ipk, + }, + ], + &instruction_data, + &program, + ) + .await + .map(|(resp, secrets)| { + let first = secrets + .into_iter() + .next() + .expect("expected recipient's secret"); + (resp, first) + }) + } +} + +fn token_program_preparation_transfer(amount: u128) -> (InstructionData, Program) { + // Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || + // 0x00 || 0x00 || 0x00]. + let mut instruction = [0; 23]; + instruction[0] = 0x01; + instruction[1..17].copy_from_slice(&amount.to_le_bytes()); + let instruction_data = Program::serialize_instruction(instruction).unwrap(); + let program = Program::token(); + + (instruction_data, program) +} + +fn token_program_preparation_definition( + name: [u8; 6], + total_supply: u128, +) -> (InstructionData, Program) { + // Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] + let mut instruction = [0; 23]; + instruction[1..17].copy_from_slice(&total_supply.to_le_bytes()); + instruction[17..].copy_from_slice(&name); + let instruction_data = Program::serialize_instruction(instruction).unwrap(); + let program = Program::token(); + + (instruction_data, program) +} diff --git a/wallet/src/token_program_interactions.rs b/wallet/src/token_program_interactions.rs deleted file mode 100644 index c441842..0000000 --- a/wallet/src/token_program_interactions.rs +++ /dev/null @@ -1,278 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use nssa::{Account, AccountId, program::Program}; -use nssa_core::{ - MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey, - program::InstructionData, -}; - -use crate::WalletCore; - -impl WalletCore { - pub fn token_program_preparation_transfer( - amount: u128, - ) -> ( - InstructionData, - Program, - impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - ) { - // Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || - // 0x00 || 0x00 || 0x00]. - let mut instruction = [0; 23]; - instruction[0] = 0x01; - instruction[1..17].copy_from_slice(&amount.to_le_bytes()); - let instruction_data = Program::serialize_instruction(instruction).unwrap(); - let program = Program::token(); - let tx_pre_check = |_: &Account, _: &Account| Ok(()); - - (instruction_data, program, tx_pre_check) - } - - pub fn token_program_preparation_definition( - name: [u8; 6], - total_supply: u128, - ) -> ( - InstructionData, - Program, - impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - ) { - // Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] - let mut instruction = [0; 23]; - instruction[1..17].copy_from_slice(&total_supply.to_le_bytes()); - instruction[17..].copy_from_slice(&name); - let instruction_data = Program::serialize_instruction(instruction).unwrap(); - let program = Program::token(); - let tx_pre_check = |_: &Account, _: &Account| Ok(()); - - (instruction_data, program, tx_pre_check) - } - - pub async fn send_new_token_definition( - &self, - definition_account_id: AccountId, - supply_account_id: AccountId, - name: [u8; 6], - total_supply: u128, - ) -> Result { - let account_ids = vec![definition_account_id, supply_account_id]; - let program_id = nssa::program::Program::token().id(); - // Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] - let mut instruction = [0; 23]; - instruction[1..17].copy_from_slice(&total_supply.to_le_bytes()); - instruction[17..].copy_from_slice(&name); - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - vec![], - instruction, - ) - .unwrap(); - - let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx_public(tx).await?) - } - - pub async fn send_new_token_definition_private_owned( - &self, - definition_account_id: AccountId, - supply_account_id: AccountId, - name: [u8; 6], - total_supply: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_definition(name, total_supply); - - // Kind of non-obvious naming - // Basically this funtion is called because authentication mask is [0, 2] - self.shielded_two_accs_receiver_uninit( - definition_account_id, - supply_account_id, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_transfer_token_transaction( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - ) -> Result { - let account_ids = vec![sender_account_id, recipient_account_id]; - let program_id = nssa::program::Program::token().id(); - // Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || - // 0x00 || 0x00 || 0x00]. - let mut instruction = [0; 23]; - instruction[0] = 0x01; - instruction[1..17].copy_from_slice(&amount.to_le_bytes()); - let Ok(nonces) = self.get_accounts_nonces(vec![sender_account_id]).await else { - return Err(ExecutionFailureKind::SequencerError); - }; - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let Some(signing_key) = self - .storage - .user_data - .get_pub_account_signing_key(&sender_account_id) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx_public(tx).await?) - } - - pub async fn send_transfer_token_transaction_private_owned_account_already_initialized( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - recipient_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.private_tx_two_accs_all_init( - sender_account_id, - recipient_account_id, - instruction_data, - tx_pre_check, - program, - recipient_proof, - ) - .await - } - - pub async fn send_transfer_token_transaction_private_owned_account_not_initialized( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.private_tx_two_accs_receiver_uninit( - sender_account_id, - recipient_account_id, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_transfer_token_transaction_private_foreign_account( - &self, - sender_account_id: AccountId, - recipient_npk: NullifierPublicKey, - recipient_ipk: IncomingViewingPublicKey, - amount: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.private_tx_two_accs_receiver_outer( - sender_account_id, - recipient_npk, - recipient_ipk, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_transfer_token_transaction_deshielded( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.deshielded_tx_two_accs( - sender_account_id, - recipient_account_id, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_transfer_token_transaction_shielded_owned_account_already_initialized( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - recipient_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.shielded_two_accs_all_init( - sender_account_id, - recipient_account_id, - instruction_data, - tx_pre_check, - program, - recipient_proof, - ) - .await - } - - pub async fn send_transfer_token_transaction_shielded_owned_account_not_initialized( - &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, - amount: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.shielded_two_accs_receiver_uninit( - sender_account_id, - recipient_account_id, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_transfer_token_transaction_shielded_foreign_account( - &self, - sender_account_id: AccountId, - recipient_npk: NullifierPublicKey, - recipient_ipk: IncomingViewingPublicKey, - amount: u128, - ) -> Result { - let (instruction_data, program, tx_pre_check) = - WalletCore::token_program_preparation_transfer(amount); - - self.shielded_two_accs_receiver_outer( - sender_account_id, - recipient_npk, - recipient_ipk, - instruction_data, - tx_pre_check, - program, - ) - .await - } -} diff --git a/wallet/src/token_transfers/deshielded.rs b/wallet/src/token_transfers/deshielded.rs deleted file mode 100644 index 4c8cbe3..0000000 --- a/wallet/src/token_transfers/deshielded.rs +++ /dev/null @@ -1,19 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use nssa::AccountId; - -use crate::WalletCore; - -impl WalletCore { - pub async fn send_deshielded_native_token_transfer( - &self, - from: AccountId, - to: AccountId, - balance_to_move: u128, - ) -> Result<(SendTxResponse, [nssa_core::SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.deshielded_tx_two_accs(from, to, instruction_data, tx_pre_check, program) - .await - } -} diff --git a/wallet/src/token_transfers/mod.rs b/wallet/src/token_transfers/mod.rs deleted file mode 100644 index a785763..0000000 --- a/wallet/src/token_transfers/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use common::error::ExecutionFailureKind; -use nssa::{Account, program::Program}; -use nssa_core::program::InstructionData; - -use crate::WalletCore; - -pub mod deshielded; -pub mod private; -pub mod public; -pub mod shielded; - -impl WalletCore { - pub fn auth_transfer_preparation( - balance_to_move: u128, - ) -> ( - InstructionData, - Program, - impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - ) { - let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); - let program = Program::authenticated_transfer_program(); - let tx_pre_check = move |from: &Account, _: &Account| { - if from.balance >= balance_to_move { - Ok(()) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - }; - - (instruction_data, program, tx_pre_check) - } -} diff --git a/wallet/src/token_transfers/private.rs b/wallet/src/token_transfers/private.rs deleted file mode 100644 index 35d3e3b..0000000 --- a/wallet/src/token_transfers/private.rs +++ /dev/null @@ -1,64 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use nssa::AccountId; -use nssa_core::{ - MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey, -}; - -use crate::WalletCore; - -impl WalletCore { - pub async fn send_private_native_token_transfer_outer_account( - &self, - from: AccountId, - to_npk: NullifierPublicKey, - to_ipk: IncomingViewingPublicKey, - balance_to_move: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.private_tx_two_accs_receiver_outer( - from, - to_npk, - to_ipk, - instruction_data, - tx_pre_check, - program, - ) - .await - } - - pub async fn send_private_native_token_transfer_owned_account_not_initialized( - &self, - from: AccountId, - to: AccountId, - balance_to_move: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.private_tx_two_accs_receiver_uninit(from, to, instruction_data, tx_pre_check, program) - .await - } - - pub async fn send_private_native_token_transfer_owned_account_already_initialized( - &self, - from: AccountId, - to: AccountId, - balance_to_move: u128, - to_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.private_tx_two_accs_all_init( - from, - to, - instruction_data, - tx_pre_check, - program, - to_proof, - ) - .await - } -} diff --git a/wallet/src/token_transfers/shielded.rs b/wallet/src/token_transfers/shielded.rs deleted file mode 100644 index 8ba260c..0000000 --- a/wallet/src/token_transfers/shielded.rs +++ /dev/null @@ -1,57 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use nssa::AccountId; -use nssa_core::{ - MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey, -}; - -use crate::WalletCore; - -impl WalletCore { - pub async fn send_shielded_native_token_transfer_already_initialized( - &self, - from: AccountId, - to: AccountId, - balance_to_move: u128, - to_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.shielded_two_accs_all_init(from, to, instruction_data, tx_pre_check, program, to_proof) - .await - } - - pub async fn send_shielded_native_token_transfer_not_initialized( - &self, - from: AccountId, - to: AccountId, - balance_to_move: u128, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.shielded_two_accs_receiver_uninit(from, to, instruction_data, tx_pre_check, program) - .await - } - - pub async fn send_shielded_native_token_transfer_outer_account( - &self, - from: AccountId, - to_npk: NullifierPublicKey, - to_ipk: IncomingViewingPublicKey, - balance_to_move: u128, - ) -> Result { - let (instruction_data, program, tx_pre_check) = - WalletCore::auth_transfer_preparation(balance_to_move); - - self.shielded_two_accs_receiver_outer( - from, - to_npk, - to_ipk, - instruction_data, - tx_pre_check, - program, - ) - .await - } -} diff --git a/wallet/src/transaction_utils.rs b/wallet/src/transaction_utils.rs deleted file mode 100644 index a54f81c..0000000 --- a/wallet/src/transaction_utils.rs +++ /dev/null @@ -1,592 +0,0 @@ -use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; -use nssa::{ - Account, AccountId, PrivacyPreservingTransaction, - privacy_preserving_transaction::{circuit, message::Message, witness_set::WitnessSet}, - program::Program, -}; -use nssa_core::{ - Commitment, MembershipProof, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, - account::AccountWithMetadata, encryption::IncomingViewingPublicKey, program::InstructionData, -}; - -use crate::{WalletCore, helperfunctions::produce_random_nonces}; - -pub(crate) struct AccountPreparedData { - pub nsk: Option, - pub npk: NullifierPublicKey, - pub ipk: IncomingViewingPublicKey, - pub auth_acc: AccountWithMetadata, - pub proof: Option, -} - -impl WalletCore { - pub(crate) async fn private_acc_preparation( - &self, - account_id: AccountId, - is_authorized: bool, - needs_proof: bool, - ) -> Result { - let Some((from_keys, from_acc)) = self - .storage - .user_data - .get_private_account(&account_id) - .cloned() - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let mut nsk = None; - let mut proof = None; - - let from_npk = from_keys.nullifer_public_key; - let from_ipk = from_keys.incoming_viewing_public_key; - - let sender_commitment = Commitment::new(&from_npk, &from_acc); - - let sender_pre = AccountWithMetadata::new(from_acc.clone(), is_authorized, &from_npk); - - if is_authorized { - nsk = Some(from_keys.private_key_holder.nullifier_secret_key); - } - - if needs_proof { - proof = self - .sequencer_client - .get_proof_for_commitment(sender_commitment) - .await - .unwrap(); - } - - Ok(AccountPreparedData { - nsk, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof, - }) - } - - pub(crate) async fn private_tx_two_accs_all_init( - &self, - from: AccountId, - to: AccountId, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - to_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: from_nsk, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof: from_proof, - } = self.private_acc_preparation(from, true, true).await?; - - let AccountPreparedData { - nsk: to_nsk, - npk: to_npk, - ipk: to_ipk, - auth_acc: recipient_pre, - proof: _, - } = self.private_acc_preparation(to, true, false).await?; - - tx_pre_check(&sender_pre.account, &recipient_pre.account)?; - - let eph_holder_from = EphemeralKeyHolder::new(&from_npk); - let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk); - - let eph_holder_to = EphemeralKeyHolder::new(&to_npk); - let shared_secret_to = eph_holder_to.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[1, 1], - &produce_random_nonces(2), - &[ - (from_npk.clone(), shared_secret_from.clone()), - (to_npk.clone(), shared_secret_to.clone()), - ], - &[ - (from_nsk.unwrap(), from_proof.unwrap()), - (to_nsk.unwrap(), to_proof), - ], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![ - ( - from_npk.clone(), - from_ipk.clone(), - eph_holder_from.generate_ephemeral_public_key(), - ), - ( - to_npk.clone(), - to_ipk.clone(), - eph_holder_to.generate_ephemeral_public_key(), - ), - ], - output, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, proof, &[]); - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_from, shared_secret_to], - )) - } - - pub(crate) async fn private_tx_two_accs_receiver_uninit( - &self, - from: AccountId, - to: AccountId, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: from_nsk, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof: from_proof, - } = self.private_acc_preparation(from, true, true).await?; - - let AccountPreparedData { - nsk: _, - npk: to_npk, - ipk: to_ipk, - auth_acc: recipient_pre, - proof: _, - } = self.private_acc_preparation(to, false, false).await?; - - tx_pre_check(&sender_pre.account, &recipient_pre.account)?; - - let eph_holder_from = EphemeralKeyHolder::new(&from_npk); - let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk); - - let eph_holder_to = EphemeralKeyHolder::new(&to_npk); - let shared_secret_to = eph_holder_to.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[1, 2], - &produce_random_nonces(2), - &[ - (from_npk.clone(), shared_secret_from.clone()), - (to_npk.clone(), shared_secret_to.clone()), - ], - &[(from_nsk.unwrap(), from_proof.unwrap())], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![ - ( - from_npk.clone(), - from_ipk.clone(), - eph_holder_from.generate_ephemeral_public_key(), - ), - ( - to_npk.clone(), - to_ipk.clone(), - eph_holder_to.generate_ephemeral_public_key(), - ), - ], - output, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, proof, &[]); - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_from, shared_secret_to], - )) - } - - pub(crate) async fn private_tx_two_accs_receiver_outer( - &self, - from: AccountId, - to_npk: NullifierPublicKey, - to_ipk: IncomingViewingPublicKey, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: from_nsk, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof: from_proof, - } = self.private_acc_preparation(from, true, true).await?; - - let to_acc = nssa_core::account::Account::default(); - - tx_pre_check(&sender_pre.account, &to_acc)?; - - let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk); - - let eph_holder = EphemeralKeyHolder::new(&to_npk); - - let shared_secret_from = eph_holder.calculate_shared_secret_sender(&from_ipk); - let shared_secret_to = eph_holder.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[1, 2], - &produce_random_nonces(2), - &[ - (from_npk.clone(), shared_secret_from.clone()), - (to_npk.clone(), shared_secret_to.clone()), - ], - &[(from_nsk.unwrap(), from_proof.unwrap())], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![ - ( - from_npk.clone(), - from_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - ), - ( - to_npk.clone(), - to_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - ), - ], - output, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, proof, &[]); - - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_from, shared_secret_to], - )) - } - - pub(crate) async fn deshielded_tx_two_accs( - &self, - from: AccountId, - to: AccountId, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - ) -> Result<(SendTxResponse, [nssa_core::SharedSecretKey; 1]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: from_nsk, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof: from_proof, - } = self.private_acc_preparation(from, true, true).await?; - - let Ok(to_acc) = self.get_account_public(to).await else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - tx_pre_check(&sender_pre.account, &to_acc)?; - - let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, to); - - let eph_holder = EphemeralKeyHolder::new(&from_npk); - let shared_secret = eph_holder.calculate_shared_secret_sender(&from_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[1, 0], - &produce_random_nonces(1), - &[(from_npk.clone(), shared_secret.clone())], - &[(from_nsk.unwrap(), from_proof.unwrap())], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![to], - vec![], - vec![( - from_npk.clone(), - from_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, proof, &[]); - - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret], - )) - } - - pub(crate) async fn shielded_two_accs_all_init( - &self, - from: AccountId, - to: AccountId, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - to_proof: MembershipProof, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let Ok(from_acc) = self.get_account_public(from).await else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let AccountPreparedData { - nsk: to_nsk, - npk: to_npk, - ipk: to_ipk, - auth_acc: recipient_pre, - proof: _, - } = self.private_acc_preparation(to, true, false).await?; - - tx_pre_check(&from_acc, &recipient_pre.account)?; - - let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from); - - let eph_holder = EphemeralKeyHolder::new(&to_npk); - let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[0, 1], - &produce_random_nonces(1), - &[(to_npk.clone(), shared_secret.clone())], - &[(to_nsk.unwrap(), to_proof)], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![from], - vec![from_acc.nonce], - vec![( - to_npk.clone(), - to_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); - - let Some(signing_key) = signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]); - - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret], - )) - } - - pub(crate) async fn shielded_two_accs_receiver_uninit( - &self, - from: AccountId, - to: AccountId, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let Ok(from_acc) = self.get_account_public(from).await else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let AccountPreparedData { - nsk: _, - npk: to_npk, - ipk: to_ipk, - auth_acc: recipient_pre, - proof: _, - } = self.private_acc_preparation(to, false, false).await?; - - tx_pre_check(&from_acc, &recipient_pre.account)?; - - let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from); - - let eph_holder = EphemeralKeyHolder::new(&to_npk); - let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[0, 2], - &produce_random_nonces(1), - &[(to_npk.clone(), shared_secret.clone())], - &[], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![from], - vec![from_acc.nonce], - vec![( - to_npk.clone(), - to_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); - - let Some(signing_key) = signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]); - - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret], - )) - } - - pub(crate) async fn shielded_two_accs_receiver_outer( - &self, - from: AccountId, - to_npk: NullifierPublicKey, - to_ipk: IncomingViewingPublicKey, - instruction_data: InstructionData, - tx_pre_check: impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>, - program: Program, - ) -> Result { - let Ok(from_acc) = self.get_account_public(from).await else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let to_acc = Account::default(); - - tx_pre_check(&from_acc, &to_acc)?; - - let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from); - let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk); - - let eph_holder = EphemeralKeyHolder::new(&to_npk); - let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk); - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &instruction_data, - &[0, 2], - &produce_random_nonces(1), - &[(to_npk.clone(), shared_secret.clone())], - &[], - &program, - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![from], - vec![from_acc.nonce], - vec![( - to_npk.clone(), - to_ipk.clone(), - eph_holder.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); - - let Some(signing_key) = signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]); - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx_private(tx).await?) - } - - pub async fn register_account_under_authenticated_transfers_programs_private( - &self, - from: AccountId, - ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { - let AccountPreparedData { - nsk: _, - npk: from_npk, - ipk: from_ipk, - auth_acc: sender_pre, - proof: _, - } = self.private_acc_preparation(from, false, false).await?; - - let eph_holder_from = EphemeralKeyHolder::new(&from_npk); - let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk); - - let instruction: u128 = 0; - - let (output, proof) = circuit::execute_and_prove( - &[sender_pre], - &Program::serialize_instruction(instruction).unwrap(), - &[2], - &produce_random_nonces(1), - &[(from_npk.clone(), shared_secret_from.clone())], - &[], - &Program::authenticated_transfer_program(), - ) - .unwrap(); - - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![( - from_npk.clone(), - from_ipk.clone(), - eph_holder_from.generate_ephemeral_public_key(), - )], - output, - ) - .unwrap(); - - let witness_set = WitnessSet::for_message(&message, proof, &[]); - let tx = PrivacyPreservingTransaction::new(message, witness_set); - - Ok(( - self.sequencer_client.send_tx_private(tx).await?, - [shared_secret_from], - )) - } -}