diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 0f890e08b..24f8cc164 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -4,15 +4,13 @@ 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**: 64 +**Total Functions**: 39 ## Table of Contents - [Client Management](#client-management) - [Configuration](#configuration) - [Synchronization](#synchronization) -- [Address Monitoring](#address-monitoring) -- [Transaction Management](#transaction-management) - [Platform Integration](#platform-integration) - [Event Callbacks](#event-callbacks) - [Error Handling](#error-handling) @@ -33,77 +31,46 @@ Functions: 4 ### Configuration -Functions: 25 +Functions: 16 | Function | Description | Module | |----------|-------------|--------| | `dash_spv_ffi_client_update_config` | Update the running client's configuration | client | | `dash_spv_ffi_config_add_peer` | Adds a peer address to the configuration Accepts socket addresses with or... | config | | `dash_spv_ffi_config_destroy` | Destroys an FFIClientConfig and frees its memory # Safety - `config` must... | config | -| `dash_spv_ffi_config_get_data_dir` | Gets the data directory path from the configuration # Safety - `config`... | config | -| `dash_spv_ffi_config_get_mempool_strategy` | Gets the mempool synchronization strategy # Safety - `config` must be a... | config | -| `dash_spv_ffi_config_get_mempool_tracking` | Gets whether mempool tracking is enabled # Safety - `config` must be a... | config | | `dash_spv_ffi_config_get_network` | Gets the network type from the configuration # Safety - `config` must be a... | config | | `dash_spv_ffi_config_mainnet` | No description | config | | `dash_spv_ffi_config_new` | No description | config | | `dash_spv_ffi_config_set_data_dir` | Sets the data directory for storing blockchain data # Safety - `config`... | config | | `dash_spv_ffi_config_set_fetch_mempool_transactions` | Sets whether to fetch full mempool transaction data # Safety - `config`... | config | -| `dash_spv_ffi_config_set_filter_load` | Sets whether to load bloom filters # Safety - `config` must be a valid... | config | | `dash_spv_ffi_config_set_masternode_sync_enabled` | Enables or disables masternode synchronization # Safety - `config` must be... | config | -| `dash_spv_ffi_config_set_max_mempool_transactions` | Sets the maximum number of mempool transactions to track # Safety -... | config | -| `dash_spv_ffi_config_set_max_peers` | Sets the maximum number of peers to connect to # Safety - `config` must be... | config | | `dash_spv_ffi_config_set_mempool_strategy` | Sets the mempool synchronization strategy # Safety - `config` must be a... | config | | `dash_spv_ffi_config_set_mempool_tracking` | Enables or disables mempool tracking # Safety - `config` must be a valid... | config | | `dash_spv_ffi_config_set_persist_mempool` | Sets whether to persist mempool state to disk # Safety - `config` must be a... | config | -| `dash_spv_ffi_config_set_relay_transactions` | Sets whether to relay transactions (currently a no-op) # Safety - `config`... | config | | `dash_spv_ffi_config_set_restrict_to_configured_peers` | Restrict connections strictly to configured peers (disable DNS discovery and... | config | | `dash_spv_ffi_config_set_start_from_height` | Sets the starting block height for synchronization # Safety - `config` must... | config | | `dash_spv_ffi_config_set_user_agent` | Sets the user agent string to advertise in the P2P handshake # Safety -... | config | -| `dash_spv_ffi_config_set_validation_mode` | Sets the validation mode for the SPV client # Safety - `config` must be a... | config | -| `dash_spv_ffi_config_set_worker_threads` | Sets the number of Tokio worker threads for the FFI runtime (0 = auto) #... | config | | `dash_spv_ffi_config_testnet` | No description | config | ### Synchronization -Functions: 6 +Functions: 4 | Function | Description | Module | |----------|-------------|--------| | `dash_spv_ffi_client_cancel_sync` | Cancels the sync operation | client | | `dash_spv_ffi_client_get_sync_progress` | Get the current sync progress snapshot | client | -| `dash_spv_ffi_client_is_filter_sync_available` | Check if compact filter sync is currently available | client | | `dash_spv_ffi_client_sync_to_tip_with_progress` | Sync the SPV client to the chain tip with detailed progress updates | client | -| `dash_spv_ffi_client_test_sync` | Performs a test synchronization of the SPV client # Parameters - `client`:... | client | | `dash_spv_ffi_sync_progress_destroy` | Destroy a `FFISyncProgress` object returned by this crate | client | -### Address Monitoring - -Functions: 1 - -| Function | Description | Module | -|----------|-------------|--------| -| `dash_spv_ffi_unconfirmed_transaction_destroy_addresses` | Destroys the addresses array allocated for an FFIUnconfirmedTransaction #... | types | - -### Transaction Management - -Functions: 3 - -| Function | Description | Module | -|----------|-------------|--------| -| `dash_spv_ffi_client_broadcast_transaction` | Broadcasts a transaction to the Dash network via connected peers | broadcast | -| `dash_spv_ffi_unconfirmed_transaction_destroy` | Destroys an FFIUnconfirmedTransaction and all its associated resources #... | types | -| `dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx` | Destroys the raw transaction bytes allocated for an FFIUnconfirmedTransaction... | types | - ### Platform Integration -Functions: 4 +Functions: 2 | Function | Description | Module | |----------|-------------|--------| -| `ffi_dash_spv_get_core_handle` | Creates a CoreSDKHandle from an FFIDashSpvClient # Safety This function is... | platform_integration | | `ffi_dash_spv_get_platform_activation_height` | Gets the platform activation height from the Core chain # Safety This... | platform_integration | | `ffi_dash_spv_get_quorum_public_key` | Gets a quorum public key from the Core chain # Safety This function is... | platform_integration | -| `ffi_dash_spv_release_core_handle` | Releases a CoreSDKHandle # Safety This function is unsafe because: - The... | platform_integration | ### Event Callbacks @@ -116,34 +83,26 @@ Functions: 2 ### Error Handling -Functions: 2 +Functions: 1 | Function | Description | Module | |----------|-------------|--------| -| `dash_spv_ffi_clear_error` | No description | error | | `dash_spv_ffi_get_last_error` | No description | error | ### Utility Functions -Functions: 17 +Functions: 10 | 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_clear_storage` | Clear all persisted SPV storage (headers, filters, metadata, sync state) | client | | `dash_spv_ffi_client_get_tip_hash` | Get the current chain tip hash (32 bytes) if available | client | | `dash_spv_ffi_client_get_tip_height` | Get the current chain tip height (absolute) | client | | `dash_spv_ffi_client_get_wallet_manager` | Get the wallet manager from the SPV client Returns a pointer to an... | client | -| `dash_spv_ffi_client_record_send` | Record that we attempted to send a transaction by its txid | client | -| `dash_spv_ffi_client_rescan_blockchain` | Request a rescan of the blockchain from a given height (not yet implemented) | client | -| `dash_spv_ffi_enable_test_mode` | No description | utils | | `dash_spv_ffi_init_logging` | Initialize logging for the SPV library | utils | -| `dash_spv_ffi_string_array_destroy` | Destroy an array of FFIString pointers (Vec<*mut FFIString>) and their contents | types | -| `dash_spv_ffi_string_destroy` | No description | types | | `dash_spv_ffi_version` | No description | utils | | `dash_spv_ffi_wallet_manager_free` | Release a wallet manager obtained from `dash_spv_ffi_client_get_wallet_manager` | client | @@ -265,54 +224,6 @@ Destroys an FFIClientConfig and frees its memory # Safety - `config` must be a --- -#### `dash_spv_ffi_config_get_data_dir` - -```c -dash_spv_ffi_config_get_data_dir(config: *const FFIClientConfig,) -> FFIString -``` - -**Description:** -Gets the data directory path from the configuration # Safety - `config` must be a valid pointer to an FFIClientConfig or null - If null or no data directory is set, returns an FFIString with null pointer - The returned FFIString must be freed by the caller using `dash_spv_ffi_string_destroy` - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig or null - If null or no data directory is set, returns an FFIString with null pointer - The returned FFIString must be freed by the caller using `dash_spv_ffi_string_destroy` - -**Module:** `config` - ---- - -#### `dash_spv_ffi_config_get_mempool_strategy` - -```c -dash_spv_ffi_config_get_mempool_strategy(config: *const FFIClientConfig,) -> FFIMempoolStrategy -``` - -**Description:** -Gets the mempool synchronization strategy # Safety - `config` must be a valid pointer to an FFIClientConfig or null - If null, returns FFIMempoolStrategy::FetchAll as default - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig or null - If null, returns FFIMempoolStrategy::FetchAll as default - -**Module:** `config` - ---- - -#### `dash_spv_ffi_config_get_mempool_tracking` - -```c -dash_spv_ffi_config_get_mempool_tracking(config: *const FFIClientConfig,) -> bool -``` - -**Description:** -Gets whether mempool tracking is enabled # Safety - `config` must be a valid pointer to an FFIClientConfig or null - If null, returns false as default - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig or null - If null, returns false as default - -**Module:** `config` - ---- - #### `dash_spv_ffi_config_get_network` ```c @@ -381,22 +292,6 @@ Sets whether to fetch full mempool transaction data # Safety - `config` must be --- -#### `dash_spv_ffi_config_set_filter_load` - -```c -dash_spv_ffi_config_set_filter_load(config: *mut FFIClientConfig, load_filters: bool,) -> i32 -``` - -**Description:** -Sets whether to load bloom filters # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Module:** `config` - ---- - #### `dash_spv_ffi_config_set_masternode_sync_enabled` ```c @@ -413,38 +308,6 @@ Enables or disables masternode synchronization # Safety - `config` must be a va --- -#### `dash_spv_ffi_config_set_max_mempool_transactions` - -```c -dash_spv_ffi_config_set_max_mempool_transactions(config: *mut FFIClientConfig, max_transactions: u32,) -> i32 -``` - -**Description:** -Sets the maximum number of mempool transactions to track # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Module:** `config` - ---- - -#### `dash_spv_ffi_config_set_max_peers` - -```c -dash_spv_ffi_config_set_max_peers(config: *mut FFIClientConfig, max_peers: u32,) -> i32 -``` - -**Description:** -Sets the maximum number of peers to connect to # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Module:** `config` - ---- - #### `dash_spv_ffi_config_set_mempool_strategy` ```c @@ -493,22 +356,6 @@ Sets whether to persist mempool state to disk # Safety - `config` must be a val --- -#### `dash_spv_ffi_config_set_relay_transactions` - -```c -dash_spv_ffi_config_set_relay_transactions(config: *mut FFIClientConfig, _relay: bool,) -> i32 -``` - -**Description:** -Sets whether to relay transactions (currently a no-op) # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Module:** `config` - ---- - #### `dash_spv_ffi_config_set_restrict_to_configured_peers` ```c @@ -557,38 +404,6 @@ Sets the user agent string to advertise in the P2P handshake # Safety - `config --- -#### `dash_spv_ffi_config_set_validation_mode` - -```c -dash_spv_ffi_config_set_validation_mode(config: *mut FFIClientConfig, mode: FFIValidationMode,) -> i32 -``` - -**Description:** -Sets the validation mode for the SPV client # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call - -**Module:** `config` - ---- - -#### `dash_spv_ffi_config_set_worker_threads` - -```c -dash_spv_ffi_config_set_worker_threads(config: *mut FFIClientConfig, threads: u32,) -> i32 -``` - -**Description:** -Sets the number of Tokio worker threads for the FFI runtime (0 = auto) # Safety - `config` must be a valid pointer to an FFIClientConfig - -**Safety:** -- `config` must be a valid pointer to an FFIClientConfig - -**Module:** `config` - ---- - #### `dash_spv_ffi_config_testnet` ```c @@ -633,22 +448,6 @@ Get the current sync progress snapshot. # Safety - `client` must be a valid, no --- -#### `dash_spv_ffi_client_is_filter_sync_available` - -```c -dash_spv_ffi_client_is_filter_sync_available(client: *mut FFIDashSpvClient,) -> bool -``` - -**Description:** -Check if compact filter sync is currently available. # Safety - `client` must be a valid, non-null pointer. - -**Safety:** -- `client` must be a valid, non-null pointer. - -**Module:** `client` - ---- - #### `dash_spv_ffi_client_sync_to_tip_with_progress` ```c @@ -665,22 +464,6 @@ This function is unsafe because: - `client` must be a valid pointer to an initia --- -#### `dash_spv_ffi_client_test_sync` - -```c -dash_spv_ffi_client_test_sync(client: *mut FFIDashSpvClient) -> i32 -``` - -**Description:** -Performs a test synchronization of the SPV client # Parameters - `client`: Pointer to an FFIDashSpvClient instance # Returns - `0` on success - Negative error code on failure # Safety This function is unsafe because it dereferences a raw pointer. The caller must ensure that the client pointer is valid. - -**Safety:** -This function is unsafe because it dereferences a raw pointer. The caller must ensure that the client pointer is valid. - -**Module:** `client` - ---- - #### `dash_spv_ffi_sync_progress_destroy` ```c @@ -697,92 +480,8 @@ Destroy a `FFISyncProgress` object returned by this crate. # Safety - `progress --- -### Address Monitoring - Detailed - -#### `dash_spv_ffi_unconfirmed_transaction_destroy_addresses` - -```c -dash_spv_ffi_unconfirmed_transaction_destroy_addresses(addresses: *mut FFIString, addresses_len: usize,) -> () -``` - -**Description:** -Destroys the addresses array allocated for an FFIUnconfirmedTransaction # Safety - `addresses` must be a valid pointer to an array of FFIString objects - `addresses_len` must be the correct length of the array - Each FFIString in the array must be destroyed separately using `dash_spv_ffi_string_destroy` - The pointer must not be used after this function is called - This function should only be called once per allocation - -**Safety:** -- `addresses` must be a valid pointer to an array of FFIString objects - `addresses_len` must be the correct length of the array - Each FFIString in the array must be destroyed separately using `dash_spv_ffi_string_destroy` - The pointer must not be used after this function is called - This function should only be called once per allocation - -**Module:** `types` - ---- - -### Transaction Management - Detailed - -#### `dash_spv_ffi_client_broadcast_transaction` - -```c -dash_spv_ffi_client_broadcast_transaction(client: *mut FFIDashSpvClient, tx_hex: *const c_char,) -> i32 -``` - -**Description:** -Broadcasts a transaction to the Dash network via connected peers. # Safety - `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient - `tx_hex` must be a valid, non-null pointer to a NUL-terminated C string containing a hex-encoded serialized transaction - -**Safety:** -- `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient - `tx_hex` must be a valid, non-null pointer to a NUL-terminated C string containing a hex-encoded serialized transaction - -**Module:** `broadcast` - ---- - -#### `dash_spv_ffi_unconfirmed_transaction_destroy` - -```c -dash_spv_ffi_unconfirmed_transaction_destroy(tx: *mut FFIUnconfirmedTransaction,) -> () -``` - -**Description:** -Destroys an FFIUnconfirmedTransaction and all its associated resources # Safety - `tx` must be a valid pointer to an FFIUnconfirmedTransaction - All resources (raw_tx, addresses array, and individual FFIStrings) will be freed - The pointer must not be used after this function is called - This function should only be called once per FFIUnconfirmedTransaction - -**Safety:** -- `tx` must be a valid pointer to an FFIUnconfirmedTransaction - All resources (raw_tx, addresses array, and individual FFIStrings) will be freed - The pointer must not be used after this function is called - This function should only be called once per FFIUnconfirmedTransaction - -**Module:** `types` - ---- - -#### `dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx` - -```c -dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx(raw_tx: *mut u8, raw_tx_len: usize,) -> () -``` - -**Description:** -Destroys the raw transaction bytes allocated for an FFIUnconfirmedTransaction # Safety - `raw_tx` must be a valid pointer to memory allocated by the caller - `raw_tx_len` must be the correct length of the allocated memory - The pointer must not be used after this function is called - This function should only be called once per allocation - -**Safety:** -- `raw_tx` must be a valid pointer to memory allocated by the caller - `raw_tx_len` must be the correct length of the allocated memory - The pointer must not be used after this function is called - This function should only be called once per allocation - -**Module:** `types` - ---- - ### Platform Integration - Detailed -#### `ffi_dash_spv_get_core_handle` - -```c -ffi_dash_spv_get_core_handle(client: *mut FFIDashSpvClient,) -> *mut CoreSDKHandle -``` - -**Description:** -Creates a CoreSDKHandle from an FFIDashSpvClient # Safety This function is unsafe because: - The caller must ensure the client pointer is valid - The returned handle must be properly released with ffi_dash_spv_release_core_handle - -**Safety:** -This function is unsafe because: - The caller must ensure the client pointer is valid - The returned handle must be properly released with ffi_dash_spv_release_core_handle - -**Module:** `platform_integration` - ---- - #### `ffi_dash_spv_get_platform_activation_height` ```c @@ -815,22 +514,6 @@ This function is unsafe because: - The caller must ensure all pointers are valid --- -#### `ffi_dash_spv_release_core_handle` - -```c -ffi_dash_spv_release_core_handle(handle: *mut CoreSDKHandle) -> () -``` - -**Description:** -Releases a CoreSDKHandle # Safety This function is unsafe because: - The caller must ensure the handle pointer is valid - The handle must not be used after this call - -**Safety:** -This function is unsafe because: - The caller must ensure the handle pointer is valid - The handle must not be used after this call - -**Module:** `platform_integration` - ---- - ### Event Callbacks - Detailed #### `dash_spv_ffi_client_drain_events` @@ -867,16 +550,6 @@ Set event callbacks for the client. # Safety - `client` must be a valid, non-nu ### Error Handling - Detailed -#### `dash_spv_ffi_clear_error` - -```c -dash_spv_ffi_clear_error() -> () -``` - -**Module:** `error` - ---- - #### `dash_spv_ffi_get_last_error` ```c @@ -889,16 +562,6 @@ dash_spv_ffi_get_last_error() -> *const c_char ### Utility Functions - Detailed -#### `dash_spv_ffi_array_destroy` - -```c -dash_spv_ffi_array_destroy(arr: *mut FFIArray) -> () -``` - -**Module:** `types` - ---- - #### `dash_spv_ffi_checkpoint_before_height` ```c @@ -947,19 +610,6 @@ Get the latest checkpoint for the given network. # Safety - `out_height` must b --- -#### `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_clear_storage` ```c @@ -1024,48 +674,6 @@ The caller must ensure that: - The client pointer is valid - The returned pointe --- -#### `dash_spv_ffi_client_record_send` - -```c -dash_spv_ffi_client_record_send(client: *mut FFIDashSpvClient, txid: *const c_char,) -> i32 -``` - -**Description:** -Record that we attempted to send a transaction by its txid. # Safety - `client` and `txid` must be valid, non-null pointers. - -**Safety:** -- `client` and `txid` must be valid, non-null pointers. - -**Module:** `client` - ---- - -#### `dash_spv_ffi_client_rescan_blockchain` - -```c -dash_spv_ffi_client_rescan_blockchain(client: *mut FFIDashSpvClient, _from_height: u32,) -> i32 -``` - -**Description:** -Request a rescan of the blockchain from a given height (not yet implemented). # Safety - `client` must be a valid, non-null pointer. - -**Safety:** -- `client` must be a valid, non-null pointer. - -**Module:** `client` - ---- - -#### `dash_spv_ffi_enable_test_mode` - -```c -dash_spv_ffi_enable_test_mode() -> () -``` - -**Module:** `utils` - ---- - #### `dash_spv_ffi_init_logging` ```c @@ -1082,29 +690,6 @@ Initialize logging for the SPV library. # Arguments - `level`: Log level string --- -#### `dash_spv_ffi_string_array_destroy` - -```c -dash_spv_ffi_string_array_destroy(arr: *mut FFIArray) -> () -``` - -**Description:** -Destroy an array of FFIString pointers (Vec<*mut FFIString>) and their contents. This function: - Iterates the array elements as pointers to FFIString and destroys each via dash_spv_ffi_string_destroy - Frees the underlying vector buffer stored in FFIArray - Does not free the FFIArray struct itself (safe for both stack- and heap-allocated structs) - -**Module:** `types` - ---- - -#### `dash_spv_ffi_string_destroy` - -```c -dash_spv_ffi_string_destroy(s: FFIString) -> () -``` - -**Module:** `types` - ---- - #### `dash_spv_ffi_version` ```c diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index 0edad000f..4d7cc65ef 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -29,12 +29,6 @@ typedef enum FFISyncStage { Failed = 9, } FFISyncStage; -typedef enum DashSpvValidationMode { - None = 0, - Basic = 1, - Full = 2, -} DashSpvValidationMode; - typedef enum FFIMempoolStrategy { FetchAll = 0, BloomFilter = 1, @@ -42,25 +36,6 @@ typedef enum FFIMempoolStrategy { typedef struct FFIDashSpvClient FFIDashSpvClient; -/** - * FFI-safe array that transfers ownership of memory to the C caller. - * - * # Safety - * - * This struct represents memory that has been allocated by Rust but ownership - * has been transferred to the C caller. The caller is responsible for: - * - Not accessing the memory after it has been freed - * - Calling `dash_spv_ffi_array_destroy` to properly deallocate the memory - * - Ensuring the data, len, and capacity fields remain consistent - */ -typedef struct FFIArray { - void *data; - uintptr_t len; - uintptr_t capacity; - uintptr_t elem_size; - uintptr_t elem_align; -} FFIArray; - typedef struct FFIClientConfig { void *inner; uint32_t worker_threads; @@ -156,13 +131,6 @@ typedef struct FFIWalletManager { uint8_t _private[0]; } FFIWalletManager; -/** - * Handle for Core SDK that can be passed to Platform SDK - */ -typedef struct CoreSDKHandle { - struct FFIDashSpvClient *client; -} CoreSDKHandle; - /** * FFIResult type for error handling */ @@ -171,39 +139,6 @@ typedef struct FFIResult { const char *error_message; } FFIResult; -/** - * FFI-safe representation of an unconfirmed transaction - * - * # Safety - * - * This struct contains raw pointers that must be properly managed: - * - * - `raw_tx`: A pointer to the raw transaction bytes. The caller is responsible for: - * - Allocating this memory before passing it to Rust - * - Ensuring the pointer remains valid for the lifetime of this struct - * - Freeing the memory after use with `dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx` - * - * - `addresses`: A pointer to an array of FFIString objects. The caller is responsible for: - * - Allocating this array before passing it to Rust - * - Ensuring the pointer remains valid for the lifetime of this struct - * - Freeing each FFIString in the array with `dash_spv_ffi_string_destroy` - * - Freeing the array itself after use with `dash_spv_ffi_unconfirmed_transaction_destroy_addresses` - * - * Use `dash_spv_ffi_unconfirmed_transaction_destroy` to safely clean up all resources - * associated with this struct. - */ -typedef struct FFIUnconfirmedTransaction { - struct FFIString txid; - uint8_t *raw_tx; - uintptr_t raw_tx_len; - int64_t amount; - uint64_t fee; - bool is_instant_send; - bool is_outgoing; - struct FFIString *addresses; - uintptr_t addresses_len; -} FFIUnconfirmedTransaction; - #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -249,18 +184,6 @@ int32_t dash_spv_ffi_checkpoint_before_timestamp(FFINetwork network, uint8_t *out_hash) ; -/** - * 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. - */ - -struct FFIArray dash_spv_ffi_checkpoints_between_heights(FFINetwork network, - uint32_t start_height, - uint32_t end_height) -; - /** * Create a new SPV client and return an opaque pointer. * @@ -307,22 +230,6 @@ int32_t dash_spv_ffi_client_update_config(struct FFIDashSpvClient *client, */ int32_t dash_spv_ffi_client_stop(struct FFIDashSpvClient *client) ; -/** - * Performs a test synchronization of the SPV client - * - * # Parameters - * - `client`: Pointer to an FFIDashSpvClient instance - * - * # Returns - * - `0` on success - * - Negative error code on failure - * - * # Safety - * This function is unsafe because it dereferences a raw pointer. - * The caller must ensure that the client pointer is valid. - */ - int32_t dash_spv_ffi_client_test_sync(struct FFIDashSpvClient *client) ; - /** * Sync the SPV client to the chain tip with detailed progress updates. * @@ -405,14 +312,6 @@ int32_t dash_spv_ffi_client_sync_to_tip_with_progress(struct FFIDashSpvClient *c */ int32_t dash_spv_ffi_client_clear_storage(struct FFIDashSpvClient *client) ; -/** - * Check if compact filter sync is currently available. - * - * # Safety - * - `client` must be a valid, non-null pointer. - */ - bool dash_spv_ffi_client_is_filter_sync_available(struct FFIDashSpvClient *client) ; - /** * Set event callbacks for the client. * @@ -440,25 +339,6 @@ int32_t dash_spv_ffi_client_set_event_callbacks(struct FFIDashSpvClient *client, */ void dash_spv_ffi_sync_progress_destroy(struct FFISyncProgress *progress) ; -/** - * Request a rescan of the blockchain from a given height (not yet implemented). - * - * # Safety - * - `client` must be a valid, non-null pointer. - */ - -int32_t dash_spv_ffi_client_rescan_blockchain(struct FFIDashSpvClient *client, - uint32_t _from_height) -; - -/** - * Record that we attempted to send a transaction by its txid. - * - * # Safety - * - `client` and `txid` must be valid, non-null pointers. - */ - int32_t dash_spv_ffi_client_record_send(struct FFIDashSpvClient *client, const char *txid) ; - /** * Get the wallet manager from the SPV client * @@ -511,30 +391,6 @@ int32_t dash_spv_ffi_config_set_data_dir(struct FFIClientConfig *config, const char *path) ; -/** - * Sets the validation mode for the SPV client - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - * - The caller must ensure the config pointer remains valid for the duration of this call - */ - -int32_t dash_spv_ffi_config_set_validation_mode(struct FFIClientConfig *config, - enum DashSpvValidationMode mode) -; - -/** - * Sets the maximum number of peers to connect to - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - * - The caller must ensure the config pointer remains valid for the duration of this call - */ - -int32_t dash_spv_ffi_config_set_max_peers(struct FFIClientConfig *config, - uint32_t max_peers) -; - /** * Adds a peer address to the configuration * @@ -570,30 +426,6 @@ int32_t dash_spv_ffi_config_set_user_agent(struct FFIClientConfig *config, const char *user_agent) ; -/** - * Sets whether to relay transactions (currently a no-op) - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - * - The caller must ensure the config pointer remains valid for the duration of this call - */ - -int32_t dash_spv_ffi_config_set_relay_transactions(struct FFIClientConfig *config, - bool _relay) -; - -/** - * Sets whether to load bloom filters - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - * - The caller must ensure the config pointer remains valid for the duration of this call - */ - -int32_t dash_spv_ffi_config_set_filter_load(struct FFIClientConfig *config, - bool load_filters) -; - /** * Restrict connections strictly to configured peers (disable DNS discovery and peer store) * @@ -626,16 +458,6 @@ int32_t dash_spv_ffi_config_set_masternode_sync_enabled(struct FFIClientConfig * */ FFINetwork dash_spv_ffi_config_get_network(const struct FFIClientConfig *config) ; -/** - * Gets the data directory path from the configuration - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig or null - * - If null or no data directory is set, returns an FFIString with null pointer - * - The returned FFIString must be freed by the caller using `dash_spv_ffi_string_destroy` - */ - struct FFIString dash_spv_ffi_config_get_data_dir(const struct FFIClientConfig *config) ; - /** * Destroys an FFIClientConfig and frees its memory * @@ -648,14 +470,6 @@ int32_t dash_spv_ffi_config_set_masternode_sync_enabled(struct FFIClientConfig * void dash_spv_ffi_config_destroy(struct FFIClientConfig *config) ; -/** - * Sets the number of Tokio worker threads for the FFI runtime (0 = auto) - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig - */ - int32_t dash_spv_ffi_config_set_worker_threads(struct FFIClientConfig *config, uint32_t threads) ; - /** * Enables or disables mempool tracking * @@ -680,18 +494,6 @@ int32_t dash_spv_ffi_config_set_mempool_strategy(struct FFIClientConfig *config, enum FFIMempoolStrategy strategy) ; -/** - * Sets the maximum number of mempool transactions to track - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - * - The caller must ensure the config pointer remains valid for the duration of this call - */ - -int32_t dash_spv_ffi_config_set_max_mempool_transactions(struct FFIClientConfig *config, - uint32_t max_transactions) -; - /** * Sets whether to fetch full mempool transaction data * @@ -716,26 +518,6 @@ int32_t dash_spv_ffi_config_set_persist_mempool(struct FFIClientConfig *config, bool persist) ; -/** - * Gets whether mempool tracking is enabled - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig or null - * - If null, returns false as default - */ - bool dash_spv_ffi_config_get_mempool_tracking(const struct FFIClientConfig *config) ; - -/** - * Gets the mempool synchronization strategy - * - * # Safety - * - `config` must be a valid pointer to an FFIClientConfig or null - * - If null, returns FFIMempoolStrategy::FetchAll as default - */ - -enum FFIMempoolStrategy dash_spv_ffi_config_get_mempool_strategy(const struct FFIClientConfig *config) -; - /** * Sets the starting block height for synchronization * @@ -750,30 +532,6 @@ int32_t dash_spv_ffi_config_set_start_from_height(struct FFIClientConfig *config const char *dash_spv_ffi_get_last_error(void) ; - void dash_spv_ffi_clear_error(void) ; - -/** - * Creates a CoreSDKHandle from an FFIDashSpvClient - * - * # Safety - * - * This function is unsafe because: - * - The caller must ensure the client pointer is valid - * - The returned handle must be properly released with ffi_dash_spv_release_core_handle - */ - struct CoreSDKHandle *ffi_dash_spv_get_core_handle(struct FFIDashSpvClient *client) ; - -/** - * Releases a CoreSDKHandle - * - * # Safety - * - * This function is unsafe because: - * - The caller must ensure the handle pointer is valid - * - The handle must not be used after this call - */ - void ffi_dash_spv_release_core_handle(struct CoreSDKHandle *handle) ; - /** * Gets a quorum public key from the Core chain * @@ -808,76 +566,6 @@ struct FFIResult ffi_dash_spv_get_platform_activation_height(struct FFIDashSpvCl uint32_t *out_height) ; -/** - * # Safety - * - `s.ptr` must be a pointer previously returned by `FFIString::new` or compatible. - * - It must not be used after this call. - */ - void dash_spv_ffi_string_destroy(struct FFIString s) ; - -/** - * # Safety - * - `arr` must be either null or a valid pointer to an `FFIArray` previously constructed in Rust. - * - The memory referenced by `arr.data` must not be used after this call. - */ - void dash_spv_ffi_array_destroy(struct FFIArray *arr) ; - -/** - * Destroy an array of FFIString pointers (Vec<*mut FFIString>) and their contents. - * - * This function: - * - Iterates the array elements as pointers to FFIString and destroys each via dash_spv_ffi_string_destroy - * - Frees the underlying vector buffer stored in FFIArray - * - Does not free the FFIArray struct itself (safe for both stack- and heap-allocated structs) - * # Safety - * - `arr` must be either null or a valid pointer to an `FFIArray` whose elements are `*mut FFIString`. - * - Each element pointer must be valid or null; non-null entries are freed. - * - The memory referenced by `arr.data` must not be used after this call. - */ - -void dash_spv_ffi_string_array_destroy(struct FFIArray *arr) -; - -/** - * Destroys the raw transaction bytes allocated for an FFIUnconfirmedTransaction - * - * # Safety - * - * - `raw_tx` must be a valid pointer to memory allocated by the caller - * - `raw_tx_len` must be the correct length of the allocated memory - * - The pointer must not be used after this function is called - * - This function should only be called once per allocation - */ - void dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx(uint8_t *raw_tx, uintptr_t raw_tx_len) ; - -/** - * Destroys the addresses array allocated for an FFIUnconfirmedTransaction - * - * # Safety - * - * - `addresses` must be a valid pointer to an array of FFIString objects - * - `addresses_len` must be the correct length of the array - * - Each FFIString in the array must be destroyed separately using `dash_spv_ffi_string_destroy` - * - The pointer must not be used after this function is called - * - This function should only be called once per allocation - */ - -void dash_spv_ffi_unconfirmed_transaction_destroy_addresses(struct FFIString *addresses, - uintptr_t addresses_len) -; - -/** - * Destroys an FFIUnconfirmedTransaction and all its associated resources - * - * # Safety - * - * - `tx` must be a valid pointer to an FFIUnconfirmedTransaction - * - All resources (raw_tx, addresses array, and individual FFIStrings) will be freed - * - The pointer must not be used after this function is called - * - This function should only be called once per FFIUnconfirmedTransaction - */ - void dash_spv_ffi_unconfirmed_transaction_destroy(struct FFIUnconfirmedTransaction *tx) ; - /** * Initialize logging for the SPV library. * @@ -900,22 +588,6 @@ int32_t dash_spv_ffi_init_logging(const char *level, const char *dash_spv_ffi_version(void) ; - void dash_spv_ffi_enable_test_mode(void) ; - -/** - * Broadcasts a transaction to the Dash network via connected peers. - * - * # Safety - * - * - `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient - * - `tx_hex` must be a valid, non-null pointer to a NUL-terminated C string - * containing a hex-encoded serialized transaction - */ - -int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, - const char *tx_hex) -; - #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/dash-spv-ffi/src/bin/ffi_cli.rs b/dash-spv-ffi/src/bin/ffi_cli.rs index b84a725dd..f7ae41dc9 100644 --- a/dash-spv-ffi/src/bin/ffi_cli.rs +++ b/dash-spv-ffi/src/bin/ffi_cli.rs @@ -74,12 +74,6 @@ fn main() { .action(ArgAction::Append) .help("Peer address host:port (repeatable)"), ) - .arg( - Arg::new("workers") - .long("workers") - .value_parser(clap::value_parser!(u32)) - .help("Tokio worker threads (0=auto)"), - ) .arg( Arg::new("log-level") .long("log-level") @@ -99,12 +93,6 @@ fn main() { .action(ArgAction::SetTrue) .help("Disable masternode list synchronization"), ) - .arg( - Arg::new("no-filters") - .long("no-filters") - .action(ArgAction::SetTrue) - .help("Disable compact filter synchronization"), - ) .get_matches(); // Map network @@ -115,8 +103,6 @@ fn main() { _ => FFINetwork::Dash, }; - let disable_filter_sync = matches.get_flag("no-filters"); - unsafe { // Initialize tracing/logging via FFI so `tracing::info!` emits output let level = matches.get_one::("log-level").map(String::as_str).unwrap_or("info"); @@ -133,12 +119,6 @@ fn main() { std::process::exit(1); } - let _ = dash_spv_ffi_config_set_filter_load(cfg, !disable_filter_sync); - - if let Some(workers) = matches.get_one::("workers") { - let _ = dash_spv_ffi_config_set_worker_threads(cfg, *workers); - } - if let Some(height) = matches.get_one::("start-height") { let _ = dash_spv_ffi_config_set_start_from_height(cfg, *height); } @@ -214,13 +194,9 @@ fn main() { if !prog_ptr.is_null() { let prog = &*prog_ptr; let headers_done = SYNC_COMPLETED.load(Ordering::SeqCst); - let filters_complete = if disable_filter_sync || !prog.filter_sync_available { - false - } else { - prog.filter_header_height >= prog.header_height - && prog.last_synced_filter_height >= prog.filter_header_height - }; - if headers_done && (filters_complete || disable_filter_sync) { + let filters_complete = prog.filter_header_height >= prog.header_height + && prog.last_synced_filter_height >= prog.filter_header_height; + if headers_done && filters_complete { dash_spv_ffi_sync_progress_destroy(prog_ptr); break; } diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs deleted file mode 100644 index f61206dac..000000000 --- a/dash-spv-ffi/src/broadcast.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::{null_check, set_last_error, FFIDashSpvClient, FFIErrorCode}; -use std::ffi::CStr; -use std::os::raw::c_char; - -/// Broadcasts a transaction to the Dash network via connected peers. -/// -/// # Safety -/// -/// - `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient -/// - `tx_hex` must be a valid, non-null pointer to a NUL-terminated C string -/// containing a hex-encoded serialized transaction -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( - client: *mut FFIDashSpvClient, - tx_hex: *const c_char, -) -> i32 { - null_check!(client); - null_check!(tx_hex); - - let tx_str = match CStr::from_ptr(tx_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in transaction: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - let tx_bytes = match hex::decode(tx_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex in transaction: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - let tx = match dashcore::consensus::deserialize::(&tx_bytes) { - Ok(t) => t, - Err(e) => { - set_last_error(&format!("Invalid transaction: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result<(), dash_spv::SpvError> = client.runtime.block_on(async { - // Take the client out to avoid holding the lock across await - let spv_client = { - let mut guard = inner.lock().unwrap(); - match guard.take() { - Some(client) => client, - None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - } - }; - - // Broadcast the transaction over P2P - let res = spv_client.broadcast_transaction(&tx).await; - - // Put the client back - let mut guard = inner.lock().unwrap(); - *guard = Some(spv_client); - res - }); - - match result { - Ok(_) => FFIErrorCode::Success as i32, - Err(e) => { - set_last_error(&format!("Failed to broadcast transaction: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} diff --git a/dash-spv-ffi/src/checkpoints.rs b/dash-spv-ffi/src/checkpoints.rs index 177a67d81..a1738207f 100644 --- a/dash-spv-ffi/src/checkpoints.rs +++ b/dash-spv-ffi/src/checkpoints.rs @@ -1,4 +1,4 @@ -use crate::{set_last_error, FFIArray, FFIErrorCode}; +use crate::{set_last_error, FFIErrorCode}; use dash_spv::chain::checkpoints::{mainnet_checkpoints, testnet_checkpoints, CheckpointManager}; use dashcore::hashes::Hash; use dashcore::Network; @@ -120,43 +120,3 @@ pub unsafe extern "C" fn dash_spv_ffi_checkpoint_before_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/client.rs b/dash-spv-ffi/src/client.rs index 688531911..3a3546903 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -9,14 +9,12 @@ use dash_spv::storage::DiskStorageManager; use dash_spv::types::SyncStage; use dash_spv::DashSpvClient; use dash_spv::Hash; -use dashcore::Txid; use futures::future::{AbortHandle, Abortable}; use once_cell::sync::Lazy; use std::collections::HashMap; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::os::raw::{c_char, c_void}; -use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -513,23 +511,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_stop(client: *mut FFIDashSpvClient) } } -/// Performs a test synchronization of the SPV client -/// -/// # Parameters -/// - `client`: Pointer to an FFIDashSpvClient instance -/// -/// # Returns -/// - `0` on success -/// - Negative error code on failure -/// -/// # Safety -/// This function is unsafe because it dereferences a raw pointer. -/// The caller must ensure that the client pointer is valid. -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_test_sync(client: *mut FFIDashSpvClient) -> i32 { - null_check!(client); - - let client = &(*client); +pub fn client_test_sync(client: &FFIDashSpvClient) -> i32 { let result = client.runtime.block_on(async { let spv_client = { let mut guard = client.inner.lock().unwrap(); @@ -1033,34 +1015,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_clear_storage(client: *mut FFIDashS } } -/// Check if compact filter sync is currently available. -/// -/// # Safety -/// - `client` must be a valid, non-null pointer. -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_is_filter_sync_available( - client: *mut FFIDashSpvClient, -) -> bool { - null_check!(client, false); - - let client = &(*client); - let inner = client.inner.clone(); - - client.runtime.block_on(async { - let spv_client = { - let mut guard = inner.lock().unwrap(); - match guard.take() { - Some(client) => client, - None => return false, - } - }; - let res = spv_client.is_filter_sync_available().await; - let mut guard = inner.lock().unwrap(); - *guard = Some(spv_client); - res - }) -} - /// Set event callbacks for the client. /// /// # Safety @@ -1144,99 +1098,6 @@ pub unsafe extern "C" fn dash_spv_ffi_sync_progress_destroy(progress: *mut FFISy // Wallet operations -/// Request a rescan of the blockchain from a given height (not yet implemented). -/// -/// # Safety -/// - `client` must be a valid, non-null pointer. -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( - client: *mut FFIDashSpvClient, - _from_height: u32, -) -> i32 { - null_check!(client); - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result<(), dash_spv::SpvError> = client.runtime.block_on(async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut _spv_client) = *guard { - // TODO: rescan_from_height not yet implemented in dash-spv - Err(dash_spv::SpvError::Config("Not implemented".to_string())) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(_) => FFIErrorCode::Success as i32, - Err(e) => { - set_last_error(&format!("Failed to rescan blockchain: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -/// Record that we attempted to send a transaction by its txid. -/// -/// # Safety -/// - `client` and `txid` must be valid, non-null pointers. -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_record_send( - client: *mut FFIDashSpvClient, - txid: *const c_char, -) -> i32 { - null_check!(client); - null_check!(txid); - - let txid_str = match CStr::from_ptr(txid).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in txid: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - let txid = match Txid::from_str(txid_str) { - Ok(t) => t, - Err(e) => { - set_last_error(&format!("Invalid txid: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let spv_client = { - let mut guard = inner.lock().unwrap(); - match guard.take() { - Some(client) => client, - None => { - return Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - } - }; - let res = spv_client.record_send(txid).await; - let mut guard = inner.lock().unwrap(); - *guard = Some(spv_client); - res - }); - - match result { - Ok(()) => FFIErrorCode::Success as i32, - Err(e) => { - set_last_error(&e.to_string()); - FFIErrorCode::from(e) as i32 - } - } -} - /// Get the wallet manager from the SPV client /// /// Returns a pointer to an `FFIWalletManager` wrapper that clones the underlying diff --git a/dash-spv-ffi/src/config.rs b/dash-spv-ffi/src/config.rs index ffb8d8477..a81764e68 100644 --- a/dash-spv-ffi/src/config.rs +++ b/dash-spv-ffi/src/config.rs @@ -1,4 +1,4 @@ -use crate::{null_check, set_last_error, FFIErrorCode, FFIMempoolStrategy, FFIString}; +use crate::{null_check, set_last_error, FFIErrorCode, FFIMempoolStrategy}; use dash_spv::{ClientConfig, ValidationMode}; use key_wallet_ffi::FFINetwork; use std::ffi::CStr; @@ -87,40 +87,6 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_data_dir( } } -/// Sets the validation mode for the SPV client -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet -/// - The caller must ensure the config pointer remains valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_validation_mode( - config: *mut FFIClientConfig, - mode: FFIValidationMode, -) -> i32 { - null_check!(config); - - let config = unsafe { &mut *((*config).inner as *mut ClientConfig) }; - config.validation_mode = mode.into(); - FFIErrorCode::Success as i32 -} - -/// Sets the maximum number of peers to connect to -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet -/// - The caller must ensure the config pointer remains valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_max_peers( - config: *mut FFIClientConfig, - max_peers: u32, -) -> i32 { - null_check!(config); - - let config = unsafe { &mut *((*config).inner as *mut ClientConfig) }; - config.max_peers = max_peers; - FFIErrorCode::Success as i32 -} - // Note: dash-spv doesn't have min_peers, only max_peers /// Adds a peer address to the configuration @@ -229,40 +195,6 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_user_agent( } } -/// Sets whether to relay transactions (currently a no-op) -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet -/// - The caller must ensure the config pointer remains valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_relay_transactions( - config: *mut FFIClientConfig, - _relay: bool, -) -> i32 { - null_check!(config); - - let _config = unsafe { &mut *((*config).inner as *mut ClientConfig) }; - // relay_transactions not directly settable in current ClientConfig - FFIErrorCode::Success as i32 -} - -/// Sets whether to load bloom filters -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet -/// - The caller must ensure the config pointer remains valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_filter_load( - config: *mut FFIClientConfig, - load_filters: bool, -) -> i32 { - null_check!(config); - - let config = unsafe { &mut *((*config).inner as *mut ClientConfig) }; - config.enable_filters = load_filters; - FFIErrorCode::Success as i32 -} - /// Restrict connections strictly to configured peers (disable DNS discovery and peer store) /// /// # Safety @@ -313,27 +245,6 @@ pub unsafe extern "C" fn dash_spv_ffi_config_get_network( config.network.into() } -/// Gets the data directory path from the configuration -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig or null -/// - If null or no data directory is set, returns an FFIString with null pointer -/// - The returned FFIString must be freed by the caller using `dash_spv_ffi_string_destroy` -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_get_data_dir( - config: *const FFIClientConfig, -) -> FFIString { - if config.is_null() { - return FFIString { - ptr: std::ptr::null_mut(), - length: 0, - }; - } - - let config = unsafe { &*((*config).inner as *const ClientConfig) }; - FFIString::new(&config.storage_path.to_string_lossy()) -} - /// Destroys an FFIClientConfig and frees its memory /// /// # Safety @@ -361,22 +272,6 @@ impl FFIClientConfig { unsafe { (*(self.inner as *const ClientConfig)).clone() } } } - -/// Sets the number of Tokio worker threads for the FFI runtime (0 = auto) -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_worker_threads( - config: *mut FFIClientConfig, - threads: u32, -) -> i32 { - null_check!(config); - let cfg = &mut *config; - cfg.worker_threads = threads; - FFIErrorCode::Success as i32 -} - // Mempool configuration functions /// Enables or disables mempool tracking @@ -413,23 +308,6 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_mempool_strategy( FFIErrorCode::Success as i32 } -/// Sets the maximum number of mempool transactions to track -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet -/// - The caller must ensure the config pointer remains valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_set_max_mempool_transactions( - config: *mut FFIClientConfig, - max_transactions: u32, -) -> i32 { - null_check!(config); - - let config = unsafe { &mut *((*config).inner as *mut ClientConfig) }; - config.max_mempool_transactions = max_transactions as usize; - FFIErrorCode::Success as i32 -} - /// Sets whether to fetch full mempool transaction data /// /// # Safety @@ -464,40 +342,6 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_persist_mempool( FFIErrorCode::Success as i32 } -/// Gets whether mempool tracking is enabled -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig or null -/// - If null, returns false as default -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_get_mempool_tracking( - config: *const FFIClientConfig, -) -> bool { - if config.is_null() { - return false; - } - - let config = unsafe { &*((*config).inner as *const ClientConfig) }; - config.enable_mempool_tracking -} - -/// Gets the mempool synchronization strategy -/// -/// # Safety -/// - `config` must be a valid pointer to an FFIClientConfig or null -/// - If null, returns FFIMempoolStrategy::FetchAll as default -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_config_get_mempool_strategy( - config: *const FFIClientConfig, -) -> FFIMempoolStrategy { - if config.is_null() { - return FFIMempoolStrategy::FetchAll; - } - - let config = unsafe { &*((*config).inner as *const ClientConfig) }; - config.mempool_strategy.into() -} - // Checkpoint sync configuration functions /// Sets the starting block height for synchronization diff --git a/dash-spv-ffi/src/error.rs b/dash-spv-ffi/src/error.rs index 18f861671..4f3116cb0 100644 --- a/dash-spv-ffi/src/error.rs +++ b/dash-spv-ffi/src/error.rs @@ -44,11 +44,6 @@ pub extern "C" fn dash_spv_ffi_get_last_error() -> *const c_char { } } -#[no_mangle] -pub extern "C" fn dash_spv_ffi_clear_error() { - clear_last_error(); -} - impl From for FFIErrorCode { fn from(err: SpvError) -> Self { match err { diff --git a/dash-spv-ffi/src/lib.rs b/dash-spv-ffi/src/lib.rs index 84e0ccb9a..36c3dabd7 100644 --- a/dash-spv-ffi/src/lib.rs +++ b/dash-spv-ffi/src/lib.rs @@ -39,7 +39,6 @@ mod test_client_lifecycle; #[path = "../tests/unit/test_async_operations.rs"] mod test_async_operations; -mod broadcast; #[cfg(test)] #[path = "../tests/unit/test_memory_management.rs"] mod test_memory_management; diff --git a/dash-spv-ffi/src/platform_integration.rs b/dash-spv-ffi/src/platform_integration.rs index d374a015e..2144a7421 100644 --- a/dash-spv-ffi/src/platform_integration.rs +++ b/dash-spv-ffi/src/platform_integration.rs @@ -28,41 +28,6 @@ impl FFIResult { } } -/// Creates a CoreSDKHandle from an FFIDashSpvClient -/// -/// # Safety -/// -/// This function is unsafe because: -/// - The caller must ensure the client pointer is valid -/// - The returned handle must be properly released with ffi_dash_spv_release_core_handle -#[no_mangle] -pub unsafe extern "C" fn ffi_dash_spv_get_core_handle( - client: *mut FFIDashSpvClient, -) -> *mut CoreSDKHandle { - if client.is_null() { - set_last_error("Null client pointer"); - return ptr::null_mut(); - } - - Box::into_raw(Box::new(CoreSDKHandle { - client, - })) -} - -/// Releases a CoreSDKHandle -/// -/// # Safety -/// -/// This function is unsafe because: -/// - The caller must ensure the handle pointer is valid -/// - The handle must not be used after this call -#[no_mangle] -pub unsafe extern "C" fn ffi_dash_spv_release_core_handle(handle: *mut CoreSDKHandle) { - if !handle.is_null() { - let _ = Box::from_raw(handle); - } -} - /// Gets a quorum public key from the Core chain /// /// # Safety diff --git a/dash-spv-ffi/src/types.rs b/dash-spv-ffi/src/types.rs index dffef6abb..c6839a8d7 100644 --- a/dash-spv-ffi/src/types.rs +++ b/dash-spv-ffi/src/types.rs @@ -1,8 +1,8 @@ use dash_spv::client::config::MempoolStrategy; use dash_spv::types::{DetailedSyncProgress, MempoolRemovalReason, SyncStage}; -use dash_spv::{ChainState, SyncProgress}; +use dash_spv::SyncProgress; use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_void}; +use std::os::raw::c_char; /// Opaque handle to the wallet manager owned by the SPV client. /// @@ -179,145 +179,15 @@ impl From for FFIDetailedSyncProgress { } } -#[repr(C)] -pub struct FFIChainState { - pub masternode_height: u32, - pub last_chainlock_height: u32, - pub last_chainlock_hash: FFIString, - pub current_filter_tip: u32, -} - -impl From for FFIChainState { - fn from(state: ChainState) -> Self { - FFIChainState { - masternode_height: state.last_masternode_diff_height.unwrap_or(0), - last_chainlock_height: state.last_chainlock_height.unwrap_or(0), - last_chainlock_hash: FFIString::new( - &state.last_chainlock_hash.map(|h| h.to_string()).unwrap_or_default(), - ), - current_filter_tip: 0, // FilterHeader not directly convertible to u32 - } - } -} - -/// FFI-safe array that transfers ownership of memory to the C caller. -/// -/// # Safety -/// -/// This struct represents memory that has been allocated by Rust but ownership -/// has been transferred to the C caller. The caller is responsible for: -/// - Not accessing the memory after it has been freed -/// - Calling `dash_spv_ffi_array_destroy` to properly deallocate the memory -/// - Ensuring the data, len, and capacity fields remain consistent -#[repr(C)] -pub struct FFIArray { - pub data: *mut c_void, - pub len: usize, - pub capacity: usize, - pub elem_size: usize, - pub elem_align: usize, -} - -impl FFIArray { - /// Creates a new FFIArray from a Vec, transferring ownership of the memory to the caller. - /// - /// # Safety - /// - /// This function uses `std::mem::forget` to prevent Rust from deallocating the Vec's memory. - /// The caller becomes responsible for freeing this memory by calling `dash_spv_ffi_array_destroy`. - /// Failure to call the destroy function will result in a memory leak. - pub fn new(vec: Vec) -> Self { - let mut vec = vec; - let data = vec.as_mut_ptr() as *mut c_void; - let len = vec.len(); - let capacity = vec.capacity(); - std::mem::forget(vec); - - FFIArray { - data, - len, - capacity, - elem_size: std::mem::size_of::(), - elem_align: std::mem::align_of::(), - } - } - - /// # Safety - /// - The `data` pointer must be valid for reads of `len * size_of::()` bytes. - /// - The memory must not be mutated for the duration of the returned slice borrow. - /// - Caller must ensure the `elem_size`/`elem_align` match `T` when interpreting the data. - pub unsafe fn as_slice(&self) -> &[T] { - if self.data.is_null() || self.len == 0 { - &[] - } else { - std::slice::from_raw_parts(self.data as *const T, self.len) - } - } -} - -#[no_mangle] /// # Safety /// - `s.ptr` must be a pointer previously returned by `FFIString::new` or compatible. /// - It must not be used after this call. -pub unsafe extern "C" fn dash_spv_ffi_string_destroy(s: FFIString) { +pub unsafe fn dash_spv_ffi_string_destroy(s: FFIString) { if !s.ptr.is_null() { let _ = CString::from_raw(s.ptr); } } -#[no_mangle] -/// # Safety -/// - `arr` must be either null or a valid pointer to an `FFIArray` previously constructed in Rust. -/// - The memory referenced by `arr.data` must not be used after this call. -pub unsafe extern "C" fn dash_spv_ffi_array_destroy(arr: *mut FFIArray) { - if !arr.is_null() { - // Only deallocate the vector buffer recorded in the struct; do not free the struct itself. - // This makes it safe to pass pointers to stack-allocated FFIArray values returned by-value. - if !(*arr).data.is_null() && (*arr).capacity > 0 { - // Deallocate the vector buffer using the original layout - use std::alloc::{dealloc, Layout}; - let size = (*arr).elem_size.saturating_mul((*arr).capacity); - if size > 0 && (*arr).elem_align.is_power_of_two() && (*arr).elem_align > 0 { - // Safety: elem_size/elem_align were recorded from the original Vec - let layout = Layout::from_size_align_unchecked(size, (*arr).elem_align); - unsafe { dealloc((*arr).data as *mut u8, layout) }; - } - } - } -} - -/// Destroy an array of FFIString pointers (Vec<*mut FFIString>) and their contents. -/// -/// This function: -/// - Iterates the array elements as pointers to FFIString and destroys each via dash_spv_ffi_string_destroy -/// - Frees the underlying vector buffer stored in FFIArray -/// - Does not free the FFIArray struct itself (safe for both stack- and heap-allocated structs) -#[no_mangle] -/// # Safety -/// - `arr` must be either null or a valid pointer to an `FFIArray` whose elements are `*mut FFIString`. -/// - Each element pointer must be valid or null; non-null entries are freed. -/// - The memory referenced by `arr.data` must not be used after this call. -pub unsafe extern "C" fn dash_spv_ffi_string_array_destroy(arr: *mut FFIArray) { - if arr.is_null() { - return; - } - - // Destroy each FFIString pointed to by the array elements - if !(*arr).data.is_null() && (*arr).len > 0 { - let slice = std::slice::from_raw_parts((*arr).data as *const *mut FFIString, (*arr).len); - for &ffi_string_ptr in slice.iter() { - if !ffi_string_ptr.is_null() { - // Take ownership and destroy - let boxed = Box::from_raw(ffi_string_ptr); - dash_spv_ffi_string_destroy(*boxed); - } - } - } - - // Free the vector buffer itself - dash_spv_ffi_array_destroy(arr); -} - #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FFIMempoolStrategy { @@ -368,113 +238,3 @@ impl From for FFIMempoolRemovalReason { } } } - -/// FFI-safe representation of an unconfirmed transaction -/// -/// # Safety -/// -/// This struct contains raw pointers that must be properly managed: -/// -/// - `raw_tx`: A pointer to the raw transaction bytes. The caller is responsible for: -/// - Allocating this memory before passing it to Rust -/// - Ensuring the pointer remains valid for the lifetime of this struct -/// - Freeing the memory after use with `dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx` -/// -/// - `addresses`: A pointer to an array of FFIString objects. The caller is responsible for: -/// - Allocating this array before passing it to Rust -/// - Ensuring the pointer remains valid for the lifetime of this struct -/// - Freeing each FFIString in the array with `dash_spv_ffi_string_destroy` -/// - Freeing the array itself after use with `dash_spv_ffi_unconfirmed_transaction_destroy_addresses` -/// -/// Use `dash_spv_ffi_unconfirmed_transaction_destroy` to safely clean up all resources -/// associated with this struct. -#[repr(C)] -pub struct FFIUnconfirmedTransaction { - pub txid: FFIString, - pub raw_tx: *mut u8, - pub raw_tx_len: usize, - pub amount: i64, - pub fee: u64, - pub is_instant_send: bool, - pub is_outgoing: bool, - pub addresses: *mut FFIString, - pub addresses_len: usize, -} - -/// Destroys the raw transaction bytes allocated for an FFIUnconfirmedTransaction -/// -/// # Safety -/// -/// - `raw_tx` must be a valid pointer to memory allocated by the caller -/// - `raw_tx_len` must be the correct length of the allocated memory -/// - The pointer must not be used after this function is called -/// - This function should only be called once per allocation -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx( - raw_tx: *mut u8, - raw_tx_len: usize, -) { - if !raw_tx.is_null() && raw_tx_len > 0 { - // Reconstruct the Vec to properly deallocate the memory - let _ = Vec::from_raw_parts(raw_tx, raw_tx_len, raw_tx_len); - } -} - -/// Destroys the addresses array allocated for an FFIUnconfirmedTransaction -/// -/// # Safety -/// -/// - `addresses` must be a valid pointer to an array of FFIString objects -/// - `addresses_len` must be the correct length of the array -/// - Each FFIString in the array must be destroyed separately using `dash_spv_ffi_string_destroy` -/// - The pointer must not be used after this function is called -/// - This function should only be called once per allocation -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_unconfirmed_transaction_destroy_addresses( - addresses: *mut FFIString, - addresses_len: usize, -) { - if !addresses.is_null() && addresses_len > 0 { - // Reconstruct the Vec to properly deallocate the memory - let _ = Vec::from_raw_parts(addresses, addresses_len, addresses_len); - } -} - -/// Destroys an FFIUnconfirmedTransaction and all its associated resources -/// -/// # Safety -/// -/// - `tx` must be a valid pointer to an FFIUnconfirmedTransaction -/// - All resources (raw_tx, addresses array, and individual FFIStrings) will be freed -/// - The pointer must not be used after this function is called -/// - This function should only be called once per FFIUnconfirmedTransaction -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_unconfirmed_transaction_destroy( - tx: *mut FFIUnconfirmedTransaction, -) { - if !tx.is_null() { - let tx = Box::from_raw(tx); - - // Destroy the txid FFIString - dash_spv_ffi_string_destroy(tx.txid); - - // Destroy the raw_tx bytes - if !tx.raw_tx.is_null() && tx.raw_tx_len > 0 { - dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx(tx.raw_tx, tx.raw_tx_len); - } - - // Destroy each FFIString in the addresses array - if !tx.addresses.is_null() && tx.addresses_len > 0 { - // We need to read the addresses and destroy them one by one - for i in 0..tx.addresses_len { - let address_ptr = tx.addresses.add(i); - let address = std::ptr::read(address_ptr); - dash_spv_ffi_string_destroy(address); - } - // Destroy the addresses array itself - dash_spv_ffi_unconfirmed_transaction_destroy_addresses(tx.addresses, tx.addresses_len); - } - - // The Box will be dropped here, freeing the FFIUnconfirmedTransaction itself - } -} diff --git a/dash-spv-ffi/src/utils.rs b/dash-spv-ffi/src/utils.rs index de6a9b9e3..d76d7bc99 100644 --- a/dash-spv-ffi/src/utils.rs +++ b/dash-spv-ffi/src/utils.rs @@ -90,8 +90,3 @@ pub unsafe extern "C" fn dash_spv_ffi_init_logging( pub extern "C" fn dash_spv_ffi_version() -> *const c_char { concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char } - -#[no_mangle] -pub extern "C" fn dash_spv_ffi_enable_test_mode() { - std::env::set_var("DASH_SPV_TEST_MODE", "1"); -} diff --git a/dash-spv-ffi/tests/test_client.rs b/dash-spv-ffi/tests/test_client.rs index 28a91cb22..afbe0747b 100644 --- a/dash-spv-ffi/tests/test_client.rs +++ b/dash-spv-ffi/tests/test_client.rs @@ -40,7 +40,6 @@ mod tests { unsafe { let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); } (config, temp_dir) @@ -150,9 +149,6 @@ mod tests { let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - // Enable test mode to use deterministic peers - dash_spv_ffi_enable_test_mode(); - // Create client let client = dash_spv_ffi_client_new(config); assert!(!client.is_null(), "Failed to create client"); @@ -170,7 +166,7 @@ mod tests { // Run the diagnostic sync test println!("Running sync diagnostic test..."); - let test_result = dash_spv_ffi_client_test_sync(client); + let test_result = client_test_sync(&*client); if test_result == FFIErrorCode::Success as i32 { println!("✅ Sync test passed!"); diff --git a/dash-spv-ffi/tests/test_config.rs b/dash-spv-ffi/tests/test_config.rs index 4531ed109..cf9409a51 100644 --- a/dash-spv-ffi/tests/test_config.rs +++ b/dash-spv-ffi/tests/test_config.rs @@ -57,13 +57,6 @@ mod tests { let result = dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); assert_eq!(result, FFIErrorCode::Success as i32); - let data_dir = dash_spv_ffi_config_get_data_dir(config); - if !data_dir.ptr.is_null() { - let dir_str = FFIString::from_ptr(data_dir.ptr).unwrap(); - assert_eq!(dir_str, "/tmp/dash-spv-test"); - dash_spv_ffi_string_destroy(data_dir); - } - dash_spv_ffi_config_destroy(config); } } @@ -83,30 +76,12 @@ mod tests { } } - #[test] - #[serial] - fn test_config_validation_mode() { - unsafe { - let config = dash_spv_ffi_config_new(FFINetwork::Testnet); - - let result = dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::Full); - assert_eq!(result, FFIErrorCode::Success as i32); - - dash_spv_ffi_config_destroy(config); - } - } - #[test] #[serial] fn test_config_peers() { unsafe { let config = dash_spv_ffi_config_new(FFINetwork::Testnet); - let result = dash_spv_ffi_config_set_max_peers(config, 10); - assert_eq!(result, FFIErrorCode::Success as i32); - - // min_peers not available in dash-spv, only max_peers - let peer_addr = CString::new("127.0.0.1:9999").unwrap(); let result = dash_spv_ffi_config_add_peer(config, peer_addr.as_ptr()); assert_eq!(result, FFIErrorCode::Success as i32); @@ -132,20 +107,4 @@ mod tests { dash_spv_ffi_config_destroy(config); } } - - #[test] - #[serial] - fn test_config_booleans() { - unsafe { - let config = dash_spv_ffi_config_new(FFINetwork::Testnet); - - let result = dash_spv_ffi_config_set_relay_transactions(config, true); - assert_eq!(result, FFIErrorCode::Success as i32); - - let result = dash_spv_ffi_config_set_filter_load(config, false); - assert_eq!(result, FFIErrorCode::Success as i32); - - dash_spv_ffi_config_destroy(config); - } - } } diff --git a/dash-spv-ffi/tests/test_error.rs b/dash-spv-ffi/tests/test_error.rs index dd407b868..8eb40ff2c 100644 --- a/dash-spv-ffi/tests/test_error.rs +++ b/dash-spv-ffi/tests/test_error.rs @@ -21,10 +21,6 @@ mod tests { let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); assert_eq!(error_str, "Test error message"); } - - dash_spv_ffi_clear_error(); - let error_ptr = dash_spv_ffi_get_last_error(); - assert!(error_ptr.is_null()); } #[test] diff --git a/dash-spv-ffi/tests/test_event_callbacks.rs b/dash-spv-ffi/tests/test_event_callbacks.rs index 9e1838038..b189d0c65 100644 --- a/dash-spv-ffi/tests/test_event_callbacks.rs +++ b/dash-spv-ffi/tests/test_event_callbacks.rs @@ -157,9 +157,6 @@ fn test_event_callbacks_setup() { let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - // Set validation mode to basic for faster testing - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::Basic); - // Create client let client = dash_spv_ffi_client_new(config); assert!(!client.is_null()); @@ -244,7 +241,6 @@ fn test_enhanced_event_callbacks() { let temp_dir = TempDir::new().unwrap(); let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); // Create client let client = dash_spv_ffi_client_new(config); @@ -299,7 +295,6 @@ fn test_drain_events_integration() { let temp_dir = TempDir::new().unwrap(); let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); // Create client let client = dash_spv_ffi_client_new(config); @@ -364,7 +359,6 @@ fn test_drain_events_concurrent_with_callbacks() { let temp_dir = TempDir::new().unwrap(); let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); let client = dash_spv_ffi_client_new(config); assert!(!client.is_null()); @@ -440,7 +434,6 @@ fn test_drain_events_callback_lifecycle() { let temp_dir = TempDir::new().unwrap(); let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); let client = dash_spv_ffi_client_new(config); assert!(!client.is_null()); diff --git a/dash-spv-ffi/tests/test_platform_integration_minimal.rs b/dash-spv-ffi/tests/test_platform_integration_minimal.rs index 00c588add..824d6e1b8 100644 --- a/dash-spv-ffi/tests/test_platform_integration_minimal.rs +++ b/dash-spv-ffi/tests/test_platform_integration_minimal.rs @@ -6,10 +6,6 @@ use std::ptr; #[test] fn test_basic_null_checks() { unsafe { - // Test null pointer handling - let handle = ffi_dash_spv_get_core_handle(ptr::null_mut()); - assert!(handle.is_null()); - // Test error code let mut height: u32 = 0; let result = diff --git a/dash-spv-ffi/tests/test_platform_integration_safety.rs b/dash-spv-ffi/tests/test_platform_integration_safety.rs index 18a236a87..91416e857 100644 --- a/dash-spv-ffi/tests/test_platform_integration_safety.rs +++ b/dash-spv-ffi/tests/test_platform_integration_safety.rs @@ -34,10 +34,6 @@ fn assert_ffi_error(result: FFIResult, expected_code: FFIErrorCode) { #[serial] fn test_get_core_handle_null_safety() { unsafe { - // Test 1: Null client pointer - let handle = ffi_dash_spv_get_core_handle(ptr::null_mut()); - assert!(handle.is_null(), "Should return null for null client"); - // Test 2: Getting last error after null pointer operation let error = dash_spv_ffi_get_last_error(); if !error.is_null() { @@ -52,21 +48,6 @@ fn test_get_core_handle_null_safety() { } } -#[test] -#[serial] -fn test_release_core_handle_safety() { - unsafe { - // Test 1: Release null handle (should be safe no-op) - ffi_dash_spv_release_core_handle(ptr::null_mut()); - - // Test 2: Double-free prevention - // In a real implementation with a valid handle: - // let handle = create_valid_handle(); - // ffi_dash_spv_release_core_handle(handle); - // ffi_dash_spv_release_core_handle(handle); // Should be safe - } -} - #[test] #[serial] fn test_get_quorum_public_key_null_pointer_safety() { @@ -227,16 +208,7 @@ fn test_thread_safety_concurrent_access() { #[serial] fn test_memory_safety_patterns() { unsafe { - // Test 1: Use after free prevention - // Get a handle and immediately release it - let handle = ffi_dash_spv_get_core_handle(ptr::null_mut()); - if !handle.is_null() { - ffi_dash_spv_release_core_handle(handle); - // Attempting to use the handle again should be safe (no crash) - // In practice, the implementation should handle this gracefully - } - - // Test 2: Buffer overflow prevention + // Test 1: Buffer overflow prevention let quorum_hash = [0u8; 32]; let mut tiny_buffer = [0u8; 1]; // Way too small @@ -258,11 +230,6 @@ fn test_memory_safety_patterns() { #[serial] fn test_error_propagation_thread_local() { unsafe { - // Test that errors are properly stored in thread-local storage - - // Clear any previous error - dash_spv_ffi_clear_error(); - // Trigger an error let result = ffi_dash_spv_get_platform_activation_height(ptr::null_mut(), ptr::null_mut()); assert_ne!(result.error_code, 0); @@ -280,12 +247,6 @@ fn test_error_propagation_thread_local() { // Note: Error strings are managed internally } - - // Verify error handling after retrieval - dash_spv_ffi_clear_error(); - let second_error = dash_spv_ffi_get_last_error(); - // Should be null after clearing - assert!(second_error.is_null(), "Error should be cleared"); } } @@ -325,9 +286,6 @@ fn test_boundary_conditions() { #[serial] fn test_error_string_lifecycle() { unsafe { - // Clear errors first - dash_spv_ffi_clear_error(); - // Trigger an error to generate an error string let _ = ffi_dash_spv_get_platform_activation_height(ptr::null_mut(), ptr::null_mut()); @@ -342,33 +300,6 @@ fn test_error_string_lifecycle() { // Multiple calls should return the same pointer until cleared let error2 = dash_spv_ffi_get_last_error(); assert_eq!(error, error2, "Should return same error pointer"); - - // Clear and verify it's gone - dash_spv_ffi_clear_error(); - let error3 = dash_spv_ffi_get_last_error(); - assert!(error3.is_null(), "Error should be null after clear"); } } } - -/// Test handle reference counting and lifecycle -#[test] -#[serial] -fn test_handle_lifecycle() { - unsafe { - // Test null handle operations - 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_client); - assert!(handle.is_null()); - - // Releasing null handle should be safe - ffi_dash_spv_release_core_handle(null_handle); - - // Multiple releases of null should be safe - ffi_dash_spv_release_core_handle(null_handle); - ffi_dash_spv_release_core_handle(null_handle); - } -} diff --git a/dash-spv-ffi/tests/test_types.rs b/dash-spv-ffi/tests/test_types.rs index 25e09feed..ab1c7c5fe 100644 --- a/dash-spv-ffi/tests/test_types.rs +++ b/dash-spv-ffi/tests/test_types.rs @@ -39,40 +39,6 @@ mod tests { assert_eq!(FFINetwork::Devnet, dashcore::Network::Devnet.into()); } - #[test] - fn test_ffi_array_new_and_destroy() { - let test_data = vec![1u32, 2, 3, 4, 5]; - let len = test_data.len(); - let mut array = FFIArray::new(test_data); - - assert!(!array.data.is_null()); - assert_eq!(array.len, len); - assert!(array.capacity >= len); - - unsafe { - let slice = array.as_slice::(); - assert_eq!(slice.len(), len); - assert_eq!(slice, &[1, 2, 3, 4, 5]); - - dash_spv_ffi_array_destroy(&mut array as *mut FFIArray); - } - } - - #[test] - fn test_ffi_array_empty() { - let empty_vec: Vec = vec![]; - let mut array = FFIArray::new(empty_vec); - - assert_eq!(array.len, 0); - - unsafe { - let slice = array.as_slice::(); - assert_eq!(slice.len(), 0); - - dash_spv_ffi_array_destroy(&mut array as *mut FFIArray); - } - } - #[test] fn test_sync_progress_conversion() { let progress = dash_spv::SyncProgress { diff --git a/dash-spv-ffi/tests/test_utils.rs b/dash-spv-ffi/tests/test_utils.rs index 13048574f..29e0048d8 100644 --- a/dash-spv-ffi/tests/test_utils.rs +++ b/dash-spv-ffi/tests/test_utils.rs @@ -63,10 +63,4 @@ mod tests { assert_eq!(name_str, "devnet"); } } - - #[test] - fn test_enable_test_mode() { - dash_spv_ffi_enable_test_mode(); - assert_eq!(std::env::var("DASH_SPV_TEST_MODE").unwrap_or_default(), "1"); - } } diff --git a/dash-spv-ffi/tests/unit/test_async_operations.rs b/dash-spv-ffi/tests/unit/test_async_operations.rs index 5546a7151..e97b02e10 100644 --- a/dash-spv-ffi/tests/unit/test_async_operations.rs +++ b/dash-spv-ffi/tests/unit/test_async_operations.rs @@ -67,7 +67,6 @@ mod tests { let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); let client = dash_spv_ffi_client_new(config); assert!(!client.is_null(), "Failed to create client"); @@ -241,7 +240,7 @@ mod tests { client, }; - extern "C" fn reentrant_callback( + unsafe extern "C" fn reentrant_callback( _success: bool, _error: *const c_char, user_data: *mut c_void, @@ -265,7 +264,7 @@ mod tests { let start_time = Instant::now(); // Try to call test_sync which is a simpler operation - let test_result = unsafe { dash_spv_ffi_client_test_sync(data.client) }; + let test_result = client_test_sync(&*data.client); let elapsed = start_time.elapsed(); // If this takes too long, it might indicate a deadlock diff --git a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs index ead73b240..d8283848e 100644 --- a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs +++ b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs @@ -19,7 +19,6 @@ mod tests { let config = dash_spv_ffi_config_new(FFINetwork::Regtest); let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); (config, temp_dir) } } diff --git a/dash-spv-ffi/tests/unit/test_configuration.rs b/dash-spv-ffi/tests/unit/test_configuration.rs index 695dc3d0b..09131299f 100644 --- a/dash-spv-ffi/tests/unit/test_configuration.rs +++ b/dash-spv-ffi/tests/unit/test_configuration.rs @@ -3,7 +3,7 @@ mod tests { use crate::*; use key_wallet_ffi::FFINetwork; use serial_test::serial; - use std::ffi::{CStr, CString}; + use std::ffi::CString; #[test] #[serial] @@ -34,14 +34,6 @@ mod tests { let result = dash_spv_ffi_config_set_data_dir(config, c_path.as_ptr()); assert_eq!(result, FFIErrorCode::Success as i32); - // Verify it was set - let retrieved = dash_spv_ffi_config_get_data_dir(config); - if !retrieved.ptr.is_null() { - let path_str = FFIString::from_ptr(retrieved.ptr).unwrap(); - assert_eq!(path_str, long_path); - dash_spv_ffi_string_destroy(retrieved); - } - dash_spv_ffi_config_destroy(config); } } @@ -183,13 +175,6 @@ mod tests { FFIErrorCode::Success as i32 ); - assert_eq!( - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::Full), - FFIErrorCode::Success as i32 - ); - - assert_eq!(dash_spv_ffi_config_set_max_peers(config, 50), FFIErrorCode::Success as i32); - let peer = CString::new("127.0.0.1:9999").unwrap(); assert_eq!( dash_spv_ffi_config_add_peer(config, peer.as_ptr()), @@ -202,16 +187,6 @@ mod tests { FFIErrorCode::Success as i32 ); - assert_eq!( - dash_spv_ffi_config_set_relay_transactions(config, true), - FFIErrorCode::Success as i32 - ); - - assert_eq!( - dash_spv_ffi_config_set_filter_load(config, true), - FFIErrorCode::Success as i32 - ); - assert_eq!( dash_spv_ffi_config_set_restrict_to_configured_peers(config, true), FFIErrorCode::Success as i32 @@ -231,19 +206,6 @@ mod tests { FFIErrorCode::NullPointer as i32 ); - assert_eq!( - dash_spv_ffi_config_set_validation_mode( - std::ptr::null_mut(), - FFIValidationMode::Basic - ), - FFIErrorCode::NullPointer as i32 - ); - - assert_eq!( - dash_spv_ffi_config_set_max_peers(std::ptr::null_mut(), 10), - FFIErrorCode::NullPointer as i32 - ); - assert_eq!( dash_spv_ffi_config_add_peer(std::ptr::null_mut(), std::ptr::null()), FFIErrorCode::NullPointer as i32 @@ -254,62 +216,21 @@ mod tests { FFIErrorCode::NullPointer as i32 ); - assert_eq!( - dash_spv_ffi_config_set_relay_transactions(std::ptr::null_mut(), false), - FFIErrorCode::NullPointer as i32 - ); - - assert_eq!( - dash_spv_ffi_config_set_filter_load(std::ptr::null_mut(), false), - FFIErrorCode::NullPointer as i32 - ); - // Test getters with null let net = dash_spv_ffi_config_get_network(std::ptr::null()); assert_eq!(net as i32, FFINetwork::Dash as i32); // Returns default - let dir = dash_spv_ffi_config_get_data_dir(std::ptr::null()); - assert!(dir.ptr.is_null()); - // Test destroy with null (should be safe) dash_spv_ffi_config_destroy(std::ptr::null_mut()); } } - #[test] - #[serial] - fn test_config_validation_modes() { - unsafe { - let config = dash_spv_ffi_config_testnet(); - - // Test all validation modes - let modes = - [FFIValidationMode::None, FFIValidationMode::Basic, FFIValidationMode::Full]; - for mode in modes { - let result = dash_spv_ffi_config_set_validation_mode(config, mode); - assert_eq!(result, FFIErrorCode::Success as i32); - } - - dash_spv_ffi_config_destroy(config); - } - } - #[test] #[serial] fn test_config_edge_case_values() { unsafe { let config = dash_spv_ffi_config_testnet(); - // Test max peers with edge values - assert_eq!(dash_spv_ffi_config_set_max_peers(config, 0), FFIErrorCode::Success as i32); - - assert_eq!(dash_spv_ffi_config_set_max_peers(config, 1), FFIErrorCode::Success as i32); - - assert_eq!( - dash_spv_ffi_config_set_max_peers(config, u32::MAX), - FFIErrorCode::Success as i32 - ); - // Test empty strings let empty = CString::new("").unwrap(); assert_eq!( @@ -320,131 +241,4 @@ mod tests { dash_spv_ffi_config_destroy(config); } } - - #[test] - #[serial] - fn test_worker_threads_configuration() { - unsafe { - let config = dash_spv_ffi_config_testnet(); - - // Test setting worker threads to 0 (auto mode) - let result = dash_spv_ffi_config_set_worker_threads(config, 0); - assert_eq!(result, FFIErrorCode::Success as i32); - - // Test setting specific worker thread counts - let thread_counts = [1, 2, 4, 8, 16, 32]; - for &count in &thread_counts { - let result = dash_spv_ffi_config_set_worker_threads(config, count); - assert_eq!(result, FFIErrorCode::Success as i32); - } - - // Test large worker thread count - let result = dash_spv_ffi_config_set_worker_threads(config, 1000); - assert_eq!(result, FFIErrorCode::Success as i32); - - // Test maximum value - let result = dash_spv_ffi_config_set_worker_threads(config, u32::MAX); - assert_eq!(result, FFIErrorCode::Success as i32); - - dash_spv_ffi_config_destroy(config); - } - } - - #[test] - #[serial] - fn test_worker_threads_with_null_config() { - unsafe { - // Test with null config pointer - let result = dash_spv_ffi_config_set_worker_threads(std::ptr::null_mut(), 4); - assert_eq!(result, FFIErrorCode::NullPointer as i32); - - // Check error was set - let error_ptr = dash_spv_ffi_get_last_error(); - assert!(!error_ptr.is_null()); - let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); - assert!( - error_str.contains("Null") - || error_str.contains("null") - || error_str.contains("invalid") - ); - } - } - - #[test] - #[serial] - fn test_worker_threads_persistence() { - unsafe { - // Test that worker thread setting is preserved - for &thread_count in &[0, 1, 4, 8] { - let config = dash_spv_ffi_config_new(FFINetwork::Testnet); - - // Set worker threads - let result = dash_spv_ffi_config_set_worker_threads(config, thread_count); - assert_eq!(result, FFIErrorCode::Success as i32); - - // Create client with this config (this tests that the setting is used) - let temp_dir = tempfile::TempDir::new().unwrap(); - let path = CString::new(temp_dir.path().to_str().unwrap()).unwrap(); - dash_spv_ffi_config_set_data_dir(config, path.as_ptr()); - dash_spv_ffi_config_set_validation_mode(config, FFIValidationMode::None); - - let client = dash_spv_ffi_client_new(config); - // Client creation should succeed regardless of worker thread count - assert!( - !client.is_null(), - "Failed to create client with {} worker threads", - thread_count - ); - - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } - } - } - - #[test] - #[serial] - fn test_worker_threads_multiple_configs() { - unsafe { - // Test that different configs can have different worker thread counts - let configs = [ - (dash_spv_ffi_config_testnet(), 1), - (dash_spv_ffi_config_mainnet(), 4), - (dash_spv_ffi_config_new(FFINetwork::Regtest), 8), - ]; - - for (config, thread_count) in configs { - let result = dash_spv_ffi_config_set_worker_threads(config, thread_count); - assert_eq!(result, FFIErrorCode::Success as i32); - } - - // Clean up all configs - for (config, _) in configs { - dash_spv_ffi_config_destroy(config); - } - } - } - - #[test] - #[serial] - fn test_worker_threads_edge_cases() { - unsafe { - let config = dash_spv_ffi_config_testnet(); - - // Test repeated setting of worker threads - for _ in 0..10 { - let result = dash_spv_ffi_config_set_worker_threads(config, 4); - assert_eq!(result, FFIErrorCode::Success as i32); - } - - // Test setting different values in sequence - let sequence = [0, 1, 0, 8, 0, 16, 0]; - for &count in &sequence { - let result = dash_spv_ffi_config_set_worker_threads(config, count); - assert_eq!(result, FFIErrorCode::Success as i32); - } - - dash_spv_ffi_config_destroy(config); - } - } } diff --git a/dash-spv-ffi/tests/unit/test_error_handling.rs b/dash-spv-ffi/tests/unit/test_error_handling.rs index f47f48c09..4ca1e7b6c 100644 --- a/dash-spv-ffi/tests/unit/test_error_handling.rs +++ b/dash-spv-ffi/tests/unit/test_error_handling.rs @@ -7,28 +7,6 @@ mod tests { use std::sync::{Arc, Barrier}; use std::thread; - #[test] - #[serial] - fn test_error_propagation() { - // Clear any existing error - dash_spv_ffi_clear_error(); - - // Test setting and getting error - set_last_error("Test error message"); - let error_ptr = dash_spv_ffi_get_last_error(); - assert!(!error_ptr.is_null()); - - unsafe { - let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); - assert_eq!(error_str, "Test error message"); - } - - // Clear and verify - dash_spv_ffi_clear_error(); - let error_ptr = dash_spv_ffi_get_last_error(); - assert!(error_ptr.is_null()); - } - #[test] #[serial] fn test_concurrent_error_handling() { @@ -89,8 +67,6 @@ mod tests { assert_eq!(error_str.len(), 10000); assert!(error_str.chars().all(|c| c == 'X')); } - - dash_spv_ffi_clear_error(); } #[test] @@ -152,7 +128,7 @@ mod tests { } // Clear using public API - dash_spv_ffi_clear_error(); + clear_last_error(); assert!(dash_spv_ffi_get_last_error().is_null()); } @@ -205,34 +181,4 @@ mod tests { assert_eq!(error_str, "Test error"); } } - - #[test] - #[serial] - fn test_error_with_special_characters() { - // Test error with newlines - set_last_error("Error\nwith\nnewlines"); - let error_ptr = dash_spv_ffi_get_last_error(); - unsafe { - let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); - assert_eq!(error_str, "Error\nwith\nnewlines"); - } - - // Test error with tabs - set_last_error("Error\twith\ttabs"); - let error_ptr = dash_spv_ffi_get_last_error(); - unsafe { - let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); - assert_eq!(error_str, "Error\twith\ttabs"); - } - - // Test error with quotes - set_last_error("Error with \"quotes\" and 'apostrophes'"); - let error_ptr = dash_spv_ffi_get_last_error(); - unsafe { - let error_str = CStr::from_ptr(error_ptr).to_str().unwrap(); - assert_eq!(error_str, "Error with \"quotes\" and 'apostrophes'"); - } - - dash_spv_ffi_clear_error(); - } } diff --git a/dash-spv-ffi/tests/unit/test_memory_management.rs b/dash-spv-ffi/tests/unit/test_memory_management.rs index 9c38bfffd..dba427a9c 100644 --- a/dash-spv-ffi/tests/unit/test_memory_management.rs +++ b/dash-spv-ffi/tests/unit/test_memory_management.rs @@ -39,34 +39,6 @@ mod tests { } } - #[test] - #[serial] - fn test_array_memory_lifecycle() { - unsafe { - // Test with different types and sizes - let small_array: Vec = vec![1, 2, 3, 4, 5]; - let mut small_ffi = FFIArray::new(small_array); - assert!(!small_ffi.data.is_null()); - assert_eq!(small_ffi.len, 5); - dash_spv_ffi_array_destroy(&mut small_ffi as *mut FFIArray); - - // Test with large array - let large_array: Vec = (0..100_000).collect(); - let mut large_ffi = FFIArray::new(large_array); - assert!(!large_ffi.data.is_null()); - assert_eq!(large_ffi.len, 100_000); - dash_spv_ffi_array_destroy(&mut large_ffi as *mut FFIArray); - - // Test with empty array - let empty_array: Vec = vec![]; - let mut empty_ffi = FFIArray::new(empty_array); - // Even empty arrays have valid pointers - assert!(!empty_ffi.data.is_null()); - assert_eq!(empty_ffi.len, 0); - dash_spv_ffi_array_destroy(&mut empty_ffi as *mut FFIArray); - } - } - #[test] #[serial] fn test_client_memory_lifecycle() { @@ -116,17 +88,6 @@ mod tests { dash_spv_ffi_string_destroy(ffi); } - - // Each thread creates and destroys arrays - for j in 0..50 { - let array: Vec = (0..j * 10).collect(); - let mut ffi_array = FFIArray::new(array); - - // Simulate some work - thread::sleep(Duration::from_micros(10)); - - dash_spv_ffi_array_destroy(&mut ffi_array as *mut FFIArray); - } } }); handles.push(handle); @@ -155,14 +116,6 @@ mod tests { assert_eq!(recovered.len(), size); dash_spv_ffi_string_destroy(ffi_string); - - // Array allocation - let large_array: Vec = vec![0xFF; size]; - let mut ffi_array = FFIArray::new(large_array); - assert!(!ffi_array.data.is_null()); - assert_eq!(ffi_array.len, size); - - dash_spv_ffi_array_destroy(&mut ffi_array as *mut FFIArray); } } } @@ -185,46 +138,6 @@ mod tests { length: 0, }; dash_spv_ffi_string_destroy(null_string); - - // Test with array - let mut ffi_array = FFIArray::new(vec![1u32, 2, 3]); - dash_spv_ffi_array_destroy(&mut ffi_array as *mut FFIArray); - - // Destroying with null should be safe - let mut null_array = FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - }; - dash_spv_ffi_array_destroy(&mut null_array as *mut FFIArray); - } - } - - #[test] - #[serial] - fn test_memory_alignment() { - unsafe { - // Test that memory is properly aligned for different types - - // u8 - 1 byte alignment - let u8_array = vec![1u8, 2, 3, 4]; - let mut u8_ffi = FFIArray::new(u8_array); - assert_eq!(u8_ffi.data as usize % std::mem::align_of::(), 0); - dash_spv_ffi_array_destroy(&mut u8_ffi as *mut FFIArray); - - // u32 - 4 byte alignment - let u32_array = vec![1u32, 2, 3, 4]; - let mut u32_ffi = FFIArray::new(u32_array); - assert_eq!(u32_ffi.data as usize % std::mem::align_of::(), 0); - dash_spv_ffi_array_destroy(&mut u32_ffi as *mut FFIArray); - - // u64 - 8 byte alignment - let u64_array = vec![1u64, 2, 3, 4]; - let mut u64_ffi = FFIArray::new(u64_array); - assert_eq!(u64_ffi.data as usize % std::mem::align_of::(), 0); - dash_spv_ffi_array_destroy(&mut u64_ffi as *mut FFIArray); } } @@ -327,13 +240,6 @@ mod tests { let recovered = FFIString::from_ptr(empty_string.ptr).unwrap(); assert_eq!(recovered, ""); dash_spv_ffi_string_destroy(empty_string); - - // Empty array - let empty_vec: Vec = vec![]; - let mut empty_array = FFIArray::new(empty_vec); - assert!(!empty_array.data.is_null()); - assert_eq!(empty_array.len, 0); - dash_spv_ffi_array_destroy(&mut empty_array as *mut FFIArray); } } @@ -381,13 +287,6 @@ mod tests { .map(|i| FFIString::new(&format!("Cycle {} String {}", cycle, i))) .collect(); - let arrays: Vec<_> = (0..10) - .map(|i| { - let data: Vec = (0..i * 10).collect(); - FFIArray::new(data) - }) - .collect(); - // Do some work thread::sleep(Duration::from_micros(100)); @@ -396,10 +295,6 @@ mod tests { dash_spv_ffi_string_destroy(s); } - for mut a in arrays { - dash_spv_ffi_array_destroy(&mut a as *mut FFIArray); - } - cycle += 1; } @@ -413,18 +308,13 @@ mod tests { // Test that memory allocated in one thread can be safely used in another unsafe { let string = FFIString::new("Allocated in thread 1"); - let mut array = FFIArray::new(vec![1u32, 2, 3, 4, 5]); // Verify we can read the data let s = FFIString::from_ptr(string.ptr).unwrap(); assert_eq!(s, "Allocated in thread 1"); - let slice = array.as_slice::(); - assert_eq!(slice, &[1, 2, 3, 4, 5]); - // Clean up dash_spv_ffi_string_destroy(string); - dash_spv_ffi_array_destroy(&mut array as *mut FFIArray); } } } diff --git a/dash-spv-ffi/tests/unit/test_type_conversions.rs b/dash-spv-ffi/tests/unit/test_type_conversions.rs index bf3f41661..8d2b60ef6 100644 --- a/dash-spv-ffi/tests/unit/test_type_conversions.rs +++ b/dash-spv-ffi/tests/unit/test_type_conversions.rs @@ -57,67 +57,6 @@ mod tests { } } - #[test] - fn test_ffi_array_different_sizes() { - // Test empty array - let empty: Vec = vec![]; - let mut empty_array = FFIArray::new(empty); - assert_eq!(empty_array.len, 0); - assert!(!empty_array.data.is_null()); // Even empty vec has allocated pointer - unsafe { - let slice = empty_array.as_slice::(); - assert_eq!(slice.len(), 0); - dash_spv_ffi_array_destroy(&mut empty_array as *mut FFIArray); - } - - // Test single element - let single = vec![42u32]; - let mut single_array = FFIArray::new(single); - assert_eq!(single_array.len, 1); - unsafe { - let slice = single_array.as_slice::(); - assert_eq!(slice.len(), 1); - assert_eq!(slice[0], 42); - dash_spv_ffi_array_destroy(&mut single_array as *mut FFIArray); - } - - // Test large array - let large: Vec = (0..10000).collect(); - let mut large_array = FFIArray::new(large.clone()); - assert_eq!(large_array.len, 10000); - unsafe { - let slice = large_array.as_slice::(); - assert_eq!(slice.len(), 10000); - for (i, &val) in slice.iter().enumerate() { - assert_eq!(val, i as u32); - } - dash_spv_ffi_array_destroy(&mut large_array as *mut FFIArray); - } - } - - #[test] - fn test_ffi_array_memory_alignment() { - // Test with u8 - let bytes: Vec = vec![1, 2, 3, 4]; - let mut byte_array = FFIArray::new(bytes); - unsafe { - let slice = byte_array.as_slice::(); - assert_eq!(slice, &[1, 2, 3, 4]); - dash_spv_ffi_array_destroy(&mut byte_array as *mut FFIArray); - } - - // Test with u64 (requires 8-byte alignment) - let longs: Vec = vec![u64::MAX, 0, 42]; - let mut long_array = FFIArray::new(longs); - unsafe { - let slice = long_array.as_slice::(); - assert_eq!(slice[0], u64::MAX); - assert_eq!(slice[1], 0); - assert_eq!(slice[2], 42); - dash_spv_ffi_array_destroy(&mut long_array as *mut FFIArray); - } - } - #[test] fn test_network_conversions() { // Test all network conversions @@ -160,30 +99,6 @@ mod tests { assert_eq!(ffi_progress.last_synced_filter_height, u32::MAX); } - #[test] - fn test_chain_state_none_values() { - let state = dash_spv::ChainState { - last_chainlock_height: None, - last_chainlock_hash: None, - current_filter_tip: None, - masternode_engine: None, - last_masternode_diff_height: None, - sync_base_height: 0, - }; - - let ffi_state = FFIChainState::from(state); - - assert_eq!(ffi_state.masternode_height, 0); - assert_eq!(ffi_state.last_chainlock_height, 0); - assert_eq!(ffi_state.current_filter_tip, 0); - - unsafe { - let hash_str = FFIString::from_ptr(ffi_state.last_chainlock_hash.ptr).unwrap(); - assert_eq!(hash_str, ""); - dash_spv_ffi_string_destroy(ffi_state.last_chainlock_hash); - } - } - #[test] fn test_concurrent_ffi_string_creation() { use std::sync::atomic::{AtomicUsize, Ordering};