From 6e3663cd5a80a9520013e1946867886fd02f5bb2 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 19 Aug 2025 06:26:12 +0700 Subject: [PATCH 1/4] fixes --- dash-spv/Cargo.toml | 1 + dash-spv/examples/filter_sync.rs | 6 +- dash-spv/examples/reorg_demo.rs | 6 +- dash-spv/examples/simple_sync.rs | 6 +- dash-spv/src/bloom/tests.rs | 10 +- dash-spv/src/chain/checkpoint_test.rs | 2 +- dash-spv/src/chain/fork_detector_test.rs | 2 +- dash-spv/src/chain/reorg.rs | 4 +- dash-spv/src/client/block_processor_test.rs | 23 +-- dash-spv/src/client/config_test.rs | 2 +- dash-spv/src/client/watch_manager_test.rs | 4 +- dash-spv/src/lib.rs | 4 +- dash-spv/src/main.rs | 7 +- dash-spv/src/mempool_filter.rs | 2 +- dash-spv/src/network/pool.rs | 1 - dash-spv/src/network/reputation_tests.rs | 1 - dash-spv/src/network/tests.rs | 3 +- dash-spv/src/sync/headers2_state.rs | 2 +- dash-spv/src/sync/sequential/recovery.rs | 2 +- dash-spv/src/sync/validation_test.rs | 10 +- dash-spv/src/validation/headers_test.rs | 2 +- dash-spv/tests/chainlock_simple_test.rs | 10 +- dash-spv/tests/header_sync_test.rs | 6 +- .../tests/instantsend_integration_test.rs | 2 +- dash-spv/tests/integration_real_node_test.rs | 6 +- dash-spv/tests/multi_peer_test.rs | 23 ++- dash-spv/tests/simple_header_test.rs | 6 +- dash-spv/tests/wallet_integration_test.rs | 9 +- dash/src/base58.rs | 4 +- dash/src/blockdata/script/borrowed.rs | 8 +- dash/src/blockdata/witness.rs | 2 +- dash/src/taproot.rs | 4 +- key-wallet-manager/src/spv_wallet_manager.rs | 2 + key-wallet-manager/src/wallet_manager/mod.rs | 2 +- .../src/wallet_manager/process_block.rs | 1 + .../wallet_manager/transaction_building.rs | 1 + key-wallet/src/tests/performance_tests.rs | 29 ++- .../src/wallet/managed_wallet_info/mod.rs | 1 + .../wallet_info_interface.rs | 179 ++++++++++++++++++ 39 files changed, 325 insertions(+), 70 deletions(-) create mode 100644 key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index 1a5c48580..1a94ece44 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -12,6 +12,7 @@ rust-version = "1.80" # Core Dash libraries dashcore = { path = "../dash", features = ["std", "serde", "core-block-hash-use-x11", "message_verification", "bls", "quorum_validation"] } dashcore_hashes = { path = "../hashes" } +key-wallet = { path = "../key-wallet" } key-wallet-manager = { path = "../key-wallet-manager" } # BLS signatures diff --git a/dash-spv/examples/filter_sync.rs b/dash-spv/examples/filter_sync.rs index 15e2abb4b..44f812527 100644 --- a/dash-spv/examples/filter_sync.rs +++ b/dash-spv/examples/filter_sync.rs @@ -4,7 +4,9 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::MemoryStorageManager; use dash_spv::{init_logging, ClientConfig, DashSpvClient}; use dashcore::{Address, Network}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use std::str::FromStr; use std::sync::Arc; use tokio::sync::RwLock; @@ -31,7 +33,9 @@ async fn main() -> Result<(), Box> { let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); // Create the client let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await?; diff --git a/dash-spv/examples/reorg_demo.rs b/dash-spv/examples/reorg_demo.rs index c2b8467b8..36e7eae3c 100644 --- a/dash-spv/examples/reorg_demo.rs +++ b/dash-spv/examples/reorg_demo.rs @@ -18,7 +18,9 @@ mod disabled_example { use dash_spv::types::ChainState; use dashcore::{blockdata::constants::genesis_block, Header as BlockHeader, Network}; use dashcore_hashes::Hash; + use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; + use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; @@ -38,7 +40,9 @@ mod disabled_example { let network = Network::Dash; let genesis = genesis_block(network).header; let mut chain_state = ChainState::new_for_network(network); - let wallet_manager = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet_manager = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let mut storage = MemoryStorageManager::new().await?; println!("📦 Building main chain: genesis -> block1 -> block2"); diff --git a/dash-spv/examples/simple_sync.rs b/dash-spv/examples/simple_sync.rs index a85f801fc..33bedf602 100644 --- a/dash-spv/examples/simple_sync.rs +++ b/dash-spv/examples/simple_sync.rs @@ -3,7 +3,9 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::MemoryStorageManager; use dash_spv::{init_logging, ClientConfig, DashSpvClient}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; @@ -24,7 +26,9 @@ async fn main() -> Result<(), Box> { let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); // Create the client let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await?; diff --git a/dash-spv/src/bloom/tests.rs b/dash-spv/src/bloom/tests.rs index 231707db1..4322cb8e6 100644 --- a/dash-spv/src/bloom/tests.rs +++ b/dash-spv/src/bloom/tests.rs @@ -5,18 +5,18 @@ mod tests { use crate::bloom::{ builder::BloomFilterBuilder, manager::{BloomFilterConfig, BloomFilterManager}, - stats::{BloomFilterStats, BloomStatsTracker, DetailedBloomStats}, + stats::BloomStatsTracker, utils, }; - use crate::error::SpvError; + use dashcore::{ address::{Address, Payload}, - blockdata::script::{Script, ScriptBuf}, - bloom::{BloomFilter, BloomFlags}, + blockdata::script::ScriptBuf, + bloom::BloomFlags, hash_types::PubkeyHash, OutPoint, Txid, }; - use std::str::FromStr; + use std::sync::Arc; // Test data helpers diff --git a/dash-spv/src/chain/checkpoint_test.rs b/dash-spv/src/chain/checkpoint_test.rs index 60ef471c9..db93963e8 100644 --- a/dash-spv/src/chain/checkpoint_test.rs +++ b/dash-spv/src/chain/checkpoint_test.rs @@ -77,7 +77,7 @@ mod tests { create_test_checkpoint(300000, 1700000000), // Nov 2023 ]; - let mut manager = CheckpointManager::new(checkpoints); + let manager = CheckpointManager::new(checkpoints); // Test wallet created in 2019 let wallet_time_2019 = 1550000000u32; diff --git a/dash-spv/src/chain/fork_detector_test.rs b/dash-spv/src/chain/fork_detector_test.rs index 8404bda07..fa8e2456e 100644 --- a/dash-spv/src/chain/fork_detector_test.rs +++ b/dash-spv/src/chain/fork_detector_test.rs @@ -7,7 +7,7 @@ mod tests { use crate::types::ChainState; use dashcore::blockdata::constants::genesis_block; use dashcore::{BlockHash, Header as BlockHeader, Network}; - use dashcore_hashes::{Hash, HashEngine}; + use dashcore_hashes::Hash; use std::sync::{Arc, Mutex}; use std::thread; diff --git a/dash-spv/src/chain/reorg.rs b/dash-spv/src/chain/reorg.rs index d71057cca..6abb6676c 100644 --- a/dash-spv/src/chain/reorg.rs +++ b/dash-spv/src/chain/reorg.rs @@ -8,7 +8,6 @@ use super::{ChainTip, Fork}; use crate::storage::ChainStorage; use crate::types::ChainState; use dashcore::{BlockHash, Header as BlockHeader, Transaction, Txid}; -use dashcore_hashes::Hash; use std::sync::Arc; use tracing; @@ -539,9 +538,10 @@ impl ReorgManager { mod tests { use super::*; use crate::chain::ChainWork; - use crate::storage::{MemoryStorage, MemoryStorageManager}; + use crate::storage::MemoryStorage; use dashcore::blockdata::constants::genesis_block; use dashcore::Network; + use dashcore_hashes::Hash; fn create_test_header(prev: &BlockHeader, nonce: u32) -> BlockHeader { let mut header = prev.clone(); diff --git a/dash-spv/src/client/block_processor_test.rs b/dash-spv/src/client/block_processor_test.rs index 10900cc97..8c7051ed3 100644 --- a/dash-spv/src/client/block_processor_test.rs +++ b/dash-spv/src/client/block_processor_test.rs @@ -3,16 +3,13 @@ #[cfg(test)] mod tests { use crate::client::block_processor::{BlockProcessingTask, BlockProcessor}; - use crate::error::SpvError; + use crate::storage::memory::MemoryStorageManager; use crate::storage::StorageManager; - use crate::types::{SpvEvent, SpvStats, WatchItem}; - use dashcore::{ - blockdata::constants::genesis_block, consensus::encode::serialize, hash_types::FilterHash, - Address, Block, Network, Transaction, - }; + use crate::types::{SpvEvent, SpvStats}; + use dashcore::{blockdata::constants::genesis_block, Block, Network, Transaction}; use std::collections::HashSet; - use std::str::FromStr; + use std::sync::Arc; use tokio::sync::{mpsc, oneshot, Mutex, RwLock}; @@ -105,7 +102,7 @@ mod tests { #[tokio::test] async fn test_process_block() { - let (mut processor, task_tx, mut event_rx, wallet, storage) = setup_processor().await; + let (processor, task_tx, mut event_rx, wallet, storage) = setup_processor().await; // Create a test block let block = create_test_block(Network::Dash); @@ -164,7 +161,7 @@ mod tests { #[tokio::test] async fn test_process_compact_filter() { - let (mut processor, task_tx, mut event_rx, _wallet, _storage) = setup_processor().await; + let (processor, task_tx, mut event_rx, _wallet, _storage) = setup_processor().await; // Create a test block let block = create_test_block(Network::Dash); @@ -270,7 +267,7 @@ mod tests { let storage = Arc::new(Mutex::new(MemoryStorageManager::new().await.unwrap())); let watch_items = Arc::new(RwLock::new(HashSet::new())); - let mut processor = BlockProcessor::new( + let processor = BlockProcessor::new( task_rx, wallet, storage, @@ -319,7 +316,7 @@ mod tests { #[tokio::test] async fn test_process_mempool_transaction() { - let (mut processor, task_tx, mut event_rx, wallet, _storage) = setup_processor().await; + let (processor, task_tx, event_rx, wallet, _storage) = setup_processor().await; // Create a test transaction let block = create_test_block(Network::Dash); @@ -358,7 +355,7 @@ mod tests { #[tokio::test] async fn test_shutdown() { - let (mut processor, task_tx, _event_rx, _wallet, _storage) = setup_processor().await; + let (processor, task_tx, _event_rx, _wallet, _storage) = setup_processor().await; // Start processor let processor_handle = tokio::spawn(async move { processor.run().await }); @@ -375,7 +372,7 @@ mod tests { #[tokio::test] async fn test_block_not_found_in_storage() { - let (mut processor, task_tx, mut event_rx, _wallet, _storage) = setup_processor().await; + let (processor, task_tx, mut event_rx, _wallet, _storage) = setup_processor().await; let block = create_test_block(Network::Dash); let block_hash = block.block_hash(); diff --git a/dash-spv/src/client/config_test.rs b/dash-spv/src/client/config_test.rs index 1fc0275b4..27070f322 100644 --- a/dash-spv/src/client/config_test.rs +++ b/dash-spv/src/client/config_test.rs @@ -7,7 +7,7 @@ mod tests { use dashcore::{Address, Network}; use std::net::SocketAddr; use std::path::PathBuf; - use std::str::FromStr; + use std::time::Duration; #[test] diff --git a/dash-spv/src/client/watch_manager_test.rs b/dash-spv/src/client/watch_manager_test.rs index 704fa6342..96e071696 100644 --- a/dash-spv/src/client/watch_manager_test.rs +++ b/dash-spv/src/client/watch_manager_test.rs @@ -2,12 +2,12 @@ #[cfg(test)] mod tests { - use crate::client::watch_manager::{WatchItemUpdateSender, WatchManager}; + use crate::client::watch_manager::WatchManager; use crate::error::SpvError; use crate::storage::memory::MemoryStorageManager; use crate::storage::StorageManager; use crate::types::WatchItem; - use dashcore::{Address, Network, OutPoint, Script, ScriptBuf, Txid}; + use dashcore::{Address, Network, OutPoint, Txid}; use std::collections::HashSet; use std::str::FromStr; use std::sync::Arc; diff --git a/dash-spv/src/lib.rs b/dash-spv/src/lib.rs index c9267e3fd..8d295ffe6 100644 --- a/dash-spv/src/lib.rs +++ b/dash-spv/src/lib.rs @@ -17,6 +17,8 @@ //! use dash_spv::storage::MemoryStorageManager; //! use dashcore::Network; //! use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +//! use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +//! use key_wallet_manager::wallet_manager::WalletManager; //! use std::sync::Arc; //! use tokio::sync::RwLock; //! @@ -30,7 +32,7 @@ //! // Create the required components //! let network = MultiPeerNetworkManager::new(&config).await?; //! let storage = MemoryStorageManager::new().await?; -//! let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); +//! let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::::new()))); //! //! // Create and start the client //! let mut client = DashSpvClient::new(config.clone(), network, storage, wallet).await?; diff --git a/dash-spv/src/main.rs b/dash-spv/src/main.rs index df2208fbd..a15b2bdfd 100644 --- a/dash-spv/src/main.rs +++ b/dash-spv/src/main.rs @@ -10,6 +10,8 @@ use tokio::signal; use dash_spv::terminal::TerminalGuard; use dash_spv::{ClientConfig, DashSpvClient, Network}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::wallet_manager::WalletManager; #[tokio::main] async fn main() { @@ -221,7 +223,10 @@ async fn run() -> Result<(), Box> { tracing::info!("Using data directory: {}", data_dir.display()); // Create the SPV wallet manager - let spv_wallet = key_wallet_manager::spv_wallet_manager::SPVWalletManager::new(); + let spv_wallet = + key_wallet_manager::spv_wallet_manager::SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()); let wallet = Arc::new(tokio::sync::RwLock::new(spv_wallet)); // Create network manager diff --git a/dash-spv/src/mempool_filter.rs b/dash-spv/src/mempool_filter.rs index 0587fca1d..a9f395f53 100644 --- a/dash-spv/src/mempool_filter.rs +++ b/dash-spv/src/mempool_filter.rs @@ -237,7 +237,7 @@ impl MempoolFilter { #[cfg(test)] mod tests { use super::*; - use dashcore::{hashes::Hash, Network, OutPoint, Script, ScriptBuf, TxIn, TxOut, Witness}; + use dashcore::{Network, OutPoint, ScriptBuf, TxIn, TxOut, Witness}; use std::str::FromStr; // Helper to create a test address diff --git a/dash-spv/src/network/pool.rs b/dash-spv/src/network/pool.rs index ce63e3a6d..aa4c4a4a8 100644 --- a/dash-spv/src/network/pool.rs +++ b/dash-spv/src/network/pool.rs @@ -150,7 +150,6 @@ impl Default for ConnectionPool { #[cfg(test)] mod tests { use super::*; - use dashcore::Network; #[tokio::test] async fn test_connection_pool_basic() { diff --git a/dash-spv/src/network/reputation_tests.rs b/dash-spv/src/network/reputation_tests.rs index 9b7967916..82c8453af 100644 --- a/dash-spv/src/network/reputation_tests.rs +++ b/dash-spv/src/network/reputation_tests.rs @@ -4,7 +4,6 @@ mod tests { use super::super::*; use std::net::SocketAddr; - use std::time::Duration; #[tokio::test] async fn test_basic_reputation_operations() { diff --git a/dash-spv/src/network/tests.rs b/dash-spv/src/network/tests.rs index 4e320f3ee..01445e859 100644 --- a/dash-spv/src/network/tests.rs +++ b/dash-spv/src/network/tests.rs @@ -6,8 +6,7 @@ mod qrinfo_tests { use dashcore::network::message::NetworkMessage; use dashcore::network::message_qrinfo::{MNSkipListMode, QRInfo, QuorumSnapshot}; use dashcore::network::message_sml::MnListDiff; - use dashcore::sml::llmq_type::LLMQType; - use dashcore::transaction::special_transaction::quorum_commitment::QuorumEntry; + use dashcore::BlockHash; fn create_test_qr_info() -> QRInfo { diff --git a/dash-spv/src/sync/headers2_state.rs b/dash-spv/src/sync/headers2_state.rs index b9a02d59e..a10ab1fb4 100644 --- a/dash-spv/src/sync/headers2_state.rs +++ b/dash-spv/src/sync/headers2_state.rs @@ -281,7 +281,7 @@ mod tests { #[test] fn test_statistics() { - let mut manager = Headers2StateManager::new(); + let manager = Headers2StateManager::new(); let stats = manager.get_stats(); assert_eq!(stats.total_headers, 0); diff --git a/dash-spv/src/sync/sequential/recovery.rs b/dash-spv/src/sync/sequential/recovery.rs index e66aa2b85..ba9252994 100644 --- a/dash-spv/src/sync/sequential/recovery.rs +++ b/dash-spv/src/sync/sequential/recovery.rs @@ -504,7 +504,7 @@ mod tests { let mut recovery_manager = RecoveryManager::new(); // Create a test phase - let mut phase = SyncPhase::DownloadingHeaders { + let phase = SyncPhase::DownloadingHeaders { start_time: std::time::Instant::now(), start_height: 50, current_height: 100, diff --git a/dash-spv/src/sync/validation_test.rs b/dash-spv/src/sync/validation_test.rs index dcc7e1e80..5bf3e1310 100644 --- a/dash-spv/src/sync/validation_test.rs +++ b/dash-spv/src/sync/validation_test.rs @@ -208,18 +208,18 @@ mod tests { /// Performance tests for validation #[cfg(test)] mod perf_tests { - use super::tests::*; + use crate::sync::chainlock_validation::{ChainLockValidationConfig, ChainLockValidator}; use crate::sync::validation::{ValidationConfig, ValidationEngine}; - use dashcore::network::message_qrinfo::QRInfo; - use dashcore::{BlockHash, Network}; + + use dashcore::BlockHash; use std::time::Instant; #[tokio::test] #[ignore] // Run with --ignored flag for performance tests async fn test_validation_performance() { let config = ValidationConfig::default(); - let mut engine = ValidationEngine::new(config); + let engine = ValidationEngine::new(config); let start = Instant::now(); @@ -244,7 +244,7 @@ mod perf_tests { let mut config = ChainLockValidationConfig::default(); config.cache_size = 10000; - let mut validator = ChainLockValidator::new(config); + let validator = ChainLockValidator::new(config); let start = Instant::now(); diff --git a/dash-spv/src/validation/headers_test.rs b/dash-spv/src/validation/headers_test.rs index 1014476b3..5e22e8484 100644 --- a/dash-spv/src/validation/headers_test.rs +++ b/dash-spv/src/validation/headers_test.rs @@ -8,7 +8,7 @@ mod tests { use dashcore::{ block::{Header as BlockHeader, Version}, blockdata::constants::genesis_block, - Network, Target, + Network, }; use dashcore_hashes::Hash; diff --git a/dash-spv/tests/chainlock_simple_test.rs b/dash-spv/tests/chainlock_simple_test.rs index b47249c60..f11994764 100644 --- a/dash-spv/tests/chainlock_simple_test.rs +++ b/dash-spv/tests/chainlock_simple_test.rs @@ -5,7 +5,9 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::DiskStorageManager; use dash_spv::types::ValidationMode; use dashcore::Network; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use tempfile::TempDir; use tokio::sync::RwLock; @@ -50,7 +52,9 @@ async fn test_chainlock_validation_flow() { DiskStorageManager::new(config.storage_path.clone().unwrap()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); // Create the SPV client let mut client = @@ -100,7 +104,9 @@ async fn test_chainlock_manager_initialization() { DiskStorageManager::new(config.storage_path.clone().unwrap()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); // Create the SPV client let client = diff --git a/dash-spv/tests/header_sync_test.rs b/dash-spv/tests/header_sync_test.rs index 97baa5e9a..f5a162753 100644 --- a/dash-spv/tests/header_sync_test.rs +++ b/dash-spv/tests/header_sync_test.rs @@ -12,7 +12,9 @@ use dash_spv::{ use dashcore::{block::Header as BlockHeader, block::Version, Network}; use dashcore_hashes::Hash; use env_logger; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use log::{debug, info}; use std::sync::Arc; use tokio::sync::RwLock; @@ -306,7 +308,9 @@ async fn test_header_sync_with_client_integration() { MemoryStorageManager::new().await.expect("Failed to create storage manager"); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await; assert!(client.is_ok(), "Client creation should succeed"); diff --git a/dash-spv/tests/instantsend_integration_test.rs b/dash-spv/tests/instantsend_integration_test.rs index a54fb9ca9..c17d13621 100644 --- a/dash-spv/tests/instantsend_integration_test.rs +++ b/dash-spv/tests/instantsend_integration_test.rs @@ -31,7 +31,7 @@ use rand::thread_rng; /// Helper to create a test wallet manager. fn create_test_wallet() -> Arc> { - Arc::new(RwLock::new(SPVWalletManager::new())) + Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::::new()))) } /// Create a deterministic test address. diff --git a/dash-spv/tests/integration_real_node_test.rs b/dash-spv/tests/integration_real_node_test.rs index c2887559d..9385efba6 100644 --- a/dash-spv/tests/integration_real_node_test.rs +++ b/dash-spv/tests/integration_real_node_test.rs @@ -14,7 +14,9 @@ use dash_spv::{ }; use dashcore::Network; use env_logger; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use log::{debug, info, warn}; use std::sync::Arc; use tokio::sync::RwLock; @@ -37,7 +39,9 @@ async fn create_test_client( let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); Ok(DashSpvClient::new(config, network_manager, storage_manager, wallet).await?) } diff --git a/dash-spv/tests/multi_peer_test.rs b/dash-spv/tests/multi_peer_test.rs index 48f377a25..9beedb960 100644 --- a/dash-spv/tests/multi_peer_test.rs +++ b/dash-spv/tests/multi_peer_test.rs @@ -12,8 +12,9 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::{DiskStorageManager, MemoryStorageManager}; use dash_spv::types::ValidationMode; use dashcore::Network; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; - +use key_wallet_manager::wallet_manager::WalletManager; /// Create a test configuration with the given network fn create_test_config(network: Network, data_dir: Option) -> ClientConfig { let mut config = ClientConfig::new(network); @@ -44,7 +45,9 @@ async fn test_multi_peer_connection() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -91,7 +94,9 @@ async fn test_peer_persistence() { let storage_manager = DiskStorageManager::new(temp_path.clone()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -117,7 +122,9 @@ async fn test_peer_persistence() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -157,7 +164,9 @@ async fn test_peer_disconnection() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -193,7 +202,9 @@ async fn test_max_peer_limit() { let storage_manager = MemoryStorageManager::new().await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let _client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); diff --git a/dash-spv/tests/simple_header_test.rs b/dash-spv/tests/simple_header_test.rs index adcc0cb50..651760496 100644 --- a/dash-spv/tests/simple_header_test.rs +++ b/dash-spv/tests/simple_header_test.rs @@ -7,7 +7,9 @@ use dash_spv::{ types::ValidationMode, }; use dashcore::Network; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; +use key_wallet_manager::wallet_manager::WalletManager; use log::info; use std::{net::SocketAddr, sync::Arc, time::Duration}; use tokio::sync::RwLock; @@ -59,7 +61,9 @@ async fn test_simple_header_sync() { MultiPeerNetworkManager::new(&config).await.expect("Failed to create network manager"); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); let mut client = DashSpvClient::new(config.clone(), network_manager, storage, wallet) .await diff --git a/dash-spv/tests/wallet_integration_test.rs b/dash-spv/tests/wallet_integration_test.rs index e54a0e906..c5a7b956d 100644 --- a/dash-spv/tests/wallet_integration_test.rs +++ b/dash-spv/tests/wallet_integration_test.rs @@ -9,8 +9,9 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::MemoryStorageManager; use dash_spv::{ClientConfig, DashSpvClient}; use dashcore::{Block, Network}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; - +use key_wallet_manager::wallet_manager::WalletManager; /// Create a test SPV client with memory storage for integration testing. async fn create_test_client( ) -> DashSpvClient { @@ -23,7 +24,9 @@ async fn create_test_client( let storage_manager = MemoryStorageManager::new().await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::new())); + let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< + ManagedWalletInfo, + >::new()))); DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap() } @@ -60,7 +63,7 @@ async fn test_spv_client_start_stop() { #[tokio::test] async fn test_wallet_manager_basic_operations() { // Test basic wallet manager operations - let mut wallet_manager = SPVWalletManager::new(); + let mut wallet_manager = SPVWalletManager::with_base(WalletManager::::new()); // Test that we can create a wallet manager // SPVWalletManager doesn't have get_watched_scripts method anymore diff --git a/dash/src/base58.rs b/dash/src/base58.rs index eae5dffd5..30a2cb132 100644 --- a/dash/src/base58.rs +++ b/dash/src/base58.rs @@ -113,12 +113,12 @@ impl SmallVec { } } - pub fn iter(&self) -> iter::Chain, slice::Iter> { + pub fn iter(&self) -> iter::Chain, slice::Iter<'_, T>> { // If len<100 then we just append an empty vec self.stack[0..self.len].iter().chain(self.heap.iter()) } - pub fn iter_mut(&mut self) -> iter::Chain, slice::IterMut> { + pub fn iter_mut(&mut self) -> iter::Chain, slice::IterMut<'_, T>> { // If len<100 then we just append an empty vec self.stack[0..self.len].iter_mut().chain(self.heap.iter_mut()) } diff --git a/dash/src/blockdata/script/borrowed.rs b/dash/src/blockdata/script/borrowed.rs index 5d8f57d71..1cf0c652c 100644 --- a/dash/src/blockdata/script/borrowed.rs +++ b/dash/src/blockdata/script/borrowed.rs @@ -366,7 +366,7 @@ impl Script { /// /// To force minimal pushes, use [`instructions_minimal`](Self::instructions_minimal). #[inline] - pub fn instructions(&self) -> Instructions { + pub fn instructions(&self) -> Instructions<'_> { Instructions { data: self.0.iter(), enforce_minimal: false, @@ -378,7 +378,7 @@ impl Script { /// This is similar to [`instructions`](Self::instructions) but an error is returned if a push /// is not minimal. #[inline] - pub fn instructions_minimal(&self) -> Instructions { + pub fn instructions_minimal(&self) -> Instructions<'_> { Instructions { data: self.0.iter(), enforce_minimal: true, @@ -393,7 +393,7 @@ impl Script { /// /// To force minimal pushes, use [`Self::instruction_indices_minimal`]. #[inline] - pub fn instruction_indices(&self) -> InstructionIndices { + pub fn instruction_indices(&self) -> InstructionIndices<'_> { InstructionIndices::from_instructions(self.instructions()) } @@ -402,7 +402,7 @@ impl Script { /// This is similar to [`instruction_indices`](Self::instruction_indices) but an error is /// returned if a push is not minimal. #[inline] - pub fn instruction_indices_minimal(&self) -> InstructionIndices { + pub fn instruction_indices_minimal(&self) -> InstructionIndices<'_> { InstructionIndices::from_instructions(self.instructions_minimal()) } diff --git a/dash/src/blockdata/witness.rs b/dash/src/blockdata/witness.rs index 9023dac76..9201318a1 100644 --- a/dash/src/blockdata/witness.rs +++ b/dash/src/blockdata/witness.rs @@ -219,7 +219,7 @@ impl Witness { } /// Returns a struct implementing [`Iterator`]. - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_> { Iter { inner: self.content.as_slice(), indices_start: self.indices_start, diff --git a/dash/src/taproot.rs b/dash/src/taproot.rs index 5fc4bff5d..167d9eeee 100644 --- a/dash/src/taproot.rs +++ b/dash/src/taproot.rs @@ -723,7 +723,7 @@ impl TapTree { /// Returns [`TapTreeIter<'_>`] iterator for a taproot script tree, operating in DFS order over /// tree [`ScriptLeaf`]s. - pub fn script_leaves(&self) -> ScriptLeaves { + pub fn script_leaves(&self) -> ScriptLeaves<'_> { ScriptLeaves { leaf_iter: self.0.leaf_nodes(), } @@ -902,7 +902,7 @@ impl NodeInfo { } /// Creates an iterator over all leaves (including hidden leaves) in the tree. - pub fn leaf_nodes(&self) -> LeafNodes { + pub fn leaf_nodes(&self) -> LeafNodes<'_> { LeafNodes { leaf_iter: self.leaves.iter(), } diff --git a/key-wallet-manager/src/spv_wallet_manager.rs b/key-wallet-manager/src/spv_wallet_manager.rs index 173ea5c14..f379f82d5 100644 --- a/key-wallet-manager/src/spv_wallet_manager.rs +++ b/key-wallet-manager/src/spv_wallet_manager.rs @@ -18,6 +18,8 @@ use key_wallet::Network; use crate::wallet_interface::WalletInterface; use crate::wallet_manager::{WalletId, WalletManager}; use key_wallet::transaction_checking::TransactionContext; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; /// SPV Wallet Manager /// diff --git a/key-wallet-manager/src/wallet_manager/mod.rs b/key-wallet-manager/src/wallet_manager/mod.rs index 7865fe348..4e9ef6b4b 100644 --- a/key-wallet-manager/src/wallet_manager/mod.rs +++ b/key-wallet-manager/src/wallet_manager/mod.rs @@ -10,7 +10,6 @@ mod transaction_building; use alloc::collections::BTreeMap; use alloc::string::String; use alloc::vec::Vec; - use dashcore::blockdata::transaction::Transaction; use dashcore::Txid; use key_wallet::wallet::managed_wallet_info::{ManagedWalletInfo, TransactionRecord}; @@ -19,6 +18,7 @@ use key_wallet::{Account, AccountType, Address, Mnemonic, Network, Wallet, Walle use key_wallet::transaction_checking::{TransactionContext, WalletTransactionChecker}; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::{Utxo, UtxoSet}; /// Unique identifier for a wallet (32-byte hash) diff --git a/key-wallet-manager/src/wallet_manager/process_block.rs b/key-wallet-manager/src/wallet_manager/process_block.rs index 8d5db21e2..282ccb26c 100644 --- a/key-wallet-manager/src/wallet_manager/process_block.rs +++ b/key-wallet-manager/src/wallet_manager/process_block.rs @@ -3,6 +3,7 @@ use dashcore::bip158::BlockFilter; use dashcore::prelude::CoreBlockHeight; use dashcore::{Block, BlockHash, Txid}; use key_wallet::transaction_checking::TransactionContext; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; impl WalletManager { pub fn process_block(&mut self, block: &Block, height: u32, network: Network) -> Vec { diff --git a/key-wallet-manager/src/wallet_manager/transaction_building.rs b/key-wallet-manager/src/wallet_manager/transaction_building.rs index fb6b8c6d1..5403cc40a 100644 --- a/key-wallet-manager/src/wallet_manager/transaction_building.rs +++ b/key-wallet-manager/src/wallet_manager/transaction_building.rs @@ -6,6 +6,7 @@ use key_wallet::wallet::managed_wallet_info::fee::FeeLevel; use key_wallet::wallet::managed_wallet_info::transaction_building::{ AccountTypePreference, TransactionError, }; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::{Address, Network}; impl WalletManager { diff --git a/key-wallet/src/tests/performance_tests.rs b/key-wallet/src/tests/performance_tests.rs index 3edfdb809..d184a19e2 100644 --- a/key-wallet/src/tests/performance_tests.rs +++ b/key-wallet/src/tests/performance_tests.rs @@ -147,8 +147,21 @@ fn test_wallet_recovery_performance() { let metrics = PerformanceMetrics::from_times("Wallet Recovery", times); + // Print detailed performance metrics before assertion + println!("\n=== Wallet Recovery Performance ==="); + println!("Average time: {:?}", metrics.avg_time); + println!("Total time for {} iterations: {:?}", iterations, metrics.total_time); + println!("Operations per second: {:.2}", metrics.ops_per_second); + println!("Min/Max times: {:?} / {:?}", metrics.min_time, metrics.max_time); + println!("Expected: < 50ms per recovery"); + println!("===================================\n"); + // Assert performance requirements - assert!(metrics.avg_time < Duration::from_millis(50), "Wallet recovery too slow"); + assert!( + metrics.avg_time < Duration::from_millis(50), + "Wallet recovery too slow: avg {:?}, expected < 50ms", + metrics.avg_time + ); } #[test] @@ -349,8 +362,20 @@ fn test_transaction_checking_performance() { let elapsed = start.elapsed(); let ops_per_second = num_transactions as f64 / elapsed.as_secs_f64(); + // Print detailed performance metrics before assertion + println!("\n=== Transaction Checking Performance ==="); + println!("Checked {} transactions in {:?}", num_transactions, elapsed); + println!("Transactions per second: {:.2}", ops_per_second); + println!("Average time per transaction: {:?}", elapsed / num_transactions as u32); + println!("Expected: > 10,000 transactions/sec"); + println!("=========================================\n"); + // Assert transaction checking performance - assert!(ops_per_second > 10000.0, "Should check >10000 transactions/sec"); + assert!( + ops_per_second > 10000.0, + "Should check >10000 transactions/sec, but got {:.2} tx/sec", + ops_per_second + ); } #[test] diff --git a/key-wallet/src/wallet/managed_wallet_info/mod.rs b/key-wallet/src/wallet/managed_wallet_info/mod.rs index cc9d3897b..5ac0ef230 100644 --- a/key-wallet/src/wallet/managed_wallet_info/mod.rs +++ b/key-wallet/src/wallet/managed_wallet_info/mod.rs @@ -7,6 +7,7 @@ pub mod coin_selection; pub mod fee; pub mod transaction_builder; pub mod transaction_building; +pub mod wallet_info_interface; use super::balance::WalletBalance; use super::immature_transaction::ImmatureTransactionCollection; diff --git a/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs b/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs new file mode 100644 index 000000000..ac7519073 --- /dev/null +++ b/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs @@ -0,0 +1,179 @@ +//! Trait defining the interface for wallet info types +//! +//! This trait allows WalletManager to work with different wallet info implementations + +use crate::account::managed_account_collection::ManagedAccountCollection; +use crate::transaction_checking::WalletTransactionChecker; +use crate::wallet::managed_wallet_info::fee::FeeLevel; +use crate::wallet::managed_wallet_info::transaction_building::{ + AccountTypePreference, TransactionError, +}; +use crate::wallet::managed_wallet_info::TransactionRecord; +use crate::wallet::ManagedWalletInfo; +use crate::{Address, Network, Utxo, Wallet, WalletBalance}; +use dashcore::blockdata::transaction::Transaction; +use dashcore::Address as DashAddress; + +/// Trait that wallet info types must implement to work with WalletManager +pub trait WalletInfoInterface: Sized + WalletTransactionChecker { + /// Create a new wallet info with the given ID and name + fn with_name(wallet_id: [u8; 32], name: String) -> Self; + /// Get the wallet's unique ID + fn wallet_id(&self) -> [u8; 32]; + + /// Get the wallet's name + fn name(&self) -> Option<&str>; + + /// Set the wallet's name + fn set_name(&mut self, name: String); + + /// Get the wallet's description + fn description(&self) -> Option<&str>; + + /// Set the wallet's description + fn set_description(&mut self, description: Option); + + /// Get the birth height for tracking + fn birth_height(&self) -> Option; + + /// Set the birth height + fn set_birth_height(&mut self, height: Option); + + /// Get the timestamp when first loaded + fn first_loaded_at(&self) -> u64; + + /// Set the timestamp when first loaded + fn set_first_loaded_at(&mut self, timestamp: u64); + + /// Update last synced timestamp + fn update_last_synced(&mut self, timestamp: u64); + + /// Get all monitored addresses for a network + fn monitored_addresses(&self, network: Network) -> Vec; + + /// Get all UTXOs for the wallet + fn get_utxos(&self) -> Vec; + + /// Get the wallet balance + fn get_balance(&self) -> WalletBalance; + + /// Update the wallet balance + fn update_balance(&mut self); + + /// Get transaction history + fn get_transaction_history(&self) -> Vec<&TransactionRecord>; + + /// Get accounts for a network (mutable) + fn accounts_mut(&mut self, network: Network) -> Option<&mut ManagedAccountCollection>; + + /// Get accounts for a network (immutable) + fn accounts(&self, network: Network) -> Option<&ManagedAccountCollection>; + + /// Create an unsigned payment transaction + #[allow(clippy::too_many_arguments)] + fn create_unsigned_payment_transaction( + &mut self, + wallet: &Wallet, + network: Network, + account_index: u32, + account_type_pref: Option, + recipients: Vec<(Address, u64)>, + fee_level: FeeLevel, + current_block_height: u32, + ) -> Result; +} + +/// Default implementation for ManagedWalletInfo +impl WalletInfoInterface for ManagedWalletInfo { + fn with_name(wallet_id: [u8; 32], name: String) -> Self { + Self::with_name(wallet_id, name) + } + fn wallet_id(&self) -> [u8; 32] { + self.wallet_id + } + + fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + fn set_name(&mut self, name: String) { + self.name = Some(name); + } + + fn description(&self) -> Option<&str> { + self.description.as_deref() + } + + fn set_description(&mut self, description: Option) { + self.description = description; + } + + fn birth_height(&self) -> Option { + self.metadata.birth_height + } + + fn set_birth_height(&mut self, height: Option) { + self.metadata.birth_height = height; + } + + fn first_loaded_at(&self) -> u64 { + self.metadata.first_loaded_at + } + + fn set_first_loaded_at(&mut self, timestamp: u64) { + self.metadata.first_loaded_at = timestamp; + } + + fn update_last_synced(&mut self, timestamp: u64) { + self.update_last_synced(timestamp); + } + + fn monitored_addresses(&self, network: Network) -> Vec { + self.monitored_addresses(network).into_iter().collect() + } + + fn get_utxos(&self) -> Vec { + self.get_utxos().into_iter().cloned().collect() + } + + fn get_balance(&self) -> WalletBalance { + self.get_balance() + } + + fn update_balance(&mut self) { + self.update_balance(); + } + + fn get_transaction_history(&self) -> Vec<&TransactionRecord> { + self.get_transaction_history() + } + + fn accounts_mut(&mut self, network: Network) -> Option<&mut ManagedAccountCollection> { + self.accounts.get_mut(&network) + } + + fn accounts(&self, network: Network) -> Option<&ManagedAccountCollection> { + self.accounts.get(&network) + } + + fn create_unsigned_payment_transaction( + &mut self, + wallet: &Wallet, + network: Network, + account_index: u32, + account_type_pref: Option, + recipients: Vec<(Address, u64)>, + fee_level: FeeLevel, + current_block_height: u32, + ) -> Result { + self.create_unsigned_payment_transaction( + wallet, + network, + account_index, + account_type_pref, + recipients, + fee_level, + current_block_height, + ) + } +} From 31d2f096715b9e377be567544f85433f23af0056 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 19 Aug 2025 04:52:53 +0700 Subject: [PATCH 2/4] update rust version --- CLAUDE.md | 2 +- README.md | 2 +- clippy.toml | 2 +- dash-spv/CLAUDE.md | 2 +- dash-spv/Cargo.toml | 2 +- .../examples/wallet_creation.rs | 5 +- key-wallet-manager/src/lib.rs | 1 + key-wallet-manager/src/spv_wallet_manager.rs | 34 +++++--- key-wallet-manager/src/wallet_manager/mod.rs | 79 +++++++++++-------- .../src/wallet_manager/process_block.rs | 3 +- .../wallet_manager/transaction_building.rs | 3 +- key-wallet-manager/tests/integration_test.rs | 15 ++-- .../tests/spv_integration_tests.rs | 17 ++-- 13 files changed, 99 insertions(+), 68 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5769b9e40..53796a839 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -153,7 +153,7 @@ cargo doc --open - **Async/Await**: Modern async Rust throughout - **FFI Support**: C and Swift bindings for cross-platform usage - **Comprehensive Testing**: Unit, integration, and fuzz testing -- **MSRV**: Rust 1.80 minimum supported version +- **MSRV**: Rust 1.89 minimum supported version ## Code Style Guidelines diff --git a/README.md b/README.md index a0793982b..f89f2bc54 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ architectural mismatches. ## Minimum Supported Rust Version (MSRV) -This library should always compile with any combination of features on **Rust 1.80**. +This library should always compile with any combination of features on **Rust 1.89**. ## Installing Rust diff --git a/clippy.toml b/clippy.toml index 8bfc90f6a..f2c7fb149 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.80" +msrv = "1.89" diff --git a/dash-spv/CLAUDE.md b/dash-spv/CLAUDE.md index e22d431d2..bbca77288 100644 --- a/dash-spv/CLAUDE.md +++ b/dash-spv/CLAUDE.md @@ -178,7 +178,7 @@ Use domain-specific error types: ## MSRV and Dependencies -- **Minimum Rust Version**: 1.80 +- **Minimum Rust Version**: 1.89 - **Core dependencies**: `dashcore`, `tokio`, `async-trait`, `thiserror` - **Built on**: `dashcore` library with Dash-specific features enabled - **Async runtime**: Tokio with full feature set diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index 1a94ece44..c2fb50cfa 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Dash Core Team"] description = "Dash SPV (Simplified Payment Verification) client library" license = "MIT" repository = "https://github.com/dashpay/rust-dashcore" -rust-version = "1.80" +rust-version = "1.89" [dependencies] # Core Dash libraries diff --git a/key-wallet-manager/examples/wallet_creation.rs b/key-wallet-manager/examples/wallet_creation.rs index ea3c68ed1..63d51c3c0 100644 --- a/key-wallet-manager/examples/wallet_creation.rs +++ b/key-wallet-manager/examples/wallet_creation.rs @@ -9,6 +9,7 @@ use hex; use key_wallet::account::StandardAccountType; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::{AccountType, Network}; use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::{WalletId, WalletManager}; @@ -19,7 +20,7 @@ fn main() { // Example 1: Basic wallet creation with WalletManager println!("1. Creating a basic wallet with WalletManager..."); - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID (32 bytes) let wallet_id: WalletId = [1u8; 32]; @@ -124,7 +125,7 @@ fn main() { // Example 5: Using SPVWalletManager println!("\n5. Using SPVWalletManager for SPV functionality..."); - let mut spv_manager = SPVWalletManager::new(); + let mut spv_manager = SPVWalletManager::with_base(WalletManager::::new()); let wallet_id3: WalletId = [3u8; 32]; diff --git a/key-wallet-manager/src/lib.rs b/key-wallet-manager/src/lib.rs index 0bab98c64..e17aaf1ea 100644 --- a/key-wallet-manager/src/lib.rs +++ b/key-wallet-manager/src/lib.rs @@ -22,6 +22,7 @@ extern crate alloc; extern crate std; pub mod spv_wallet_manager; +pub mod wallet_info_trait; pub mod wallet_interface; pub mod wallet_manager; diff --git a/key-wallet-manager/src/spv_wallet_manager.rs b/key-wallet-manager/src/spv_wallet_manager.rs index f379f82d5..155d4ae34 100644 --- a/key-wallet-manager/src/spv_wallet_manager.rs +++ b/key-wallet-manager/src/spv_wallet_manager.rs @@ -15,6 +15,7 @@ use dashcore::prelude::CoreBlockHeight; use dashcore::{BlockHash, Txid}; use key_wallet::Network; +use crate::wallet_info_trait::WalletInfoInterface; use crate::wallet_interface::WalletInterface; use crate::wallet_manager::{WalletId, WalletManager}; use key_wallet::transaction_checking::TransactionContext; @@ -31,9 +32,9 @@ use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; /// All wallet state, UTXO tracking, and transaction processing is delegated /// to the underlying WalletManager. #[derive(Debug)] -pub struct SPVWalletManager { +pub struct SPVWalletManager { /// Base wallet manager (handles all wallet state) - pub base: WalletManager, + pub base: WalletManager, // SPV-specific fields only /// Block download queue (per network) @@ -48,12 +49,15 @@ pub struct SPVWalletManager { stats: BTreeMap, } -impl From for SPVWalletManager { - fn from(manager: WalletManager) -> Self { +impl From> for SPVWalletManager { + fn from(manager: WalletManager) -> Self { Self { base: manager, + download_queues: BTreeMap::new(), + pending_blocks: BTreeMap::new(), + filter_matches: BTreeMap::new(), max_download_queue: 100, - ..Default::default() + stats: BTreeMap::new(), } } } @@ -97,11 +101,11 @@ pub enum SPVSyncStatus { Error(String), } -impl SPVWalletManager { - /// Create a new SPV wallet manager - pub fn new() -> Self { +impl SPVWalletManager { + /// Create a new SPV wallet manager from a WalletManager + pub fn with_base(base: WalletManager) -> Self { Self { - base: WalletManager::new(), + base, download_queues: BTreeMap::new(), pending_blocks: BTreeMap::new(), filter_matches: BTreeMap::new(), @@ -110,6 +114,14 @@ impl SPVWalletManager { } } + /// Create a new SPV wallet manager (only available when T implements Default) + pub fn new() -> Self + where + T: Default, + { + Self::with_base(WalletManager::new()) + } + /// Queue a block for download pub fn queue_block_download(&mut self, network: Network, block_hash: BlockHash) -> bool { let queue = self.download_queues.entry(network).or_default(); @@ -202,14 +214,14 @@ pub struct ProcessBlockResult { pub affected_wallets: Vec, } -impl Default for SPVWalletManager { +impl Default for SPVWalletManager { fn default() -> Self { Self::new() } } #[async_trait] -impl WalletInterface for SPVWalletManager { +impl WalletInterface for SPVWalletManager { /// Process a block and return relevant transaction IDs async fn process_block( &mut self, diff --git a/key-wallet-manager/src/wallet_manager/mod.rs b/key-wallet-manager/src/wallet_manager/mod.rs index 4e9ef6b4b..017ca5a3c 100644 --- a/key-wallet-manager/src/wallet_manager/mod.rs +++ b/key-wallet-manager/src/wallet_manager/mod.rs @@ -16,6 +16,8 @@ use key_wallet::wallet::managed_wallet_info::{ManagedWalletInfo, TransactionReco use key_wallet::WalletBalance; use key_wallet::{Account, AccountType, Address, Mnemonic, Network, Wallet, WalletConfig}; +use crate::wallet_info_trait::WalletInfoInterface; + use key_wallet::transaction_checking::{TransactionContext, WalletTransactionChecker}; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; @@ -78,22 +80,25 @@ impl NetworkState { /// Each wallet can contain multiple accounts following BIP44 standard. /// This is the main entry point for wallet operations. #[derive(Debug)] -pub struct WalletManager { +pub struct WalletManager { /// Immutable wallets indexed by wallet ID pub(crate) wallets: BTreeMap, /// Mutable wallet info indexed by wallet ID - pub(crate) wallet_infos: BTreeMap, + pub(crate) wallet_infos: BTreeMap, /// Network-specific state (UTXO sets, transactions, heights) network_states: BTreeMap, } -impl Default for WalletManager { +impl Default for WalletManager +where + T: Default, +{ fn default() -> Self { Self::new() } } -impl WalletManager { +impl WalletManager { /// Create a new wallet manager pub fn new() -> Self { Self { @@ -112,7 +117,7 @@ impl WalletManager { passphrase: &str, network: Option, birth_height: Option, - ) -> Result<&ManagedWalletInfo, WalletError> { + ) -> Result<&T, WalletError> { if self.wallets.contains_key(&wallet_id) { return Err(WalletError::WalletExists(wallet_id)); } @@ -145,9 +150,9 @@ impl WalletManager { }; // Create managed wallet info - let mut managed_info = ManagedWalletInfo::with_name(wallet.wallet_id, name); - managed_info.metadata.birth_height = birth_height; - managed_info.metadata.first_loaded_at = current_timestamp(); + let mut managed_info = T::with_name(wallet.wallet_id, name); + managed_info.set_birth_height(birth_height); + managed_info.set_first_loaded_at(current_timestamp()); // Create default account in the wallet let mut wallet_mut = wallet.clone(); @@ -181,7 +186,7 @@ impl WalletManager { wallet_id: WalletId, name: String, network: Network, - ) -> Result<&ManagedWalletInfo, WalletError> { + ) -> Result<&T, WalletError> { if self.wallets.contains_key(&wallet_id) { return Err(WalletError::WalletExists(wallet_id)); } @@ -202,10 +207,10 @@ impl WalletManager { .map_err(|e| WalletError::WalletCreation(e.to_string()))?; // Create managed wallet info - let mut managed_info = ManagedWalletInfo::with_name(wallet.wallet_id, name); + let mut managed_info = T::with_name(wallet.wallet_id, name); let network_state = self.get_or_create_network_state(network); - managed_info.metadata.birth_height = Some(network_state.current_height); - managed_info.metadata.first_loaded_at = current_timestamp(); + managed_info.set_birth_height(Some(network_state.current_height)); + managed_info.set_first_loaded_at(current_timestamp()); // Check if account 0 already exists (from_mnemonic might create it) let mut wallet_mut = wallet.clone(); @@ -234,20 +239,17 @@ impl WalletManager { } /// Get wallet info by ID - pub fn get_wallet_info(&self, wallet_id: &WalletId) -> Option<&ManagedWalletInfo> { + pub fn get_wallet_info(&self, wallet_id: &WalletId) -> Option<&T> { self.wallet_infos.get(wallet_id) } /// Get mutable wallet info by ID - pub fn get_wallet_info_mut(&mut self, wallet_id: &WalletId) -> Option<&mut ManagedWalletInfo> { + pub fn get_wallet_info_mut(&mut self, wallet_id: &WalletId) -> Option<&mut T> { self.wallet_infos.get_mut(wallet_id) } /// Get both wallet and info by ID - pub fn get_wallet_and_info( - &self, - wallet_id: &WalletId, - ) -> Option<(&Wallet, &ManagedWalletInfo)> { + pub fn get_wallet_and_info(&self, wallet_id: &WalletId) -> Option<(&Wallet, &T)> { match (self.wallets.get(wallet_id), self.wallet_infos.get(wallet_id)) { (Some(wallet), Some(info)) => Some((wallet, info)), _ => None, @@ -255,10 +257,7 @@ impl WalletManager { } /// Remove a wallet - pub fn remove_wallet( - &mut self, - wallet_id: &WalletId, - ) -> Result<(Wallet, ManagedWalletInfo), WalletError> { + pub fn remove_wallet(&mut self, wallet_id: &WalletId) -> Result<(Wallet, T), WalletError> { let wallet = self.wallets.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; let info = @@ -277,7 +276,7 @@ impl WalletManager { } /// Get all wallet infos - pub fn get_all_wallet_infos(&self) -> &BTreeMap { + pub fn get_all_wallet_infos(&self) -> &BTreeMap { &self.wallet_infos } @@ -302,8 +301,24 @@ impl WalletManager { for wallet_id in wallet_ids { // Check the transaction for this wallet if let Some(wallet_info) = self.wallet_infos.get_mut(&wallet_id) { - let result = - wallet_info.check_transaction(tx, network, context, update_state_if_found); + // Convert TransactionContext to the expected format + let context_tuple = match context { + TransactionContext::InBlock { + height, + .. + } + | TransactionContext::InChainLockedBlock { + height, + .. + } => Some((height, tx.txid())), + TransactionContext::Mempool => None, + }; + let result = wallet_info.check_transaction( + tx, + network, + context_tuple, + update_state_if_found, + ); // If the transaction is relevant if result.is_relevant { @@ -427,8 +442,7 @@ impl WalletManager { self.wallet_infos.get_mut(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; // Get the account collection for the network - let collection = - managed_info.accounts.get_mut(&network).ok_or(WalletError::InvalidNetwork)?; + let collection = managed_info.accounts_mut(network).ok_or(WalletError::InvalidNetwork)?; // Try to get address based on preference let (address_opt, account_type_used) = match account_type_pref { @@ -550,7 +564,7 @@ impl WalletManager { if let Some(ref address) = address_opt { if mark_as_used { // Get the account collection again for marking - if let Some(collection) = managed_info.accounts.get_mut(&network) { + if let Some(collection) = managed_info.accounts_mut(network) { // Mark address as used in the appropriate account type match account_type_used { Some(AccountTypeUsed::BIP44) => { @@ -594,8 +608,7 @@ impl WalletManager { self.wallet_infos.get_mut(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; // Get the account collection for the network - let collection = - managed_info.accounts.get_mut(&network).ok_or(WalletError::InvalidNetwork)?; + let collection = managed_info.accounts_mut(network).ok_or(WalletError::InvalidNetwork)?; // Try to get address based on preference let (address_opt, account_type_used) = match account_type_pref { @@ -717,7 +730,7 @@ impl WalletManager { if let Some(ref address) = address_opt { if mark_as_used { // Get the account collection again for marking - if let Some(collection) = managed_info.accounts.get_mut(&network) { + if let Some(collection) = managed_info.accounts_mut(network) { // Mark address as used in the appropriate account type match account_type_used { Some(AccountTypeUsed::BIP44) => { @@ -782,7 +795,7 @@ impl WalletManager { self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; // Get UTXOs from the wallet info and clone them - let utxos = wallet_info.get_utxos().into_iter().cloned().collect(); + let utxos = wallet_info.get_utxos(); Ok(utxos) } @@ -826,7 +839,7 @@ impl WalletManager { } if let Some(desc) = description { - managed_info.set_description(desc); + managed_info.set_description(Some(desc)); } managed_info.update_last_synced(current_timestamp()); diff --git a/key-wallet-manager/src/wallet_manager/process_block.rs b/key-wallet-manager/src/wallet_manager/process_block.rs index 282ccb26c..d178ce94d 100644 --- a/key-wallet-manager/src/wallet_manager/process_block.rs +++ b/key-wallet-manager/src/wallet_manager/process_block.rs @@ -1,3 +1,4 @@ +use crate::wallet_info_trait::WalletInfoInterface; use crate::{Network, WalletManager}; use dashcore::bip158::BlockFilter; use dashcore::prelude::CoreBlockHeight; @@ -5,7 +6,7 @@ use dashcore::{Block, BlockHash, Txid}; use key_wallet::transaction_checking::TransactionContext; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; -impl WalletManager { +impl WalletManager { pub fn process_block(&mut self, block: &Block, height: u32, network: Network) -> Vec { let mut relevant_txids = Vec::new(); let block_hash = Some(block.block_hash()); diff --git a/key-wallet-manager/src/wallet_manager/transaction_building.rs b/key-wallet-manager/src/wallet_manager/transaction_building.rs index 5403cc40a..cc7660597 100644 --- a/key-wallet-manager/src/wallet_manager/transaction_building.rs +++ b/key-wallet-manager/src/wallet_manager/transaction_building.rs @@ -1,6 +1,7 @@ //! Transaction building functionality for the wallet manager use super::{WalletError, WalletId, WalletManager}; +use crate::wallet_info_trait::WalletInfoInterface; use dashcore::Transaction; use key_wallet::wallet::managed_wallet_info::fee::FeeLevel; use key_wallet::wallet::managed_wallet_info::transaction_building::{ @@ -9,7 +10,7 @@ use key_wallet::wallet::managed_wallet_info::transaction_building::{ use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::{Address, Network}; -impl WalletManager { +impl WalletManager { /// Creates an unsigned transaction from a specific wallet and account /// /// This method delegates to the ManagedWalletInfo's create_payment_transaction method diff --git a/key-wallet-manager/tests/integration_test.rs b/key-wallet-manager/tests/integration_test.rs index ad2a95cdf..eb695964f 100644 --- a/key-wallet-manager/tests/integration_test.rs +++ b/key-wallet-manager/tests/integration_test.rs @@ -4,13 +4,14 @@ //! works correctly with the low-level key-wallet primitives. use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::{mnemonic::Language, Mnemonic, Network}; use key_wallet_manager::wallet_manager::{WalletError, WalletId, WalletManager}; #[test] fn test_wallet_manager_creation() { // Create a wallet manager - let manager = WalletManager::new(); + let manager = WalletManager::::new(); // WalletManager::new returns Self, not Result assert_eq!(manager.current_height(Network::Testnet), 0); @@ -21,7 +22,7 @@ fn test_wallet_manager_creation() { fn test_wallet_manager_from_mnemonic() { // Create from a test mnemonic let mnemonic = Mnemonic::generate(12, Language::English).unwrap(); - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID let wallet_id: WalletId = [1u8; 32]; @@ -41,7 +42,7 @@ fn test_wallet_manager_from_mnemonic() { #[test] fn test_account_management() { - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID let wallet_id: WalletId = [1u8; 32]; @@ -70,7 +71,7 @@ fn test_account_management() { #[test] fn test_address_generation() { - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID let wallet_id: WalletId = [1u8; 32]; @@ -126,7 +127,7 @@ fn test_address_generation() { fn test_utxo_management() { // Unused imports removed - UTXOs are created by processing transactions - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID let wallet_id: WalletId = [1u8; 32]; @@ -151,7 +152,7 @@ fn test_utxo_management() { #[test] fn test_balance_calculation() { - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); // Create a wallet ID let wallet_id: WalletId = [1u8; 32]; @@ -175,7 +176,7 @@ fn test_balance_calculation() { #[test] fn test_block_height_tracking() { - let mut manager = WalletManager::new(); + let mut manager = WalletManager::::new(); assert_eq!(manager.current_height(Network::Testnet), 0); diff --git a/key-wallet-manager/tests/spv_integration_tests.rs b/key-wallet-manager/tests/spv_integration_tests.rs index 3e8dc6ef9..666b22adc 100644 --- a/key-wallet-manager/tests/spv_integration_tests.rs +++ b/key-wallet-manager/tests/spv_integration_tests.rs @@ -8,10 +8,11 @@ use dashcore::{TxIn, TxOut}; use dashcore_hashes::Hash; use dashcore::bip158::{BlockFilter, BlockFilterWriter}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; use key_wallet_manager::spv_wallet_manager::{SPVSyncStatus, SPVWalletManager}; use key_wallet_manager::wallet_interface::WalletInterface; -use key_wallet_manager::wallet_manager::WalletId; +use key_wallet_manager::wallet_manager::{WalletId, WalletManager}; /// Create a test transaction fn create_test_transaction(value: u64) -> Transaction { @@ -69,7 +70,7 @@ fn create_mock_filter(block: &Block) -> BlockFilter { #[test] fn test_spv_integration_basic() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create a test wallet let wallet_id: WalletId = [1u8; 32]; @@ -82,7 +83,7 @@ fn test_spv_integration_basic() { #[tokio::test] async fn test_filter_checking() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create a test wallet let wallet_id: WalletId = [1u8; 32]; @@ -109,7 +110,7 @@ async fn test_filter_checking() { #[tokio::test] async fn test_block_processing() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create a test wallet let wallet_id: WalletId = [1u8; 32]; @@ -136,7 +137,7 @@ fn test_mempool_transaction() { #[test] fn test_queued_blocks() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create blocks let block1 = create_test_block(101, vec![create_test_transaction(1000)]); @@ -159,7 +160,7 @@ fn test_queued_blocks() { #[test] fn test_sync_status_tracking() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Set target height spv.set_target_height(Network::Testnet, 1000); @@ -217,7 +218,7 @@ fn test_reorg_handling() { #[tokio::test] async fn test_multiple_wallets() { - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create and add multiple wallets for i in 0..3 { @@ -250,7 +251,7 @@ async fn test_multiple_wallets() { async fn test_spent_utxo_tracking() { // This test requires more complex UTXO tracking that's not fully implemented // We'll create a simpler version - let mut spv = SPVWalletManager::new(); + let mut spv = SPVWalletManager::with_base(WalletManager::::new()); // Create a test wallet let wallet_id: WalletId = [1u8; 32]; From 84cc9843fc07c881087d0f6604c72656efae74a5 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 19 Aug 2025 05:01:57 +0700 Subject: [PATCH 3/4] update rust --- Cargo.toml | 5 ----- dash-spv/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed0c1fc08..1031c6a54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,3 @@ version = "0.39.6" [patch.crates-io.dashcore_hashes] path = "hashes" -[patch.crates-io] -blsful = { git = "https://github.com/dashpay/agora-blsful", rev = "5f017aa1a0452ebc73e47f219f50c906522df4ea" } - - - diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index c2fb50cfa..76d373a9e 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -16,7 +16,7 @@ key-wallet = { path = "../key-wallet" } key-wallet-manager = { path = "../key-wallet-manager" } # BLS signatures -blsful = "2.5" +blsful = { git = "https://github.com/dashpay/agora-blsful", rev = "be108b2cf6ac64eedbe04f91c63731533c8956bc" } # CLI clap = { version = "4.0", features = ["derive"] } From 590ec6475733c74302d9faeda56b0995cae1db1f Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 19 Aug 2025 09:19:42 +0700 Subject: [PATCH 4/4] cleanup --- key-wallet-manager/src/lib.rs | 1 - key-wallet-manager/src/spv_wallet_manager.rs | 1 - key-wallet-manager/src/wallet_manager/mod.rs | 33 +-- .../src/wallet_manager/process_block.rs | 1 - .../wallet_manager/transaction_building.rs | 1 - key-wallet-manager/tests/integration_test.rs | 2 +- .../transaction_checking/wallet_checker.rs | 1 + .../src/wallet/managed_wallet_info/mod.rs | 218 ------------------ .../transaction_building.rs | 6 +- .../wallet_info_interface.rs | 197 ++++++++++++++-- key-wallet/src/wallet/mod.rs | 3 +- 11 files changed, 193 insertions(+), 271 deletions(-) diff --git a/key-wallet-manager/src/lib.rs b/key-wallet-manager/src/lib.rs index e17aaf1ea..0bab98c64 100644 --- a/key-wallet-manager/src/lib.rs +++ b/key-wallet-manager/src/lib.rs @@ -22,7 +22,6 @@ extern crate alloc; extern crate std; pub mod spv_wallet_manager; -pub mod wallet_info_trait; pub mod wallet_interface; pub mod wallet_manager; diff --git a/key-wallet-manager/src/spv_wallet_manager.rs b/key-wallet-manager/src/spv_wallet_manager.rs index 155d4ae34..a4598757b 100644 --- a/key-wallet-manager/src/spv_wallet_manager.rs +++ b/key-wallet-manager/src/spv_wallet_manager.rs @@ -15,7 +15,6 @@ use dashcore::prelude::CoreBlockHeight; use dashcore::{BlockHash, Txid}; use key_wallet::Network; -use crate::wallet_info_trait::WalletInfoInterface; use crate::wallet_interface::WalletInterface; use crate::wallet_manager::{WalletId, WalletManager}; use key_wallet::transaction_checking::TransactionContext; diff --git a/key-wallet-manager/src/wallet_manager/mod.rs b/key-wallet-manager/src/wallet_manager/mod.rs index 017ca5a3c..6af736140 100644 --- a/key-wallet-manager/src/wallet_manager/mod.rs +++ b/key-wallet-manager/src/wallet_manager/mod.rs @@ -15,10 +15,9 @@ use dashcore::Txid; use key_wallet::wallet::managed_wallet_info::{ManagedWalletInfo, TransactionRecord}; use key_wallet::WalletBalance; use key_wallet::{Account, AccountType, Address, Mnemonic, Network, Wallet, WalletConfig}; +use std::collections::BTreeSet; -use crate::wallet_info_trait::WalletInfoInterface; - -use key_wallet::transaction_checking::{TransactionContext, WalletTransactionChecker}; +use key_wallet::transaction_checking::TransactionContext; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::{Utxo, UtxoSet}; @@ -301,24 +300,8 @@ impl WalletManager { for wallet_id in wallet_ids { // Check the transaction for this wallet if let Some(wallet_info) = self.wallet_infos.get_mut(&wallet_id) { - // Convert TransactionContext to the expected format - let context_tuple = match context { - TransactionContext::InBlock { - height, - .. - } - | TransactionContext::InChainLockedBlock { - height, - .. - } => Some((height, tx.txid())), - TransactionContext::Mempool => None, - }; - let result = wallet_info.check_transaction( - tx, - network, - context_tuple, - update_state_if_found, - ); + let result = + wallet_info.check_transaction(tx, network, context, update_state_if_found); // If the transaction is relevant if result.is_relevant { @@ -776,7 +759,7 @@ impl WalletManager { let managed_info = self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - Ok(managed_info.get_transaction_history()) + Ok(managed_info.transaction_history()) } /// Get UTXOs for all wallets across all networks @@ -789,13 +772,13 @@ impl WalletManager { } /// Get UTXOs for a specific wallet - pub fn get_wallet_utxos(&self, wallet_id: &WalletId) -> Result, WalletError> { + pub fn wallet_utxos(&self, wallet_id: &WalletId) -> Result, WalletError> { // Get the wallet info let wallet_info = self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; // Get UTXOs from the wallet info and clone them - let utxos = wallet_info.get_utxos(); + let utxos = wallet_info.utxos(); Ok(utxos) } @@ -812,7 +795,7 @@ impl WalletManager { self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; // Get balance from the wallet info - Ok(wallet_info.get_balance()) + Ok(wallet_info.balance()) } /// Update the cached balance for a specific wallet diff --git a/key-wallet-manager/src/wallet_manager/process_block.rs b/key-wallet-manager/src/wallet_manager/process_block.rs index d178ce94d..7381e7419 100644 --- a/key-wallet-manager/src/wallet_manager/process_block.rs +++ b/key-wallet-manager/src/wallet_manager/process_block.rs @@ -1,4 +1,3 @@ -use crate::wallet_info_trait::WalletInfoInterface; use crate::{Network, WalletManager}; use dashcore::bip158::BlockFilter; use dashcore::prelude::CoreBlockHeight; diff --git a/key-wallet-manager/src/wallet_manager/transaction_building.rs b/key-wallet-manager/src/wallet_manager/transaction_building.rs index cc7660597..7f6edf84f 100644 --- a/key-wallet-manager/src/wallet_manager/transaction_building.rs +++ b/key-wallet-manager/src/wallet_manager/transaction_building.rs @@ -1,7 +1,6 @@ //! Transaction building functionality for the wallet manager use super::{WalletError, WalletId, WalletManager}; -use crate::wallet_info_trait::WalletInfoInterface; use dashcore::Transaction; use key_wallet::wallet::managed_wallet_info::fee::FeeLevel; use key_wallet::wallet::managed_wallet_info::transaction_building::{ diff --git a/key-wallet-manager/tests/integration_test.rs b/key-wallet-manager/tests/integration_test.rs index eb695964f..5c301292d 100644 --- a/key-wallet-manager/tests/integration_test.rs +++ b/key-wallet-manager/tests/integration_test.rs @@ -140,7 +140,7 @@ fn test_utxo_management() { // The WalletManager doesn't have an add_utxo method directly // Instead, UTXOs are created by processing transactions - let utxos = manager.get_wallet_utxos(&wallet_id); + let utxos = manager.wallet_utxos(&wallet_id); assert!(utxos.is_ok()); // Initially empty assert_eq!(utxos.unwrap().len(), 0); diff --git a/key-wallet/src/transaction_checking/wallet_checker.rs b/key-wallet/src/transaction_checking/wallet_checker.rs index e2e19b7aa..c365a26b2 100644 --- a/key-wallet/src/transaction_checking/wallet_checker.rs +++ b/key-wallet/src/transaction_checking/wallet_checker.rs @@ -6,6 +6,7 @@ pub(crate) use super::account_checker::TransactionCheckResult; use super::transaction_router::TransactionRouter; use crate::wallet::immature_transaction::ImmatureTransaction; +use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use crate::wallet::managed_wallet_info::ManagedWalletInfo; use crate::Network; use dashcore::blockdata::transaction::Transaction; diff --git a/key-wallet/src/wallet/managed_wallet_info/mod.rs b/key-wallet/src/wallet/managed_wallet_info/mod.rs index 5ac0ef230..ecb6e155f 100644 --- a/key-wallet/src/wallet/managed_wallet_info/mod.rs +++ b/key-wallet/src/wallet/managed_wallet_info/mod.rs @@ -91,68 +91,11 @@ impl ManagedWalletInfo { info } - /// Set the wallet name - pub fn set_name(&mut self, name: String) { - self.name = Some(name); - } - - /// Set the wallet description - pub fn set_description(&mut self, description: String) { - self.description = Some(description); - } - - /// Update the last synced timestamp - pub fn update_last_synced(&mut self, timestamp: u64) { - self.metadata.last_synced = Some(timestamp); - } - /// Increment the transaction count pub fn increment_transactions(&mut self) { self.metadata.total_transactions += 1; } - /// Get a managed account by network and index - pub fn get_account(&self, network: Network, index: u32) -> Option<&ManagedAccount> { - self.accounts.get(&network).and_then(|collection| collection.get(index)) - } - - /// Get a mutable managed account by network and index - pub fn get_account_mut(&mut self, network: Network, index: u32) -> Option<&mut ManagedAccount> { - self.accounts.get_mut(&network).and_then(|collection| collection.get_mut(index)) - } - - /// Update the cached wallet balance by summing all accounts - pub fn update_balance(&mut self) { - let mut confirmed = 0u64; - let mut unconfirmed = 0u64; - let mut locked = 0u64; - - // Sum balances from all accounts across all networks - for collection in self.accounts.values() { - for account in collection.all_accounts() { - for utxo in account.utxos.values() { - let value = utxo.txout.value; - if utxo.is_locked { - locked += value; - } else if utxo.is_confirmed { - confirmed += value; - } else { - unconfirmed += value; - } - } - } - } - - // Update balance, ignoring overflow errors as we're recalculating from scratch - self.balance = WalletBalance::new(confirmed, unconfirmed, locked) - .unwrap_or_else(|_| WalletBalance::default()); - } - - /// Get the cached wallet balance - pub fn get_balance(&self) -> WalletBalance { - self.balance - } - /// Get total wallet balance by recalculating from all accounts (for verification) pub fn calculate_balance(&self) -> WalletBalance { let mut confirmed = 0u64; @@ -178,167 +121,6 @@ impl ManagedWalletInfo { WalletBalance::new(confirmed, unconfirmed, locked) .unwrap_or_else(|_| WalletBalance::default()) } - - /// Get all transaction history across all accounts - pub fn get_transaction_history(&self) -> Vec<&TransactionRecord> { - let mut transactions = Vec::new(); - - // Collect transactions from all accounts across all networks - for collection in self.accounts.values() { - for account in collection.all_accounts() { - transactions.extend(account.transactions.values()); - } - } - - transactions - } - - /// Get all UTXOs across all accounts - pub fn get_utxos(&self) -> BTreeSet<&Utxo> { - let mut utxos = BTreeSet::new(); - - // Collect UTXOs from all accounts across all networks - for collection in self.accounts.values() { - for account in collection.all_accounts() { - utxos.extend(account.utxos.values()); - } - } - - utxos - } - - /// Get spendable UTXOs (confirmed and not locked) - pub fn get_spendable_utxos(&self) -> Vec<&Utxo> { - self.get_utxos() - .into_iter() - .filter(|utxo| !utxo.is_locked && (utxo.is_confirmed || utxo.is_instantlocked)) - .collect() - } - - /// Add an immature transaction - pub fn add_immature_transaction( - &mut self, - network: Network, - tx: super::immature_transaction::ImmatureTransaction, - ) { - self.immature_transactions - .entry(network) - .or_insert_with(ImmatureTransactionCollection::new) - .insert(tx); - } - - /// Process matured transactions for a given chain height - pub fn process_matured_transactions( - &mut self, - network: Network, - current_height: u32, - ) -> Vec { - if let Some(collection) = self.immature_transactions.get_mut(&network) { - let matured = collection.remove_matured(current_height); - - // Update accounts with matured transactions - if let Some(account_collection) = self.accounts.get_mut(&network) { - for tx in &matured { - // Process BIP44 accounts - for &index in &tx.affected_accounts.bip44_accounts { - if let Some(account) = - account_collection.standard_bip44_accounts.get_mut(&index) - { - // Add transaction record as confirmed - let tx_record = crate::account::TransactionRecord::new_confirmed( - tx.transaction.clone(), - tx.height, - tx.block_hash, - tx.timestamp, - tx.total_received as i64, - false, // Not ours (we received) - ); - account.transactions.insert(tx.txid, tx_record); - } - } - - // Process BIP32 accounts - for &index in &tx.affected_accounts.bip32_accounts { - if let Some(account) = - account_collection.standard_bip32_accounts.get_mut(&index) - { - let tx_record = crate::account::TransactionRecord::new_confirmed( - tx.transaction.clone(), - tx.height, - tx.block_hash, - tx.timestamp, - tx.total_received as i64, - false, - ); - account.transactions.insert(tx.txid, tx_record); - } - } - - // Process CoinJoin accounts - for &index in &tx.affected_accounts.coinjoin_accounts { - if let Some(account) = account_collection.coinjoin_accounts.get_mut(&index) - { - let tx_record = crate::account::TransactionRecord::new_confirmed( - tx.transaction.clone(), - tx.height, - tx.block_hash, - tx.timestamp, - tx.total_received as i64, - false, - ); - account.transactions.insert(tx.txid, tx_record); - } - } - } - } - - // Update balance after processing matured transactions - self.update_balance(); - - matured - } else { - Vec::new() - } - } - - /// Get immature transactions for a network - pub fn get_immature_transactions( - &self, - network: Network, - ) -> Option<&ImmatureTransactionCollection> { - self.immature_transactions.get(&network) - } - - /// Get total immature balance across all networks - pub fn total_immature_balance(&self) -> u64 { - self.immature_transactions - .values() - .map(|collection| collection.total_immature_balance()) - .sum() - } - - /// Get immature balance for a specific network - pub fn network_immature_balance(&self, network: Network) -> u64 { - self.immature_transactions - .get(&network) - .map(|collection| collection.total_immature_balance()) - .unwrap_or(0) - } - - /// Get monitored addresses for a specific network - /// These are automatically collected from all accounts in the network - pub fn monitored_addresses(&self, network: Network) -> Vec
{ - let mut addresses = Vec::new(); - - if let Some(collection) = self.accounts.get(&network) { - // Collect from all accounts using the account's get_all_addresses method - for account in collection.all_accounts() { - addresses.extend(account.get_all_addresses()); - } - } - - addresses - } } /// Re-export types from account module for convenience diff --git a/key-wallet/src/wallet/managed_wallet_info/transaction_building.rs b/key-wallet/src/wallet/managed_wallet_info/transaction_building.rs index 8b6a03a8e..f8e0ebce4 100644 --- a/key-wallet/src/wallet/managed_wallet_info/transaction_building.rs +++ b/key-wallet/src/wallet/managed_wallet_info/transaction_building.rs @@ -39,7 +39,7 @@ pub enum TransactionError { impl ManagedWalletInfo { /// Create an unsigned payment transaction #[allow(clippy::too_many_arguments)] - pub fn create_unsigned_payment_transaction( + pub(crate) fn create_unsigned_payment_transaction_internal( &mut self, wallet: &Wallet, network: Network, @@ -169,15 +169,11 @@ impl ManagedWalletInfo { mod tests { use super::*; use crate::wallet::managed_wallet_info::transaction_builder::TransactionBuilder; - use crate::wallet::Wallet; use crate::Utxo; use dashcore::blockdata::script::ScriptBuf; - use dashcore::blockdata::transaction::special_transaction::asset_lock::AssetLockPayload; use dashcore::blockdata::transaction::special_transaction::TransactionPayload; use dashcore::{Address, Network, OutPoint, Transaction, TxOut, Txid}; use dashcore_hashes::{sha256d, Hash}; - use secp256k1::SecretKey; - use std::collections::BTreeMap; use std::str::FromStr; fn test_utxo(value: u64, confirmed: bool) -> Utxo { diff --git a/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs b/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs index ac7519073..b28590d05 100644 --- a/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs +++ b/key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs @@ -4,15 +4,16 @@ use crate::account::managed_account_collection::ManagedAccountCollection; use crate::transaction_checking::WalletTransactionChecker; +use crate::wallet::immature_transaction::{ImmatureTransaction, ImmatureTransactionCollection}; use crate::wallet::managed_wallet_info::fee::FeeLevel; use crate::wallet::managed_wallet_info::transaction_building::{ AccountTypePreference, TransactionError, }; use crate::wallet::managed_wallet_info::TransactionRecord; use crate::wallet::ManagedWalletInfo; -use crate::{Address, Network, Utxo, Wallet, WalletBalance}; -use dashcore::blockdata::transaction::Transaction; -use dashcore::Address as DashAddress; +use crate::{Network, Utxo, Wallet, WalletBalance}; +use dashcore::{Address as DashAddress, Address, Transaction}; +use std::collections::BTreeSet; /// Trait that wallet info types must implement to work with WalletManager pub trait WalletInfoInterface: Sized + WalletTransactionChecker { @@ -52,16 +53,19 @@ pub trait WalletInfoInterface: Sized + WalletTransactionChecker { fn monitored_addresses(&self, network: Network) -> Vec; /// Get all UTXOs for the wallet - fn get_utxos(&self) -> Vec; + fn utxos(&self) -> BTreeSet<&Utxo>; + + /// Get spendable UTXOs (confirmed and not locked) + fn get_spendable_utxos(&self) -> BTreeSet<&Utxo>; /// Get the wallet balance - fn get_balance(&self) -> WalletBalance; + fn balance(&self) -> WalletBalance; /// Update the wallet balance fn update_balance(&mut self); /// Get transaction history - fn get_transaction_history(&self) -> Vec<&TransactionRecord>; + fn transaction_history(&self) -> Vec<&TransactionRecord>; /// Get accounts for a network (mutable) fn accounts_mut(&mut self, network: Network) -> Option<&mut ManagedAccountCollection>; @@ -69,7 +73,22 @@ pub trait WalletInfoInterface: Sized + WalletTransactionChecker { /// Get accounts for a network (immutable) fn accounts(&self, network: Network) -> Option<&ManagedAccountCollection>; - /// Create an unsigned payment transaction + /// Process matured transactions for a given chain height + fn process_matured_transactions( + &mut self, + network: Network, + current_height: u32, + ) -> Vec; + + /// Add an immature transaction + fn add_immature_transaction(&mut self, network: Network, tx: ImmatureTransaction); + /// Get immature transactions for a network + fn immature_transactions(&self, network: Network) -> Option<&ImmatureTransactionCollection>; + + /// Get immature balance for a specific network + fn network_immature_balance(&self, network: Network) -> u64; + + /// Get immature balance for a specific network #[allow(clippy::too_many_arguments)] fn create_unsigned_payment_transaction( &mut self, @@ -125,27 +144,82 @@ impl WalletInfoInterface for ManagedWalletInfo { } fn update_last_synced(&mut self, timestamp: u64) { - self.update_last_synced(timestamp); + self.metadata.last_synced = Some(timestamp); } fn monitored_addresses(&self, network: Network) -> Vec { - self.monitored_addresses(network).into_iter().collect() + let mut addresses = Vec::new(); + + if let Some(collection) = self.accounts.get(&network) { + // Collect from all accounts using the account's get_all_addresses method + for account in collection.all_accounts() { + addresses.extend(account.get_all_addresses()); + } + } + + addresses } - fn get_utxos(&self) -> Vec { - self.get_utxos().into_iter().cloned().collect() + fn utxos(&self) -> BTreeSet<&Utxo> { + let mut utxos = BTreeSet::new(); + + // Collect UTXOs from all accounts across all networks + for collection in self.accounts.values() { + for account in collection.all_accounts() { + utxos.extend(account.utxos.values()); + } + } + + utxos + } + fn get_spendable_utxos(&self) -> BTreeSet<&Utxo> { + self.utxos() + .into_iter() + .filter(|utxo| !utxo.is_locked && (utxo.is_confirmed || utxo.is_instantlocked)) + .collect() } - fn get_balance(&self) -> WalletBalance { - self.get_balance() + fn balance(&self) -> WalletBalance { + self.balance } fn update_balance(&mut self) { - self.update_balance(); + let mut confirmed = 0u64; + let mut unconfirmed = 0u64; + let mut locked = 0u64; + + // Sum balances from all accounts across all networks + for collection in self.accounts.values() { + for account in collection.all_accounts() { + for utxo in account.utxos.values() { + let value = utxo.txout.value; + if utxo.is_locked { + locked += value; + } else if utxo.is_confirmed { + confirmed += value; + } else { + unconfirmed += value; + } + } + } + } + + // Update balance, ignoring overflow errors as we're recalculating from scratch + self.balance = WalletBalance::new(confirmed, unconfirmed, locked) + .unwrap_or_else(|_| WalletBalance::default()); } - fn get_transaction_history(&self) -> Vec<&TransactionRecord> { - self.get_transaction_history() + fn transaction_history(&self) -> Vec<&TransactionRecord> { + let mut transactions = Vec::new(); + + // Collect transactions from all accounts across all networks + for collection in self.accounts.values() { + for account in collection.all_accounts() { + transactions.extend(account.transactions.values()); + } + } + + transactions } fn accounts_mut(&mut self, network: Network) -> Option<&mut ManagedAccountCollection> { @@ -156,6 +230,95 @@ impl WalletInfoInterface for ManagedWalletInfo { self.accounts.get(&network) } + fn process_matured_transactions( + &mut self, + network: Network, + current_height: u32, + ) -> Vec { + if let Some(collection) = self.immature_transactions.get_mut(&network) { + let matured = collection.remove_matured(current_height); + + // Update accounts with matured transactions + if let Some(account_collection) = self.accounts.get_mut(&network) { + for tx in &matured { + // Process BIP44 accounts + for &index in &tx.affected_accounts.bip44_accounts { + if let Some(account) = + account_collection.standard_bip44_accounts.get_mut(&index) + { + // Add transaction record as confirmed + let tx_record = TransactionRecord::new_confirmed( + tx.transaction.clone(), + tx.height, + tx.block_hash, + tx.timestamp, + tx.total_received as i64, + false, // Not ours (we received) + ); + account.transactions.insert(tx.txid, tx_record); + } + } + + // Process BIP32 accounts + for &index in &tx.affected_accounts.bip32_accounts { + if let Some(account) = + account_collection.standard_bip32_accounts.get_mut(&index) + { + let tx_record = TransactionRecord::new_confirmed( + tx.transaction.clone(), + tx.height, + tx.block_hash, + tx.timestamp, + tx.total_received as i64, + false, + ); + account.transactions.insert(tx.txid, tx_record); + } + } + + // Process CoinJoin accounts + for &index in &tx.affected_accounts.coinjoin_accounts { + if let Some(account) = account_collection.coinjoin_accounts.get_mut(&index) + { + let tx_record = TransactionRecord::new_confirmed( + tx.transaction.clone(), + tx.height, + tx.block_hash, + tx.timestamp, + tx.total_received as i64, + false, + ); + account.transactions.insert(tx.txid, tx_record); + } + } + } + } + + // Update balance after processing matured transactions + self.update_balance(); + + matured + } else { + Vec::new() + } + } + + /// Add an immature transaction + fn add_immature_transaction(&mut self, network: Network, tx: ImmatureTransaction) { + self.immature_transactions.entry(network).or_default().insert(tx); + } + + fn immature_transactions(&self, network: Network) -> Option<&ImmatureTransactionCollection> { + self.immature_transactions.get(&network) + } + + fn network_immature_balance(&self, network: Network) -> u64 { + self.immature_transactions + .get(&network) + .map(|collection| collection.total_immature_balance()) + .unwrap_or(0) + } + fn create_unsigned_payment_transaction( &mut self, wallet: &Wallet, @@ -166,7 +329,7 @@ impl WalletInfoInterface for ManagedWalletInfo { fee_level: FeeLevel, current_block_height: u32, ) -> Result { - self.create_unsigned_payment_transaction( + self.create_unsigned_payment_transaction_internal( wallet, network, account_index, diff --git a/key-wallet/src/wallet/mod.rs b/key-wallet/src/wallet/mod.rs index 8ba47c4fb..4cabaaf2f 100644 --- a/key-wallet/src/wallet/mod.rs +++ b/key-wallet/src/wallet/mod.rs @@ -132,6 +132,7 @@ mod tests { use super::*; use crate::account::{AccountType, StandardAccountType}; use crate::mnemonic::Language; + use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; #[test] fn test_wallet_creation() { @@ -394,7 +395,7 @@ mod tests { // Create managed info from the wallet let mut managed_info = ManagedWalletInfo::from_wallet(&wallet); managed_info.set_name("Test Wallet".to_string()); - managed_info.set_description("A test wallet".to_string()); + managed_info.set_description(Some("A test wallet".to_string())); // Test initial managed info assert_eq!(managed_info.wallet_id, wallet.wallet_id);