diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 8642c7a56..0e4b18fa5 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 57 +**Total Functions**: 61 ## Table of Contents @@ -132,11 +132,15 @@ Functions: 2 ### Utility Functions -Functions: 11 +Functions: 15 | Function | Description | Module | |----------|-------------|--------| | `dash_spv_ffi_array_destroy` | No description | types | +| `dash_spv_ffi_checkpoint_before_height` | Get the last checkpoint at or before a given height | checkpoints | +| `dash_spv_ffi_checkpoint_before_timestamp` | Get the last checkpoint at or before a given UNIX timestamp (seconds) | checkpoints | +| `dash_spv_ffi_checkpoint_latest` | Get the latest checkpoint for the given network | checkpoints | +| `dash_spv_ffi_checkpoints_between_heights` | Get all checkpoints between two heights (inclusive) | checkpoints | | `dash_spv_ffi_client_get_stats` | No description | client | | `dash_spv_ffi_client_get_wallet_manager` | Get the wallet manager from the SPV client Returns an opaque pointer to FFIW... | client | | `dash_spv_ffi_client_record_send` | No description | client | @@ -826,6 +830,67 @@ dash_spv_ffi_array_destroy(arr: *mut FFIArray) -> () --- +#### `dash_spv_ffi_checkpoint_before_height` + +```c +dash_spv_ffi_checkpoint_before_height(network: FFINetwork, height: u32, out_height: *mut u32, out_hash: *mut u8, // expects at least 32 bytes) -> i32 +``` + +**Description:** +Get the last checkpoint at or before a given height. # Safety - `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Safety:** +- `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Module:** `checkpoints` + +--- + +#### `dash_spv_ffi_checkpoint_before_timestamp` + +```c +dash_spv_ffi_checkpoint_before_timestamp(network: FFINetwork, timestamp: u32, out_height: *mut u32, out_hash: *mut u8, // expects at least 32 bytes) -> i32 +``` + +**Description:** +Get the last checkpoint at or before a given UNIX timestamp (seconds). # Safety - `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Safety:** +- `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Module:** `checkpoints` + +--- + +#### `dash_spv_ffi_checkpoint_latest` + +```c +dash_spv_ffi_checkpoint_latest(network: FFINetwork, out_height: *mut u32, out_hash: *mut u8, // expects at least 32 bytes) -> i32 +``` + +**Description:** +Get the latest checkpoint for the given network. # Safety - `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Safety:** +- `out_height` must be a valid pointer to a `u32`. - `out_hash` must point to at least 32 writable bytes. + +**Module:** `checkpoints` + +--- + +#### `dash_spv_ffi_checkpoints_between_heights` + +```c +dash_spv_ffi_checkpoints_between_heights(network: FFINetwork, start_height: u32, end_height: u32,) -> FFIArray +``` + +**Description:** +Get all checkpoints between two heights (inclusive). Returns an `FFIArray` of `FFICheckpoint` items. The caller owns the memory and must free the array buffer using `dash_spv_ffi_array_destroy` when done. + +**Module:** `checkpoints` + +--- + #### `dash_spv_ffi_client_get_stats` ```c diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index 932dddefe..8ed308121 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -129,21 +129,6 @@ typedef struct FFIEventCallbacks { void *user_data; } FFIEventCallbacks; -/** - * Handle for Core SDK that can be passed to Platform SDK - */ -typedef struct CoreSDKHandle { - struct FFIDashSpvClient *client; -} CoreSDKHandle; - -/** - * FFIResult type for error handling - */ -typedef struct FFIResult { - int32_t error_code; - const char *error_message; -} FFIResult; - /** * FFI-safe array that transfers ownership of memory to the C caller. * @@ -163,6 +148,21 @@ typedef struct FFIArray { uintptr_t elem_align; } FFIArray; +/** + * Handle for Core SDK that can be passed to Platform SDK + */ +typedef struct CoreSDKHandle { + struct FFIDashSpvClient *client; +} CoreSDKHandle; + +/** + * FFIResult type for error handling + */ +typedef struct FFIResult { + int32_t error_code; + const char *error_message; +} FFIResult; + /** * FFI-safe representation of an unconfirmed transaction * @@ -541,6 +541,22 @@ int32_t dash_spv_ffi_config_set_start_from_height(FFIClientConfig *config, int32_t dash_spv_ffi_config_set_wallet_creation_time(FFIClientConfig *config, uint32_t timestamp); +int32_t dash_spv_ffi_checkpoint_latest(FFINetwork network, uint32_t *out_height, uint8_t *out_hash); + +int32_t dash_spv_ffi_checkpoint_before_height(FFINetwork network, + uint32_t height, + uint32_t *out_height, + uint8_t *out_hash); + +int32_t dash_spv_ffi_checkpoint_before_timestamp(FFINetwork network, + uint32_t timestamp, + uint32_t *out_height, + uint8_t *out_hash); + +struct FFIArray dash_spv_ffi_checkpoints_between_heights(FFINetwork network, + uint32_t start_height, + uint32_t end_height); + const char *dash_spv_ffi_get_last_error(void); void dash_spv_ffi_clear_error(void); diff --git a/dash-spv-ffi/src/checkpoints.rs b/dash-spv-ffi/src/checkpoints.rs new file mode 100644 index 000000000..8c949a76a --- /dev/null +++ b/dash-spv-ffi/src/checkpoints.rs @@ -0,0 +1,168 @@ +use crate::{set_last_error, FFIArray, FFIErrorCode}; +use dash_spv::chain::checkpoints::{mainnet_checkpoints, testnet_checkpoints, CheckpointManager}; +use dashcore::Network; +use dashcore::hashes::Hash; +use key_wallet_ffi::FFINetwork; + +/// FFI representation of a checkpoint (height + block hash) +#[repr(C)] +pub struct FFICheckpoint { + pub height: u32, + pub block_hash: [u8; 32], +} + +fn manager_for_network(network: FFINetwork) -> Result { + let net: Network = network.into(); + match net { + Network::Dash => Ok(CheckpointManager::new(mainnet_checkpoints())), + Network::Testnet => Ok(CheckpointManager::new(testnet_checkpoints())), + _ => Err("Checkpoints are only available for Dash and Testnet".to_string()), + } +} + +/// Get the latest checkpoint for the given network. +/// +/// # Safety +/// - `out_height` must be a valid pointer to a `u32`. +/// - `out_hash` must point to at least 32 writable bytes. +#[no_mangle] +pub extern "C" fn dash_spv_ffi_checkpoint_latest( + network: FFINetwork, + out_height: *mut u32, + out_hash: *mut u8, // expects at least 32 bytes +) -> i32 { + if out_height.is_null() || out_hash.is_null() { + set_last_error("Null output pointer provided"); + return FFIErrorCode::NullPointer as i32; + } + let mgr = match manager_for_network(network) { + Ok(m) => m, + Err(e) => { + set_last_error(&e); + return FFIErrorCode::InvalidArgument as i32; + } + }; + if let Some(cp) = mgr.last_checkpoint() { + unsafe { + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); + } + FFIErrorCode::Success as i32 + } else { + set_last_error("No checkpoints available for network"); + FFIErrorCode::NotImplemented as i32 + } +} + +/// Get the last checkpoint at or before a given height. +/// +/// # Safety +/// - `out_height` must be a valid pointer to a `u32`. +/// - `out_hash` must point to at least 32 writable bytes. +#[no_mangle] +pub extern "C" fn dash_spv_ffi_checkpoint_before_height( + network: FFINetwork, + height: u32, + out_height: *mut u32, + out_hash: *mut u8, // expects at least 32 bytes +) -> i32 { + if out_height.is_null() || out_hash.is_null() { + set_last_error("Null output pointer provided"); + return FFIErrorCode::NullPointer as i32; + } + let mgr = match manager_for_network(network) { + Ok(m) => m, + Err(e) => { + set_last_error(&e); + return FFIErrorCode::InvalidArgument as i32; + } + }; + if let Some(cp) = mgr.last_checkpoint_before_height(height) { + unsafe { + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); + } + FFIErrorCode::Success as i32 + } else { + set_last_error("No checkpoint at or before given height"); + FFIErrorCode::ValidationError as i32 + } +} + +/// Get the last checkpoint at or before a given UNIX timestamp (seconds). +/// +/// # Safety +/// - `out_height` must be a valid pointer to a `u32`. +/// - `out_hash` must point to at least 32 writable bytes. +#[no_mangle] +pub extern "C" fn dash_spv_ffi_checkpoint_before_timestamp( + network: FFINetwork, + timestamp: u32, + out_height: *mut u32, + out_hash: *mut u8, // expects at least 32 bytes +) -> i32 { + if out_height.is_null() || out_hash.is_null() { + set_last_error("Null output pointer provided"); + return FFIErrorCode::NullPointer as i32; + } + let mgr = match manager_for_network(network) { + Ok(m) => m, + Err(e) => { + set_last_error(&e); + return FFIErrorCode::InvalidArgument as i32; + } + }; + if let Some(cp) = mgr.last_checkpoint_before_timestamp(timestamp) { + unsafe { + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); + } + FFIErrorCode::Success as i32 + } else { + set_last_error("No checkpoint at or before given timestamp"); + FFIErrorCode::ValidationError as i32 + } +} + +/// Get all checkpoints between two heights (inclusive). +/// +/// Returns an `FFIArray` of `FFICheckpoint` items. The caller owns the memory and +/// must free the array buffer using `dash_spv_ffi_array_destroy` when done. +#[no_mangle] +pub extern "C" fn dash_spv_ffi_checkpoints_between_heights( + network: FFINetwork, + start_height: u32, + end_height: u32, +) -> FFIArray { + match manager_for_network(network) { + Ok(mgr) => { + // Collect checkpoints within inclusive range + let mut out: Vec = Vec::new(); + for &h in mgr.checkpoint_heights() { + if h >= start_height && h <= end_height { + if let Some(cp) = mgr.get_checkpoint(h) { + out.push(FFICheckpoint { + height: cp.height, + block_hash: cp.block_hash.to_byte_array(), + }); + } + } + } + FFIArray::new(out) + } + Err(e) => { + set_last_error(&e); + // Return empty array on error + FFIArray { + data: std::ptr::null_mut(), + len: 0, + capacity: 0, + elem_size: std::mem::size_of::(), + elem_align: std::mem::align_of::(), + } + } + } +} diff --git a/dash-spv-ffi/src/lib.rs b/dash-spv-ffi/src/lib.rs index 59733372e..f6beb755d 100644 --- a/dash-spv-ffi/src/lib.rs +++ b/dash-spv-ffi/src/lib.rs @@ -1,6 +1,7 @@ pub mod callbacks; pub mod client; pub mod config; +pub mod checkpoints; pub mod error; pub mod platform_integration; pub mod types; @@ -9,6 +10,7 @@ pub mod utils; pub use callbacks::*; pub use client::*; pub use config::*; +pub use checkpoints::*; pub use error::*; pub use platform_integration::*; pub use types::*;