From d3742867ed14a8b153e31fed5cfaf2cf00cd1e17 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Mon, 22 Sep 2025 16:38:25 +0300 Subject: [PATCH] fix: more integration tests --- integration_tests/src/lib.rs | 88 +++-- wallet/src/lib.rs | 62 +++ wallet/src/token_transfers.rs | 479 ----------------------- wallet/src/token_transfers/deshielded.rs | 185 +++++++++ wallet/src/token_transfers/mod.rs | 4 + wallet/src/token_transfers/private.rs | 222 +++++++++++ wallet/src/token_transfers/public.rs | 48 +++ wallet/src/token_transfers/shielded.rs | 188 +++++++++ 8 files changed, 772 insertions(+), 504 deletions(-) delete mode 100644 wallet/src/token_transfers.rs create mode 100644 wallet/src/token_transfers/deshielded.rs create mode 100644 wallet/src/token_transfers/mod.rs create mode 100644 wallet/src/token_transfers/private.rs create mode 100644 wallet/src/token_transfers/public.rs create mode 100644 wallet/src/token_transfers/shielded.rs diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 9480f59..7f29eaa 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -324,29 +324,6 @@ pub async fn test_success_private_transfer_to_another_owned_account() { let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); - let old_commitment1 = { - let from_acc = wallet_storage - .storage - .user_data - .get_private_account_mut(&from) - .unwrap(); - - nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) - }; - - let old_commitment2 = { - let to_acc = wallet_storage - .storage - .user_data - .get_private_account_mut(&to) - .unwrap(); - - nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1) - }; - - println!("Old commitment {old_commitment1:?}"); - println!("Old commitment {old_commitment2:?}"); - wallet::execute_subcommand(command).await.unwrap(); info!("Waiting for next block creation"); @@ -378,8 +355,69 @@ pub async fn test_success_private_transfer_to_another_owned_account() { nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1) }; - println!("New commitment {new_commitment1:?}"); - println!("New commitment {new_commitment2:?}"); + 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:#?}"); + + info!("Success!"); +} + +pub async fn test_success_private_transfer_to_another_foreign_account() { + let command = Command::SendNativeTokenTransferPrivate { + from: ACC_SENDER_PRIVATE.to_string(), + to: ACC_RECEIVER_PRIVATE.to_string(), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + let to = produce_account_addr_from_hex(ACC_RECEIVER_PRIVATE.to_string()).unwrap(); + + 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).unwrap(); + + wallet::execute_subcommand(command).await.unwrap(); + + 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.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let new_commitment2 = { + let to_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&to) + .unwrap(); + + to_acc.1.balance += 100; + to_acc.1.nonce += 1; + + nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1) + }; let proof1 = seq_client .get_proof_for_commitment(new_commitment1) diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 46c26d5..082b382 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -149,6 +149,23 @@ pub enum Command { #[arg(long)] amount: u128, }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Private operation + SendNativeTokenTransferPrivateForeignAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: String, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, ///Register new public account RegisterAccountPublic {}, ///Register new private account @@ -240,6 +257,51 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Transaction data is {:?}", tx.message); } } + Command::SendNativeTokenTransferPrivateForeignAccount { from, to_npk, to_ipk, amount } => { + let from = produce_account_addr_from_hex(from)?; + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); + + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + + let (res, secret) = wallet_core + .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let from_ebc = tx.message.encrypted_private_post_states[0].clone(); + let from_comm = tx.message.new_commitments[0].clone(); + + let to_ebc = tx.message.encrypted_private_post_states[1].clone(); + let to_comm = tx.message.new_commitments[1].clone(); + + let res_acc = nssa_core::EncryptionScheme::decrypt( + &from_ebc.ciphertext, + &secret, + &from_comm, + 0, + ) + .unwrap(); + + let res_acc_to = + nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 1) + .unwrap(); + + println!("RES acc {res_acc:#?}"); + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + } + } Command::RegisterAccountPublic {} => { let addr = wallet_core.create_new_account_public(); diff --git a/wallet/src/token_transfers.rs b/wallet/src/token_transfers.rs deleted file mode 100644 index 28ed600..0000000 --- a/wallet/src/token_transfers.rs +++ /dev/null @@ -1,479 +0,0 @@ -use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; -use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; -use nssa::Address; - -use crate::WalletCore; - -impl WalletCore { - pub async fn send_public_native_token_transfer( - &self, - from: Address, - to: Address, - balance_to_move: u128, - ) -> Result { - let Ok(balance) = self.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 { - return Err(ExecutionFailureKind::SequencerError); - }; - - let addresses = vec![from, to]; - let program_id = nssa::program::Program::authenticated_transfer_program().id(); - let message = nssa::public_transaction::Message::try_new( - program_id, - addresses, - nonces, - balance_to_move, - ) - .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 = - 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?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - } - - pub async fn send_private_native_token_transfer_maybe_outer_account( - &self, - from: Address, - to_npk: nssa_core::NullifierPublicKey, - to_ipk: nssa_core::encryption::IncomingViewingPublicKey, - balance_to_move: u128, - to_acc: Option, - ) -> Result { - let from_data = self.storage.user_data.get_private_account(&from); - - let Some((from_keys, from_acc)) = from_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let to_acc = to_acc.unwrap_or_default(); - - if from_acc.balance >= balance_to_move { - let program = nssa::program::Program::authenticated_transfer_program(); - let sender_commitment = - nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); - - let sender_pre = nssa_core::account::AccountWithMetadata { - account: from_acc.clone(), - is_authorized: true, - }; - let recipient_pre = nssa_core::account::AccountWithMetadata { - account: to_acc.clone(), - is_authorized: false, - }; - - let eph_holder = EphemeralKeyHolder::new( - to_npk.clone(), - from_keys.private_key_holder.outgoing_viewing_secret_key, - from_acc.nonce.try_into().unwrap(), - ); - - let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); - - let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), - &[1, 2], - &[from_acc.nonce + 1, to_acc.nonce + 1], - &[ - (from_keys.nullifer_public_key.clone(), shared_secret.clone()), - (to_npk.clone(), shared_secret), - ], - &[( - from_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(sender_commitment) - .await - .unwrap() - .unwrap(), - )], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( - vec![], - vec![], - vec![ - ( - from_keys.nullifer_public_key.clone(), - from_keys.incoming_viewing_public_key.clone(), - eph_holder.generate_ephemeral_public_key(), - ), - ( - to_npk.clone(), - to_ipk.clone(), - eph_holder.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?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - } - - pub async fn send_shielded_native_token_transfer_maybe_outer_account( - &self, - from: Address, - to_npk: nssa_core::NullifierPublicKey, - to_ipk: nssa_core::encryption::IncomingViewingPublicKey, - balance_to_move: u128, - to_acc: Option, - ) -> Result { - let from_data = self.storage.user_data.get_private_account(&from); - - let Some((from_keys, from_acc)) = from_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let to_acc = to_acc.unwrap_or_default(); - - if from_acc.balance >= balance_to_move { - let program = nssa::program::Program::authenticated_transfer_program(); - - let sender_pre = nssa_core::account::AccountWithMetadata { - account: from_acc.clone(), - is_authorized: true, - }; - let recipient_pre = nssa_core::account::AccountWithMetadata { - account: to_acc.clone(), - is_authorized: false, - }; - - let eph_holder = EphemeralKeyHolder::new( - to_npk.clone(), - from_keys.private_key_holder.outgoing_viewing_secret_key, - from_acc.nonce.try_into().unwrap(), - ); - - let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); - - let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), - &[0, 2], - &[to_acc.nonce + 1], - &[(to_npk.clone(), shared_secret)], - &[], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::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 = - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - &message, - proof, - &[signing_key], - ); - - let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( - message, - witness_set, - ); - - Ok(self.sequencer_client.send_tx_private(tx).await?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - } - - pub async fn send_deshielded_native_token_transfer_maybe_outer_account( - &self, - from: Address, - to_addr: Address, - to_npk: nssa_core::NullifierPublicKey, - to_ipk: nssa_core::encryption::IncomingViewingPublicKey, - balance_to_move: u128, - to_acc: Option, - ) -> Result { - let from_data = self.storage.user_data.get_private_account(&from); - - let Some((from_keys, from_acc)) = from_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let to_acc = to_acc.unwrap_or_default(); - - if from_acc.balance >= balance_to_move { - let program = nssa::program::Program::authenticated_transfer_program(); - let sender_commitment = - nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); - - let sender_pre = nssa_core::account::AccountWithMetadata { - account: from_acc.clone(), - is_authorized: true, - }; - let recipient_pre = nssa_core::account::AccountWithMetadata { - account: to_acc.clone(), - is_authorized: false, - }; - - let eph_holder = EphemeralKeyHolder::new( - to_npk.clone(), - from_keys.private_key_holder.outgoing_viewing_secret_key, - from_acc.nonce.try_into().unwrap(), - ); - - let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); - - let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), - &[1, 0], - &[from_acc.nonce + 1], - &[(from_keys.nullifer_public_key.clone(), shared_secret.clone())], - &[( - from_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(sender_commitment) - .await - .unwrap() - .unwrap(), - )], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( - vec![to_addr], - vec![], - vec![( - from_keys.nullifer_public_key.clone(), - from_keys.incoming_viewing_public_key.clone(), - eph_holder.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?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - } - - pub async fn send_private_native_token_transfer( - &self, - from: Address, - to: Address, - balance_to_move: u128, - ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { - let from_data = self.storage.user_data.get_private_account(&from); - let to_data = self.storage.user_data.get_private_account(&to); - - let Some((from_keys, from_acc)) = from_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let Some((to_keys, to_acc)) = to_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let to_npk = to_keys.nullifer_public_key.clone(); - let to_ipk = to_keys.incoming_viewing_public_key.clone(); - - if from_acc.balance >= balance_to_move { - let program = nssa::program::Program::authenticated_transfer_program(); - let sender_commitment = - nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); - let receiver_commitment = - nssa_core::Commitment::new(&to_keys.nullifer_public_key, to_acc); - - let sender_pre = nssa_core::account::AccountWithMetadata { - account: from_acc.clone(), - is_authorized: true, - }; - let recipient_pre = nssa_core::account::AccountWithMetadata { - account: to_acc.clone(), - is_authorized: true, - }; - - let eph_holder = EphemeralKeyHolder::new( - to_npk.clone(), - from_keys.private_key_holder.outgoing_viewing_secret_key, - from_acc.nonce.try_into().unwrap(), - ); - - let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); - - let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( - &[sender_pre, recipient_pre], - &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), - &[1, 1], - &[from_acc.nonce + 1, to_acc.nonce + 1], - &[ - (from_keys.nullifer_public_key.clone(), shared_secret.clone()), - (to_npk.clone(), shared_secret.clone()), - ], - &[ - ( - from_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(sender_commitment) - .await - .unwrap() - .unwrap(), - ), - ( - to_keys.private_key_holder.nullifier_secret_key, - self.sequencer_client - .get_proof_for_commitment(receiver_commitment) - .await - .unwrap() - .unwrap(), - ), - ], - &program, - ) - .unwrap(); - - let message = - nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( - vec![], - vec![], - vec![ - ( - from_keys.nullifer_public_key.clone(), - from_keys.incoming_viewing_public_key.clone(), - eph_holder.generate_ephemeral_public_key(), - ), - ( - to_npk.clone(), - to_ipk.clone(), - eph_holder.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, - )) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } - } - - pub async fn send_shiedled_native_token_transfer( - &self, - from: Address, - to: Address, - balance_to_move: u128, - ) -> Result { - let to_data = self.storage.user_data.get_private_account(&to); - - let Some((to_keys, to_acc)) = to_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - self.send_shielded_native_token_transfer_maybe_outer_account( - from, - to_keys.nullifer_public_key.clone(), - to_keys.incoming_viewing_public_key.clone(), - balance_to_move, - Some(to_acc.clone()), - ) - .await - } - - pub async fn send_deshielded_native_token_transfer( - &self, - from: Address, - to: Address, - balance_to_move: u128, - ) -> Result { - let to_data = self.storage.user_data.get_private_account(&to); - - let Some((to_keys, to_acc)) = to_data else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - self.send_deshielded_native_token_transfer_maybe_outer_account( - from, - to, - to_keys.nullifer_public_key.clone(), - to_keys.incoming_viewing_public_key.clone(), - balance_to_move, - Some(to_acc.clone()), - ) - .await - } -} diff --git a/wallet/src/token_transfers/deshielded.rs b/wallet/src/token_transfers/deshielded.rs new file mode 100644 index 0000000..eacd02c --- /dev/null +++ b/wallet/src/token_transfers/deshielded.rs @@ -0,0 +1,185 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_deshielded_native_token_transfer_maybe_outer_account( + &self, + from: Address, + to_addr: Address, + to_npk: nssa_core::NullifierPublicKey, + to_ipk: nssa_core::encryption::IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result { + let from_data = self.storage.user_data.get_private_account(&from); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_acc = nssa_core::account::Account::default(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 0], + &[from_acc.nonce + 1], + &[(from_keys.nullifer_public_key.clone(), shared_secret.clone())], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![to_addr], + vec![], + vec![( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.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?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_deshielded_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result { + let from_data = self.storage.user_data.get_private_account(&from); + let to_data = self.storage.user_data.get_private_account(&to); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, to_acc)) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 0], + &[from_acc.nonce + 1], + &[(from_keys.nullifer_public_key.clone(), shared_secret.clone())], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![to], + vec![], + vec![( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.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?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/mod.rs b/wallet/src/token_transfers/mod.rs new file mode 100644 index 0000000..edfbe3e --- /dev/null +++ b/wallet/src/token_transfers/mod.rs @@ -0,0 +1,4 @@ +pub mod deshielded; +pub mod private; +pub mod public; +pub mod shielded; diff --git a/wallet/src/token_transfers/private.rs b/wallet/src/token_transfers/private.rs new file mode 100644 index 0000000..12a16dd --- /dev/null +++ b/wallet/src/token_transfers/private.rs @@ -0,0 +1,222 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_private_native_token_transfer_outer_account( + &self, + from: Address, + to_npk: nssa_core::NullifierPublicKey, + to_ipk: nssa_core::encryption::IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.storage.user_data.get_private_account(&from); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_acc = nssa_core::account::Account::default(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 2], + &[from_acc.nonce + 1, to_acc.nonce + 1], + &[ + (from_keys.nullifer_public_key.clone(), shared_secret.clone()), + (to_npk.clone(), shared_secret.clone()), + ], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![], + vec![], + vec![ + ( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ( + to_npk.clone(), + to_ipk.clone(), + eph_holder.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, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_private_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.storage.user_data.get_private_account(&from); + let to_data = self.storage.user_data.get_private_account(&to); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, to_acc)) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, from_acc); + let receiver_commitment = + nssa_core::Commitment::new(&to_keys.nullifer_public_key, to_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: true, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 1], + &[from_acc.nonce + 1, to_acc.nonce + 1], + &[ + (from_keys.nullifer_public_key.clone(), shared_secret.clone()), + (to_npk.clone(), shared_secret.clone()), + ], + &[ + ( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + ), + ( + to_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(receiver_commitment) + .await + .unwrap() + .unwrap(), + ), + ], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![], + vec![], + vec![ + ( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ( + to_npk.clone(), + to_ipk.clone(), + eph_holder.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, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/public.rs b/wallet/src/token_transfers/public.rs new file mode 100644 index 0000000..7a5a78e --- /dev/null +++ b/wallet/src/token_transfers/public.rs @@ -0,0 +1,48 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_public_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result { + let Ok(balance) = self.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 { + return Err(ExecutionFailureKind::SequencerError); + }; + + let addresses = vec![from, to]; + let program_id = nssa::program::Program::authenticated_transfer_program().id(); + let message = nssa::public_transaction::Message::try_new( + program_id, + addresses, + nonces, + balance_to_move, + ) + .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 = + 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?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/shielded.rs b/wallet/src/token_transfers/shielded.rs new file mode 100644 index 0000000..24a5292 --- /dev/null +++ b/wallet/src/token_transfers/shielded.rs @@ -0,0 +1,188 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_shiedled_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result { + let from_data = self.storage.user_data.get_private_account(&from); + let to_data = self.storage.user_data.get_private_account(&to); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, to_acc)) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + let receiver_commitment = + nssa_core::Commitment::new(&to_keys.nullifer_public_key, to_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[0, 1], + &[to_acc.nonce + 1], + &[(to_npk.clone(), shared_secret)], + &[( + to_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(receiver_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::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 = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[signing_key], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(self.sequencer_client.send_tx_private(tx).await?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_shielded_native_token_transfer_maybe_outer_account( + &self, + from: Address, + to_npk: nssa_core::NullifierPublicKey, + to_ipk: nssa_core::encryption::IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result { + let from_data = self.storage.user_data.get_private_account(&from); + + let Some((from_keys, from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_acc = nssa_core::account::Account::default(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[0, 2], + &[to_acc.nonce + 1], + &[(to_npk.clone(), shared_secret)], + &[], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::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 = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[signing_key], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(self.sequencer_client.send_tx_private(tx).await?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +}