From 2296598191c885282d483e0a0eeee18fa733cf8b Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 20:16:30 +0000 Subject: [PATCH 01/15] removed error tests --- dash-spv/src/error.rs | 69 ----- dash-spv/tests/error_types_test.rs | 445 ----------------------------- 2 files changed, 514 deletions(-) delete mode 100644 dash-spv/tests/error_types_test.rs diff --git a/dash-spv/src/error.rs b/dash-spv/src/error.rs index 5e411449d..04229abd1 100644 --- a/dash-spv/src/error.rs +++ b/dash-spv/src/error.rs @@ -137,22 +137,6 @@ pub enum StorageError { DirectoryLocked(String), } -impl Clone for StorageError { - fn clone(&self) -> Self { - match self { - StorageError::Corruption(s) => StorageError::Corruption(s.clone()), - StorageError::NotFound(s) => StorageError::NotFound(s.clone()), - StorageError::WriteFailed(s) => StorageError::WriteFailed(s.clone()), - StorageError::ReadFailed(s) => StorageError::ReadFailed(s.clone()), - StorageError::Io(err) => StorageError::Io(io::Error::new(err.kind(), err.to_string())), - StorageError::Serialization(s) => StorageError::Serialization(s.clone()), - StorageError::InconsistentState(s) => StorageError::InconsistentState(s.clone()), - StorageError::LockPoisoned(s) => StorageError::LockPoisoned(s.clone()), - StorageError::DirectoryLocked(s) => StorageError::DirectoryLocked(s.clone()), - } - } -} - /// Validation-related errors. #[derive(Debug, Error)] pub enum ValidationError { @@ -228,24 +212,6 @@ pub enum SyncError { Headers2DecompressionFailed(String), } -impl SyncError { - /// Returns a static string representing the error category based on the variant - pub fn category(&self) -> &'static str { - match self { - SyncError::SyncInProgress | SyncError::InvalidState(_) => "state", - SyncError::Timeout(_) => "timeout", - SyncError::Validation(_) => "validation", - SyncError::MissingDependency(_) => "dependency", - SyncError::Network(_) => "network", - SyncError::Storage(_) => "storage", - SyncError::Headers2DecompressionFailed(_) => "headers2", - // Deprecated variant - should not be used - #[allow(deprecated)] - SyncError::SyncFailed(_) => "unknown", - } - } -} - /// Type alias for Result with SpvError. pub type Result = std::result::Result; @@ -297,38 +263,3 @@ pub enum WalletError { /// Type alias for wallet operation results. pub type WalletResult = std::result::Result; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_sync_error_category() { - // Test explicit variant categories - assert_eq!(SyncError::Timeout("test".to_string()).category(), "timeout"); - assert_eq!(SyncError::Network("test".to_string()).category(), "network"); - assert_eq!(SyncError::Validation("test".to_string()).category(), "validation"); - assert_eq!(SyncError::Storage("test".to_string()).category(), "storage"); - - // Test existing variant categories - assert_eq!(SyncError::SyncInProgress.category(), "state"); - assert_eq!(SyncError::InvalidState("test".to_string()).category(), "state"); - assert_eq!(SyncError::MissingDependency("test".to_string()).category(), "dependency"); - - // Test deprecated SyncFailed always returns "unknown" - #[allow(deprecated)] - { - assert_eq!( - SyncError::SyncFailed("connection timeout".to_string()).category(), - "unknown" - ); - assert_eq!(SyncError::SyncFailed("network error".to_string()).category(), "unknown"); - assert_eq!( - SyncError::SyncFailed("validation failed".to_string()).category(), - "unknown" - ); - assert_eq!(SyncError::SyncFailed("disk full".to_string()).category(), "unknown"); - assert_eq!(SyncError::SyncFailed("something else".to_string()).category(), "unknown"); - } - } -} diff --git a/dash-spv/tests/error_types_test.rs b/dash-spv/tests/error_types_test.rs deleted file mode 100644 index 96e3c603e..000000000 --- a/dash-spv/tests/error_types_test.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! Unit tests for error types, conversions, and formatting -//! -//! This test suite focuses on: -//! - Error type conversions and From implementations -//! - Error message formatting and context preservation -//! - Error category classification -//! - Nested error handling - -use dashcore::{OutPoint, Txid}; -use dashcore_hashes::Hash; -use std::io; - -use dash_spv::error::*; - -#[test] -fn test_network_error_from_io_error() { - let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "Connection refused"); - let net_err: NetworkError = io_err.into(); - - match net_err { - NetworkError::Io(_) => { - assert!(net_err.to_string().contains("Connection refused")); - } - _ => panic!("Expected NetworkError::Io variant"), - } -} - -#[test] -fn test_storage_error_from_io_error() { - let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "Permission denied"); - let storage_err: StorageError = io_err.into(); - - match storage_err { - StorageError::Io(_) => { - assert!(storage_err.to_string().contains("Permission denied")); - } - _ => panic!("Expected StorageError::Io variant"), - } -} - -#[test] -fn test_spv_error_from_network_error() { - let net_err = NetworkError::Timeout; - let spv_err: SpvError = net_err.into(); - - match spv_err { - SpvError::Network(NetworkError::Timeout) => { - assert_eq!(spv_err.to_string(), "Network error: Timeout occurred"); - } - _ => panic!("Expected SpvError::Network variant"), - } -} - -#[test] -fn test_spv_error_from_storage_error() { - let storage_err = StorageError::Corruption("Header checksum mismatch".to_string()); - let spv_err: SpvError = storage_err.into(); - - match &spv_err { - SpvError::Storage(StorageError::Corruption(msg)) => { - assert_eq!(msg, "Header checksum mismatch"); - assert!(spv_err.to_string().contains("Header checksum mismatch")); - } - _ => panic!("Expected SpvError::Storage variant"), - } -} - -#[test] -fn test_spv_error_from_validation_error() { - let val_err = ValidationError::InvalidProofOfWork; - let spv_err: SpvError = val_err.into(); - - match spv_err { - SpvError::Validation(ValidationError::InvalidProofOfWork) => { - assert_eq!(spv_err.to_string(), "Validation error: Invalid proof of work"); - } - _ => panic!("Expected SpvError::Validation variant"), - } -} - -#[test] -fn test_spv_error_from_sync_error() { - let sync_err = SyncError::SyncInProgress; - let spv_err: SpvError = sync_err.into(); - - match spv_err { - SpvError::Sync(SyncError::SyncInProgress) => { - assert_eq!(spv_err.to_string(), "Sync error: Sync already in progress"); - } - _ => panic!("Expected SpvError::Sync variant"), - } -} - -#[test] -fn test_spv_error_from_io_error() { - let io_err = io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected end of file"); - let spv_err: SpvError = io_err.into(); - - match spv_err { - SpvError::Io(_) => { - assert!(spv_err.to_string().contains("Unexpected end of file")); - } - _ => panic!("Expected SpvError::Io variant"), - } -} - -#[test] -fn test_validation_error_from_storage_error() { - let storage_err = StorageError::NotFound("Block header at height 12345".to_string()); - let val_err: ValidationError = storage_err.into(); - - match val_err { - ValidationError::StorageError(StorageError::NotFound(msg)) => { - assert_eq!(msg, "Block header at height 12345"); - } - _ => panic!("Expected ValidationError::StorageError variant"), - } -} - -#[test] -fn test_network_error_variants() { - let errors = vec![ - ( - NetworkError::ConnectionFailed("127.0.0.1:9999 refused connection".to_string()), - "Connection failed: 127.0.0.1:9999 refused connection", - ), - ( - NetworkError::HandshakeFailed("Version mismatch".to_string()), - "Handshake failed: Version mismatch", - ), - ( - NetworkError::ProtocolError("Invalid message format".to_string()), - "Protocol error: Invalid message format", - ), - (NetworkError::Timeout, "Timeout occurred"), - (NetworkError::PeerDisconnected, "Peer disconnected"), - (NetworkError::NotConnected, "Not connected"), - ( - NetworkError::AddressParse("Invalid IP address".to_string()), - "Address parse error: Invalid IP address", - ), - ( - NetworkError::SystemTime("Clock drift detected".to_string()), - "System time error: Clock drift detected", - ), - ]; - - for (error, expected_msg) in errors { - assert_eq!(error.to_string(), expected_msg); - } -} - -#[test] -fn test_storage_error_variants() { - let errors = vec![ - ( - StorageError::Corruption("Invalid segment header".to_string()), - "Corruption detected: Invalid segment header", - ), - ( - StorageError::NotFound("Header at height 1000".to_string()), - "Data not found: Header at height 1000", - ), - ( - StorageError::WriteFailed("/tmp/headers.dat: Permission denied".to_string()), - "Write failed: /tmp/headers.dat: Permission denied", - ), - ( - StorageError::ReadFailed("Segment file truncated".to_string()), - "Read failed: Segment file truncated", - ), - ( - StorageError::Serialization("Invalid encoding".to_string()), - "Serialization error: Invalid encoding", - ), - ( - StorageError::InconsistentState("Height mismatch".to_string()), - "Inconsistent state: Height mismatch", - ), - ( - StorageError::LockPoisoned("Mutex poisoned by panic".to_string()), - "Lock poisoned: Mutex poisoned by panic", - ), - ]; - - for (error, expected_msg) in errors { - assert_eq!(error.to_string(), expected_msg); - } -} - -#[test] -fn test_validation_error_variants() { - let errors = vec![ - (ValidationError::InvalidProofOfWork, "Invalid proof of work"), - ( - ValidationError::InvalidHeaderChain("Height 5000: timestamp regression".to_string()), - "Invalid header chain: Height 5000: timestamp regression", - ), - ( - ValidationError::InvalidChainLock("Signature verification failed".to_string()), - "Invalid ChainLock: Signature verification failed", - ), - ( - ValidationError::InvalidInstantLock("Quorum not found".to_string()), - "Invalid InstantLock: Quorum not found", - ), - ( - ValidationError::InvalidFilterHeaderChain("Hash mismatch at height 3000".to_string()), - "Invalid filter header chain: Hash mismatch at height 3000", - ), - ( - ValidationError::Consensus("Block size exceeds limit".to_string()), - "Consensus error: Block size exceeds limit", - ), - ( - ValidationError::MasternodeVerification("Invalid ProRegTx".to_string()), - "Masternode verification failed: Invalid ProRegTx", - ), - ]; - - for (error, expected_msg) in errors { - assert_eq!(error.to_string(), expected_msg); - } -} - -#[test] -fn test_sync_error_variants_and_categories() { - let test_cases = vec![ - (SyncError::SyncInProgress, "state", "Sync already in progress"), - ( - SyncError::InvalidState("Unexpected phase transition".to_string()), - "state", - "Invalid sync state: Unexpected phase transition", - ), - ( - SyncError::MissingDependency("Previous block not found".to_string()), - "dependency", - "Missing dependency: Previous block not found", - ), - ( - SyncError::Timeout("Peer response timeout".to_string()), - "timeout", - "Timeout error: Peer response timeout", - ), - ( - SyncError::Network("Connection lost".to_string()), - "network", - "Network error: Connection lost", - ), - ( - SyncError::Validation("Invalid block header".to_string()), - "validation", - "Validation error: Invalid block header", - ), - ( - SyncError::Storage("Database locked".to_string()), - "storage", - "Storage error: Database locked", - ), - ( - SyncError::Headers2DecompressionFailed("Invalid zstd stream".to_string()), - "headers2", - "Headers2 decompression failed: Invalid zstd stream", - ), - ]; - - for (error, expected_category, expected_msg) in test_cases { - assert_eq!(error.category(), expected_category); - assert_eq!(error.to_string(), expected_msg); - } -} - -#[test] -fn test_wallet_error_variants() { - let outpoint = OutPoint { - txid: Txid::from_byte_array([0xAB; 32]), - vout: 5, - }; - - let errors = vec![ - (WalletError::BalanceOverflow, "Balance calculation overflow"), - ( - WalletError::UnsupportedAddressType("P2WSH".to_string()), - "Unsupported address type: P2WSH", - ), - (WalletError::InvalidScriptPubkey, "Invalid script pubkey"), - (WalletError::NotInitialized, "Wallet not initialized"), - ( - WalletError::TransactionValidation("Invalid signature".to_string()), - "Transaction validation failed: Invalid signature", - ), - (WalletError::InvalidOutput(3), "Invalid transaction output at index 3"), - ( - WalletError::AddressError("Invalid network byte".to_string()), - "Address error: Invalid network byte", - ), - ( - WalletError::ScriptError("Script execution failed".to_string()), - "Script error: Script execution failed", - ), - ]; - - for (error, expected_msg) in errors { - assert_eq!(error.to_string(), expected_msg); - } - - // Special case for UTXO not found (contains hex) - let utxo_error = WalletError::UtxoNotFound(outpoint); - assert!(utxo_error.to_string().contains("UTXO not found")); - assert!(utxo_error.to_string().contains("abab")); // Partial hex from txid -} - -#[test] -fn test_parse_error_variants() { - let errors = vec![ - (ParseError::InvalidAddress("xyz123".to_string()), "Invalid network address: xyz123"), - (ParseError::InvalidNetwork("mainnet2".to_string()), "Invalid network name: mainnet2"), - ( - ParseError::MissingArgument("--storage-path".to_string()), - "Missing required argument: --storage-path", - ), - ( - ParseError::InvalidArgument("port".to_string(), "abc".to_string()), - "Invalid argument value for port: abc", - ), - ]; - - for (error, expected_msg) in errors { - assert_eq!(error.to_string(), expected_msg); - } -} - -#[test] -fn test_error_context_preservation() { - // Create a chain of errors to test context preservation - let io_err = io::Error::other("Disk failure"); - let storage_err: StorageError = io_err.into(); - let val_err: ValidationError = storage_err.into(); - let spv_err: SpvError = val_err.into(); - - // The final error should still contain the original context - let error_string = spv_err.to_string(); - assert!(error_string.contains("Validation error")); - assert!(error_string.contains("Storage error")); - assert!(error_string.contains("Disk failure")); -} - -#[test] -fn test_result_type_aliases() { - // Test that type aliases work correctly - fn network_operation() -> NetworkResult { - Err(NetworkError::Timeout) - } - - fn storage_operation() -> StorageResult { - Err(StorageError::NotFound("test".to_string())) - } - - fn validation_operation() -> ValidationResult { - Err(ValidationError::InvalidProofOfWork) - } - - fn sync_operation() -> SyncResult<()> { - Err(SyncError::SyncInProgress) - } - - fn wallet_operation() -> WalletResult { - Err(WalletError::BalanceOverflow) - } - - assert!(network_operation().is_err()); - assert!(storage_operation().is_err()); - assert!(validation_operation().is_err()); - assert!(sync_operation().is_err()); - assert!(wallet_operation().is_err()); -} - -#[test] -#[ignore] -fn test_error_display_formatting() { - // Test that errors format nicely for user display - let errors: Vec> = vec![ - Box::new(NetworkError::ConnectionFailed( - "peer1.example.com:9999 - Connection timed out after 30s".to_string(), - )), - Box::new(StorageError::WriteFailed( - "Cannot write to /var/lib/dash-spv/headers.dat: No space left on device (28)" - .to_string(), - )), - Box::new(ValidationError::InvalidHeaderChain( - "Block 523412: Previous block hash mismatch. Expected: 0x1234..., Got: 0x5678..." - .to_string(), - )), - Box::new(SyncError::Timeout( - "No response from peer after 60 seconds during header download".to_string(), - )), - Box::new(WalletError::TransactionValidation( - "Transaction abc123... has invalid signature in input 0".to_string(), - )), - ]; - - for error in errors { - let formatted = format!("{}", error); - assert!(!formatted.is_empty()); - assert!(formatted.len() > 10); // Should have meaningful content - - // Test that error chain formatting works - let debug_formatted = format!("{:?}", error); - assert!(debug_formatted.len() > formatted.len()); // Debug format should be more verbose - } -} - -#[test] -fn test_sync_error_deprecated_variant() { - // Test that deprecated SyncFailed variant still works but is marked deprecated - #[allow(deprecated)] - let error = SyncError::SyncFailed("This should not be used".to_string()); - - assert_eq!(error.category(), "unknown"); - assert!(error.to_string().contains("This should not be used")); -} - -#[test] -fn test_error_source_chain() { - // Test std::error::Error source() implementation - let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "Access denied"); - let storage_err = StorageError::Io(io_err); - let spv_err = SpvError::Storage(storage_err); - - // Should be able to walk the error chain - let mut error_messages = vec![]; - let mut current_error: &dyn std::error::Error = &spv_err; - - loop { - error_messages.push(current_error.to_string()); - match current_error.source() { - Some(source) => current_error = source, - None => break, - } - } - - assert!(error_messages.len() >= 2); - assert!(error_messages[0].contains("Storage error")); - assert!(error_messages.iter().any(|m| m.contains("Access denied"))); -} From 6239e3feaae344f60fd201d3406279eb7df5784d Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 22:22:39 +0000 Subject: [PATCH 02/15] removed unused error and Config validation reuturn the Config error instead of a String --- dash-spv-ffi/src/error.rs | 2 -- dash-spv/src/client/config.rs | 11 +++--- dash-spv/src/client/config_test.rs | 4 +-- dash-spv/src/client/core.rs | 2 +- dash-spv/src/client/lifecycle.rs | 2 +- dash-spv/src/error.rs | 56 ------------------------------ 6 files changed, 10 insertions(+), 67 deletions(-) diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 18f861671..7e2467e18 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -59,9 +59,7 @@ impl From for FFIErrorCode { SpvError::Sync(_) => FFIErrorCode::SyncError, SpvError::Io(_) => FFIErrorCode::RuntimeError, SpvError::Config(_) => FFIErrorCode::ConfigError, - SpvError::Parse(_) => FFIErrorCode::ValidationError, SpvError::Logging(_) => FFIErrorCode::RuntimeError, - SpvError::Wallet(_) => FFIErrorCode::WalletError, SpvError::QuorumLookupError(_) => FFIErrorCode::ValidationError, SpvError::General(_) => FFIErrorCode::Unknown, } diff --git a/dash-spv/src/client/config.rs b/dash-spv/src/client/config.rs index 0505b93e1..c82942ba5 100644 --- a/dash-spv/src/client/config.rs +++ b/dash-spv/src/client/config.rs @@ -7,6 +7,7 @@ use dashcore::Network; // Serialization removed due to complex Address types use crate::types::ValidationMode; +use crate::SpvError; /// Strategy for handling mempool (unconfirmed) transactions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -192,18 +193,18 @@ impl ClientConfig { } /// Validate the configuration. - pub fn validate(&self) -> Result<(), String> { + pub fn validate(&self) -> Result<(), SpvError> { // Note: Empty peers list is now valid - DNS discovery will be used automatically if self.max_peers == 0 { - return Err("max_peers must be > 0".to_string()); + return Err(SpvError::Config(String::from("max_peers must be > 0"))); } // Mempool validation if self.enable_mempool_tracking && self.max_mempool_transactions == 0 { - return Err( - "max_mempool_transactions must be > 0 when mempool tracking is enabled".to_string() - ); + return Err(SpvError::Config(String::from( + "max_mempool_transactions must be > 0 when mempool tracking is enabled", + ))); } Ok(()) diff --git a/dash-spv/src/client/config_test.rs b/dash-spv/src/client/config_test.rs index 6c564799e..8b853d729 100644 --- a/dash-spv/src/client/config_test.rs +++ b/dash-spv/src/client/config_test.rs @@ -103,7 +103,7 @@ mod tests { let result = config.validate(); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "max_peers must be > 0"); + assert!(result.unwrap_err().to_string().contains("max_peers must be > 0")); } #[test] @@ -116,7 +116,7 @@ mod tests { let result = config.validate(); assert!(result.is_err()); - assert!(result.unwrap_err().contains("max_mempool_transactions must be > 0")); + assert!(result.unwrap_err().to_string().contains("max_mempool_transactions must be > 0")); } // Removed selective strategy validation test; Selective variant no longer exists diff --git a/dash-spv/src/client/core.rs b/dash-spv/src/client/core.rs index b41a7aeae..da641e93b 100644 --- a/dash-spv/src/client/core.rs +++ b/dash-spv/src/client/core.rs @@ -269,7 +269,7 @@ impl DashSpvClient Result<()> { // Validate new configuration - new_config.validate().map_err(SpvError::Config)?; + new_config.validate()?; // Ensure network hasn't changed if new_config.network != self.config.network { diff --git a/dash-spv/src/client/lifecycle.rs b/dash-spv/src/client/lifecycle.rs index ce061bc8d..5c66be8cb 100644 --- a/dash-spv/src/client/lifecycle.rs +++ b/dash-spv/src/client/lifecycle.rs @@ -34,7 +34,7 @@ impl DashSpvClient>, ) -> Result { // Validate configuration - config.validate().map_err(SpvError::Config)?; + config.validate()?; // Initialize state for the network let state = Arc::new(RwLock::new(ChainState::new_for_network(config.network))); diff --git a/dash-spv/src/error.rs b/dash-spv/src/error.rs index 04229abd1..4fab71f99 100644 --- a/dash-spv/src/error.rs +++ b/dash-spv/src/error.rs @@ -30,35 +30,13 @@ pub enum SpvError { #[error("General error: {0}")] General(String), - #[error("Parse error: {0}")] - Parse(#[from] ParseError), - #[error("Logging error: {0}")] Logging(#[from] LoggingError), - #[error("Wallet error: {0}")] - Wallet(#[from] WalletError), - #[error("Quorum lookup error: {0}")] QuorumLookupError(String), } -/// Parse-related errors. -#[derive(Debug, Error)] -pub enum ParseError { - #[error("Invalid network address: {0}")] - InvalidAddress(String), - - #[error("Invalid network name: {0}")] - InvalidNetwork(String), - - #[error("Missing required argument: {0}")] - MissingArgument(String), - - #[error("Invalid argument value for {0}: {1}")] - InvalidArgument(String, String), -} - /// Logging-related errors. #[derive(Debug, Error)] pub enum LoggingError { @@ -229,37 +207,3 @@ pub type SyncResult = std::result::Result; /// Type alias for logging operation results. pub type LoggingResult = std::result::Result; - -/// Wallet-related errors. -#[derive(Debug, Error)] -pub enum WalletError { - #[error("Balance calculation overflow")] - BalanceOverflow, - - #[error("Unsupported address type: {0}")] - UnsupportedAddressType(String), - - #[error("UTXO not found: {0}")] - UtxoNotFound(dashcore::OutPoint), - - #[error("Invalid script pubkey")] - InvalidScriptPubkey, - - #[error("Wallet not initialized")] - NotInitialized, - - #[error("Transaction validation failed: {0}")] - TransactionValidation(String), - - #[error("Invalid transaction output at index {0}")] - InvalidOutput(usize), - - #[error("Address error: {0}")] - AddressError(String), - - #[error("Script error: {0}")] - ScriptError(String), -} - -/// Type alias for wallet operation results. -pub type WalletResult = std::result::Result; From ba92a3ada381b08bf1fe206112589401c6ca9cd7 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 22:44:09 +0000 Subject: [PATCH 03/15] spv io error removed --- dash-spv-ffi/src/error.rs | 1 - .../tests/unit/test_error_handling.rs | 3 --- dash-spv/src/error.rs | 21 ++++++++----------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 7e2467e18..1f6c4d0b3 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -57,7 +57,6 @@ impl From for FFIErrorCode { SpvError::Storage(_) => FFIErrorCode::StorageError, SpvError::Validation(_) => FFIErrorCode::ValidationError, SpvError::Sync(_) => FFIErrorCode::SyncError, - SpvError::Io(_) => FFIErrorCode::RuntimeError, SpvError::Config(_) => FFIErrorCode::ConfigError, SpvError::Logging(_) => FFIErrorCode::RuntimeError, SpvError::QuorumLookupError(_) => FFIErrorCode::ValidationError, diff --git a/dash-spv-ffi/tests/unit/test_error_handling.rs b/dash-spv-ffi/tests/unit/test_error_handling.rs index f47f48c09..32e2cec00 100644 --- a/dash-spv-ffi/tests/unit/test_error_handling.rs +++ b/dash-spv-ffi/tests/unit/test_error_handling.rs @@ -123,9 +123,6 @@ mod tests { let sync_err = SpvError::Sync(SyncError::Timeout("Test timeout".to_string())); assert_eq!(FFIErrorCode::from(sync_err) as i32, FFIErrorCode::SyncError as i32); - let io_err = SpvError::Io(std::io::Error::other("test")); - assert_eq!(FFIErrorCode::from(io_err) as i32, FFIErrorCode::RuntimeError as i32); - let config_err = SpvError::Config("test".to_string()); assert_eq!(FFIErrorCode::from(config_err) as i32, FFIErrorCode::ConfigError as i32); } diff --git a/dash-spv/src/error.rs b/dash-spv/src/error.rs index 4fab71f99..269d6096b 100644 --- a/dash-spv/src/error.rs +++ b/dash-spv/src/error.rs @@ -9,6 +9,15 @@ pub enum SpvError { #[error("Channel failure for: {0} - Failure: {1}")] ChannelFailure(String, String), + #[error("Configuration error: {0}")] + Config(String), + + #[error("Quorum lookup error: {0}")] + QuorumLookupError(String), + + #[error("General error: {0}")] + General(String), + #[error("Network error: {0}")] Network(#[from] NetworkError), @@ -21,20 +30,8 @@ pub enum SpvError { #[error("Sync error: {0}")] Sync(#[from] SyncError), - #[error("Configuration error: {0}")] - Config(String), - - #[error("IO error: {0}")] - Io(#[from] io::Error), - - #[error("General error: {0}")] - General(String), - #[error("Logging error: {0}")] Logging(#[from] LoggingError), - - #[error("Quorum lookup error: {0}")] - QuorumLookupError(String), } /// Logging-related errors. From a3f0ad42b6c053bbf798063a686892c229ebefad Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:05:40 +0000 Subject: [PATCH 04/15] removed redundant error conversions --- dash-spv/src/client/lifecycle.rs | 17 ++----- dash-spv/src/client/message_handler.rs | 62 ++++++++------------------ 2 files changed, 21 insertions(+), 58 deletions(-) diff --git a/dash-spv/src/client/lifecycle.rs b/dash-spv/src/client/lifecycle.rs index 5c66be8cb..7327fb023 100644 --- a/dash-spv/src/client/lifecycle.rs +++ b/dash-spv/src/client/lifecycle.rs @@ -114,14 +114,7 @@ impl DashSpvClient DashSpvClient DashSpvClient MessageHandle } // Move to sync manager without cloning - return self - .sync_manager + self.sync_manager .handle_message(message, &mut *self.network, &mut *self.storage) - .await - .map_err(|e| { - tracing::error!("Sequential sync manager error handling message: {}", e); - SpvError::Sync(e) - }); + .await?; + return Ok(()); } NetworkMessage::MnListDiff(ref diff) => { tracing::info!("📨 Received MnListDiff message: {} new masternodes, {} deleted masternodes, {} quorums", diff.new_masternodes.len(), diff.deleted_masternodes.len(), diff.new_quorums.len()); // Move to sync manager without cloning - return self - .sync_manager + self.sync_manager .handle_message(message, &mut *self.network, &mut *self.storage) - .await - .map_err(|e| { - tracing::error!("Sequential sync manager error handling message: {}", e); - SpvError::Sync(e) - }); + .await?; + return Ok(()); } NetworkMessage::CFHeaders(ref cf_headers) => { // Try to include the peer address for better diagnostics @@ -111,14 +103,10 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle } } // Move to sync manager without cloning - return self - .sync_manager + self.sync_manager .handle_message(message, &mut *self.network, &mut *self.storage) - .await - .map_err(|e| { - tracing::error!("Sequential sync manager error handling message: {}", e); - SpvError::Sync(e) - }); + .await?; + return Ok(()); } NetworkMessage::QRInfo(ref qr_info) => { tracing::info!( @@ -127,14 +115,10 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle qr_info.quorum_snapshot_list.len() ); // Move to sync manager without cloning - return self - .sync_manager + self.sync_manager .handle_message(message, &mut *self.network, &mut *self.storage) - .await - .map_err(|e| { - tracing::error!("Sequential sync manager error handling QRInfo: {}", e); - SpvError::Sync(e) - }); + .await?; + return Ok(()); } NetworkMessage::Headers(_) | NetworkMessage::CFilter(_) => { // Headers and CFilters are relatively small, cloning is acceptable @@ -200,18 +184,9 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle // 1) Ensure header processing and chain tip update for this block // Route the header through the sequential sync manager as a Headers message let headers_msg = NetworkMessage::Headers(vec![block.header]); - if let Err(e) = self - .sync_manager + self.sync_manager .handle_message(&headers_msg, &mut *self.network, &mut *self.storage) - .await - { - tracing::error!( - "❌ Failed to process header for block {} via sync manager: {}", - block_hash, - e - ); - return Err(SpvError::Sync(e)); - } + .await? } NetworkMessage::Inv(inv) => { tracing::debug!("Received inventory message with {} items", inv.len()); @@ -384,14 +359,14 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle if !chainlocks_to_request.is_empty() { tracing::info!("Requesting {} ChainLocks", chainlocks_to_request.len()); let getdata = NetworkMessage::GetData(chainlocks_to_request); - self.network.send_message(getdata).await.map_err(SpvError::Network)?; + self.network.send_message(getdata).await?; } // Auto-request InstantLocks (only when synced and masternodes available; gated above) if !islocks_to_request.is_empty() { tracing::info!("Requesting {} InstantLocks", islocks_to_request.len()); let getdata = NetworkMessage::GetData(islocks_to_request); - self.network.send_message(getdata).await.map_err(SpvError::Network)?; + self.network.send_message(getdata).await?; } // For blocks announced via inventory during tip sync, request full blocks for privacy @@ -433,8 +408,7 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle // request filter headers and filters as needed self.sync_manager .handle_new_headers(headers, &mut *self.network, &mut *self.storage) - .await - .map_err(SpvError::Sync)?; + .await?; Ok(()) } From 11191518cbd715c0df40a2ce5268f362efd83146 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:09:16 +0000 Subject: [PATCH 05/15] SpvError renamed to Error following Rust pattern avoiding use as statements usage --- dash-spv-ffi/src/broadcast.rs | 4 +- dash-spv-ffi/src/client.rs | 46 ++++++++----------- dash-spv-ffi/src/error.rs | 24 +++++----- .../tests/unit/test_error_handling.rs | 12 ++--- dash-spv/src/client/chainlock.rs | 10 ++-- dash-spv/src/client/config.rs | 8 ++-- dash-spv/src/client/core.rs | 6 +-- dash-spv/src/client/interface.rs | 8 ++-- dash-spv/src/client/lifecycle.rs | 12 ++--- dash-spv/src/client/queries.rs | 12 ++--- dash-spv/src/client/sync_coordinator.rs | 14 +++--- dash-spv/src/client/transactions.rs | 8 ++-- dash-spv/src/error.rs | 6 +-- dash-spv/src/lib.rs | 2 +- dash-spv/src/main.rs | 12 ++--- dash-spv/src/network/discovery.rs | 2 +- dash-spv/src/network/manager.rs | 2 +- dash-spv/src/network/persist.rs | 2 +- dash-spv/src/network/pool.rs | 2 +- 19 files changed, 91 insertions(+), 101 deletions(-) diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs index f61206dac..13f81da62 100644 --- a/dash-spv-ffi/src/broadcast.rs +++ b/dash-spv-ffi/src/broadcast.rs @@ -44,14 +44,14 @@ pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( let client = &(*client); let inner = client.inner.clone(); - let result: Result<(), dash_spv::SpvError> = client.runtime.block_on(async { + let result: Result<(), dash_spv::Error> = client.runtime.block_on(async { // Take the client out to avoid holding the lock across await let spv_client = { let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 8848d188b..385edd015 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -186,7 +186,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new( DashSpvClient::new(client_config, network, storage, wallet).await } (Err(e), _) => Err(e), - (_, Err(e)) => Err(dash_spv::SpvError::Storage(e)), + (_, Err(e)) => Err(dash_spv::Error::Storage(e)), } }); @@ -379,7 +379,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_drain_events(client: *mut FFIDashSp FFIErrorCode::Success as i32 } -fn stop_client_internal(client: &mut FFIDashSpvClient) -> Result<(), dash_spv::SpvError> { +fn stop_client_internal(client: &mut FFIDashSpvClient) -> Result<(), dash_spv::Error> { client.shutdown_token.cancel(); // Ensure callbacks are cleared so no further progress/completion notifications fire. @@ -400,7 +400,7 @@ fn stop_client_internal(client: &mut FFIDashSpvClient) -> Result<(), dash_spv::S match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -440,9 +440,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_update_config( let mut guard = client.inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::SpvError::Config("Client not initialized".to_string())) - } + None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), } }; @@ -480,7 +478,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_start(client: *mut FFIDashSpvClient match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -558,9 +556,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_test_sync(client: *mut FFIDashSpvCl let mut guard = client.inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::SpvError::Config("Client not initialized".to_string())) - } + None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), } }; tracing::info!("Starting test sync..."); @@ -596,7 +592,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_test_sync(client: *mut FFIDashSpvCl } else { let msg = "No headers downloaded".to_string(); tracing::error!("❌ {}", msg); - Err(dash_spv::SpvError::Sync(dash_spv::SyncError::Network(msg))) + Err(dash_spv::Error::Sync(dash_spv::SyncError::Network(msg))) }; // put client back @@ -765,7 +761,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress( match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Config( + return Err(dash_spv::Error::Config( "Client not initialized".to_string(), )) } @@ -893,7 +889,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_sync_progress( match guard.take() { Some(c) => c, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -933,7 +929,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_stats( match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -978,9 +974,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_tip_hash( let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => { - return Err(dash_spv::SpvError::Config("Client not initialized".to_string())) - } + None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), } }; let tip = spv_client.tip_hash().await; @@ -1031,9 +1025,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_tip_height( let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => { - return Err(dash_spv::SpvError::Config("Client not initialized".to_string())) - } + None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), } }; let height = spv_client.tip_height().await; @@ -1070,9 +1062,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_clear_storage(client: *mut FFIDashS let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => { - return Err(dash_spv::SpvError::Config("Client not initialized".to_string())) - } + None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), } }; @@ -1232,13 +1222,13 @@ pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( let client = &(*client); let inner = client.inner.clone(); - let result: Result<(), dash_spv::SpvError> = client.runtime.block_on(async { + let result: Result<(), dash_spv::Error> = client.runtime.block_on(async { let mut guard = inner.lock().unwrap(); if let Some(ref mut _spv_client) = *guard { // TODO: rescan_from_height not yet implemented in dash-spv - Err(dash_spv::SpvError::Config("Not implemented".to_string())) + Err(dash_spv::Error::Config("Not implemented".to_string())) } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -1275,7 +1265,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_enable_mempool_tracking( match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } @@ -1333,7 +1323,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_record_send( match guard.take() { Some(client) => client, None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( + return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( "Client not initialized".to_string(), ))) } diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 1f6c4d0b3..22bdc96d0 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -1,4 +1,4 @@ -use dash_spv::error::SpvError; +use dash_spv::error::Error; use std::ffi::CString; use std::os::raw::c_char; use std::sync::Mutex; @@ -49,18 +49,18 @@ pub extern "C" fn dash_spv_ffi_clear_error() { clear_last_error(); } -impl From for FFIErrorCode { - fn from(err: SpvError) -> Self { +impl From for FFIErrorCode { + fn from(err: Error) -> Self { match err { - SpvError::ChannelFailure(_, _) => FFIErrorCode::RuntimeError, - SpvError::Network(_) => FFIErrorCode::NetworkError, - SpvError::Storage(_) => FFIErrorCode::StorageError, - SpvError::Validation(_) => FFIErrorCode::ValidationError, - SpvError::Sync(_) => FFIErrorCode::SyncError, - SpvError::Config(_) => FFIErrorCode::ConfigError, - SpvError::Logging(_) => FFIErrorCode::RuntimeError, - SpvError::QuorumLookupError(_) => FFIErrorCode::ValidationError, - SpvError::General(_) => FFIErrorCode::Unknown, + Error::ChannelFailure(_, _) => FFIErrorCode::RuntimeError, + Error::Network(_) => FFIErrorCode::NetworkError, + Error::Storage(_) => FFIErrorCode::StorageError, + Error::Validation(_) => FFIErrorCode::ValidationError, + Error::Sync(_) => FFIErrorCode::SyncError, + Error::Config(_) => FFIErrorCode::ConfigError, + Error::Logging(_) => FFIErrorCode::RuntimeError, + Error::QuorumLookupError(_) => FFIErrorCode::ValidationError, + Error::General(_) => FFIErrorCode::Unknown, } } } diff --git a/dash-spv-ffi/tests/unit/test_error_handling.rs b/dash-spv-ffi/tests/unit/test_error_handling.rs index 32e2cec00..0deb4b2a6 100644 --- a/dash-spv-ffi/tests/unit/test_error_handling.rs +++ b/dash-spv-ffi/tests/unit/test_error_handling.rs @@ -109,21 +109,21 @@ mod tests { assert_eq!(FFIErrorCode::Unknown as i32, 99); // Test conversions from SpvError - use dash_spv::{NetworkError, SpvError, StorageError, SyncError, ValidationError}; + use dash_spv::{Error, NetworkError, StorageError, SyncError, ValidationError}; - let net_err = SpvError::Network(NetworkError::ConnectionFailed("test".to_string())); + let net_err = Error::Network(NetworkError::ConnectionFailed("test".to_string())); assert_eq!(FFIErrorCode::from(net_err) as i32, FFIErrorCode::NetworkError as i32); - let storage_err = SpvError::Storage(StorageError::NotFound("test".to_string())); + let storage_err = Error::Storage(StorageError::NotFound("test".to_string())); assert_eq!(FFIErrorCode::from(storage_err) as i32, FFIErrorCode::StorageError as i32); - let val_err = SpvError::Validation(ValidationError::InvalidProofOfWork); + let val_err = Error::Validation(ValidationError::InvalidProofOfWork); assert_eq!(FFIErrorCode::from(val_err) as i32, FFIErrorCode::ValidationError as i32); - let sync_err = SpvError::Sync(SyncError::Timeout("Test timeout".to_string())); + let sync_err = Error::Sync(SyncError::Timeout("Test timeout".to_string())); assert_eq!(FFIErrorCode::from(sync_err) as i32, FFIErrorCode::SyncError as i32); - let config_err = SpvError::Config("test".to_string()); + let config_err = Error::Config("test".to_string()); assert_eq!(FFIErrorCode::from(config_err) as i32, FFIErrorCode::ConfigError as i32); } diff --git a/dash-spv/src/client/chainlock.rs b/dash-spv/src/client/chainlock.rs index 2c374ca4c..ca5fe555f 100644 --- a/dash-spv/src/client/chainlock.rs +++ b/dash-spv/src/client/chainlock.rs @@ -8,7 +8,7 @@ use std::sync::Arc; -use crate::error::{Result, SpvError}; +use crate::error::{Error, Result}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::SpvEvent; @@ -41,7 +41,7 @@ impl DashSpvClient DashSpvClient DashSpvClient DashSpvClient { tracing::error!("Failed to validate pending ChainLocks: {}", e); - Err(SpvError::Validation(e)) + Err(Error::Validation(e)) } } } diff --git a/dash-spv/src/client/config.rs b/dash-spv/src/client/config.rs index c82942ba5..b3bcd8455 100644 --- a/dash-spv/src/client/config.rs +++ b/dash-spv/src/client/config.rs @@ -7,7 +7,7 @@ use dashcore::Network; // Serialization removed due to complex Address types use crate::types::ValidationMode; -use crate::SpvError; +use crate::Error; /// Strategy for handling mempool (unconfirmed) transactions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -193,16 +193,16 @@ impl ClientConfig { } /// Validate the configuration. - pub fn validate(&self) -> Result<(), SpvError> { + pub fn validate(&self) -> Result<(), Error> { // Note: Empty peers list is now valid - DNS discovery will be used automatically if self.max_peers == 0 { - return Err(SpvError::Config(String::from("max_peers must be > 0"))); + return Err(Error::Config(String::from("max_peers must be > 0"))); } // Mempool validation if self.enable_mempool_tracking && self.max_mempool_transactions == 0 { - return Err(SpvError::Config(String::from( + return Err(Error::Config(String::from( "max_mempool_transactions must be > 0 when mempool tracking is enabled", ))); } diff --git a/dash-spv/src/client/core.rs b/dash-spv/src/client/core.rs index da641e93b..b3da06241 100644 --- a/dash-spv/src/client/core.rs +++ b/dash-spv/src/client/core.rs @@ -15,7 +15,7 @@ use tokio::sync::{mpsc, Mutex, RwLock}; use crate::terminal::TerminalUI; use crate::chain::ChainLockManager; -use crate::error::{Result, SpvError}; +use crate::error::{Error, Result}; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -205,7 +205,7 @@ impl DashSpvClient DashSpvClient = std::result::Result; +pub type Result = std::result::Result; pub type GetQuorumByHeightResult = Result; async fn receive(context: String, receiver: oneshot::Receiver) -> Result { - receiver.await.map_err(|error| SpvError::ChannelFailure(context, error.to_string())) + receiver.await.map_err(|error| Error::ChannelFailure(context, error.to_string())) } pub enum DashSpvClientCommand { @@ -28,7 +28,7 @@ impl DashSpvClientCommand { context: String, sender: mpsc::UnboundedSender, ) -> Result<()> { - sender.send(self).map_err(|error| SpvError::ChannelFailure(context, error.to_string()))?; + sender.send(self).map_err(|error| Error::ChannelFailure(context, error.to_string()))?; Ok(()) } } diff --git a/dash-spv/src/client/lifecycle.rs b/dash-spv/src/client/lifecycle.rs index 7327fb023..2d5bd9fcb 100644 --- a/dash-spv/src/client/lifecycle.rs +++ b/dash-spv/src/client/lifecycle.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use tokio::sync::{mpsc, Mutex, RwLock}; use crate::chain::ChainLockManager; -use crate::error::{Result, SpvError}; +use crate::error::{Error, Result}; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -53,7 +53,7 @@ impl DashSpvClient DashSpvClient DashSpvClient DashSpvClient DashSpvClient DashSpvClient() .ok_or_else(|| { - SpvError::Config("Network manager does not support peer disconnection".to_string()) + Error::Config("Network manager does not support peer disconnection".to_string()) })?; network.disconnect_peer(addr, reason).await @@ -95,7 +95,7 @@ impl DashSpvClient { @@ -104,7 +104,7 @@ impl DashSpvClient DashSpvClient DashSpvClient Result { // This method requires wallet-specific functionality not in WalletInterface // The wallet should expose balance info through its own interface - Err(SpvError::Config( + Err(Error::Config( "Address balance queries should be made directly to the wallet implementation" .to_string(), )) diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index 356e3c20c..a68e74919 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -12,7 +12,7 @@ use super::{DashSpvClient, MessageHandler}; use crate::client::interface::DashSpvClientCommand; -use crate::error::{Result, SpvError}; +use crate::error::{Error, Result}; use crate::network::constants::MESSAGE_RECEIVE_TIMEOUT; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -34,7 +34,7 @@ impl DashSpvClient Result<()> { let running = self.running.read().await; if !*running { - return Err(SpvError::Config("Client not running".to_string())); + return Err(Error::Config("Client not running".to_string())); } drop(running); @@ -431,13 +431,13 @@ impl DashSpvClient { + Error::Network(_) => { tracing::warn!("Network error during message handling - may recover automatically"); } - SpvError::Storage(_) => { + Error::Storage(_) => { tracing::error!("Storage error during message handling - this may affect data consistency"); } - SpvError::Validation(_) => { + Error::Validation(_) => { tracing::warn!("Validation error during message handling - message rejected"); } _ => { @@ -522,7 +522,7 @@ impl DashSpvClient Result<()> { @@ -535,7 +535,7 @@ impl DashSpvClient { let result = self.get_quorum_at_height(height, quorum_type, quorum_hash); if sender.send(result).is_err() { - return Err(SpvError::ChannelFailure( + return Err(Error::ChannelFailure( format!("GetQuorumByHeight({height}, {quorum_type}, {quorum_hash})"), "Failed to send quorum result".to_string(), )); diff --git a/dash-spv/src/client/transactions.rs b/dash-spv/src/client/transactions.rs index e45285a2d..bebdc6de1 100644 --- a/dash-spv/src/client/transactions.rs +++ b/dash-spv/src/client/transactions.rs @@ -1,6 +1,6 @@ //! Transaction-related client APIs (e.g., broadcasting) -use crate::error::{Result, SpvError}; +use crate::error::{Error, Result}; use crate::network::NetworkManager; use crate::storage::StorageManager; use dashcore::network::message::NetworkMessage; @@ -16,11 +16,11 @@ impl DashSpvClient() .ok_or_else(|| { - SpvError::Config("Network manager does not support broadcasting".to_string()) + Error::Config("Network manager does not support broadcasting".to_string()) })?; if network.peer_count() == 0 { - return Err(SpvError::Network(crate::error::NetworkError::NotConnected)); + return Err(Error::Network(crate::error::NetworkError::NotConnected)); } let message = NetworkMessage::Tx(tx.clone()); @@ -38,7 +38,7 @@ impl DashSpvClient = std::result::Result; +/// Type alias for Result with dash_spv::Error. +pub type Result = std::result::Result; /// Type alias for network operation results. pub type NetworkResult = std::result::Result; diff --git a/dash-spv/src/lib.rs b/dash-spv/src/lib.rs index 472784574..079df5f14 100644 --- a/dash-spv/src/lib.rs +++ b/dash-spv/src/lib.rs @@ -75,7 +75,7 @@ pub mod validation; // Re-export main types for convenience pub use client::{ClientConfig, DashSpvClient}; pub use error::{ - LoggingError, LoggingResult, NetworkError, SpvError, StorageError, SyncError, ValidationError, + Error, LoggingError, LoggingResult, NetworkError, StorageError, SyncError, ValidationError, }; pub use logging::{init_console_logging, init_logging, LogFileConfig, LoggingConfig, LoggingGuard}; pub use tracing::level_filters::LevelFilter; diff --git a/dash-spv/src/main.rs b/dash-spv/src/main.rs index 5566c5116..e0a35882f 100644 --- a/dash-spv/src/main.rs +++ b/dash-spv/src/main.rs @@ -18,13 +18,13 @@ async fn main() { eprintln!("Error: {}", e); // Provide specific exit codes for different error types - let exit_code = if let Some(spv_error) = e.downcast_ref::() { + let exit_code = if let Some(spv_error) = e.downcast_ref::() { match spv_error { - dash_spv::SpvError::Network(_) => 1, - dash_spv::SpvError::Storage(_) => 2, - dash_spv::SpvError::Validation(_) => 3, - dash_spv::SpvError::Config(_) => 4, - dash_spv::SpvError::Parse(_) => 5, + dash_spv::Error::Network(_) => 1, + dash_spv::Error::Storage(_) => 2, + dash_spv::Error::Validation(_) => 3, + dash_spv::Error::Config(_) => 4, + dash_spv::Error::Parse(_) => 5, _ => 255, } } else { diff --git a/dash-spv/src/network/discovery.rs b/dash-spv/src/network/discovery.rs index b0f4408a0..d6611a06e 100644 --- a/dash-spv/src/network/discovery.rs +++ b/dash-spv/src/network/discovery.rs @@ -6,7 +6,7 @@ use hickory_resolver::name_server::TokioConnectionProvider; use hickory_resolver::TokioResolver; use std::net::{IpAddr, SocketAddr}; -use crate::error::SpvError as Error; +use crate::error::Error; use crate::network::constants::{MAINNET_DNS_SEEDS, TESTNET_DNS_SEEDS}; /// DNS discovery for finding initial peers diff --git a/dash-spv/src/network/manager.rs b/dash-spv/src/network/manager.rs index 9f41d6566..653e446b5 100644 --- a/dash-spv/src/network/manager.rs +++ b/dash-spv/src/network/manager.rs @@ -12,7 +12,7 @@ use tokio::time; use crate::client::config::MempoolStrategy; use crate::client::ClientConfig; -use crate::error::{NetworkError, NetworkResult, SpvError as Error}; +use crate::error::{Error, NetworkError, NetworkResult}; use crate::network::addrv2::AddrV2Handler; use crate::network::constants::*; use crate::network::discovery::DnsDiscovery; diff --git a/dash-spv/src/network/persist.rs b/dash-spv/src/network/persist.rs index 814eedeff..362a1b0bd 100644 --- a/dash-spv/src/network/persist.rs +++ b/dash-spv/src/network/persist.rs @@ -4,7 +4,7 @@ use dashcore::Network; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use crate::error::{SpvError as Error, StorageError}; +use crate::error::{Error, StorageError}; use crate::storage::io::atomic_write; /// Peer persistence for saving and loading known peer addresses diff --git a/dash-spv/src/network/pool.rs b/dash-spv/src/network/pool.rs index b66777859..9688a2cdd 100644 --- a/dash-spv/src/network/pool.rs +++ b/dash-spv/src/network/pool.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::RwLock; -use crate::error::{NetworkError, SpvError as Error}; +use crate::error::{Error, NetworkError}; use crate::network::constants::{MAX_PEERS, MIN_PEERS}; use crate::network::peer::Peer; From 06e17af23653c420ef9f3840b806bc98ba33e64f Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:11:21 +0000 Subject: [PATCH 06/15] removed duplicate Result type --- dash-spv/src/client/interface.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dash-spv/src/client/interface.rs b/dash-spv/src/client/interface.rs index 2c924b506..1955269f6 100644 --- a/dash-spv/src/client/interface.rs +++ b/dash-spv/src/client/interface.rs @@ -1,12 +1,10 @@ -use crate::error::Error; +use crate::error::{Error, Result}; use dashcore::sml::llmq_type::LLMQType; use dashcore::sml::quorum_entry::qualified_quorum_entry::QualifiedQuorumEntry; use dashcore::QuorumHash; use std::fmt::Display; use tokio::sync::{mpsc, oneshot}; -pub type Result = std::result::Result; - pub type GetQuorumByHeightResult = Result; async fn receive(context: String, receiver: oneshot::Receiver) -> Result { From 7b8e31ce29cba6f58930febf6946c3d0aba8c0bf Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:16:59 +0000 Subject: [PATCH 07/15] error mod made private for better API --- dash-spv-ffi/src/error.rs | 2 +- dash-spv/src/lib.rs | 6 ++++-- dash-spv/tests/edge_case_filter_sync_test.rs | 4 ++-- dash-spv/tests/filter_header_verification_test.rs | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 22bdc96d0..adc4dd5a7 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -1,4 +1,4 @@ -use dash_spv::error::Error; +use dash_spv::Error; use std::ffi::CString; use std::os::raw::c_char; use std::sync::Mutex; diff --git a/dash-spv/src/lib.rs b/dash-spv/src/lib.rs index 079df5f14..8cb70616f 100644 --- a/dash-spv/src/lib.rs +++ b/dash-spv/src/lib.rs @@ -59,9 +59,10 @@ #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; +mod error; + pub mod chain; pub mod client; -pub mod error; pub mod logging; pub mod mempool_filter; pub mod network; @@ -75,7 +76,8 @@ pub mod validation; // Re-export main types for convenience pub use client::{ClientConfig, DashSpvClient}; pub use error::{ - Error, LoggingError, LoggingResult, NetworkError, StorageError, SyncError, ValidationError, + Error, LoggingError, LoggingResult, NetworkError, NetworkResult, Result, StorageError, + StorageResult, SyncError, SyncResult, ValidationError, ValidationResult, }; pub use logging::{init_console_logging, init_logging, LogFileConfig, LoggingConfig, LoggingGuard}; pub use tracing::level_filters::LevelFilter; diff --git a/dash-spv/tests/edge_case_filter_sync_test.rs b/dash-spv/tests/edge_case_filter_sync_test.rs index f1fe6a85c..893f0964b 100644 --- a/dash-spv/tests/edge_case_filter_sync_test.rs +++ b/dash-spv/tests/edge_case_filter_sync_test.rs @@ -7,10 +7,10 @@ use tokio::sync::Mutex; use dash_spv::{ client::ClientConfig, - error::NetworkResult, network::NetworkManager, storage::{BlockHeaderStorage, DiskStorageManager, FilterHeaderStorage}, sync::filters::FilterSyncManager, + NetworkResult, }; use dashcore::{ block::Header as BlockHeader, hash_types::FilterHeader, network::message::NetworkMessage, @@ -87,7 +87,7 @@ impl NetworkManager for MockNetworkManager { Vec::new() } - async fn get_peer_best_height(&self) -> dash_spv::error::NetworkResult> { + async fn get_peer_best_height(&self) -> dash_spv::NetworkResult> { Ok(Some(100)) } diff --git a/dash-spv/tests/filter_header_verification_test.rs b/dash-spv/tests/filter_header_verification_test.rs index 60b748294..5c53806b3 100644 --- a/dash-spv/tests/filter_header_verification_test.rs +++ b/dash-spv/tests/filter_header_verification_test.rs @@ -10,11 +10,11 @@ use dash_spv::{ client::ClientConfig, - error::{NetworkError, NetworkResult, SyncError}, network::NetworkManager, storage::{BlockHeaderStorage, DiskStorageManager, FilterHeaderStorage}, sync::filters::FilterSyncManager, types::PeerInfo, + {NetworkError, NetworkResult, SyncError}, }; use dashcore::{ block::{Header as BlockHeader, Version}, @@ -83,7 +83,7 @@ impl NetworkManager for MockNetworkManager { self } - async fn get_peer_best_height(&self) -> dash_spv::error::NetworkResult> { + async fn get_peer_best_height(&self) -> dash_spv::NetworkResult> { Ok(Some(100)) } From 102c41bd9e30e04c26bf314384987ebf042b78d4 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:40:17 +0000 Subject: [PATCH 08/15] using full qualified dash_spv::Error and dash_spv::Result --- dash-spv-ffi/src/broadcast.rs | 2 +- dash-spv-ffi/src/client.rs | 4 +- dash-spv-ffi/src/error.rs | 23 +++-- .../tests/unit/test_error_handling.rs | 12 +-- dash-spv/src/client/chainlock.rs | 17 ++-- dash-spv/src/client/config.rs | 7 +- dash-spv/src/client/core.rs | 11 +-- dash-spv/src/client/interface.rs | 13 +-- dash-spv/src/client/lifecycle.rs | 30 ++++--- dash-spv/src/client/queries.rs | 25 +++--- dash-spv/src/client/sync_coordinator.rs | 23 +++-- dash-spv/src/client/transactions.rs | 9 +- dash-spv/src/network/discovery.rs | 3 +- dash-spv/src/network/manager.rs | 83 ++++++++++--------- dash-spv/src/network/persist.rs | 24 +++--- dash-spv/src/network/pool.rs | 7 +- 16 files changed, 148 insertions(+), 145 deletions(-) diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs index 13f81da62..dff3a86ce 100644 --- a/dash-spv-ffi/src/broadcast.rs +++ b/dash-spv-ffi/src/broadcast.rs @@ -44,7 +44,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( let client = &(*client); let inner = client.inner.clone(); - let result: Result<(), dash_spv::Error> = client.runtime.block_on(async { + let result: dash_spv::Result<()> = client.runtime.block_on(async { // Take the client out to avoid holding the lock across await let spv_client = { let mut guard = inner.lock().unwrap(); diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 385edd015..9f03504b3 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -379,7 +379,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_drain_events(client: *mut FFIDashSp FFIErrorCode::Success as i32 } -fn stop_client_internal(client: &mut FFIDashSpvClient) -> Result<(), dash_spv::Error> { +fn stop_client_internal(client: &mut FFIDashSpvClient) -> dash_spv::Result<()> { client.shutdown_token.cancel(); // Ensure callbacks are cleared so no further progress/completion notifications fire. @@ -1222,7 +1222,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( let client = &(*client); let inner = client.inner.clone(); - let result: Result<(), dash_spv::Error> = client.runtime.block_on(async { + let result: dash_spv::Result<()> = client.runtime.block_on(async { let mut guard = inner.lock().unwrap(); if let Some(ref mut _spv_client) = *guard { // TODO: rescan_from_height not yet implemented in dash-spv diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index adc4dd5a7..5b4b09d69 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -1,4 +1,3 @@ -use dash_spv::Error; use std::ffi::CString; use std::os::raw::c_char; use std::sync::Mutex; @@ -49,18 +48,18 @@ pub extern "C" fn dash_spv_ffi_clear_error() { clear_last_error(); } -impl From for FFIErrorCode { - fn from(err: Error) -> Self { +impl From for FFIErrorCode { + fn from(err: dash_spv::Error) -> Self { match err { - Error::ChannelFailure(_, _) => FFIErrorCode::RuntimeError, - Error::Network(_) => FFIErrorCode::NetworkError, - Error::Storage(_) => FFIErrorCode::StorageError, - Error::Validation(_) => FFIErrorCode::ValidationError, - Error::Sync(_) => FFIErrorCode::SyncError, - Error::Config(_) => FFIErrorCode::ConfigError, - Error::Logging(_) => FFIErrorCode::RuntimeError, - Error::QuorumLookupError(_) => FFIErrorCode::ValidationError, - Error::General(_) => FFIErrorCode::Unknown, + dash_spv::Error::ChannelFailure(_, _) => FFIErrorCode::RuntimeError, + dash_spv::Error::Network(_) => FFIErrorCode::NetworkError, + dash_spv::Error::Storage(_) => FFIErrorCode::StorageError, + dash_spv::Error::Validation(_) => FFIErrorCode::ValidationError, + dash_spv::Error::Sync(_) => FFIErrorCode::SyncError, + dash_spv::Error::Config(_) => FFIErrorCode::ConfigError, + dash_spv::Error::Logging(_) => FFIErrorCode::RuntimeError, + dash_spv::Error::QuorumLookupError(_) => FFIErrorCode::ValidationError, + dash_spv::Error::General(_) => FFIErrorCode::Unknown, } } } diff --git a/dash-spv-ffi/tests/unit/test_error_handling.rs b/dash-spv-ffi/tests/unit/test_error_handling.rs index 0deb4b2a6..b3b3202e1 100644 --- a/dash-spv-ffi/tests/unit/test_error_handling.rs +++ b/dash-spv-ffi/tests/unit/test_error_handling.rs @@ -109,21 +109,21 @@ mod tests { assert_eq!(FFIErrorCode::Unknown as i32, 99); // Test conversions from SpvError - use dash_spv::{Error, NetworkError, StorageError, SyncError, ValidationError}; + use dash_spv::{NetworkError, StorageError, SyncError, ValidationError}; - let net_err = Error::Network(NetworkError::ConnectionFailed("test".to_string())); + let net_err = dash_spv::Error::Network(NetworkError::ConnectionFailed("test".to_string())); assert_eq!(FFIErrorCode::from(net_err) as i32, FFIErrorCode::NetworkError as i32); - let storage_err = Error::Storage(StorageError::NotFound("test".to_string())); + let storage_err = dash_spv::Error::Storage(StorageError::NotFound("test".to_string())); assert_eq!(FFIErrorCode::from(storage_err) as i32, FFIErrorCode::StorageError as i32); - let val_err = Error::Validation(ValidationError::InvalidProofOfWork); + let val_err = dash_spv::Error::Validation(ValidationError::InvalidProofOfWork); assert_eq!(FFIErrorCode::from(val_err) as i32, FFIErrorCode::ValidationError as i32); - let sync_err = Error::Sync(SyncError::Timeout("Test timeout".to_string())); + let sync_err = dash_spv::Error::Sync(SyncError::Timeout("Test timeout".to_string())); assert_eq!(FFIErrorCode::from(sync_err) as i32, FFIErrorCode::SyncError as i32); - let config_err = Error::Config("test".to_string()); + let config_err = dash_spv::Error::Config("test".to_string()); assert_eq!(FFIErrorCode::from(config_err) as i32, FFIErrorCode::ConfigError as i32); } diff --git a/dash-spv/src/client/chainlock.rs b/dash-spv/src/client/chainlock.rs index ca5fe555f..81c14173a 100644 --- a/dash-spv/src/client/chainlock.rs +++ b/dash-spv/src/client/chainlock.rs @@ -8,7 +8,6 @@ use std::sync::Arc; -use crate::error::{Error, Result}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::SpvEvent; @@ -22,7 +21,7 @@ impl DashSpvClient Result<()> { + ) -> crate::Result<()> { tracing::info!( "Processing ChainLock for block {} at height {}", chainlock.block_hash, @@ -41,7 +40,7 @@ impl DashSpvClient DashSpvClient Result<()> { + ) -> crate::Result<()> { tracing::info!("Processing InstantSendLock for tx {}", islock.txid); // Get the masternode engine from sync manager for proper quorum verification let masternode_engine = self.sync_manager.get_masternode_engine().ok_or_else(|| { - Error::Validation(crate::error::ValidationError::MasternodeVerification( + crate::Error::Validation(crate::ValidationError::MasternodeVerification( "Masternode engine not available for InstantLock verification".to_string(), )) })?; @@ -109,7 +108,7 @@ impl DashSpvClient DashSpvClient Result { + pub fn update_chainlock_validation(&self) -> crate::Result { // Check if masternode sync has an engine available if let Some(engine) = self.sync_manager.get_masternode_engine() { // Clone the engine for the ChainLockManager @@ -152,7 +151,7 @@ impl DashSpvClient Result<()> { + pub async fn validate_pending_chainlocks(&mut self) -> crate::Result<()> { let chain_state = self.state.read().await; let mut storage = self.storage.lock().await; @@ -164,7 +163,7 @@ impl DashSpvClient { tracing::error!("Failed to validate pending ChainLocks: {}", e); - Err(Error::Validation(e)) + Err(crate::Error::Validation(e)) } } } diff --git a/dash-spv/src/client/config.rs b/dash-spv/src/client/config.rs index b3bcd8455..3e6de35ef 100644 --- a/dash-spv/src/client/config.rs +++ b/dash-spv/src/client/config.rs @@ -7,7 +7,6 @@ use dashcore::Network; // Serialization removed due to complex Address types use crate::types::ValidationMode; -use crate::Error; /// Strategy for handling mempool (unconfirmed) transactions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -193,16 +192,16 @@ impl ClientConfig { } /// Validate the configuration. - pub fn validate(&self) -> Result<(), Error> { + pub fn validate(&self) -> Result<(), crate::Error> { // Note: Empty peers list is now valid - DNS discovery will be used automatically if self.max_peers == 0 { - return Err(Error::Config(String::from("max_peers must be > 0"))); + return Err(crate::Error::Config(String::from("max_peers must be > 0"))); } // Mempool validation if self.enable_mempool_tracking && self.max_mempool_transactions == 0 { - return Err(Error::Config(String::from( + return Err(crate::Error::Config(String::from( "max_mempool_transactions must be > 0 when mempool tracking is enabled", ))); } diff --git a/dash-spv/src/client/core.rs b/dash-spv/src/client/core.rs index b3da06241..232b1fa1f 100644 --- a/dash-spv/src/client/core.rs +++ b/dash-spv/src/client/core.rs @@ -15,7 +15,6 @@ use tokio::sync::{mpsc, Mutex, RwLock}; use crate::terminal::TerminalUI; use crate::chain::ChainLockManager; -use crate::error::{Error, Result}; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -201,11 +200,11 @@ impl DashSpvClient Result<()> { + pub async fn clear_storage(&mut self) -> crate::Result<()> { // Wipe on-disk persistence fully { let mut storage = self.storage.lock().await; - storage.clear().await.map_err(Error::Storage)?; + storage.clear().await.map_err(crate::Error::Storage)?; } // Reset in-memory chain state to a clean baseline for the current network @@ -267,13 +266,15 @@ impl DashSpvClient Result<()> { + pub async fn update_config(&mut self, new_config: ClientConfig) -> crate::Result<()> { // Validate new configuration new_config.validate()?; // Ensure network hasn't changed if new_config.network != self.config.network { - return Err(Error::Config("Cannot change network on running client".to_string())); + return Err(crate::Error::Config( + "Cannot change network on running client".to_string(), + )); } // Update configuration diff --git a/dash-spv/src/client/interface.rs b/dash-spv/src/client/interface.rs index 1955269f6..122fa41c6 100644 --- a/dash-spv/src/client/interface.rs +++ b/dash-spv/src/client/interface.rs @@ -1,14 +1,13 @@ -use crate::error::{Error, Result}; use dashcore::sml::llmq_type::LLMQType; use dashcore::sml::quorum_entry::qualified_quorum_entry::QualifiedQuorumEntry; use dashcore::QuorumHash; use std::fmt::Display; use tokio::sync::{mpsc, oneshot}; -pub type GetQuorumByHeightResult = Result; +pub type GetQuorumByHeightResult = crate::Result; -async fn receive(context: String, receiver: oneshot::Receiver) -> Result { - receiver.await.map_err(|error| Error::ChannelFailure(context, error.to_string())) +async fn receive(context: String, receiver: oneshot::Receiver) -> crate::Result { + receiver.await.map_err(|error| crate::Error::ChannelFailure(context, error.to_string())) } pub enum DashSpvClientCommand { @@ -25,8 +24,10 @@ impl DashSpvClientCommand { self, context: String, sender: mpsc::UnboundedSender, - ) -> Result<()> { - sender.send(self).map_err(|error| Error::ChannelFailure(context, error.to_string()))?; + ) -> crate::Result<()> { + sender + .send(self) + .map_err(|error| crate::Error::ChannelFailure(context, error.to_string()))?; Ok(()) } } diff --git a/dash-spv/src/client/lifecycle.rs b/dash-spv/src/client/lifecycle.rs index 2d5bd9fcb..1ed693323 100644 --- a/dash-spv/src/client/lifecycle.rs +++ b/dash-spv/src/client/lifecycle.rs @@ -13,7 +13,6 @@ use std::sync::Arc; use tokio::sync::{mpsc, Mutex, RwLock}; use crate::chain::ChainLockManager; -use crate::error::{Error, Result}; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -32,7 +31,7 @@ impl DashSpvClient>, - ) -> Result { + ) -> crate::Result { // Validate configuration config.validate()?; @@ -53,7 +52,7 @@ impl DashSpvClient DashSpvClient Result<()> { + pub async fn start(&mut self) -> crate::Result<()> { { let running = self.running.read().await; if *running { - return Err(Error::Config("Client already running".to_string())); + return Err(crate::Error::Config("Client already running".to_string())); } } @@ -172,7 +171,7 @@ impl DashSpvClient Result<()> { + pub async fn stop(&mut self) -> crate::Result<()> { // Check if already stopped { let running = self.running.read().await; @@ -199,12 +198,12 @@ impl DashSpvClient Result<()> { + pub async fn shutdown(&mut self) -> crate::Result<()> { self.stop().await } /// Initialize genesis block or checkpoint. - pub(super) async fn initialize_genesis_block(&mut self) -> Result<()> { + pub(super) async fn initialize_genesis_block(&mut self) -> crate::Result<()> { // Check if we already have any headers in storage let current_tip = { let storage = self.storage.lock().await; @@ -314,11 +313,10 @@ impl DashSpvClient DashSpvClient DashSpvClient DashSpvClient Result<()> { + pub(super) async fn load_wallet_data(&self) -> crate::Result<()> { tracing::info!("Loading wallet data from storage..."); let _wallet = self.wallet.read().await; diff --git a/dash-spv/src/client/queries.rs b/dash-spv/src/client/queries.rs index dca96d6af..af73979ee 100644 --- a/dash-spv/src/client/queries.rs +++ b/dash-spv/src/client/queries.rs @@ -6,7 +6,6 @@ //! - Balance queries //! - Filter availability checks -use crate::error::{Error, Result}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::AddressBalance; @@ -38,14 +37,20 @@ impl DashSpvClient Result<()> { + pub async fn disconnect_peer( + &self, + addr: &std::net::SocketAddr, + reason: &str, + ) -> crate::Result<()> { // Cast network manager to PeerNetworkManager to access disconnect_peer let network = self .network .as_any() .downcast_ref::() .ok_or_else(|| { - Error::Config("Network manager does not support peer disconnection".to_string()) + crate::Error::Config( + "Network manager does not support peer disconnection".to_string(), + ) })?; network.disconnect_peer(addr, reason).await @@ -72,7 +77,7 @@ impl DashSpvClient Result { + ) -> crate::Result { // First check if we have the masternode list at this height match self.get_masternode_list_at_height(height) { Some(ml) => { @@ -95,7 +100,7 @@ impl DashSpvClient { @@ -104,7 +109,7 @@ impl DashSpvClient DashSpvClient DashSpvClient Result { + ) -> crate::Result { // This method requires wallet-specific functionality not in WalletInterface // The wallet should expose balance info through its own interface - Err(Error::Config( + Err(crate::Error::Config( "Address balance queries should be made directly to the wallet implementation" .to_string(), )) @@ -146,7 +151,7 @@ impl DashSpvClient Result> { + ) -> crate::Result> { // TODO: Get balances from wallet instead of tracking separately // Will be implemented when wallet integration is complete Ok(std::collections::HashMap::new()) diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index a68e74919..a44d992c3 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -12,7 +12,6 @@ use super::{DashSpvClient, MessageHandler}; use crate::client::interface::DashSpvClientCommand; -use crate::error::{Error, Result}; use crate::network::constants::MESSAGE_RECEIVE_TIMEOUT; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -31,10 +30,10 @@ impl DashSpvClient, token: CancellationToken, - ) -> Result<()> { + ) -> crate::Result<()> { let running = self.running.read().await; if !*running { - return Err(Error::Config("Client not running".to_string())); + return Err(crate::Error::Config("Client not running".to_string())); } drop(running); @@ -431,13 +430,13 @@ impl DashSpvClient { + crate::Error::Network(_) => { tracing::warn!("Network error during message handling - may recover automatically"); } - Error::Storage(_) => { + crate::Error::Storage(_) => { tracing::error!("Storage error during message handling - this may affect data consistency"); } - Error::Validation(_) => { + crate::Error::Validation(_) => { tracing::warn!("Validation error during message handling - message rejected"); } _ => { @@ -499,7 +498,7 @@ impl DashSpvClient, shutdown_token: CancellationToken, - ) -> Result<()> { + ) -> crate::Result<()> { let client_token = shutdown_token.clone(); let client_task = tokio::spawn(async move { @@ -522,10 +521,10 @@ impl DashSpvClient Result<()> { + async fn handle_command(&mut self, command: DashSpvClientCommand) -> crate::Result<()> { match command { DashSpvClientCommand::GetQuorumByHeight { height, @@ -535,7 +534,7 @@ impl DashSpvClient { let result = self.get_quorum_at_height(height, quorum_type, quorum_hash); if sender.send(result).is_err() { - return Err(Error::ChannelFailure( + return Err(crate::Error::ChannelFailure( format!("GetQuorumByHeight({height}, {quorum_type}, {quorum_hash})"), "Failed to send quorum result".to_string(), )); @@ -549,7 +548,7 @@ impl DashSpvClient Result<()> { + ) -> crate::Result<()> { // Check if this is a special message that needs client-level processing let needs_special_processing = matches!( &message, @@ -614,7 +613,7 @@ impl DashSpvClient, block_height: u32, - ) -> Result<()> { + ) -> crate::Result<()> { tracing::info!("💰 Balance changes detected in block at height {}:", block_height); for (address, change_sat) in balance_changes { diff --git a/dash-spv/src/client/transactions.rs b/dash-spv/src/client/transactions.rs index bebdc6de1..7ded91d12 100644 --- a/dash-spv/src/client/transactions.rs +++ b/dash-spv/src/client/transactions.rs @@ -1,6 +1,5 @@ //! Transaction-related client APIs (e.g., broadcasting) -use crate::error::{Error, Result}; use crate::network::NetworkManager; use crate::storage::StorageManager; use dashcore::network::message::NetworkMessage; @@ -10,17 +9,17 @@ use super::DashSpvClient; impl DashSpvClient { /// Broadcast a transaction to all connected peers. - pub async fn broadcast_transaction(&self, tx: &dashcore::Transaction) -> Result<()> { + pub async fn broadcast_transaction(&self, tx: &dashcore::Transaction) -> crate::Result<()> { let network = self .network .as_any() .downcast_ref::() .ok_or_else(|| { - Error::Config("Network manager does not support broadcasting".to_string()) + crate::Error::Config("Network manager does not support broadcasting".to_string()) })?; if network.peer_count() == 0 { - return Err(Error::Network(crate::error::NetworkError::NotConnected)); + return Err(crate::Error::Network(crate::error::NetworkError::NotConnected)); } let message = NetworkMessage::Tx(tx.clone()); @@ -38,7 +37,7 @@ impl DashSpvClient Result { + pub async fn new() -> Result { let resolver = hickory_resolver::Resolver::builder_with_config( ResolverConfig::default(), TokioConnectionProvider::default(), diff --git a/dash-spv/src/network/manager.rs b/dash-spv/src/network/manager.rs index 653e446b5..417a308bd 100644 --- a/dash-spv/src/network/manager.rs +++ b/dash-spv/src/network/manager.rs @@ -12,7 +12,6 @@ use tokio::time; use crate::client::config::MempoolStrategy; use crate::client::ClientConfig; -use crate::error::{Error, NetworkError, NetworkResult}; use crate::network::addrv2::AddrV2Handler; use crate::network::constants::*; use crate::network::discovery::DnsDiscovery; @@ -77,7 +76,7 @@ pub struct PeerNetworkManager { impl PeerNetworkManager { /// Create a new peer network manager - pub async fn new(config: &ClientConfig) -> Result { + pub async fn new(config: &ClientConfig) -> Result { let (message_tx, message_rx) = mpsc::channel(1000); let discovery = DnsDiscovery::new().await?; @@ -131,7 +130,7 @@ impl PeerNetworkManager { } /// Start the network manager - pub async fn start(&self) -> Result<(), Error> { + pub async fn start(&self) -> Result<(), crate::Error> { log::info!("Starting peer network manager for {:?}", self.network); let mut peer_addresses = self.initial_peers.clone(); @@ -394,7 +393,7 @@ impl PeerNetworkManager { if let Err(e) = peer_guard.handle_ping(*nonce).await { log::error!("Failed to handle ping from {}: {}", addr, e); // If we can't send pong, connection is likely broken - if matches!(e, NetworkError::ConnectionFailed(_)) { + if matches!(e, crate::NetworkError::ConnectionFailed(_)) { log::warn!("Breaking peer reader loop for {} - failed to send pong response (iteration {})", addr, loop_iteration); break; } @@ -536,11 +535,11 @@ impl PeerNetworkManager { } Err(e) => { match e { - NetworkError::PeerDisconnected => { + crate::NetworkError::PeerDisconnected => { log::info!("Peer {} disconnected", addr); break; } - NetworkError::Timeout => { + crate::NetworkError::Timeout => { log::debug!("Timeout reading from {}, continuing...", addr); // Minor reputation penalty for timeout reputation_manager @@ -556,7 +555,7 @@ impl PeerNetworkManager { log::error!("Fatal error reading from {}: {}", addr, e); // Check if this is a serialization error that might have context - if let NetworkError::Serialization(ref decode_error) = e { + if let crate::NetworkError::Serialization(ref decode_error) = e { let error_msg = decode_error.to_string(); if error_msg.contains("unknown special transaction type") { log::warn!("Peer {} sent block with unsupported transaction type: {}", addr, decode_error); @@ -820,11 +819,11 @@ impl PeerNetworkManager { } /// Send a message to a single peer (using sticky peer selection for sync consistency) - async fn send_to_single_peer(&self, message: NetworkMessage) -> NetworkResult<()> { + async fn send_to_single_peer(&self, message: NetworkMessage) -> crate::NetworkResult<()> { let peers = self.pool.get_all_peers().await; if peers.is_empty() { - return Err(NetworkError::ConnectionFailed("No connected peers".to_string())); + return Err(crate::NetworkError::ConnectionFailed("No connected peers".to_string())); } // For filter-related messages, we need a peer that supports compact filters @@ -854,7 +853,7 @@ impl PeerNetworkManager { } None => { log::warn!("No peers support compact filters, cannot send {}", message.cmd()); - return Err(NetworkError::ProtocolError( + return Err(crate::NetworkError::ProtocolError( "No peers support compact filters".to_string(), )); } @@ -921,10 +920,9 @@ impl PeerNetworkManager { }; // Find the peer for the selected address - let (addr, peer) = peers - .iter() - .find(|(a, _)| *a == selected_peer) - .ok_or_else(|| NetworkError::ConnectionFailed("Selected peer not found".to_string()))?; + let (addr, peer) = peers.iter().find(|(a, _)| *a == selected_peer).ok_or_else(|| { + crate::NetworkError::ConnectionFailed("Selected peer not found".to_string()) + })?; // Upgrade GetHeaders to GetHeaders2 if this specific peer supports it and not disabled let peer_supports_headers2 = { @@ -970,14 +968,13 @@ impl PeerNetworkManager { } let mut peer_guard = peer.write().await; - peer_guard - .send_message(message) - .await - .map_err(|e| NetworkError::ProtocolError(format!("Failed to send to {}: {}", addr, e))) + peer_guard.send_message(message).await.map_err(|e| { + crate::NetworkError::ProtocolError(format!("Failed to send to {}: {}", addr, e)) + }) } /// Broadcast a message to all connected peers - pub async fn broadcast(&self, message: NetworkMessage) -> Vec> { + pub async fn broadcast(&self, message: NetworkMessage) -> Vec> { let peers = self.pool.get_all_peers().await; let mut handles = Vec::new(); @@ -996,7 +993,7 @@ impl PeerNetworkManager { let handle = tokio::spawn(async move { let mut peer_guard = peer.write().await; - peer_guard.send_message(msg).await.map_err(Error::Network) + peer_guard.send_message(msg).await.map_err(crate::Error::Network) }); handles.push(handle); } @@ -1006,9 +1003,11 @@ impl PeerNetworkManager { for handle in handles { match handle.await { Ok(result) => results.push(result), - Err(_) => results.push(Err(Error::Network(NetworkError::ConnectionFailed( - "Task panicked during broadcast".to_string(), - )))), + Err(_) => { + results.push(Err(crate::Error::Network(crate::NetworkError::ConnectionFailed( + "Task panicked during broadcast".to_string(), + )))) + } } } @@ -1016,7 +1015,11 @@ impl PeerNetworkManager { } /// Disconnect a specific peer - pub async fn disconnect_peer(&self, addr: &SocketAddr, reason: &str) -> Result<(), Error> { + pub async fn disconnect_peer( + &self, + addr: &SocketAddr, + reason: &str, + ) -> Result<(), crate::Error> { log::info!("Disconnecting peer {} - reason: {}", addr, reason); // Remove the peer @@ -1058,7 +1061,7 @@ impl PeerNetworkManager { } /// Ban a specific peer manually - pub async fn ban_peer(&self, addr: &SocketAddr, reason: &str) -> Result<(), Error> { + pub async fn ban_peer(&self, addr: &SocketAddr, reason: &str) -> Result<(), crate::Error> { log::info!("Manually banning peer {} - reason: {}", addr, reason); // Disconnect the peer first @@ -1151,16 +1154,16 @@ impl NetworkManager for PeerNetworkManager { self } - async fn connect(&mut self) -> NetworkResult<()> { - self.start().await.map_err(|e| NetworkError::ConnectionFailed(e.to_string())) + async fn connect(&mut self) -> crate::NetworkResult<()> { + self.start().await.map_err(|e| crate::NetworkError::ConnectionFailed(e.to_string())) } - async fn disconnect(&mut self) -> NetworkResult<()> { + async fn disconnect(&mut self) -> crate::NetworkResult<()> { self.shutdown().await; Ok(()) } - async fn send_message(&mut self, message: NetworkMessage) -> NetworkResult<()> { + async fn send_message(&mut self, message: NetworkMessage) -> crate::NetworkResult<()> { // For sync messages that require consistent responses, send to only one peer match &message { NetworkMessage::GetHeaders(_) @@ -1175,12 +1178,14 @@ impl NetworkManager for PeerNetworkManager { // Return error if all sends failed if results.is_empty() { - return Err(NetworkError::ConnectionFailed("No connected peers".to_string())); + return Err(crate::NetworkError::ConnectionFailed( + "No connected peers".to_string(), + )); } let successes = results.iter().filter(|r| r.is_ok()).count(); if successes == 0 { - return Err(NetworkError::ProtocolError( + return Err(crate::NetworkError::ProtocolError( "Failed to send to any peer".to_string(), )); } @@ -1194,7 +1199,7 @@ impl NetworkManager for PeerNetworkManager { &self, score_change: i32, reason: &str, - ) -> NetworkResult<()> { + ) -> crate::NetworkResult<()> { // Get the last peer that sent us a message if let Some(addr) = self.get_last_message_peer().await { self.reputation_manager.update_reputation(addr, score_change, reason).await; @@ -1205,7 +1210,7 @@ impl NetworkManager for PeerNetworkManager { async fn penalize_last_message_peer_invalid_chainlock( &self, reason: &str, - ) -> NetworkResult<()> { + ) -> crate::NetworkResult<()> { if let Some(addr) = self.get_last_message_peer().await { match self.disconnect_peer(&addr, reason).await { Ok(()) => { @@ -1241,7 +1246,7 @@ impl NetworkManager for PeerNetworkManager { async fn penalize_last_message_peer_invalid_instantlock( &self, reason: &str, - ) -> NetworkResult<()> { + ) -> crate::NetworkResult<()> { if let Some(addr) = self.get_last_message_peer().await { // Apply misbehavior score and a short temporary ban self.reputation_manager @@ -1274,7 +1279,7 @@ impl NetworkManager for PeerNetworkManager { Ok(()) } - async fn receive_message(&mut self) -> NetworkResult> { + async fn receive_message(&mut self) -> crate::NetworkResult> { let mut rx = self.message_rx.lock().await; // Use a timeout to prevent indefinite blocking when peers disconnect @@ -1330,7 +1335,7 @@ impl NetworkManager for PeerNetworkManager { }) } - async fn get_peer_best_height(&self) -> NetworkResult> { + async fn get_peer_best_height(&self) -> crate::NetworkResult> { let peers = self.pool.get_all_peers().await; if peers.is_empty() { @@ -1409,12 +1414,12 @@ impl NetworkManager for PeerNetworkManager { self.get_last_message_peer_id().await } - async fn update_peer_dsq_preference(&mut self, wants_dsq: bool) -> NetworkResult<()> { + async fn update_peer_dsq_preference(&mut self, wants_dsq: bool) -> crate::NetworkResult<()> { // Get the last peer that sent us a message let peer_id = self.get_last_message_peer_id().await; if peer_id.0 == 0 { - return Err(NetworkError::ConnectionFailed("No peer to update".to_string())); + return Err(crate::NetworkError::ConnectionFailed("No peer to update".to_string())); } // Find the peer's address from the last message data @@ -1428,7 +1433,7 @@ impl NetworkManager for PeerNetworkManager { Ok(()) } - async fn mark_peer_sent_headers2(&mut self) -> NetworkResult<()> { + async fn mark_peer_sent_headers2(&mut self) -> crate::NetworkResult<()> { // Get the last peer that sent us a message let last_msg_peer = self.last_message_peer.lock().await; if let Some(addr) = &*last_msg_peer { diff --git a/dash-spv/src/network/persist.rs b/dash-spv/src/network/persist.rs index 362a1b0bd..09b001985 100644 --- a/dash-spv/src/network/persist.rs +++ b/dash-spv/src/network/persist.rs @@ -4,8 +4,7 @@ use dashcore::Network; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use crate::error::{Error, StorageError}; -use crate::storage::io::atomic_write; +use crate::{storage::io::atomic_write, StorageError}; /// Peer persistence for saving and loading known peer addresses pub struct PeerStore { @@ -43,7 +42,7 @@ impl PeerStore { pub async fn save_peers( &self, peers: &[dashcore::network::address::AddrV2Message], - ) -> Result<(), Error> { + ) -> Result<(), crate::Error> { let saved = SavedPeers { version: 1, network: format!("{:?}", self.network), @@ -59,21 +58,22 @@ impl PeerStore { .collect(), }; - let json = serde_json::to_string_pretty(&saved) - .map_err(|e| Error::Storage(StorageError::Serialization(e.to_string())))?; + let json = serde_json::to_string_pretty(&saved).map_err(|e| { + crate::Error::Storage(crate::StorageError::Serialization(e.to_string())) + })?; - atomic_write(&self.path, json.as_bytes()).await.map_err(Error::Storage)?; + atomic_write(&self.path, json.as_bytes()).await.map_err(crate::Error::Storage)?; log::debug!("Saved {} peers to {:?}", saved.peers.len(), self.path); Ok(()) } /// Load peers from disk - pub async fn load_peers(&self) -> Result, Error> { + pub async fn load_peers(&self) -> crate::Result> { match tokio::fs::read_to_string(&self.path).await { Ok(json) => { let saved: SavedPeers = serde_json::from_str(&json).map_err(|e| { - Error::Storage(StorageError::Corruption(format!( + crate::Error::Storage(crate::StorageError::Corruption(format!( "Failed to parse peers file: {}", e ))) @@ -81,7 +81,7 @@ impl PeerStore { // Verify network matches if saved.network != format!("{:?}", self.network) { - return Err(Error::Storage(StorageError::Corruption(format!( + return Err(crate::Error::Storage(crate::StorageError::Corruption(format!( "Peers file is for network {} but we are on {:?}", saved.network, self.network )))); @@ -97,19 +97,19 @@ impl PeerStore { log::debug!("No saved peers file found at {:?}", self.path); Ok(vec![]) } - Err(e) => Err(Error::Storage(StorageError::ReadFailed(e.to_string()))), + Err(e) => Err(crate::Error::Storage(StorageError::from(e))), } } /// Delete the peers file - pub async fn clear(&self) -> Result<(), Error> { + pub async fn clear(&self) -> crate::Result<()> { match tokio::fs::remove_file(&self.path).await { Ok(_) => { log::info!("Cleared peer store at {:?}", self.path); Ok(()) } Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(e) => Err(Error::Storage(StorageError::WriteFailed(e.to_string()))), + Err(e) => Err(crate::Error::Storage(StorageError::from(e))), } } } diff --git a/dash-spv/src/network/pool.rs b/dash-spv/src/network/pool.rs index 9688a2cdd..f8b19fde1 100644 --- a/dash-spv/src/network/pool.rs +++ b/dash-spv/src/network/pool.rs @@ -5,7 +5,6 @@ use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::RwLock; -use crate::error::{Error, NetworkError}; use crate::network::constants::{MAX_PEERS, MIN_PEERS}; use crate::network::peer::Peer; @@ -33,7 +32,7 @@ impl PeerPool { } /// Add a peer to the pool - pub async fn add_peer(&self, addr: SocketAddr, peer: Peer) -> Result<(), Error> { + pub async fn add_peer(&self, addr: SocketAddr, peer: Peer) -> crate::Result<()> { let mut peers = self.peers.write().await; let mut connecting = self.connecting.write().await; @@ -42,7 +41,7 @@ impl PeerPool { // Check if we're at capacity if peers.len() >= MAX_PEERS { - return Err(Error::Network(NetworkError::ConnectionFailed(format!( + return Err(crate::Error::Network(crate::NetworkError::ConnectionFailed(format!( "Maximum peers ({}) reached", MAX_PEERS )))); @@ -50,7 +49,7 @@ impl PeerPool { // Check if already connected if peers.contains_key(&addr) { - return Err(Error::Network(NetworkError::ConnectionFailed(format!( + return Err(crate::Error::Network(crate::NetworkError::ConnectionFailed(format!( "Already connected to {}", addr )))); From 1f9c84221669cd3c7fdb36e215348495b7b76f1c Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Wed, 14 Jan 2026 23:55:11 +0000 Subject: [PATCH 09/15] removed unused error variants --- dash-spv/src/chain/chainlock_manager.rs | 6 +----- dash-spv/src/error.rs | 23 ----------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/dash-spv/src/chain/chainlock_manager.rs b/dash-spv/src/chain/chainlock_manager.rs index 84b1db5aa..e05bffd18 100644 --- a/dash-spv/src/chain/chainlock_manager.rs +++ b/dash-spv/src/chain/chainlock_manager.rs @@ -175,11 +175,7 @@ impl ChainLockManager { } // Verify the block exists in our chain - if let Some(header) = storage - .get_header(chain_lock.block_height) - .await - .map_err(ValidationError::StorageError)? - { + if let Some(header) = storage.get_header(chain_lock.block_height).await? { let header_hash = header.block_hash(); if header_hash != chain_lock.block_hash { return Err(ValidationError::InvalidChainLock(format!( diff --git a/dash-spv/src/error.rs b/dash-spv/src/error.rs index f94b4358d..6c29e61f1 100644 --- a/dash-spv/src/error.rs +++ b/dash-spv/src/error.rs @@ -53,9 +53,6 @@ pub enum NetworkError { #[error("Connection failed: {0}")] ConnectionFailed(String), - #[error("Handshake failed: {0}")] - HandshakeFailed(String), - #[error("Protocol error: {0}")] ProtocolError(String), @@ -71,14 +68,8 @@ pub enum NetworkError { #[error("Message serialization error: {0}")] Serialization(#[from] dashcore::consensus::encode::Error), - #[error("IO error: {0}")] - Io(#[from] io::Error), - #[error("Address parse error: {0}")] AddressParse(String), - - #[error("System time error: {0}")] - SystemTime(String), } /// Storage-related errors. @@ -102,9 +93,6 @@ pub enum StorageError { #[error("Serialization error: {0}")] Serialization(String), - #[error("Inconsistent state: {0}")] - InconsistentState(String), - #[error("Lock poisoned: {0}")] LockPoisoned(String), @@ -130,12 +118,6 @@ pub enum ValidationError { #[error("Invalid signature: {0}")] InvalidSignature(String), - #[error("Invalid filter header chain: {0}")] - InvalidFilterHeaderChain(String), - - #[error("Consensus error: {0}")] - Consensus(String), - #[error("Masternode verification failed: {0}")] MasternodeVerification(String), @@ -150,11 +132,6 @@ pub enum SyncError { #[error("Sync already in progress")] SyncInProgress, - /// Deprecated: Use specific error variants instead - #[deprecated(note = "Use Network, Storage, Validation, or Timeout variants instead")] - #[error("Sync failed: {0}")] - SyncFailed(String), - /// Indicates an invalid state in the sync process (e.g., unexpected phase transitions) /// Use this for sync state machine errors, not validation errors #[error("Invalid sync state: {0}")] From a135b28a75fa5ed5146cd5daaf326cb79386cab2 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Thu, 15 Jan 2026 00:23:24 +0000 Subject: [PATCH 10/15] added UninitializedClient to dash_spv::Error enum --- dash-spv-ffi/src/broadcast.rs | 6 +--- dash-spv-ffi/src/client.rs | 58 ++++++++------------------------- dash-spv-ffi/src/error.rs | 1 + dash-spv/src/client/core.rs | 2 +- dash-spv/src/error.rs | 3 ++ dash-spv/src/network/manager.rs | 4 +-- dash-spv/src/network/persist.rs | 2 +- 7 files changed, 22 insertions(+), 54 deletions(-) diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs index dff3a86ce..6f656c89b 100644 --- a/dash-spv-ffi/src/broadcast.rs +++ b/dash-spv-ffi/src/broadcast.rs @@ -50,11 +50,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 9f03504b3..70238de35 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -186,7 +186,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new( DashSpvClient::new(client_config, network, storage, wallet).await } (Err(e), _) => Err(e), - (_, Err(e)) => Err(dash_spv::Error::Storage(e)), + (_, Err(e)) => Err(e.into()), } }); @@ -399,11 +399,7 @@ fn stop_client_internal(client: &mut FFIDashSpvClient) -> dash_spv::Result<()> { let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.stop().await; @@ -440,7 +436,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_update_config( let mut guard = client.inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), + None => return Err(dash_spv::Error::UninitializedClient), } }; @@ -477,11 +473,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_start(client: *mut FFIDashSpvClient let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.start().await; @@ -556,7 +548,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_test_sync(client: *mut FFIDashSpvCl let mut guard = client.inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), + None => return Err(dash_spv::Error::UninitializedClient), } }; tracing::info!("Starting test sync..."); @@ -760,11 +752,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress( let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Config( - "Client not initialized".to_string(), - )) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let (_command_sender, command_receiver) = tokio::sync::mpsc::unbounded_channel(); @@ -888,11 +876,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_sync_progress( let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.sync_progress().await; @@ -928,11 +912,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_stats( let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.stats().await; @@ -974,7 +954,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_tip_hash( let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), + None => return Err(dash_spv::Error::UninitializedClient), } }; let tip = spv_client.tip_hash().await; @@ -1025,7 +1005,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_tip_height( let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), + None => return Err(dash_spv::Error::UninitializedClient), } }; let height = spv_client.tip_height().await; @@ -1062,7 +1042,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_clear_storage(client: *mut FFIDashS let mut guard = inner.lock().unwrap(); match guard.take() { Some(c) => c, - None => return Err(dash_spv::Error::Config("Client not initialized".to_string())), + None => return Err(dash_spv::Error::UninitializedClient), } }; @@ -1228,9 +1208,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( // TODO: rescan_from_height not yet implemented in dash-spv Err(dash_spv::Error::Config("Not implemented".to_string())) } else { - Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) + Err(dash_spv::Error::UninitializedClient) } }); @@ -1264,11 +1242,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_enable_mempool_tracking( let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.enable_mempool_tracking(mempool_strategy).await; @@ -1322,11 +1296,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_record_send( let mut guard = inner.lock().unwrap(); match guard.take() { Some(client) => client, - None => { - return Err(dash_spv::Error::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } + None => return Err(dash_spv::Error::UninitializedClient), } }; let res = spv_client.record_send(txid).await; diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 5b4b09d69..097abf641 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -60,6 +60,7 @@ impl From for FFIErrorCode { dash_spv::Error::Logging(_) => FFIErrorCode::RuntimeError, dash_spv::Error::QuorumLookupError(_) => FFIErrorCode::ValidationError, dash_spv::Error::General(_) => FFIErrorCode::Unknown, + dash_spv::Error::UninitializedClient => FFIErrorCode::RuntimeError, } } } diff --git a/dash-spv/src/client/core.rs b/dash-spv/src/client/core.rs index 232b1fa1f..b077e2d2e 100644 --- a/dash-spv/src/client/core.rs +++ b/dash-spv/src/client/core.rs @@ -204,7 +204,7 @@ impl DashSpvClient Date: Thu, 15 Jan 2026 01:05:16 +0000 Subject: [PATCH 11/15] dash_spv::Error::General replaced with TaskFailed variant --- dash-spv-ffi/src/error.rs | 2 +- dash-spv/src/client/sync_coordinator.rs | 2 +- dash-spv/src/error.rs | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 097abf641..0fb10e629 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -59,8 +59,8 @@ impl From for FFIErrorCode { dash_spv::Error::Config(_) => FFIErrorCode::ConfigError, dash_spv::Error::Logging(_) => FFIErrorCode::RuntimeError, dash_spv::Error::QuorumLookupError(_) => FFIErrorCode::ValidationError, - dash_spv::Error::General(_) => FFIErrorCode::Unknown, dash_spv::Error::UninitializedClient => FFIErrorCode::RuntimeError, + dash_spv::Error::TaskFailed(_) => FFIErrorCode::RuntimeError, } } } diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index a44d992c3..cdf85f8e2 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -521,7 +521,7 @@ impl DashSpvClient crate::Result<()> { diff --git a/dash-spv/src/error.rs b/dash-spv/src/error.rs index 6df4af806..d47fe68ec 100644 --- a/dash-spv/src/error.rs +++ b/dash-spv/src/error.rs @@ -2,6 +2,7 @@ use std::io; use thiserror::Error; +use tokio::task::JoinError; /// Main error type for the Dash SPV client. #[derive(Debug, Error)] @@ -18,8 +19,8 @@ pub enum Error { #[error("Quorum lookup error: {0}")] QuorumLookupError(String), - #[error("General error: {0}")] - General(String), + #[error("Tokio task failed: {0}")] + TaskFailed(#[from] JoinError), #[error("Network error: {0}")] Network(#[from] NetworkError), From 72aafe08d30c3dfa181b65600bac0e6bbb04dcf4 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Thu, 15 Jan 2026 01:15:29 +0000 Subject: [PATCH 12/15] cleaned more error module imports --- dash-spv/src/chain/chainlock_manager.rs | 2 +- dash-spv/src/client/mempool.rs | 7 +++---- dash-spv/src/client/message_handler.rs | 7 +++---- dash-spv/src/client/progress.rs | 5 ++--- dash-spv/src/client/status_display.rs | 5 ++--- dash-spv/src/client/sync_coordinator.rs | 2 +- dash-spv/src/client/transactions.rs | 4 ++-- dash-spv/src/logging.rs | 2 +- dash-spv/src/network/handshake.rs | 2 +- dash-spv/src/network/mod.rs | 2 +- dash-spv/src/network/peer.rs | 2 +- dash-spv/src/storage/blocks.rs | 9 +++++---- dash-spv/src/storage/chainstate.rs | 2 +- dash-spv/src/storage/io.rs | 2 +- dash-spv/src/storage/lockfile.rs | 2 +- dash-spv/src/storage/masternode.rs | 4 ++-- dash-spv/src/storage/mod.rs | 4 ++-- dash-spv/src/sync/filters/download.rs | 2 +- dash-spv/src/sync/filters/headers.rs | 2 +- dash-spv/src/sync/filters/manager.rs | 2 +- dash-spv/src/sync/filters/matching.rs | 2 +- dash-spv/src/sync/filters/requests.rs | 2 +- dash-spv/src/sync/filters/retry.rs | 2 +- dash-spv/src/sync/headers/manager.rs | 2 +- dash-spv/src/sync/manager.rs | 2 +- dash-spv/src/sync/masternodes/manager.rs | 2 +- dash-spv/src/sync/message_handlers.rs | 2 +- dash-spv/src/sync/phase_execution.rs | 2 +- dash-spv/src/sync/post_sync.rs | 2 +- dash-spv/src/sync/transitions.rs | 2 +- dash-spv/src/test_utils/network.rs | 2 +- dash-spv/src/validation/header.rs | 2 +- 32 files changed, 45 insertions(+), 48 deletions(-) diff --git a/dash-spv/src/chain/chainlock_manager.rs b/dash-spv/src/chain/chainlock_manager.rs index e05bffd18..754f3e01f 100644 --- a/dash-spv/src/chain/chainlock_manager.rs +++ b/dash-spv/src/chain/chainlock_manager.rs @@ -9,9 +9,9 @@ use indexmap::IndexMap; use std::sync::{Arc, RwLock}; use tracing::{debug, error, info, warn}; -use crate::error::{StorageError, StorageResult, ValidationError, ValidationResult}; use crate::storage::StorageManager; use crate::types::ChainState; +use crate::{StorageError, StorageResult, ValidationError, ValidationResult}; /// Maximum number of pending ChainLocks to queue const MAX_PENDING_CHAINLOCKS: usize = 100; diff --git a/dash-spv/src/client/mempool.rs b/dash-spv/src/client/mempool.rs index fac807360..325bb2c33 100644 --- a/dash-spv/src/client/mempool.rs +++ b/dash-spv/src/client/mempool.rs @@ -9,7 +9,6 @@ use std::collections::HashSet; use std::sync::Arc; -use crate::error::Result; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -22,7 +21,7 @@ impl DashSpvClient Result<()> { + ) -> crate::Result<()> { // Update config self.config.enable_mempool_tracking = true; self.config.mempool_strategy = strategy; @@ -46,7 +45,7 @@ impl DashSpvClient Result { + ) -> crate::Result { let _wallet = self.wallet.read().await; let mempool_state = self.mempool_state.read().await; @@ -134,7 +133,7 @@ impl DashSpvClient Result<()> { + pub async fn record_send(&self, txid: dashcore::Txid) -> crate::Result<()> { let mut mempool_state = self.mempool_state.write().await; mempool_state.record_send(txid); Ok(()) diff --git a/dash-spv/src/client/message_handler.rs b/dash-spv/src/client/message_handler.rs index f984efce9..3c7937f2b 100644 --- a/dash-spv/src/client/message_handler.rs +++ b/dash-spv/src/client/message_handler.rs @@ -1,7 +1,6 @@ //! Network message handling for the Dash SPV client. use crate::client::ClientConfig; -use crate::error::Result; use crate::mempool_filter::MempoolFilter; use crate::network::NetworkManager; use crate::storage::StorageManager; @@ -50,7 +49,7 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle pub async fn handle_network_message( &mut self, message: &dashcore::network::message::NetworkMessage, - ) -> Result<()> { + ) -> crate::Result<()> { use dashcore::network::message::NetworkMessage; tracing::debug!("Client handling network message: {:?}", std::mem::discriminant(message)); @@ -292,7 +291,7 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle async fn handle_inventory( &mut self, inv: Vec, - ) -> Result<()> { + ) -> crate::Result<()> { use dashcore::network::message::NetworkMessage; use dashcore::network::message_blockdata::Inventory; @@ -390,7 +389,7 @@ impl<'a, S: StorageManager, N: NetworkManager, W: WalletInterface> MessageHandle pub async fn handle_post_sync_headers( &mut self, headers: &[dashcore::block::Header], - ) -> Result<()> { + ) -> crate::Result<()> { if !self.config.enable_filters { tracing::debug!( "Filters not enabled, skipping post-sync filter requests for {} headers", diff --git a/dash-spv/src/client/progress.rs b/dash-spv/src/client/progress.rs index 315fe700d..f15ece148 100644 --- a/dash-spv/src/client/progress.rs +++ b/dash-spv/src/client/progress.rs @@ -5,7 +5,6 @@ //! - Phase-to-stage mapping //! - Statistics gathering -use crate::error::Result; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::sync::SyncPhase; @@ -16,13 +15,13 @@ use super::DashSpvClient; impl DashSpvClient { /// Get current sync progress. - pub async fn sync_progress(&self) -> Result { + pub async fn sync_progress(&self) -> crate::Result { let display = self.create_status_display().await; display.sync_progress().await } /// Get current statistics. - pub async fn stats(&self) -> Result { + pub async fn stats(&self) -> crate::Result { let display = self.create_status_display().await; let mut stats = display.stats().await?; diff --git a/dash-spv/src/client/status_display.rs b/dash-spv/src/client/status_display.rs index cc3e9aee5..3777db313 100644 --- a/dash-spv/src/client/status_display.rs +++ b/dash-spv/src/client/status_display.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use tokio::sync::{Mutex, RwLock}; use crate::client::ClientConfig; -use crate::error::Result; use crate::storage::StorageManager; #[cfg(feature = "terminal-ui")] use crate::terminal::TerminalUI; @@ -98,7 +97,7 @@ impl<'a, S: StorageManager, W: WalletInterface> StatusDisplay<'a, S, W> { } /// Get current sync progress. - pub async fn sync_progress(&self) -> Result { + pub async fn sync_progress(&self) -> crate::Result { let state = self.state.read().await; // Clone the inner heights handle and copy needed counters without awaiting while holding the RwLock let (filters_received, received_heights) = { @@ -135,7 +134,7 @@ impl<'a, S: StorageManager, W: WalletInterface> StatusDisplay<'a, S, W> { } /// Get current statistics. - pub async fn stats(&self) -> Result { + pub async fn stats(&self) -> crate::Result { let stats = self.stats.read().await; Ok(stats.clone()) } diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index cdf85f8e2..89454d414 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -453,7 +453,7 @@ impl DashSpvClient { // Handle specific network error types - if let crate::error::NetworkError::ConnectionFailed(msg) = &err { + if let crate::NetworkError::ConnectionFailed(msg) = &err { if msg.contains("No connected peers") || self.network.peer_count() == 0 { tracing::warn!("All peers disconnected during monitoring, checking connection health"); diff --git a/dash-spv/src/client/transactions.rs b/dash-spv/src/client/transactions.rs index 7ded91d12..ed273a3dd 100644 --- a/dash-spv/src/client/transactions.rs +++ b/dash-spv/src/client/transactions.rs @@ -19,7 +19,7 @@ impl DashSpvClient DashSpvClient return Err(crate::error::StorageError::Io(e)), + Err(e) => return Err(crate::StorageError::Io(e)), } tokio::fs::create_dir_all(&self.storage_path).await?; } diff --git a/dash-spv/src/sync/filters/download.rs b/dash-spv/src/sync/filters/download.rs index ce1052671..5de94971f 100644 --- a/dash-spv/src/sync/filters/download.rs +++ b/dash-spv/src/sync/filters/download.rs @@ -16,10 +16,10 @@ use dashcore::{ BlockHash, }; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::SyncProgress; +use crate::{SyncError, SyncResult}; impl super::manager::FilterSyncManager { pub async fn verify_cfilter_against_headers( diff --git a/dash-spv/src/sync/filters/headers.rs b/dash-spv/src/sync/filters/headers.rs index e1b53da1f..4d075ae14 100644 --- a/dash-spv/src/sync/filters/headers.rs +++ b/dash-spv/src/sync/filters/headers.rs @@ -22,9 +22,9 @@ use dashcore::{ use dashcore_hashes::{sha256d, Hash}; use super::types::*; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; impl super::manager::FilterSyncManager { pub(super) async fn find_available_header_at_or_before( diff --git a/dash-spv/src/sync/filters/manager.rs b/dash-spv/src/sync/filters/manager.rs index 1ae55501f..44fd3edad 100644 --- a/dash-spv/src/sync/filters/manager.rs +++ b/dash-spv/src/sync/filters/manager.rs @@ -4,10 +4,10 @@ //! that delegates to specialized sub-modules for headers, downloads, matching, etc. use crate::client::ClientConfig; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::SharedFilterHeights; +use crate::{SyncError, SyncResult}; use dashcore::{hash_types::FilterHeader, network::message_filter::CFHeaders, BlockHash}; use dashcore_hashes::{sha256d, Hash}; use std::collections::{HashMap, HashSet, VecDeque}; diff --git a/dash-spv/src/sync/filters/matching.rs b/dash-spv/src/sync/filters/matching.rs index 56161a148..9595d4c33 100644 --- a/dash-spv/src/sync/filters/matching.rs +++ b/dash-spv/src/sync/filters/matching.rs @@ -15,9 +15,9 @@ use dashcore::{ BlockHash, ScriptBuf, }; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; impl super::manager::FilterSyncManager { pub async fn check_filter_for_matches< diff --git a/dash-spv/src/sync/filters/requests.rs b/dash-spv/src/sync/filters/requests.rs index e6f61d806..746f58d1e 100644 --- a/dash-spv/src/sync/filters/requests.rs +++ b/dash-spv/src/sync/filters/requests.rs @@ -7,9 +7,9 @@ //! - Sending individual requests to the network use super::types::*; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; impl super::manager::FilterSyncManager { /// Build a queue of filter requests covering the specified range. diff --git a/dash-spv/src/sync/filters/retry.rs b/dash-spv/src/sync/filters/retry.rs index c0bd7fdbc..d5a7aa981 100644 --- a/dash-spv/src/sync/filters/retry.rs +++ b/dash-spv/src/sync/filters/retry.rs @@ -7,9 +7,9 @@ //! - Sync progress timeout detection use super::types::*; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; use dashcore::BlockHash; impl super::manager::FilterSyncManager { diff --git a/dash-spv/src/sync/headers/manager.rs b/dash-spv/src/sync/headers/manager.rs index 4498c16d9..fcdbb3f15 100644 --- a/dash-spv/src/sync/headers/manager.rs +++ b/dash-spv/src/sync/headers/manager.rs @@ -9,12 +9,12 @@ use dashcore_hashes::Hash; use crate::chain::checkpoints::{mainnet_checkpoints, testnet_checkpoints, CheckpointManager}; use crate::chain::{ChainTip, ChainTipManager, ChainWork}; use crate::client::ClientConfig; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::types::{ChainState, HashedBlockHeader}; use crate::validation::{BlockHeaderValidator, Validator}; use crate::ValidationMode; +use crate::{SyncError, SyncResult}; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/dash-spv/src/sync/manager.rs b/dash-spv/src/sync/manager.rs index 3b1d5d1fc..94c89f836 100644 --- a/dash-spv/src/sync/manager.rs +++ b/dash-spv/src/sync/manager.rs @@ -3,11 +3,11 @@ use super::phases::{PhaseTransition, SyncPhase}; use super::transitions::TransitionManager; use crate::client::ClientConfig; -use crate::error::SyncResult; use crate::network::NetworkManager; use crate::storage::StorageManager; use crate::sync::{FilterSyncManager, HeaderSyncManager, MasternodeSyncManager, ReorgConfig}; use crate::types::{SharedFilterHeights, SyncProgress}; +use crate::SyncResult; use crate::{SpvStats, SyncError}; use dashcore::prelude::CoreBlockHeight; use dashcore::BlockHash; diff --git a/dash-spv/src/sync/masternodes/manager.rs b/dash-spv/src/sync/masternodes/manager.rs index 38b35918c..024039a9c 100644 --- a/dash-spv/src/sync/masternodes/manager.rs +++ b/dash-spv/src/sync/masternodes/manager.rs @@ -15,9 +15,9 @@ use std::collections::HashMap; use std::time::{Duration, Instant}; use crate::client::ClientConfig; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; /// Simplified masternode synchronization following dash-evo-tool pattern. pub struct MasternodeSyncManager { diff --git a/dash-spv/src/sync/message_handlers.rs b/dash-spv/src/sync/message_handlers.rs index ef2f7b56a..d20cb3f49 100644 --- a/dash-spv/src/sync/message_handlers.rs +++ b/dash-spv/src/sync/message_handlers.rs @@ -7,9 +7,9 @@ use dashcore::block::Block; use dashcore::network::message::NetworkMessage; use dashcore::network::message_blockdata::Inventory; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; use key_wallet_manager::wallet_interface::WalletInterface; use super::manager::SyncManager; diff --git a/dash-spv/src/sync/phase_execution.rs b/dash-spv/src/sync/phase_execution.rs index 5922e43ca..8cb6b765d 100644 --- a/dash-spv/src/sync/phase_execution.rs +++ b/dash-spv/src/sync/phase_execution.rs @@ -2,9 +2,9 @@ use std::time::Instant; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; use key_wallet_manager::wallet_interface::WalletInterface; use super::manager::SyncManager; diff --git a/dash-spv/src/sync/post_sync.rs b/dash-spv/src/sync/post_sync.rs index c505ebf25..7a764d068 100644 --- a/dash-spv/src/sync/post_sync.rs +++ b/dash-spv/src/sync/post_sync.rs @@ -5,9 +5,9 @@ use dashcore::network::message::NetworkMessage; use dashcore::network::message_blockdata::Inventory; use dashcore::BlockHash; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; use key_wallet_manager::wallet_interface::WalletInterface; use super::manager::{SyncManager, CHAINLOCK_VALIDATION_MASTERNODE_OFFSET}; diff --git a/dash-spv/src/sync/transitions.rs b/dash-spv/src/sync/transitions.rs index e8ce58e93..e410e8db1 100644 --- a/dash-spv/src/sync/transitions.rs +++ b/dash-spv/src/sync/transitions.rs @@ -1,9 +1,9 @@ //! Phase transition logic for sequential sync use crate::client::ClientConfig; -use crate::error::{SyncError, SyncResult}; use crate::network::NetworkManager; use crate::storage::StorageManager; +use crate::{SyncError, SyncResult}; use dashcore::network::constants::ServiceFlags; use super::phases::{PhaseTransition, SyncPhase}; diff --git a/dash-spv/src/test_utils/network.rs b/dash-spv/src/test_utils/network.rs index 66ce349fc..61ebb91d3 100644 --- a/dash-spv/src/test_utils/network.rs +++ b/dash-spv/src/test_utils/network.rs @@ -8,9 +8,9 @@ use dashcore::{ }; use dashcore_hashes::Hash; -use crate::error::{NetworkError, NetworkResult}; use crate::network::NetworkManager; use crate::types::PeerInfo; +use crate::{NetworkError, NetworkResult}; /// Mock network manager for testing pub struct MockNetworkManager { diff --git a/dash-spv/src/validation/header.rs b/dash-spv/src/validation/header.rs index 8141e9992..0b4474e20 100644 --- a/dash-spv/src/validation/header.rs +++ b/dash-spv/src/validation/header.rs @@ -1,9 +1,9 @@ use rayon::prelude::*; use std::time::Instant; -use crate::error::{ValidationError, ValidationResult}; use crate::types::HashedBlockHeader; use crate::validation::Validator; +use crate::{ValidationError, ValidationResult}; #[derive(Default)] pub struct BlockHeaderValidator {} From d9e6a71f253ac835fc450bf6ea2e3741d5cba283 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Thu, 15 Jan 2026 02:00:00 +0000 Subject: [PATCH 13/15] lock posisoning error removed from dash_spv by replacing std RwLock with tokio --- dash-spv/clippy.toml | 1 + dash-spv/src/chain/chainlock_manager.rs | 109 ++++++++---------------- dash-spv/src/chain/chainlock_test.rs | 29 ++++--- dash-spv/src/client/chainlock.rs | 4 +- dash-spv/src/client/sync_coordinator.rs | 2 +- dash-spv/src/error.rs | 3 - dash-spv/src/lib.rs | 2 + dash-spv/tests/chainlock_simple_test.rs | 2 +- 8 files changed, 58 insertions(+), 94 deletions(-) create mode 100644 dash-spv/clippy.toml diff --git a/dash-spv/clippy.toml b/dash-spv/clippy.toml new file mode 100644 index 000000000..5f0d3b552 --- /dev/null +++ b/dash-spv/clippy.toml @@ -0,0 +1 @@ +disallowed-types = ["std::sync::RwLock", "std::sync::Mutex"] diff --git a/dash-spv/src/chain/chainlock_manager.rs b/dash-spv/src/chain/chainlock_manager.rs index 754f3e01f..6da83bd7e 100644 --- a/dash-spv/src/chain/chainlock_manager.rs +++ b/dash-spv/src/chain/chainlock_manager.rs @@ -6,7 +6,8 @@ use dashcore::sml::masternode_list_engine::MasternodeListEngine; use dashcore::{BlockHash, ChainLock}; use indexmap::IndexMap; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; +use tokio::sync::RwLock; use tracing::{debug, error, info, warn}; use crate::storage::StorageManager; @@ -62,24 +63,15 @@ impl ChainLockManager { } /// Set the masternode engine for validation - pub fn set_masternode_engine(&self, engine: Arc) { - match self.masternode_engine.write() { - Ok(mut guard) => { - *guard = Some(engine); - info!("Masternode engine set for ChainLock validation"); - } - Err(e) => { - error!("Failed to set masternode engine: {}", e); - } - } + pub async fn set_masternode_engine(&self, engine: Arc) { + let mut guard = self.masternode_engine.write().await; + *guard = Some(engine); + info!("Masternode engine set for ChainLock validation"); } /// Queue a ChainLock for validation when masternode data is available - pub fn queue_pending_chainlock(&self, chain_lock: ChainLock) -> StorageResult<()> { - let mut pending = self - .pending_chainlocks - .write() - .map_err(|_| StorageError::LockPoisoned("pending_chainlocks".to_string()))?; + pub async fn queue_pending_chainlock(&self, chain_lock: ChainLock) { + let mut pending = self.pending_chainlocks.write().await; // If at capacity, drop the oldest ChainLock if pending.len() >= MAX_PENDING_CHAINLOCKS { @@ -92,7 +84,6 @@ impl ChainLockManager { pending.push(chain_lock); debug!("Queued ChainLock for pending validation, total pending: {}", pending.len()); - Ok(()) } /// Validate all pending ChainLocks after masternode sync @@ -101,20 +92,14 @@ impl ChainLockManager { chain_state: &ChainState, storage: &mut S, ) -> ValidationResult<()> { - let pending = { - let mut pending_guard = self - .pending_chainlocks - .write() - .map_err(|_| ValidationError::InvalidChainLock("Lock poisoned".to_string()))?; - std::mem::take(&mut *pending_guard) - }; + let pending = self.pending_chainlocks.write().await; info!("Validating {} pending ChainLocks", pending.len()); let mut validated_count = 0; let mut failed_count = 0; - for chain_lock in pending { + for chain_lock in pending.iter() { match self.process_chain_lock(chain_lock.clone(), chain_state, storage).await { Ok(_) => { validated_count += 1; @@ -154,8 +139,8 @@ impl ChainLockManager { ); // Check if we already have this chain lock - if self.has_chain_lock_at_height(chain_lock.block_height) { - let existing = self.get_chain_lock_by_height(chain_lock.block_height); + if self.has_chain_lock_at_height(chain_lock.block_height).await { + let existing = self.get_chain_lock_by_height(chain_lock.block_height).await; if let Some(existing_entry) = existing { if existing_entry.chain_lock.block_hash != chain_lock.block_hash { error!( @@ -191,10 +176,7 @@ impl ChainLockManager { // Full validation with masternode engine if available let mut validated = false; { - let engine_guard = self - .masternode_engine - .read() - .map_err(|_| ValidationError::InvalidChainLock("Lock poisoned".to_string()))?; + let engine_guard = self.masternode_engine.read().await; if let Some(engine) = engine_guard.as_ref() { // Use the masternode engine's verify_chain_lock method @@ -216,12 +198,7 @@ impl ChainLockManager { .saturating_sub(CHAINLOCK_VALIDATION_MASTERNODE_OFFSET); warn!("⚠️ Masternode engine exists but lacks required masternode lists for height {} (needs list at height {} for ChainLock validation), queueing ChainLock for later validation", chain_lock.block_height, required_height); - self.queue_pending_chainlock(chain_lock.clone()).map_err(|e| { - ValidationError::InvalidChainLock(format!( - "Failed to queue pending ChainLock: {}", - e - )) - })?; + self.queue_pending_chainlock(chain_lock.clone()).await; } else { return Err(ValidationError::InvalidChainLock(format!( "MasternodeListEngine validation failed: {:?}", @@ -235,12 +212,7 @@ impl ChainLockManager { warn!( "⚠️ Masternode engine not available, queueing ChainLock for later validation" ); - self.queue_pending_chainlock(chain_lock.clone()).map_err(|e| { - ValidationError::InvalidChainLock(format!( - "Failed to queue pending ChainLock: {}", - e - )) - })?; + self.queue_pending_chainlock(chain_lock.clone()).await; } } // engine_guard dropped before any await @@ -300,14 +272,8 @@ impl ChainLockManager { ) -> StorageResult<()> { // Store in memory caches { - let mut by_height = self - .chain_locks_by_height - .write() - .map_err(|_| StorageError::LockPoisoned("chain_locks_by_height".to_string()))?; - let mut by_hash = self - .chain_locks_by_hash - .write() - .map_err(|_| StorageError::LockPoisoned("chain_locks_by_hash".to_string()))?; + let mut by_height = self.chain_locks_by_height.write().await; + let mut by_hash = self.chain_locks_by_hash.write().await; by_height.insert(chain_lock.block_height, entry.clone()); by_hash.insert(chain_lock.block_hash, entry.clone()); @@ -342,29 +308,29 @@ impl ChainLockManager { } /// Check if we have a chain lock at the given height - pub fn has_chain_lock_at_height(&self, height: u32) -> bool { - self.chain_locks_by_height.read().map(|locks| locks.contains_key(&height)).unwrap_or(false) + pub async fn has_chain_lock_at_height(&self, height: u32) -> bool { + self.chain_locks_by_height.read().await.contains_key(&height) } /// Get chain lock by height - pub fn get_chain_lock_by_height(&self, height: u32) -> Option { - self.chain_locks_by_height.read().ok().and_then(|locks| locks.get(&height).cloned()) + pub async fn get_chain_lock_by_height(&self, height: u32) -> Option { + self.chain_locks_by_height.read().await.get(&height).cloned() } /// Get chain lock by block hash - pub fn get_chain_lock_by_hash(&self, hash: &BlockHash) -> Option { - self.chain_locks_by_hash.read().ok().and_then(|locks| locks.get(hash).cloned()) + pub async fn get_chain_lock_by_hash(&self, hash: &BlockHash) -> Option { + self.chain_locks_by_hash.read().await.get(hash).cloned() } /// Check if a block is chain-locked - pub fn is_block_chain_locked(&self, block_hash: &BlockHash, height: u32) -> bool { + pub async fn is_block_chain_locked(&self, block_hash: &BlockHash, height: u32) -> bool { // First check by hash (most specific) - if let Some(entry) = self.get_chain_lock_by_hash(block_hash) { + if let Some(entry) = self.get_chain_lock_by_hash(block_hash).await { return entry.validated && entry.chain_lock.block_hash == *block_hash; } // Then check by height - if let Some(entry) = self.get_chain_lock_by_height(height) { + if let Some(entry) = self.get_chain_lock_by_height(height).await { return entry.validated && entry.chain_lock.block_hash == *block_hash; } @@ -372,20 +338,21 @@ impl ChainLockManager { } /// Get the highest chain-locked block height - pub fn get_highest_chain_locked_height(&self) -> Option { - self.chain_locks_by_height.read().ok().and_then(|locks| locks.keys().max().cloned()) + pub async fn get_highest_chain_locked_height(&self) -> Option { + self.chain_locks_by_height.read().await.keys().max().cloned() } /// Check if a reorganization would violate chain locks - pub fn would_violate_chain_lock(&self, reorg_from_height: u32, reorg_to_height: u32) -> bool { + pub async fn would_violate_chain_lock( + &self, + reorg_from_height: u32, + reorg_to_height: u32, + ) -> bool { if !self.enforce_chain_locks { return false; } - let locks = match self.chain_locks_by_height.read() { - Ok(locks) => locks, - Err(_) => return false, // If we can't read locks, assume no violation - }; + let locks = self.chain_locks_by_height.read().await; // Check if any chain-locked block would be reorganized for height in reorg_from_height..=reorg_to_height { @@ -425,12 +392,8 @@ impl ChainLockManager { validated: true, }; - let mut by_height = self.chain_locks_by_height.write().map_err(|_| { - StorageError::LockPoisoned("chain_locks_by_height".to_string()) - })?; - let mut by_hash = self.chain_locks_by_hash.write().map_err(|_| { - StorageError::LockPoisoned("chain_locks_by_hash".to_string()) - })?; + let mut by_height = self.chain_locks_by_height.write().await; + let mut by_hash = self.chain_locks_by_hash.write().await; by_height.insert(chain_lock.block_height, entry.clone()); by_hash.insert(chain_lock.block_hash, entry); diff --git a/dash-spv/src/chain/chainlock_test.rs b/dash-spv/src/chain/chainlock_test.rs index 96f3b2cae..9c906e3d4 100644 --- a/dash-spv/src/chain/chainlock_test.rs +++ b/dash-spv/src/chain/chainlock_test.rs @@ -26,11 +26,12 @@ mod tests { assert!(result.is_ok(), "ChainLock processing should succeed"); // Verify it was stored - assert!(chainlock_manager.has_chain_lock_at_height(1000)); + assert!(chainlock_manager.has_chain_lock_at_height(1000).await); // Verify we can retrieve it let entry = chainlock_manager .get_chain_lock_by_height(1000) + .await .expect("ChainLock should be retrievable after storing"); assert_eq!(entry.chain_lock.block_height, 1000); assert_eq!(entry.chain_lock.block_hash, chainlock.block_hash); @@ -58,11 +59,11 @@ mod tests { .expect("Second ChainLock should process successfully"); // Verify both are stored - assert!(chainlock_manager.has_chain_lock_at_height(1000)); - assert!(chainlock_manager.has_chain_lock_at_height(2000)); + assert!(chainlock_manager.has_chain_lock_at_height(1000).await); + assert!(chainlock_manager.has_chain_lock_at_height(2000).await); // Get highest ChainLock - let highest = chainlock_manager.get_highest_chain_locked_height(); + let highest = chainlock_manager.get_highest_chain_locked_height().await; assert_eq!(highest, Some(2000)); } @@ -85,9 +86,9 @@ mod tests { } // Test reorganization protection - assert!(!chainlock_manager.would_violate_chain_lock(500, 999)); // Before ChainLocks - OK - assert!(chainlock_manager.would_violate_chain_lock(1500, 2500)); // Would reorg ChainLock at 2000 - assert!(!chainlock_manager.would_violate_chain_lock(3001, 4000)); // After ChainLocks - OK + assert!(!chainlock_manager.would_violate_chain_lock(500, 999).await); // Before ChainLocks - OK + assert!(chainlock_manager.would_violate_chain_lock(1500, 2500).await); // Would reorg ChainLock at 2000 + assert!(!chainlock_manager.would_violate_chain_lock(3001, 4000).await); // After ChainLocks - OK } #[tokio::test] @@ -99,14 +100,14 @@ mod tests { let chain_lock2 = ChainLock::dummy(200); let chain_lock3 = ChainLock::dummy(300); - chainlock_manager.queue_pending_chainlock(chain_lock1).unwrap(); - chainlock_manager.queue_pending_chainlock(chain_lock2).unwrap(); - chainlock_manager.queue_pending_chainlock(chain_lock3).unwrap(); + chainlock_manager.queue_pending_chainlock(chain_lock1).await; + chainlock_manager.queue_pending_chainlock(chain_lock2).await; + chainlock_manager.queue_pending_chainlock(chain_lock3).await; // Verify all are queued { // Note: pending_chainlocks is private, can't access directly - let pending = chainlock_manager.pending_chainlocks.read().unwrap(); + let pending = chainlock_manager.pending_chainlocks.read().await; assert_eq!(pending.len(), 3); assert_eq!(pending[0].block_height, 100); assert_eq!(pending[1].block_height, 200); @@ -132,13 +133,13 @@ mod tests { .await; // Test cache operations - assert!(chainlock_manager.has_chain_lock_at_height(0)); + assert!(chainlock_manager.has_chain_lock_at_height(0).await); - let entry = chainlock_manager.get_chain_lock_by_height(0); + let entry = chainlock_manager.get_chain_lock_by_height(0).await; assert!(entry.is_some()); assert_eq!(entry.unwrap().chain_lock.block_height, 0); - let entry_by_hash = chainlock_manager.get_chain_lock_by_hash(&header.block_hash()); + let entry_by_hash = chainlock_manager.get_chain_lock_by_hash(&header.block_hash()).await; assert!(entry_by_hash.is_some()); assert_eq!(entry_by_hash.unwrap().chain_lock.block_height, 0); } diff --git a/dash-spv/src/client/chainlock.rs b/dash-spv/src/client/chainlock.rs index 81c14173a..fee3c7c80 100644 --- a/dash-spv/src/client/chainlock.rs +++ b/dash-spv/src/client/chainlock.rs @@ -129,12 +129,12 @@ impl DashSpvClient crate::Result { + pub async fn update_chainlock_validation(&self) -> crate::Result { // Check if masternode sync has an engine available if let Some(engine) = self.sync_manager.get_masternode_engine() { // Clone the engine for the ChainLockManager let engine_arc = Arc::new(engine.clone()); - self.chainlock_manager.set_masternode_engine(engine_arc); + self.chainlock_manager.set_masternode_engine(engine_arc).await; tracing::info!("Updated ChainLockManager with masternode engine for full validation"); diff --git a/dash-spv/src/client/sync_coordinator.rs b/dash-spv/src/client/sync_coordinator.rs index 89454d414..b51a1366e 100644 --- a/dash-spv/src/client/sync_coordinator.rs +++ b/dash-spv/src/client/sync_coordinator.rs @@ -376,7 +376,7 @@ impl DashSpvClient Date: Thu, 15 Jan 2026 02:04:24 +0000 Subject: [PATCH 14/15] dash_spv main.rs changes applied --- dash-spv/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/dash-spv/src/main.rs b/dash-spv/src/main.rs index e0a35882f..6e3e154f2 100644 --- a/dash-spv/src/main.rs +++ b/dash-spv/src/main.rs @@ -24,7 +24,6 @@ async fn main() { dash_spv::Error::Storage(_) => 2, dash_spv::Error::Validation(_) => 3, dash_spv::Error::Config(_) => 4, - dash_spv::Error::Parse(_) => 5, _ => 255, } } else { From dda15a37bd27c01cda9f3fc04877f95d447c1f74 Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Fri, 16 Jan 2026 02:38:34 +0000 Subject: [PATCH 15/15] disallowed types self documented --- dash-spv/clippy.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dash-spv/clippy.toml b/dash-spv/clippy.toml index 5f0d3b552..7dc72ab7b 100644 --- a/dash-spv/clippy.toml +++ b/dash-spv/clippy.toml @@ -1 +1,4 @@ -disallowed-types = ["std::sync::RwLock", "std::sync::Mutex"] +disallowed-types = [ + { path = "std::sync::RwLock", reason = "This struct is blocking and can be poisoned", replacement = "tokio::sync::RwLock" }, + { path = "std::sync::Mutex", reason = "This struct is blocking and can be poisoned", replacement = "tokio::sync::Mutex" }, +]