Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a4f0226
sync from genesis working kind of
pauldelucia Jul 21, 2025
2df6271
synced fully
pauldelucia Jul 21, 2025
f5aeb27
progress
pauldelucia Jul 25, 2025
66fb254
ok
pauldelucia Jul 25, 2025
11168f8
fix(swift-sdk): remove DashSPVFFI target for unified SDK compatibility
Jul 16, 2025
cce18a0
fix: handle filter sync skip properly in sequential sync manager
Jul 16, 2025
2db420b
fix: improve masternode sync robustness and completion handling
pauldelucia Jul 25, 2025
9f73404
ok
pauldelucia Jul 25, 2025
c8ecb70
works
pauldelucia Jul 28, 2025
f0445db
feat: make header storage batched and atomic
pauldelucia Jul 30, 2025
45b5c4b
delete some logs
pauldelucia Jul 30, 2025
dfac99a
fix logging of states and ask only one peer for mnlist diffs
pauldelucia Jul 31, 2025
8695a9f
transition to fully synced
pauldelucia Jul 31, 2025
3841bd7
remove "received command" log
pauldelucia Aug 1, 2025
d12d3ee
big refactor, working very well
pauldelucia Aug 3, 2025
ba73c8e
fix: storing duplicate headers when manually stopped spv
pauldelucia Aug 4, 2025
d14594a
chore: run `cargo fmt`
PastaPastaPasta Aug 4, 2025
2e7d966
build: add test-utils workspace member and dependencies
Jul 22, 2025
2c0b4e3
feat(dash): add LLMQ DKG window calculations and fix intervals
Jul 22, 2025
e0c72c1
feat(swift-sdk): expose FFI client handle for Platform SDK integration
Jul 22, 2025
f5a847e
refactor(dash-spv-ffi): improve error handling traits
Jul 22, 2025
a6f32d2
test(dash-spv-ffi): add platform integration tests and documentation
Jul 22, 2025
ba59404
refactor(ffi): replace underscore-prefixed parameters with standard n…
Jul 23, 2025
a3278ee
feat: update mainnet checkpoints to match dash-cli
PastaPastaPasta Jul 23, 2025
d7fc749
docs(dash-spv): improve storage API documentation
PastaPastaPasta Jul 24, 2025
62e5c8a
sync from genesis working kind of
pauldelucia Jul 21, 2025
3a78281
checkpoints working - values for 1900000 fixed. others probably need …
pauldelucia Aug 4, 2025
fc254c5
sync from checkpoint works well
pauldelucia Aug 5, 2025
58a51d6
feat: add in a key wallet manager
QuantumExplorer Aug 11, 2025
883c5ba
a lot of fixes
QuantumExplorer Aug 11, 2025
4226906
cleanup
QuantumExplorer Aug 11, 2025
7ba6404
fixes
QuantumExplorer Aug 12, 2025
0a3e0b5
refactoring of key-wallet
QuantumExplorer Aug 12, 2025
26ccc5a
various fixes
QuantumExplorer Aug 12, 2025
f4dd524
fixes for wallet manager
QuantumExplorer Aug 12, 2025
1343894
Merge branch 'v0.40-dev' into spv-in-det-rebased-sam
QuantumExplorer Aug 12, 2025
fcb8aaa
revert
QuantumExplorer Aug 12, 2025
c3b133c
clean
QuantumExplorer Aug 12, 2025
4cccbe4
clean
QuantumExplorer Aug 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["dash", "dash-network", "dash-network-ffi", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-ffi", "dash-spv", "dash-spv-ffi"]
members = ["dash", "dash-network", "dash-network-ffi", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-ffi", "key-wallet-manager", "dash-spv", "dash-spv-ffi"]
resolver = "2"

[workspace.package]
Expand Down
791 changes: 791 additions & 0 deletions PLAN.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Supports (or should support)
* JSONRPC interaction with Dash Core
* FFI bindings for C/Swift integration (dash-spv-ffi, key-wallet-ffi)
* [Unified SDK](UNIFIED_SDK.md) option for iOS that combines Core and Platform functionality
* [High-level wallet management](key-wallet-manager/README.md) with transaction building and UTXO management

# Known limitations

Expand Down Expand Up @@ -79,6 +80,17 @@ fn main() {

See `client/examples/` for more usage examples.

# Wallet Management

This library provides comprehensive wallet functionality through multiple components:

* **key-wallet**: Low-level cryptographic primitives for HD wallets, mnemonic generation, and key derivation
* **[key-wallet-manager](key-wallet-manager/README.md)**: High-level wallet management with transaction building, UTXO tracking, and coin selection
* **key-wallet-ffi**: C/Swift FFI bindings for mobile integration
* **dash-spv**: SPV (Simplified Payment Verification) client implementation

For most applications, start with [key-wallet-manager](key-wallet-manager/README.md) which provides a complete, easy-to-use interface for wallet operations.

# Supported Dash Core Versions
The following versions are officially supported and automatically tested:
* 0.18.0
Expand Down Expand Up @@ -109,6 +121,11 @@ cargo update --package "byteorder" --precise "1.3.4"

Documentation can be found on [dashcore.readme.io/docs](https://dashcore.readme.io/docs).

## Component Documentation

* **[key-wallet-manager](key-wallet-manager/README.md)** - High-level wallet management guide
* **[Unified SDK](UNIFIED_SDK.md)** - iOS SDK combining Core and Platform functionality

# Contributing

Contributions are generally welcome. If you intend to make larger changes please
Expand Down
61 changes: 61 additions & 0 deletions TODOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# TODOs for Rust Dashcore Key Wallet System

## Critical Issues (Prevent Compilation)

### dashcore crate compilation errors
The underlying `dashcore` crate has pre-existing compilation errors that prevent `key-wallet-manager` from building:

1. **Missing imports in crypto/sighash.rs**: Two unresolved imports are causing E0432 errors
2. **65 warnings in dashcore**: Various deprecated method usage and unused variables

**Impact**: key-wallet-manager cannot compile until dashcore is fixed.
**Priority**: Critical - blocks all high-level wallet functionality.

## Remaining Features (Optional)

### Serialization support
The last pending feature from the original plan:

1. **Create wallet serialization**: Add serde support for saving/loading wallets from disk
2. **Encrypted wallet storage**: Add password protection for saved wallets
3. **Backup and restore**: Implement mnemonic and xprv/xpub backup functionality

**Impact**: Wallets cannot be persisted between application runs.
**Priority**: Medium - useful for production applications.

### Testing improvements
1. **Multi-language mnemonic tests**: Currently marked as `#[ignore]` - need actual multi-language support
2. **Integration tests**: More comprehensive testing of key-wallet + key-wallet-manager integration
3. **Transaction building tests**: Test actual transaction creation and signing

## Known Limitations

### Watch-only wallet derivation
The current watch-only wallet implementation creates its own derivation paths rather than using the exact same addresses as the original wallet. This is due to the separation between account-level xpubs and the AddressPool API requirements.

### dashcore dependency issues
The architecture assumes dashcore will eventually compile. If dashcore continues to have issues, key-wallet-manager may need to:
1. Use a different transaction library
2. Implement transaction types internally
3. Wait for dashcore fixes

## Status Summary

✅ **Completed Successfully:**
- Restructured crate architecture (key-wallet + key-wallet-manager)
- Fixed all key-wallet compilation issues
- Added comprehensive tests for mnemonics and address management
- Created watch-only wallet functionality
- Enhanced derivation module with builder pattern
- Separated low-level primitives from high-level operations

❌ **Blocked by External Issues:**
- key-wallet-manager compilation (blocked by dashcore)
- Transaction building functionality (blocked by dashcore)
- Integration tests (blocked by dashcore)

✅ **Architecture Goals Met:**
- Clean separation of concerns
- No circular dependencies
- Proper use of existing dashcore types
- Extensible design for future features
10 changes: 5 additions & 5 deletions dash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ default = [ "std", "secp-recovery", "bincode" ]
base64 = [ "base64-compat" ]
rand-std = ["secp256k1/rand"]
rand = ["secp256k1/rand"]
serde = ["actual-serde", "dashcore_hashes/serde", "secp256k1/serde", "key-wallet/serde", "dash-network/serde"]
serde = ["dep:serde", "dashcore_hashes/serde", "secp256k1/serde", "dash-network/serde"]
secp-lowmemory = ["secp256k1/lowmemory"]
secp-recovery = ["secp256k1/recovery"]
signer = ["secp-recovery", "rand", "base64"]
Expand All @@ -39,7 +39,7 @@ bincode = [ "dep:bincode", "dep:bincode_derive", "dashcore_hashes/bincode", "das
# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
# Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict.
std = ["secp256k1/std", "dashcore_hashes/std", "bech32/std", "internals/std", "key-wallet/std", "dash-network/std"]
std = ["secp256k1/std", "dashcore_hashes/std", "bech32/std", "internals/std", "dash-network/std"]
no-std = ["core2", "dashcore_hashes/alloc", "dashcore_hashes/core2", "secp256k1/alloc", "dash-network/no-std"]

[package.metadata.docs.rs]
Expand All @@ -51,12 +51,10 @@ internals = { path = "../internals", package = "dashcore-private" }
bech32 = { version = "0.9.1", default-features = false }
dashcore_hashes = { path = "../hashes", default-features = false }
secp256k1 = { default-features = false, features = ["hashes"], version= "0.30.0" }
key-wallet = { path = "../key-wallet", default-features = false }
dash-network = { path = "../dash-network", default-features = false }
core2 = { version = "0.4.0", optional = true, features = ["alloc"], default-features = false }
rustversion = { version="1.0.20"}
# Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0.219", default-features = false, features = [ "derive", "alloc" ], optional = true }
serde = { version = "1.0.219", default-features = false, features = [ "derive", "alloc" ], optional = true }

base64-compat = { version = "1.0.0", optional = true }
bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
Expand All @@ -71,7 +69,9 @@ ed25519-dalek = { version = "2.1", features = ["rand_core"], optional = true }
blake3 = "1.8.1"
thiserror = "2"
bitvec = "1.0"
log = "0.4"
# bls-signatures removed during migration to agora-blsful
tracing = "0.1"

[dev-dependencies]
serde_json = "1.0.140"
Expand Down
112 changes: 107 additions & 5 deletions dash/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;

use bech32;
use hashes::{Hash, HashEngine, sha256};
use internals::write_err;
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};

use crate::base58;
use crate::blockdata::constants::{
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
Expand All @@ -66,7 +61,13 @@ use crate::error::ParseIntError;
use crate::hash_types::{PubkeyHash, ScriptHash};
use crate::prelude::*;
use crate::taproot::TapNodeHash;
use bech32;
use dash_network::Network;
use hashes::{Hash, HashEngine, sha256};
use internals::write_err;
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Address error.
#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -183,6 +184,7 @@ impl From<bech32::Error> for Error {

/// The different types of addresses.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum AddressType {
/// Pay to pubkey hash.
Expand Down Expand Up @@ -812,6 +814,25 @@ crate::serde_utils::serde_string_serialize_impl!(Address, "a Dash address");
#[cfg(feature = "serde")]
crate::serde_utils::serde_string_deserialize_impl!(Address<NetworkUnchecked>, "a Dash address");

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Address<NetworkChecked> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use core::str::FromStr;
use serde::de::Error;

let s = String::deserialize(deserializer)?;
let addr_unchecked = Address::<NetworkUnchecked>::from_str(&s).map_err(D::Error::custom)?;

// For NetworkChecked, we need to assume a network. This is a limitation
// of deserializing without network context. Users should use Address<NetworkUnchecked>
// for serde when the network is not known at compile time.
addr_unchecked.require_network(Network::Dash).map_err(D::Error::custom)
}
}

#[cfg(feature = "serde")]
impl serde::Serialize for Address<NetworkUnchecked> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
Expand All @@ -822,6 +843,87 @@ impl serde::Serialize for Address<NetworkUnchecked> {
}
}

#[cfg(feature = "bincode")]
impl bincode::Encode for Address {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
self.to_string().encode(encoder)
}
}

#[cfg(feature = "bincode")]
impl bincode::Decode for Address {
fn decode<D: bincode::de::Decoder>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
use core::str::FromStr;
let s = String::decode(decoder)?;
Address::from_str(&s)
.map_err(|e| bincode::error::DecodeError::OtherString(e.to_string()))
.map(|a| a.assume_checked())
}
}

#[cfg(feature = "bincode")]
impl<'de> bincode::BorrowDecode<'de> for Address {
fn borrow_decode<D: bincode::de::BorrowDecoder<'de>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
use core::str::FromStr;
let s = String::borrow_decode(decoder)?;
Address::from_str(&s)
.map_err(|e| bincode::error::DecodeError::OtherString(e.to_string()))
.map(|a| a.assume_checked())
}
}

#[cfg(feature = "bincode")]
impl bincode::Encode for AddressType {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
use bincode::Encode;
(*self as u8).encode(encoder)
}
}

#[cfg(feature = "bincode")]
impl bincode::Decode for AddressType {
fn decode<D: bincode::de::Decoder>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let val = u8::decode(decoder)?;
match val {
0 => Ok(AddressType::P2pkh),
1 => Ok(AddressType::P2sh),
2 => Ok(AddressType::P2wpkh),
3 => Ok(AddressType::P2wsh),
4 => Ok(AddressType::P2tr),
_ => Err(bincode::error::DecodeError::OtherString("invalid address type".to_string())),
}
}
}

#[cfg(feature = "bincode")]
impl<'de> bincode::BorrowDecode<'de> for AddressType {
fn borrow_decode<D: bincode::de::BorrowDecoder<'de>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let val = u8::borrow_decode(decoder)?;
match val {
0 => Ok(AddressType::P2pkh),
1 => Ok(AddressType::P2sh),
2 => Ok(AddressType::P2wpkh),
3 => Ok(AddressType::P2wsh),
4 => Ok(AddressType::P2tr),
_ => Err(bincode::error::DecodeError::OtherString("invalid address type".to_string())),
}
}
}

/// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and
/// `Address<NetworkUnchecked>`.
impl<V: NetworkValidation> Address<V> {
Expand Down
13 changes: 6 additions & 7 deletions dash/src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ fn unsigned_abs(x: i8) -> u8 {
x.wrapping_abs() as u8
}

fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
fn repeat_char(f: &mut dyn Write, c: char, count: usize) -> fmt::Result {
for _ in 0..count {
f.write_char(c)?;
}
Expand All @@ -369,7 +369,7 @@ fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
fn fmt_satoshi_in(
satoshi: u64,
negative: bool,
f: &mut dyn fmt::Write,
f: &mut dyn Write,
denom: Denomination,
show_denom: bool,
options: FormatOptions,
Expand Down Expand Up @@ -1264,7 +1264,7 @@ pub mod serde {
//! use dash::Amount;
//!
//! #[derive(Serialize, Deserialize)]
//! # #[serde(crate = "actual_serde")]
//! # #[serde(crate = "serde")]
//! pub struct HasAmount {
//! #[serde(with = "dash::amount::serde::as_btc")]
//! pub amount: Amount,
Expand Down Expand Up @@ -2151,7 +2151,6 @@ mod tests {
#[test]
fn serde_as_sat() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "actual_serde")]
struct T {
#[serde(with = "crate::amount::serde::as_sat")]
pub amt: Amount,
Expand Down Expand Up @@ -2185,7 +2184,7 @@ mod tests {
use serde_json;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "actual_serde")]

struct T {
#[serde(with = "crate::amount::serde::as_btc")]
pub amt: Amount,
Expand Down Expand Up @@ -2221,7 +2220,7 @@ mod tests {
use serde_json;

#[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
#[serde(crate = "actual_serde")]

struct T {
#[serde(default, with = "crate::amount::serde::as_btc::opt")]
pub amt: Option<Amount>,
Expand Down Expand Up @@ -2266,7 +2265,7 @@ mod tests {
use serde_json;

#[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
#[serde(crate = "actual_serde")]

struct T {
#[serde(default, with = "crate::amount::serde::as_sat::opt")]
pub amt: Option<Amount>,
Expand Down
6 changes: 3 additions & 3 deletions dash/src/blockdata/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use crate::{VarInt, io, merkle_tree};
/// * [CBlockHeader definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L20)
#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]

pub struct Header {
/// Block version, now repurposed for soft fork signalling.
pub version: Version,
Expand Down Expand Up @@ -113,7 +113,7 @@ impl Header {
/// * [BIP34 - Block v2, Height in Coinbase](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki)
#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]

pub struct Version(i32);

impl Version {
Expand Down Expand Up @@ -199,7 +199,7 @@ impl Decodable for Version {
/// * [CBlock definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L62)
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]

pub struct Block {
/// The block header
pub header: Header,
Expand Down
1 change: 0 additions & 1 deletion dash/src/blockdata/fee_rate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::prelude::*;
/// up the types as well as basic formatting features.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct FeeRate(u64);

Expand Down
Loading
Loading