From 7ed888f03536696c674b2a79a16bb6f7751a8b6e Mon Sep 17 00:00:00 2001 From: pasta Date: Fri, 15 Aug 2025 22:59:45 -0500 Subject: [PATCH 1/2] fix: resolve workspace build and test compilation errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes multiple compilation errors across the workspace: Build Fixes: - Fix PSBT module import in dash-fuzz: change from dashcore::psbt to key_wallet::psbt - Add key-wallet dependency to fuzz/Cargo.toml - Fix address module import in key-wallet-ffi: remove non-existent address module - Add missing Error pattern matches: CoinJoinNotEnabled, Serialization, InvalidParameter - Fix type mismatches: secp256k1::PublicKey vs dashcore::PublicKey conversions - Fix Address vs Address using require_network() - Implement AddressGenerator using BIP44 derivation instead of missing struct Test Fixes: - Fix platform integration safety test: separate null pointers for different types - Add missing ChainState fields: sync_base_height, synced_from_checkpoint - Add missing SpvStats fields: connected_peers, total_peers, header_height, filter_height All workspace crates now compile successfully with both cargo build and cargo test. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../tests/test_platform_integration_safety.rs | 5 +- .../tests/unit/test_type_conversions.rs | 6 + fuzz/Cargo.toml | 1 + fuzz/fuzz_targets/dash/deserialize_psbt.rs | 10 +- key-wallet-ffi/src/lib.rs | 137 +++++++++--------- 5 files changed, 83 insertions(+), 76 deletions(-) diff --git a/dash-spv-ffi/tests/test_platform_integration_safety.rs b/dash-spv-ffi/tests/test_platform_integration_safety.rs index 2c6357da3..18a236a87 100644 --- a/dash-spv-ffi/tests/test_platform_integration_safety.rs +++ b/dash-spv-ffi/tests/test_platform_integration_safety.rs @@ -357,10 +357,11 @@ fn test_error_string_lifecycle() { fn test_handle_lifecycle() { unsafe { // Test null handle operations - let null_handle = ptr::null_mut(); + let null_client: *mut FFIDashSpvClient = ptr::null_mut(); + let null_handle: *mut CoreSDKHandle = ptr::null_mut(); // Getting core handle from null client - let handle = ffi_dash_spv_get_core_handle(null_handle); + let handle = ffi_dash_spv_get_core_handle(null_client); assert!(handle.is_null()); // Releasing null handle should be safe diff --git a/dash-spv-ffi/tests/unit/test_type_conversions.rs b/dash-spv-ffi/tests/unit/test_type_conversions.rs index 581f62481..9df36642e 100644 --- a/dash-spv-ffi/tests/unit/test_type_conversions.rs +++ b/dash-spv-ffi/tests/unit/test_type_conversions.rs @@ -172,6 +172,8 @@ mod tests { current_filter_tip: None, masternode_engine: None, last_masternode_diff_height: None, + sync_base_height: 0, + synced_from_checkpoint: false, }; let ffi_state = FFIChainState::from(state); @@ -213,6 +215,10 @@ mod tests { pending_filter_requests: 0, filter_request_timeouts: u64::MAX, filter_requests_retried: u64::MAX, + connected_peers: 0, + total_peers: 0, + header_height: 0, + filter_height: 0, }; let ffi_stats = FFISpvStats::from(stats); diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 7a62ced53..63dd1be50 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true [dependencies] honggfuzz = { version = "0.5", default-features = false } dashcore = { path = "../dash", features = [ "serde" ] } +key-wallet = { path = "../key-wallet" } serde = { version = "1.0.219", features = [ "derive" ] } serde_json = "1.0" diff --git a/fuzz/fuzz_targets/dash/deserialize_psbt.rs b/fuzz/fuzz_targets/dash/deserialize_psbt.rs index c0a0a581d..7159b9982 100644 --- a/fuzz/fuzz_targets/dash/deserialize_psbt.rs +++ b/fuzz/fuzz_targets/dash/deserialize_psbt.rs @@ -1,15 +1,15 @@ use honggfuzz::fuzz; fn do_test(data: &[u8]) { - let psbt: Result = - dashcore::psbt::Psbt::deserialize(data); + let psbt: Result = + key_wallet::psbt::Psbt::deserialize(data); match psbt { Err(_) => {} Ok(psbt) => { - let ser = dashcore::psbt::Psbt::serialize(&psbt); - let deser = dashcore::psbt::Psbt::deserialize(&ser).unwrap(); + let ser = key_wallet::psbt::Psbt::serialize(&psbt); + let deser = key_wallet::psbt::Psbt::deserialize(&ser).unwrap(); // Since the fuzz data could order psbt fields differently, we compare to our deser/ser instead of data - assert_eq!(ser, dashcore::psbt::Psbt::serialize(&deser)); + assert_eq!(ser, key_wallet::psbt::Psbt::serialize(&deser)); } } } diff --git a/key-wallet-ffi/src/lib.rs b/key-wallet-ffi/src/lib.rs index a15cd4e48..119095d9a 100644 --- a/key-wallet-ffi/src/lib.rs +++ b/key-wallet-ffi/src/lib.rs @@ -4,8 +4,9 @@ use std::str::FromStr; use std::sync::Arc; use key_wallet::{ - self as kw, address as kw_address, derivation::HDWallet as KwHDWallet, mnemonic as kw_mnemonic, + self as kw, derivation::HDWallet as KwHDWallet, mnemonic as kw_mnemonic, DerivationPath as KwDerivationPath, ExtendedPrivKey, ExtendedPubKey, Network as KwNetwork, + Address as KwAddress, AddressType as KwAddressType, }; use secp256k1::{PublicKey, Secp256k1}; @@ -75,20 +76,21 @@ pub enum AddressType { P2SH, } -impl From for AddressType { - fn from(t: kw_address::AddressType) -> Self { +impl From for AddressType { + fn from(t: KwAddressType) -> Self { match t { - kw_address::AddressType::P2PKH => AddressType::P2PKH, - kw_address::AddressType::P2SH => AddressType::P2SH, + KwAddressType::P2pkh => AddressType::P2PKH, + KwAddressType::P2sh => AddressType::P2SH, + _ => AddressType::P2PKH, // Default to P2PKH for unknown types } } } -impl From for kw_address::AddressType { +impl From for KwAddressType { fn from(t: AddressType) -> Self { match t { - AddressType::P2PKH => kw_address::AddressType::P2PKH, - AddressType::P2SH => kw_address::AddressType::P2SH, + AddressType::P2PKH => KwAddressType::P2pkh, + AddressType::P2SH => KwAddressType::P2sh, } } } @@ -189,6 +191,15 @@ impl From for KeyWalletError { kw::Error::KeyError(msg) => KeyWalletError::KeyError { message: msg, }, + kw::Error::CoinJoinNotEnabled => KeyWalletError::KeyError { + message: "CoinJoin not enabled".into(), + }, + kw::Error::Serialization(msg) => KeyWalletError::KeyError { + message: format!("Serialization error: {}", msg), + }, + kw::Error::InvalidParameter(msg) => KeyWalletError::KeyError { + message: format!("Invalid parameter: {}", msg), + }, } } } @@ -201,6 +212,14 @@ impl From for KeyWalletError { } } +impl From for KeyWalletError { + fn from(e: kw::dashcore::address::Error) -> Self { + KeyWalletError::AddressError { + message: e.to_string(), + } + } +} + // Validate mnemonic function pub fn validate_mnemonic(phrase: String, language: Language) -> Result { Ok(kw::Mnemonic::validate(&phrase, language.into())) @@ -439,41 +458,18 @@ impl ExtPubKey { // Address wrapper pub struct Address { - inner: kw_address::Address, + inner: KwAddress, } impl Address { pub fn from_string(address: String, network: Network) -> Result { - let inner = kw_address::Address::from_str(&address).map_err(|e| KeyWalletError::from(e))?; + let unchecked_addr = KwAddress::from_str(&address).map_err(|e| KeyWalletError::from(e))?; - // Validate that the parsed network matches the expected network - // Note: Testnet, Devnet, and Regtest all share the same address prefixes (140/19) - // so we need to be flexible when comparing these networks - let parsed_network: KwNetwork = inner.network; + // Convert to expected network and require it let expected_network: KwNetwork = network.into(); - - let networks_compatible = match (parsed_network, expected_network) { - // Exact matches are always OK - (n1, n2) if n1 == n2 => true, - // Testnet addresses can be used on devnet/regtest and vice versa - (KwNetwork::Testnet, KwNetwork::Devnet) - | (KwNetwork::Testnet, KwNetwork::Regtest) - | (KwNetwork::Devnet, KwNetwork::Testnet) - | (KwNetwork::Devnet, KwNetwork::Regtest) - | (KwNetwork::Regtest, KwNetwork::Testnet) - | (KwNetwork::Regtest, KwNetwork::Devnet) => true, - // All other combinations are incompatible - _ => false, - }; - - if !networks_compatible { - return Err(KeyWalletError::AddressError { - message: format!( - "Address is for network {:?}, expected {:?}", - inner.network, network - ), - }); - } + let inner = unchecked_addr.require_network(expected_network).map_err(|e| KeyWalletError::AddressError { + message: format!("Address network validation failed: {}", e), + })?; Ok(Self { inner, @@ -481,11 +477,12 @@ impl Address { } pub fn from_public_key(public_key: Vec, network: Network) -> Result { - let pubkey = + let secp_pubkey = PublicKey::from_slice(&public_key).map_err(|e| KeyWalletError::Secp256k1Error { message: e.to_string(), })?; - let inner = kw_address::Address::p2pkh(&pubkey, network.into()); + let dashcore_pubkey = kw::dashcore::PublicKey::new(secp_pubkey); + let inner = KwAddress::p2pkh(&dashcore_pubkey, network.into()); Ok(Self { inner, }) @@ -496,11 +493,11 @@ impl Address { } pub fn get_type(&self) -> AddressType { - self.inner.address_type.into() + self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into() } pub fn get_network(&self) -> Network { - match self.inner.network { + match *self.inner.network() { KwNetwork::Dash => Network::Dash, KwNetwork::Testnet => Network::Testnet, KwNetwork::Regtest => Network::Regtest, @@ -510,19 +507,19 @@ impl Address { } pub fn get_script_pubkey(&self) -> Vec { - self.inner.script_pubkey() + self.inner.script_pubkey().into() } } // Address generator wrapper pub struct AddressGenerator { - inner: kw_address::AddressGenerator, + network: Network, } impl AddressGenerator { pub fn new(network: Network) -> Self { Self { - inner: kw_address::AddressGenerator::new(network.into()), + network, } } @@ -538,15 +535,28 @@ impl AddressGenerator { message: e.to_string(), })?; - // Generate addresses for a single index - let addrs = self - .inner - .generate_range(&xpub, external, index, 1) - .map_err(|e| KeyWalletError::from(e))?; + let secp = Secp256k1::new(); - let addr = addrs.into_iter().next().ok_or_else(|| KeyWalletError::KeyError { - message: "Failed to generate address".into(), - })?; + // Derive child key: 0 for external (receiving), 1 for internal (change) + let chain_code = if external { 0 } else { 1 }; + let child_chain = xpub.ckd_pub(&secp, kw::ChildNumber::from_normal_idx(chain_code) + .map_err(|e| KeyWalletError::InvalidDerivationPath { + message: e.to_string(), + })?).map_err(|e| KeyWalletError::KeyError { + message: e.to_string(), + })?; + + // Derive specific index + let child = child_chain.ckd_pub(&secp, kw::ChildNumber::from_normal_idx(index) + .map_err(|e| KeyWalletError::InvalidDerivationPath { + message: e.to_string(), + })?).map_err(|e| KeyWalletError::KeyError { + message: e.to_string(), + })?; + + // Generate P2PKH address from the public key + let dashcore_pubkey = kw::dashcore::PublicKey::new(child.public_key); + let addr = KwAddress::p2pkh(&dashcore_pubkey, self.network.into()); Ok(Arc::new(Address { inner: addr, @@ -560,25 +570,14 @@ impl AddressGenerator { start: u32, count: u32, ) -> Result>, KeyWalletError> { - // Parse the extended public key from string - let xpub = - ExtendedPubKey::from_str(&account_xpub.xpub).map_err(|e| KeyWalletError::KeyError { - message: e.to_string(), - })?; + let mut addresses = Vec::new(); - let addrs = self - .inner - .generate_range(&xpub, external, start, count) - .map_err(|e| KeyWalletError::from(e))?; + for i in 0..count { + let addr = self.generate(account_xpub.clone(), external, start + i)?; + addresses.push(addr); + } - Ok(addrs - .into_iter() - .map(|addr| { - Arc::new(Address { - inner: addr, - }) - }) - .collect()) + Ok(addresses) } } From 2bea21c7877cd4a719dc593d51c1300da37c75bb Mon Sep 17 00:00:00 2001 From: pasta Date: Fri, 15 Aug 2025 23:01:52 -0500 Subject: [PATCH 2/2] chore: run cargo fmt --- key-wallet-ffi/src/lib.rs | 46 +++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/key-wallet-ffi/src/lib.rs b/key-wallet-ffi/src/lib.rs index 119095d9a..7cbced7bd 100644 --- a/key-wallet-ffi/src/lib.rs +++ b/key-wallet-ffi/src/lib.rs @@ -4,9 +4,9 @@ use std::str::FromStr; use std::sync::Arc; use key_wallet::{ - self as kw, derivation::HDWallet as KwHDWallet, mnemonic as kw_mnemonic, - DerivationPath as KwDerivationPath, ExtendedPrivKey, ExtendedPubKey, Network as KwNetwork, - Address as KwAddress, AddressType as KwAddressType, + self as kw, derivation::HDWallet as KwHDWallet, mnemonic as kw_mnemonic, Address as KwAddress, + AddressType as KwAddressType, DerivationPath as KwDerivationPath, ExtendedPrivKey, + ExtendedPubKey, Network as KwNetwork, }; use secp256k1::{PublicKey, Secp256k1}; @@ -467,8 +467,10 @@ impl Address { // Convert to expected network and require it let expected_network: KwNetwork = network.into(); - let inner = unchecked_addr.require_network(expected_network).map_err(|e| KeyWalletError::AddressError { - message: format!("Address network validation failed: {}", e), + let inner = unchecked_addr.require_network(expected_network).map_err(|e| { + KeyWalletError::AddressError { + message: format!("Address network validation failed: {}", e), + } })?; Ok(Self { @@ -538,19 +540,35 @@ impl AddressGenerator { let secp = Secp256k1::new(); // Derive child key: 0 for external (receiving), 1 for internal (change) - let chain_code = if external { 0 } else { 1 }; - let child_chain = xpub.ckd_pub(&secp, kw::ChildNumber::from_normal_idx(chain_code) - .map_err(|e| KeyWalletError::InvalidDerivationPath { - message: e.to_string(), - })?).map_err(|e| KeyWalletError::KeyError { + let chain_code = if external { + 0 + } else { + 1 + }; + let child_chain = xpub + .ckd_pub( + &secp, + kw::ChildNumber::from_normal_idx(chain_code).map_err(|e| { + KeyWalletError::InvalidDerivationPath { + message: e.to_string(), + } + })?, + ) + .map_err(|e| KeyWalletError::KeyError { message: e.to_string(), })?; // Derive specific index - let child = child_chain.ckd_pub(&secp, kw::ChildNumber::from_normal_idx(index) - .map_err(|e| KeyWalletError::InvalidDerivationPath { - message: e.to_string(), - })?).map_err(|e| KeyWalletError::KeyError { + let child = child_chain + .ckd_pub( + &secp, + kw::ChildNumber::from_normal_idx(index).map_err(|e| { + KeyWalletError::InvalidDerivationPath { + message: e.to_string(), + } + })?, + ) + .map_err(|e| KeyWalletError::KeyError { message: e.to_string(), })?;