From f92f114b83f6e442af8290611a10f2246ee58d3a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Fri, 27 Mar 2026 16:36:29 +0300 Subject: [PATCH] refactor: extract key-wallet-manager into separate crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the manager module from key-wallet into its own key-wallet-manager crate to enforce a clean dependency boundary between wallet primitives and the async wallet runtime (tokio). This solves the problem where Cargo's feature unification caused tokio to leak into WASM and other lightweight consumers through the default "manager" feature flag — a fragile boundary that relied on every consumer remembering default-features = false. With separate crates, key-wallet is tokio-free by construction. Consumers that need WalletManager, WalletInterface, or event support add key-wallet-manager as an explicit dependency. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/ci-groups.yml | 1 + Cargo.toml | 2 +- dash-spv-ffi/Cargo.toml | 1 + dash-spv-ffi/src/callbacks.rs | 2 +- dash-spv-ffi/src/client.rs | 4 +- dash-spv-ffi/tests/test_wallet_manager.rs | 2 +- dash-spv/Cargo.toml | 4 +- dash-spv/examples/filter_sync.rs | 2 +- dash-spv/examples/simple_sync.rs | 2 +- dash-spv/examples/spv_with_wallet.rs | 2 +- dash-spv/src/client/core.rs | 2 +- dash-spv/src/client/event_handler.rs | 4 +- dash-spv/src/client/events.rs | 2 +- dash-spv/src/client/lifecycle.rs | 2 +- dash-spv/src/client/mempool.rs | 2 +- dash-spv/src/client/mod.rs | 2 +- dash-spv/src/client/queries.rs | 2 +- dash-spv/src/client/sync_coordinator.rs | 2 +- dash-spv/src/client/transactions.rs | 2 +- dash-spv/src/lib.rs | 2 +- dash-spv/src/main.rs | 2 +- dash-spv/src/sync/blocks/manager.rs | 6 +- dash-spv/src/sync/blocks/pipeline.rs | 2 +- dash-spv/src/sync/blocks/sync_manager.rs | 2 +- dash-spv/src/sync/events.rs | 2 +- dash-spv/src/sync/filters/batch.rs | 4 +- dash-spv/src/sync/filters/batch_tracker.rs | 2 +- dash-spv/src/sync/filters/manager.rs | 6 +- dash-spv/src/sync/filters/pipeline.rs | 2 +- dash-spv/src/sync/filters/sync_manager.rs | 2 +- dash-spv/src/sync/mempool/manager.rs | 4 +- dash-spv/src/sync/mempool/sync_manager.rs | 4 +- dash-spv/src/sync/sync_coordinator.rs | 2 +- dash-spv/src/test_utils/event_handler.rs | 2 +- dash-spv/src/validation/filter.rs | 4 +- dash-spv/tests/dashd_sync/helpers.rs | 4 +- dash-spv/tests/dashd_sync/setup.rs | 4 +- dash-spv/tests/dashd_sync/tests_basic.rs | 2 +- dash-spv/tests/peer_test.rs | 2 +- dash-spv/tests/wallet_integration_test.rs | 2 +- fuzz/Cargo.toml | 2 +- key-wallet-ffi/Cargo.toml | 8 +- key-wallet-ffi/src/error.rs | 6 +- key-wallet-ffi/src/wallet_manager.rs | 8 +- key-wallet-ffi/src/wallet_manager_tests.rs | 2 +- .../tests/test_error_conversions.rs | 6 +- key-wallet-manager/Cargo.toml | 27 ++ key-wallet-manager/src/accessors.rs | 213 +++++++++++ key-wallet-manager/src/error.rs | 99 +++++ .../src}/event_tests.rs | 4 +- .../src}/events.rs | 4 +- .../mod.rs => key-wallet-manager/src/lib.rs | 353 ++---------------- .../src}/matching.rs | 2 +- .../src}/process_block.rs | 24 +- .../src}/test_helpers.rs | 6 +- .../src/test_utils/mock_wallet.rs | 6 +- key-wallet-manager/src/test_utils/mod.rs | 4 + .../src}/wallet_interface.rs | 2 +- key-wallet/Cargo.toml | 8 +- key-wallet/examples/wallet_creation.rs | 4 +- key-wallet/src/lib.rs | 2 - key-wallet/src/test_utils/mod.rs | 6 - key-wallet/tests/integration_test.rs | 4 +- key-wallet/tests/spv_integration_tests.rs | 4 +- key-wallet/tests/test_serialized_wallets.rs | 2 +- 65 files changed, 474 insertions(+), 436 deletions(-) create mode 100644 key-wallet-manager/Cargo.toml create mode 100644 key-wallet-manager/src/accessors.rs create mode 100644 key-wallet-manager/src/error.rs rename {key-wallet/src/manager => key-wallet-manager/src}/event_tests.rs (99%) rename {key-wallet/src/manager => key-wallet-manager/src}/events.rs (97%) rename key-wallet/src/manager/mod.rs => key-wallet-manager/src/lib.rs (74%) rename {key-wallet/src/manager => key-wallet-manager/src}/matching.rs (99%) rename {key-wallet/src/manager => key-wallet-manager/src}/process_block.rs (95%) rename {key-wallet/src/manager => key-wallet-manager/src}/test_helpers.rs (96%) rename {key-wallet => key-wallet-manager}/src/test_utils/mock_wallet.rs (97%) create mode 100644 key-wallet-manager/src/test_utils/mod.rs rename {key-wallet/src/manager => key-wallet-manager/src}/wallet_interface.rs (99%) diff --git a/.github/ci-groups.yml b/.github/ci-groups.yml index 03f114508..e996ce208 100644 --- a/.github/ci-groups.yml +++ b/.github/ci-groups.yml @@ -12,6 +12,7 @@ groups: wallet: - key-wallet + - key-wallet-manager ffi: - dash-spv-ffi diff --git a/Cargo.toml b/Cargo.toml index e899d83f4..7be8607fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["dash", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-ffi", "dash-spv", "dash-spv-ffi"] +members = ["dash", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-manager", "key-wallet-ffi", "dash-spv", "dash-spv-ffi"] resolver = "2" [workspace.package] diff --git a/dash-spv-ffi/Cargo.toml b/dash-spv-ffi/Cargo.toml index 17c2687b2..da11bea58 100644 --- a/dash-spv-ffi/Cargo.toml +++ b/dash-spv-ffi/Cargo.toml @@ -28,6 +28,7 @@ tracing = "0.1" key-wallet-ffi = { path = "../key-wallet-ffi" } # Still need these for SPV client internals (not for FFI types) key-wallet = { path = "../key-wallet" } +key-wallet-manager = { path = "../key-wallet-manager" } rand = "0.8" clap = { version = "4.5", features = ["derive"] } diff --git a/dash-spv-ffi/src/callbacks.rs b/dash-spv-ffi/src/callbacks.rs index 6422463e3..cbff4419e 100644 --- a/dash-spv-ffi/src/callbacks.rs +++ b/dash-spv-ffi/src/callbacks.rs @@ -11,8 +11,8 @@ use dash_spv::network::NetworkEvent; use dash_spv::sync::{SyncEvent, SyncProgress}; use dash_spv::EventHandler; use dashcore::hashes::Hash; -use key_wallet::manager::WalletEvent; use key_wallet_ffi::types::FFITransactionContext; +use key_wallet_manager::WalletEvent; use std::ffi::CString; use std::os::raw::{c_char, c_void}; diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 02a60515b..b588a5bb7 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -17,7 +17,7 @@ use tokio_util::sync::CancellationToken; /// FFI wrapper around `DashSpvClient`. type InnerClient = DashSpvClient< - key_wallet::manager::WalletManager, + key_wallet_manager::WalletManager, dash_spv::network::PeerNetworkManager, DiskStorageManager, FFIEventCallbacks, @@ -78,7 +78,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new( // Construct concrete implementations for generics let network = dash_spv::network::PeerNetworkManager::new(&client_config).await; let storage = DiskStorageManager::new(&client_config).await; - let wallet = key_wallet::manager::WalletManager::< + let wallet = key_wallet_manager::WalletManager::< key_wallet::wallet::managed_wallet_info::ManagedWalletInfo, >::new(client_config.network); let wallet = std::sync::Arc::new(tokio::sync::RwLock::new(wallet)); diff --git a/dash-spv-ffi/tests/test_wallet_manager.rs b/dash-spv-ffi/tests/test_wallet_manager.rs index 15f5df2fd..5c4d6710f 100644 --- a/dash-spv-ffi/tests/test_wallet_manager.rs +++ b/dash-spv-ffi/tests/test_wallet_manager.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; - use key_wallet::manager::WalletManager; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_ffi::{ @@ -11,6 +10,7 @@ mod tests { }, FFIError, FFINetwork, FFIWalletManager, }; + use key_wallet_manager::WalletManager; use std::ffi::{CStr, CString}; use tempfile::TempDir; diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index c05f46999..5a468b02c 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -12,7 +12,8 @@ rust-version = "1.89" # Core Dash libraries dashcore = { path = "../dash", features = ["serde", "core-block-hash-use-x11", "message_verification", "bls", "quorum_validation"] } dashcore_hashes = { path = "../hashes" } -key-wallet = { path = "../key-wallet", features = ["parallel-filters"] } +key-wallet = { path = "../key-wallet" } +key-wallet-manager = { path = "../key-wallet-manager", features = ["parallel-filters"] } # BLS signatures blsful = { git = "https://github.com/dashpay/agora-blsful", rev = "0c34a7a488a0bd1c9a9a2196e793b303ad35c900" } @@ -64,6 +65,7 @@ log = "0.4" dash-spv = { path = ".", features = ["test-utils"] } dashcore = { path = "../dash", features = ["test-utils"] } key-wallet = { path = "../key-wallet", features = ["test-utils"] } +key-wallet-manager = { path = "../key-wallet-manager", features = ["test-utils"] } criterion = { version = "0.8.1", features = ["async_tokio"] } tempfile = "3.0" tokio-test = "0.4" diff --git a/dash-spv/examples/filter_sync.rs b/dash-spv/examples/filter_sync.rs index 07e0840e9..22a78b418 100644 --- a/dash-spv/examples/filter_sync.rs +++ b/dash-spv/examples/filter_sync.rs @@ -4,8 +4,8 @@ use dash_spv::network::PeerNetworkManager; use dash_spv::storage::DiskStorageManager; use dash_spv::{init_console_logging, ClientConfig, DashSpvClient, LevelFilter}; use dashcore::Address; -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletManager; use std::str::FromStr; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/dash-spv/examples/simple_sync.rs b/dash-spv/examples/simple_sync.rs index 33344a824..2f162dc15 100644 --- a/dash-spv/examples/simple_sync.rs +++ b/dash-spv/examples/simple_sync.rs @@ -5,7 +5,7 @@ use dash_spv::storage::DiskStorageManager; use dash_spv::{init_console_logging, ClientConfig, DashSpvClient, LevelFilter}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet::manager::WalletManager; +use key_wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; use tokio_util::sync::CancellationToken; diff --git a/dash-spv/examples/spv_with_wallet.rs b/dash-spv/examples/spv_with_wallet.rs index 5c21534a3..d8f913089 100644 --- a/dash-spv/examples/spv_with_wallet.rs +++ b/dash-spv/examples/spv_with_wallet.rs @@ -5,8 +5,8 @@ use dash_spv::network::PeerNetworkManager; use dash_spv::storage::DiskStorageManager; use dash_spv::{ClientConfig, DashSpvClient, LevelFilter}; -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; use tokio_util::sync::CancellationToken; diff --git a/dash-spv/src/client/core.rs b/dash-spv/src/client/core.rs index 746ffd5f1..9a046b4c4 100644 --- a/dash-spv/src/client/core.rs +++ b/dash-spv/src/client/core.rs @@ -23,7 +23,7 @@ use crate::storage::{ }; use crate::sync::SyncCoordinator; use crate::types::MempoolState; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; pub(super) type PersistentSyncCoordinator = SyncCoordinator< PersistentBlockHeaderStorage, diff --git a/dash-spv/src/client/event_handler.rs b/dash-spv/src/client/event_handler.rs index b3c3026df..b2b42c3c3 100644 --- a/dash-spv/src/client/event_handler.rs +++ b/dash-spv/src/client/event_handler.rs @@ -12,7 +12,7 @@ use tokio_util::sync::CancellationToken; use crate::network::NetworkEvent; use crate::sync::{SyncEvent, SyncProgress}; -use key_wallet::manager::WalletEvent; +use key_wallet_manager::WalletEvent; /// Trait for receiving SPV client events. /// @@ -105,7 +105,7 @@ mod tests { use super::{spawn_broadcast_monitor, spawn_progress_monitor, EventHandler}; use crate::network::NetworkEvent; use crate::sync::{ManagerIdentifier, SyncEvent, SyncProgress}; - use key_wallet::manager::WalletEvent; + use key_wallet_manager::WalletEvent; struct RecordingHandler { sync_count: AtomicUsize, diff --git a/dash-spv/src/client/events.rs b/dash-spv/src/client/events.rs index 073d3dfef..62881860c 100644 --- a/dash-spv/src/client/events.rs +++ b/dash-spv/src/client/events.rs @@ -9,7 +9,7 @@ use tokio::sync::watch; use crate::network::{NetworkEvent, NetworkManager}; use crate::storage::StorageManager; use crate::sync::{SyncEvent, SyncProgress}; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; use tokio::sync::broadcast; use super::{DashSpvClient, EventHandler}; diff --git a/dash-spv/src/client/lifecycle.rs b/dash-spv/src/client/lifecycle.rs index 5eade3316..739ca8a43 100644 --- a/dash-spv/src/client/lifecycle.rs +++ b/dash-spv/src/client/lifecycle.rs @@ -28,7 +28,7 @@ use crate::sync::{ use crate::types::MempoolState; use dashcore::sml::masternode_list_engine::MasternodeListEngine; use dashcore_hashes::Hash; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; impl DashSpvClient diff --git a/dash-spv/src/client/mempool.rs b/dash-spv/src/client/mempool.rs index 694930152..d15a9e67e 100644 --- a/dash-spv/src/client/mempool.rs +++ b/dash-spv/src/client/mempool.rs @@ -13,7 +13,7 @@ use crate::error::Result; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; use super::{DashSpvClient, EventHandler}; diff --git a/dash-spv/src/client/mod.rs b/dash-spv/src/client/mod.rs index 53d857023..aac6deed3 100644 --- a/dash-spv/src/client/mod.rs +++ b/dash-spv/src/client/mod.rs @@ -52,8 +52,8 @@ mod tests { use crate::storage::DiskStorageManager; use crate::{test_utils::MockNetworkManager, types::UnconfirmedTransaction}; use dashcore::{Address, Amount, Transaction, TxOut}; - use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; + use key_wallet_manager::WalletManager; use std::sync::Arc; use tempfile::TempDir; use tokio::sync::RwLock; diff --git a/dash-spv/src/client/queries.rs b/dash-spv/src/client/queries.rs index 7f8749260..c89fc86e9 100644 --- a/dash-spv/src/client/queries.rs +++ b/dash-spv/src/client/queries.rs @@ -13,7 +13,7 @@ use dashcore::sml::llmq_type::LLMQType; use dashcore::sml::masternode_list_engine::MasternodeListEngine; use dashcore::sml::quorum_entry::qualified_quorum_entry::QualifiedQuorumEntry; use dashcore::QuorumHash; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index 8a99fb840..b4257a943 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -11,7 +11,7 @@ use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::sync::SyncProgress; use crate::SpvError; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; const SYNC_COORDINATOR_TICK_MS: Duration = Duration::from_millis(100); diff --git a/dash-spv/src/client/transactions.rs b/dash-spv/src/client/transactions.rs index 3fbf68b8a..6b7d363c6 100644 --- a/dash-spv/src/client/transactions.rs +++ b/dash-spv/src/client/transactions.rs @@ -4,7 +4,7 @@ use crate::error::{Result, SpvError}; use crate::network::NetworkManager; use crate::storage::StorageManager; use dashcore::network::message::NetworkMessage; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; use super::{DashSpvClient, EventHandler}; diff --git a/dash-spv/src/lib.rs b/dash-spv/src/lib.rs index 5fb3d392a..17644bc08 100644 --- a/dash-spv/src/lib.rs +++ b/dash-spv/src/lib.rs @@ -17,7 +17,7 @@ //! use dash_spv::storage::DiskStorageManager; //! use dashcore::Network; //! use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -//! use key_wallet::manager::WalletManager; +//! use key_wallet_manager::WalletManager; //! use std::sync::Arc; //! use tokio::sync::RwLock; //! use tokio_util::sync::CancellationToken; diff --git a/dash-spv/src/main.rs b/dash-spv/src/main.rs index ec01c3002..1d3ca097d 100644 --- a/dash-spv/src/main.rs +++ b/dash-spv/src/main.rs @@ -9,8 +9,8 @@ use clap::{Arg, Command}; use dash_spv::network::NetworkEvent; use dash_spv::sync::{SyncEvent, SyncProgress}; use dash_spv::{ClientConfig, DashSpvClient, EventHandler, LevelFilter, MempoolStrategy, Network}; -use key_wallet::manager::{WalletEvent, WalletManager}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::{WalletEvent, WalletManager}; use tokio_util::sync::CancellationToken; /// Logs all SPV client events via tracing. diff --git a/dash-spv/src/sync/blocks/manager.rs b/dash-spv/src/sync/blocks/manager.rs index bf289057b..8ea4e2f59 100644 --- a/dash-spv/src/sync/blocks/manager.rs +++ b/dash-spv/src/sync/blocks/manager.rs @@ -12,7 +12,7 @@ use crate::error::SyncResult; use crate::network::RequestSender; use crate::storage::{BlockHeaderStorage, BlockStorage}; use crate::sync::{BlocksProgress, SyncEvent, SyncManager, SyncState}; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; /// Blocks manager for downloading and processing matching blocks. /// @@ -168,8 +168,8 @@ mod tests { }; use crate::sync::{ManagerIdentifier, SyncEvent, SyncManagerProgress}; use crate::test_utils::MockNetworkManager; - use key_wallet::manager::FilterMatchKey; - use key_wallet::test_utils::MockWallet; + use key_wallet_manager::test_utils::MockWallet; + use key_wallet_manager::FilterMatchKey; use std::collections::BTreeSet; type TestBlocksManager = diff --git a/dash-spv/src/sync/blocks/pipeline.rs b/dash-spv/src/sync/blocks/pipeline.rs index a431f1032..5daf0962e 100644 --- a/dash-spv/src/sync/blocks/pipeline.rs +++ b/dash-spv/src/sync/blocks/pipeline.rs @@ -11,7 +11,7 @@ use crate::network::RequestSender; use crate::sync::download_coordinator::{DownloadConfig, DownloadCoordinator}; use dashcore::blockdata::block::Block; use dashcore::BlockHash; -use key_wallet::manager::FilterMatchKey; +use key_wallet_manager::FilterMatchKey; /// Maximum number of concurrent block downloads. const MAX_CONCURRENT_BLOCK_DOWNLOADS: usize = 20; diff --git a/dash-spv/src/sync/blocks/sync_manager.rs b/dash-spv/src/sync/blocks/sync_manager.rs index beeeb279a..faf8ce9b0 100644 --- a/dash-spv/src/sync/blocks/sync_manager.rs +++ b/dash-spv/src/sync/blocks/sync_manager.rs @@ -10,7 +10,7 @@ use crate::types::HashedBlock; use crate::SyncError; use async_trait::async_trait; use dashcore::network::message::NetworkMessage; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; #[async_trait] impl SyncManager diff --git a/dash-spv/src/sync/events.rs b/dash-spv/src/sync/events.rs index a7f55a50c..a5ed9734d 100644 --- a/dash-spv/src/sync/events.rs +++ b/dash-spv/src/sync/events.rs @@ -2,7 +2,7 @@ use crate::sync::ManagerIdentifier; use dashcore::ephemerealdata::chain_lock::ChainLock; use dashcore::ephemerealdata::instant_lock::InstantLock; use dashcore::{Address, BlockHash, Txid}; -use key_wallet::manager::FilterMatchKey; +use key_wallet_manager::FilterMatchKey; use std::collections::BTreeSet; /// Events that managers can emit and subscribe to. diff --git a/dash-spv/src/sync/filters/batch.rs b/dash-spv/src/sync/filters/batch.rs index 202d169d9..75ed22025 100644 --- a/dash-spv/src/sync/filters/batch.rs +++ b/dash-spv/src/sync/filters/batch.rs @@ -1,6 +1,6 @@ use dashcore::bip158::BlockFilter; use dashcore::Address; -use key_wallet::manager::FilterMatchKey; +use key_wallet_manager::FilterMatchKey; use std::collections::{HashMap, HashSet}; /// A completed batch of compact block filters ready for verification. @@ -134,7 +134,7 @@ mod tests { use crate::sync::filters::batch::FiltersBatch; use dashcore::bip158::BlockFilter; use dashcore::Header; - use key_wallet::manager::FilterMatchKey; + use key_wallet_manager::FilterMatchKey; use std::collections::{BTreeSet, HashMap}; #[test] diff --git a/dash-spv/src/sync/filters/batch_tracker.rs b/dash-spv/src/sync/filters/batch_tracker.rs index ceae6b731..d67a74653 100644 --- a/dash-spv/src/sync/filters/batch_tracker.rs +++ b/dash-spv/src/sync/filters/batch_tracker.rs @@ -1,6 +1,6 @@ use dashcore::bip158::BlockFilter; use dashcore::BlockHash; -use key_wallet::manager::FilterMatchKey; +use key_wallet_manager::FilterMatchKey; use std::collections::{HashMap, HashSet}; /// Tracks individual filters within a batch. diff --git a/dash-spv/src/sync/filters/manager.rs b/dash-spv/src/sync/filters/manager.rs index 5080af045..06bf440e4 100644 --- a/dash-spv/src/sync/filters/manager.rs +++ b/dash-spv/src/sync/filters/manager.rs @@ -21,8 +21,8 @@ use crate::validation::{FilterValidationInput, FilterValidator, Validator}; use crate::sync::progress::ProgressPercentage; use dashcore::hash_types::FilterHeader; -use key_wallet::manager::WalletInterface; -use key_wallet::manager::{check_compact_filters_for_addresses, FilterMatchKey}; +use key_wallet_manager::WalletInterface; +use key_wallet_manager::{check_compact_filters_for_addresses, FilterMatchKey}; use tokio::sync::RwLock; /// Batch size for processing filters. @@ -771,7 +771,7 @@ mod tests { PersistentFilterStorage, StorageManager, }; use crate::sync::{ManagerIdentifier, SyncManagerProgress}; - use key_wallet::test_utils::MockWallet; + use key_wallet_manager::test_utils::MockWallet; use tokio::sync::mpsc::unbounded_channel; type TestFiltersManager = FiltersManager< diff --git a/dash-spv/src/sync/filters/pipeline.rs b/dash-spv/src/sync/filters/pipeline.rs index 6f2ca8025..8029fd54d 100644 --- a/dash-spv/src/sync/filters/pipeline.rs +++ b/dash-spv/src/sync/filters/pipeline.rs @@ -279,7 +279,7 @@ mod tests { use dashcore::block::Header; use dashcore::network::message::NetworkMessage; use dashcore_hashes::Hash; - use key_wallet::manager::FilterMatchKey; + use key_wallet_manager::FilterMatchKey; use std::time::Duration; use tempfile::TempDir; use tokio::sync::mpsc::unbounded_channel; diff --git a/dash-spv/src/sync/filters/sync_manager.rs b/dash-spv/src/sync/filters/sync_manager.rs index a9423ee1b..e49df3405 100644 --- a/dash-spv/src/sync/filters/sync_manager.rs +++ b/dash-spv/src/sync/filters/sync_manager.rs @@ -9,7 +9,7 @@ use crate::sync::{ }; use async_trait::async_trait; use dashcore::network::message::NetworkMessage; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; #[async_trait] impl< diff --git a/dash-spv/src/sync/mempool/manager.rs b/dash-spv/src/sync/mempool/manager.rs index 4250672af..27d3e4681 100644 --- a/dash-spv/src/sync/mempool/manager.rs +++ b/dash-spv/src/sync/mempool/manager.rs @@ -23,7 +23,7 @@ use crate::network::RequestSender; use crate::sync::mempool::MempoolProgress; use crate::sync::SyncEvent; use crate::types::{MempoolState, UnconfirmedTransaction}; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; /// Timeout for pruning expired mempool transactions (24 hours). pub(super) const MEMPOOL_TX_EXPIRY: Duration = Duration::from_secs(24 * 3600); @@ -497,8 +497,8 @@ mod tests { use dashcore::hashes::Hash; use dashcore::network::message::NetworkMessage; use dashcore::{Address, BlockHash, Network, ScriptBuf, Transaction}; - use key_wallet::test_utils::MockWallet; use key_wallet::transaction_checking::TransactionContext; + use key_wallet_manager::test_utils::MockWallet; use crate::sync::SyncState; use crate::test_utils::test_socket_address; diff --git a/dash-spv/src/sync/mempool/sync_manager.rs b/dash-spv/src/sync/mempool/sync_manager.rs index 33cf41bd4..3292ae770 100644 --- a/dash-spv/src/sync/mempool/sync_manager.rs +++ b/dash-spv/src/sync/mempool/sync_manager.rs @@ -6,7 +6,7 @@ use crate::sync::{ }; use async_trait::async_trait; use dashcore::network::message::NetworkMessage; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; #[async_trait] impl SyncManager for MempoolManager { @@ -191,7 +191,7 @@ mod tests { use crate::test_utils::test_socket_address; use crate::types::MempoolState; use dashcore::hashes::Hash; - use key_wallet::test_utils::MockWallet; + use key_wallet_manager::test_utils::MockWallet; use std::sync::Arc; use tokio::sync::{mpsc, RwLock}; diff --git a/dash-spv/src/sync/sync_coordinator.rs b/dash-spv/src/sync/sync_coordinator.rs index 889aea4c0..7f9bc245d 100644 --- a/dash-spv/src/sync/sync_coordinator.rs +++ b/dash-spv/src/sync/sync_coordinator.rs @@ -24,7 +24,7 @@ use crate::sync::{ SyncManager, SyncManagerProgress, SyncManagerTaskContext, SyncProgress, }; use crate::SyncError; -use key_wallet::manager::WalletInterface; +use key_wallet_manager::WalletInterface; const TASK_JOIN_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_SYNC_EVENT_CAPACITY: usize = 10000; diff --git a/dash-spv/src/test_utils/event_handler.rs b/dash-spv/src/test_utils/event_handler.rs index 7d7f21f98..25d9f6b19 100644 --- a/dash-spv/src/test_utils/event_handler.rs +++ b/dash-spv/src/test_utils/event_handler.rs @@ -8,7 +8,7 @@ use tokio::sync::{broadcast, watch}; use crate::client::EventHandler; use crate::network::NetworkEvent; use crate::sync::{SyncEvent, SyncProgress}; -use key_wallet::manager::WalletEvent; +use key_wallet_manager::WalletEvent; /// Event handler that forwards all events to internal channels. /// diff --git a/dash-spv/src/validation/filter.rs b/dash-spv/src/validation/filter.rs index 45b3dd6c1..d9a0f8f3f 100644 --- a/dash-spv/src/validation/filter.rs +++ b/dash-spv/src/validation/filter.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use dashcore::bip158::BlockFilter; use dashcore::hash_types::FilterHeader; -use key_wallet::manager::FilterMatchKey; +use key_wallet_manager::FilterMatchKey; use rayon::prelude::*; use crate::error::{ValidationError, ValidationResult}; @@ -132,7 +132,7 @@ mod tests { use dashcore::bip158::BlockFilter; use dashcore::BlockHash; use dashcore_hashes::Hash; - use key_wallet::manager::FilterMatchKey; + use key_wallet_manager::FilterMatchKey; use super::*; diff --git a/dash-spv/tests/dashd_sync/helpers.rs b/dash-spv/tests/dashd_sync/helpers.rs index 2a27e51aa..a9eb92481 100644 --- a/dash-spv/tests/dashd_sync/helpers.rs +++ b/dash-spv/tests/dashd_sync/helpers.rs @@ -2,11 +2,11 @@ use dash_spv::network::NetworkEvent; use dash_spv::sync::{ProgressPercentage, SyncEvent, SyncProgress, SyncState}; use dash_spv::test_utils::DashCoreNode; use dashcore::Txid; -use key_wallet::manager::WalletEvent; -use key_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; +use key_wallet_manager::WalletEvent; +use key_wallet_manager::{WalletId, WalletManager}; use std::collections::HashSet; use std::sync::Arc; use std::time::Duration; diff --git a/dash-spv/tests/dashd_sync/setup.rs b/dash-spv/tests/dashd_sync/setup.rs index 002663a5b..6608a3c58 100644 --- a/dash-spv/tests/dashd_sync/setup.rs +++ b/dash-spv/tests/dashd_sync/setup.rs @@ -13,11 +13,11 @@ use dashcore::network::address::AddrV2Message; use dashcore::network::constants::ServiceFlags; use dashcore::Txid; use key_wallet::managed_account::managed_account_type::ManagedAccountType; -use key_wallet::manager::WalletEvent; -use key_wallet::manager::{WalletId, WalletManager}; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletEvent; +use key_wallet_manager::{WalletId, WalletManager}; use std::collections::{BTreeSet, HashSet}; use std::path::PathBuf; use std::sync::Arc; diff --git a/dash-spv/tests/dashd_sync/tests_basic.rs b/dash-spv/tests/dashd_sync/tests_basic.rs index 0ad906d09..3cae9b907 100644 --- a/dash-spv/tests/dashd_sync/tests_basic.rs +++ b/dash-spv/tests/dashd_sync/tests_basic.rs @@ -1,5 +1,5 @@ -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/dash-spv/tests/peer_test.rs b/dash-spv/tests/peer_test.rs index 519dd5fe4..c69d0786c 100644 --- a/dash-spv/tests/peer_test.rs +++ b/dash-spv/tests/peer_test.rs @@ -13,8 +13,8 @@ use dash_spv::network::PeerNetworkManager; use dash_spv::storage::DiskStorageManager; use dash_spv::types::ValidationMode; use dashcore::Network; -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletManager; /// Create a test configuration with the given network fn create_test_config(network: Network) -> ClientConfig { let mut config = ClientConfig::new(network); diff --git a/dash-spv/tests/wallet_integration_test.rs b/dash-spv/tests/wallet_integration_test.rs index 4aaeb3be9..f518b5343 100644 --- a/dash-spv/tests/wallet_integration_test.rs +++ b/dash-spv/tests/wallet_integration_test.rs @@ -12,8 +12,8 @@ use dash_spv::network::PeerNetworkManager; use dash_spv::storage::DiskStorageManager; use dash_spv::{ClientConfig, DashSpvClient}; use dashcore::Network; -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletManager; /// Create a test SPV client with memory storage for integration testing. async fn create_test_client( ) -> DashSpvClient, PeerNetworkManager, DiskStorageManager> { diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f93bdf390..2aa8a704f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -15,7 +15,7 @@ cargo-fuzz = true [dependencies] honggfuzz = { version = "0.5", default-features = false } dashcore = { path = "../dash", features = [ "serde" ] } -key-wallet = { path = "../key-wallet" } +key-wallet = { path = "../key-wallet", default-features = false, features = ["getrandom"] } serde = { version = "1.0.219", features = [ "derive" ] } serde_json = "1.0" diff --git a/key-wallet-ffi/Cargo.toml b/key-wallet-ffi/Cargo.toml index fea901453..e787a170d 100644 --- a/key-wallet-ffi/Cargo.toml +++ b/key-wallet-ffi/Cargo.toml @@ -15,12 +15,13 @@ crate-type = ["cdylib", "staticlib", "lib"] [features] default = ["bincode", "eddsa", "bls", "bip38"] bip38 = ["key-wallet/bip38"] -bincode = ["key-wallet/bincode"] +bincode = ["key-wallet/bincode", "key-wallet-manager/bincode"] eddsa = ["dashcore/eddsa", "key-wallet/eddsa"] bls = ["dashcore/bls", "key-wallet/bls"] [dependencies] -key-wallet = { path = "../key-wallet", features = ["manager"] } +key-wallet = { path = "../key-wallet" } +key-wallet-manager = { path = "../key-wallet-manager" } dashcore = { path = "../dash" } secp256k1 = { version = "0.30.0", features = ["global-context"] } tokio = { version = "1.32", features = ["rt-multi-thread", "sync"] } @@ -31,6 +32,7 @@ hex = "0.4" cbindgen = "0.29" [dev-dependencies] -key-wallet = { path = "../key-wallet", features = ["manager", "test-utils"] } +key-wallet = { path = "../key-wallet", features = ["test-utils"] } +key-wallet-manager = { path = "../key-wallet-manager", features = ["test-utils"] } tempfile = "3.0" hex = "0.4" diff --git a/key-wallet-ffi/src/error.rs b/key-wallet-ffi/src/error.rs index 3654e163c..cc942f619 100644 --- a/key-wallet-ffi/src/error.rs +++ b/key-wallet-ffi/src/error.rs @@ -191,9 +191,9 @@ impl From for FFIError { } } -impl From for FFIError { - fn from(err: key_wallet::manager::WalletError) -> Self { - use key_wallet::manager::WalletError; +impl From for FFIError { + fn from(err: key_wallet_manager::WalletError) -> Self { + use key_wallet_manager::WalletError; let (code, msg) = match &err { WalletError::WalletCreation(msg) => { diff --git a/key-wallet-ffi/src/wallet_manager.rs b/key-wallet-ffi/src/wallet_manager.rs index ccb844f51..b607b1b68 100644 --- a/key-wallet-ffi/src/wallet_manager.rs +++ b/key-wallet-ffi/src/wallet_manager.rs @@ -16,9 +16,9 @@ use tokio::sync::RwLock; use crate::error::{FFIError, FFIErrorCode}; use crate::FFINetwork; -use key_wallet::manager::WalletInterface; -use key_wallet::manager::WalletManager; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::WalletInterface; +use key_wallet_manager::WalletManager; /// FFI wrapper for WalletManager /// @@ -470,14 +470,14 @@ pub unsafe extern "C" fn wallet_manager_import_wallet_from_bytes( Err(e) => { // Convert the error to FFI error match e { - key_wallet::manager::WalletError::WalletExists(_) => { + key_wallet_manager::WalletError::WalletExists(_) => { FFIError::set_error( error, FFIErrorCode::InvalidState, "Wallet already exists in the manager".to_string(), ); } - key_wallet::manager::WalletError::InvalidParameter(msg) => { + key_wallet_manager::WalletError::InvalidParameter(msg) => { FFIError::set_error( error, FFIErrorCode::SerializationError, diff --git a/key-wallet-ffi/src/wallet_manager_tests.rs b/key-wallet-ffi/src/wallet_manager_tests.rs index 6997c237f..d7afd4e8a 100644 --- a/key-wallet-ffi/src/wallet_manager_tests.rs +++ b/key-wallet-ffi/src/wallet_manager_tests.rs @@ -5,7 +5,7 @@ mod tests { use crate::error::{FFIError, FFIErrorCode}; use crate::{wallet, wallet_manager, FFINetwork}; - use key_wallet::manager::WalletInterface; + use key_wallet_manager::WalletInterface; use std::ffi::{CStr, CString}; use std::ptr; use std::slice; diff --git a/key-wallet-ffi/tests/test_error_conversions.rs b/key-wallet-ffi/tests/test_error_conversions.rs index dbeefcd97..d9af8a761 100644 --- a/key-wallet-ffi/tests/test_error_conversions.rs +++ b/key-wallet-ffi/tests/test_error_conversions.rs @@ -55,7 +55,7 @@ fn test_key_wallet_error_to_ffi_error() { #[test] fn test_wallet_manager_error_to_ffi_error() { - use key_wallet::manager::WalletError; + use key_wallet_manager::WalletError; // Test WalletNotFound conversion let wallet_id = [0u8; 32]; @@ -105,8 +105,8 @@ fn test_wallet_manager_error_to_ffi_error() { #[test] fn test_key_wallet_error_to_wallet_manager_error() { - use key_wallet::manager::WalletError; use key_wallet::Error as KeyWalletError; + use key_wallet_manager::WalletError; // Test InvalidMnemonic conversion let err = KeyWalletError::InvalidMnemonic("bad mnemonic".to_string()); @@ -172,8 +172,8 @@ fn test_key_wallet_error_to_wallet_manager_error() { #[test] fn test_error_message_consistency() { - use key_wallet::manager::WalletError; use key_wallet::Error as KeyWalletError; + use key_wallet_manager::WalletError; // Test that error messages are preserved through conversions let original_msg = "This is a test error message"; diff --git a/key-wallet-manager/Cargo.toml b/key-wallet-manager/Cargo.toml new file mode 100644 index 000000000..169c444b1 --- /dev/null +++ b/key-wallet-manager/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "key-wallet-manager" +version = { workspace = true } +authors = ["The Dash Core Developers"] +edition = "2021" +description = "High-level wallet management for Dash (WalletManager, WalletInterface, events, compact filters)" +keywords = ["dash", "wallet", "manager", "spv"] +license = "CC0-1.0" + +[features] +default = [] +parallel-filters = ["dep:rayon"] +bincode = ["key-wallet/bincode", "dep:bincode"] +test-utils = ["key-wallet/test-utils"] + +[dependencies] +key-wallet = { path = "../key-wallet" } +dashcore = { path = "../dash" } +tokio = { version = "1", features = ["macros", "rt", "sync"] } +async-trait = "0.1" +rayon = { version = "1.11", optional = true } +zeroize = { version = "1.8", features = ["derive"] } +bincode = { version = "2.0.1", optional = true } + +[dev-dependencies] +key-wallet = { path = "../key-wallet", features = ["test-utils"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/key-wallet-manager/src/accessors.rs b/key-wallet-manager/src/accessors.rs new file mode 100644 index 000000000..1f72d4616 --- /dev/null +++ b/key-wallet-manager/src/accessors.rs @@ -0,0 +1,213 @@ +//! Accessor and query methods for WalletManager. + +use crate::{ + current_timestamp, WalletCoreBalance, WalletError, WalletEvent, WalletId, WalletManager, +}; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; +use key_wallet::wallet::managed_wallet_info::TransactionRecord; +use key_wallet::{Account, Address, Network, Utxo, Wallet}; +use std::collections::{BTreeMap, BTreeSet}; +use tokio::sync::broadcast; + +impl WalletManager { + /// Get a wallet by ID + pub fn get_wallet(&self, wallet_id: &WalletId) -> Option<&Wallet> { + self.wallets.get(wallet_id) + } + + /// Get wallet info by ID + 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 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, &T)> { + match (self.wallets.get(wallet_id), self.wallet_infos.get(wallet_id)) { + (Some(wallet), Some(info)) => Some((wallet, info)), + _ => None, + } + } + + /// Remove a wallet + 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 = + self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + // Absorb the removed wallet's account-level revision so the total + // stays monotonically increasing even though we lost a contributor. + self.structural_revision += info.monitor_revision() + 1; + Ok((wallet, info)) + } + + /// List all wallet IDs + pub fn list_wallets(&self) -> Vec<&WalletId> { + self.wallets.keys().collect() + } + + /// Get all wallets + pub fn get_all_wallets(&self) -> &BTreeMap { + &self.wallets + } + + /// Get all wallet infos + pub fn get_all_wallet_infos(&self) -> &BTreeMap { + &self.wallet_infos + } + + /// Get wallet count + pub fn wallet_count(&self) -> usize { + self.wallets.len() + } + + /// Get all accounts in a specific wallet + pub fn get_accounts(&self, wallet_id: &WalletId) -> Result, WalletError> { + let wallet = self.wallets.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + Ok(wallet.all_accounts()) + } + + /// Get account by index in a specific wallet + pub fn get_account( + &self, + wallet_id: &WalletId, + index: u32, + ) -> Result, WalletError> { + let wallet = self.wallets.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + Ok(wallet.get_bip44_account(index)) + } + + /// Get transaction history for a specific wallet + pub fn wallet_transaction_history( + &self, + wallet_id: &WalletId, + ) -> Result, WalletError> { + let managed_info = + self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + Ok(managed_info.transaction_history()) + } + + /// Get UTXOs for all wallets across all networks + pub fn get_all_utxos(&self) -> Vec<&Utxo> { + let mut all_utxos = Vec::new(); + for info in self.wallet_infos.values() { + all_utxos.extend(info.utxos().iter()); + } + all_utxos + } + + /// Get UTXOs for a specific wallet + pub fn wallet_utxos(&self, wallet_id: &WalletId) -> Result, WalletError> { + let wallet_info = + self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + Ok(wallet_info.utxos()) + } + + /// Get total balance across all wallets and networks + pub fn get_total_balance(&self) -> u64 { + self.wallet_infos.values().map(|info| info.balance().total()).sum() + } + + /// Get balance for a specific wallet + pub fn get_wallet_balance( + &self, + wallet_id: &WalletId, + ) -> Result { + let wallet_info = + self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + Ok(wallet_info.balance()) + } + + /// Update wallet metadata + pub fn update_wallet_metadata( + &mut self, + wallet_id: &WalletId, + name: Option, + description: Option, + ) -> Result<(), WalletError> { + let managed_info = + self.wallet_infos.get_mut(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; + + if let Some(new_name) = name { + managed_info.set_name(new_name); + } + + if let Some(desc) = description { + managed_info.set_description(Some(desc)); + } + + managed_info.update_last_synced(current_timestamp()); + + Ok(()) + } + + /// Get the network this manager is configured for + pub fn network(&self) -> Network { + self.network + } + + /// Get monitored addresses for all wallets + pub fn monitored_addresses(&self) -> Vec
{ + let mut addresses = Vec::new(); + for info in self.wallet_infos.values() { + addresses.extend(info.monitored_addresses()); + } + addresses + } + + /// Subscribe to wallet events. + /// + /// Returns a receiver that will receive all wallet events emitted by this manager. + pub fn subscribe_events(&self) -> broadcast::Receiver { + self.event_sender.subscribe() + } + + /// Get a reference to the event sender for emitting events. + pub fn event_sender(&self) -> &broadcast::Sender { + &self.event_sender + } + + /// Return the total monitor revision (structural + per-wallet account revisions). + pub fn monitor_revision(&self) -> u64 { + self.structural_revision + + self.wallet_infos.values().map(|w| w.monitor_revision()).sum::() + } + + /// Snapshot the current balance of every managed wallet. + pub(crate) fn snapshot_balances(&self) -> Vec<(WalletId, WalletCoreBalance)> { + self.wallet_infos.iter().map(|(id, info)| (*id, info.balance())).collect() + } + + /// Emit `BalanceUpdated` events for wallets whose balance differs from the snapshot. + pub(crate) fn emit_balance_changes(&self, old_balances: &[(WalletId, WalletCoreBalance)]) { + for (wallet_id, old_balance) in old_balances { + if let Some(info) = self.wallet_infos.get(wallet_id) { + let new_balance = info.balance(); + if *old_balance != new_balance { + let event = WalletEvent::BalanceUpdated { + wallet_id: *wallet_id, + spendable: new_balance.spendable(), + unconfirmed: new_balance.unconfirmed(), + immature: new_balance.immature(), + locked: new_balance.locked(), + }; + let _ = self.event_sender.send(event); + } + } + } + } + + /// Get all outpoints from wallet UTXOs across all managed wallets. + /// Used for bloom filter construction to detect spends of our UTXOs. + pub fn watched_outpoints(&self) -> Vec { + let mut outpoints = Vec::new(); + for info in self.wallet_infos.values() { + outpoints.extend(info.utxos().into_iter().map(|u| u.outpoint)); + } + outpoints + } +} diff --git a/key-wallet-manager/src/error.rs b/key-wallet-manager/src/error.rs new file mode 100644 index 000000000..9f96b7e2c --- /dev/null +++ b/key-wallet-manager/src/error.rs @@ -0,0 +1,99 @@ +//! Error types for the wallet manager. + +use crate::WalletId; + +/// Wallet manager errors +#[derive(Debug)] +pub enum WalletError { + /// Wallet creation failed + WalletCreation(String), + /// Wallet not found + WalletNotFound(WalletId), + /// Wallet already exists + WalletExists(WalletId), + /// Invalid mnemonic + InvalidMnemonic(String), + /// Account creation failed + AccountCreation(String), + /// Account not found + AccountNotFound(u32), + /// Address generation failed + AddressGeneration(String), + /// Invalid network + InvalidNetwork, + /// Invalid parameter + InvalidParameter(String), + /// Transaction building failed + TransactionBuild(String), + /// Insufficient funds + InsufficientFunds, +} + +impl core::fmt::Display for WalletError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + WalletError::WalletCreation(msg) => write!(f, "Wallet creation failed: {}", msg), + WalletError::WalletNotFound(id) => { + write!(f, "Wallet not found: ")?; + for byte in id.iter() { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } + WalletError::WalletExists(id) => { + write!(f, "Wallet already exists: ")?; + for byte in id.iter() { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } + WalletError::InvalidMnemonic(msg) => write!(f, "Invalid mnemonic: {}", msg), + WalletError::AccountCreation(msg) => write!(f, "Account creation failed: {}", msg), + WalletError::AccountNotFound(idx) => write!(f, "Account not found: {}", idx), + WalletError::AddressGeneration(msg) => write!(f, "Address generation failed: {}", msg), + WalletError::InvalidNetwork => write!(f, "Invalid network"), + WalletError::InvalidParameter(msg) => write!(f, "Invalid parameter: {}", msg), + WalletError::TransactionBuild(err) => write!(f, "Transaction build failed: {}", err), + WalletError::InsufficientFunds => write!(f, "Insufficient funds"), + } + } +} + +impl std::error::Error for WalletError {} + +/// Conversion from key_wallet::Error to WalletError +impl From for WalletError { + fn from(err: key_wallet::Error) -> Self { + use key_wallet::Error; + + match err { + Error::InvalidMnemonic(msg) => WalletError::InvalidMnemonic(msg), + Error::InvalidDerivationPath(msg) => { + WalletError::InvalidParameter(format!("Invalid derivation path: {}", msg)) + } + Error::InvalidAddress(msg) => { + WalletError::AddressGeneration(format!("Invalid address: {}", msg)) + } + Error::InvalidNetwork => WalletError::InvalidNetwork, + Error::InvalidParameter(msg) => WalletError::InvalidParameter(msg), + Error::WatchOnly => WalletError::InvalidParameter( + "Operation not supported on watch-only wallet".to_string(), + ), + Error::CoinJoinNotEnabled => { + WalletError::InvalidParameter("CoinJoin not enabled".to_string()) + } + Error::KeyError(msg) => WalletError::AccountCreation(format!("Key error: {}", msg)), + Error::Serialization(msg) => { + WalletError::InvalidParameter(format!("Serialization error: {}", msg)) + } + Error::Bip32(e) => WalletError::AccountCreation(format!("BIP32 error: {}", e)), + Error::Secp256k1(e) => WalletError::AccountCreation(format!("Secp256k1 error: {}", e)), + Error::Base58 => WalletError::InvalidParameter("Base58 decoding error".to_string()), + Error::NoKeySource => { + WalletError::InvalidParameter("No key source available".to_string()) + } + #[allow(unreachable_patterns)] + _ => WalletError::InvalidParameter(format!("Key wallet error: {}", err)), + } + } +} diff --git a/key-wallet/src/manager/event_tests.rs b/key-wallet-manager/src/event_tests.rs similarity index 99% rename from key-wallet/src/manager/event_tests.rs rename to key-wallet-manager/src/event_tests.rs index afc02f604..a22237861 100644 --- a/key-wallet/src/manager/event_tests.rs +++ b/key-wallet-manager/src/event_tests.rs @@ -1,9 +1,9 @@ use super::test_helpers::*; use super::*; -use crate::manager::wallet_interface::WalletInterface; -use crate::transaction_checking::BlockInfo; +use crate::wallet_interface::WalletInterface; use dashcore::hashes::Hash; use dashcore::BlockHash; +use key_wallet::transaction_checking::BlockInfo; // --------------------------------------------------------------------------- // Lifecycle flow tests diff --git a/key-wallet/src/manager/events.rs b/key-wallet-manager/src/events.rs similarity index 97% rename from key-wallet/src/manager/events.rs rename to key-wallet-manager/src/events.rs index 1049294a6..7fc63ceab 100644 --- a/key-wallet/src/manager/events.rs +++ b/key-wallet-manager/src/events.rs @@ -3,9 +3,9 @@ //! These events are emitted by the WalletManager when significant wallet //! operations occur, allowing consumers to receive push-based notifications. -use crate::manager::WalletId; -use crate::transaction_checking::TransactionContext; +use crate::WalletId; use dashcore::{Address, Amount, SignedAmount, Txid}; +use key_wallet::transaction_checking::TransactionContext; /// Events emitted by the wallet manager. /// diff --git a/key-wallet/src/manager/mod.rs b/key-wallet-manager/src/lib.rs similarity index 74% rename from key-wallet/src/manager/mod.rs rename to key-wallet-manager/src/lib.rs index 138ac463a..493acb58d 100644 --- a/key-wallet/src/manager/mod.rs +++ b/key-wallet-manager/src/lib.rs @@ -1,3 +1,6 @@ +/// Re-export key-wallet so consumers can access wallet primitives through this crate. +pub use key_wallet; + /// High-level wallet management for Dash /// /// This module provides high-level wallet functionality that builds on top of @@ -9,26 +12,28 @@ /// - BIP 157/158 compact block filter support /// - Address generation and gap limit handling /// - Blockchain synchronization +mod accessors; +mod error; mod events; mod matching; mod process_block; mod wallet_interface; +pub use error::WalletError; pub use events::WalletEvent; pub use matching::{check_compact_filters_for_addresses, FilterMatchKey}; pub use wallet_interface::{BlockProcessingResult, MempoolTransactionResult, WalletInterface}; -use crate::account::AccountCollection; -use crate::transaction_checking::TransactionContext; -use crate::wallet::managed_wallet_info::transaction_building::AccountTypePreference; -use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; -use crate::wallet::managed_wallet_info::{ManagedWalletInfo, TransactionRecord}; -use crate::Utxo; -use crate::{Account, AccountType, Address, ExtendedPrivKey, Mnemonic, Network, Wallet}; -use crate::{ExtendedPubKey, WalletCoreBalance}; use dashcore::blockdata::transaction::Transaction; use dashcore::prelude::CoreBlockHeight; -use std::collections::{BTreeMap, BTreeSet}; +use key_wallet::account::AccountCollection; +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::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet::{AccountType, Address, ExtendedPrivKey, Mnemonic, Network, Wallet}; +use key_wallet::{ExtendedPubKey, WalletCoreBalance}; +use std::collections::BTreeMap; use std::str::FromStr; use tokio::sync::broadcast; @@ -115,24 +120,6 @@ impl WalletManager { } } - /// Subscribe to wallet events. - /// - /// Returns a receiver that will receive all wallet events emitted by this manager. - pub fn subscribe_events(&self) -> broadcast::Receiver { - self.event_sender.subscribe() - } - - /// Get a reference to the event sender for emitting events. - pub fn event_sender(&self) -> &broadcast::Sender { - &self.event_sender - } - - /// Return the total monitor revision (structural + per-wallet account revisions). - pub fn monitor_revision(&self) -> u64 { - self.structural_revision - + self.wallet_infos.values().map(|w| w.monitor_revision()).sum::() - } - /// Increment the structural revision for wallet/account additions or removals. fn bump_structural_revision(&mut self) { self.structural_revision += 1; @@ -145,9 +132,9 @@ impl WalletManager { mnemonic: &str, passphrase: &str, birth_height: CoreBlockHeight, - account_creation_options: crate::wallet::initialization::WalletAccountCreationOptions, + account_creation_options: key_wallet::wallet::initialization::WalletAccountCreationOptions, ) -> Result { - let mnemonic_obj = Mnemonic::from_phrase(mnemonic, crate::mnemonic::Language::English) + let mnemonic_obj = Mnemonic::from_phrase(mnemonic, key_wallet::mnemonic::Language::English) .map_err(|e| WalletError::InvalidMnemonic(e.to_string()))?; // Use appropriate wallet creation method based on whether a passphrase is provided @@ -221,14 +208,14 @@ impl WalletManager { mnemonic: &str, passphrase: &str, birth_height: CoreBlockHeight, - account_creation_options: crate::wallet::initialization::WalletAccountCreationOptions, + account_creation_options: key_wallet::wallet::initialization::WalletAccountCreationOptions, downgrade_to_pubkey_wallet: bool, allow_external_signing: bool, ) -> Result<(Vec, WalletId), WalletError> { - use crate::wallet::WalletType; + use key_wallet::wallet::WalletType; use zeroize::Zeroize; - let mnemonic_obj = Mnemonic::from_phrase(mnemonic, crate::mnemonic::Language::English) + let mnemonic_obj = Mnemonic::from_phrase(mnemonic, key_wallet::mnemonic::Language::English) .map_err(|e| WalletError::InvalidMnemonic(e.to_string()))?; // Create the initial wallet from mnemonic @@ -305,12 +292,13 @@ impl WalletManager { /// Returns the generated wallet ID pub fn create_wallet_with_random_mnemonic( &mut self, - account_creation_options: crate::wallet::initialization::WalletAccountCreationOptions, + account_creation_options: key_wallet::wallet::initialization::WalletAccountCreationOptions, ) -> Result { // Generate a random mnemonic (24 words for maximum security) - let mnemonic = Mnemonic::generate(24, crate::mnemonic::Language::English).map_err(|e| { - WalletError::WalletCreation(format!("Failed to generate mnemonic: {}", e)) - })?; + let mnemonic = + Mnemonic::generate(24, key_wallet::mnemonic::Language::English).map_err(|e| { + WalletError::WalletCreation(format!("Failed to generate mnemonic: {}", e)) + })?; let wallet = Wallet::from_mnemonic(mnemonic, self.network, account_creation_options) .map_err(|e| WalletError::WalletCreation(e.to_string()))?; @@ -334,61 +322,6 @@ impl WalletManager { Ok(wallet_id) } - /// Get a wallet by ID - pub fn get_wallet(&self, wallet_id: &WalletId) -> Option<&Wallet> { - self.wallets.get(wallet_id) - } - - /// Get wallet info by ID - 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 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, &T)> { - match (self.wallets.get(wallet_id), self.wallet_infos.get(wallet_id)) { - (Some(wallet), Some(info)) => Some((wallet, info)), - _ => None, - } - } - - /// Remove a wallet - 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 = - self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - // Absorb the removed wallet's account-level revision so the total - // stays monotonically increasing even though we lost a contributor. - self.structural_revision += info.monitor_revision() + 1; - Ok((wallet, info)) - } - - /// List all wallet IDs - pub fn list_wallets(&self) -> Vec<&WalletId> { - self.wallets.keys().collect() - } - - /// Get all wallets - pub fn get_all_wallets(&self) -> &BTreeMap { - &self.wallets - } - - /// Get all wallet infos - pub fn get_all_wallet_infos(&self) -> &BTreeMap { - &self.wallet_infos - } - - /// Get wallet count - pub fn wallet_count(&self) -> usize { - self.wallets.len() - } - /// Import a wallet from an extended private key and add it to the manager /// /// # Arguments @@ -401,7 +334,7 @@ impl WalletManager { pub fn import_wallet_from_extended_priv_key( &mut self, xprv: &str, - account_creation_options: crate::wallet::initialization::WalletAccountCreationOptions, + account_creation_options: key_wallet::wallet::initialization::WalletAccountCreationOptions, ) -> Result { // Parse the extended private key let extended_priv_key = ExtendedPrivKey::from_str(xprv) @@ -635,24 +568,6 @@ impl WalletManager { Ok(()) } - /// Get all accounts in a specific wallet - pub fn get_accounts(&self, wallet_id: &WalletId) -> Result, WalletError> { - let wallet = self.wallets.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - - Ok(wallet.all_accounts()) - } - - /// Get account by index in a specific wallet - pub fn get_account( - &self, - wallet_id: &WalletId, - index: u32, - ) -> Result, WalletError> { - let wallet = self.wallets.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - - Ok(wallet.get_bip44_account(index)) - } - /// Get receive address from a specific wallet and account pub fn get_receive_address( &mut self, @@ -981,184 +896,6 @@ impl WalletManager { account_type_used, }) } - - /// Get transaction history for a specific wallet - pub fn wallet_transaction_history( - &self, - wallet_id: &WalletId, - ) -> Result, WalletError> { - let managed_info = - self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - - Ok(managed_info.transaction_history()) - } - - /// Get UTXOs for all wallets across all networks - pub fn get_all_utxos(&self) -> Vec<&Utxo> { - let mut all_utxos = Vec::new(); - for info in self.wallet_infos.values() { - all_utxos.extend(info.utxos().iter()); - } - all_utxos - } - - /// Get UTXOs for a specific wallet - 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.utxos(); - - Ok(utxos) - } - - /// Get total balance across all wallets and networks - pub fn get_total_balance(&self) -> u64 { - self.wallet_infos.values().map(|info| info.balance().total()).sum() - } - - /// Get balance for a specific wallet - pub fn get_wallet_balance( - &self, - wallet_id: &WalletId, - ) -> Result { - // Get the wallet info - let wallet_info = - self.wallet_infos.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - - // Get balance from the wallet info - Ok(wallet_info.balance()) - } - - /// Update wallet metadata - pub fn update_wallet_metadata( - &mut self, - wallet_id: &WalletId, - name: Option, - description: Option, - ) -> Result<(), WalletError> { - let managed_info = - self.wallet_infos.get_mut(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?; - - if let Some(new_name) = name { - managed_info.set_name(new_name); - } - - if let Some(desc) = description { - managed_info.set_description(Some(desc)); - } - - managed_info.update_last_synced(current_timestamp()); - - Ok(()) - } - - /// Get the network this manager is configured for - pub fn network(&self) -> Network { - self.network - } - - /// Get monitored addresses for all wallets for a specific network - pub fn monitored_addresses(&self) -> Vec
{ - let mut addresses = Vec::new(); - for info in self.wallet_infos.values() { - addresses.extend(info.monitored_addresses()); - } - addresses - } - - /// Snapshot the current balance of every managed wallet. - pub(crate) fn snapshot_balances(&self) -> Vec<(WalletId, WalletCoreBalance)> { - self.wallet_infos.iter().map(|(id, info)| (*id, info.balance())).collect() - } - - /// Emit `BalanceUpdated` events for wallets whose balance differs from the snapshot. - pub(crate) fn emit_balance_changes(&self, old_balances: &[(WalletId, WalletCoreBalance)]) { - for (wallet_id, old_balance) in old_balances { - if let Some(info) = self.wallet_infos.get(wallet_id) { - let new_balance = info.balance(); - if *old_balance != new_balance { - let event = WalletEvent::BalanceUpdated { - wallet_id: *wallet_id, - spendable: new_balance.spendable(), - unconfirmed: new_balance.unconfirmed(), - immature: new_balance.immature(), - locked: new_balance.locked(), - }; - let _ = self.event_sender.send(event); - } - } - } - } - - /// Get all outpoints from wallet UTXOs across all managed wallets. - /// Used for bloom filter construction to detect spends of our UTXOs. - pub fn watched_outpoints(&self) -> Vec { - let mut outpoints = Vec::new(); - for info in self.wallet_infos.values() { - outpoints.extend(info.utxos().into_iter().map(|u| u.outpoint)); - } - outpoints - } -} - -/// Wallet manager errors -#[derive(Debug)] -pub enum WalletError { - /// Wallet creation failed - WalletCreation(String), - /// Wallet not found - WalletNotFound(WalletId), - /// Wallet already exists - WalletExists(WalletId), - /// Invalid mnemonic - InvalidMnemonic(String), - /// Account creation failed - AccountCreation(String), - /// Account not found - AccountNotFound(u32), - /// Address generation failed - AddressGeneration(String), - /// Invalid network - InvalidNetwork, - /// Invalid parameter - InvalidParameter(String), - /// Transaction building failed - TransactionBuild(String), - /// Insufficient funds - InsufficientFunds, -} - -impl core::fmt::Display for WalletError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - WalletError::WalletCreation(msg) => write!(f, "Wallet creation failed: {}", msg), - WalletError::WalletNotFound(id) => { - write!(f, "Wallet not found: ")?; - for byte in id.iter() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } - WalletError::WalletExists(id) => { - write!(f, "Wallet already exists: ")?; - for byte in id.iter() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } - WalletError::InvalidMnemonic(msg) => write!(f, "Invalid mnemonic: {}", msg), - WalletError::AccountCreation(msg) => write!(f, "Account creation failed: {}", msg), - WalletError::AccountNotFound(idx) => write!(f, "Account not found: {}", idx), - WalletError::AddressGeneration(msg) => write!(f, "Address generation failed: {}", msg), - WalletError::InvalidNetwork => write!(f, "Invalid network"), - WalletError::InvalidParameter(msg) => write!(f, "Invalid parameter: {}", msg), - WalletError::TransactionBuild(err) => write!(f, "Transaction build failed: {}", err), - WalletError::InsufficientFunds => write!(f, "Insufficient funds"), - } - } } /// Helper function for getting current timestamp @@ -1166,46 +903,10 @@ fn current_timestamp() -> u64 { std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs() } -impl std::error::Error for WalletError {} - #[cfg(test)] mod event_tests; #[cfg(test)] mod test_helpers; -/// Conversion from crate::Error to WalletError -impl From for WalletError { - fn from(err: crate::Error) -> Self { - use crate::Error; - - match err { - Error::InvalidMnemonic(msg) => WalletError::InvalidMnemonic(msg), - Error::InvalidDerivationPath(msg) => { - WalletError::InvalidParameter(format!("Invalid derivation path: {}", msg)) - } - Error::InvalidAddress(msg) => { - WalletError::AddressGeneration(format!("Invalid address: {}", msg)) - } - Error::InvalidNetwork => WalletError::InvalidNetwork, - Error::InvalidParameter(msg) => WalletError::InvalidParameter(msg), - Error::WatchOnly => WalletError::InvalidParameter( - "Operation not supported on watch-only wallet".to_string(), - ), - Error::CoinJoinNotEnabled => { - WalletError::InvalidParameter("CoinJoin not enabled".to_string()) - } - Error::KeyError(msg) => WalletError::AccountCreation(format!("Key error: {}", msg)), - Error::Serialization(msg) => { - WalletError::InvalidParameter(format!("Serialization error: {}", msg)) - } - Error::Bip32(e) => WalletError::AccountCreation(format!("BIP32 error: {}", e)), - Error::Secp256k1(e) => WalletError::AccountCreation(format!("Secp256k1 error: {}", e)), - Error::Base58 => WalletError::InvalidParameter("Base58 decoding error".to_string()), - Error::NoKeySource => { - WalletError::InvalidParameter("No key source available".to_string()) - } - #[allow(unreachable_patterns)] - _ => WalletError::InvalidParameter(format!("Key wallet error: {}", err)), - } - } -} +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; diff --git a/key-wallet/src/manager/matching.rs b/key-wallet-manager/src/matching.rs similarity index 99% rename from key-wallet/src/manager/matching.rs rename to key-wallet-manager/src/matching.rs index f976cd269..0b61289f8 100644 --- a/key-wallet/src/manager/matching.rs +++ b/key-wallet-manager/src/matching.rs @@ -55,8 +55,8 @@ pub fn check_compact_filters_for_addresses( #[cfg(test)] mod tests { use super::*; - use crate::Network; use dashcore::{Block, Transaction}; + use key_wallet::Network; #[test] fn test_empty_input_returns_empty() { diff --git a/key-wallet/src/manager/process_block.rs b/key-wallet-manager/src/process_block.rs similarity index 95% rename from key-wallet/src/manager/process_block.rs rename to key-wallet-manager/src/process_block.rs index bfb9848a1..7b7e0d3d1 100644 --- a/key-wallet/src/manager/process_block.rs +++ b/key-wallet-manager/src/process_block.rs @@ -1,14 +1,12 @@ -use crate::manager::wallet_interface::{ - BlockProcessingResult, MempoolTransactionResult, WalletInterface, -}; -use crate::manager::{WalletEvent, WalletManager}; -use crate::transaction_checking::transaction_router::TransactionRouter; -use crate::transaction_checking::{BlockInfo, TransactionContext}; -use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; +use crate::wallet_interface::{BlockProcessingResult, MempoolTransactionResult, WalletInterface}; +use crate::{WalletEvent, WalletManager}; use async_trait::async_trait; use core::fmt::Write as _; use dashcore::prelude::CoreBlockHeight; use dashcore::{Address, Block, Transaction, Txid}; +use key_wallet::transaction_checking::transaction_router::TransactionRouter; +use key_wallet::transaction_checking::{BlockInfo, TransactionContext}; +use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use std::collections::BTreeSet; use tokio::sync::broadcast; @@ -228,18 +226,18 @@ impl WalletInterface for WalletM #[cfg(test)] mod tests { use super::*; - use crate::account::StandardAccountType; - use crate::manager::test_helpers::*; - use crate::wallet::initialization::WalletAccountCreationOptions; - use crate::wallet::managed_wallet_info::transaction_building::AccountTypePreference; - use crate::wallet::managed_wallet_info::ManagedWalletInfo; - use crate::AccountType; + use crate::test_helpers::*; use dashcore::block::{Header, Version}; use dashcore::hashes::Hash; use dashcore::pow::CompactTarget; use dashcore::{ BlockHash, Network, OutPoint, ScriptBuf, TxIn, TxMerkleNode, TxOut, Txid, Witness, }; + use key_wallet::account::StandardAccountType; + use key_wallet::wallet::initialization::WalletAccountCreationOptions; + use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; + use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; + use key_wallet::AccountType; fn make_block(txdata: Vec) -> Block { Block { diff --git a/key-wallet/src/manager/test_helpers.rs b/key-wallet-manager/src/test_helpers.rs similarity index 96% rename from key-wallet/src/manager/test_helpers.rs rename to key-wallet-manager/src/test_helpers.rs index ebd882c04..268bf6a47 100644 --- a/key-wallet/src/manager/test_helpers.rs +++ b/key-wallet-manager/src/test_helpers.rs @@ -1,9 +1,9 @@ use super::*; -use crate::wallet::initialization::WalletAccountCreationOptions; -use crate::wallet::managed_wallet_info::ManagedWalletInfo; -use crate::Network; use dashcore::hashes::Hash; use dashcore::{OutPoint, ScriptBuf, TxIn, TxOut, Txid, Witness}; +use key_wallet::wallet::initialization::WalletAccountCreationOptions; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet::Network; use tokio::sync::broadcast; pub(crate) const TEST_MNEMONIC: &str = diff --git a/key-wallet/src/test_utils/mock_wallet.rs b/key-wallet-manager/src/test_utils/mock_wallet.rs similarity index 97% rename from key-wallet/src/test_utils/mock_wallet.rs rename to key-wallet-manager/src/test_utils/mock_wallet.rs index 856dc31e0..9c19472ce 100644 --- a/key-wallet/src/test_utils/mock_wallet.rs +++ b/key-wallet-manager/src/test_utils/mock_wallet.rs @@ -1,9 +1,9 @@ -use crate::manager::MempoolTransactionResult; -use crate::manager::{BlockProcessingResult, WalletEvent, WalletInterface}; -use crate::transaction_checking::TransactionContext; +use crate::MempoolTransactionResult; +use crate::{BlockProcessingResult, WalletEvent, WalletInterface}; use dashcore::address::NetworkUnchecked; use dashcore::prelude::CoreBlockHeight; use dashcore::{Address, Block, OutPoint, Transaction, Txid}; +use key_wallet::transaction_checking::TransactionContext; use std::collections::BTreeMap; use std::str::FromStr; use std::sync::Arc; diff --git a/key-wallet-manager/src/test_utils/mod.rs b/key-wallet-manager/src/test_utils/mod.rs new file mode 100644 index 000000000..108a02fd5 --- /dev/null +++ b/key-wallet-manager/src/test_utils/mod.rs @@ -0,0 +1,4 @@ +mod mock_wallet; + +pub use mock_wallet::MockWallet; +pub use mock_wallet::NonMatchingMockWallet; diff --git a/key-wallet/src/manager/wallet_interface.rs b/key-wallet-manager/src/wallet_interface.rs similarity index 99% rename from key-wallet/src/manager/wallet_interface.rs rename to key-wallet-manager/src/wallet_interface.rs index fd2e69023..cdd5dbb39 100644 --- a/key-wallet/src/manager/wallet_interface.rs +++ b/key-wallet-manager/src/wallet_interface.rs @@ -2,7 +2,7 @@ //! //! This module defines the trait that SPV clients use to interact with wallets. -use crate::manager::WalletEvent; +use crate::WalletEvent; use async_trait::async_trait; use dashcore::prelude::CoreBlockHeight; use dashcore::{Address, Block, OutPoint, Transaction, Txid}; diff --git a/key-wallet/Cargo.toml b/key-wallet/Cargo.toml index 336f5cae2..653724bd4 100644 --- a/key-wallet/Cargo.toml +++ b/key-wallet/Cargo.toml @@ -9,9 +9,7 @@ readme = "README.md" license = "CC0-1.0" [features] -default = ["getrandom", "manager"] -manager = ["dep:tokio"] -parallel-filters = ["manager", "dep:rayon"] +default = ["getrandom"] serde = ["dep:serde", "dep:serde_json", "dashcore_hashes/serde", "secp256k1/serde", "dashcore/serde"] bincode = ["serde", "dep:bincode", "dep:bincode_derive", "dashcore_hashes/bincode", "dashcore/bincode"] bip38 = ["scrypt", "aes", "bs58", "rand"] @@ -44,12 +42,12 @@ hex = { version = "0.4"} zeroize = { version = "1.8", features = ["derive"] } tracing = "0.1" async-trait = "0.1" -tokio = { version = "1", features = ["macros", "rt", "sync"], optional = true } -rayon = { version = "1.11", optional = true } [dev-dependencies] dashcore = { path="../dash", features = ["test-utils"] } hex = "0.4" key-wallet = { path = ".", features = ["test-utils", "bip38", "serde", "bincode", "eddsa", "bls"] } +key-wallet-manager = { path = "../key-wallet-manager", features = ["test-utils", "bincode"] } +tokio = { version = "1", features = ["macros", "rt"] } rand = { version = "0.8", features = ["std", "std_rng"] } test-case = "3.3" diff --git a/key-wallet/examples/wallet_creation.rs b/key-wallet/examples/wallet_creation.rs index 5d0943cf0..4529f5bb7 100644 --- a/key-wallet/examples/wallet_creation.rs +++ b/key-wallet/examples/wallet_creation.rs @@ -6,12 +6,12 @@ //! - Managing wallet accounts and addresses use key_wallet::account::StandardAccountType; -use key_wallet::manager::WalletInterface; -use key_wallet::manager::WalletManager; use key_wallet::wallet::initialization::WalletAccountCreationOptions; 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::WalletInterface; +use key_wallet_manager::WalletManager; fn main() { println!("=== Wallet Creation Example ===\n"); diff --git a/key-wallet/src/lib.rs b/key-wallet/src/lib.rs index d6c8a81a5..e12224ae4 100644 --- a/key-wallet/src/lib.rs +++ b/key-wallet/src/lib.rs @@ -35,8 +35,6 @@ pub mod dip9; pub mod error; pub mod gap_limit; pub mod managed_account; -#[cfg(feature = "manager")] -pub mod manager; pub mod mnemonic; pub mod psbt; pub mod seed; diff --git a/key-wallet/src/test_utils/mod.rs b/key-wallet/src/test_utils/mod.rs index bb5390835..d9de8de38 100644 --- a/key-wallet/src/test_utils/mod.rs +++ b/key-wallet/src/test_utils/mod.rs @@ -1,11 +1,5 @@ mod account; -#[cfg(feature = "manager")] -mod mock_wallet; mod utxo; mod wallet; -#[cfg(feature = "manager")] -pub use mock_wallet::MockWallet; -#[cfg(feature = "manager")] -pub use mock_wallet::NonMatchingMockWallet; pub use wallet::TestWalletContext; diff --git a/key-wallet/tests/integration_test.rs b/key-wallet/tests/integration_test.rs index 37d7580c8..e48674469 100644 --- a/key-wallet/tests/integration_test.rs +++ b/key-wallet/tests/integration_test.rs @@ -3,13 +3,13 @@ //! These tests verify that the high-level wallet management functionality //! works correctly with the low-level key-wallet primitives. -use key_wallet::manager::WalletInterface; -use key_wallet::manager::{WalletError, WalletManager}; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::{mnemonic::Language, Mnemonic, Network}; +use key_wallet_manager::WalletInterface; +use key_wallet_manager::{WalletError, WalletManager}; #[test] fn test_wallet_manager_creation() { diff --git a/key-wallet/tests/spv_integration_tests.rs b/key-wallet/tests/spv_integration_tests.rs index d3fd99655..ddcffc29a 100644 --- a/key-wallet/tests/spv_integration_tests.rs +++ b/key-wallet/tests/spv_integration_tests.rs @@ -4,12 +4,12 @@ use dashcore::blockdata::block::Block; use dashcore::blockdata::transaction::Transaction; use dashcore::constants::COINBASE_MATURITY; use dashcore::Address; -use key_wallet::manager::WalletInterface; -use key_wallet::manager::WalletManager; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; +use key_wallet_manager::WalletInterface; +use key_wallet_manager::WalletManager; #[tokio::test] async fn test_block_processing() { diff --git a/key-wallet/tests/test_serialized_wallets.rs b/key-wallet/tests/test_serialized_wallets.rs index 0dd9b8f44..015f19c7c 100644 --- a/key-wallet/tests/test_serialized_wallets.rs +++ b/key-wallet/tests/test_serialized_wallets.rs @@ -1,10 +1,10 @@ #[cfg(feature = "bincode")] #[cfg(test)] mod tests { - use key_wallet::manager::WalletManager; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; + use key_wallet_manager::WalletManager; #[test] fn test_create_wallet_return_serialized_bytes() {