diff --git a/ci_scripts/lint-ubuntu.sh b/ci_scripts/lint-ubuntu.sh index 35a0e4a..8c60825 100755 --- a/ci_scripts/lint-ubuntu.sh +++ b/ci_scripts/lint-ubuntu.sh @@ -6,5 +6,4 @@ cargo install taplo-cli --locked cargo fmt -- --check taplo fmt --check -export RISC0_SKIP_BUILD=1 -cargo clippy --workspace --all-targets -- -D warnings +RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 20cb4ef..e96ddde 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -12,7 +12,7 @@ use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ - Command, WalletCore, + Command, SubcommandReturnValue, WalletCore, helperfunctions::{fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex}, }; @@ -384,7 +384,9 @@ pub async fn test_success_private_transfer_to_another_foreign_account() { let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + println!("SUB RET is {sub_ret:#?}"); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -431,6 +433,105 @@ pub async fn test_success_private_transfer_to_another_foreign_account() { info!("Success!"); } +pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() { + let command = Command::RegisterAccountPrivate {}; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { + panic!("FAILED TO REGISTER ACCOUNT"); + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = + WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + + let (to_keys, mut to_acc) = wallet_storage + .storage + .user_data + .user_private_accounts + .get(&to_addr) + .cloned() + .unwrap(); + + let command = Command::SendNativeTokenTransferPrivateForeignAccount { + from: ACC_SENDER_PRIVATE.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + panic!("FAILED TO SEND TX"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = { + let from_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&from) + .unwrap(); + + from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + from_acc.1.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let new_commitment2 = { + to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.balance = 100; + to_acc.nonce = 1; + + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc) + }; + + let proof1 = seq_client + .get_proof_for_commitment(new_commitment1) + .await + .unwrap() + .unwrap(); + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + println!("New proof is {proof1:#?}"); + println!("New proof is {proof2:#?}"); + + let command = Command::ClaimPrivateAccount { + tx_hash, + acc_addr: hex::encode(to_addr), + ciph_id: 1, + }; + + wallet::execute_subcommand(command).await.unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + let (_, to_res_acc) = wallet_storage + .storage + .user_data + .get_private_account(&to_addr) + .unwrap(); + + assert_eq!(to_res_acc.balance, 100); + + info!("Success!"); +} + pub async fn test_success_deshielded_transfer_to_another_account() { let command = Command::SendNativeTokenTransferDeshielded { from: ACC_SENDER_PRIVATE.to_string(), @@ -583,6 +684,88 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() { info!("Success!"); } +pub async fn test_success_shielded_transfer_to_another_owned_account_claiming_path() { + let command = Command::RegisterAccountPrivate {}; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { + panic!("FAILED TO REGISTER ACCOUNT"); + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + + let (to_keys, mut to_acc) = wallet_storage + .storage + .user_data + .user_private_accounts + .get(&to_addr) + .cloned() + .unwrap(); + + let command = Command::SendNativeTokenTransferShieldedForeignAccount { + from: ACC_SENDER.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + panic!("FAILED TO SEND TX"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment2 = { + to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.balance = 100; + to_acc.nonce = 1; + + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc) + }; + + let acc_1_balance = seq_client + .get_account_balance(ACC_SENDER.to_string()) + .await + .unwrap(); + + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + assert_eq!(acc_1_balance.balance, 9900); + println!("New proof is {proof2:#?}"); + + let command = Command::ClaimPrivateAccount { + tx_hash, + acc_addr: hex::encode(to_addr), + ciph_id: 0, + }; + + wallet::execute_subcommand(command).await.unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + let (_, to_res_acc) = wallet_storage + .storage + .user_data + .get_private_account(&to_addr) + .unwrap(); + + assert_eq!(to_res_acc.balance, 100); + + info!("Success!"); +} + pub async fn test_pinata() { let pinata_addr = "cafe".repeat(16); let pinata_prize = 150; @@ -677,6 +860,12 @@ pub async fn main_tests_runner() -> Result<()> { test_success_private_transfer_to_another_foreign_account ); } + "test_success_private_transfer_to_another_owned_account_claiming_path" => { + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_claiming_path + ); + } "test_success_deshielded_transfer_to_another_account" => { test_cleanup_wrap!( home_dir, @@ -695,6 +884,12 @@ pub async fn main_tests_runner() -> Result<()> { test_success_shielded_transfer_to_another_foreign_account ); } + "test_success_shielded_transfer_to_another_owned_account_claiming_path" => { + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_owned_account_claiming_path + ); + } "test_pinata" => { test_cleanup_wrap!(home_dir, test_pinata); } diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 05a6edd..d2227a8 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -12,8 +12,8 @@ pub type ViewTag = u8; #[derive(Debug, Clone, PartialEq, Eq)] pub struct EncryptedAccountData { pub ciphertext: Ciphertext, - pub(crate) epk: EphemeralPublicKey, - pub(crate) view_tag: ViewTag, + pub epk: EphemeralPublicKey, + pub view_tag: ViewTag, } impl EncryptedAccountData { diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index be31a66..26652d1 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -86,15 +86,6 @@ impl WalletCore { .generate_new_privacy_preserving_transaction_key_chain() } - // pub fn search_for_initial_account(&self, acc_addr: Address) -> Option { - // for initial_acc in &self.storage.wallet_config.initial_accounts { - // if initial_acc.address() == acc_addr { - // return Some(initial_acc.account().clone()); - // } - // } - // None - // } - pub async fn claim_pinata( &self, pinata_addr: Address, @@ -249,9 +240,6 @@ pub enum Command { ///acc_addr - valid 32 byte hex string #[arg(long)] acc_addr: String, - ///sh_secret - valid 32 byte hex string - #[arg(long)] - sh_secret: String, ///ciph_id - id of cipher in transaction #[arg(long)] ciph_id: usize, @@ -304,11 +292,18 @@ pub struct Args { pub command: Command, } -pub async fn execute_subcommand(command: Command) -> Result<()> { +#[derive(Debug, Clone)] +pub enum SubcommandReturnValue { + PrivacyPreservingTransfer { tx_hash: String }, + RegisterAccount { addr: nssa::Address }, + Empty, +} + +pub async fn execute_subcommand(command: Command) -> Result { let wallet_config = fetch_config()?; let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?; - match command { + let subcommand_ret = match command { Command::SendNativeTokenTransferPublic { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; let to = produce_account_addr_from_hex(to)?; @@ -326,6 +321,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::Empty } Command::SendNativeTokenTransferPrivate { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; @@ -337,7 +334,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let from_ebc = tx.message.encrypted_private_post_states[0].clone(); @@ -374,6 +374,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } } Command::SendNativeTokenTransferPrivateForeignAccount { from, @@ -399,7 +401,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let from_ebc = tx.message.encrypted_private_post_states[0].clone(); @@ -433,6 +438,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } } Command::SendNativeTokenTransferDeshielded { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; @@ -444,7 +451,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let from_ebc = tx.message.encrypted_private_post_states[0].clone(); @@ -470,6 +480,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } } Command::SendNativeTokenTransferShielded { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; @@ -481,7 +493,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let to_ebc = tx.message.encrypted_private_post_states[0].clone(); @@ -499,6 +514,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } } Command::SendNativeTokenTransferShieldedForeignAccount { from, @@ -527,7 +544,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let to_ebc = tx.message.encrypted_private_post_states[0].clone(); @@ -545,29 +565,36 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } } Command::ClaimPrivateAccount { tx_hash, acc_addr, - sh_secret, ciph_id, } => { let acc_addr = produce_account_addr_from_hex(acc_addr)?; - let secret_res = hex::decode(sh_secret)?; - let mut secret = [0; 32]; - secret.copy_from_slice(&secret_res); - let secret = nssa_core::SharedSecretKey(secret); + let account_key_chain = wallet_core + .storage + .user_data + .user_private_accounts + .get(&acc_addr); + + let Some((account_key_chain, _)) = account_key_chain else { + anyhow::bail!("Account not found"); + }; let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); let to_comm = tx.message.new_commitments[ciph_id].clone(); + let shared_secret = account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); let res_acc_to = nssa_core::EncryptionScheme::decrypt( &to_ebc.ciphertext, - &secret, + &shared_secret, &to_comm, ciph_id as u32, ) @@ -585,6 +612,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::Empty } Command::RegisterAccountPublic {} => { let addr = wallet_core.create_new_account_public(); @@ -594,6 +623,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::RegisterAccount { addr } } Command::RegisterAccountPrivate {} => { let addr = wallet_core.create_new_account_private(); @@ -611,6 +642,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let path = wallet_core.store_persistent_accounts()?; println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::RegisterAccount { addr } } Command::FetchTx { tx_hash } => { let tx_obj = wallet_core @@ -619,23 +652,31 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { .await?; println!("Transaction object {tx_obj:#?}"); + + SubcommandReturnValue::Empty } Command::GetAccountBalance { addr } => { let addr = Address::from_str(&addr)?; let balance = wallet_core.get_account_balance(addr).await?; println!("Accounts {addr} balance is {balance}"); + + SubcommandReturnValue::Empty } Command::GetAccountNonce { addr } => { let addr = Address::from_str(&addr)?; let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; println!("Accounts {addr} nonce is {nonce}"); + + SubcommandReturnValue::Empty } Command::GetAccount { addr } => { let addr: Address = addr.parse()?; let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into(); println!("{}", serde_json::to_string(&account).unwrap()); + + SubcommandReturnValue::Empty } Command::ClaimPinata { pinata_addr, @@ -650,8 +691,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { ) .await?; info!("Results of tx send is {res:#?}"); - } - } - Ok(()) + SubcommandReturnValue::Empty + } + }; + + Ok(subcommand_ret) }