Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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"]
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"]
resolver = "2"

[workspace.package]
Expand Down
4 changes: 2 additions & 2 deletions dash-network-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ mod tests {
assert_eq!(devnet_info.magic(), 0xCEFFCAE2);

let regtest_info = NetworkInfo::new(Network::Regtest);
assert_eq!(regtest_info.magic(), 0xDAB5BFFA);
assert_eq!(regtest_info.magic(), 0xDCB7C1FC);
}

#[test]
Expand All @@ -132,7 +132,7 @@ mod tests {
assert!(NetworkInfo::from_magic(0xBD6B0CBF).is_ok());
assert!(NetworkInfo::from_magic(0xFFCAE2CE).is_ok());
assert!(NetworkInfo::from_magic(0xCEFFCAE2).is_ok());
assert!(NetworkInfo::from_magic(0xDAB5BFFA).is_ok());
assert!(NetworkInfo::from_magic(0xDCB7C1FC).is_ok());

// Invalid magic bytes
assert!(matches!(NetworkInfo::from_magic(0x12345678), Err(NetworkError::InvalidMagic)));
Expand Down
6 changes: 3 additions & 3 deletions dash-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Network {
0xBD6B0CBF => Some(Network::Dash),
0xFFCAE2CE => Some(Network::Testnet),
0xCEFFCAE2 => Some(Network::Devnet),
0xDAB5BFFA => Some(Network::Regtest),
0xDCB7C1FC => Some(Network::Regtest),
_ => None,
}
}
Expand Down Expand Up @@ -114,15 +114,15 @@ mod tests {
assert_eq!(Network::Dash.magic(), 0xBD6B0CBF);
assert_eq!(Network::Testnet.magic(), 0xFFCAE2CE);
assert_eq!(Network::Devnet.magic(), 0xCEFFCAE2);
assert_eq!(Network::Regtest.magic(), 0xDAB5BFFA);
assert_eq!(Network::Regtest.magic(), 0xDCB7C1FC);
}

#[test]
fn test_network_from_magic() {
assert_eq!(Network::from_magic(0xBD6B0CBF), Some(Network::Dash));
assert_eq!(Network::from_magic(0xFFCAE2CE), Some(Network::Testnet));
assert_eq!(Network::from_magic(0xCEFFCAE2), Some(Network::Devnet));
assert_eq!(Network::from_magic(0xDAB5BFFA), Some(Network::Regtest));
assert_eq!(Network::from_magic(0xDCB7C1FC), Some(Network::Regtest));
assert_eq!(Network::from_magic(0x12345678), None);
}

Expand Down
37 changes: 37 additions & 0 deletions dash-spv-ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "dash-spv-ffi"
version = "0.1.0"
edition = "2021"
authors = ["Dash Core Developers"]
license = "MIT"
description = "FFI bindings for the Dash SPV client"
repository = "https://github.com/dashpay/rust-dashcore"

[lib]
name = "dash_spv_ffi"
crate-type = ["cdylib", "staticlib", "rlib"]

[dependencies]
dash-spv = { path = "../dash-spv" }
dashcore = { path = "../dash", package = "dashcore" }
libc = "0.2"
once_cell = "1.19"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
log = "0.4"
hex = "0.4"

[dev-dependencies]
tempfile = "3.8"
serial_test = "3.0"
env_logger = "0.10"

[build-dependencies]
cbindgen = "0.26"

[profile.release]
panic = "abort"

[profile.dev]
panic = "abort"
120 changes: 120 additions & 0 deletions dash-spv-ffi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Dash SPV FFI

This crate provides C-compatible FFI bindings for the Dash SPV client library.

## Features

- Complete FFI wrapper for DashSpvClient
- Configuration management
- Wallet operations (watch addresses, balance queries, UTXO management)
- Async operation support via callbacks
- Comprehensive error handling
- Memory-safe abstractions

## Building

```bash
cargo build --release
```

This will generate:
- Static library: `target/release/libdash_spv_ffi.a`
- Dynamic library: `target/release/libdash_spv_ffi.so` (or `.dylib` on macOS)
- C header: `include/dash_spv_ffi.h`

## Usage

See `examples/basic_usage.c` for a simple example of using the FFI bindings.

### Basic Example

```c
#include "dash_spv_ffi.h"

// Initialize logging
dash_spv_ffi_init_logging("info");

// Create configuration
FFIClientConfig* config = dash_spv_ffi_config_testnet();
dash_spv_ffi_config_set_data_dir(config, "/path/to/data");

// Create client
FFIDashSpvClient* client = dash_spv_ffi_client_new(config);
if (client == NULL) {
const char* error = dash_spv_ffi_get_last_error();
// Handle error
}

// Start the client
if (dash_spv_ffi_client_start(client) != 0) {
// Handle error
}

// ... use the client ...

// Clean up
dash_spv_ffi_client_destroy(client);
dash_spv_ffi_config_destroy(config);
```

## API Documentation

### Configuration

- `dash_spv_ffi_config_new(network)` - Create new config
- `dash_spv_ffi_config_mainnet()` - Create mainnet config
- `dash_spv_ffi_config_testnet()` - Create testnet config
- `dash_spv_ffi_config_set_data_dir(config, path)` - Set data directory
- `dash_spv_ffi_config_set_validation_mode(config, mode)` - Set validation mode
- `dash_spv_ffi_config_set_max_peers(config, max)` - Set maximum peers
- `dash_spv_ffi_config_add_peer(config, addr)` - Add a peer address
- `dash_spv_ffi_config_destroy(config)` - Free config memory

### Client Operations

- `dash_spv_ffi_client_new(config)` - Create new client
- `dash_spv_ffi_client_start(client)` - Start the client
- `dash_spv_ffi_client_stop(client)` - Stop the client
- `dash_spv_ffi_client_sync_to_tip(client, callbacks)` - Sync to chain tip
- `dash_spv_ffi_client_get_sync_progress(client)` - Get sync progress
- `dash_spv_ffi_client_get_stats(client)` - Get client statistics
- `dash_spv_ffi_client_destroy(client)` - Free client memory

### Wallet Operations

- `dash_spv_ffi_client_add_watch_item(client, item)` - Add address/script to watch
- `dash_spv_ffi_client_remove_watch_item(client, item)` - Remove watch item
- `dash_spv_ffi_client_get_address_balance(client, address)` - Get address balance
- `dash_spv_ffi_client_get_utxos(client)` - Get all UTXOs
- `dash_spv_ffi_client_get_utxos_for_address(client, address)` - Get UTXOs for address

### Watch Items

- `dash_spv_ffi_watch_item_address(address)` - Create address watch item
- `dash_spv_ffi_watch_item_script(script_hex)` - Create script watch item
- `dash_spv_ffi_watch_item_outpoint(txid, vout)` - Create outpoint watch item
- `dash_spv_ffi_watch_item_destroy(item)` - Free watch item memory

### Error Handling

- `dash_spv_ffi_get_last_error()` - Get last error message
- `dash_spv_ffi_clear_error()` - Clear last error

### Memory Management

All created objects must be explicitly destroyed:
- Config: `dash_spv_ffi_config_destroy()`
- Client: `dash_spv_ffi_client_destroy()`
- Progress: `dash_spv_ffi_sync_progress_destroy()`
- Stats: `dash_spv_ffi_spv_stats_destroy()`
- Balance: `dash_spv_ffi_balance_destroy()`
- Arrays: `dash_spv_ffi_array_destroy()`
- Strings: `dash_spv_ffi_string_destroy()`

## Thread Safety

The FFI bindings are thread-safe. The client uses internal synchronization to ensure safe concurrent access.

## License

MIT
19 changes: 19 additions & 0 deletions dash-spv-ffi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::env;
use std::path::PathBuf;

fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let output_path = PathBuf::from(&crate_dir).join("include");

std::fs::create_dir_all(&output_path).unwrap();

let config = cbindgen::Config::default();

cbindgen::Builder::new()
.with_crate(crate_dir)
.with_config(config)
.with_language(cbindgen::Language::C)
.generate()
.expect("Unable to generate bindings")
.write_to_file(output_path.join("dash_spv_ffi.h"));
}
37 changes: 37 additions & 0 deletions dash-spv-ffi/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# cbindgen configuration for dash-spv-ffi

language = "C"
header = "/* dash-spv-ffi C bindings - Auto-generated by cbindgen */"
include_guard = "DASH_SPV_FFI_H"
autogen_warning = "/* Warning: This file is auto-generated by cbindgen. Do not modify manually. */"
include_version = true
namespace = "dash_spv_ffi"
cpp_compat = true

[export]
include = ["FFI"]
exclude = []
prefix = "dash_spv_ffi_"

[export.rename]
"FFINetwork" = "DashSpvNetwork"
"FFIValidationMode" = "DashSpvValidationMode"
"FFIErrorCode" = "DashSpvErrorCode"
"FFIWatchItemType" = "DashSpvWatchItemType"

[fn]
prefix = ""
postfix = ""

[struct]
rename_fields = "None"

[enum]
rename_variants = "None"

[parse]
parse_deps = false
include = []

[macro_expansion]
bitflags = false
42 changes: 42 additions & 0 deletions dash-spv-ffi/examples/basic_usage.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <stdio.h>
#include <stdlib.h>
#include "../include/dash_spv_ffi.h"

int main() {
// Initialize logging
if (dash_spv_ffi_init_logging("info") != 0) {
fprintf(stderr, "Failed to initialize logging\n");
return 1;
}

// Create a configuration for testnet
FFIClientConfig* config = dash_spv_ffi_config_testnet();
if (config == NULL) {
fprintf(stderr, "Failed to create config\n");
return 1;
}

// Set data directory
if (dash_spv_ffi_config_set_data_dir(config, "/tmp/dash-spv-test") != 0) {
fprintf(stderr, "Failed to set data dir\n");
dash_spv_ffi_config_destroy(config);
return 1;
}

// Create the client
FFIDashSpvClient* client = dash_spv_ffi_client_new(config);
if (client == NULL) {
const char* error = dash_spv_ffi_get_last_error();
fprintf(stderr, "Failed to create client: %s\n", error);
dash_spv_ffi_config_destroy(config);
return 1;
}

printf("Successfully created Dash SPV client!\n");

// Clean up
dash_spv_ffi_client_destroy(client);
dash_spv_ffi_config_destroy(config);

return 0;
}
Loading
Loading