From 2b2275ee7406bffbeeb35b479a9ee77efa8762b3 Mon Sep 17 00:00:00 2001 From: Moudy Date: Fri, 8 May 2026 11:03:13 +0200 Subject: [PATCH] fix: resolve shared accounts in auth-transfer commands --- wallet/src/lib.rs | 53 +++++++++++++++++++ .../native_token_transfer/private.rs | 21 ++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 307b253a..1d9c2c7e 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -317,6 +317,59 @@ impl WalletCore { self.storage.user_data.sealing_secret_key = Some(key); } + /// 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 { + // Check key tree first + if self + .storage + .user_data + .get_private_account(account_id) + .is_some() + { + return Some(PrivacyPreservingAccount::PrivateOwned(account_id)); + } + + // Check shared private accounts + let entry = self.storage.user_data.shared_private_account(&account_id)?; + let holder = self + .storage + .user_data + .group_key_holder(&entry.group_label)?; + + if let Some(pda_seed) = &entry.pda_seed { + let program_id = entry.pda_program_id?; + let keys = holder.derive_keys_for_pda(&program_id, pda_seed); + Some(PrivacyPreservingAccount::PrivatePda { + nsk: keys.nullifier_secret_key, + npk: keys.generate_nullifier_public_key(), + vpk: keys.generate_viewing_public_key(), + program_id, + seed: *pda_seed, + }) + } else { + let derivation_seed = { + use sha2::Digest as _; + let mut hasher = sha2::Sha256::new(); + hasher.update(b"/LEE/v0.3/SharedAccountTag/\x00\x00\x00\x00\x00"); + hasher.update(entry.identifier.to_le_bytes()); + let result: [u8; 32] = hasher.finalize().into(); + result + }; + let keys = holder.derive_keys_for_shared_account(&derivation_seed); + Some(PrivacyPreservingAccount::PrivateShared { + nsk: keys.nullifier_secret_key, + npk: keys.generate_nullifier_public_key(), + vpk: keys.generate_viewing_public_key(), + identifier: entry.identifier, + }) + } + } + /// Remove a group key holder from storage. Returns the removed holder if it existed. pub fn remove_group_key_holder( &mut self, diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index d317b31c..436c2d41 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -14,9 +14,14 @@ impl NativeTokenTransfer<'_> { ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction: u128 = 0; + let account = self + .0 + .resolve_private_account(from) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + self.0 .send_privacy_preserving_tx( - vec![PrivacyPreservingAccount::PrivateOwned(from)], + vec![account], Program::serialize_instruction(instruction).unwrap(), &Program::authenticated_transfer_program().into(), ) @@ -69,12 +74,18 @@ impl NativeTokenTransfer<'_> { ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); + let from_account = self + .0 + .resolve_private_account(from) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let to_account = self + .0 + .resolve_private_account(to) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + self.0 .send_privacy_preserving_tx_with_pre_check( - vec![ - PrivacyPreservingAccount::PrivateOwned(from), - PrivacyPreservingAccount::PrivateOwned(to), - ], + vec![from_account, to_account], instruction_data, &program.into(), tx_pre_check,