diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index 24fd6f527..c867818ee 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -703,14 +703,14 @@ Get the network for this wallet manager # Safety - `manager` must be a valid p #### `wallet_manager_process_transaction` ```c -wallet_manager_process_transaction(manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, 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, context: *const crate::types::FFITransactionContext, 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 - `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 +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 - `context` must be a valid pointer to FFITransactionContext - `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 - `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 +- `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 - `context` must be a valid pointer to FFITransactionContext - `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` @@ -853,7 +853,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 FFIManagedWalletInfo, wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +managed_wallet_check_transaction(managed_wallet: *mut FFIManagedWalletInfo, wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContextType, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool ``` **Description:** @@ -1317,7 +1317,7 @@ Build and sign a transaction using the wallet's managed info This is the recomm #### `wallet_check_transaction` ```c -wallet_check_transaction(wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContext, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool +wallet_check_transaction(wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, context_type: FFITransactionContextType, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, error: *mut FFIError,) -> bool ``` **Description:** diff --git a/key-wallet-ffi/examples/check_transaction.c b/key-wallet-ffi/examples/check_transaction.c index e30cb2b52..1baeaccdc 100644 --- a/key-wallet-ffi/examples/check_transaction.c +++ b/key-wallet-ffi/examples/check_transaction.c @@ -18,7 +18,7 @@ typedef enum { InstantSend = 1, InBlock = 2, InChainLockedBlock = 3 -} FFITransactionContext; +} FFITransactionContextType; typedef struct { bool is_relevant; @@ -45,7 +45,7 @@ extern bool wallet_check_transaction( FFINetwork network, const uint8_t* tx_bytes, size_t tx_len, - FFITransactionContext context_type, + FFITransactionContextType context_type, uint32_t block_height, const uint8_t* block_hash, // 32 bytes if not null uint64_t timestamp, diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index 016226160..b55138969 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -11,7 +11,7 @@ use dashcore::hashes::Hash; use crate::address_pool::{FFIAddressPool, FFIAddressPoolType}; use crate::error::{FFIError, FFIErrorCode}; -use crate::types::{FFIAccountType, FFITransactionContextDetails}; +use crate::types::{FFIAccountType, FFITransactionContext}; use crate::wallet_manager::FFIWalletManager; use crate::FFINetwork; use key_wallet::account::account_collection::{DashpayAccountKey, PlatformPaymentAccountKey}; @@ -667,7 +667,7 @@ pub struct FFITransactionRecord { /// Net amount for this account (positive = received, negative = sent) pub net_amount: i64, /// Transaction context (mempool, instant-send, in-block, chain-locked + block info) - pub context: FFITransactionContextDetails, + pub context: FFITransactionContext, /// Fee if known, 0 if unknown pub fee: u64, } @@ -724,7 +724,7 @@ pub unsafe extern "C" fn managed_core_account_get_transactions( ffi_record.net_amount = record.net_amount; // Copy transaction context - ffi_record.context = FFITransactionContextDetails::from(record.context); + ffi_record.context = FFITransactionContext::from(record.context); // Copy fee (0 if unknown) ffi_record.fee = record.fee.unwrap_or(0); diff --git a/key-wallet-ffi/src/transaction.rs b/key-wallet-ffi/src/transaction.rs index 215ff10fa..9d7d09d5c 100644 --- a/key-wallet-ffi/src/transaction.rs +++ b/key-wallet-ffi/src/transaction.rs @@ -16,7 +16,7 @@ use secp256k1::{Message, Secp256k1, SecretKey}; use crate::error::{FFIError, FFIErrorCode}; use crate::types::{ - transaction_context_from_ffi, FFIBlockInfo, FFINetwork, FFITransactionContext, FFIWallet, + transaction_context_from_ffi, FFIBlockInfo, FFINetwork, FFITransactionContextType, FFIWallet, }; use crate::FFIWalletManager; @@ -362,7 +362,7 @@ pub unsafe extern "C" fn wallet_build_and_sign_transaction( } // Transaction context for checking -// FFITransactionContext is imported from types module at the top +// FFITransactionContextType is imported from types module at the top /// Transaction check result #[repr(C)] pub struct FFITransactionCheckResult { @@ -393,7 +393,7 @@ pub unsafe extern "C" fn wallet_check_transaction( wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, - context_type: FFITransactionContext, + context_type: FFITransactionContextType, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, diff --git a/key-wallet-ffi/src/transaction_checking.rs b/key-wallet-ffi/src/transaction_checking.rs index b2e9ae36b..77631721b 100644 --- a/key-wallet-ffi/src/transaction_checking.rs +++ b/key-wallet-ffi/src/transaction_checking.rs @@ -10,7 +10,9 @@ use std::slice; use crate::error::{FFIError, FFIErrorCode}; use crate::managed_wallet::{managed_wallet_info_free, FFIManagedWalletInfo}; -use crate::types::{transaction_context_from_ffi, FFIBlockInfo, FFITransactionContext, FFIWallet}; +use crate::types::{ + transaction_context_from_ffi, FFIBlockInfo, FFITransactionContextType, FFIWallet, +}; use dashcore::consensus::Decodable; use dashcore::Transaction; use key_wallet::transaction_checking::{ @@ -19,7 +21,7 @@ use key_wallet::transaction_checking::{ use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; // Transaction context for checking -// FFITransactionContext is imported from types module at the top +// FFITransactionContextType is imported from types module at the top /// Account type match result #[repr(C)] pub struct FFIAccountMatch { @@ -111,7 +113,7 @@ pub unsafe extern "C" fn managed_wallet_check_transaction( wallet: *mut FFIWallet, tx_bytes: *const u8, tx_len: usize, - context_type: FFITransactionContext, + context_type: FFITransactionContextType, block_info: FFIBlockInfo, update_state: bool, result_out: *mut FFITransactionCheckResult, @@ -581,9 +583,9 @@ mod tests { #[test] fn test_transaction_context_conversion() { // Test that FFI transaction context values match expectations - assert_eq!(FFITransactionContext::Mempool as u32, 0); - assert_eq!(FFITransactionContext::InstantSend as u32, 1); - assert_eq!(FFITransactionContext::InBlock as u32, 2); - assert_eq!(FFITransactionContext::InChainLockedBlock as u32, 3); + assert_eq!(FFITransactionContextType::Mempool as u32, 0); + assert_eq!(FFITransactionContextType::InstantSend as u32, 1); + assert_eq!(FFITransactionContextType::InBlock as u32, 2); + assert_eq!(FFITransactionContextType::InChainLockedBlock as u32, 3); } } diff --git a/key-wallet-ffi/src/types.rs b/key-wallet-ffi/src/types.rs index 0e369a4a6..a0f81d188 100644 --- a/key-wallet-ffi/src/types.rs +++ b/key-wallet-ffi/src/types.rs @@ -50,19 +50,19 @@ impl From for FFIBlockInfo { /// Returns `None` when block info is all-zeros for confirmed contexts (`InBlock`, /// `InChainLockedBlock`), indicating invalid input from the FFI caller. pub(crate) fn transaction_context_from_ffi( - context_type: FFITransactionContext, + context_type: FFITransactionContextType, block_info: &FFIBlockInfo, ) -> Option { match context_type { - FFITransactionContext::Mempool => Some(TransactionContext::Mempool), - FFITransactionContext::InstantSend => Some(TransactionContext::InstantSend), - FFITransactionContext::InBlock => { + FFITransactionContextType::Mempool => Some(TransactionContext::Mempool), + FFITransactionContextType::InstantSend => Some(TransactionContext::InstantSend), + FFITransactionContextType::InBlock => { if block_info.block_hash == [0u8; 32] && block_info.timestamp == 0 { return None; } Some(TransactionContext::InBlock(block_info.to_block_info())) } - FFITransactionContext::InChainLockedBlock => { + FFITransactionContextType::InChainLockedBlock => { if block_info.block_hash == [0u8; 32] && block_info.timestamp == 0 { return None; } @@ -517,15 +517,17 @@ mod tests { #[test] fn transaction_context_from_ffi_mempool_with_empty_block_info() { - let result = - transaction_context_from_ffi(FFITransactionContext::Mempool, &FFIBlockInfo::empty()); + let result = transaction_context_from_ffi( + FFITransactionContextType::Mempool, + &FFIBlockInfo::empty(), + ); assert!(matches!(result, Some(TransactionContext::Mempool))); } #[test] fn transaction_context_from_ffi_instant_send_with_empty_block_info() { let result = transaction_context_from_ffi( - FFITransactionContext::InstantSend, + FFITransactionContextType::InstantSend, &FFIBlockInfo::empty(), ); assert!(matches!(result, Some(TransactionContext::InstantSend))); @@ -533,15 +535,17 @@ mod tests { #[test] fn transaction_context_from_ffi_in_block_with_empty_block_info() { - let result = - transaction_context_from_ffi(FFITransactionContext::InBlock, &FFIBlockInfo::empty()); + let result = transaction_context_from_ffi( + FFITransactionContextType::InBlock, + &FFIBlockInfo::empty(), + ); assert!(result.is_none()); } #[test] fn transaction_context_from_ffi_in_chain_locked_block_with_empty_block_info() { let result = transaction_context_from_ffi( - FFITransactionContext::InChainLockedBlock, + FFITransactionContextType::InChainLockedBlock, &FFIBlockInfo::empty(), ); assert!(result.is_none()); @@ -550,7 +554,7 @@ mod tests { #[test] fn transaction_context_from_ffi_in_block_with_valid_block_info() { let block_info = valid_block_info(); - let result = transaction_context_from_ffi(FFITransactionContext::InBlock, &block_info); + let result = transaction_context_from_ffi(FFITransactionContextType::InBlock, &block_info); let ctx = result.expect("should return Some for InBlock with valid block info"); assert!(matches!(ctx, TransactionContext::InBlock(info) if info.height() == 1000)); } @@ -558,8 +562,10 @@ mod tests { #[test] fn transaction_context_from_ffi_in_chain_locked_block_with_valid_block_info() { let block_info = valid_block_info(); - let result = - transaction_context_from_ffi(FFITransactionContext::InChainLockedBlock, &block_info); + let result = transaction_context_from_ffi( + FFITransactionContextType::InChainLockedBlock, + &block_info, + ); let ctx = result.expect("should return Some for InChainLockedBlock with valid block info"); assert!( matches!(ctx, TransactionContext::InChainLockedBlock(info) if info.height() == 1000) @@ -832,10 +838,10 @@ impl FFIWalletAccountCreationOptions { } } -/// FFI-compatible transaction context +/// FFI-compatible transaction context type #[repr(C)] #[derive(Debug, Clone, Copy)] -pub enum FFITransactionContext { +pub enum FFITransactionContextType { /// Transaction is in the mempool (unconfirmed) Mempool = 0, /// Transaction is in the mempool with an InstantSend lock @@ -846,32 +852,34 @@ pub enum FFITransactionContext { InChainLockedBlock = 3, } -impl From for FFITransactionContext { +impl From for FFITransactionContextType { fn from(ctx: TransactionContext) -> Self { match ctx { - TransactionContext::Mempool => FFITransactionContext::Mempool, - TransactionContext::InstantSend => FFITransactionContext::InstantSend, - TransactionContext::InBlock(_) => FFITransactionContext::InBlock, - TransactionContext::InChainLockedBlock(_) => FFITransactionContext::InChainLockedBlock, + TransactionContext::Mempool => FFITransactionContextType::Mempool, + TransactionContext::InstantSend => FFITransactionContextType::InstantSend, + TransactionContext::InBlock(_) => FFITransactionContextType::InBlock, + TransactionContext::InChainLockedBlock(_) => { + FFITransactionContextType::InChainLockedBlock + } } } } -/// FFI-compatible transaction context details +/// FFI-compatible transaction context (type + optional block info) #[repr(C)] #[derive(Debug, Clone, Copy)] -pub struct FFITransactionContextDetails { +pub struct FFITransactionContext { /// The context type - pub context_type: FFITransactionContext, + pub context_type: FFITransactionContextType, /// Block info (zeroed for mempool/instant-send contexts) pub block_info: FFIBlockInfo, } -impl FFITransactionContextDetails { +impl FFITransactionContext { /// Create a mempool context pub fn mempool() -> Self { Self { - context_type: FFITransactionContext::Mempool, + context_type: FFITransactionContextType::Mempool, block_info: FFIBlockInfo::empty(), } } @@ -879,7 +887,7 @@ impl FFITransactionContextDetails { /// Create an in-block context pub fn in_block(block_info: FFIBlockInfo) -> Self { Self { - context_type: FFITransactionContext::InBlock, + context_type: FFITransactionContextType::InBlock, block_info, } } @@ -887,7 +895,7 @@ impl FFITransactionContextDetails { /// Create a chain-locked block context pub fn in_chain_locked_block(block_info: FFIBlockInfo) -> Self { Self { - context_type: FFITransactionContext::InChainLockedBlock, + context_type: FFITransactionContextType::InChainLockedBlock, block_info, } } @@ -900,9 +908,9 @@ impl FFITransactionContextDetails { } } -impl From for FFITransactionContextDetails { +impl From for FFITransactionContext { fn from(ctx: TransactionContext) -> Self { - let context_type = FFITransactionContext::from(ctx); + let context_type = FFITransactionContextType::from(ctx); let block_info = ctx .block_info() .map(|info| FFIBlockInfo::from(*info)) diff --git a/key-wallet-ffi/src/wallet_manager.rs b/key-wallet-ffi/src/wallet_manager.rs index 4af93731f..cc9652f9a 100644 --- a/key-wallet-ffi/src/wallet_manager.rs +++ b/key-wallet-ffi/src/wallet_manager.rs @@ -730,7 +730,7 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_balance( /// - `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 -/// - `context` must be a valid pointer to FFITransactionContextDetails +/// - `context` must be a valid pointer to FFITransactionContext /// - `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 @@ -739,7 +739,7 @@ pub unsafe extern "C" fn wallet_manager_process_transaction( manager: *mut FFIWalletManager, tx_bytes: *const u8, tx_len: usize, - context: *const crate::types::FFITransactionContextDetails, + context: *const crate::types::FFITransactionContext, update_state_if_found: bool, error: *mut FFIError, ) -> bool { diff --git a/key-wallet-ffi/src/wallet_manager_tests.rs b/key-wallet-ffi/src/wallet_manager_tests.rs index 1753ab235..2acaa98d0 100644 --- a/key-wallet-ffi/src/wallet_manager_tests.rs +++ b/key-wallet-ffi/src/wallet_manager_tests.rs @@ -627,10 +627,10 @@ mod tests { ]; // Create transaction contexts for testing - let mempool_context = crate::types::FFITransactionContextDetails::mempool(); + let mempool_context = crate::types::FFITransactionContext::mempool(); let block_context = - crate::types::FFITransactionContextDetails::in_block(crate::types::FFIBlockInfo { + crate::types::FFITransactionContext::in_block(crate::types::FFIBlockInfo { height: 100000, block_hash: [0u8; 32], timestamp: 1234567890, @@ -667,14 +667,13 @@ mod tests { assert_eq!(unsafe { (*error).code }, FFIErrorCode::InvalidInput); // Test processing a chain-locked block transaction - let chain_locked_context = - crate::types::FFITransactionContextDetails::in_chain_locked_block( - crate::types::FFIBlockInfo { - height: 100000, - block_hash: [0u8; 32], - timestamp: 1234567890, - }, - ); + let chain_locked_context = crate::types::FFITransactionContext::in_chain_locked_block( + crate::types::FFIBlockInfo { + height: 100000, + block_hash: [0u8; 32], + timestamp: 1234567890, + }, + ); let processed = unsafe { wallet_manager::wallet_manager_process_transaction( manager,