Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test"]
members = ["dash", "dash-network", "dash-network-ffi", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-ffi"]
resolver = "2"

[workspace.package]
Expand Down
29 changes: 29 additions & 0 deletions dash-network-ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "dash-network-ffi"
version.workspace = true
edition = "2021"
authors = ["Quantum Explorer <[email protected]>"]
license = "CC0-1.0"
repository = "https://github.com/dashpay/rust-dashcore/"
description = "FFI bindings for dash-network types"
keywords = ["dash", "network", "ffi", "bindings"]
readme = "README.md"

[dependencies]
dash-network = { path = "../dash-network", default-features = false }
uniffi = { version = "0.27", features = ["cli"] }
thiserror = "1.0"

[build-dependencies]
uniffi = { version = "0.27", features = ["build"] }

[dev-dependencies]
hex = "0.4"

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

[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"
116 changes: 116 additions & 0 deletions dash-network-ffi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# dash-network-ffi

FFI bindings for the dash-network crate, providing language bindings via UniFFI.

## Overview

This crate provides Foreign Function Interface (FFI) bindings for the `dash-network` types, allowing them to be used from other programming languages like Swift, Python, Kotlin, and Ruby.

## Features

- UniFFI-based bindings for the Network enum
- Network information and utilities exposed through FFI
- Support for magic bytes operations
- Core version activation queries

## Usage

### Building

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

### Generating Bindings

To generate bindings for your target language:

```bash
cargo run --bin uniffi-bindgen generate src/dash_network.udl --language swift
cargo run --bin uniffi-bindgen generate src/dash_network.udl --language python
cargo run --bin uniffi-bindgen generate src/dash_network.udl --language kotlin
```

### Example Usage (Swift)

```swift
// Initialize the library
dashNetworkFfiInitialize()

// Create a network info object
let networkInfo = NetworkInfo(network: .dash)

// Get magic bytes
let magic = networkInfo.magic()
print("Dash network magic: \(String(format: "0x%08X", magic))")

// Check if core v20 is active
if networkInfo.isCoreV20Active(blockHeight: 2000000) {
print("Core v20 is active!")
}

// Create from magic bytes
do {
let network = try NetworkInfo.fromMagic(magic: 0xBD6B0CBF)
print("Network: \(network.toString())")
} catch {
print("Invalid magic bytes")
}
```

### Example Usage (Python)

```python
import dash_network_ffi

# Initialize the library
dash_network_ffi.initialize()

# Create a network info object
network_info = dash_network_ffi.NetworkInfo(dash_network_ffi.Network.DASH)

# Get magic bytes
magic = network_info.magic()
print(f"Dash network magic: 0x{magic:08X}")

# Check if core v20 is active
if network_info.is_core_v20_active(2000000):
print("Core v20 is active!")

# Create from magic bytes
try:
network = dash_network_ffi.NetworkInfo.from_magic(0xBD6B0CBF)
print(f"Network: {network.to_string()}")
except dash_network_ffi.NetworkError.InvalidMagic:
print("Invalid magic bytes")
```

## API

### Network Enum

- `Dash` - Dash mainnet
- `Testnet` - Dash testnet
- `Devnet` - Dash devnet
- `Regtest` - Regression test network

### NetworkInfo Class

#### Constructors
- `new(network: Network)` - Create from a Network enum value
- `from_magic(magic: u32)` - Create from magic bytes (throws NetworkError)

#### Methods
- `magic() -> u32` - Get the network's magic bytes
- `to_string() -> String` - Get the network name as a string
- `is_core_v20_active(block_height: u32) -> bool` - Check if core v20 is active at height
- `core_v20_activation_height() -> u32` - Get the activation height for core v20

### NetworkError Enum

- `InvalidMagic` - Invalid magic bytes provided
- `InvalidNetwork` - Invalid network specified

## License

This project is licensed under the CC0 1.0 Universal license.
3 changes: 3 additions & 0 deletions dash-network-ffi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
uniffi::generate_scaffolding("src/dash_network.udl").unwrap();
}
41 changes: 41 additions & 0 deletions dash-network-ffi/src/dash_network.udl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace dash_network_ffi {
// Initialize function for any setup needs
void initialize();
};

// Network enum matching the dash-network crate
enum Network {
"Dash",
"Testnet",
"Devnet",
"Regtest",
};

// Interface for network-related operations
interface NetworkInfo {
// Constructor
[Name=new]
constructor(Network network);

// Create from magic bytes
[Name=from_magic, Throws=NetworkError]
constructor(u32 magic);

// Get the magic bytes for this network
u32 magic();

// Get the network as a string
string to_string();

// Check if core v20 is active at a given height
boolean is_core_v20_active(u32 block_height);

// Get the core v20 activation height
u32 core_v20_activation_height();
};

[Error]
enum NetworkError {
"InvalidMagic",
"InvalidNetwork",
};
173 changes: 173 additions & 0 deletions dash-network-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//! FFI bindings for dash-network library

use dash_network::Network as DashNetwork;

// Include the UniFFI scaffolding
uniffi::include_scaffolding!("dash_network");

// Initialize function
pub fn initialize() {
// Any global initialization if needed
}

// Re-export Network enum for UniFFI
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Network {
Dash,
Testnet,
Devnet,
Regtest,
}

impl From<Network> for DashNetwork {
fn from(n: Network) -> Self {
match n {
Network::Dash => DashNetwork::Dash,
Network::Testnet => DashNetwork::Testnet,
Network::Devnet => DashNetwork::Devnet,
Network::Regtest => DashNetwork::Regtest,
}
}
}

impl From<DashNetwork> for Network {
fn from(n: DashNetwork) -> Self {
match n {
DashNetwork::Dash => Network::Dash,
DashNetwork::Testnet => Network::Testnet,
DashNetwork::Devnet => Network::Devnet,
DashNetwork::Regtest => Network::Regtest,
_ => Network::Testnet, // Default for unknown networks
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum NetworkError {
#[error("Invalid magic bytes")]
InvalidMagic,
#[error("Invalid network")]
InvalidNetwork,
}

pub struct NetworkInfo {
network: DashNetwork,
}

impl NetworkInfo {
pub fn new(network: Network) -> Self {
Self {
network: network.into(),
}
}

pub fn from_magic(magic: u32) -> Result<Self, NetworkError> {
DashNetwork::from_magic(magic)
.map(|network| Self {
network,
})
.ok_or(NetworkError::InvalidMagic)
}

pub fn magic(&self) -> u32 {
self.network.magic()
}

pub fn to_string(&self) -> String {
self.network.to_string()
}

pub fn is_core_v20_active(&self, block_height: u32) -> bool {
self.network.core_v20_is_active_at(block_height)
}

pub fn core_v20_activation_height(&self) -> u32 {
self.network.core_v20_activation_height()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_network_conversion() {
// Test FFI to Dash Network conversion
assert_eq!(DashNetwork::from(Network::Dash), DashNetwork::Dash);
assert_eq!(DashNetwork::from(Network::Testnet), DashNetwork::Testnet);
assert_eq!(DashNetwork::from(Network::Devnet), DashNetwork::Devnet);
assert_eq!(DashNetwork::from(Network::Regtest), DashNetwork::Regtest);

// Test Dash Network to FFI conversion
assert_eq!(Network::from(DashNetwork::Dash), Network::Dash);
assert_eq!(Network::from(DashNetwork::Testnet), Network::Testnet);
assert_eq!(Network::from(DashNetwork::Devnet), Network::Devnet);
assert_eq!(Network::from(DashNetwork::Regtest), Network::Regtest);
}

#[test]
fn test_network_info_creation() {
let info = NetworkInfo::new(Network::Dash);
assert_eq!(info.network, DashNetwork::Dash);
}

#[test]
fn test_magic_bytes() {
let dash_info = NetworkInfo::new(Network::Dash);
assert_eq!(dash_info.magic(), 0xBD6B0CBF);

let testnet_info = NetworkInfo::new(Network::Testnet);
assert_eq!(testnet_info.magic(), 0xFFCAE2CE);

let devnet_info = NetworkInfo::new(Network::Devnet);
assert_eq!(devnet_info.magic(), 0xCEFFCAE2);

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

#[test]
fn test_from_magic() {
// Valid magic bytes
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());

// Invalid magic bytes
assert!(matches!(NetworkInfo::from_magic(0x12345678), Err(NetworkError::InvalidMagic)));
}

#[test]
fn test_network_to_string() {
assert_eq!(NetworkInfo::new(Network::Dash).to_string(), "dash");
assert_eq!(NetworkInfo::new(Network::Testnet).to_string(), "testnet");
assert_eq!(NetworkInfo::new(Network::Devnet).to_string(), "devnet");
assert_eq!(NetworkInfo::new(Network::Regtest).to_string(), "regtest");
}

#[test]
fn test_core_v20_activation() {
let dash_info = NetworkInfo::new(Network::Dash);
assert_eq!(dash_info.core_v20_activation_height(), 1987776);
assert!(!dash_info.is_core_v20_active(1987775));
assert!(dash_info.is_core_v20_active(1987776));
assert!(dash_info.is_core_v20_active(2000000));

let testnet_info = NetworkInfo::new(Network::Testnet);
assert_eq!(testnet_info.core_v20_activation_height(), 905100);
assert!(!testnet_info.is_core_v20_active(905099));
assert!(testnet_info.is_core_v20_active(905100));
}

#[test]
fn test_round_trip_conversions() {
let networks = vec![Network::Dash, Network::Testnet, Network::Devnet, Network::Regtest];

for network in networks {
let dash_network: DashNetwork = network.into();
let back_to_ffi: Network = dash_network.into();
assert_eq!(network, back_to_ffi);
}
}
}
3 changes: 3 additions & 0 deletions dash-network-ffi/uniffi-bindgen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
uniffi::uniffi_bindgen_main()
}
Loading
Loading