From 3732f16df974d2b985a035d9170b259c58810157 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Mon, 18 May 2026 13:44:03 +0300 Subject: [PATCH 1/7] feat: account manager extension --- .../src/bin/run_hello_world_private.rs | 4 +- ...n_hello_world_through_tail_call_private.rs | 4 +- .../bin/run_hello_world_with_move_function.rs | 8 +- integration_tests/src/setup.rs | 6 +- integration_tests/tests/private_pda.rs | 10 +- ...cy_preserving_tx.rs => account_manager.rs} | 22 +- wallet/src/lib.rs | 80 ++++++- wallet/src/program_facades/ata.rs | 14 +- .../native_token_transfer/deshielded.rs | 4 +- .../native_token_transfer/private.rs | 4 +- .../native_token_transfer/public.rs | 225 ++++++++++-------- .../native_token_transfer/shielded.rs | 8 +- wallet/src/program_facades/pinata.rs | 4 +- wallet/src/program_facades/token.rs | 30 +-- 14 files changed, 260 insertions(+), 163 deletions(-) rename wallet/src/{privacy_preserving_tx.rs => account_manager.rs} (95%) diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index 27ac2079..4fa149ea 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -1,5 +1,5 @@ use nssa::{AccountId, program::Program}; -use wallet::{PrivacyPreservingAccount, WalletCore}; +use wallet::{AccountManagerAccountIdentity, WalletCore}; // Before running this example, compile the `hello_world.rs` guest program with: // @@ -44,7 +44,7 @@ async fn main() { // Define the desired greeting in ASCII let greeting: Vec = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33]; - let accounts = vec![PrivacyPreservingAccount::PrivateOwned(account_id)]; + let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction wallet_core diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 4fac3eec..60d5df7c 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -4,7 +4,7 @@ use nssa::{ AccountId, ProgramId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; -use wallet::{PrivacyPreservingAccount, WalletCore}; +use wallet::{AccountManagerAccountIdentity, WalletCore}; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -51,7 +51,7 @@ async fn main() { std::iter::once((hello_world.id(), hello_world)).collect(); let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies); - let accounts = vec![PrivacyPreservingAccount::PrivateOwned(account_id)]; + let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction let instruction = (); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index a1c2517e..e2056c4c 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand}; use common::transaction::NSSATransaction; use nssa::{PublicTransaction, program::Program, public_transaction}; use sequencer_service_rpc::RpcClient as _; -use wallet::{PrivacyPreservingAccount, WalletCore}; +use wallet::{AccountManagerAccountIdentity, WalletCore}; // Before running this example, compile the `hello_world_with_move_function.rs` guest program with: // @@ -99,7 +99,7 @@ async fn main() { } => { let instruction: Instruction = (WRITE_FUNCTION_ID, greeting.into_bytes()); let account_id = account_id.parse().unwrap(); - let accounts = vec![PrivacyPreservingAccount::PrivateOwned(account_id)]; + let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; wallet_core .send_privacy_preserving_tx( @@ -138,8 +138,8 @@ async fn main() { let to = to.parse().unwrap(); let accounts = vec![ - PrivacyPreservingAccount::Public(from), - PrivacyPreservingAccount::PrivateOwned(to), + AccountManagerAccountIdentity::Public(from), + AccountManagerAccountIdentity::PrivateOwned(to), ]; wallet_core diff --git a/integration_tests/src/setup.rs b/integration_tests/src/setup.rs index c43590d0..c1165285 100644 --- a/integration_tests/src/setup.rs +++ b/integration_tests/src/setup.rs @@ -10,7 +10,7 @@ use sequencer_service_rpc::RpcClient as _; use tempfile::TempDir; use testcontainers::compose::DockerCompose; use wallet::{ - AccDecodeData::Decode, PrivacyPreservingAccount, WalletCore, config::WalletConfigOverrides, + AccDecodeData::Decode, AccountManagerAccountIdentity, WalletCore, config::WalletConfigOverrides, }; use crate::{ @@ -293,8 +293,8 @@ async fn claim_funds_from_vault_to_private( let (tx_hash, mut secrets) = wallet .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::PrivateOwned(owner_id), - PrivacyPreservingAccount::Public(owner_vault_id), + AccountManagerAccountIdentity::PrivateOwned(owner_id), + AccountManagerAccountIdentity::Public(owner_vault_id), ], instruction_data, &program_with_dependencies, diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index f9969d98..4cbc07d1 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -18,7 +18,7 @@ use nssa::{ use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey, program::PdaSeed}; use tokio::test; use wallet::{ - PrivacyPreservingAccount, WalletCore, + AccountManagerAccountIdentity, WalletCore, cli::{Command, account::AccountSubcommand}, }; @@ -46,8 +46,8 @@ async fn fund_private_pda( wallet .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(sender), - PrivacyPreservingAccount::PrivatePdaForeign { + AccountManagerAccountIdentity::Public(sender), + AccountManagerAccountIdentity::PrivatePdaForeign { account_id: pda_account_id, npk, vpk, @@ -83,8 +83,8 @@ async fn spend_private_pda( wallet .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::PrivatePdaOwned(pda_account_id), - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::PrivatePdaOwned(pda_account_id), + AccountManagerAccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: 0, diff --git a/wallet/src/privacy_preserving_tx.rs b/wallet/src/account_manager.rs similarity index 95% rename from wallet/src/privacy_preserving_tx.rs rename to wallet/src/account_manager.rs index 005eaf75..0f43baf2 100644 --- a/wallet/src/privacy_preserving_tx.rs +++ b/wallet/src/account_manager.rs @@ -11,7 +11,7 @@ use nssa_core::{ use crate::{ExecutionFailureKind, WalletCore}; #[derive(Clone)] -pub enum PrivacyPreservingAccount { +pub enum AccountManagerAccountIdentity { Public(AccountId), PrivateOwned(AccountId), PrivateForeign { @@ -50,7 +50,7 @@ pub enum PrivacyPreservingAccount { }, } -impl PrivacyPreservingAccount { +impl AccountManagerAccountIdentity { #[must_use] pub const fn is_public(&self) -> bool { matches!(&self, Self::Public(_)) @@ -92,13 +92,13 @@ pub struct AccountManager { impl AccountManager { pub async fn new( wallet: &WalletCore, - accounts: Vec, + accounts: Vec, ) -> Result { let mut states = Vec::with_capacity(accounts.len()); for account in accounts { let state = match account { - PrivacyPreservingAccount::Public(account_id) => { + AccountManagerAccountIdentity::Public(account_id) => { let acc = wallet .get_account_public(account_id) .await @@ -109,12 +109,12 @@ impl AccountManager { State::Public { account, sk } } - PrivacyPreservingAccount::PrivateOwned(account_id) => { + AccountManagerAccountIdentity::PrivateOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, false).await?; State::Private(pre) } - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::PrivateForeign { npk, vpk, identifier, @@ -138,11 +138,11 @@ impl AccountManager { State::Private(pre) } - PrivacyPreservingAccount::PrivatePdaOwned(account_id) => { + AccountManagerAccountIdentity::PrivatePdaOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, true).await?; State::Private(pre) } - PrivacyPreservingAccount::PrivatePdaForeign { + AccountManagerAccountIdentity::PrivatePdaForeign { account_id, npk, vpk, @@ -166,7 +166,7 @@ impl AccountManager { }; State::Private(pre) } - PrivacyPreservingAccount::PrivateShared { + AccountManagerAccountIdentity::PrivateShared { nsk, npk, vpk, @@ -180,7 +180,7 @@ impl AccountManager { State::Private(pre) } - PrivacyPreservingAccount::PrivatePdaShared { + AccountManagerAccountIdentity::PrivatePdaShared { account_id, nsk, npk, @@ -410,7 +410,7 @@ mod tests { #[test] fn private_shared_is_private() { - let acc = PrivacyPreservingAccount::PrivateShared { + let acc = AccountManagerAccountIdentity::PrivateShared { nsk: [0; 32], npk: NullifierPublicKey([1; 32]), vpk: ViewingPublicKey::from_scalar([2; 32]), diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index bfbe7145..e1fec4d1 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -9,6 +9,7 @@ use std::path::PathBuf; +pub use account_manager::AccountManagerAccountIdentity; use anyhow::{Context as _, Result}; use bip39::Mnemonic; use common::{HashType, transaction::NSSATransaction}; @@ -24,7 +25,6 @@ use nssa::{ use nssa_core::{ Commitment, MembershipProof, SharedSecretKey, account::Nonce, program::InstructionData, }; -pub use privacy_preserving_tx::PrivacyPreservingAccount; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use storage::Storage; use tokio::io::AsyncWriteExt as _; @@ -37,11 +37,11 @@ use crate::{ }; pub mod account; +mod account_manager; pub mod cli; pub mod config; pub mod helperfunctions; pub mod poller; -mod privacy_preserving_tx; pub mod program_facades; pub mod storage; @@ -276,7 +276,7 @@ impl WalletCore { pub fn resolve_private_account( &self, account_id: nssa::AccountId, - ) -> Option { + ) -> Option { // Check key tree first if self .storage @@ -284,7 +284,7 @@ impl WalletCore { .private_account(account_id) .is_some() { - return Some(PrivacyPreservingAccount::PrivateOwned(account_id)); + return Some(AccountManagerAccountIdentity::PrivateOwned(account_id)); } // Check shared private accounts @@ -299,7 +299,7 @@ impl WalletCore { if let (Some(pda_seed), Some(program_id)) = (entry.pda_seed, entry.pda_program_id) { let keys = holder.derive_keys_for_pda(&program_id, &pda_seed); - Some(PrivacyPreservingAccount::PrivatePdaShared { + Some(AccountManagerAccountIdentity::PrivatePdaShared { account_id, nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), @@ -316,7 +316,7 @@ impl WalletCore { result }; let keys = holder.derive_keys_for_shared_account(&derivation_seed); - Some(PrivacyPreservingAccount::PrivateShared { + Some(AccountManagerAccountIdentity::PrivateShared { nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), vpk: keys.generate_viewing_public_key(), @@ -541,7 +541,7 @@ impl WalletCore { pub async fn send_privacy_preserving_tx( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, ) -> Result<(HashType, Vec), ExecutionFailureKind> { @@ -553,12 +553,12 @@ impl WalletCore { pub async fn send_privacy_preserving_tx_with_pre_check( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, ) -> Result<(HashType, Vec), ExecutionFailureKind> { - let acc_manager = privacy_preserving_tx::AccountManager::new(self, accounts).await?; + let acc_manager = account_manager::AccountManager::new(self, accounts).await?; let pre_states = acc_manager.pre_states(); tx_pre_check( @@ -610,6 +610,68 @@ impl WalletCore { )) } + pub async fn send_pub_tx( + &self, + accounts: Vec, + instruction_data: InstructionData, + program: &ProgramWithDependencies, + ) -> Result { + self.send_pub_tx_with_pre_check(accounts, instruction_data, program, |_| Ok(())) + .await + } + + pub async fn send_pub_tx_with_pre_check( + &self, + accounts: Vec, + instruction_data: InstructionData, + program: &ProgramWithDependencies, + tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, + ) -> Result { + // Public transaction, all accounts must be public + if accounts + .iter() + .any(AccountManagerAccountIdentity::is_private) + { + return Err(ExecutionFailureKind::TransactionBuildError( + nssa::error::NssaError::InvalidInput( + "Private accounts are not allowed in public transactions".to_owned(), + ), + )); + } + + let acc_manager = account_manager::AccountManager::new(self, accounts).await?; + + let pre_states = acc_manager.pre_states(); + tx_pre_check( + &pre_states + .iter() + .map(|pre| &pre.account) + .collect::>(), + )?; + + let account_ids = acc_manager.public_account_ids(); + let program_id = program.program.id(); + let nonces = acc_manager.public_account_nonces(); + let private_keys = acc_manager.public_account_auth(); + + let message = nssa::public_transaction::Message::new_preserialized( + program_id, + account_ids, + nonces, + instruction_data, + ); + + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); + + let tx = nssa::public_transaction::PublicTransaction::new(message, witness_set); + + Ok(self + .sequencer_client + .send_transaction(NSSATransaction::Public(tx)) + .await?) + } + pub async fn sync_to_latest_block(&mut self) -> Result { let latest_block_id = self.sequencer_client.get_last_block_id().await?; println!("Latest block is {latest_block_id}"); diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index bdb72620..0a6a4eba 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -8,7 +8,7 @@ use nssa::{ use nssa_core::SharedSecretKey; use sequencer_service_rpc::RpcClient as _; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Ata<'wallet>(pub &'wallet WalletCore); @@ -176,8 +176,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(definition_id), - PrivacyPreservingAccount::Public(ata_id), + AccountManagerAccountIdentity::Public(definition_id), + AccountManagerAccountIdentity::Public(ata_id), ]; self.0 @@ -213,8 +213,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(sender_ata_id), - PrivacyPreservingAccount::Public(recipient_id), + AccountManagerAccountIdentity::Public(sender_ata_id), + AccountManagerAccountIdentity::Public(recipient_id), ]; self.0 @@ -249,8 +249,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(holder_ata_id), - PrivacyPreservingAccount::Public(definition_id), + AccountManagerAccountIdentity::Public(holder_ata_id), + AccountManagerAccountIdentity::Public(definition_id), ]; self.0 diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index d4bde39f..3cfaea1b 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -2,7 +2,7 @@ use common::HashType; use nssa::AccountId; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_deshielded_transfer( @@ -19,7 +19,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(to), + AccountManagerAccountIdentity::Public(to), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index e8438d08..508bb62f 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -5,7 +5,7 @@ use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn register_account_private( @@ -49,7 +49,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index ca5c45f9..93d0f42d 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -1,117 +1,152 @@ use authenticated_transfer_core::Instruction as AuthTransferInstruction; -use common::{HashType, transaction::NSSATransaction}; -use nssa::{ - AccountId, PublicTransaction, - program::Program, - public_transaction::{Message, WitnessSet}, -}; -use sequencer_service_rpc::RpcClient as _; +use common::HashType; +use nssa::{AccountId, program::Program}; use super::NativeTokenTransfer; -use crate::ExecutionFailureKind; +use crate::{ + AccountManagerAccountIdentity, ExecutionFailureKind, + program_facades::native_token_transfer::auth_transfer_preparation, +}; impl NativeTokenTransfer<'_> { + // pub async fn send_public_transfer( + // &self, + // from: AccountId, + // to: AccountId, + // balance_to_move: u128, + // ) -> Result { + // let balance = self + // .0 + // .get_account_balance(from) + // .await + // .map_err(ExecutionFailureKind::SequencerError)?; + + // if balance >= balance_to_move { + // let account_ids = vec![from, to]; + // let program_id = Program::authenticated_transfer_program().id(); + + // let mut nonces = self + // .0 + // .get_accounts_nonces(vec![from]) + // .await + // .map_err(ExecutionFailureKind::SequencerError)?; + + // let mut private_keys = Vec::new(); + // let from_signing_key = self.0.storage.key_chain().pub_account_signing_key(from); + // let Some(from_signing_key) = from_signing_key else { + // return Err(ExecutionFailureKind::KeyNotFoundError); + // }; + // private_keys.push(from_signing_key); + + // let to_signing_key = self.0.storage.key_chain().pub_account_signing_key(to); + // if let Some(to_signing_key) = to_signing_key { + // private_keys.push(to_signing_key); + // let to_nonces = self + // .0 + // .get_accounts_nonces(vec![to]) + // .await + // .map_err(ExecutionFailureKind::SequencerError)?; + // nonces.extend(to_nonces); + // } else { + // println!( + // "Receiver's account ({to}) private key not found in wallet. Proceeding with + // only sender's key." ); + // } + + // let message = Message::try_new( + // program_id, + // account_ids, + // nonces, + // AuthTransferInstruction::Transfer { + // amount: balance_to_move, + // }, + // ) + // .unwrap(); + // let witness_set = WitnessSet::for_message(&message, &private_keys); + + // let tx = PublicTransaction::new(message, witness_set); + + // Ok(self + // .0 + // .sequencer_client + // .send_transaction(NSSATransaction::Public(tx)) + // .await?) + // } else { + // Err(ExecutionFailureKind::InsufficientFundsError) + // } + // } + pub async fn send_public_transfer( &self, from: AccountId, to: AccountId, balance_to_move: u128, ) -> Result { - let balance = self - .0 - .get_account_balance(from) - .await - .map_err(ExecutionFailureKind::SequencerError)?; + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); - if balance >= balance_to_move { - let account_ids = vec![from, to]; - let program_id = Program::authenticated_transfer_program().id(); - - let mut nonces = self - .0 - .get_accounts_nonces(vec![from]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - let from_signing_key = self.0.storage.key_chain().pub_account_signing_key(from); - let Some(from_signing_key) = from_signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - private_keys.push(from_signing_key); - - let to_signing_key = self.0.storage.key_chain().pub_account_signing_key(to); - if let Some(to_signing_key) = to_signing_key { - private_keys.push(to_signing_key); - let to_nonces = self - .0 - .get_accounts_nonces(vec![to]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(to_nonces); - } else { - println!( - "Receiver's account ({to}) private key not found in wallet. Proceeding with only sender's key." - ); - } - - let message = Message::try_new( - program_id, - account_ids, - nonces, - AuthTransferInstruction::Transfer { - amount: balance_to_move, - }, + self.0 + .send_pub_tx_with_pre_check( + vec![ + AccountManagerAccountIdentity::Public(from), + AccountManagerAccountIdentity::Public(to), + ], + instruction_data, + &program.into(), + tx_pre_check, ) - .unwrap(); - let witness_set = WitnessSet::for_message(&message, &private_keys); - - let tx = PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } + .await } + // pub async fn register_account( + // &self, + // from: AccountId, + // ) -> Result { + // let nonces = self + // .0 + // .get_accounts_nonces(vec![from]) + // .await + // .map_err(ExecutionFailureKind::SequencerError)?; + + // let account_ids = vec![from]; + // let program_id = Program::authenticated_transfer_program().id(); + // let message = Message::try_new( + // program_id, + // account_ids, + // nonces, + // AuthTransferInstruction::Initialize, + // ) + // .unwrap(); + + // let signing_key = self.0.storage.key_chain().pub_account_signing_key(from); + + // let Some(signing_key) = signing_key else { + // return Err(ExecutionFailureKind::KeyNotFoundError); + // }; + + // let witness_set = WitnessSet::for_message(&message, &[signing_key]); + + // let tx = PublicTransaction::new(message, witness_set); + + // Ok(self + // .0 + // .sequencer_client + // .send_transaction(NSSATransaction::Public(tx)) + // .await?) + // } + pub async fn register_account( &self, from: AccountId, ) -> Result { - let nonces = self - .0 - .get_accounts_nonces(vec![from]) + let instruction_data = Program::serialize_instruction(AuthTransferInstruction::Initialize)?; + let program = Program::authenticated_transfer_program(); + + self.0 + .send_pub_tx( + vec![AccountManagerAccountIdentity::Public(from)], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let account_ids = vec![from]; - let program_id = Program::authenticated_transfer_program().id(); - let message = Message::try_new( - program_id, - account_ids, - nonces, - AuthTransferInstruction::Initialize, - ) - .unwrap(); - - let signing_key = self.0.storage.key_chain().pub_account_signing_key(from); - - let Some(signing_key) = signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let witness_set = WitnessSet::for_message(&message, &[signing_key]); - - let tx = PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::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 index 98dd0081..385187ce 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -3,7 +3,7 @@ use nssa::AccountId; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( @@ -17,7 +17,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - PrivacyPreservingAccount::Public(from), + AccountManagerAccountIdentity::Public(from), self.0 .resolve_private_account(to) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -49,8 +49,8 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - PrivacyPreservingAccount::Public(from), - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::Public(from), + AccountManagerAccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 0575455e..28767366 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -3,7 +3,7 @@ use nssa::AccountId; use nssa_core::{MembershipProof, SharedSecretKey}; use sequencer_service_rpc::RpcClient as _; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Pinata<'wallet>(pub &'wallet WalletCore); @@ -55,7 +55,7 @@ impl Pinata<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(pinata_account_id), + AccountManagerAccountIdentity::Public(pinata_account_id), self.0 .resolve_private_account(winner_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 7197b1f0..096ea40c 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -4,7 +4,7 @@ use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::Vie use sequencer_service_rpc::RpcClient as _; use token_core::Instruction; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Token<'wallet>(pub &'wallet WalletCore); @@ -73,7 +73,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(definition_account_id), + AccountManagerAccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(supply_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -108,7 +108,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(supply_account_id), + AccountManagerAccountIdentity::Public(supply_account_id), ], instruction_data, &Program::token().into(), @@ -274,7 +274,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -310,7 +310,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(recipient_account_id), + AccountManagerAccountIdentity::Public(recipient_account_id), ], instruction_data, &Program::token().into(), @@ -340,7 +340,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(sender_account_id), + AccountManagerAccountIdentity::Public(sender_account_id), self.0 .resolve_private_account(recipient_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -375,8 +375,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(sender_account_id), - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::Public(sender_account_id), + AccountManagerAccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -489,7 +489,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(holder_account_id), + AccountManagerAccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -519,7 +519,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(definition_account_id), + AccountManagerAccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -655,7 +655,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, @@ -691,7 +691,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - PrivacyPreservingAccount::Public(holder_account_id), + AccountManagerAccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -721,7 +721,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(definition_account_id), + AccountManagerAccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -756,8 +756,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - PrivacyPreservingAccount::Public(definition_account_id), - PrivacyPreservingAccount::PrivateForeign { + AccountManagerAccountIdentity::Public(definition_account_id), + AccountManagerAccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, From 0210d7060222b0c514502860b83e2e8c42b160be Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Mon, 18 May 2026 15:06:09 +0300 Subject: [PATCH 2/7] feat(wallet): added unified way of sending public transactions to all facades --- wallet/src/program_facades/amm.rs | 387 +++++------------- wallet/src/program_facades/ata.rs | 132 ++---- .../native_token_transfer/public.rs | 104 ----- wallet/src/program_facades/pinata.rs | 32 +- wallet/src/program_facades/token.rs | 224 +++------- 5 files changed, 208 insertions(+), 671 deletions(-) diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 2ac2ab79..189c79f1 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -1,10 +1,9 @@ use amm_core::{compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda}; -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{AccountId, program::Program}; -use sequencer_service_rpc::RpcClient as _; use token_core::TokenHolding; -use crate::{ExecutionFailureKind, WalletCore}; +use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Amm<'wallet>(pub &'wallet WalletCore); impl Amm<'_> { @@ -18,12 +17,6 @@ impl Amm<'_> { ) -> Result { let program = Program::amm(); let amm_program_id = Program::amm().id(); - let instruction = amm_core::Instruction::NewDefinition { - token_a_amount: balance_a, - token_b_amount: balance_b, - amm_program_id, - }; - let user_a_acc = self .0 .get_account_public(user_holding_a) @@ -47,78 +40,29 @@ impl Amm<'_> { let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + let instruction = amm_core::Instruction::NewDefinition { + token_a_amount: balance_a, + token_b_amount: balance_b, + amm_program_id, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; - - let mut nonces = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(amm_pool), + AccountManagerAccountIdentity::Public(vault_holding_a), + AccountManagerAccountIdentity::Public(vault_holding_b), + AccountManagerAccountIdentity::Public(pool_lp), + AccountManagerAccountIdentity::Public(user_holding_a), + AccountManagerAccountIdentity::Public(user_holding_b), + AccountManagerAccountIdentity::Public(user_holding_lp), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - - let signing_key_a = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_a); - - let signing_key_b = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_b); - - if let Some(signing_key_lp) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_lp) - { - private_keys.push(signing_key_lp); - let lp_nonces = self - .0 - .get_accounts_nonces(vec![user_holding_lp]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(lp_nonces); - } else { - println!( - "Liquidity pool tokens receiver's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity provider's keys." - ); - } - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_swap_exact_input( @@ -129,14 +73,8 @@ impl Amm<'_> { min_amount_out: u128, token_definition_id_in: AccountId, ) -> Result { - let instruction = amm_core::Instruction::SwapExactInput { - swap_amount_in, - min_amount_out, - token_definition_id_in, - }; let program = Program::amm(); let amm_program_id = Program::amm().id(); - let user_a_acc = self .0 .get_account_public(user_holding_a) @@ -159,56 +97,27 @@ impl Amm<'_> { compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); - - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - user_holding_a, - user_holding_b, - ]; - - let account_id_auth = if definition_token_a_id == token_definition_id_in { - user_holding_a - } else if definition_token_b_id == token_definition_id_in { - user_holding_b - } else { - return Err(ExecutionFailureKind::AccountDataError( - token_definition_id_in, - )); + let instruction = amm_core::Instruction::SwapExactInput { + swap_amount_in, + min_amount_out, + token_definition_id_in, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let nonces = self - .0 - .get_accounts_nonces(vec![account_id_auth]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(amm_pool), + AccountManagerAccountIdentity::Public(vault_holding_a), + AccountManagerAccountIdentity::Public(vault_holding_b), + AccountManagerAccountIdentity::Public(user_holding_a), + AccountManagerAccountIdentity::Public(user_holding_b), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - 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_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_swap_exact_output( @@ -219,14 +128,8 @@ impl Amm<'_> { max_amount_in: u128, token_definition_id_in: AccountId, ) -> Result { - let instruction = amm_core::Instruction::SwapExactOutput { - exact_amount_out, - max_amount_in, - token_definition_id_in, - }; let program = Program::amm(); let amm_program_id = Program::amm().id(); - let user_a_acc = self .0 .get_account_public(user_holding_a) @@ -249,56 +152,27 @@ impl Amm<'_> { compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); - - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - user_holding_a, - user_holding_b, - ]; - - let account_id_auth = if definition_token_a_id == token_definition_id_in { - user_holding_a - } else if definition_token_b_id == token_definition_id_in { - user_holding_b - } else { - return Err(ExecutionFailureKind::AccountDataError( - token_definition_id_in, - )); + let instruction = amm_core::Instruction::SwapExactOutput { + exact_amount_out, + max_amount_in, + token_definition_id_in, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let nonces = self - .0 - .get_accounts_nonces(vec![account_id_auth]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(amm_pool), + AccountManagerAccountIdentity::Public(vault_holding_a), + AccountManagerAccountIdentity::Public(vault_holding_b), + AccountManagerAccountIdentity::Public(user_holding_a), + AccountManagerAccountIdentity::Public(user_holding_b), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - 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_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_add_liquidity( @@ -310,14 +184,8 @@ impl Amm<'_> { max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, ) -> Result { - let instruction = amm_core::Instruction::AddLiquidity { - min_amount_liquidity, - max_amount_to_add_token_a, - max_amount_to_add_token_b, - }; let program = Program::amm(); let amm_program_id = Program::amm().id(); - let user_a_acc = self .0 .get_account_public(user_holding_a) @@ -341,57 +209,29 @@ impl Amm<'_> { let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + let instruction = amm_core::Instruction::AddLiquidity { + min_amount_liquidity, + max_amount_to_add_token_a, + max_amount_to_add_token_b, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; - - let nonces = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(amm_pool), + AccountManagerAccountIdentity::Public(vault_holding_a), + AccountManagerAccountIdentity::Public(vault_holding_b), + AccountManagerAccountIdentity::Public(pool_lp), + AccountManagerAccountIdentity::Public(user_holding_a), + AccountManagerAccountIdentity::Public(user_holding_b), + AccountManagerAccountIdentity::Public(user_holding_lp), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key_a = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let signing_key_b = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_remove_liquidity( @@ -403,14 +243,8 @@ impl Amm<'_> { min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, ) -> Result { - let instruction = amm_core::Instruction::RemoveLiquidity { - remove_liquidity_amount, - min_amount_to_remove_token_a, - min_amount_to_remove_token_b, - }; let program = Program::amm(); let amm_program_id = Program::amm().id(); - let user_a_acc = self .0 .get_account_public(user_holding_a) @@ -434,47 +268,28 @@ impl Amm<'_> { let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + let instruction = amm_core::Instruction::RemoveLiquidity { + remove_liquidity_amount, + min_amount_to_remove_token_a, + min_amount_to_remove_token_b, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; - - let nonces = self - .0 - .get_accounts_nonces(vec![user_holding_lp]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(amm_pool), + AccountManagerAccountIdentity::Public(vault_holding_a), + AccountManagerAccountIdentity::Public(vault_holding_b), + AccountManagerAccountIdentity::Public(pool_lp), + AccountManagerAccountIdentity::Public(user_holding_a), + AccountManagerAccountIdentity::Public(user_holding_b), + AccountManagerAccountIdentity::Public(user_holding_lp), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key_lp = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_lp) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } } diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index 0a6a4eba..d6e3fea8 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; use ata_core::{compute_ata_seed, get_associated_token_account_id}; -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{ AccountId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; use nssa_core::SharedSecretKey; -use sequencer_service_rpc::RpcClient as _; use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; @@ -24,38 +23,21 @@ impl Ata<'_> { &ata_program_id, &compute_ata_seed(owner_id, definition_id), ); - - let account_ids = vec![owner_id, definition_id, ata_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Create { ata_program_id }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - 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_transaction(NSSATransaction::Public(tx)) - .await?) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(owner_id), + AccountManagerAccountIdentity::Public(definition_id), + AccountManagerAccountIdentity::Public(ata_id), + ], + instruction_data, + &program.into(), + ) + .await } pub async fn send_transfer( @@ -71,41 +53,24 @@ impl Ata<'_> { &ata_program_id, &compute_ata_seed(owner_id, definition_id), ); - - let account_ids = vec![owner_id, sender_ata_id, recipient_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Transfer { ata_program_id, amount, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - 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_transaction(NSSATransaction::Public(tx)) - .await?) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(owner_id), + AccountManagerAccountIdentity::Public(sender_ata_id), + AccountManagerAccountIdentity::Public(recipient_id), + ], + instruction_data, + &program.into(), + ) + .await } pub async fn send_burn( @@ -120,41 +85,24 @@ impl Ata<'_> { &ata_program_id, &compute_ata_seed(owner_id, definition_id), ); - - let account_ids = vec![owner_id, holder_ata_id, definition_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Burn { ata_program_id, amount, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - 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_transaction(NSSATransaction::Public(tx)) - .await?) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(owner_id), + AccountManagerAccountIdentity::Public(holder_ata_id), + AccountManagerAccountIdentity::Public(definition_id), + ], + instruction_data, + &program.into(), + ) + .await } pub async fn send_create_private_owner( diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index 93d0f42d..6054383c 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -9,73 +9,6 @@ use crate::{ }; impl NativeTokenTransfer<'_> { - // pub async fn send_public_transfer( - // &self, - // from: AccountId, - // to: AccountId, - // balance_to_move: u128, - // ) -> Result { - // let balance = self - // .0 - // .get_account_balance(from) - // .await - // .map_err(ExecutionFailureKind::SequencerError)?; - - // if balance >= balance_to_move { - // let account_ids = vec![from, to]; - // let program_id = Program::authenticated_transfer_program().id(); - - // let mut nonces = self - // .0 - // .get_accounts_nonces(vec![from]) - // .await - // .map_err(ExecutionFailureKind::SequencerError)?; - - // let mut private_keys = Vec::new(); - // let from_signing_key = self.0.storage.key_chain().pub_account_signing_key(from); - // let Some(from_signing_key) = from_signing_key else { - // return Err(ExecutionFailureKind::KeyNotFoundError); - // }; - // private_keys.push(from_signing_key); - - // let to_signing_key = self.0.storage.key_chain().pub_account_signing_key(to); - // if let Some(to_signing_key) = to_signing_key { - // private_keys.push(to_signing_key); - // let to_nonces = self - // .0 - // .get_accounts_nonces(vec![to]) - // .await - // .map_err(ExecutionFailureKind::SequencerError)?; - // nonces.extend(to_nonces); - // } else { - // println!( - // "Receiver's account ({to}) private key not found in wallet. Proceeding with - // only sender's key." ); - // } - - // let message = Message::try_new( - // program_id, - // account_ids, - // nonces, - // AuthTransferInstruction::Transfer { - // amount: balance_to_move, - // }, - // ) - // .unwrap(); - // let witness_set = WitnessSet::for_message(&message, &private_keys); - - // let tx = PublicTransaction::new(message, witness_set); - - // Ok(self - // .0 - // .sequencer_client - // .send_transaction(NSSATransaction::Public(tx)) - // .await?) - // } else { - // Err(ExecutionFailureKind::InsufficientFundsError) - // } - // } - pub async fn send_public_transfer( &self, from: AccountId, @@ -97,43 +30,6 @@ impl NativeTokenTransfer<'_> { .await } - // pub async fn register_account( - // &self, - // from: AccountId, - // ) -> Result { - // let nonces = self - // .0 - // .get_accounts_nonces(vec![from]) - // .await - // .map_err(ExecutionFailureKind::SequencerError)?; - - // let account_ids = vec![from]; - // let program_id = Program::authenticated_transfer_program().id(); - // let message = Message::try_new( - // program_id, - // account_ids, - // nonces, - // AuthTransferInstruction::Initialize, - // ) - // .unwrap(); - - // let signing_key = self.0.storage.key_chain().pub_account_signing_key(from); - - // let Some(signing_key) = signing_key else { - // return Err(ExecutionFailureKind::KeyNotFoundError); - // }; - - // let witness_set = WitnessSet::for_message(&message, &[signing_key]); - - // let tx = PublicTransaction::new(message, witness_set); - - // Ok(self - // .0 - // .sequencer_client - // .send_transaction(NSSATransaction::Public(tx)) - // .await?) - // } - pub async fn register_account( &self, from: AccountId, diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 28767366..e933787d 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -1,7 +1,6 @@ -use common::{HashType, transaction::NSSATransaction}; -use nssa::AccountId; +use common::HashType; +use nssa::{AccountId, program::Program}; use nssa_core::{MembershipProof, SharedSecretKey}; -use sequencer_service_rpc::RpcClient as _; use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; @@ -14,20 +13,21 @@ impl Pinata<'_> { 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 program = Program::pinata(); + let instruction = solution; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(pinata_account_id), + AccountManagerAccountIdentity::Public(winner_account_id), + ], + instruction_data, + &program.into(), + ) + .await } /// Claim a pinata reward using a privacy-preserving transaction for an already-initialized diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 096ea40c..96ed5338 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -1,7 +1,6 @@ -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; -use sequencer_service_rpc::RpcClient as _; use token_core::Instruction; use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; @@ -16,47 +15,21 @@ impl Token<'_> { name: String, total_supply: u128, ) -> Result { - let account_ids = vec![definition_account_id, supply_account_id]; - let program_id = nssa::program::Program::token().id(); + let program = Program::token(); let instruction = Instruction::NewFungibleDefinition { name, total_supply }; - let nonces = self - .0 - .get_accounts_nonces(account_ids.clone()) + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(definition_account_id), + AccountManagerAccountIdentity::Public(supply_account_id), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let def_private_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let supply_private_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(supply_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[def_private_key, supply_private_key], - ); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_new_definition_private_owned_supply( @@ -162,62 +135,23 @@ impl Token<'_> { 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(); + let program = Program::token(); let instruction = Instruction::Transfer { amount_to_transfer: amount, }; - let mut nonces = self - .0 - .get_accounts_nonces(vec![sender_account_id]) + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(sender_account_id), + AccountManagerAccountIdentity::Public(recipient_account_id), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - let sender_sk = self - .0 - .storage - .key_chain() - .pub_account_signing_key(sender_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(sender_sk); - - if let Some(recipient_sk) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(recipient_account_id) - { - private_keys.push(recipient_sk); - let recipient_nonces = self - .0 - .get_accounts_nonces(vec![recipient_account_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(recipient_nonces); - } else { - println!( - "Receiver's account ({recipient_account_id}) private key not found in wallet. Proceeding with only sender's key." - ); - } - - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - nonces, - instruction, - ) - .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_transfer_transaction_private_owned_account( @@ -401,40 +335,23 @@ impl Token<'_> { holder_account_id: AccountId, amount: u128, ) -> Result { - let account_ids = vec![definition_account_id, holder_account_id]; + let program = Program::token(); let instruction = Instruction::Burn { amount_to_burn: amount, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let nonces = self - .0 - .get_accounts_nonces(vec![holder_account_id]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(definition_account_id), + AccountManagerAccountIdentity::Public(holder_account_id), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - let message = nssa::public_transaction::Message::try_new( - Program::token().id(), - account_ids, - nonces, - instruction, - ) - .expect("Instruction should serialize"); - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(holder_account_id) - .ok_or(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_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_burn_transaction_private_owned_account( @@ -543,62 +460,23 @@ impl Token<'_> { holder_account_id: AccountId, amount: u128, ) -> Result { - let account_ids = vec![definition_account_id, holder_account_id]; + let program = Program::token(); let instruction = Instruction::Mint { amount_to_mint: amount, }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); - let mut nonces = self - .0 - .get_accounts_nonces(vec![definition_account_id]) + self.0 + .send_pub_tx( + vec![ + AccountManagerAccountIdentity::Public(definition_account_id), + AccountManagerAccountIdentity::Public(holder_account_id), + ], + instruction_data, + &program.into(), + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - let definition_sk = self - .0 - .storage - .key_chain() - .pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(definition_sk); - - if let Some(holder_sk) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(holder_account_id) - { - private_keys.push(holder_sk); - let recipient_nonces = self - .0 - .get_accounts_nonces(vec![holder_account_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(recipient_nonces); - } else { - println!( - "Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key." - ); - } - - let message = nssa::public_transaction::Message::try_new( - Program::token().id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_mint_transaction_private_owned_account( From 43e7fa9be245a6ddb6edefc9e8e91d87c6b3f6ed Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 19 May 2026 17:54:25 +0300 Subject: [PATCH 3/7] fix(wallet): no sign option added --- wallet/src/account_manager.rs | 15 ++++- wallet/src/program_facades/amm.rs | 84 ++++++++++++++++++++-------- wallet/src/program_facades/ata.rs | 12 ++-- wallet/src/program_facades/pinata.rs | 4 +- wallet/src/program_facades/token.rs | 2 +- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/wallet/src/account_manager.rs b/wallet/src/account_manager.rs index 0f43baf2..5a209d27 100644 --- a/wallet/src/account_manager.rs +++ b/wallet/src/account_manager.rs @@ -13,6 +13,8 @@ use crate::{ExecutionFailureKind, WalletCore}; #[derive(Clone)] pub enum AccountManagerAccountIdentity { Public(AccountId), + /// A public account without signing. Would not try to sign, even if account is owned. + PublicNoSign(AccountId), PrivateOwned(AccountId), PrivateForeign { npk: NullifierPublicKey, @@ -53,7 +55,7 @@ pub enum AccountManagerAccountIdentity { impl AccountManagerAccountIdentity { #[must_use] pub const fn is_public(&self) -> bool { - matches!(&self, Self::Public(_)) + matches!(&self, Self::Public(_) | Self::PublicNoSign(_)) } #[must_use] @@ -109,6 +111,17 @@ impl AccountManager { State::Public { account, sk } } + AccountManagerAccountIdentity::PublicNoSign(account_id) => { + let acc = wallet + .get_account_public(account_id) + .await + .map_err(ExecutionFailureKind::SequencerError)?; + + let sk = None; + let account = AccountWithMetadata::new(acc.clone(), sk.is_some(), account_id); + + State::Public { account, sk } + } AccountManagerAccountIdentity::PrivateOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, false).await?; diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 189c79f1..43352386 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -51,10 +51,10 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), - AccountManagerAccountIdentity::Public(pool_lp), + AccountManagerAccountIdentity::PublicNoSign(amm_pool), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + AccountManagerAccountIdentity::PublicNoSign(pool_lp), AccountManagerAccountIdentity::Public(user_holding_a), AccountManagerAccountIdentity::Public(user_holding_b), AccountManagerAccountIdentity::Public(user_holding_lp), @@ -105,14 +105,34 @@ impl Amm<'_> { let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); + if (token_definition_id_in != definition_token_a_id) + && (token_definition_id_in != definition_token_b_id) + { + return Err(ExecutionFailureKind::AccountDataError( + token_definition_id_in, + )); + } + + let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { + AccountManagerAccountIdentity::Public(user_holding_a) + } else { + AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + }; + + let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { + AccountManagerAccountIdentity::Public(user_holding_b) + } else { + AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + }; + self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), + AccountManagerAccountIdentity::PublicNoSign(amm_pool), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + user_a_signing_indentity, + user_b_signing_indentity, ], instruction_data, &program.into(), @@ -160,14 +180,34 @@ impl Amm<'_> { let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); + if (token_definition_id_in != definition_token_a_id) + && (token_definition_id_in != definition_token_b_id) + { + return Err(ExecutionFailureKind::AccountDataError( + token_definition_id_in, + )); + } + + let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { + AccountManagerAccountIdentity::Public(user_holding_a) + } else { + AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + }; + + let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { + AccountManagerAccountIdentity::Public(user_holding_b) + } else { + AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + }; + self.0 .send_pub_tx( vec![ AccountManagerAccountIdentity::Public(amm_pool), AccountManagerAccountIdentity::Public(vault_holding_a), AccountManagerAccountIdentity::Public(vault_holding_b), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), + user_a_signing_indentity, + user_b_signing_indentity, ], instruction_data, &program.into(), @@ -220,13 +260,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), - AccountManagerAccountIdentity::Public(pool_lp), + AccountManagerAccountIdentity::PublicNoSign(amm_pool), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + AccountManagerAccountIdentity::PublicNoSign(pool_lp), AccountManagerAccountIdentity::Public(user_holding_a), AccountManagerAccountIdentity::Public(user_holding_b), - AccountManagerAccountIdentity::Public(user_holding_lp), + AccountManagerAccountIdentity::PublicNoSign(user_holding_lp), ], instruction_data, &program.into(), @@ -279,12 +319,12 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), - AccountManagerAccountIdentity::Public(pool_lp), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), + AccountManagerAccountIdentity::PublicNoSign(amm_pool), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), + AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + AccountManagerAccountIdentity::PublicNoSign(pool_lp), + AccountManagerAccountIdentity::PublicNoSign(user_holding_a), + AccountManagerAccountIdentity::PublicNoSign(user_holding_b), AccountManagerAccountIdentity::Public(user_holding_lp), ], instruction_data, diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index d6e3fea8..e7131b39 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -31,8 +31,8 @@ impl Ata<'_> { .send_pub_tx( vec![ AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::Public(definition_id), - AccountManagerAccountIdentity::Public(ata_id), + AccountManagerAccountIdentity::PublicNoSign(definition_id), + AccountManagerAccountIdentity::PublicNoSign(ata_id), ], instruction_data, &program.into(), @@ -64,8 +64,8 @@ impl Ata<'_> { .send_pub_tx( vec![ AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::Public(sender_ata_id), - AccountManagerAccountIdentity::Public(recipient_id), + AccountManagerAccountIdentity::PublicNoSign(sender_ata_id), + AccountManagerAccountIdentity::PublicNoSign(recipient_id), ], instruction_data, &program.into(), @@ -96,8 +96,8 @@ impl Ata<'_> { .send_pub_tx( vec![ AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::Public(holder_ata_id), - AccountManagerAccountIdentity::Public(definition_id), + AccountManagerAccountIdentity::PublicNoSign(holder_ata_id), + AccountManagerAccountIdentity::PublicNoSign(definition_id), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index e933787d..0c3a599b 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -21,8 +21,8 @@ impl Pinata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(pinata_account_id), - AccountManagerAccountIdentity::Public(winner_account_id), + AccountManagerAccountIdentity::PublicNoSign(pinata_account_id), + AccountManagerAccountIdentity::PublicNoSign(winner_account_id), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 96ed5338..2de2b796 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -345,7 +345,7 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountManagerAccountIdentity::PublicNoSign(definition_account_id), AccountManagerAccountIdentity::Public(holder_account_id), ], instruction_data, From 7920b17c6d3e7158a162b8b6155f30ca386abe6d Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 19 May 2026 18:00:03 +0300 Subject: [PATCH 4/7] fix(deny): deny fix --- Cargo.lock | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 195a5087..a96ed685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,9 +674,9 @@ checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "astral-tokio-tar" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce73b17c62717c4b6a9af10b43e87c578b0cac27e00666d48304d3b7d2c0693" +checksum = "cb50a7aae84a03bf55b067832bc376f4961b790c97e64d3eacee97d389b90277" dependencies = [ "filetime", "futures-core", @@ -2245,7 +2245,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2576,7 +2576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3555,7 +3555,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -6373,7 +6373,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7227,7 +7227,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -7264,9 +7264,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8161,7 +8161,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8219,7 +8219,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9132,7 +9132,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10434,7 +10434,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10573,15 +10573,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.61.2" From fb4c5ce46d7d9345f0b32c9ae3546cfe440827df Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Wed, 20 May 2026 18:33:16 +0300 Subject: [PATCH 5/7] feat(wallet-ffi): exposed account identity, added resolve functions. --- .../src/bin/run_hello_world_private.rs | 4 +- ...n_hello_world_through_tail_call_private.rs | 4 +- .../bin/run_hello_world_with_move_function.rs | 8 +- integration_tests/src/setup.rs | 6 +- integration_tests/tests/private_pda.rs | 10 +- wallet-ffi/src/keys.rs | 84 ++++++ wallet-ffi/src/types.rs | 275 +++++++++++++++++- wallet-ffi/wallet_ffi.h | 66 +++++ wallet/src/account_manager.rs | 24 +- wallet/src/lib.rs | 20 +- wallet/src/program_facades/amm.rs | 72 ++--- wallet/src/program_facades/ata.rs | 32 +- .../native_token_transfer/deshielded.rs | 4 +- .../native_token_transfer/private.rs | 4 +- .../native_token_transfer/public.rs | 8 +- .../native_token_transfer/shielded.rs | 8 +- wallet/src/program_facades/pinata.rs | 8 +- wallet/src/program_facades/token.rs | 46 +-- 18 files changed, 553 insertions(+), 130 deletions(-) diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index 4fa149ea..725019f1 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -1,5 +1,5 @@ use nssa::{AccountId, program::Program}; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `hello_world.rs` guest program with: // @@ -44,7 +44,7 @@ async fn main() { // Define the desired greeting in ASCII let greeting: Vec = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33]; - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction wallet_core diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 60d5df7c..d68e99dc 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -4,7 +4,7 @@ use nssa::{ AccountId, ProgramId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -51,7 +51,7 @@ async fn main() { std::iter::once((hello_world.id(), hello_world)).collect(); let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies); - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction let instruction = (); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index e2056c4c..e6f667a6 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand}; use common::transaction::NSSATransaction; use nssa::{PublicTransaction, program::Program, public_transaction}; use sequencer_service_rpc::RpcClient as _; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `hello_world_with_move_function.rs` guest program with: // @@ -99,7 +99,7 @@ async fn main() { } => { let instruction: Instruction = (WRITE_FUNCTION_ID, greeting.into_bytes()); let account_id = account_id.parse().unwrap(); - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; wallet_core .send_privacy_preserving_tx( @@ -138,8 +138,8 @@ async fn main() { let to = to.parse().unwrap(); let accounts = vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::PrivateOwned(to), + AccountIdentity::Public(from), + AccountIdentity::PrivateOwned(to), ]; wallet_core diff --git a/integration_tests/src/setup.rs b/integration_tests/src/setup.rs index c1165285..8ec9c466 100644 --- a/integration_tests/src/setup.rs +++ b/integration_tests/src/setup.rs @@ -10,7 +10,7 @@ use sequencer_service_rpc::RpcClient as _; use tempfile::TempDir; use testcontainers::compose::DockerCompose; use wallet::{ - AccDecodeData::Decode, AccountManagerAccountIdentity, WalletCore, config::WalletConfigOverrides, + AccDecodeData::Decode, AccountIdentity, WalletCore, config::WalletConfigOverrides, }; use crate::{ @@ -293,8 +293,8 @@ async fn claim_funds_from_vault_to_private( let (tx_hash, mut secrets) = wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::PrivateOwned(owner_id), - AccountManagerAccountIdentity::Public(owner_vault_id), + AccountIdentity::PrivateOwned(owner_id), + AccountIdentity::Public(owner_vault_id), ], instruction_data, &program_with_dependencies, diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index 4cbc07d1..0f8d7b8d 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -18,7 +18,7 @@ use nssa::{ use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey, program::PdaSeed}; use tokio::test; use wallet::{ - AccountManagerAccountIdentity, WalletCore, + AccountIdentity, WalletCore, cli::{Command, account::AccountSubcommand}, }; @@ -46,8 +46,8 @@ async fn fund_private_pda( wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender), - AccountManagerAccountIdentity::PrivatePdaForeign { + AccountIdentity::Public(sender), + AccountIdentity::PrivatePdaForeign { account_id: pda_account_id, npk, vpk, @@ -83,8 +83,8 @@ async fn spend_private_pda( wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::PrivatePdaOwned(pda_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivatePdaOwned(pda_account_id), + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: 0, diff --git a/wallet-ffi/src/keys.rs b/wallet-ffi/src/keys.rs index b676ffab..29fe8184 100644 --- a/wallet-ffi/src/keys.rs +++ b/wallet-ffi/src/keys.rs @@ -3,11 +3,13 @@ use std::ptr; use nssa::{AccountId, PublicKey}; +use wallet::AccountIdentity; use crate::{ error::{print_error, WalletFfiError}, types::{FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey, WalletHandle}, wallet::get_wallet, + FfiAccountIdentity, }; /// Get the public key for a public account. @@ -250,3 +252,85 @@ pub unsafe extern "C" fn wallet_ffi_account_id_from_base58( WalletFfiError::Success } + +/// Resolve public account. +/// +/// # Parameters +/// - `account_id`: 32 bytes of the public account ID +/// - `needs_sign`: does account needs signing +/// - `out_account_identity`: valid pointer, where output will be written +/// +/// # Returns +/// - `Success` on successful retrieval +/// +/// # Safety +/// - `out_account_identity` must be a valid pointer to a `FfiAccountManagerAccountIdentity` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_resolve_public_account( + account_id: FfiBytes32, + needs_sign: bool, + out_account_identity: *mut FfiAccountIdentity, +) -> WalletFfiError { + let resolved_account = if needs_sign { + AccountIdentity::Public(account_id.into()) + } else { + AccountIdentity::PublicNoSign(account_id.into()) + }; + + unsafe { + *out_account_identity = resolved_account.into(); + } + + WalletFfiError::Success +} + +/// Resolve private account. +/// +/// # Parameters +/// - `handle`: Valid wallet handle +/// - `account_id`: 32 bytes of the public account ID +/// - `out_account_identity`: valid pointer, where output will be written +/// +/// # Returns +/// - `Success` on successful retrieval +/// - `InternalError` if wailed to lock wallet +/// - `AccountNotFound` if failed to found account +/// +/// # Safety +/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` +/// - `out_account_identity` must be a valid pointer to a `FfiAccountManagerAccountIdentity` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_resolve_private_account( + handle: *mut WalletHandle, + account_id: FfiBytes32, + out_account_identity: *mut FfiAccountIdentity, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let account_id = account_id.into(); + + let resolved_account = match wallet.resolve_private_account(account_id) { + Some(v) => v, + None => { + print_error(format!("Account not found")); + return WalletFfiError::AccountNotFound; + } + }; + + unsafe { + *out_account_identity = resolved_account.into(); + } + + WalletFfiError::Success +} diff --git a/wallet-ffi/src/types.rs b/wallet-ffi/src/types.rs index b970a8d3..06833f74 100644 --- a/wallet-ffi/src/types.rs +++ b/wallet-ffi/src/types.rs @@ -4,7 +4,8 @@ use core::slice; use std::{ffi::c_char, ptr}; use nssa::Data; -use nssa_core::encryption::shared_key_derivation::Secp256k1Point; +use nssa_core::{encryption::shared_key_derivation::Secp256k1Point, NullifierPublicKey}; +use wallet::AccountIdentity; use crate::error::WalletFfiError; @@ -172,6 +173,45 @@ impl FfiPrivateAccountKeys { } } +/// Enumeration to represent kinds of FfiAccountManagerAccountIdentity +#[repr(C)] +pub enum FfiAccountIdentityKind { + Public = 0, + PublicNoSign = 1, + PrivateOwned = 2, + PrivateForeign = 3, + PrivatePdaOwned = 4, + PrivatePdaForeign = 5, + PrivateShared = 6, + PrivatePdaShared = 7, +} + +/// Struct representing of account identity, given to `AccountManager` at intialization +#[repr(C)] +pub struct FfiAccountIdentity { + kind: FfiAccountIdentityKind, + pub account_id: FfiBytes32, + pub nullifier_secret_key: FfiBytes32, + pub nullifier_public_key: FfiBytes32, + pub viewing_public_key: *const u8, + pub viewing_public_key_len: usize, + pub identifier: FfiU128, +} + +impl Default for FfiAccountIdentity { + fn default() -> Self { + Self { + kind: FfiAccountIdentityKind::Public, + account_id: FfiBytes32::default(), + nullifier_secret_key: FfiBytes32::default(), + nullifier_public_key: FfiBytes32::default(), + viewing_public_key: std::ptr::null(), + viewing_public_key_len: 0, + identifier: FfiU128::default(), + } + } +} + impl From for FfiU128 { fn from(value: u128) -> Self { Self { @@ -192,6 +232,12 @@ impl From for FfiBytes32 { } } +impl From<[u8; 32]> for FfiBytes32 { + fn from(value: [u8; 32]) -> Self { + Self { data: value } + } +} + impl From for nssa::AccountId { fn from(bytes: FfiBytes32) -> Self { Self::new(bytes.data) @@ -266,3 +312,230 @@ impl TryFrom<&FfiPublicAccountKey> for nssa::PublicKey { Ok(public_key) } } + +impl From for FfiAccountIdentity { + fn from(value: AccountIdentity) -> Self { + match value { + AccountIdentity::Public(account_id) => Self { + kind: FfiAccountIdentityKind::Public, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PublicNoSign(account_id) => Self { + kind: FfiAccountIdentityKind::PublicNoSign, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PrivateOwned(account_id) => Self { + kind: FfiAccountIdentityKind::PrivateOwned, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PrivateForeign { + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.0; + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivateForeign, + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivatePdaOwned(account_id) => Self { + kind: FfiAccountIdentityKind::PrivatePdaOwned, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PrivatePdaForeign { + account_id, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.0; + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivatePdaForeign, + account_id: account_id.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivateShared { + nsk, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.0; + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivateShared, + nullifier_secret_key: nsk.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivatePdaShared { + account_id, + nsk, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.0; + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivateShared, + account_id: account_id.into(), + nullifier_secret_key: nsk.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + } + } + } + } +} + +impl TryFrom<&FfiAccountIdentity> for AccountIdentity { + type Error = WalletFfiError; + + fn try_from(value: &FfiAccountIdentity) -> Result { + match value.kind { + FfiAccountIdentityKind::Public => Ok( + AccountIdentity::Public(value.account_id.into()), + ), + FfiAccountIdentityKind::PublicNoSign => Ok( + AccountIdentity::PublicNoSign(value.account_id.into()), + ), + FfiAccountIdentityKind::PrivateOwned => Ok( + AccountIdentity::PrivateOwned(value.account_id.into()), + ), + FfiAccountIdentityKind::PrivateForeign => { + let vpk = if value.viewing_public_key_len == 33 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(Secp256k1Point(slice.to_vec())) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(AccountIdentity::PrivateForeign { + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivatePdaOwned => Ok( + AccountIdentity::PrivatePdaOwned(value.account_id.into()), + ), + FfiAccountIdentityKind::PrivatePdaForeign => { + let vpk = if value.viewing_public_key_len == 33 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(Secp256k1Point(slice.to_vec())) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(AccountIdentity::PrivatePdaForeign { + account_id: value.account_id.into(), + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivateShared => { + let vpk = if value.viewing_public_key_len == 33 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(Secp256k1Point(slice.to_vec())) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(AccountIdentity::PrivateShared { + nsk: value.nullifier_secret_key.data, + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivatePdaShared => { + let vpk = if value.viewing_public_key_len == 33 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(Secp256k1Point(slice.to_vec())) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(AccountIdentity::PrivatePdaShared { + account_id: value.account_id.into(), + nsk: value.nullifier_secret_key.data, + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + } + } +} diff --git a/wallet-ffi/wallet_ffi.h b/wallet-ffi/wallet_ffi.h index adbb7b50..211d6513 100644 --- a/wallet-ffi/wallet_ffi.h +++ b/wallet-ffi/wallet_ffi.h @@ -109,6 +109,20 @@ typedef enum WalletFfiError { INTERNAL_ERROR = 99, } WalletFfiError; +/** + * Enumeration to represent kinds of FfiAccountManagerAccountIdentity + */ +typedef enum FfiAccountIdentityKind { + PUBLIC = 0, + PUBLIC_NO_SIGN = 1, + PRIVATE_OWNED = 2, + PRIVATE_FOREIGN = 3, + PRIVATE_PDA_OWNED = 4, + PRIVATE_PDA_FOREIGN = 5, + PRIVATE_SHARED = 6, + PRIVATE_PDA_SHARED = 7, +} FfiAccountIdentityKind; + /** * Opaque pointer to the Wallet instance. * @@ -207,6 +221,19 @@ typedef struct FfiPublicAccountKey { struct FfiBytes32 public_key; } FfiPublicAccountKey; +/** + * Struct representing of account identity, given to `AccountManager` at intialization + */ +typedef struct FfiAccountIdentity { + enum FfiAccountIdentityKind kind; + struct FfiBytes32 account_id; + struct FfiBytes32 nullifier_secret_key; + struct FfiBytes32 nullifier_public_key; + const uint8_t *viewing_public_key; + uintptr_t viewing_public_key_len; + struct FfiU128 identifier; +} FfiAccountIdentity; + /** * Result of a transfer operation. */ @@ -552,6 +579,45 @@ char *wallet_ffi_account_id_to_base58(const struct FfiBytes32 *account_id); enum WalletFfiError wallet_ffi_account_id_from_base58(const char *base58_str, struct FfiBytes32 *out_account_id); +/** + * Resolve public account. + * + * # Parameters + * - `account_id`: 32 bytes of the public account ID + * - `needs_sign`: does account needs signing + * - `out_account_identity`: valid pointer, where output will be written + * + * # Returns + * - `Success` on successful retrieval + * + * # Safety + * - `out_account_identity` must be a valid pointer to a `FfiAccountManagerAccountIdentity` struct + */ +enum WalletFfiError wallet_ffi_resolve_public_account(struct FfiBytes32 account_id, + bool needs_sign, + struct FfiAccountIdentity *out_account_identity); + +/** + * Resolve private account. + * + * # Parameters + * - `handle`: Valid wallet handle + * - `account_id`: 32 bytes of the public account ID + * - `out_account_identity`: valid pointer, where output will be written + * + * # Returns + * - `Success` on successful retrieval + * - `InternalError` if wailed to lock wallet + * - `AccountNotFound` if failed to found account + * + * # Safety + * - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` + * - `out_account_identity` must be a valid pointer to a `FfiAccountManagerAccountIdentity` struct + */ +enum WalletFfiError wallet_ffi_resolve_private_account(struct WalletHandle *handle, + struct FfiBytes32 account_id, + struct FfiAccountIdentity *out_account_identity); + /** * Claim a pinata reward using a public transaction. * diff --git a/wallet/src/account_manager.rs b/wallet/src/account_manager.rs index 5a209d27..a088bcd6 100644 --- a/wallet/src/account_manager.rs +++ b/wallet/src/account_manager.rs @@ -11,7 +11,7 @@ use nssa_core::{ use crate::{ExecutionFailureKind, WalletCore}; #[derive(Clone)] -pub enum AccountManagerAccountIdentity { +pub enum AccountIdentity { Public(AccountId), /// A public account without signing. Would not try to sign, even if account is owned. PublicNoSign(AccountId), @@ -52,7 +52,7 @@ pub enum AccountManagerAccountIdentity { }, } -impl AccountManagerAccountIdentity { +impl AccountIdentity { #[must_use] pub const fn is_public(&self) -> bool { matches!(&self, Self::Public(_) | Self::PublicNoSign(_)) @@ -94,13 +94,13 @@ pub struct AccountManager { impl AccountManager { pub async fn new( wallet: &WalletCore, - accounts: Vec, + accounts: Vec, ) -> Result { let mut states = Vec::with_capacity(accounts.len()); for account in accounts { let state = match account { - AccountManagerAccountIdentity::Public(account_id) => { + AccountIdentity::Public(account_id) => { let acc = wallet .get_account_public(account_id) .await @@ -111,7 +111,7 @@ impl AccountManager { State::Public { account, sk } } - AccountManagerAccountIdentity::PublicNoSign(account_id) => { + AccountIdentity::PublicNoSign(account_id) => { let acc = wallet .get_account_public(account_id) .await @@ -122,12 +122,12 @@ impl AccountManager { State::Public { account, sk } } - AccountManagerAccountIdentity::PrivateOwned(account_id) => { + AccountIdentity::PrivateOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, false).await?; State::Private(pre) } - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk, vpk, identifier, @@ -151,11 +151,11 @@ impl AccountManager { State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaOwned(account_id) => { + AccountIdentity::PrivatePdaOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, true).await?; State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaForeign { + AccountIdentity::PrivatePdaForeign { account_id, npk, vpk, @@ -179,7 +179,7 @@ impl AccountManager { }; State::Private(pre) } - AccountManagerAccountIdentity::PrivateShared { + AccountIdentity::PrivateShared { nsk, npk, vpk, @@ -193,7 +193,7 @@ impl AccountManager { State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaShared { + AccountIdentity::PrivatePdaShared { account_id, nsk, npk, @@ -423,7 +423,7 @@ mod tests { #[test] fn private_shared_is_private() { - let acc = AccountManagerAccountIdentity::PrivateShared { + let acc = AccountIdentity::PrivateShared { nsk: [0; 32], npk: NullifierPublicKey([1; 32]), vpk: ViewingPublicKey::from_scalar([2; 32]), diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index e1fec4d1..50fb993f 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; -pub use account_manager::AccountManagerAccountIdentity; +pub use account_manager::AccountIdentity; use anyhow::{Context as _, Result}; use bip39::Mnemonic; use common::{HashType, transaction::NSSATransaction}; @@ -276,7 +276,7 @@ impl WalletCore { pub fn resolve_private_account( &self, account_id: nssa::AccountId, - ) -> Option { + ) -> Option { // Check key tree first if self .storage @@ -284,7 +284,7 @@ impl WalletCore { .private_account(account_id) .is_some() { - return Some(AccountManagerAccountIdentity::PrivateOwned(account_id)); + return Some(AccountIdentity::PrivateOwned(account_id)); } // Check shared private accounts @@ -299,7 +299,7 @@ impl WalletCore { if let (Some(pda_seed), Some(program_id)) = (entry.pda_seed, entry.pda_program_id) { let keys = holder.derive_keys_for_pda(&program_id, &pda_seed); - Some(AccountManagerAccountIdentity::PrivatePdaShared { + Some(AccountIdentity::PrivatePdaShared { account_id, nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), @@ -316,7 +316,7 @@ impl WalletCore { result }; let keys = holder.derive_keys_for_shared_account(&derivation_seed); - Some(AccountManagerAccountIdentity::PrivateShared { + Some(AccountIdentity::PrivateShared { nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), vpk: keys.generate_viewing_public_key(), @@ -541,7 +541,7 @@ impl WalletCore { pub async fn send_privacy_preserving_tx( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, ) -> Result<(HashType, Vec), ExecutionFailureKind> { @@ -553,7 +553,7 @@ impl WalletCore { pub async fn send_privacy_preserving_tx_with_pre_check( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, @@ -612,7 +612,7 @@ impl WalletCore { pub async fn send_pub_tx( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, ) -> Result { @@ -622,7 +622,7 @@ impl WalletCore { pub async fn send_pub_tx_with_pre_check( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, @@ -630,7 +630,7 @@ impl WalletCore { // Public transaction, all accounts must be public if accounts .iter() - .any(AccountManagerAccountIdentity::is_private) + .any(AccountIdentity::is_private) { return Err(ExecutionFailureKind::TransactionBuildError( nssa::error::NssaError::InvalidInput( diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 43352386..2dbab6c5 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -3,7 +3,7 @@ use common::HashType; use nssa::{AccountId, program::Program}; use token_core::TokenHolding; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Amm<'wallet>(pub &'wallet WalletCore); impl Amm<'_> { @@ -51,13 +51,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), - AccountManagerAccountIdentity::Public(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::Public(user_holding_a), + AccountIdentity::Public(user_holding_b), + AccountIdentity::Public(user_holding_lp), ], instruction_data, &program.into(), @@ -114,23 +114,23 @@ impl Amm<'_> { } let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { - AccountManagerAccountIdentity::Public(user_holding_a) + AccountIdentity::Public(user_holding_a) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(user_holding_a) }; let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { - AccountManagerAccountIdentity::Public(user_holding_b) + AccountIdentity::Public(user_holding_b) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(user_holding_b) }; self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), user_a_signing_indentity, user_b_signing_indentity, ], @@ -189,23 +189,23 @@ impl Amm<'_> { } let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { - AccountManagerAccountIdentity::Public(user_holding_a) + AccountIdentity::Public(user_holding_a) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(user_holding_a) }; let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { - AccountManagerAccountIdentity::Public(user_holding_b) + AccountIdentity::Public(user_holding_b) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(user_holding_b) }; self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), + AccountIdentity::Public(amm_pool), + AccountIdentity::Public(vault_holding_a), + AccountIdentity::Public(vault_holding_b), user_a_signing_indentity, user_b_signing_indentity, ], @@ -260,13 +260,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), - AccountManagerAccountIdentity::PublicNoSign(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::Public(user_holding_a), + AccountIdentity::Public(user_holding_b), + AccountIdentity::PublicNoSign(user_holding_lp), ], instruction_data, &program.into(), @@ -319,13 +319,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::PublicNoSign(user_holding_a), - AccountManagerAccountIdentity::PublicNoSign(user_holding_b), - AccountManagerAccountIdentity::Public(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::PublicNoSign(user_holding_a), + AccountIdentity::PublicNoSign(user_holding_b), + AccountIdentity::Public(user_holding_lp), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index e7131b39..68d45bbe 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -7,7 +7,7 @@ use nssa::{ }; use nssa_core::SharedSecretKey; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Ata<'wallet>(pub &'wallet WalletCore); @@ -30,9 +30,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(definition_id), - AccountManagerAccountIdentity::PublicNoSign(ata_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(definition_id), + AccountIdentity::PublicNoSign(ata_id), ], instruction_data, &program.into(), @@ -63,9 +63,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(sender_ata_id), - AccountManagerAccountIdentity::PublicNoSign(recipient_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(sender_ata_id), + AccountIdentity::PublicNoSign(recipient_id), ], instruction_data, &program.into(), @@ -95,9 +95,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(holder_ata_id), - AccountManagerAccountIdentity::PublicNoSign(definition_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(holder_ata_id), + AccountIdentity::PublicNoSign(definition_id), ], instruction_data, &program.into(), @@ -124,8 +124,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(definition_id), - AccountManagerAccountIdentity::Public(ata_id), + AccountIdentity::Public(definition_id), + AccountIdentity::Public(ata_id), ]; self.0 @@ -161,8 +161,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(sender_ata_id), - AccountManagerAccountIdentity::Public(recipient_id), + AccountIdentity::Public(sender_ata_id), + AccountIdentity::Public(recipient_id), ]; self.0 @@ -197,8 +197,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_ata_id), - AccountManagerAccountIdentity::Public(definition_id), + AccountIdentity::Public(holder_ata_id), + AccountIdentity::Public(definition_id), ]; self.0 diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index 3cfaea1b..31374f99 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -2,7 +2,7 @@ use common::HashType; use nssa::AccountId; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_deshielded_transfer( @@ -19,7 +19,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(to), + AccountIdentity::Public(to), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index 508bb62f..481e4a5f 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -5,7 +5,7 @@ use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn register_account_private( @@ -49,7 +49,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index 6054383c..03fccc9d 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -4,7 +4,7 @@ use nssa::{AccountId, program::Program}; use super::NativeTokenTransfer; use crate::{ - AccountManagerAccountIdentity, ExecutionFailureKind, + AccountIdentity, ExecutionFailureKind, program_facades::native_token_transfer::auth_transfer_preparation, }; @@ -20,8 +20,8 @@ impl NativeTokenTransfer<'_> { self.0 .send_pub_tx_with_pre_check( vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::Public(to), + AccountIdentity::Public(from), + AccountIdentity::Public(to), ], instruction_data, &program.into(), @@ -39,7 +39,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_pub_tx( - vec![AccountManagerAccountIdentity::Public(from)], + vec![AccountIdentity::Public(from)], instruction_data, &program.into(), ) diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 385187ce..44916529 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -3,7 +3,7 @@ use nssa::AccountId; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( @@ -17,7 +17,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountManagerAccountIdentity::Public(from), + AccountIdentity::Public(from), self.0 .resolve_private_account(to) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -49,8 +49,8 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(from), + AccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 0c3a599b..2e40e78b 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -2,7 +2,7 @@ use common::HashType; use nssa::{AccountId, program::Program}; use nssa_core::{MembershipProof, SharedSecretKey}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Pinata<'wallet>(pub &'wallet WalletCore); @@ -21,8 +21,8 @@ impl Pinata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(pinata_account_id), - AccountManagerAccountIdentity::PublicNoSign(winner_account_id), + AccountIdentity::PublicNoSign(pinata_account_id), + AccountIdentity::PublicNoSign(winner_account_id), ], instruction_data, &program.into(), @@ -55,7 +55,7 @@ impl Pinata<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(pinata_account_id), + AccountIdentity::Public(pinata_account_id), self.0 .resolve_private_account(winner_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 2de2b796..8e9b4f8f 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -3,7 +3,7 @@ use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use token_core::Instruction; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Token<'wallet>(pub &'wallet WalletCore); @@ -23,8 +23,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::Public(supply_account_id), + AccountIdentity::Public(definition_account_id), + AccountIdentity::Public(supply_account_id), ], instruction_data, &program.into(), @@ -46,7 +46,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(supply_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -81,7 +81,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(supply_account_id), + AccountIdentity::Public(supply_account_id), ], instruction_data, &Program::token().into(), @@ -145,8 +145,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), - AccountManagerAccountIdentity::Public(recipient_account_id), + AccountIdentity::Public(sender_account_id), + AccountIdentity::Public(recipient_account_id), ], instruction_data, &program.into(), @@ -208,7 +208,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -244,7 +244,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(recipient_account_id), + AccountIdentity::Public(recipient_account_id), ], instruction_data, &Program::token().into(), @@ -274,7 +274,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), + AccountIdentity::Public(sender_account_id), self.0 .resolve_private_account(recipient_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -309,8 +309,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(sender_account_id), + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -345,8 +345,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(definition_account_id), - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::PublicNoSign(definition_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &program.into(), @@ -406,7 +406,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -436,7 +436,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -470,8 +470,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(definition_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &program.into(), @@ -533,7 +533,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, @@ -569,7 +569,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -599,7 +599,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -634,8 +634,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(definition_account_id), + AccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, From 97d9188c3838c07aa6bae5a501970e330a895590 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Wed, 20 May 2026 18:55:39 +0300 Subject: [PATCH 6/7] fix(wallet): suggestion 1 --- .deny.toml | 1 - .../src/bin/run_hello_world_private.rs | 4 +- ...n_hello_world_through_tail_call_private.rs | 4 +- .../bin/run_hello_world_with_move_function.rs | 8 +-- integration_tests/tests/private_pda.rs | 10 +-- test_fixtures/src/setup.rs | 8 +-- wallet/src/account_manager.rs | 24 +++---- wallet/src/lib.rs | 26 +++---- wallet/src/program_facades/amm.rs | 72 +++++++++---------- wallet/src/program_facades/ata.rs | 32 ++++----- .../native_token_transfer/deshielded.rs | 4 +- .../native_token_transfer/private.rs | 4 +- .../native_token_transfer/public.rs | 9 +-- .../native_token_transfer/shielded.rs | 8 +-- wallet/src/program_facades/pinata.rs | 8 +-- wallet/src/program_facades/token.rs | 46 ++++++------ 16 files changed, 128 insertions(+), 140 deletions(-) diff --git a/.deny.toml b/.deny.toml index 320a9eda..fb1ce3cf 100644 --- a/.deny.toml +++ b/.deny.toml @@ -16,7 +16,6 @@ ignore = [ { id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, - { id = "RUSTSEC-2026-0145", reason = "`astral-tokio-tar` v0.6.1 is pulled transitively via testcontainers (integration_tests dev/test path); waiting on upstream fix" }, ] yanked = "deny" unused-ignored-advisory = "deny" diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index 4fa149ea..725019f1 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -1,5 +1,5 @@ use nssa::{AccountId, program::Program}; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `hello_world.rs` guest program with: // @@ -44,7 +44,7 @@ async fn main() { // Define the desired greeting in ASCII let greeting: Vec = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33]; - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction wallet_core diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 60d5df7c..d68e99dc 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -4,7 +4,7 @@ use nssa::{ AccountId, ProgramId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -51,7 +51,7 @@ async fn main() { std::iter::once((hello_world.id(), hello_world)).collect(); let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies); - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; // Construct and submit the privacy-preserving transaction let instruction = (); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index e2056c4c..e6f667a6 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand}; use common::transaction::NSSATransaction; use nssa::{PublicTransaction, program::Program, public_transaction}; use sequencer_service_rpc::RpcClient as _; -use wallet::{AccountManagerAccountIdentity, WalletCore}; +use wallet::{AccountIdentity, WalletCore}; // Before running this example, compile the `hello_world_with_move_function.rs` guest program with: // @@ -99,7 +99,7 @@ async fn main() { } => { let instruction: Instruction = (WRITE_FUNCTION_ID, greeting.into_bytes()); let account_id = account_id.parse().unwrap(); - let accounts = vec![AccountManagerAccountIdentity::PrivateOwned(account_id)]; + let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; wallet_core .send_privacy_preserving_tx( @@ -138,8 +138,8 @@ async fn main() { let to = to.parse().unwrap(); let accounts = vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::PrivateOwned(to), + AccountIdentity::Public(from), + AccountIdentity::PrivateOwned(to), ]; wallet_core diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index 4cbc07d1..0f8d7b8d 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -18,7 +18,7 @@ use nssa::{ use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey, program::PdaSeed}; use tokio::test; use wallet::{ - AccountManagerAccountIdentity, WalletCore, + AccountIdentity, WalletCore, cli::{Command, account::AccountSubcommand}, }; @@ -46,8 +46,8 @@ async fn fund_private_pda( wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender), - AccountManagerAccountIdentity::PrivatePdaForeign { + AccountIdentity::Public(sender), + AccountIdentity::PrivatePdaForeign { account_id: pda_account_id, npk, vpk, @@ -83,8 +83,8 @@ async fn spend_private_pda( wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::PrivatePdaOwned(pda_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivatePdaOwned(pda_account_id), + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: 0, diff --git a/test_fixtures/src/setup.rs b/test_fixtures/src/setup.rs index c1165285..5d7377b1 100644 --- a/test_fixtures/src/setup.rs +++ b/test_fixtures/src/setup.rs @@ -9,9 +9,7 @@ use sequencer_service::{GenesisAction, SequencerHandle}; use sequencer_service_rpc::RpcClient as _; use tempfile::TempDir; use testcontainers::compose::DockerCompose; -use wallet::{ - AccDecodeData::Decode, AccountManagerAccountIdentity, WalletCore, config::WalletConfigOverrides, -}; +use wallet::{AccDecodeData::Decode, AccountIdentity, WalletCore, config::WalletConfigOverrides}; use crate::{ BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT, @@ -293,8 +291,8 @@ async fn claim_funds_from_vault_to_private( let (tx_hash, mut secrets) = wallet .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::PrivateOwned(owner_id), - AccountManagerAccountIdentity::Public(owner_vault_id), + AccountIdentity::PrivateOwned(owner_id), + AccountIdentity::Public(owner_vault_id), ], instruction_data, &program_with_dependencies, diff --git a/wallet/src/account_manager.rs b/wallet/src/account_manager.rs index 5a209d27..a088bcd6 100644 --- a/wallet/src/account_manager.rs +++ b/wallet/src/account_manager.rs @@ -11,7 +11,7 @@ use nssa_core::{ use crate::{ExecutionFailureKind, WalletCore}; #[derive(Clone)] -pub enum AccountManagerAccountIdentity { +pub enum AccountIdentity { Public(AccountId), /// A public account without signing. Would not try to sign, even if account is owned. PublicNoSign(AccountId), @@ -52,7 +52,7 @@ pub enum AccountManagerAccountIdentity { }, } -impl AccountManagerAccountIdentity { +impl AccountIdentity { #[must_use] pub const fn is_public(&self) -> bool { matches!(&self, Self::Public(_) | Self::PublicNoSign(_)) @@ -94,13 +94,13 @@ pub struct AccountManager { impl AccountManager { pub async fn new( wallet: &WalletCore, - accounts: Vec, + accounts: Vec, ) -> Result { let mut states = Vec::with_capacity(accounts.len()); for account in accounts { let state = match account { - AccountManagerAccountIdentity::Public(account_id) => { + AccountIdentity::Public(account_id) => { let acc = wallet .get_account_public(account_id) .await @@ -111,7 +111,7 @@ impl AccountManager { State::Public { account, sk } } - AccountManagerAccountIdentity::PublicNoSign(account_id) => { + AccountIdentity::PublicNoSign(account_id) => { let acc = wallet .get_account_public(account_id) .await @@ -122,12 +122,12 @@ impl AccountManager { State::Public { account, sk } } - AccountManagerAccountIdentity::PrivateOwned(account_id) => { + AccountIdentity::PrivateOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, false).await?; State::Private(pre) } - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk, vpk, identifier, @@ -151,11 +151,11 @@ impl AccountManager { State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaOwned(account_id) => { + AccountIdentity::PrivatePdaOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, true).await?; State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaForeign { + AccountIdentity::PrivatePdaForeign { account_id, npk, vpk, @@ -179,7 +179,7 @@ impl AccountManager { }; State::Private(pre) } - AccountManagerAccountIdentity::PrivateShared { + AccountIdentity::PrivateShared { nsk, npk, vpk, @@ -193,7 +193,7 @@ impl AccountManager { State::Private(pre) } - AccountManagerAccountIdentity::PrivatePdaShared { + AccountIdentity::PrivatePdaShared { account_id, nsk, npk, @@ -423,7 +423,7 @@ mod tests { #[test] fn private_shared_is_private() { - let acc = AccountManagerAccountIdentity::PrivateShared { + let acc = AccountIdentity::PrivateShared { nsk: [0; 32], npk: NullifierPublicKey([1; 32]), vpk: ViewingPublicKey::from_scalar([2; 32]), diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index e1fec4d1..fa60b18c 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; -pub use account_manager::AccountManagerAccountIdentity; +pub use account_manager::AccountIdentity; use anyhow::{Context as _, Result}; use bip39::Mnemonic; use common::{HashType, transaction::NSSATransaction}; @@ -273,10 +273,7 @@ impl WalletCore { /// Resolve an `AccountId` to the appropriate `PrivacyPreservingAccount` variant. /// Checks the key tree first, then shared private accounts. #[must_use] - pub fn resolve_private_account( - &self, - account_id: nssa::AccountId, - ) -> Option { + pub fn resolve_private_account(&self, account_id: nssa::AccountId) -> Option { // Check key tree first if self .storage @@ -284,7 +281,7 @@ impl WalletCore { .private_account(account_id) .is_some() { - return Some(AccountManagerAccountIdentity::PrivateOwned(account_id)); + return Some(AccountIdentity::PrivateOwned(account_id)); } // Check shared private accounts @@ -299,7 +296,7 @@ impl WalletCore { if let (Some(pda_seed), Some(program_id)) = (entry.pda_seed, entry.pda_program_id) { let keys = holder.derive_keys_for_pda(&program_id, &pda_seed); - Some(AccountManagerAccountIdentity::PrivatePdaShared { + Some(AccountIdentity::PrivatePdaShared { account_id, nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), @@ -316,7 +313,7 @@ impl WalletCore { result }; let keys = holder.derive_keys_for_shared_account(&derivation_seed); - Some(AccountManagerAccountIdentity::PrivateShared { + Some(AccountIdentity::PrivateShared { nsk: keys.nullifier_secret_key, npk: keys.generate_nullifier_public_key(), vpk: keys.generate_viewing_public_key(), @@ -541,7 +538,7 @@ impl WalletCore { pub async fn send_privacy_preserving_tx( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, ) -> Result<(HashType, Vec), ExecutionFailureKind> { @@ -553,7 +550,7 @@ impl WalletCore { pub async fn send_privacy_preserving_tx_with_pre_check( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, @@ -612,7 +609,7 @@ impl WalletCore { pub async fn send_pub_tx( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, ) -> Result { @@ -622,16 +619,13 @@ impl WalletCore { pub async fn send_pub_tx_with_pre_check( &self, - accounts: Vec, + accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, ) -> Result { // Public transaction, all accounts must be public - if accounts - .iter() - .any(AccountManagerAccountIdentity::is_private) - { + if accounts.iter().any(AccountIdentity::is_private) { return Err(ExecutionFailureKind::TransactionBuildError( nssa::error::NssaError::InvalidInput( "Private accounts are not allowed in public transactions".to_owned(), diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 43352386..2dbab6c5 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -3,7 +3,7 @@ use common::HashType; use nssa::{AccountId, program::Program}; use token_core::TokenHolding; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Amm<'wallet>(pub &'wallet WalletCore); impl Amm<'_> { @@ -51,13 +51,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), - AccountManagerAccountIdentity::Public(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::Public(user_holding_a), + AccountIdentity::Public(user_holding_b), + AccountIdentity::Public(user_holding_lp), ], instruction_data, &program.into(), @@ -114,23 +114,23 @@ impl Amm<'_> { } let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { - AccountManagerAccountIdentity::Public(user_holding_a) + AccountIdentity::Public(user_holding_a) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(user_holding_a) }; let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { - AccountManagerAccountIdentity::Public(user_holding_b) + AccountIdentity::Public(user_holding_b) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(user_holding_b) }; self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), user_a_signing_indentity, user_b_signing_indentity, ], @@ -189,23 +189,23 @@ impl Amm<'_> { } let user_a_signing_indentity = if token_definition_id_in == definition_token_a_id { - AccountManagerAccountIdentity::Public(user_holding_a) + AccountIdentity::Public(user_holding_a) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(user_holding_a) }; let user_b_signing_indentity = if token_definition_id_in == definition_token_b_id { - AccountManagerAccountIdentity::Public(user_holding_b) + AccountIdentity::Public(user_holding_b) } else { - AccountManagerAccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(user_holding_b) }; self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(amm_pool), - AccountManagerAccountIdentity::Public(vault_holding_a), - AccountManagerAccountIdentity::Public(vault_holding_b), + AccountIdentity::Public(amm_pool), + AccountIdentity::Public(vault_holding_a), + AccountIdentity::Public(vault_holding_b), user_a_signing_indentity, user_b_signing_indentity, ], @@ -260,13 +260,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::Public(user_holding_a), - AccountManagerAccountIdentity::Public(user_holding_b), - AccountManagerAccountIdentity::PublicNoSign(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::Public(user_holding_a), + AccountIdentity::Public(user_holding_b), + AccountIdentity::PublicNoSign(user_holding_lp), ], instruction_data, &program.into(), @@ -319,13 +319,13 @@ impl Amm<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(amm_pool), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_a), - AccountManagerAccountIdentity::PublicNoSign(vault_holding_b), - AccountManagerAccountIdentity::PublicNoSign(pool_lp), - AccountManagerAccountIdentity::PublicNoSign(user_holding_a), - AccountManagerAccountIdentity::PublicNoSign(user_holding_b), - AccountManagerAccountIdentity::Public(user_holding_lp), + AccountIdentity::PublicNoSign(amm_pool), + AccountIdentity::PublicNoSign(vault_holding_a), + AccountIdentity::PublicNoSign(vault_holding_b), + AccountIdentity::PublicNoSign(pool_lp), + AccountIdentity::PublicNoSign(user_holding_a), + AccountIdentity::PublicNoSign(user_holding_b), + AccountIdentity::Public(user_holding_lp), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index e7131b39..68d45bbe 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -7,7 +7,7 @@ use nssa::{ }; use nssa_core::SharedSecretKey; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Ata<'wallet>(pub &'wallet WalletCore); @@ -30,9 +30,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(definition_id), - AccountManagerAccountIdentity::PublicNoSign(ata_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(definition_id), + AccountIdentity::PublicNoSign(ata_id), ], instruction_data, &program.into(), @@ -63,9 +63,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(sender_ata_id), - AccountManagerAccountIdentity::PublicNoSign(recipient_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(sender_ata_id), + AccountIdentity::PublicNoSign(recipient_id), ], instruction_data, &program.into(), @@ -95,9 +95,9 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(owner_id), - AccountManagerAccountIdentity::PublicNoSign(holder_ata_id), - AccountManagerAccountIdentity::PublicNoSign(definition_id), + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(holder_ata_id), + AccountIdentity::PublicNoSign(definition_id), ], instruction_data, &program.into(), @@ -124,8 +124,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(definition_id), - AccountManagerAccountIdentity::Public(ata_id), + AccountIdentity::Public(definition_id), + AccountIdentity::Public(ata_id), ]; self.0 @@ -161,8 +161,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(sender_ata_id), - AccountManagerAccountIdentity::Public(recipient_id), + AccountIdentity::Public(sender_ata_id), + AccountIdentity::Public(recipient_id), ]; self.0 @@ -197,8 +197,8 @@ impl Ata<'_> { self.0 .resolve_private_account(owner_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_ata_id), - AccountManagerAccountIdentity::Public(definition_id), + AccountIdentity::Public(holder_ata_id), + AccountIdentity::Public(definition_id), ]; self.0 diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index 3cfaea1b..31374f99 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -2,7 +2,7 @@ use common::HashType; use nssa::AccountId; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_deshielded_transfer( @@ -19,7 +19,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(to), + AccountIdentity::Public(to), ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index 508bb62f..481e4a5f 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -5,7 +5,7 @@ use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn register_account_private( @@ -49,7 +49,7 @@ impl NativeTokenTransfer<'_> { self.0 .resolve_private_account(from) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index 6054383c..f1d7699d 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -4,7 +4,7 @@ use nssa::{AccountId, program::Program}; use super::NativeTokenTransfer; use crate::{ - AccountManagerAccountIdentity, ExecutionFailureKind, + AccountIdentity, ExecutionFailureKind, program_facades::native_token_transfer::auth_transfer_preparation, }; @@ -19,10 +19,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_pub_tx_with_pre_check( - vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::Public(to), - ], + vec![AccountIdentity::Public(from), AccountIdentity::Public(to)], instruction_data, &program.into(), tx_pre_check, @@ -39,7 +36,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_pub_tx( - vec![AccountManagerAccountIdentity::Public(from)], + vec![AccountIdentity::Public(from)], instruction_data, &program.into(), ) diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 385187ce..44916529 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -3,7 +3,7 @@ use nssa::AccountId; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind}; +use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( @@ -17,7 +17,7 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountManagerAccountIdentity::Public(from), + AccountIdentity::Public(from), self.0 .resolve_private_account(to) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -49,8 +49,8 @@ impl NativeTokenTransfer<'_> { self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountManagerAccountIdentity::Public(from), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(from), + AccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, identifier: to_identifier, diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 0c3a599b..2e40e78b 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -2,7 +2,7 @@ use common::HashType; use nssa::{AccountId, program::Program}; use nssa_core::{MembershipProof, SharedSecretKey}; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Pinata<'wallet>(pub &'wallet WalletCore); @@ -21,8 +21,8 @@ impl Pinata<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(pinata_account_id), - AccountManagerAccountIdentity::PublicNoSign(winner_account_id), + AccountIdentity::PublicNoSign(pinata_account_id), + AccountIdentity::PublicNoSign(winner_account_id), ], instruction_data, &program.into(), @@ -55,7 +55,7 @@ impl Pinata<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(pinata_account_id), + AccountIdentity::Public(pinata_account_id), self.0 .resolve_private_account(winner_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 2de2b796..8e9b4f8f 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -3,7 +3,7 @@ use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use token_core::Instruction; -use crate::{AccountManagerAccountIdentity, ExecutionFailureKind, WalletCore}; +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; pub struct Token<'wallet>(pub &'wallet WalletCore); @@ -23,8 +23,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::Public(supply_account_id), + AccountIdentity::Public(definition_account_id), + AccountIdentity::Public(supply_account_id), ], instruction_data, &program.into(), @@ -46,7 +46,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(supply_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -81,7 +81,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(supply_account_id), + AccountIdentity::Public(supply_account_id), ], instruction_data, &Program::token().into(), @@ -145,8 +145,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), - AccountManagerAccountIdentity::Public(recipient_account_id), + AccountIdentity::Public(sender_account_id), + AccountIdentity::Public(recipient_account_id), ], instruction_data, &program.into(), @@ -208,7 +208,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -244,7 +244,7 @@ impl Token<'_> { self.0 .resolve_private_account(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(recipient_account_id), + AccountIdentity::Public(recipient_account_id), ], instruction_data, &Program::token().into(), @@ -274,7 +274,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), + AccountIdentity::Public(sender_account_id), self.0 .resolve_private_account(recipient_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -309,8 +309,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(sender_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(sender_account_id), + AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, @@ -345,8 +345,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::PublicNoSign(definition_account_id), - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::PublicNoSign(definition_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &program.into(), @@ -406,7 +406,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -436,7 +436,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -470,8 +470,8 @@ impl Token<'_> { self.0 .send_pub_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(definition_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &program.into(), @@ -533,7 +533,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, @@ -569,7 +569,7 @@ impl Token<'_> { self.0 .resolve_private_account(definition_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, - AccountManagerAccountIdentity::Public(holder_account_id), + AccountIdentity::Public(holder_account_id), ], instruction_data, &Program::token().into(), @@ -599,7 +599,7 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), + AccountIdentity::Public(definition_account_id), self.0 .resolve_private_account(holder_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -634,8 +634,8 @@ impl Token<'_> { self.0 .send_privacy_preserving_tx( vec![ - AccountManagerAccountIdentity::Public(definition_account_id), - AccountManagerAccountIdentity::PrivateForeign { + AccountIdentity::Public(definition_account_id), + AccountIdentity::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, From bc6ba30f6620b8eb938142202983cbfe7385bf86 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 21 May 2026 17:04:48 +0300 Subject: [PATCH 7/7] feat: generic public transactions --- Cargo.lock | 1 + wallet-ffi/Cargo.toml | 1 + wallet-ffi/src/account.rs | 28 ++- wallet-ffi/src/error.rs | 2 + wallet-ffi/src/generic_transaction.rs | 247 ++++++++++++++++++++++++++ wallet-ffi/src/lib.rs | 1 + wallet-ffi/wallet_ffi.h | 95 +++++++++- 7 files changed, 367 insertions(+), 8 deletions(-) create mode 100644 wallet-ffi/src/generic_transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 52bb1ce8..91bbd3b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10240,6 +10240,7 @@ dependencies = [ "key_protocol", "nssa", "nssa_core", + "risc0-zkvm", "sequencer_service_rpc", "serde_json", "tempfile", diff --git a/wallet-ffi/Cargo.toml b/wallet-ffi/Cargo.toml index 869845c8..9940607f 100644 --- a/wallet-ffi/Cargo.toml +++ b/wallet-ffi/Cargo.toml @@ -19,6 +19,7 @@ sequencer_service_rpc = { workspace = true, features = ["client"] } tokio.workspace = true key_protocol.workspace = true serde_json.workspace = true +risc0-zkvm.workspace = true [build-dependencies] cbindgen = "0.29" diff --git a/wallet-ffi/src/account.rs b/wallet-ffi/src/account.rs index ed27abe6..b4fb1e84 100644 --- a/wallet-ffi/src/account.rs +++ b/wallet-ffi/src/account.rs @@ -14,7 +14,7 @@ use crate::{ WalletHandle, }, wallet::get_wallet, - FfiU128, + FfiAccountIdentity, FfiU128, }; /// Create a new public account. @@ -653,3 +653,29 @@ pub unsafe extern "C" fn wallet_ffi_import_private_account( } } } + +/// Free account identity returned by `wallet_ffi_resolve_private_account` or +/// `wallet_ffi_resolve_public_account`. +/// +/// # Safety +/// The account must be either null or a valid account returned by +/// `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_free_account_identity( + account_identity: *mut FfiAccountIdentity, +) { + if account_identity.is_null() { + return; + } + + unsafe { + let account_identity = &*account_identity; + if !account_identity.viewing_public_key.is_null() { + let slice = std::slice::from_raw_parts_mut( + account_identity.viewing_public_key.cast_mut(), + account_identity.viewing_public_key_len, + ); + drop(Box::from_raw(std::ptr::from_mut::<[u8]>(slice))); + } + } +} diff --git a/wallet-ffi/src/error.rs b/wallet-ffi/src/error.rs index 17b73075..35b084a9 100644 --- a/wallet-ffi/src/error.rs +++ b/wallet-ffi/src/error.rs @@ -41,6 +41,8 @@ pub enum WalletFfiError { InvalidTypeConversion = 15, /// Invalid Key value. InvalidKeyValue = 16, + /// Invalid program bytecode + InvalidBytecode = 17, /// Internal error (catch-all). InternalError = 99, } diff --git a/wallet-ffi/src/generic_transaction.rs b/wallet-ffi/src/generic_transaction.rs new file mode 100644 index 00000000..1195c131 --- /dev/null +++ b/wallet-ffi/src/generic_transaction.rs @@ -0,0 +1,247 @@ +use std::{collections::HashMap, ffi::CString}; + +use nssa::{privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program}; + +use crate::{ + block_on, + error::{print_error, WalletFfiError}, + map_execution_error, + wallet::get_wallet, + FfiAccountIdentity, FfiTransferResult, WalletHandle, +}; + +#[repr(C)] +pub struct SerializationHelperResult { + pub instruction_words: *mut u32, + pub instruction_words_size: usize, + pub error: WalletFfiError, +} + +impl SerializationHelperResult { + fn from_err(error: WalletFfiError) -> Self { + Self { + instruction_words: std::ptr::null_mut(), + instruction_words_size: 0, + error, + } + } +} + +/// Serialize sequence of bytes into RISC0 readable words +/// +/// # Parameters +/// - `input_instruction_data`: Valid pointer to a sequence of bytes +/// - `input_instruction_data_size`: Size of `input_instruction_data` +/// +/// # Returns +/// - `Success` on successful creation +/// - Error code on failure +/// +/// # Safety +/// - `input_instruction_data` must be a valid pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_serialization_helper( + input_instruction_data: *const u8, + input_instruction_data_size: usize, +) -> SerializationHelperResult { + if input_instruction_data.is_null() { + print_error("Null input pointer for instruction_data"); + return SerializationHelperResult::from_err(WalletFfiError::NullPointer); + } + + let input_slice = + unsafe { std::slice::from_raw_parts(input_instruction_data, input_instruction_data_size) }; + let res_vec_u32 = match risc0_zkvm::serde::to_vec(input_slice).map_err(|err| { + print_error(format!( + "Failed to serialize input into words with err {err}" + )); + WalletFfiError::SerializationError + }) { + Ok(res) => res, + Err(err) => return SerializationHelperResult::from_err(err), + }; + let res_len = res_vec_u32.len(); + let res_boxed = res_vec_u32.into_boxed_slice(); + let res_ptr = Box::into_raw(res_boxed).cast::(); + + SerializationHelperResult { + instruction_words: res_ptr, + instruction_words_size: res_len, + error: WalletFfiError::Success, + } +} + +#[repr(C)] +/// Intended to be created manually +pub struct FfiProgram { + pub elf_data: *const u8, + pub elf_size: usize, +} + +impl TryFrom<&FfiProgram> for Program { + type Error = WalletFfiError; + + fn try_from(value: &FfiProgram) -> Result { + let mut elf = Vec::with_capacity(value.elf_size); + + // Alignment will be different, we need to read elements one-by-one + for i in 0..value.elf_size { + elf.push(unsafe { *value.elf_data.add(i) }); + } + + Self::new(elf).map_err(|err| { + print_error(format!("Invalid program bytecode, err: {err}")); + WalletFfiError::InvalidBytecode + }) + } +} + +#[repr(C)] +/// Intended to be created manually +pub struct FfiProgramWithDependencies { + pub program: FfiProgram, + pub deps: *const FfiProgram, + pub deps_size: usize, +} + +impl TryFrom for ProgramWithDependencies { + type Error = WalletFfiError; + + fn try_from(value: FfiProgramWithDependencies) -> Result { + let mut program_map = HashMap::new(); + + let orig_program = (&value.program).try_into()?; + + // Alignment will be different, we need to read elements one-by-one + for i in 0..value.deps_size { + let program_dep: Program = unsafe { value.deps.add(i).as_ref() } + .ok_or(WalletFfiError::NullPointer)? + .try_into()?; + + program_map.insert(program_dep.id(), program_dep); + } + + Ok(Self { + program: orig_program, + dependencies: program_map, + }) + } +} + +#[repr(C)] +pub enum FfiExecutionFlow { + Public = 0, + PrivacyPreserving = 1, +} + +/// Send generic transaction +/// +/// # Parameters +/// - `handle`: Valid pointer to wallet handle +/// - `account_identities`: Valid pointer to list of `FfiAccountIdentity` +/// - `instruction_words`: Valid pointer to instruction words +/// - `out_result`: Valid pointer to `FfiTransferResult` +/// +/// # Returns +/// - `Success` on successful creation +/// - Error code on failure +/// +/// # Safety +/// - `handle` must be a valid pointer +/// - `account_identities` must be a valid pointer +/// - `instruction_words` must be a valid pointer +/// - `out_result` must be a valid pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_send_generic_transaction( + handle: *mut WalletHandle, + account_identities: *const FfiAccountIdentity, + account_identities_size: usize, + instruction_words: *const u32, + instruction_words_size: usize, + program_with_dependencies: FfiProgramWithDependencies, + out_result: *mut FfiTransferResult, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + if account_identities.is_null() { + print_error("Null output pointer for account identities list"); + return WalletFfiError::NullPointer; + } + + if instruction_words.is_null() { + print_error("Null output pointer for instruction data"); + return WalletFfiError::NullPointer; + } + + if out_result.is_null() { + print_error("Null output pointer return hash"); + return WalletFfiError::NullPointer; + } + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let mut accounts = Vec::with_capacity(account_identities_size); + let mut instruction_data = Vec::with_capacity(instruction_words_size); + + // Alignment will be different, we need to read elements one-by-one + for i in 0..account_identities_size { + accounts.push( + match match unsafe { account_identities.add(i).as_ref() } + .ok_or(WalletFfiError::NullPointer) + { + Ok(v) => v, + Err(err) => { + print_error(format!( + "account_identities_size does not match actual size of account_identities" + )); + return err; + } + } + .try_into() + { + Ok(v) => v, + Err(err) => return err, + }, + ); + } + + // Alignment will be different, we need to read elements one-by-one + for i in 0..instruction_words_size { + instruction_data.push(unsafe { *instruction_words.add(i) }); + } + + let program = match program_with_dependencies.try_into() { + Ok(v) => v, + Err(err) => return err, + }; + + match block_on(wallet.send_pub_tx(accounts, instruction_data, &program)) { + Ok(tx_hash) => { + let tx_hash = CString::new(tx_hash.to_string()) + .map_or(std::ptr::null_mut(), std::ffi::CString::into_raw); + + unsafe { + (*out_result).tx_hash = tx_hash; + (*out_result).success = true; + } + WalletFfiError::Success + } + Err(e) => { + print_error(format!("Public send failed: {e:?}")); + unsafe { + (*out_result).tx_hash = std::ptr::null_mut(); + (*out_result).success = false; + } + map_execution_error(e) + } + } +} diff --git a/wallet-ffi/src/lib.rs b/wallet-ffi/src/lib.rs index 16943d3e..e28a0560 100644 --- a/wallet-ffi/src/lib.rs +++ b/wallet-ffi/src/lib.rs @@ -42,6 +42,7 @@ use crate::error::print_error; pub mod account; pub mod error; +pub mod generic_transaction; pub mod keys; pub mod pinata; pub mod sync; diff --git a/wallet-ffi/wallet_ffi.h b/wallet-ffi/wallet_ffi.h index 211d6513..86b50de9 100644 --- a/wallet-ffi/wallet_ffi.h +++ b/wallet-ffi/wallet_ffi.h @@ -103,6 +103,10 @@ typedef enum WalletFfiError { * Invalid Key value. */ INVALID_KEY_VALUE = 16, + /** + * Invalid program bytecode + */ + INVALID_BYTECODE = 17, /** * Internal error (catch-all). */ @@ -214,13 +218,6 @@ typedef struct FfiAccount { struct FfiU128 nonce; } FfiAccount; -/** - * Public key info for a public account. - */ -typedef struct FfiPublicAccountKey { - struct FfiBytes32 public_key; -} FfiPublicAccountKey; - /** * Struct representing of account identity, given to `AccountManager` at intialization */ @@ -234,6 +231,29 @@ typedef struct FfiAccountIdentity { struct FfiU128 identifier; } FfiAccountIdentity; +typedef struct SerializationHelperResult { + uint32_t *instruction_words; + uintptr_t instruction_words_size; + enum WalletFfiError error; +} SerializationHelperResult; + +/** + * Intended to be created manually + */ +typedef struct FfiProgram { + const uint8_t *elf_data; + uintptr_t elf_size; +} FfiProgram; + +/** + * Intended to be created manually + */ +typedef struct FfiProgramWithDependencies { + struct FfiProgram program; + const struct FfiProgram *deps; + uintptr_t deps_size; +} FfiProgramWithDependencies; + /** * Result of a transfer operation. */ @@ -248,6 +268,13 @@ typedef struct FfiTransferResult { bool success; } FfiTransferResult; +/** + * Public key info for a public account. + */ +typedef struct FfiPublicAccountKey { + struct FfiBytes32 public_key; +} FfiPublicAccountKey; + /** * Create a new public account. * @@ -481,6 +508,60 @@ enum WalletFfiError wallet_ffi_import_private_account(struct WalletHandle *handl const struct FfiU128 *identifier, const char *account_state_json); +/** + * Free account identity returned by `wallet_ffi_resolve_private_account` or + * `wallet_ffi_resolve_public_account`. + * + * # Safety + * The account must be either null or a valid account returned by + * `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`. + */ +void wallet_ffi_free_account_identity(struct FfiAccountIdentity *account_identity); + +/** + * Serialize sequence of bytes into RISC0 readable words + * + * # Parameters + * - `input_instruction_data`: Valid pointer to a sequence of bytes + * - `input_instruction_data_size`: Size of `input_instruction_data` + * + * # Returns + * - `Success` on successful creation + * - Error code on failure + * + * # Safety + * - `input_instruction_data` must be a valid pointer + */ +struct SerializationHelperResult wallet_ffi_serialization_helper(const uint8_t *input_instruction_data, + uintptr_t input_instruction_data_size); + +/** + * Send generic transaction + * + * # Parameters + * - `handle`: Valid pointer to wallet handle + * - `account_identities`: Valid pointer to list of `FfiAccountIdentity` + * - `instruction_words`: Valid pointer to instruction words + * - `out_result`: Valid pointer to `FfiTransferResult` + * + * # Returns + * - `Success` on successful creation + * - Error code on failure + * + * # Safety + * - `handle` must be a valid pointer + * - `account_identities` must be a valid pointer + * - `instruction_words` must be a valid pointer + * - `out_result` must be a valid pointer + */ +enum WalletFfiError wallet_ffi_send_generic_transaction(struct WalletHandle *handle, + const struct FfiAccountIdentity *account_identities, + uintptr_t account_identities_size, + const uint32_t *instruction_words, + uintptr_t instruction_words_size, + struct FfiProgramWithDependencies program_with_dependencies, + struct FfiTransferResult *out_result); + /** * Get the public key for a public account. *