From 67e5bc3aabfee769c1197da30e47f4c39285e976 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Wed, 24 Dec 2025 09:45:16 +0100 Subject: [PATCH] fix: correct confirmations for UTXOs in wallet FFI --- key-wallet-ffi/src/utxo.rs | 8 ++------ key-wallet-ffi/src/utxo_tests.rs | 6 ++++-- key-wallet/Cargo.toml | 1 + key-wallet/src/utxo.rs | 25 +++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/key-wallet-ffi/src/utxo.rs b/key-wallet-ffi/src/utxo.rs index ca9d7b1f9..5fd757be2 100644 --- a/key-wallet-ffi/src/utxo.rs +++ b/key-wallet-ffi/src/utxo.rs @@ -119,12 +119,8 @@ pub unsafe extern "C" fn managed_wallet_get_utxos( // Get script bytes let script_bytes = utxo.txout.script_pubkey.as_bytes().to_vec(); - // Calculate confirmations (0 if unconfirmed) - let confirmations = if utxo.is_confirmed { - 1 - } else { - 0 - }; + let current_height = managed_info.inner().synced_height(); + let confirmations = utxo.confirmations(current_height); let ffi_utxo = FFIUTXO::new( txid_bytes, diff --git a/key-wallet-ffi/src/utxo_tests.rs b/key-wallet-ffi/src/utxo_tests.rs index 422c71b4a..6875fb666 100644 --- a/key-wallet-ffi/src/utxo_tests.rs +++ b/key-wallet-ffi/src/utxo_tests.rs @@ -235,7 +235,7 @@ mod utxo_tests { managed_info.accounts.insert(bip44_account); let ffi_managed_info = Box::into_raw(Box::new(FFIManagedWalletInfo::new(managed_info))); - + unsafe { (*ffi_managed_info).inner_mut() }.update_synced_height(300); let result = unsafe { managed_wallet_get_utxos(&*ffi_managed_info, &mut utxos_out, &mut count_out, error) }; @@ -254,19 +254,21 @@ mod utxo_tests { assert_eq!(utxos[0].vout, 0); assert_eq!(utxos[0].amount, 50000); assert_eq!(utxos[0].height, 100); - assert_eq!(utxos[0].confirmations, 1); + assert_eq!(utxos[0].confirmations, 201); // Check second UTXO assert_eq!(utxos[1].txid[0], 1); assert_eq!(utxos[1].vout, 1); assert_eq!(utxos[1].amount, 100000); assert_eq!(utxos[1].height, 101); + assert_eq!(utxos[1].confirmations, 200); // Check third UTXO assert_eq!(utxos[2].txid[0], 2); assert_eq!(utxos[2].vout, 2); assert_eq!(utxos[2].amount, 150000); assert_eq!(utxos[2].height, 102); + assert_eq!(utxos[2].confirmations, 199); } // Clean up diff --git a/key-wallet/Cargo.toml b/key-wallet/Cargo.toml index 520f2bcdd..af07f6e58 100644 --- a/key-wallet/Cargo.toml +++ b/key-wallet/Cargo.toml @@ -50,3 +50,4 @@ async-trait = "0.1" hex = "0.4" key-wallet = { path = ".", features = ["test-utils", "bip38", "serde", "bincode", "eddsa", "bls"] } tokio = { version = "1", features = ["macros", "rt"] } +test-case = "3.3" diff --git a/key-wallet/src/utxo.rs b/key-wallet/src/utxo.rs index 8b187aca7..9df1fb67c 100644 --- a/key-wallet/src/utxo.rs +++ b/key-wallet/src/utxo.rs @@ -86,6 +86,15 @@ impl Utxo { } } + /// Get the number of confirmations for this UTXO + pub fn confirmations(&self, current_height: u32) -> u32 { + if self.is_confirmed && current_height >= self.height { + current_height - self.height + 1 + } else { + 0 + } + } + /// Lock this UTXO to prevent it from being selected pub fn lock(&mut self) { self.is_locked = true; @@ -307,6 +316,7 @@ impl Default for UtxoSet { #[cfg(test)] mod tests { use super::*; + use test_case::test_case; #[test] fn test_utxo_spendability() { @@ -350,4 +360,19 @@ mod tests { assert_eq!(set.len(), 1); assert_eq!(set.total_balance(), 200000); } + + #[test_case(false, 0, 500, 0 ; "unconfirmed utxo has 0 confirmations")] + #[test_case(true, 0, 500, 501 ; "confirmed utxo at genesis height has 501 confirmations")] + #[test_case(true, 1000, 500, 0 ; "utxo height greater than current height has 0 confirmations")] + #[test_case(true, 500, 500, 1 ; "utxo at current height has 1 confirmation")] + #[test_case(true, 100, 500, 401 ; "normal case has current_height minus utxo_height plus 1 confirmations")] + fn test_confirmations( + is_confirmed: bool, + utxo_height: u32, + current_height: u32, + expected: u32, + ) { + let utxo = Utxo::new_test(0, 100000, utxo_height, false, is_confirmed); + assert_eq!(utxo.confirmations(current_height), expected); + } }