Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions key-wallet-ffi/FFI_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down Expand Up @@ -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:**
Expand Down Expand Up @@ -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:**
Expand Down
4 changes: 2 additions & 2 deletions key-wallet-ffi/examples/check_transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ typedef enum {
InstantSend = 1,
InBlock = 2,
InChainLockedBlock = 3
} FFITransactionContext;
} FFITransactionContextType;

typedef struct {
bool is_relevant;
Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions key-wallet-ffi/src/managed_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions key-wallet-ffi/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 9 additions & 7 deletions key-wallet-ffi/src/transaction_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
68 changes: 38 additions & 30 deletions key-wallet-ffi/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,19 @@ impl From<BlockInfo> 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<TransactionContext> {
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;
}
Expand Down Expand Up @@ -517,31 +517,35 @@ 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)));
}

#[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());
Expand All @@ -550,16 +554,18 @@ 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));
}

#[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)
Expand Down Expand Up @@ -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
Expand All @@ -846,48 +852,50 @@ pub enum FFITransactionContext {
InChainLockedBlock = 3,
}

impl From<TransactionContext> for FFITransactionContext {
impl From<TransactionContext> 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(),
}
}

/// Create an in-block context
pub fn in_block(block_info: FFIBlockInfo) -> Self {
Self {
context_type: FFITransactionContext::InBlock,
context_type: FFITransactionContextType::InBlock,
block_info,
}
}

/// 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,
}
}
Expand All @@ -900,9 +908,9 @@ impl FFITransactionContextDetails {
}
}

impl From<TransactionContext> for FFITransactionContextDetails {
impl From<TransactionContext> 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))
Expand Down
4 changes: 2 additions & 2 deletions key-wallet-ffi/src/wallet_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
19 changes: 9 additions & 10 deletions key-wallet-ffi/src/wallet_manager_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading