diff --git a/key-wallet-manager/README.md b/key-wallet-manager/README.md index 94f5a3e67..1e82d0779 100644 --- a/key-wallet-manager/README.md +++ b/key-wallet-manager/README.md @@ -146,28 +146,6 @@ let transaction = wallet_manager.send_transaction( )?; ``` -### UTXO Management - -Track unspent outputs: - -```rust -use key_wallet_manager::{Utxo, UtxoSet}; - -// Create UTXO set -let mut utxo_set = UtxoSet::new(); - -// Add UTXOs -let utxo = Utxo::new(outpoint, txout, address, 100, false); -utxo_set.add(utxo); - -// Query UTXOs -let available = utxo_set.spendable(current_height); -let total_value = utxo_set.total_balance(); - -// Rollback transactions -utxo_set.rollback_to_height(12345); -``` - ### Coin Selection Choose optimal UTXOs for transactions: @@ -343,7 +321,6 @@ match wallet_manager.send_transaction("wallet1", 0, recipients, FeeLevel::Normal ### Performance -- **Reuse UTXOSet**: Don't recreate for each transaction - **Batch operations**: Group multiple recipients in single transaction - **Optimize coin selection**: Use appropriate strategy for your use case - **Cache address pools**: Avoid regenerating addresses unnecessarily diff --git a/key-wallet-manager/src/lib.rs b/key-wallet-manager/src/lib.rs index 2f13f19aa..53c608e9e 100644 --- a/key-wallet-manager/src/lib.rs +++ b/key-wallet-manager/src/lib.rs @@ -31,7 +31,7 @@ pub mod wallet_manager; // Re-export key-wallet types pub use key_wallet::{ Account, AccountType, Address, AddressType, ChildNumber, DerivationPath, ExtendedPrivKey, - ExtendedPubKey, Mnemonic, Network, Utxo, UtxoSet, Wallet, + ExtendedPubKey, Mnemonic, Network, Utxo, Wallet, }; // Re-export dashcore transaction types diff --git a/key-wallet/src/lib.rs b/key-wallet/src/lib.rs index 38b4e3cd8..03760edbb 100644 --- a/key-wallet/src/lib.rs +++ b/key-wallet/src/lib.rs @@ -69,7 +69,7 @@ pub use managed_account::managed_platform_account::ManagedPlatformAccount; pub use managed_account::platform_address::PlatformP2PKHAddress; pub use mnemonic::Mnemonic; pub use seed::Seed; -pub use utxo::{Utxo, UtxoSet}; +pub use utxo::Utxo; pub use wallet::{balance::WalletCoreBalance, Wallet}; /// Re-export commonly used types diff --git a/key-wallet/src/utxo.rs b/key-wallet/src/utxo.rs index dbf4eed0b..d144fe507 100644 --- a/key-wallet/src/utxo.rs +++ b/key-wallet/src/utxo.rs @@ -3,8 +3,6 @@ //! This module provides UTXO tracking and management functionality //! that works with dashcore transaction types. -use alloc::collections::BTreeMap; -use alloc::vec::Vec; use core::cmp::Ordering; use crate::Address; @@ -119,200 +117,6 @@ impl PartialOrd for Utxo { } } -/// UTXO set management -#[derive(Debug, Clone)] -pub struct UtxoSet { - /// UTXOs indexed by outpoint - utxos: BTreeMap, - /// Total balance - total_balance: u64, - /// Confirmed balance - confirmed_balance: u64, - /// Unconfirmed balance - unconfirmed_balance: u64, - /// Locked balance - locked_balance: u64, -} - -impl UtxoSet { - /// Create a new empty UTXO set - pub fn new() -> Self { - Self { - utxos: BTreeMap::new(), - total_balance: 0, - confirmed_balance: 0, - unconfirmed_balance: 0, - locked_balance: 0, - } - } - - /// Add a UTXO to the set - pub fn add(&mut self, utxo: Utxo) { - let value = utxo.value(); - - // Update balances - self.total_balance += value; - - if utxo.is_confirmed || utxo.is_instantlocked { - self.confirmed_balance += value; - } else { - self.unconfirmed_balance += value; - } - - if utxo.is_locked { - self.locked_balance += value; - } - - self.utxos.insert(utxo.outpoint, utxo); - } - - /// Remove a UTXO from the set - pub fn remove(&mut self, outpoint: &OutPoint) -> Option { - if let Some(utxo) = self.utxos.remove(outpoint) { - let value = utxo.value(); - - // Update balances - self.total_balance = self.total_balance.saturating_sub(value); - - if utxo.is_confirmed || utxo.is_instantlocked { - self.confirmed_balance = self.confirmed_balance.saturating_sub(value); - } else { - self.unconfirmed_balance = self.unconfirmed_balance.saturating_sub(value); - } - - if utxo.is_locked { - self.locked_balance = self.locked_balance.saturating_sub(value); - } - - Some(utxo) - } else { - None - } - } - - /// Get a UTXO by outpoint - pub fn get(&self, outpoint: &OutPoint) -> Option<&Utxo> { - self.utxos.get(outpoint) - } - - /// Get a mutable UTXO by outpoint - pub fn get_mut(&mut self, outpoint: &OutPoint) -> Option<&mut Utxo> { - self.utxos.get_mut(outpoint) - } - - /// Check if a UTXO exists - pub fn contains(&self, outpoint: &OutPoint) -> bool { - self.utxos.contains_key(outpoint) - } - - /// Get all UTXOs - pub fn all(&self) -> Vec<&Utxo> { - self.utxos.values().collect() - } - - /// Get all spendable UTXOs - pub fn spendable(&self, current_height: u32) -> Vec<&Utxo> { - self.utxos.values().filter(|utxo| utxo.is_spendable(current_height)).collect() - } - - /// Get all UTXOs for a specific address - pub fn for_address(&self, address: &Address) -> Vec<&Utxo> { - self.utxos.values().filter(|utxo| &utxo.address == address).collect() - } - - /// Get total balance - pub fn total_balance(&self) -> u64 { - self.total_balance - } - - /// Get confirmed balance - pub fn confirmed_balance(&self) -> u64 { - self.confirmed_balance - } - - /// Get unconfirmed balance - pub fn unconfirmed_balance(&self) -> u64 { - self.unconfirmed_balance - } - - /// Get locked balance - pub fn locked_balance(&self) -> u64 { - self.locked_balance - } - - /// Get spendable balance - pub fn spendable_balance(&self, current_height: u32) -> u64 { - self.spendable(current_height).iter().map(|utxo| utxo.value()).sum() - } - - /// Clear all UTXOs - pub fn clear(&mut self) { - self.utxos.clear(); - self.total_balance = 0; - self.confirmed_balance = 0; - self.unconfirmed_balance = 0; - self.locked_balance = 0; - } - - /// Get the number of UTXOs - pub fn len(&self) -> usize { - self.utxos.len() - } - - /// Check if the set is empty - pub fn is_empty(&self) -> bool { - self.utxos.is_empty() - } - - /// Update confirmation status for a UTXO - pub fn update_confirmation(&mut self, outpoint: &OutPoint, confirmed: bool) { - if let Some(utxo) = self.utxos.get_mut(outpoint) { - let value = utxo.value(); - - if utxo.is_confirmed != confirmed { - if confirmed { - self.confirmed_balance += value; - self.unconfirmed_balance = self.unconfirmed_balance.saturating_sub(value); - } else { - self.unconfirmed_balance += value; - self.confirmed_balance = self.confirmed_balance.saturating_sub(value); - } - utxo.is_confirmed = confirmed; - } - } - } - - /// Lock a UTXO - pub fn lock_utxo(&mut self, outpoint: &OutPoint) -> bool { - if let Some(utxo) = self.utxos.get_mut(outpoint) { - if !utxo.is_locked { - utxo.lock(); - self.locked_balance += utxo.value(); - return true; - } - } - false - } - - /// Unlock a UTXO - pub fn unlock_utxo(&mut self, outpoint: &OutPoint) -> bool { - if let Some(utxo) = self.utxos.get_mut(outpoint) { - if utxo.is_locked { - utxo.unlock(); - self.locked_balance = self.locked_balance.saturating_sub(utxo.value()); - return true; - } - } - false - } -} - -impl Default for UtxoSet { - fn default() -> Self { - Self::new() - } -} - #[cfg(test)] mod tests { use super::*; @@ -334,33 +138,6 @@ mod tests { assert!(!utxo.is_spendable(200)); } - #[test] - fn test_utxo_set_operations() { - let mut set = UtxoSet::new(); - - let utxo1 = Utxo::dummy(0, 100000, 100, false, false); - let utxo2 = Utxo::dummy(1, 200000, 150, false, false); - - set.add(utxo1.clone()); - set.add(utxo2.clone()); - - assert_eq!(set.len(), 2); - assert_eq!(set.total_balance(), 300000); - assert_eq!(set.unconfirmed_balance(), 300000); - assert_eq!(set.confirmed_balance(), 0); - - // Update confirmation - set.update_confirmation(&utxo1.outpoint, true); - assert_eq!(set.confirmed_balance(), 100000); - assert_eq!(set.unconfirmed_balance(), 200000); - - // Remove UTXO - let removed = set.remove(&utxo1.outpoint); - assert!(removed.is_some()); - 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")]