From a8c0697831d376433e0552d30465ce44b77d873b Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 31 Aug 2025 18:01:02 +0700 Subject: [PATCH 1/9] refactor: spv clean up --- .github/workflows/verify-ffi-docs.yml | 134 + .gitignore | 1 + dash-spv-ffi/FFI_API.md | 910 +++++ dash-spv-ffi/FFI_DOCS_README.md | 109 + dash-spv-ffi/Makefile | 60 + dash-spv-ffi/cbindgen.toml | 1 - dash-spv-ffi/examples/wallet_manager_usage.rs | 80 + dash-spv-ffi/include/dash_spv_ffi.h | 115 +- dash-spv-ffi/scripts/check_ffi_docs.sh | 30 + dash-spv-ffi/scripts/generate_ffi_docs.py | 328 ++ dash-spv-ffi/src/broadcast.rs | 59 + dash-spv-ffi/src/client.rs | 853 +--- dash-spv-ffi/src/lib.rs | 1 + dash-spv-ffi/src/types.rs | 9 - dash-spv-ffi/src/wallet.rs | 1717 -------- dash-spv-ffi/tests/test_client.rs | 44 - dash-spv-ffi/tests/test_event_callbacks.rs | 61 - dash-spv-ffi/tests/test_mempool_tracking.rs | 183 - .../tests/test_platform_integration.rs | 59 - dash-spv-ffi/tests/test_wallet_manager.rs | 32 + .../tests/unit/test_client_lifecycle.rs | 65 - dash-spv/examples/filter_sync.rs | 5 +- dash-spv/examples/simple_sync.rs | 6 +- dash-spv/src/client/block_processor_test.rs | 15 +- dash-spv/src/lib.rs | 3 +- dash-spv/src/main.rs | 21 +- dash-spv/tests/block_download_test.rs | 9 +- dash-spv/tests/chainlock_simple_test.rs | 9 +- dash-spv/tests/chainlock_validation_test.rs | 21 +- dash-spv/tests/header_sync_test.rs | 5 +- .../tests/instantsend_integration_test.rs | 16 +- dash-spv/tests/integration_real_node_test.rs | 7 +- dash-spv/tests/multi_peer_test.rs | 21 +- dash-spv/tests/simple_header_test.rs | 5 +- dash-spv/tests/wallet_integration_test.rs | 16 +- key-wallet-ffi/Cargo.toml | 3 +- key-wallet-ffi/FFI_API.md | 3634 +++++++++++++++++ key-wallet-ffi/FFI_DOCS_README.md | 111 + key-wallet-ffi/Makefile | 58 + key-wallet-ffi/include/key_wallet_ffi.h | 4 + key-wallet-ffi/scripts/check_ffi_docs.sh | 30 + key-wallet-ffi/scripts/generate_ffi_docs.py | 282 ++ key-wallet-ffi/src/account_collection.rs | 2 +- key-wallet-ffi/src/lib.rs | 10 + key-wallet-ffi/src/managed_account.rs | 4 +- .../src/managed_account_collection.rs | 2 +- key-wallet-ffi/src/managed_wallet.rs | 8 +- key-wallet-ffi/src/wallet_manager.rs | 472 +-- .../examples/wallet_creation.rs | 38 +- key-wallet-manager/src/lib.rs | 2 - key-wallet-manager/src/spv_wallet_manager.rs | 295 -- key-wallet-manager/src/wallet_interface.rs | 3 - key-wallet-manager/src/wallet_manager/mod.rs | 6 +- .../src/wallet_manager/process_block.rs | 40 +- .../tests/spv_integration_tests.rs | 238 +- key-wallet/src/wallet/helper.rs | 4 + .../src/wallet/managed_wallet_info/helpers.rs | 4 + 57 files changed, 6303 insertions(+), 3957 deletions(-) create mode 100644 .github/workflows/verify-ffi-docs.yml create mode 100644 dash-spv-ffi/FFI_API.md create mode 100644 dash-spv-ffi/FFI_DOCS_README.md create mode 100644 dash-spv-ffi/Makefile create mode 100644 dash-spv-ffi/examples/wallet_manager_usage.rs create mode 100755 dash-spv-ffi/scripts/check_ffi_docs.sh create mode 100644 dash-spv-ffi/scripts/generate_ffi_docs.py create mode 100644 dash-spv-ffi/src/broadcast.rs delete mode 100644 dash-spv-ffi/src/wallet.rs delete mode 100644 dash-spv-ffi/tests/test_mempool_tracking.rs delete mode 100644 dash-spv-ffi/tests/test_platform_integration.rs create mode 100644 dash-spv-ffi/tests/test_wallet_manager.rs create mode 100644 key-wallet-ffi/FFI_API.md create mode 100644 key-wallet-ffi/FFI_DOCS_README.md create mode 100644 key-wallet-ffi/Makefile create mode 100755 key-wallet-ffi/scripts/check_ffi_docs.sh create mode 100755 key-wallet-ffi/scripts/generate_ffi_docs.py delete mode 100644 key-wallet-manager/src/spv_wallet_manager.rs diff --git a/.github/workflows/verify-ffi-docs.yml b/.github/workflows/verify-ffi-docs.yml new file mode 100644 index 000000000..f24caab1e --- /dev/null +++ b/.github/workflows/verify-ffi-docs.yml @@ -0,0 +1,134 @@ +name: Verify FFI Documentation + +on: + push: + paths: + - 'key-wallet-ffi/src/**/*.rs' + - 'key-wallet-ffi/FFI_API.md' + - 'key-wallet-ffi/scripts/generate_ffi_docs.py' + - 'dash-spv-ffi/src/**/*.rs' + - 'dash-spv-ffi/FFI_API.md' + - 'dash-spv-ffi/scripts/generate_ffi_docs.py' + - '.github/workflows/verify-ffi-docs.yml' + pull_request: + paths: + - 'key-wallet-ffi/src/**/*.rs' + - 'key-wallet-ffi/FFI_API.md' + - 'key-wallet-ffi/scripts/generate_ffi_docs.py' + - 'dash-spv-ffi/src/**/*.rs' + - 'dash-spv-ffi/FFI_API.md' + - 'dash-spv-ffi/scripts/generate_ffi_docs.py' + - '.github/workflows/verify-ffi-docs.yml' + +jobs: + verify-key-wallet-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Generate key-wallet-ffi documentation + run: | + cd key-wallet-ffi + python3 scripts/generate_ffi_docs.py + + - name: Check if key-wallet-ffi documentation is up to date + run: | + cd key-wallet-ffi + if ! git diff --exit-code FFI_API.md; then + echo "❌ key-wallet-ffi documentation is out of date!" + echo "" + echo "The FFI_API.md file needs to be regenerated." + echo "Please run the following command and commit the changes:" + echo "" + echo " cd key-wallet-ffi && make update-docs" + echo "" + echo "Changes detected:" + git diff FFI_API.md + exit 1 + else + echo "✅ key-wallet-ffi documentation is up to date" + fi + + verify-dash-spv-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Generate dash-spv-ffi documentation + run: | + cd dash-spv-ffi + python3 scripts/generate_ffi_docs.py + + - name: Check if dash-spv-ffi documentation is up to date + run: | + cd dash-spv-ffi + if ! git diff --exit-code FFI_API.md; then + echo "❌ dash-spv-ffi documentation is out of date!" + echo "" + echo "The FFI_API.md file needs to be regenerated." + echo "Please run the following command and commit the changes:" + echo "" + echo " cd dash-spv-ffi && make update-docs" + echo "" + echo "Changes detected:" + git diff FFI_API.md + exit 1 + else + echo "✅ dash-spv-ffi documentation is up to date" + fi + + update-docs-comment: + runs-on: ubuntu-latest + if: failure() && github.event_name == 'pull_request' + needs: [verify-key-wallet-docs, verify-dash-spv-docs] + + permissions: + pull-requests: write + + steps: + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ⚠️ FFI Documentation Update Required + + The FFI API documentation is out of date. Please regenerate it by running: + + For key-wallet-ffi: + \`\`\`bash + cd key-wallet-ffi + make update-docs + \`\`\` + + For dash-spv-ffi: + \`\`\`bash + cd dash-spv-ffi + make update-docs + \`\`\` + + Then commit the changes: + \`\`\`bash + git add */FFI_API.md + git commit -m "docs: update FFI API documentation" + \`\`\` + + This ensures the documentation stays in sync with the actual FFI functions.` + }) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 641f4f03b..7eb2f1671 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ cobertura.xml # Build scripts artifacts *.log +/dash-spv-ffi/peer_reputation.json diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md new file mode 100644 index 000000000..6c01046b7 --- /dev/null +++ b/dash-spv-ffi/FFI_API.md @@ -0,0 +1,910 @@ +# Dash SPV FFI API Documentation + +This document provides a comprehensive reference for all FFI (Foreign Function Interface) functions available in the dash-spv-ffi library. + +**Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. + +**Total Functions**: 58 + +## Table of Contents + +- [Client Management](#client-management) +- [Configuration](#configuration) +- [Synchronization](#synchronization) +- [Address Monitoring](#address-monitoring) +- [Transaction Management](#transaction-management) +- [Mempool Operations](#mempool-operations) +- [Platform Integration](#platform-integration) +- [Event Callbacks](#event-callbacks) +- [Error Handling](#error-handling) +- [Utility Functions](#utility-functions) + +## Function Reference + +### Client Management + +Functions: 4 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_client_destroy` | No description | client | +| `dash_spv_ffi_client_new` | No description | client | +| `dash_spv_ffi_client_start` | No description | client | +| `dash_spv_ffi_client_stop` | No description | client | + +### Configuration + +Functions: 23 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_config_add_peer` | No description | config | +| `dash_spv_ffi_config_destroy` | No description | config | +| `dash_spv_ffi_config_get_data_dir` | No description | config | +| `dash_spv_ffi_config_get_mempool_strategy` | No description | config | +| `dash_spv_ffi_config_get_mempool_tracking` | No description | config | +| `dash_spv_ffi_config_get_network` | No description | config | +| `dash_spv_ffi_config_mainnet` | No description | config | +| `dash_spv_ffi_config_new` | No description | config | +| `dash_spv_ffi_config_set_data_dir` | No description | config | +| `dash_spv_ffi_config_set_fetch_mempool_transactions` | No description | config | +| `dash_spv_ffi_config_set_filter_load` | No description | config | +| `dash_spv_ffi_config_set_max_mempool_transactions` | No description | config | +| `dash_spv_ffi_config_set_max_peers` | No description | config | +| `dash_spv_ffi_config_set_mempool_strategy` | No description | config | +| `dash_spv_ffi_config_set_mempool_timeout` | No description | config | +| `dash_spv_ffi_config_set_mempool_tracking` | No description | config | +| `dash_spv_ffi_config_set_persist_mempool` | No description | config | +| `dash_spv_ffi_config_set_relay_transactions` | No description | config | +| `dash_spv_ffi_config_set_start_from_height` | No description | config | +| `dash_spv_ffi_config_set_user_agent` | No description | config | +| `dash_spv_ffi_config_set_validation_mode` | No description | config | +| `dash_spv_ffi_config_set_wallet_creation_time` | No description | config | +| `dash_spv_ffi_config_testnet` | No description | config | + +### Synchronization + +Functions: 7 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_client_cancel_sync` | Cancels the sync operation | client | +| `dash_spv_ffi_client_get_sync_progress` | No description | client | +| `dash_spv_ffi_client_is_filter_sync_available` | No description | client | +| `dash_spv_ffi_client_sync_to_tip` | Sync the SPV client to the chain tip | 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`: P... | client | +| `dash_spv_ffi_sync_progress_destroy` | No description | client | + +### Address Monitoring + +Functions: 1 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_unconfirmed_transaction_destroy_addresses` | Destroys the addresses array allocated for an FFIUnconfirmedTransaction # Sa... | types | + +### Transaction Management + +Functions: 3 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_client_broadcast_transaction` | No description | broadcast | +| `dash_spv_ffi_unconfirmed_transaction_destroy` | Destroys an FFIUnconfirmedTransaction and all its associated resources # Saf... | types | +| `dash_spv_ffi_unconfirmed_transaction_destroy_raw_tx` | Destroys the raw transaction bytes allocated for an FFIUnconfirmedTransaction... | types | + +### Mempool Operations + +Functions: 1 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_client_enable_mempool_tracking` | No description | client | + +### Platform Integration + +Functions: 4 + +| 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 funct... | platform_integration | +| `ffi_dash_spv_get_quorum_public_key` | Gets a quorum public key from the Core chain # Safety This function is unsa... | platform_integration | +| `ffi_dash_spv_release_core_handle` | Releases a CoreSDKHandle # Safety This function is unsafe because: - The ca... | platform_integration | + +### Event Callbacks + +Functions: 1 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_client_set_event_callbacks` | No description | client | + +### Error Handling + +Functions: 2 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_clear_error` | No description | error | +| `dash_spv_ffi_get_last_error` | No description | error | + +### Utility Functions + +Functions: 12 + +| Function | Description | Module | +|----------|-------------|--------| +| `dash_spv_ffi_array_destroy` | No description | types | +| `dash_spv_ffi_client_get_stats` | No description | client | +| `dash_spv_ffi_client_get_wallet_manager` | Get the wallet manager from the SPV client Returns an opaque pointer to FFIW... | client | +| `dash_spv_ffi_client_record_send` | No description | client | +| `dash_spv_ffi_client_rescan_blockchain` | No description | client | +| `dash_spv_ffi_enable_test_mode` | No description | utils | +| `dash_spv_ffi_get_network_name` | No description | utils | +| `dash_spv_ffi_init_logging` | No description | utils | +| `dash_spv_ffi_spv_stats_destroy` | No description | client | +| `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 | + +## Detailed Function Documentation + +### Client Management - Detailed + +#### `dash_spv_ffi_client_destroy` + +```c +dash_spv_ffi_client_destroy(client: *mut FFIDashSpvClient) -> () +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_new` + +```c +dash_spv_ffi_client_new(config: *const FFIClientConfig,) -> *mut FFIDashSpvClient +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_start` + +```c +dash_spv_ffi_client_start(client: *mut FFIDashSpvClient) -> i32 +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_stop` + +```c +dash_spv_ffi_client_stop(client: *mut FFIDashSpvClient) -> i32 +``` + +**Module:** `client` + +--- + +### Configuration - Detailed + +#### `dash_spv_ffi_config_add_peer` + +```c +dash_spv_ffi_config_add_peer(config: *mut FFIClientConfig, addr: *const c_char,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_destroy` + +```c +dash_spv_ffi_config_destroy(config: *mut FFIClientConfig) -> () +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_get_data_dir` + +```c +dash_spv_ffi_config_get_data_dir(config: *const FFIClientConfig,) -> FFIString +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_get_mempool_strategy` + +```c +dash_spv_ffi_config_get_mempool_strategy(config: *const FFIClientConfig,) -> FFIMempoolStrategy +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_get_mempool_tracking` + +```c +dash_spv_ffi_config_get_mempool_tracking(config: *const FFIClientConfig,) -> bool +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_get_network` + +```c +dash_spv_ffi_config_get_network(config: *const FFIClientConfig,) -> FFINetwork +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_mainnet` + +```c +dash_spv_ffi_config_mainnet() -> *mut FFIClientConfig +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_new` + +```c +dash_spv_ffi_config_new(network: FFINetwork) -> *mut FFIClientConfig +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_data_dir` + +```c +dash_spv_ffi_config_set_data_dir(config: *mut FFIClientConfig, path: *const c_char,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_fetch_mempool_transactions` + +```c +dash_spv_ffi_config_set_fetch_mempool_transactions(config: *mut FFIClientConfig, fetch: bool,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_filter_load` + +```c +dash_spv_ffi_config_set_filter_load(config: *mut FFIClientConfig, load_filters: bool,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_max_mempool_transactions` + +```c +dash_spv_ffi_config_set_max_mempool_transactions(config: *mut FFIClientConfig, max_transactions: u32,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_max_peers` + +```c +dash_spv_ffi_config_set_max_peers(config: *mut FFIClientConfig, max_peers: u32,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_mempool_strategy` + +```c +dash_spv_ffi_config_set_mempool_strategy(config: *mut FFIClientConfig, strategy: FFIMempoolStrategy,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_mempool_timeout` + +```c +dash_spv_ffi_config_set_mempool_timeout(config: *mut FFIClientConfig, timeout_secs: u64,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_mempool_tracking` + +```c +dash_spv_ffi_config_set_mempool_tracking(config: *mut FFIClientConfig, enable: bool,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_persist_mempool` + +```c +dash_spv_ffi_config_set_persist_mempool(config: *mut FFIClientConfig, persist: bool,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_relay_transactions` + +```c +dash_spv_ffi_config_set_relay_transactions(config: *mut FFIClientConfig, _relay: bool,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_start_from_height` + +```c +dash_spv_ffi_config_set_start_from_height(config: *mut FFIClientConfig, height: u32,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_user_agent` + +```c +dash_spv_ffi_config_set_user_agent(config: *mut FFIClientConfig, user_agent: *const c_char,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_validation_mode` + +```c +dash_spv_ffi_config_set_validation_mode(config: *mut FFIClientConfig, mode: FFIValidationMode,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_set_wallet_creation_time` + +```c +dash_spv_ffi_config_set_wallet_creation_time(config: *mut FFIClientConfig, timestamp: u32,) -> i32 +``` + +**Module:** `config` + +--- + +#### `dash_spv_ffi_config_testnet` + +```c +dash_spv_ffi_config_testnet() -> *mut FFIClientConfig +``` + +**Module:** `config` + +--- + +### Synchronization - Detailed + +#### `dash_spv_ffi_client_cancel_sync` + +```c +dash_spv_ffi_client_cancel_sync(client: *mut FFIDashSpvClient) -> i32 +``` + +**Description:** +Cancels the sync operation. **Note**: This function currently only stops the SPV client and clears sync callbacks, but does not fully abort the ongoing sync process. The sync operation may continue running in the background until it completes naturally. Full sync cancellation with proper task abortion is not yet implemented. # Safety The client pointer must be valid and non-null. # Returns Returns 0 on success, or an error code on failure. + +**Safety:** +The client pointer must be valid and non-null. + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_get_sync_progress` + +```c +dash_spv_ffi_client_get_sync_progress(client: *mut FFIDashSpvClient,) -> *mut FFISyncProgress +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_is_filter_sync_available` + +```c +dash_spv_ffi_client_is_filter_sync_available(client: *mut FFIDashSpvClient,) -> bool +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_sync_to_tip` + +```c +dash_spv_ffi_client_sync_to_tip(client: *mut FFIDashSpvClient, completion_callback: Option () +``` + +**Description:** +Sync the SPV client to the chain tip. # Safety This function is unsafe because: - `client` must be a valid pointer to an initialized `FFIDashSpvClient` - `user_data` must satisfy thread safety requirements: - If non-null, it must point to data that is safe to access from multiple threads - The caller must ensure proper synchronization if the data is mutable - The data must remain valid for the entire duration of the sync operation - `completion_callback` must be thread-safe and can be called from any thread # Parameters - `client`: Pointer to the SPV client - `completion_callback`: Optional callback invoked on completion - `user_data`: Optional user data pointer passed to callbacks # Returns 0 on success, error code on failure + +**Safety:** +This function is unsafe because: - `client` must be a valid pointer to an initialized `FFIDashSpvClient` - `user_data` must satisfy thread safety requirements: - If non-null, it must point to data that is safe to access from multiple threads - The caller must ensure proper synchronization if the data is mutable - The data must remain valid for the entire duration of the sync operation - `completion_callback` must be thread-safe and can be called from any thread + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_sync_to_tip_with_progress` + +```c +dash_spv_ffi_client_sync_to_tip_with_progress(client: *mut FFIDashSpvClient, progress_callback: Option () +``` + +**Description:** +Sync the SPV client to the chain tip with detailed progress updates. # Safety This function is unsafe because: - `client` must be a valid pointer to an initialized `FFIDashSpvClient` - `user_data` must satisfy thread safety requirements: - If non-null, it must point to data that is safe to access from multiple threads - The caller must ensure proper synchronization if the data is mutable - The data must remain valid for the entire duration of the sync operation - Both `progress_callback` and `completion_callback` must be thread-safe and can be called from any thread # Parameters - `client`: Pointer to the SPV client - `progress_callback`: Optional callback invoked periodically with sync progress - `completion_callback`: Optional callback invoked on completion - `user_data`: Optional user data pointer passed to all callbacks # Returns 0 on success, error code on failure + +**Safety:** +This function is unsafe because: - `client` must be a valid pointer to an initialized `FFIDashSpvClient` - `user_data` must satisfy thread safety requirements: - If non-null, it must point to data that is safe to access from multiple threads - The caller must ensure proper synchronization if the data is mutable - The data must remain valid for the entire duration of the sync operation - Both `progress_callback` and `completion_callback` must be thread-safe and can be called from any thread + +**Module:** `client` + +--- + +#### `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 +dash_spv_ffi_sync_progress_destroy(progress: *mut FFISyncProgress) -> () +``` + +**Module:** `client` + +--- + +### 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 +``` + +**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` + +--- + +### Mempool Operations - Detailed + +#### `dash_spv_ffi_client_enable_mempool_tracking` + +```c +dash_spv_ffi_client_enable_mempool_tracking(client: *mut FFIDashSpvClient, strategy: FFIMempoolStrategy,) -> i32 +``` + +**Module:** `client` + +--- + +### 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 +ffi_dash_spv_get_platform_activation_height(client: *mut FFIDashSpvClient, out_height: *mut u32,) -> FFIResult +``` + +**Description:** +Gets the platform activation height from the Core chain # Safety This function is unsafe because: - The caller must ensure all pointers are valid - out_height must point to a valid u32 + +**Safety:** +This function is unsafe because: - The caller must ensure all pointers are valid - out_height must point to a valid u32 + +**Module:** `platform_integration` + +--- + +#### `ffi_dash_spv_get_quorum_public_key` + +```c +ffi_dash_spv_get_quorum_public_key(client: *mut FFIDashSpvClient, quorum_type: u32, quorum_hash: *const u8, core_chain_locked_height: u32, out_pubkey: *mut u8, out_pubkey_size: usize,) -> FFIResult +``` + +**Description:** +Gets a quorum public key from the Core chain # Safety This function is unsafe because: - The caller must ensure all pointers are valid - quorum_hash must point to a 32-byte array - out_pubkey must point to a buffer of at least out_pubkey_size bytes - out_pubkey_size must be at least 48 bytes + +**Safety:** +This function is unsafe because: - The caller must ensure all pointers are valid - quorum_hash must point to a 32-byte array - out_pubkey must point to a buffer of at least out_pubkey_size bytes - out_pubkey_size must be at least 48 bytes + +**Module:** `platform_integration` + +--- + +#### `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_set_event_callbacks` + +```c +dash_spv_ffi_client_set_event_callbacks(client: *mut FFIDashSpvClient, callbacks: FFIEventCallbacks,) -> i32 +``` + +**Module:** `client` + +--- + +### Error Handling - Detailed + +#### `dash_spv_ffi_clear_error` + +```c +dash_spv_ffi_clear_error() -> () +``` + +**Module:** `error` + +--- + +#### `dash_spv_ffi_get_last_error` + +```c +dash_spv_ffi_get_last_error() -> *const c_char +``` + +**Module:** `error` + +--- + +### Utility Functions - Detailed + +#### `dash_spv_ffi_array_destroy` + +```c +dash_spv_ffi_array_destroy(arr: *mut FFIArray) -> () +``` + +**Module:** `types` + +--- + +#### `dash_spv_ffi_client_get_stats` + +```c +dash_spv_ffi_client_get_stats(client: *mut FFIDashSpvClient,) -> *mut FFISpvStats +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_get_wallet_manager` + +```c +dash_spv_ffi_client_get_wallet_manager(client: *mut FFIDashSpvClient,) -> *mut c_void +``` + +**Description:** +Get the wallet manager from the SPV client Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. This allows direct interaction with the wallet manager without going through the client. # Safety The caller must ensure that: - The client pointer is valid - The returned pointer is freed using wallet_manager_free() # Returns An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. Swift should treat this as an OpaquePointer. + +**Safety:** +The caller must ensure that: - The client pointer is valid - The returned pointer is freed using wallet_manager_free() + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_record_send` + +```c +dash_spv_ffi_client_record_send(client: *mut FFIDashSpvClient, txid: *const c_char,) -> i32 +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_client_rescan_blockchain` + +```c +dash_spv_ffi_client_rescan_blockchain(client: *mut FFIDashSpvClient, _from_height: u32,) -> i32 +``` + +**Module:** `client` + +--- + +#### `dash_spv_ffi_enable_test_mode` + +```c +dash_spv_ffi_enable_test_mode() -> () +``` + +**Module:** `utils` + +--- + +#### `dash_spv_ffi_get_network_name` + +```c +dash_spv_ffi_get_network_name(network: crate::FFINetwork) -> *const c_char +``` + +**Module:** `utils` + +--- + +#### `dash_spv_ffi_init_logging` + +```c +dash_spv_ffi_init_logging(level: *const c_char) -> i32 +``` + +**Module:** `utils` + +--- + +#### `dash_spv_ffi_spv_stats_destroy` + +```c +dash_spv_ffi_spv_stats_destroy(stats: *mut FFISpvStats) -> () +``` + +**Module:** `client` + +--- + +#### `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 +dash_spv_ffi_version() -> *const c_char +``` + +**Module:** `utils` + +--- + +## Type Definitions + +### Core Types + +- `FFIDashSpvClient` - SPV client handle +- `FFIClientConfig` - Client configuration +- `FFISyncProgress` - Synchronization progress +- `FFIDetailedSyncProgress` - Detailed sync progress +- `FFISpvStats` - SPV statistics +- `FFITransaction` - Transaction information +- `FFIUnconfirmedTransaction` - Unconfirmed transaction +- `FFIEventCallbacks` - Event callback structure +- `CoreSDKHandle` - Platform SDK integration handle + +### Enumerations + +- `FFINetwork` - Network type (Dash, Testnet, Regtest, Devnet) +- `FFIValidationMode` - Validation mode (None, Basic, Full) +- `FFIMempoolStrategy` - Mempool strategy (FetchAll, BloomFilter, Selective) +- `FFISyncStage` - Synchronization stage + +## Memory Management + +### Important Rules + +1. **Ownership Transfer**: Functions returning pointers transfer ownership to the caller +2. **Cleanup Required**: All returned pointers must be freed using the appropriate `_destroy` function +3. **Thread Safety**: The SPV client is thread-safe +4. **Error Handling**: Check return codes and use `dash_spv_ffi_get_last_error()` for details +5. **Opaque Pointers**: `dash_spv_ffi_client_get_wallet_manager()` returns `void*` for Swift compatibility + +## Usage Examples + +### Basic SPV Client Usage + +```c +// Create configuration +FFIClientConfig* config = dash_spv_ffi_config_testnet(); + +// Create client +FFIDashSpvClient* client = dash_spv_ffi_client_new(config); + +// Start the client +int32_t result = dash_spv_ffi_client_start(client); +if (result != 0) { + const char* error = dash_spv_ffi_get_last_error(); + // Handle error +} + +// Sync to chain tip +dash_spv_ffi_client_sync_to_tip(client, NULL, NULL); + +// Get wallet manager (returns void* for Swift) +void* wallet_manager = dash_spv_ffi_client_get_wallet_manager(client); + +// Clean up +dash_spv_ffi_client_destroy(client); +dash_spv_ffi_config_destroy(config); +``` + +### Event Callbacks + +```c +void on_block(uint32_t height, const uint8_t (*hash)[32], void* user_data) { + printf("New block at height %u\n", height); +} + +void on_transaction(const uint8_t (*txid)[32], bool confirmed, + int64_t amount, const char* addresses, + uint32_t block_height, void* user_data) { + printf("Transaction: %lld duffs\n", amount); +} + +// Set up callbacks +FFIEventCallbacks callbacks = { + .on_block = on_block, + .on_transaction = on_transaction, + .user_data = NULL +}; + +dash_spv_ffi_client_set_event_callbacks(client, callbacks); +``` diff --git a/dash-spv-ffi/FFI_DOCS_README.md b/dash-spv-ffi/FFI_DOCS_README.md new file mode 100644 index 000000000..058561b88 --- /dev/null +++ b/dash-spv-ffi/FFI_DOCS_README.md @@ -0,0 +1,109 @@ +# FFI Documentation Guide + +## Overview + +The `FFI_API.md` file contains comprehensive documentation for all FFI functions in the dash-spv-ffi library. This documentation is automatically generated from the source code to ensure it stays up-to-date. + +## Keeping Documentation Updated + +### Automatic Verification + +A GitHub Action automatically verifies that the FFI documentation is up-to-date on every push and pull request. If the documentation is out of sync, the CI will fail and provide instructions on how to update it. + +### Manual Updates + +To update the FFI documentation after making changes to FFI functions: + +```bash +# Using Make +make update-docs + +# Or directly with Python +cd dash-spv-ffi +python3 scripts/generate_ffi_docs.py +``` + +### Checking Documentation + +To verify the documentation is current without updating: + +```bash +# Using Make +make check-docs + +# Or directly with the script +bash scripts/check_ffi_docs.sh +``` + +## Documentation Structure + +The `FFI_API.md` file includes: + +1. **Table of Contents** - Quick navigation to different sections +2. **Function Reference** - Categorized list of all functions +3. **Detailed Documentation** - Full signatures and descriptions +4. **Type Definitions** - Core FFI types used +5. **Memory Management** - Important rules for FFI usage +6. **Usage Examples** - Sample code for common operations + +## Categories + +Functions are automatically categorized into: + +- Client Management +- Configuration +- Synchronization +- Wallet Operations +- Address Monitoring +- Transaction Management +- Balance & UTXOs +- Mempool Operations +- Platform Integration +- Event Callbacks +- Error Handling +- Utility Functions + +## Adding New FFI Functions + +When adding new FFI functions: + +1. Add the function with `#[no_mangle]` and `extern "C"` attributes +2. Include doc comments with `///` +3. Add safety documentation if the function is `unsafe` +4. Run `make update-docs` to regenerate documentation +5. Commit both the code changes and updated `FFI_API.md` + +## Example FFI Function + +```rust +/// Sync the SPV client to the chain tip +/// +/// # Safety +/// +/// - `client` must be a valid pointer to an FFIDashSpvClient +/// - `on_progress` callback may be invoked from any thread +/// - `on_completion` will be called exactly once +#[no_mangle] +pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip( + client: *mut FFIDashSpvClient, + on_progress: Option, + on_completion: Option, +) -> i32 { + // Implementation +} +``` + +## CI/CD Integration + +The documentation verification is integrated into the CI pipeline: + +1. **On Push/PR**: Verifies documentation is up-to-date +2. **On Failure**: Comments on PR with update instructions +3. **Required Check**: Must pass before merging + +## Tools + +- `scripts/generate_ffi_docs.py` - Python script that parses Rust files and generates documentation +- `scripts/check_ffi_docs.sh` - Bash script to verify documentation is current +- `.github/workflows/verify-ffi-docs.yml` - GitHub Action for CI verification +- `Makefile` - Convenient commands for documentation tasks \ No newline at end of file diff --git a/dash-spv-ffi/Makefile b/dash-spv-ffi/Makefile new file mode 100644 index 000000000..3c3fb4915 --- /dev/null +++ b/dash-spv-ffi/Makefile @@ -0,0 +1,60 @@ +.PHONY: all build test clean docs check-docs update-docs + +# Default target +all: build + +# Build the library +build: + cargo build --release + +# Run tests +test: + cargo test + +# Clean build artifacts +clean: + cargo clean + rm -f include/dash_spv_ffi.h + +# Generate C header +header: + cargo build --release + @echo "Generated header at include/dash_spv_ffi.h" + +# Update FFI documentation +update-docs: + @echo "Generating FFI API documentation..." + @python3 scripts/generate_ffi_docs.py + @echo "Documentation updated at FFI_API.md" + +# Check if FFI documentation is up to date +check-docs: + @bash scripts/check_ffi_docs.sh + +# Generate all documentation +docs: header update-docs + @echo "All documentation generated" + +# Build for iOS +ios: + cargo build --release --target aarch64-apple-ios + cargo build --release --target aarch64-apple-ios-sim + cargo build --release --target x86_64-apple-ios + +# Full build with documentation +full: clean build header update-docs + @echo "Full build complete with documentation" + +# Help +help: + @echo "Available targets:" + @echo " make build - Build the library" + @echo " make test - Run tests" + @echo " make clean - Clean build artifacts" + @echo " make header - Generate C header" + @echo " make update-docs - Update FFI API documentation" + @echo " make check-docs - Check if FFI docs are up to date" + @echo " make docs - Generate all documentation" + @echo " make ios - Build for iOS platforms" + @echo " make full - Full build with documentation" + @echo " make help - Show this help message" \ No newline at end of file diff --git a/dash-spv-ffi/cbindgen.toml b/dash-spv-ffi/cbindgen.toml index 5f2dddd6b..8e8e4a8f3 100644 --- a/dash-spv-ffi/cbindgen.toml +++ b/dash-spv-ffi/cbindgen.toml @@ -17,7 +17,6 @@ prefix = "dash_spv_ffi_" "FFINetwork" = "DashSpvNetwork" "FFIValidationMode" = "DashSpvValidationMode" "FFIErrorCode" = "DashSpvErrorCode" -"FFIWatchItemType" = "DashSpvWatchItemType" [fn] prefix = "" diff --git a/dash-spv-ffi/examples/wallet_manager_usage.rs b/dash-spv-ffi/examples/wallet_manager_usage.rs new file mode 100644 index 000000000..1ddb4b9b9 --- /dev/null +++ b/dash-spv-ffi/examples/wallet_manager_usage.rs @@ -0,0 +1,80 @@ +/// Example demonstrating the simplified FFIWalletManager usage +/// +/// The refactored design removes unnecessary indirection by: +/// 1. FFIWalletManager directly contains Arc> +/// 2. No longer requires going through the client for each operation +/// 3. Cleaner and more efficient access to wallet functionality + +use dash_spv_ffi::*; +use key_wallet_ffi::{ + wallet_manager_wallet_count, + wallet_manager_free, + FFIError, +}; + +fn main() { + unsafe { + // Create a config for testnet + let config = dash_spv_ffi_config_testnet(); + if config.is_null() { + panic!("Failed to create config"); + } + + // Create an SPV client + let client = dash_spv_ffi_client_new(config); + if client.is_null() { + panic!("Failed to create client"); + } + + // Get the wallet manager - now returns void* for Swift compatibility + // This contains a cloned Arc to the wallet manager, allowing + // direct interaction without going through the client + let wallet_manager_ptr = dash_spv_ffi_client_get_wallet_manager(client); + if wallet_manager_ptr.is_null() { + panic!("Failed to get wallet manager"); + } + // Cast back to FFIWalletManager for use + let wallet_manager = wallet_manager_ptr as *mut key_wallet_ffi::FFIWalletManager; + + // Now we can use the wallet manager directly + // No need to go through client -> inner -> spv_client -> wallet() + + // Get the number of wallets (should be 0 initially) + let mut error = std::mem::zeroed::(); + let wallet_count = wallet_manager_wallet_count(wallet_manager, &mut error); + println!("Number of wallets: {}", wallet_count); + + // Note: To get total balance, you would need to iterate through wallets + // For now, just show the wallet count + println!("Currently managing {} wallets", wallet_count); + + // Example of processing a transaction (with mock data) + // In real usage, you would have actual transaction hex + /* + let tx_hex = "01000000..."; // Transaction hex string + let mut error = std::mem::zeroed(); + let affected = wallet_manager_process_transaction( + wallet_manager, + tx_hex.as_ptr() as *const i8, + FFINetworks::Testnet, + 100000, // block height + &mut error + ); + + if affected >= 0 { + println!("Transaction affected {} wallets", affected); + } else { + println!("Failed to process transaction"); + } + */ + + // Clean up + // The wallet manager can now be independently destroyed + // It maintains its own Arc reference to the underlying wallet + wallet_manager_free(wallet_manager); + dash_spv_ffi_client_destroy(client); + dash_spv_ffi_config_destroy(config); + + println!("Example completed successfully!"); + } +} \ No newline at end of file diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index decd0efb9..d358bca08 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -85,25 +85,6 @@ typedef struct FFISpvStats { uint64_t uptime; } FFISpvStats; -/** - * 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 void (*BlockCallback)(uint32_t height, const uint8_t (*hash)[32], void *user_data); typedef void (*TransactionCallback)(const uint8_t (*txid)[32], @@ -155,14 +136,6 @@ typedef struct FFIEventCallbacks { void *user_data; } FFIEventCallbacks; -typedef struct FFITransaction { - struct FFIString txid; - int32_t version; - uint32_t locktime; - uint32_t size; - uint32_t weight; -} FFITransaction; - /** * Handle for Core SDK that can be passed to Platform SDK */ @@ -178,6 +151,25 @@ typedef struct FFIResult { const char *error_message; } FFIResult; +/** + * 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; + /** * FFI-safe representation of an unconfirmed transaction * @@ -314,14 +306,6 @@ struct FFISpvStats *dash_spv_ffi_client_get_stats(struct FFIDashSpvClient *clien bool dash_spv_ffi_client_is_filter_sync_available(struct FFIDashSpvClient *client); -FFIBalance *dash_spv_ffi_client_get_address_balance(struct FFIDashSpvClient *client, - const char *address); - -struct FFIArray *dash_spv_ffi_client_get_utxos(struct FFIDashSpvClient *client); - -struct FFIArray *dash_spv_ffi_client_get_utxos_for_address(struct FFIDashSpvClient *client, - const char *address); - int32_t dash_spv_ffi_client_set_event_callbacks(struct FFIDashSpvClient *client, struct FFIEventCallbacks callbacks); @@ -331,54 +315,32 @@ void dash_spv_ffi_sync_progress_destroy(struct FFISyncProgress *progress); void dash_spv_ffi_spv_stats_destroy(struct FFISpvStats *stats); -int32_t dash_spv_ffi_client_watch_address(struct FFIDashSpvClient *client, const char *address); - -int32_t dash_spv_ffi_client_unwatch_address(struct FFIDashSpvClient *client, const char *address); - -int32_t dash_spv_ffi_client_watch_script(struct FFIDashSpvClient *client, const char *script_hex); - -int32_t dash_spv_ffi_client_unwatch_script(struct FFIDashSpvClient *client, const char *script_hex); - -struct FFIArray *dash_spv_ffi_client_get_address_history(struct FFIDashSpvClient *client, - const char *address); - -struct FFITransaction *dash_spv_ffi_client_get_transaction(struct FFIDashSpvClient *client, - const char *txid); - -int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, - const char *tx_hex); - -struct FFIArray *dash_spv_ffi_client_get_watched_addresses(struct FFIDashSpvClient *client); - -struct FFIArray *dash_spv_ffi_client_get_watched_scripts(struct FFIDashSpvClient *client); - -FFIBalance *dash_spv_ffi_client_get_total_balance(struct FFIDashSpvClient *client); - int32_t dash_spv_ffi_client_rescan_blockchain(struct FFIDashSpvClient *client, uint32_t _from_height); -int32_t dash_spv_ffi_client_get_transaction_confirmations(struct FFIDashSpvClient *client, - const char *txid); - -int32_t dash_spv_ffi_client_is_transaction_confirmed(struct FFIDashSpvClient *client, - const char *txid); - -void dash_spv_ffi_transaction_destroy(struct FFITransaction *tx); - -struct FFIArray *dash_spv_ffi_client_get_address_utxos(struct FFIDashSpvClient *client, - const char *address); - int32_t dash_spv_ffi_client_enable_mempool_tracking(struct FFIDashSpvClient *client, enum FFIMempoolStrategy strategy); -FFIBalance *dash_spv_ffi_client_get_balance_with_mempool(struct FFIDashSpvClient *client); - -int32_t dash_spv_ffi_client_get_mempool_transaction_count(struct FFIDashSpvClient *client); - int32_t dash_spv_ffi_client_record_send(struct FFIDashSpvClient *client, const char *txid); -FFIBalance *dash_spv_ffi_client_get_mempool_balance(struct FFIDashSpvClient *client, - const char *address); +/** + * Get the wallet manager from the SPV client + * + * Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. + * This allows direct interaction with the wallet manager without going through the client. + * + * # Safety + * + * The caller must ensure that: + * - The client pointer is valid + * - The returned pointer is freed using wallet_manager_free() + * + * # Returns + * + * An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. + * Swift should treat this as an OpaquePointer. + */ +void *dash_spv_ffi_client_get_wallet_manager(struct FFIDashSpvClient *client); FFIClientConfig *dash_spv_ffi_config_new(enum FFINetwork network); @@ -544,3 +506,6 @@ const char *dash_spv_ffi_version(void); const char *dash_spv_ffi_get_network_name(enum FFINetwork network); void dash_spv_ffi_enable_test_mode(void); + +int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, + const char *tx_hex); diff --git a/dash-spv-ffi/scripts/check_ffi_docs.sh b/dash-spv-ffi/scripts/check_ffi_docs.sh new file mode 100755 index 000000000..da819b526 --- /dev/null +++ b/dash-spv-ffi/scripts/check_ffi_docs.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Check if FFI documentation is up to date + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +echo "Checking FFI documentation..." + +cd "$PROJECT_DIR" + +# Generate new documentation +python3 scripts/generate_ffi_docs.py > /dev/null 2>&1 + +# Check if there are any changes +if ! git diff --quiet FFI_API.md; then + echo "❌ FFI documentation is out of date!" + echo "" + echo "Please regenerate the documentation by running:" + echo " cd dash-spv-ffi && python3 scripts/generate_ffi_docs.py" + echo "" + echo "Or use the make command:" + echo " make update-docs" + echo "" + exit 1 +else + echo "✅ FFI documentation is up to date" +fi \ No newline at end of file diff --git a/dash-spv-ffi/scripts/generate_ffi_docs.py b/dash-spv-ffi/scripts/generate_ffi_docs.py new file mode 100644 index 000000000..d3a361691 --- /dev/null +++ b/dash-spv-ffi/scripts/generate_ffi_docs.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +""" +Generate FFI API documentation for dash-spv-ffi +""" + +import os +import re +import sys +from pathlib import Path +from dataclasses import dataclass +from typing import List, Optional, Dict +import subprocess + +@dataclass +class FFIFunction: + name: str + signature: str + module: str + doc_comment: Optional[str] = None + safety_comment: Optional[str] = None + params: List[str] = None + return_type: str = None + +def extract_ffi_functions(file_path: Path) -> List[FFIFunction]: + """Extract all #[no_mangle] functions from a Rust file.""" + functions = [] + + with open(file_path, 'r') as f: + content = f.read() + + # Find all #[no_mangle] functions with their documentation + pattern = r'(?:///.*\n)*(?:\s*#\[no_mangle\]\s*\n)(?:pub\s+)?(?:unsafe\s+)?extern\s+"C"\s+fn\s+(\w+)\s*\(((?:[^)]|\n)*)\)\s*(?:->\s*([^{]+))?' + + for match in re.finditer(pattern, content): + full_match = match.group(0) + func_name = match.group(1) + params = match.group(2) + return_type = match.group(3) if match.group(3) else "()" + + # Extract doc comments + doc_lines = [] + for line in full_match.split('\n'): + if line.strip().startswith('///'): + doc_lines.append(line.strip()[3:].strip()) + + # Extract safety comments + safety_comment = None + if '# Safety' in '\n'.join(doc_lines): + safety_start = False + safety_lines = [] + for line in doc_lines: + if '# Safety' in line: + safety_start = True + continue + if safety_start: + if line.startswith('#'): + break + safety_lines.append(line) + safety_comment = ' '.join(safety_lines).strip() + + # Clean up parameters + params_clean = re.sub(r'\s+', ' ', params.strip()) + return_type_clean = return_type.strip() + + module_name = file_path.stem + + functions.append(FFIFunction( + name=func_name, + signature=f"{func_name}({params_clean}) -> {return_type_clean}", + module=module_name, + doc_comment=' '.join(doc_lines) if doc_lines else None, + safety_comment=safety_comment, + params=params_clean, + return_type=return_type_clean + )) + + return functions + +def categorize_functions(functions: List[FFIFunction]) -> Dict[str, List[FFIFunction]]: + """Categorize functions by their module/purpose.""" + categories = { + 'Client Management': [], + 'Configuration': [], + 'Synchronization': [], + 'Wallet Operations': [], + 'Address Monitoring': [], + 'Transaction Management': [], + 'Balance & UTXOs': [], + 'Mempool Operations': [], + 'Platform Integration': [], + 'Event Callbacks': [], + 'Error Handling': [], + 'Utility Functions': [], + } + + for func in functions: + name = func.name.lower() + + if 'client_new' in name or 'client_start' in name or 'client_stop' in name or 'client_destroy' in name: + categories['Client Management'].append(func) + elif 'config' in name: + categories['Configuration'].append(func) + elif 'sync' in name: + categories['Synchronization'].append(func) + elif 'wallet' in name and 'manager' not in name: + categories['Wallet Operations'].append(func) + elif 'watch' in name or 'unwatch' in name or 'address' in name and 'balance' not in name: + categories['Address Monitoring'].append(func) + elif 'transaction' in name or 'broadcast' in name or 'tx' in name: + categories['Transaction Management'].append(func) + elif 'balance' in name or 'utxo' in name: + categories['Balance & UTXOs'].append(func) + elif 'mempool' in name: + categories['Mempool Operations'].append(func) + elif 'platform' in name or 'quorum' in name or 'core_handle' in name: + categories['Platform Integration'].append(func) + elif 'callback' in name or 'event' in name: + categories['Event Callbacks'].append(func) + elif 'error' in name or 'last_error' in name: + categories['Error Handling'].append(func) + else: + categories['Utility Functions'].append(func) + + # Remove empty categories + return {k: v for k, v in categories.items() if v} + +def generate_markdown(functions: List[FFIFunction]) -> str: + """Generate markdown documentation from FFI functions.""" + + categories = categorize_functions(functions) + + md = [] + md.append("# Dash SPV FFI API Documentation") + md.append("") + md.append("This document provides a comprehensive reference for all FFI (Foreign Function Interface) functions available in the dash-spv-ffi library.") + md.append("") + md.append("**Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually.") + md.append("") + md.append(f"**Total Functions**: {len(functions)}") + md.append("") + + # Table of Contents + md.append("## Table of Contents") + md.append("") + for category in categories.keys(): + anchor = category.lower().replace(' ', '-').replace('&', 'and') + md.append(f"- [{category}](#{anchor})") + md.append("") + + # Function Reference + md.append("## Function Reference") + md.append("") + + for category, funcs in categories.items(): + if not funcs: + continue + + anchor = category.lower().replace(' ', '-').replace('&', 'and') + md.append(f"### {category}") + md.append("") + md.append(f"Functions: {len(funcs)}") + md.append("") + + # Create a table for each category + md.append("| Function | Description | Module |") + md.append("|----------|-------------|--------|") + + for func in sorted(funcs, key=lambda f: f.name): + desc = func.doc_comment.split('.')[0] if func.doc_comment else "No description" + desc = desc.replace('|', '\\|') # Escape pipes in description + if len(desc) > 80: + desc = desc[:77] + "..." + md.append(f"| `{func.name}` | {desc} | {func.module} |") + + md.append("") + + # Detailed Function Documentation + md.append("## Detailed Function Documentation") + md.append("") + + for category, funcs in categories.items(): + if not funcs: + continue + + md.append(f"### {category} - Detailed") + md.append("") + + for func in sorted(funcs, key=lambda f: f.name): + md.append(f"#### `{func.name}`") + md.append("") + md.append("```c") + md.append(func.signature) + md.append("```") + md.append("") + + if func.doc_comment: + md.append("**Description:**") + md.append(func.doc_comment) + md.append("") + + if func.safety_comment: + md.append("**Safety:**") + md.append(func.safety_comment) + md.append("") + + md.append(f"**Module:** `{func.module}`") + md.append("") + md.append("---") + md.append("") + + # Type Definitions + md.append("## Type Definitions") + md.append("") + md.append("### Core Types") + md.append("") + md.append("- `FFIDashSpvClient` - SPV client handle") + md.append("- `FFIClientConfig` - Client configuration") + md.append("- `FFISyncProgress` - Synchronization progress") + md.append("- `FFIDetailedSyncProgress` - Detailed sync progress") + md.append("- `FFISpvStats` - SPV statistics") + md.append("- `FFITransaction` - Transaction information") + md.append("- `FFIUnconfirmedTransaction` - Unconfirmed transaction") + md.append("- `FFIEventCallbacks` - Event callback structure") + md.append("- `CoreSDKHandle` - Platform SDK integration handle") + md.append("") + + md.append("### Enumerations") + md.append("") + md.append("- `FFINetwork` - Network type (Dash, Testnet, Regtest, Devnet)") + md.append("- `FFIValidationMode` - Validation mode (None, Basic, Full)") + md.append("- `FFIMempoolStrategy` - Mempool strategy (FetchAll, BloomFilter, Selective)") + md.append("- `FFISyncStage` - Synchronization stage") + md.append("") + + # Memory Management + md.append("## Memory Management") + md.append("") + md.append("### Important Rules") + md.append("") + md.append("1. **Ownership Transfer**: Functions returning pointers transfer ownership to the caller") + md.append("2. **Cleanup Required**: All returned pointers must be freed using the appropriate `_destroy` function") + md.append("3. **Thread Safety**: The SPV client is thread-safe") + md.append("4. **Error Handling**: Check return codes and use `dash_spv_ffi_get_last_error()` for details") + md.append("5. **Opaque Pointers**: `dash_spv_ffi_client_get_wallet_manager()` returns `void*` for Swift compatibility") + md.append("") + + # Usage Examples + md.append("## Usage Examples") + md.append("") + md.append("### Basic SPV Client Usage") + md.append("") + md.append("```c") + md.append("// Create configuration") + md.append("FFIClientConfig* config = dash_spv_ffi_config_testnet();") + md.append("") + md.append("// Create client") + md.append("FFIDashSpvClient* client = dash_spv_ffi_client_new(config);") + md.append("") + md.append("// Start the client") + md.append("int32_t result = dash_spv_ffi_client_start(client);") + md.append("if (result != 0) {") + md.append(" const char* error = dash_spv_ffi_get_last_error();") + md.append(" // Handle error") + md.append("}") + md.append("") + md.append("// Sync to chain tip") + md.append("dash_spv_ffi_client_sync_to_tip(client, NULL, NULL);") + md.append("") + md.append("// Get wallet manager (returns void* for Swift)") + md.append("void* wallet_manager = dash_spv_ffi_client_get_wallet_manager(client);") + md.append("") + md.append("// Clean up") + md.append("dash_spv_ffi_client_destroy(client);") + md.append("dash_spv_ffi_config_destroy(config);") + md.append("```") + md.append("") + + md.append("### Event Callbacks") + md.append("") + md.append("```c") + md.append("void on_block(uint32_t height, const uint8_t (*hash)[32], void* user_data) {") + md.append(" printf(\"New block at height %u\\n\", height);") + md.append("}") + md.append("") + md.append("void on_transaction(const uint8_t (*txid)[32], bool confirmed, ") + md.append(" int64_t amount, const char* addresses, ") + md.append(" uint32_t block_height, void* user_data) {") + md.append(" printf(\"Transaction: %lld duffs\\n\", amount);") + md.append("}") + md.append("") + md.append("// Set up callbacks") + md.append("FFIEventCallbacks callbacks = {") + md.append(" .on_block = on_block,") + md.append(" .on_transaction = on_transaction,") + md.append(" .user_data = NULL") + md.append("};") + md.append("") + md.append("dash_spv_ffi_client_set_event_callbacks(client, callbacks);") + md.append("```") + md.append("") + + return '\n'.join(md) + +def main(): + # Find all Rust source files + src_dir = Path(__file__).parent.parent / "src" + + all_functions = [] + + for rust_file in src_dir.glob("*.rs"): + functions = extract_ffi_functions(rust_file) + all_functions.extend(functions) + + # Generate markdown + markdown = generate_markdown(all_functions) + + # Write to file + output_file = Path(__file__).parent.parent / "FFI_API.md" + with open(output_file, 'w') as f: + f.write(markdown) + + print(f"Generated FFI documentation with {len(all_functions)} functions") + print(f"Output: {output_file}") + + return 0 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs new file mode 100644 index 000000000..ff68369ea --- /dev/null +++ b/dash-spv-ffi/src/broadcast.rs @@ -0,0 +1,59 @@ +use std::ffi::CStr; +use std::os::raw::c_char; +use crate::{null_check, set_last_error, FFIDashSpvClient, FFIErrorCode}; + +#[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 { + let guard = inner.lock().unwrap(); + if let Some(ref _spv_client) = *guard { + // TODO: broadcast_transaction 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 broadcast transaction: {}", e)); + FFIErrorCode::from(e) as i32 + } + } +} \ No newline at end of file diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index cdccffb91..41e2301af 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -1,55 +1,13 @@ use crate::{ - null_check, set_last_error, FFIArray, FFIClientConfig, FFIDetailedSyncProgress, FFIErrorCode, - FFIEventCallbacks, FFIMempoolStrategy, FFISpvStats, FFISyncProgress, FFITransaction, + null_check, set_last_error, FFIClientConfig, FFIDetailedSyncProgress, FFIErrorCode, + FFIEventCallbacks, FFIMempoolStrategy, FFISpvStats, FFISyncProgress, }; // Import wallet types from key-wallet-ffi -use key_wallet_ffi::{FFIBalance, FFIUTXO as FFIUtxo}; - -// Helper function to convert AddressBalance to FFIBalance -fn address_balance_to_ffi(balance: dash_spv::types::AddressBalance) -> FFIBalance { - FFIBalance { - confirmed: balance.confirmed.to_sat(), - unconfirmed: balance.unconfirmed.to_sat(), - immature: 0, - total: balance.total().to_sat(), - } -} - -// Helper function to convert key_wallet::Utxo to FFIUtxo -fn wallet_utxo_to_ffi(utxo: key_wallet::Utxo) -> FFIUtxo { - use std::ffi::CString; - - // Convert txid to byte array - let mut txid_bytes = [0u8; 32]; - txid_bytes.copy_from_slice(&utxo.outpoint.txid[..]); +use key_wallet_ffi::FFIWalletManager; - // Create FFI UTXO - let address_str = utxo.address.to_string(); - let script_bytes = utxo.txout.script_pubkey.to_bytes(); - - FFIUtxo { - txid: txid_bytes, - vout: utxo.outpoint.vout, - amount: utxo.txout.value, - address: CString::new(address_str).unwrap_or_default().into_raw(), - script_pubkey: if script_bytes.is_empty() { - std::ptr::null_mut() - } else { - let script_box = script_bytes.into_boxed_slice(); - Box::into_raw(script_box) as *mut u8 - }, - script_len: utxo.txout.script_pubkey.len(), - height: utxo.height, - confirmations: if utxo.is_confirmed { - 1 - } else { - 0 - }, - } -} use dash_spv::types::SyncStage; use dash_spv::DashSpvClient; -use dashcore::{Address, ScriptBuf, Txid}; +use dashcore::Txid; use once_cell::sync::Lazy; use std::collections::HashMap; @@ -148,7 +106,7 @@ pub struct FFIDashSpvClient { Mutex< Option< DashSpvClient< - key_wallet_manager::spv_wallet_manager::SPVWalletManager< + key_wallet_manager::wallet_manager::WalletManager< key_wallet::wallet::managed_wallet_info::ManagedWalletInfo, >, dash_spv::network::MultiPeerNetworkManager, @@ -157,53 +115,13 @@ pub struct FFIDashSpvClient { >, >, >, - runtime: Arc, + pub(crate) runtime: Arc, event_callbacks: Arc>, active_threads: Arc>>>, sync_callbacks: Arc>>, shutdown_signal: Arc, } -/// Validate a script hex string and convert it to ScriptBuf -unsafe fn validate_script_hex(script_hex: *const c_char) -> Result { - let script_str = match CStr::from_ptr(script_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in script: {}", e)); - return Err(FFIErrorCode::InvalidArgument as i32); - } - }; - - // Check for odd-length hex string - if script_str.len() % 2 != 0 { - set_last_error("Hex string must have even length"); - return Err(FFIErrorCode::InvalidArgument as i32); - } - - let script_bytes = match hex::decode(script_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex in script: {}", e)); - return Err(FFIErrorCode::InvalidArgument as i32); - } - }; - - // Check for empty script - if script_bytes.is_empty() { - set_last_error("Script cannot be empty"); - return Err(FFIErrorCode::InvalidArgument as i32); - } - - // Check for minimum script length (scripts should be at least 1 byte) - // But very short scripts (like 2 bytes) might not be meaningful - if script_bytes.len() < 3 { - set_last_error("Script too short to be meaningful"); - return Err(FFIErrorCode::InvalidArgument as i32); - } - - Ok(ScriptBuf::from(script_bytes)) -} - #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_new( config: *const FFIClientConfig, @@ -229,11 +147,9 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new( // Construct concrete implementations for generics let network = dash_spv::network::MultiPeerNetworkManager::new(&client_config).await; let storage = dash_spv::storage::MemoryStorageManager::new().await; - let wallet = key_wallet_manager::spv_wallet_manager::SPVWalletManager::with_base( - key_wallet_manager::wallet_manager::WalletManager::< - key_wallet::wallet::managed_wallet_info::ManagedWalletInfo, - >::new(), - ); + let wallet = key_wallet_manager::wallet_manager::WalletManager::< + key_wallet::wallet::managed_wallet_info::ManagedWalletInfo, + >::new(); let wallet = std::sync::Arc::new(tokio::sync::RwLock::new(wallet)); match (network, storage) { @@ -960,215 +876,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_is_filter_sync_available( }) } -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_address_balance( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> *mut FFIBalance { - null_check!(client, std::ptr::null_mut()); - null_check!(address, std::ptr::null_mut()); - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return std::ptr::null_mut(); - } - }; - - let addr = match Address::from_str(addr_str) { - Ok(a) => a.assume_checked(), - Err(e) => { - set_last_error(&format!("Invalid address: {}", e)); - return std::ptr::null_mut(); - } - }; - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - // Aggregate from wallet UTXOs matching the address - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - let target = addr.to_string(); - let mut confirmed: u64 = 0; - let mut unconfirmed: u64 = 0; - for u in wallet.base.get_all_utxos() { - if u.address.to_string() == target { - if u.is_confirmed || u.is_instantlocked { - confirmed = confirmed.saturating_add(u.txout.value); - } else { - unconfirmed = unconfirmed.saturating_add(u.txout.value); - } - } - } - Ok(dash_spv::types::AddressBalance { - confirmed: dashcore::Amount::from_sat(confirmed), - unconfirmed: dashcore::Amount::from_sat(unconfirmed), - pending: dashcore::Amount::from_sat(0), - pending_instant: dashcore::Amount::from_sat(0), - }) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(balance) => Box::into_raw(Box::new(address_balance_to_ffi(balance))), - Err(e) => { - set_last_error(&e.to_string()); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_utxos( - client: *mut FFIDashSpvClient, -) -> *mut FFIArray { - null_check!( - client, - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1 - })) - ); - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - let utxos = wallet.base.get_all_utxos(); - let ffi_utxos: Vec = - utxos.into_iter().cloned().map(wallet_utxo_to_ffi).collect(); - Ok(FFIArray::new(ffi_utxos)) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(arr) => Box::into_raw(Box::new(arr)), - Err(e) => { - set_last_error(&e.to_string()); - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })) - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_utxos_for_address( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> *mut FFIArray { - null_check!( - client, - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1 - })) - ); - null_check!( - address, - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1 - })) - ); - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })); - } - }; - - let _addr = match Address::from_str(addr_str) { - Ok(a) => a.assume_checked(), - Err(e) => { - set_last_error(&format!("Invalid address: {}", e)); - return Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })); - } - }; - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - let target = _addr.to_string(); - let utxos = wallet.base.get_all_utxos(); - let filtered: Vec = utxos - .into_iter() - .filter(|u| u.address.to_string() == target) - .cloned() - .map(wallet_utxo_to_ffi) - .collect(); - Ok(FFIArray::new(filtered)) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(arr) => Box::into_raw(Box::new(arr)), - Err(e) => { - set_last_error(&e.to_string()); - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })) - } - } -} - #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_set_event_callbacks( client: *mut FFIDashSpvClient, @@ -1253,382 +960,6 @@ pub unsafe extern "C" fn dash_spv_ffi_spv_stats_destroy(stats: *mut FFISpvStats) // Wallet operations -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_watch_address( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> i32 { - null_check!(client); - null_check!(address); - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - match dashcore::Address::::from_str(addr_str) { - Ok(a) => { - let _ = a.assume_checked(); - } - Err(e) => { - set_last_error(&format!("Invalid address: {}", 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 { - let guard = inner.lock().unwrap(); - if let Some(ref _spv_client) = *guard { - // TODO: watch_address 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 watch address: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_unwatch_address( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> i32 { - null_check!(client); - null_check!(address); - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return FFIErrorCode::InvalidArgument as i32; - } - }; - - match dashcore::Address::::from_str(addr_str) { - Ok(a) => { - let _ = a.assume_checked(); - } - Err(e) => { - set_last_error(&format!("Invalid address: {}", 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 { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut _spv_client) = *guard { - // TODO: unwatch_address 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 unwatch address: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_watch_script( - client: *mut FFIDashSpvClient, - script_hex: *const c_char, -) -> i32 { - null_check!(client); - null_check!(script_hex); - - if let Err(error_code) = validate_script_hex(script_hex) { - return error_code; - } - - 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: watch_script 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 watch script: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_unwatch_script( - client: *mut FFIDashSpvClient, - script_hex: *const c_char, -) -> i32 { - null_check!(client); - null_check!(script_hex); - - if let Err(error_code) = validate_script_hex(script_hex) { - return error_code; - } - - 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: unwatch_script 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 unwatch script: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_address_history( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> *mut FFIArray { - if client.is_null() || address.is_null() { - return std::ptr::null_mut(); - } - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })); - } - }; - - let _addr = match Address::from_str(addr_str) { - Ok(a) => a.assume_checked(), - Err(e) => { - set_last_error(&format!("Invalid address: {}", e)); - return Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })); - } - }; - - // Not implemented in dash-spv yet - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })) -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_transaction( - client: *mut FFIDashSpvClient, - txid: *const c_char, -) -> *mut FFITransaction { - null_check!(client, std::ptr::null_mut()); - null_check!(txid, std::ptr::null_mut()); - - 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 std::ptr::null_mut(); - } - }; - - let _txid = match Txid::from_str(txid_str) { - Ok(t) => t, - Err(e) => { - set_last_error(&format!("Invalid txid: {}", e)); - return std::ptr::null_mut(); - } - }; - - // Not implemented in dash-spv yet - std::ptr::null_mut() -} - -#[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 { - let guard = inner.lock().unwrap(); - if let Some(ref _spv_client) = *guard { - // TODO: broadcast_transaction 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 broadcast transaction: {}", e)); - FFIErrorCode::from(e) as i32 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_watched_addresses( - client: *mut FFIDashSpvClient, -) -> *mut FFIArray { - if client.is_null() { - return std::ptr::null_mut(); - } - - // Not implemented in dash-spv yet - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })) -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_watched_scripts( - client: *mut FFIDashSpvClient, -) -> *mut FFIArray { - if client.is_null() { - return std::ptr::null_mut(); - } - - // Not implemented in dash-spv yet - Box::into_raw(Box::new(FFIArray { - data: std::ptr::null_mut(), - len: 0, - capacity: 0, - elem_size: 0, - elem_align: 1, - })) -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_total_balance( - client: *mut FFIDashSpvClient, -) -> *mut FFIBalance { - null_check!(client, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - let total = wallet.base.get_total_balance(); - Ok(FFIBalance { - confirmed: total, - unconfirmed: 0, - immature: 0, - total, - }) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(bal) => Box::into_raw(Box::new(bal)), - Err(e) => { - set_last_error(&format!("Failed to get total balance: {}", e)); - std::ptr::null_mut() - } - } -} - #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( client: *mut FFIDashSpvClient, @@ -1660,48 +991,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( } } -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_transaction_confirmations( - client: *mut FFIDashSpvClient, - txid: *const c_char, -) -> i32 { - null_check!(client, -1); - null_check!(txid, -1); - - // Not implemented in dash-spv yet - -1 -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_is_transaction_confirmed( - client: *mut FFIDashSpvClient, - txid: *const c_char, -) -> i32 { - null_check!(client, 0); - null_check!(txid, 0); - - // Not implemented in dash-spv yet - 0 -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_transaction_destroy(tx: *mut FFITransaction) { - if !tx.is_null() { - let _ = Box::from_raw(tx); - } -} - -// This was already implemented earlier but let me add it for tests that import it directly -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_address_utxos( - client: *mut FFIDashSpvClient, - address: *const c_char, -) -> *mut FFIArray { - if client.is_null() || address.is_null() { - return std::ptr::null_mut(); - } - crate::client::dash_spv_ffi_client_get_utxos_for_address(client, address) -} #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_enable_mempool_tracking( @@ -1735,45 +1024,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_enable_mempool_tracking( } } -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_balance_with_mempool( - client: *mut FFIDashSpvClient, -) -> *mut FFIBalance { - null_check!(client, std::ptr::null_mut()); - - set_last_error("Wallet-wide mempool balance not available in current dash-spv version"); - std::ptr::null_mut() -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_mempool_transaction_count( - client: *mut FFIDashSpvClient, -) -> i32 { - null_check!(client, -1); - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - Ok(spv_client.get_mempool_transaction_count().await as i32) - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(count) => count, - Err(e) => { - set_last_error(&e.to_string()); - -1 - } - } -} - #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_record_send( client: *mut FFIDashSpvClient, @@ -1822,60 +1072,41 @@ pub unsafe extern "C" fn dash_spv_ffi_client_record_send( } } +/// Get the wallet manager from the SPV client +/// +/// Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. +/// This allows direct interaction with the wallet manager without going through the client. +/// +/// # Safety +/// +/// The caller must ensure that: +/// - The client pointer is valid +/// - The returned pointer is freed using wallet_manager_free() +/// +/// # Returns +/// +/// An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. +/// Swift should treat this as an OpaquePointer. #[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_client_get_mempool_balance( +pub unsafe extern "C" fn dash_spv_ffi_client_get_wallet_manager( client: *mut FFIDashSpvClient, - address: *const c_char, -) -> *mut FFIBalance { +) -> *mut c_void { null_check!(client, std::ptr::null_mut()); - null_check!(address, std::ptr::null_mut()); - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - return std::ptr::null_mut(); - } - }; - - let addr = match Address::from_str(addr_str) { - Ok(a) => a.assume_checked(), - Err(e) => { - set_last_error(&format!("Invalid address: {}", e)); - return std::ptr::null_mut(); - } - }; - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result = - client.runtime.block_on(async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - spv_client.get_mempool_balance(&addr).await - } else { - Err(dash_spv::SpvError::Storage(dash_spv::StorageError::NotFound( - "Client not initialized".to_string(), - ))) - } - }); - - match result { - Ok(mempool_balance) => { - // Convert MempoolBalance to FFIBalance - let balance = FFIBalance { - confirmed: 0, // No confirmed balance in mempool - unconfirmed: mempool_balance.pending.to_sat() - + mempool_balance.pending_instant.to_sat(), - immature: 0, - total: mempool_balance.pending.to_sat() + mempool_balance.pending_instant.to_sat(), - }; - Box::into_raw(Box::new(balance)) - } - Err(e) => { - set_last_error(&e.to_string()); - std::ptr::null_mut() - } + + let client = &*client; + let inner = client.inner.lock().unwrap(); + + if let Some(ref spv_client) = *inner { + // Clone the Arc to the wallet manager + let wallet_arc = spv_client.wallet().clone(); + let runtime = client.runtime.clone(); + + // Create the FFIWalletManager with the cloned Arc + let manager = FFIWalletManager::from_arc(wallet_arc, runtime); + + Box::into_raw(Box::new(manager)) as *mut c_void + } else { + set_last_error("Client not initialized"); + std::ptr::null_mut() } } diff --git a/dash-spv-ffi/src/lib.rs b/dash-spv-ffi/src/lib.rs index f825937ec..87ef6b74b 100644 --- a/dash-spv-ffi/src/lib.rs +++ b/dash-spv-ffi/src/lib.rs @@ -40,3 +40,4 @@ mod test_async_operations; #[cfg(test)] #[path = "../tests/unit/test_memory_management.rs"] mod test_memory_management; +mod broadcast; diff --git a/dash-spv-ffi/src/types.rs b/dash-spv-ffi/src/types.rs index 043b765dd..f4ab41f5d 100644 --- a/dash-spv-ffi/src/types.rs +++ b/dash-spv-ffi/src/types.rs @@ -379,15 +379,6 @@ pub unsafe extern "C" fn dash_spv_ffi_string_array_destroy(arr: *mut FFIArray) { dash_spv_ffi_array_destroy(arr); } -#[repr(C)] -pub struct FFITransaction { - pub txid: FFIString, - pub version: i32, - pub locktime: u32, - pub size: u32, - pub weight: u32, -} - #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FFIMempoolStrategy { diff --git a/dash-spv-ffi/src/wallet.rs b/dash-spv-ffi/src/wallet.rs deleted file mode 100644 index 10aa3da8c..000000000 --- a/dash-spv-ffi/src/wallet.rs +++ /dev/null @@ -1,1717 +0,0 @@ -use crate::client::FFIDashSpvClient; -use crate::types::FFINetwork; -use crate::{null_check, FFIArray}; -use crate::{set_last_error, FFIString}; -use dash_spv::FilterMatch; -use dashcore::{consensus, OutPoint, ScriptBuf, Txid}; -use key_wallet::account::StandardAccountType; -use key_wallet::wallet::initialization::WalletAccountCreationOptions; -use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; -use key_wallet::AccountType; -use key_wallet::Utxo as KWUtxo; -use key_wallet::WalletBalance; -use key_wallet_manager::wallet_interface::WalletInterface; -use key_wallet_manager::wallet_manager::{AccountTypeUsed, WalletId}; -use std::ffi::CStr; -use std::os::raw::c_char; -use std::str::FromStr; - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FFIWatchItemType { - Address = 0, - Script = 1, - Outpoint = 2, -} - -#[repr(C)] -pub struct FFIWatchItem { - pub item_type: FFIWatchItemType, - pub data: FFIString, -} - -impl FFIWatchItem { - pub unsafe fn to_watch_item(&self) -> Result<(), String> { - // Note: This method uses NetworkUnchecked for backward compatibility. - // Consider using to_watch_item_with_network for proper network validation. - let data_str = FFIString::from_ptr(self.data.ptr)?; - - match self.item_type { - FFIWatchItemType::Address => { - let _addr = - dashcore::Address::::from_str(&data_str) - .map_err(|e| format!("Invalid address: {}", e))? - .assume_checked(); - Ok(()) - } - FFIWatchItemType::Script => { - let script_bytes = - hex::decode(&data_str).map_err(|e| format!("Invalid script hex: {}", e))?; - let _script = ScriptBuf::from(script_bytes); - Ok(()) - } - FFIWatchItemType::Outpoint => { - let parts: Vec<&str> = data_str.split(':').collect(); - if parts.len() != 2 { - return Err("Invalid outpoint format (expected txid:vout)".to_string()); - } - let txid: Txid = parts[0].parse().map_err(|e| format!("Invalid txid: {}", e))?; - let vout: u32 = parts[1].parse().map_err(|e| format!("Invalid vout: {}", e))?; - let _ = OutPoint::new(txid, vout); - Ok(()) - } - } - } - - /// Convert FFIWatchItem to WatchItem with network validation - pub unsafe fn to_watch_item_with_network( - &self, - network: dashcore::Network, - ) -> Result<(), String> { - let data_str = FFIString::from_ptr(self.data.ptr)?; - - match self.item_type { - FFIWatchItemType::Address => { - let addr = - dashcore::Address::::from_str(&data_str) - .map_err(|e| format!("Invalid address: {}", e))?; - - // Validate that the address belongs to the expected network - let _checked_addr = addr.require_network(network).map_err(|_| { - format!("Address {} is not valid for network {:?}", data_str, network) - })?; - Ok(()) - } - FFIWatchItemType::Script => { - let script_bytes = - hex::decode(&data_str).map_err(|e| format!("Invalid script hex: {}", e))?; - let _script = ScriptBuf::from(script_bytes); - Ok(()) - } - FFIWatchItemType::Outpoint => { - let _outpoint = OutPoint::from_str(&data_str) - .map_err(|e| format!("Invalid outpoint: {}", e))?; - Ok(()) - } - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct FFIBalance { - pub confirmed: u64, - pub pending: u64, - pub instantlocked: u64, - pub mempool: u64, - pub mempool_instant: u64, - pub total: u64, -} - -// Balance struct removed from dash-spv public API; use AddressBalance conversion below - -impl From for FFIBalance { - fn from(balance: dash_spv::types::AddressBalance) -> Self { - FFIBalance { - confirmed: balance.confirmed.to_sat(), - pending: balance.unconfirmed.to_sat(), - instantlocked: 0, // AddressBalance doesn't have instantlocked - mempool: balance.pending.to_sat(), - mempool_instant: balance.pending_instant.to_sat(), - total: balance.total().to_sat(), - } - } -} - -#[repr(C)] -pub struct FFIUtxo { - pub txid: FFIString, - pub vout: u32, - pub amount: u64, - pub script_pubkey: FFIString, - pub address: FFIString, - pub height: u32, - pub is_coinbase: bool, - pub is_confirmed: bool, - pub is_instantlocked: bool, -} - -impl From for FFIUtxo { - fn from(utxo: KWUtxo) -> Self { - FFIUtxo { - txid: FFIString::new(&utxo.outpoint.txid.to_string()), - vout: utxo.outpoint.vout, - amount: utxo.txout.value, - script_pubkey: FFIString::new(&hex::encode(utxo.txout.script_pubkey.to_bytes())), - address: FFIString::new(&utxo.address.to_string()), - height: utxo.height, - is_coinbase: utxo.is_coinbase, - is_confirmed: utxo.is_confirmed, - is_instantlocked: utxo.is_instantlocked, - } - } -} - -#[repr(C)] -pub struct FFITransactionResult { - pub txid: FFIString, - pub version: i32, - pub locktime: u32, - pub size: u32, - pub weight: u32, - pub fee: u64, - pub confirmation_time: u64, - pub confirmation_height: u32, -} - -/// FFI-safe representation of a TransactionRecord with owned data -/// -/// This structure contains owned data that must be properly cleaned up -/// using dash_spv_ffi_transaction_record_destroy -#[repr(C)] -pub struct FFITransactionRecord { - /// Transaction as hex string - pub transaction_hex: FFIString, - /// Transaction ID as hex string - pub txid: FFIString, - /// Block height (0 if unconfirmed) - pub height: u32, - /// Whether this transaction is confirmed - pub is_confirmed: bool, - /// Block hash as hex string (empty if unconfirmed) - pub block_hash: FFIString, - /// Timestamp - pub timestamp: u64, - /// Net amount for this account (positive = incoming, negative = outgoing) - pub net_amount: i64, - /// Fee paid (0 if not available) - pub fee: u64, - /// Transaction label (empty if no label) - pub label: FFIString, - /// Whether this is our transaction - pub is_ours: bool, -} - -// TransactionResult no longer available from dash-spv; conversion removed - -#[repr(C)] -pub struct FFIBlockResult { - pub hash: FFIString, - pub height: u32, - pub time: u32, - pub tx_count: u32, -} - -// BlockResult no longer available from dash-spv; conversion removed - -#[repr(C)] -pub struct FFIFilterMatch { - pub block_hash: FFIString, - pub height: u32, - pub block_requested: bool, -} - -impl From for FFIFilterMatch { - fn from(filter_match: FilterMatch) -> Self { - FFIFilterMatch { - block_hash: FFIString::new(&filter_match.block_hash.to_string()), - height: filter_match.height, - block_requested: filter_match.block_requested, - } - } -} - -#[repr(C)] -pub struct FFIAddressStats { - pub address: FFIString, - pub utxo_count: u32, - pub total_value: u64, - pub confirmed_value: u64, - pub pending_value: u64, - pub spendable_count: u32, - pub coinbase_count: u32, -} - -// AddressStats no longer available from dash-spv; conversion removed - -impl From for FFIBalance { - fn from(bal: WalletBalance) -> Self { - // Map confirmed/unconfirmed/locked; mempool fields are not tracked here - let confirmed = bal.confirmed; - let unconfirmed = bal.unconfirmed; - // "locked" is not exposed in FFIBalance directly; keep in total implicitly - FFIBalance { - confirmed, - pending: unconfirmed, - instantlocked: 0, - mempool: 0, - mempool_instant: 0, - total: bal.total, - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_watch_item_address( - address: *const c_char, -) -> *mut FFIWatchItem { - if address.is_null() { - set_last_error("Null address pointer"); - return std::ptr::null_mut(); - } - - match CStr::from_ptr(address).to_str() { - Ok(addr_str) => { - let item = FFIWatchItem { - item_type: FFIWatchItemType::Address, - data: FFIString::new(addr_str), - }; - Box::into_raw(Box::new(item)) - } - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in address: {}", e)); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_watch_item_script( - script_hex: *const c_char, -) -> *mut FFIWatchItem { - if script_hex.is_null() { - set_last_error("Null script pointer"); - return std::ptr::null_mut(); - } - - match CStr::from_ptr(script_hex).to_str() { - Ok(script_str) => { - let item = FFIWatchItem { - item_type: FFIWatchItemType::Script, - data: FFIString::new(script_str), - }; - Box::into_raw(Box::new(item)) - } - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in script: {}", e)); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_watch_item_outpoint( - txid: *const c_char, - vout: u32, -) -> *mut FFIWatchItem { - if txid.is_null() { - set_last_error("Null txid pointer"); - return std::ptr::null_mut(); - } - - match CStr::from_ptr(txid).to_str() { - Ok(txid_str) => { - let outpoint_str = format!("{}:{}", txid_str, vout); - let item = FFIWatchItem { - item_type: FFIWatchItemType::Outpoint, - data: FFIString::new(&outpoint_str), - }; - Box::into_raw(Box::new(item)) - } - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in txid: {}", e)); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_watch_item_destroy(item: *mut FFIWatchItem) { - if !item.is_null() { - let item = Box::from_raw(item); - dash_spv_ffi_string_destroy(item.data); - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_balance_destroy(balance: *mut FFIBalance) { - if !balance.is_null() { - let _ = Box::from_raw(balance); - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_utxo_destroy(utxo: *mut FFIUtxo) { - if !utxo.is_null() { - let utxo = Box::from_raw(utxo); - dash_spv_ffi_string_destroy(utxo.txid); - dash_spv_ffi_string_destroy(utxo.script_pubkey); - dash_spv_ffi_string_destroy(utxo.address); - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_transaction_result_destroy(tx: *mut FFITransactionResult) { - if !tx.is_null() { - let tx = Box::from_raw(tx); - dash_spv_ffi_string_destroy(tx.txid); - } -} - -/// Destroy a single FFITransactionRecord and all its owned resources -/// -/// # Safety -/// * `record` must be a valid pointer to an FFITransactionRecord -/// * The pointer must not be used after this function is called -/// * This function should only be called once per FFITransactionRecord -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_transaction_record_destroy( - record: *mut FFITransactionRecord, -) { - if !record.is_null() { - let record = Box::from_raw(record); - dash_spv_ffi_string_destroy(record.transaction_hex); - dash_spv_ffi_string_destroy(record.txid); - dash_spv_ffi_string_destroy(record.block_hash); - dash_spv_ffi_string_destroy(record.label); - } -} - -/// Destroy an array of FFITransactionRecord pointers and their contents. -/// -/// This function: -/// - Iterates the array elements as pointers to FFITransactionRecord and destroys each via dash_spv_ffi_transaction_record_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 a valid pointer to an FFIArray containing FFITransactionRecord pointers -/// * Each element in the array must be a valid FFITransactionRecord pointer -/// * The array elements will be destroyed and should not be used after this call -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_transaction_record_array_destroy(arr: *mut FFIArray) { - if arr.is_null() { - return; - } - - // Destroy each FFITransactionRecord 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 FFITransactionRecord, (*arr).len); - for &record_ptr in slice.iter() { - if !record_ptr.is_null() { - // Take ownership and destroy - dash_spv_ffi_transaction_record_destroy(record_ptr); - } - } - } - - // Free the vector buffer itself - crate::types::dash_spv_ffi_array_destroy(arr); -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_block_result_destroy(block: *mut FFIBlockResult) { - if !block.is_null() { - let block = Box::from_raw(block); - dash_spv_ffi_string_destroy(block.hash); - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_filter_match_destroy(filter_match: *mut FFIFilterMatch) { - if !filter_match.is_null() { - let filter_match = Box::from_raw(filter_match); - dash_spv_ffi_string_destroy(filter_match.block_hash); - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_address_stats_destroy(stats: *mut FFIAddressStats) { - if !stats.is_null() { - let stats = Box::from_raw(stats); - dash_spv_ffi_string_destroy(stats.address); - } -} - -use crate::types::dash_spv_ffi_string_destroy; - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_validate_address( - address: *const c_char, - network: FFINetwork, -) -> i32 { - if address.is_null() { - return 0; - } - - let addr_str = match CStr::from_ptr(address).to_str() { - Ok(s) => s, - Err(_) => return 0, - }; - - // Convert FFI network to dashcore network - let net: dashcore::Network = network.into(); - - // Try to parse the address as unchecked first - match dashcore::Address::::from_str(addr_str) { - Ok(addr_unchecked) => { - // Check if the address is valid for the given network - match addr_unchecked.require_network(net) { - Ok(_) => 1, // Address is valid for the specified network - Err(_) => 0, // Address is for a different network - } - } - Err(_) => 0, - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_monitored_addresses( - client: *mut FFIDashSpvClient, - network: FFINetwork, -) -> *mut crate::FFIArray { - null_check!(client, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet_manager = &spv_client.wallet().read().await.base; - let addresses = wallet_manager.monitored_addresses(network.into()); - - let ffi_strings: Vec<*mut FFIString> = addresses - .into_iter() - .map(|addr| Box::into_raw(Box::new(FFIString::new(&addr.to_string())))) - .collect(); - - Ok(crate::FFIArray::new(ffi_strings)) - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(array) => Box::into_raw(Box::new(array)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_balance( - client: *mut FFIDashSpvClient, - wallet_id_ptr: *const c_char, -) -> *mut crate::FFIBalance { - null_check!(client, std::ptr::null_mut()); - null_check!(wallet_id_ptr, std::ptr::null_mut()); - - // Parse wallet id as 64-char hex string - let wallet_id_hex = match CStr::from_ptr(wallet_id_ptr).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet id: {}", e)); - return std::ptr::null_mut(); - } - }; - - let mut id: [u8; 32] = [0u8; 32]; - let bytes = hex::decode(wallet_id_hex).unwrap_or_default(); - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return std::ptr::null_mut(); - } - id.copy_from_slice(&bytes); - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - match wallet.base.get_wallet_balance(&id) { - Ok(b) => Ok(crate::FFIBalance::from(b)), - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(bal) => Box::into_raw(Box::new(bal)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_utxos( - client: *mut FFIDashSpvClient, - wallet_id_ptr: *const c_char, -) -> *mut FFIArray { - null_check!(client, std::ptr::null_mut()); - null_check!(wallet_id_ptr, std::ptr::null_mut()); - - let wallet_id_hex = match CStr::from_ptr(wallet_id_ptr).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet id: {}", e)); - return std::ptr::null_mut(); - } - }; - - let mut id: [u8; 32] = [0u8; 32]; - let bytes = hex::decode(wallet_id_hex).unwrap_or_default(); - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return std::ptr::null_mut(); - } - id.copy_from_slice(&bytes); - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet = spv_client.wallet().clone(); - let wallet = wallet.read().await; - match wallet.base.wallet_utxos(&id) { - Ok(set) => { - let ffi: Vec = - set.into_iter().cloned().map(crate::FFIUtxo::from).collect(); - Ok(FFIArray::new(ffi)) - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(arr) => Box::into_raw(Box::new(arr)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FFIWalletAccountCreationOptions { - /// Default account creation: Creates account 0 for BIP44, account 0 for CoinJoin, - /// and all special purpose accounts (Identity Registration, Identity Invitation, - /// Provider keys, etc.) - Default = 0, - /// Create only BIP44 accounts (no CoinJoin or special accounts) - BIP44AccountsOnly = 1, - /// Create no accounts at all - useful for tests that want to manually control account creation - None = 2, -} - -impl From for WalletAccountCreationOptions { - fn from(options: FFIWalletAccountCreationOptions) -> Self { - match options { - FFIWalletAccountCreationOptions::Default => WalletAccountCreationOptions::Default, - FFIWalletAccountCreationOptions::BIP44AccountsOnly => { - WalletAccountCreationOptions::BIP44AccountsOnly(Default::default()) - } - FFIWalletAccountCreationOptions::None => WalletAccountCreationOptions::None, - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FFIAccountType { - /// Standard BIP44 account for regular transactions - BIP44 = 0, - /// Standard BIP32 account for regular transactions - BIP32 = 1, - /// CoinJoin account for private transactions - CoinJoin = 2, - /// Identity registration funding - IdentityRegistration = 3, - /// Identity top-up funding - IdentityTopUp = 4, - /// Identity invitation funding - IdentityInvitation = 5, - /// Provider voting keys (DIP-3) - ProviderVotingKeys = 6, - /// Provider owner keys (DIP-3) - ProviderOwnerKeys = 7, - /// Provider operator keys (DIP-3) - ProviderOperatorKeys = 8, - /// Provider platform P2P keys (DIP-3, ED25519) - ProviderPlatformKeys = 9, -} - -impl FFIAccountType { - /// Convert FFI account type to internal AccountType - /// - /// # Arguments - /// * `account_index` - Required for BIP44, BIP32, and CoinJoin account types - /// * `registration_index` - Required for IdentityTopUp account type - pub fn to_account_type( - self, - account_index: Option, - registration_index: Option, - ) -> Result { - use key_wallet::AccountType::*; - - match self { - FFIAccountType::BIP44 => { - let index = account_index.ok_or("Account index required for BIP44 accounts")?; - Ok(Standard { - index, - standard_account_type: StandardAccountType::BIP44Account, - }) - } - FFIAccountType::BIP32 => { - let index = account_index.ok_or("Account index required for BIP32 accounts")?; - Ok(Standard { - index, - standard_account_type: StandardAccountType::BIP32Account, - }) - } - FFIAccountType::CoinJoin => { - let index = account_index.ok_or("Account index required for CoinJoin accounts")?; - Ok(CoinJoin { - index, - }) - } - FFIAccountType::IdentityRegistration => Ok(IdentityRegistration), - FFIAccountType::IdentityTopUp => { - let registration_index = registration_index - .ok_or("Registration index required for IdentityTopUp accounts")?; - Ok(IdentityTopUp { - registration_index, - }) - } - FFIAccountType::IdentityInvitation => Ok(IdentityInvitation), - FFIAccountType::ProviderVotingKeys => Ok(ProviderVotingKeys), - FFIAccountType::ProviderOwnerKeys => Ok(ProviderOwnerKeys), - FFIAccountType::ProviderOperatorKeys => Ok(ProviderOperatorKeys), - FFIAccountType::ProviderPlatformKeys => Ok(ProviderPlatformKeys), - } - } -} - -/// Create a new wallet from mnemonic phrase -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `mnemonic` - The mnemonic phrase as null-terminated C string -/// * `passphrase` - Optional BIP39 passphrase (can be null/empty) -/// * `network` - The network to use -/// * `account_options` - Account creation options -/// * `name` - Wallet name as null-terminated C string -/// * `birth_height` - Optional birth height (can be 0 for none) -/// -/// # Returns -/// * Pointer to FFIString containing hex-encoded WalletId (32 bytes as 64-char hex) -/// * Returns null on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_create_from_mnemonic( - client: *mut FFIDashSpvClient, - mnemonic: *const c_char, - passphrase: *const c_char, - network: FFINetwork, - account_options: FFIWalletAccountCreationOptions, - name: *const c_char, - birth_height: u32, -) -> *mut FFIString { - null_check!(client, std::ptr::null_mut()); - null_check!(mnemonic, std::ptr::null_mut()); - null_check!(name, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let mnemonic_str = match CStr::from_ptr(mnemonic).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in mnemonic: {}", e)); - return std::ptr::null_mut(); - } - }; - - let passphrase_str = if passphrase.is_null() { - "" - } else { - match CStr::from_ptr(passphrase).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in passphrase: {}", e)); - return std::ptr::null_mut(); - } - } - }; - - let name_str = match CStr::from_ptr(name).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in name: {}", e)); - return std::ptr::null_mut(); - } - }; - - let result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - // Generate a random WalletId - let wallet_id = WalletId::from(rand::random::<[u8; 32]>()); - - let network = network.into(); - let account_creation_options: WalletAccountCreationOptions = account_options.into(); - let birth_height_opt = if birth_height == 0 { - None - } else { - Some(birth_height) - }; - - match wallet_manager.create_wallet_from_mnemonic( - wallet_id, - name_str.to_string(), - mnemonic_str, - passphrase_str, - Some(network), - birth_height_opt, - account_creation_options, - ) { - Ok(_) => { - // Convert WalletId to hex string - Ok(hex::encode(wallet_id)) - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(wallet_id_hex) => Box::into_raw(Box::new(FFIString::new(&wallet_id_hex))), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Create a new empty wallet (test wallet with fixed mnemonic) -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `network` - The network to use -/// * `account_options` - Account creation options -/// * `name` - Wallet name as null-terminated C string -/// -/// # Returns -/// * Pointer to FFIString containing hex-encoded WalletId (32 bytes as 64-char hex) -/// * Returns null on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_create( - client: *mut FFIDashSpvClient, - network: FFINetwork, - account_options: FFIWalletAccountCreationOptions, - name: *const c_char, -) -> *mut FFIString { - null_check!(client, std::ptr::null_mut()); - null_check!(name, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let name_str = match CStr::from_ptr(name).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in name: {}", e)); - return std::ptr::null_mut(); - } - }; - - let result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - // Generate a random WalletId - let wallet_id = WalletId::from(rand::random::<[u8; 32]>()); - - let network = network.into(); - let account_creation_options: WalletAccountCreationOptions = account_options.into(); - - match wallet_manager.create_wallet( - wallet_id, - name_str.to_string(), - account_creation_options, - network, - ) { - Ok(_) => { - // Convert WalletId to hex string - Ok(hex::encode(wallet_id)) - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(wallet_id_hex) => Box::into_raw(Box::new(FFIString::new(&wallet_id_hex))), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Get a list of all wallet IDs -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// -/// # Returns -/// * Pointer to FFIArray of FFIString objects containing hex-encoded WalletIds -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_list(client: *mut FFIDashSpvClient) -> *mut FFIArray { - null_check!(client, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let result: Result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet_manager = &spv_client.wallet().read().await.base; - // Return an array of pointers to FFIString to match FFI expectations - let wallet_ids: Vec<*mut FFIString> = wallet_manager - .list_wallets() - .iter() - .map(|id| Box::into_raw(Box::new(FFIString::new(&hex::encode(id))))) - .collect(); - - Ok(FFIArray::new(wallet_ids)) - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(arr) => Box::into_raw(Box::new(arr)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Import a wallet from an extended private key -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `xprv` - The extended private key string (base58check encoded) -/// * `network` - The network to use -/// * `account_options` - Account creation options -/// * `name` - Wallet name as null-terminated C string -/// -/// # Returns -/// * Pointer to FFIString containing hex-encoded WalletId (32 bytes as 64-char hex) -/// * Returns null on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_import_from_xprv( - client: *mut FFIDashSpvClient, - xprv: *const c_char, - network: FFINetwork, - account_options: FFIWalletAccountCreationOptions, - name: *const c_char, -) -> *mut FFIString { - null_check!(client, std::ptr::null_mut()); - null_check!(xprv, std::ptr::null_mut()); - null_check!(name, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let xprv_str = match CStr::from_ptr(xprv).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in xprv: {}", e)); - return std::ptr::null_mut(); - } - }; - - let name_str = match CStr::from_ptr(name).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in name: {}", e)); - return std::ptr::null_mut(); - } - }; - - let result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - // Generate a random WalletId - let wallet_id = WalletId::from(rand::random::<[u8; 32]>()); - - let network = network.into(); - let account_creation_options: WalletAccountCreationOptions = account_options.into(); - - match wallet_manager.import_wallet_from_extended_priv_key( - wallet_id, - name_str.to_string(), - xprv_str, - network, - account_creation_options, - ) { - Ok(_) => { - // Convert WalletId to hex string - Ok(hex::encode(wallet_id)) - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(wallet_id_hex) => Box::into_raw(Box::new(FFIString::new(&wallet_id_hex))), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Import a watch-only wallet from an extended public key -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `xpub` - The extended public key string (base58check encoded) -/// * `network` - The network to use -/// * `name` - Wallet name as null-terminated C string -/// -/// # Returns -/// * Pointer to FFIString containing hex-encoded WalletId (32 bytes as 64-char hex) -/// * Returns null on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_import_from_xpub( - client: *mut FFIDashSpvClient, - xpub: *const c_char, - network: FFINetwork, - name: *const c_char, -) -> *mut FFIString { - null_check!(client, std::ptr::null_mut()); - null_check!(xpub, std::ptr::null_mut()); - null_check!(name, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let xpub_str = match CStr::from_ptr(xpub).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in xpub: {}", e)); - return std::ptr::null_mut(); - } - }; - - let name_str = match CStr::from_ptr(name).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in name: {}", e)); - return std::ptr::null_mut(); - } - }; - - let result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - // Generate a random WalletId - let wallet_id = WalletId::from(rand::random::<[u8; 32]>()); - - let network = network.into(); - - match wallet_manager.import_wallet_from_xpub( - wallet_id, - name_str.to_string(), - xpub_str, - network, - ) { - Ok(_) => { - // Convert WalletId to hex string - Ok(hex::encode(wallet_id)) - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(wallet_id_hex) => Box::into_raw(Box::new(FFIString::new(&wallet_id_hex))), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FFIAccountTypePreference { - /// Use BIP44 account only - BIP44 = 0, - /// Use BIP32 account only - BIP32 = 1, - /// Prefer BIP44, fallback to BIP32 - PreferBIP44 = 2, - /// Prefer BIP32, fallback to BIP44 - PreferBIP32 = 3, -} - -impl From for AccountTypePreference { - fn from(pref: FFIAccountTypePreference) -> Self { - match pref { - FFIAccountTypePreference::BIP44 => AccountTypePreference::BIP44, - FFIAccountTypePreference::BIP32 => AccountTypePreference::BIP32, - FFIAccountTypePreference::PreferBIP44 => AccountTypePreference::PreferBIP44, - FFIAccountTypePreference::PreferBIP32 => AccountTypePreference::PreferBIP32, - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FFIAccountTypeUsed { - /// BIP44 account was used - BIP44 = 0, - /// BIP32 account was used - BIP32 = 1, -} - -#[repr(C)] -pub struct FFIAddressGenerationResult { - pub address: *mut FFIString, - pub account_type_used: FFIAccountTypeUsed, -} - -/// Add a new account to an existing wallet from an extended public key -/// -/// This creates a watch-only account that can monitor addresses and transactions -/// but cannot sign them. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `wallet_id_hex` - Hex-encoded wallet ID (64 characters) -/// * `xpub` - The extended public key string (base58check encoded) -/// * `account_type` - The type of account to create -/// * `network` - The network for the account -/// * `account_index` - Account index (required for BIP44, BIP32, CoinJoin) -/// * `registration_index` - Registration index (required for IdentityTopUp) -/// -/// # Returns -/// * FFIErrorCode::Success on success -/// * FFIErrorCode::InvalidArgument on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_add_account_from_xpub( - client: *mut FFIDashSpvClient, - wallet_id_hex: *const c_char, - xpub: *const c_char, - account_type: FFIAccountType, - network: FFINetwork, - account_index: u32, - registration_index: u32, -) -> i32 { - null_check!(client, crate::FFIErrorCode::InvalidArgument as i32); - null_check!(wallet_id_hex, crate::FFIErrorCode::InvalidArgument as i32); - null_check!(xpub, crate::FFIErrorCode::InvalidArgument as i32); - - let client = &(*client); - let inner = client.inner.clone(); - - let wallet_id_hex_str = match CStr::from_ptr(wallet_id_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet ID: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - let xpub_str = match CStr::from_ptr(xpub).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in xpub: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - // Parse wallet ID - let mut wallet_id: [u8; 32] = [0u8; 32]; - let bytes = match hex::decode(wallet_id_hex_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex wallet ID: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return crate::FFIErrorCode::InvalidArgument as i32; - } - wallet_id.copy_from_slice(&bytes); - - // Convert account type with parameters - let account_index_opt = if matches!( - account_type, - FFIAccountType::BIP44 | FFIAccountType::BIP32 | FFIAccountType::CoinJoin - ) { - Some(account_index) - } else { - None - }; - - let registration_index_opt = if matches!(account_type, FFIAccountType::IdentityTopUp) { - Some(registration_index) - } else { - None - }; - - let account_type_internal = - match account_type.to_account_type(account_index_opt, registration_index_opt) { - Ok(at) => at, - Err(e) => { - set_last_error(&e); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - let result: Result<(), String> = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - // Parse the extended public key - let extended_pub_key = key_wallet::ExtendedPubKey::from_str(xpub_str) - .map_err(|e| format!("Invalid xpub: {}", e))?; - - match wallet_manager.create_account( - &wallet_id, - account_type_internal, - network.into(), - Some(extended_pub_key), - ) { - Ok(()) => Ok(()), - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(()) => crate::FFIErrorCode::Success as i32, - Err(e) => { - set_last_error(&e); - crate::FFIErrorCode::InvalidArgument as i32 - } - } -} - -/// Get wallet-wide mempool balance -/// -/// This returns the total unconfirmed balance (mempool transactions) across all -/// accounts in the specified wallet. This represents the balance from transactions -/// that have been broadcast but not yet confirmed in a block. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `wallet_id_hex` - Hex-encoded wallet ID (64 characters), or null for all wallets -/// -/// # Returns -/// * Total mempool balance in satoshis -/// * Returns 0 if wallet not found or client not initialized (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_mempool_balance( - client: *mut FFIDashSpvClient, - wallet_id_hex: *const c_char, -) -> u64 { - null_check!(client, 0); - - let client = &(*client); - let inner = client.inner.clone(); - - // Parse wallet ID if provided - let wallet_id_opt = if wallet_id_hex.is_null() { - None - } else { - let wallet_id_hex_str = match CStr::from_ptr(wallet_id_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet ID: {}", e)); - return 0; - } - }; - - let mut wallet_id: [u8; 32] = [0u8; 32]; - let bytes = match hex::decode(wallet_id_hex_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex wallet ID: {}", e)); - return 0; - } - }; - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return 0; - } - wallet_id.copy_from_slice(&bytes); - Some(wallet_id) - }; - - let result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet_manager = &spv_client.wallet().read().await.base; - - if let Some(wallet_id) = wallet_id_opt { - // Get mempool balance for specific wallet - match wallet_manager.get_wallet_balance(&wallet_id) { - Ok(balance) => Ok(balance.unconfirmed), - Err(e) => Err(e.to_string()), - } - } else { - // Get total mempool balance across all wallets - let mut total_mempool_balance = 0u64; - for wallet_id in wallet_manager.list_wallets() { - if let Ok(balance) = wallet_manager.get_wallet_balance(wallet_id) { - total_mempool_balance = - total_mempool_balance.saturating_add(balance.unconfirmed); - } - } - Ok(total_mempool_balance) - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(balance) => balance, - Err(e) => { - set_last_error(&e); - 0 - } - } -} - -/// Get wallet-wide mempool transaction count -/// -/// This returns the total number of unconfirmed transactions (in mempool) across all -/// accounts in the specified wallet. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `wallet_id_hex` - Hex-encoded wallet ID (64 characters), or null for all wallets -/// -/// # Returns -/// * Total mempool transaction count -/// * Returns 0 if wallet not found or client not initialized (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_mempool_transaction_count( - client: *mut FFIDashSpvClient, - wallet_id_hex: *const c_char, -) -> u32 { - null_check!(client, 0); - - let client = &(*client); - let inner = client.inner.clone(); - - // Parse wallet ID if provided - let wallet_id_opt = if wallet_id_hex.is_null() { - None - } else { - let wallet_id_hex_str = match CStr::from_ptr(wallet_id_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet ID: {}", e)); - return 0; - } - }; - - let mut wallet_id: [u8; 32] = [0u8; 32]; - let bytes = match hex::decode(wallet_id_hex_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex wallet ID: {}", e)); - return 0; - } - }; - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return 0; - } - wallet_id.copy_from_slice(&bytes); - Some(wallet_id) - }; - - let result = client.run_async(|| async { - let guard = inner.lock().unwrap(); - if let Some(ref spv_client) = *guard { - let wallet_manager = &spv_client.wallet().read().await.base; - - if let Some(wallet_id) = wallet_id_opt { - // Get mempool transaction count for specific wallet - match wallet_manager.wallet_transaction_history(&wallet_id) { - Ok(txs) => { - let mempool_count = - txs.iter().filter(|tx_record| tx_record.height.is_none()).count(); - Ok(mempool_count as u32) - } - Err(e) => Err(e.to_string()), - } - } else { - // Get total mempool transaction count across all wallets - let mut total_mempool_count = 0u32; - for wallet_id in wallet_manager.list_wallets() { - if let Ok(txs) = wallet_manager.wallet_transaction_history(wallet_id) { - let wallet_mempool_count = - txs.iter().filter(|tx_record| tx_record.height.is_none()).count() - as u32; - total_mempool_count = - total_mempool_count.saturating_add(wallet_mempool_count); - } - } - Ok(total_mempool_count) - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(count) => count, - Err(e) => { - set_last_error(&e); - 0 - } - } -} - -/// Record a sent transaction in the wallet -/// -/// This records a transaction that was sent/broadcast by the client, updating the -/// wallet state to reflect the outgoing transaction. The transaction will be tracked -/// in mempool until it's confirmed in a block. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `tx_hex` - Hex-encoded transaction data -/// * `network` - The network for the transaction -/// -/// # Returns -/// * FFIErrorCode::Success on success -/// * FFIErrorCode::InvalidArgument on error (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_record_sent_transaction( - client: *mut FFIDashSpvClient, - tx_hex: *const c_char, - network: FFINetwork, -) -> i32 { - null_check!(client, crate::FFIErrorCode::InvalidArgument as i32); - null_check!(tx_hex, crate::FFIErrorCode::InvalidArgument as i32); - - let client = &(*client); - let inner = client.inner.clone(); - - let tx_hex_str = match CStr::from_ptr(tx_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in transaction hex: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - // Parse transaction from hex - let tx_bytes = match hex::decode(tx_hex_str) { - Ok(bytes) => bytes, - Err(e) => { - set_last_error(&format!("Invalid hex transaction: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - let transaction: dashcore::Transaction = match consensus::deserialize(&tx_bytes) { - Ok(tx) => tx, - Err(e) => { - set_last_error(&format!("Invalid transaction format: {}", e)); - return crate::FFIErrorCode::InvalidArgument as i32; - } - }; - - let result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - // Record the sent transaction by processing it as a mempool transaction - // This will update the wallet state to reflect the outgoing transaction - spv_client - .wallet() - .write() - .await - .process_mempool_transaction(&transaction, network.into()) - .await; - Ok(()) - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(()) => crate::FFIErrorCode::Success as i32, - Err(e) => { - set_last_error(&e); - crate::FFIErrorCode::InvalidArgument as i32 - } - } -} - -/// Get a receive address from a specific wallet and account -/// -/// This generates a new unused receive address (external chain) for the specified -/// wallet and account. The address will be marked as used if mark_as_used is true. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `wallet_id_hex` - Hex-encoded wallet ID (64 characters) -/// * `network` - The network for the address -/// * `account_index` - Account index (0 for first account) -/// * `account_type_pref` - Account type preference (BIP44, BIP32, or preference) -/// * `mark_as_used` - Whether to mark the address as used after generation -/// -/// # Returns -/// * Pointer to FFIAddressGenerationResult containing the address and account type used -/// * Returns null if address generation fails (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_receive_address( - client: *mut FFIDashSpvClient, - wallet_id_hex: *const c_char, - network: FFINetwork, - account_index: u32, - account_type_pref: FFIAccountTypePreference, - mark_as_used: bool, -) -> *mut FFIAddressGenerationResult { - null_check!(client, std::ptr::null_mut()); - null_check!(wallet_id_hex, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let wallet_id_hex_str = match CStr::from_ptr(wallet_id_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet ID: {}", e)); - return std::ptr::null_mut(); - } - }; - - // Parse wallet ID - let mut wallet_id: [u8; 32] = [0u8; 32]; - let bytes = match hex::decode(wallet_id_hex_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex wallet ID: {}", e)); - return std::ptr::null_mut(); - } - }; - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return std::ptr::null_mut(); - } - wallet_id.copy_from_slice(&bytes); - - let result: Result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - match wallet_manager.get_receive_address( - &wallet_id, - network.into(), - account_index, - account_type_pref.into(), - mark_as_used, - ) { - Ok(addr_result) => { - if let (Some(address), Some(account_type_used)) = - (addr_result.address, addr_result.account_type_used) - { - let ffi_account_type = match account_type_used { - AccountTypeUsed::BIP44 => FFIAccountTypeUsed::BIP44, - AccountTypeUsed::BIP32 => FFIAccountTypeUsed::BIP32, - }; - - Ok(FFIAddressGenerationResult { - address: Box::into_raw(Box::new(FFIString::new(&address.to_string()))), - account_type_used: ffi_account_type, - }) - } else { - Err("No address could be generated".to_string()) - } - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(result) => Box::into_raw(Box::new(result)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Get a change address from a specific wallet and account -/// -/// This generates a new unused change address (internal chain) for the specified -/// wallet and account. The address will be marked as used if mark_as_used is true. -/// -/// # Arguments -/// * `client` - Pointer to FFIDashSpvClient -/// * `wallet_id_hex` - Hex-encoded wallet ID (64 characters) -/// * `network` - The network for the address -/// * `account_index` - Account index (0 for first account) -/// * `account_type_pref` - Account type preference (BIP44, BIP32, or preference) -/// * `mark_as_used` - Whether to mark the address as used after generation -/// -/// # Returns -/// * Pointer to FFIAddressGenerationResult containing the address and account type used -/// * Returns null if address generation fails (check last_error) -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_wallet_get_change_address( - client: *mut FFIDashSpvClient, - wallet_id_hex: *const c_char, - network: FFINetwork, - account_index: u32, - account_type_pref: FFIAccountTypePreference, - mark_as_used: bool, -) -> *mut FFIAddressGenerationResult { - null_check!(client, std::ptr::null_mut()); - null_check!(wallet_id_hex, std::ptr::null_mut()); - - let client = &(*client); - let inner = client.inner.clone(); - - let wallet_id_hex_str = match CStr::from_ptr(wallet_id_hex).to_str() { - Ok(s) => s, - Err(e) => { - set_last_error(&format!("Invalid UTF-8 in wallet ID: {}", e)); - return std::ptr::null_mut(); - } - }; - - // Parse wallet ID - let mut wallet_id: [u8; 32] = [0u8; 32]; - let bytes = match hex::decode(wallet_id_hex_str) { - Ok(b) => b, - Err(e) => { - set_last_error(&format!("Invalid hex wallet ID: {}", e)); - return std::ptr::null_mut(); - } - }; - if bytes.len() != 32 { - set_last_error("Wallet ID must be 32 bytes hex"); - return std::ptr::null_mut(); - } - wallet_id.copy_from_slice(&bytes); - - let result: Result = client.run_async(|| async { - let mut guard = inner.lock().unwrap(); - if let Some(ref mut spv_client) = *guard { - let wallet_manager = &mut spv_client.wallet().write().await.base; - - match wallet_manager.get_change_address( - &wallet_id, - network.into(), - account_index, - account_type_pref.into(), - mark_as_used, - ) { - Ok(addr_result) => { - if let (Some(address), Some(account_type_used)) = - (addr_result.address, addr_result.account_type_used) - { - let ffi_account_type = match account_type_used { - AccountTypeUsed::BIP44 => FFIAccountTypeUsed::BIP44, - AccountTypeUsed::BIP32 => FFIAccountTypeUsed::BIP32, - }; - - Ok(FFIAddressGenerationResult { - address: Box::into_raw(Box::new(FFIString::new(&address.to_string()))), - account_type_used: ffi_account_type, - }) - } else { - Err("No address could be generated".to_string()) - } - } - Err(e) => Err(e.to_string()), - } - } else { - Err("Client not initialized".to_string()) - } - }); - - match result { - Ok(result) => Box::into_raw(Box::new(result)), - Err(e) => { - set_last_error(&e); - std::ptr::null_mut() - } - } -} - -/// Free an FFIAddressGenerationResult and its associated resources -/// -/// # Safety -/// * `result` must be a valid pointer to an FFIAddressGenerationResult -/// * The pointer must not be used after this function is called -/// * This function should only be called once per FFIAddressGenerationResult -#[no_mangle] -pub unsafe extern "C" fn dash_spv_ffi_address_generation_result_destroy( - result: *mut FFIAddressGenerationResult, -) { - if !result.is_null() { - let result = Box::from_raw(result); - if !result.address.is_null() { - let addr_ptr = result.address; - // Read the FFIString from the raw pointer and destroy it - let addr_string = unsafe { *Box::from_raw(addr_ptr) }; - dash_spv_ffi_string_destroy(addr_string); - } - } -} diff --git a/dash-spv-ffi/tests/test_client.rs b/dash-spv-ffi/tests/test_client.rs index 72dc98b23..b8ddcb419 100644 --- a/dash-spv-ffi/tests/test_client.rs +++ b/dash-spv-ffi/tests/test_client.rs @@ -140,50 +140,6 @@ mod tests { } } - #[test] - #[serial] - fn test_address_balance() { - unsafe { - let (config, _temp_dir) = create_test_config(); - let client = dash_spv_ffi_client_new(config); - - let addr = CString::new("XjSgy6PaVCB3V4KhCiCDkaVbx9ewxe9R1E").unwrap(); - let balance = dash_spv_ffi_client_get_address_balance(client, addr.as_ptr()); - - if !balance.is_null() { - let balance_ref = &*balance; - assert_eq!( - balance_ref.total, - balance_ref.confirmed + balance_ref.unconfirmed + balance_ref.immature - ); - // FFIBalance from key-wallet-ffi doesn't need explicit destruction - // since it's a simple struct without heap allocations - } - - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } - } - - #[test] - #[serial] - fn test_utxos() { - unsafe { - let (config, _temp_dir) = create_test_config(); - let client = dash_spv_ffi_client_new(config); - - let utxos = dash_spv_ffi_client_get_utxos(client); - // adapt to pointer return - if !utxos.is_null() { - assert_eq!((*utxos).len, 0); - dash_spv_ffi_array_destroy(utxos); - } - - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } - } - #[test] #[serial] #[ignore] diff --git a/dash-spv-ffi/tests/test_event_callbacks.rs b/dash-spv-ffi/tests/test_event_callbacks.rs index d2ed2067d..fb3459c74 100644 --- a/dash-spv-ffi/tests/test_event_callbacks.rs +++ b/dash-spv-ffi/tests/test_event_callbacks.rs @@ -184,13 +184,6 @@ fn test_event_callbacks_setup() { println!("Client started, waiting for events..."); - // Add a test address to watch - let test_address = b"yNDp83M8aHDGNkXPFaVoJZa2D9KparfWDc\0".as_ptr() as *const c_char; - let watch_result = dash_spv_ffi_client_watch_address(client, test_address); - if watch_result != 0 { - println!("Warning: Failed to watch address (may not be implemented)"); - } - // Try to sync for a short time to see if we get any events println!("Starting sync to trigger events..."); let sync_result = dash_spv_ffi_client_test_sync(client); @@ -239,60 +232,6 @@ fn test_event_callbacks_setup() { println!("Test completed - callbacks were set up successfully"); } -#[test] -fn test_get_total_balance() { - unsafe { - dash_spv_ffi_init_logging(b"info\0".as_ptr() as *const c_char); - - // Create config - let config = dash_spv_ffi_config_new(FFINetwork::Testnet); - assert!(!config.is_null()); - - // Create client - let client = dash_spv_ffi_client_new(config); - assert!(!client.is_null()); - - // Start client - let start_result = dash_spv_ffi_client_start(client); - assert_eq!(start_result, 0, "Failed to start client"); - - // Add some test addresses to watch - let addresses = [ - b"yNDp83M8aHDGNkXPFaVoJZa2D9KparfWDc\0".as_ptr() as *const c_char, - b"yP8JPjW4VUbfmtY1KD7zfRyCVVvQQMgZLe\0".as_ptr() as *const c_char, - ]; - - for address in addresses.iter() { - let watch_result = dash_spv_ffi_client_watch_address(client, *address); - if watch_result != 0 { - println!("Warning: Failed to watch address"); - } - } - - // Get total balance - let balance_ptr = dash_spv_ffi_client_get_total_balance(client); - - if !balance_ptr.is_null() { - let balance = &*balance_ptr; - println!( - "Total balance - Confirmed: {}, Unconfirmed: {}, Total: {}", - balance.confirmed, balance.unconfirmed, balance.total - ); - - // FFIBalance from key-wallet-ffi doesn't need explicit destruction - // dash_spv_ffi_balance_destroy(balance_ptr); - println!("✅ Get total balance works!"); - } else { - println!("⚠️ Failed to get total balance (may need sync first)"); - } - - // Cleanup - dash_spv_ffi_client_stop(client); - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } -} - #[test] #[serial] fn test_enhanced_event_callbacks() { diff --git a/dash-spv-ffi/tests/test_mempool_tracking.rs b/dash-spv-ffi/tests/test_mempool_tracking.rs deleted file mode 100644 index e3395bfcd..000000000 --- a/dash-spv-ffi/tests/test_mempool_tracking.rs +++ /dev/null @@ -1,183 +0,0 @@ -use dash_spv_ffi::*; -use std::ffi::CString; -use std::os::raw::{c_char, c_void}; -use std::sync::{Arc, Mutex}; - -#[derive(Default)] -struct TestCallbacks { - mempool_added_count: Arc>, - mempool_confirmed_count: Arc>, - mempool_removed_count: Arc>, -} - -extern "C" fn test_mempool_added( - _txid: *const [u8; 32], - _amount: i64, - _addresses: *const c_char, - _is_instant_send: bool, - user_data: *mut c_void, -) { - let callbacks = unsafe { &*(user_data as *const TestCallbacks) }; - let mut count = callbacks.mempool_added_count.lock().unwrap(); - *count += 1; -} - -extern "C" fn test_mempool_confirmed( - _txid: *const [u8; 32], - _block_height: u32, - _block_hash: *const [u8; 32], - user_data: *mut c_void, -) { - let callbacks = unsafe { &*(user_data as *const TestCallbacks) }; - let mut count = callbacks.mempool_confirmed_count.lock().unwrap(); - *count += 1; -} - -extern "C" fn test_mempool_removed(_txid: *const [u8; 32], _reason: u8, user_data: *mut c_void) { - let callbacks = unsafe { &*(user_data as *const TestCallbacks) }; - let mut count = callbacks.mempool_removed_count.lock().unwrap(); - *count += 1; -} - -#[test] -fn test_mempool_configuration() { - unsafe { - // Initialize logging - let _ = dash_spv_ffi_init_logging(CString::new("info").unwrap().as_ptr()); - - // Create configuration for testnet - let config = dash_spv_ffi_config_testnet(); - assert!(!config.is_null()); - - // Set data directory - let data_dir = CString::new("/tmp/dash-spv-test-mempool").unwrap(); - let result = dash_spv_ffi_config_set_data_dir(config, data_dir.as_ptr()); - assert_eq!(result, 0); - - // Enable mempool tracking - let result = dash_spv_ffi_config_set_mempool_tracking(config, true); - assert_eq!(result, 0); - - // Set mempool strategy to FetchAll - let result = dash_spv_ffi_config_set_mempool_strategy(config, FFIMempoolStrategy::FetchAll); - assert_eq!(result, 0); - - // Set max mempool transactions - let result = dash_spv_ffi_config_set_max_mempool_transactions(config, 1000); - assert_eq!(result, 0); - - // Set mempool timeout - let result = dash_spv_ffi_config_set_mempool_timeout(config, 3600); - assert_eq!(result, 0); - - // Verify configuration - assert!(dash_spv_ffi_config_get_mempool_tracking(config)); - assert_eq!(dash_spv_ffi_config_get_mempool_strategy(config), FFIMempoolStrategy::FetchAll); - - // Create client - let client = dash_spv_ffi_client_new(config); - assert!(!client.is_null()); - - // Clean up - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } -} - -#[test] -fn test_mempool_event_callbacks() { - unsafe { - // Initialize logging - let _ = dash_spv_ffi_init_logging(CString::new("info").unwrap().as_ptr()); - - // Create configuration - let config = dash_spv_ffi_config_testnet(); - assert!(!config.is_null()); - - // Set data directory - let data_dir = CString::new("/tmp/dash-spv-test-mempool-events").unwrap(); - dash_spv_ffi_config_set_data_dir(config, data_dir.as_ptr()); - - // Enable mempool tracking - dash_spv_ffi_config_set_mempool_tracking(config, true); - dash_spv_ffi_config_set_mempool_strategy(config, FFIMempoolStrategy::FetchAll); - - // Create client - let client = dash_spv_ffi_client_new(config); - assert!(!client.is_null()); - - // Set up test callbacks - let test_callbacks = Box::new(TestCallbacks::default()); - let test_callbacks_ptr = Box::into_raw(test_callbacks); - - let callbacks = FFIEventCallbacks { - on_block: None, - on_transaction: None, - on_balance_update: None, - on_mempool_transaction_added: Some(test_mempool_added), - on_mempool_transaction_confirmed: Some(test_mempool_confirmed), - on_mempool_transaction_removed: Some(test_mempool_removed), - on_compact_filter_matched: None, - on_wallet_transaction: None, - user_data: test_callbacks_ptr as *mut c_void, - }; - - let result = dash_spv_ffi_client_set_event_callbacks(client, callbacks); - assert_eq!(result, 0); - - // Clean up - let _ = Box::from_raw(test_callbacks_ptr); - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } -} - -#[test] -fn test_mempool_balance_query() { - unsafe { - // Initialize logging - let _ = dash_spv_ffi_init_logging(CString::new("info").unwrap().as_ptr()); - - // Create configuration - let config = dash_spv_ffi_config_testnet(); - assert!(!config.is_null()); - - // Set data directory - let data_dir = CString::new("/tmp/dash-spv-test-mempool-balance").unwrap(); - dash_spv_ffi_config_set_data_dir(config, data_dir.as_ptr()); - - // Enable mempool tracking - dash_spv_ffi_config_set_mempool_tracking(config, true); - - // Create client - let client = dash_spv_ffi_client_new(config); - assert!(!client.is_null()); - - // Start client (would fail without network but tests structure) - let result = dash_spv_ffi_client_start(client); - // Allow failure since we're not connected to network - if result == 0 { - // Test mempool transaction count - let count = dash_spv_ffi_client_get_mempool_transaction_count(client); - assert!(count >= 0); - - // Test mempool balance for address - let address = CString::new("yXdxAYfAkQnrFZNxdVfqwJMRpDcCuC6YLi").unwrap(); - let balance = dash_spv_ffi_client_get_mempool_balance(client, address.as_ptr()); - if !balance.is_null() { - let balance_ref = &(*balance); - assert_eq!(balance_ref.confirmed, 0); // No confirmed balance in mempool - // mempool and mempool_instant fields contain the actual mempool balance - // FFIBalance from key-wallet-ffi doesn't need explicit destruction - // dash_spv_ffi_balance_destroy(balance); - } - - // Stop client - let _ = dash_spv_ffi_client_stop(client); - } - - // Clean up - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } -} diff --git a/dash-spv-ffi/tests/test_platform_integration.rs b/dash-spv-ffi/tests/test_platform_integration.rs deleted file mode 100644 index a0780a7ab..000000000 --- a/dash-spv-ffi/tests/test_platform_integration.rs +++ /dev/null @@ -1,59 +0,0 @@ -#[cfg(test)] -mod test_platform_integration { - use dash_spv_ffi::*; - use std::ptr; - - #[test] - fn test_quorum_public_key_buffer_size_validation() { - // Test that buffer size validation works correctly - let _client: *mut FFIDashSpvClient = ptr::null_mut(); - let quorum_hash = [0u8; 32]; - let _small_buffer = [0u8; 47]; // Too small - should fail - let mut correct_buffer = [0u8; 48]; // Correct size - should succeed (if implemented) - let _large_buffer = [0u8; 100]; // Larger than needed - should succeed (if implemented) - - unsafe { - // Test with null client - should fail with NullPointer - let result = ffi_dash_spv_get_quorum_public_key( - ptr::null_mut(), - 0, - quorum_hash.as_ptr(), - 0, - correct_buffer.as_mut_ptr(), - correct_buffer.len(), - ); - assert_eq!(result.error_code, FFIErrorCode::NullPointer as i32); - - // For a real test, we'd need a valid client, but since the function - // is not fully implemented, we can at least test the parameter validation - - // Test with small buffer - should fail with InvalidArgument - // Note: This would work if we had a valid client - /* - let result = ffi_dash_spv_get_quorum_public_key( - valid_client, - 0, - quorum_hash.as_ptr(), - 0, - small_buffer.as_mut_ptr(), - small_buffer.len(), - ); - assert_eq!(result.error_code, FFIErrorCode::InvalidArgument as i32); - */ - - // Test with null output buffer - should fail - // Note: This would work if we had a valid client - /* - let result = ffi_dash_spv_get_quorum_public_key( - valid_client, - 0, - quorum_hash.as_ptr(), - 0, - ptr::null_mut(), - 48, - ); - assert_eq!(result.error_code, FFIErrorCode::NullPointer as i32); - */ - } - } -} diff --git a/dash-spv-ffi/tests/test_wallet_manager.rs b/dash-spv-ffi/tests/test_wallet_manager.rs new file mode 100644 index 000000000..7288529ee --- /dev/null +++ b/dash-spv-ffi/tests/test_wallet_manager.rs @@ -0,0 +1,32 @@ +#[cfg(test)] +mod tests { + use dash_spv_ffi::*; + use key_wallet_ffi::{FFIError, FFIWalletManager, wallet_manager::{wallet_manager_wallet_count, wallet_manager_free}}; + + #[test] + fn test_get_wallet_manager() { + unsafe { + // Create a config + let config = dash_spv_ffi_config_testnet(); + assert!(!config.is_null()); + + // Create a client + let client = dash_spv_ffi_client_new(config); + assert!(!client.is_null()); + + // Get wallet manager + let wallet_manager = dash_spv_ffi_client_get_wallet_manager(client); + assert!(!wallet_manager.is_null()); + + // Get wallet count (should be 0 initially) + let mut error = FFIError::success(); + let count = wallet_manager_wallet_count(wallet_manager as *const FFIWalletManager, &mut error as *mut FFIError); + assert_eq!(count, 0); + + // Clean up + wallet_manager_free(wallet_manager as *mut FFIWalletManager); + dash_spv_ffi_client_destroy(client); + dash_spv_ffi_config_destroy(config); + } + } +} \ No newline at end of file diff --git a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs index 0838cb54e..31df478fa 100644 --- a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs +++ b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs @@ -8,7 +8,6 @@ mod tests { use crate::*; use serial_test::serial; use std::ffi::CString; - use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use tempfile::TempDir; @@ -158,70 +157,6 @@ mod tests { // Can't directly compare thread counts, but test passes if no panic/leak } - // Wrapper to make pointer Send - struct SendableClient(*mut FFIDashSpvClient); - unsafe impl Send for SendableClient {} - - #[test] - #[serial] - #[ignore] // Requires network - client operations hang without peers - fn test_concurrent_client_operations() { - unsafe { - let (config, _temp_dir) = create_test_config_with_dir(); - let client = dash_spv_ffi_client_new(config); - assert!(!client.is_null()); - - let client_ptr = Arc::new(Mutex::new(SendableClient(client))); - let mut handles = vec![]; - - // Spawn threads doing different operations - for i in 0..5 { - let client_clone = client_ptr.clone(); - let handle = thread::spawn(move || { - let client = client_clone.lock().unwrap().0; - - match i % 3 { - 0 => { - // Get sync progress - let progress = dash_spv_ffi_client_get_sync_progress(client); - if !progress.is_null() { - dash_spv_ffi_sync_progress_destroy(progress); - } - } - 1 => { - // Get stats - let stats = dash_spv_ffi_client_get_stats(client); - if !stats.is_null() { - dash_spv_ffi_spv_stats_destroy(stats); - } - } - 2 => { - // Get balance for random address - let addr = CString::new("XjSgy6PaVCB3V4KhCiCDkaVbx9ewxe9R1E").unwrap(); - let balance = - dash_spv_ffi_client_get_address_balance(client, addr.as_ptr()); - if !balance.is_null() { - // FFIBalance from key-wallet-ffi doesn't need explicit destruction - // dash_spv_ffi_balance_destroy(balance); - } - } - _ => {} - } - }); - handles.push(handle); - } - - // Wait for all threads - for handle in handles { - handle.join().unwrap(); - } - - let client = client_ptr.lock().unwrap().0; - dash_spv_ffi_client_destroy(client); - dash_spv_ffi_config_destroy(config); - } - } - #[test] #[serial] fn test_client_null_operations() { diff --git a/dash-spv/examples/filter_sync.rs b/dash-spv/examples/filter_sync.rs index 306724b84..9b6269467 100644 --- a/dash-spv/examples/filter_sync.rs +++ b/dash-spv/examples/filter_sync.rs @@ -5,7 +5,6 @@ use dash_spv::storage::MemoryStorageManager; use dash_spv::{init_logging, ClientConfig, DashSpvClient}; use dashcore::Address; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use std::str::FromStr; use std::sync::Arc; @@ -31,9 +30,7 @@ async fn main() -> Result<(), Box> { let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create the client let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await?; diff --git a/dash-spv/examples/simple_sync.rs b/dash-spv/examples/simple_sync.rs index 33bedf602..2f0807eb4 100644 --- a/dash-spv/examples/simple_sync.rs +++ b/dash-spv/examples/simple_sync.rs @@ -4,7 +4,7 @@ use dash_spv::network::MultiPeerNetworkManager; use dash_spv::storage::MemoryStorageManager; use dash_spv::{init_logging, ClientConfig, DashSpvClient}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; + use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use tokio::sync::RwLock; @@ -26,9 +26,7 @@ async fn main() -> Result<(), Box> { let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create the client let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await?; diff --git a/dash-spv/src/client/block_processor_test.rs b/dash-spv/src/client/block_processor_test.rs index 59a64295f..ef2eac760 100644 --- a/dash-spv/src/client/block_processor_test.rs +++ b/dash-spv/src/client/block_processor_test.rs @@ -60,10 +60,6 @@ mod tests { // Return true for all filters in test true } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } fn create_test_block(network: Network) -> Block { @@ -142,8 +138,7 @@ mod tests { // Since we're using key_wallet_manager::wallet_interface::WalletInterface, // we need to use the trait to access as_any use key_wallet_manager::wallet_interface::WalletInterface; - let mock_wallet = wallet.as_any().downcast_ref::().unwrap(); - let processed = mock_wallet.processed_blocks.lock().await; + let processed = wallet.processed_blocks.lock().await; assert_eq!(processed.len(), 1); assert_eq!(processed[0].0, block_hash); } @@ -244,10 +239,6 @@ mod tests { // Always return false - filter doesn't match false } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } let (task_tx, task_rx) = mpsc::unbounded_channel(); @@ -323,9 +314,7 @@ mod tests { // Verify wallet was called { let wallet = wallet.read().await; - use key_wallet_manager::wallet_interface::WalletInterface; - let mock_wallet = wallet.as_any().downcast_ref::().unwrap(); - let processed = mock_wallet.processed_transactions.lock().await; + let processed = wallet.processed_transactions.lock().await; assert_eq!(processed.len(), 1); assert_eq!(processed[0], txid); } diff --git a/dash-spv/src/lib.rs b/dash-spv/src/lib.rs index fdb4c32d7..6c88e3a4e 100644 --- a/dash-spv/src/lib.rs +++ b/dash-spv/src/lib.rs @@ -16,7 +16,6 @@ //! use dash_spv::network::MultiPeerNetworkManager; //! use dash_spv::storage::MemoryStorageManager; //! use dashcore::Network; -//! use key_wallet_manager::spv_wallet_manager::SPVWalletManager; //! use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; //! use key_wallet_manager::wallet_manager::WalletManager; //! use std::sync::Arc; @@ -32,7 +31,7 @@ //! // Create the required components //! let network = MultiPeerNetworkManager::new(&config).await?; //! let storage = MemoryStorageManager::new().await?; -//! let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::::new()))); +//! let wallet = Arc::new(RwLock::new(WalletManager::::new())); //! //! // Create and start the client //! let mut client = DashSpvClient::new(config.clone(), network, storage, wallet).await?; diff --git a/dash-spv/src/main.rs b/dash-spv/src/main.rs index e4090bbd8..4a21a6c32 100644 --- a/dash-spv/src/main.rs +++ b/dash-spv/src/main.rs @@ -11,7 +11,7 @@ use tokio::signal; use dash_spv::terminal::TerminalGuard; use dash_spv::{ClientConfig, DashSpvClient, Network}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::wallet_manager::{WalletId, WalletManager}; +use key_wallet_manager::wallet_manager::WalletManager; #[tokio::main] async fn main() { @@ -222,19 +222,16 @@ async fn run() -> Result<(), Box> { // Log the data directory being used tracing::info!("Using data directory: {}", data_dir.display()); - // Create the SPV wallet manager - let mut spv_wallet = - key_wallet_manager::spv_wallet_manager::SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()); - spv_wallet.base.create_wallet_from_mnemonic( + // Create the wallet manager + let mut wallet_manager = WalletManager::::new(); + wallet_manager.create_wallet_from_mnemonic( "enemy check owner stumble unaware debris suffer peanut good fabric bleak outside", "", &[network], None, key_wallet::wallet::initialization::WalletAccountCreationOptions::default(), )?; - let wallet = Arc::new(tokio::sync::RwLock::new(spv_wallet)); + let wallet = Arc::new(tokio::sync::RwLock::new(wallet_manager)); // Create network manager let network_manager = @@ -303,14 +300,14 @@ async fn run_client>, + wallet: Arc>>, enable_terminal_ui: bool, matches: &clap::ArgMatches, ) -> Result<(), Box> { // Create and start the client let mut client = match DashSpvClient::< - key_wallet_manager::spv_wallet_manager::SPVWalletManager, + WalletManager, dash_spv::network::multi_peer::MultiPeerNetworkManager, S, >::new(config.clone(), network_manager, storage_manager, wallet.clone()) @@ -442,7 +439,7 @@ async fn run_client::new(), - ))); + let wallet = Arc::new(RwLock::new( + key_wallet_manager::wallet_manager::WalletManager::::new() + )); let mut sync_manager: SequentialSyncManager = SequentialSyncManager::new(&config, received_heights, wallet) .expect("Failed to create SequentialSyncManager for integration test"); diff --git a/dash-spv/tests/chainlock_simple_test.rs b/dash-spv/tests/chainlock_simple_test.rs index 7efccda69..51ee40f79 100644 --- a/dash-spv/tests/chainlock_simple_test.rs +++ b/dash-spv/tests/chainlock_simple_test.rs @@ -6,7 +6,6 @@ use dash_spv::storage::DiskStorageManager; use dash_spv::types::ValidationMode; use dashcore::Network; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use tempfile::TempDir; @@ -52,9 +51,7 @@ async fn test_chainlock_validation_flow() { DiskStorageManager::new(config.storage_path.clone().unwrap()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create the SPV client let client = @@ -104,9 +101,7 @@ async fn test_chainlock_manager_initialization() { DiskStorageManager::new(config.storage_path.clone().unwrap()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create the SPV client let client = diff --git a/dash-spv/tests/chainlock_validation_test.rs b/dash-spv/tests/chainlock_validation_test.rs index 5001b66d5..e74d40c99 100644 --- a/dash-spv/tests/chainlock_validation_test.rs +++ b/dash-spv/tests/chainlock_validation_test.rs @@ -18,7 +18,6 @@ use dashcore::sml::masternode_list_engine::MasternodeListEngine; use dashcore::Network; use dashcore::{BlockHash, ChainLock}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use std::sync::Arc; use std::time::Duration; @@ -189,9 +188,7 @@ async fn test_chainlock_validation_without_masternode_engine() { let network = MockNetworkManager::new(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create client config let config = ClientConfig { @@ -249,9 +246,7 @@ async fn test_chainlock_validation_with_masternode_engine() { network.add_chain_lock(chain_lock.clone()); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create client config with masternodes enabled let config = ClientConfig { @@ -304,9 +299,7 @@ async fn test_chainlock_queue_and_process_flow() { let network = MockNetworkManager::new(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create client config let config = ClientConfig { @@ -360,9 +353,7 @@ async fn test_chainlock_manager_cache_operations() { let network = MockNetworkManager::new(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create client config let config = ClientConfig { @@ -420,9 +411,7 @@ async fn test_client_chainlock_update_flow() { let network = MockNetworkManager::new(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); // Create client config with masternodes enabled let config = ClientConfig { diff --git a/dash-spv/tests/header_sync_test.rs b/dash-spv/tests/header_sync_test.rs index f5a162753..9bf2f8880 100644 --- a/dash-spv/tests/header_sync_test.rs +++ b/dash-spv/tests/header_sync_test.rs @@ -13,7 +13,6 @@ use dashcore::{block::Header as BlockHeader, block::Version, Network}; use dashcore_hashes::Hash; use env_logger; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use log::{debug, info}; use std::sync::Arc; @@ -308,9 +307,7 @@ async fn test_header_sync_with_client_integration() { MemoryStorageManager::new().await.expect("Failed to create storage manager"); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await; assert!(client.is_ok(), "Client creation should succeed"); diff --git a/dash-spv/tests/instantsend_integration_test.rs b/dash-spv/tests/instantsend_integration_test.rs index 751431fa5..92da7faf2 100644 --- a/dash-spv/tests/instantsend_integration_test.rs +++ b/dash-spv/tests/instantsend_integration_test.rs @@ -1,13 +1,13 @@ #![cfg(feature = "skip_mock_implementation_incomplete")] -// This test is currently disabled because the SPVWalletManager API has changed +// This test is currently disabled because the WalletManager API has changed // and these methods don't exist anymore. The test needs to be rewritten to use // the new wallet interface. // dash-spv/tests/instantsend_integration_test.rs // -// TODO: These tests need to be updated to work with the new SPVWalletManager API -// The following methods don't exist in SPVWalletManager: +// TODO: These tests need to be updated to work with the new WalletManager API +// The following methods don't exist in WalletManager: // - add_utxo // - add_watched_address // - get_utxos @@ -32,13 +32,13 @@ use dashcore::{ use dashcore_hashes::{sha256d, Hash}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::{ - spv_wallet_manager::SPVWalletManager, wallet_manager::WalletManager, Utxo, + wallet_manager::WalletManager, Utxo, }; use rand::thread_rng; /// Helper to create a test wallet manager. -fn create_test_wallet() -> Arc> { - Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::::new()))) +fn create_test_wallet() -> Arc>> { + Arc::new(RwLock::new(WalletManager::::new())) } /// Create a deterministic test address. @@ -125,7 +125,7 @@ async fn test_instantsend_end_to_end() { ); initial_utxo.is_confirmed = true; - // TODO: The SPVWalletManager API has changed. These methods no longer exist: + // TODO: The WalletManager API has changed. These methods no longer exist: // - add_utxo() - need to use WalletInterface methods or direct base access // - add_watched_address() - need to use different approach for monitoring // wallet.write().await.add_utxo(initial_utxo).await.unwrap(); @@ -159,7 +159,7 @@ async fn test_instantsend_end_to_end() { // not creating new UTXOs for us. We'll test InstantLock processing in the next section. // 5. Assert the wallet state has been updated correctly. - // TODO: get_utxos() method no longer exists on SPVWalletManager + // TODO: get_utxos() method no longer exists on WalletManager // Need to access UTXOs through WalletInterface or base WalletManager // let utxos = wallet.read().await.get_utxos().await; // let spent_utxo = utxos.iter().find(|u| u.outpoint == initial_outpoint); diff --git a/dash-spv/tests/integration_real_node_test.rs b/dash-spv/tests/integration_real_node_test.rs index 2841d8b0f..19b22860b 100644 --- a/dash-spv/tests/integration_real_node_test.rs +++ b/dash-spv/tests/integration_real_node_test.rs @@ -15,7 +15,6 @@ use dash_spv::{ use dashcore::Network; use env_logger; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use log::{debug, info, warn}; use std::sync::Arc; @@ -29,7 +28,7 @@ const HEADER_SYNC_TIMEOUT: Duration = Duration::from_secs(120); // 2 minutes for async fn create_test_client( config: ClientConfig, ) -> Result< - DashSpvClient, + DashSpvClient, MultiPeerNetworkManager, MemoryStorageManager>, Box, > { // Create network manager @@ -39,9 +38,7 @@ async fn create_test_client( let storage_manager = MemoryStorageManager::new().await?; // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); Ok(DashSpvClient::new(config, network_manager, storage_manager, wallet).await?) } diff --git a/dash-spv/tests/multi_peer_test.rs b/dash-spv/tests/multi_peer_test.rs index ba6aa24f3..0f5e66541 100644 --- a/dash-spv/tests/multi_peer_test.rs +++ b/dash-spv/tests/multi_peer_test.rs @@ -13,7 +13,6 @@ use dash_spv::storage::{DiskStorageManager, MemoryStorageManager}; use dash_spv::types::ValidationMode; use dashcore::Network; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; /// Create a test configuration with the given network fn create_test_config(network: Network, data_dir: Option) -> ClientConfig { @@ -45,9 +44,7 @@ async fn test_multi_peer_connection() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -94,9 +91,7 @@ async fn test_peer_persistence() { let storage_manager = DiskStorageManager::new(temp_path.clone()).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -122,9 +117,7 @@ async fn test_peer_persistence() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -164,9 +157,7 @@ async fn test_peer_disconnection() { let storage_manager = DiskStorageManager::new(temp_path).await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); @@ -201,9 +192,7 @@ async fn test_max_peer_limit() { let storage_manager = MemoryStorageManager::new().await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let _client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap(); diff --git a/dash-spv/tests/simple_header_test.rs b/dash-spv/tests/simple_header_test.rs index 651760496..5a8113169 100644 --- a/dash-spv/tests/simple_header_test.rs +++ b/dash-spv/tests/simple_header_test.rs @@ -8,7 +8,6 @@ use dash_spv::{ }; use dashcore::Network; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; use log::info; use std::{net::SocketAddr, sync::Arc, time::Duration}; @@ -61,9 +60,7 @@ async fn test_simple_header_sync() { MultiPeerNetworkManager::new(&config).await.expect("Failed to create network manager"); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); let mut client = DashSpvClient::new(config.clone(), network_manager, storage, wallet) .await diff --git a/dash-spv/tests/wallet_integration_test.rs b/dash-spv/tests/wallet_integration_test.rs index 3a6456ab7..3302fcc69 100644 --- a/dash-spv/tests/wallet_integration_test.rs +++ b/dash-spv/tests/wallet_integration_test.rs @@ -10,11 +10,10 @@ use dash_spv::storage::MemoryStorageManager; use dash_spv::{ClientConfig, DashSpvClient}; use dashcore::Network; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; /// Create a test SPV client with memory storage for integration testing. async fn create_test_client( -) -> DashSpvClient { +) -> DashSpvClient, MultiPeerNetworkManager, MemoryStorageManager> { let config = ClientConfig::testnet().without_filters().without_masternodes(); // Create network manager @@ -24,9 +23,7 @@ async fn create_test_client( let storage_manager = MemoryStorageManager::new().await.unwrap(); // Create wallet manager - let wallet = Arc::new(RwLock::new(SPVWalletManager::with_base(WalletManager::< - ManagedWalletInfo, - >::new()))); + let wallet = Arc::new(RwLock::new(WalletManager::::new())); DashSpvClient::new(config, network_manager, storage_manager, wallet).await.unwrap() } @@ -63,16 +60,15 @@ async fn test_spv_client_start_stop() { #[tokio::test] async fn test_wallet_manager_basic_operations() { // Test basic wallet manager operations - let wallet_manager = SPVWalletManager::with_base(WalletManager::::new()); + let wallet_manager = WalletManager::::new(); // Test that we can create a wallet manager - // SPVWalletManager doesn't have get_watched_scripts method anymore - // Check wallet count instead - assert_eq!(wallet_manager.base.wallet_count(), 0); + // Check wallet count + assert_eq!(wallet_manager.wallet_count(), 0); // Test adding a wallet (this would need actual wallet creation logic) // For now, just verify the manager is working - let balance = wallet_manager.base.get_total_balance(); + let balance = wallet_manager.get_total_balance(); assert_eq!(balance, 0); } diff --git a/key-wallet-ffi/Cargo.toml b/key-wallet-ffi/Cargo.toml index 0be44e3ae..7cb307f86 100644 --- a/key-wallet-ffi/Cargo.toml +++ b/key-wallet-ffi/Cargo.toml @@ -13,7 +13,7 @@ name = "key_wallet_ffi" crate-type = ["cdylib", "staticlib", "lib"] [features] -default = ["bincode", "eddsa", "bls"] +default = ["bincode", "eddsa", "bls", "bip38"] bip38 = ["key-wallet/bip38"] bincode = ["key-wallet-manager/bincode", "key-wallet/bincode"] eddsa = ["dashcore/eddsa", "key-wallet/eddsa"] @@ -25,6 +25,7 @@ key-wallet-manager = { path = "../key-wallet-manager", features = ["std"] } dashcore = { path = "../dash", features = ["std"] } dash-network = { path = "../dash-network" } secp256k1 = { version = "0.30.0", features = ["global-context"] } +tokio = { version = "1.32", features = ["rt-multi-thread", "sync"] } libc = "0.2" hex = "0.4" diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md new file mode 100644 index 000000000..f5b24e6a7 --- /dev/null +++ b/key-wallet-ffi/FFI_API.md @@ -0,0 +1,3634 @@ +# Key-Wallet FFI API Documentation + +This document provides a comprehensive reference for all FFI (Foreign Function Interface) functions available in the key-wallet-ffi library. + +**Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. + +**Total Functions**: 212 + +## Table of Contents + +- [Initialization](#initialization) +- [Error Handling](#error-handling) +- [Wallet Manager](#wallet-manager) +- [Wallet Operations](#wallet-operations) +- [Account Management](#account-management) +- [Address Management](#address-management) +- [Transaction Management](#transaction-management) +- [Key Management](#key-management) +- [Mnemonic Operations](#mnemonic-operations) +- [Utility Functions](#utility-functions) + +## Function Reference + +### Initialization + +Functions: 2 + +| Function | Description | Module | +|----------|-------------|--------| +| `key_wallet_ffi_initialize` | Initialize the library | lib | +| `key_wallet_ffi_version` | Get library version Returns a static string that should NOT be freed by the ... | lib | + +### Error Handling + +Functions: 3 + +| Function | Description | Module | +|----------|-------------|--------| +| `account_result_free_error` | Free an account result's error message (if any) Note: This does NOT free the ... | account | +| `error_message_free` | Free an error message # Safety - `message` must be a valid pointer to a C s... | error | +| `managed_account_result_free_error` | Free a managed account result's error message (if any) Note: This does NOT fr... | managed_account | + +### Wallet Manager + +Functions: 20 + +| Function | Description | Module | +|----------|-------------|--------| +| `wallet_manager_add_wallet_from_mnemonic` | Add a wallet from mnemonic to the manager (backward compatibility) # Safety ... | wallet_manager | +| `wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes` | No description | wallet_manager | +| `wallet_manager_add_wallet_from_mnemonic_with_options` | Add a wallet from mnemonic to the manager with options # Safety - `manager`... | wallet_manager | +| `wallet_manager_create` | Create a new wallet manager | wallet_manager | +| `wallet_manager_current_height` | Get current height for a network # Safety - `manager` must be a valid point... | wallet_manager | +| `wallet_manager_free` | Free wallet manager # Safety - `manager` must be a valid pointer to an FFIW... | wallet_manager | +| `wallet_manager_free_addresses` | Free address array # Safety - `addresses` must be a valid pointer to an arr... | wallet_manager | +| `wallet_manager_free_wallet_bytes` | No description | wallet_manager | +| `wallet_manager_free_wallet_ids` | Free wallet IDs buffer # Safety - `wallet_ids` must be a valid pointer to a... | wallet_manager | +| `wallet_manager_get_change_address` | Get next change address for a wallet # Safety - `manager` must be a valid p... | wallet_manager | +| `wallet_manager_get_managed_wallet_info` | Get managed wallet info from the manager Returns a reference to the managed ... | wallet_manager | +| `wallet_manager_get_monitored_addresses` | Get monitored addresses for a network # Safety - `manager` must be a valid ... | wallet_manager | +| `wallet_manager_get_receive_address` | Get next receive address for a wallet # Safety - `manager` must be a valid ... | wallet_manager | +| `wallet_manager_get_wallet` | Get a wallet from the manager Returns a reference to the wallet if found # ... | wallet_manager | +| `wallet_manager_get_wallet_balance` | Get wallet balance Returns the confirmed and unconfirmed balance for a speci... | wallet_manager | +| `wallet_manager_get_wallet_ids` | Get wallet IDs # Safety - `manager` must be a valid pointer to an FFIWallet... | wallet_manager | +| `wallet_manager_import_wallet_from_bytes` | No description | wallet_manager | +| `wallet_manager_process_transaction` | Process a transaction through all wallets Checks a transaction against all w... | wallet_manager | +| `wallet_manager_update_height` | Update block height for a network # Safety - `manager` must be a valid poin... | wallet_manager | +| `wallet_manager_wallet_count` | Get wallet count # Safety - `manager` must be a valid pointer to an FFIWall... | wallet_manager | + +### Wallet Operations + +Functions: 55 + +| Function | Description | Module | +|----------|-------------|--------| +| `account_get_parent_wallet_id` | Get the parent wallet ID of an account # Safety - `account` must be a valid... | account | +| `bls_account_get_parent_wallet_id` | No description | account | +| `eddsa_account_get_parent_wallet_id` | No description | account | +| `ffi_managed_wallet_free` | Free a managed wallet (FFIManagedWallet type) # Safety - `managed_wallet` m... | transaction_checking | +| `managed_account_get_parent_wallet_id` | Get the parent wallet ID of a managed account Note: ManagedAccount doesn't s... | managed_account | +| `managed_wallet_check_transaction` | Check if a transaction belongs to the wallet This function checks a transact... | transaction_checking | +| `managed_wallet_free` | Free managed wallet info # Safety - `managed_wallet` must be a valid pointe... | managed_wallet | +| `managed_wallet_generate_addresses_to_index` | Generate addresses up to a specific index in a pool This ensures that addres... | address_pool | +| `managed_wallet_get_account` | Get a managed account from a managed wallet This function gets a ManagedAcco... | managed_account | +| `managed_wallet_get_account_collection` | Get managed account collection for a specific network from wallet manager # ... | managed_account_collection | +| `managed_wallet_get_account_count` | Get number of accounts in a managed wallet # Safety - `manager` must be a v... | managed_account | +| `managed_wallet_get_address_pool_info` | Get address pool information for an account # Safety - `managed_wallet` mus... | address_pool | +| `managed_wallet_get_balance` | Get wallet balance from managed wallet info Returns the balance breakdown in... | managed_wallet | +| `managed_wallet_get_bip_44_external_address_range` | Get BIP44 external (receive) addresses in the specified range Returns extern... | managed_wallet | +| `managed_wallet_get_bip_44_internal_address_range` | Get BIP44 internal (change) addresses in the specified range Returns interna... | managed_wallet | +| `managed_wallet_get_next_bip44_change_address` | Get the next unused change address Generates the next unused change address ... | managed_wallet | +| `managed_wallet_get_next_bip44_receive_address` | Get the next unused receive address Generates the next unused receive addres... | managed_wallet | +| `managed_wallet_get_top_up_account_with_registration_index` | Get a managed IdentityTopUp account with a specific registration index This ... | managed_account | +| `managed_wallet_get_utxos` | Get all UTXOs from managed wallet info # Safety - `managed_info` must be a ... | utxo | +| `managed_wallet_info_free` | Free managed wallet info returned by wallet_manager_get_managed_wallet_info ... | managed_wallet | +| `managed_wallet_mark_address_used` | Mark an address as used in the pool This updates the pool's tracking of whic... | address_pool | +| `managed_wallet_set_gap_limit` | Set the gap limit for an address pool The gap limit determines how many unus... | address_pool | +| `wallet_add_account` | Add an account to the wallet without xpub # Safety This function dereferenc... | wallet | +| `wallet_add_account_with_string_xpub` | Add an account to the wallet with xpub as string # Safety This function der... | wallet | +| `wallet_add_account_with_xpub_bytes` | Add an account to the wallet with xpub as byte array # Safety This function... | wallet | +| `wallet_build_transaction` | Build a transaction # Safety - `wallet` must be a valid pointer to an FFIWa... | transaction | +| `wallet_check_transaction` | Check if a transaction belongs to the wallet using ManagedWalletInfo # Safet... | transaction | +| `wallet_create_from_mnemonic` | Create a new wallet from mnemonic (backward compatibility - single network) ... | wallet | +| `wallet_create_from_mnemonic_with_options` | Create a new wallet from mnemonic with options # Safety - `mnemonic` must b... | wallet | +| `wallet_create_from_seed` | Create a new wallet from seed (backward compatibility) # Safety - `seed` mu... | wallet | +| `wallet_create_from_seed_with_options` | Create a new wallet from seed with options # Safety - `seed` must be a vali... | wallet | +| `wallet_create_managed_wallet` | Create a managed wallet from a regular wallet This creates a ManagedWalletIn... | transaction_checking | +| `wallet_create_random` | Create a new random wallet (backward compatibility) # Safety - `error` must... | wallet | +| `wallet_create_random_with_options` | Create a new random wallet with options # Safety - `account_options` must b... | wallet | +| `wallet_derive_extended_private_key` | Derive extended private key at a specific path Returns an opaque FFIExtendedP... | keys | +| `wallet_derive_extended_public_key` | Derive extended public key at a specific path Returns an opaque FFIExtendedPu... | keys | +| `wallet_derive_private_key` | Derive private key at a specific path Returns an opaque FFIPrivateKey pointer... | keys | +| `wallet_derive_private_key_as_wif` | Derive private key at a specific path and return as WIF string # Safety - `... | keys | +| `wallet_derive_public_key` | Derive public key at a specific path Returns an opaque FFIPublicKey pointer t... | keys | +| `wallet_derive_public_key_as_hex` | Derive public key at a specific path and return as hex string # Safety - `w... | keys | +| `wallet_free` | Free a wallet # Safety - `wallet` must be a valid pointer to an FFIWallet t... | wallet | +| `wallet_free_const` | Free a const wallet handle This is a const-safe wrapper for wallet_free() th... | wallet | +| `wallet_generate_provider_key` | Generate a provider key at a specific index This generates a provider key (B... | provider_keys | +| `wallet_get_account` | Get an account handle for a specific account type Returns a result containing... | account | +| `wallet_get_account_collection` | Get account collection for a specific network from wallet # Safety - `walle... | account_collection | +| `wallet_get_account_count` | Get number of accounts # Safety - `wallet` must be a valid pointer to an FF... | account | +| `wallet_get_account_xpriv` | Get extended private key for account # Safety - `wallet` must be a valid po... | keys | +| `wallet_get_account_xpub` | Get extended public key for account # Safety - `wallet` must be a valid poi... | keys | +| `wallet_get_id` | Get wallet ID (32-byte hash) # Safety - `wallet` must be a valid pointer to... | wallet | +| `wallet_get_top_up_account_with_registration_index` | Get an IdentityTopUp account handle with a specific registration index This i... | account | +| `wallet_get_xpub` | Get extended public key for account # Safety - `wallet` must be a valid poi... | wallet | +| `wallet_has_mnemonic` | Check if wallet has mnemonic # Safety - `wallet` must be a valid pointer to... | wallet | +| `wallet_is_watch_only` | Check if wallet is watch-only # Safety - `wallet` must be a valid pointer t... | wallet | +| `wallet_sign_transaction` | Sign a transaction # Safety - `wallet` must be a valid pointer to an FFIWal... | transaction | +| `wallet_sign_with_provider_key` | Sign data with a provider key This signs arbitrary data with the provider ke... | provider_keys | + +### Account Management + +Functions: 81 + +| Function | Description | Module | +|----------|-------------|--------| +| `account_collection_count` | Get the total number of accounts in the collection # Safety - `collection` ... | account_collection | +| `account_collection_free` | Free an account collection handle # Safety - `collection` must be a valid p... | account_collection | +| `account_collection_get_bip32_account` | Get a BIP32 account by index from the collection # Safety - `collection` mu... | account_collection | +| `account_collection_get_bip32_indices` | Get all BIP32 account indices # Safety - `collection` must be a valid point... | account_collection | +| `account_collection_get_bip44_account` | Get a BIP44 account by index from the collection # Safety - `collection` mu... | account_collection | +| `account_collection_get_bip44_indices` | Get all BIP44 account indices # Safety - `collection` must be a valid point... | account_collection | +| `account_collection_get_coinjoin_account` | Get a CoinJoin account by index from the collection # Safety - `collection`... | account_collection | +| `account_collection_get_coinjoin_indices` | Get all CoinJoin account indices # Safety - `collection` must be a valid po... | account_collection | +| `account_collection_get_identity_invitation` | Get the identity invitation account if it exists # Safety - `collection` mu... | account_collection | +| `account_collection_get_identity_registration` | Get the identity registration account if it exists # Safety - `collection` ... | account_collection | +| `account_collection_get_identity_topup` | Get an identity topup account by registration index # Safety - `collection`... | account_collection | +| `account_collection_get_identity_topup_indices` | Get all identity topup registration indices # Safety - `collection` must be... | account_collection | +| `account_collection_get_identity_topup_not_bound` | Get the identity topup not bound account if it exists # Safety - `collectio... | account_collection | +| `account_collection_get_provider_operator_keys` | Get the provider operator keys account if it exists Note: Returns null if the... | account_collection | +| `account_collection_get_provider_owner_keys` | Get the provider owner keys account if it exists # Safety - `collection` mu... | account_collection | +| `account_collection_get_provider_platform_keys` | Get the provider platform keys account if it exists Note: Returns null if the... | account_collection | +| `account_collection_get_provider_voting_keys` | Get the provider voting keys account if it exists # Safety - `collection` m... | account_collection | +| `account_collection_has_identity_invitation` | Check if identity invitation account exists # Safety - `collection` must be... | account_collection | +| `account_collection_has_identity_registration` | Check if identity registration account exists # Safety - `collection` must ... | account_collection | +| `account_collection_has_identity_topup_not_bound` | Check if identity topup not bound account exists # Safety - `collection` mu... | account_collection | +| `account_collection_has_provider_operator_keys` | Check if provider operator keys account exists # Safety - `collection` must... | account_collection | +| `account_collection_has_provider_owner_keys` | Check if provider owner keys account exists # Safety - `collection` must be... | account_collection | +| `account_collection_has_provider_platform_keys` | Check if provider platform keys account exists # Safety - `collection` must... | account_collection | +| `account_collection_has_provider_voting_keys` | Check if provider voting keys account exists # Safety - `collection` must b... | account_collection | +| `account_collection_summary` | Get a human-readable summary of all accounts in the collection Returns a for... | account_collection | +| `account_collection_summary_data` | Get structured account collection summary data Returns a struct containing a... | account_collection | +| `account_collection_summary_free` | Free an account collection summary and all its allocated memory # Safety - ... | account_collection | +| `account_free` | Free an account handle # Safety - `account` must be a valid pointer to an F... | account | +| `account_get_account_type` | Get the account type of an account # Safety - `account` must be a valid poi... | account | +| `account_get_extended_public_key_as_string` | Get the extended public key of an account as a string # Safety - `account` ... | account | +| `account_get_is_watch_only` | Check if an account is watch-only # Safety - `account` must be a valid poin... | account | +| `account_get_network` | Get the network of an account # Safety - `account` must be a valid pointer ... | account | +| `bls_account_free` | No description | account | +| `bls_account_get_account_type` | No description | account | +| `bls_account_get_extended_public_key_as_string` | No description | account | +| `bls_account_get_is_watch_only` | No description | account | +| `bls_account_get_network` | No description | account | +| `derivation_bip44_account_path` | Derive a BIP44 account path (m/44'/5'/account') | derivation | +| `eddsa_account_free` | No description | account | +| `eddsa_account_get_account_type` | No description | account | +| `eddsa_account_get_extended_public_key_as_string` | No description | account | +| `eddsa_account_get_is_watch_only` | No description | account | +| `eddsa_account_get_network` | No description | account | +| `managed_account_collection_count` | Get the total number of accounts in the managed collection # Safety - `coll... | managed_account_collection | +| `managed_account_collection_free` | Free a managed account collection handle # Safety - `collection` must be a ... | managed_account_collection | +| `managed_account_collection_get_bip32_account` | Get a BIP32 account by index from the managed collection # Safety - `collec... | managed_account_collection | +| `managed_account_collection_get_bip32_indices` | Get all BIP32 account indices from managed collection # Safety - `collectio... | managed_account_collection | +| `managed_account_collection_get_bip44_account` | Get a BIP44 account by index from the managed collection # Safety - `collec... | managed_account_collection | +| `managed_account_collection_get_bip44_indices` | Get all BIP44 account indices from managed collection # Safety - `collectio... | managed_account_collection | +| `managed_account_collection_get_coinjoin_account` | Get a CoinJoin account by index from the managed collection # Safety - `col... | managed_account_collection | +| `managed_account_collection_get_coinjoin_indices` | Get all CoinJoin account indices from managed collection # Safety - `collec... | managed_account_collection | +| `managed_account_collection_get_identity_invitation` | Get the identity invitation account if it exists in managed collection # Saf... | managed_account_collection | +| `managed_account_collection_get_identity_registration` | Get the identity registration account if it exists in managed collection # S... | managed_account_collection | +| `managed_account_collection_get_identity_topup` | Get an identity topup account by registration index from managed collection ... | managed_account_collection | +| `managed_account_collection_get_identity_topup_indices` | Get all identity topup registration indices from managed collection # Safety... | managed_account_collection | +| `managed_account_collection_get_identity_topup_not_bound` | Get the identity topup not bound account if it exists in managed collection ... | managed_account_collection | +| `managed_account_collection_get_provider_operator_keys` | Get the provider operator keys account if it exists in managed collection Not... | managed_account_collection | +| `managed_account_collection_get_provider_owner_keys` | Get the provider owner keys account if it exists in managed collection # Saf... | managed_account_collection | +| `managed_account_collection_get_provider_platform_keys` | Get the provider platform keys account if it exists in managed collection Not... | managed_account_collection | +| `managed_account_collection_get_provider_voting_keys` | Get the provider voting keys account if it exists in managed collection # Sa... | managed_account_collection | +| `managed_account_collection_has_identity_invitation` | Check if identity invitation account exists in managed collection # Safety ... | managed_account_collection | +| `managed_account_collection_has_identity_registration` | Check if identity registration account exists in managed collection # Safety... | managed_account_collection | +| `managed_account_collection_has_identity_topup_not_bound` | Check if identity topup not bound account exists in managed collection # Saf... | managed_account_collection | +| `managed_account_collection_has_provider_operator_keys` | Check if provider operator keys account exists in managed collection # Safet... | managed_account_collection | +| `managed_account_collection_has_provider_owner_keys` | Check if provider owner keys account exists in managed collection # Safety ... | managed_account_collection | +| `managed_account_collection_has_provider_platform_keys` | Check if provider platform keys account exists in managed collection # Safet... | managed_account_collection | +| `managed_account_collection_has_provider_voting_keys` | Check if provider voting keys account exists in managed collection # Safety ... | managed_account_collection | +| `managed_account_collection_summary` | Get a human-readable summary of all accounts in the managed collection Retur... | managed_account_collection | +| `managed_account_collection_summary_data` | Get structured account collection summary data for managed collection Return... | managed_account_collection | +| `managed_account_collection_summary_free` | Free a managed account collection summary and all its allocated memory # Saf... | managed_account_collection | +| `managed_account_free` | Free a managed account handle # Safety - `account` must be a valid pointer ... | managed_account | +| `managed_account_get_account_type` | Get the account type of a managed account # Safety - `account` must be a va... | managed_account | +| `managed_account_get_address_pool` | Get an address pool from a managed account by type This function returns the... | managed_account | +| `managed_account_get_balance` | Get the balance of a managed account # Safety - `account` must be a valid p... | managed_account | +| `managed_account_get_external_address_pool` | Get the external address pool from a managed account This function returns t... | managed_account | +| `managed_account_get_index` | Get the account index from a managed account Returns the primary account ind... | managed_account | +| `managed_account_get_internal_address_pool` | Get the internal address pool from a managed account This function returns t... | managed_account | +| `managed_account_get_is_watch_only` | Check if a managed account is watch-only # Safety - `account` must be a val... | managed_account | +| `managed_account_get_network` | Get the network of a managed account # Safety - `account` must be a valid p... | managed_account | +| `managed_account_get_transaction_count` | Get the number of transactions in a managed account # Safety - `account` mu... | managed_account | +| `managed_account_get_utxo_count` | Get the number of UTXOs in a managed account # Safety - `account` must be a... | managed_account | + +### Address Management + +Functions: 9 + +| Function | Description | Module | +|----------|-------------|--------| +| `address_array_free` | Free address array # Safety - `addresses` must be a valid pointer to an arr... | address | +| `address_free` | Free address string # Safety - `address` must be a valid pointer created by... | address | +| `address_get_type` | Get address type Returns: - 0: P2PKH address - 1: P2SH address - 2: Other ad... | address | +| `address_info_array_free` | Free an array of FFIAddressInfo structures # Safety - `infos` must be a val... | address_pool | +| `address_info_free` | Free a single FFIAddressInfo structure # Safety - `info` must be a valid po... | address_pool | +| `address_pool_free` | Free an address pool handle # Safety - `pool` must be a valid pointer to an... | address_pool | +| `address_pool_get_address_at_index` | Get a single address info at a specific index from the pool Returns detailed... | address_pool | +| `address_pool_get_addresses_in_range` | Get a range of addresses from the pool Returns an array of FFIAddressInfo st... | address_pool | +| `address_validate` | Validate an address # Safety - `address` must be a valid null-terminated C ... | address | + +### Transaction Management + +Functions: 4 + +| Function | Description | Module | +|----------|-------------|--------| +| `transaction_bytes_free` | Free transaction bytes # Safety - `tx_bytes` must be a valid pointer create... | transaction | +| `transaction_check_result_free` | Free a transaction check result # Safety - `result` must be a valid pointer... | transaction_checking | +| `transaction_classify` | Get the transaction classification for routing Returns a string describing t... | transaction_checking | +| `utxo_array_free` | Free UTXO array # Safety - `utxos` must be a valid pointer to an array of F... | utxo | + +### Key Management + +Functions: 16 + +| Function | Description | Module | +|----------|-------------|--------| +| `bip38_decrypt_private_key` | Decrypt a BIP38 encrypted private key # Safety This function is unsafe beca... | bip38 | +| `bip38_encrypt_private_key` | Encrypt a private key with BIP38 # Safety This function is unsafe because i... | bip38 | +| `derivation_derive_private_key_from_seed` | Derive private key for a specific path from seed # Safety - `seed` must be ... | derivation | +| `derivation_new_master_key` | Create a new master extended private key from seed # Safety - `seed` must b... | derivation | +| `dip9_derive_identity_key` | Derive key using DIP9 path constants for identity # Safety - `seed` must be... | derivation | +| `extended_private_key_free` | Free an extended private key # Safety - `key` must be a valid pointer creat... | keys | +| `extended_private_key_get_private_key` | Get the private key from an extended private key Extracts the non-extended p... | keys | +| `extended_private_key_to_string` | Get extended private key as string (xprv format) Returns the extended privat... | keys | +| `extended_public_key_free` | Free an extended public key # Safety - `key` must be a valid pointer create... | keys | +| `extended_public_key_get_public_key` | Get the public key from an extended public key Extracts the non-extended pub... | keys | +| `extended_public_key_to_string` | Get extended public key as string (xpub format) Returns the extended public ... | keys | +| `private_key_free` | Free a private key # Safety - `key` must be a valid pointer created by priv... | keys | +| `private_key_to_wif` | Get private key as WIF string from FFIPrivateKey # Safety - `key` must be a... | keys | +| `provider_key_info_free` | Free provider key info # Safety - `info` must be a valid pointer to an FFIP... | provider_keys | +| `public_key_free` | Free a public key # Safety - `key` must be a valid pointer created by publi... | keys | +| `public_key_to_hex` | Get public key as hex string from FFIPublicKey # Safety - `key` must be a v... | keys | + +### Mnemonic Operations + +Functions: 6 + +| Function | Description | Module | +|----------|-------------|--------| +| `mnemonic_free` | Free a mnemonic string # Safety - `mnemonic` must be a valid pointer create... | mnemonic | +| `mnemonic_generate` | Generate a new mnemonic with specified word count (12, 15, 18, 21, or 24) | mnemonic | +| `mnemonic_generate_with_language` | Generate a new mnemonic with specified language and word count | mnemonic | +| `mnemonic_to_seed` | Convert mnemonic to seed with optional passphrase # Safety - `mnemonic` mus... | mnemonic | +| `mnemonic_validate` | Validate a mnemonic phrase # Safety - `mnemonic` must be a valid null-termi... | mnemonic | +| `mnemonic_word_count` | Get word count from mnemonic # Safety - `mnemonic` must be a valid null-ter... | mnemonic | + +### Utility Functions + +Functions: 16 + +| Function | Description | Module | +|----------|-------------|--------| +| `derivation_bip44_payment_path` | Derive a BIP44 payment path (m/44'/5'/account'/change/index) | derivation | +| `derivation_coinjoin_path` | Derive CoinJoin path (m/9'/5'/4'/account') | derivation | +| `derivation_identity_authentication_path` | Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') | derivation | +| `derivation_identity_registration_path` | Derive identity registration path (m/9'/5'/5'/1'/index') | derivation | +| `derivation_identity_topup_path` | Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') | derivation | +| `derivation_path_free` | Free derivation path arrays Note: This function expects the count to properly... | keys | +| `derivation_path_parse` | Convert derivation path string to indices # Safety - `path` must be a valid... | keys | +| `derivation_string_free` | Free derivation path string # Safety - `s` must be a valid pointer to a C s... | derivation | +| `derivation_xpriv_free` | Free extended private key # Safety - `xpriv` must be a valid pointer to an ... | derivation | +| `derivation_xpriv_to_string` | Get extended private key as string # Safety - `xpriv` must be a valid point... | derivation | +| `derivation_xpriv_to_xpub` | Derive public key from extended private key # Safety - `xpriv` must be a va... | derivation | +| `derivation_xpub_fingerprint` | Get fingerprint from extended public key (4 bytes) # Safety - `xpub` must b... | derivation | +| `derivation_xpub_free` | Free extended public key # Safety - `xpub` must be a valid pointer to an FF... | derivation | +| `derivation_xpub_to_string` | Get extended public key as string # Safety - `xpub` must be a valid pointer... | derivation | +| `free_u32_array` | Free a u32 array allocated by this library # Safety - `array` must be a val... | account_collection | +| `string_free` | Free a string # Safety - `s` must be a valid pointer created by C string cr... | utils | + +## Detailed Function Documentation + +### Initialization - Detailed + +#### `key_wallet_ffi_initialize` + +```c +key_wallet_ffi_initialize() -> bool +``` + +**Description:** +Initialize the library + +**Module:** `lib` + +--- + +#### `key_wallet_ffi_version` + +```c +key_wallet_ffi_version() -> *const c_char +``` + +**Description:** +Get library version Returns a static string that should NOT be freed by the caller + +**Module:** `lib` + +--- + +### Error Handling - Detailed + +#### `account_result_free_error` + +```c +account_result_free_error(result: *mut FFIAccountResult) -> () +``` + +**Description:** +Free an account result's error message (if any) Note: This does NOT free the account handle itself - use account_free for that # Safety - `result` must be a valid pointer to an FFIAccountResult - The error_message field must be either null or a valid CString allocated by this library - The caller must ensure the result pointer remains valid for the duration of this call + +**Safety:** +- `result` must be a valid pointer to an FFIAccountResult - The error_message field must be either null or a valid CString allocated by this library - The caller must ensure the result pointer remains valid for the duration of this call + +**Module:** `account` + +--- + +#### `error_message_free` + +```c +error_message_free(message: *mut c_char) -> () +``` + +**Description:** +Free an error message # Safety - `message` must be a valid pointer to a C string that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `message` must be a valid pointer to a C string that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `error` + +--- + +#### `managed_account_result_free_error` + +```c +managed_account_result_free_error(result: *mut FFIManagedAccountResult) -> () +``` + +**Description:** +Free a managed account result's error message (if any) Note: This does NOT free the account handle itself - use managed_account_free for that # Safety - `result` must be a valid pointer to an FFIManagedAccountResult - The error_message field must be either null or a valid CString allocated by this library - The caller must ensure the result pointer remains valid for the duration of this call + +**Safety:** +- `result` must be a valid pointer to an FFIManagedAccountResult - The error_message field must be either null or a valid CString allocated by this library - The caller must ensure the result pointer remains valid for the duration of this call + +**Module:** `managed_account` + +--- + +### Wallet Manager - Detailed + +#### `wallet_manager_add_wallet_from_mnemonic` + +```c +wallet_manager_add_wallet_from_mnemonic(manager: *mut FFIWalletManager, mnemonic: *const c_char, passphrase: *const c_char, network: FFINetworks, error: *mut FFIError,) -> bool +``` + +**Description:** +Add a wallet from mnemonic to the manager (backward compatibility) # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes` + +```c +wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes(manager: *mut FFIWalletManager, mnemonic: *const c_char, passphrase: *const c_char, network: FFINetworks, birth_height: c_uint, account_options: *const crate::types::FFIWalletAccountCreationOptions, downgrade_to_pubkey_wallet: bool, allow_external_signing: bool, wallet_bytes_out: *mut *mut u8, wallet_bytes_len_out: *mut usize, wallet_id_out: *mut u8, error: *mut FFIError,) -> bool +``` + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_add_wallet_from_mnemonic_with_options` + +```c +wallet_manager_add_wallet_from_mnemonic_with_options(manager: *mut FFIWalletManager, mnemonic: *const c_char, passphrase: *const c_char, network: FFINetworks, account_options: *const crate::types::FFIWalletAccountCreationOptions, error: *mut FFIError,) -> bool +``` + +**Description:** +Add a wallet from mnemonic to the manager with options # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_create` + +```c +wallet_manager_create(error: *mut FFIError) -> *mut FFIWalletManager +``` + +**Description:** +Create a new wallet manager + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_current_height` + +```c +wallet_manager_current_height(manager: *const FFIWalletManager, network: FFINetworks, error: *mut FFIError,) -> c_uint +``` + +**Description:** +Get current height for a network # Safety - `manager` must be a valid pointer to an FFIWalletManager - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_free` + +```c +wallet_manager_free(manager: *mut FFIWalletManager) -> () +``` + +**Description:** +Free wallet manager # Safety - `manager` must be a valid pointer to an FFIWalletManager that was created by this library - The pointer must not be used after calling this function - This function must only be called once per manager + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager that was created by this library - The pointer must not be used after calling this function - This function must only be called once per manager + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_free_addresses` + +```c +wallet_manager_free_addresses(addresses: *mut *mut c_char, count: usize) -> () +``` + +**Description:** +Free address array # Safety - `addresses` must be a valid pointer to an array of C string pointers allocated by this library - `count` must match the original allocation size - Each address pointer in the array must be either null or a valid C string allocated by this library - The pointers must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `addresses` must be a valid pointer to an array of C string pointers allocated by this library - `count` must match the original allocation size - Each address pointer in the array must be either null or a valid C string allocated by this library - The pointers must not be used after calling this function - This function must only be called once per allocation + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_free_wallet_bytes` + +```c +wallet_manager_free_wallet_bytes(wallet_bytes: *mut u8, bytes_len: usize) -> () +``` + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_free_wallet_ids` + +```c +wallet_manager_free_wallet_ids(wallet_ids: *mut u8, count: usize) -> () +``` + +**Description:** +Free wallet IDs buffer # Safety - `wallet_ids` must be a valid pointer to a buffer allocated by this library - `count` must match the number of wallet IDs in the buffer - The pointer must not be used after calling this function - This function must only be called once per buffer + +**Safety:** +- `wallet_ids` must be a valid pointer to a buffer allocated by this library - `count` must match the number of wallet IDs in the buffer - The pointer must not be used after calling this function - This function must only be called once per buffer + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_change_address` + +```c +wallet_manager_get_change_address(manager: *mut FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get next change address for a wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_managed_wallet_info` + +```c +wallet_manager_get_managed_wallet_info(manager: *const FFIWalletManager, wallet_id: *const u8, error: *mut FFIError,) -> *mut crate::managed_wallet::FFIManagedWalletInfo +``` + +**Description:** +Get managed wallet info from the manager Returns a reference to the managed wallet info if found # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned managed wallet info must be freed with managed_wallet_info_free() + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned managed wallet info must be freed with managed_wallet_info_free() + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_monitored_addresses` + +```c +wallet_manager_get_monitored_addresses(manager: *const FFIWalletManager, network: FFINetworks, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get monitored addresses for a network # Safety - `manager` must be a valid pointer to an FFIWalletManager - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_receive_address` + +```c +wallet_manager_get_receive_address(manager: *mut FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get next receive address for a wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_wallet` + +```c +wallet_manager_get_wallet(manager: *const FFIWalletManager, wallet_id: *const u8, error: *mut FFIError,) -> *const crate::types::FFIWallet +``` + +**Description:** +Get a wallet from the manager Returns a reference to the wallet if found # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned wallet must be freed with wallet_free_const() + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned wallet must be freed with wallet_free_const() + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_wallet_balance` + +```c +wallet_manager_get_wallet_balance(manager: *const FFIWalletManager, wallet_id: *const u8, confirmed_out: *mut u64, unconfirmed_out: *mut u64, error: *mut FFIError,) -> bool +``` + +**Description:** +Get wallet balance Returns the confirmed and unconfirmed balance for a specific wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `confirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) - `unconfirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `confirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) - `unconfirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_get_wallet_ids` + +```c +wallet_manager_get_wallet_ids(manager: *const FFIWalletManager, wallet_ids_out: *mut *mut u8, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get wallet IDs # Safety - `manager` must be a valid pointer to an FFIWalletManager - `wallet_ids_out` must be a valid pointer to a pointer that will receive the wallet IDs - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `wallet_ids_out` must be a valid pointer to a pointer that will receive the wallet IDs - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_import_wallet_from_bytes` + +```c +wallet_manager_import_wallet_from_bytes(manager: *mut FFIWalletManager, wallet_bytes: *const u8, wallet_bytes_len: usize, wallet_id_out: *mut u8, error: *mut FFIError,) -> bool +``` + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_process_transaction` + +```c +wallet_manager_process_transaction(manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, network: FFINetworks, context: *const crate::types::FFITransactionContextDetails, update_state_if_found: bool, error: *mut FFIError,) -> bool +``` + +**Description:** +Process a transaction through all wallets Checks a transaction against all wallets and updates their states if relevant. Returns true if the transaction was relevant to at least one wallet. # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `tx_bytes` must be a valid pointer to transaction bytes - `tx_len` must be the length of the transaction bytes - `network` is the network type - `context` must be a valid pointer to FFITransactionContextDetails - `update_state_if_found` indicates whether to update wallet state when transaction is relevant - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `tx_bytes` must be a valid pointer to transaction bytes - `tx_len` must be the length of the transaction bytes - `network` is the network type - `context` must be a valid pointer to FFITransactionContextDetails - `update_state_if_found` indicates whether to update wallet state when transaction is relevant - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_update_height` + +```c +wallet_manager_update_height(manager: *mut FFIWalletManager, network: FFINetworks, height: c_uint, error: *mut FFIError,) -> bool +``` + +**Description:** +Update block height for a network # Safety - `manager` must be a valid pointer to an FFIWalletManager - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +#### `wallet_manager_wallet_count` + +```c +wallet_manager_wallet_count(manager: *const FFIWalletManager, error: *mut FFIError,) -> usize +``` + +**Description:** +Get wallet count # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet_manager` + +--- + +### Wallet Operations - Detailed + +#### `account_get_parent_wallet_id` + +```c +account_get_parent_wallet_id(account: *const FFIAccount) -> *const u8 +``` + +**Description:** +Get the parent wallet ID of an account # Safety - `account` must be a valid pointer to an FFIAccount instance - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null - The returned pointer is valid only as long as the account exists - The caller should copy the data if needed for longer use + +**Safety:** +- `account` must be a valid pointer to an FFIAccount instance - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null - The returned pointer is valid only as long as the account exists - The caller should copy the data if needed for longer use + +**Module:** `account` + +--- + +#### `bls_account_get_parent_wallet_id` + +```c +bls_account_get_parent_wallet_id(account: *const FFIBLSAccount,) -> *const u8 +``` + +**Module:** `account` + +--- + +#### `eddsa_account_get_parent_wallet_id` + +```c +eddsa_account_get_parent_wallet_id(account: *const FFIEdDSAAccount,) -> *const u8 +``` + +**Module:** `account` + +--- + +#### `ffi_managed_wallet_free` + +```c +ffi_managed_wallet_free(managed_wallet: *mut FFIManagedWallet) -> () +``` + +**Description:** +Free a managed wallet (FFIManagedWallet type) # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - This function must only be called once per managed wallet + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - This function must only be called once per managed wallet + +**Module:** `transaction_checking` + +--- + +#### `managed_account_get_parent_wallet_id` + +```c +managed_account_get_parent_wallet_id(wallet_id: *const u8) -> *const u8 +``` + +**Description:** +Get the parent wallet ID of a managed account Note: ManagedAccount doesn't store the parent wallet ID directly. The wallet ID is typically known from the context (e.g., when getting the account from a managed wallet). # Safety - `wallet_id` must be a valid pointer to a 32-byte wallet ID buffer that was provided by the caller - The returned pointer is the same as the input pointer for convenience - The caller must not free the returned pointer as it's the same as the input + +**Safety:** +- `wallet_id` must be a valid pointer to a 32-byte wallet ID buffer that was provided by the caller - The returned pointer is the same as the input pointer for convenience - The caller must not free the returned pointer as it's the same as the input + +**Module:** `managed_account` + +--- + +#### `managed_wallet_check_transaction` + +```c +managed_wallet_check_transaction(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetworks, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: c_uint, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +``` + +**Description:** +Check if a transaction belongs to the wallet This function checks a transaction against all relevant account types in the wallet and returns detailed information about which accounts are affected. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - `wallet` must be a valid pointer to an FFIWallet (needed for address generation) - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `result_out` must be a valid pointer to store the result - `error` must be a valid pointer to an FFIError - The affected_accounts array in the result must be freed with `transaction_check_result_free` + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - `wallet` must be a valid pointer to an FFIWallet (needed for address generation) - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `result_out` must be a valid pointer to store the result - `error` must be a valid pointer to an FFIError - The affected_accounts array in the result must be freed with `transaction_check_result_free` + +**Module:** `transaction_checking` + +--- + +#### `managed_wallet_free` + +```c +managed_wallet_free(managed_wallet: *mut FFIManagedWalletInfo) -> () +``` + +**Description:** +Free managed wallet info # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo or null - After calling this function, the pointer becomes invalid and must not be used + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo or null - After calling this function, the pointer becomes invalid and must not be used + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_generate_addresses_to_index` + +```c +managed_wallet_generate_addresses_to_index(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, target_index: c_uint, error: *mut FFIError,) -> bool +``` + +**Description:** +Generate addresses up to a specific index in a pool This ensures that addresses up to and including the specified index exist in the pool. This is useful for wallet recovery or when specific indices are needed. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - `wallet` must be a valid pointer to an FFIWallet (for key derivation) - `error` must be a valid pointer to an FFIError or null + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - `wallet` must be a valid pointer to an FFIWallet (for key derivation) - `error` must be a valid pointer to an FFIError or null + +**Module:** `address_pool` + +--- + +#### `managed_wallet_get_account` + +```c +managed_wallet_get_account(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, account_type: FFIAccountType,) -> FFIManagedAccountResult +``` + +**Description:** +Get a managed account from a managed wallet This function gets a ManagedAccount from the wallet manager's managed wallet info, returning a managed account handle that wraps the ManagedAccount. # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - The caller must ensure all pointers remain valid for the duration of this call - The returned account must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - The caller must ensure all pointers remain valid for the duration of this call - The returned account must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account` + +--- + +#### `managed_wallet_get_account_collection` + +```c +managed_wallet_get_account_collection(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, error: *mut FFIError,) -> *mut FFIManagedAccountCollection +``` + +**Description:** +Get managed account collection for a specific network from wallet manager # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The returned pointer must be freed with `managed_account_collection_free` when no longer needed + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The returned pointer must be freed with `managed_account_collection_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_wallet_get_account_count` + +```c +managed_wallet_get_account_count(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, error: *mut FFIError,) -> c_uint +``` + +**Description:** +Get number of accounts in a managed wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `managed_account` + +--- + +#### `managed_wallet_get_address_pool_info` + +```c +managed_wallet_get_address_pool_info(managed_wallet: *const FFIManagedWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, info_out: *mut FFIAddressPoolInfo, error: *mut FFIError,) -> bool +``` + +**Description:** +Get address pool information for an account # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - `info_out` must be a valid pointer to store the pool info - `error` must be a valid pointer to an FFIError or null + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - `info_out` must be a valid pointer to store the pool info - `error` must be a valid pointer to an FFIError or null + +**Module:** `address_pool` + +--- + +#### `managed_wallet_get_balance` + +```c +managed_wallet_get_balance(managed_wallet: *const FFIManagedWalletInfo, confirmed_out: *mut u64, unconfirmed_out: *mut u64, locked_out: *mut u64, total_out: *mut u64, error: *mut FFIError,) -> bool +``` + +**Description:** +Get wallet balance from managed wallet info Returns the balance breakdown including confirmed, unconfirmed, locked, and total amounts. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `confirmed_out` must be a valid pointer to store the confirmed balance - `unconfirmed_out` must be a valid pointer to store the unconfirmed balance - `locked_out` must be a valid pointer to store the locked balance - `total_out` must be a valid pointer to store the total balance - `error` must be a valid pointer to an FFIError + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `confirmed_out` must be a valid pointer to store the confirmed balance - `unconfirmed_out` must be a valid pointer to store the unconfirmed balance - `locked_out` must be a valid pointer to store the locked balance - `total_out` must be a valid pointer to store the total balance - `error` must be a valid pointer to an FFIError + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_get_bip_44_external_address_range` + +```c +managed_wallet_get_bip_44_external_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get BIP44 external (receive) addresses in the specified range Returns external addresses from start_index (inclusive) to end_index (exclusive). If addresses in the range haven't been generated yet, they will be generated. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `addresses_out` must be a valid pointer to store the address array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - Free the result with address_array_free(addresses_out, count_out) + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `addresses_out` must be a valid pointer to store the address array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - Free the result with address_array_free(addresses_out, count_out) + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_get_bip_44_internal_address_range` + +```c +managed_wallet_get_bip_44_internal_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get BIP44 internal (change) addresses in the specified range Returns internal addresses from start_index (inclusive) to end_index (exclusive). If addresses in the range haven't been generated yet, they will be generated. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `addresses_out` must be a valid pointer to store the address array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - Free the result with address_array_free(addresses_out, count_out) + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `addresses_out` must be a valid pointer to store the address array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - Free the result with address_array_free(addresses_out, count_out) + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_get_next_bip44_change_address` + +```c +managed_wallet_get_next_bip44_change_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get the next unused change address Generates the next unused change address for the specified account. This properly manages address gaps and updates the managed wallet state. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed by the caller + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed by the caller + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_get_next_bip44_receive_address` + +```c +managed_wallet_get_next_bip44_receive_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get the next unused receive address Generates the next unused receive address for the specified account. This properly manages address gaps and updates the managed wallet state. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed by the caller + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed by the caller + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_get_top_up_account_with_registration_index` + +```c +managed_wallet_get_top_up_account_with_registration_index(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, registration_index: c_uint,) -> FFIManagedAccountResult +``` + +**Description:** +Get a managed IdentityTopUp account with a specific registration index This is used for top-up accounts that are bound to a specific identity. Returns a managed account handle that wraps the ManagedAccount. # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - The caller must ensure all pointers remain valid for the duration of this call - The returned account must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `network` must specify exactly one network - The caller must ensure all pointers remain valid for the duration of this call - The returned account must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account` + +--- + +#### `managed_wallet_get_utxos` + +```c +managed_wallet_get_utxos(managed_info: *const FFIManagedWalletInfo, network: FFINetworks, utxos_out: *mut *mut FFIUTXO, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get all UTXOs from managed wallet info # Safety - `managed_info` must be a valid pointer to an FFIManagedWalletInfo instance - `utxos_out` must be a valid pointer to store the UTXO array pointer - `count_out` must be a valid pointer to store the UTXO count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned UTXO array must be freed with `utxo_array_free` when no longer needed + +**Safety:** +- `managed_info` must be a valid pointer to an FFIManagedWalletInfo instance - `utxos_out` must be a valid pointer to store the UTXO array pointer - `count_out` must be a valid pointer to store the UTXO count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned UTXO array must be freed with `utxo_array_free` when no longer needed + +**Module:** `utxo` + +--- + +#### `managed_wallet_info_free` + +```c +managed_wallet_info_free(wallet_info: *mut FFIManagedWalletInfo) -> () +``` + +**Description:** +Free managed wallet info returned by wallet_manager_get_managed_wallet_info # Safety - `wallet_info` must be a valid pointer returned by wallet_manager_get_managed_wallet_info or null - After calling this function, the pointer becomes invalid and must not be used + +**Safety:** +- `wallet_info` must be a valid pointer returned by wallet_manager_get_managed_wallet_info or null - After calling this function, the pointer becomes invalid and must not be used + +**Module:** `managed_wallet` + +--- + +#### `managed_wallet_mark_address_used` + +```c +managed_wallet_mark_address_used(managed_wallet: *mut FFIManagedWallet, network: FFINetworks, address: *const c_char, error: *mut FFIError,) -> bool +``` + +**Description:** +Mark an address as used in the pool This updates the pool's tracking of which addresses have been used, which is important for gap limit management and wallet recovery. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - `address` must be a valid C string - `error` must be a valid pointer to an FFIError or null + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - `address` must be a valid C string - `error` must be a valid pointer to an FFIError or null + +**Module:** `address_pool` + +--- + +#### `managed_wallet_set_gap_limit` + +```c +managed_wallet_set_gap_limit(managed_wallet: *mut FFIManagedWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, gap_limit: c_uint, error: *mut FFIError,) -> bool +``` + +**Description:** +Set the gap limit for an address pool The gap limit determines how many unused addresses to maintain at the end of the pool. This is important for wallet recovery and address discovery. # Safety - `managed_wallet` must be a valid pointer to an FFIManagedWallet - `error` must be a valid pointer to an FFIError or null + +**Safety:** +- `managed_wallet` must be a valid pointer to an FFIManagedWallet - `error` must be a valid pointer to an FFIError or null + +**Module:** `address_pool` + +--- + +#### `wallet_add_account` + +```c +wallet_add_account(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint,) -> crate::types::FFIAccountResult +``` + +**Description:** +Add an account to the wallet without xpub # Safety This function dereferences a raw pointer to FFIWallet. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The FFIWallet remains valid for the duration of this call + +**Safety:** +This function dereferences a raw pointer to FFIWallet. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The FFIWallet remains valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_add_account_with_string_xpub` + +```c +wallet_add_account_with_string_xpub(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_string: *const c_char,) -> crate::types::FFIAccountResult +``` + +**Description:** +Add an account to the wallet with xpub as string # Safety This function dereferences raw pointers. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The xpub_string pointer is either null or points to a valid null-terminated C string - The FFIWallet remains valid for the duration of this call + +**Safety:** +This function dereferences raw pointers. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The xpub_string pointer is either null or points to a valid null-terminated C string - The FFIWallet remains valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_add_account_with_xpub_bytes` + +```c +wallet_add_account_with_xpub_bytes(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_bytes: *const u8, xpub_len: usize,) -> crate::types::FFIAccountResult +``` + +**Description:** +Add an account to the wallet with xpub as byte array # Safety This function dereferences raw pointers. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The xpub_bytes pointer is either null or points to at least xpub_len bytes - The FFIWallet remains valid for the duration of this call + +**Safety:** +This function dereferences raw pointers. The caller must ensure that: - The wallet pointer is either null or points to a valid FFIWallet - The xpub_bytes pointer is either null or points to at least xpub_len bytes - The FFIWallet remains valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_build_transaction` + +```c +wallet_build_transaction(wallet: *mut FFIWallet, _network: FFINetworks, account_index: c_uint, outputs: *const FFITxOutput, outputs_count: usize, fee_per_kb: u64, tx_bytes_out: *mut *mut u8, tx_len_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Build a transaction # Safety - `wallet` must be a valid pointer to an FFIWallet - `outputs` must be a valid pointer to an array of FFITxOutput with at least `outputs_count` elements - `tx_bytes_out` must be a valid pointer to store the transaction bytes pointer - `tx_len_out` must be a valid pointer to store the transaction length - `error` must be a valid pointer to an FFIError - The returned transaction bytes must be freed with `transaction_bytes_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `outputs` must be a valid pointer to an array of FFITxOutput with at least `outputs_count` elements - `tx_bytes_out` must be a valid pointer to store the transaction bytes pointer - `tx_len_out` must be a valid pointer to store the transaction length - `error` must be a valid pointer to an FFIError - The returned transaction bytes must be freed with `transaction_bytes_free` + +**Module:** `transaction` + +--- + +#### `wallet_check_transaction` + +```c +wallet_check_transaction(wallet: *mut FFIWallet, network: FFINetworks, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: u32, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +``` + +**Description:** +Check if a transaction belongs to the wallet using ManagedWalletInfo # Safety - `wallet` must be a valid mutable pointer to an FFIWallet - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `inputs_spent_out` must be a valid pointer to store the spent inputs count - `addresses_used_out` must be a valid pointer to store the used addresses count - `new_balance_out` must be a valid pointer to store the new balance - `new_address_out` must be a valid pointer to store the address array pointer - `new_address_count_out` must be a valid pointer to store the address count - `error` must be a valid pointer to an FFIError + +**Safety:** +- `wallet` must be a valid mutable pointer to an FFIWallet - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `inputs_spent_out` must be a valid pointer to store the spent inputs count - `addresses_used_out` must be a valid pointer to store the used addresses count - `new_balance_out` must be a valid pointer to store the new balance - `new_address_out` must be a valid pointer to store the address array pointer - `new_address_count_out` must be a valid pointer to store the address count - `error` must be a valid pointer to an FFIError + +**Module:** `transaction` + +--- + +#### `wallet_create_from_mnemonic` + +```c +wallet_create_from_mnemonic(mnemonic: *const c_char, passphrase: *const c_char, network: FFINetworks, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new wallet from mnemonic (backward compatibility - single network) # Safety - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned pointer must be freed with `wallet_free` when no longer needed + +**Safety:** +- `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned pointer must be freed with `wallet_free` when no longer needed + +**Module:** `wallet` + +--- + +#### `wallet_create_from_mnemonic_with_options` + +```c +wallet_create_from_mnemonic_with_options(mnemonic: *const c_char, passphrase: *const c_char, networks: FFINetworks, account_options: *const FFIWalletAccountCreationOptions, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new wallet from mnemonic with options # Safety - `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned pointer must be freed with `wallet_free` when no longer needed + +**Safety:** +- `mnemonic` must be a valid pointer to a null-terminated C string - `passphrase` must be a valid pointer to a null-terminated C string or null - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned pointer must be freed with `wallet_free` when no longer needed + +**Module:** `wallet` + +--- + +#### `wallet_create_from_seed` + +```c +wallet_create_from_seed(seed: *const u8, seed_len: usize, network: FFINetworks, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new wallet from seed (backward compatibility) # Safety - `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_create_from_seed_with_options` + +```c +wallet_create_from_seed_with_options(seed: *const u8, seed_len: usize, networks: FFINetworks, account_options: *const FFIWalletAccountCreationOptions, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new wallet from seed with options # Safety - `seed` must be a valid pointer to a byte array of `seed_len` length - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `seed` must be a valid pointer to a byte array of `seed_len` length - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_create_managed_wallet` + +```c +wallet_create_managed_wallet(wallet: *const FFIWallet, error: *mut FFIError,) -> *mut FFIManagedWallet +``` + +**Description:** +Create a managed wallet from a regular wallet This creates a ManagedWalletInfo instance from a Wallet, which includes address pools and transaction checking capabilities. # Safety - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError or null - The returned pointer must be freed with `ffi_managed_wallet_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError or null - The returned pointer must be freed with `ffi_managed_wallet_free` + +**Module:** `transaction_checking` + +--- + +#### `wallet_create_random` + +```c +wallet_create_random(network: FFINetworks, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new random wallet (backward compatibility) # Safety - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the pointer remains valid for the duration of this call + +**Safety:** +- `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the pointer remains valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_create_random_with_options` + +```c +wallet_create_random_with_options(networks: FFINetworks, account_options: *const FFIWalletAccountCreationOptions, error: *mut FFIError,) -> *mut FFIWallet +``` + +**Description:** +Create a new random wallet with options # Safety - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_derive_extended_private_key` + +```c +wallet_derive_extended_private_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPrivateKey +``` + +**Description:** +Derive extended private key at a specific path Returns an opaque FFIExtendedPrivateKey pointer that must be freed with extended_private_key_free # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_private_key_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_private_key_free` + +**Module:** `keys` + +--- + +#### `wallet_derive_extended_public_key` + +```c +wallet_derive_extended_public_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPublicKey +``` + +**Description:** +Derive extended public key at a specific path Returns an opaque FFIExtendedPublicKey pointer that must be freed with extended_public_key_free # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_public_key_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_public_key_free` + +**Module:** `keys` + +--- + +#### `wallet_derive_private_key` + +```c +wallet_derive_private_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPrivateKey +``` + +**Description:** +Derive private key at a specific path Returns an opaque FFIPrivateKey pointer that must be freed with private_key_free # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `private_key_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `private_key_free` + +**Module:** `keys` + +--- + +#### `wallet_derive_private_key_as_wif` + +```c +wallet_derive_private_key_as_wif(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Derive private key at a specific path and return as WIF string # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `wallet_derive_public_key` + +```c +wallet_derive_public_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPublicKey +``` + +**Description:** +Derive public key at a specific path Returns an opaque FFIPublicKey pointer that must be freed with public_key_free # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `public_key_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `public_key_free` + +**Module:** `keys` + +--- + +#### `wallet_derive_public_key_as_hex` + +```c +wallet_derive_public_key_as_hex(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Derive public key at a specific path and return as hex string # Safety - `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `derivation_path` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `wallet_free` + +```c +wallet_free(wallet: *mut FFIWallet) -> () +``` + +**Description:** +Free a wallet # Safety - `wallet` must be a valid pointer to an FFIWallet that was created by this library - The pointer must not be used after calling this function - This function must only be called once per wallet + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet that was created by this library - The pointer must not be used after calling this function - This function must only be called once per wallet + +**Module:** `wallet` + +--- + +#### `wallet_free_const` + +```c +wallet_free_const(wallet: *const FFIWallet) -> () +``` + +**Description:** +Free a const wallet handle This is a const-safe wrapper for wallet_free() that accepts a const pointer. Use this function when you have a *const FFIWallet that needs to be freed, such as wallets returned from wallet_manager_get_wallet(). # Safety - `wallet` must be a valid pointer created by wallet creation functions or null - After calling this function, the pointer becomes invalid - This function must only be called once per wallet - The wallet must have been allocated by this library (not stack or static memory) + +**Safety:** +- `wallet` must be a valid pointer created by wallet creation functions or null - After calling this function, the pointer becomes invalid - This function must only be called once per wallet - The wallet must have been allocated by this library (not stack or static memory) + +**Module:** `wallet` + +--- + +#### `wallet_generate_provider_key` + +```c +wallet_generate_provider_key(wallet: *const FFIWallet, network: FFINetworks, key_type: FFIProviderKeyType, key_index: c_uint, include_private: bool, info_out: *mut FFIProviderKeyInfo, error: *mut FFIError,) -> bool +``` + +**Description:** +Generate a provider key at a specific index This generates a provider key (BLS or EdDSA) at the specified index. For voting, owner, and operator keys, this generates BLS keys. For platform keys, this generates EdDSA keys. # Safety - `wallet` must be a valid pointer to an FFIWallet - `info_out` must be a valid pointer to store the key info - `error` must be a valid pointer to an FFIError or null - The returned public_key, private_key, and derivation_path must be freed by the caller + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `info_out` must be a valid pointer to store the key info - `error` must be a valid pointer to an FFIError or null - The returned public_key, private_key, and derivation_path must be freed by the caller + +**Module:** `provider_keys` + +--- + +#### `wallet_get_account` + +```c +wallet_get_account(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, account_type: FFIAccountType,) -> FFIAccountResult +``` + +**Description:** +Get an account handle for a specific account type Returns a result containing either the account handle or an error # Safety - `wallet` must be a valid pointer to an FFIWallet instance - The caller must ensure the wallet pointer remains valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - The caller must ensure the wallet pointer remains valid for the duration of this call + +**Module:** `account` + +--- + +#### `wallet_get_account_collection` + +```c +wallet_get_account_collection(wallet: *const FFIWallet, network: FFINetworks, error: *mut FFIError,) -> *mut FFIAccountCollection +``` + +**Description:** +Get account collection for a specific network from wallet # Safety - `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The returned pointer must be freed with `account_collection_free` when no longer needed + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The returned pointer must be freed with `account_collection_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `wallet_get_account_count` + +```c +wallet_get_account_count(wallet: *const FFIWallet, network: FFINetworks, error: *mut FFIError,) -> c_uint +``` + +**Description:** +Get number of accounts # Safety - `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure both pointers remain valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure both pointers remain valid for the duration of this call + +**Module:** `account` + +--- + +#### `wallet_get_account_xpriv` + +```c +wallet_get_account_xpriv(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended private key for account # Safety - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `wallet_get_account_xpub` + +```c +wallet_get_account_xpub(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended public key for account # Safety - `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `wallet_get_id` + +```c +wallet_get_id(wallet: *const FFIWallet, id_out: *mut u8, error: *mut FFIError,) -> bool +``` + +**Description:** +Get wallet ID (32-byte hash) # Safety - `wallet` must be a valid pointer to an FFIWallet - `id_out` must be a valid pointer to a 32-byte buffer - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `id_out` must be a valid pointer to a 32-byte buffer - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_get_top_up_account_with_registration_index` + +```c +wallet_get_top_up_account_with_registration_index(wallet: *const FFIWallet, network: FFINetworks, registration_index: c_uint,) -> FFIAccountResult +``` + +**Description:** +Get an IdentityTopUp account handle with a specific registration index This is used for top-up accounts that are bound to a specific identity Returns a result containing either the account handle or an error # Safety - `wallet` must be a valid pointer to an FFIWallet instance - The caller must ensure the wallet pointer remains valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - The caller must ensure the wallet pointer remains valid for the duration of this call + +**Module:** `account` + +--- + +#### `wallet_get_xpub` + +```c +wallet_get_xpub(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended public key for account # Safety - `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned C string must be freed by the caller when no longer needed + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - The returned C string must be freed by the caller when no longer needed + +**Module:** `wallet` + +--- + +#### `wallet_has_mnemonic` + +```c +wallet_has_mnemonic(wallet: *const FFIWallet, error: *mut FFIError,) -> bool +``` + +**Description:** +Check if wallet has mnemonic # Safety - `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_is_watch_only` + +```c +wallet_is_watch_only(wallet: *const FFIWallet, error: *mut FFIError,) -> bool +``` + +**Description:** +Check if wallet is watch-only # Safety - `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet instance - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `wallet` + +--- + +#### `wallet_sign_transaction` + +```c +wallet_sign_transaction(wallet: *const FFIWallet, _network: FFINetworks, tx_bytes: *const u8, tx_len: usize, signed_tx_out: *mut *mut u8, signed_len_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Sign a transaction # Safety - `wallet` must be a valid pointer to an FFIWallet - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `signed_tx_out` must be a valid pointer to store the signed transaction bytes pointer - `signed_len_out` must be a valid pointer to store the signed transaction length - `error` must be a valid pointer to an FFIError - The returned signed transaction bytes must be freed with `transaction_bytes_free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `signed_tx_out` must be a valid pointer to store the signed transaction bytes pointer - `signed_len_out` must be a valid pointer to store the signed transaction length - `error` must be a valid pointer to an FFIError - The returned signed transaction bytes must be freed with `transaction_bytes_free` + +**Module:** `transaction` + +--- + +#### `wallet_sign_with_provider_key` + +```c +wallet_sign_with_provider_key(wallet: *const FFIWallet, network: FFINetworks, key_type: FFIProviderKeyType, _key_index: c_uint, data: *const u8, data_len: usize, signature_out: *mut *mut u8, signature_len_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Sign data with a provider key This signs arbitrary data with the provider key at the specified index. For BLS keys, this produces a BLS signature. For EdDSA keys, this produces an Ed25519 signature. # Safety - `wallet` must be a valid pointer to an FFIWallet - `data` must be a valid pointer to data with at least `data_len` bytes - `signature_out` must be a valid pointer to store the signature pointer - `signature_len_out` must be a valid pointer to store the signature length - `error` must be a valid pointer to an FFIError or null - The returned signature must be freed with `libc::free` + +**Safety:** +- `wallet` must be a valid pointer to an FFIWallet - `data` must be a valid pointer to data with at least `data_len` bytes - `signature_out` must be a valid pointer to store the signature pointer - `signature_len_out` must be a valid pointer to store the signature length - `error` must be a valid pointer to an FFIError or null - The returned signature must be freed with `libc::free` + +**Module:** `provider_keys` + +--- + +### Account Management - Detailed + +#### `account_collection_count` + +```c +account_collection_count(collection: *const FFIAccountCollection,) -> c_uint +``` + +**Description:** +Get the total number of accounts in the collection # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_free` + +```c +account_collection_free(collection: *mut FFIAccountCollection) -> () +``` + +**Description:** +Free an account collection handle # Safety - `collection` must be a valid pointer to an FFIAccountCollection created by this library - `collection` must not be used after calling this function + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection created by this library - `collection` must not be used after calling this function + +**Module:** `account_collection` + +--- + +#### `account_collection_get_bip32_account` + +```c +account_collection_get_bip32_account(collection: *const FFIAccountCollection, index: c_uint,) -> *mut FFIAccount +``` + +**Description:** +Get a BIP32 account by index from the collection # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_bip32_indices` + +```c +account_collection_get_bip32_indices(collection: *const FFIAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all BIP32 account indices # Safety - `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_bip44_account` + +```c +account_collection_get_bip44_account(collection: *const FFIAccountCollection, index: c_uint,) -> *mut FFIAccount +``` + +**Description:** +Get a BIP44 account by index from the collection # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_bip44_indices` + +```c +account_collection_get_bip44_indices(collection: *const FFIAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all BIP44 account indices # Safety - `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_coinjoin_account` + +```c +account_collection_get_coinjoin_account(collection: *const FFIAccountCollection, index: c_uint,) -> *mut FFIAccount +``` + +**Description:** +Get a CoinJoin account by index from the collection # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_coinjoin_indices` + +```c +account_collection_get_coinjoin_indices(collection: *const FFIAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all CoinJoin account indices # Safety - `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_identity_invitation` + +```c +account_collection_get_identity_invitation(collection: *const FFIAccountCollection,) -> *mut FFIAccount +``` + +**Description:** +Get the identity invitation account if it exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_identity_registration` + +```c +account_collection_get_identity_registration(collection: *const FFIAccountCollection,) -> *mut FFIAccount +``` + +**Description:** +Get the identity registration account if it exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_identity_topup` + +```c +account_collection_get_identity_topup(collection: *const FFIAccountCollection, registration_index: c_uint,) -> *mut FFIAccount +``` + +**Description:** +Get an identity topup account by registration index # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_identity_topup_indices` + +```c +account_collection_get_identity_topup_indices(collection: *const FFIAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all identity topup registration indices # Safety - `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_identity_topup_not_bound` + +```c +account_collection_get_identity_topup_not_bound(collection: *const FFIAccountCollection,) -> *mut FFIAccount +``` + +**Description:** +Get the identity topup not bound account if it exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_provider_operator_keys` + +```c +account_collection_get_provider_operator_keys(collection: *const FFIAccountCollection,) -> *mut std::os::raw::c_void +``` + +**Description:** +Get the provider operator keys account if it exists Note: Returns null if the `bls` feature is not enabled # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `bls_account_free` when no longer needed (when BLS is enabled) + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `bls_account_free` when no longer needed (when BLS is enabled) + +**Module:** `account_collection` + +--- + +#### `account_collection_get_provider_owner_keys` + +```c +account_collection_get_provider_owner_keys(collection: *const FFIAccountCollection,) -> *mut FFIAccount +``` + +**Description:** +Get the provider owner keys account if it exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_get_provider_platform_keys` + +```c +account_collection_get_provider_platform_keys(collection: *const FFIAccountCollection,) -> *mut std::os::raw::c_void +``` + +**Description:** +Get the provider platform keys account if it exists Note: Returns null if the `eddsa` feature is not enabled # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `eddsa_account_free` when no longer needed (when EdDSA is enabled) + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `eddsa_account_free` when no longer needed (when EdDSA is enabled) + +**Module:** `account_collection` + +--- + +#### `account_collection_get_provider_voting_keys` + +```c +account_collection_get_provider_voting_keys(collection: *const FFIAccountCollection,) -> *mut FFIAccount +``` + +**Description:** +Get the provider voting keys account if it exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_free` when no longer needed + +**Module:** `account_collection` + +--- + +#### `account_collection_has_identity_invitation` + +```c +account_collection_has_identity_invitation(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if identity invitation account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_identity_registration` + +```c +account_collection_has_identity_registration(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if identity registration account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_identity_topup_not_bound` + +```c +account_collection_has_identity_topup_not_bound(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if identity topup not bound account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_provider_operator_keys` + +```c +account_collection_has_provider_operator_keys(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if provider operator keys account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_provider_owner_keys` + +```c +account_collection_has_provider_owner_keys(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if provider owner keys account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_provider_platform_keys` + +```c +account_collection_has_provider_platform_keys(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if provider platform keys account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_has_provider_voting_keys` + +```c +account_collection_has_provider_voting_keys(collection: *const FFIAccountCollection,) -> bool +``` + +**Description:** +Check if provider voting keys account exists # Safety - `collection` must be a valid pointer to an FFIAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection + +**Module:** `account_collection` + +--- + +#### `account_collection_summary` + +```c +account_collection_summary(collection: *const FFIAccountCollection,) -> *mut c_char +``` + +**Description:** +Get a human-readable summary of all accounts in the collection Returns a formatted string showing all account types and their indices. The format is designed to be clear and readable for end users. # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned string must be freed with `string_free` when no longer needed - Returns null if the collection pointer is null + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned string must be freed with `string_free` when no longer needed - Returns null if the collection pointer is null + +**Module:** `account_collection` + +--- + +#### `account_collection_summary_data` + +```c +account_collection_summary_data(collection: *const FFIAccountCollection,) -> *mut FFIAccountCollectionSummary +``` + +**Description:** +Get structured account collection summary data Returns a struct containing arrays of indices for each account type and boolean flags for special accounts. This provides Swift with programmatic access to account information. # Safety - `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_collection_summary_free` when no longer needed - Returns null if the collection pointer is null + +**Safety:** +- `collection` must be a valid pointer to an FFIAccountCollection - The returned pointer must be freed with `account_collection_summary_free` when no longer needed - Returns null if the collection pointer is null + +**Module:** `account_collection` + +--- + +#### `account_collection_summary_free` + +```c +account_collection_summary_free(summary: *mut FFIAccountCollectionSummary,) -> () +``` + +**Description:** +Free an account collection summary and all its allocated memory # Safety - `summary` must be a valid pointer to an FFIAccountCollectionSummary created by `account_collection_summary_data` - `summary` must not be used after calling this function + +**Safety:** +- `summary` must be a valid pointer to an FFIAccountCollectionSummary created by `account_collection_summary_data` - `summary` must not be used after calling this function + +**Module:** `account_collection` + +--- + +#### `account_free` + +```c +account_free(account: *mut FFIAccount) -> () +``` + +**Description:** +Free an account handle # Safety - `account` must be a valid pointer to an FFIAccount that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `account` must be a valid pointer to an FFIAccount that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `account` + +--- + +#### `account_get_account_type` + +```c +account_get_account_type(account: *const FFIAccount, out_index: *mut c_uint,) -> FFIAccountType +``` + +**Description:** +Get the account type of an account # Safety - `account` must be a valid pointer to an FFIAccount instance - `out_index` must be a valid pointer to a c_uint where the index will be stored - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + +**Safety:** +- `account` must be a valid pointer to an FFIAccount instance - `out_index` must be a valid pointer to a c_uint where the index will be stored - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + +**Module:** `account` + +--- + +#### `account_get_extended_public_key_as_string` + +```c +account_get_extended_public_key_as_string(account: *const FFIAccount,) -> *mut std::os::raw::c_char +``` + +**Description:** +Get the extended public key of an account as a string # Safety - `account` must be a valid pointer to an FFIAccount instance - The returned string must be freed by the caller using `string_free` - Returns NULL if the account is null + +**Safety:** +- `account` must be a valid pointer to an FFIAccount instance - The returned string must be freed by the caller using `string_free` - Returns NULL if the account is null + +**Module:** `account` + +--- + +#### `account_get_is_watch_only` + +```c +account_get_is_watch_only(account: *const FFIAccount) -> bool +``` + +**Description:** +Check if an account is watch-only # Safety - `account` must be a valid pointer to an FFIAccount instance - Returns false if the account is null + +**Safety:** +- `account` must be a valid pointer to an FFIAccount instance - Returns false if the account is null + +**Module:** `account` + +--- + +#### `account_get_network` + +```c +account_get_network(account: *const FFIAccount) -> FFINetworks +``` + +**Description:** +Get the network of an account # Safety - `account` must be a valid pointer to an FFIAccount instance - Returns FFINetwork::NoNetworks if the account is null + +**Safety:** +- `account` must be a valid pointer to an FFIAccount instance - Returns FFINetwork::NoNetworks if the account is null + +**Module:** `account` + +--- + +#### `bls_account_free` + +```c +bls_account_free(account: *mut FFIBLSAccount) -> () +``` + +**Module:** `account` + +--- + +#### `bls_account_get_account_type` + +```c +bls_account_get_account_type(account: *const FFIBLSAccount, out_index: *mut c_uint,) -> FFIAccountType +``` + +**Module:** `account` + +--- + +#### `bls_account_get_extended_public_key_as_string` + +```c +bls_account_get_extended_public_key_as_string(account: *const FFIBLSAccount,) -> *mut std::os::raw::c_char +``` + +**Module:** `account` + +--- + +#### `bls_account_get_is_watch_only` + +```c +bls_account_get_is_watch_only(account: *const FFIBLSAccount) -> bool +``` + +**Module:** `account` + +--- + +#### `bls_account_get_network` + +```c +bls_account_get_network(account: *const FFIBLSAccount) -> FFINetworks +``` + +**Module:** `account` + +--- + +#### `derivation_bip44_account_path` + +```c +derivation_bip44_account_path(network: FFINetworks, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive a BIP44 account path (m/44'/5'/account') + +**Module:** `derivation` + +--- + +#### `eddsa_account_free` + +```c +eddsa_account_free(account: *mut FFIEdDSAAccount) -> () +``` + +**Module:** `account` + +--- + +#### `eddsa_account_get_account_type` + +```c +eddsa_account_get_account_type(account: *const FFIEdDSAAccount, out_index: *mut c_uint,) -> FFIAccountType +``` + +**Module:** `account` + +--- + +#### `eddsa_account_get_extended_public_key_as_string` + +```c +eddsa_account_get_extended_public_key_as_string(account: *const FFIEdDSAAccount,) -> *mut std::os::raw::c_char +``` + +**Module:** `account` + +--- + +#### `eddsa_account_get_is_watch_only` + +```c +eddsa_account_get_is_watch_only(account: *const FFIEdDSAAccount) -> bool +``` + +**Module:** `account` + +--- + +#### `eddsa_account_get_network` + +```c +eddsa_account_get_network(account: *const FFIEdDSAAccount) -> FFINetworks +``` + +**Module:** `account` + +--- + +#### `managed_account_collection_count` + +```c +managed_account_collection_count(collection: *const FFIManagedAccountCollection,) -> c_uint +``` + +**Description:** +Get the total number of accounts in the managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_free` + +```c +managed_account_collection_free(collection: *mut FFIManagedAccountCollection,) -> () +``` + +**Description:** +Free a managed account collection handle # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection created by this library - `collection` must not be used after calling this function + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection created by this library - `collection` must not be used after calling this function + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_bip32_account` + +```c +managed_account_collection_get_bip32_account(collection: *const FFIManagedAccountCollection, index: c_uint,) -> *mut FFIManagedAccount +``` + +**Description:** +Get a BIP32 account by index from the managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_bip32_indices` + +```c +managed_account_collection_get_bip32_indices(collection: *const FFIManagedAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all BIP32 account indices from managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_bip44_account` + +```c +managed_account_collection_get_bip44_account(collection: *const FFIManagedAccountCollection, index: c_uint,) -> *mut FFIManagedAccount +``` + +**Description:** +Get a BIP44 account by index from the managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_bip44_indices` + +```c +managed_account_collection_get_bip44_indices(collection: *const FFIManagedAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all BIP44 account indices from managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_coinjoin_account` + +```c +managed_account_collection_get_coinjoin_account(collection: *const FFIManagedAccountCollection, index: c_uint,) -> *mut FFIManagedAccount +``` + +**Description:** +Get a CoinJoin account by index from the managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_coinjoin_indices` + +```c +managed_account_collection_get_coinjoin_indices(collection: *const FFIManagedAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all CoinJoin account indices from managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_identity_invitation` + +```c +managed_account_collection_get_identity_invitation(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccount +``` + +**Description:** +Get the identity invitation account if it exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_identity_registration` + +```c +managed_account_collection_get_identity_registration(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccount +``` + +**Description:** +Get the identity registration account if it exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_identity_topup` + +```c +managed_account_collection_get_identity_topup(collection: *const FFIManagedAccountCollection, registration_index: c_uint,) -> *mut FFIManagedAccount +``` + +**Description:** +Get an identity topup account by registration index from managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_identity_topup_indices` + +```c +managed_account_collection_get_identity_topup_indices(collection: *const FFIManagedAccountCollection, out_indices: *mut *mut c_uint, out_count: *mut usize,) -> bool +``` + +**Description:** +Get all identity topup registration indices from managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - `out_indices` must be a valid pointer to store the indices array - `out_count` must be a valid pointer to store the count - The returned array must be freed with `free_u32_array` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_identity_topup_not_bound` + +```c +managed_account_collection_get_identity_topup_not_bound(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccount +``` + +**Description:** +Get the identity topup not bound account if it exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - `manager` must be a valid pointer to an FFIWalletManager - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - `manager` must be a valid pointer to an FFIWalletManager - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_provider_operator_keys` + +```c +managed_account_collection_get_provider_operator_keys(collection: *const FFIManagedAccountCollection,) -> *mut std::os::raw::c_void +``` + +**Description:** +Get the provider operator keys account if it exists in managed collection Note: Returns null if the `bls` feature is not enabled # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed (when BLS is enabled) + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed (when BLS is enabled) + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_provider_owner_keys` + +```c +managed_account_collection_get_provider_owner_keys(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccount +``` + +**Description:** +Get the provider owner keys account if it exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_provider_platform_keys` + +```c +managed_account_collection_get_provider_platform_keys(collection: *const FFIManagedAccountCollection,) -> *mut std::os::raw::c_void +``` + +**Description:** +Get the provider platform keys account if it exists in managed collection Note: Returns null if the `eddsa` feature is not enabled # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed (when EdDSA is enabled) + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed (when EdDSA is enabled) + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_get_provider_voting_keys` + +```c +managed_account_collection_get_provider_voting_keys(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccount +``` + +**Description:** +Get the provider voting keys account if it exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_free` when no longer needed + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_identity_invitation` + +```c +managed_account_collection_has_identity_invitation(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if identity invitation account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_identity_registration` + +```c +managed_account_collection_has_identity_registration(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if identity registration account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_identity_topup_not_bound` + +```c +managed_account_collection_has_identity_topup_not_bound(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if identity topup not bound account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_provider_operator_keys` + +```c +managed_account_collection_has_provider_operator_keys(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if provider operator keys account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_provider_owner_keys` + +```c +managed_account_collection_has_provider_owner_keys(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if provider owner keys account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_provider_platform_keys` + +```c +managed_account_collection_has_provider_platform_keys(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if provider platform keys account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_has_provider_voting_keys` + +```c +managed_account_collection_has_provider_voting_keys(collection: *const FFIManagedAccountCollection,) -> bool +``` + +**Description:** +Check if provider voting keys account exists in managed collection # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_summary` + +```c +managed_account_collection_summary(collection: *const FFIManagedAccountCollection,) -> *mut c_char +``` + +**Description:** +Get a human-readable summary of all accounts in the managed collection Returns a formatted string showing all account types and their indices. The format is designed to be clear and readable for end users. # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned string must be freed with `string_free` when no longer needed - Returns null if the collection pointer is null + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned string must be freed with `string_free` when no longer needed - Returns null if the collection pointer is null + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_summary_data` + +```c +managed_account_collection_summary_data(collection: *const FFIManagedAccountCollection,) -> *mut FFIManagedAccountCollectionSummary +``` + +**Description:** +Get structured account collection summary data for managed collection Returns a struct containing arrays of indices for each account type and boolean flags for special accounts. This provides Swift with programmatic access to account information. # Safety - `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_collection_summary_free` when no longer needed - Returns null if the collection pointer is null + +**Safety:** +- `collection` must be a valid pointer to an FFIManagedAccountCollection - The returned pointer must be freed with `managed_account_collection_summary_free` when no longer needed - Returns null if the collection pointer is null + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_collection_summary_free` + +```c +managed_account_collection_summary_free(summary: *mut FFIManagedAccountCollectionSummary,) -> () +``` + +**Description:** +Free a managed account collection summary and all its allocated memory # Safety - `summary` must be a valid pointer to an FFIManagedAccountCollectionSummary created by `managed_account_collection_summary_data` - `summary` must not be used after calling this function + +**Safety:** +- `summary` must be a valid pointer to an FFIManagedAccountCollectionSummary created by `managed_account_collection_summary_data` - `summary` must not be used after calling this function + +**Module:** `managed_account_collection` + +--- + +#### `managed_account_free` + +```c +managed_account_free(account: *mut FFIManagedAccount) -> () +``` + +**Description:** +Free a managed account handle # Safety - `account` must be a valid pointer to an FFIManagedAccount that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `managed_account` + +--- + +#### `managed_account_get_account_type` + +```c +managed_account_get_account_type(account: *const FFIManagedAccount, index_out: *mut c_uint,) -> FFIAccountType +``` + +**Description:** +Get the account type of a managed account # Safety - `account` must be a valid pointer to an FFIManagedAccount instance - `index_out` must be a valid pointer to receive the account index (or null) + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance - `index_out` must be a valid pointer to receive the account index (or null) + +**Module:** `managed_account` + +--- + +#### `managed_account_get_address_pool` + +```c +managed_account_get_address_pool(account: *const FFIManagedAccount, pool_type: FFIAddressPoolType,) -> *mut FFIAddressPool +``` + +**Description:** +Get an address pool from a managed account by type This function returns the appropriate address pool based on the pool type parameter. For Standard accounts with External/Internal pool types, returns the corresponding pool. For non-standard accounts with Single pool type, returns their single address pool. # Safety - `manager` must be a valid pointer to an FFIWalletManager instance - `account` must be a valid pointer to an FFIManagedAccount instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - The returned pool must be freed with `address_pool_free` when no longer needed + +**Safety:** +- `manager` must be a valid pointer to an FFIWalletManager instance - `account` must be a valid pointer to an FFIManagedAccount instance - `wallet_id` must be a valid pointer to a 32-byte wallet ID - The returned pool must be freed with `address_pool_free` when no longer needed + +**Module:** `managed_account` + +--- + +#### `managed_account_get_balance` + +```c +managed_account_get_balance(account: *const FFIManagedAccount, balance_out: *mut crate::types::FFIBalance,) -> bool +``` + +**Description:** +Get the balance of a managed account # Safety - `account` must be a valid pointer to an FFIManagedAccount instance - `balance_out` must be a valid pointer to an FFIBalance structure + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance - `balance_out` must be a valid pointer to an FFIBalance structure + +**Module:** `managed_account` + +--- + +#### `managed_account_get_external_address_pool` + +```c +managed_account_get_external_address_pool(account: *const FFIManagedAccount,) -> *mut FFIAddressPool +``` + +**Description:** +Get the external address pool from a managed account This function returns the external (receive) address pool for Standard accounts. Returns NULL for account types that don't have separate external/internal pools. # Safety - `account` must be a valid pointer to an FFIManagedAccount instance - The returned pool must be freed with `address_pool_free` when no longer needed + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance - The returned pool must be freed with `address_pool_free` when no longer needed + +**Module:** `managed_account` + +--- + +#### `managed_account_get_index` + +```c +managed_account_get_index(account: *const FFIManagedAccount) -> c_uint +``` + +**Description:** +Get the account index from a managed account Returns the primary account index for Standard and CoinJoin accounts. Returns 0 for account types that don't have an index (like Identity or Provider accounts). # Safety - `account` must be a valid pointer to an FFIManagedAccount instance + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance + +**Module:** `managed_account` + +--- + +#### `managed_account_get_internal_address_pool` + +```c +managed_account_get_internal_address_pool(account: *const FFIManagedAccount,) -> *mut FFIAddressPool +``` + +**Description:** +Get the internal address pool from a managed account This function returns the internal (change) address pool for Standard accounts. Returns NULL for account types that don't have separate external/internal pools. # Safety - `account` must be a valid pointer to an FFIManagedAccount instance - The returned pool must be freed with `address_pool_free` when no longer needed + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance - The returned pool must be freed with `address_pool_free` when no longer needed + +**Module:** `managed_account` + +--- + +#### `managed_account_get_is_watch_only` + +```c +managed_account_get_is_watch_only(account: *const FFIManagedAccount,) -> bool +``` + +**Description:** +Check if a managed account is watch-only # Safety - `account` must be a valid pointer to an FFIManagedAccount instance + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance + +**Module:** `managed_account` + +--- + +#### `managed_account_get_network` + +```c +managed_account_get_network(account: *const FFIManagedAccount,) -> FFINetworks +``` + +**Description:** +Get the network of a managed account # Safety - `account` must be a valid pointer to an FFIManagedAccount instance + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance + +**Module:** `managed_account` + +--- + +#### `managed_account_get_transaction_count` + +```c +managed_account_get_transaction_count(account: *const FFIManagedAccount,) -> c_uint +``` + +**Description:** +Get the number of transactions in a managed account # Safety - `account` must be a valid pointer to an FFIManagedAccount instance + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance + +**Module:** `managed_account` + +--- + +#### `managed_account_get_utxo_count` + +```c +managed_account_get_utxo_count(account: *const FFIManagedAccount,) -> c_uint +``` + +**Description:** +Get the number of UTXOs in a managed account # Safety - `account` must be a valid pointer to an FFIManagedAccount instance + +**Safety:** +- `account` must be a valid pointer to an FFIManagedAccount instance + +**Module:** `managed_account` + +--- + +### Address Management - Detailed + +#### `address_array_free` + +```c +address_array_free(addresses: *mut *mut c_char, count: usize) -> () +``` + +**Description:** +Free address array # Safety - `addresses` must be a valid pointer to an array of address strings or null - Each address in the array must be a valid C string pointer - `count` must be the correct number of addresses in the array - After calling this function, all pointers become invalid + +**Safety:** +- `addresses` must be a valid pointer to an array of address strings or null - Each address in the array must be a valid C string pointer - `count` must be the correct number of addresses in the array - After calling this function, all pointers become invalid + +**Module:** `address` + +--- + +#### `address_free` + +```c +address_free(address: *mut c_char) -> () +``` + +**Description:** +Free address string # Safety - `address` must be a valid pointer created by address functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `address` must be a valid pointer created by address functions or null - After calling this function, the pointer becomes invalid + +**Module:** `address` + +--- + +#### `address_get_type` + +```c +address_get_type(address: *const c_char, network: FFINetworks, error: *mut FFIError,) -> c_uchar +``` + +**Description:** +Get address type Returns: - 0: P2PKH address - 1: P2SH address - 2: Other address type - u8::MAX (255): Error occurred # Safety - `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError + +**Safety:** +- `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError + +**Module:** `address` + +--- + +#### `address_info_array_free` + +```c +address_info_array_free(infos: *mut *mut FFIAddressInfo, count: usize) -> () +``` + +**Description:** +Free an array of FFIAddressInfo structures # Safety - `infos` must be a valid pointer to an array of FFIAddressInfo pointers allocated by this library or null - `count` must be the exact number of elements in the array - The pointers must not be used after calling this function + +**Safety:** +- `infos` must be a valid pointer to an array of FFIAddressInfo pointers allocated by this library or null - `count` must be the exact number of elements in the array - The pointers must not be used after calling this function + +**Module:** `address_pool` + +--- + +#### `address_info_free` + +```c +address_info_free(info: *mut FFIAddressInfo) -> () +``` + +**Description:** +Free a single FFIAddressInfo structure # Safety - `info` must be a valid pointer to an FFIAddressInfo allocated by this library or null - The pointer must not be used after calling this function + +**Safety:** +- `info` must be a valid pointer to an FFIAddressInfo allocated by this library or null - The pointer must not be used after calling this function + +**Module:** `address_pool` + +--- + +#### `address_pool_free` + +```c +address_pool_free(pool: *mut FFIAddressPool) -> () +``` + +**Description:** +Free an address pool handle # Safety - `pool` must be a valid pointer to an FFIAddressPool that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `pool` must be a valid pointer to an FFIAddressPool that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `address_pool` + +--- + +#### `address_pool_get_address_at_index` + +```c +address_pool_get_address_at_index(pool: *const FFIAddressPool, index: u32, error: *mut FFIError,) -> *mut FFIAddressInfo +``` + +**Description:** +Get a single address info at a specific index from the pool Returns detailed information about the address at the given index, or NULL if the index is out of bounds or not generated yet. # Safety - `pool` must be a valid pointer to an FFIAddressPool - `error` must be a valid pointer to an FFIError or null - The returned FFIAddressInfo must be freed using `address_info_free` + +**Safety:** +- `pool` must be a valid pointer to an FFIAddressPool - `error` must be a valid pointer to an FFIError or null - The returned FFIAddressInfo must be freed using `address_info_free` + +**Module:** `address_pool` + +--- + +#### `address_pool_get_addresses_in_range` + +```c +address_pool_get_addresses_in_range(pool: *const FFIAddressPool, start_index: u32, end_index: u32, count_out: *mut usize, error: *mut FFIError,) -> *mut *mut FFIAddressInfo +``` + +**Description:** +Get a range of addresses from the pool Returns an array of FFIAddressInfo structures for addresses in the range [start_index, end_index). The count_out parameter will be set to the actual number of addresses returned. Note: This function only reads existing addresses from the pool. It does not generate new addresses. Use managed_wallet_generate_addresses_to_index if you need to generate addresses first. # Safety - `pool` must be a valid pointer to an FFIAddressPool - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError or null - The returned array must be freed using `address_info_array_free` + +**Safety:** +- `pool` must be a valid pointer to an FFIAddressPool - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError or null - The returned array must be freed using `address_info_array_free` + +**Module:** `address_pool` + +--- + +#### `address_validate` + +```c +address_validate(address: *const c_char, network: FFINetworks, error: *mut FFIError,) -> bool +``` + +**Description:** +Validate an address # Safety - `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError + +**Safety:** +- `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError + +**Module:** `address` + +--- + +### Transaction Management - Detailed + +#### `transaction_bytes_free` + +```c +transaction_bytes_free(tx_bytes: *mut u8) -> () +``` + +**Description:** +Free transaction bytes # Safety - `tx_bytes` must be a valid pointer created by transaction functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `tx_bytes` must be a valid pointer created by transaction functions or null - After calling this function, the pointer becomes invalid + +**Module:** `transaction` + +--- + +#### `transaction_check_result_free` + +```c +transaction_check_result_free(result: *mut FFITransactionCheckResult) -> () +``` + +**Description:** +Free a transaction check result # Safety - `result` must be a valid pointer to an FFITransactionCheckResult - This function must only be called once per result + +**Safety:** +- `result` must be a valid pointer to an FFITransactionCheckResult - This function must only be called once per result + +**Module:** `transaction_checking` + +--- + +#### `transaction_classify` + +```c +transaction_classify(tx_bytes: *const u8, tx_len: usize, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get the transaction classification for routing Returns a string describing the transaction type (e.g., "Standard", "CoinJoin", "AssetLock", "AssetUnlock", "ProviderRegistration", etc.) # Safety - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `error` must be a valid pointer to an FFIError or null - The returned string must be freed by the caller + +**Safety:** +- `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes - `error` must be a valid pointer to an FFIError or null - The returned string must be freed by the caller + +**Module:** `transaction_checking` + +--- + +#### `utxo_array_free` + +```c +utxo_array_free(utxos: *mut FFIUTXO, count: usize) -> () +``` + +**Description:** +Free UTXO array # Safety - `utxos` must be a valid pointer to an array of FFIUTXO structs allocated by this library - `count` must match the number of UTXOs in the array - The pointer must not be used after calling this function - This function must only be called once per array + +**Safety:** +- `utxos` must be a valid pointer to an array of FFIUTXO structs allocated by this library - `count` must match the number of UTXOs in the array - The pointer must not be used after calling this function - This function must only be called once per array + +**Module:** `utxo` + +--- + +### Key Management - Detailed + +#### `bip38_decrypt_private_key` + +```c +bip38_decrypt_private_key(encrypted_key: *const c_char, passphrase: *const c_char, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Decrypt a BIP38 encrypted private key # Safety This function is unsafe because it dereferences raw pointers: - `encrypted_key` must be a valid, null-terminated C string - `passphrase` must be a valid, null-terminated C string - `error` must be a valid pointer to an FFIError or null + +**Safety:** +This function is unsafe because it dereferences raw pointers: - `encrypted_key` must be a valid, null-terminated C string - `passphrase` must be a valid, null-terminated C string - `error` must be a valid pointer to an FFIError or null + +**Module:** `bip38` + +--- + +#### `bip38_encrypt_private_key` + +```c +bip38_encrypt_private_key(private_key: *const c_char, passphrase: *const c_char, _network: FFINetworks, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Encrypt a private key with BIP38 # Safety This function is unsafe because it dereferences raw pointers: - `private_key` must be a valid, null-terminated C string - `passphrase` must be a valid, null-terminated C string - `error` must be a valid pointer to an FFIError or null + +**Safety:** +This function is unsafe because it dereferences raw pointers: - `private_key` must be a valid, null-terminated C string - `passphrase` must be a valid, null-terminated C string - `error` must be a valid pointer to an FFIError or null + +**Module:** `bip38` + +--- + +#### `derivation_derive_private_key_from_seed` + +```c +derivation_derive_private_key_from_seed(seed: *const u8, seed_len: usize, path: *const c_char, network: FFINetworks, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +``` + +**Description:** +Derive private key for a specific path from seed # Safety - `seed` must be a valid pointer to a byte array of `seed_len` length - `path` must be a valid pointer to a null-terminated C string - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Safety:** +- `seed` must be a valid pointer to a byte array of `seed_len` length - `path` must be a valid pointer to a null-terminated C string - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call + +**Module:** `derivation` + +--- + +#### `derivation_new_master_key` + +```c +derivation_new_master_key(seed: *const u8, seed_len: usize, network: FFINetworks, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +``` + +**Description:** +Create a new master extended private key from seed # Safety - `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the seed pointer remains valid for the duration of this call + +**Safety:** +- `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the seed pointer remains valid for the duration of this call + +**Module:** `derivation` + +--- + +#### `dip9_derive_identity_key` + +```c +dip9_derive_identity_key(seed: *const u8, seed_len: usize, network: FFINetworks, identity_index: c_uint, key_index: c_uint, key_type: FFIDerivationPathType, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +``` + +**Description:** +Derive key using DIP9 path constants for identity # Safety - `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the seed pointer remains valid for the duration of this call + +**Safety:** +- `seed` must be a valid pointer to a byte array of `seed_len` length - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure the seed pointer remains valid for the duration of this call + +**Module:** `derivation` + +--- + +#### `extended_private_key_free` + +```c +extended_private_key_free(key: *mut FFIExtendedPrivateKey) -> () +``` + +**Description:** +Free an extended private key # Safety - `key` must be a valid pointer created by extended private key functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `key` must be a valid pointer created by extended private key functions or null - After calling this function, the pointer becomes invalid + +**Module:** `keys` + +--- + +#### `extended_private_key_get_private_key` + +```c +extended_private_key_get_private_key(extended_key: *const FFIExtendedPrivateKey, error: *mut FFIError,) -> *mut FFIPrivateKey +``` + +**Description:** +Get the private key from an extended private key Extracts the non-extended private key from an extended private key. # Safety - `extended_key` must be a valid pointer to an FFIExtendedPrivateKey - `error` must be a valid pointer to an FFIError - The returned FFIPrivateKey must be freed with `private_key_free` + +**Safety:** +- `extended_key` must be a valid pointer to an FFIExtendedPrivateKey - `error` must be a valid pointer to an FFIError - The returned FFIPrivateKey must be freed with `private_key_free` + +**Module:** `keys` + +--- + +#### `extended_private_key_to_string` + +```c +extended_private_key_to_string(key: *const FFIExtendedPrivateKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended private key as string (xprv format) Returns the extended private key in base58 format (xprv... for mainnet, tprv... for testnet) # Safety - `key` must be a valid pointer to an FFIExtendedPrivateKey - `network` is ignored; the network is encoded in the extended key - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `key` must be a valid pointer to an FFIExtendedPrivateKey - `network` is ignored; the network is encoded in the extended key - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `extended_public_key_free` + +```c +extended_public_key_free(key: *mut FFIExtendedPublicKey) -> () +``` + +**Description:** +Free an extended public key # Safety - `key` must be a valid pointer created by extended public key functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `key` must be a valid pointer created by extended public key functions or null - After calling this function, the pointer becomes invalid + +**Module:** `keys` + +--- + +#### `extended_public_key_get_public_key` + +```c +extended_public_key_get_public_key(extended_key: *const FFIExtendedPublicKey, error: *mut FFIError,) -> *mut FFIPublicKey +``` + +**Description:** +Get the public key from an extended public key Extracts the non-extended public key from an extended public key. # Safety - `extended_key` must be a valid pointer to an FFIExtendedPublicKey - `error` must be a valid pointer to an FFIError - The returned FFIPublicKey must be freed with `public_key_free` + +**Safety:** +- `extended_key` must be a valid pointer to an FFIExtendedPublicKey - `error` must be a valid pointer to an FFIError - The returned FFIPublicKey must be freed with `public_key_free` + +**Module:** `keys` + +--- + +#### `extended_public_key_to_string` + +```c +extended_public_key_to_string(key: *const FFIExtendedPublicKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended public key as string (xpub format) Returns the extended public key in base58 format (xpub... for mainnet, tpub... for testnet) # Safety - `key` must be a valid pointer to an FFIExtendedPublicKey - `network` is ignored; the network is encoded in the extended key - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `key` must be a valid pointer to an FFIExtendedPublicKey - `network` is ignored; the network is encoded in the extended key - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `private_key_free` + +```c +private_key_free(key: *mut FFIPrivateKey) -> () +``` + +**Description:** +Free a private key # Safety - `key` must be a valid pointer created by private key functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `key` must be a valid pointer created by private key functions or null - After calling this function, the pointer becomes invalid + +**Module:** `keys` + +--- + +#### `private_key_to_wif` + +```c +private_key_to_wif(key: *const FFIPrivateKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get private key as WIF string from FFIPrivateKey # Safety - `key` must be a valid pointer to an FFIPrivateKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `key` must be a valid pointer to an FFIPrivateKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +#### `provider_key_info_free` + +```c +provider_key_info_free(info: *mut FFIProviderKeyInfo) -> () +``` + +**Description:** +Free provider key info # Safety - `info` must be a valid pointer to an FFIProviderKeyInfo - This function must only be called once per info structure + +**Safety:** +- `info` must be a valid pointer to an FFIProviderKeyInfo - This function must only be called once per info structure + +**Module:** `provider_keys` + +--- + +#### `public_key_free` + +```c +public_key_free(key: *mut FFIPublicKey) -> () +``` + +**Description:** +Free a public key # Safety - `key` must be a valid pointer created by public key functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `key` must be a valid pointer created by public key functions or null - After calling this function, the pointer becomes invalid + +**Module:** `keys` + +--- + +#### `public_key_to_hex` + +```c +public_key_to_hex(key: *const FFIPublicKey, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get public key as hex string from FFIPublicKey # Safety - `key` must be a valid pointer to an FFIPublicKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `key` must be a valid pointer to an FFIPublicKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `keys` + +--- + +### Mnemonic Operations - Detailed + +#### `mnemonic_free` + +```c +mnemonic_free(mnemonic: *mut c_char) -> () +``` + +**Description:** +Free a mnemonic string # Safety - `mnemonic` must be a valid pointer created by mnemonic generation functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `mnemonic` must be a valid pointer created by mnemonic generation functions or null - After calling this function, the pointer becomes invalid + +**Module:** `mnemonic` + +--- + +#### `mnemonic_generate` + +```c +mnemonic_generate(word_count: c_uint, error: *mut FFIError) -> *mut c_char +``` + +**Description:** +Generate a new mnemonic with specified word count (12, 15, 18, 21, or 24) + +**Module:** `mnemonic` + +--- + +#### `mnemonic_generate_with_language` + +```c +mnemonic_generate_with_language(word_count: c_uint, language: FFILanguage, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Generate a new mnemonic with specified language and word count + +**Module:** `mnemonic` + +--- + +#### `mnemonic_to_seed` + +```c +mnemonic_to_seed(mnemonic: *const c_char, passphrase: *const c_char, seed_out: *mut u8, seed_len: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Convert mnemonic to seed with optional passphrase # Safety - `mnemonic` must be a valid null-terminated C string - `passphrase` must be a valid null-terminated C string or null - `seed_out` must be a valid pointer to a buffer of at least 64 bytes - `seed_len` must be a valid pointer to store the seed length - `error` must be a valid pointer to an FFIError + +**Safety:** +- `mnemonic` must be a valid null-terminated C string - `passphrase` must be a valid null-terminated C string or null - `seed_out` must be a valid pointer to a buffer of at least 64 bytes - `seed_len` must be a valid pointer to store the seed length - `error` must be a valid pointer to an FFIError + +**Module:** `mnemonic` + +--- + +#### `mnemonic_validate` + +```c +mnemonic_validate(mnemonic: *const c_char, error: *mut FFIError) -> bool +``` + +**Description:** +Validate a mnemonic phrase # Safety - `mnemonic` must be a valid null-terminated C string or null - `error` must be a valid pointer to an FFIError + +**Safety:** +- `mnemonic` must be a valid null-terminated C string or null - `error` must be a valid pointer to an FFIError + +**Module:** `mnemonic` + +--- + +#### `mnemonic_word_count` + +```c +mnemonic_word_count(mnemonic: *const c_char, error: *mut FFIError,) -> c_uint +``` + +**Description:** +Get word count from mnemonic # Safety - `mnemonic` must be a valid null-terminated C string or null - `error` must be a valid pointer to an FFIError + +**Safety:** +- `mnemonic` must be a valid null-terminated C string or null - `error` must be a valid pointer to an FFIError + +**Module:** `mnemonic` + +--- + +### Utility Functions - Detailed + +#### `derivation_bip44_payment_path` + +```c +derivation_bip44_payment_path(network: FFINetworks, account_index: c_uint, is_change: bool, address_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive a BIP44 payment path (m/44'/5'/account'/change/index) + +**Module:** `derivation` + +--- + +#### `derivation_coinjoin_path` + +```c +derivation_coinjoin_path(network: FFINetworks, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive CoinJoin path (m/9'/5'/4'/account') + +**Module:** `derivation` + +--- + +#### `derivation_identity_authentication_path` + +```c +derivation_identity_authentication_path(network: FFINetworks, identity_index: c_uint, key_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') + +**Module:** `derivation` + +--- + +#### `derivation_identity_registration_path` + +```c +derivation_identity_registration_path(network: FFINetworks, identity_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive identity registration path (m/9'/5'/5'/1'/index') + +**Module:** `derivation` + +--- + +#### `derivation_identity_topup_path` + +```c +derivation_identity_topup_path(network: FFINetworks, identity_index: c_uint, topup_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') + +**Module:** `derivation` + +--- + +#### `derivation_path_free` + +```c +derivation_path_free(indices: *mut u32, hardened: *mut bool, count: usize,) -> () +``` + +**Description:** +Free derivation path arrays Note: This function expects the count to properly free the slices # Safety - `indices` must be a valid pointer created by `derivation_path_parse` or null - `hardened` must be a valid pointer created by `derivation_path_parse` or null - `count` must match the count from `derivation_path_parse` - After calling this function, the pointers become invalid + +**Safety:** +- `indices` must be a valid pointer created by `derivation_path_parse` or null - `hardened` must be a valid pointer created by `derivation_path_parse` or null - `count` must match the count from `derivation_path_parse` - After calling this function, the pointers become invalid + +**Module:** `keys` + +--- + +#### `derivation_path_parse` + +```c +derivation_path_parse(path: *const c_char, indices_out: *mut *mut u32, hardened_out: *mut *mut bool, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Convert derivation path string to indices # Safety - `path` must be a valid null-terminated C string or null - `indices_out` must be a valid pointer to store the indices array pointer - `hardened_out` must be a valid pointer to store the hardened flags array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - The returned arrays must be freed with `derivation_path_free` + +**Safety:** +- `path` must be a valid null-terminated C string or null - `indices_out` must be a valid pointer to store the indices array pointer - `hardened_out` must be a valid pointer to store the hardened flags array pointer - `count_out` must be a valid pointer to store the count - `error` must be a valid pointer to an FFIError - The returned arrays must be freed with `derivation_path_free` + +**Module:** `keys` + +--- + +#### `derivation_string_free` + +```c +derivation_string_free(s: *mut c_char) -> () +``` + +**Description:** +Free derivation path string # Safety - `s` must be a valid pointer to a C string that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `s` must be a valid pointer to a C string that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `derivation` + +--- + +#### `derivation_xpriv_free` + +```c +derivation_xpriv_free(xpriv: *mut FFIExtendedPrivKey) -> () +``` + +**Description:** +Free extended private key # Safety - `xpriv` must be a valid pointer to an FFIExtendedPrivKey that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `xpriv` must be a valid pointer to an FFIExtendedPrivKey that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `derivation` + +--- + +#### `derivation_xpriv_to_string` + +```c +derivation_xpriv_to_string(xpriv: *const FFIExtendedPrivKey, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended private key as string # Safety - `xpriv` must be a valid pointer to an FFIExtendedPrivKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `xpriv` must be a valid pointer to an FFIExtendedPrivKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `derivation` + +--- + +#### `derivation_xpriv_to_xpub` + +```c +derivation_xpriv_to_xpub(xpriv: *const FFIExtendedPrivKey, error: *mut FFIError,) -> *mut FFIExtendedPubKey +``` + +**Description:** +Derive public key from extended private key # Safety - `xpriv` must be a valid pointer to an FFIExtendedPrivKey - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_public_key_free` + +**Safety:** +- `xpriv` must be a valid pointer to an FFIExtendedPrivKey - `error` must be a valid pointer to an FFIError - The returned pointer must be freed with `extended_public_key_free` + +**Module:** `derivation` + +--- + +#### `derivation_xpub_fingerprint` + +```c +derivation_xpub_fingerprint(xpub: *const FFIExtendedPubKey, fingerprint_out: *mut u8, error: *mut FFIError,) -> bool +``` + +**Description:** +Get fingerprint from extended public key (4 bytes) # Safety - `xpub` must be a valid pointer to an FFIExtendedPubKey - `fingerprint_out` must be a valid pointer to a buffer of at least 4 bytes - `error` must be a valid pointer to an FFIError + +**Safety:** +- `xpub` must be a valid pointer to an FFIExtendedPubKey - `fingerprint_out` must be a valid pointer to a buffer of at least 4 bytes - `error` must be a valid pointer to an FFIError + +**Module:** `derivation` + +--- + +#### `derivation_xpub_free` + +```c +derivation_xpub_free(xpub: *mut FFIExtendedPubKey) -> () +``` + +**Description:** +Free extended public key # Safety - `xpub` must be a valid pointer to an FFIExtendedPubKey that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Safety:** +- `xpub` must be a valid pointer to an FFIExtendedPubKey that was allocated by this library - The pointer must not be used after calling this function - This function must only be called once per allocation + +**Module:** `derivation` + +--- + +#### `derivation_xpub_to_string` + +```c +derivation_xpub_to_string(xpub: *const FFIExtendedPubKey, error: *mut FFIError,) -> *mut c_char +``` + +**Description:** +Get extended public key as string # Safety - `xpub` must be a valid pointer to an FFIExtendedPubKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Safety:** +- `xpub` must be a valid pointer to an FFIExtendedPubKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` + +**Module:** `derivation` + +--- + +#### `free_u32_array` + +```c +free_u32_array(array: *mut c_uint, count: usize) -> () +``` + +**Description:** +Free a u32 array allocated by this library # Safety - `array` must be a valid pointer to an array allocated by this library - `array` must not be used after calling this function + +**Safety:** +- `array` must be a valid pointer to an array allocated by this library - `array` must not be used after calling this function + +**Module:** `account_collection` + +--- + +#### `string_free` + +```c +string_free(s: *mut c_char) -> () +``` + +**Description:** +Free a string # Safety - `s` must be a valid pointer created by C string creation functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `s` must be a valid pointer created by C string creation functions or null - After calling this function, the pointer becomes invalid + +**Module:** `utils` + +--- + +## Type Definitions + +### Core Types + +- `FFIError` - Error handling structure +- `FFIWallet` - Wallet handle +- `FFIWalletManager` - Wallet manager handle +- `FFIBalance` - Balance information +- `FFIUTXO` - Unspent transaction output +- `FFINetworks` - Network enumeration + +## Memory Management + +### Important Rules + +1. **Ownership Transfer**: Functions returning pointers transfer ownership to the caller +2. **Cleanup Required**: All returned pointers must be freed using the appropriate `_free` or `_destroy` function +3. **Thread Safety**: Most functions are thread-safe, but check individual function documentation +4. **Error Handling**: Always check the `FFIError` parameter after function calls + +## Usage Examples + +### Basic Wallet Manager Usage + +```c +// Create wallet manager +FFIError error = {0}; +FFIWalletManager* manager = wallet_manager_create(&error); +if (error.code != 0) { + // Handle error +} + +// Get wallet count +size_t count = wallet_manager_wallet_count(manager, &error); + +// Clean up +wallet_manager_free(manager); +``` diff --git a/key-wallet-ffi/FFI_DOCS_README.md b/key-wallet-ffi/FFI_DOCS_README.md new file mode 100644 index 000000000..9b0a37e97 --- /dev/null +++ b/key-wallet-ffi/FFI_DOCS_README.md @@ -0,0 +1,111 @@ +# FFI Documentation Guide + +## Overview + +The `FFI_API.md` file contains comprehensive documentation for all FFI functions in the key-wallet-ffi library. This documentation is automatically generated from the source code to ensure it stays up-to-date. + +## Keeping Documentation Updated + +### Automatic Verification + +A GitHub Action automatically verifies that the FFI documentation is up-to-date on every push and pull request. If the documentation is out of sync, the CI will fail and provide instructions on how to update it. + +### Manual Updates + +To update the FFI documentation after making changes to FFI functions: + +```bash +# Using Make +make update-docs + +# Or directly with Python +cd key-wallet-ffi +python3 scripts/generate_ffi_docs.py +``` + +### Checking Documentation + +To verify the documentation is current without updating: + +```bash +# Using Make +make check-docs + +# Or directly with the script +bash scripts/check_ffi_docs.sh +``` + +## Documentation Structure + +The `FFI_API.md` file includes: + +1. **Table of Contents** - Quick navigation to different sections +2. **Function Reference** - Categorized list of all functions +3. **Detailed Documentation** - Full signatures and descriptions +4. **Type Definitions** - Core FFI types used +5. **Memory Management** - Important rules for FFI usage +6. **Usage Examples** - Sample code for common operations + +## Categories + +Functions are automatically categorized into: + +- Initialization +- Error Handling +- Wallet Manager +- Wallet Operations +- Account Management +- Address Management +- Transaction Management +- Key Management +- BIP38 Encryption +- UTXO Management +- Mnemonic Operations +- Utility Functions + +## Adding New FFI Functions + +When adding new FFI functions: + +1. Add the function with `#[no_mangle]` and `extern "C"` attributes +2. Include doc comments with `///` +3. Add safety documentation if the function is `unsafe` +4. Run `make update-docs` to regenerate documentation +5. Commit both the code changes and updated `FFI_API.md` + +## Example FFI Function + +```rust +/// Get wallet balance for a specific network +/// +/// # Safety +/// +/// - `manager` must be a valid pointer to an FFIWalletManager +/// - `wallet_id` must point to exactly 32 bytes +/// - `error` must be a valid pointer to an FFIError +#[no_mangle] +pub unsafe extern "C" fn wallet_manager_get_wallet_balance( + manager: *const FFIWalletManager, + wallet_id: *const u8, + wallet_id_len: usize, + network: FFINetworks, + error: *mut FFIError, +) -> FFIBalance { + // Implementation +} +``` + +## CI/CD Integration + +The documentation verification is integrated into the CI pipeline: + +1. **On Push/PR**: Verifies documentation is up-to-date +2. **On Failure**: Comments on PR with update instructions +3. **Required Check**: Must pass before merging + +## Tools + +- `scripts/generate_ffi_docs.py` - Python script that parses Rust files and generates documentation +- `scripts/check_ffi_docs.sh` - Bash script to verify documentation is current +- `.github/workflows/verify-ffi-docs.yml` - GitHub Action for CI verification +- `Makefile` - Convenient commands for documentation tasks \ No newline at end of file diff --git a/key-wallet-ffi/Makefile b/key-wallet-ffi/Makefile new file mode 100644 index 000000000..112b4c298 --- /dev/null +++ b/key-wallet-ffi/Makefile @@ -0,0 +1,58 @@ +.PHONY: all build test clean docs check-docs update-docs + +# Default target +all: build + +# Build the library +build: + cargo build --release + +# Run tests +test: + cargo test + +# Clean build artifacts +clean: + cargo clean + rm -f include/key_wallet_ffi.h + +# Generate C header +header: + cargo build --release + @echo "Generated header at include/key_wallet_ffi.h" + +# Update FFI documentation +update-docs: + @echo "Generating FFI API documentation..." + @python3 scripts/generate_ffi_docs.py + @echo "Documentation updated at FFI_API.md" + +# Check if FFI documentation is up to date +check-docs: + @bash scripts/check_ffi_docs.sh + +# Generate all documentation +docs: header update-docs + @echo "All documentation generated" + +# Build for iOS +ios: + ./build-ios.sh + +# Full build with documentation +full: clean build header update-docs + @echo "Full build complete with documentation" + +# Help +help: + @echo "Available targets:" + @echo " make build - Build the library" + @echo " make test - Run tests" + @echo " make clean - Clean build artifacts" + @echo " make header - Generate C header" + @echo " make update-docs - Update FFI API documentation" + @echo " make check-docs - Check if FFI docs are up to date" + @echo " make docs - Generate all documentation" + @echo " make ios - Build for iOS platforms" + @echo " make full - Full build with documentation" + @echo " make help - Show this help message" \ No newline at end of file diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index 83d29f1d5..defc4e107 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -320,6 +320,10 @@ typedef struct FFIWallet FFIWallet; /* FFI wrapper for WalletManager + + This struct holds a cloned Arc reference to the WalletManager, + allowing FFI code to interact with it directly without going through + the SPV client. */ typedef struct FFIWalletManager FFIWalletManager; diff --git a/key-wallet-ffi/scripts/check_ffi_docs.sh b/key-wallet-ffi/scripts/check_ffi_docs.sh new file mode 100755 index 000000000..13173078e --- /dev/null +++ b/key-wallet-ffi/scripts/check_ffi_docs.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Check if FFI documentation is up to date + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +echo "Checking FFI documentation..." + +cd "$PROJECT_DIR" + +# Generate new documentation +python3 scripts/generate_ffi_docs.py > /dev/null 2>&1 + +# Check if there are any changes +if ! git diff --quiet FFI_API.md; then + echo "❌ FFI documentation is out of date!" + echo "" + echo "Please regenerate the documentation by running:" + echo " cd key-wallet-ffi && python3 scripts/generate_ffi_docs.py" + echo "" + echo "Or use the make command:" + echo " make update-ffi-docs" + echo "" + exit 1 +else + echo "✅ FFI documentation is up to date" +fi \ No newline at end of file diff --git a/key-wallet-ffi/scripts/generate_ffi_docs.py b/key-wallet-ffi/scripts/generate_ffi_docs.py new file mode 100755 index 000000000..d739cda99 --- /dev/null +++ b/key-wallet-ffi/scripts/generate_ffi_docs.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +""" +Generate FFI API documentation for key-wallet-ffi +""" + +import os +import re +import sys +from pathlib import Path +from dataclasses import dataclass +from typing import List, Optional, Dict +import subprocess + +@dataclass +class FFIFunction: + name: str + signature: str + module: str + doc_comment: Optional[str] = None + safety_comment: Optional[str] = None + params: List[str] = None + return_type: str = None + +def extract_ffi_functions(file_path: Path) -> List[FFIFunction]: + """Extract all #[no_mangle] functions from a Rust file.""" + functions = [] + + with open(file_path, 'r') as f: + content = f.read() + + # Find all #[no_mangle] functions with their documentation + pattern = r'(?:///.*\n)*(?:\s*#\[no_mangle\]\s*\n)(?:pub\s+)?(?:unsafe\s+)?extern\s+"C"\s+fn\s+(\w+)\s*\(((?:[^)]|\n)*)\)\s*(?:->\s*([^{]+))?' + + for match in re.finditer(pattern, content): + full_match = match.group(0) + func_name = match.group(1) + params = match.group(2) + return_type = match.group(3) if match.group(3) else "()" + + # Extract doc comments + doc_lines = [] + for line in full_match.split('\n'): + if line.strip().startswith('///'): + doc_lines.append(line.strip()[3:].strip()) + + # Extract safety comments + safety_comment = None + if '# Safety' in '\n'.join(doc_lines): + safety_start = False + safety_lines = [] + for line in doc_lines: + if '# Safety' in line: + safety_start = True + continue + if safety_start: + if line.startswith('#'): + break + safety_lines.append(line) + safety_comment = ' '.join(safety_lines).strip() + + # Clean up parameters + params_clean = re.sub(r'\s+', ' ', params.strip()) + return_type_clean = return_type.strip() + + module_name = file_path.stem + + functions.append(FFIFunction( + name=func_name, + signature=f"{func_name}({params_clean}) -> {return_type_clean}", + module=module_name, + doc_comment=' '.join(doc_lines) if doc_lines else None, + safety_comment=safety_comment, + params=params_clean, + return_type=return_type_clean + )) + + return functions + +def categorize_functions(functions: List[FFIFunction]) -> Dict[str, List[FFIFunction]]: + """Categorize functions by their module/purpose.""" + categories = { + 'Initialization': [], + 'Error Handling': [], + 'Wallet Manager': [], + 'Wallet Operations': [], + 'Account Management': [], + 'Address Management': [], + 'Transaction Management': [], + 'Key Management': [], + 'BIP38 Encryption': [], + 'UTXO Management': [], + 'Mnemonic Operations': [], + 'Utility Functions': [], + } + + for func in functions: + name = func.name.lower() + + if 'initialize' in name or 'version' in name: + categories['Initialization'].append(func) + elif 'error' in name: + categories['Error Handling'].append(func) + elif 'wallet_manager' in name: + categories['Wallet Manager'].append(func) + elif 'wallet' in name and 'manager' not in name: + categories['Wallet Operations'].append(func) + elif 'account' in name: + categories['Account Management'].append(func) + elif 'address' in name: + categories['Address Management'].append(func) + elif 'transaction' in name or 'tx' in name: + categories['Transaction Management'].append(func) + elif 'key' in name or 'derive' in name: + categories['Key Management'].append(func) + elif 'bip38' in name: + categories['BIP38 Encryption'].append(func) + elif 'utxo' in name: + categories['UTXO Management'].append(func) + elif 'mnemonic' in name: + categories['Mnemonic Operations'].append(func) + else: + categories['Utility Functions'].append(func) + + # Remove empty categories + return {k: v for k, v in categories.items() if v} + +def generate_markdown(functions: List[FFIFunction]) -> str: + """Generate markdown documentation from FFI functions.""" + + categories = categorize_functions(functions) + + md = [] + md.append("# Key-Wallet FFI API Documentation") + md.append("") + md.append("This document provides a comprehensive reference for all FFI (Foreign Function Interface) functions available in the key-wallet-ffi library.") + md.append("") + md.append("**Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually.") + md.append("") + md.append(f"**Total Functions**: {len(functions)}") + md.append("") + + # Table of Contents + md.append("## Table of Contents") + md.append("") + for category in categories.keys(): + anchor = category.lower().replace(' ', '-') + md.append(f"- [{category}](#{anchor})") + md.append("") + + # Function Reference + md.append("## Function Reference") + md.append("") + + for category, funcs in categories.items(): + if not funcs: + continue + + anchor = category.lower().replace(' ', '-') + md.append(f"### {category}") + md.append("") + md.append(f"Functions: {len(funcs)}") + md.append("") + + # Create a table for each category + md.append("| Function | Description | Module |") + md.append("|----------|-------------|--------|") + + for func in sorted(funcs, key=lambda f: f.name): + desc = func.doc_comment.split('.')[0] if func.doc_comment else "No description" + desc = desc.replace('|', '\\|') # Escape pipes in description + if len(desc) > 80: + desc = desc[:77] + "..." + md.append(f"| `{func.name}` | {desc} | {func.module} |") + + md.append("") + + # Detailed Function Documentation + md.append("## Detailed Function Documentation") + md.append("") + + for category, funcs in categories.items(): + if not funcs: + continue + + md.append(f"### {category} - Detailed") + md.append("") + + for func in sorted(funcs, key=lambda f: f.name): + md.append(f"#### `{func.name}`") + md.append("") + md.append("```c") + md.append(func.signature) + md.append("```") + md.append("") + + if func.doc_comment: + md.append("**Description:**") + md.append(func.doc_comment) + md.append("") + + if func.safety_comment: + md.append("**Safety:**") + md.append(func.safety_comment) + md.append("") + + md.append(f"**Module:** `{func.module}`") + md.append("") + md.append("---") + md.append("") + + # Type Definitions + md.append("## Type Definitions") + md.append("") + md.append("### Core Types") + md.append("") + md.append("- `FFIError` - Error handling structure") + md.append("- `FFIWallet` - Wallet handle") + md.append("- `FFIWalletManager` - Wallet manager handle") + md.append("- `FFIBalance` - Balance information") + md.append("- `FFIUTXO` - Unspent transaction output") + md.append("- `FFINetworks` - Network enumeration") + md.append("") + + # Memory Management + md.append("## Memory Management") + md.append("") + md.append("### Important Rules") + md.append("") + md.append("1. **Ownership Transfer**: Functions returning pointers transfer ownership to the caller") + md.append("2. **Cleanup Required**: All returned pointers must be freed using the appropriate `_free` or `_destroy` function") + md.append("3. **Thread Safety**: Most functions are thread-safe, but check individual function documentation") + md.append("4. **Error Handling**: Always check the `FFIError` parameter after function calls") + md.append("") + + # Usage Examples + md.append("## Usage Examples") + md.append("") + md.append("### Basic Wallet Manager Usage") + md.append("") + md.append("```c") + md.append("// Create wallet manager") + md.append("FFIError error = {0};") + md.append("FFIWalletManager* manager = wallet_manager_create(&error);") + md.append("if (error.code != 0) {") + md.append(" // Handle error") + md.append("}") + md.append("") + md.append("// Get wallet count") + md.append("size_t count = wallet_manager_wallet_count(manager, &error);") + md.append("") + md.append("// Clean up") + md.append("wallet_manager_free(manager);") + md.append("```") + md.append("") + + return '\n'.join(md) + +def main(): + # Find all Rust source files + src_dir = Path(__file__).parent.parent / "src" + + all_functions = [] + + for rust_file in src_dir.glob("*.rs"): + functions = extract_ffi_functions(rust_file) + all_functions.extend(functions) + + # Generate markdown + markdown = generate_markdown(all_functions) + + # Write to file + output_file = Path(__file__).parent.parent / "FFI_API.md" + with open(output_file, 'w') as f: + f.write(markdown) + + print(f"Generated FFI documentation with {len(all_functions)} functions") + print(f"Output: {output_file}") + + return 0 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/key-wallet-ffi/src/account_collection.rs b/key-wallet-ffi/src/account_collection.rs index d92655441..331bd81a5 100644 --- a/key-wallet-ffi/src/account_collection.rs +++ b/key-wallet-ffi/src/account_collection.rs @@ -113,7 +113,7 @@ pub unsafe extern "C" fn wallet_get_account_collection( FFIError::set_error( error, FFIErrorCode::NotFound, - format!("No accounts found for network {:?}", network_rust), + format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, wallet.inner().networks_supported()), ); ptr::null_mut() } diff --git a/key-wallet-ffi/src/lib.rs b/key-wallet-ffi/src/lib.rs index f1f4dac21..510090a98 100644 --- a/key-wallet-ffi/src/lib.rs +++ b/key-wallet-ffi/src/lib.rs @@ -33,6 +33,16 @@ pub mod bip38; pub use error::{FFIError, FFIErrorCode}; pub use types::{FFIBalance, FFINetworks, FFIWallet}; pub use utxo::FFIUTXO; +pub use wallet_manager::{ + FFIWalletManager, + wallet_manager_create, + wallet_manager_free, + wallet_manager_wallet_count, + wallet_manager_get_wallet, + wallet_manager_get_wallet_balance, + wallet_manager_get_wallet_ids, + wallet_manager_free_wallet_ids, +}; // ============================================================================ // Initialization and Version diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index de2364ba6..569a0f701 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -192,7 +192,7 @@ pub unsafe extern "C" fn managed_wallet_get_account( } None => FFIManagedAccountResult::error( FFIErrorCode::NotFound, - format!("No accounts found for network {:?}", network_rust), + format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), ), }; @@ -288,7 +288,7 @@ pub unsafe extern "C" fn managed_wallet_get_top_up_account_with_registration_ind } None => FFIManagedAccountResult::error( FFIErrorCode::NotFound, - format!("No accounts found for network {:?}", network_rust), + format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), ), }; diff --git a/key-wallet-ffi/src/managed_account_collection.rs b/key-wallet-ffi/src/managed_account_collection.rs index 53a00f899..5b9f9f8c7 100644 --- a/key-wallet-ffi/src/managed_account_collection.rs +++ b/key-wallet-ffi/src/managed_account_collection.rs @@ -137,7 +137,7 @@ pub unsafe extern "C" fn managed_wallet_get_account_collection( FFIError::set_error( error, FFIErrorCode::NotFound, - format!("No accounts found for network {:?}", network_rust), + format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), ); ptr::null_mut() } diff --git a/key-wallet-ffi/src/managed_wallet.rs b/key-wallet-ffi/src/managed_wallet.rs index d20231e93..df4ceed96 100644 --- a/key-wallet-ffi/src/managed_wallet.rs +++ b/key-wallet-ffi/src/managed_wallet.rs @@ -89,7 +89,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_receive_address( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}", network), + format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), ); return ptr::null_mut(); } @@ -219,7 +219,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}", network), + format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), ); return ptr::null_mut(); } @@ -370,7 +370,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}", network), + format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), ); *count_out = 0; *addresses_out = ptr::null_mut(); @@ -564,7 +564,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}", network), + format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), ); *count_out = 0; *addresses_out = ptr::null_mut(); diff --git a/key-wallet-ffi/src/wallet_manager.rs b/key-wallet-ffi/src/wallet_manager.rs index 68c1d350e..8319dd384 100644 --- a/key-wallet-ffi/src/wallet_manager.rs +++ b/key-wallet-ffi/src/wallet_manager.rs @@ -11,29 +11,50 @@ mod serialization_tests; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_uint}; use std::ptr; -use std::sync::Mutex; +use std::sync::Arc; +use tokio::sync::RwLock; use crate::error::{FFIError, FFIErrorCode}; use crate::types::FFINetworks; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; -use key_wallet_manager::wallet_manager::{WalletId, WalletManager}; +use key_wallet_manager::wallet_manager::WalletManager; /// FFI wrapper for WalletManager +/// +/// This struct holds a cloned Arc reference to the WalletManager, +/// allowing FFI code to interact with it directly without going through +/// the SPV client. pub struct FFIWalletManager { - manager: Mutex>, - // Track wallet IDs for FFI purposes - wallet_ids: Mutex>, + pub(crate) manager: Arc>>, + pub(crate) runtime: Arc, +} + +impl FFIWalletManager { + /// Create a new FFIWalletManager from an Arc> + pub fn from_arc(manager: Arc>>, runtime: Arc) -> Self { + FFIWalletManager { + manager, + runtime, + } + } } /// Create a new wallet manager #[no_mangle] pub extern "C" fn wallet_manager_create(error: *mut FFIError) -> *mut FFIWalletManager { let manager = WalletManager::new(); + let runtime = match tokio::runtime::Runtime::new() { + Ok(rt) => Arc::new(rt), + Err(e) => { + FFIError::set_error(error, FFIErrorCode::AllocationFailed, format!("Failed to create runtime: {}", e)); + return ptr::null_mut(); + } + }; FFIError::set_success(error); Box::into_raw(Box::new(FFIWalletManager { - manager: Mutex::new(manager), - wallet_ids: Mutex::new(Vec::new()), + manager: Arc::new(RwLock::new(manager)), + runtime, })) } @@ -97,18 +118,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_with_options( unsafe { let manager_ref = &*manager; - let mut manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return false; - } - }; - + // Convert account creation options let creation_options = if account_options.is_null() { key_wallet::wallet::initialization::WalletAccountCreationOptions::Default @@ -116,29 +126,22 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_with_options( (*account_options).to_wallet_options() }; - // Use the WalletManager's public method to create the wallet - match manager_guard.create_wallet_from_mnemonic( - mnemonic_str, - passphrase_str, - networks_rust.as_slice(), - None, // birth_height - creation_options, - ) { - Ok(wallet_id) => { - // Track the wallet ID - let mut ids_guard = match manager_ref.wallet_ids.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet IDs".to_string(), - ); - return false; - } - }; - ids_guard.push(wallet_id); - + // Use the runtime to execute async code + let result = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + + // Use the WalletManager's public method to create the wallet + manager_guard.create_wallet_from_mnemonic( + mnemonic_str, + passphrase_str, + networks_rust.as_slice(), + None, // birth_height + creation_options, + ) + }); + + match result { + Ok(_wallet_id) => { FFIError::set_success(error); true } @@ -274,18 +277,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ // Get the manager and call the proper method let manager_ref = unsafe { &*manager }; - let mut manager_guard = match manager_ref.manager.lock() { - Ok(guard) => guard, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Failed to lock manager".to_string(), - ); - return false; - } - }; - + // Convert birth_height: 0 means None, any other value means Some(value) let birth_height = if birth_height == 0 { None @@ -293,8 +285,10 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ Some(birth_height) }; - let (serialized, wallet_id) = match manager_guard - .create_wallet_from_mnemonic_return_serialized_bytes( + let result = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + + manager_guard.create_wallet_from_mnemonic_return_serialized_bytes( mnemonic_str, passphrase_str, &networks, @@ -302,7 +296,10 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ creation_options, downgrade_to_pubkey_wallet, allow_external_signing, - ) { + ) + }); + + let (serialized, wallet_id) = match result { Ok(result) => result, Err(e) => { let ffi_error: FFIError = e.into(); @@ -315,11 +312,6 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ } }; - // Track the wallet ID in the FFI manager - if let Ok(mut wallet_ids) = manager_ref.wallet_ids.lock() { - wallet_ids.push(wallet_id); - } - // Allocate memory for the serialized bytes let boxed_bytes = serialized.into_boxed_slice(); let bytes_len = boxed_bytes.len(); @@ -398,41 +390,19 @@ pub unsafe extern "C" fn wallet_manager_import_wallet_from_bytes( // Get the manager reference let manager_ref = unsafe { &*manager }; - // Lock the manager - let mut manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet manager".to_string(), - ); - return false; - } - }; + // Import the wallet using async runtime + let result = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + manager_guard.import_wallet_from_bytes(wallet_bytes_slice) + }); - // Import the wallet - match manager_guard.import_wallet_from_bytes(wallet_bytes_slice) { + match result { Ok(wallet_id) => { // Copy the wallet ID to the output buffer unsafe { ptr::copy_nonoverlapping(wallet_id.as_ptr(), wallet_id_out, 32); } - // Track the wallet ID in our FFI manager - let mut ids_guard = match manager_ref.wallet_ids.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet IDs".to_string(), - ); - return false; - } - }; - ids_guard.push(wallet_id); - FFIError::set_success(error); true } @@ -488,26 +458,21 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_ids( } let manager_ref = &*manager; - let ids_guard = match manager_ref.wallet_ids.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet IDs".to_string(), - ); - return false; - } - }; - - let count = ids_guard.len(); + + // Get wallet IDs from the manager + let wallet_ids = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard.list_wallets().into_iter().cloned().collect::>() + }); + + let count = wallet_ids.len(); if count == 0 { *count_out = 0; *wallet_ids_out = ptr::null_mut(); } else { // Allocate memory for wallet IDs (32 bytes each) as a boxed slice let mut ids_buffer = Vec::with_capacity(count * 32); - for wallet_id in ids_guard.iter() { + for wallet_id in wallet_ids.iter() { ids_buffer.extend_from_slice(wallet_id); } // Convert to boxed slice for consistent memory layout @@ -553,21 +518,14 @@ pub unsafe extern "C" fn wallet_manager_get_wallet( // Get the manager let manager_ref = unsafe { &*manager }; - // Lock the manager and get the wallet - let manager_guard = match manager_ref.manager.lock() { - Ok(guard) => guard, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet manager".to_string(), - ); - return ptr::null(); - } - }; + // Get the wallet using async runtime + let wallet_opt = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard.get_wallet(&wallet_id_array).cloned() + }); - // Get the wallet - match manager_guard.get_wallet(&wallet_id_array) { + // Return the wallet + match wallet_opt { Some(wallet) => { // Create an FFIWallet wrapper // Note: We need to store this somewhere that will outlive this function @@ -615,21 +573,14 @@ pub unsafe extern "C" fn wallet_manager_get_managed_wallet_info( // Get the manager let manager_ref = unsafe { &*manager }; - // Lock the manager and get the wallet info - let manager_guard = match manager_ref.manager.lock() { - Ok(guard) => guard, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet manager".to_string(), - ); - return ptr::null_mut(); - } - }; + // Get the wallet info using async runtime + let wallet_info_opt = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard.get_wallet_info(&wallet_id_array).cloned() + }); - // Get the wallet info - match manager_guard.get_wallet_info(&wallet_id_array) { + // Return the wallet info + match wallet_info_opt { Some(wallet_info) => { // Create an FFIManagedWalletInfo wrapper let ffi_wallet_info = @@ -670,18 +621,7 @@ pub unsafe extern "C" fn wallet_manager_get_receive_address( wallet_id_array.copy_from_slice(wallet_id_slice); let manager_ref = &*manager; - let mut manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return ptr::null_mut(); - } - }; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -696,13 +636,18 @@ pub unsafe extern "C" fn wallet_manager_get_receive_address( // Use the WalletManager's public method to get next receive address use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; - match manager_guard.get_receive_address( - &wallet_id_array, - network_rust, - account_index, - AccountTypePreference::BIP44, - true, // mark_as_used - ) { + let result = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + manager_guard.get_receive_address( + &wallet_id_array, + network_rust, + account_index, + AccountTypePreference::BIP44, + true, // mark_as_used + ) + }); + + match result { Ok(result) => { if let Some(address) = result.address { FFIError::set_success(error); @@ -763,18 +708,7 @@ pub unsafe extern "C" fn wallet_manager_get_change_address( wallet_id_array.copy_from_slice(wallet_id_slice); let manager_ref = &*manager; - let mut manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return ptr::null_mut(); - } - }; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -789,13 +723,18 @@ pub unsafe extern "C" fn wallet_manager_get_change_address( // Use the WalletManager's public method to get next change address use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; - match manager_guard.get_change_address( - &wallet_id_array, - network_rust, - account_index, - AccountTypePreference::BIP44, - true, // mark_as_used - ) { + let result = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + manager_guard.get_change_address( + &wallet_id_array, + network_rust, + account_index, + AccountTypePreference::BIP44, + true, // mark_as_used + ) + }); + + match result { Ok(result) => { if let Some(address) = result.address { FFIError::set_success(error); @@ -868,21 +807,13 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_balance( // Get the manager let manager_ref = unsafe { &*manager }; - // Lock the manager and get the wallet balance - let manager_guard = match manager_ref.manager.lock() { - Ok(guard) => guard, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet manager".to_string(), - ); - return false; - } - }; + // Get the wallet balance using async runtime + let result = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard.get_wallet_balance(&wallet_id_array) + }); - // Get the wallet balance - match manager_guard.get_wallet_balance(&wallet_id_array) { + match result { Ok(balance) => { unsafe { *confirmed_out = balance.confirmed; @@ -974,26 +905,16 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( // Get the manager let manager_ref = unsafe { &mut *manager }; - // Lock the manager and process the transaction - let mut manager_guard = match manager_ref.manager.lock() { - Ok(guard) => guard, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock wallet manager".to_string(), - ); - return false; - } - }; - - // Check the transaction against all wallets - let relevant_wallets = manager_guard.check_transaction_in_all_wallets( - &tx, - network, - context, - update_state_if_found, - ); + // Process the transaction using async runtime + let relevant_wallets = manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + manager_guard.check_transaction_in_all_wallets( + &tx, + network, + context, + update_state_if_found, + ) + }); FFIError::set_success(error); !relevant_wallets.is_empty() @@ -1022,18 +943,7 @@ pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( } let manager_ref = &*manager; - let manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return false; - } - }; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -1045,43 +955,48 @@ pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( return false; } }; - let mut all_addresses: Vec<*mut c_char> = Vec::new(); - + // Collect addresses from all wallets for this network - for wallet in manager_guard.get_all_wallets().values() { - if let Some(account) = wallet.get_bip44_account(network_rust, 0) { - // Generate a few addresses from each wallet (simplified) - use key_wallet::ChildNumber; - use secp256k1::Secp256k1; - let secp = Secp256k1::new(); - - // Generate first 3 receive addresses - for i in 0..3 { - let child_external = match ChildNumber::from_normal_idx(0) { - Ok(c) => c, - Err(_) => continue, - }; - - let child_index = match ChildNumber::from_normal_idx(i) { - Ok(c) => c, - Err(_) => continue, - }; - - if let Ok(derived_key) = - account.account_xpub.derive_pub(&secp, &[child_external, child_index]) - { - let public_key = derived_key.public_key; - let dash_pubkey = dashcore::PublicKey::new(public_key); - let dash_network = network_rust; - let address = key_wallet::Address::p2pkh(&dash_pubkey, dash_network); - - if let Ok(c_str) = CString::new(address.to_string()) { - all_addresses.push(c_str.into_raw()); + let all_addresses = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + let mut addresses: Vec<*mut c_char> = Vec::new(); + + for wallet in manager_guard.get_all_wallets().values() { + if let Some(account) = wallet.get_bip44_account(network_rust, 0) { + // Generate a few addresses from each wallet (simplified) + use key_wallet::ChildNumber; + use secp256k1::Secp256k1; + let secp = Secp256k1::new(); + + // Generate first 3 receive addresses + for i in 0..3 { + let child_external = match ChildNumber::from_normal_idx(0) { + Ok(c) => c, + Err(_) => continue, + }; + + let child_index = match ChildNumber::from_normal_idx(i) { + Ok(c) => c, + Err(_) => continue, + }; + + if let Ok(derived_key) = + account.account_xpub.derive_pub(&secp, &[child_external, child_index]) + { + let public_key = derived_key.public_key; + let dash_pubkey = dashcore::PublicKey::new(public_key); + let dash_network = network_rust; + let address = key_wallet::Address::p2pkh(&dash_pubkey, dash_network); + + if let Ok(c_str) = CString::new(address.to_string()) { + addresses.push(c_str.into_raw()); + } } } } } - } + addresses + }); if all_addresses.is_empty() { *count_out = 0; @@ -1118,18 +1033,7 @@ pub unsafe extern "C" fn wallet_manager_update_height( } let manager_ref = &*manager; - let mut manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return false; - } - }; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -1141,7 +1045,11 @@ pub unsafe extern "C" fn wallet_manager_update_height( return false; } }; - manager_guard.update_height(network_rust, height); + + manager_ref.runtime.block_on(async { + let mut manager_guard = manager_ref.manager.write().await; + manager_guard.update_height(network_rust, height); + }); FFIError::set_success(error); true @@ -1166,18 +1074,7 @@ pub unsafe extern "C" fn wallet_manager_current_height( } let manager_ref = &*manager; - let manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return 0; - } - }; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -1191,10 +1088,13 @@ pub unsafe extern "C" fn wallet_manager_current_height( }; // Get current height from network state if it exists - let height = manager_guard - .get_network_state(network_rust) - .map(|state| state.current_height) - .unwrap_or(0); + let height = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard + .get_network_state(network_rust) + .map(|state| state.current_height) + .unwrap_or(0) + }); FFIError::set_success(error); height @@ -1219,20 +1119,14 @@ pub unsafe extern "C" fn wallet_manager_wallet_count( unsafe { let manager_ref = &*manager; - let manager_guard = match manager_ref.manager.lock() { - Ok(g) => g, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - "Failed to lock manager".to_string(), - ); - return 0; - } - }; + + let count = manager_ref.runtime.block_on(async { + let manager_guard = manager_ref.manager.read().await; + manager_guard.wallet_count() + }); FFIError::set_success(error); - manager_guard.wallet_count() + count } } diff --git a/key-wallet-manager/examples/wallet_creation.rs b/key-wallet-manager/examples/wallet_creation.rs index 5680a0f63..241b90a19 100644 --- a/key-wallet-manager/examples/wallet_creation.rs +++ b/key-wallet-manager/examples/wallet_creation.rs @@ -1,9 +1,8 @@ -//! Example demonstrating how to create and manage wallets using WalletManager and SPVWalletManager +//! Example demonstrating how to create and manage wallets using WalletManager //! //! This example shows: //! - Creating wallets with WalletManager //! - Creating wallets from mnemonics -//! - Using SPVWalletManager for SPV-specific functionality //! - Managing wallet accounts and addresses use hex; @@ -12,7 +11,6 @@ use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::{AccountType, Network}; -use key_wallet_manager::spv_wallet_manager::SPVWalletManager; use key_wallet_manager::wallet_manager::WalletManager; fn main() { @@ -124,35 +122,11 @@ fn main() { } } - // Example 5: Using SPVWalletManager - println!("\n5. Using SPVWalletManager for SPV functionality..."); - - let mut spv_manager = SPVWalletManager::with_base(WalletManager::::new()); - - // Create a wallet through SPVWalletManager - let spv_result = spv_manager.base.create_wallet_with_random_mnemonic( - WalletAccountCreationOptions::Default, - Network::Testnet, - ); - - match spv_result { - Ok(wallet_id3) => { - println!("✅ SPV wallet created!"); - println!(" Wallet ID: {}", hex::encode(wallet_id3)); - println!(" Sync status: {:?}", spv_manager.sync_status(Network::Testnet)); - println!(" Sync height: {}", spv_manager.sync_height(Network::Testnet)); - - // Set target height for sync - spv_manager.set_target_height(Network::Testnet, 1_000_000); - println!(" Target height set to: 1,000,000"); - - // Update sync status after setting target - println!(" Updated sync status: {:?}", spv_manager.sync_status(Network::Testnet)); - } - Err(e) => { - println!("❌ Failed to create SPV wallet: {:?}", e); - } - } + // Example 5: WalletManager now includes SPV functionality + println!("\n5. WalletManager now includes filter caching for SPV..."); + println!(" The SPVWalletManager has been merged into WalletManager"); + println!(" Filter caching is now built into WalletManager's check_compact_filter method"); + println!(" WalletManager implements the WalletInterface trait for SPV integration"); // Example 6: Getting wallet balance println!("\n6. Checking wallet balances..."); diff --git a/key-wallet-manager/src/lib.rs b/key-wallet-manager/src/lib.rs index cd4997106..036bd546b 100644 --- a/key-wallet-manager/src/lib.rs +++ b/key-wallet-manager/src/lib.rs @@ -21,7 +21,6 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -pub mod spv_wallet_manager; pub mod wallet_interface; pub mod wallet_manager; @@ -41,5 +40,4 @@ pub use key_wallet::wallet::managed_wallet_info::coin_selection::{ }; pub use key_wallet::wallet::managed_wallet_info::fee::{FeeEstimator, FeeRate}; pub use key_wallet::wallet::managed_wallet_info::transaction_builder::TransactionBuilder; -pub use spv_wallet_manager::{ProcessBlockResult, SPVStats, SPVSyncStatus, SPVWalletManager}; pub use wallet_manager::{WalletError, WalletManager}; diff --git a/key-wallet-manager/src/spv_wallet_manager.rs b/key-wallet-manager/src/spv_wallet_manager.rs deleted file mode 100644 index a4598757b..000000000 --- a/key-wallet-manager/src/spv_wallet_manager.rs +++ /dev/null @@ -1,295 +0,0 @@ -//! Simplified SPV Wallet Manager -//! -//! This module provides a thin wrapper around WalletManager that adds -//! SPV-specific functionality without duplicating wallet management logic. - -use alloc::collections::{BTreeMap, VecDeque}; -use alloc::string::String; -use alloc::vec::Vec; - -use async_trait::async_trait; -use dashcore::bip158::BlockFilter; -use dashcore::blockdata::block::Block; -use dashcore::blockdata::transaction::Transaction; -use dashcore::prelude::CoreBlockHeight; -use dashcore::{BlockHash, Txid}; -use key_wallet::Network; - -use crate::wallet_interface::WalletInterface; -use crate::wallet_manager::{WalletId, WalletManager}; -use key_wallet::transaction_checking::TransactionContext; -use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; -use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; - -/// SPV Wallet Manager -/// -/// A thin wrapper around WalletManager that adds SPV-specific functionality: -/// - Compact filter checking -/// - Block download queue management -/// - SPV synchronization statistics -/// -/// All wallet state, UTXO tracking, and transaction processing is delegated -/// to the underlying WalletManager. -#[derive(Debug)] -pub struct SPVWalletManager { - /// Base wallet manager (handles all wallet state) - pub base: WalletManager, - - // SPV-specific fields only - /// Block download queue (per network) - download_queues: BTreeMap>, - /// Pending blocks waiting for dependencies (per network) - pending_blocks: BTreeMap>, - /// Filter match cache (per network) - caches whether a filter matched - filter_matches: BTreeMap>, - /// Maximum blocks to queue for download - max_download_queue: usize, - /// SPV statistics (per network) - stats: BTreeMap, -} - -impl From> for SPVWalletManager { - fn from(manager: WalletManager) -> Self { - Self { - base: manager, - download_queues: BTreeMap::new(), - pending_blocks: BTreeMap::new(), - filter_matches: BTreeMap::new(), - max_download_queue: 100, - stats: BTreeMap::new(), - } - } -} - -/// SPV synchronization statistics -#[derive(Debug, Clone, Default)] -pub struct SPVStats { - /// Total filters checked - pub filters_checked: u64, - /// Filters that matched - pub filters_matched: u64, - /// Blocks downloaded - pub blocks_downloaded: u64, - /// Relevant transactions found - pub transactions_found: u64, - /// Current sync height - pub sync_height: u32, - /// Target height - pub target_height: u32, -} - -/// SPV sync status -#[derive(Debug, Clone, PartialEq)] -pub enum SPVSyncStatus { - /// Not syncing - Idle, - /// Checking filters - CheckingFilters { - current: u32, - target: u32, - }, - /// Downloading blocks - DownloadingBlocks { - pending: usize, - }, - /// Processing blocks - ProcessingBlocks, - /// Synced - Synced, - /// Error occurred - Error(String), -} - -impl SPVWalletManager { - /// Create a new SPV wallet manager from a WalletManager - pub fn with_base(base: WalletManager) -> Self { - Self { - base, - download_queues: BTreeMap::new(), - pending_blocks: BTreeMap::new(), - filter_matches: BTreeMap::new(), - max_download_queue: 100, - stats: BTreeMap::new(), - } - } - - /// Create a new SPV wallet manager (only available when T implements Default) - pub fn new() -> Self - where - T: Default, - { - Self::with_base(WalletManager::new()) - } - - /// Queue a block for download - pub fn queue_block_download(&mut self, network: Network, block_hash: BlockHash) -> bool { - let queue = self.download_queues.entry(network).or_default(); - - if queue.len() >= self.max_download_queue { - return false; - } - - if !queue.contains(&block_hash) { - queue.push_back(block_hash); - } - - true - } - - /// Get next block to download - pub fn next_block_to_download(&mut self, network: Network) -> Option { - self.download_queues.get_mut(&network)?.pop_front() - } - - /// Add a pending block (waiting for dependencies) - pub fn add_pending_block( - &mut self, - network: Network, - height: u32, - block: Block, - hash: BlockHash, - ) { - self.pending_blocks.entry(network).or_default().insert(height, (block, hash)); - } - - /// Get and remove a pending block - pub fn take_pending_block( - &mut self, - network: Network, - height: u32, - ) -> Option<(Block, BlockHash)> { - self.pending_blocks.get_mut(&network)?.remove(&height) - } - - /// Get SPV sync status for a network - pub fn sync_status(&self, network: Network) -> SPVSyncStatus { - let stats = self.stats.get(&network); - let queue_size = self.download_queues.get(&network).map(|q| q.len()).unwrap_or(0); - - if let Some(stats) = stats { - if stats.sync_height >= stats.target_height { - SPVSyncStatus::Synced - } else if queue_size > 0 { - SPVSyncStatus::DownloadingBlocks { - pending: queue_size, - } - } else { - SPVSyncStatus::CheckingFilters { - current: stats.sync_height, - target: stats.target_height, - } - } - } else { - SPVSyncStatus::Idle - } - } - - /// Update sync statistics - pub fn update_stats(&mut self, network: Network, update: F) - where - F: FnOnce(&mut SPVStats), - { - let stats = self.stats.entry(network).or_default(); - update(stats); - } - - /// Get current sync height for a network - pub fn sync_height(&self, network: Network) -> u32 { - self.base.get_network_state(network).map(|state| state.current_height).unwrap_or(0) - } - - /// Set target sync height - pub fn set_target_height(&mut self, network: Network, height: u32) { - self.stats.entry(network).or_default().target_height = height; - } -} - -/// Result of processing a block -#[derive(Debug, Default)] -pub struct ProcessBlockResult { - /// Number of relevant transactions found - pub relevant_transactions: usize, - /// Wallets that were affected - pub affected_wallets: Vec, -} - -impl Default for SPVWalletManager { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl WalletInterface for SPVWalletManager { - /// Process a block and return relevant transaction IDs - async fn process_block( - &mut self, - block: &Block, - height: CoreBlockHeight, - network: Network, - ) -> Vec { - let relevant_tx_ids = self.base.process_block(block, height, network); - - // Update statistics - if let Some(stats) = self.stats.get_mut(&network) { - stats.blocks_downloaded += 1; - stats.transactions_found += relevant_tx_ids.len() as u64; - stats.sync_height = height; - } - - relevant_tx_ids - } - - /// Process a mempool transaction - async fn process_mempool_transaction(&mut self, tx: &Transaction, network: Network) { - let context = TransactionContext::Mempool; - - // Check transaction against all wallets - self.base.check_transaction_in_all_wallets( - tx, network, context, true, // update state - ); - } - - /// Handle a blockchain reorganization - async fn handle_reorg( - &mut self, - from_height: CoreBlockHeight, - to_height: CoreBlockHeight, - network: Network, - ) { - self.base.handle_reorg(from_height, to_height, network); - - // Update SPV stats - if let Some(stats) = self.stats.get_mut(&network) { - if stats.sync_height >= from_height { - stats.sync_height = to_height; - } - } - } - - /// Check if a compact filter matches any watched addresses - async fn check_compact_filter( - &mut self, - filter: &BlockFilter, - block_hash: &BlockHash, - network: Network, - ) -> bool { - // Check if we've already evaluated this filter - if let Some(network_cache) = self.filter_matches.get(&network) { - if let Some(&matched) = network_cache.get(block_hash) { - return matched; - } - } - - let hit = self.base.check_compact_filter(filter, block_hash, network); - - self.filter_matches.entry(network).or_default().insert(*block_hash, hit); - - hit - } - - /// Get a reference to self as Any for downcasting in tests - fn as_any(&self) -> &dyn std::any::Any { - self - } -} diff --git a/key-wallet-manager/src/wallet_interface.rs b/key-wallet-manager/src/wallet_interface.rs index 11f75f125..c7d6f9303 100644 --- a/key-wallet-manager/src/wallet_interface.rs +++ b/key-wallet-manager/src/wallet_interface.rs @@ -39,7 +39,4 @@ pub trait WalletInterface: Send + Sync { block_hash: &dashcore::BlockHash, network: Network, ) -> bool; - - /// Get a reference to self as Any for downcasting in tests - fn as_any(&self) -> &dyn std::any::Any; } diff --git a/key-wallet-manager/src/wallet_manager/mod.rs b/key-wallet-manager/src/wallet_manager/mod.rs index 4ca8d7bc8..143b890c1 100644 --- a/key-wallet-manager/src/wallet_manager/mod.rs +++ b/key-wallet-manager/src/wallet_manager/mod.rs @@ -11,7 +11,7 @@ use alloc::collections::BTreeMap; use alloc::string::String; use alloc::vec::Vec; use dashcore::blockdata::transaction::Transaction; -use dashcore::Txid; +use dashcore::{BlockHash, Txid}; use key_wallet::transaction_checking::TransactionContext; use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; @@ -88,6 +88,9 @@ pub struct WalletManager { pub(crate) wallet_infos: BTreeMap, /// Network-specific state (UTXO sets, transactions, heights) network_states: BTreeMap, + /// Filter match cache (per network) - caches whether a filter matched + /// This is used for SPV operations to avoid rechecking filters + filter_matches: BTreeMap>, } impl Default for WalletManager @@ -106,6 +109,7 @@ impl WalletManager { wallets: BTreeMap::new(), wallet_infos: BTreeMap::new(), network_states: BTreeMap::new(), + filter_matches: BTreeMap::new(), } } diff --git a/key-wallet-manager/src/wallet_manager/process_block.rs b/key-wallet-manager/src/wallet_manager/process_block.rs index 7381e7419..4e8c48908 100644 --- a/key-wallet-manager/src/wallet_manager/process_block.rs +++ b/key-wallet-manager/src/wallet_manager/process_block.rs @@ -1,12 +1,15 @@ use crate::{Network, WalletManager}; use dashcore::bip158::BlockFilter; use dashcore::prelude::CoreBlockHeight; -use dashcore::{Block, BlockHash, Txid}; +use dashcore::{Block, BlockHash, Transaction, Txid}; use key_wallet::transaction_checking::TransactionContext; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; +use crate::wallet_interface::WalletInterface; +use async_trait::async_trait; -impl WalletManager { - pub fn process_block(&mut self, block: &Block, height: u32, network: Network) -> Vec { +#[async_trait] +impl WalletInterface for WalletManager { + async fn process_block(&mut self, block: &Block, height: CoreBlockHeight, network: Network) -> Vec { let mut relevant_txids = Vec::new(); let block_hash = Some(block.block_hash()); let timestamp = block.header.time; @@ -36,7 +39,16 @@ impl WalletManager { relevant_txids } - pub fn handle_reorg( + async fn process_mempool_transaction(&mut self, tx: &Transaction, network: Network) { + let context = TransactionContext::Mempool; + + // Check transaction against all wallets + self.check_transaction_in_all_wallets( + tx, network, context, true, // update state + ); + } + + async fn handle_reorg( &mut self, from_height: CoreBlockHeight, to_height: CoreBlockHeight, @@ -60,12 +72,19 @@ impl WalletManager { } } - pub fn check_compact_filter( - &self, + async fn check_compact_filter( + &mut self, filter: &BlockFilter, block_hash: &BlockHash, network: Network, ) -> bool { + // Check if we've already evaluated this filter + if let Some(network_cache) = self.filter_matches.get(&network) { + if let Some(&matched) = network_cache.get(block_hash) { + return matched; + } + } + // Collect all scripts we're watching let mut script_bytes = Vec::new(); @@ -78,8 +97,13 @@ impl WalletManager { } // Check if any of our scripts match the filter - filter + let hit = filter .match_any(block_hash, &mut script_bytes.iter().map(|s| s.as_slice())) - .unwrap_or(false) + .unwrap_or(false); + + // Cache the result + self.filter_matches.entry(network).or_default().insert(*block_hash, hit); + + hit } } diff --git a/key-wallet-manager/tests/spv_integration_tests.rs b/key-wallet-manager/tests/spv_integration_tests.rs index 1e0088e00..98b59acf6 100644 --- a/key-wallet-manager/tests/spv_integration_tests.rs +++ b/key-wallet-manager/tests/spv_integration_tests.rs @@ -1,8 +1,9 @@ //! Integration tests for SPV wallet functionality -use dashcore::blockdata::block::{Block, Header}; +use dashcore::blockdata::block::{Block, Header, Version}; use dashcore::blockdata::script::ScriptBuf; use dashcore::blockdata::transaction::{OutPoint, Transaction}; +use dashcore::pow::CompactTarget; use dashcore::{BlockHash, Txid}; use dashcore::{TxIn, TxOut}; use dashcore_hashes::Hash; @@ -11,7 +12,6 @@ use dashcore::bip158::{BlockFilter, BlockFilterWriter}; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; -use key_wallet_manager::spv_wallet_manager::{SPVSyncStatus, SPVWalletManager}; use key_wallet_manager::wallet_interface::WalletInterface; use key_wallet_manager::wallet_manager::WalletManager; @@ -37,47 +37,42 @@ fn create_test_transaction(value: u64) -> Transaction { } } -/// Create a test block with transactions +/// Create a test block fn create_test_block(height: u32, transactions: Vec) -> Block { - use dashcore::blockdata::block::Version; - use dashcore::CompactTarget; - use dashcore::TxMerkleNode; - - let header = Header { - version: Version::from_consensus(0x20000000), - prev_blockhash: BlockHash::from_byte_array([0u8; 32]), - merkle_root: TxMerkleNode::from_byte_array([0u8; 32]), - time: 1234567890 + height, - bits: CompactTarget::from_consensus(0x1d00ffff), - nonce: height, - }; - Block { - header, + header: Header { + version: Version::ONE, + prev_blockhash: BlockHash::from_byte_array([0u8; 32]), + merkle_root: dashcore::TxMerkleNode::from_byte_array([0u8; 32]), + time: height, + bits: CompactTarget::from_consensus(0x1d00ffff), + nonce: 0, + }, txdata: transactions, } } -/// Create a mock compact filter +/// Create a mock filter that matches everything (for testing) fn create_mock_filter(block: &Block) -> BlockFilter { - // Create a proper BIP158 filter from the block - let mut filter_bytes = Vec::new(); - let mut writer = BlockFilterWriter::new(&mut filter_bytes, block); + let mut content = Vec::new(); + let mut writer = BlockFilterWriter::new(&mut content, block); + + // Add output scripts from the block writer.add_output_scripts(); - // For testing, we'll ignore input scripts since we don't have a UTXO lookup - writer.finish().unwrap(); - BlockFilter::new(&filter_bytes) + + // Finish writing and construct the filter + writer.finish().expect("Failed to finish filter"); + BlockFilter::new(&content) } #[tokio::test] async fn test_filter_checking() { - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); + let mut manager = WalletManager::::new(); // Add a test address to monitor - simplified for testing // In reality, addresses would be generated from wallet accounts - let _wallet_id = spv - .base + let _wallet_id = manager .create_wallet_with_random_mnemonic(WalletAccountCreationOptions::Default, Network::Testnet) .expect("Failed to create wallet"); @@ -88,21 +83,24 @@ async fn test_filter_checking() { let block_hash = block.block_hash(); // Check the filter - let should_download = spv.check_compact_filter(&filter, &block_hash, Network::Testnet).await; + let should_download = manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; // The filter matching depends on whether the wallet has any addresses // being watched. Since we just created an empty wallet, it may or may not match. // We'll just check that the method doesn't panic let _ = should_download; + + // Test filter caching - calling again should use cached result + let should_download_cached = manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; + assert_eq!(should_download, should_download_cached, "Cached result should match original"); } #[tokio::test] async fn test_block_processing() { - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); + let mut manager = WalletManager::::new(); // Create a test wallet - let _wallet_id = spv - .base + let _wallet_id = manager .create_wallet_with_random_mnemonic(WalletAccountCreationOptions::Default, Network::Testnet) .expect("Failed to create wallet"); @@ -113,164 +111,40 @@ async fn test_block_processing() { let block = create_test_block(100, vec![tx.clone()]); // Process the block - let result = spv.process_block(&block, 100, Network::Testnet).await; + let result = manager.process_block(&block, 100, Network::Testnet).await; // Since we're not watching specific addresses, no transactions should be relevant assert_eq!(result.len(), 0); } -#[test] -fn test_queued_blocks() { - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); - - // Create blocks - let block1 = create_test_block(101, vec![create_test_transaction(1000)]); - let block2 = create_test_block(102, vec![create_test_transaction(2000)]); - let block3 = create_test_block(103, vec![create_test_transaction(3000)]); - - // Add pending blocks - spv.add_pending_block(Network::Testnet, 103, block3.clone(), block3.block_hash()); - spv.add_pending_block(Network::Testnet, 101, block1.clone(), block1.block_hash()); - spv.add_pending_block(Network::Testnet, 102, block2.clone(), block2.block_hash()); - - // Get a pending block - let taken = spv.take_pending_block(Network::Testnet, 101); - assert!(taken.is_some()); - - // Block 101 should be removed, others should remain - assert!(spv.take_pending_block(Network::Testnet, 101).is_none()); - assert!(spv.take_pending_block(Network::Testnet, 102).is_some()); -} - -#[test] -fn test_sync_status_tracking() { - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); - - // Set target height - spv.set_target_height(Network::Testnet, 1000); - - // Initially the status depends on implementation details - // It could be Idle or CheckingFilters - let initial_status = spv.sync_status(Network::Testnet); - assert!( - matches!(initial_status, SPVSyncStatus::Idle) - || matches!(initial_status, SPVSyncStatus::CheckingFilters { .. }), - "Unexpected initial status: {:?}", - initial_status - ); - - // Queue a block for download - let block_hash = BlockHash::from_byte_array([0u8; 32]); - assert!(spv.queue_block_download(Network::Testnet, block_hash)); - - // Should be downloading blocks - assert_eq!( - spv.sync_status(Network::Testnet), - SPVSyncStatus::DownloadingBlocks { - pending: 1 - } - ); - - // Take the block from queue - let next = spv.next_block_to_download(Network::Testnet); - assert!(next.is_some()); - - // Queue should be empty now - assert_eq!( - spv.sync_status(Network::Testnet), - SPVSyncStatus::CheckingFilters { - current: 0, - target: 1000 - } - ); - - // Update sync height to target - spv.update_stats(Network::Testnet, |stats| { - stats.sync_height = 1000; - stats.target_height = 1000; - }); - - // Should be synced - assert_eq!(spv.sync_status(Network::Testnet), SPVSyncStatus::Synced); -} - -#[tokio::test] -async fn test_multiple_wallets() { - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); - - // Create and add multiple wallets - for _i in 0..3 { - spv.base - .create_wallet_with_random_mnemonic( - WalletAccountCreationOptions::Default, - Network::Testnet, - ) - .ok(); - } - - // Verify all wallets are added - assert_eq!(spv.base.wallet_count(), 3); - - // Create a block with multiple transactions - let mut transactions = Vec::new(); - for i in 0..3 { - let tx = create_test_transaction(100000 * (i + 1) as u64); - transactions.push(tx); - } - - let block = create_test_block(100, transactions); - - // Process the block - let result = spv.process_block(&block, 100, Network::Testnet).await; - - // Without watching specific addresses, transactions won't be relevant - assert_eq!(result.len(), 0); -} - #[tokio::test] -async fn test_spent_utxo_tracking() { - // This test requires more complex UTXO tracking that's not fully implemented - // We'll create a simpler version - let mut spv = SPVWalletManager::with_base(WalletManager::::new()); +async fn test_filter_caching() { + let mut manager = WalletManager::::new(); - // Create a test wallet - let _wallet_id = spv - .base + // Create a wallet with some addresses + let _wallet_id = manager .create_wallet_with_random_mnemonic(WalletAccountCreationOptions::Default, Network::Testnet) .expect("Failed to create wallet"); - // Create a transaction - let tx1 = create_test_transaction(100000); - let tx1_id = tx1.txid(); - - let block1 = create_test_block(100, vec![tx1]); - let result1 = spv.process_block(&block1, 100, Network::Testnet).await; - assert_eq!(result1.len(), 0); - - // Create a transaction that spends the first - let tx2 = Transaction { - version: 2, - lock_time: 0, - input: vec![TxIn { - previous_output: OutPoint { - txid: tx1_id, - vout: 0, - }, - script_sig: ScriptBuf::new(), - sequence: 0xffffffff, - witness: dashcore::Witness::default(), - }], - output: vec![TxOut { - value: 90000, - script_pubkey: ScriptBuf::new(), - }], - special_transaction_payload: None, - }; - - let block2 = create_test_block(101, vec![tx2]); - let result2 = spv.process_block(&block2, 101, Network::Testnet).await; - assert_eq!(result2.len(), 0); - - // Without proper UTXO tracking in wallets, we can't verify spent status - // This is a simplified test -} + // Create multiple blocks with different hashes + let block1 = create_test_block(100, vec![create_test_transaction(1000)]); + let block2 = create_test_block(101, vec![create_test_transaction(2000)]); + + let filter1 = create_mock_filter(&block1); + let filter2 = create_mock_filter(&block2); + + let hash1 = block1.block_hash(); + let hash2 = block2.block_hash(); + + // Check filters for both blocks + let result1 = manager.check_compact_filter(&filter1, &hash1, Network::Testnet).await; + let result2 = manager.check_compact_filter(&filter2, &hash2, Network::Testnet).await; + + // Check again - should use cached results + let cached1 = manager.check_compact_filter(&filter1, &hash1, Network::Testnet).await; + let cached2 = manager.check_compact_filter(&filter2, &hash2, Network::Testnet).await; + + // Cached results should match originals + assert_eq!(result1, cached1, "Cached result for block1 should match"); + assert_eq!(result2, cached2, "Cached result for block2 should match"); +} \ No newline at end of file diff --git a/key-wallet/src/wallet/helper.rs b/key-wallet/src/wallet/helper.rs index 469c90027..27b6ad93f 100644 --- a/key-wallet/src/wallet/helper.rs +++ b/key-wallet/src/wallet/helper.rs @@ -12,6 +12,10 @@ use alloc::vec::Vec; use hex; impl Wallet { + /// Get the networks supported for the wallet + pub fn networks_supported(&self) -> Vec { + self.accounts.keys().cloned().collect() + } /// Get the collection of accounts on a network pub fn accounts_on_network(&self, network: Network) -> Option<&AccountCollection> { self.accounts.get(&network) diff --git a/key-wallet/src/wallet/managed_wallet_info/helpers.rs b/key-wallet/src/wallet/managed_wallet_info/helpers.rs index e48eb9757..658130069 100644 --- a/key-wallet/src/wallet/managed_wallet_info/helpers.rs +++ b/key-wallet/src/wallet/managed_wallet_info/helpers.rs @@ -5,6 +5,10 @@ use crate::account::ManagedAccount; use crate::Network; impl ManagedWalletInfo { + /// Get the networks supported for the wallet + pub fn networks_supported(&self) -> Vec { + self.accounts.keys().cloned().collect() + } // BIP44 Account Helpers /// Get the first BIP44 managed account for a given network From c8dbac7a2c4a8ca5352a49a636afdb396920979b Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 00:16:18 +0700 Subject: [PATCH 2/9] more work --- dash-spv-ffi/FFI_API.md | 160 +++++- dash-spv-ffi/cbindgen.toml | 1 - dash-spv-ffi/examples/wallet_manager_usage.rs | 29 +- dash-spv-ffi/include/dash_spv_ffi.h | 194 ++++++- dash-spv-ffi/src/broadcast.rs | 4 +- dash-spv-ffi/src/client.rs | 13 +- dash-spv-ffi/src/config.rs | 108 +++- dash-spv-ffi/src/lib.rs | 6 +- dash-spv-ffi/src/types.rs | 33 -- dash-spv-ffi/src/utils.rs | 10 - dash-spv-ffi/tests/test_client.rs | 1 + dash-spv-ffi/tests/test_config.rs | 1 + dash-spv-ffi/tests/test_event_callbacks.rs | 1 + dash-spv-ffi/tests/test_types.rs | 1 + dash-spv-ffi/tests/test_utils.rs | 10 +- dash-spv-ffi/tests/test_wallet_manager.rs | 22 +- .../tests/unit/test_async_operations.rs | 1 + .../tests/unit/test_client_lifecycle.rs | 1 + dash-spv-ffi/tests/unit/test_configuration.rs | 1 + .../tests/unit/test_error_handling.rs | 1 + .../tests/unit/test_memory_management.rs | 1 + .../tests/unit/test_type_conversions.rs | 1 + dash-spv/examples/spv_with_wallet.rs | 57 +++ dash-spv/tests/block_download_test.rs | 6 +- .../tests/instantsend_integration_test.rs | 4 +- dash-spv/tests/wallet_integration_test.rs | 3 +- key-wallet-ffi/FFI_API.md | 245 ++++++++- key-wallet-ffi/include/key_wallet_ffi.h | 274 +++++++++- key-wallet-ffi/src/account_collection.rs | 6 +- key-wallet-ffi/src/derivation.rs | 166 +++++- key-wallet-ffi/src/lib.rs | 13 +- key-wallet-ffi/src/managed_account.rs | 12 +- .../src/managed_account_collection.rs | 6 +- key-wallet-ffi/src/managed_wallet.rs | 24 +- key-wallet-ffi/src/transaction.rs | 479 +++++++++++++++++- key-wallet-ffi/src/transaction_checking.rs | 12 +- key-wallet-ffi/src/types.rs | 47 +- key-wallet-ffi/src/wallet_manager.rs | 53 +- .../src/wallet_manager/process_block.rs | 11 +- .../tests/spv_integration_tests.rs | 18 +- .../Sources/DashSPVFFI/include/dash_spv_ffi.h | 309 +++++++---- 41 files changed, 2022 insertions(+), 323 deletions(-) create mode 100644 dash-spv/examples/spv_with_wallet.rs diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 6c01046b7..17f603258 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -38,28 +38,28 @@ Functions: 23 | Function | Description | Module | |----------|-------------|--------| -| `dash_spv_ffi_config_add_peer` | No description | config | -| `dash_spv_ffi_config_destroy` | No description | config | -| `dash_spv_ffi_config_get_data_dir` | No description | config | -| `dash_spv_ffi_config_get_mempool_strategy` | No description | config | -| `dash_spv_ffi_config_get_mempool_tracking` | No description | config | -| `dash_spv_ffi_config_get_network` | No description | config | +| `dash_spv_ffi_config_add_peer` | Adds a peer address to the configuration # Safety - `config` must be a valid... | config | +| `dash_spv_ffi_config_destroy` | Destroys an FFIClientConfig and frees its memory # Safety - `config` must be... | config | +| `dash_spv_ffi_config_get_data_dir` | Gets the data directory path from the configuration # Safety - `config` must... | config | +| `dash_spv_ffi_config_get_mempool_strategy` | Gets the mempool synchronization strategy # Safety - `config` must be a vali... | config | +| `dash_spv_ffi_config_get_mempool_tracking` | Gets whether mempool tracking is enabled # Safety - `config` must be a valid... | config | +| `dash_spv_ffi_config_get_network` | Gets the network type from the configuration # Safety - `config` must be a v... | config | | `dash_spv_ffi_config_mainnet` | No description | config | | `dash_spv_ffi_config_new` | No description | config | -| `dash_spv_ffi_config_set_data_dir` | No description | config | -| `dash_spv_ffi_config_set_fetch_mempool_transactions` | No description | config | -| `dash_spv_ffi_config_set_filter_load` | No description | config | -| `dash_spv_ffi_config_set_max_mempool_transactions` | No description | config | -| `dash_spv_ffi_config_set_max_peers` | No description | config | -| `dash_spv_ffi_config_set_mempool_strategy` | No description | config | -| `dash_spv_ffi_config_set_mempool_timeout` | No description | config | -| `dash_spv_ffi_config_set_mempool_tracking` | No description | config | -| `dash_spv_ffi_config_set_persist_mempool` | No description | config | -| `dash_spv_ffi_config_set_relay_transactions` | No description | config | -| `dash_spv_ffi_config_set_start_from_height` | No description | config | -| `dash_spv_ffi_config_set_user_agent` | No description | config | -| `dash_spv_ffi_config_set_validation_mode` | No description | config | -| `dash_spv_ffi_config_set_wallet_creation_time` | No description | config | +| `dash_spv_ffi_config_set_data_dir` | Sets the data directory for storing blockchain data # Safety - `config` must... | config | +| `dash_spv_ffi_config_set_fetch_mempool_transactions` | Sets whether to fetch full mempool transaction data # Safety - `config` must... | config | +| `dash_spv_ffi_config_set_filter_load` | Sets whether to load bloom filters # Safety - `config` must be a valid point... | config | +| `dash_spv_ffi_config_set_max_mempool_transactions` | Sets the maximum number of mempool transactions to track # Safety - `config`... | config | +| `dash_spv_ffi_config_set_max_peers` | Sets the maximum number of peers to connect to # Safety - `config` must be a... | config | +| `dash_spv_ffi_config_set_mempool_strategy` | Sets the mempool synchronization strategy # Safety - `config` must be a vali... | config | +| `dash_spv_ffi_config_set_mempool_timeout` | Sets the mempool transaction timeout in seconds # Safety - `config` must be ... | config | +| `dash_spv_ffi_config_set_mempool_tracking` | Enables or disables mempool tracking # Safety - `config` must be a valid poi... | 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` m... | 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 (currently not supported) # Safety - `config` mus... | config | +| `dash_spv_ffi_config_set_validation_mode` | Sets the validation mode for the SPV client # Safety - `config` must be a va... | config | +| `dash_spv_ffi_config_set_wallet_creation_time` | Sets the wallet creation timestamp for synchronization optimization # Safety... | config | | `dash_spv_ffi_config_testnet` | No description | config | ### Synchronization @@ -201,6 +201,12 @@ dash_spv_ffi_client_stop(client: *mut FFIDashSpvClient) -> i32 dash_spv_ffi_config_add_peer(config: *mut FFIClientConfig, addr: *const c_char,) -> i32 ``` +**Description:** +Adds a peer address to the configuration # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - `addr` must be a valid null-terminated C string containing a socket address (e.g., "192.168.1.1:9999") - The caller must ensure both pointers remain 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 - `addr` must be a valid null-terminated C string containing a socket address (e.g., "192.168.1.1:9999") - The caller must ensure both pointers remain valid for the duration of this call + **Module:** `config` --- @@ -211,6 +217,12 @@ dash_spv_ffi_config_add_peer(config: *mut FFIClientConfig, addr: *const c_char,) dash_spv_ffi_config_destroy(config: *mut FFIClientConfig) -> () ``` +**Description:** +Destroys an FFIClientConfig and frees its memory # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet, or null - After calling this function, the config pointer becomes invalid and must not be used - This function should only be called once per config instance + +**Safety:** +- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet, or null - After calling this function, the config pointer becomes invalid and must not be used - This function should only be called once per config instance + **Module:** `config` --- @@ -221,6 +233,12 @@ dash_spv_ffi_config_destroy(config: *mut FFIClientConfig) -> () 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_string_free + +**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_string_free + **Module:** `config` --- @@ -231,6 +249,12 @@ dash_spv_ffi_config_get_data_dir(config: *const FFIClientConfig,) -> FFIString 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::Selective as default + +**Safety:** +- `config` must be a valid pointer to an FFIClientConfig or null - If null, returns FFIMempoolStrategy::Selective as default + **Module:** `config` --- @@ -241,6 +265,12 @@ dash_spv_ffi_config_get_mempool_strategy(config: *const FFIClientConfig,) -> FFI 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` --- @@ -251,6 +281,12 @@ dash_spv_ffi_config_get_mempool_tracking(config: *const FFIClientConfig,) -> boo dash_spv_ffi_config_get_network(config: *const FFIClientConfig,) -> FFINetwork ``` +**Description:** +Gets the network type from the configuration # Safety - `config` must be a valid pointer to an FFIClientConfig or null - If null, returns FFINetwork::Dash as default + +**Safety:** +- `config` must be a valid pointer to an FFIClientConfig or null - If null, returns FFINetwork::Dash as default + **Module:** `config` --- @@ -281,6 +317,12 @@ dash_spv_ffi_config_new(network: FFINetwork) -> *mut FFIClientConfig dash_spv_ffi_config_set_data_dir(config: *mut FFIClientConfig, path: *const c_char,) -> i32 ``` +**Description:** +Sets the data directory for storing blockchain data # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - `path` must be a valid null-terminated C string - 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 - `path` must be a valid null-terminated C string - The caller must ensure the config pointer remains valid for the duration of this call + **Module:** `config` --- @@ -291,6 +333,12 @@ dash_spv_ffi_config_set_data_dir(config: *mut FFIClientConfig, path: *const c_ch dash_spv_ffi_config_set_fetch_mempool_transactions(config: *mut FFIClientConfig, fetch: bool,) -> i32 ``` +**Description:** +Sets whether to fetch full mempool transaction data # 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` --- @@ -301,6 +349,12 @@ dash_spv_ffi_config_set_fetch_mempool_transactions(config: *mut FFIClientConfig, 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` --- @@ -311,6 +365,12 @@ dash_spv_ffi_config_set_filter_load(config: *mut FFIClientConfig, load_filters: 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` --- @@ -321,6 +381,12 @@ dash_spv_ffi_config_set_max_mempool_transactions(config: *mut FFIClientConfig, m 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` --- @@ -331,6 +397,12 @@ dash_spv_ffi_config_set_max_peers(config: *mut FFIClientConfig, max_peers: u32,) dash_spv_ffi_config_set_mempool_strategy(config: *mut FFIClientConfig, strategy: FFIMempoolStrategy,) -> i32 ``` +**Description:** +Sets the mempool synchronization strategy # 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` --- @@ -341,6 +413,12 @@ dash_spv_ffi_config_set_mempool_strategy(config: *mut FFIClientConfig, strategy: dash_spv_ffi_config_set_mempool_timeout(config: *mut FFIClientConfig, timeout_secs: u64,) -> i32 ``` +**Description:** +Sets the mempool transaction timeout in seconds # 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` --- @@ -351,6 +429,12 @@ dash_spv_ffi_config_set_mempool_timeout(config: *mut FFIClientConfig, timeout_se dash_spv_ffi_config_set_mempool_tracking(config: *mut FFIClientConfig, enable: bool,) -> i32 ``` +**Description:** +Enables or disables mempool tracking # 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` --- @@ -361,6 +445,12 @@ dash_spv_ffi_config_set_mempool_tracking(config: *mut FFIClientConfig, enable: b dash_spv_ffi_config_set_persist_mempool(config: *mut FFIClientConfig, persist: bool,) -> i32 ``` +**Description:** +Sets whether to persist mempool state to disk # 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` --- @@ -371,6 +461,12 @@ dash_spv_ffi_config_set_persist_mempool(config: *mut FFIClientConfig, persist: b 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` --- @@ -381,6 +477,12 @@ dash_spv_ffi_config_set_relay_transactions(config: *mut FFIClientConfig, _relay: dash_spv_ffi_config_set_start_from_height(config: *mut FFIClientConfig, height: u32,) -> i32 ``` +**Description:** +Sets the starting block height for synchronization # 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` --- @@ -391,6 +493,12 @@ dash_spv_ffi_config_set_start_from_height(config: *mut FFIClientConfig, height: dash_spv_ffi_config_set_user_agent(config: *mut FFIClientConfig, user_agent: *const c_char,) -> i32 ``` +**Description:** +Sets the user agent string (currently not supported) # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - `user_agent` must be a valid null-terminated C string - The caller must ensure both pointers remain 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 - `user_agent` must be a valid null-terminated C string - The caller must ensure both pointers remain valid for the duration of this call + **Module:** `config` --- @@ -401,6 +509,12 @@ dash_spv_ffi_config_set_user_agent(config: *mut FFIClientConfig, user_agent: *co 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` --- @@ -411,6 +525,12 @@ dash_spv_ffi_config_set_validation_mode(config: *mut FFIClientConfig, mode: FFIV dash_spv_ffi_config_set_wallet_creation_time(config: *mut FFIClientConfig, timestamp: u32,) -> i32 ``` +**Description:** +Sets the wallet creation timestamp for synchronization optimization # 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` --- diff --git a/dash-spv-ffi/cbindgen.toml b/dash-spv-ffi/cbindgen.toml index 8e8e4a8f3..56077bb51 100644 --- a/dash-spv-ffi/cbindgen.toml +++ b/dash-spv-ffi/cbindgen.toml @@ -14,7 +14,6 @@ exclude = ["Option_BlockCallback", "Option_TransactionCallback", "Option_Balance prefix = "dash_spv_ffi_" [export.rename] -"FFINetwork" = "DashSpvNetwork" "FFIValidationMode" = "DashSpvValidationMode" "FFIErrorCode" = "DashSpvErrorCode" diff --git a/dash-spv-ffi/examples/wallet_manager_usage.rs b/dash-spv-ffi/examples/wallet_manager_usage.rs index 1ddb4b9b9..4f7dc1603 100644 --- a/dash-spv-ffi/examples/wallet_manager_usage.rs +++ b/dash-spv-ffi/examples/wallet_manager_usage.rs @@ -1,16 +1,11 @@ /// Example demonstrating the simplified FFIWalletManager usage -/// +/// /// The refactored design removes unnecessary indirection by: /// 1. FFIWalletManager directly contains Arc> /// 2. No longer requires going through the client for each operation /// 3. Cleaner and more efficient access to wallet functionality - use dash_spv_ffi::*; -use key_wallet_ffi::{ - wallet_manager_wallet_count, - wallet_manager_free, - FFIError, -}; +use key_wallet_ffi::{wallet_manager_free, wallet_manager_wallet_count, FFIError}; fn main() { unsafe { @@ -19,13 +14,13 @@ fn main() { if config.is_null() { panic!("Failed to create config"); } - + // Create an SPV client let client = dash_spv_ffi_client_new(config); if client.is_null() { panic!("Failed to create client"); } - + // Get the wallet manager - now returns void* for Swift compatibility // This contains a cloned Arc to the wallet manager, allowing // direct interaction without going through the client @@ -35,19 +30,19 @@ fn main() { } // Cast back to FFIWalletManager for use let wallet_manager = wallet_manager_ptr as *mut key_wallet_ffi::FFIWalletManager; - + // Now we can use the wallet manager directly // No need to go through client -> inner -> spv_client -> wallet() - + // Get the number of wallets (should be 0 initially) let mut error = std::mem::zeroed::(); let wallet_count = wallet_manager_wallet_count(wallet_manager, &mut error); println!("Number of wallets: {}", wallet_count); - + // Note: To get total balance, you would need to iterate through wallets // For now, just show the wallet count println!("Currently managing {} wallets", wallet_count); - + // Example of processing a transaction (with mock data) // In real usage, you would have actual transaction hex /* @@ -60,21 +55,21 @@ fn main() { 100000, // block height &mut error ); - + if affected >= 0 { println!("Transaction affected {} wallets", affected); } else { println!("Failed to process transaction"); } */ - + // Clean up // The wallet manager can now be independently destroyed // It maintains its own Arc reference to the underlying wallet wallet_manager_free(wallet_manager); dash_spv_ffi_client_destroy(client); dash_spv_ffi_config_destroy(config); - + println!("Example completed successfully!"); } -} \ No newline at end of file +} diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index d358bca08..b3270a16d 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -9,13 +9,6 @@ typedef enum FFIMempoolStrategy { Selective = 2, } FFIMempoolStrategy; -typedef enum FFINetwork { - Dash = 0, - Testnet = 1, - Regtest = 2, - Devnet = 3, -} FFINetwork; - typedef enum FFISyncStage { Connecting = 0, QueryingHeight = 1, @@ -342,54 +335,211 @@ int32_t dash_spv_ffi_client_record_send(struct FFIDashSpvClient *client, const c */ void *dash_spv_ffi_client_get_wallet_manager(struct FFIDashSpvClient *client); -FFIClientConfig *dash_spv_ffi_config_new(enum FFINetwork network); +FFIClientConfig *dash_spv_ffi_config_new(FFINetwork network); FFIClientConfig *dash_spv_ffi_config_mainnet(void); FFIClientConfig *dash_spv_ffi_config_testnet(void); -int32_t dash_spv_ffi_config_set_data_dir(FFIClientConfig *config, const char *path); +/** + * Sets the data directory for storing blockchain data + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `path` must be a valid null-terminated C string + * - The caller must ensure the config pointer remains valid for the duration of this call + */ +int32_t dash_spv_ffi_config_set_data_dir(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(FFIClientConfig *config, enum FFIValidationMode mode); -int32_t dash_spv_ffi_config_set_max_peers(FFIClientConfig *config, uint32_t max_peers); +/** + * 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(FFIClientConfig *config, + uint32_t max_peers); -int32_t dash_spv_ffi_config_add_peer(FFIClientConfig *config, const char *addr); +/** + * Adds a peer address to the configuration + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `addr` must be a valid null-terminated C string containing a socket address (e.g., "192.168.1.1:9999") + * - The caller must ensure both pointers remain valid for the duration of this call + */ +int32_t dash_spv_ffi_config_add_peer(FFIClientConfig *config, + const char *addr); -int32_t dash_spv_ffi_config_set_user_agent(FFIClientConfig *config, const char *user_agent); +/** + * Sets the user agent string (currently not supported) + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `user_agent` must be a valid null-terminated C string + * - The caller must ensure both pointers remain valid for the duration of this call + */ +int32_t dash_spv_ffi_config_set_user_agent(FFIClientConfig *config, + const char *user_agent); -int32_t dash_spv_ffi_config_set_relay_transactions(FFIClientConfig *config, bool _relay); +/** + * 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(FFIClientConfig *config, + bool _relay); -int32_t dash_spv_ffi_config_set_filter_load(FFIClientConfig *config, bool load_filters); +/** + * 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(FFIClientConfig *config, + bool load_filters); -enum FFINetwork dash_spv_ffi_config_get_network(const FFIClientConfig *config); +/** + * Gets the network type from the configuration + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig or null + * - If null, returns FFINetwork::Dash as default + */ +FFINetwork dash_spv_ffi_config_get_network(const 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_string_free + */ struct FFIString dash_spv_ffi_config_get_data_dir(const FFIClientConfig *config); +/** + * Destroys an FFIClientConfig and frees its memory + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet, or null + * - After calling this function, the config pointer becomes invalid and must not be used + * - This function should only be called once per config instance + */ void dash_spv_ffi_config_destroy(FFIClientConfig *config); -int32_t dash_spv_ffi_config_set_mempool_tracking(FFIClientConfig *config, bool enable); +/** + * Enables or disables mempool tracking + * + * # 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_mempool_tracking(FFIClientConfig *config, + bool enable); +/** + * Sets the mempool synchronization strategy + * + * # 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_mempool_strategy(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(FFIClientConfig *config, uint32_t max_transactions); -int32_t dash_spv_ffi_config_set_mempool_timeout(FFIClientConfig *config, uint64_t timeout_secs); +/** + * Sets the mempool transaction timeout in seconds + * + * # 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_mempool_timeout(FFIClientConfig *config, + uint64_t timeout_secs); -int32_t dash_spv_ffi_config_set_fetch_mempool_transactions(FFIClientConfig *config, bool fetch); +/** + * Sets whether to fetch full mempool transaction data + * + * # 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_fetch_mempool_transactions(FFIClientConfig *config, + bool fetch); -int32_t dash_spv_ffi_config_set_persist_mempool(FFIClientConfig *config, bool persist); +/** + * Sets whether to persist mempool state to disk + * + * # 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_persist_mempool(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 FFIClientConfig *config); +/** + * Gets the mempool synchronization strategy + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig or null + * - If null, returns FFIMempoolStrategy::Selective as default + */ enum FFIMempoolStrategy dash_spv_ffi_config_get_mempool_strategy(const FFIClientConfig *config); -int32_t dash_spv_ffi_config_set_start_from_height(FFIClientConfig *config, uint32_t height); +/** + * Sets the starting block height for synchronization + * + * # 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_start_from_height(FFIClientConfig *config, + uint32_t height); -int32_t dash_spv_ffi_config_set_wallet_creation_time(FFIClientConfig *config, uint32_t timestamp); +/** + * Sets the wallet creation timestamp for synchronization optimization + * + * # 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_wallet_creation_time(FFIClientConfig *config, + uint32_t timestamp); const char *dash_spv_ffi_get_last_error(void); @@ -503,8 +653,6 @@ int32_t dash_spv_ffi_init_logging(const char *level); const char *dash_spv_ffi_version(void); -const char *dash_spv_ffi_get_network_name(enum FFINetwork network); - void dash_spv_ffi_enable_test_mode(void); int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, diff --git a/dash-spv-ffi/src/broadcast.rs b/dash-spv-ffi/src/broadcast.rs index ff68369ea..1aebd31e7 100644 --- a/dash-spv-ffi/src/broadcast.rs +++ b/dash-spv-ffi/src/broadcast.rs @@ -1,6 +1,6 @@ +use crate::{null_check, set_last_error, FFIDashSpvClient, FFIErrorCode}; use std::ffi::CStr; use std::os::raw::c_char; -use crate::{null_check, set_last_error, FFIDashSpvClient, FFIErrorCode}; #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( @@ -56,4 +56,4 @@ pub unsafe extern "C" fn dash_spv_ffi_client_broadcast_transaction( FFIErrorCode::from(e) as i32 } } -} \ No newline at end of file +} diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 41e2301af..e00a47161 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -991,7 +991,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_rescan_blockchain( } } - #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_enable_mempool_tracking( client: *mut FFIDashSpvClient, @@ -1082,9 +1081,9 @@ pub unsafe extern "C" fn dash_spv_ffi_client_record_send( /// The caller must ensure that: /// - The client pointer is valid /// - The returned pointer is freed using wallet_manager_free() -/// +/// /// # Returns -/// +/// /// An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. /// Swift should treat this as an OpaquePointer. #[no_mangle] @@ -1092,18 +1091,18 @@ pub unsafe extern "C" fn dash_spv_ffi_client_get_wallet_manager( client: *mut FFIDashSpvClient, ) -> *mut c_void { null_check!(client, std::ptr::null_mut()); - + let client = &*client; let inner = client.inner.lock().unwrap(); - + if let Some(ref spv_client) = *inner { // Clone the Arc to the wallet manager let wallet_arc = spv_client.wallet().clone(); let runtime = client.runtime.clone(); - + // Create the FFIWalletManager with the cloned Arc let manager = FFIWalletManager::from_arc(wallet_arc, runtime); - + Box::into_raw(Box::new(manager)) as *mut c_void } else { set_last_error("Client not initialized"); diff --git a/dash-spv-ffi/src/config.rs b/dash-spv-ffi/src/config.rs index 913b19a92..985e2d2df 100644 --- a/dash-spv-ffi/src/config.rs +++ b/dash-spv-ffi/src/config.rs @@ -1,5 +1,6 @@ -use crate::{null_check, set_last_error, FFIErrorCode, FFIMempoolStrategy, FFINetwork, FFIString}; +use crate::{null_check, set_last_error, FFIErrorCode, FFIMempoolStrategy, FFIString}; use dash_spv::{ClientConfig, ValidationMode}; +use key_wallet_ffi::FFINetwork; use std::ffi::CStr; use std::os::raw::c_char; @@ -49,6 +50,12 @@ pub extern "C" fn dash_spv_ffi_config_testnet() -> *mut FFIClientConfig { })) } +/// Sets the data directory for storing blockchain data +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet +/// - `path` must be a valid null-terminated C string +/// - 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_data_dir( config: *mut FFIClientConfig, @@ -70,6 +77,11 @@ 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, @@ -82,6 +94,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_validation_mode( 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, @@ -96,6 +113,12 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_max_peers( // Note: dash-spv doesn't have min_peers, only max_peers +/// Adds a peer address to the configuration +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet +/// - `addr` must be a valid null-terminated C string containing a socket address (e.g., "192.168.1.1:9999") +/// - The caller must ensure both pointers remain valid for the duration of this call #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_add_peer( config: *mut FFIClientConfig, @@ -123,6 +146,12 @@ pub unsafe extern "C" fn dash_spv_ffi_config_add_peer( } } +/// Sets the user agent string (currently not supported) +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet +/// - `user_agent` must be a valid null-terminated C string +/// - The caller must ensure both pointers remain valid for the duration of this call #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_set_user_agent( config: *mut FFIClientConfig, @@ -145,6 +174,11 @@ 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, @@ -157,6 +191,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_relay_transactions( 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, @@ -169,6 +208,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_filter_load( FFIErrorCode::Success as i32 } +/// Gets the network type from the configuration +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig or null +/// - If null, returns FFINetwork::Dash as default #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_get_network( config: *const FFIClientConfig, @@ -181,6 +225,12 @@ 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_string_free #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_get_data_dir( config: *const FFIClientConfig, @@ -202,6 +252,12 @@ pub unsafe extern "C" fn dash_spv_ffi_config_get_data_dir( } } +/// Destroys an FFIClientConfig and frees its memory +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet, or null +/// - After calling this function, the config pointer becomes invalid and must not be used +/// - This function should only be called once per config instance #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_destroy(config: *mut FFIClientConfig) { if !config.is_null() { @@ -221,6 +277,11 @@ impl FFIClientConfig { // Mempool configuration functions +/// Enables or disables mempool tracking +/// +/// # 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_mempool_tracking( config: *mut FFIClientConfig, @@ -233,6 +294,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_mempool_tracking( FFIErrorCode::Success as i32 } +/// Sets the mempool synchronization strategy +/// +/// # 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_mempool_strategy( config: *mut FFIClientConfig, @@ -245,6 +311,11 @@ 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, @@ -257,6 +328,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_max_mempool_transactions( FFIErrorCode::Success as i32 } +/// Sets the mempool transaction timeout in seconds +/// +/// # 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_mempool_timeout( config: *mut FFIClientConfig, @@ -269,6 +345,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_mempool_timeout( FFIErrorCode::Success as i32 } +/// Sets whether to fetch full mempool transaction data +/// +/// # 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_fetch_mempool_transactions( config: *mut FFIClientConfig, @@ -281,6 +362,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_fetch_mempool_transactions( FFIErrorCode::Success as i32 } +/// Sets whether to persist mempool state to disk +/// +/// # 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_persist_mempool( config: *mut FFIClientConfig, @@ -293,6 +379,11 @@ 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, @@ -305,6 +396,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_get_mempool_tracking( 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::Selective as default #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_config_get_mempool_strategy( config: *const FFIClientConfig, @@ -319,6 +415,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_get_mempool_strategy( // Checkpoint sync configuration functions +/// Sets the starting block height for synchronization +/// +/// # 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_start_from_height( config: *mut FFIClientConfig, @@ -331,6 +432,11 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_start_from_height( FFIErrorCode::Success as i32 } +/// Sets the wallet creation timestamp for synchronization optimization +/// +/// # 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_wallet_creation_time( config: *mut FFIClientConfig, diff --git a/dash-spv-ffi/src/lib.rs b/dash-spv-ffi/src/lib.rs index 87ef6b74b..59733372e 100644 --- a/dash-spv-ffi/src/lib.rs +++ b/dash-spv-ffi/src/lib.rs @@ -14,8 +14,8 @@ pub use platform_integration::*; pub use types::*; pub use utils::*; -// Re-export commonly used types -pub use types::FFINetwork; +// FFINetwork is now defined in types.rs for cbindgen compatibility +// It must match the definition in key_wallet_ffi #[cfg(test)] #[path = "../tests/unit/test_type_conversions.rs"] @@ -37,7 +37,7 @@ 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; -mod broadcast; diff --git a/dash-spv-ffi/src/types.rs b/dash-spv-ffi/src/types.rs index f4ab41f5d..98b3be71e 100644 --- a/dash-spv-ffi/src/types.rs +++ b/dash-spv-ffi/src/types.rs @@ -1,7 +1,6 @@ use dash_spv::client::config::MempoolStrategy; use dash_spv::types::{DetailedSyncProgress, MempoolRemovalReason, SyncStage}; use dash_spv::{ChainState, PeerInfo, SpvStats, SyncProgress}; -use dashcore::Network; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; @@ -30,38 +29,6 @@ impl FFIString { } } -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum FFINetwork { - Dash = 0, - Testnet = 1, - Regtest = 2, - Devnet = 3, -} - -impl From for Network { - fn from(net: FFINetwork) -> Self { - match net { - FFINetwork::Dash => Network::Dash, - FFINetwork::Testnet => Network::Testnet, - FFINetwork::Regtest => Network::Regtest, - FFINetwork::Devnet => Network::Devnet, - } - } -} - -impl From for FFINetwork { - fn from(net: Network) -> Self { - match net { - Network::Dash => FFINetwork::Dash, - Network::Testnet => FFINetwork::Testnet, - Network::Regtest => FFINetwork::Regtest, - Network::Devnet => FFINetwork::Devnet, - _ => FFINetwork::Dash, - } - } -} - #[repr(C)] pub struct FFISyncProgress { pub header_height: u32, diff --git a/dash-spv-ffi/src/utils.rs b/dash-spv-ffi/src/utils.rs index 26ffc4834..326ec57a3 100644 --- a/dash-spv-ffi/src/utils.rs +++ b/dash-spv-ffi/src/utils.rs @@ -30,16 +30,6 @@ 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_get_network_name(network: crate::FFINetwork) -> *const c_char { - match network { - crate::FFINetwork::Dash => "dash\0".as_ptr() as *const c_char, - crate::FFINetwork::Testnet => "testnet\0".as_ptr() as *const c_char, - crate::FFINetwork::Regtest => "regtest\0".as_ptr() as *const c_char, - crate::FFINetwork::Devnet => "devnet\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 b8ddcb419..532238559 100644 --- a/dash-spv-ffi/tests/test_client.rs +++ b/dash-spv-ffi/tests/test_client.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::CString; use std::os::raw::c_void; diff --git a/dash-spv-ffi/tests/test_config.rs b/dash-spv-ffi/tests/test_config.rs index b933555de..92d1e6232 100644 --- a/dash-spv-ffi/tests/test_config.rs +++ b/dash-spv-ffi/tests/test_config.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::CString; diff --git a/dash-spv-ffi/tests/test_event_callbacks.rs b/dash-spv-ffi/tests/test_event_callbacks.rs index fb3459c74..724485451 100644 --- a/dash-spv-ffi/tests/test_event_callbacks.rs +++ b/dash-spv-ffi/tests/test_event_callbacks.rs @@ -1,4 +1,5 @@ use dash_spv_ffi::*; +use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::{c_char, c_void, CStr, CString}; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; diff --git a/dash-spv-ffi/tests/test_types.rs b/dash-spv-ffi/tests/test_types.rs index 48b11baaa..a68ded8ed 100644 --- a/dash-spv-ffi/tests/test_types.rs +++ b/dash-spv-ffi/tests/test_types.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; + use key_wallet_ffi::FFINetwork; #[test] fn test_ffi_string_new_and_destroy() { diff --git a/dash-spv-ffi/tests/test_utils.rs b/dash-spv-ffi/tests/test_utils.rs index 6dc8eff46..2ea1b6504 100644 --- a/dash-spv-ffi/tests/test_utils.rs +++ b/dash-spv-ffi/tests/test_utils.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; + use key_wallet_ffi::types::ffi_network_get_name; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::{CStr, CString}; @@ -40,22 +42,22 @@ mod tests { #[test] fn test_network_names() { unsafe { - let name = dash_spv_ffi_get_network_name(FFINetwork::Dash); + let name = ffi_network_get_name(FFINetwork::Dash); assert!(!name.is_null()); let name_str = CStr::from_ptr(name).to_str().unwrap(); assert_eq!(name_str, "dash"); - let name = dash_spv_ffi_get_network_name(FFINetwork::Testnet); + let name = ffi_network_get_name(FFINetwork::Testnet); assert!(!name.is_null()); let name_str = CStr::from_ptr(name).to_str().unwrap(); assert_eq!(name_str, "testnet"); - let name = dash_spv_ffi_get_network_name(FFINetwork::Regtest); + let name = ffi_network_get_name(FFINetwork::Regtest); assert!(!name.is_null()); let name_str = CStr::from_ptr(name).to_str().unwrap(); assert_eq!(name_str, "regtest"); - let name = dash_spv_ffi_get_network_name(FFINetwork::Devnet); + let name = ffi_network_get_name(FFINetwork::Devnet); assert!(!name.is_null()); let name_str = CStr::from_ptr(name).to_str().unwrap(); assert_eq!(name_str, "devnet"); diff --git a/dash-spv-ffi/tests/test_wallet_manager.rs b/dash-spv-ffi/tests/test_wallet_manager.rs index 7288529ee..570ffefce 100644 --- a/dash-spv-ffi/tests/test_wallet_manager.rs +++ b/dash-spv-ffi/tests/test_wallet_manager.rs @@ -1,32 +1,38 @@ #[cfg(test)] mod tests { use dash_spv_ffi::*; - use key_wallet_ffi::{FFIError, FFIWalletManager, wallet_manager::{wallet_manager_wallet_count, wallet_manager_free}}; - + use key_wallet_ffi::{ + wallet_manager::{wallet_manager_free, wallet_manager_wallet_count}, + FFIError, FFIWalletManager, + }; + #[test] fn test_get_wallet_manager() { unsafe { // Create a config let config = dash_spv_ffi_config_testnet(); assert!(!config.is_null()); - + // Create a client let client = dash_spv_ffi_client_new(config); assert!(!client.is_null()); - + // Get wallet manager let wallet_manager = dash_spv_ffi_client_get_wallet_manager(client); assert!(!wallet_manager.is_null()); - + // Get wallet count (should be 0 initially) let mut error = FFIError::success(); - let count = wallet_manager_wallet_count(wallet_manager as *const FFIWalletManager, &mut error as *mut FFIError); + let count = wallet_manager_wallet_count( + wallet_manager as *const FFIWalletManager, + &mut error as *mut FFIError, + ); assert_eq!(count, 0); - + // Clean up wallet_manager_free(wallet_manager as *mut FFIWalletManager); dash_spv_ffi_client_destroy(client); dash_spv_ffi_config_destroy(config); } } -} \ No newline at end of file +} diff --git a/dash-spv-ffi/tests/unit/test_async_operations.rs b/dash-spv-ffi/tests/unit/test_async_operations.rs index 30373b9e0..19f2422f8 100644 --- a/dash-spv-ffi/tests/unit/test_async_operations.rs +++ b/dash-spv-ffi/tests/unit/test_async_operations.rs @@ -2,6 +2,7 @@ mod tests { use crate::types::FFIDetailedSyncProgress; use crate::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; diff --git a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs index 31df478fa..067c1ba6d 100644 --- a/dash-spv-ffi/tests/unit/test_client_lifecycle.rs +++ b/dash-spv-ffi/tests/unit/test_client_lifecycle.rs @@ -6,6 +6,7 @@ #[cfg(test)] mod tests { use crate::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::CString; use std::thread; diff --git a/dash-spv-ffi/tests/unit/test_configuration.rs b/dash-spv-ffi/tests/unit/test_configuration.rs index 18fb98550..2f4f794bc 100644 --- a/dash-spv-ffi/tests/unit/test_configuration.rs +++ b/dash-spv-ffi/tests/unit/test_configuration.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use crate::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::CString; diff --git a/dash-spv-ffi/tests/unit/test_error_handling.rs b/dash-spv-ffi/tests/unit/test_error_handling.rs index c32b92b9c..52588661d 100644 --- a/dash-spv-ffi/tests/unit/test_error_handling.rs +++ b/dash-spv-ffi/tests/unit/test_error_handling.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use crate::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::CStr; use std::sync::{Arc, Barrier}; diff --git a/dash-spv-ffi/tests/unit/test_memory_management.rs b/dash-spv-ffi/tests/unit/test_memory_management.rs index 326965f50..0a29e7b18 100644 --- a/dash-spv-ffi/tests/unit/test_memory_management.rs +++ b/dash-spv-ffi/tests/unit/test_memory_management.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use crate::*; + use key_wallet_ffi::FFINetwork; use serial_test::serial; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; diff --git a/dash-spv-ffi/tests/unit/test_type_conversions.rs b/dash-spv-ffi/tests/unit/test_type_conversions.rs index 9df36642e..d02c9828f 100644 --- a/dash-spv-ffi/tests/unit/test_type_conversions.rs +++ b/dash-spv-ffi/tests/unit/test_type_conversions.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use crate::*; + use key_wallet_ffi::FFINetwork; #[test] fn test_ffi_string_utf8_edge_cases() { diff --git a/dash-spv/examples/spv_with_wallet.rs b/dash-spv/examples/spv_with_wallet.rs new file mode 100644 index 000000000..4c80afe2d --- /dev/null +++ b/dash-spv/examples/spv_with_wallet.rs @@ -0,0 +1,57 @@ +//! Example of using DashSpvClient with a wallet implementation +//! +//! This example shows how to integrate the SPV client with a wallet manager. + +use dash_spv::network::MultiPeerNetworkManager; +use dash_spv::storage::DiskStorageManager; +use dash_spv::{ClientConfig, DashSpvClient}; +use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; +use key_wallet_manager::wallet_manager::WalletManager; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Initialize logging + dash_spv::init_logging("info")?; + + // Create SPV client configuration + let mut config = ClientConfig::testnet(); + config.storage_path = Some("/tmp/dash-spv-example".into()); + config.validation_mode = dash_spv::types::ValidationMode::Full; + config.enable_filters = true; + + // Create network manager + let network_manager = MultiPeerNetworkManager::new(&config).await?; + + // Create storage manager - use disk storage for persistence + let storage_manager = DiskStorageManager::new("/tmp/dash-spv-example".into()).await?; + + // Create wallet manager + let wallet = Arc::new(RwLock::new(WalletManager::::new())); + + // Create the SPV client with all components + let mut client = DashSpvClient::new(config, network_manager, storage_manager, wallet).await?; + + // Start the client + println!("Starting SPV client..."); + client.start().await?; + + // Sync to the tip of the blockchain + println!("Syncing to blockchain tip..."); + let progress = client.sync_to_tip().await?; + println!("Synced to height: {}", progress.header_height); + + // The wallet will automatically be notified of: + // - New blocks via process_block() + // - Mempool transactions via process_mempool_transaction() + // - Reorgs via handle_reorg() + // - Compact filter checks via check_compact_filter() + + // Stop the client + println!("Stopping SPV client..."); + client.stop().await?; + + println!("Done!"); + Ok(()) +} diff --git a/dash-spv/tests/block_download_test.rs b/dash-spv/tests/block_download_test.rs index 3825b15fd..f4a95edeb 100644 --- a/dash-spv/tests/block_download_test.rs +++ b/dash-spv/tests/block_download_test.rs @@ -376,9 +376,9 @@ async fn test_process_multiple_filter_matches() { async fn test_sync_manager_integration() { let config = create_test_config(); let received_heights = Arc::new(Mutex::new(HashSet::new())); - let wallet = Arc::new(RwLock::new( - key_wallet_manager::wallet_manager::WalletManager::::new() - )); + let wallet = Arc::new(RwLock::new(key_wallet_manager::wallet_manager::WalletManager::< + ManagedWalletInfo, + >::new())); let mut sync_manager: SequentialSyncManager = SequentialSyncManager::new(&config, received_heights, wallet) .expect("Failed to create SequentialSyncManager for integration test"); diff --git a/dash-spv/tests/instantsend_integration_test.rs b/dash-spv/tests/instantsend_integration_test.rs index 92da7faf2..fda99e5df 100644 --- a/dash-spv/tests/instantsend_integration_test.rs +++ b/dash-spv/tests/instantsend_integration_test.rs @@ -31,9 +31,7 @@ use dashcore::{ }; use dashcore_hashes::{sha256d, Hash}; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::{ - wallet_manager::WalletManager, Utxo, -}; +use key_wallet_manager::{wallet_manager::WalletManager, Utxo}; use rand::thread_rng; /// Helper to create a test wallet manager. diff --git a/dash-spv/tests/wallet_integration_test.rs b/dash-spv/tests/wallet_integration_test.rs index 3302fcc69..bc7464d33 100644 --- a/dash-spv/tests/wallet_integration_test.rs +++ b/dash-spv/tests/wallet_integration_test.rs @@ -13,7 +13,8 @@ use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet_manager::wallet_manager::WalletManager; /// Create a test SPV client with memory storage for integration testing. async fn create_test_client( -) -> DashSpvClient, MultiPeerNetworkManager, MemoryStorageManager> { +) -> DashSpvClient, MultiPeerNetworkManager, MemoryStorageManager> +{ let config = ClientConfig::testnet().without_filters().without_masternodes(); // Create network manager diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index f5b24e6a7..23006b70c 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 212 +**Total Functions**: 226 ## Table of Contents @@ -69,7 +69,7 @@ Functions: 20 ### Wallet Operations -Functions: 55 +Functions: 58 | Function | Description | Module | |----------|-------------|--------| @@ -77,6 +77,9 @@ Functions: 55 | `bls_account_get_parent_wallet_id` | No description | account | | `eddsa_account_get_parent_wallet_id` | No description | account | | `ffi_managed_wallet_free` | Free a managed wallet (FFIManagedWallet type) # Safety - `managed_wallet` m... | transaction_checking | +| `key_wallet_derive_address_from_key` | Derive an address from a private key # Safety - `private_key` must be a vali... | derivation | +| `key_wallet_derive_address_from_seed` | Derive an address from a seed at a specific derivation path # Safety - `seed... | derivation | +| `key_wallet_derive_private_key_from_seed` | Derive a private key from a seed at a specific derivation path # Safety - `s... | derivation | | `managed_account_get_parent_wallet_id` | Get the parent wallet ID of a managed account Note: ManagedAccount doesn't s... | managed_account | | `managed_wallet_check_transaction` | Check if a transaction belongs to the wallet This function checks a transact... | transaction_checking | | `managed_wallet_free` | Free managed wallet info # Safety - `managed_wallet` must be a valid pointe... | managed_wallet | @@ -219,7 +222,7 @@ Functions: 81 ### Address Management -Functions: 9 +Functions: 10 | Function | Description | Module | |----------|-------------|--------| @@ -231,17 +234,27 @@ Functions: 9 | `address_pool_free` | Free an address pool handle # Safety - `pool` must be a valid pointer to an... | address_pool | | `address_pool_get_address_at_index` | Get a single address info at a specific index from the pool Returns detailed... | address_pool | | `address_pool_get_addresses_in_range` | Get a range of addresses from the pool Returns an array of FFIAddressInfo st... | address_pool | +| `address_to_pubkey_hash` | Extract public key hash from P2PKH address # Safety - `address` must be a va... | transaction | | `address_validate` | Validate an address # Safety - `address` must be a valid null-terminated C ... | address | ### Transaction Management -Functions: 4 +Functions: 13 | Function | Description | Module | |----------|-------------|--------| +| `transaction_add_input` | Add an input to a transaction # Safety - `tx` must be a valid pointer to an ... | transaction | +| `transaction_add_output` | Add an output to a transaction # Safety - `tx` must be a valid pointer to an... | transaction | | `transaction_bytes_free` | Free transaction bytes # Safety - `tx_bytes` must be a valid pointer create... | transaction | | `transaction_check_result_free` | Free a transaction check result # Safety - `result` must be a valid pointer... | transaction_checking | | `transaction_classify` | Get the transaction classification for routing Returns a string describing t... | transaction_checking | +| `transaction_create` | Create a new empty transaction # Returns - Pointer to FFITransaction on succ... | transaction | +| `transaction_deserialize` | Deserialize a transaction # Safety - `data` must be a valid pointer to seria... | transaction | +| `transaction_destroy` | Destroy a transaction # Safety - `tx` must be a valid pointer to an FFITrans... | transaction | +| `transaction_get_txid` | Get the transaction ID # Safety - `tx` must be a valid pointer to an FFITran... | transaction | +| `transaction_serialize` | Serialize a transaction # Safety - `tx` must be a valid pointer to an FFITra... | transaction | +| `transaction_sighash` | Calculate signature hash for an input # Safety - `tx` must be a valid pointe... | transaction | +| `transaction_sign_input` | Sign a transaction input # Safety - `tx` must be a valid pointer to an FFITr... | transaction | | `utxo_array_free` | Free UTXO array # Safety - `utxos` must be a valid pointer to an array of F... | utxo | ### Key Management @@ -282,7 +295,7 @@ Functions: 6 ### Utility Functions -Functions: 16 +Functions: 17 | Function | Description | Module | |----------|-------------|--------| @@ -301,6 +314,7 @@ Functions: 16 | `derivation_xpub_free` | Free extended public key # Safety - `xpub` must be a valid pointer to an FF... | derivation | | `derivation_xpub_to_string` | Get extended public key as string # Safety - `xpub` must be a valid pointer... | derivation | | `free_u32_array` | Free a u32 array allocated by this library # Safety - `array` must be a val... | account_collection | +| `script_p2pkh` | Create a P2PKH script pubkey # Safety - `pubkey_hash` must be a valid pointe... | transaction | | `string_free` | Free a string # Safety - `s` must be a valid pointer created by C string cr... | utils | ## Detailed Function Documentation @@ -738,6 +752,54 @@ Free a managed wallet (FFIManagedWallet type) # Safety - `managed_wallet` must --- +#### `key_wallet_derive_address_from_key` + +```c +key_wallet_derive_address_from_key(private_key: *const u8, network: FFINetwork,) -> *mut c_char +``` + +**Description:** +Derive an address from a private key # Safety - `private_key` must be a valid pointer to 32 bytes - `network` is the network for the address # Returns - Pointer to C string with address (caller must free) - NULL on error + +**Safety:** +- `private_key` must be a valid pointer to 32 bytes - `network` is the network for the address + +**Module:** `derivation` + +--- + +#### `key_wallet_derive_address_from_seed` + +```c +key_wallet_derive_address_from_seed(seed: *const u8, network: FFINetwork, path: *const c_char,) -> *mut c_char +``` + +**Description:** +Derive an address from a seed at a specific derivation path # Safety - `seed` must be a valid pointer to 64 bytes - `network` is the network for the address - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") # Returns - Pointer to C string with address (caller must free) - NULL on error + +**Safety:** +- `seed` must be a valid pointer to 64 bytes - `network` is the network for the address - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") + +**Module:** `derivation` + +--- + +#### `key_wallet_derive_private_key_from_seed` + +```c +key_wallet_derive_private_key_from_seed(seed: *const u8, path: *const c_char, key_out: *mut u8,) -> i32 +``` + +**Description:** +Derive a private key from a seed at a specific derivation path # Safety - `seed` must be a valid pointer to 64 bytes - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") - `key_out` must be a valid pointer to a buffer of at least 32 bytes # Returns - 0 on success - -1 on error + +**Safety:** +- `seed` must be a valid pointer to 64 bytes - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") - `key_out` must be a valid pointer to a buffer of at least 32 bytes + +**Module:** `derivation` + +--- + #### `managed_account_get_parent_wallet_id` ```c @@ -2919,6 +2981,22 @@ Get a range of addresses from the pool Returns an array of FFIAddressInfo struc --- +#### `address_to_pubkey_hash` + +```c +address_to_pubkey_hash(address: *const c_char, network: FFINetwork, hash_out: *mut u8,) -> i32 +``` + +**Description:** +Extract public key hash from P2PKH address # Safety - `address` must be a valid pointer to a null-terminated C string - `hash_out` must be a valid pointer to a buffer of at least 20 bytes # Returns - 0 on success - -1 on error + +**Safety:** +- `address` must be a valid pointer to a null-terminated C string - `hash_out` must be a valid pointer to a buffer of at least 20 bytes + +**Module:** `transaction` + +--- + #### `address_validate` ```c @@ -2937,6 +3015,38 @@ Validate an address # Safety - `address` must be a valid null-terminated C str ### Transaction Management - Detailed +#### `transaction_add_input` + +```c +transaction_add_input(tx: *mut FFITransaction, input: *const FFITxIn) -> i32 +``` + +**Description:** +Add an input to a transaction # Safety - `tx` must be a valid pointer to an FFITransaction - `input` must be a valid pointer to an FFITxIn # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `input` must be a valid pointer to an FFITxIn + +**Module:** `transaction` + +--- + +#### `transaction_add_output` + +```c +transaction_add_output(tx: *mut FFITransaction, output: *const FFITxOut) -> i32 +``` + +**Description:** +Add an output to a transaction # Safety - `tx` must be a valid pointer to an FFITransaction - `output` must be a valid pointer to an FFITxOut # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `output` must be a valid pointer to an FFITxOut + +**Module:** `transaction` + +--- + #### `transaction_bytes_free` ```c @@ -2985,6 +3095,115 @@ Get the transaction classification for routing Returns a string describing the --- +#### `transaction_create` + +```c +transaction_create() -> *mut FFITransaction +``` + +**Description:** +Create a new empty transaction # Returns - Pointer to FFITransaction on success - NULL on error + +**Module:** `transaction` + +--- + +#### `transaction_deserialize` + +```c +transaction_deserialize(data: *const u8, len: u32) -> *mut FFITransaction +``` + +**Description:** +Deserialize a transaction # Safety - `data` must be a valid pointer to serialized transaction data - `len` must be the correct length of the data # Returns - Pointer to FFITransaction on success - NULL on error + +**Safety:** +- `data` must be a valid pointer to serialized transaction data - `len` must be the correct length of the data + +**Module:** `transaction` + +--- + +#### `transaction_destroy` + +```c +transaction_destroy(tx: *mut FFITransaction) -> () +``` + +**Description:** +Destroy a transaction # Safety - `tx` must be a valid pointer to an FFITransaction created by transaction functions or null - After calling this function, the pointer becomes invalid + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction created by transaction functions or null - After calling this function, the pointer becomes invalid + +**Module:** `transaction` + +--- + +#### `transaction_get_txid` + +```c +transaction_get_txid(tx: *const FFITransaction, txid_out: *mut u8) -> i32 +``` + +**Description:** +Get the transaction ID # Safety - `tx` must be a valid pointer to an FFITransaction - `txid_out` must be a valid pointer to a buffer of at least 32 bytes # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `txid_out` must be a valid pointer to a buffer of at least 32 bytes + +**Module:** `transaction` + +--- + +#### `transaction_serialize` + +```c +transaction_serialize(tx: *const FFITransaction, out_buf: *mut u8, out_len: *mut u32,) -> i32 +``` + +**Description:** +Serialize a transaction # Safety - `tx` must be a valid pointer to an FFITransaction - `out_buf` can be NULL to get size only - `out_len` must be a valid pointer to store the size # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `out_buf` can be NULL to get size only - `out_len` must be a valid pointer to store the size + +**Module:** `transaction` + +--- + +#### `transaction_sighash` + +```c +transaction_sighash(tx: *const FFITransaction, input_index: u32, script_pubkey: *const u8, script_pubkey_len: u32, sighash_type: u32, hash_out: *mut u8,) -> i32 +``` + +**Description:** +Calculate signature hash for an input # Safety - `tx` must be a valid pointer to an FFITransaction - `script_pubkey` must be a valid pointer to the script pubkey - `hash_out` must be a valid pointer to a buffer of at least 32 bytes # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `script_pubkey` must be a valid pointer to the script pubkey - `hash_out` must be a valid pointer to a buffer of at least 32 bytes + +**Module:** `transaction` + +--- + +#### `transaction_sign_input` + +```c +transaction_sign_input(tx: *mut FFITransaction, input_index: u32, private_key: *const u8, script_pubkey: *const u8, script_pubkey_len: u32, sighash_type: u32,) -> i32 +``` + +**Description:** +Sign a transaction input # Safety - `tx` must be a valid pointer to an FFITransaction - `private_key` must be a valid pointer to a 32-byte private key - `script_pubkey` must be a valid pointer to the script pubkey # Returns - 0 on success - -1 on error + +**Safety:** +- `tx` must be a valid pointer to an FFITransaction - `private_key` must be a valid pointer to a 32-byte private key - `script_pubkey` must be a valid pointer to the script pubkey + +**Module:** `transaction` + +--- + #### `utxo_array_free` ```c @@ -3578,6 +3797,22 @@ Free a u32 array allocated by this library # Safety - `array` must be a valid --- +#### `script_p2pkh` + +```c +script_p2pkh(pubkey_hash: *const u8, out_buf: *mut u8, out_len: *mut u32,) -> i32 +``` + +**Description:** +Create a P2PKH script pubkey # Safety - `pubkey_hash` must be a valid pointer to a 20-byte public key hash - `out_buf` can be NULL to get size only - `out_len` must be a valid pointer to store the size # Returns - 0 on success - -1 on error + +**Safety:** +- `pubkey_hash` must be a valid pointer to a 20-byte public key hash - `out_buf` can be NULL to get size only - `out_len` must be a valid pointer to store the size + +**Module:** `transaction` + +--- + #### `string_free` ```c diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index defc4e107..b5c552750 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -188,6 +188,16 @@ typedef enum { SPANISH = 9, } FFILanguage; +/* + FFI Network type (single network) + */ +typedef enum { + DASH = 0, + TESTNET = 1, + REGTEST = 2, + DEVNET = 3, +} FFINetwork; + /* FFI Network type (bit flags for multiple networks) */ @@ -223,19 +233,19 @@ typedef enum { } FFIProviderKeyType; /* - Transaction context for checking + FFI-compatible transaction context */ typedef enum { /* - Transaction is in mempool (unconfirmed) + Transaction is in the mempool (unconfirmed) */ MEMPOOL = 0, /* - Transaction is in a block + Transaction is in a block at the given height */ IN_BLOCK = 1, /* - Transaction is in a chain-locked block + Transaction is in a chain-locked block at the given height */ IN_CHAIN_LOCKED_BLOCK = 2, } FFITransactionContext; @@ -313,6 +323,11 @@ typedef struct FFIPrivateKey FFIPrivateKey; */ typedef struct FFIPublicKey FFIPublicKey; +/* + Opaque handle for a transaction + */ +typedef struct FFITransaction FFITransaction; + /* Opaque wallet handle */ @@ -668,7 +683,7 @@ typedef struct { } FFIProviderKeyInfo; /* - Transaction output for building + Transaction output for building (legacy structure) */ typedef struct { const char *address; @@ -676,6 +691,7 @@ typedef struct { } FFITxOutput; /* + Transaction context for checking Transaction check result */ typedef struct { @@ -697,6 +713,50 @@ typedef struct { uint32_t affected_accounts_count; } FFITransactionCheckResult; +/* + FFI-compatible transaction input + */ +typedef struct { + /* + Transaction ID (32 bytes) + */ + uint8_t txid[32]; + /* + Output index + */ + uint32_t vout; + /* + Script signature length + */ + uint32_t script_sig_len; + /* + Script signature data pointer + */ + const uint8_t *script_sig; + /* + Sequence number + */ + uint32_t sequence; +} FFITxIn; + +/* + FFI-compatible transaction output + */ +typedef struct { + /* + Amount in duffs + */ + uint64_t amount; + /* + Script pubkey length + */ + uint32_t script_pubkey_len; + /* + Script pubkey data pointer + */ + const uint8_t *script_pubkey; +} FFITxOut; + /* UTXO structure for FFI */ @@ -1803,6 +1863,55 @@ FFIExtendedPrivKey *dip9_derive_identity_key(const uint8_t *seed, FFIError *error) ; +/* + Derive an address from a private key + + # Safety + - `private_key` must be a valid pointer to 32 bytes + - `network` is the network for the address + + # Returns + - Pointer to C string with address (caller must free) + - NULL on error + */ + char *key_wallet_derive_address_from_key(const uint8_t *private_key, FFINetwork network) ; + +/* + Derive an address from a seed at a specific derivation path + + # Safety + - `seed` must be a valid pointer to 64 bytes + - `network` is the network for the address + - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") + + # Returns + - Pointer to C string with address (caller must free) + - NULL on error + */ + +char *key_wallet_derive_address_from_seed(const uint8_t *seed, + FFINetwork network, + const char *path) +; + +/* + Derive a private key from a seed at a specific derivation path + + # Safety + - `seed` must be a valid pointer to 64 bytes + - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") + - `key_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + +int32_t key_wallet_derive_private_key_from_seed(const uint8_t *seed, + const char *path, + uint8_t *key_out) +; + /* Free an error message @@ -3038,6 +3147,159 @@ bool wallet_check_transaction(FFIWallet *wallet, */ void transaction_bytes_free(uint8_t *tx_bytes) ; +/* + Create a new empty transaction + + # Returns + - Pointer to FFITransaction on success + - NULL on error + */ + FFITransaction *transaction_create(void) ; + +/* + Add an input to a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `input` must be a valid pointer to an FFITxIn + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_add_input(FFITransaction *tx, const FFITxIn *input) ; + +/* + Add an output to a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `output` must be a valid pointer to an FFITxOut + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_add_output(FFITransaction *tx, const FFITxOut *output) ; + +/* + Get the transaction ID + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `txid_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_get_txid(const FFITransaction *tx, uint8_t *txid_out) ; + +/* + Serialize a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `out_buf` can be NULL to get size only + - `out_len` must be a valid pointer to store the size + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_serialize(const FFITransaction *tx, uint8_t *out_buf, uint32_t *out_len) ; + +/* + Deserialize a transaction + + # Safety + - `data` must be a valid pointer to serialized transaction data + - `len` must be the correct length of the data + + # Returns + - Pointer to FFITransaction on success + - NULL on error + */ + FFITransaction *transaction_deserialize(const uint8_t *data, uint32_t len) ; + +/* + Destroy a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction created by transaction functions or null + - After calling this function, the pointer becomes invalid + */ + void transaction_destroy(FFITransaction *tx) ; + +/* + Calculate signature hash for an input + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `script_pubkey` must be a valid pointer to the script pubkey + - `hash_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + +int32_t transaction_sighash(const FFITransaction *tx, + uint32_t input_index, + const uint8_t *script_pubkey, + uint32_t script_pubkey_len, + uint32_t sighash_type, + uint8_t *hash_out) +; + +/* + Sign a transaction input + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `private_key` must be a valid pointer to a 32-byte private key + - `script_pubkey` must be a valid pointer to the script pubkey + + # Returns + - 0 on success + - -1 on error + */ + +int32_t transaction_sign_input(FFITransaction *tx, + uint32_t input_index, + const uint8_t *private_key, + const uint8_t *script_pubkey, + uint32_t script_pubkey_len, + uint32_t sighash_type) +; + +/* + Create a P2PKH script pubkey + + # Safety + - `pubkey_hash` must be a valid pointer to a 20-byte public key hash + - `out_buf` can be NULL to get size only + - `out_len` must be a valid pointer to store the size + + # Returns + - 0 on success + - -1 on error + */ + int32_t script_p2pkh(const uint8_t *pubkey_hash, uint8_t *out_buf, uint32_t *out_len) ; + +/* + Extract public key hash from P2PKH address + + # Safety + - `address` must be a valid pointer to a null-terminated C string + - `hash_out` must be a valid pointer to a buffer of at least 20 bytes + + # Returns + - 0 on success + - -1 on error + */ + int32_t address_to_pubkey_hash(const char *address, FFINetwork network, uint8_t *hash_out) ; + /* Create a managed wallet from a regular wallet @@ -3116,6 +3378,8 @@ bool managed_wallet_check_transaction(FFIManagedWallet *managed_wallet, */ char *transaction_classify(const uint8_t *tx_bytes, size_t tx_len, FFIError *error) ; + const char *ffi_network_get_name(FFINetwork network) ; + /* Free a string diff --git a/key-wallet-ffi/src/account_collection.rs b/key-wallet-ffi/src/account_collection.rs index 331bd81a5..f6ebde5dd 100644 --- a/key-wallet-ffi/src/account_collection.rs +++ b/key-wallet-ffi/src/account_collection.rs @@ -113,7 +113,11 @@ pub unsafe extern "C" fn wallet_get_account_collection( FFIError::set_error( error, FFIErrorCode::NotFound, - format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network_rust, + wallet.inner().networks_supported() + ), ); ptr::null_mut() } diff --git a/key-wallet-ffi/src/derivation.rs b/key-wallet-ffi/src/derivation.rs index 3932266b5..184cf758a 100644 --- a/key-wallet-ffi/src/derivation.rs +++ b/key-wallet-ffi/src/derivation.rs @@ -1,8 +1,10 @@ //! BIP32 and DIP9 derivation path functions use crate::error::{FFIError, FFIErrorCode}; -use crate::types::FFINetworks; +use crate::types::{FFINetwork, FFINetworks}; use dash_network::Network; +use key_wallet::{ExtendedPrivKey, ExtendedPubKey}; +use secp256k1::Secp256k1; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_uint}; use std::ptr; @@ -932,6 +934,168 @@ pub unsafe extern "C" fn dip9_derive_identity_key( } } +// MARK: - Simplified Derivation Functions + +/// Derive an address from a private key +/// +/// # Safety +/// - `private_key` must be a valid pointer to 32 bytes +/// - `network` is the network for the address +/// +/// # Returns +/// - Pointer to C string with address (caller must free) +/// - NULL on error +#[no_mangle] +pub unsafe extern "C" fn key_wallet_derive_address_from_key( + private_key: *const u8, + network: FFINetwork, +) -> *mut c_char { + if private_key.is_null() { + return ptr::null_mut(); + } + + let key_slice = slice::from_raw_parts(private_key, 32); + + // Create a secp256k1 private key + let secp = Secp256k1::new(); + let secret_key = match secp256k1::SecretKey::from_slice(key_slice) { + Ok(sk) => sk, + Err(_) => return ptr::null_mut(), + }; + + // Get public key + let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key); + + // Convert to dashcore PublicKey + let dash_pubkey = dashcore::PublicKey::new(public_key); + + // Convert to Dash address + let dash_network: key_wallet::Network = network.into(); + let address = key_wallet::Address::p2pkh(&dash_pubkey, dash_network); + + match CString::new(address.to_string()) { + Ok(c_str) => c_str.into_raw(), + Err(_) => ptr::null_mut(), + } +} + +/// Derive an address from a seed at a specific derivation path +/// +/// # Safety +/// - `seed` must be a valid pointer to 64 bytes +/// - `network` is the network for the address +/// - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") +/// +/// # Returns +/// - Pointer to C string with address (caller must free) +/// - NULL on error +#[no_mangle] +pub unsafe extern "C" fn key_wallet_derive_address_from_seed( + seed: *const u8, + network: FFINetwork, + path: *const c_char, +) -> *mut c_char { + if seed.is_null() || path.is_null() { + return ptr::null_mut(); + } + + let seed_slice = slice::from_raw_parts(seed, 64); + let dash_network: key_wallet::Network = network.into(); + + // Parse derivation path + let path_str = match CStr::from_ptr(path).to_str() { + Ok(s) => s, + Err(_) => return ptr::null_mut(), + }; + + use std::str::FromStr; + let derivation_path = match key_wallet::DerivationPath::from_str(path_str) { + Ok(dp) => dp, + Err(_) => return ptr::null_mut(), + }; + + // Create master key from seed + let master_key = match ExtendedPrivKey::new_master(dash_network, seed_slice) { + Ok(xprv) => xprv, + Err(_) => return ptr::null_mut(), + }; + + // Derive at path + let secp = Secp256k1::new(); + let derived_key = match master_key.derive_priv(&secp, &derivation_path) { + Ok(xprv) => xprv, + Err(_) => return ptr::null_mut(), + }; + + // Get public key + let extended_pubkey = ExtendedPubKey::from_priv(&secp, &derived_key); + + // Convert secp256k1::PublicKey to dashcore::PublicKey + let dash_pubkey = dashcore::PublicKey::new(extended_pubkey.public_key); + + // Convert to address + let address = key_wallet::Address::p2pkh(&dash_pubkey, dash_network); + + match CString::new(address.to_string()) { + Ok(c_str) => c_str.into_raw(), + Err(_) => ptr::null_mut(), + } +} + +/// Derive a private key from a seed at a specific derivation path +/// +/// # Safety +/// - `seed` must be a valid pointer to 64 bytes +/// - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") +/// - `key_out` must be a valid pointer to a buffer of at least 32 bytes +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn key_wallet_derive_private_key_from_seed( + seed: *const u8, + path: *const c_char, + key_out: *mut u8, +) -> i32 { + if seed.is_null() || path.is_null() || key_out.is_null() { + return -1; + } + + let seed_slice = slice::from_raw_parts(seed, 64); + + // Parse derivation path + let path_str = match CStr::from_ptr(path).to_str() { + Ok(s) => s, + Err(_) => return -1, + }; + + use std::str::FromStr; + let derivation_path = match key_wallet::DerivationPath::from_str(path_str) { + Ok(dp) => dp, + Err(_) => return -1, + }; + + // Create master key from seed (use testnet as default, doesn't affect key derivation) + let master_key = match ExtendedPrivKey::new_master(key_wallet::Network::Testnet, seed_slice) { + Ok(xprv) => xprv, + Err(_) => return -1, + }; + + // Derive at path + let secp = Secp256k1::new(); + let derived_key = match master_key.derive_priv(&secp, &derivation_path) { + Ok(xprv) => xprv, + Err(_) => return -1, + }; + + // Copy private key bytes + let key_bytes = derived_key.private_key.secret_bytes(); + ptr::copy_nonoverlapping(key_bytes.as_ptr(), key_out, 32); + + 0 +} + #[cfg(test)] #[path = "derivation_tests.rs"] mod tests; diff --git a/key-wallet-ffi/src/lib.rs b/key-wallet-ffi/src/lib.rs index 510090a98..f6accf7e5 100644 --- a/key-wallet-ffi/src/lib.rs +++ b/key-wallet-ffi/src/lib.rs @@ -31,17 +31,12 @@ pub mod bip38; // Re-export main types for convenience pub use error::{FFIError, FFIErrorCode}; -pub use types::{FFIBalance, FFINetworks, FFIWallet}; +pub use types::{FFIBalance, FFINetwork, FFINetworks, FFIWallet}; pub use utxo::FFIUTXO; pub use wallet_manager::{ - FFIWalletManager, - wallet_manager_create, - wallet_manager_free, - wallet_manager_wallet_count, - wallet_manager_get_wallet, - wallet_manager_get_wallet_balance, - wallet_manager_get_wallet_ids, - wallet_manager_free_wallet_ids, + wallet_manager_create, wallet_manager_free, wallet_manager_free_wallet_ids, + wallet_manager_get_wallet, wallet_manager_get_wallet_balance, wallet_manager_get_wallet_ids, + wallet_manager_wallet_count, FFIWalletManager, }; // ============================================================================ diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index 569a0f701..a7981bdbe 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -192,7 +192,11 @@ pub unsafe extern "C" fn managed_wallet_get_account( } None => FFIManagedAccountResult::error( FFIErrorCode::NotFound, - format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network_rust, + managed_wallet.inner().networks_supported() + ), ), }; @@ -288,7 +292,11 @@ pub unsafe extern "C" fn managed_wallet_get_top_up_account_with_registration_ind } None => FFIManagedAccountResult::error( FFIErrorCode::NotFound, - format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network_rust, + managed_wallet.inner().networks_supported() + ), ), }; diff --git a/key-wallet-ffi/src/managed_account_collection.rs b/key-wallet-ffi/src/managed_account_collection.rs index 5b9f9f8c7..c39dc0281 100644 --- a/key-wallet-ffi/src/managed_account_collection.rs +++ b/key-wallet-ffi/src/managed_account_collection.rs @@ -137,7 +137,11 @@ pub unsafe extern "C" fn managed_wallet_get_account_collection( FFIError::set_error( error, FFIErrorCode::NotFound, - format!("No accounts found for network {:?}, wallet has networks {:?}", network_rust, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network_rust, + managed_wallet.inner().networks_supported() + ), ); ptr::null_mut() } diff --git a/key-wallet-ffi/src/managed_wallet.rs b/key-wallet-ffi/src/managed_wallet.rs index df4ceed96..bef4c9105 100644 --- a/key-wallet-ffi/src/managed_wallet.rs +++ b/key-wallet-ffi/src/managed_wallet.rs @@ -89,7 +89,11 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_receive_address( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network, + managed_wallet.inner().networks_supported() + ), ); return ptr::null_mut(); } @@ -219,7 +223,11 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network, + managed_wallet.inner().networks_supported() + ), ); return ptr::null_mut(); } @@ -370,7 +378,11 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network, + managed_wallet.inner().networks_supported() + ), ); *count_out = 0; *addresses_out = ptr::null_mut(); @@ -564,7 +576,11 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range( FFIError::set_error( error, FFIErrorCode::WalletError, - format!("No accounts found for network {:?}, wallet has networks {:?}", network, managed_wallet.inner().networks_supported()), + format!( + "No accounts found for network {:?}, wallet has networks {:?}", + network, + managed_wallet.inner().networks_supported() + ), ); *count_out = 0; *addresses_out = ptr::null_mut(); diff --git a/key-wallet-ffi/src/transaction.rs b/key-wallet-ffi/src/transaction.rs index 1a5d29823..c0a7aef4a 100644 --- a/key-wallet-ffi/src/transaction.rs +++ b/key-wallet-ffi/src/transaction.rs @@ -1,12 +1,53 @@ //! Transaction building and management +use std::ffi::CStr; use std::os::raw::{c_char, c_uint}; +use std::ptr; use std::slice; +use dashcore::{ + consensus, hashes::Hash, sighash::SighashCache, EcdsaSighashType, Network, OutPoint, Script, + ScriptBuf, Transaction, TxIn, TxOut, Txid, +}; +use secp256k1::{Message, Secp256k1, SecretKey}; + use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; +use crate::types::{FFINetwork, FFINetworks, FFITransactionContext, FFIWallet}; + +// MARK: - Transaction Types + +/// Opaque handle for a transaction +pub struct FFITransaction { + inner: Transaction, +} + +/// FFI-compatible transaction input +#[repr(C)] +pub struct FFITxIn { + /// Transaction ID (32 bytes) + pub txid: [u8; 32], + /// Output index + pub vout: u32, + /// Script signature length + pub script_sig_len: u32, + /// Script signature data pointer + pub script_sig: *const u8, + /// Sequence number + pub sequence: u32, +} -/// Transaction output for building +/// FFI-compatible transaction output +#[repr(C)] +pub struct FFITxOut { + /// Amount in duffs + pub amount: u64, + /// Script pubkey length + pub script_pubkey_len: u32, + /// Script pubkey data pointer + pub script_pubkey: *const u8, +} + +/// Transaction output for building (legacy structure) #[repr(C)] pub struct FFITxOutput { pub address: *const c_char, @@ -98,15 +139,7 @@ pub unsafe extern "C" fn wallet_sign_transaction( } /// Transaction context for checking -#[repr(C)] -pub enum FFITransactionContext { - /// Transaction is in mempool (unconfirmed) - Mempool = 0, - /// Transaction is in a block - InBlock = 1, - /// Transaction is in a chain-locked block - InChainLockedBlock = 2, -} +// FFITransactionContext is imported from types module at the top /// Transaction check result #[repr(C)] @@ -273,6 +306,430 @@ pub unsafe extern "C" fn transaction_bytes_free(tx_bytes: *mut u8) { } } +// MARK: - Transaction Creation + +/// Create a new empty transaction +/// +/// # Returns +/// - Pointer to FFITransaction on success +/// - NULL on error +#[no_mangle] +pub extern "C" fn transaction_create() -> *mut FFITransaction { + let tx = Transaction { + version: 2, + lock_time: 0, + input: vec![], + output: vec![], + special_transaction_payload: None, + }; + + Box::into_raw(Box::new(FFITransaction { + inner: tx, + })) +} + +/// Add an input to a transaction +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `input` must be a valid pointer to an FFITxIn +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_add_input( + tx: *mut FFITransaction, + input: *const FFITxIn, +) -> i32 { + if tx.is_null() || input.is_null() { + return -1; + } + + let tx = &mut *tx; + let input = &*input; + + // Convert txid + let txid = match Txid::from_slice(&input.txid) { + Ok(txid) => txid, + Err(_) => { + return -1; + } + }; + + // Convert script + let script_sig = if input.script_sig.is_null() || input.script_sig_len == 0 { + ScriptBuf::new() + } else { + let script_slice = slice::from_raw_parts(input.script_sig, input.script_sig_len as usize); + ScriptBuf::from(script_slice.to_vec()) + }; + + let tx_in = TxIn { + previous_output: OutPoint { + txid, + vout: input.vout, + }, + script_sig, + sequence: input.sequence, + witness: Default::default(), + }; + + tx.inner.input.push(tx_in); + 0 +} + +/// Add an output to a transaction +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `output` must be a valid pointer to an FFITxOut +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_add_output( + tx: *mut FFITransaction, + output: *const FFITxOut, +) -> i32 { + if tx.is_null() || output.is_null() { + return -1; + } + + let tx = &mut *tx; + let output = &*output; + + // Convert script + let script_pubkey = if output.script_pubkey.is_null() || output.script_pubkey_len == 0 { + return -1; + } else { + let script_slice = + slice::from_raw_parts(output.script_pubkey, output.script_pubkey_len as usize); + ScriptBuf::from(script_slice.to_vec()) + }; + + let tx_out = TxOut { + value: output.amount, + script_pubkey, + }; + + tx.inner.output.push(tx_out); + 0 +} + +/// Get the transaction ID +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `txid_out` must be a valid pointer to a buffer of at least 32 bytes +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_get_txid(tx: *const FFITransaction, txid_out: *mut u8) -> i32 { + if tx.is_null() || txid_out.is_null() { + return -1; + } + + let tx = &*tx; + let txid = tx.inner.txid(); + + let txid_bytes = txid.as_byte_array(); + ptr::copy_nonoverlapping(txid_bytes.as_ptr(), txid_out, 32); + 0 +} + +/// Serialize a transaction +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `out_buf` can be NULL to get size only +/// - `out_len` must be a valid pointer to store the size +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_serialize( + tx: *const FFITransaction, + out_buf: *mut u8, + out_len: *mut u32, +) -> i32 { + if tx.is_null() || out_len.is_null() { + return -1; + } + + let tx = &*tx; + let serialized = consensus::serialize(&tx.inner); + let size = serialized.len() as u32; + + if out_buf.is_null() { + // Just return size + *out_len = size; + return 0; + } + + let provided_size = *out_len; + if provided_size < size { + *out_len = size; + return -1; + } + + ptr::copy_nonoverlapping(serialized.as_ptr(), out_buf, serialized.len()); + *out_len = size; + 0 +} + +/// Deserialize a transaction +/// +/// # Safety +/// - `data` must be a valid pointer to serialized transaction data +/// - `len` must be the correct length of the data +/// +/// # Returns +/// - Pointer to FFITransaction on success +/// - NULL on error +#[no_mangle] +pub unsafe extern "C" fn transaction_deserialize(data: *const u8, len: u32) -> *mut FFITransaction { + if data.is_null() { + return ptr::null_mut(); + } + + let slice = slice::from_raw_parts(data, len as usize); + + match consensus::deserialize::(slice) { + Ok(tx) => Box::into_raw(Box::new(FFITransaction { + inner: tx, + })), + Err(_) => ptr::null_mut(), + } +} + +/// Destroy a transaction +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction created by transaction functions or null +/// - After calling this function, the pointer becomes invalid +#[no_mangle] +pub unsafe extern "C" fn transaction_destroy(tx: *mut FFITransaction) { + if !tx.is_null() { + let _ = Box::from_raw(tx); + } +} + +// MARK: - Transaction Signing + +/// Calculate signature hash for an input +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `script_pubkey` must be a valid pointer to the script pubkey +/// - `hash_out` must be a valid pointer to a buffer of at least 32 bytes +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_sighash( + tx: *const FFITransaction, + input_index: u32, + script_pubkey: *const u8, + script_pubkey_len: u32, + sighash_type: u32, + hash_out: *mut u8, +) -> i32 { + if tx.is_null() || script_pubkey.is_null() || hash_out.is_null() { + return -1; + } + + let tx = &*tx; + let script_slice = slice::from_raw_parts(script_pubkey, script_pubkey_len as usize); + let script = Script::from_bytes(script_slice); + + let sighash_type = EcdsaSighashType::from_consensus(sighash_type); + let cache = SighashCache::new(&tx.inner); + + match cache.legacy_signature_hash(input_index as usize, script, sighash_type.to_u32()) { + Ok(hash) => { + let hash_bytes: &[u8] = hash.as_ref(); + ptr::copy_nonoverlapping(hash_bytes.as_ptr(), hash_out, 32); + 0 + } + Err(_) => -1, + } +} + +/// Sign a transaction input +/// +/// # Safety +/// - `tx` must be a valid pointer to an FFITransaction +/// - `private_key` must be a valid pointer to a 32-byte private key +/// - `script_pubkey` must be a valid pointer to the script pubkey +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn transaction_sign_input( + tx: *mut FFITransaction, + input_index: u32, + private_key: *const u8, + script_pubkey: *const u8, + script_pubkey_len: u32, + sighash_type: u32, +) -> i32 { + if tx.is_null() || private_key.is_null() || script_pubkey.is_null() { + return -1; + } + + let tx = &mut *tx; + let input_index = input_index as usize; + + if input_index >= tx.inner.input.len() { + return -1; + } + + // Calculate sighash + let mut sighash = [0u8; 32]; + if transaction_sighash( + tx as *const FFITransaction, + input_index as u32, + script_pubkey, + script_pubkey_len, + sighash_type, + sighash.as_mut_ptr(), + ) != 0 + { + return -1; + } + + // Parse private key + let privkey_slice = slice::from_raw_parts(private_key, 32); + let privkey = match SecretKey::from_slice(privkey_slice) { + Ok(k) => k, + Err(_) => { + return -1; + } + }; + + // Sign + let secp = Secp256k1::new(); + let message = Message::from_digest(sighash); + let sig = secp.sign_ecdsa(&message, &privkey); + + // Build signature script (simplified P2PKH) + let mut sig_bytes = sig.serialize_der().to_vec(); + sig_bytes.push(sighash_type as u8); + + let pubkey = secp256k1::PublicKey::from_secret_key(&secp, &privkey); + let pubkey_bytes = pubkey.serialize(); + + let mut script_sig = vec![]; + script_sig.push(sig_bytes.len() as u8); + script_sig.extend_from_slice(&sig_bytes); + script_sig.push(pubkey_bytes.len() as u8); + script_sig.extend_from_slice(&pubkey_bytes); + + tx.inner.input[input_index].script_sig = ScriptBuf::from(script_sig); + 0 +} + +// MARK: - Script Utilities + +/// Create a P2PKH script pubkey +/// +/// # Safety +/// - `pubkey_hash` must be a valid pointer to a 20-byte public key hash +/// - `out_buf` can be NULL to get size only +/// - `out_len` must be a valid pointer to store the size +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn script_p2pkh( + pubkey_hash: *const u8, + out_buf: *mut u8, + out_len: *mut u32, +) -> i32 { + if pubkey_hash.is_null() || out_len.is_null() { + return -1; + } + + let hash_slice = slice::from_raw_parts(pubkey_hash, 20); + + // Build P2PKH script: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + let mut script = vec![0x76, 0xa9, 0x14]; // OP_DUP OP_HASH160 PUSH(20) + script.extend_from_slice(hash_slice); + script.extend_from_slice(&[0x88, 0xac]); // OP_EQUALVERIFY OP_CHECKSIG + + let size = script.len() as u32; + + if out_buf.is_null() { + *out_len = size; + return 0; + } + + let provided_size = *out_len; + if provided_size < size { + *out_len = size; + return -1; + } + + ptr::copy_nonoverlapping(script.as_ptr(), out_buf, script.len()); + *out_len = size; + 0 +} + +/// Extract public key hash from P2PKH address +/// +/// # Safety +/// - `address` must be a valid pointer to a null-terminated C string +/// - `hash_out` must be a valid pointer to a buffer of at least 20 bytes +/// +/// # Returns +/// - 0 on success +/// - -1 on error +#[no_mangle] +pub unsafe extern "C" fn address_to_pubkey_hash( + address: *const c_char, + network: FFINetwork, + hash_out: *mut u8, +) -> i32 { + if address.is_null() || hash_out.is_null() { + return -1; + } + + let address_str = match CStr::from_ptr(address).to_str() { + Ok(s) => s, + Err(_) => { + return -1; + } + }; + + let expected_network: Network = network.into(); + + match address_str.parse::>() { + Ok(addr) => { + if *addr.network() != expected_network { + return -1; + } + + match addr.payload() { + dashcore::address::Payload::PubkeyHash(hash) => { + let hash_bytes = hash.as_byte_array(); + ptr::copy_nonoverlapping(hash_bytes.as_ptr(), hash_out, 20); + 0 + } + _ => -1, + } + } + Err(_) => -1, + } +} + #[cfg(test)] #[path = "transaction_tests.rs"] mod transaction_tests; diff --git a/key-wallet-ffi/src/transaction_checking.rs b/key-wallet-ffi/src/transaction_checking.rs index 840eb1f0e..91f8d4ccd 100644 --- a/key-wallet-ffi/src/transaction_checking.rs +++ b/key-wallet-ffi/src/transaction_checking.rs @@ -9,7 +9,7 @@ use std::os::raw::{c_char, c_uint}; use std::slice; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; +use crate::types::{FFINetworks, FFITransactionContext, FFIWallet}; use dashcore::consensus::Decodable; use dashcore::Transaction; use key_wallet::transaction_checking::{ @@ -24,15 +24,7 @@ pub struct FFIManagedWallet { } /// Transaction context for checking -#[repr(C)] -pub enum FFITransactionContext { - /// Transaction is in mempool (unconfirmed) - Mempool = 0, - /// Transaction is in a block - InBlock = 1, - /// Transaction is in a chain-locked block - InChainLockedBlock = 2, -} +// FFITransactionContext is imported from types module at the top /// Account type match result #[repr(C)] diff --git a/key-wallet-ffi/src/types.rs b/key-wallet-ffi/src/types.rs index 5c6d2c407..8c7a96dad 100644 --- a/key-wallet-ffi/src/types.rs +++ b/key-wallet-ffi/src/types.rs @@ -1,7 +1,7 @@ //! Common types for FFI interface use key_wallet::{Network, Wallet}; -use std::os::raw::c_uint; +use std::os::raw::{c_char, c_uint}; use std::sync::Arc; /// FFI Network type (bit flags for multiple networks) @@ -62,6 +62,49 @@ impl FFINetworks { } } +/// FFI Network type (single network) +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FFINetwork { + Dash = 0, + Testnet = 1, + Regtest = 2, + Devnet = 3, +} + +#[no_mangle] +pub extern "C" fn ffi_network_get_name(network: FFINetwork) -> *const c_char { + match network { + FFINetwork::Dash => c"dash".as_ptr() as *const c_char, + FFINetwork::Testnet => c"testnet".as_ptr() as *const c_char, + FFINetwork::Regtest => c"regtest".as_ptr() as *const c_char, + FFINetwork::Devnet => c"devnet".as_ptr() as *const c_char, + } +} + +impl From for Network { + fn from(net: FFINetwork) -> Self { + match net { + FFINetwork::Dash => Network::Dash, + FFINetwork::Testnet => Network::Testnet, + FFINetwork::Regtest => Network::Regtest, + FFINetwork::Devnet => Network::Devnet, + } + } +} + +impl From for FFINetwork { + fn from(net: Network) -> Self { + match net { + Network::Dash => FFINetwork::Dash, + Network::Testnet => FFINetwork::Testnet, + Network::Regtest => FFINetwork::Regtest, + Network::Devnet => FFINetwork::Devnet, + _ => FFINetwork::Dash, + } + } +} + use std::convert::TryFrom; impl TryFrom for Network { @@ -144,7 +187,7 @@ pub struct FFIAccountResult { /// Error code (0 = success) pub error_code: i32, /// Error message (NULL if success, must be freed by caller if not NULL) - pub error_message: *mut std::os::raw::c_char, + pub error_message: *mut c_char, } impl FFIAccountResult { diff --git a/key-wallet-ffi/src/wallet_manager.rs b/key-wallet-ffi/src/wallet_manager.rs index 8319dd384..7b86c3b32 100644 --- a/key-wallet-ffi/src/wallet_manager.rs +++ b/key-wallet-ffi/src/wallet_manager.rs @@ -21,7 +21,7 @@ use key_wallet::Network; use key_wallet_manager::wallet_manager::WalletManager; /// FFI wrapper for WalletManager -/// +/// /// This struct holds a cloned Arc reference to the WalletManager, /// allowing FFI code to interact with it directly without going through /// the SPV client. @@ -32,7 +32,10 @@ pub struct FFIWalletManager { impl FFIWalletManager { /// Create a new FFIWalletManager from an Arc> - pub fn from_arc(manager: Arc>>, runtime: Arc) -> Self { + pub fn from_arc( + manager: Arc>>, + runtime: Arc, + ) -> Self { FFIWalletManager { manager, runtime, @@ -47,7 +50,11 @@ pub extern "C" fn wallet_manager_create(error: *mut FFIError) -> *mut FFIWalletM let runtime = match tokio::runtime::Runtime::new() { Ok(rt) => Arc::new(rt), Err(e) => { - FFIError::set_error(error, FFIErrorCode::AllocationFailed, format!("Failed to create runtime: {}", e)); + FFIError::set_error( + error, + FFIErrorCode::AllocationFailed, + format!("Failed to create runtime: {}", e), + ); return ptr::null_mut(); } }; @@ -118,7 +125,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_with_options( unsafe { let manager_ref = &*manager; - + // Convert account creation options let creation_options = if account_options.is_null() { key_wallet::wallet::initialization::WalletAccountCreationOptions::Default @@ -129,7 +136,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_with_options( // Use the runtime to execute async code let result = manager_ref.runtime.block_on(async { let mut manager_guard = manager_ref.manager.write().await; - + // Use the WalletManager's public method to create the wallet manager_guard.create_wallet_from_mnemonic( mnemonic_str, @@ -277,7 +284,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ // Get the manager and call the proper method let manager_ref = unsafe { &*manager }; - + // Convert birth_height: 0 means None, any other value means Some(value) let birth_height = if birth_height == 0 { None @@ -287,7 +294,7 @@ pub unsafe extern "C" fn wallet_manager_add_wallet_from_mnemonic_return_serializ let result = manager_ref.runtime.block_on(async { let mut manager_guard = manager_ref.manager.write().await; - + manager_guard.create_wallet_from_mnemonic_return_serialized_bytes( mnemonic_str, passphrase_str, @@ -458,7 +465,7 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_ids( } let manager_ref = &*manager; - + // Get wallet IDs from the manager let wallet_ids = manager_ref.runtime.block_on(async { let manager_guard = manager_ref.manager.read().await; @@ -621,7 +628,7 @@ pub unsafe extern "C" fn wallet_manager_get_receive_address( wallet_id_array.copy_from_slice(wallet_id_slice); let manager_ref = &*manager; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -708,7 +715,7 @@ pub unsafe extern "C" fn wallet_manager_get_change_address( wallet_id_array.copy_from_slice(wallet_id_slice); let manager_ref = &*manager; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -908,12 +915,7 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( // Process the transaction using async runtime let relevant_wallets = manager_ref.runtime.block_on(async { let mut manager_guard = manager_ref.manager.write().await; - manager_guard.check_transaction_in_all_wallets( - &tx, - network, - context, - update_state_if_found, - ) + manager_guard.check_transaction_in_all_wallets(&tx, network, context, update_state_if_found) }); FFIError::set_success(error); @@ -943,7 +945,7 @@ pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( } let manager_ref = &*manager; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -955,12 +957,12 @@ pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( return false; } }; - + // Collect addresses from all wallets for this network let all_addresses = manager_ref.runtime.block_on(async { let manager_guard = manager_ref.manager.read().await; let mut addresses: Vec<*mut c_char> = Vec::new(); - + for wallet in manager_guard.get_all_wallets().values() { if let Some(account) = wallet.get_bip44_account(network_rust, 0) { // Generate a few addresses from each wallet (simplified) @@ -1033,7 +1035,7 @@ pub unsafe extern "C" fn wallet_manager_update_height( } let manager_ref = &*manager; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -1045,7 +1047,7 @@ pub unsafe extern "C" fn wallet_manager_update_height( return false; } }; - + manager_ref.runtime.block_on(async { let mut manager_guard = manager_ref.manager.write().await; manager_guard.update_height(network_rust, height); @@ -1074,7 +1076,7 @@ pub unsafe extern "C" fn wallet_manager_current_height( } let manager_ref = &*manager; - + let network_rust: Network = match network.try_into() { Ok(n) => n, Err(_) => { @@ -1090,10 +1092,7 @@ pub unsafe extern "C" fn wallet_manager_current_height( // Get current height from network state if it exists let height = manager_ref.runtime.block_on(async { let manager_guard = manager_ref.manager.read().await; - manager_guard - .get_network_state(network_rust) - .map(|state| state.current_height) - .unwrap_or(0) + manager_guard.get_network_state(network_rust).map(|state| state.current_height).unwrap_or(0) }); FFIError::set_success(error); @@ -1119,7 +1118,7 @@ pub unsafe extern "C" fn wallet_manager_wallet_count( unsafe { let manager_ref = &*manager; - + let count = manager_ref.runtime.block_on(async { let manager_guard = manager_ref.manager.read().await; manager_guard.wallet_count() diff --git a/key-wallet-manager/src/wallet_manager/process_block.rs b/key-wallet-manager/src/wallet_manager/process_block.rs index 4e8c48908..8fa82dd1b 100644 --- a/key-wallet-manager/src/wallet_manager/process_block.rs +++ b/key-wallet-manager/src/wallet_manager/process_block.rs @@ -1,15 +1,20 @@ +use crate::wallet_interface::WalletInterface; use crate::{Network, WalletManager}; +use async_trait::async_trait; use dashcore::bip158::BlockFilter; use dashcore::prelude::CoreBlockHeight; use dashcore::{Block, BlockHash, Transaction, Txid}; use key_wallet::transaction_checking::TransactionContext; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; -use crate::wallet_interface::WalletInterface; -use async_trait::async_trait; #[async_trait] impl WalletInterface for WalletManager { - async fn process_block(&mut self, block: &Block, height: CoreBlockHeight, network: Network) -> Vec { + async fn process_block( + &mut self, + block: &Block, + height: CoreBlockHeight, + network: Network, + ) -> Vec { let mut relevant_txids = Vec::new(); let block_hash = Some(block.block_hash()); let timestamp = block.header.time; diff --git a/key-wallet-manager/tests/spv_integration_tests.rs b/key-wallet-manager/tests/spv_integration_tests.rs index 98b59acf6..c7a830bd9 100644 --- a/key-wallet-manager/tests/spv_integration_tests.rs +++ b/key-wallet-manager/tests/spv_integration_tests.rs @@ -56,10 +56,10 @@ fn create_test_block(height: u32, transactions: Vec) -> Block { fn create_mock_filter(block: &Block) -> BlockFilter { let mut content = Vec::new(); let mut writer = BlockFilterWriter::new(&mut content, block); - + // Add output scripts from the block writer.add_output_scripts(); - + // Finish writing and construct the filter writer.finish().expect("Failed to finish filter"); BlockFilter::new(&content) @@ -83,15 +83,17 @@ async fn test_filter_checking() { let block_hash = block.block_hash(); // Check the filter - let should_download = manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; + let should_download = + manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; // The filter matching depends on whether the wallet has any addresses // being watched. Since we just created an empty wallet, it may or may not match. // We'll just check that the method doesn't panic let _ = should_download; - + // Test filter caching - calling again should use cached result - let should_download_cached = manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; + let should_download_cached = + manager.check_compact_filter(&filter, &block_hash, Network::Testnet).await; assert_eq!(should_download, should_download_cached, "Cached result should match original"); } @@ -129,10 +131,10 @@ async fn test_filter_caching() { // Create multiple blocks with different hashes let block1 = create_test_block(100, vec![create_test_transaction(1000)]); let block2 = create_test_block(101, vec![create_test_transaction(2000)]); - + let filter1 = create_mock_filter(&block1); let filter2 = create_mock_filter(&block2); - + let hash1 = block1.block_hash(); let hash2 = block2.block_hash(); @@ -147,4 +149,4 @@ async fn test_filter_caching() { // Cached results should match originals assert_eq!(result1, cached1, "Cached result for block1 should match"); assert_eq!(result2, cached2, "Cached result for block2 should match"); -} \ No newline at end of file +} diff --git a/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h b/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h index decd0efb9..b3270a16d 100644 --- a/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h +++ b/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h @@ -9,13 +9,6 @@ typedef enum FFIMempoolStrategy { Selective = 2, } FFIMempoolStrategy; -typedef enum FFINetwork { - Dash = 0, - Testnet = 1, - Regtest = 2, - Devnet = 3, -} FFINetwork; - typedef enum FFISyncStage { Connecting = 0, QueryingHeight = 1, @@ -85,25 +78,6 @@ typedef struct FFISpvStats { uint64_t uptime; } FFISpvStats; -/** - * 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 void (*BlockCallback)(uint32_t height, const uint8_t (*hash)[32], void *user_data); typedef void (*TransactionCallback)(const uint8_t (*txid)[32], @@ -155,14 +129,6 @@ typedef struct FFIEventCallbacks { void *user_data; } FFIEventCallbacks; -typedef struct FFITransaction { - struct FFIString txid; - int32_t version; - uint32_t locktime; - uint32_t size; - uint32_t weight; -} FFITransaction; - /** * Handle for Core SDK that can be passed to Platform SDK */ @@ -178,6 +144,25 @@ typedef struct FFIResult { const char *error_message; } FFIResult; +/** + * 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; + /** * FFI-safe representation of an unconfirmed transaction * @@ -314,14 +299,6 @@ struct FFISpvStats *dash_spv_ffi_client_get_stats(struct FFIDashSpvClient *clien bool dash_spv_ffi_client_is_filter_sync_available(struct FFIDashSpvClient *client); -FFIBalance *dash_spv_ffi_client_get_address_balance(struct FFIDashSpvClient *client, - const char *address); - -struct FFIArray *dash_spv_ffi_client_get_utxos(struct FFIDashSpvClient *client); - -struct FFIArray *dash_spv_ffi_client_get_utxos_for_address(struct FFIDashSpvClient *client, - const char *address); - int32_t dash_spv_ffi_client_set_event_callbacks(struct FFIDashSpvClient *client, struct FFIEventCallbacks callbacks); @@ -331,103 +308,238 @@ void dash_spv_ffi_sync_progress_destroy(struct FFISyncProgress *progress); void dash_spv_ffi_spv_stats_destroy(struct FFISpvStats *stats); -int32_t dash_spv_ffi_client_watch_address(struct FFIDashSpvClient *client, const char *address); - -int32_t dash_spv_ffi_client_unwatch_address(struct FFIDashSpvClient *client, const char *address); - -int32_t dash_spv_ffi_client_watch_script(struct FFIDashSpvClient *client, const char *script_hex); - -int32_t dash_spv_ffi_client_unwatch_script(struct FFIDashSpvClient *client, const char *script_hex); - -struct FFIArray *dash_spv_ffi_client_get_address_history(struct FFIDashSpvClient *client, - const char *address); - -struct FFITransaction *dash_spv_ffi_client_get_transaction(struct FFIDashSpvClient *client, - const char *txid); - -int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, - const char *tx_hex); - -struct FFIArray *dash_spv_ffi_client_get_watched_addresses(struct FFIDashSpvClient *client); - -struct FFIArray *dash_spv_ffi_client_get_watched_scripts(struct FFIDashSpvClient *client); - -FFIBalance *dash_spv_ffi_client_get_total_balance(struct FFIDashSpvClient *client); - int32_t dash_spv_ffi_client_rescan_blockchain(struct FFIDashSpvClient *client, uint32_t _from_height); -int32_t dash_spv_ffi_client_get_transaction_confirmations(struct FFIDashSpvClient *client, - const char *txid); - -int32_t dash_spv_ffi_client_is_transaction_confirmed(struct FFIDashSpvClient *client, - const char *txid); - -void dash_spv_ffi_transaction_destroy(struct FFITransaction *tx); - -struct FFIArray *dash_spv_ffi_client_get_address_utxos(struct FFIDashSpvClient *client, - const char *address); - int32_t dash_spv_ffi_client_enable_mempool_tracking(struct FFIDashSpvClient *client, enum FFIMempoolStrategy strategy); -FFIBalance *dash_spv_ffi_client_get_balance_with_mempool(struct FFIDashSpvClient *client); - -int32_t dash_spv_ffi_client_get_mempool_transaction_count(struct FFIDashSpvClient *client); - int32_t dash_spv_ffi_client_record_send(struct FFIDashSpvClient *client, const char *txid); -FFIBalance *dash_spv_ffi_client_get_mempool_balance(struct FFIDashSpvClient *client, - const char *address); +/** + * Get the wallet manager from the SPV client + * + * Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. + * This allows direct interaction with the wallet manager without going through the client. + * + * # Safety + * + * The caller must ensure that: + * - The client pointer is valid + * - The returned pointer is freed using wallet_manager_free() + * + * # Returns + * + * An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. + * Swift should treat this as an OpaquePointer. + */ +void *dash_spv_ffi_client_get_wallet_manager(struct FFIDashSpvClient *client); -FFIClientConfig *dash_spv_ffi_config_new(enum FFINetwork network); +FFIClientConfig *dash_spv_ffi_config_new(FFINetwork network); FFIClientConfig *dash_spv_ffi_config_mainnet(void); FFIClientConfig *dash_spv_ffi_config_testnet(void); -int32_t dash_spv_ffi_config_set_data_dir(FFIClientConfig *config, const char *path); +/** + * Sets the data directory for storing blockchain data + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `path` must be a valid null-terminated C string + * - The caller must ensure the config pointer remains valid for the duration of this call + */ +int32_t dash_spv_ffi_config_set_data_dir(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(FFIClientConfig *config, enum FFIValidationMode mode); -int32_t dash_spv_ffi_config_set_max_peers(FFIClientConfig *config, uint32_t max_peers); +/** + * 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(FFIClientConfig *config, + uint32_t max_peers); -int32_t dash_spv_ffi_config_add_peer(FFIClientConfig *config, const char *addr); +/** + * Adds a peer address to the configuration + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `addr` must be a valid null-terminated C string containing a socket address (e.g., "192.168.1.1:9999") + * - The caller must ensure both pointers remain valid for the duration of this call + */ +int32_t dash_spv_ffi_config_add_peer(FFIClientConfig *config, + const char *addr); -int32_t dash_spv_ffi_config_set_user_agent(FFIClientConfig *config, const char *user_agent); +/** + * Sets the user agent string (currently not supported) + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - `user_agent` must be a valid null-terminated C string + * - The caller must ensure both pointers remain valid for the duration of this call + */ +int32_t dash_spv_ffi_config_set_user_agent(FFIClientConfig *config, + const char *user_agent); -int32_t dash_spv_ffi_config_set_relay_transactions(FFIClientConfig *config, bool _relay); +/** + * 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(FFIClientConfig *config, + bool _relay); -int32_t dash_spv_ffi_config_set_filter_load(FFIClientConfig *config, bool load_filters); +/** + * 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(FFIClientConfig *config, + bool load_filters); -enum FFINetwork dash_spv_ffi_config_get_network(const FFIClientConfig *config); +/** + * Gets the network type from the configuration + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig or null + * - If null, returns FFINetwork::Dash as default + */ +FFINetwork dash_spv_ffi_config_get_network(const 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_string_free + */ struct FFIString dash_spv_ffi_config_get_data_dir(const FFIClientConfig *config); +/** + * Destroys an FFIClientConfig and frees its memory + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet, or null + * - After calling this function, the config pointer becomes invalid and must not be used + * - This function should only be called once per config instance + */ void dash_spv_ffi_config_destroy(FFIClientConfig *config); -int32_t dash_spv_ffi_config_set_mempool_tracking(FFIClientConfig *config, bool enable); +/** + * Enables or disables mempool tracking + * + * # 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_mempool_tracking(FFIClientConfig *config, + bool enable); +/** + * Sets the mempool synchronization strategy + * + * # 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_mempool_strategy(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(FFIClientConfig *config, uint32_t max_transactions); -int32_t dash_spv_ffi_config_set_mempool_timeout(FFIClientConfig *config, uint64_t timeout_secs); +/** + * Sets the mempool transaction timeout in seconds + * + * # 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_mempool_timeout(FFIClientConfig *config, + uint64_t timeout_secs); -int32_t dash_spv_ffi_config_set_fetch_mempool_transactions(FFIClientConfig *config, bool fetch); +/** + * Sets whether to fetch full mempool transaction data + * + * # 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_fetch_mempool_transactions(FFIClientConfig *config, + bool fetch); -int32_t dash_spv_ffi_config_set_persist_mempool(FFIClientConfig *config, bool persist); +/** + * Sets whether to persist mempool state to disk + * + * # 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_persist_mempool(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 FFIClientConfig *config); +/** + * Gets the mempool synchronization strategy + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig or null + * - If null, returns FFIMempoolStrategy::Selective as default + */ enum FFIMempoolStrategy dash_spv_ffi_config_get_mempool_strategy(const FFIClientConfig *config); -int32_t dash_spv_ffi_config_set_start_from_height(FFIClientConfig *config, uint32_t height); +/** + * Sets the starting block height for synchronization + * + * # 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_start_from_height(FFIClientConfig *config, + uint32_t height); -int32_t dash_spv_ffi_config_set_wallet_creation_time(FFIClientConfig *config, uint32_t timestamp); +/** + * Sets the wallet creation timestamp for synchronization optimization + * + * # 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_wallet_creation_time(FFIClientConfig *config, + uint32_t timestamp); const char *dash_spv_ffi_get_last_error(void); @@ -541,6 +653,7 @@ int32_t dash_spv_ffi_init_logging(const char *level); const char *dash_spv_ffi_version(void); -const char *dash_spv_ffi_get_network_name(enum FFINetwork network); - void dash_spv_ffi_enable_test_mode(void); + +int32_t dash_spv_ffi_client_broadcast_transaction(struct FFIDashSpvClient *client, + const char *tx_hex); From 3e3bb602408ce37153ee29f4d8d48795f5f25496 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 02:09:44 +0700 Subject: [PATCH 3/9] clean up --- dash-spv-ffi/examples/wallet_manager_usage.rs | 2 +- key-wallet-ffi/FFI_API.md | 59 ++++++---- key-wallet-ffi/include/key_wallet_ffi.h | 48 ++++---- key-wallet-ffi/src/account.rs | 40 ++----- key-wallet-ffi/src/account_collection.rs | 89 +++++---------- key-wallet-ffi/src/account_tests.rs | 22 ++-- key-wallet-ffi/src/address.rs | 30 +---- key-wallet-ffi/src/address_pool.rs | 9 +- key-wallet-ffi/src/address_tests.rs | 28 +++-- key-wallet-ffi/src/derivation.rs | 104 +++--------------- key-wallet-ffi/src/derivation_tests.rs | 102 ++++++++--------- key-wallet-ffi/src/keys_tests.rs | 70 +++++++----- key-wallet-ffi/src/managed_account.rs | 70 ++++-------- .../src/managed_account_collection.rs | 16 +-- key-wallet-ffi/src/managed_wallet.rs | 30 ++--- key-wallet-ffi/src/transaction_tests.rs | 20 ++-- key-wallet-ffi/src/types.rs | 36 +++--- key-wallet-ffi/src/utxo.rs | 18 +-- key-wallet-ffi/src/utxo_tests.rs | 54 ++++----- .../src/wallet_manager_serialization_tests.rs | 16 +-- key-wallet-ffi/src/wallet_manager_tests.rs | 86 +++++++-------- key-wallet-ffi/src/wallet_tests.rs | 47 ++++---- key-wallet-ffi/tests/debug_wallet_add.rs | 2 +- key-wallet-ffi/tests/integration_test.rs | 19 ++-- .../tests/test_account_collection.rs | 9 +- key-wallet-ffi/tests/test_addr_simple.rs | 2 +- key-wallet-ffi/tests/test_import_wallet.rs | 2 +- .../tests/test_managed_account_collection.rs | 27 ++--- .../tests/test_passphrase_wallets.rs | 17 +-- 29 files changed, 453 insertions(+), 621 deletions(-) diff --git a/dash-spv-ffi/examples/wallet_manager_usage.rs b/dash-spv-ffi/examples/wallet_manager_usage.rs index 4f7dc1603..baffeb79d 100644 --- a/dash-spv-ffi/examples/wallet_manager_usage.rs +++ b/dash-spv-ffi/examples/wallet_manager_usage.rs @@ -51,7 +51,7 @@ fn main() { let affected = wallet_manager_process_transaction( wallet_manager, tx_hex.as_ptr() as *const i8, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 100000, // block height &mut error ); diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index 23006b70c..a1580d57d 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 226 +**Total Functions**: 227 ## Table of Contents @@ -295,7 +295,7 @@ Functions: 6 ### Utility Functions -Functions: 17 +Functions: 18 | Function | Description | Module | |----------|-------------|--------| @@ -313,6 +313,7 @@ Functions: 17 | `derivation_xpub_fingerprint` | Get fingerprint from extended public key (4 bytes) # Safety - `xpub` must b... | derivation | | `derivation_xpub_free` | Free extended public key # Safety - `xpub` must be a valid pointer to an FF... | derivation | | `derivation_xpub_to_string` | Get extended public key as string # Safety - `xpub` must be a valid pointer... | derivation | +| `ffi_network_get_name` | No description | types | | `free_u32_array` | Free a u32 array allocated by this library # Safety - `array` must be a val... | account_collection | | `script_p2pkh` | Create a P2PKH script pubkey # Safety - `pubkey_hash` must be a valid pointe... | transaction | | `string_free` | Free a string # Safety - `s` must be a valid pointer created by C string cr... | utils | @@ -867,7 +868,7 @@ Generate addresses up to a specific index in a pool This ensures that addresses #### `managed_wallet_get_account` ```c -managed_wallet_get_account(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, account_type: FFIAccountType,) -> FFIManagedAccountResult +managed_wallet_get_account(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetwork, account_index: c_uint, account_type: FFIAccountType,) -> FFIManagedAccountResult ``` **Description:** @@ -883,7 +884,7 @@ Get a managed account from a managed wallet This function gets a ManagedAccount #### `managed_wallet_get_account_collection` ```c -managed_wallet_get_account_collection(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, error: *mut FFIError,) -> *mut FFIManagedAccountCollection +managed_wallet_get_account_collection(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetwork, error: *mut FFIError,) -> *mut FFIManagedAccountCollection ``` **Description:** @@ -899,7 +900,7 @@ Get managed account collection for a specific network from wallet manager # Saf #### `managed_wallet_get_account_count` ```c -managed_wallet_get_account_count(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, error: *mut FFIError,) -> c_uint +managed_wallet_get_account_count(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetwork, error: *mut FFIError,) -> c_uint ``` **Description:** @@ -1011,7 +1012,7 @@ Get the next unused receive address Generates the next unused receive address f #### `managed_wallet_get_top_up_account_with_registration_index` ```c -managed_wallet_get_top_up_account_with_registration_index(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetworks, registration_index: c_uint,) -> FFIManagedAccountResult +managed_wallet_get_top_up_account_with_registration_index(manager: *const FFIWalletManager, wallet_id: *const u8, network: FFINetwork, registration_index: c_uint,) -> FFIManagedAccountResult ``` **Description:** @@ -1027,7 +1028,7 @@ Get a managed IdentityTopUp account with a specific registration index This is #### `managed_wallet_get_utxos` ```c -managed_wallet_get_utxos(managed_info: *const FFIManagedWalletInfo, network: FFINetworks, utxos_out: *mut *mut FFIUTXO, count_out: *mut usize, error: *mut FFIError,) -> bool +managed_wallet_get_utxos(managed_info: *const FFIManagedWalletInfo, network: FFINetwork, utxos_out: *mut *mut FFIUTXO, count_out: *mut usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -1427,7 +1428,7 @@ Generate a provider key at a specific index This generates a provider key (BLS #### `wallet_get_account` ```c -wallet_get_account(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, account_type: FFIAccountType,) -> FFIAccountResult +wallet_get_account(wallet: *const FFIWallet, network: FFINetwork, account_index: c_uint, account_type: FFIAccountType,) -> FFIAccountResult ``` **Description:** @@ -1443,7 +1444,7 @@ Get an account handle for a specific account type Returns a result containing ei #### `wallet_get_account_collection` ```c -wallet_get_account_collection(wallet: *const FFIWallet, network: FFINetworks, error: *mut FFIError,) -> *mut FFIAccountCollection +wallet_get_account_collection(wallet: *const FFIWallet, network: FFINetwork, error: *mut FFIError,) -> *mut FFIAccountCollection ``` **Description:** @@ -1459,7 +1460,7 @@ Get account collection for a specific network from wallet # Safety - `wallet` #### `wallet_get_account_count` ```c -wallet_get_account_count(wallet: *const FFIWallet, network: FFINetworks, error: *mut FFIError,) -> c_uint +wallet_get_account_count(wallet: *const FFIWallet, network: FFINetwork, error: *mut FFIError,) -> c_uint ``` **Description:** @@ -1523,7 +1524,7 @@ Get wallet ID (32-byte hash) # Safety - `wallet` must be a valid pointer to an #### `wallet_get_top_up_account_with_registration_index` ```c -wallet_get_top_up_account_with_registration_index(wallet: *const FFIWallet, network: FFINetworks, registration_index: c_uint,) -> FFIAccountResult +wallet_get_top_up_account_with_registration_index(wallet: *const FFIWallet, network: FFINetwork, registration_index: c_uint,) -> FFIAccountResult ``` **Description:** @@ -2183,7 +2184,7 @@ bls_account_get_network(account: *const FFIBLSAccount) -> FFINetworks #### `derivation_bip44_account_path` ```c -derivation_bip44_account_path(network: FFINetworks, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_bip44_account_path(network: FFINetwork, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -2888,7 +2889,7 @@ Free address string # Safety - `address` must be a valid pointer created by ad #### `address_get_type` ```c -address_get_type(address: *const c_char, network: FFINetworks, error: *mut FFIError,) -> c_uchar +address_get_type(address: *const c_char, network: FFINetwork, error: *mut FFIError,) -> c_uchar ``` **Description:** @@ -3000,7 +3001,7 @@ Extract public key hash from P2PKH address # Safety - `address` must be a valid #### `address_validate` ```c -address_validate(address: *const c_char, network: FFINetworks, error: *mut FFIError,) -> bool +address_validate(address: *const c_char, network: FFINetwork, error: *mut FFIError,) -> bool ``` **Description:** @@ -3018,7 +3019,7 @@ Validate an address # Safety - `address` must be a valid null-terminated C str #### `transaction_add_input` ```c -transaction_add_input(tx: *mut FFITransaction, input: *const FFITxIn) -> i32 +transaction_add_input(tx: *mut FFITransaction, input: *const FFITxIn,) -> i32 ``` **Description:** @@ -3034,7 +3035,7 @@ Add an input to a transaction # Safety - `tx` must be a valid pointer to an FFI #### `transaction_add_output` ```c -transaction_add_output(tx: *mut FFITransaction, output: *const FFITxOut) -> i32 +transaction_add_output(tx: *mut FFITransaction, output: *const FFITxOut,) -> i32 ``` **Description:** @@ -3257,7 +3258,7 @@ This function is unsafe because it dereferences raw pointers: - `private_key` mu #### `derivation_derive_private_key_from_seed` ```c -derivation_derive_private_key_from_seed(seed: *const u8, seed_len: usize, path: *const c_char, network: FFINetworks, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +derivation_derive_private_key_from_seed(seed: *const u8, seed_len: usize, path: *const c_char, network: FFINetwork, error: *mut FFIError,) -> *mut FFIExtendedPrivKey ``` **Description:** @@ -3273,7 +3274,7 @@ Derive private key for a specific path from seed # Safety - `seed` must be a v #### `derivation_new_master_key` ```c -derivation_new_master_key(seed: *const u8, seed_len: usize, network: FFINetworks, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +derivation_new_master_key(seed: *const u8, seed_len: usize, network: FFINetwork, error: *mut FFIError,) -> *mut FFIExtendedPrivKey ``` **Description:** @@ -3289,7 +3290,7 @@ Create a new master extended private key from seed # Safety - `seed` must be a #### `dip9_derive_identity_key` ```c -dip9_derive_identity_key(seed: *const u8, seed_len: usize, network: FFINetworks, identity_index: c_uint, key_index: c_uint, key_type: FFIDerivationPathType, error: *mut FFIError,) -> *mut FFIExtendedPrivKey +dip9_derive_identity_key(seed: *const u8, seed_len: usize, network: FFINetwork, identity_index: c_uint, key_index: c_uint, key_type: FFIDerivationPathType, error: *mut FFIError,) -> *mut FFIExtendedPrivKey ``` **Description:** @@ -3575,7 +3576,7 @@ Get word count from mnemonic # Safety - `mnemonic` must be a valid null-termin #### `derivation_bip44_payment_path` ```c -derivation_bip44_payment_path(network: FFINetworks, account_index: c_uint, is_change: bool, address_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_bip44_payment_path(network: FFINetwork, account_index: c_uint, is_change: bool, address_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -3588,7 +3589,7 @@ Derive a BIP44 payment path (m/44'/5'/account'/change/index) #### `derivation_coinjoin_path` ```c -derivation_coinjoin_path(network: FFINetworks, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_coinjoin_path(network: FFINetwork, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -3601,7 +3602,7 @@ Derive CoinJoin path (m/9'/5'/4'/account') #### `derivation_identity_authentication_path` ```c -derivation_identity_authentication_path(network: FFINetworks, identity_index: c_uint, key_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_identity_authentication_path(network: FFINetwork, identity_index: c_uint, key_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -3614,7 +3615,7 @@ Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') #### `derivation_identity_registration_path` ```c -derivation_identity_registration_path(network: FFINetworks, identity_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_identity_registration_path(network: FFINetwork, identity_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -3627,7 +3628,7 @@ Derive identity registration path (m/9'/5'/5'/1'/index') #### `derivation_identity_topup_path` ```c -derivation_identity_topup_path(network: FFINetworks, identity_index: c_uint, topup_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool +derivation_identity_topup_path(network: FFINetwork, identity_index: c_uint, topup_index: c_uint, path_out: *mut c_char, path_max_len: usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -3781,6 +3782,16 @@ Get extended public key as string # Safety - `xpub` must be a valid pointer to --- +#### `ffi_network_get_name` + +```c +ffi_network_get_name(network: FFINetwork) -> *const c_char +``` + +**Module:** `types` + +--- + #### `free_u32_array` ```c diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index b5c552750..c6ee6beed 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -203,10 +203,10 @@ typedef enum { */ typedef enum { NO_NETWORKS = 0, - DASH = 1, - TESTNET = 2, - REGTEST = 4, - DEVNET = 8, + DASH_FLAG = 1, + TESTNET_FLAG = 2, + REGTEST_FLAG = 4, + DEVNET_FLAG = 8, ALL_NETWORKS = 15, } FFINetworks; @@ -858,7 +858,7 @@ extern "C" { */ FFIAccountResult wallet_get_account(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIAccountType account_type) ; @@ -875,7 +875,7 @@ FFIAccountResult wallet_get_account(const FFIWallet *wallet, */ FFIAccountResult wallet_get_top_up_account_with_registration_index(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int registration_index) ; @@ -1103,7 +1103,7 @@ FFIAccountType eddsa_account_get_account_type(const FFIEdDSAAccount *account, */ unsigned int wallet_get_account_count(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -1118,7 +1118,7 @@ unsigned int wallet_get_account_count(const FFIWallet *wallet, */ FFIAccountCollection *wallet_get_account_collection(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -1481,7 +1481,7 @@ void account_collection_summary_free(FFIAccountCollectionSummary *summary) - `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError */ - bool address_validate(const char *address, FFINetworks network, FFIError *error) ; + bool address_validate(const char *address, FFINetwork network, FFIError *error) ; /* Get address type @@ -1497,7 +1497,7 @@ void account_collection_summary_free(FFIAccountCollectionSummary *summary) - `address` must be a valid null-terminated C string - `error` must be a valid pointer to an FFIError */ - unsigned char address_get_type(const char *address, FFINetworks network, FFIError *error) ; + unsigned char address_get_type(const char *address, FFINetwork network, FFIError *error) ; /* Free an address pool handle @@ -1671,7 +1671,7 @@ void address_info_array_free(FFIAddressInfo **infos, FFIExtendedPrivKey *derivation_new_master_key(const uint8_t *seed, size_t seed_len, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -1679,7 +1679,7 @@ FFIExtendedPrivKey *derivation_new_master_key(const uint8_t *seed, Derive a BIP44 account path (m/44'/5'/account') */ -bool derivation_bip44_account_path(FFINetworks network, +bool derivation_bip44_account_path(FFINetwork network, unsigned int account_index, char *path_out, size_t path_max_len, @@ -1690,7 +1690,7 @@ bool derivation_bip44_account_path(FFINetworks network, Derive a BIP44 payment path (m/44'/5'/account'/change/index) */ -bool derivation_bip44_payment_path(FFINetworks network, +bool derivation_bip44_payment_path(FFINetwork network, unsigned int account_index, bool is_change, unsigned int address_index, @@ -1703,7 +1703,7 @@ bool derivation_bip44_payment_path(FFINetworks network, Derive CoinJoin path (m/9'/5'/4'/account') */ -bool derivation_coinjoin_path(FFINetworks network, +bool derivation_coinjoin_path(FFINetwork network, unsigned int account_index, char *path_out, size_t path_max_len, @@ -1714,7 +1714,7 @@ bool derivation_coinjoin_path(FFINetworks network, Derive identity registration path (m/9'/5'/5'/1'/index') */ -bool derivation_identity_registration_path(FFINetworks network, +bool derivation_identity_registration_path(FFINetwork network, unsigned int identity_index, char *path_out, size_t path_max_len, @@ -1725,7 +1725,7 @@ bool derivation_identity_registration_path(FFINetworks network, Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') */ -bool derivation_identity_topup_path(FFINetworks network, +bool derivation_identity_topup_path(FFINetwork network, unsigned int identity_index, unsigned int topup_index, char *path_out, @@ -1737,7 +1737,7 @@ bool derivation_identity_topup_path(FFINetworks network, Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') */ -bool derivation_identity_authentication_path(FFINetworks network, +bool derivation_identity_authentication_path(FFINetwork network, unsigned int identity_index, unsigned int key_index, char *path_out, @@ -1759,7 +1759,7 @@ bool derivation_identity_authentication_path(FFINetworks network, FFIExtendedPrivKey *derivation_derive_private_key_from_seed(const uint8_t *seed, size_t seed_len, const char *path, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -1856,7 +1856,7 @@ bool derivation_xpub_fingerprint(const FFIExtendedPubKey *xpub, FFIExtendedPrivKey *dip9_derive_identity_key(const uint8_t *seed, size_t seed_len, - FFINetworks network, + FFINetwork network, unsigned int identity_index, unsigned int key_index, FFIDerivationPathType key_type, @@ -2241,7 +2241,7 @@ bool derivation_path_parse(const char *path, FFIManagedAccountResult managed_wallet_get_account(const FFIWalletManager *manager, const uint8_t *wallet_id, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIAccountType account_type) ; @@ -2263,7 +2263,7 @@ FFIManagedAccountResult managed_wallet_get_account(const FFIWalletManager *manag FFIManagedAccountResult managed_wallet_get_top_up_account_with_registration_index(const FFIWalletManager *manager, const uint8_t *wallet_id, - FFINetworks network, + FFINetwork network, unsigned int registration_index) ; @@ -2379,7 +2379,7 @@ FFIAccountType managed_account_get_account_type(const FFIManagedAccount *account unsigned int managed_wallet_get_account_count(const FFIWalletManager *manager, const uint8_t *wallet_id, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -2453,7 +2453,7 @@ FFIAddressPool *managed_account_get_address_pool(const FFIManagedAccount *accoun FFIManagedAccountCollection *managed_wallet_get_account_collection(const FFIWalletManager *manager, const uint8_t *wallet_id, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -3404,7 +3404,7 @@ bool managed_wallet_check_transaction(FFIManagedWallet *managed_wallet, */ bool managed_wallet_get_utxos(const FFIManagedWalletInfo *managed_info, - FFINetworks network, + FFINetwork network, FFIUTXO **utxos_out, size_t *count_out, FFIError *error) diff --git a/key-wallet-ffi/src/account.rs b/key-wallet-ffi/src/account.rs index a26a59fbd..bd9077ca2 100644 --- a/key-wallet-ffi/src/account.rs +++ b/key-wallet-ffi/src/account.rs @@ -4,7 +4,7 @@ use std::os::raw::c_uint; use std::sync::Arc; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFIAccountResult, FFIAccountType, FFINetworks, FFIWallet}; +use crate::types::{FFIAccountResult, FFIAccountType, FFINetwork, FFINetworks, FFIWallet}; #[cfg(feature = "bls")] use key_wallet::account::BLSAccount; #[cfg(feature = "eddsa")] @@ -81,7 +81,7 @@ impl FFIEdDSAAccount { #[no_mangle] pub unsafe extern "C" fn wallet_get_account( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: c_uint, account_type: FFIAccountType, ) -> FFIAccountResult { @@ -90,15 +90,7 @@ pub unsafe extern "C" fn wallet_get_account( } let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return FFIAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: key_wallet::Network = network.into(); let account_type_rust = account_type.to_account_type(account_index); @@ -126,7 +118,7 @@ pub unsafe extern "C" fn wallet_get_account( #[no_mangle] pub unsafe extern "C" fn wallet_get_top_up_account_with_registration_index( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, registration_index: c_uint, ) -> FFIAccountResult { if wallet.is_null() { @@ -134,15 +126,7 @@ pub unsafe extern "C" fn wallet_get_top_up_account_with_registration_index( } let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return FFIAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: key_wallet::Network = network.into(); // This function is specifically for IdentityTopUp accounts let account_type = key_wallet::AccountType::IdentityTopUp { @@ -581,7 +565,7 @@ pub unsafe extern "C" fn eddsa_account_get_is_watch_only(account: *const FFIEdDS #[no_mangle] pub unsafe extern "C" fn wallet_get_account_count( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> c_uint { if wallet.is_null() { @@ -590,17 +574,7 @@ pub unsafe extern "C" fn wallet_get_account_count( } let wallet = &*wallet; - let network: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return 0; - } - }; + let network: key_wallet::Network = network.into(); match wallet.inner().accounts.get(&network) { Some(accounts) => { diff --git a/key-wallet-ffi/src/account_collection.rs b/key-wallet-ffi/src/account_collection.rs index f6ebde5dd..ba5c0499a 100644 --- a/key-wallet-ffi/src/account_collection.rs +++ b/key-wallet-ffi/src/account_collection.rs @@ -9,7 +9,8 @@ use std::ptr; use crate::account::FFIAccount; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; +use crate::types::FFIWallet; +use crate::FFINetwork; /// Opaque handle to an account collection pub struct FFIAccountCollection { @@ -83,7 +84,7 @@ pub struct FFIAccountCollectionSummary { #[no_mangle] pub unsafe extern "C" fn wallet_get_account_collection( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut FFIAccountCollection { if wallet.is_null() { @@ -92,17 +93,7 @@ pub unsafe extern "C" fn wallet_get_account_collection( } let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); match wallet.inner().accounts_on_network(network_rust) { Some(collection) => { @@ -1102,18 +1093,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, ptr::null(), ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Check that we have some accounts @@ -1160,18 +1148,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Check for provider operator keys account (BLS) @@ -1210,18 +1195,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Check for provider platform keys account (EdDSA) @@ -1284,18 +1266,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Get the summary @@ -1340,18 +1319,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); // With SpecificAccounts and empty lists, collection might be null or empty if collection.is_null() { @@ -1428,18 +1404,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Get the summary data @@ -1518,18 +1491,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, &options, ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); // With AllAccounts but empty lists, collection should still exist if collection.is_null() { @@ -1592,18 +1562,15 @@ mod tests { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - crate::types::FFINetworks::Testnet, + crate::types::FFINetworks::TestnetFlag, ptr::null(), ptr::null_mut(), ); assert!(!wallet.is_null()); // Get account collection - let collection = wallet_get_account_collection( - wallet, - crate::types::FFINetworks::Testnet, - ptr::null_mut(), - ); + let collection = + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Get multiple summaries to test memory management diff --git a/key-wallet-ffi/src/account_tests.rs b/key-wallet-ffi/src/account_tests.rs index dda2c8a57..748c9078b 100644 --- a/key-wallet-ffi/src/account_tests.rs +++ b/key-wallet-ffi/src/account_tests.rs @@ -11,7 +11,7 @@ mod tests { #[test] fn test_wallet_get_account_null_wallet() { let result = unsafe { - wallet_get_account(ptr::null(), FFINetworks::Testnet, 0, FFIAccountType::StandardBIP44) + wallet_get_account(ptr::null(), FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44) }; assert!(result.account.is_null()); @@ -38,14 +38,14 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; // Try to get the default account (should exist) let result = unsafe { - wallet_get_account(wallet, FFINetworks::Testnet, 0, FFIAccountType::StandardBIP44) + wallet_get_account(wallet, FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44) }; // Note: Since the account may not exist yet (depends on wallet creation logic), @@ -77,7 +77,7 @@ mod tests { let mut error = FFIError::success(); let count = - unsafe { wallet_get_account_count(ptr::null(), FFINetworks::Testnet, &mut error) }; + unsafe { wallet_get_account_count(ptr::null(), FFINetwork::Testnet, &mut error) }; assert_eq!(count, 0); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -95,12 +95,12 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; - let count = unsafe { wallet_get_account_count(wallet, FFINetworks::Testnet, &mut error) }; + let count = unsafe { wallet_get_account_count(wallet, FFINetwork::Testnet, &mut error) }; // Should have at least one default account assert!(count >= 1); @@ -124,7 +124,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -133,7 +133,7 @@ mod tests { let count = unsafe { wallet_get_account_count( wallet, - FFINetworks::Dash, // Different network + FFINetwork::Dash, // Different network &mut error, ) }; @@ -176,7 +176,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -186,7 +186,7 @@ mod tests { // Get an account let result = unsafe { - wallet_get_account(wallet, FFINetworks::Testnet, 0, FFIAccountType::StandardBIP44) + wallet_get_account(wallet, FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44) }; if !result.account.is_null() { @@ -201,7 +201,7 @@ mod tests { // Test get network let network = account_get_network(result.account); - assert_eq!(network as u32, FFINetworks::Testnet as u32); + assert_eq!(network as u32, FFINetworks::TestnetFlag as u32); // Test get parent wallet id (may be null) let _wallet_id = account_get_parent_wallet_id(result.account); diff --git a/key-wallet-ffi/src/address.rs b/key-wallet-ffi/src/address.rs index 6421ba7a5..0fec4a946 100644 --- a/key-wallet-ffi/src/address.rs +++ b/key-wallet-ffi/src/address.rs @@ -8,7 +8,7 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_uchar}; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::FFINetworks; +use crate::types::FFINetwork; /// Free address string /// @@ -58,7 +58,7 @@ pub unsafe extern "C" fn address_array_free(addresses: *mut *mut c_char, count: #[no_mangle] pub unsafe extern "C" fn address_validate( address: *const c_char, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> bool { if address.is_null() { @@ -80,17 +80,7 @@ pub unsafe extern "C" fn address_validate( } }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use std::str::FromStr; match key_wallet::Address::from_str(address_str) { @@ -138,7 +128,7 @@ pub unsafe extern "C" fn address_validate( #[no_mangle] pub unsafe extern "C" fn address_get_type( address: *const c_char, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> c_uchar { if address.is_null() { @@ -160,17 +150,7 @@ pub unsafe extern "C" fn address_get_type( } }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return u8::MAX; - } - }; + let network_rust: key_wallet::Network = network.into(); use std::str::FromStr; match key_wallet::Address::from_str(address_str) { diff --git a/key-wallet-ffi/src/address_pool.rs b/key-wallet-ffi/src/address_pool.rs index 0f35707b6..3bdfa095c 100644 --- a/key-wallet-ffi/src/address_pool.rs +++ b/key-wallet-ffi/src/address_pool.rs @@ -1001,6 +1001,7 @@ pub unsafe extern "C" fn address_info_array_free(infos: *mut *mut FFIAddressInfo #[cfg(test)] mod tests { use super::*; + use crate::FFINetwork; #[test] fn test_address_pool_type_values() { @@ -1105,7 +1106,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -1130,7 +1131,7 @@ mod tests { let result = crate::managed_account::managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); @@ -1206,7 +1207,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -1231,7 +1232,7 @@ mod tests { let result = crate::managed_account::managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); diff --git a/key-wallet-ffi/src/address_tests.rs b/key-wallet-ffi/src/address_tests.rs index f588a3de9..6a343f672 100644 --- a/key-wallet-ffi/src/address_tests.rs +++ b/key-wallet-ffi/src/address_tests.rs @@ -4,7 +4,7 @@ mod address_tests { use crate::address::{address_array_free, address_free, address_get_type, address_validate}; use crate::error::{FFIError, FFIErrorCode}; - use crate::types::FFINetworks; + use crate::FFINetwork; use std::ffi::CString; use std::ptr; @@ -15,19 +15,18 @@ mod address_tests { // Test valid testnet address (generated from test mnemonic) let valid_addr = CString::new("yRd4FhXfVGHXpsuZXPNkMrfD9GVj46pnjt").unwrap(); - let is_valid = - unsafe { address_validate(valid_addr.as_ptr(), FFINetworks::Testnet, error) }; + let is_valid = unsafe { address_validate(valid_addr.as_ptr(), FFINetwork::Testnet, error) }; assert!(is_valid); // Test invalid address let invalid_addr = CString::new("invalid_address").unwrap(); let is_valid = - unsafe { address_validate(invalid_addr.as_ptr(), FFINetworks::Testnet, error) }; + unsafe { address_validate(invalid_addr.as_ptr(), FFINetwork::Testnet, error) }; assert!(!is_valid); assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidAddress); // Test null address - let is_valid = unsafe { address_validate(ptr::null(), FFINetworks::Testnet, error) }; + let is_valid = unsafe { address_validate(ptr::null(), FFINetwork::Testnet, error) }; assert!(!is_valid); assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidInput); } @@ -40,7 +39,7 @@ mod address_tests { // Test P2PKH address (generated from test mnemonic) let p2pkh_addr = CString::new("yRd4FhXfVGHXpsuZXPNkMrfD9GVj46pnjt").unwrap(); let addr_type = - unsafe { address_get_type(p2pkh_addr.as_ptr(), FFINetworks::Testnet, error) }; + unsafe { address_get_type(p2pkh_addr.as_ptr(), FFINetwork::Testnet, error) }; assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success); // Returns 0 for P2PKH assert_eq!(addr_type, 0); @@ -53,7 +52,7 @@ mod address_tests { // Test with valid testnet address - may fail due to library version differences let addr_str = CString::new("yeRZBWYfeNE4yVUHV4ZLs83Ppn9aMRH57A").unwrap(); let is_valid = - unsafe { address_validate(addr_str.as_ptr(), FFINetworks::Testnet, &mut error) }; + unsafe { address_validate(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) }; assert!(is_valid); } @@ -65,7 +64,7 @@ mod address_tests { // Test with invalid address let addr_str = CString::new("invalid_address").unwrap(); let is_valid = - unsafe { address_validate(addr_str.as_ptr(), FFINetworks::Testnet, &mut error) }; + unsafe { address_validate(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) }; assert!(!is_valid); assert_eq!(error.code, FFIErrorCode::InvalidAddress); @@ -75,7 +74,7 @@ mod address_tests { fn test_address_validate_null() { let mut error = FFIError::success(); - let is_valid = unsafe { address_validate(ptr::null(), FFINetworks::Testnet, &mut error) }; + let is_valid = unsafe { address_validate(ptr::null(), FFINetwork::Testnet, &mut error) }; assert!(!is_valid); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -88,7 +87,7 @@ mod address_tests { // Test P2PKH address type (use same known-valid address from other tests) let addr_str = CString::new("yRd4FhXfVGHXpsuZXPNkMrfD9GVj46pnjt").unwrap(); let addr_type = - unsafe { address_get_type(addr_str.as_ptr(), FFINetworks::Testnet, &mut error) }; + unsafe { address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) }; // Type should be 0, 1, or 2 for valid addresses // If it's invalid (255), the address might not be valid for testnet @@ -106,7 +105,7 @@ mod address_tests { let addr_str = CString::new("invalid_address").unwrap(); let addr_type = - unsafe { address_get_type(addr_str.as_ptr(), FFINetworks::Testnet, &mut error) }; + unsafe { address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) }; // Should return 255 (u8::MAX) for invalid assert_eq!(addr_type, 255); @@ -117,7 +116,7 @@ mod address_tests { fn test_address_get_type_null() { let mut error = FFIError::success(); - let addr_type = unsafe { address_get_type(ptr::null(), FFINetworks::Testnet, &mut error) }; + let addr_type = unsafe { address_get_type(ptr::null(), FFINetwork::Testnet, &mut error) }; // Should return 255 (u8::MAX) for null input assert_eq!(addr_type, 255); @@ -175,8 +174,7 @@ mod address_tests { unsafe { for invalid_addr in invalid_addresses.iter() { let addr_str = CString::new(*invalid_addr).unwrap(); - let is_valid = - address_validate(addr_str.as_ptr(), FFINetworks::Testnet, &mut error); + let is_valid = address_validate(addr_str.as_ptr(), FFINetwork::Testnet, &mut error); assert!(!is_valid); } } @@ -197,7 +195,7 @@ mod address_tests { for addr in test_addresses.iter() { let addr_str = CString::new(*addr).unwrap(); let addr_type = - address_get_type(addr_str.as_ptr(), FFINetworks::Testnet, &mut error); + address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error); // Should return a valid type (0, 1, 2) or 255 for error assert!(addr_type <= 2 || addr_type == 255); diff --git a/key-wallet-ffi/src/derivation.rs b/key-wallet-ffi/src/derivation.rs index 184cf758a..ba039b8b3 100644 --- a/key-wallet-ffi/src/derivation.rs +++ b/key-wallet-ffi/src/derivation.rs @@ -1,7 +1,7 @@ //! BIP32 and DIP9 derivation path functions use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetwork, FFINetworks}; +use crate::types::FFINetwork; use dash_network::Network; use key_wallet::{ExtendedPrivKey, ExtendedPubKey}; use secp256k1::Secp256k1; @@ -54,7 +54,7 @@ pub struct FFIExtendedPubKey { pub unsafe extern "C" fn derivation_new_master_key( seed: *const u8, seed_len: usize, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut FFIExtendedPrivKey { if seed.is_null() { @@ -63,17 +63,7 @@ pub unsafe extern "C" fn derivation_new_master_key( } let seed_slice = slice::from_raw_parts(seed, seed_len); - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); match key_wallet::bip32::ExtendedPrivKey::new_master(network_rust, seed_slice) { Ok(xpriv) => { @@ -96,7 +86,7 @@ pub unsafe extern "C" fn derivation_new_master_key( /// Derive a BIP44 account path (m/44'/5'/account') #[no_mangle] pub extern "C" fn derivation_bip44_account_path( - network: FFINetworks, + network: FFINetwork, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, @@ -111,17 +101,7 @@ pub extern "C" fn derivation_bip44_account_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::DerivationPath; let derivation = DerivationPath::bip_44_account(network_rust, account_index); @@ -161,7 +141,7 @@ pub extern "C" fn derivation_bip44_account_path( /// Derive a BIP44 payment path (m/44'/5'/account'/change/index) #[no_mangle] pub extern "C" fn derivation_bip44_payment_path( - network: FFINetworks, + network: FFINetwork, account_index: c_uint, is_change: bool, address_index: c_uint, @@ -178,17 +158,7 @@ pub extern "C" fn derivation_bip44_payment_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::DerivationPath; let derivation = @@ -229,7 +199,7 @@ pub extern "C" fn derivation_bip44_payment_path( /// Derive CoinJoin path (m/9'/5'/4'/account') #[no_mangle] pub extern "C" fn derivation_coinjoin_path( - network: FFINetworks, + network: FFINetwork, account_index: c_uint, path_out: *mut c_char, path_max_len: usize, @@ -244,17 +214,7 @@ pub extern "C" fn derivation_coinjoin_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::DerivationPath; let derivation = DerivationPath::coinjoin_path(network_rust, account_index); @@ -294,7 +254,7 @@ pub extern "C" fn derivation_coinjoin_path( /// Derive identity registration path (m/9'/5'/5'/1'/index') #[no_mangle] pub extern "C" fn derivation_identity_registration_path( - network: FFINetworks, + network: FFINetwork, identity_index: c_uint, path_out: *mut c_char, path_max_len: usize, @@ -309,17 +269,7 @@ pub extern "C" fn derivation_identity_registration_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::DerivationPath; let derivation = DerivationPath::identity_registration_path(network_rust, identity_index); @@ -359,7 +309,7 @@ pub extern "C" fn derivation_identity_registration_path( /// Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') #[no_mangle] pub extern "C" fn derivation_identity_topup_path( - network: FFINetworks, + network: FFINetwork, identity_index: c_uint, topup_index: c_uint, path_out: *mut c_char, @@ -375,17 +325,7 @@ pub extern "C" fn derivation_identity_topup_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::DerivationPath; let derivation = @@ -426,7 +366,7 @@ pub extern "C" fn derivation_identity_topup_path( /// Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') #[no_mangle] pub extern "C" fn derivation_identity_authentication_path( - network: FFINetworks, + network: FFINetwork, identity_index: c_uint, key_index: c_uint, path_out: *mut c_char, @@ -442,17 +382,7 @@ pub extern "C" fn derivation_identity_authentication_path( return false; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); use key_wallet::bip32::{DerivationPath, KeyDerivationType}; let derivation = DerivationPath::identity_authentication_path( @@ -507,7 +437,7 @@ pub unsafe extern "C" fn derivation_derive_private_key_from_seed( seed: *const u8, seed_len: usize, path: *const c_char, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut FFIExtendedPrivKey { if seed.is_null() || path.is_null() { @@ -780,7 +710,7 @@ pub unsafe extern "C" fn derivation_string_free(s: *mut c_char) { pub unsafe extern "C" fn dip9_derive_identity_key( seed: *const u8, seed_len: usize, - network: FFINetworks, + network: FFINetwork, identity_index: c_uint, key_index: c_uint, key_type: FFIDerivationPathType, diff --git a/key-wallet-ffi/src/derivation_tests.rs b/key-wallet-ffi/src/derivation_tests.rs index 9359dfb07..ce4b64ed4 100644 --- a/key-wallet-ffi/src/derivation_tests.rs +++ b/key-wallet-ffi/src/derivation_tests.rs @@ -6,7 +6,6 @@ mod tests { use crate::derivation::*; use crate::error::{FFIError, FFIErrorCode}; use crate::mnemonic; - use crate::types::FFINetworks; use std::ffi::{CStr, CString}; use std::ptr; @@ -34,7 +33,7 @@ mod tests { // Create master key from seed let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; assert!(!xprv.is_null()); @@ -56,7 +55,7 @@ mod tests { } let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; // Get public key @@ -84,7 +83,7 @@ mod tests { } let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; // Convert to string @@ -114,7 +113,7 @@ mod tests { } let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpub = unsafe { derivation_xpriv_to_xpub(xprv, &mut error) }; @@ -149,7 +148,7 @@ mod tests { } let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpub = unsafe { derivation_xpriv_to_xpub(xprv, &mut error) }; @@ -179,7 +178,7 @@ mod tests { // Test BIP44 account path let mut account_path = vec![0u8; 256]; let success = derivation_bip44_account_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, account_path.as_mut_ptr() as *mut i8, account_path.len(), @@ -194,7 +193,7 @@ mod tests { // Test BIP44 payment path let mut payment_path = vec![0u8; 256]; let success = derivation_bip44_payment_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // account_index false, // is_change 0, // address_index @@ -216,7 +215,7 @@ mod tests { // Test CoinJoin path let mut coinjoin_path = vec![0u8; 256]; let success = derivation_coinjoin_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // account_index coinjoin_path.as_mut_ptr() as *mut i8, coinjoin_path.len(), @@ -227,7 +226,7 @@ mod tests { // Test identity registration path - takes 2 args: network and identity_index let mut id_reg_path = vec![0u8; 256]; let success = derivation_identity_registration_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index id_reg_path.as_mut_ptr() as *mut i8, id_reg_path.len(), @@ -238,7 +237,7 @@ mod tests { // Test identity topup path - takes 3 args: network, identity_index, topup_index let mut id_topup_path = vec![0u8; 256]; let success = derivation_identity_topup_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index 2, // topup_index id_topup_path.as_mut_ptr() as *mut i8, @@ -250,7 +249,7 @@ mod tests { // Test identity authentication path - takes 3 args: network, identity_index, key_index let mut id_auth_path = vec![0u8; 256]; let success = derivation_identity_authentication_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index 3, // key_index id_auth_path.as_mut_ptr() as *mut i8, @@ -279,7 +278,7 @@ mod tests { seed.as_ptr(), seed.len(), path.as_ptr(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ) }; @@ -307,7 +306,7 @@ mod tests { dip9_derive_identity_key( seed.as_ptr(), seed.len(), - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity index 0, // key index FFIDerivationPathType::PathBlockchainIdentities, // key_type @@ -329,7 +328,7 @@ mod tests { // Test with null seed let xprv = - unsafe { derivation_new_master_key(ptr::null(), 64, FFINetworks::Testnet, &mut error) }; + unsafe { derivation_new_master_key(ptr::null(), 64, FFINetwork::Testnet, &mut error) }; assert!(xprv.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -348,7 +347,7 @@ mod tests { } let master_key = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpub = unsafe { derivation_xpriv_to_xpub(master_key, &mut error) }; @@ -380,7 +379,7 @@ mod tests { } let master_key = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpriv_string = unsafe { derivation_xpriv_to_string(master_key, &mut error) }; @@ -411,7 +410,7 @@ mod tests { } let master_key = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpub = unsafe { derivation_xpriv_to_xpub(master_key, &mut error) }; @@ -440,7 +439,7 @@ mod tests { // Test identity registration path let mut buffer = vec![0u8; 256]; let success = derivation_identity_registration_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index buffer.as_mut_ptr() as *mut i8, buffer.len(), @@ -454,7 +453,7 @@ mod tests { // Test identity topup path let mut buffer = vec![0u8; 256]; let success = derivation_identity_topup_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index 0, // topup_index buffer.as_mut_ptr() as *mut i8, @@ -469,7 +468,7 @@ mod tests { // Test identity authentication path let mut buffer = vec![0u8; 256]; let success = derivation_identity_authentication_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // identity_index 0, // key_index buffer.as_mut_ptr() as *mut i8, @@ -502,7 +501,7 @@ mod tests { // Test with null seed let xprv = - unsafe { derivation_new_master_key(ptr::null(), 64, FFINetworks::Testnet, &mut error) }; + unsafe { derivation_new_master_key(ptr::null(), 64, FFINetwork::Testnet, &mut error) }; assert!(xprv.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -512,7 +511,7 @@ mod tests { derivation_new_master_key( seed.as_ptr(), seed.len(), - FFINetworks::Testnet, + FFINetwork::Testnet, ptr::null_mut(), ) }; @@ -529,19 +528,14 @@ mod tests { let mut error = FFIError::success(); // Test BIP44 account path with null buffer - let success = derivation_bip44_account_path( - FFINetworks::Testnet, - 0, - ptr::null_mut(), - 256, - &mut error, - ); + let success = + derivation_bip44_account_path(FFINetwork::Testnet, 0, ptr::null_mut(), 256, &mut error); assert!(!success); assert_eq!(error.code, FFIErrorCode::InvalidInput); // Test BIP44 payment path with null buffer let success = derivation_bip44_payment_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, false, 0, @@ -554,7 +548,7 @@ mod tests { // Test CoinJoin path with null buffer let success = - derivation_coinjoin_path(FFINetworks::Testnet, 0, ptr::null_mut(), 256, &mut error); + derivation_coinjoin_path(FFINetwork::Testnet, 0, ptr::null_mut(), 256, &mut error); assert!(!success); assert_eq!(error.code, FFIErrorCode::InvalidInput); } @@ -566,7 +560,7 @@ mod tests { // Test with very small buffer (should fail) let mut small_buffer = [0i8; 5]; let success = derivation_bip44_account_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, small_buffer.as_mut_ptr(), small_buffer.len(), @@ -577,7 +571,7 @@ mod tests { // Test BIP44 payment path with small buffer let success = derivation_bip44_payment_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, false, 0, @@ -599,14 +593,14 @@ mod tests { // Test with Mainnet let xprv_main = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Dash, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Dash, &mut error) }; assert!(!xprv_main.is_null()); assert_eq!(error.code, FFIErrorCode::Success); // Test with Testnet let xprv_test = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; assert!(!xprv_test.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -685,7 +679,7 @@ mod tests { } let xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; let xpub = unsafe { derivation_xpriv_to_xpub(xprv, &mut error) }; @@ -715,7 +709,7 @@ mod tests { ptr::null(), 64, path.as_ptr(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ) }; @@ -728,7 +722,7 @@ mod tests { seed.as_ptr(), seed.len(), ptr::null(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ) }; @@ -751,7 +745,7 @@ mod tests { seed.as_ptr(), seed.len(), invalid_path.as_ptr(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ) }; @@ -773,7 +767,7 @@ mod tests { dip9_derive_identity_key( ptr::null(), 64, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, FFIDerivationPathType::PathBlockchainIdentities, @@ -797,7 +791,7 @@ mod tests { dip9_derive_identity_key( seed.as_ptr(), seed.len(), - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, FFIDerivationPathType::PathBlockchainIdentities, @@ -818,7 +812,7 @@ mod tests { // Test identity registration with null buffer let success = derivation_identity_registration_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, ptr::null_mut(), 256, @@ -829,7 +823,7 @@ mod tests { // Test identity topup with null buffer let success = derivation_identity_topup_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, ptr::null_mut(), @@ -841,7 +835,7 @@ mod tests { // Test identity authentication with null buffer let success = derivation_identity_authentication_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, ptr::null_mut(), @@ -859,7 +853,7 @@ mod tests { // Test identity registration with small buffer let success = derivation_identity_registration_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, small_buffer.as_mut_ptr(), small_buffer.len(), @@ -870,7 +864,7 @@ mod tests { // Test identity topup with small buffer let success = derivation_identity_topup_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, small_buffer.as_mut_ptr(), @@ -882,7 +876,7 @@ mod tests { // Test identity authentication with small buffer let success = derivation_identity_authentication_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, 0, small_buffer.as_mut_ptr(), @@ -901,7 +895,7 @@ mod tests { // Test BIP44 account path with different account indices let success1 = derivation_bip44_account_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, buffer1.as_mut_ptr() as *mut i8, buffer1.len(), @@ -910,7 +904,7 @@ mod tests { assert!(success1); let success2 = derivation_bip44_account_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 5, buffer2.as_mut_ptr() as *mut i8, buffer2.len(), @@ -933,7 +927,7 @@ mod tests { // Test receive address path let mut buffer = vec![0u8; 256]; let success = derivation_bip44_payment_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // account_index false, // is_change (receive) 5, // address_index @@ -948,7 +942,7 @@ mod tests { // Test change address path let mut buffer = vec![0u8; 256]; let success = derivation_bip44_payment_path( - FFINetworks::Testnet, + FFINetwork::Testnet, 0, // account_index true, // is_change 3, // address_index @@ -973,7 +967,7 @@ mod tests { // Create master key let master_xprv = unsafe { - derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetworks::Testnet, &mut error) + derivation_new_master_key(seed.as_ptr(), seed.len(), FFINetwork::Testnet, &mut error) }; assert!(!master_xprv.is_null()); @@ -995,7 +989,7 @@ mod tests { seed.as_ptr(), seed.len(), path.as_ptr(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ) }; diff --git a/key-wallet-ffi/src/keys_tests.rs b/key-wallet-ffi/src/keys_tests.rs index ee357f75c..2a1aa8034 100644 --- a/key-wallet-ffi/src/keys_tests.rs +++ b/key-wallet-ffi/src/keys_tests.rs @@ -21,7 +21,7 @@ mod tests { let wallet = wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ); assert!(!wallet.is_null()); @@ -31,7 +31,7 @@ mod tests { let path = CString::new("m/44'/1'/0'").unwrap(); let ext_priv = wallet_derive_extended_private_key( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, path.as_ptr(), &mut error, ); @@ -40,7 +40,7 @@ mod tests { // Test extended_private_key_to_string let xprv_str = - extended_private_key_to_string(ext_priv, FFINetworks::Testnet, &mut error); + extended_private_key_to_string(ext_priv, FFINetworks::TestnetFlag, &mut error); assert!(!xprv_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -54,7 +54,7 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Get WIF from the extracted private key - let wif = private_key_to_wif(priv_key, FFINetworks::Testnet, &mut error); + let wif = private_key_to_wif(priv_key, FFINetworks::TestnetFlag, &mut error); assert!(!wif.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -70,7 +70,7 @@ mod tests { // Now test extended public key let ext_pub = wallet_derive_extended_public_key( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, path.as_ptr(), &mut error, ); @@ -78,7 +78,8 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Test extended_public_key_to_string - let xpub_str = extended_public_key_to_string(ext_pub, FFINetworks::Testnet, &mut error); + let xpub_str = + extended_public_key_to_string(ext_pub, FFINetworks::TestnetFlag, &mut error); assert!(!xpub_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -121,7 +122,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -129,7 +130,7 @@ mod tests { // Try to get account xpriv - should fail let xpriv_str = - unsafe { wallet_get_account_xpriv(wallet, FFINetworks::Testnet, 0, &mut error) }; + unsafe { wallet_get_account_xpriv(wallet, FFINetworks::TestnetFlag, 0, &mut error) }; // Should return null (not implemented for security) assert!(xpriv_str.is_null()); @@ -153,7 +154,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -161,7 +162,7 @@ mod tests { // Get account xpub let xpub_str = - unsafe { wallet_get_account_xpub(wallet, FFINetworks::Testnet, 0, &mut error) }; + unsafe { wallet_get_account_xpub(wallet, FFINetworks::TestnetFlag, 0, &mut error) }; assert!(!xpub_str.is_null()); @@ -189,7 +190,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -197,7 +198,7 @@ mod tests { // Try to derive private key - should now succeed (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let privkey_ptr = unsafe { - wallet_derive_private_key(wallet, FFINetworks::Testnet, path.as_ptr(), &mut error) + wallet_derive_private_key(wallet, FFINetworks::TestnetFlag, path.as_ptr(), &mut error) }; // Should succeed and return a valid pointer @@ -205,7 +206,8 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Convert to WIF to verify it's valid - let wif_str = unsafe { private_key_to_wif(privkey_ptr, FFINetworks::Testnet, &mut error) }; + let wif_str = + unsafe { private_key_to_wif(privkey_ptr, FFINetworks::TestnetFlag, &mut error) }; assert!(!wif_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -239,7 +241,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -251,7 +253,7 @@ mod tests { // Derive public key using derivation path (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key(wallet, FFINetworks::Testnet, path.as_ptr(), &mut error) + wallet_derive_public_key(wallet, FFINetworks::TestnetFlag, path.as_ptr(), &mut error) }; if pubkey_ptr.is_null() { @@ -295,7 +297,7 @@ mod tests { let wallet = wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ); assert!(!wallet.is_null()); @@ -305,7 +307,7 @@ mod tests { let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let hex_str = wallet_derive_public_key_as_hex( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, path.as_ptr(), &mut error, ); @@ -405,8 +407,9 @@ mod tests { let mut error = FFIError::success(); // Test with null wallet - let xpriv = - unsafe { wallet_get_account_xpriv(ptr::null(), FFINetworks::Testnet, 0, &mut error) }; + let xpriv = unsafe { + wallet_get_account_xpriv(ptr::null(), FFINetworks::TestnetFlag, 0, &mut error) + }; assert!(xpriv.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -436,7 +439,12 @@ mod tests { // Test with null wallet (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key(ptr::null(), FFINetworks::Testnet, path.as_ptr(), &mut error) + wallet_derive_public_key( + ptr::null(), + FFINetworks::TestnetFlag, + path.as_ptr(), + &mut error, + ) }; assert!(pubkey_ptr.is_null()); @@ -450,14 +458,14 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; // Test with null path let pubkey_ptr = unsafe { - wallet_derive_public_key(wallet, FFINetworks::Testnet, ptr::null(), &mut error) + wallet_derive_public_key(wallet, FFINetworks::TestnetFlag, ptr::null(), &mut error) }; assert!(pubkey_ptr.is_null()); @@ -586,7 +594,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -595,7 +603,7 @@ mod tests { // Test different account indices for account_index in 0..3 { let xpub_str = unsafe { - wallet_get_account_xpub(wallet, FFINetworks::Testnet, account_index, &mut error) + wallet_get_account_xpub(wallet, FFINetworks::TestnetFlag, account_index, &mut error) }; if !xpub_str.is_null() { @@ -610,8 +618,9 @@ mod tests { } // Test with null wallet - let xpub_str = - unsafe { wallet_get_account_xpub(ptr::null(), FFINetworks::Testnet, 0, &mut error) }; + let xpub_str = unsafe { + wallet_get_account_xpub(ptr::null(), FFINetworks::TestnetFlag, 0, &mut error) + }; assert!(xpub_str.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -634,7 +643,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -651,7 +660,12 @@ mod tests { let path = CString::new(*path_str).unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key(wallet, FFINetworks::Testnet, path.as_ptr(), &mut error) + wallet_derive_public_key( + wallet, + FFINetworks::TestnetFlag, + path.as_ptr(), + &mut error, + ) }; if !pubkey_ptr.is_null() { diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index a7981bdbe..e90093019 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use crate::address_pool::{FFIAddressPool, FFIAddressPoolType}; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFIAccountType, FFINetworks}; +use crate::types::{FFIAccountType, FFINetwork, FFINetworks}; use crate::wallet_manager::FFIWalletManager; use key_wallet::managed_account::address_pool::AddressPool; use key_wallet::managed_account::ManagedAccount; @@ -86,7 +86,7 @@ impl FFIManagedAccountResult { pub unsafe extern "C" fn managed_wallet_get_account( manager: *const FFIWalletManager, wallet_id: *const u8, - network: FFINetworks, + network: FFINetwork, account_index: c_uint, account_type: FFIAccountType, ) -> FFIManagedAccountResult { @@ -108,15 +108,7 @@ pub unsafe extern "C" fn managed_wallet_get_account( let mut wallet_id_array = [0u8; 32]; std::ptr::copy_nonoverlapping(wallet_id, wallet_id_array.as_mut_ptr(), 32); - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return FFIManagedAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: key_wallet::Network = network.into(); // Get the managed wallet info from the manager let mut error = FFIError::success(); @@ -222,7 +214,7 @@ pub unsafe extern "C" fn managed_wallet_get_account( pub unsafe extern "C" fn managed_wallet_get_top_up_account_with_registration_index( manager: *const FFIWalletManager, wallet_id: *const u8, - network: FFINetworks, + network: FFINetwork, registration_index: c_uint, ) -> FFIManagedAccountResult { if manager.is_null() { @@ -243,15 +235,7 @@ pub unsafe extern "C" fn managed_wallet_get_top_up_account_with_registration_ind let mut wallet_id_array = [0u8; 32]; std::ptr::copy_nonoverlapping(wallet_id, wallet_id_array.as_mut_ptr(), 32); - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return FFIManagedAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: key_wallet::Network = network.into(); // Get the managed wallet info from the manager let mut error = FFIError::success(); @@ -519,7 +503,7 @@ pub unsafe extern "C" fn managed_account_result_free_error(result: *mut FFIManag pub unsafe extern "C" fn managed_wallet_get_account_count( manager: *const FFIWalletManager, wallet_id: *const u8, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> c_uint { if manager.is_null() || wallet_id.is_null() { @@ -527,17 +511,7 @@ pub unsafe extern "C" fn managed_wallet_get_account_count( return 0; } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return 0; - } - }; + let network_rust: key_wallet::Network = network.into(); // Get the wallet from the manager let wallet_ptr = crate::wallet_manager::wallet_manager_get_wallet(manager, wallet_id, error); @@ -803,7 +777,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -828,7 +802,7 @@ mod tests { let result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); @@ -872,7 +846,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -895,7 +869,7 @@ mod tests { let mut result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::CoinJoin, ); @@ -952,7 +926,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -974,7 +948,7 @@ mod tests { let count = managed_wallet_get_account_count( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); @@ -1006,7 +980,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -1031,7 +1005,7 @@ mod tests { let result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); @@ -1044,7 +1018,7 @@ mod tests { // Test get_network let network = managed_account_get_network(account); - assert_eq!(network, FFINetworks::Testnet); + assert_eq!(network, FFINetworks::TestnetFlag); // Test get_account_type let mut index_out: c_uint = 999; // Initialize with unexpected value @@ -1127,7 +1101,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -1149,7 +1123,7 @@ mod tests { let result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); @@ -1184,7 +1158,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -1209,7 +1183,7 @@ mod tests { let result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::StandardBIP44, ); @@ -1279,7 +1253,7 @@ mod tests { manager, mnemonic2.as_ptr(), passphrase2.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -1299,7 +1273,7 @@ mod tests { let cj_result = managed_wallet_get_account( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, 0, FFIAccountType::CoinJoin, ); diff --git a/key-wallet-ffi/src/managed_account_collection.rs b/key-wallet-ffi/src/managed_account_collection.rs index c39dc0281..917adf2a2 100644 --- a/key-wallet-ffi/src/managed_account_collection.rs +++ b/key-wallet-ffi/src/managed_account_collection.rs @@ -11,8 +11,8 @@ use std::ptr; use crate::error::{FFIError, FFIErrorCode}; use crate::managed_account::FFIManagedAccount; -use crate::types::FFINetworks; use crate::wallet_manager::FFIWalletManager; +use crate::FFINetwork; /// Opaque handle to a managed account collection pub struct FFIManagedAccountCollection { @@ -90,7 +90,7 @@ pub struct FFIManagedAccountCollectionSummary { pub unsafe extern "C" fn managed_wallet_get_account_collection( manager: *const FFIWalletManager, wallet_id: *const u8, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut FFIManagedAccountCollection { if manager.is_null() || wallet_id.is_null() { @@ -98,17 +98,7 @@ pub unsafe extern "C" fn managed_wallet_get_account_collection( return ptr::null_mut(); } - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Get the managed wallet info from the manager let managed_wallet_ptr = diff --git a/key-wallet-ffi/src/managed_wallet.rs b/key-wallet-ffi/src/managed_wallet.rs index bef4c9105..672c4cb3a 100644 --- a/key-wallet-ffi/src/managed_wallet.rs +++ b/key-wallet-ffi/src/managed_wallet.rs @@ -818,7 +818,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -837,7 +837,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -858,7 +858,7 @@ mod tests { managed_wallet_get_bip_44_external_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 10, @@ -885,7 +885,7 @@ mod tests { managed_wallet_get_bip_44_internal_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 10, @@ -913,7 +913,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -930,7 +930,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( &mut ffi_managed, wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -957,7 +957,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( &mut ffi_managed, wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -999,7 +999,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -1063,7 +1063,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -1082,7 +1082,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &mut error, ) @@ -1104,7 +1104,7 @@ mod tests { managed_wallet_get_bip_44_external_address_range( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 5, @@ -1138,7 +1138,7 @@ mod tests { managed_wallet_get_bip_44_internal_address_range( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 3, @@ -1185,7 +1185,7 @@ mod tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -1275,7 +1275,7 @@ mod tests { managed_wallet_get_bip_44_external_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 10, @@ -1294,7 +1294,7 @@ mod tests { managed_wallet_get_bip_44_internal_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, 0, 10, diff --git a/key-wallet-ffi/src/transaction_tests.rs b/key-wallet-ffi/src/transaction_tests.rs index ec47fdab1..38d55184f 100644 --- a/key-wallet-ffi/src/transaction_tests.rs +++ b/key-wallet-ffi/src/transaction_tests.rs @@ -23,7 +23,7 @@ mod transaction_tests { let success = unsafe { wallet_build_transaction( ptr::null_mut(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &output, 1, @@ -55,7 +55,7 @@ mod transaction_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -66,7 +66,7 @@ mod transaction_tests { let success = unsafe { wallet_build_transaction( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), 0, @@ -97,7 +97,7 @@ mod transaction_tests { let success = unsafe { wallet_sign_transaction( ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, tx_bytes.as_ptr(), tx_bytes.len(), &mut signed_tx_out, @@ -122,7 +122,7 @@ mod transaction_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -133,7 +133,7 @@ mod transaction_tests { let success = unsafe { wallet_sign_transaction( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), 0, &mut signed_tx_out, @@ -171,7 +171,7 @@ mod transaction_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -187,7 +187,7 @@ mod transaction_tests { let success = unsafe { wallet_build_transaction( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &output, 1, @@ -221,7 +221,7 @@ mod transaction_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut error, ) }; @@ -233,7 +233,7 @@ mod transaction_tests { let success = unsafe { wallet_sign_transaction( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, tx_bytes.as_ptr(), tx_bytes.len(), &mut signed_tx_out, diff --git a/key-wallet-ffi/src/types.rs b/key-wallet-ffi/src/types.rs index 8c7a96dad..8abef7252 100644 --- a/key-wallet-ffi/src/types.rs +++ b/key-wallet-ffi/src/types.rs @@ -9,11 +9,11 @@ use std::sync::Arc; #[derive(Debug, Clone, Copy, PartialEq)] pub enum FFINetworks { NoNetworks = 0, - Dash = 1, - Testnet = 2, - Regtest = 4, - Devnet = 8, - AllNetworks = 15, // Dash | Testnet | Regtest | Devnet + DashFlag = 1, + TestnetFlag = 2, + RegtestFlag = 4, + DevnetFlag = 8, + AllNetworks = 15, // DashFlag | TestnetFlag | RegtestFlag | DevnetFlag } impl FFINetworks { @@ -28,16 +28,16 @@ impl FFINetworks { let mut networks = Vec::new(); - if flags & (FFINetworks::Dash as c_uint) != 0 { + if flags & (FFINetworks::DashFlag as c_uint) != 0 { networks.push(Network::Dash); } - if flags & (FFINetworks::Testnet as c_uint) != 0 { + if flags & (FFINetworks::TestnetFlag as c_uint) != 0 { networks.push(Network::Testnet); } - if flags & (FFINetworks::Regtest as c_uint) != 0 { + if flags & (FFINetworks::RegtestFlag as c_uint) != 0 { networks.push(Network::Regtest); } - if flags & (FFINetworks::Devnet as c_uint) != 0 { + if flags & (FFINetworks::DevnetFlag as c_uint) != 0 { networks.push(Network::Devnet); } @@ -53,10 +53,10 @@ impl FFINetworks { // Check if it's a single network match flags { - x if x == FFINetworks::Dash as c_uint => Some(Network::Dash), - x if x == FFINetworks::Testnet as c_uint => Some(Network::Testnet), - x if x == FFINetworks::Regtest as c_uint => Some(Network::Regtest), - x if x == FFINetworks::Devnet as c_uint => Some(Network::Devnet), + x if x == FFINetworks::DashFlag as c_uint => Some(Network::Dash), + x if x == FFINetworks::TestnetFlag as c_uint => Some(Network::Testnet), + x if x == FFINetworks::RegtestFlag as c_uint => Some(Network::Regtest), + x if x == FFINetworks::DevnetFlag as c_uint => Some(Network::Devnet), _ => None, // Multiple networks or NoNetworks } } @@ -121,11 +121,11 @@ impl TryFrom for Network { impl From for FFINetworks { fn from(n: Network) -> Self { match n { - Network::Dash => FFINetworks::Dash, - Network::Testnet => FFINetworks::Testnet, - Network::Regtest => FFINetworks::Regtest, - Network::Devnet => FFINetworks::Devnet, - _ => FFINetworks::Dash, // Default to Dash for unknown networks + Network::Dash => FFINetworks::DashFlag, + Network::Testnet => FFINetworks::TestnetFlag, + Network::Regtest => FFINetworks::RegtestFlag, + Network::Devnet => FFINetworks::DevnetFlag, + _ => FFINetworks::DashFlag, // Default to Dash for unknown networks } } } diff --git a/key-wallet-ffi/src/utxo.rs b/key-wallet-ffi/src/utxo.rs index 756150e99..457048919 100644 --- a/key-wallet-ffi/src/utxo.rs +++ b/key-wallet-ffi/src/utxo.rs @@ -6,7 +6,7 @@ use std::ptr; use crate::error::{FFIError, FFIErrorCode}; use crate::managed_wallet::FFIManagedWalletInfo; -use crate::types::FFINetworks; +use crate::types::{FFINetwork, FFINetworks}; /// UTXO structure for FFI #[repr(C)] @@ -88,7 +88,7 @@ impl FFIUTXO { #[no_mangle] pub unsafe extern "C" fn managed_wallet_get_utxos( managed_info: *const FFIManagedWalletInfo, - network: FFINetworks, + network: FFINetwork, utxos_out: *mut *mut FFIUTXO, count_out: *mut usize, error: *mut FFIError, @@ -99,19 +99,7 @@ pub unsafe extern "C" fn managed_wallet_get_utxos( } let managed_info = &*managed_info; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - *count_out = 0; - *utxos_out = ptr::null_mut(); - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); // Get UTXOs from the managed wallet info let utxos = managed_info.inner().get_utxos(network_rust); diff --git a/key-wallet-ffi/src/utxo_tests.rs b/key-wallet-ffi/src/utxo_tests.rs index b0802c48a..bece5ff5e 100644 --- a/key-wallet-ffi/src/utxo_tests.rs +++ b/key-wallet-ffi/src/utxo_tests.rs @@ -81,9 +81,9 @@ mod utxo_tests { // The deprecated function should always return an empty list let success = unsafe { #[allow(deprecated)] - super::super::wallet_get_utxos( + wallet_get_utxos( ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut utxos_out, &mut count_out, error, @@ -104,9 +104,9 @@ mod utxo_tests { // Test with null managed_info let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( ptr::null(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, &mut count_out, error, @@ -131,9 +131,9 @@ mod utxo_tests { let ffi_managed_info = FFIManagedWalletInfo::new(managed_info); let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, &mut count_out, error, @@ -173,7 +173,7 @@ mod utxo_tests { // Free the UTXOs unsafe { - super::super::utxo_array_free(utxos_ptr, count); + utxo_array_free(utxos_ptr, count); } } @@ -181,7 +181,7 @@ mod utxo_tests { fn test_utxo_array_free_null() { // Should handle null gracefully unsafe { - super::super::utxo_array_free(ptr::null_mut(), 0); + utxo_array_free(ptr::null_mut(), 0); } } @@ -254,9 +254,9 @@ mod utxo_tests { let ffi_managed_info = FFIManagedWalletInfo::new(managed_info); let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, &mut count_out, error, @@ -294,7 +294,7 @@ mod utxo_tests { // Clean up unsafe { - super::super::utxo_array_free(utxos_out, count_out); + utxo_array_free(utxos_out, count_out); } } @@ -427,9 +427,9 @@ mod utxo_tests { let ffi_managed_info = FFIManagedWalletInfo::new(managed_info); let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, &mut count_out, error, @@ -442,7 +442,7 @@ mod utxo_tests { // Clean up unsafe { - super::super::utxo_array_free(utxos_out, count_out); + utxo_array_free(utxos_out, count_out); } } @@ -545,9 +545,9 @@ mod utxo_tests { // Get Testnet UTXOs let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, &mut count_out, error, @@ -556,14 +556,14 @@ mod utxo_tests { assert!(result); assert_eq!(count_out, 1); unsafe { - super::super::utxo_array_free(utxos_out, count_out); + utxo_array_free(utxos_out, count_out); } // Get Mainnet UTXOs let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Dash, + FFINetwork::Dash, &mut utxos_out, &mut count_out, error, @@ -572,14 +572,14 @@ mod utxo_tests { assert!(result); assert_eq!(count_out, 1); unsafe { - super::super::utxo_array_free(utxos_out, count_out); + utxo_array_free(utxos_out, count_out); } // Get Regtest UTXOs (should be empty) let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Regtest, + FFINetwork::Regtest, &mut utxos_out, &mut count_out, error, @@ -671,7 +671,7 @@ mod utxo_tests { // Free should handle all different UTXO types unsafe { - super::super::utxo_array_free(utxos_ptr, count); + utxo_array_free(utxos_ptr, count); } } @@ -689,9 +689,9 @@ mod utxo_tests { // Test with null utxos_out let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, ptr::null_mut(), &mut count_out, error, @@ -703,9 +703,9 @@ mod utxo_tests { // Test with null count_out let mut utxos_out: *mut FFIUTXO = ptr::null_mut(); let result = unsafe { - super::super::managed_wallet_get_utxos( + managed_wallet_get_utxos( &ffi_managed_info, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut utxos_out, ptr::null_mut(), error, diff --git a/key-wallet-ffi/src/wallet_manager_serialization_tests.rs b/key-wallet-ffi/src/wallet_manager_serialization_tests.rs index 3f152e0c6..9aa598896 100644 --- a/key-wallet-ffi/src/wallet_manager_serialization_tests.rs +++ b/key-wallet-ffi/src/wallet_manager_serialization_tests.rs @@ -32,7 +32,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, // birth_height ptr::null(), // default account options false, // don't downgrade to pubkey wallet @@ -84,7 +84,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), true, // downgrade to pubkey wallet @@ -133,7 +133,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), true, // downgrade to pubkey wallet @@ -182,7 +182,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), false, @@ -231,7 +231,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), false, @@ -298,7 +298,7 @@ mod tests { manager, invalid_mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), false, @@ -334,7 +334,7 @@ mod tests { manager, ptr::null(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), false, @@ -390,7 +390,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, &account_options, false, diff --git a/key-wallet-ffi/src/wallet_manager_tests.rs b/key-wallet-ffi/src/wallet_manager_tests.rs index fe76e7c54..a9ff8f653 100644 --- a/key-wallet-ffi/src/wallet_manager_tests.rs +++ b/key-wallet-ffi/src/wallet_manager_tests.rs @@ -54,7 +54,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, // Create 3 accounts + FFINetworks::TestnetFlag, // Create 3 accounts error, ) }; @@ -92,7 +92,7 @@ mod tests { manager, mnemonic.as_ptr(), ptr::null(), // No passphrase - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ); if !success { @@ -167,7 +167,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -193,7 +193,7 @@ mod tests { wallet_manager::wallet_manager_get_receive_address( manager, wallet_ids, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, // account_index error, ) @@ -209,7 +209,7 @@ mod tests { wallet_manager::wallet_manager_get_receive_address( manager, wallet_ids, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ) @@ -249,7 +249,7 @@ mod tests { manager, mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -274,7 +274,7 @@ mod tests { wallet_manager::wallet_manager_get_change_address( manager, wallet_ids, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ) @@ -305,7 +305,7 @@ mod tests { manager, mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -367,7 +367,7 @@ mod tests { let success = unsafe { wallet_manager::wallet_manager_get_monitored_addresses( manager, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut addresses as *mut *mut *mut c_char, &mut count, error, @@ -385,7 +385,7 @@ mod tests { manager, mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -412,7 +412,7 @@ mod tests { let addr = wallet_manager::wallet_manager_get_receive_address( manager, wallet_ids, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ); @@ -426,7 +426,7 @@ mod tests { let success = unsafe { wallet_manager::wallet_manager_get_monitored_addresses( manager, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mut addresses as *mut *mut *mut c_char, &mut count, error, @@ -463,7 +463,7 @@ mod tests { // Get initial height let height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::Testnet, error) + wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) }; assert_eq!(height, 0); @@ -471,7 +471,7 @@ mod tests { let success = unsafe { wallet_manager::wallet_manager_update_height( manager, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 100000, error, ) @@ -480,7 +480,7 @@ mod tests { // Verify height was updated let height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::Testnet, error) + wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) }; assert_eq!(height, 100000); @@ -510,7 +510,7 @@ mod tests { manager, invalid_mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -544,8 +544,8 @@ mod tests { let success = wallet_manager::wallet_manager_add_wallet_from_mnemonic( manager, mnemonic.as_ptr(), - ptr::null(), // No passphrase - FFINetworks::Testnet, // 2 accounts per wallet + ptr::null(), // No passphrase + FFINetworks::TestnetFlag, // 2 accounts per wallet error, ); assert!(success); @@ -579,7 +579,7 @@ mod tests { let addr = wallet_manager::wallet_manager_get_receive_address( manager, wallet_id, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ); @@ -616,7 +616,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, // account_count + FFINetworks::TestnetFlag, // account_count error, ) }; @@ -648,7 +648,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -703,7 +703,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -727,7 +727,7 @@ mod tests { wallet_manager::wallet_manager_get_change_address( manager, wallet_ids, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, // address_index error, ) @@ -767,7 +767,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -848,7 +848,7 @@ mod tests { ptr::null_mut(), mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, // account_count + FFINetworks::TestnetFlag, // account_count error, ) }; @@ -873,7 +873,7 @@ mod tests { // Get initial height let _height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::Testnet, error) + wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) }; // Update height @@ -881,7 +881,7 @@ mod tests { unsafe { wallet_manager::wallet_manager_update_height( manager, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, new_height, error, ); @@ -889,7 +889,7 @@ mod tests { // Get updated height let current_height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::Testnet, error) + wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) }; assert_eq!(current_height, new_height); @@ -916,7 +916,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -1036,7 +1036,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -1072,7 +1072,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mempool_context, false, error, @@ -1089,7 +1089,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &block_context, false, error, @@ -1110,7 +1110,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &chain_locked_context, true, error, @@ -1125,7 +1125,7 @@ mod tests { ptr::null_mut(), tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mempool_context, false, error, @@ -1140,7 +1140,7 @@ mod tests { manager, ptr::null(), 10, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mempool_context, false, error, @@ -1155,7 +1155,7 @@ mod tests { manager, tx_bytes.as_ptr(), 0, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mempool_context, false, error, @@ -1171,7 +1171,7 @@ mod tests { manager, invalid_tx.as_ptr(), invalid_tx.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &mempool_context, false, error, @@ -1203,7 +1203,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -1318,7 +1318,7 @@ mod tests { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, // birth_height ptr::null(), // default account options false, // don't downgrade to pubkey wallet @@ -1360,7 +1360,7 @@ mod tests { manager2, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), true, // downgrade to pubkey wallet @@ -1431,7 +1431,7 @@ mod tests { manager4, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), true, // downgrade to pubkey wallet @@ -1471,7 +1471,7 @@ mod tests { manager5, invalid_mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, ptr::null(), false, @@ -1519,7 +1519,7 @@ mod tests { manager1, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 100, // birth_height ptr::null(), // default account options false, // don't downgrade to pubkey wallet diff --git a/key-wallet-ffi/src/wallet_tests.rs b/key-wallet-ffi/src/wallet_tests.rs index 23349ebfa..4bd48a542 100644 --- a/key-wallet-ffi/src/wallet_tests.rs +++ b/key-wallet-ffi/src/wallet_tests.rs @@ -23,7 +23,7 @@ mod wallet_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -45,7 +45,12 @@ mod wallet_tests { let seed = [0x01u8; 64]; let wallet = unsafe { - wallet::wallet_create_from_seed(seed.as_ptr(), seed.len(), FFINetworks::Testnet, error) + wallet::wallet_create_from_seed( + seed.as_ptr(), + seed.len(), + FFINetworks::TestnetFlag, + error, + ) }; assert!(!wallet.is_null()); @@ -63,7 +68,8 @@ mod wallet_tests { let error = &mut error as *mut FFIError; // Test random wallet creation - let random_wallet = unsafe { wallet::wallet_create_random(FFINetworks::Testnet, error) }; + let random_wallet = + unsafe { wallet::wallet_create_random(FFINetworks::TestnetFlag, error) }; assert!(!random_wallet.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success); @@ -90,7 +96,7 @@ mod wallet_tests { let wallet = wallet::wallet_create_from_seed( seed.as_ptr(), seed.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ); @@ -115,7 +121,7 @@ mod wallet_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -139,7 +145,7 @@ mod wallet_tests { wallet::wallet_create_from_mnemonic( ptr::null(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -152,7 +158,7 @@ mod wallet_tests { wallet::wallet_create_from_mnemonic( invalid_mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -161,7 +167,7 @@ mod wallet_tests { // Test with null seed let wallet = unsafe { - wallet::wallet_create_from_seed(ptr::null(), 64, FFINetworks::Testnet, error) + wallet::wallet_create_from_seed(ptr::null(), 64, FFINetworks::TestnetFlag, error) }; assert!(wallet.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidInput); @@ -172,7 +178,7 @@ mod wallet_tests { let mut error = FFIError::success(); let error = &mut error as *mut FFIError; - let wallet = unsafe { wallet::wallet_create_random(FFINetworks::Testnet, error) }; + let wallet = unsafe { wallet::wallet_create_random(FFINetworks::TestnetFlag, error) }; assert!(!wallet.is_null()); // Get wallet ID @@ -206,7 +212,7 @@ mod wallet_tests { wallet::wallet_create_from_seed( seed_bytes.as_ptr(), seed_bytes.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -227,7 +233,7 @@ mod wallet_tests { // Test with null seed bytes let wallet = unsafe { - wallet::wallet_create_from_seed(ptr::null(), 64, FFINetworks::Testnet, error) + wallet::wallet_create_from_seed(ptr::null(), 64, FFINetworks::TestnetFlag, error) }; assert!(wallet.is_null()); @@ -247,7 +253,7 @@ mod wallet_tests { wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -280,14 +286,14 @@ mod wallet_tests { let mut error = FFIError::success(); let error = &mut error as *mut FFIError; - let wallet = unsafe { wallet::wallet_create_random(FFINetworks::Testnet, error) }; + let wallet = unsafe { wallet::wallet_create_random(FFINetworks::TestnetFlag, error) }; assert!(!wallet.is_null()); // Test adding account - check if it succeeds or fails gracefully let result = unsafe { wallet::wallet_add_account( wallet, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, FFIAccountType::StandardBIP44, 1, ) @@ -322,7 +328,7 @@ mod wallet_tests { let result = unsafe { wallet::wallet_add_account( ptr::null_mut(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, FFIAccountType::StandardBIP44, 0, ) @@ -349,7 +355,7 @@ mod wallet_tests { wallet::wallet_create_from_seed( normal_seed.as_ptr(), normal_seed.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -364,7 +370,7 @@ mod wallet_tests { wallet::wallet_create_from_seed( large_seed.as_ptr(), large_seed.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -381,11 +387,11 @@ mod wallet_tests { let mut error = FFIError::success(); let error = &mut error as *mut FFIError; - let wallet = unsafe { wallet::wallet_create_random(FFINetworks::Testnet, error) }; + let wallet = unsafe { wallet::wallet_create_random(FFINetworks::TestnetFlag, error) }; assert!(!wallet.is_null()); // Get xpub for account 0 - let xpub = unsafe { wallet::wallet_get_xpub(wallet, FFINetworks::Testnet, 0, error) }; + let xpub = unsafe { wallet::wallet_get_xpub(wallet, FFINetworks::TestnetFlag, 0, error) }; assert!(!xpub.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success); @@ -406,7 +412,8 @@ mod wallet_tests { let error = &mut error as *mut FFIError; // Test with null wallet - let xpub = unsafe { wallet::wallet_get_xpub(ptr::null(), FFINetworks::Testnet, 0, error) }; + let xpub = + unsafe { wallet::wallet_get_xpub(ptr::null(), FFINetworks::TestnetFlag, 0, error) }; assert!(xpub.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidInput); } diff --git a/key-wallet-ffi/tests/debug_wallet_add.rs b/key-wallet-ffi/tests/debug_wallet_add.rs index 1e4be5a48..c56fb889e 100644 --- a/key-wallet-ffi/tests/debug_wallet_add.rs +++ b/key-wallet-ffi/tests/debug_wallet_add.rs @@ -21,7 +21,7 @@ fn test_debug_wallet_add() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; diff --git a/key-wallet-ffi/tests/integration_test.rs b/key-wallet-ffi/tests/integration_test.rs index a24498147..bf833f442 100644 --- a/key-wallet-ffi/tests/integration_test.rs +++ b/key-wallet-ffi/tests/integration_test.rs @@ -4,6 +4,7 @@ use key_wallet_ffi::error::{FFIError, FFIErrorCode}; use key_wallet_ffi::types::FFINetworks; +use key_wallet_ffi::FFINetwork; use std::ffi::CString; use std::ptr; @@ -35,7 +36,7 @@ fn test_full_wallet_workflow() { manager, mnemonic, passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -62,7 +63,7 @@ fn test_full_wallet_workflow() { key_wallet_ffi::wallet_manager::wallet_manager_get_receive_address( manager, wallet_id, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ) @@ -73,7 +74,7 @@ fn test_full_wallet_workflow() { key_wallet_ffi::wallet_manager::wallet_manager_get_change_address( manager, wallet_id, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, error, ) @@ -135,7 +136,7 @@ fn test_seed_to_wallet_workflow() { key_wallet_ffi::wallet::wallet_create_from_seed( seed.as_ptr(), seed_len, - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -157,7 +158,7 @@ fn test_derivation_paths() { // Account path let success = key_wallet_ffi::derivation::derivation_bip44_account_path( - FFINetworks::Dash, + FFINetwork::Dash, 0, path_buffer.as_mut_ptr() as *mut std::os::raw::c_char, path_buffer.len(), @@ -175,7 +176,7 @@ fn test_derivation_paths() { // Payment path path_buffer.fill(0); let success = key_wallet_ffi::derivation::derivation_bip44_payment_path( - FFINetworks::Dash, + FFINetwork::Dash, 0, false, 5, @@ -206,7 +207,7 @@ fn test_error_handling() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( invalid_mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -218,7 +219,7 @@ fn test_error_handling() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( ptr::null(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -231,7 +232,7 @@ fn test_error_handling() { key_wallet_ffi::wallet::wallet_create_from_seed( invalid_seed.as_ptr(), invalid_seed.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; diff --git a/key-wallet-ffi/tests/test_account_collection.rs b/key-wallet-ffi/tests/test_account_collection.rs index 9c973b419..4049f3775 100644 --- a/key-wallet-ffi/tests/test_account_collection.rs +++ b/key-wallet-ffi/tests/test_account_collection.rs @@ -6,6 +6,7 @@ use key_wallet_ffi::types::{ FFIAccountCreationOptionType, FFINetworks, FFIWalletAccountCreationOptions, }; use key_wallet_ffi::wallet::{wallet_create_from_mnemonic_with_options, wallet_free}; +use key_wallet_ffi::FFINetwork; use std::ffi::CString; use std::ptr; @@ -35,7 +36,7 @@ fn test_account_collection_comprehensive() { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &account_options, ptr::null_mut(), ); @@ -43,7 +44,7 @@ fn test_account_collection_comprehensive() { // Get account collection for testnet let collection = - wallet_get_account_collection(wallet, FFINetworks::Testnet, ptr::null_mut()); + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Test account count @@ -153,7 +154,7 @@ fn test_account_collection_minimal() { let wallet = wallet_create_from_mnemonic_with_options( mnemonic.as_ptr(), ptr::null(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), // Use default options ptr::null_mut(), ); @@ -161,7 +162,7 @@ fn test_account_collection_minimal() { // Get account collection let collection = - wallet_get_account_collection(wallet, FFINetworks::Testnet, ptr::null_mut()); + wallet_get_account_collection(wallet, FFINetwork::Testnet, ptr::null_mut()); assert!(!collection.is_null()); // Should have at least some default accounts diff --git a/key-wallet-ffi/tests/test_addr_simple.rs b/key-wallet-ffi/tests/test_addr_simple.rs index 99b21e0f9..7eeab61a2 100644 --- a/key-wallet-ffi/tests/test_addr_simple.rs +++ b/key-wallet-ffi/tests/test_addr_simple.rs @@ -12,7 +12,7 @@ fn test_address_simple() { key_wallet_ffi::wallet::wallet_create_from_seed( seed.as_ptr(), seed.len(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; diff --git a/key-wallet-ffi/tests/test_import_wallet.rs b/key-wallet-ffi/tests/test_import_wallet.rs index 56cf493a2..0bd310297 100644 --- a/key-wallet-ffi/tests/test_import_wallet.rs +++ b/key-wallet-ffi/tests/test_import_wallet.rs @@ -25,7 +25,7 @@ mod tests { manager, mnemonic.as_ptr() as *const i8, passphrase.as_ptr() as *const i8, - FFINetworks::Dash, + FFINetworks::DashFlag, &mut error, ); assert!(success); diff --git a/key-wallet-ffi/tests/test_managed_account_collection.rs b/key-wallet-ffi/tests/test_managed_account_collection.rs index 8310d8282..d410a8b1b 100644 --- a/key-wallet-ffi/tests/test_managed_account_collection.rs +++ b/key-wallet-ffi/tests/test_managed_account_collection.rs @@ -9,6 +9,7 @@ use key_wallet_ffi::wallet_manager::{ wallet_manager_add_wallet_from_mnemonic_with_options, wallet_manager_create, wallet_manager_free, wallet_manager_free_wallet_ids, wallet_manager_get_wallet_ids, }; +use key_wallet_ffi::FFINetwork; use std::ffi::CString; use std::ptr; @@ -33,7 +34,7 @@ fn test_managed_account_collection_basic() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), // Use default options &mut error, ); @@ -54,7 +55,7 @@ fn test_managed_account_collection_basic() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(!collection.is_null()); @@ -138,7 +139,7 @@ fn test_managed_account_collection_with_special_accounts() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -157,7 +158,7 @@ fn test_managed_account_collection_with_special_accounts() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(!collection.is_null()); @@ -263,7 +264,7 @@ fn test_managed_account_collection_summary() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -282,7 +283,7 @@ fn test_managed_account_collection_summary() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(!collection.is_null()); @@ -357,7 +358,7 @@ fn test_managed_account_collection_summary_data() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, &options, &mut error, ); @@ -376,7 +377,7 @@ fn test_managed_account_collection_summary_data() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(!collection.is_null()); @@ -427,7 +428,7 @@ fn test_managed_account_collection_null_safety() { let collection = managed_wallet_get_account_collection( ptr::null(), ptr::null(), - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(collection.is_null()); @@ -463,7 +464,7 @@ fn test_managed_account_collection_nonexistent_accounts() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), // Default options &mut error, ); @@ -482,7 +483,7 @@ fn test_managed_account_collection_nonexistent_accounts() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Testnet, + FFINetwork::Testnet, &mut error, ); assert!(!collection.is_null()); @@ -524,7 +525,7 @@ fn test_managed_account_collection_wrong_network() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, ptr::null(), &mut error, ); @@ -543,7 +544,7 @@ fn test_managed_account_collection_wrong_network() { let collection = managed_wallet_get_account_collection( manager, wallet_ids_out, - FFINetworks::Dash, // Wrong network (mainnet) + FFINetwork::Dash, // Wrong network (mainnet) &mut error, ); diff --git a/key-wallet-ffi/tests/test_passphrase_wallets.rs b/key-wallet-ffi/tests/test_passphrase_wallets.rs index 46ba17bff..d99a0f924 100644 --- a/key-wallet-ffi/tests/test_passphrase_wallets.rs +++ b/key-wallet-ffi/tests/test_passphrase_wallets.rs @@ -3,6 +3,7 @@ use key_wallet_ffi::error::{FFIError, FFIErrorCode}; use key_wallet_ffi::types::FFINetworks; +use key_wallet_ffi::FFINetwork; use std::ffi::{CStr, CString}; #[test] @@ -20,7 +21,7 @@ fn test_ffi_wallet_create_from_mnemonic_with_passphrase() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -69,7 +70,7 @@ fn test_ffi_wallet_manager_add_wallet_with_passphrase() { manager, mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, // account_count (ignored) + FFINetworks::TestnetFlag, // account_count (ignored) error, ) }; @@ -99,7 +100,7 @@ fn test_ffi_wallet_manager_add_wallet_with_passphrase() { key_wallet_ffi::wallet_manager::wallet_manager_get_receive_address( manager, wallet_ids_ptr, // First wallet ID - FFINetworks::Testnet, + FFINetworks::TestnetFlag, 0, // account_index error, ) @@ -146,7 +147,7 @@ fn test_ffi_wallet_with_passphrase_ideal_workflow() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -185,7 +186,7 @@ fn test_demonstrate_passphrase_issue_with_account_creation() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), empty_passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -196,7 +197,7 @@ fn test_demonstrate_passphrase_issue_with_account_creation() { key_wallet_ffi::wallet::wallet_create_from_mnemonic( mnemonic.as_ptr(), actual_passphrase.as_ptr(), - FFINetworks::Testnet, + FFINetworks::TestnetFlag, error, ) }; @@ -206,7 +207,7 @@ fn test_demonstrate_passphrase_issue_with_account_creation() { let count_no_pass = unsafe { key_wallet_ffi::account::wallet_get_account_count( wallet_no_pass, - FFINetworks::Testnet, + FFINetwork::Testnet, error, ) }; @@ -214,7 +215,7 @@ fn test_demonstrate_passphrase_issue_with_account_creation() { let count_with_pass = unsafe { key_wallet_ffi::account::wallet_get_account_count( wallet_with_pass, - FFINetworks::Testnet, + FFINetwork::Testnet, error, ) }; From ff6c2e38c211c1ae81ad98a53b2a55b916fc6e58 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 04:11:13 +0700 Subject: [PATCH 4/9] fixes --- key-wallet-ffi/include/key_wallet_ffi.h | 2 +- key-wallet-ffi/src/managed_account.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index c6ee6beed..497a4ac70 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -2274,7 +2274,7 @@ FFIManagedAccountResult managed_wallet_get_top_up_account_with_registration_inde - `account` must be a valid pointer to an FFIManagedAccount instance */ - FFINetworks managed_account_get_network(const FFIManagedAccount *account) ; + FFINetwork managed_account_get_network(const FFIManagedAccount *account) ; /* Get the parent wallet ID of a managed account diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index e90093019..0a16872c9 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use crate::address_pool::{FFIAddressPool, FFIAddressPoolType}; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFIAccountType, FFINetwork, FFINetworks}; +use crate::types::{FFIAccountType, FFINetwork}; use crate::wallet_manager::FFIWalletManager; use key_wallet::managed_account::address_pool::AddressPool; use key_wallet::managed_account::ManagedAccount; @@ -298,9 +298,9 @@ pub unsafe extern "C" fn managed_wallet_get_top_up_account_with_registration_ind #[no_mangle] pub unsafe extern "C" fn managed_account_get_network( account: *const FFIManagedAccount, -) -> FFINetworks { +) -> FFINetwork { if account.is_null() { - return FFINetworks::NoNetworks; + return FFINetwork::Dash; } let account = &*account; @@ -1018,7 +1018,7 @@ mod tests { // Test get_network let network = managed_account_get_network(account); - assert_eq!(network, FFINetworks::TestnetFlag); + assert_eq!(network, FFINetwork::Testnet); // Test get_account_type let mut index_out: c_uint = 999; // Initialize with unexpected value @@ -1069,7 +1069,7 @@ mod tests { unsafe { // Test null account let network = managed_account_get_network(ptr::null()); - assert_eq!(network, FFINetworks::NoNetworks); + assert_eq!(network, FFINetwork::Dash); let mut index_out: c_uint = 0; let account_type = managed_account_get_account_type(ptr::null(), &mut index_out); From 0165ab2aea76417b15a094f7aae4858702c06b85 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 04:49:52 +0700 Subject: [PATCH 5/9] more clean up --- key-wallet-ffi/include/key_wallet_ffi.h | 225 ++-------- key-wallet-ffi/src/address_pool.rs | 61 +-- key-wallet-ffi/src/keys.rs | 132 +----- key-wallet-ffi/src/keys_tests.rs | 52 +-- key-wallet-ffi/src/lib.rs | 1 - key-wallet-ffi/src/managed_account.rs | 1 + key-wallet-ffi/src/managed_wallet.rs | 97 ++--- key-wallet-ffi/src/provider_keys.rs | 384 ----------------- key-wallet-ffi/src/transaction.rs | 14 +- key-wallet-ffi/src/transaction_checking.rs | 16 +- key-wallet-ffi/src/wallet.rs | 56 +-- key-wallet-ffi/src/wallet_manager.rs | 310 +------------ key-wallet-ffi/src/wallet_manager_tests.rs | 408 +----------------- key-wallet-ffi/src/wallet_tests.rs | 11 +- key-wallet-ffi/tests/integration_test.rs | 27 +- .../tests/test_passphrase_wallets.rs | 30 +- 16 files changed, 151 insertions(+), 1674 deletions(-) delete mode 100644 key-wallet-ffi/src/provider_keys.rs diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index 497a4ac70..ceff25e1a 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -210,28 +210,6 @@ typedef enum { ALL_NETWORKS = 15, } FFINetworks; -/* - Provider key type - */ -typedef enum { - /* - BLS voting keys (m/9'/5'/3'/1'/[key_index]) - */ - VOTING_KEYS = 0, - /* - BLS owner keys (m/9'/5'/3'/2'/[key_index]) - */ - OWNER_KEYS = 1, - /* - BLS operator keys (m/9'/5'/3'/3'/[key_index]) - */ - OPERATOR_KEYS = 2, - /* - EdDSA platform P2P keys (m/9'/5'/3'/4'/[key_index]) - */ - PLATFORM_KEYS = 3, -} FFIProviderKeyType; - /* FFI-compatible transaction context */ @@ -652,36 +630,6 @@ typedef struct { bool has_provider_platform_keys; } FFIManagedAccountCollectionSummary; -/* - Provider key info - */ -typedef struct { - /* - Key index - */ - unsigned int key_index; - /* - Public key bytes (48 bytes for BLS, 32 bytes for EdDSA) - */ - uint8_t *public_key; - /* - Public key length - */ - size_t public_key_len; - /* - Private key bytes (32 bytes, only if available) - */ - uint8_t *private_key; - /* - Private key length (0 if not available) - */ - size_t private_key_len; - /* - Derivation path as string - */ - char *derivation_path; -} FFIProviderKeyInfo; - /* Transaction output for building (legacy structure) */ @@ -1521,7 +1469,7 @@ void account_collection_summary_free(FFIAccountCollectionSummary *summary) */ bool managed_wallet_get_address_pool_info(const FFIManagedWallet *managed_wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index, FFIAddressPoolType pool_type, @@ -1542,7 +1490,7 @@ bool managed_wallet_get_address_pool_info(const FFIManagedWallet *managed_wallet */ bool managed_wallet_set_gap_limit(FFIManagedWallet *managed_wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index, FFIAddressPoolType pool_type, @@ -1566,7 +1514,7 @@ bool managed_wallet_set_gap_limit(FFIManagedWallet *managed_wallet, bool managed_wallet_generate_addresses_to_index(FFIManagedWallet *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index, FFIAddressPoolType pool_type, @@ -1588,7 +1536,7 @@ bool managed_wallet_generate_addresses_to_index(FFIManagedWallet *managed_wallet */ bool managed_wallet_mark_address_used(FFIManagedWallet *managed_wallet, - FFINetworks network, + FFINetwork network, const char *address, FFIError *error) ; @@ -1934,7 +1882,7 @@ int32_t key_wallet_derive_private_key_from_seed(const uint8_t *seed, */ char *wallet_get_account_xpriv(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIError *error) ; @@ -1950,7 +1898,7 @@ char *wallet_get_account_xpriv(const FFIWallet *wallet, */ char *wallet_get_account_xpub(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIError *error) ; @@ -1968,7 +1916,7 @@ char *wallet_get_account_xpub(const FFIWallet *wallet, */ FFIPrivateKey *wallet_derive_private_key(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -1986,7 +1934,7 @@ FFIPrivateKey *wallet_derive_private_key(const FFIWallet *wallet, */ FFIExtendedPrivateKey *wallet_derive_extended_private_key(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -2003,7 +1951,7 @@ FFIExtendedPrivateKey *wallet_derive_extended_private_key(const FFIWallet *walle */ char *wallet_derive_private_key_as_wif(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -2042,7 +1990,7 @@ char *wallet_derive_private_key_as_wif(const FFIWallet *wallet, */ char *extended_private_key_to_string(const FFIExtendedPrivateKey *key, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -2071,7 +2019,7 @@ FFIPrivateKey *extended_private_key_get_private_key(const FFIExtendedPrivateKey - `error` must be a valid pointer to an FFIError - The returned string must be freed with `string_free` */ - char *private_key_to_wif(const FFIPrivateKey *key, FFINetworks network, FFIError *error) ; + char *private_key_to_wif(const FFIPrivateKey *key, FFINetwork network, FFIError *error) ; /* Derive public key at a specific path @@ -2086,7 +2034,7 @@ FFIPrivateKey *extended_private_key_get_private_key(const FFIExtendedPrivateKey */ FFIPublicKey *wallet_derive_public_key(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -2104,7 +2052,7 @@ FFIPublicKey *wallet_derive_public_key(const FFIWallet *wallet, */ FFIExtendedPublicKey *wallet_derive_extended_public_key(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -2121,7 +2069,7 @@ FFIExtendedPublicKey *wallet_derive_extended_public_key(const FFIWallet *wallet, */ char *wallet_derive_public_key_as_hex(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const char *derivation_path, FFIError *error) ; @@ -2160,7 +2108,7 @@ char *wallet_derive_public_key_as_hex(const FFIWallet *wallet, */ char *extended_public_key_to_string(const FFIExtendedPublicKey *key, - FFINetworks network, + FFINetwork network, FFIError *error) ; @@ -2815,7 +2763,7 @@ void managed_account_collection_summary_free(FFIManagedAccountCollectionSummary char *managed_wallet_get_next_bip44_receive_address(FFIManagedWalletInfo *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIError *error) ; @@ -2836,7 +2784,7 @@ char *managed_wallet_get_next_bip44_receive_address(FFIManagedWalletInfo *manage char *managed_wallet_get_next_bip44_change_address(FFIManagedWalletInfo *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIError *error) ; @@ -2859,7 +2807,7 @@ char *managed_wallet_get_next_bip44_change_address(FFIManagedWalletInfo *managed bool managed_wallet_get_bip_44_external_address_range(FFIManagedWalletInfo *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, unsigned int start_index, unsigned int end_index, @@ -2886,7 +2834,7 @@ bool managed_wallet_get_bip_44_external_address_range(FFIManagedWalletInfo *mana bool managed_wallet_get_bip_44_internal_address_range(FFIManagedWalletInfo *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, unsigned int start_index, unsigned int end_index, @@ -3001,68 +2949,6 @@ bool mnemonic_to_seed(const char *mnemonic, */ void mnemonic_free(char *mnemonic) ; -/* - Generate a provider key at a specific index - - This generates a provider key (BLS or EdDSA) at the specified index. - For voting, owner, and operator keys, this generates BLS keys. - For platform keys, this generates EdDSA keys. - - # Safety - - - `wallet` must be a valid pointer to an FFIWallet - - `info_out` must be a valid pointer to store the key info - - `error` must be a valid pointer to an FFIError or null - - The returned public_key, private_key, and derivation_path must be freed by the caller - */ - -bool wallet_generate_provider_key(const FFIWallet *wallet, - FFINetworks network, - FFIProviderKeyType key_type, - unsigned int key_index, - bool include_private, - FFIProviderKeyInfo *info_out, - FFIError *error) -; - -/* - Free provider key info - - # Safety - - - `info` must be a valid pointer to an FFIProviderKeyInfo - - This function must only be called once per info structure - */ - void provider_key_info_free(FFIProviderKeyInfo *info) ; - -/* - Sign data with a provider key - - This signs arbitrary data with the provider key at the specified index. - For BLS keys, this produces a BLS signature. - For EdDSA keys, this produces an Ed25519 signature. - - # Safety - - - `wallet` must be a valid pointer to an FFIWallet - - `data` must be a valid pointer to data with at least `data_len` bytes - - `signature_out` must be a valid pointer to store the signature pointer - - `signature_len_out` must be a valid pointer to store the signature length - - `error` must be a valid pointer to an FFIError or null - - The returned signature must be freed with `libc::free` - */ - -bool wallet_sign_with_provider_key(const FFIWallet *wallet, - FFINetworks network, - FFIProviderKeyType key_type, - unsigned int _key_index, - const uint8_t *data, - size_t data_len, - uint8_t **signature_out, - size_t *signature_len_out, - FFIError *error) -; - /* Build a transaction @@ -3125,7 +3011,7 @@ bool wallet_sign_transaction(const FFIWallet *wallet, */ bool wallet_check_transaction(FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const uint8_t *tx_bytes, size_t tx_len, FFITransactionContext context_type, @@ -3332,7 +3218,7 @@ int32_t transaction_sign_input(FFITransaction *tx, bool managed_wallet_check_transaction(FFIManagedWallet *managed_wallet, const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, const uint8_t *tx_bytes, size_t tx_len, FFITransactionContext context_type, @@ -3581,7 +3467,7 @@ FFIWallet *wallet_create_random_with_options(FFINetworks networks, */ char *wallet_get_xpub(const FFIWallet *wallet, - FFINetworks network, + FFINetwork network, unsigned int account_index, FFIError *error) ; @@ -3625,7 +3511,7 @@ char *wallet_get_xpub(const FFIWallet *wallet, */ FFIAccountResult wallet_add_account(FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index) ; @@ -3643,7 +3529,7 @@ FFIAccountResult wallet_add_account(FFIWallet *wallet, */ FFIAccountResult wallet_add_account_with_xpub_bytes(FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index, const uint8_t *xpub_bytes, @@ -3663,7 +3549,7 @@ FFIAccountResult wallet_add_account_with_xpub_bytes(FFIWallet *wallet, */ FFIAccountResult wallet_add_account_with_string_xpub(FFIWallet *wallet, - FFINetworks network, + FFINetwork network, FFIAccountType account_type, unsigned int account_index, const char *xpub_string) @@ -3845,42 +3731,6 @@ FFIManagedWalletInfo *wallet_manager_get_managed_wallet_info(const FFIWalletMana FFIError *error) ; -/* - Get next receive address for a wallet - - # Safety - - - `manager` must be a valid pointer to an FFIWalletManager - - `wallet_id` must be a valid pointer to a 32-byte wallet ID - - `error` must be a valid pointer to an FFIError structure or null - - The caller must ensure all pointers remain valid for the duration of this call - */ - -char *wallet_manager_get_receive_address(FFIWalletManager *manager, - const uint8_t *wallet_id, - FFINetworks network, - unsigned int account_index, - FFIError *error) -; - -/* - Get next change address for a wallet - - # Safety - - - `manager` must be a valid pointer to an FFIWalletManager - - `wallet_id` must be a valid pointer to a 32-byte wallet ID - - `error` must be a valid pointer to an FFIError structure or null - - The caller must ensure all pointers remain valid for the duration of this call - */ - -char *wallet_manager_get_change_address(FFIWalletManager *manager, - const uint8_t *wallet_id, - FFINetworks network, - unsigned int account_index, - FFIError *error) -; - /* Get wallet balance @@ -3924,31 +3774,12 @@ bool wallet_manager_get_wallet_balance(const FFIWalletManager *manager, bool wallet_manager_process_transaction(FFIWalletManager *manager, const uint8_t *tx_bytes, size_t tx_len, - FFINetworks network, + FFINetwork network, const FFITransactionContextDetails *context, bool update_state_if_found, FFIError *error) ; -/* - Get monitored addresses for a network - - # Safety - - - `manager` must be a valid pointer to an FFIWalletManager - - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array - - `count_out` must be a valid pointer to receive the count - - `error` must be a valid pointer to an FFIError structure or null - - The caller must ensure all pointers remain valid for the duration of this call - */ - -bool wallet_manager_get_monitored_addresses(const FFIWalletManager *manager, - FFINetworks network, - char ***addresses_out, - size_t *count_out, - FFIError *error) -; - /* Update block height for a network @@ -3960,7 +3791,7 @@ bool wallet_manager_get_monitored_addresses(const FFIWalletManager *manager, */ bool wallet_manager_update_height(FFIWalletManager *manager, - FFINetworks network, + FFINetwork network, unsigned int height, FFIError *error) ; @@ -3976,7 +3807,7 @@ bool wallet_manager_update_height(FFIWalletManager *manager, */ unsigned int wallet_manager_current_height(const FFIWalletManager *manager, - FFINetworks network, + FFINetwork network, FFIError *error) ; diff --git a/key-wallet-ffi/src/address_pool.rs b/key-wallet-ffi/src/address_pool.rs index 3bdfa095c..9ce6fa8a7 100644 --- a/key-wallet-ffi/src/address_pool.rs +++ b/key-wallet-ffi/src/address_pool.rs @@ -8,8 +8,9 @@ use std::os::raw::{c_char, c_uint}; use crate::error::{FFIError, FFIErrorCode}; use crate::transaction_checking::FFIManagedWallet; -use crate::types::{FFIAccountType, FFINetworks, FFIWallet}; +use crate::types::{FFIAccountType, FFIWallet}; use crate::utils::rust_string_to_c; +use crate::FFINetwork; use key_wallet::account::ManagedAccountCollection; use key_wallet::managed_account::address_pool::{ AddressInfo, AddressPool, KeySource, PublicKeyType, @@ -246,7 +247,7 @@ pub struct FFIAddressPoolInfo { #[no_mangle] pub unsafe extern "C" fn managed_wallet_get_address_pool_info( managed_wallet: *const FFIManagedWallet, - network: FFINetworks, + network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, @@ -259,17 +260,7 @@ pub unsafe extern "C" fn managed_wallet_get_address_pool_info( } let managed_wallet = &*(*managed_wallet).inner; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); let account_type_rust = account_type.to_account_type(account_index); @@ -376,7 +367,7 @@ pub unsafe extern "C" fn managed_wallet_get_address_pool_info( #[no_mangle] pub unsafe extern "C" fn managed_wallet_set_gap_limit( managed_wallet: *mut FFIManagedWallet, - network: FFINetworks, + network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, @@ -389,17 +380,7 @@ pub unsafe extern "C" fn managed_wallet_set_gap_limit( } let managed_wallet = &mut *(*managed_wallet).inner; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); let account_type_rust = account_type.to_account_type(account_index); @@ -496,7 +477,7 @@ pub unsafe extern "C" fn managed_wallet_set_gap_limit( pub unsafe extern "C" fn managed_wallet_generate_addresses_to_index( managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, @@ -510,17 +491,7 @@ pub unsafe extern "C" fn managed_wallet_generate_addresses_to_index( let managed_wallet = &mut *(*managed_wallet).inner; let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); let account_type_rust = account_type.to_account_type(account_index); @@ -671,7 +642,7 @@ pub unsafe extern "C" fn managed_wallet_generate_addresses_to_index( #[no_mangle] pub unsafe extern "C" fn managed_wallet_mark_address_used( managed_wallet: *mut FFIManagedWallet, - network: FFINetworks, + network: FFINetwork, address: *const c_char, error: *mut FFIError, ) -> bool { @@ -681,17 +652,7 @@ pub unsafe extern "C" fn managed_wallet_mark_address_used( } let managed_wallet = &mut *(*managed_wallet).inner; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); // Parse the address string let address_str = match std::ffi::CStr::from_ptr(address).to_str() { @@ -1001,7 +962,7 @@ pub unsafe extern "C" fn address_info_array_free(infos: *mut *mut FFIAddressInfo #[cfg(test)] mod tests { use super::*; - use crate::FFINetwork; + use crate::{FFINetwork, FFINetworks}; #[test] fn test_address_pool_type_values() { diff --git a/key-wallet-ffi/src/keys.rs b/key-wallet-ffi/src/keys.rs index a5275e84e..5efe97a3e 100644 --- a/key-wallet-ffi/src/keys.rs +++ b/key-wallet-ffi/src/keys.rs @@ -1,7 +1,7 @@ //! Key derivation and management use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; +use crate::types::{FFINetwork, FFIWallet}; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_uint}; use std::ptr; @@ -36,7 +36,7 @@ pub struct FFIExtendedPublicKey { #[no_mangle] pub unsafe extern "C" fn wallet_get_account_xpriv( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: c_uint, error: *mut FFIError, ) -> *mut c_char { @@ -46,17 +46,7 @@ pub unsafe extern "C" fn wallet_get_account_xpriv( } let wallet = unsafe { &*wallet }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); match wallet.inner().get_bip44_account(network_rust, account_index) { Some(account) => { @@ -96,7 +86,7 @@ pub unsafe extern "C" fn wallet_get_account_xpriv( #[no_mangle] pub unsafe extern "C" fn wallet_get_account_xpub( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: c_uint, error: *mut FFIError, ) -> *mut c_char { @@ -106,17 +96,7 @@ pub unsafe extern "C" fn wallet_get_account_xpub( } let wallet = unsafe { &*wallet }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); match wallet.inner().get_bip44_account(network_rust, account_index) { Some(account) => { @@ -153,7 +133,7 @@ pub unsafe extern "C" fn wallet_get_account_xpub( #[no_mangle] pub unsafe extern "C" fn wallet_derive_private_key( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut FFIPrivateKey { @@ -190,17 +170,7 @@ pub unsafe extern "C" fn wallet_derive_private_key( }; let wallet = unsafe { &*wallet }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the private key match wallet.inner().derive_private_key(network_rust, &path) { @@ -233,7 +203,7 @@ pub unsafe extern "C" fn wallet_derive_private_key( #[no_mangle] pub unsafe extern "C" fn wallet_derive_extended_private_key( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut FFIExtendedPrivateKey { @@ -270,17 +240,7 @@ pub unsafe extern "C" fn wallet_derive_extended_private_key( }; let wallet = unsafe { &*wallet }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the extended private key match wallet.inner().derive_extended_private_key(network_rust, &path) { @@ -312,7 +272,7 @@ pub unsafe extern "C" fn wallet_derive_extended_private_key( #[no_mangle] pub unsafe extern "C" fn wallet_derive_private_key_as_wif( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut c_char { @@ -349,17 +309,7 @@ pub unsafe extern "C" fn wallet_derive_private_key_as_wif( }; let wallet = unsafe { &*wallet }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the private key as WIF match wallet.inner().derive_private_key_as_wif(network_rust, &path) { @@ -427,7 +377,7 @@ pub unsafe extern "C" fn extended_private_key_free(key: *mut FFIExtendedPrivateK #[no_mangle] pub unsafe extern "C" fn extended_private_key_to_string( key: *const FFIExtendedPrivateKey, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut c_char { if key.is_null() { @@ -503,7 +453,7 @@ pub unsafe extern "C" fn extended_private_key_get_private_key( #[no_mangle] pub unsafe extern "C" fn private_key_to_wif( key: *const FFIPrivateKey, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut c_char { if key.is_null() { @@ -512,17 +462,7 @@ pub unsafe extern "C" fn private_key_to_wif( } let key = unsafe { &*key }; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Convert to WIF format use dashcore::PrivateKey as DashPrivateKey; @@ -559,7 +499,7 @@ pub unsafe extern "C" fn private_key_to_wif( #[no_mangle] pub unsafe extern "C" fn wallet_derive_public_key( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut FFIPublicKey { @@ -597,17 +537,7 @@ pub unsafe extern "C" fn wallet_derive_public_key( unsafe { let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the public key match wallet.inner().derive_public_key(network_rust, &path) { @@ -641,7 +571,7 @@ pub unsafe extern "C" fn wallet_derive_public_key( #[no_mangle] pub unsafe extern "C" fn wallet_derive_extended_public_key( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut FFIExtendedPublicKey { @@ -679,17 +609,7 @@ pub unsafe extern "C" fn wallet_derive_extended_public_key( unsafe { let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the extended public key match wallet.inner().derive_extended_public_key(network_rust, &path) { @@ -722,7 +642,7 @@ pub unsafe extern "C" fn wallet_derive_extended_public_key( #[no_mangle] pub unsafe extern "C" fn wallet_derive_public_key_as_hex( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError, ) -> *mut c_char { @@ -760,17 +680,7 @@ pub unsafe extern "C" fn wallet_derive_public_key_as_hex( unsafe { let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network_rust: key_wallet::Network = network.into(); // Use the new wallet method to derive the public key as hex match wallet.inner().derive_public_key_as_hex(network_rust, &path) { @@ -843,7 +753,7 @@ pub unsafe extern "C" fn extended_public_key_free(key: *mut FFIExtendedPublicKey #[no_mangle] pub unsafe extern "C" fn extended_public_key_to_string( key: *const FFIExtendedPublicKey, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> *mut c_char { if key.is_null() { diff --git a/key-wallet-ffi/src/keys_tests.rs b/key-wallet-ffi/src/keys_tests.rs index 2a1aa8034..3c16f8dc7 100644 --- a/key-wallet-ffi/src/keys_tests.rs +++ b/key-wallet-ffi/src/keys_tests.rs @@ -31,7 +31,7 @@ mod tests { let path = CString::new("m/44'/1'/0'").unwrap(); let ext_priv = wallet_derive_extended_private_key( wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, path.as_ptr(), &mut error, ); @@ -40,7 +40,7 @@ mod tests { // Test extended_private_key_to_string let xprv_str = - extended_private_key_to_string(ext_priv, FFINetworks::TestnetFlag, &mut error); + extended_private_key_to_string(ext_priv, FFINetwork::Testnet, &mut error); assert!(!xprv_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -54,7 +54,7 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Get WIF from the extracted private key - let wif = private_key_to_wif(priv_key, FFINetworks::TestnetFlag, &mut error); + let wif = private_key_to_wif(priv_key, FFINetwork::Testnet, &mut error); assert!(!wif.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -70,7 +70,7 @@ mod tests { // Now test extended public key let ext_pub = wallet_derive_extended_public_key( wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, path.as_ptr(), &mut error, ); @@ -78,8 +78,7 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Test extended_public_key_to_string - let xpub_str = - extended_public_key_to_string(ext_pub, FFINetworks::TestnetFlag, &mut error); + let xpub_str = extended_public_key_to_string(ext_pub, FFINetwork::Testnet, &mut error); assert!(!xpub_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -130,7 +129,7 @@ mod tests { // Try to get account xpriv - should fail let xpriv_str = - unsafe { wallet_get_account_xpriv(wallet, FFINetworks::TestnetFlag, 0, &mut error) }; + unsafe { wallet_get_account_xpriv(wallet, FFINetwork::Testnet, 0, &mut error) }; // Should return null (not implemented for security) assert!(xpriv_str.is_null()); @@ -162,7 +161,7 @@ mod tests { // Get account xpub let xpub_str = - unsafe { wallet_get_account_xpub(wallet, FFINetworks::TestnetFlag, 0, &mut error) }; + unsafe { wallet_get_account_xpub(wallet, FFINetwork::Testnet, 0, &mut error) }; assert!(!xpub_str.is_null()); @@ -198,7 +197,7 @@ mod tests { // Try to derive private key - should now succeed (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let privkey_ptr = unsafe { - wallet_derive_private_key(wallet, FFINetworks::TestnetFlag, path.as_ptr(), &mut error) + wallet_derive_private_key(wallet, FFINetwork::Testnet, path.as_ptr(), &mut error) }; // Should succeed and return a valid pointer @@ -206,8 +205,7 @@ mod tests { assert_eq!(error.code, FFIErrorCode::Success); // Convert to WIF to verify it's valid - let wif_str = - unsafe { private_key_to_wif(privkey_ptr, FFINetworks::TestnetFlag, &mut error) }; + let wif_str = unsafe { private_key_to_wif(privkey_ptr, FFINetwork::Testnet, &mut error) }; assert!(!wif_str.is_null()); assert_eq!(error.code, FFIErrorCode::Success); @@ -253,7 +251,7 @@ mod tests { // Derive public key using derivation path (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key(wallet, FFINetworks::TestnetFlag, path.as_ptr(), &mut error) + wallet_derive_public_key(wallet, FFINetwork::Testnet, path.as_ptr(), &mut error) }; if pubkey_ptr.is_null() { @@ -307,7 +305,7 @@ mod tests { let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let hex_str = wallet_derive_public_key_as_hex( wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, path.as_ptr(), &mut error, ); @@ -407,9 +405,8 @@ mod tests { let mut error = FFIError::success(); // Test with null wallet - let xpriv = unsafe { - wallet_get_account_xpriv(ptr::null(), FFINetworks::TestnetFlag, 0, &mut error) - }; + let xpriv = + unsafe { wallet_get_account_xpriv(ptr::null(), FFINetwork::Testnet, 0, &mut error) }; assert!(xpriv.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -439,12 +436,7 @@ mod tests { // Test with null wallet (44'/1'/0'/0/0 for Dash) let path = CString::new("m/44'/1'/0'/0/0").unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key( - ptr::null(), - FFINetworks::TestnetFlag, - path.as_ptr(), - &mut error, - ) + wallet_derive_public_key(ptr::null(), FFINetwork::Testnet, path.as_ptr(), &mut error) }; assert!(pubkey_ptr.is_null()); @@ -465,7 +457,7 @@ mod tests { // Test with null path let pubkey_ptr = unsafe { - wallet_derive_public_key(wallet, FFINetworks::TestnetFlag, ptr::null(), &mut error) + wallet_derive_public_key(wallet, FFINetwork::Testnet, ptr::null(), &mut error) }; assert!(pubkey_ptr.is_null()); @@ -603,7 +595,7 @@ mod tests { // Test different account indices for account_index in 0..3 { let xpub_str = unsafe { - wallet_get_account_xpub(wallet, FFINetworks::TestnetFlag, account_index, &mut error) + wallet_get_account_xpub(wallet, FFINetwork::Testnet, account_index, &mut error) }; if !xpub_str.is_null() { @@ -618,9 +610,8 @@ mod tests { } // Test with null wallet - let xpub_str = unsafe { - wallet_get_account_xpub(ptr::null(), FFINetworks::TestnetFlag, 0, &mut error) - }; + let xpub_str = + unsafe { wallet_get_account_xpub(ptr::null(), FFINetwork::Testnet, 0, &mut error) }; assert!(xpub_str.is_null()); assert_eq!(error.code, FFIErrorCode::InvalidInput); @@ -660,12 +651,7 @@ mod tests { let path = CString::new(*path_str).unwrap(); let pubkey_ptr = unsafe { - wallet_derive_public_key( - wallet, - FFINetworks::TestnetFlag, - path.as_ptr(), - &mut error, - ) + wallet_derive_public_key(wallet, FFINetwork::Testnet, path.as_ptr(), &mut error) }; if !pubkey_ptr.is_null() { diff --git a/key-wallet-ffi/src/lib.rs b/key-wallet-ffi/src/lib.rs index f6accf7e5..6338155e9 100644 --- a/key-wallet-ffi/src/lib.rs +++ b/key-wallet-ffi/src/lib.rs @@ -15,7 +15,6 @@ pub mod managed_account; pub mod managed_account_collection; pub mod managed_wallet; pub mod mnemonic; -pub mod provider_keys; pub mod transaction; pub mod transaction_checking; pub mod types; diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index 0a16872c9..2768a65ee 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -754,6 +754,7 @@ mod tests { wallet_manager_add_wallet_from_mnemonic_with_options, wallet_manager_create, wallet_manager_free, wallet_manager_free_wallet_ids, wallet_manager_get_wallet_ids, }; + use crate::FFINetworks; use std::ffi::CString; use std::ptr; diff --git a/key-wallet-ffi/src/managed_wallet.rs b/key-wallet-ffi/src/managed_wallet.rs index 672c4cb3a..c2af70287 100644 --- a/key-wallet-ffi/src/managed_wallet.rs +++ b/key-wallet-ffi/src/managed_wallet.rs @@ -9,7 +9,8 @@ use std::os::raw::c_char; use std::ptr; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; +use crate::types::FFIWallet; +use crate::FFINetwork; use key_wallet::managed_account::address_pool::KeySource; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; @@ -50,7 +51,7 @@ impl FFIManagedWalletInfo { pub unsafe extern "C" fn managed_wallet_get_next_bip44_receive_address( managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: std::os::raw::c_uint, error: *mut FFIError, ) -> *mut c_char { @@ -70,17 +71,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_receive_address( let managed_wallet = unsafe { &mut *managed_wallet }; let wallet = unsafe { &*wallet }; - let network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network = network.into(); // Get the account collection for the network let account_collection = match managed_wallet.inner.accounts.get_mut(&network) { @@ -184,7 +175,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_receive_address( pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address( managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: std::os::raw::c_uint, error: *mut FFIError, ) -> *mut c_char { @@ -204,17 +195,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address( let managed_wallet = unsafe { &mut *managed_wallet }; let wallet = unsafe { &*wallet }; - let network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; + let network = network.into(); // Get the account collection for the network let account_collection = match managed_wallet.inner.accounts.get_mut(&network) { @@ -320,7 +301,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address( pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range( managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, @@ -357,19 +338,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range( let managed_wallet = unsafe { &mut *managed_wallet }; let wallet = unsafe { &*wallet }; - let network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - *count_out = 0; - *addresses_out = ptr::null_mut(); - return false; - } - }; + let network = network.into(); // Get the account collection for the network let account_collection = match managed_wallet.inner.accounts.get_mut(&network) { @@ -518,7 +487,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range( pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range( managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, @@ -555,19 +524,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range( let managed_wallet = unsafe { &mut *managed_wallet }; let wallet = unsafe { &*wallet }; - let network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - *count_out = 0; - *addresses_out = ptr::null_mut(); - return false; - } - }; + let network = network.into(); // Get the account collection for the network let account_collection = match managed_wallet.inner.accounts.get_mut(&network) { @@ -818,7 +775,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -837,7 +794,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -850,7 +807,7 @@ mod tests { #[test] fn test_managed_wallet_get_bip_44_external_address_range_null_pointers() { let mut error = FFIError::success(); - let mut addresses_out: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let mut addresses_out: *mut *mut c_char = ptr::null_mut(); let mut count_out: usize = 0; // Test with null managed wallet @@ -858,7 +815,7 @@ mod tests { managed_wallet_get_bip_44_external_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 10, @@ -877,7 +834,7 @@ mod tests { #[test] fn test_managed_wallet_get_bip_44_internal_address_range_null_pointers() { let mut error = FFIError::success(); - let mut addresses_out: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let mut addresses_out: *mut *mut c_char = ptr::null_mut(); let mut count_out: usize = 0; // Test with null managed wallet @@ -885,7 +842,7 @@ mod tests { managed_wallet_get_bip_44_internal_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 10, @@ -930,7 +887,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( &mut ffi_managed, wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -957,7 +914,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( &mut ffi_managed, wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -1063,7 +1020,7 @@ mod tests { managed_wallet_get_next_bip44_receive_address( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -1082,7 +1039,7 @@ mod tests { managed_wallet_get_next_bip44_change_address( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, &mut error, ) @@ -1097,14 +1054,14 @@ mod tests { } // Test 3: Get external address range - let mut addresses_out: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let mut addresses_out: *mut *mut c_char = ptr::null_mut(); let mut count_out: usize = 0; let success = unsafe { managed_wallet_get_bip_44_external_address_range( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 5, @@ -1131,14 +1088,14 @@ mod tests { } // Test 4: Get internal address range - let mut addresses_out: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let mut addresses_out: *mut *mut c_char = ptr::null_mut(); let mut count_out: usize = 0; let success = unsafe { managed_wallet_get_bip_44_internal_address_range( &mut ffi_managed, ffi_wallet_ptr, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 3, @@ -1275,7 +1232,7 @@ mod tests { managed_wallet_get_bip_44_external_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 10, @@ -1289,12 +1246,12 @@ mod tests { assert_eq!(error.code, FFIErrorCode::InvalidInput); // Test with null count_out for internal range - let mut addresses_out: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let mut addresses_out: *mut *mut c_char = ptr::null_mut(); let success = unsafe { managed_wallet_get_bip_44_internal_address_range( ptr::null_mut(), ptr::null(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 0, 0, 10, diff --git a/key-wallet-ffi/src/provider_keys.rs b/key-wallet-ffi/src/provider_keys.rs deleted file mode 100644 index 81654a1de..000000000 --- a/key-wallet-ffi/src/provider_keys.rs +++ /dev/null @@ -1,384 +0,0 @@ -//! Provider keys FFI bindings -//! -//! This module provides FFI bindings for provider (masternode) keys, -//! including BLS keys for voting/owner/operator roles and EdDSA keys -//! for platform operations. - -use std::ffi::CString; -use std::os::raw::{c_char, c_uint}; -use std::ptr; -use std::slice; - -use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFIWallet}; -use key_wallet::AccountType; - -/// Provider key type -#[repr(C)] -pub enum FFIProviderKeyType { - /// BLS voting keys (m/9'/5'/3'/1'/[key_index]) - VotingKeys = 0, - /// BLS owner keys (m/9'/5'/3'/2'/[key_index]) - OwnerKeys = 1, - /// BLS operator keys (m/9'/5'/3'/3'/[key_index]) - OperatorKeys = 2, - /// EdDSA platform P2P keys (m/9'/5'/3'/4'/[key_index]) - PlatformKeys = 3, -} - -/// Provider key info -#[repr(C)] -pub struct FFIProviderKeyInfo { - /// Key index - pub key_index: c_uint, - /// Public key bytes (48 bytes for BLS, 32 bytes for EdDSA) - pub public_key: *mut u8, - /// Public key length - pub public_key_len: usize, - /// Private key bytes (32 bytes, only if available) - pub private_key: *mut u8, - /// Private key length (0 if not available) - pub private_key_len: usize, - /// Derivation path as string - pub derivation_path: *mut c_char, -} - -/// Generate a provider key at a specific index -/// -/// This generates a provider key (BLS or EdDSA) at the specified index. -/// For voting, owner, and operator keys, this generates BLS keys. -/// For platform keys, this generates EdDSA keys. -/// -/// # Safety -/// -/// - `wallet` must be a valid pointer to an FFIWallet -/// - `info_out` must be a valid pointer to store the key info -/// - `error` must be a valid pointer to an FFIError or null -/// - The returned public_key, private_key, and derivation_path must be freed by the caller -#[no_mangle] -pub unsafe extern "C" fn wallet_generate_provider_key( - wallet: *const FFIWallet, - network: FFINetworks, - key_type: FFIProviderKeyType, - key_index: c_uint, - include_private: bool, - info_out: *mut FFIProviderKeyInfo, - error: *mut FFIError, -) -> bool { - if wallet.is_null() || info_out.is_null() { - FFIError::set_error(error, FFIErrorCode::InvalidInput, "Null pointer provided".to_string()); - return false; - } - - let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; - - // Determine the account type based on key type - let account_type = match key_type { - FFIProviderKeyType::VotingKeys => AccountType::ProviderVotingKeys, - FFIProviderKeyType::OwnerKeys => AccountType::ProviderOwnerKeys, - FFIProviderKeyType::OperatorKeys => AccountType::ProviderOperatorKeys, - FFIProviderKeyType::PlatformKeys => AccountType::ProviderPlatformKeys, - }; - - // Get the account - let accounts = match wallet.inner().accounts.get(&network_rust) { - Some(accounts) => accounts, - None => { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - "No accounts for network".to_string(), - ); - return false; - } - }; - - let account = match &account_type { - AccountType::ProviderVotingKeys => accounts.provider_voting_keys.as_ref(), - AccountType::ProviderOwnerKeys => accounts.provider_owner_keys.as_ref(), - AccountType::ProviderOperatorKeys => None, // BLSAccount not yet supported - AccountType::ProviderPlatformKeys => None, // EdDSAAccount not yet supported - _ => None, - }; - - let _account = match account { - Some(acc) => acc, - None => { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - format!("Provider account type {:?} not found", account_type), - ); - return false; - } - }; - - // Generate the key at the specified index - // TODO: Get proper derivation path when available - use key_wallet::DerivationPath; - let derivation_path = DerivationPath::default(); - - // For now, return placeholder data until BLS/EdDSA key generation is implemented - // TODO: Implement actual BLS/EdDSA key generation when available in Account - let (public_key_bytes, private_key_bytes) = match key_type { - FFIProviderKeyType::VotingKeys - | FFIProviderKeyType::OwnerKeys - | FFIProviderKeyType::OperatorKeys => { - // BLS keys - placeholder - let pub_bytes = vec![0u8; 48]; // BLS public key is 48 bytes - let priv_bytes = if include_private { - Some(vec![0u8; 32]) // BLS private key is 32 bytes - } else { - None - }; - (pub_bytes, priv_bytes) - } - FFIProviderKeyType::PlatformKeys => { - // EdDSA keys - placeholder - let pub_bytes = vec![0u8; 32]; // Ed25519 public key is 32 bytes - let priv_bytes = if include_private { - Some(vec![0u8; 32]) // Ed25519 private key is 32 bytes - } else { - None - }; - (pub_bytes, priv_bytes) - } - }; - - // Allocate and copy public key - let pub_key_len = public_key_bytes.len(); - let pub_key_ptr = libc::malloc(pub_key_len) as *mut u8; - if pub_key_ptr.is_null() { - FFIError::set_error( - error, - FFIErrorCode::InternalError, - "Failed to allocate memory for public key".to_string(), - ); - return false; - } - ptr::copy_nonoverlapping(public_key_bytes.as_ptr(), pub_key_ptr, pub_key_len); - - // Allocate and copy private key if available - let (priv_key_ptr, priv_key_len) = if let Some(priv_bytes) = private_key_bytes { - let len = priv_bytes.len(); - let ptr = libc::malloc(len) as *mut u8; - if ptr.is_null() { - libc::free(pub_key_ptr as *mut libc::c_void); - FFIError::set_error( - error, - FFIErrorCode::InternalError, - "Failed to allocate memory for private key".to_string(), - ); - return false; - } - ptr::copy_nonoverlapping(priv_bytes.as_ptr(), ptr, len); - (ptr, len) - } else { - (ptr::null_mut(), 0) - }; - - // Create derivation path string - let path_str = format!("{}", derivation_path); - let path_cstring = match CString::new(path_str) { - Ok(s) => s, - Err(_) => { - libc::free(pub_key_ptr as *mut libc::c_void); - if !priv_key_ptr.is_null() { - libc::free(priv_key_ptr as *mut libc::c_void); - } - FFIError::set_error( - error, - FFIErrorCode::InternalError, - "Failed to create derivation path string".to_string(), - ); - return false; - } - }; - - // Fill the output structure - *info_out = FFIProviderKeyInfo { - key_index, - public_key: pub_key_ptr, - public_key_len: pub_key_len, - private_key: priv_key_ptr, - private_key_len: priv_key_len, - derivation_path: path_cstring.into_raw(), - }; - - FFIError::set_success(error); - true -} - -/// Free provider key info -/// -/// # Safety -/// -/// - `info` must be a valid pointer to an FFIProviderKeyInfo -/// - This function must only be called once per info structure -#[no_mangle] -pub unsafe extern "C" fn provider_key_info_free(info: *mut FFIProviderKeyInfo) { - if !info.is_null() { - let info = &mut *info; - - if !info.public_key.is_null() { - libc::free(info.public_key as *mut libc::c_void); - info.public_key = ptr::null_mut(); - } - - if !info.private_key.is_null() { - libc::free(info.private_key as *mut libc::c_void); - info.private_key = ptr::null_mut(); - } - - if !info.derivation_path.is_null() { - let _ = CString::from_raw(info.derivation_path); - info.derivation_path = ptr::null_mut(); - } - } -} - -/// Sign data with a provider key -/// -/// This signs arbitrary data with the provider key at the specified index. -/// For BLS keys, this produces a BLS signature. -/// For EdDSA keys, this produces an Ed25519 signature. -/// -/// # Safety -/// -/// - `wallet` must be a valid pointer to an FFIWallet -/// - `data` must be a valid pointer to data with at least `data_len` bytes -/// - `signature_out` must be a valid pointer to store the signature pointer -/// - `signature_len_out` must be a valid pointer to store the signature length -/// - `error` must be a valid pointer to an FFIError or null -/// - The returned signature must be freed with `libc::free` -#[no_mangle] -pub unsafe extern "C" fn wallet_sign_with_provider_key( - wallet: *const FFIWallet, - network: FFINetworks, - key_type: FFIProviderKeyType, - _key_index: c_uint, - data: *const u8, - data_len: usize, - signature_out: *mut *mut u8, - signature_len_out: *mut usize, - error: *mut FFIError, -) -> bool { - if wallet.is_null() || data.is_null() || signature_out.is_null() || signature_len_out.is_null() - { - FFIError::set_error(error, FFIErrorCode::InvalidInput, "Null pointer provided".to_string()); - return false; - } - - let wallet = &*wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; - let _data_slice = slice::from_raw_parts(data, data_len); - - // Determine the account type based on key type - let account_type = match key_type { - FFIProviderKeyType::VotingKeys => AccountType::ProviderVotingKeys, - FFIProviderKeyType::OwnerKeys => AccountType::ProviderOwnerKeys, - FFIProviderKeyType::OperatorKeys => AccountType::ProviderOperatorKeys, - FFIProviderKeyType::PlatformKeys => AccountType::ProviderPlatformKeys, - }; - - // Get the account - let accounts = match wallet.inner().accounts.get(&network_rust) { - Some(accounts) => accounts, - None => { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - "No accounts for network".to_string(), - ); - return false; - } - }; - - let account = match &account_type { - AccountType::ProviderVotingKeys => accounts.provider_voting_keys.as_ref(), - AccountType::ProviderOwnerKeys => accounts.provider_owner_keys.as_ref(), - AccountType::ProviderOperatorKeys => None, // BLSAccount not yet supported - AccountType::ProviderPlatformKeys => None, // EdDSAAccount not yet supported - _ => None, - }; - - let _account = match account { - Some(acc) => acc, - None => { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - format!("Provider account type {:?} not found", account_type), - ); - return false; - } - }; - - // Sign the data - // TODO: Implement actual signing when BLS/EdDSA signing is available in Account - let signature_bytes = match key_type { - FFIProviderKeyType::VotingKeys - | FFIProviderKeyType::OwnerKeys - | FFIProviderKeyType::OperatorKeys => { - // BLS signature - placeholder - vec![0u8; 96] // BLS signature is 96 bytes - } - FFIProviderKeyType::PlatformKeys => { - // EdDSA signature - placeholder - vec![0u8; 64] // Ed25519 signature is 64 bytes - } - }; - - // Allocate and copy signature - let sig_len = signature_bytes.len(); - let sig_ptr = libc::malloc(sig_len) as *mut u8; - if sig_ptr.is_null() { - FFIError::set_error( - error, - FFIErrorCode::InternalError, - "Failed to allocate memory for signature".to_string(), - ); - return false; - } - ptr::copy_nonoverlapping(signature_bytes.as_ptr(), sig_ptr, sig_len); - - *signature_out = sig_ptr; - *signature_len_out = sig_len; - - FFIError::set_success(error); - true -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_provider_key_type_values() { - assert_eq!(FFIProviderKeyType::VotingKeys as u32, 0); - assert_eq!(FFIProviderKeyType::OwnerKeys as u32, 1); - assert_eq!(FFIProviderKeyType::OperatorKeys as u32, 2); - assert_eq!(FFIProviderKeyType::PlatformKeys as u32, 3); - } -} diff --git a/key-wallet-ffi/src/transaction.rs b/key-wallet-ffi/src/transaction.rs index c0a7aef4a..2b2f57a07 100644 --- a/key-wallet-ffi/src/transaction.rs +++ b/key-wallet-ffi/src/transaction.rs @@ -169,7 +169,7 @@ pub struct FFITransactionCheckResult { #[no_mangle] pub unsafe extern "C" fn wallet_check_transaction( wallet: *mut FFIWallet, - network: FFINetworks, + network: FFINetwork, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, @@ -187,17 +187,7 @@ pub unsafe extern "C" fn wallet_check_transaction( unsafe { let wallet = &mut *wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: Network = network.into(); let tx_slice = slice::from_raw_parts(tx_bytes, tx_len); // Parse the transaction diff --git a/key-wallet-ffi/src/transaction_checking.rs b/key-wallet-ffi/src/transaction_checking.rs index 91f8d4ccd..632084467 100644 --- a/key-wallet-ffi/src/transaction_checking.rs +++ b/key-wallet-ffi/src/transaction_checking.rs @@ -9,7 +9,7 @@ use std::os::raw::{c_char, c_uint}; use std::slice; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFINetworks, FFITransactionContext, FFIWallet}; +use crate::types::{FFINetwork, FFITransactionContext, FFIWallet}; use dashcore::consensus::Decodable; use dashcore::Transaction; use key_wallet::transaction_checking::{ @@ -117,7 +117,7 @@ pub unsafe extern "C" fn wallet_create_managed_wallet( pub unsafe extern "C" fn managed_wallet_check_transaction( managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, @@ -134,17 +134,7 @@ pub unsafe extern "C" fn managed_wallet_check_transaction( } let managed_wallet = &mut *(*managed_wallet).inner; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: key_wallet::Network = network.into(); let tx_slice = slice::from_raw_parts(tx_bytes, tx_len); // Parse the transaction diff --git a/key-wallet-ffi/src/wallet.rs b/key-wallet-ffi/src/wallet.rs index bdad72c6b..59d3f16cb 100644 --- a/key-wallet-ffi/src/wallet.rs +++ b/key-wallet-ffi/src/wallet.rs @@ -14,6 +14,7 @@ use key_wallet::{Mnemonic, Network, Seed, Wallet}; use crate::error::{FFIError, FFIErrorCode}; use crate::types::{FFINetworks, FFIWallet, FFIWalletAccountCreationOptions}; +use crate::FFINetwork; /// Create a new wallet from mnemonic with options /// @@ -318,7 +319,7 @@ pub unsafe extern "C" fn wallet_get_id( let wallet = &*wallet; let wallet_id = wallet.inner().wallet_id; - std::ptr::copy_nonoverlapping(wallet_id.as_ptr(), id_out, 32); + ptr::copy_nonoverlapping(wallet_id.as_ptr(), id_out, 32); FFIError::set_success(error); true } @@ -382,7 +383,7 @@ pub unsafe extern "C" fn wallet_is_watch_only( #[no_mangle] pub unsafe extern "C" fn wallet_get_xpub( wallet: *const FFIWallet, - network: FFINetworks, + network: FFINetwork, account_index: c_uint, error: *mut FFIError, ) -> *mut c_char { @@ -394,19 +395,8 @@ pub unsafe extern "C" fn wallet_get_xpub( unsafe { let wallet = &*wallet; - use std::convert::TryInto; - // Try to convert to a single network - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network for getting xpub".to_string(), - ); - return ptr::null_mut(); - } - }; + // Convert to a single network + let network_rust: Network = network.into(); match wallet.inner().get_bip44_account(network_rust, account_index) { Some(account) => { @@ -482,7 +472,7 @@ pub unsafe extern "C" fn wallet_free_const(wallet: *const FFIWallet) { #[no_mangle] pub unsafe extern "C" fn wallet_add_account( wallet: *mut FFIWallet, - network: FFINetworks, + network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint, ) -> crate::types::FFIAccountResult { @@ -494,15 +484,7 @@ pub unsafe extern "C" fn wallet_add_account( } let wallet = &mut *wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return crate::types::FFIAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: Network = network.into(); let account_type_rust = account_type.to_account_type(account_index); @@ -551,7 +533,7 @@ pub unsafe extern "C" fn wallet_add_account( #[no_mangle] pub unsafe extern "C" fn wallet_add_account_with_xpub_bytes( wallet: *mut FFIWallet, - network: FFINetworks, + network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_bytes: *const u8, @@ -572,15 +554,7 @@ pub unsafe extern "C" fn wallet_add_account_with_xpub_bytes( } let wallet = &mut *wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return crate::types::FFIAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: Network = network.into(); use key_wallet::ExtendedPubKey; @@ -649,7 +623,7 @@ pub unsafe extern "C" fn wallet_add_account_with_xpub_bytes( #[no_mangle] pub unsafe extern "C" fn wallet_add_account_with_string_xpub( wallet: *mut FFIWallet, - network: FFINetworks, + network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_string: *const c_char, @@ -669,15 +643,7 @@ pub unsafe extern "C" fn wallet_add_account_with_string_xpub( } let wallet = &mut *wallet; - let network_rust: key_wallet::Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - return crate::types::FFIAccountResult::error( - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - } - }; + let network_rust: Network = network.into(); use key_wallet::ExtendedPubKey; diff --git a/key-wallet-ffi/src/wallet_manager.rs b/key-wallet-ffi/src/wallet_manager.rs index 7b86c3b32..cefeced72 100644 --- a/key-wallet-ffi/src/wallet_manager.rs +++ b/key-wallet-ffi/src/wallet_manager.rs @@ -16,6 +16,7 @@ use tokio::sync::RwLock; use crate::error::{FFIError, FFIErrorCode}; use crate::types::FFINetworks; +use crate::FFINetwork; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; use key_wallet::Network; use key_wallet_manager::wallet_manager::WalletManager; @@ -602,180 +603,6 @@ pub unsafe extern "C" fn wallet_manager_get_managed_wallet_info( } } -/// Get next receive address for a wallet -/// -/// # Safety -/// -/// - `manager` must be a valid pointer to an FFIWalletManager -/// - `wallet_id` must be a valid pointer to a 32-byte wallet ID -/// - `error` must be a valid pointer to an FFIError structure or null -/// - The caller must ensure all pointers remain valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn wallet_manager_get_receive_address( - manager: *mut FFIWalletManager, - wallet_id: *const u8, - network: FFINetworks, - account_index: c_uint, - error: *mut FFIError, -) -> *mut c_char { - if manager.is_null() || wallet_id.is_null() { - FFIError::set_error(error, FFIErrorCode::InvalidInput, "Null pointer provided".to_string()); - return ptr::null_mut(); - } - - let wallet_id_slice = std::slice::from_raw_parts(wallet_id, 32); - let mut wallet_id_array = [0u8; 32]; - wallet_id_array.copy_from_slice(wallet_id_slice); - - let manager_ref = &*manager; - - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; - - // Use the WalletManager's public method to get next receive address - use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; - let result = manager_ref.runtime.block_on(async { - let mut manager_guard = manager_ref.manager.write().await; - manager_guard.get_receive_address( - &wallet_id_array, - network_rust, - account_index, - AccountTypePreference::BIP44, - true, // mark_as_used - ) - }); - - match result { - Ok(result) => { - if let Some(address) = result.address { - FFIError::set_success(error); - match CString::new(address.to_string()) { - Ok(c_str) => c_str.into_raw(), - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::AllocationFailed, - "Failed to allocate string".to_string(), - ); - ptr::null_mut() - } - } - } else { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - "Failed to generate address".to_string(), - ); - ptr::null_mut() - } - } - Err(e) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - format!("Failed to get receive address: {:?}", e), - ); - ptr::null_mut() - } - } -} - -/// Get next change address for a wallet -/// -/// # Safety -/// -/// - `manager` must be a valid pointer to an FFIWalletManager -/// - `wallet_id` must be a valid pointer to a 32-byte wallet ID -/// - `error` must be a valid pointer to an FFIError structure or null -/// - The caller must ensure all pointers remain valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn wallet_manager_get_change_address( - manager: *mut FFIWalletManager, - wallet_id: *const u8, - network: FFINetworks, - account_index: c_uint, - error: *mut FFIError, -) -> *mut c_char { - if manager.is_null() || wallet_id.is_null() { - FFIError::set_error(error, FFIErrorCode::InvalidInput, "Null pointer provided".to_string()); - return ptr::null_mut(); - } - - let wallet_id_slice = std::slice::from_raw_parts(wallet_id, 32); - let mut wallet_id_array = [0u8; 32]; - wallet_id_array.copy_from_slice(wallet_id_slice); - - let manager_ref = &*manager; - - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return ptr::null_mut(); - } - }; - - // Use the WalletManager's public method to get next change address - use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference; - let result = manager_ref.runtime.block_on(async { - let mut manager_guard = manager_ref.manager.write().await; - manager_guard.get_change_address( - &wallet_id_array, - network_rust, - account_index, - AccountTypePreference::BIP44, - true, // mark_as_used - ) - }); - - match result { - Ok(result) => { - if let Some(address) = result.address { - FFIError::set_success(error); - match CString::new(address.to_string()) { - Ok(c_str) => c_str.into_raw(), - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::AllocationFailed, - "Failed to allocate string".to_string(), - ); - ptr::null_mut() - } - } - } else { - FFIError::set_error( - error, - FFIErrorCode::NotFound, - "Failed to generate address".to_string(), - ); - ptr::null_mut() - } - } - Err(e) => { - FFIError::set_error( - error, - FFIErrorCode::WalletError, - format!("Failed to get change address: {:?}", e), - ); - ptr::null_mut() - } - } -} - /// Get wallet balance /// /// Returns the confirmed and unconfirmed balance for a specific wallet @@ -860,7 +687,7 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, - network: FFINetworks, + network: FFINetwork, context: *const crate::types::FFITransactionContextDetails, update_state_if_found: bool, error: *mut FFIError, @@ -894,17 +721,7 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( }; // Convert FFINetwork to Network - let network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network = network.into(); // Convert FFI context to native TransactionContext let context = unsafe { (*context).to_transaction_context() }; @@ -922,99 +739,6 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( !relevant_wallets.is_empty() } -/// Get monitored addresses for a network -/// -/// # Safety -/// -/// - `manager` must be a valid pointer to an FFIWalletManager -/// - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array -/// - `count_out` must be a valid pointer to receive the count -/// - `error` must be a valid pointer to an FFIError structure or null -/// - The caller must ensure all pointers remain valid for the duration of this call -#[no_mangle] -pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( - manager: *const FFIWalletManager, - network: FFINetworks, - addresses_out: *mut *mut *mut c_char, - count_out: *mut usize, - error: *mut FFIError, -) -> bool { - if manager.is_null() || addresses_out.is_null() || count_out.is_null() { - FFIError::set_error(error, FFIErrorCode::InvalidInput, "Null pointer provided".to_string()); - return false; - } - - let manager_ref = &*manager; - - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; - - // Collect addresses from all wallets for this network - let all_addresses = manager_ref.runtime.block_on(async { - let manager_guard = manager_ref.manager.read().await; - let mut addresses: Vec<*mut c_char> = Vec::new(); - - for wallet in manager_guard.get_all_wallets().values() { - if let Some(account) = wallet.get_bip44_account(network_rust, 0) { - // Generate a few addresses from each wallet (simplified) - use key_wallet::ChildNumber; - use secp256k1::Secp256k1; - let secp = Secp256k1::new(); - - // Generate first 3 receive addresses - for i in 0..3 { - let child_external = match ChildNumber::from_normal_idx(0) { - Ok(c) => c, - Err(_) => continue, - }; - - let child_index = match ChildNumber::from_normal_idx(i) { - Ok(c) => c, - Err(_) => continue, - }; - - if let Ok(derived_key) = - account.account_xpub.derive_pub(&secp, &[child_external, child_index]) - { - let public_key = derived_key.public_key; - let dash_pubkey = dashcore::PublicKey::new(public_key); - let dash_network = network_rust; - let address = key_wallet::Address::p2pkh(&dash_pubkey, dash_network); - - if let Ok(c_str) = CString::new(address.to_string()) { - addresses.push(c_str.into_raw()); - } - } - } - } - } - addresses - }); - - if all_addresses.is_empty() { - *count_out = 0; - *addresses_out = ptr::null_mut(); - } else { - *count_out = all_addresses.len(); - // Convert Vec to boxed slice for consistent memory layout - let boxed = all_addresses.into_boxed_slice(); - let ptr = Box::into_raw(boxed) as *mut *mut c_char; - *addresses_out = ptr; - } - - FFIError::set_success(error); - true -} - /// Update block height for a network /// /// # Safety @@ -1025,7 +749,7 @@ pub unsafe extern "C" fn wallet_manager_get_monitored_addresses( #[no_mangle] pub unsafe extern "C" fn wallet_manager_update_height( manager: *mut FFIWalletManager, - network: FFINetworks, + network: FFINetwork, height: c_uint, error: *mut FFIError, ) -> bool { @@ -1036,17 +760,7 @@ pub unsafe extern "C" fn wallet_manager_update_height( let manager_ref = &*manager; - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return false; - } - }; + let network_rust: Network = network.into(); manager_ref.runtime.block_on(async { let mut manager_guard = manager_ref.manager.write().await; @@ -1067,7 +781,7 @@ pub unsafe extern "C" fn wallet_manager_update_height( #[no_mangle] pub unsafe extern "C" fn wallet_manager_current_height( manager: *const FFIWalletManager, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> c_uint { if manager.is_null() { @@ -1077,17 +791,7 @@ pub unsafe extern "C" fn wallet_manager_current_height( let manager_ref = &*manager; - let network_rust: Network = match network.try_into() { - Ok(n) => n, - Err(_) => { - FFIError::set_error( - error, - FFIErrorCode::InvalidInput, - "Must specify exactly one network".to_string(), - ); - return 0; - } - }; + let network_rust: Network = network.into(); // Get current height from network state if it exists let height = manager_ref.runtime.block_on(async { diff --git a/key-wallet-ffi/src/wallet_manager_tests.rs b/key-wallet-ffi/src/wallet_manager_tests.rs index a9ff8f653..d086f1cd9 100644 --- a/key-wallet-ffi/src/wallet_manager_tests.rs +++ b/key-wallet-ffi/src/wallet_manager_tests.rs @@ -5,9 +5,8 @@ mod tests { use crate::error::{FFIError, FFIErrorCode}; use crate::types::FFINetworks; - use crate::{wallet, wallet_manager}; + use crate::{wallet, wallet_manager, FFINetwork}; use std::ffi::{CStr, CString}; - use std::os::raw::c_char; use std::ptr; use std::slice; @@ -150,146 +149,6 @@ mod tests { } } - #[test] - fn test_get_receive_address() { - let mut error = FFIError::success(); - let error = &mut error as *mut FFIError; - - let manager = wallet_manager::wallet_manager_create(error); - assert!(!manager.is_null()); - - // Add a wallet - let mnemonic = CString::new(TEST_MNEMONIC).unwrap(); - let passphrase = CString::new("").unwrap(); - - let success = unsafe { - wallet_manager::wallet_manager_add_wallet_from_mnemonic( - manager, - mnemonic.as_ptr(), - passphrase.as_ptr(), - FFINetworks::TestnetFlag, - error, - ) - }; - assert!(success); - - // Get wallet ID - let mut wallet_ids: *mut u8 = ptr::null_mut(); - let mut count: usize = 0; - - let success = unsafe { - wallet_manager::wallet_manager_get_wallet_ids( - manager, - &mut wallet_ids, - &mut count, - error, - ) - }; - assert!(success); - assert_eq!(count, 1); - - // Get receive address - let address = unsafe { - wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_ids, - FFINetworks::TestnetFlag, - 0, // account_index - error, - ) - }; - - assert!(!address.is_null(), "Failed to get receive address"); - - let addr_str = unsafe { CStr::from_ptr(address).to_str().unwrap() }; - assert!(!addr_str.is_empty()); - - // Get another address - should be different - let address2 = unsafe { - wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_ids, - FFINetworks::TestnetFlag, - 0, - error, - ) - }; - - if !address2.is_null() { - let addr_str2 = unsafe { CStr::from_ptr(address2).to_str().unwrap() }; - // Addresses should be different (auto-incremented) - assert_ne!(addr_str, addr_str2); - } - - // Clean up - unsafe { - if !address.is_null() { - let _ = CString::from_raw(address); - } - if !address2.is_null() { - let _ = CString::from_raw(address2); - } - wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, count); - wallet_manager::wallet_manager_free(manager); - } - } - - #[test] - fn test_get_change_address() { - let mut error = FFIError::success(); - let error = &mut error as *mut FFIError; - - let manager = wallet_manager::wallet_manager_create(error); - assert!(!manager.is_null()); - - // Add a wallet - let mnemonic = CString::new(TEST_MNEMONIC).unwrap(); - let success = unsafe { - wallet_manager::wallet_manager_add_wallet_from_mnemonic( - manager, - mnemonic.as_ptr(), - ptr::null(), - FFINetworks::TestnetFlag, - error, - ) - }; - assert!(success); - - // Get wallet ID - let mut wallet_ids: *mut u8 = ptr::null_mut(); - let mut count: usize = 0; - - let success = unsafe { - wallet_manager::wallet_manager_get_wallet_ids( - manager, - &mut wallet_ids, - &mut count, - error, - ) - }; - assert!(success); - - // Get change address - let address = unsafe { - wallet_manager::wallet_manager_get_change_address( - manager, - wallet_ids, - FFINetworks::TestnetFlag, - 0, - error, - ) - }; - - assert!(!address.is_null(), "Failed to get change address"); - - // Clean up - unsafe { - let _ = CString::from_raw(address); - wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, count); - wallet_manager::wallet_manager_free(manager); - } - } - #[test] fn test_wallet_balance() { let mut error = FFIError::success(); @@ -352,107 +211,6 @@ mod tests { } } - #[test] - fn test_monitored_addresses() { - let mut error = FFIError::success(); - let error = &mut error as *mut FFIError; - - let manager = wallet_manager::wallet_manager_create(error); - assert!(!manager.is_null()); - - // Initially no monitored addresses - let mut addresses: *mut *mut c_char = ptr::null_mut(); - let mut count: usize = 0; - - let success = unsafe { - wallet_manager::wallet_manager_get_monitored_addresses( - manager, - FFINetworks::TestnetFlag, - &mut addresses as *mut *mut *mut c_char, - &mut count, - error, - ) - }; - - assert!(success); - assert_eq!(count, 0); - assert!(addresses.is_null()); - - // Add a wallet and generate addresses - let mnemonic = CString::new(TEST_MNEMONIC).unwrap(); - let success = unsafe { - wallet_manager::wallet_manager_add_wallet_from_mnemonic( - manager, - mnemonic.as_ptr(), - ptr::null(), - FFINetworks::TestnetFlag, - error, - ) - }; - assert!(success); - - // Get wallet ID and generate some addresses - let mut wallet_ids: *mut u8 = ptr::null_mut(); - let mut wallet_count: usize = 0; - - let success = unsafe { - wallet_manager::wallet_manager_get_wallet_ids( - manager, - &mut wallet_ids, - &mut wallet_count, - error, - ) - }; - assert!(success); - - // Try to generate a few addresses - // Generate a few addresses - unsafe { - for _ in 0..3 { - let addr = wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_ids, - FFINetworks::TestnetFlag, - 0, - error, - ); - assert!(!addr.is_null(), "Failed to generate address"); - - let _ = CString::from_raw(addr); - } - } - - // Now check monitored addresses - let success = unsafe { - wallet_manager::wallet_manager_get_monitored_addresses( - manager, - FFINetworks::TestnetFlag, - &mut addresses as *mut *mut *mut c_char, - &mut count, - error, - ) - }; - - assert!(success); - // Should have some monitored addresses now - if count > 0 { - assert!(!addresses.is_null()); - - // Clean up addresses - unsafe { - wallet_manager::wallet_manager_free_addresses(addresses, count); - } - } - - // Clean up - unsafe { - wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, wallet_count); - } - unsafe { - wallet_manager::wallet_manager_free(manager); - } - } - #[test] fn test_height_management() { let mut error = FFIError::success(); @@ -463,7 +221,7 @@ mod tests { // Get initial height let height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) + wallet_manager::wallet_manager_current_height(manager, FFINetwork::Testnet, error) }; assert_eq!(height, 0); @@ -471,7 +229,7 @@ mod tests { let success = unsafe { wallet_manager::wallet_manager_update_height( manager, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, 100000, error, ) @@ -480,7 +238,7 @@ mod tests { // Verify height was updated let height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) + wallet_manager::wallet_manager_current_height(manager, FFINetwork::Testnet, error) }; assert_eq!(height, 100000); @@ -525,80 +283,6 @@ mod tests { } } - #[test] - fn test_multiple_wallets_management() { - let mut error = FFIError::success(); - let error = &mut error as *mut FFIError; - - let manager = wallet_manager::wallet_manager_create(error); - assert!(!manager.is_null()); - - // Add multiple wallets with different mnemonics - // (passphrases don't work due to library bug) - let wallet_count = 3; - let mnemonics = [TEST_MNEMONIC, TEST_MNEMONIC_2, TEST_MNEMONIC_3]; - unsafe { - for mnemonic_str in &mnemonics[..wallet_count] { - let mnemonic = CString::new(*mnemonic_str).unwrap(); - - let success = wallet_manager::wallet_manager_add_wallet_from_mnemonic( - manager, - mnemonic.as_ptr(), - ptr::null(), // No passphrase - FFINetworks::TestnetFlag, // 2 accounts per wallet - error, - ); - assert!(success); - } - } - - // Verify wallet count - let count = unsafe { wallet_manager::wallet_manager_wallet_count(manager, error) }; - assert_eq!(count, wallet_count); - - // Get all wallet IDs - let mut wallet_ids: *mut u8 = ptr::null_mut(); - let mut id_count: usize = 0; - - let success = unsafe { - wallet_manager::wallet_manager_get_wallet_ids( - manager, - &mut wallet_ids, - &mut id_count, - error, - ) - }; - assert!(success); - assert_eq!(id_count, wallet_count); - - // Generate addresses for each wallet - unsafe { - for i in 0..id_count { - let wallet_id = wallet_ids.add(i * 32); - - let addr = wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_id, - FFINetworks::TestnetFlag, - 0, - error, - ); - - assert!(!addr.is_null(), "Failed to get address for wallet {}", i); - - let _ = CString::from_raw(addr); - } - } - - // Clean up - unsafe { - wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, id_count); - } - unsafe { - wallet_manager::wallet_manager_free(manager); - } - } - #[test] fn test_wallet_manager_add_wallet_with_account_count() { let mut error = FFIError::success(); @@ -687,70 +371,6 @@ mod tests { } } - #[test] - fn test_wallet_manager_get_change_address() { - let mut error = FFIError::success(); - let error = &mut error as *mut FFIError; - - let manager = wallet_manager::wallet_manager_create(error); - assert!(!manager.is_null()); - - // Add a wallet - let mnemonic = CString::new(TEST_MNEMONIC).unwrap(); - let passphrase = CString::new("").unwrap(); - let success = unsafe { - wallet_manager::wallet_manager_add_wallet_from_mnemonic( - manager, - mnemonic.as_ptr(), - passphrase.as_ptr(), - FFINetworks::TestnetFlag, - error, - ) - }; - assert!(success); - - // Get wallet ID - let mut wallet_ids: *mut u8 = ptr::null_mut(); - let mut id_count: usize = 0; - let success = unsafe { - wallet_manager::wallet_manager_get_wallet_ids( - manager, - &mut wallet_ids, - &mut id_count, - error, - ) - }; - assert!(success); - - // Get change address - let change_addr = unsafe { - wallet_manager::wallet_manager_get_change_address( - manager, - wallet_ids, - FFINetworks::TestnetFlag, - 0, // address_index - error, - ) - }; - - if !change_addr.is_null() { - let addr_str = unsafe { CStr::from_ptr(change_addr).to_str().unwrap() }; - assert!(!addr_str.is_empty()); - - unsafe { - let _ = CString::from_raw(change_addr); - } - } - - // Clean up - unsafe { - wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, id_count); - } - unsafe { - wallet_manager::wallet_manager_free(manager); - } - } - #[test] fn test_wallet_manager_get_wallet_balance() { let mut error = FFIError::success(); @@ -873,7 +493,7 @@ mod tests { // Get initial height let _height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) + wallet_manager::wallet_manager_current_height(manager, FFINetwork::Testnet, error) }; // Update height @@ -881,7 +501,7 @@ mod tests { unsafe { wallet_manager::wallet_manager_update_height( manager, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, new_height, error, ); @@ -889,7 +509,7 @@ mod tests { // Get updated height let current_height = unsafe { - wallet_manager::wallet_manager_current_height(manager, FFINetworks::TestnetFlag, error) + wallet_manager::wallet_manager_current_height(manager, FFINetwork::Testnet, error) }; assert_eq!(current_height, new_height); @@ -1072,7 +692,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &mempool_context, false, error, @@ -1089,7 +709,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &block_context, false, error, @@ -1110,7 +730,7 @@ mod tests { manager, tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &chain_locked_context, true, error, @@ -1125,7 +745,7 @@ mod tests { ptr::null_mut(), tx_bytes.as_ptr(), tx_bytes.len(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &mempool_context, false, error, @@ -1140,7 +760,7 @@ mod tests { manager, ptr::null(), 10, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &mempool_context, false, error, @@ -1155,7 +775,7 @@ mod tests { manager, tx_bytes.as_ptr(), 0, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &mempool_context, false, error, @@ -1171,7 +791,7 @@ mod tests { manager, invalid_tx.as_ptr(), invalid_tx.len(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, &mempool_context, false, error, diff --git a/key-wallet-ffi/src/wallet_tests.rs b/key-wallet-ffi/src/wallet_tests.rs index 4bd48a542..4992c9c54 100644 --- a/key-wallet-ffi/src/wallet_tests.rs +++ b/key-wallet-ffi/src/wallet_tests.rs @@ -5,7 +5,7 @@ mod wallet_tests { use crate::account::account_free; use crate::error::{FFIError, FFIErrorCode}; use crate::types::{FFIAccountType, FFINetworks}; - use crate::wallet; + use crate::{wallet, FFINetwork}; use std::ffi::CString; use std::ptr; @@ -293,7 +293,7 @@ mod wallet_tests { let result = unsafe { wallet::wallet_add_account( wallet, - FFINetworks::TestnetFlag, + FFINetwork::Testnet, FFIAccountType::StandardBIP44, 1, ) @@ -328,7 +328,7 @@ mod wallet_tests { let result = unsafe { wallet::wallet_add_account( ptr::null_mut(), - FFINetworks::TestnetFlag, + FFINetwork::Testnet, FFIAccountType::StandardBIP44, 0, ) @@ -391,7 +391,7 @@ mod wallet_tests { assert!(!wallet.is_null()); // Get xpub for account 0 - let xpub = unsafe { wallet::wallet_get_xpub(wallet, FFINetworks::TestnetFlag, 0, error) }; + let xpub = unsafe { wallet::wallet_get_xpub(wallet, FFINetwork::Testnet, 0, error) }; assert!(!xpub.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success); @@ -412,8 +412,7 @@ mod wallet_tests { let error = &mut error as *mut FFIError; // Test with null wallet - let xpub = - unsafe { wallet::wallet_get_xpub(ptr::null(), FFINetworks::TestnetFlag, 0, error) }; + let xpub = unsafe { wallet::wallet_get_xpub(ptr::null(), FFINetwork::Testnet, 0, error) }; assert!(xpub.is_null()); assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidInput); } diff --git a/key-wallet-ffi/tests/integration_test.rs b/key-wallet-ffi/tests/integration_test.rs index bf833f442..e46ffd433 100644 --- a/key-wallet-ffi/tests/integration_test.rs +++ b/key-wallet-ffi/tests/integration_test.rs @@ -58,30 +58,7 @@ fn test_full_wallet_workflow() { let wallet_id = wallet_ids; // First wallet ID starts at offset 0 - // 6. Derive addresses using wallet manager - let receive_addr = unsafe { - key_wallet_ffi::wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_id, - FFINetworks::TestnetFlag, - 0, - error, - ) - }; - assert!(!receive_addr.is_null()); - - let change_addr = unsafe { - key_wallet_ffi::wallet_manager::wallet_manager_get_change_address( - manager, - wallet_id, - FFINetworks::TestnetFlag, - 0, - error, - ) - }; - assert!(!change_addr.is_null()); - - // 7. Get balance + // 6. Get balance let mut confirmed: u64 = 0; let mut unconfirmed: u64 = 0; let success = unsafe { @@ -99,8 +76,6 @@ fn test_full_wallet_workflow() { // Clean up unsafe { - key_wallet_ffi::address::address_free(receive_addr); - key_wallet_ffi::address::address_free(change_addr); key_wallet_ffi::wallet_manager::wallet_manager_free_wallet_ids(wallet_ids, count); key_wallet_ffi::wallet_manager::wallet_manager_free(manager); key_wallet_ffi::mnemonic::mnemonic_free(mnemonic); diff --git a/key-wallet-ffi/tests/test_passphrase_wallets.rs b/key-wallet-ffi/tests/test_passphrase_wallets.rs index d99a0f924..e98860af1 100644 --- a/key-wallet-ffi/tests/test_passphrase_wallets.rs +++ b/key-wallet-ffi/tests/test_passphrase_wallets.rs @@ -4,7 +4,7 @@ use key_wallet_ffi::error::{FFIError, FFIErrorCode}; use key_wallet_ffi::types::FFINetworks; use key_wallet_ffi::FFINetwork; -use std::ffi::{CStr, CString}; +use std::ffi::CString; #[test] fn test_ffi_wallet_create_from_mnemonic_with_passphrase() { @@ -93,34 +93,6 @@ fn test_ffi_wallet_manager_add_wallet_with_passphrase() { assert!(success); assert_eq!(count, 1); - // Try to get a receive address from the wallet - // With the updated implementation, wallet_manager now creates accounts for passphrase wallets - // using the Default options, so this should succeed - let addr = unsafe { - key_wallet_ffi::wallet_manager::wallet_manager_get_receive_address( - manager, - wallet_ids_ptr, // First wallet ID - FFINetworks::TestnetFlag, - 0, // account_index - error, - ) - }; - - // This should now succeed because wallet_manager creates accounts with Default options - assert!(!addr.is_null(), "Should be able to get address from wallet with passphrase"); - assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success); - - if !addr.is_null() { - let addr_str = unsafe { CStr::from_ptr(addr).to_str().unwrap() }; - println!("Successfully got address from wallet manager: {}", addr_str); - assert!(!addr_str.is_empty()); - - // Clean up address - unsafe { - key_wallet_ffi::address::address_free(addr); - } - } - // Clean up if !wallet_ids_ptr.is_null() && count > 0 { unsafe { From 1a1b4139b05ec01f7cef302fc7dddda473bb1b10 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 04:51:28 +0700 Subject: [PATCH 6/9] updated docs. --- dash-spv-ffi/FFI_API.md | 15 +--- key-wallet-ffi/FFI_API.md | 168 ++++++++------------------------------ 2 files changed, 35 insertions(+), 148 deletions(-) diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 17f603258..027ac4970 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 58 +**Total Functions**: 57 ## Table of Contents @@ -132,7 +132,7 @@ Functions: 2 ### Utility Functions -Functions: 12 +Functions: 11 | Function | Description | Module | |----------|-------------|--------| @@ -142,7 +142,6 @@ Functions: 12 | `dash_spv_ffi_client_record_send` | No description | client | | `dash_spv_ffi_client_rescan_blockchain` | No description | client | | `dash_spv_ffi_enable_test_mode` | No description | utils | -| `dash_spv_ffi_get_network_name` | No description | utils | | `dash_spv_ffi_init_logging` | No description | utils | | `dash_spv_ffi_spv_stats_destroy` | No description | client | | `dash_spv_ffi_string_array_destroy` | Destroy an array of FFIString pointers (Vec<*mut FFIString>) and their contents | types | @@ -883,16 +882,6 @@ dash_spv_ffi_enable_test_mode() -> () --- -#### `dash_spv_ffi_get_network_name` - -```c -dash_spv_ffi_get_network_name(network: crate::FFINetwork) -> *const c_char -``` - -**Module:** `utils` - ---- - #### `dash_spv_ffi_init_logging` ```c diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index a1580d57d..6ef6791cf 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 227 +**Total Functions**: 221 ## Table of Contents @@ -42,7 +42,7 @@ Functions: 3 ### Wallet Manager -Functions: 20 +Functions: 17 | Function | Description | Module | |----------|-------------|--------| @@ -55,10 +55,7 @@ Functions: 20 | `wallet_manager_free_addresses` | Free address array # Safety - `addresses` must be a valid pointer to an arr... | wallet_manager | | `wallet_manager_free_wallet_bytes` | No description | wallet_manager | | `wallet_manager_free_wallet_ids` | Free wallet IDs buffer # Safety - `wallet_ids` must be a valid pointer to a... | wallet_manager | -| `wallet_manager_get_change_address` | Get next change address for a wallet # Safety - `manager` must be a valid p... | wallet_manager | | `wallet_manager_get_managed_wallet_info` | Get managed wallet info from the manager Returns a reference to the managed ... | wallet_manager | -| `wallet_manager_get_monitored_addresses` | Get monitored addresses for a network # Safety - `manager` must be a valid ... | wallet_manager | -| `wallet_manager_get_receive_address` | Get next receive address for a wallet # Safety - `manager` must be a valid ... | wallet_manager | | `wallet_manager_get_wallet` | Get a wallet from the manager Returns a reference to the wallet if found # ... | wallet_manager | | `wallet_manager_get_wallet_balance` | Get wallet balance Returns the confirmed and unconfirmed balance for a speci... | wallet_manager | | `wallet_manager_get_wallet_ids` | Get wallet IDs # Safety - `manager` must be a valid pointer to an FFIWallet... | wallet_manager | @@ -69,7 +66,7 @@ Functions: 20 ### Wallet Operations -Functions: 58 +Functions: 56 | Function | Description | Module | |----------|-------------|--------| @@ -118,7 +115,6 @@ Functions: 58 | `wallet_derive_public_key_as_hex` | Derive public key at a specific path and return as hex string # Safety - `w... | keys | | `wallet_free` | Free a wallet # Safety - `wallet` must be a valid pointer to an FFIWallet t... | wallet | | `wallet_free_const` | Free a const wallet handle This is a const-safe wrapper for wallet_free() th... | wallet | -| `wallet_generate_provider_key` | Generate a provider key at a specific index This generates a provider key (B... | provider_keys | | `wallet_get_account` | Get an account handle for a specific account type Returns a result containing... | account | | `wallet_get_account_collection` | Get account collection for a specific network from wallet # Safety - `walle... | account_collection | | `wallet_get_account_count` | Get number of accounts # Safety - `wallet` must be a valid pointer to an FF... | account | @@ -130,7 +126,6 @@ Functions: 58 | `wallet_has_mnemonic` | Check if wallet has mnemonic # Safety - `wallet` must be a valid pointer to... | wallet | | `wallet_is_watch_only` | Check if wallet is watch-only # Safety - `wallet` must be a valid pointer t... | wallet | | `wallet_sign_transaction` | Sign a transaction # Safety - `wallet` must be a valid pointer to an FFIWal... | transaction | -| `wallet_sign_with_provider_key` | Sign data with a provider key This signs arbitrary data with the provider ke... | provider_keys | ### Account Management @@ -259,7 +254,7 @@ Functions: 13 ### Key Management -Functions: 16 +Functions: 15 | Function | Description | Module | |----------|-------------|--------| @@ -276,7 +271,6 @@ Functions: 16 | `extended_public_key_to_string` | Get extended public key as string (xpub format) Returns the extended public ... | keys | | `private_key_free` | Free a private key # Safety - `key` must be a valid pointer created by priv... | keys | | `private_key_to_wif` | Get private key as WIF string from FFIPrivateKey # Safety - `key` must be a... | keys | -| `provider_key_info_free` | Free provider key info # Safety - `info` must be a valid pointer to an FFIP... | provider_keys | | `public_key_free` | Free a public key # Safety - `key` must be a valid pointer created by publi... | keys | | `public_key_to_hex` | Get public key as hex string from FFIPublicKey # Safety - `key` must be a v... | keys | @@ -458,7 +452,7 @@ Create a new wallet manager #### `wallet_manager_current_height` ```c -wallet_manager_current_height(manager: *const FFIWalletManager, network: FFINetworks, error: *mut FFIError,) -> c_uint +wallet_manager_current_height(manager: *const FFIWalletManager, network: FFINetwork, error: *mut FFIError,) -> c_uint ``` **Description:** @@ -529,22 +523,6 @@ Free wallet IDs buffer # Safety - `wallet_ids` must be a valid pointer to a bu --- -#### `wallet_manager_get_change_address` - -```c -wallet_manager_get_change_address(manager: *mut FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char -``` - -**Description:** -Get next change address for a wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Safety:** -- `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Module:** `wallet_manager` - ---- - #### `wallet_manager_get_managed_wallet_info` ```c @@ -561,38 +539,6 @@ Get managed wallet info from the manager Returns a reference to the managed wal --- -#### `wallet_manager_get_monitored_addresses` - -```c -wallet_manager_get_monitored_addresses(manager: *const FFIWalletManager, network: FFINetworks, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool -``` - -**Description:** -Get monitored addresses for a network # Safety - `manager` must be a valid pointer to an FFIWalletManager - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Safety:** -- `manager` must be a valid pointer to an FFIWalletManager - `addresses_out` must be a valid pointer to a pointer that will receive the addresses array - `count_out` must be a valid pointer to receive the count - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Module:** `wallet_manager` - ---- - -#### `wallet_manager_get_receive_address` - -```c -wallet_manager_get_receive_address(manager: *mut FFIWalletManager, wallet_id: *const u8, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char -``` - -**Description:** -Get next receive address for a wallet # Safety - `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Safety:** -- `manager` must be a valid pointer to an FFIWalletManager - `wallet_id` must be a valid pointer to a 32-byte wallet ID - `error` must be a valid pointer to an FFIError structure or null - The caller must ensure all pointers remain valid for the duration of this call - -**Module:** `wallet_manager` - ---- - #### `wallet_manager_get_wallet` ```c @@ -654,7 +600,7 @@ wallet_manager_import_wallet_from_bytes(manager: *mut FFIWalletManager, wallet_b #### `wallet_manager_process_transaction` ```c -wallet_manager_process_transaction(manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, network: FFINetworks, context: *const crate::types::FFITransactionContextDetails, update_state_if_found: bool, error: *mut FFIError,) -> bool +wallet_manager_process_transaction(manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, network: FFINetwork, context: *const crate::types::FFITransactionContextDetails, update_state_if_found: bool, error: *mut FFIError,) -> bool ``` **Description:** @@ -670,7 +616,7 @@ Process a transaction through all wallets Checks a transaction against all wall #### `wallet_manager_update_height` ```c -wallet_manager_update_height(manager: *mut FFIWalletManager, network: FFINetworks, height: c_uint, error: *mut FFIError,) -> bool +wallet_manager_update_height(manager: *mut FFIWalletManager, network: FFINetwork, height: c_uint, error: *mut FFIError,) -> bool ``` **Description:** @@ -820,7 +766,7 @@ Get the parent wallet ID of a managed account Note: ManagedAccount doesn't stor #### `managed_wallet_check_transaction` ```c -managed_wallet_check_transaction(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetworks, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: c_uint, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +managed_wallet_check_transaction(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetwork, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: c_uint, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool ``` **Description:** @@ -852,7 +798,7 @@ Free managed wallet info # Safety - `managed_wallet` must be a valid pointer t #### `managed_wallet_generate_addresses_to_index` ```c -managed_wallet_generate_addresses_to_index(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, target_index: c_uint, error: *mut FFIError,) -> bool +managed_wallet_generate_addresses_to_index(managed_wallet: *mut FFIManagedWallet, wallet: *const FFIWallet, network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, target_index: c_uint, error: *mut FFIError,) -> bool ``` **Description:** @@ -916,7 +862,7 @@ Get number of accounts in a managed wallet # Safety - `manager` must be a vali #### `managed_wallet_get_address_pool_info` ```c -managed_wallet_get_address_pool_info(managed_wallet: *const FFIManagedWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, info_out: *mut FFIAddressPoolInfo, error: *mut FFIError,) -> bool +managed_wallet_get_address_pool_info(managed_wallet: *const FFIManagedWallet, network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, info_out: *mut FFIAddressPoolInfo, error: *mut FFIError,) -> bool ``` **Description:** @@ -948,7 +894,7 @@ Get wallet balance from managed wallet info Returns the balance breakdown inclu #### `managed_wallet_get_bip_44_external_address_range` ```c -managed_wallet_get_bip_44_external_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool +managed_wallet_get_bip_44_external_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetwork, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -964,7 +910,7 @@ Get BIP44 external (receive) addresses in the specified range Returns external #### `managed_wallet_get_bip_44_internal_address_range` ```c -managed_wallet_get_bip_44_internal_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool +managed_wallet_get_bip_44_internal_address_range(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetwork, account_index: std::os::raw::c_uint, start_index: std::os::raw::c_uint, end_index: std::os::raw::c_uint, addresses_out: *mut *mut *mut c_char, count_out: *mut usize, error: *mut FFIError,) -> bool ``` **Description:** @@ -980,7 +926,7 @@ Get BIP44 internal (change) addresses in the specified range Returns internal a #### `managed_wallet_get_next_bip44_change_address` ```c -managed_wallet_get_next_bip44_change_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char +managed_wallet_get_next_bip44_change_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetwork, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -996,7 +942,7 @@ Get the next unused change address Generates the next unused change address for #### `managed_wallet_get_next_bip44_receive_address` ```c -managed_wallet_get_next_bip44_receive_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetworks, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char +managed_wallet_get_next_bip44_receive_address(managed_wallet: *mut FFIManagedWalletInfo, wallet: *const FFIWallet, network: FFINetwork, account_index: std::os::raw::c_uint, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1060,7 +1006,7 @@ Free managed wallet info returned by wallet_manager_get_managed_wallet_info # S #### `managed_wallet_mark_address_used` ```c -managed_wallet_mark_address_used(managed_wallet: *mut FFIManagedWallet, network: FFINetworks, address: *const c_char, error: *mut FFIError,) -> bool +managed_wallet_mark_address_used(managed_wallet: *mut FFIManagedWallet, network: FFINetwork, address: *const c_char, error: *mut FFIError,) -> bool ``` **Description:** @@ -1076,7 +1022,7 @@ Mark an address as used in the pool This updates the pool's tracking of which a #### `managed_wallet_set_gap_limit` ```c -managed_wallet_set_gap_limit(managed_wallet: *mut FFIManagedWallet, network: FFINetworks, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, gap_limit: c_uint, error: *mut FFIError,) -> bool +managed_wallet_set_gap_limit(managed_wallet: *mut FFIManagedWallet, network: FFINetwork, account_type: FFIAccountType, account_index: c_uint, pool_type: FFIAddressPoolType, gap_limit: c_uint, error: *mut FFIError,) -> bool ``` **Description:** @@ -1092,7 +1038,7 @@ Set the gap limit for an address pool The gap limit determines how many unused #### `wallet_add_account` ```c -wallet_add_account(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint,) -> crate::types::FFIAccountResult +wallet_add_account(wallet: *mut FFIWallet, network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint,) -> crate::types::FFIAccountResult ``` **Description:** @@ -1108,7 +1054,7 @@ This function dereferences a raw pointer to FFIWallet. The caller must ensure th #### `wallet_add_account_with_string_xpub` ```c -wallet_add_account_with_string_xpub(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_string: *const c_char,) -> crate::types::FFIAccountResult +wallet_add_account_with_string_xpub(wallet: *mut FFIWallet, network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_string: *const c_char,) -> crate::types::FFIAccountResult ``` **Description:** @@ -1124,7 +1070,7 @@ This function dereferences raw pointers. The caller must ensure that: - The wall #### `wallet_add_account_with_xpub_bytes` ```c -wallet_add_account_with_xpub_bytes(wallet: *mut FFIWallet, network: FFINetworks, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_bytes: *const u8, xpub_len: usize,) -> crate::types::FFIAccountResult +wallet_add_account_with_xpub_bytes(wallet: *mut FFIWallet, network: FFINetwork, account_type: crate::types::FFIAccountType, account_index: c_uint, xpub_bytes: *const u8, xpub_len: usize,) -> crate::types::FFIAccountResult ``` **Description:** @@ -1156,7 +1102,7 @@ Build a transaction # Safety - `wallet` must be a valid pointer to an FFIWalle #### `wallet_check_transaction` ```c -wallet_check_transaction(wallet: *mut FFIWallet, network: FFINetworks, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: u32, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +wallet_check_transaction(wallet: *mut FFIWallet, network: FFINetwork, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_height: u32, block_hash: *const u8, // 32 bytes if not null timestamp: u64, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool ``` **Description:** @@ -1284,7 +1230,7 @@ Create a new random wallet with options # Safety - `account_options` must be a #### `wallet_derive_extended_private_key` ```c -wallet_derive_extended_private_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPrivateKey +wallet_derive_extended_private_key(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPrivateKey ``` **Description:** @@ -1300,7 +1246,7 @@ Derive extended private key at a specific path Returns an opaque FFIExtendedPriv #### `wallet_derive_extended_public_key` ```c -wallet_derive_extended_public_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPublicKey +wallet_derive_extended_public_key(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIExtendedPublicKey ``` **Description:** @@ -1316,7 +1262,7 @@ Derive extended public key at a specific path Returns an opaque FFIExtendedPubli #### `wallet_derive_private_key` ```c -wallet_derive_private_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPrivateKey +wallet_derive_private_key(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPrivateKey ``` **Description:** @@ -1332,7 +1278,7 @@ Derive private key at a specific path Returns an opaque FFIPrivateKey pointer th #### `wallet_derive_private_key_as_wif` ```c -wallet_derive_private_key_as_wif(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char +wallet_derive_private_key_as_wif(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1348,7 +1294,7 @@ Derive private key at a specific path and return as WIF string # Safety - `wal #### `wallet_derive_public_key` ```c -wallet_derive_public_key(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPublicKey +wallet_derive_public_key(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut FFIPublicKey ``` **Description:** @@ -1364,7 +1310,7 @@ Derive public key at a specific path Returns an opaque FFIPublicKey pointer that #### `wallet_derive_public_key_as_hex` ```c -wallet_derive_public_key_as_hex(wallet: *const FFIWallet, network: FFINetworks, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char +wallet_derive_public_key_as_hex(wallet: *const FFIWallet, network: FFINetwork, derivation_path: *const c_char, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1409,22 +1355,6 @@ Free a const wallet handle This is a const-safe wrapper for wallet_free() that --- -#### `wallet_generate_provider_key` - -```c -wallet_generate_provider_key(wallet: *const FFIWallet, network: FFINetworks, key_type: FFIProviderKeyType, key_index: c_uint, include_private: bool, info_out: *mut FFIProviderKeyInfo, error: *mut FFIError,) -> bool -``` - -**Description:** -Generate a provider key at a specific index This generates a provider key (BLS or EdDSA) at the specified index. For voting, owner, and operator keys, this generates BLS keys. For platform keys, this generates EdDSA keys. # Safety - `wallet` must be a valid pointer to an FFIWallet - `info_out` must be a valid pointer to store the key info - `error` must be a valid pointer to an FFIError or null - The returned public_key, private_key, and derivation_path must be freed by the caller - -**Safety:** -- `wallet` must be a valid pointer to an FFIWallet - `info_out` must be a valid pointer to store the key info - `error` must be a valid pointer to an FFIError or null - The returned public_key, private_key, and derivation_path must be freed by the caller - -**Module:** `provider_keys` - ---- - #### `wallet_get_account` ```c @@ -1476,7 +1406,7 @@ Get number of accounts # Safety - `wallet` must be a valid pointer to an FFIWa #### `wallet_get_account_xpriv` ```c -wallet_get_account_xpriv(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +wallet_get_account_xpriv(wallet: *const FFIWallet, network: FFINetwork, account_index: c_uint, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1492,7 +1422,7 @@ Get extended private key for account # Safety - `wallet` must be a valid point #### `wallet_get_account_xpub` ```c -wallet_get_account_xpub(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +wallet_get_account_xpub(wallet: *const FFIWallet, network: FFINetwork, account_index: c_uint, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1540,7 +1470,7 @@ Get an IdentityTopUp account handle with a specific registration index This is u #### `wallet_get_xpub` ```c -wallet_get_xpub(wallet: *const FFIWallet, network: FFINetworks, account_index: c_uint, error: *mut FFIError,) -> *mut c_char +wallet_get_xpub(wallet: *const FFIWallet, network: FFINetwork, account_index: c_uint, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -1601,22 +1531,6 @@ Sign a transaction # Safety - `wallet` must be a valid pointer to an FFIWallet --- -#### `wallet_sign_with_provider_key` - -```c -wallet_sign_with_provider_key(wallet: *const FFIWallet, network: FFINetworks, key_type: FFIProviderKeyType, _key_index: c_uint, data: *const u8, data_len: usize, signature_out: *mut *mut u8, signature_len_out: *mut usize, error: *mut FFIError,) -> bool -``` - -**Description:** -Sign data with a provider key This signs arbitrary data with the provider key at the specified index. For BLS keys, this produces a BLS signature. For EdDSA keys, this produces an Ed25519 signature. # Safety - `wallet` must be a valid pointer to an FFIWallet - `data` must be a valid pointer to data with at least `data_len` bytes - `signature_out` must be a valid pointer to store the signature pointer - `signature_len_out` must be a valid pointer to store the signature length - `error` must be a valid pointer to an FFIError or null - The returned signature must be freed with `libc::free` - -**Safety:** -- `wallet` must be a valid pointer to an FFIWallet - `data` must be a valid pointer to data with at least `data_len` bytes - `signature_out` must be a valid pointer to store the signature pointer - `signature_len_out` must be a valid pointer to store the signature length - `error` must be a valid pointer to an FFIError or null - The returned signature must be freed with `libc::free` - -**Module:** `provider_keys` - ---- - ### Account Management - Detailed #### `account_collection_count` @@ -2807,7 +2721,7 @@ Check if a managed account is watch-only # Safety - `account` must be a valid #### `managed_account_get_network` ```c -managed_account_get_network(account: *const FFIManagedAccount,) -> FFINetworks +managed_account_get_network(account: *const FFIManagedAccount,) -> FFINetwork ``` **Description:** @@ -3338,7 +3252,7 @@ Get the private key from an extended private key Extracts the non-extended priv #### `extended_private_key_to_string` ```c -extended_private_key_to_string(key: *const FFIExtendedPrivateKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +extended_private_key_to_string(key: *const FFIExtendedPrivateKey, network: FFINetwork, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -3386,7 +3300,7 @@ Get the public key from an extended public key Extracts the non-extended public #### `extended_public_key_to_string` ```c -extended_public_key_to_string(key: *const FFIExtendedPublicKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +extended_public_key_to_string(key: *const FFIExtendedPublicKey, network: FFINetwork, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -3418,7 +3332,7 @@ Free a private key # Safety - `key` must be a valid pointer created by private #### `private_key_to_wif` ```c -private_key_to_wif(key: *const FFIPrivateKey, network: FFINetworks, error: *mut FFIError,) -> *mut c_char +private_key_to_wif(key: *const FFIPrivateKey, network: FFINetwork, error: *mut FFIError,) -> *mut c_char ``` **Description:** @@ -3431,22 +3345,6 @@ Get private key as WIF string from FFIPrivateKey # Safety - `key` must be a va --- -#### `provider_key_info_free` - -```c -provider_key_info_free(info: *mut FFIProviderKeyInfo) -> () -``` - -**Description:** -Free provider key info # Safety - `info` must be a valid pointer to an FFIProviderKeyInfo - This function must only be called once per info structure - -**Safety:** -- `info` must be a valid pointer to an FFIProviderKeyInfo - This function must only be called once per info structure - -**Module:** `provider_keys` - ---- - #### `public_key_free` ```c From 39a7b6b0d5402ac2423b758d9c7dc3157eadd63b Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 21:21:33 +0700 Subject: [PATCH 7/9] small fixes --- dash-spv/tests/block_download_test.rs | 1 + dash-spv/tests/edge_case_filter_sync_test.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dash-spv/tests/block_download_test.rs b/dash-spv/tests/block_download_test.rs index f4a95edeb..6a3d4d92c 100644 --- a/dash-spv/tests/block_download_test.rs +++ b/dash-spv/tests/block_download_test.rs @@ -27,6 +27,7 @@ use dash_spv::{ sync::{sequential::SequentialSyncManager, FilterSyncManager}, types::FilterMatch, }; +use key_wallet::wallet::ManagedWalletInfo; /// Mock network manager for testing struct MockNetworkManager { diff --git a/dash-spv/tests/edge_case_filter_sync_test.rs b/dash-spv/tests/edge_case_filter_sync_test.rs index 43961532f..83686867d 100644 --- a/dash-spv/tests/edge_case_filter_sync_test.rs +++ b/dash-spv/tests/edge_case_filter_sync_test.rs @@ -178,7 +178,7 @@ async fn test_filter_sync_at_tip_edge_case() { // Try to start filter sync when already at tip let result = filter_sync.start_sync_headers(&mut network, &mut storage).await; assert!(result.is_ok()); - assert_eq!(result.unwrap(), false, "Should not start sync when already at tip"); + assert!(!result.unwrap(), "Should not start sync when already at tip"); // Verify no messages were sent let sent_messages = network.get_sent_messages(); From 84057e7e49c5683125ebe7fe3ba526d00f126c2f Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 23:22:12 +0700 Subject: [PATCH 8/9] more work --- .github/workflows/verify-ffi-docs.yml | 186 +- dash-spv-ffi/FFI_API.md | 12 +- dash-spv-ffi/include/dash_spv_ffi.h | 4 +- dash-spv-ffi/scripts/check_ffi_docs.sh | 8 +- dash-spv-ffi/scripts/generate_ffi_docs.py | 130 +- dash-spv-ffi/src/client.rs | 2 +- dash-spv-ffi/src/config.rs | 2 +- key-wallet-ffi/FFI_API.md | 21 +- key-wallet-ffi/FFI_DOCS_README.md | 4 +- key-wallet-ffi/include/key_wallet_ffi_test.h | 3452 +++++++++++++++ key-wallet-ffi/scripts/check_ffi_docs.sh | 8 +- key-wallet-ffi/scripts/generate_ffi_docs.py | 118 +- key-wallet-ffi/src/keys_tests.rs | 2 +- .../Sources/DashSPVFFI/include/dash_spv_ffi.h | 3 + .../KeyWalletFFI/include/key_wallet_ffi.h | 3901 +++++++++++++++++ swift-dash-core-sdk/build-ios.sh | 9 +- 16 files changed, 7655 insertions(+), 207 deletions(-) create mode 100644 key-wallet-ffi/include/key_wallet_ffi_test.h create mode 100644 swift-dash-core-sdk/Sources/KeyWalletFFI/include/key_wallet_ffi.h diff --git a/.github/workflows/verify-ffi-docs.yml b/.github/workflows/verify-ffi-docs.yml index f24caab1e..bbb0a70f0 100644 --- a/.github/workflows/verify-ffi-docs.yml +++ b/.github/workflows/verify-ffi-docs.yml @@ -23,112 +23,104 @@ on: jobs: verify-key-wallet-docs: runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - - name: Generate key-wallet-ffi documentation - run: | - cd key-wallet-ffi - python3 scripts/generate_ffi_docs.py - - - name: Check if key-wallet-ffi documentation is up to date - run: | - cd key-wallet-ffi - if ! git diff --exit-code FFI_API.md; then - echo "❌ key-wallet-ffi documentation is out of date!" - echo "" - echo "The FFI_API.md file needs to be regenerated." - echo "Please run the following command and commit the changes:" - echo "" - echo " cd key-wallet-ffi && make update-docs" - echo "" - echo "Changes detected:" - git diff FFI_API.md - exit 1 - else - echo "✅ key-wallet-ffi documentation is up to date" - fi + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Generate key-wallet-ffi documentation + run: | + cd key-wallet-ffi + python3 scripts/generate_ffi_docs.py + - name: Check if key-wallet-ffi documentation is up to date + run: | + cd key-wallet-ffi + if ! git diff --exit-code FFI_API.md; then + echo "❌ key-wallet-ffi documentation is out of date!" + echo "" + echo "The FFI_API.md file needs to be regenerated." + echo "Please run the following command and commit the changes:" + echo "" + echo " cd key-wallet-ffi && make update-docs" + echo "" + echo "Changes detected:" + git diff FFI_API.md + exit 1 + else + echo "✅ key-wallet-ffi documentation is up to date" + fi verify-dash-spv-docs: runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - - name: Generate dash-spv-ffi documentation - run: | - cd dash-spv-ffi - python3 scripts/generate_ffi_docs.py - - - name: Check if dash-spv-ffi documentation is up to date - run: | - cd dash-spv-ffi - if ! git diff --exit-code FFI_API.md; then - echo "❌ dash-spv-ffi documentation is out of date!" - echo "" - echo "The FFI_API.md file needs to be regenerated." - echo "Please run the following command and commit the changes:" - echo "" - echo " cd dash-spv-ffi && make update-docs" - echo "" - echo "Changes detected:" - git diff FFI_API.md - exit 1 - else - echo "✅ dash-spv-ffi documentation is up to date" - fi + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Generate dash-spv-ffi documentation + run: | + cd dash-spv-ffi + python3 scripts/generate_ffi_docs.py + - name: Check if dash-spv-ffi documentation is up to date + run: | + cd dash-spv-ffi + if ! git diff --exit-code FFI_API.md; then + echo "❌ dash-spv-ffi documentation is out of date!" + echo "" + echo "The FFI_API.md file needs to be regenerated." + echo "Please run the following command and commit the changes:" + echo "" + echo " cd dash-spv-ffi && make update-docs" + echo "" + echo "Changes detected:" + git diff FFI_API.md + exit 1 + else + echo "✅ dash-spv-ffi documentation is up to date" + fi update-docs-comment: runs-on: ubuntu-latest if: failure() && github.event_name == 'pull_request' - needs: [verify-key-wallet-docs, verify-dash-spv-docs] - + needs: + - verify-key-wallet-docs + - verify-dash-spv-docs permissions: pull-requests: write - steps: - - name: Comment on PR - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `## ⚠️ FFI Documentation Update Required - - The FFI API documentation is out of date. Please regenerate it by running: - - For key-wallet-ffi: - \`\`\`bash - cd key-wallet-ffi - make update-docs - \`\`\` - - For dash-spv-ffi: - \`\`\`bash - cd dash-spv-ffi - make update-docs - \`\`\` - - Then commit the changes: - \`\`\`bash - git add */FFI_API.md - git commit -m "docs: update FFI API documentation" - \`\`\` - - This ensures the documentation stays in sync with the actual FFI functions.` - }) \ No newline at end of file + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ⚠️ FFI Documentation Update Required + + The FFI API documentation is out of date. Please regenerate it by running: + + For key-wallet-ffi: + \`\`\`bash + cd key-wallet-ffi + make update-docs + \`\`\` + + For dash-spv-ffi: + \`\`\`bash + cd dash-spv-ffi + make update-docs + \`\`\` + + Then commit the changes: + \`\`\`bash + git add */FFI_API.md + git commit -m "docs: update FFI API documentation" + \`\`\` + + This ensures the documentation stays in sync with the actual FFI functions.` + }) diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 027ac4970..8642c7a56 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -233,10 +233,10 @@ 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_string_free +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_string_free +- `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` @@ -585,7 +585,7 @@ dash_spv_ffi_client_is_filter_sync_available(client: *mut FFIDashSpvClient,) -> #### `dash_spv_ffi_client_sync_to_tip` ```c -dash_spv_ffi_client_sync_to_tip(client: *mut FFIDashSpvClient, completion_callback: Option () +dash_spv_ffi_client_sync_to_tip(client: *mut FFIDashSpvClient, completion_callback: Option, user_data: *mut c_void,) -> i32 ``` **Description:** @@ -601,7 +601,7 @@ This function is unsafe because: - `client` must be a valid pointer to an initia #### `dash_spv_ffi_client_sync_to_tip_with_progress` ```c -dash_spv_ffi_client_sync_to_tip_with_progress(client: *mut FFIDashSpvClient, progress_callback: Option () +dash_spv_ffi_client_sync_to_tip_with_progress(client: *mut FFIDashSpvClient, progress_callback: Option, completion_callback: Option, user_data: *mut c_void,) -> i32 ``` **Description:** @@ -843,10 +843,10 @@ dash_spv_ffi_client_get_wallet_manager(client: *mut FFIDashSpvClient,) -> *mut c ``` **Description:** -Get the wallet manager from the SPV client Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. This allows direct interaction with the wallet manager without going through the client. # Safety The caller must ensure that: - The client pointer is valid - The returned pointer is freed using wallet_manager_free() # Returns An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. Swift should treat this as an OpaquePointer. +Get the wallet manager from the SPV client Returns an opaque pointer to FFIWalletManager that contains a cloned Arc reference to the wallet manager. This allows direct interaction with the wallet manager without going through the client. # Safety The caller must ensure that: - The client pointer is valid - The returned pointer is freed using `wallet_manager_free` from key-wallet-ffi # Returns An opaque pointer (void*) to the wallet manager, or NULL if the client is not initialized. Swift should treat this as an OpaquePointer. **Safety:** -The caller must ensure that: - The client pointer is valid - The returned pointer is freed using wallet_manager_free() +The caller must ensure that: - The client pointer is valid - The returned pointer is freed using `wallet_manager_free` from key-wallet-ffi **Module:** `client` diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index b3270a16d..932dddefe 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -326,7 +326,7 @@ int32_t dash_spv_ffi_client_record_send(struct FFIDashSpvClient *client, const c * * The caller must ensure that: * - The client pointer is valid - * - The returned pointer is freed using wallet_manager_free() + * - The returned pointer is freed using `wallet_manager_free` from key-wallet-ffi * * # Returns * @@ -429,7 +429,7 @@ FFINetwork dash_spv_ffi_config_get_network(const FFIClientConfig *config); * # 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_string_free + * - 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 FFIClientConfig *config); diff --git a/dash-spv-ffi/scripts/check_ffi_docs.sh b/dash-spv-ffi/scripts/check_ffi_docs.sh index da819b526..6dede2442 100755 --- a/dash-spv-ffi/scripts/check_ffi_docs.sh +++ b/dash-spv-ffi/scripts/check_ffi_docs.sh @@ -14,8 +14,10 @@ cd "$PROJECT_DIR" # Generate new documentation python3 scripts/generate_ffi_docs.py > /dev/null 2>&1 -# Check if there are any changes -if ! git diff --quiet FFI_API.md; then +# Check if there are any changes (tracked, staged, or untracked) +if ! git diff --quiet --exit-code -- FFI_API.md \ + || ! git diff --quiet --cached -- FFI_API.md \ + || [ -n "$(git ls-files --others --exclude-standard -- FFI_API.md)" ]; then echo "❌ FFI documentation is out of date!" echo "" echo "Please regenerate the documentation by running:" @@ -27,4 +29,4 @@ if ! git diff --quiet FFI_API.md; then exit 1 else echo "✅ FFI documentation is up to date" -fi \ No newline at end of file +fi diff --git a/dash-spv-ffi/scripts/generate_ffi_docs.py b/dash-spv-ffi/scripts/generate_ffi_docs.py index d3a361691..1c5d2b719 100644 --- a/dash-spv-ffi/scripts/generate_ffi_docs.py +++ b/dash-spv-ffi/scripts/generate_ffi_docs.py @@ -22,58 +22,104 @@ class FFIFunction: return_type: str = None def extract_ffi_functions(file_path: Path) -> List[FFIFunction]: - """Extract all #[no_mangle] functions from a Rust file.""" - functions = [] - + """Extract all #[no_mangle] extern "C" functions from a Rust file. + + Uses a lightweight parser to correctly capture parameters with nested parentheses + (e.g., function pointer types) and the return type. + """ + functions: List[FFIFunction] = [] + with open(file_path, 'r') as f: content = f.read() - - # Find all #[no_mangle] functions with their documentation - pattern = r'(?:///.*\n)*(?:\s*#\[no_mangle\]\s*\n)(?:pub\s+)?(?:unsafe\s+)?extern\s+"C"\s+fn\s+(\w+)\s*\(((?:[^)]|\n)*)\)\s*(?:->\s*([^{]+))?' - - for match in re.finditer(pattern, content): - full_match = match.group(0) - func_name = match.group(1) - params = match.group(2) - return_type = match.group(3) if match.group(3) else "()" - - # Extract doc comments - doc_lines = [] - for line in full_match.split('\n'): + + # Iterate over all #[no_mangle] attribute occurrences + for m in re.finditer(r'(?m)^\s*#\[no_mangle\]\s*$', content): + idx = m.end() + # Find the next extern "C" fn declaration + fn_match = re.search(r'\bextern\s+"C"\s+fn\s+([A-Za-z0-9_]+)\s*\(', content[idx:], re.S) + if not fn_match: + continue + name = fn_match.group(1) + abs_start = idx + fn_match.start() + paren_start = content.find('(', abs_start) + if paren_start == -1: + continue + + # Scan for the matching closing parenthesis with nesting + depth = 0 + i = paren_start + while i < len(content): + ch = content[i] + if ch == '(': + depth += 1 + elif ch == ')': + depth -= 1 + if depth == 0: + break + i += 1 + if depth != 0: + continue # Unbalanced; skip + paren_end = i + + params_raw = content[paren_start + 1:paren_end] + + # Find return type between paren_end and the opening brace '{' + brace_idx = content.find('{', paren_end) + header_tail = content[paren_end:brace_idx if brace_idx != -1 else len(content)] + ret_match = re.search(r'->\s*([^\n{]+)', header_tail) + return_type = ret_match.group(1).strip() if ret_match else '()' + + # Collect contiguous doc comments above #[no_mangle] + # Walk backwards line-by-line accumulating lines starting with '///' + doc_lines_rev: List[str] = [] + # Position of start of the #[no_mangle] line + line_start = content.rfind('\n', 0, m.start()) + 1 + j = line_start - 1 + while j > 0: + prev_nl = content.rfind('\n', 0, j) + line = content[prev_nl + 1:j] if line.strip().startswith('///'): - doc_lines.append(line.strip()[3:].strip()) - - # Extract safety comments + # Strip leading /// and whitespace + doc_lines_rev.append(line.strip()[3:].strip()) + j = prev_nl + continue + # Allow a single blank line between doc comment blocks + if line.strip() == '' and doc_lines_rev: + j = prev_nl + continue + break + doc_lines = list(reversed(doc_lines_rev)) if doc_lines_rev else [] + + # Extract safety sub-section from doc lines safety_comment = None - if '# Safety' in '\n'.join(doc_lines): - safety_start = False - safety_lines = [] - for line in doc_lines: - if '# Safety' in line: - safety_start = True - continue - if safety_start: - if line.startswith('#'): + if doc_lines: + joined = '\n'.join(doc_lines) + if '# Safety' in joined: + safety_lines: List[str] = [] + in_safety = False + for dl in doc_lines: + if dl.strip().startswith('# Safety'): + in_safety = True + continue + if in_safety and dl.strip().startswith('#'): break - safety_lines.append(line) - safety_comment = ' '.join(safety_lines).strip() - - # Clean up parameters - params_clean = re.sub(r'\s+', ' ', params.strip()) - return_type_clean = return_type.strip() - + if in_safety: + safety_lines.append(dl) + safety_comment = ' '.join(safety_lines).strip() if safety_lines else None + + params_clean = re.sub(r'\s+', ' ', params_raw.strip()) module_name = file_path.stem - + functions.append(FFIFunction( - name=func_name, - signature=f"{func_name}({params_clean}) -> {return_type_clean}", + name=name, + signature=f"{name}({params_clean}) -> {return_type}", module=module_name, doc_comment=' '.join(doc_lines) if doc_lines else None, safety_comment=safety_comment, params=params_clean, - return_type=return_type_clean + return_type=return_type, )) - + return functions def categorize_functions(functions: List[FFIFunction]) -> Dict[str, List[FFIFunction]]: @@ -307,7 +353,7 @@ def main(): all_functions = [] - for rust_file in src_dir.glob("*.rs"): + for rust_file in src_dir.rglob("*.rs"): functions = extract_ffi_functions(rust_file) all_functions.extend(functions) @@ -325,4 +371,4 @@ def main(): return 0 if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index e00a47161..2b7148e14 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -1080,7 +1080,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_record_send( /// /// The caller must ensure that: /// - The client pointer is valid -/// - The returned pointer is freed using wallet_manager_free() +/// - The returned pointer is freed using `wallet_manager_free` from key-wallet-ffi /// /// # Returns /// diff --git a/dash-spv-ffi/src/config.rs b/dash-spv-ffi/src/config.rs index 985e2d2df..fb52a0eb0 100644 --- a/dash-spv-ffi/src/config.rs +++ b/dash-spv-ffi/src/config.rs @@ -230,7 +230,7 @@ pub unsafe extern "C" fn dash_spv_ffi_config_get_network( /// # 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_string_free +/// - 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, diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index 6ef6791cf..b2db10a5c 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 221 +**Total Functions**: 222 ## Table of Contents @@ -66,7 +66,7 @@ Functions: 17 ### Wallet Operations -Functions: 56 +Functions: 57 | Function | Description | Module | |----------|-------------|--------| @@ -122,6 +122,7 @@ Functions: 56 | `wallet_get_account_xpub` | Get extended public key for account # Safety - `wallet` must be a valid poi... | keys | | `wallet_get_id` | Get wallet ID (32-byte hash) # Safety - `wallet` must be a valid pointer to... | wallet | | `wallet_get_top_up_account_with_registration_index` | Get an IdentityTopUp account handle with a specific registration index This i... | account | +| `wallet_get_utxos` | Get all UTXOs (deprecated - use managed_wallet_get_utxos instead) # Safety ... | utxo | | `wallet_get_xpub` | Get extended public key for account # Safety - `wallet` must be a valid poi... | wallet | | `wallet_has_mnemonic` | Check if wallet has mnemonic # Safety - `wallet` must be a valid pointer to... | wallet | | `wallet_is_watch_only` | Check if wallet is watch-only # Safety - `wallet` must be a valid pointer t... | wallet | @@ -1467,6 +1468,22 @@ Get an IdentityTopUp account handle with a specific registration index This is u --- +#### `wallet_get_utxos` + +```c +wallet_get_utxos(_wallet: *const crate::types::FFIWallet, _network: FFINetworks, utxos_out: *mut *mut FFIUTXO, count_out: *mut usize, error: *mut FFIError,) -> bool +``` + +**Description:** +Get all UTXOs (deprecated - use managed_wallet_get_utxos instead) # Safety This function is deprecated and returns an empty list. Use `managed_wallet_get_utxos` with a ManagedWalletInfo instead. + +**Safety:** +This function is deprecated and returns an empty list. Use `managed_wallet_get_utxos` with a ManagedWalletInfo instead. + +**Module:** `utxo` + +--- + #### `wallet_get_xpub` ```c diff --git a/key-wallet-ffi/FFI_DOCS_README.md b/key-wallet-ffi/FFI_DOCS_README.md index 9b0a37e97..00257e0e6 100644 --- a/key-wallet-ffi/FFI_DOCS_README.md +++ b/key-wallet-ffi/FFI_DOCS_README.md @@ -88,7 +88,7 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_balance( manager: *const FFIWalletManager, wallet_id: *const u8, wallet_id_len: usize, - network: FFINetworks, + network: FFINetwork, error: *mut FFIError, ) -> FFIBalance { // Implementation @@ -108,4 +108,4 @@ The documentation verification is integrated into the CI pipeline: - `scripts/generate_ffi_docs.py` - Python script that parses Rust files and generates documentation - `scripts/check_ffi_docs.sh` - Bash script to verify documentation is current - `.github/workflows/verify-ffi-docs.yml` - GitHub Action for CI verification -- `Makefile` - Convenient commands for documentation tasks \ No newline at end of file +- `Makefile` - Convenient commands for documentation tasks diff --git a/key-wallet-ffi/include/key_wallet_ffi_test.h b/key-wallet-ffi/include/key_wallet_ffi_test.h new file mode 100644 index 000000000..987427650 --- /dev/null +++ b/key-wallet-ffi/include/key_wallet_ffi_test.h @@ -0,0 +1,3452 @@ +/** + * Key Wallet FFI - C Header File + * + * This header provides C-compatible function declarations for the key-wallet + * Rust library FFI bindings. + * + * AUTO-GENERATED FILE - DO NOT EDIT + * Generated using cbindgen + */ + +#ifndef KEY_WALLET_FFI_H +#define KEY_WALLET_FFI_H + +/* Generated with cbindgen:0.29.0 */ + +/* Warning: This file is auto-generated by cbindgen. Do not modify manually. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + FFI Account Creation Option Type + */ +typedef enum { + /* + Create default accounts (BIP44 account 0, CoinJoin account 0, and special accounts) + */ + DEFAULT = 0, + /* + Create all specified accounts plus all special purpose accounts + */ + ALL_ACCOUNTS = 1, + /* + Create only BIP44 accounts (no CoinJoin or special accounts) + */ + BIP44_ACCOUNTS_ONLY = 2, + /* + Create specific accounts with full control + */ + SPECIFIC_ACCOUNTS = 3, + /* + Create no accounts at all + */ + NO_ACCOUNTS = 4, +} FFIAccountCreationOptionType; + +/* + Account type enumeration matching all key_wallet AccountType variants + + This enum provides a complete FFI representation of all account types + supported by the key_wallet library: + + - Standard accounts: BIP44 and BIP32 variants for regular transactions + - CoinJoin: Privacy-enhanced transactions + - Identity accounts: Registration, top-up, and invitation funding + - Provider accounts: Various masternode provider key types (voting, owner, operator, platform) + */ +typedef enum { + /* + Standard BIP44 account (m/44'/coin_type'/account'/x/x) + */ + STANDARD_BIP44 = 0, + /* + Standard BIP32 account (m/account'/x/x) + */ + STANDARD_BIP32 = 1, + /* + CoinJoin account for private transactions + */ + COIN_JOIN = 2, + /* + Identity registration funding + */ + IDENTITY_REGISTRATION = 3, + /* + Identity top-up funding (requires registration_index) + */ + IDENTITY_TOP_UP = 4, + /* + Identity top-up funding not bound to a specific identity + */ + IDENTITY_TOP_UP_NOT_BOUND_TO_IDENTITY = 5, + /* + Identity invitation funding + */ + IDENTITY_INVITATION = 6, + /* + Provider voting keys (DIP-3) - Path: m/9'/5'/3'/1'/[key_index] + */ + PROVIDER_VOTING_KEYS = 7, + /* + Provider owner keys (DIP-3) - Path: m/9'/5'/3'/2'/[key_index] + */ + PROVIDER_OWNER_KEYS = 8, + /* + Provider operator keys (DIP-3) - Path: m/9'/5'/3'/3'/[key_index] + */ + PROVIDER_OPERATOR_KEYS = 9, + /* + Provider platform P2P keys (DIP-3, ED25519) - Path: m/9'/5'/3'/4'/[key_index] + */ + PROVIDER_PLATFORM_KEYS = 10, +} FFIAccountType; + +/* + Address pool type + */ +typedef enum { + /* + External (receive) addresses + */ + EXTERNAL = 0, + /* + Internal (change) addresses + */ + INTERNAL = 1, + /* + Single pool (for non-standard accounts) + */ + SINGLE = 2, +} FFIAddressPoolType; + +/* + Derivation path type for DIP9 + */ +typedef enum { + PATH_UNKNOWN = 0, + PATH_BIP32 = 1, + PATH_BIP44 = 2, + PATH_BLOCKCHAIN_IDENTITIES = 3, + PATH_PROVIDER_FUNDS = 4, + PATH_PROVIDER_VOTING_KEYS = 5, + PATH_PROVIDER_OPERATOR_KEYS = 6, + PATH_PROVIDER_OWNER_KEYS = 7, + PATH_CONTACT_BASED_FUNDS = 8, + PATH_CONTACT_BASED_FUNDS_ROOT = 9, + PATH_CONTACT_BASED_FUNDS_EXTERNAL = 10, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_REGISTRATION_FUNDING = 11, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_TOPUP_FUNDING = 12, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_INVITATION_FUNDING = 13, + PATH_PROVIDER_PLATFORM_NODE_KEYS = 14, + PATH_COIN_JOIN = 15, + PATH_ROOT = 255, +} FFIDerivationPathType; + +/* + FFI Error code + */ +typedef enum { + SUCCESS = 0, + INVALID_INPUT = 1, + ALLOCATION_FAILED = 2, + INVALID_MNEMONIC = 3, + INVALID_DERIVATION_PATH = 4, + INVALID_NETWORK = 5, + INVALID_ADDRESS = 6, + INVALID_TRANSACTION = 7, + WALLET_ERROR = 8, + SERIALIZATION_ERROR = 9, + NOT_FOUND = 10, + INVALID_STATE = 11, + INTERNAL_ERROR = 12, +} FFIErrorCode; + +/* + Language enumeration for mnemonic generation + + This enum must be kept in sync with key_wallet::mnemonic::Language. + When adding new languages to the key_wallet crate, remember to update + this FFI enum and both From implementations below. + */ +typedef enum { + ENGLISH = 0, + CHINESE_SIMPLIFIED = 1, + CHINESE_TRADITIONAL = 2, + CZECH = 3, + FRENCH = 4, + ITALIAN = 5, + JAPANESE = 6, + KOREAN = 7, + PORTUGUESE = 8, + SPANISH = 9, +} FFILanguage; + +/* + FFI Network type (bit flags for multiple networks) + */ +typedef enum { + NO_NETWORKS = 0, + DASH = 1, + TESTNET = 2, + REGTEST = 4, + DEVNET = 8, + ALL_NETWORKS = 15, +} FFINetworks; + +/* + Provider key type + */ +typedef enum { + /* + BLS voting keys (m/9'/5'/3'/1'/[key_index]) + */ + VOTING_KEYS = 0, + /* + BLS owner keys (m/9'/5'/3'/2'/[key_index]) + */ + OWNER_KEYS = 1, + /* + BLS operator keys (m/9'/5'/3'/3'/[key_index]) + */ + OPERATOR_KEYS = 2, + /* + EdDSA platform P2P keys (m/9'/5'/3'/4'/[key_index]) + */ + PLATFORM_KEYS = 3, +} FFIProviderKeyType; + +/* + Transaction context for checking + */ +typedef enum { + /* + Transaction is in mempool (unconfirmed) + */ + MEMPOOL = 0, + /* + Transaction is in a block + */ + IN_BLOCK = 1, + /* + Transaction is in a chain-locked block + */ + IN_CHAIN_LOCKED_BLOCK = 2, +} FFITransactionContext; + +/* + Opaque account handle + */ +typedef struct FFIAccount FFIAccount; + +/* + Opaque handle to an account collection + */ +typedef struct FFIAccountCollection FFIAccountCollection; + +/* + FFI wrapper for an AddressPool from a ManagedAccount + + This is a lightweight wrapper that holds a reference to an AddressPool + from within a ManagedAccount. It allows querying addresses and pool information. + */ +typedef struct FFIAddressPool FFIAddressPool; + +/* + Opaque BLS account handle + */ +typedef struct FFIBLSAccount FFIBLSAccount; + +/* + Opaque EdDSA account handle + */ +typedef struct FFIEdDSAAccount FFIEdDSAAccount; + +/* + Extended private key structure + */ +typedef struct FFIExtendedPrivKey FFIExtendedPrivKey; + +/* + Opaque type for an extended private key + */ +typedef struct FFIExtendedPrivateKey FFIExtendedPrivateKey; + +/* + Extended public key structure + */ +typedef struct FFIExtendedPubKey FFIExtendedPubKey; + +/* + Opaque type for an extended public key + */ +typedef struct FFIExtendedPublicKey FFIExtendedPublicKey; + +/* + Opaque managed account handle that wraps ManagedAccount + */ +typedef struct FFIManagedAccount FFIManagedAccount; + +/* + Opaque handle to a managed account collection + */ +typedef struct FFIManagedAccountCollection FFIManagedAccountCollection; + +/* + FFI wrapper for ManagedWalletInfo + */ +typedef struct FFIManagedWalletInfo FFIManagedWalletInfo; + +/* + Opaque type for a private key (SecretKey) + */ +typedef struct FFIPrivateKey FFIPrivateKey; + +/* + Opaque type for a public key + */ +typedef struct FFIPublicKey FFIPublicKey; + +/* + Opaque wallet handle + */ +typedef struct FFIWallet FFIWallet; + +/* + FFI wrapper for WalletManager + + This struct holds a cloned Arc reference to the WalletManager, + allowing FFI code to interact with it directly without going through + the SPV client. + */ +typedef struct FFIWalletManager FFIWalletManager; + +/* + FFI Result type for Account operations + */ +typedef struct { + /* + The account handle if successful, NULL if error + */ + FFIAccount *account; + /* + Error code (0 = success) + */ + int32_t error_code; + /* + Error message (NULL if success, must be freed by caller if not NULL) + */ + char *error_message; +} FFIAccountResult; + +/* + FFI Error structure + */ +typedef struct { + FFIErrorCode code; + char *message; +} FFIError; + +/* + C-compatible summary of all accounts in a collection + + This struct provides Swift with structured data about all accounts + that exist in the collection, allowing programmatic access to account + indices and presence information. + */ +typedef struct { + /* + Array of BIP44 account indices + */ + unsigned int *bip44_indices; + /* + Number of BIP44 accounts + */ + size_t bip44_count; + /* + Array of BIP32 account indices + */ + unsigned int *bip32_indices; + /* + Number of BIP32 accounts + */ + size_t bip32_count; + /* + Array of CoinJoin account indices + */ + unsigned int *coinjoin_indices; + /* + Number of CoinJoin accounts + */ + size_t coinjoin_count; + /* + Array of identity top-up registration indices + */ + unsigned int *identity_topup_indices; + /* + Number of identity top-up accounts + */ + size_t identity_topup_count; + /* + Whether identity registration account exists + */ + bool has_identity_registration; + /* + Whether identity invitation account exists + */ + bool has_identity_invitation; + /* + Whether identity top-up not bound account exists + */ + bool has_identity_topup_not_bound; + /* + Whether provider voting keys account exists + */ + bool has_provider_voting_keys; + /* + Whether provider owner keys account exists + */ + bool has_provider_owner_keys; + /* + Whether provider operator keys account exists + */ + bool has_provider_operator_keys; + /* + Whether provider platform keys account exists + */ + bool has_provider_platform_keys; +} FFIAccountCollectionSummary; + +/* + FFI wrapper for ManagedWalletInfo that includes transaction checking capabilities + */ +typedef struct { + ManagedWalletInfo *inner; + +} FFIManagedWallet; + +/* + Address pool info + */ +typedef struct { + /* + Pool type + */ + FFIAddressPoolType pool_type; + /* + Number of generated addresses + */ + unsigned int generated_count; + /* + Number of used addresses + */ + unsigned int used_count; + /* + Current gap (unused addresses at the end) + */ + unsigned int current_gap; + /* + Gap limit setting + */ + unsigned int gap_limit; + /* + Highest used index (-1 if none used) + */ + int32_t highest_used_index; +} FFIAddressPoolInfo; + +/* + FFI-compatible version of AddressInfo + */ +typedef struct { + /* + Address as string + */ + char *address; + /* + Script pubkey bytes + */ + uint8_t *script_pubkey; + /* + Length of script pubkey + */ + size_t script_pubkey_len; + /* + Public key bytes (nullable) + */ + uint8_t *public_key; + /* + Length of public key + */ + size_t public_key_len; + /* + Derivation index + */ + uint32_t index; + /* + Derivation path as string + */ + char *path; + /* + Whether address has been used + */ + bool used; + /* + When generated (timestamp) + */ + uint64_t generated_at; + /* + When first used (0 if never) + */ + uint64_t used_at; + /* + Transaction count + */ + uint32_t tx_count; + /* + Total received + */ + uint64_t total_received; + /* + Total sent + */ + uint64_t total_sent; + /* + Current balance + */ + uint64_t balance; + /* + Custom label (nullable) + */ + char *label; +} FFIAddressInfo; + +/* + FFI Result type for ManagedAccount operations + */ +typedef struct { + /* + The managed account handle if successful, NULL if error + */ + FFIManagedAccount *account; + /* + Error code (0 = success) + */ + int32_t error_code; + /* + Error message (NULL if success, must be freed by caller if not NULL) + */ + char *error_message; +} FFIManagedAccountResult; + +/* + FFI Balance type for representing wallet balances + */ +typedef struct { + /* + Confirmed balance in duffs + */ + uint64_t confirmed; + /* + Unconfirmed balance in duffs + */ + uint64_t unconfirmed; + /* + Immature balance in duffs (e.g., mining rewards) + */ + uint64_t immature; + /* + Total balance (confirmed + unconfirmed) in duffs + */ + uint64_t total; +} FFIBalance; + +/* + C-compatible summary of all accounts in a managed collection + + This struct provides Swift with structured data about all accounts + that exist in the managed collection, allowing programmatic access to account + indices and presence information. + */ +typedef struct { + /* + Array of BIP44 account indices + */ + unsigned int *bip44_indices; + /* + Number of BIP44 accounts + */ + size_t bip44_count; + /* + Array of BIP32 account indices + */ + unsigned int *bip32_indices; + /* + Number of BIP32 accounts + */ + size_t bip32_count; + /* + Array of CoinJoin account indices + */ + unsigned int *coinjoin_indices; + /* + Number of CoinJoin accounts + */ + size_t coinjoin_count; + /* + Array of identity top-up registration indices + */ + unsigned int *identity_topup_indices; + /* + Number of identity top-up accounts + */ + size_t identity_topup_count; + /* + Whether identity registration account exists + */ + bool has_identity_registration; + /* + Whether identity invitation account exists + */ + bool has_identity_invitation; + /* + Whether identity top-up not bound account exists + */ + bool has_identity_topup_not_bound; + /* + Whether provider voting keys account exists + */ + bool has_provider_voting_keys; + /* + Whether provider owner keys account exists + */ + bool has_provider_owner_keys; + /* + Whether provider operator keys account exists + */ + bool has_provider_operator_keys; + /* + Whether provider platform keys account exists + */ + bool has_provider_platform_keys; +} FFIManagedAccountCollectionSummary; + +/* + Provider key info + */ +typedef struct { + /* + Key index + */ + unsigned int key_index; + /* + Public key bytes (48 bytes for BLS, 32 bytes for EdDSA) + */ + uint8_t *public_key; + /* + Public key length + */ + size_t public_key_len; + /* + Private key bytes (32 bytes, only if available) + */ + uint8_t *private_key; + /* + Private key length (0 if not available) + */ + size_t private_key_len; + /* + Derivation path as string + */ + char *derivation_path; +} FFIProviderKeyInfo; + +/* + Transaction output for building + */ +typedef struct { + const char *address; + uint64_t amount; +} FFITxOutput; + +/* + Transaction check result + */ +typedef struct { + /* + Whether the transaction belongs to the wallet + */ + bool is_relevant; + /* + Total amount received + */ + uint64_t total_received; + /* + Total amount sent + */ + uint64_t total_sent; + /* + Number of affected accounts + */ + uint32_t affected_accounts_count; +} FFITransactionCheckResult; + +/* + UTXO structure for FFI + */ +typedef struct { + uint8_t txid[32]; + uint32_t vout; + uint64_t amount; + char *address; + uint8_t *script_pubkey; + size_t script_len; + uint32_t height; + uint32_t confirmations; +} FFIUTXO; + +/* + FFI structure for wallet account creation options + This single struct represents all possible account creation configurations + */ +typedef struct { + /* + The type of account creation option + */ + FFIAccountCreationOptionType option_type; + /* + Array of BIP44 account indices to create + */ + const uint32_t *bip44_indices; + size_t bip44_count; + /* + Array of BIP32 account indices to create + */ + const uint32_t *bip32_indices; + size_t bip32_count; + /* + Array of CoinJoin account indices to create + */ + const uint32_t *coinjoin_indices; + size_t coinjoin_count; + /* + Array of identity top-up registration indices to create + */ + const uint32_t *topup_indices; + size_t topup_count; + /* + For SpecificAccounts: Additional special account types to create + (e.g., IdentityRegistration, ProviderKeys, etc.) + This is an array of FFIAccountType values + */ + const FFIAccountType *special_account_types; + size_t special_account_types_count; +} FFIWalletAccountCreationOptions; + +/* + FFI-compatible transaction context details + */ +typedef struct { + /* + The context type + */ + FFITransactionContext context_type; + /* + Block height (0 for mempool) + */ + unsigned int height; + /* + Block hash (32 bytes, null for mempool or if unknown) + */ + const uint8_t *block_hash; + /* + Timestamp (0 if unknown) + */ + unsigned int timestamp; +} FFITransactionContextDetails; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* + Initialize the library + */ + bool key_wallet_ffi_initialize(void) ; + +/* + Get library version + + Returns a static string that should NOT be freed by the caller + */ + const char *key_wallet_ffi_version(void) ; + +/* + Get an account handle for a specific account type + Returns a result containing either the account handle or an error + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - The caller must ensure the wallet pointer remains valid for the duration of this call + */ + +FFIAccountResult wallet_get_account(const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIAccountType account_type) +; + +/* + Get an IdentityTopUp account handle with a specific registration index + This is used for top-up accounts that are bound to a specific identity + Returns a result containing either the account handle or an error + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - The caller must ensure the wallet pointer remains valid for the duration of this call + */ + +FFIAccountResult wallet_get_top_up_account_with_registration_index(const FFIWallet *wallet, + FFINetworks network, + unsigned int registration_index) +; + +/* + Free an account handle + + # Safety + + - `account` must be a valid pointer to an FFIAccount that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void account_free(FFIAccount *account) ; + +/* + Free a BLS account handle + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void bls_account_free(FFIBLSAccount *account) ; + +/* + Free an EdDSA account handle + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void eddsa_account_free(FFIEdDSAAccount *account) ; + +/* + Free an account result's error message (if any) + Note: This does NOT free the account handle itself - use account_free for that + + # Safety + + - `result` must be a valid pointer to an FFIAccountResult + - The error_message field must be either null or a valid CString allocated by this library + - The caller must ensure the result pointer remains valid for the duration of this call + */ + void account_result_free_error(FFIAccountResult *result) ; + +/* + Get the extended public key of an account as a string + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *account_get_extended_public_key_as_string(const FFIAccount *account) ; + +/* + Get the network of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks account_get_network(const FFIAccount *account) ; + +/* + Get the parent wallet ID of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *account_get_parent_wallet_id(const FFIAccount *account) ; + +/* + Get the account type of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + FFIAccountType account_get_account_type(const FFIAccount *account, unsigned int *out_index) ; + +/* + Check if an account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns false if the account is null + */ + bool account_get_is_watch_only(const FFIAccount *account) ; + +/* + Get the extended public key of a BLS account as a string + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *bls_account_get_extended_public_key_as_string(const FFIBLSAccount *account) ; + +/* + Get the network of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks bls_account_get_network(const FFIBLSAccount *account) ; + +/* + Get the parent wallet ID of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *bls_account_get_parent_wallet_id(const FFIBLSAccount *account) ; + +/* + Get the account type of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + +FFIAccountType bls_account_get_account_type(const FFIBLSAccount *account, + unsigned int *out_index) +; + +/* + Check if a BLS account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns false if the account is null + */ + bool bls_account_get_is_watch_only(const FFIBLSAccount *account) ; + +/* + Get the extended public key of an EdDSA account as a string + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *eddsa_account_get_extended_public_key_as_string(const FFIEdDSAAccount *account) ; + +/* + Get the network of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks eddsa_account_get_network(const FFIEdDSAAccount *account) ; + +/* + Get the parent wallet ID of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *eddsa_account_get_parent_wallet_id(const FFIEdDSAAccount *account) ; + +/* + Get the account type of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + +FFIAccountType eddsa_account_get_account_type(const FFIEdDSAAccount *account, + unsigned int *out_index) +; + +/* + Check if an EdDSA account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns false if the account is null + */ + bool eddsa_account_get_is_watch_only(const FFIEdDSAAccount *account) ; + +/* + Get number of accounts + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure both pointers remain valid for the duration of this call + */ + +unsigned int wallet_get_account_count(const FFIWallet *wallet, + FFINetworks network, + FFIError *error) +; + +/* + Get account collection for a specific network from wallet + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The returned pointer must be freed with `account_collection_free` when no longer needed + */ + +FFIAccountCollection *wallet_get_account_collection(const FFIWallet *wallet, + FFINetworks network, + FFIError *error) +; + +/* + Free an account collection handle + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection created by this library + - `collection` must not be used after calling this function + */ + void account_collection_free(FFIAccountCollection *collection) ; + +/* + Get a BIP44 account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_bip44_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP44 account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_bip44_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a BIP32 account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_bip32_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP32 account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_bip32_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a CoinJoin account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_coinjoin_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all CoinJoin account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_coinjoin_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity registration account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_identity_registration(const FFIAccountCollection *collection) ; + +/* + Check if identity registration account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_registration(const FFIAccountCollection *collection) ; + +/* + Get an identity topup account by registration index + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_identity_topup(const FFIAccountCollection *collection, + unsigned int registration_index) +; + +/* + Get all identity topup registration indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_identity_topup_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity topup not bound account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_identity_topup_not_bound(const FFIAccountCollection *collection) +; + +/* + Check if identity topup not bound account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_topup_not_bound(const FFIAccountCollection *collection) ; + +/* + Get the identity invitation account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_identity_invitation(const FFIAccountCollection *collection) ; + +/* + Check if identity invitation account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_invitation(const FFIAccountCollection *collection) ; + +/* + Get the provider voting keys account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_provider_voting_keys(const FFIAccountCollection *collection) ; + +/* + Check if provider voting keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_voting_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider owner keys account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_provider_owner_keys(const FFIAccountCollection *collection) ; + +/* + Check if provider owner keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_owner_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider operator keys account if it exists + Note: Returns null if the `bls` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `bls_account_free` when no longer needed (when BLS is enabled) + */ + +void *account_collection_get_provider_operator_keys(const FFIAccountCollection *collection) +; + +/* + Check if provider operator keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_operator_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider platform keys account if it exists + Note: Returns null if the `eddsa` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `eddsa_account_free` when no longer needed (when EdDSA is enabled) + */ + +void *account_collection_get_provider_platform_keys(const FFIAccountCollection *collection) +; + +/* + Check if provider platform keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_platform_keys(const FFIAccountCollection *collection) ; + +/* + Free a u32 array allocated by this library + + # Safety + + - `array` must be a valid pointer to an array allocated by this library + - `array` must not be used after calling this function + */ + void free_u32_array(unsigned int *array, size_t count) ; + +/* + Get the total number of accounts in the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + unsigned int account_collection_count(const FFIAccountCollection *collection) ; + +/* + Get a human-readable summary of all accounts in the collection + + Returns a formatted string showing all account types and their indices. + The format is designed to be clear and readable for end users. + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned string must be freed with `string_free` when no longer needed + - Returns null if the collection pointer is null + */ + char *account_collection_summary(const FFIAccountCollection *collection) ; + +/* + Get structured account collection summary data + + Returns a struct containing arrays of indices for each account type and boolean + flags for special accounts. This provides Swift with programmatic access to + account information. + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_collection_summary_free` when no longer needed + - Returns null if the collection pointer is null + */ + +FFIAccountCollectionSummary *account_collection_summary_data(const FFIAccountCollection *collection) +; + +/* + Free an account collection summary and all its allocated memory + + # Safety + + - `summary` must be a valid pointer to an FFIAccountCollectionSummary created by `account_collection_summary_data` + - `summary` must not be used after calling this function + */ + +void account_collection_summary_free(FFIAccountCollectionSummary *summary) +; + +/* + Free address string + + # Safety + + - `address` must be a valid pointer created by address functions or null + - After calling this function, the pointer becomes invalid + */ + void address_free(char *address) ; + +/* + Free address array + + # Safety + + - `addresses` must be a valid pointer to an array of address strings or null + - Each address in the array must be a valid C string pointer + - `count` must be the correct number of addresses in the array + - After calling this function, all pointers become invalid + */ + void address_array_free(char **addresses, size_t count) ; + +/* + Validate an address + + # Safety + + - `address` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + */ + bool address_validate(const char *address, FFINetworks network, FFIError *error) ; + +/* + Get address type + + Returns: + - 0: P2PKH address + - 1: P2SH address + - 2: Other address type + - u8::MAX (255): Error occurred + + # Safety + + - `address` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + */ + unsigned char address_get_type(const char *address, FFINetworks network, FFIError *error) ; + +/* + Free an address pool handle + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void address_pool_free(FFIAddressPool *pool) ; + +/* + Get address pool information for an account + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `info_out` must be a valid pointer to store the pool info + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_get_address_pool_info(const FFIManagedWallet *managed_wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + FFIAddressPoolInfo *info_out, + FFIError *error) +; + +/* + Set the gap limit for an address pool + + The gap limit determines how many unused addresses to maintain at the end + of the pool. This is important for wallet recovery and address discovery. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_set_gap_limit(FFIManagedWallet *managed_wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + unsigned int gap_limit, + FFIError *error) +; + +/* + Generate addresses up to a specific index in a pool + + This ensures that addresses up to and including the specified index exist + in the pool. This is useful for wallet recovery or when specific indices + are needed. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `wallet` must be a valid pointer to an FFIWallet (for key derivation) + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_generate_addresses_to_index(FFIManagedWallet *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + unsigned int target_index, + FFIError *error) +; + +/* + Mark an address as used in the pool + + This updates the pool's tracking of which addresses have been used, + which is important for gap limit management and wallet recovery. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `address` must be a valid C string + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_mark_address_used(FFIManagedWallet *managed_wallet, + FFINetworks network, + const char *address, + FFIError *error) +; + +/* + Get a single address info at a specific index from the pool + + Returns detailed information about the address at the given index, or NULL + if the index is out of bounds or not generated yet. + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool + - `error` must be a valid pointer to an FFIError or null + - The returned FFIAddressInfo must be freed using `address_info_free` + */ + +FFIAddressInfo *address_pool_get_address_at_index(const FFIAddressPool *pool, + uint32_t index, + FFIError *error) +; + +/* + Get a range of addresses from the pool + + Returns an array of FFIAddressInfo structures for addresses in the range [start_index, end_index). + The count_out parameter will be set to the actual number of addresses returned. + + Note: This function only reads existing addresses from the pool. It does not generate new addresses. + Use managed_wallet_generate_addresses_to_index if you need to generate addresses first. + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError or null + - The returned array must be freed using `address_info_array_free` + */ + +FFIAddressInfo **address_pool_get_addresses_in_range(const FFIAddressPool *pool, + uint32_t start_index, + uint32_t end_index, + size_t *count_out, + FFIError *error) +; + +/* + Free a single FFIAddressInfo structure + + # Safety + + - `info` must be a valid pointer to an FFIAddressInfo allocated by this library or null + - The pointer must not be used after calling this function + */ + void address_info_free(FFIAddressInfo *info) ; + +/* + Free an array of FFIAddressInfo structures + + # Safety + + - `infos` must be a valid pointer to an array of FFIAddressInfo pointers allocated by this library or null + - `count` must be the exact number of elements in the array + - The pointers must not be used after calling this function + */ + +void address_info_array_free(FFIAddressInfo **infos, + size_t count) +; + +/* + Create a new master extended private key from seed + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the seed pointer remains valid for the duration of this call + */ + +FFIExtendedPrivKey *derivation_new_master_key(const uint8_t *seed, + size_t seed_len, + FFINetworks network, + FFIError *error) +; + +/* + Derive a BIP44 account path (m/44'/5'/account') + */ + +bool derivation_bip44_account_path(FFINetworks network, + unsigned int account_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive a BIP44 payment path (m/44'/5'/account'/change/index) + */ + +bool derivation_bip44_payment_path(FFINetworks network, + unsigned int account_index, + bool is_change, + unsigned int address_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive CoinJoin path (m/9'/5'/4'/account') + */ + +bool derivation_coinjoin_path(FFINetworks network, + unsigned int account_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity registration path (m/9'/5'/5'/1'/index') + */ + +bool derivation_identity_registration_path(FFINetworks network, + unsigned int identity_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') + */ + +bool derivation_identity_topup_path(FFINetworks network, + unsigned int identity_index, + unsigned int topup_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') + */ + +bool derivation_identity_authentication_path(FFINetworks network, + unsigned int identity_index, + unsigned int key_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive private key for a specific path from seed + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `path` must be a valid pointer to a null-terminated C string + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIExtendedPrivKey *derivation_derive_private_key_from_seed(const uint8_t *seed, + size_t seed_len, + const char *path, + FFINetworks network, + FFIError *error) +; + +/* + Derive public key from extended private key + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_public_key_free` + */ + FFIExtendedPubKey *derivation_xpriv_to_xpub(const FFIExtendedPrivKey *xpriv, FFIError *error) ; + +/* + Get extended private key as string + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *derivation_xpriv_to_string(const FFIExtendedPrivKey *xpriv, FFIError *error) ; + +/* + Get extended public key as string + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *derivation_xpub_to_string(const FFIExtendedPubKey *xpub, FFIError *error) ; + +/* + Get fingerprint from extended public key (4 bytes) + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey + - `fingerprint_out` must be a valid pointer to a buffer of at least 4 bytes + - `error` must be a valid pointer to an FFIError + */ + +bool derivation_xpub_fingerprint(const FFIExtendedPubKey *xpub, + uint8_t *fingerprint_out, + FFIError *error) +; + +/* + Free extended private key + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_xpriv_free(FFIExtendedPrivKey *xpriv) ; + +/* + Free extended public key + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_xpub_free(FFIExtendedPubKey *xpub) ; + +/* + Free derivation path string + + # Safety + + - `s` must be a valid pointer to a C string that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_string_free(char *s) ; + +/* + Derive key using DIP9 path constants for identity + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the seed pointer remains valid for the duration of this call + */ + +FFIExtendedPrivKey *dip9_derive_identity_key(const uint8_t *seed, + size_t seed_len, + FFINetworks network, + unsigned int identity_index, + unsigned int key_index, + FFIDerivationPathType key_type, + FFIError *error) +; + +/* + Free an error message + + # Safety + + - `message` must be a valid pointer to a C string that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void error_message_free(char *message) ; + +/* + Get extended private key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_get_account_xpriv(const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIError *error) +; + +/* + Get extended public key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_get_account_xpub(const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIError *error) +; + +/* + Derive private key at a specific path + Returns an opaque FFIPrivateKey pointer that must be freed with private_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `private_key_free` + */ + +FFIPrivateKey *wallet_derive_private_key(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive extended private key at a specific path + Returns an opaque FFIExtendedPrivateKey pointer that must be freed with extended_private_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_private_key_free` + */ + +FFIExtendedPrivateKey *wallet_derive_extended_private_key(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive private key at a specific path and return as WIF string + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_derive_private_key_as_wif(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Free a private key + + # Safety + + - `key` must be a valid pointer created by private key functions or null + - After calling this function, the pointer becomes invalid + */ + void private_key_free(FFIPrivateKey *key) ; + +/* + Free an extended private key + + # Safety + + - `key` must be a valid pointer created by extended private key functions or null + - After calling this function, the pointer becomes invalid + */ + void extended_private_key_free(FFIExtendedPrivateKey *key) ; + +/* + Get extended private key as string (xprv format) + + Returns the extended private key in base58 format (xprv... for mainnet, tprv... for testnet) + + # Safety + + - `key` must be a valid pointer to an FFIExtendedPrivateKey + - `network` is ignored; the network is encoded in the extended key + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *extended_private_key_to_string(const FFIExtendedPrivateKey *key, + FFINetworks network, + FFIError *error) +; + +/* + Get the private key from an extended private key + + Extracts the non-extended private key from an extended private key. + + # Safety + + - `extended_key` must be a valid pointer to an FFIExtendedPrivateKey + - `error` must be a valid pointer to an FFIError + - The returned FFIPrivateKey must be freed with `private_key_free` + */ + +FFIPrivateKey *extended_private_key_get_private_key(const FFIExtendedPrivateKey *extended_key, + FFIError *error) +; + +/* + Get private key as WIF string from FFIPrivateKey + + # Safety + + - `key` must be a valid pointer to an FFIPrivateKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *private_key_to_wif(const FFIPrivateKey *key, FFINetworks network, FFIError *error) ; + +/* + Derive public key at a specific path + Returns an opaque FFIPublicKey pointer that must be freed with public_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `public_key_free` + */ + +FFIPublicKey *wallet_derive_public_key(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive extended public key at a specific path + Returns an opaque FFIExtendedPublicKey pointer that must be freed with extended_public_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_public_key_free` + */ + +FFIExtendedPublicKey *wallet_derive_extended_public_key(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive public key at a specific path and return as hex string + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_derive_public_key_as_hex(const FFIWallet *wallet, + FFINetworks network, + const char *derivation_path, + FFIError *error) +; + +/* + Free a public key + + # Safety + + - `key` must be a valid pointer created by public key functions or null + - After calling this function, the pointer becomes invalid + */ + void public_key_free(FFIPublicKey *key) ; + +/* + Free an extended public key + + # Safety + + - `key` must be a valid pointer created by extended public key functions or null + - After calling this function, the pointer becomes invalid + */ + void extended_public_key_free(FFIExtendedPublicKey *key) ; + +/* + Get extended public key as string (xpub format) + + Returns the extended public key in base58 format (xpub... for mainnet, tpub... for testnet) + + # Safety + + - `key` must be a valid pointer to an FFIExtendedPublicKey + - `network` is ignored; the network is encoded in the extended key + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *extended_public_key_to_string(const FFIExtendedPublicKey *key, + FFINetworks network, + FFIError *error) +; + +/* + Get the public key from an extended public key + + Extracts the non-extended public key from an extended public key. + + # Safety + + - `extended_key` must be a valid pointer to an FFIExtendedPublicKey + - `error` must be a valid pointer to an FFIError + - The returned FFIPublicKey must be freed with `public_key_free` + */ + +FFIPublicKey *extended_public_key_get_public_key(const FFIExtendedPublicKey *extended_key, + FFIError *error) +; + +/* + Get public key as hex string from FFIPublicKey + + # Safety + + - `key` must be a valid pointer to an FFIPublicKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *public_key_to_hex(const FFIPublicKey *key, FFIError *error) ; + +/* + Convert derivation path string to indices + + # Safety + + - `path` must be a valid null-terminated C string or null + - `indices_out` must be a valid pointer to store the indices array pointer + - `hardened_out` must be a valid pointer to store the hardened flags array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - The returned arrays must be freed with `derivation_path_free` + */ + +bool derivation_path_parse(const char *path, + uint32_t **indices_out, + bool **hardened_out, + size_t *count_out, + FFIError *error) +; + +/* + Free derivation path arrays + Note: This function expects the count to properly free the slices + + # Safety + + - `indices` must be a valid pointer created by `derivation_path_parse` or null + - `hardened` must be a valid pointer created by `derivation_path_parse` or null + - `count` must match the count from `derivation_path_parse` + - After calling this function, the pointers become invalid + */ + void derivation_path_free(uint32_t *indices, bool *hardened, size_t count) ; + +/* + Get a managed account from a managed wallet + + This function gets a ManagedAccount from the wallet manager's managed wallet info, + returning a managed account handle that wraps the ManagedAccount. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - The caller must ensure all pointers remain valid for the duration of this call + - The returned account must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccountResult managed_wallet_get_account(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetworks network, + unsigned int account_index, + FFIAccountType account_type) +; + +/* + Get a managed IdentityTopUp account with a specific registration index + + This is used for top-up accounts that are bound to a specific identity. + Returns a managed account handle that wraps the ManagedAccount. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - The caller must ensure all pointers remain valid for the duration of this call + - The returned account must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccountResult managed_wallet_get_top_up_account_with_registration_index(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetworks network, + unsigned int registration_index) +; + +/* + Get the network of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + FFINetworks managed_account_get_network(const FFIManagedAccount *account) ; + +/* + Get the parent wallet ID of a managed account + + Note: ManagedAccount doesn't store the parent wallet ID directly. + The wallet ID is typically known from the context (e.g., when getting the account from a managed wallet). + + # Safety + + - `wallet_id` must be a valid pointer to a 32-byte wallet ID buffer that was provided by the caller + - The returned pointer is the same as the input pointer for convenience + - The caller must not free the returned pointer as it's the same as the input + */ + +const uint8_t *managed_account_get_parent_wallet_id(const uint8_t *wallet_id) +; + +/* + Get the account type of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - `index_out` must be a valid pointer to receive the account index (or null) + */ + +FFIAccountType managed_account_get_account_type(const FFIManagedAccount *account, + unsigned int *index_out) +; + +/* + Check if a managed account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + bool managed_account_get_is_watch_only(const FFIManagedAccount *account) ; + +/* + Get the balance of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - `balance_out` must be a valid pointer to an FFIBalance structure + */ + bool managed_account_get_balance(const FFIManagedAccount *account, FFIBalance *balance_out) ; + +/* + Get the number of transactions in a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_transaction_count(const FFIManagedAccount *account) ; + +/* + Get the number of UTXOs in a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_utxo_count(const FFIManagedAccount *account) ; + +/* + Free a managed account handle + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void managed_account_free(FFIManagedAccount *account) ; + +/* + Free a managed account result's error message (if any) + Note: This does NOT free the account handle itself - use managed_account_free for that + + # Safety + + - `result` must be a valid pointer to an FFIManagedAccountResult + - The error_message field must be either null or a valid CString allocated by this library + - The caller must ensure the result pointer remains valid for the duration of this call + */ + void managed_account_result_free_error(FFIManagedAccountResult *result) ; + +/* + Get number of accounts in a managed wallet + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +unsigned int managed_wallet_get_account_count(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetworks network, + FFIError *error) +; + +/* + Get the account index from a managed account + + Returns the primary account index for Standard and CoinJoin accounts. + Returns 0 for account types that don't have an index (like Identity or Provider accounts). + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_index(const FFIManagedAccount *account) ; + +/* + Get the external address pool from a managed account + + This function returns the external (receive) address pool for Standard accounts. + Returns NULL for account types that don't have separate external/internal pools. + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + FFIAddressPool *managed_account_get_external_address_pool(const FFIManagedAccount *account) ; + +/* + Get the internal address pool from a managed account + + This function returns the internal (change) address pool for Standard accounts. + Returns NULL for account types that don't have separate external/internal pools. + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + FFIAddressPool *managed_account_get_internal_address_pool(const FFIManagedAccount *account) ; + +/* + Get an address pool from a managed account by type + + This function returns the appropriate address pool based on the pool type parameter. + For Standard accounts with External/Internal pool types, returns the corresponding pool. + For non-standard accounts with Single pool type, returns their single address pool. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `account` must be a valid pointer to an FFIManagedAccount instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + +FFIAddressPool *managed_account_get_address_pool(const FFIManagedAccount *account, + FFIAddressPoolType pool_type) +; + +/* + Get managed account collection for a specific network from wallet manager + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The returned pointer must be freed with `managed_account_collection_free` when no longer needed + */ + +FFIManagedAccountCollection *managed_wallet_get_account_collection(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetworks network, + FFIError *error) +; + +/* + Free a managed account collection handle + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection created by this library + - `collection` must not be used after calling this function + */ + void managed_account_collection_free(FFIManagedAccountCollection *collection) ; + +/* + Get a BIP44 account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_bip44_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP44 account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_bip44_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a BIP32 account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_bip32_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP32 account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_bip32_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a CoinJoin account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_coinjoin_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all CoinJoin account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_coinjoin_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity registration account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_registration(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity registration account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_registration(const FFIManagedAccountCollection *collection) +; + +/* + Get an identity topup account by registration index from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_topup(const FFIManagedAccountCollection *collection, + unsigned int registration_index) +; + +/* + Get all identity topup registration indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_identity_topup_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity topup not bound account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `manager` must be a valid pointer to an FFIWalletManager + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_topup_not_bound(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity topup not bound account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_topup_not_bound(const FFIManagedAccountCollection *collection) +; + +/* + Get the identity invitation account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_invitation(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity invitation account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_invitation(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider voting keys account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_provider_voting_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider voting keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_voting_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider owner keys account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_provider_owner_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider owner keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_owner_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider operator keys account if it exists in managed collection + Note: Returns null if the `bls` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed (when BLS is enabled) + */ + +void *managed_account_collection_get_provider_operator_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider operator keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_operator_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider platform keys account if it exists in managed collection + Note: Returns null if the `eddsa` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed (when EdDSA is enabled) + */ + +void *managed_account_collection_get_provider_platform_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider platform keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_platform_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the total number of accounts in the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + unsigned int managed_account_collection_count(const FFIManagedAccountCollection *collection) ; + +/* + Get a human-readable summary of all accounts in the managed collection + + Returns a formatted string showing all account types and their indices. + The format is designed to be clear and readable for end users. + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned string must be freed with `string_free` when no longer needed + - Returns null if the collection pointer is null + */ + char *managed_account_collection_summary(const FFIManagedAccountCollection *collection) ; + +/* + Get structured account collection summary data for managed collection + + Returns a struct containing arrays of indices for each account type and boolean + flags for special accounts. This provides Swift with programmatic access to + account information. + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_collection_summary_free` when no longer needed + - Returns null if the collection pointer is null + */ + +FFIManagedAccountCollectionSummary *managed_account_collection_summary_data(const FFIManagedAccountCollection *collection) +; + +/* + Free a managed account collection summary and all its allocated memory + + # Safety + + - `summary` must be a valid pointer to an FFIManagedAccountCollectionSummary created by `managed_account_collection_summary_data` + - `summary` must not be used after calling this function + */ + +void managed_account_collection_summary_free(FFIManagedAccountCollectionSummary *summary) +; + +/* + Get the next unused receive address + + Generates the next unused receive address for the specified account. + This properly manages address gaps and updates the managed wallet state. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed by the caller + */ + +char *managed_wallet_get_next_bip44_receive_address(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIError *error) +; + +/* + Get the next unused change address + + Generates the next unused change address for the specified account. + This properly manages address gaps and updates the managed wallet state. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed by the caller + */ + +char *managed_wallet_get_next_bip44_change_address(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIError *error) +; + +/* + Get BIP44 external (receive) addresses in the specified range + + Returns external addresses from start_index (inclusive) to end_index (exclusive). + If addresses in the range haven't been generated yet, they will be generated. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `addresses_out` must be a valid pointer to store the address array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - Free the result with address_array_free(addresses_out, count_out) + */ + +bool managed_wallet_get_bip_44_external_address_range(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + unsigned int start_index, + unsigned int end_index, + char ***addresses_out, + size_t *count_out, + FFIError *error) +; + +/* + Get BIP44 internal (change) addresses in the specified range + + Returns internal addresses from start_index (inclusive) to end_index (exclusive). + If addresses in the range haven't been generated yet, they will be generated. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `addresses_out` must be a valid pointer to store the address array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - Free the result with address_array_free(addresses_out, count_out) + */ + +bool managed_wallet_get_bip_44_internal_address_range(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + unsigned int start_index, + unsigned int end_index, + char ***addresses_out, + size_t *count_out, + FFIError *error) +; + +/* + Get wallet balance from managed wallet info + + Returns the balance breakdown including confirmed, unconfirmed, locked, and total amounts. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `confirmed_out` must be a valid pointer to store the confirmed balance + - `unconfirmed_out` must be a valid pointer to store the unconfirmed balance + - `locked_out` must be a valid pointer to store the locked balance + - `total_out` must be a valid pointer to store the total balance + - `error` must be a valid pointer to an FFIError + */ + +bool managed_wallet_get_balance(const FFIManagedWalletInfo *managed_wallet, + uint64_t *confirmed_out, + uint64_t *unconfirmed_out, + uint64_t *locked_out, + uint64_t *total_out, + FFIError *error) +; + +/* + Free managed wallet info + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo or null + - After calling this function, the pointer becomes invalid and must not be used + */ + void managed_wallet_free(FFIManagedWalletInfo *managed_wallet) ; + +/* + Free managed wallet info returned by wallet_manager_get_managed_wallet_info + + # Safety + + - `wallet_info` must be a valid pointer returned by wallet_manager_get_managed_wallet_info or null + - After calling this function, the pointer becomes invalid and must not be used + */ + void managed_wallet_info_free(FFIManagedWalletInfo *wallet_info) ; + +/* + Generate a new mnemonic with specified word count (12, 15, 18, 21, or 24) + */ + char *mnemonic_generate(unsigned int word_count, FFIError *error) ; + +/* + Generate a new mnemonic with specified language and word count + */ + +char *mnemonic_generate_with_language(unsigned int word_count, + FFILanguage language, + FFIError *error) +; + +/* + Validate a mnemonic phrase + + # Safety + + - `mnemonic` must be a valid null-terminated C string or null + - `error` must be a valid pointer to an FFIError + */ + bool mnemonic_validate(const char *mnemonic, FFIError *error) ; + +/* + Convert mnemonic to seed with optional passphrase + + # Safety + + - `mnemonic` must be a valid null-terminated C string + - `passphrase` must be a valid null-terminated C string or null + - `seed_out` must be a valid pointer to a buffer of at least 64 bytes + - `seed_len` must be a valid pointer to store the seed length + - `error` must be a valid pointer to an FFIError + */ + +bool mnemonic_to_seed(const char *mnemonic, + const char *passphrase, + uint8_t *seed_out, + size_t *seed_len, + FFIError *error) +; + +/* + Get word count from mnemonic + + # Safety + + - `mnemonic` must be a valid null-terminated C string or null + - `error` must be a valid pointer to an FFIError + */ + unsigned int mnemonic_word_count(const char *mnemonic, FFIError *error) ; + +/* + Free a mnemonic string + + # Safety + + - `mnemonic` must be a valid pointer created by mnemonic generation functions or null + - After calling this function, the pointer becomes invalid + */ + void mnemonic_free(char *mnemonic) ; + +/* + Generate a provider key at a specific index + + This generates a provider key (BLS or EdDSA) at the specified index. + For voting, owner, and operator keys, this generates BLS keys. + For platform keys, this generates EdDSA keys. + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `info_out` must be a valid pointer to store the key info + - `error` must be a valid pointer to an FFIError or null + - The returned public_key, private_key, and derivation_path must be freed by the caller + */ + +bool wallet_generate_provider_key(const FFIWallet *wallet, + FFINetworks network, + FFIProviderKeyType key_type, + unsigned int key_index, + bool include_private, + FFIProviderKeyInfo *info_out, + FFIError *error) +; + +/* + Free provider key info + + # Safety + + - `info` must be a valid pointer to an FFIProviderKeyInfo + - This function must only be called once per info structure + */ + void provider_key_info_free(FFIProviderKeyInfo *info) ; + +/* + Sign data with a provider key + + This signs arbitrary data with the provider key at the specified index. + For BLS keys, this produces a BLS signature. + For EdDSA keys, this produces an Ed25519 signature. + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `data` must be a valid pointer to data with at least `data_len` bytes + - `signature_out` must be a valid pointer to store the signature pointer + - `signature_len_out` must be a valid pointer to store the signature length + - `error` must be a valid pointer to an FFIError or null + - The returned signature must be freed with `libc::free` + */ + +bool wallet_sign_with_provider_key(const FFIWallet *wallet, + FFINetworks network, + FFIProviderKeyType key_type, + unsigned int _key_index, + const uint8_t *data, + size_t data_len, + uint8_t **signature_out, + size_t *signature_len_out, + FFIError *error) +; + +/* + Build a transaction + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `outputs` must be a valid pointer to an array of FFITxOutput with at least `outputs_count` elements + - `tx_bytes_out` must be a valid pointer to store the transaction bytes pointer + - `tx_len_out` must be a valid pointer to store the transaction length + - `error` must be a valid pointer to an FFIError + - The returned transaction bytes must be freed with `transaction_bytes_free` + */ + +bool wallet_build_transaction(FFIWallet *wallet, + FFINetworks _network, + unsigned int account_index, + const FFITxOutput *outputs, + size_t outputs_count, + uint64_t fee_per_kb, + uint8_t **tx_bytes_out, + size_t *tx_len_out, + FFIError *error) +; + +/* + Sign a transaction + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `signed_tx_out` must be a valid pointer to store the signed transaction bytes pointer + - `signed_len_out` must be a valid pointer to store the signed transaction length + - `error` must be a valid pointer to an FFIError + - The returned signed transaction bytes must be freed with `transaction_bytes_free` + */ + +bool wallet_sign_transaction(const FFIWallet *wallet, + FFINetworks _network, + const uint8_t *tx_bytes, + size_t tx_len, + uint8_t **signed_tx_out, + size_t *signed_len_out, + FFIError *error) +; + +/* + Check if a transaction belongs to the wallet using ManagedWalletInfo + + # Safety + + - `wallet` must be a valid mutable pointer to an FFIWallet + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `inputs_spent_out` must be a valid pointer to store the spent inputs count + - `addresses_used_out` must be a valid pointer to store the used addresses count + - `new_balance_out` must be a valid pointer to store the new balance + - `new_address_out` must be a valid pointer to store the address array pointer + - `new_address_count_out` must be a valid pointer to store the address count + - `error` must be a valid pointer to an FFIError + */ + +bool wallet_check_transaction(FFIWallet *wallet, + FFINetworks network, + const uint8_t *tx_bytes, + size_t tx_len, + FFITransactionContext context_type, + uint32_t block_height, + const uint8_t *block_hash, + uint64_t timestamp, + bool update_state, + FFITransactionCheckResult *result_out, + FFIError *error) +; + +/* + Free transaction bytes + + # Safety + + - `tx_bytes` must be a valid pointer created by transaction functions or null + - After calling this function, the pointer becomes invalid + */ + void transaction_bytes_free(uint8_t *tx_bytes) ; + +/* + Create a managed wallet from a regular wallet + + This creates a ManagedWalletInfo instance from a Wallet, which includes + address pools and transaction checking capabilities. + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError or null + - The returned pointer must be freed with `ffi_managed_wallet_free` + */ + FFIManagedWallet *wallet_create_managed_wallet(const FFIWallet *wallet, FFIError *error) ; + +/* + Check if a transaction belongs to the wallet + + This function checks a transaction against all relevant account types in the wallet + and returns detailed information about which accounts are affected. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `wallet` must be a valid pointer to an FFIWallet (needed for address generation) + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `result_out` must be a valid pointer to store the result + - `error` must be a valid pointer to an FFIError + - The affected_accounts array in the result must be freed with `transaction_check_result_free` + */ + +bool managed_wallet_check_transaction(FFIManagedWallet *managed_wallet, + const FFIWallet *wallet, + FFINetworks network, + const uint8_t *tx_bytes, + size_t tx_len, + FFITransactionContext context_type, + unsigned int block_height, + const uint8_t *block_hash, + uint64_t timestamp, + bool update_state, + FFITransactionCheckResult *result_out, + FFIError *error) +; + +/* + Free a transaction check result + + # Safety + + - `result` must be a valid pointer to an FFITransactionCheckResult + - This function must only be called once per result + */ + void transaction_check_result_free(FFITransactionCheckResult *result) ; + +/* + Free a managed wallet (FFIManagedWallet type) + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - This function must only be called once per managed wallet + */ + void ffi_managed_wallet_free(FFIManagedWallet *managed_wallet) ; + +/* + Get the transaction classification for routing + + Returns a string describing the transaction type (e.g., "Standard", "CoinJoin", + "AssetLock", "AssetUnlock", "ProviderRegistration", etc.) + + # Safety + + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `error` must be a valid pointer to an FFIError or null + - The returned string must be freed by the caller + */ + char *transaction_classify(const uint8_t *tx_bytes, size_t tx_len, FFIError *error) ; + +/* + Free a string + + # Safety + + - `s` must be a valid pointer created by C string creation functions or null + - After calling this function, the pointer becomes invalid + */ + void string_free(char *s) ; + +/* + Get all UTXOs from managed wallet info + + # Safety + + - `managed_info` must be a valid pointer to an FFIManagedWalletInfo instance + - `utxos_out` must be a valid pointer to store the UTXO array pointer + - `count_out` must be a valid pointer to store the UTXO count + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned UTXO array must be freed with `utxo_array_free` when no longer needed + */ + +bool managed_wallet_get_utxos(const FFIManagedWalletInfo *managed_info, + FFINetworks network, + FFIUTXO **utxos_out, + size_t *count_out, + FFIError *error) +; + +/* + Get all UTXOs (deprecated - use managed_wallet_get_utxos instead) + + # Safety + + This function is deprecated and returns an empty list. + Use `managed_wallet_get_utxos` with a ManagedWalletInfo instead. + */ + +bool wallet_get_utxos(const FFIWallet *_wallet, + FFINetworks _network, + FFIUTXO **utxos_out, + size_t *count_out, + FFIError *error) +; + +/* + Free UTXO array + + # Safety + + - `utxos` must be a valid pointer to an array of FFIUTXO structs allocated by this library + - `count` must match the number of UTXOs in the array + - The pointer must not be used after calling this function + - This function must only be called once per array + */ + void utxo_array_free(FFIUTXO *utxos, size_t count) ; + +/* + Create a new wallet from mnemonic with options + + # Safety + + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned pointer must be freed with `wallet_free` when no longer needed + */ + +FFIWallet *wallet_create_from_mnemonic_with_options(const char *mnemonic, + const char *passphrase, + FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new wallet from mnemonic (backward compatibility - single network) + + # Safety + + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned pointer must be freed with `wallet_free` when no longer needed + */ + +FFIWallet *wallet_create_from_mnemonic(const char *mnemonic, + const char *passphrase, + FFINetworks network, + FFIError *error) +; + +/* + Create a new wallet from seed with options + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_from_seed_with_options(const uint8_t *seed, + size_t seed_len, + FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new wallet from seed (backward compatibility) + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_from_seed(const uint8_t *seed, + size_t seed_len, + FFINetworks network, + FFIError *error) +; + +/* + Create a new random wallet with options + + # Safety + + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_random_with_options(FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new random wallet (backward compatibility) + + # Safety + + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the pointer remains valid for the duration of this call + */ + FFIWallet *wallet_create_random(FFINetworks network, FFIError *error) ; + +/* + Get wallet ID (32-byte hash) + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `id_out` must be a valid pointer to a 32-byte buffer + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_get_id(const FFIWallet *wallet, uint8_t *id_out, FFIError *error) ; + +/* + Check if wallet has mnemonic + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_has_mnemonic(const FFIWallet *wallet, FFIError *error) ; + +/* + Check if wallet is watch-only + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_is_watch_only(const FFIWallet *wallet, FFIError *error) ; + +/* + Get extended public key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned C string must be freed by the caller when no longer needed + */ + +char *wallet_get_xpub(const FFIWallet *wallet, + FFINetworks network, + unsigned int account_index, + FFIError *error) +; + +/* + Free a wallet + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet that was created by this library + - The pointer must not be used after calling this function + - This function must only be called once per wallet + */ + void wallet_free(FFIWallet *wallet) ; + +/* + Free a const wallet handle + + This is a const-safe wrapper for wallet_free() that accepts a const pointer. + Use this function when you have a *const FFIWallet that needs to be freed, + such as wallets returned from wallet_manager_get_wallet(). + + # Safety + + - `wallet` must be a valid pointer created by wallet creation functions or null + - After calling this function, the pointer becomes invalid + - This function must only be called once per wallet + - The wallet must have been allocated by this library (not stack or static memory) + */ + void wallet_free_const(const FFIWallet *wallet) ; + +/* + Add an account to the wallet without xpub + + # Safety + + This function dereferences a raw pointer to FFIWallet. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account(FFIWallet *wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index) +; + +/* + Add an account to the wallet with xpub as byte array + + # Safety + + This function dereferences raw pointers. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The xpub_bytes pointer is either null or points to at least xpub_len bytes + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account_with_xpub_bytes(FFIWallet *wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index, + const uint8_t *xpub_bytes, + size_t xpub_len) +; + +/* + Add an account to the wallet with xpub as string + + # Safety + + This function dereferences raw pointers. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The xpub_string pointer is either null or points to a valid null-terminated C string + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account_with_string_xpub(FFIWallet *wallet, + FFINetworks network, + FFIAccountType account_type, + unsigned int account_index, + const char *xpub_string) +; + +/* + Create a new wallet manager + */ + FFIWalletManager *wallet_manager_create(FFIError *error) ; + +/* + Add a wallet from mnemonic to the manager with options + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_add_wallet_from_mnemonic_with_options(FFIWalletManager *manager, + const char *mnemonic, + const char *passphrase, + FFINetworks network, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Add a wallet from mnemonic to the manager (backward compatibility) + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_add_wallet_from_mnemonic(FFIWalletManager *manager, + const char *mnemonic, + const char *passphrase, + FFINetworks network, + FFIError *error) +; + diff --git a/key-wallet-ffi/scripts/check_ffi_docs.sh b/key-wallet-ffi/scripts/check_ffi_docs.sh index 13173078e..b2c807f8b 100755 --- a/key-wallet-ffi/scripts/check_ffi_docs.sh +++ b/key-wallet-ffi/scripts/check_ffi_docs.sh @@ -14,8 +14,10 @@ cd "$PROJECT_DIR" # Generate new documentation python3 scripts/generate_ffi_docs.py > /dev/null 2>&1 -# Check if there are any changes -if ! git diff --quiet FFI_API.md; then +# Check if there are any changes (tracked, staged, or untracked) +if ! git diff --quiet --exit-code -- FFI_API.md \ + || ! git diff --quiet --cached -- FFI_API.md \ + || [ -n "$(git ls-files --others --exclude-standard -- FFI_API.md)" ]; then echo "❌ FFI documentation is out of date!" echo "" echo "Please regenerate the documentation by running:" @@ -27,4 +29,4 @@ if ! git diff --quiet FFI_API.md; then exit 1 else echo "✅ FFI documentation is up to date" -fi \ No newline at end of file +fi diff --git a/key-wallet-ffi/scripts/generate_ffi_docs.py b/key-wallet-ffi/scripts/generate_ffi_docs.py index d739cda99..db1502f9f 100755 --- a/key-wallet-ffi/scripts/generate_ffi_docs.py +++ b/key-wallet-ffi/scripts/generate_ffi_docs.py @@ -22,58 +22,92 @@ class FFIFunction: return_type: str = None def extract_ffi_functions(file_path: Path) -> List[FFIFunction]: - """Extract all #[no_mangle] functions from a Rust file.""" - functions = [] - + """Extract all #[no_mangle] extern "C" functions from a Rust file. + + Handles nested parentheses in parameter types (e.g., function pointers). + """ + functions: List[FFIFunction] = [] + with open(file_path, 'r') as f: content = f.read() - - # Find all #[no_mangle] functions with their documentation - pattern = r'(?:///.*\n)*(?:\s*#\[no_mangle\]\s*\n)(?:pub\s+)?(?:unsafe\s+)?extern\s+"C"\s+fn\s+(\w+)\s*\(((?:[^)]|\n)*)\)\s*(?:->\s*([^{]+))?' - - for match in re.finditer(pattern, content): - full_match = match.group(0) - func_name = match.group(1) - params = match.group(2) - return_type = match.group(3) if match.group(3) else "()" - - # Extract doc comments - doc_lines = [] - for line in full_match.split('\n'): + + for m in re.finditer(r'(?m)^\s*#\[no_mangle\]\s*$', content): + idx = m.end() + fn_match = re.search(r'\bextern\s+"C"\s+fn\s+([A-Za-z0-9_]+)\s*\(', content[idx:], re.S) + if not fn_match: + continue + name = fn_match.group(1) + abs_start = idx + fn_match.start() + paren_start = content.find('(', abs_start) + if paren_start == -1: + continue + depth = 0 + i = paren_start + while i < len(content): + ch = content[i] + if ch == '(': + depth += 1 + elif ch == ')': + depth -= 1 + if depth == 0: + break + i += 1 + if depth != 0: + continue + paren_end = i + + params_raw = content[paren_start + 1:paren_end] + brace_idx = content.find('{', paren_end) + header_tail = content[paren_end:brace_idx if brace_idx != -1 else len(content)] + ret_match = re.search(r'->\s*([^\n{]+)', header_tail) + return_type = ret_match.group(1).strip() if ret_match else '()' + + # Collect contiguous doc comments above #[no_mangle] + doc_lines_rev: List[str] = [] + line_start = content.rfind('\n', 0, m.start()) + 1 + j = line_start - 1 + while j > 0: + prev_nl = content.rfind('\n', 0, j) + line = content[prev_nl + 1:j] if line.strip().startswith('///'): - doc_lines.append(line.strip()[3:].strip()) - - # Extract safety comments + doc_lines_rev.append(line.strip()[3:].strip()) + j = prev_nl + continue + if line.strip() == '' and doc_lines_rev: + j = prev_nl + continue + break + doc_lines = list(reversed(doc_lines_rev)) if doc_lines_rev else [] + safety_comment = None - if '# Safety' in '\n'.join(doc_lines): - safety_start = False - safety_lines = [] - for line in doc_lines: - if '# Safety' in line: - safety_start = True - continue - if safety_start: - if line.startswith('#'): + if doc_lines: + joined = '\n'.join(doc_lines) + if '# Safety' in joined: + safety_lines: List[str] = [] + in_safety = False + for dl in doc_lines: + if dl.strip().startswith('# Safety'): + in_safety = True + continue + if in_safety and dl.strip().startswith('#'): break - safety_lines.append(line) - safety_comment = ' '.join(safety_lines).strip() - - # Clean up parameters - params_clean = re.sub(r'\s+', ' ', params.strip()) - return_type_clean = return_type.strip() - + if in_safety: + safety_lines.append(dl) + safety_comment = ' '.join(safety_lines).strip() if safety_lines else None + + params_clean = re.sub(r'\s+', ' ', params_raw.strip()) module_name = file_path.stem - + functions.append(FFIFunction( - name=func_name, - signature=f"{func_name}({params_clean}) -> {return_type_clean}", + name=name, + signature=f"{name}({params_clean}) -> {return_type}", module=module_name, doc_comment=' '.join(doc_lines) if doc_lines else None, safety_comment=safety_comment, params=params_clean, - return_type=return_type_clean + return_type=return_type, )) - + return functions def categorize_functions(functions: List[FFIFunction]) -> Dict[str, List[FFIFunction]]: @@ -261,7 +295,7 @@ def main(): all_functions = [] - for rust_file in src_dir.glob("*.rs"): + for rust_file in src_dir.rglob("*.rs"): functions = extract_ffi_functions(rust_file) all_functions.extend(functions) @@ -279,4 +313,4 @@ def main(): return 0 if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/key-wallet-ffi/src/keys_tests.rs b/key-wallet-ffi/src/keys_tests.rs index 3c16f8dc7..a9f5f7381 100644 --- a/key-wallet-ffi/src/keys_tests.rs +++ b/key-wallet-ffi/src/keys_tests.rs @@ -5,7 +5,7 @@ mod tests { use crate::error::{FFIError, FFIErrorCode}; use crate::keys::*; - use crate::types::FFINetworks; + use crate::types::{FFINetwork, FFINetworks}; use crate::wallet; use std::ffi::{CStr, CString}; use std::ptr; diff --git a/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h b/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h index b3270a16d..6b00b1311 100644 --- a/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h +++ b/swift-dash-core-sdk/Sources/DashSPVFFI/include/dash_spv_ffi.h @@ -3,6 +3,9 @@ #include #include +// Include shared FFI types (FFINetwork, etc.) from key-wallet-ffi +#include "key_wallet_ffi.h" + typedef enum FFIMempoolStrategy { FetchAll = 0, BloomFilter = 1, diff --git a/swift-dash-core-sdk/Sources/KeyWalletFFI/include/key_wallet_ffi.h b/swift-dash-core-sdk/Sources/KeyWalletFFI/include/key_wallet_ffi.h new file mode 100644 index 000000000..ceff25e1a --- /dev/null +++ b/swift-dash-core-sdk/Sources/KeyWalletFFI/include/key_wallet_ffi.h @@ -0,0 +1,3901 @@ +/** + * Key Wallet FFI - C Header File + * + * This header provides C-compatible function declarations for the key-wallet + * Rust library FFI bindings. + * + * AUTO-GENERATED FILE - DO NOT EDIT + * Generated using cbindgen + */ + +#ifndef KEY_WALLET_FFI_H +#define KEY_WALLET_FFI_H + +/* Generated with cbindgen:0.29.0 */ + +/* Warning: This file is auto-generated by cbindgen. Do not modify manually. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + FFI Account Creation Option Type + */ +typedef enum { + /* + Create default accounts (BIP44 account 0, CoinJoin account 0, and special accounts) + */ + DEFAULT = 0, + /* + Create all specified accounts plus all special purpose accounts + */ + ALL_ACCOUNTS = 1, + /* + Create only BIP44 accounts (no CoinJoin or special accounts) + */ + BIP44_ACCOUNTS_ONLY = 2, + /* + Create specific accounts with full control + */ + SPECIFIC_ACCOUNTS = 3, + /* + Create no accounts at all + */ + NO_ACCOUNTS = 4, +} FFIAccountCreationOptionType; + +/* + Account type enumeration matching all key_wallet AccountType variants + + This enum provides a complete FFI representation of all account types + supported by the key_wallet library: + + - Standard accounts: BIP44 and BIP32 variants for regular transactions + - CoinJoin: Privacy-enhanced transactions + - Identity accounts: Registration, top-up, and invitation funding + - Provider accounts: Various masternode provider key types (voting, owner, operator, platform) + */ +typedef enum { + /* + Standard BIP44 account (m/44'/coin_type'/account'/x/x) + */ + STANDARD_BIP44 = 0, + /* + Standard BIP32 account (m/account'/x/x) + */ + STANDARD_BIP32 = 1, + /* + CoinJoin account for private transactions + */ + COIN_JOIN = 2, + /* + Identity registration funding + */ + IDENTITY_REGISTRATION = 3, + /* + Identity top-up funding (requires registration_index) + */ + IDENTITY_TOP_UP = 4, + /* + Identity top-up funding not bound to a specific identity + */ + IDENTITY_TOP_UP_NOT_BOUND_TO_IDENTITY = 5, + /* + Identity invitation funding + */ + IDENTITY_INVITATION = 6, + /* + Provider voting keys (DIP-3) - Path: m/9'/5'/3'/1'/[key_index] + */ + PROVIDER_VOTING_KEYS = 7, + /* + Provider owner keys (DIP-3) - Path: m/9'/5'/3'/2'/[key_index] + */ + PROVIDER_OWNER_KEYS = 8, + /* + Provider operator keys (DIP-3) - Path: m/9'/5'/3'/3'/[key_index] + */ + PROVIDER_OPERATOR_KEYS = 9, + /* + Provider platform P2P keys (DIP-3, ED25519) - Path: m/9'/5'/3'/4'/[key_index] + */ + PROVIDER_PLATFORM_KEYS = 10, +} FFIAccountType; + +/* + Address pool type + */ +typedef enum { + /* + External (receive) addresses + */ + EXTERNAL = 0, + /* + Internal (change) addresses + */ + INTERNAL = 1, + /* + Single pool (for non-standard accounts) + */ + SINGLE = 2, +} FFIAddressPoolType; + +/* + Derivation path type for DIP9 + */ +typedef enum { + PATH_UNKNOWN = 0, + PATH_BIP32 = 1, + PATH_BIP44 = 2, + PATH_BLOCKCHAIN_IDENTITIES = 3, + PATH_PROVIDER_FUNDS = 4, + PATH_PROVIDER_VOTING_KEYS = 5, + PATH_PROVIDER_OPERATOR_KEYS = 6, + PATH_PROVIDER_OWNER_KEYS = 7, + PATH_CONTACT_BASED_FUNDS = 8, + PATH_CONTACT_BASED_FUNDS_ROOT = 9, + PATH_CONTACT_BASED_FUNDS_EXTERNAL = 10, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_REGISTRATION_FUNDING = 11, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_TOPUP_FUNDING = 12, + PATH_BLOCKCHAIN_IDENTITY_CREDIT_INVITATION_FUNDING = 13, + PATH_PROVIDER_PLATFORM_NODE_KEYS = 14, + PATH_COIN_JOIN = 15, + PATH_ROOT = 255, +} FFIDerivationPathType; + +/* + FFI Error code + */ +typedef enum { + SUCCESS = 0, + INVALID_INPUT = 1, + ALLOCATION_FAILED = 2, + INVALID_MNEMONIC = 3, + INVALID_DERIVATION_PATH = 4, + INVALID_NETWORK = 5, + INVALID_ADDRESS = 6, + INVALID_TRANSACTION = 7, + WALLET_ERROR = 8, + SERIALIZATION_ERROR = 9, + NOT_FOUND = 10, + INVALID_STATE = 11, + INTERNAL_ERROR = 12, +} FFIErrorCode; + +/* + Language enumeration for mnemonic generation + + This enum must be kept in sync with key_wallet::mnemonic::Language. + When adding new languages to the key_wallet crate, remember to update + this FFI enum and both From implementations below. + */ +typedef enum { + ENGLISH = 0, + CHINESE_SIMPLIFIED = 1, + CHINESE_TRADITIONAL = 2, + CZECH = 3, + FRENCH = 4, + ITALIAN = 5, + JAPANESE = 6, + KOREAN = 7, + PORTUGUESE = 8, + SPANISH = 9, +} FFILanguage; + +/* + FFI Network type (single network) + */ +typedef enum { + DASH = 0, + TESTNET = 1, + REGTEST = 2, + DEVNET = 3, +} FFINetwork; + +/* + FFI Network type (bit flags for multiple networks) + */ +typedef enum { + NO_NETWORKS = 0, + DASH_FLAG = 1, + TESTNET_FLAG = 2, + REGTEST_FLAG = 4, + DEVNET_FLAG = 8, + ALL_NETWORKS = 15, +} FFINetworks; + +/* + FFI-compatible transaction context + */ +typedef enum { + /* + Transaction is in the mempool (unconfirmed) + */ + MEMPOOL = 0, + /* + Transaction is in a block at the given height + */ + IN_BLOCK = 1, + /* + Transaction is in a chain-locked block at the given height + */ + IN_CHAIN_LOCKED_BLOCK = 2, +} FFITransactionContext; + +/* + Opaque account handle + */ +typedef struct FFIAccount FFIAccount; + +/* + Opaque handle to an account collection + */ +typedef struct FFIAccountCollection FFIAccountCollection; + +/* + FFI wrapper for an AddressPool from a ManagedAccount + + This is a lightweight wrapper that holds a reference to an AddressPool + from within a ManagedAccount. It allows querying addresses and pool information. + */ +typedef struct FFIAddressPool FFIAddressPool; + +/* + Opaque BLS account handle + */ +typedef struct FFIBLSAccount FFIBLSAccount; + +/* + Opaque EdDSA account handle + */ +typedef struct FFIEdDSAAccount FFIEdDSAAccount; + +/* + Extended private key structure + */ +typedef struct FFIExtendedPrivKey FFIExtendedPrivKey; + +/* + Opaque type for an extended private key + */ +typedef struct FFIExtendedPrivateKey FFIExtendedPrivateKey; + +/* + Extended public key structure + */ +typedef struct FFIExtendedPubKey FFIExtendedPubKey; + +/* + Opaque type for an extended public key + */ +typedef struct FFIExtendedPublicKey FFIExtendedPublicKey; + +/* + Opaque managed account handle that wraps ManagedAccount + */ +typedef struct FFIManagedAccount FFIManagedAccount; + +/* + Opaque handle to a managed account collection + */ +typedef struct FFIManagedAccountCollection FFIManagedAccountCollection; + +/* + FFI wrapper for ManagedWalletInfo + */ +typedef struct FFIManagedWalletInfo FFIManagedWalletInfo; + +/* + Opaque type for a private key (SecretKey) + */ +typedef struct FFIPrivateKey FFIPrivateKey; + +/* + Opaque type for a public key + */ +typedef struct FFIPublicKey FFIPublicKey; + +/* + Opaque handle for a transaction + */ +typedef struct FFITransaction FFITransaction; + +/* + Opaque wallet handle + */ +typedef struct FFIWallet FFIWallet; + +/* + FFI wrapper for WalletManager + + This struct holds a cloned Arc reference to the WalletManager, + allowing FFI code to interact with it directly without going through + the SPV client. + */ +typedef struct FFIWalletManager FFIWalletManager; + +/* + FFI Result type for Account operations + */ +typedef struct { + /* + The account handle if successful, NULL if error + */ + FFIAccount *account; + /* + Error code (0 = success) + */ + int32_t error_code; + /* + Error message (NULL if success, must be freed by caller if not NULL) + */ + char *error_message; +} FFIAccountResult; + +/* + FFI Error structure + */ +typedef struct { + FFIErrorCode code; + char *message; +} FFIError; + +/* + C-compatible summary of all accounts in a collection + + This struct provides Swift with structured data about all accounts + that exist in the collection, allowing programmatic access to account + indices and presence information. + */ +typedef struct { + /* + Array of BIP44 account indices + */ + unsigned int *bip44_indices; + /* + Number of BIP44 accounts + */ + size_t bip44_count; + /* + Array of BIP32 account indices + */ + unsigned int *bip32_indices; + /* + Number of BIP32 accounts + */ + size_t bip32_count; + /* + Array of CoinJoin account indices + */ + unsigned int *coinjoin_indices; + /* + Number of CoinJoin accounts + */ + size_t coinjoin_count; + /* + Array of identity top-up registration indices + */ + unsigned int *identity_topup_indices; + /* + Number of identity top-up accounts + */ + size_t identity_topup_count; + /* + Whether identity registration account exists + */ + bool has_identity_registration; + /* + Whether identity invitation account exists + */ + bool has_identity_invitation; + /* + Whether identity top-up not bound account exists + */ + bool has_identity_topup_not_bound; + /* + Whether provider voting keys account exists + */ + bool has_provider_voting_keys; + /* + Whether provider owner keys account exists + */ + bool has_provider_owner_keys; + /* + Whether provider operator keys account exists + */ + bool has_provider_operator_keys; + /* + Whether provider platform keys account exists + */ + bool has_provider_platform_keys; +} FFIAccountCollectionSummary; + +/* + FFI wrapper for ManagedWalletInfo that includes transaction checking capabilities + */ +typedef struct { + ManagedWalletInfo *inner; + +} FFIManagedWallet; + +/* + Address pool info + */ +typedef struct { + /* + Pool type + */ + FFIAddressPoolType pool_type; + /* + Number of generated addresses + */ + unsigned int generated_count; + /* + Number of used addresses + */ + unsigned int used_count; + /* + Current gap (unused addresses at the end) + */ + unsigned int current_gap; + /* + Gap limit setting + */ + unsigned int gap_limit; + /* + Highest used index (-1 if none used) + */ + int32_t highest_used_index; +} FFIAddressPoolInfo; + +/* + FFI-compatible version of AddressInfo + */ +typedef struct { + /* + Address as string + */ + char *address; + /* + Script pubkey bytes + */ + uint8_t *script_pubkey; + /* + Length of script pubkey + */ + size_t script_pubkey_len; + /* + Public key bytes (nullable) + */ + uint8_t *public_key; + /* + Length of public key + */ + size_t public_key_len; + /* + Derivation index + */ + uint32_t index; + /* + Derivation path as string + */ + char *path; + /* + Whether address has been used + */ + bool used; + /* + When generated (timestamp) + */ + uint64_t generated_at; + /* + When first used (0 if never) + */ + uint64_t used_at; + /* + Transaction count + */ + uint32_t tx_count; + /* + Total received + */ + uint64_t total_received; + /* + Total sent + */ + uint64_t total_sent; + /* + Current balance + */ + uint64_t balance; + /* + Custom label (nullable) + */ + char *label; +} FFIAddressInfo; + +/* + FFI Result type for ManagedAccount operations + */ +typedef struct { + /* + The managed account handle if successful, NULL if error + */ + FFIManagedAccount *account; + /* + Error code (0 = success) + */ + int32_t error_code; + /* + Error message (NULL if success, must be freed by caller if not NULL) + */ + char *error_message; +} FFIManagedAccountResult; + +/* + FFI Balance type for representing wallet balances + */ +typedef struct { + /* + Confirmed balance in duffs + */ + uint64_t confirmed; + /* + Unconfirmed balance in duffs + */ + uint64_t unconfirmed; + /* + Immature balance in duffs (e.g., mining rewards) + */ + uint64_t immature; + /* + Total balance (confirmed + unconfirmed) in duffs + */ + uint64_t total; +} FFIBalance; + +/* + C-compatible summary of all accounts in a managed collection + + This struct provides Swift with structured data about all accounts + that exist in the managed collection, allowing programmatic access to account + indices and presence information. + */ +typedef struct { + /* + Array of BIP44 account indices + */ + unsigned int *bip44_indices; + /* + Number of BIP44 accounts + */ + size_t bip44_count; + /* + Array of BIP32 account indices + */ + unsigned int *bip32_indices; + /* + Number of BIP32 accounts + */ + size_t bip32_count; + /* + Array of CoinJoin account indices + */ + unsigned int *coinjoin_indices; + /* + Number of CoinJoin accounts + */ + size_t coinjoin_count; + /* + Array of identity top-up registration indices + */ + unsigned int *identity_topup_indices; + /* + Number of identity top-up accounts + */ + size_t identity_topup_count; + /* + Whether identity registration account exists + */ + bool has_identity_registration; + /* + Whether identity invitation account exists + */ + bool has_identity_invitation; + /* + Whether identity top-up not bound account exists + */ + bool has_identity_topup_not_bound; + /* + Whether provider voting keys account exists + */ + bool has_provider_voting_keys; + /* + Whether provider owner keys account exists + */ + bool has_provider_owner_keys; + /* + Whether provider operator keys account exists + */ + bool has_provider_operator_keys; + /* + Whether provider platform keys account exists + */ + bool has_provider_platform_keys; +} FFIManagedAccountCollectionSummary; + +/* + Transaction output for building (legacy structure) + */ +typedef struct { + const char *address; + uint64_t amount; +} FFITxOutput; + +/* + Transaction context for checking + Transaction check result + */ +typedef struct { + /* + Whether the transaction belongs to the wallet + */ + bool is_relevant; + /* + Total amount received + */ + uint64_t total_received; + /* + Total amount sent + */ + uint64_t total_sent; + /* + Number of affected accounts + */ + uint32_t affected_accounts_count; +} FFITransactionCheckResult; + +/* + FFI-compatible transaction input + */ +typedef struct { + /* + Transaction ID (32 bytes) + */ + uint8_t txid[32]; + /* + Output index + */ + uint32_t vout; + /* + Script signature length + */ + uint32_t script_sig_len; + /* + Script signature data pointer + */ + const uint8_t *script_sig; + /* + Sequence number + */ + uint32_t sequence; +} FFITxIn; + +/* + FFI-compatible transaction output + */ +typedef struct { + /* + Amount in duffs + */ + uint64_t amount; + /* + Script pubkey length + */ + uint32_t script_pubkey_len; + /* + Script pubkey data pointer + */ + const uint8_t *script_pubkey; +} FFITxOut; + +/* + UTXO structure for FFI + */ +typedef struct { + uint8_t txid[32]; + uint32_t vout; + uint64_t amount; + char *address; + uint8_t *script_pubkey; + size_t script_len; + uint32_t height; + uint32_t confirmations; +} FFIUTXO; + +/* + FFI structure for wallet account creation options + This single struct represents all possible account creation configurations + */ +typedef struct { + /* + The type of account creation option + */ + FFIAccountCreationOptionType option_type; + /* + Array of BIP44 account indices to create + */ + const uint32_t *bip44_indices; + size_t bip44_count; + /* + Array of BIP32 account indices to create + */ + const uint32_t *bip32_indices; + size_t bip32_count; + /* + Array of CoinJoin account indices to create + */ + const uint32_t *coinjoin_indices; + size_t coinjoin_count; + /* + Array of identity top-up registration indices to create + */ + const uint32_t *topup_indices; + size_t topup_count; + /* + For SpecificAccounts: Additional special account types to create + (e.g., IdentityRegistration, ProviderKeys, etc.) + This is an array of FFIAccountType values + */ + const FFIAccountType *special_account_types; + size_t special_account_types_count; +} FFIWalletAccountCreationOptions; + +/* + FFI-compatible transaction context details + */ +typedef struct { + /* + The context type + */ + FFITransactionContext context_type; + /* + Block height (0 for mempool) + */ + unsigned int height; + /* + Block hash (32 bytes, null for mempool or if unknown) + */ + const uint8_t *block_hash; + /* + Timestamp (0 if unknown) + */ + unsigned int timestamp; +} FFITransactionContextDetails; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* + Initialize the library + */ + bool key_wallet_ffi_initialize(void) ; + +/* + Get library version + + Returns a static string that should NOT be freed by the caller + */ + const char *key_wallet_ffi_version(void) ; + +/* + Get an account handle for a specific account type + Returns a result containing either the account handle or an error + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - The caller must ensure the wallet pointer remains valid for the duration of this call + */ + +FFIAccountResult wallet_get_account(const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIAccountType account_type) +; + +/* + Get an IdentityTopUp account handle with a specific registration index + This is used for top-up accounts that are bound to a specific identity + Returns a result containing either the account handle or an error + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - The caller must ensure the wallet pointer remains valid for the duration of this call + */ + +FFIAccountResult wallet_get_top_up_account_with_registration_index(const FFIWallet *wallet, + FFINetwork network, + unsigned int registration_index) +; + +/* + Free an account handle + + # Safety + + - `account` must be a valid pointer to an FFIAccount that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void account_free(FFIAccount *account) ; + +/* + Free a BLS account handle + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void bls_account_free(FFIBLSAccount *account) ; + +/* + Free an EdDSA account handle + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void eddsa_account_free(FFIEdDSAAccount *account) ; + +/* + Free an account result's error message (if any) + Note: This does NOT free the account handle itself - use account_free for that + + # Safety + + - `result` must be a valid pointer to an FFIAccountResult + - The error_message field must be either null or a valid CString allocated by this library + - The caller must ensure the result pointer remains valid for the duration of this call + */ + void account_result_free_error(FFIAccountResult *result) ; + +/* + Get the extended public key of an account as a string + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *account_get_extended_public_key_as_string(const FFIAccount *account) ; + +/* + Get the network of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks account_get_network(const FFIAccount *account) ; + +/* + Get the parent wallet ID of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *account_get_parent_wallet_id(const FFIAccount *account) ; + +/* + Get the account type of an account + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + FFIAccountType account_get_account_type(const FFIAccount *account, unsigned int *out_index) ; + +/* + Check if an account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIAccount instance + - Returns false if the account is null + */ + bool account_get_is_watch_only(const FFIAccount *account) ; + +/* + Get the extended public key of a BLS account as a string + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *bls_account_get_extended_public_key_as_string(const FFIBLSAccount *account) ; + +/* + Get the network of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks bls_account_get_network(const FFIBLSAccount *account) ; + +/* + Get the parent wallet ID of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *bls_account_get_parent_wallet_id(const FFIBLSAccount *account) ; + +/* + Get the account type of a BLS account + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + +FFIAccountType bls_account_get_account_type(const FFIBLSAccount *account, + unsigned int *out_index) +; + +/* + Check if a BLS account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIBLSAccount instance + - Returns false if the account is null + */ + bool bls_account_get_is_watch_only(const FFIBLSAccount *account) ; + +/* + Get the extended public key of an EdDSA account as a string + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - The returned string must be freed by the caller using `string_free` + - Returns NULL if the account is null + */ + char *eddsa_account_get_extended_public_key_as_string(const FFIEdDSAAccount *account) ; + +/* + Get the network of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns FFINetwork::NoNetworks if the account is null + */ + FFINetworks eddsa_account_get_network(const FFIEdDSAAccount *account) ; + +/* + Get the parent wallet ID of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns a pointer to the 32-byte wallet ID, or NULL if not set or account is null + - The returned pointer is valid only as long as the account exists + - The caller should copy the data if needed for longer use + */ + const uint8_t *eddsa_account_get_parent_wallet_id(const FFIEdDSAAccount *account) ; + +/* + Get the account type of an EdDSA account + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - `out_index` must be a valid pointer to a c_uint where the index will be stored + - Returns FFIAccountType::StandardBIP44 with index 0 if the account is null + */ + +FFIAccountType eddsa_account_get_account_type(const FFIEdDSAAccount *account, + unsigned int *out_index) +; + +/* + Check if an EdDSA account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIEdDSAAccount instance + - Returns false if the account is null + */ + bool eddsa_account_get_is_watch_only(const FFIEdDSAAccount *account) ; + +/* + Get number of accounts + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure both pointers remain valid for the duration of this call + */ + +unsigned int wallet_get_account_count(const FFIWallet *wallet, + FFINetwork network, + FFIError *error) +; + +/* + Get account collection for a specific network from wallet + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The returned pointer must be freed with `account_collection_free` when no longer needed + */ + +FFIAccountCollection *wallet_get_account_collection(const FFIWallet *wallet, + FFINetwork network, + FFIError *error) +; + +/* + Free an account collection handle + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection created by this library + - `collection` must not be used after calling this function + */ + void account_collection_free(FFIAccountCollection *collection) ; + +/* + Get a BIP44 account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_bip44_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP44 account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_bip44_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a BIP32 account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_bip32_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP32 account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_bip32_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a CoinJoin account by index from the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_coinjoin_account(const FFIAccountCollection *collection, + unsigned int index) +; + +/* + Get all CoinJoin account indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_coinjoin_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity registration account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_identity_registration(const FFIAccountCollection *collection) ; + +/* + Check if identity registration account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_registration(const FFIAccountCollection *collection) ; + +/* + Get an identity topup account by registration index + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_identity_topup(const FFIAccountCollection *collection, + unsigned int registration_index) +; + +/* + Get all identity topup registration indices + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool account_collection_get_identity_topup_indices(const FFIAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity topup not bound account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + +FFIAccount *account_collection_get_identity_topup_not_bound(const FFIAccountCollection *collection) +; + +/* + Check if identity topup not bound account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_topup_not_bound(const FFIAccountCollection *collection) ; + +/* + Get the identity invitation account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_identity_invitation(const FFIAccountCollection *collection) ; + +/* + Check if identity invitation account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_identity_invitation(const FFIAccountCollection *collection) ; + +/* + Get the provider voting keys account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_provider_voting_keys(const FFIAccountCollection *collection) ; + +/* + Check if provider voting keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_voting_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider owner keys account if it exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_free` when no longer needed + */ + FFIAccount *account_collection_get_provider_owner_keys(const FFIAccountCollection *collection) ; + +/* + Check if provider owner keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_owner_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider operator keys account if it exists + Note: Returns null if the `bls` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `bls_account_free` when no longer needed (when BLS is enabled) + */ + +void *account_collection_get_provider_operator_keys(const FFIAccountCollection *collection) +; + +/* + Check if provider operator keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_operator_keys(const FFIAccountCollection *collection) ; + +/* + Get the provider platform keys account if it exists + Note: Returns null if the `eddsa` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `eddsa_account_free` when no longer needed (when EdDSA is enabled) + */ + +void *account_collection_get_provider_platform_keys(const FFIAccountCollection *collection) +; + +/* + Check if provider platform keys account exists + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + bool account_collection_has_provider_platform_keys(const FFIAccountCollection *collection) ; + +/* + Free a u32 array allocated by this library + + # Safety + + - `array` must be a valid pointer to an array allocated by this library + - `array` must not be used after calling this function + */ + void free_u32_array(unsigned int *array, size_t count) ; + +/* + Get the total number of accounts in the collection + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + */ + unsigned int account_collection_count(const FFIAccountCollection *collection) ; + +/* + Get a human-readable summary of all accounts in the collection + + Returns a formatted string showing all account types and their indices. + The format is designed to be clear and readable for end users. + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned string must be freed with `string_free` when no longer needed + - Returns null if the collection pointer is null + */ + char *account_collection_summary(const FFIAccountCollection *collection) ; + +/* + Get structured account collection summary data + + Returns a struct containing arrays of indices for each account type and boolean + flags for special accounts. This provides Swift with programmatic access to + account information. + + # Safety + + - `collection` must be a valid pointer to an FFIAccountCollection + - The returned pointer must be freed with `account_collection_summary_free` when no longer needed + - Returns null if the collection pointer is null + */ + +FFIAccountCollectionSummary *account_collection_summary_data(const FFIAccountCollection *collection) +; + +/* + Free an account collection summary and all its allocated memory + + # Safety + + - `summary` must be a valid pointer to an FFIAccountCollectionSummary created by `account_collection_summary_data` + - `summary` must not be used after calling this function + */ + +void account_collection_summary_free(FFIAccountCollectionSummary *summary) +; + +/* + Free address string + + # Safety + + - `address` must be a valid pointer created by address functions or null + - After calling this function, the pointer becomes invalid + */ + void address_free(char *address) ; + +/* + Free address array + + # Safety + + - `addresses` must be a valid pointer to an array of address strings or null + - Each address in the array must be a valid C string pointer + - `count` must be the correct number of addresses in the array + - After calling this function, all pointers become invalid + */ + void address_array_free(char **addresses, size_t count) ; + +/* + Validate an address + + # Safety + + - `address` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + */ + bool address_validate(const char *address, FFINetwork network, FFIError *error) ; + +/* + Get address type + + Returns: + - 0: P2PKH address + - 1: P2SH address + - 2: Other address type + - u8::MAX (255): Error occurred + + # Safety + + - `address` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + */ + unsigned char address_get_type(const char *address, FFINetwork network, FFIError *error) ; + +/* + Free an address pool handle + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void address_pool_free(FFIAddressPool *pool) ; + +/* + Get address pool information for an account + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `info_out` must be a valid pointer to store the pool info + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_get_address_pool_info(const FFIManagedWallet *managed_wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + FFIAddressPoolInfo *info_out, + FFIError *error) +; + +/* + Set the gap limit for an address pool + + The gap limit determines how many unused addresses to maintain at the end + of the pool. This is important for wallet recovery and address discovery. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_set_gap_limit(FFIManagedWallet *managed_wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + unsigned int gap_limit, + FFIError *error) +; + +/* + Generate addresses up to a specific index in a pool + + This ensures that addresses up to and including the specified index exist + in the pool. This is useful for wallet recovery or when specific indices + are needed. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `wallet` must be a valid pointer to an FFIWallet (for key derivation) + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_generate_addresses_to_index(FFIManagedWallet *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index, + FFIAddressPoolType pool_type, + unsigned int target_index, + FFIError *error) +; + +/* + Mark an address as used in the pool + + This updates the pool's tracking of which addresses have been used, + which is important for gap limit management and wallet recovery. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `address` must be a valid C string + - `error` must be a valid pointer to an FFIError or null + */ + +bool managed_wallet_mark_address_used(FFIManagedWallet *managed_wallet, + FFINetwork network, + const char *address, + FFIError *error) +; + +/* + Get a single address info at a specific index from the pool + + Returns detailed information about the address at the given index, or NULL + if the index is out of bounds or not generated yet. + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool + - `error` must be a valid pointer to an FFIError or null + - The returned FFIAddressInfo must be freed using `address_info_free` + */ + +FFIAddressInfo *address_pool_get_address_at_index(const FFIAddressPool *pool, + uint32_t index, + FFIError *error) +; + +/* + Get a range of addresses from the pool + + Returns an array of FFIAddressInfo structures for addresses in the range [start_index, end_index). + The count_out parameter will be set to the actual number of addresses returned. + + Note: This function only reads existing addresses from the pool. It does not generate new addresses. + Use managed_wallet_generate_addresses_to_index if you need to generate addresses first. + + # Safety + + - `pool` must be a valid pointer to an FFIAddressPool + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError or null + - The returned array must be freed using `address_info_array_free` + */ + +FFIAddressInfo **address_pool_get_addresses_in_range(const FFIAddressPool *pool, + uint32_t start_index, + uint32_t end_index, + size_t *count_out, + FFIError *error) +; + +/* + Free a single FFIAddressInfo structure + + # Safety + + - `info` must be a valid pointer to an FFIAddressInfo allocated by this library or null + - The pointer must not be used after calling this function + */ + void address_info_free(FFIAddressInfo *info) ; + +/* + Free an array of FFIAddressInfo structures + + # Safety + + - `infos` must be a valid pointer to an array of FFIAddressInfo pointers allocated by this library or null + - `count` must be the exact number of elements in the array + - The pointers must not be used after calling this function + */ + +void address_info_array_free(FFIAddressInfo **infos, + size_t count) +; + +/* + Create a new master extended private key from seed + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the seed pointer remains valid for the duration of this call + */ + +FFIExtendedPrivKey *derivation_new_master_key(const uint8_t *seed, + size_t seed_len, + FFINetwork network, + FFIError *error) +; + +/* + Derive a BIP44 account path (m/44'/5'/account') + */ + +bool derivation_bip44_account_path(FFINetwork network, + unsigned int account_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive a BIP44 payment path (m/44'/5'/account'/change/index) + */ + +bool derivation_bip44_payment_path(FFINetwork network, + unsigned int account_index, + bool is_change, + unsigned int address_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive CoinJoin path (m/9'/5'/4'/account') + */ + +bool derivation_coinjoin_path(FFINetwork network, + unsigned int account_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity registration path (m/9'/5'/5'/1'/index') + */ + +bool derivation_identity_registration_path(FFINetwork network, + unsigned int identity_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity top-up path (m/9'/5'/5'/2'/identity_index'/top_up_index') + */ + +bool derivation_identity_topup_path(FFINetwork network, + unsigned int identity_index, + unsigned int topup_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive identity authentication path (m/9'/5'/5'/0'/identity_index'/key_index') + */ + +bool derivation_identity_authentication_path(FFINetwork network, + unsigned int identity_index, + unsigned int key_index, + char *path_out, + size_t path_max_len, + FFIError *error) +; + +/* + Derive private key for a specific path from seed + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `path` must be a valid pointer to a null-terminated C string + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIExtendedPrivKey *derivation_derive_private_key_from_seed(const uint8_t *seed, + size_t seed_len, + const char *path, + FFINetwork network, + FFIError *error) +; + +/* + Derive public key from extended private key + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_public_key_free` + */ + FFIExtendedPubKey *derivation_xpriv_to_xpub(const FFIExtendedPrivKey *xpriv, FFIError *error) ; + +/* + Get extended private key as string + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *derivation_xpriv_to_string(const FFIExtendedPrivKey *xpriv, FFIError *error) ; + +/* + Get extended public key as string + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *derivation_xpub_to_string(const FFIExtendedPubKey *xpub, FFIError *error) ; + +/* + Get fingerprint from extended public key (4 bytes) + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey + - `fingerprint_out` must be a valid pointer to a buffer of at least 4 bytes + - `error` must be a valid pointer to an FFIError + */ + +bool derivation_xpub_fingerprint(const FFIExtendedPubKey *xpub, + uint8_t *fingerprint_out, + FFIError *error) +; + +/* + Free extended private key + + # Safety + + - `xpriv` must be a valid pointer to an FFIExtendedPrivKey that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_xpriv_free(FFIExtendedPrivKey *xpriv) ; + +/* + Free extended public key + + # Safety + + - `xpub` must be a valid pointer to an FFIExtendedPubKey that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_xpub_free(FFIExtendedPubKey *xpub) ; + +/* + Free derivation path string + + # Safety + + - `s` must be a valid pointer to a C string that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void derivation_string_free(char *s) ; + +/* + Derive key using DIP9 path constants for identity + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the seed pointer remains valid for the duration of this call + */ + +FFIExtendedPrivKey *dip9_derive_identity_key(const uint8_t *seed, + size_t seed_len, + FFINetwork network, + unsigned int identity_index, + unsigned int key_index, + FFIDerivationPathType key_type, + FFIError *error) +; + +/* + Derive an address from a private key + + # Safety + - `private_key` must be a valid pointer to 32 bytes + - `network` is the network for the address + + # Returns + - Pointer to C string with address (caller must free) + - NULL on error + */ + char *key_wallet_derive_address_from_key(const uint8_t *private_key, FFINetwork network) ; + +/* + Derive an address from a seed at a specific derivation path + + # Safety + - `seed` must be a valid pointer to 64 bytes + - `network` is the network for the address + - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") + + # Returns + - Pointer to C string with address (caller must free) + - NULL on error + */ + +char *key_wallet_derive_address_from_seed(const uint8_t *seed, + FFINetwork network, + const char *path) +; + +/* + Derive a private key from a seed at a specific derivation path + + # Safety + - `seed` must be a valid pointer to 64 bytes + - `path` must be a valid null-terminated C string (e.g., "m/44'/5'/0'/0/0") + - `key_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + +int32_t key_wallet_derive_private_key_from_seed(const uint8_t *seed, + const char *path, + uint8_t *key_out) +; + +/* + Free an error message + + # Safety + + - `message` must be a valid pointer to a C string that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void error_message_free(char *message) ; + +/* + Get extended private key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_get_account_xpriv(const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIError *error) +; + +/* + Get extended public key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_get_account_xpub(const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIError *error) +; + +/* + Derive private key at a specific path + Returns an opaque FFIPrivateKey pointer that must be freed with private_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `private_key_free` + */ + +FFIPrivateKey *wallet_derive_private_key(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive extended private key at a specific path + Returns an opaque FFIExtendedPrivateKey pointer that must be freed with extended_private_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_private_key_free` + */ + +FFIExtendedPrivateKey *wallet_derive_extended_private_key(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive private key at a specific path and return as WIF string + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_derive_private_key_as_wif(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Free a private key + + # Safety + + - `key` must be a valid pointer created by private key functions or null + - After calling this function, the pointer becomes invalid + */ + void private_key_free(FFIPrivateKey *key) ; + +/* + Free an extended private key + + # Safety + + - `key` must be a valid pointer created by extended private key functions or null + - After calling this function, the pointer becomes invalid + */ + void extended_private_key_free(FFIExtendedPrivateKey *key) ; + +/* + Get extended private key as string (xprv format) + + Returns the extended private key in base58 format (xprv... for mainnet, tprv... for testnet) + + # Safety + + - `key` must be a valid pointer to an FFIExtendedPrivateKey + - `network` is ignored; the network is encoded in the extended key + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *extended_private_key_to_string(const FFIExtendedPrivateKey *key, + FFINetwork network, + FFIError *error) +; + +/* + Get the private key from an extended private key + + Extracts the non-extended private key from an extended private key. + + # Safety + + - `extended_key` must be a valid pointer to an FFIExtendedPrivateKey + - `error` must be a valid pointer to an FFIError + - The returned FFIPrivateKey must be freed with `private_key_free` + */ + +FFIPrivateKey *extended_private_key_get_private_key(const FFIExtendedPrivateKey *extended_key, + FFIError *error) +; + +/* + Get private key as WIF string from FFIPrivateKey + + # Safety + + - `key` must be a valid pointer to an FFIPrivateKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *private_key_to_wif(const FFIPrivateKey *key, FFINetwork network, FFIError *error) ; + +/* + Derive public key at a specific path + Returns an opaque FFIPublicKey pointer that must be freed with public_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `public_key_free` + */ + +FFIPublicKey *wallet_derive_public_key(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive extended public key at a specific path + Returns an opaque FFIExtendedPublicKey pointer that must be freed with extended_public_key_free + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned pointer must be freed with `extended_public_key_free` + */ + +FFIExtendedPublicKey *wallet_derive_extended_public_key(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Derive public key at a specific path and return as hex string + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `derivation_path` must be a valid null-terminated C string + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *wallet_derive_public_key_as_hex(const FFIWallet *wallet, + FFINetwork network, + const char *derivation_path, + FFIError *error) +; + +/* + Free a public key + + # Safety + + - `key` must be a valid pointer created by public key functions or null + - After calling this function, the pointer becomes invalid + */ + void public_key_free(FFIPublicKey *key) ; + +/* + Free an extended public key + + # Safety + + - `key` must be a valid pointer created by extended public key functions or null + - After calling this function, the pointer becomes invalid + */ + void extended_public_key_free(FFIExtendedPublicKey *key) ; + +/* + Get extended public key as string (xpub format) + + Returns the extended public key in base58 format (xpub... for mainnet, tpub... for testnet) + + # Safety + + - `key` must be a valid pointer to an FFIExtendedPublicKey + - `network` is ignored; the network is encoded in the extended key + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + +char *extended_public_key_to_string(const FFIExtendedPublicKey *key, + FFINetwork network, + FFIError *error) +; + +/* + Get the public key from an extended public key + + Extracts the non-extended public key from an extended public key. + + # Safety + + - `extended_key` must be a valid pointer to an FFIExtendedPublicKey + - `error` must be a valid pointer to an FFIError + - The returned FFIPublicKey must be freed with `public_key_free` + */ + +FFIPublicKey *extended_public_key_get_public_key(const FFIExtendedPublicKey *extended_key, + FFIError *error) +; + +/* + Get public key as hex string from FFIPublicKey + + # Safety + + - `key` must be a valid pointer to an FFIPublicKey + - `error` must be a valid pointer to an FFIError + - The returned string must be freed with `string_free` + */ + char *public_key_to_hex(const FFIPublicKey *key, FFIError *error) ; + +/* + Convert derivation path string to indices + + # Safety + + - `path` must be a valid null-terminated C string or null + - `indices_out` must be a valid pointer to store the indices array pointer + - `hardened_out` must be a valid pointer to store the hardened flags array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - The returned arrays must be freed with `derivation_path_free` + */ + +bool derivation_path_parse(const char *path, + uint32_t **indices_out, + bool **hardened_out, + size_t *count_out, + FFIError *error) +; + +/* + Free derivation path arrays + Note: This function expects the count to properly free the slices + + # Safety + + - `indices` must be a valid pointer created by `derivation_path_parse` or null + - `hardened` must be a valid pointer created by `derivation_path_parse` or null + - `count` must match the count from `derivation_path_parse` + - After calling this function, the pointers become invalid + */ + void derivation_path_free(uint32_t *indices, bool *hardened, size_t count) ; + +/* + Get a managed account from a managed wallet + + This function gets a ManagedAccount from the wallet manager's managed wallet info, + returning a managed account handle that wraps the ManagedAccount. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - The caller must ensure all pointers remain valid for the duration of this call + - The returned account must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccountResult managed_wallet_get_account(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetwork network, + unsigned int account_index, + FFIAccountType account_type) +; + +/* + Get a managed IdentityTopUp account with a specific registration index + + This is used for top-up accounts that are bound to a specific identity. + Returns a managed account handle that wraps the ManagedAccount. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - The caller must ensure all pointers remain valid for the duration of this call + - The returned account must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccountResult managed_wallet_get_top_up_account_with_registration_index(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetwork network, + unsigned int registration_index) +; + +/* + Get the network of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + FFINetwork managed_account_get_network(const FFIManagedAccount *account) ; + +/* + Get the parent wallet ID of a managed account + + Note: ManagedAccount doesn't store the parent wallet ID directly. + The wallet ID is typically known from the context (e.g., when getting the account from a managed wallet). + + # Safety + + - `wallet_id` must be a valid pointer to a 32-byte wallet ID buffer that was provided by the caller + - The returned pointer is the same as the input pointer for convenience + - The caller must not free the returned pointer as it's the same as the input + */ + +const uint8_t *managed_account_get_parent_wallet_id(const uint8_t *wallet_id) +; + +/* + Get the account type of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - `index_out` must be a valid pointer to receive the account index (or null) + */ + +FFIAccountType managed_account_get_account_type(const FFIManagedAccount *account, + unsigned int *index_out) +; + +/* + Check if a managed account is watch-only + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + bool managed_account_get_is_watch_only(const FFIManagedAccount *account) ; + +/* + Get the balance of a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - `balance_out` must be a valid pointer to an FFIBalance structure + */ + bool managed_account_get_balance(const FFIManagedAccount *account, FFIBalance *balance_out) ; + +/* + Get the number of transactions in a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_transaction_count(const FFIManagedAccount *account) ; + +/* + Get the number of UTXOs in a managed account + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_utxo_count(const FFIManagedAccount *account) ; + +/* + Free a managed account handle + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount that was allocated by this library + - The pointer must not be used after calling this function + - This function must only be called once per allocation + */ + void managed_account_free(FFIManagedAccount *account) ; + +/* + Free a managed account result's error message (if any) + Note: This does NOT free the account handle itself - use managed_account_free for that + + # Safety + + - `result` must be a valid pointer to an FFIManagedAccountResult + - The error_message field must be either null or a valid CString allocated by this library + - The caller must ensure the result pointer remains valid for the duration of this call + */ + void managed_account_result_free_error(FFIManagedAccountResult *result) ; + +/* + Get number of accounts in a managed wallet + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `network` must specify exactly one network + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +unsigned int managed_wallet_get_account_count(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetwork network, + FFIError *error) +; + +/* + Get the account index from a managed account + + Returns the primary account index for Standard and CoinJoin accounts. + Returns 0 for account types that don't have an index (like Identity or Provider accounts). + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + */ + unsigned int managed_account_get_index(const FFIManagedAccount *account) ; + +/* + Get the external address pool from a managed account + + This function returns the external (receive) address pool for Standard accounts. + Returns NULL for account types that don't have separate external/internal pools. + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + FFIAddressPool *managed_account_get_external_address_pool(const FFIManagedAccount *account) ; + +/* + Get the internal address pool from a managed account + + This function returns the internal (change) address pool for Standard accounts. + Returns NULL for account types that don't have separate external/internal pools. + + # Safety + + - `account` must be a valid pointer to an FFIManagedAccount instance + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + FFIAddressPool *managed_account_get_internal_address_pool(const FFIManagedAccount *account) ; + +/* + Get an address pool from a managed account by type + + This function returns the appropriate address pool based on the pool type parameter. + For Standard accounts with External/Internal pool types, returns the corresponding pool. + For non-standard accounts with Single pool type, returns their single address pool. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `account` must be a valid pointer to an FFIManagedAccount instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - The returned pool must be freed with `address_pool_free` when no longer needed + */ + +FFIAddressPool *managed_account_get_address_pool(const FFIManagedAccount *account, + FFIAddressPoolType pool_type) +; + +/* + Get managed account collection for a specific network from wallet manager + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The returned pointer must be freed with `managed_account_collection_free` when no longer needed + */ + +FFIManagedAccountCollection *managed_wallet_get_account_collection(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFINetwork network, + FFIError *error) +; + +/* + Free a managed account collection handle + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection created by this library + - `collection` must not be used after calling this function + */ + void managed_account_collection_free(FFIManagedAccountCollection *collection) ; + +/* + Get a BIP44 account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_bip44_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP44 account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_bip44_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a BIP32 account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_bip32_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all BIP32 account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_bip32_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get a CoinJoin account by index from the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_coinjoin_account(const FFIManagedAccountCollection *collection, + unsigned int index) +; + +/* + Get all CoinJoin account indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_coinjoin_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity registration account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_registration(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity registration account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_registration(const FFIManagedAccountCollection *collection) +; + +/* + Get an identity topup account by registration index from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_topup(const FFIManagedAccountCollection *collection, + unsigned int registration_index) +; + +/* + Get all identity topup registration indices from managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `out_indices` must be a valid pointer to store the indices array + - `out_count` must be a valid pointer to store the count + - The returned array must be freed with `free_u32_array` when no longer needed + */ + +bool managed_account_collection_get_identity_topup_indices(const FFIManagedAccountCollection *collection, + unsigned int **out_indices, + size_t *out_count) +; + +/* + Get the identity topup not bound account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - `manager` must be a valid pointer to an FFIWalletManager + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_topup_not_bound(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity topup not bound account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_topup_not_bound(const FFIManagedAccountCollection *collection) +; + +/* + Get the identity invitation account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_identity_invitation(const FFIManagedAccountCollection *collection) +; + +/* + Check if identity invitation account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_identity_invitation(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider voting keys account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_provider_voting_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider voting keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_voting_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider owner keys account if it exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed + */ + +FFIManagedAccount *managed_account_collection_get_provider_owner_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider owner keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_owner_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider operator keys account if it exists in managed collection + Note: Returns null if the `bls` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed (when BLS is enabled) + */ + +void *managed_account_collection_get_provider_operator_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider operator keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_operator_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the provider platform keys account if it exists in managed collection + Note: Returns null if the `eddsa` feature is not enabled + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_free` when no longer needed (when EdDSA is enabled) + */ + +void *managed_account_collection_get_provider_platform_keys(const FFIManagedAccountCollection *collection) +; + +/* + Check if provider platform keys account exists in managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + +bool managed_account_collection_has_provider_platform_keys(const FFIManagedAccountCollection *collection) +; + +/* + Get the total number of accounts in the managed collection + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + */ + unsigned int managed_account_collection_count(const FFIManagedAccountCollection *collection) ; + +/* + Get a human-readable summary of all accounts in the managed collection + + Returns a formatted string showing all account types and their indices. + The format is designed to be clear and readable for end users. + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned string must be freed with `string_free` when no longer needed + - Returns null if the collection pointer is null + */ + char *managed_account_collection_summary(const FFIManagedAccountCollection *collection) ; + +/* + Get structured account collection summary data for managed collection + + Returns a struct containing arrays of indices for each account type and boolean + flags for special accounts. This provides Swift with programmatic access to + account information. + + # Safety + + - `collection` must be a valid pointer to an FFIManagedAccountCollection + - The returned pointer must be freed with `managed_account_collection_summary_free` when no longer needed + - Returns null if the collection pointer is null + */ + +FFIManagedAccountCollectionSummary *managed_account_collection_summary_data(const FFIManagedAccountCollection *collection) +; + +/* + Free a managed account collection summary and all its allocated memory + + # Safety + + - `summary` must be a valid pointer to an FFIManagedAccountCollectionSummary created by `managed_account_collection_summary_data` + - `summary` must not be used after calling this function + */ + +void managed_account_collection_summary_free(FFIManagedAccountCollectionSummary *summary) +; + +/* + Get the next unused receive address + + Generates the next unused receive address for the specified account. + This properly manages address gaps and updates the managed wallet state. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed by the caller + */ + +char *managed_wallet_get_next_bip44_receive_address(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIError *error) +; + +/* + Get the next unused change address + + Generates the next unused change address for the specified account. + This properly manages address gaps and updates the managed wallet state. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError + - The returned string must be freed by the caller + */ + +char *managed_wallet_get_next_bip44_change_address(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIError *error) +; + +/* + Get BIP44 external (receive) addresses in the specified range + + Returns external addresses from start_index (inclusive) to end_index (exclusive). + If addresses in the range haven't been generated yet, they will be generated. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `addresses_out` must be a valid pointer to store the address array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - Free the result with address_array_free(addresses_out, count_out) + */ + +bool managed_wallet_get_bip_44_external_address_range(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + unsigned int start_index, + unsigned int end_index, + char ***addresses_out, + size_t *count_out, + FFIError *error) +; + +/* + Get BIP44 internal (change) addresses in the specified range + + Returns internal addresses from start_index (inclusive) to end_index (exclusive). + If addresses in the range haven't been generated yet, they will be generated. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `wallet` must be a valid pointer to an FFIWallet + - `addresses_out` must be a valid pointer to store the address array pointer + - `count_out` must be a valid pointer to store the count + - `error` must be a valid pointer to an FFIError + - Free the result with address_array_free(addresses_out, count_out) + */ + +bool managed_wallet_get_bip_44_internal_address_range(FFIManagedWalletInfo *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + unsigned int start_index, + unsigned int end_index, + char ***addresses_out, + size_t *count_out, + FFIError *error) +; + +/* + Get wallet balance from managed wallet info + + Returns the balance breakdown including confirmed, unconfirmed, locked, and total amounts. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo + - `confirmed_out` must be a valid pointer to store the confirmed balance + - `unconfirmed_out` must be a valid pointer to store the unconfirmed balance + - `locked_out` must be a valid pointer to store the locked balance + - `total_out` must be a valid pointer to store the total balance + - `error` must be a valid pointer to an FFIError + */ + +bool managed_wallet_get_balance(const FFIManagedWalletInfo *managed_wallet, + uint64_t *confirmed_out, + uint64_t *unconfirmed_out, + uint64_t *locked_out, + uint64_t *total_out, + FFIError *error) +; + +/* + Free managed wallet info + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWalletInfo or null + - After calling this function, the pointer becomes invalid and must not be used + */ + void managed_wallet_free(FFIManagedWalletInfo *managed_wallet) ; + +/* + Free managed wallet info returned by wallet_manager_get_managed_wallet_info + + # Safety + + - `wallet_info` must be a valid pointer returned by wallet_manager_get_managed_wallet_info or null + - After calling this function, the pointer becomes invalid and must not be used + */ + void managed_wallet_info_free(FFIManagedWalletInfo *wallet_info) ; + +/* + Generate a new mnemonic with specified word count (12, 15, 18, 21, or 24) + */ + char *mnemonic_generate(unsigned int word_count, FFIError *error) ; + +/* + Generate a new mnemonic with specified language and word count + */ + +char *mnemonic_generate_with_language(unsigned int word_count, + FFILanguage language, + FFIError *error) +; + +/* + Validate a mnemonic phrase + + # Safety + + - `mnemonic` must be a valid null-terminated C string or null + - `error` must be a valid pointer to an FFIError + */ + bool mnemonic_validate(const char *mnemonic, FFIError *error) ; + +/* + Convert mnemonic to seed with optional passphrase + + # Safety + + - `mnemonic` must be a valid null-terminated C string + - `passphrase` must be a valid null-terminated C string or null + - `seed_out` must be a valid pointer to a buffer of at least 64 bytes + - `seed_len` must be a valid pointer to store the seed length + - `error` must be a valid pointer to an FFIError + */ + +bool mnemonic_to_seed(const char *mnemonic, + const char *passphrase, + uint8_t *seed_out, + size_t *seed_len, + FFIError *error) +; + +/* + Get word count from mnemonic + + # Safety + + - `mnemonic` must be a valid null-terminated C string or null + - `error` must be a valid pointer to an FFIError + */ + unsigned int mnemonic_word_count(const char *mnemonic, FFIError *error) ; + +/* + Free a mnemonic string + + # Safety + + - `mnemonic` must be a valid pointer created by mnemonic generation functions or null + - After calling this function, the pointer becomes invalid + */ + void mnemonic_free(char *mnemonic) ; + +/* + Build a transaction + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `outputs` must be a valid pointer to an array of FFITxOutput with at least `outputs_count` elements + - `tx_bytes_out` must be a valid pointer to store the transaction bytes pointer + - `tx_len_out` must be a valid pointer to store the transaction length + - `error` must be a valid pointer to an FFIError + - The returned transaction bytes must be freed with `transaction_bytes_free` + */ + +bool wallet_build_transaction(FFIWallet *wallet, + FFINetworks _network, + unsigned int account_index, + const FFITxOutput *outputs, + size_t outputs_count, + uint64_t fee_per_kb, + uint8_t **tx_bytes_out, + size_t *tx_len_out, + FFIError *error) +; + +/* + Sign a transaction + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `signed_tx_out` must be a valid pointer to store the signed transaction bytes pointer + - `signed_len_out` must be a valid pointer to store the signed transaction length + - `error` must be a valid pointer to an FFIError + - The returned signed transaction bytes must be freed with `transaction_bytes_free` + */ + +bool wallet_sign_transaction(const FFIWallet *wallet, + FFINetworks _network, + const uint8_t *tx_bytes, + size_t tx_len, + uint8_t **signed_tx_out, + size_t *signed_len_out, + FFIError *error) +; + +/* + Check if a transaction belongs to the wallet using ManagedWalletInfo + + # Safety + + - `wallet` must be a valid mutable pointer to an FFIWallet + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `inputs_spent_out` must be a valid pointer to store the spent inputs count + - `addresses_used_out` must be a valid pointer to store the used addresses count + - `new_balance_out` must be a valid pointer to store the new balance + - `new_address_out` must be a valid pointer to store the address array pointer + - `new_address_count_out` must be a valid pointer to store the address count + - `error` must be a valid pointer to an FFIError + */ + +bool wallet_check_transaction(FFIWallet *wallet, + FFINetwork network, + const uint8_t *tx_bytes, + size_t tx_len, + FFITransactionContext context_type, + uint32_t block_height, + const uint8_t *block_hash, + uint64_t timestamp, + bool update_state, + FFITransactionCheckResult *result_out, + FFIError *error) +; + +/* + Free transaction bytes + + # Safety + + - `tx_bytes` must be a valid pointer created by transaction functions or null + - After calling this function, the pointer becomes invalid + */ + void transaction_bytes_free(uint8_t *tx_bytes) ; + +/* + Create a new empty transaction + + # Returns + - Pointer to FFITransaction on success + - NULL on error + */ + FFITransaction *transaction_create(void) ; + +/* + Add an input to a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `input` must be a valid pointer to an FFITxIn + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_add_input(FFITransaction *tx, const FFITxIn *input) ; + +/* + Add an output to a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `output` must be a valid pointer to an FFITxOut + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_add_output(FFITransaction *tx, const FFITxOut *output) ; + +/* + Get the transaction ID + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `txid_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_get_txid(const FFITransaction *tx, uint8_t *txid_out) ; + +/* + Serialize a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `out_buf` can be NULL to get size only + - `out_len` must be a valid pointer to store the size + + # Returns + - 0 on success + - -1 on error + */ + int32_t transaction_serialize(const FFITransaction *tx, uint8_t *out_buf, uint32_t *out_len) ; + +/* + Deserialize a transaction + + # Safety + - `data` must be a valid pointer to serialized transaction data + - `len` must be the correct length of the data + + # Returns + - Pointer to FFITransaction on success + - NULL on error + */ + FFITransaction *transaction_deserialize(const uint8_t *data, uint32_t len) ; + +/* + Destroy a transaction + + # Safety + - `tx` must be a valid pointer to an FFITransaction created by transaction functions or null + - After calling this function, the pointer becomes invalid + */ + void transaction_destroy(FFITransaction *tx) ; + +/* + Calculate signature hash for an input + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `script_pubkey` must be a valid pointer to the script pubkey + - `hash_out` must be a valid pointer to a buffer of at least 32 bytes + + # Returns + - 0 on success + - -1 on error + */ + +int32_t transaction_sighash(const FFITransaction *tx, + uint32_t input_index, + const uint8_t *script_pubkey, + uint32_t script_pubkey_len, + uint32_t sighash_type, + uint8_t *hash_out) +; + +/* + Sign a transaction input + + # Safety + - `tx` must be a valid pointer to an FFITransaction + - `private_key` must be a valid pointer to a 32-byte private key + - `script_pubkey` must be a valid pointer to the script pubkey + + # Returns + - 0 on success + - -1 on error + */ + +int32_t transaction_sign_input(FFITransaction *tx, + uint32_t input_index, + const uint8_t *private_key, + const uint8_t *script_pubkey, + uint32_t script_pubkey_len, + uint32_t sighash_type) +; + +/* + Create a P2PKH script pubkey + + # Safety + - `pubkey_hash` must be a valid pointer to a 20-byte public key hash + - `out_buf` can be NULL to get size only + - `out_len` must be a valid pointer to store the size + + # Returns + - 0 on success + - -1 on error + */ + int32_t script_p2pkh(const uint8_t *pubkey_hash, uint8_t *out_buf, uint32_t *out_len) ; + +/* + Extract public key hash from P2PKH address + + # Safety + - `address` must be a valid pointer to a null-terminated C string + - `hash_out` must be a valid pointer to a buffer of at least 20 bytes + + # Returns + - 0 on success + - -1 on error + */ + int32_t address_to_pubkey_hash(const char *address, FFINetwork network, uint8_t *hash_out) ; + +/* + Create a managed wallet from a regular wallet + + This creates a ManagedWalletInfo instance from a Wallet, which includes + address pools and transaction checking capabilities. + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `error` must be a valid pointer to an FFIError or null + - The returned pointer must be freed with `ffi_managed_wallet_free` + */ + FFIManagedWallet *wallet_create_managed_wallet(const FFIWallet *wallet, FFIError *error) ; + +/* + Check if a transaction belongs to the wallet + + This function checks a transaction against all relevant account types in the wallet + and returns detailed information about which accounts are affected. + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - `wallet` must be a valid pointer to an FFIWallet (needed for address generation) + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `result_out` must be a valid pointer to store the result + - `error` must be a valid pointer to an FFIError + - The affected_accounts array in the result must be freed with `transaction_check_result_free` + */ + +bool managed_wallet_check_transaction(FFIManagedWallet *managed_wallet, + const FFIWallet *wallet, + FFINetwork network, + const uint8_t *tx_bytes, + size_t tx_len, + FFITransactionContext context_type, + unsigned int block_height, + const uint8_t *block_hash, + uint64_t timestamp, + bool update_state, + FFITransactionCheckResult *result_out, + FFIError *error) +; + +/* + Free a transaction check result + + # Safety + + - `result` must be a valid pointer to an FFITransactionCheckResult + - This function must only be called once per result + */ + void transaction_check_result_free(FFITransactionCheckResult *result) ; + +/* + Free a managed wallet (FFIManagedWallet type) + + # Safety + + - `managed_wallet` must be a valid pointer to an FFIManagedWallet + - This function must only be called once per managed wallet + */ + void ffi_managed_wallet_free(FFIManagedWallet *managed_wallet) ; + +/* + Get the transaction classification for routing + + Returns a string describing the transaction type (e.g., "Standard", "CoinJoin", + "AssetLock", "AssetUnlock", "ProviderRegistration", etc.) + + # Safety + + - `tx_bytes` must be a valid pointer to transaction bytes with at least `tx_len` bytes + - `error` must be a valid pointer to an FFIError or null + - The returned string must be freed by the caller + */ + char *transaction_classify(const uint8_t *tx_bytes, size_t tx_len, FFIError *error) ; + + const char *ffi_network_get_name(FFINetwork network) ; + +/* + Free a string + + # Safety + + - `s` must be a valid pointer created by C string creation functions or null + - After calling this function, the pointer becomes invalid + */ + void string_free(char *s) ; + +/* + Get all UTXOs from managed wallet info + + # Safety + + - `managed_info` must be a valid pointer to an FFIManagedWalletInfo instance + - `utxos_out` must be a valid pointer to store the UTXO array pointer + - `count_out` must be a valid pointer to store the UTXO count + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned UTXO array must be freed with `utxo_array_free` when no longer needed + */ + +bool managed_wallet_get_utxos(const FFIManagedWalletInfo *managed_info, + FFINetwork network, + FFIUTXO **utxos_out, + size_t *count_out, + FFIError *error) +; + +/* + Get all UTXOs (deprecated - use managed_wallet_get_utxos instead) + + # Safety + + This function is deprecated and returns an empty list. + Use `managed_wallet_get_utxos` with a ManagedWalletInfo instead. + */ + +bool wallet_get_utxos(const FFIWallet *_wallet, + FFINetworks _network, + FFIUTXO **utxos_out, + size_t *count_out, + FFIError *error) +; + +/* + Free UTXO array + + # Safety + + - `utxos` must be a valid pointer to an array of FFIUTXO structs allocated by this library + - `count` must match the number of UTXOs in the array + - The pointer must not be used after calling this function + - This function must only be called once per array + */ + void utxo_array_free(FFIUTXO *utxos, size_t count) ; + +/* + Create a new wallet from mnemonic with options + + # Safety + + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned pointer must be freed with `wallet_free` when no longer needed + */ + +FFIWallet *wallet_create_from_mnemonic_with_options(const char *mnemonic, + const char *passphrase, + FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new wallet from mnemonic (backward compatibility - single network) + + # Safety + + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned pointer must be freed with `wallet_free` when no longer needed + */ + +FFIWallet *wallet_create_from_mnemonic(const char *mnemonic, + const char *passphrase, + FFINetworks network, + FFIError *error) +; + +/* + Create a new wallet from seed with options + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_from_seed_with_options(const uint8_t *seed, + size_t seed_len, + FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new wallet from seed (backward compatibility) + + # Safety + + - `seed` must be a valid pointer to a byte array of `seed_len` length + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_from_seed(const uint8_t *seed, + size_t seed_len, + FFINetworks network, + FFIError *error) +; + +/* + Create a new random wallet with options + + # Safety + + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +FFIWallet *wallet_create_random_with_options(FFINetworks networks, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Create a new random wallet (backward compatibility) + + # Safety + + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure the pointer remains valid for the duration of this call + */ + FFIWallet *wallet_create_random(FFINetworks network, FFIError *error) ; + +/* + Get wallet ID (32-byte hash) + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet + - `id_out` must be a valid pointer to a 32-byte buffer + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_get_id(const FFIWallet *wallet, uint8_t *id_out, FFIError *error) ; + +/* + Check if wallet has mnemonic + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_has_mnemonic(const FFIWallet *wallet, FFIError *error) ; + +/* + Check if wallet is watch-only + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + bool wallet_is_watch_only(const FFIWallet *wallet, FFIError *error) ; + +/* + Get extended public key for account + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned C string must be freed by the caller when no longer needed + */ + +char *wallet_get_xpub(const FFIWallet *wallet, + FFINetwork network, + unsigned int account_index, + FFIError *error) +; + +/* + Free a wallet + + # Safety + + - `wallet` must be a valid pointer to an FFIWallet that was created by this library + - The pointer must not be used after calling this function + - This function must only be called once per wallet + */ + void wallet_free(FFIWallet *wallet) ; + +/* + Free a const wallet handle + + This is a const-safe wrapper for wallet_free() that accepts a const pointer. + Use this function when you have a *const FFIWallet that needs to be freed, + such as wallets returned from wallet_manager_get_wallet(). + + # Safety + + - `wallet` must be a valid pointer created by wallet creation functions or null + - After calling this function, the pointer becomes invalid + - This function must only be called once per wallet + - The wallet must have been allocated by this library (not stack or static memory) + */ + void wallet_free_const(const FFIWallet *wallet) ; + +/* + Add an account to the wallet without xpub + + # Safety + + This function dereferences a raw pointer to FFIWallet. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account(FFIWallet *wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index) +; + +/* + Add an account to the wallet with xpub as byte array + + # Safety + + This function dereferences raw pointers. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The xpub_bytes pointer is either null or points to at least xpub_len bytes + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account_with_xpub_bytes(FFIWallet *wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index, + const uint8_t *xpub_bytes, + size_t xpub_len) +; + +/* + Add an account to the wallet with xpub as string + + # Safety + + This function dereferences raw pointers. + The caller must ensure that: + - The wallet pointer is either null or points to a valid FFIWallet + - The xpub_string pointer is either null or points to a valid null-terminated C string + - The FFIWallet remains valid for the duration of this call + */ + +FFIAccountResult wallet_add_account_with_string_xpub(FFIWallet *wallet, + FFINetwork network, + FFIAccountType account_type, + unsigned int account_index, + const char *xpub_string) +; + +/* + Create a new wallet manager + */ + FFIWalletManager *wallet_manager_create(FFIError *error) ; + +/* + Add a wallet from mnemonic to the manager with options + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_add_wallet_from_mnemonic_with_options(FFIWalletManager *manager, + const char *mnemonic, + const char *passphrase, + FFINetworks network, + const FFIWalletAccountCreationOptions *account_options, + FFIError *error) +; + +/* + Add a wallet from mnemonic to the manager (backward compatibility) + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_add_wallet_from_mnemonic(FFIWalletManager *manager, + const char *mnemonic, + const char *passphrase, + FFINetworks network, + FFIError *error) +; + +/* + Add a wallet from mnemonic to the manager and return serialized bytes + + Creates a wallet from a mnemonic phrase, adds it to the manager, optionally downgrading it + to a pubkey-only wallet (watch-only or externally signable), and returns the serialized wallet bytes. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `mnemonic` must be a valid pointer to a null-terminated C string + - `passphrase` must be a valid pointer to a null-terminated C string or null + - `birth_height` is optional, pass 0 for default + - `account_options` must be a valid pointer to FFIWalletAccountCreationOptions or null + - `downgrade_to_pubkey_wallet` if true, creates a watch-only or externally signable wallet + - `allow_external_signing` if true AND downgrade_to_pubkey_wallet is true, creates an externally signable wallet + - `wallet_bytes_out` must be a valid pointer to a pointer that will receive the serialized bytes + - `wallet_bytes_len_out` must be a valid pointer that will receive the byte length + - `wallet_id_out` must be a valid pointer to a 32-byte array that will receive the wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The caller must free the returned wallet_bytes using wallet_manager_free_wallet_bytes() + */ + +bool wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes(FFIWalletManager *manager, + const char *mnemonic, + const char *passphrase, + FFINetworks network, + unsigned int birth_height, + const FFIWalletAccountCreationOptions *account_options, + bool downgrade_to_pubkey_wallet, + bool allow_external_signing, + uint8_t **wallet_bytes_out, + size_t *wallet_bytes_len_out, + uint8_t *wallet_id_out, + FFIError *error) +; + +/* + Free wallet bytes buffer + + # Safety + + - `wallet_bytes` must be a valid pointer to a buffer allocated by wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes + - `bytes_len` must match the original allocation size + - The pointer must not be used after calling this function + - This function must only be called once per buffer + */ + +void wallet_manager_free_wallet_bytes(uint8_t *wallet_bytes, + size_t bytes_len) +; + +/* + Import a wallet from bincode-serialized bytes + + Deserializes a wallet from bytes and adds it to the manager. + Returns a 32-byte wallet ID on success. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_bytes` must be a valid pointer to bincode-serialized wallet bytes + - `wallet_bytes_len` must be the exact length of the wallet bytes + - `wallet_id_out` must be a valid pointer to a 32-byte array that will receive the wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_import_wallet_from_bytes(FFIWalletManager *manager, + const uint8_t *wallet_bytes, + size_t wallet_bytes_len, + uint8_t *wallet_id_out, + FFIError *error) +; + +/* + Get wallet IDs + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager + - `wallet_ids_out` must be a valid pointer to a pointer that will receive the wallet IDs + - `count_out` must be a valid pointer to receive the count + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_get_wallet_ids(const FFIWalletManager *manager, + uint8_t **wallet_ids_out, + size_t *count_out, + FFIError *error) +; + +/* + Get a wallet from the manager + + Returns a reference to the wallet if found + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned wallet must be freed with wallet_free_const() + */ + +const FFIWallet *wallet_manager_get_wallet(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFIError *error) +; + +/* + Get managed wallet info from the manager + + Returns a reference to the managed wallet info if found + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + - The returned managed wallet info must be freed with managed_wallet_info_free() + */ + +FFIManagedWalletInfo *wallet_manager_get_managed_wallet_info(const FFIWalletManager *manager, + const uint8_t *wallet_id, + FFIError *error) +; + +/* + Get wallet balance + + Returns the confirmed and unconfirmed balance for a specific wallet + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `wallet_id` must be a valid pointer to a 32-byte wallet ID + - `confirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) + - `unconfirmed_out` must be a valid pointer to a u64 (maps to C uint64_t) + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_get_wallet_balance(const FFIWalletManager *manager, + const uint8_t *wallet_id, + uint64_t *confirmed_out, + uint64_t *unconfirmed_out, + FFIError *error) +; + +/* + Process a transaction through all wallets + + Checks a transaction against all wallets and updates their states if relevant. + Returns true if the transaction was relevant to at least one wallet. + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `tx_bytes` must be a valid pointer to transaction bytes + - `tx_len` must be the length of the transaction bytes + - `network` is the network type + - `context` must be a valid pointer to FFITransactionContextDetails + - `update_state_if_found` indicates whether to update wallet state when transaction is relevant + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_process_transaction(FFIWalletManager *manager, + const uint8_t *tx_bytes, + size_t tx_len, + FFINetwork network, + const FFITransactionContextDetails *context, + bool update_state_if_found, + FFIError *error) +; + +/* + Update block height for a network + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +bool wallet_manager_update_height(FFIWalletManager *manager, + FFINetwork network, + unsigned int height, + FFIError *error) +; + +/* + Get current height for a network + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + +unsigned int wallet_manager_current_height(const FFIWalletManager *manager, + FFINetwork network, + FFIError *error) +; + +/* + Get wallet count + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager instance + - `error` must be a valid pointer to an FFIError structure or null + - The caller must ensure all pointers remain valid for the duration of this call + */ + size_t wallet_manager_wallet_count(const FFIWalletManager *manager, FFIError *error) ; + +/* + Free wallet manager + + # Safety + + - `manager` must be a valid pointer to an FFIWalletManager that was created by this library + - The pointer must not be used after calling this function + - This function must only be called once per manager + */ + void wallet_manager_free(FFIWalletManager *manager) ; + +/* + Free wallet IDs buffer + + # Safety + + - `wallet_ids` must be a valid pointer to a buffer allocated by this library + - `count` must match the number of wallet IDs in the buffer + - The pointer must not be used after calling this function + - This function must only be called once per buffer + */ + void wallet_manager_free_wallet_ids(uint8_t *wallet_ids, size_t count) ; + +/* + Free address array + + # Safety + + - `addresses` must be a valid pointer to an array of C string pointers allocated by this library + - `count` must match the original allocation size + - Each address pointer in the array must be either null or a valid C string allocated by this library + - The pointers must not be used after calling this function + - This function must only be called once per allocation + */ + +void wallet_manager_free_addresses(char **addresses, + size_t count) +; + +/* + Encrypt a private key with BIP38 + + # Safety + + This function is unsafe because it dereferences raw pointers: + - `private_key` must be a valid, null-terminated C string + - `passphrase` must be a valid, null-terminated C string + - `error` must be a valid pointer to an FFIError or null + */ + +char *bip38_encrypt_private_key(const char *private_key, + const char *passphrase, + FFINetworks _network, + FFIError *error) +; + +/* + Decrypt a BIP38 encrypted private key + + # Safety + + This function is unsafe because it dereferences raw pointers: + - `encrypted_key` must be a valid, null-terminated C string + - `passphrase` must be a valid, null-terminated C string + - `error` must be a valid pointer to an FFIError or null + */ + +char *bip38_decrypt_private_key(const char *encrypted_key, + const char *passphrase, + FFIError *error) +; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif /* KEY_WALLET_FFI_H */ diff --git a/swift-dash-core-sdk/build-ios.sh b/swift-dash-core-sdk/build-ios.sh index 9907c42b2..406c51374 100755 --- a/swift-dash-core-sdk/build-ios.sh +++ b/swift-dash-core-sdk/build-ios.sh @@ -75,12 +75,11 @@ mkdir -p target/ios/release cp target/aarch64-apple-ios/release/libdash_spv_ffi.a target/ios/release/ cp target/aarch64-apple-ios/release/libkey_wallet_ffi.a target/ios/release/ -# Navigate back to swift directory +# Navigate back to swift directory and sync headers cd swift-dash-core-sdk -# Copy the generated header file -echo -e "${GREEN}Copying generated header file...${NC}" -cp ../dash-spv-ffi/include/dash_spv_ffi.h Sources/DashSPVFFI/include/ +echo -e "${GREEN}Syncing headers to Swift SDK...${NC}" +./sync-headers.sh # Copy libraries to example directory echo -e "${GREEN}Copying libraries to example directory...${NC}" @@ -108,4 +107,4 @@ echo "Symlinks created for Xcode:" echo " - libdash_spv_ffi.a -> libdash_spv_ffi_sim.a" echo " - libkey_wallet_ffi.a -> libkey_wallet_ffi_sim.a" echo "" -echo "You can now open DashHDWalletExample.xcodeproj in Xcode and build!" \ No newline at end of file +echo "You can now open DashHDWalletExample.xcodeproj in Xcode and build!" From b3437f5984b7118f8921edb30fd4bbe7ce7e30d1 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 1 Sep 2025 23:46:32 +0700 Subject: [PATCH 9/9] fixes --- key-wallet-ffi/include/key_wallet_ffi.h | 1 - key-wallet-ffi/src/derivation.rs | 4 ++-- key-wallet-ffi/src/transaction.rs | 3 +-- key-wallet-ffi/src/transaction_checking.rs | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/key-wallet-ffi/include/key_wallet_ffi.h b/key-wallet-ffi/include/key_wallet_ffi.h index ceff25e1a..d04985444 100644 --- a/key-wallet-ffi/include/key_wallet_ffi.h +++ b/key-wallet-ffi/include/key_wallet_ffi.h @@ -639,7 +639,6 @@ typedef struct { } FFITxOutput; /* - Transaction context for checking Transaction check result */ typedef struct { diff --git a/key-wallet-ffi/src/derivation.rs b/key-wallet-ffi/src/derivation.rs index ba039b8b3..d2ceb5c4c 100644 --- a/key-wallet-ffi/src/derivation.rs +++ b/key-wallet-ffi/src/derivation.rs @@ -446,7 +446,7 @@ pub unsafe extern "C" fn derivation_derive_private_key_from_seed( } let seed_slice = slice::from_raw_parts(seed, seed_len); - let network_rust: Network = network.try_into().unwrap_or(Network::Dash); + let network_rust: Network = network.into(); let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, @@ -722,7 +722,7 @@ pub unsafe extern "C" fn dip9_derive_identity_key( } let seed_slice = slice::from_raw_parts(seed, seed_len); - let network_rust: Network = network.try_into().unwrap_or(Network::Dash); + let network_rust: Network = network.into(); use key_wallet::bip32::{ChildNumber, DerivationPath}; use key_wallet::dip9::{ diff --git a/key-wallet-ffi/src/transaction.rs b/key-wallet-ffi/src/transaction.rs index 2b2f57a07..2a07e042c 100644 --- a/key-wallet-ffi/src/transaction.rs +++ b/key-wallet-ffi/src/transaction.rs @@ -138,9 +138,8 @@ pub unsafe extern "C" fn wallet_sign_transaction( } } -/// Transaction context for checking +// Transaction context for checking // FFITransactionContext is imported from types module at the top - /// Transaction check result #[repr(C)] pub struct FFITransactionCheckResult { diff --git a/key-wallet-ffi/src/transaction_checking.rs b/key-wallet-ffi/src/transaction_checking.rs index 632084467..feb56a1fd 100644 --- a/key-wallet-ffi/src/transaction_checking.rs +++ b/key-wallet-ffi/src/transaction_checking.rs @@ -23,9 +23,8 @@ pub struct FFIManagedWallet { pub(crate) inner: *mut ManagedWalletInfo, } -/// Transaction context for checking +// Transaction context for checking // FFITransactionContext is imported from types module at the top - /// Account type match result #[repr(C)] pub struct FFIAccountMatch {