diff --git a/Cargo.lock b/Cargo.lock index d4c52e89..73156027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11574,6 +11574,7 @@ version = "0.1.0" dependencies = [ "bip39", "cbindgen", + "common", "key_protocol", "lee", "lee_core", diff --git a/integration_tests/tests/wallet_ffi.rs b/integration_tests/tests/wallet_ffi.rs index a19bc355..e9fe3920 100644 --- a/integration_tests/tests/wallet_ffi.rs +++ b/integration_tests/tests/wallet_ffi.rs @@ -284,6 +284,12 @@ unsafe extern "C" { ) -> LabelList; fn wallet_ffi_free_label_list(label_list: *mut LabelList) -> error::WalletFfiError; + + fn wallet_ffi_poll_transaction_status( + handle: *mut WalletHandle, + tx_hash: FfiBytes32, + transaction_status: *mut bool, + ) -> error::WalletFfiError; } fn new_wallet_ffi_with_test_context_config( @@ -987,8 +993,18 @@ fn test_wallet_ffi_transfer_public() -> Result<()> { assert_eq!(from_balance, 9900); assert_eq!(to_balance, 20100); + // Also check for transaction inclusion + let hash_bytes = unsafe { transfer_result.tx_hash_bytes() }; + let mut is_included = false; + + unsafe { + wallet_ffi_poll_transaction_status(wallet_ffi_handle, hash_bytes, &raw mut is_included) + .unwrap(); + } + + assert!(is_included); + unsafe { - wallet_ffi_free_transfer_result(&raw mut transfer_result); wallet_ffi_destroy(wallet_ffi_handle); } diff --git a/lez/wallet-ffi/Cargo.toml b/lez/wallet-ffi/Cargo.toml index 5440bee2..25d5f2a5 100644 --- a/lez/wallet-ffi/Cargo.toml +++ b/lez/wallet-ffi/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["rlib", "cdylib", "staticlib"] wallet.workspace = true lee.workspace = true lee_core.workspace = true +common.workspace = true programs.workspace = true tokio.workspace = true diff --git a/lez/wallet-ffi/src/generic_transaction.rs b/lez/wallet-ffi/src/generic_transaction.rs index 7be6ddaf..6420e5e8 100644 --- a/lez/wallet-ffi/src/generic_transaction.rs +++ b/lez/wallet-ffi/src/generic_transaction.rs @@ -3,6 +3,7 @@ use std::{ ffi::{c_char, CString}, }; +use common::HashType; use lee::{privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program}; use crate::{ @@ -390,6 +391,43 @@ pub unsafe extern "C" fn wallet_ffi_send_generic_private_transaction( } } +/// Poll transaction for its status. +/// +/// # Parameters +/// - `handle`: Valid pointer to wallet handle. +/// - `tx_hash`: Bytes of a transaction hash, +/// - `transaction_status`: Valid pointer into `bool`. +/// +/// # Returns +/// - `true` if seen included, `false` othervise. +/// +/// # Safety +/// - `handle` must be a valid pointer. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_poll_transaction_status( + handle: *mut WalletHandle, + tx_hash: FfiBytes32, + // ToDo: Replace with status enum. + transaction_status: *mut bool, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + *transaction_status = block_on(wallet.poll_transaction(HashType(tx_hash.data))).is_ok(); + + WalletFfiError::Success +} + /// Free a transaction result returned by `wallet_ffi_send_generic_public_transaction` or /// `wallet_ffi_send_generic_private_transaction`. /// diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 082f6b03..de2d9496 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -7,6 +7,7 @@ use std::{ str::FromStr as _, }; +use common::HashType; use lee::{Data, ProgramId, SharedSecretKey}; use lee_core::{ encryption::MlKem768EncapsulationKey, program::PdaSeed, AuthorizationSecretKey, @@ -159,6 +160,7 @@ impl Default for FfiAccountList { /// Result of a transfer operation. #[repr(C)] +#[derive(Debug)] pub struct FfiTransferResult { // TODO: Replace with HashType FFI representation /// Transaction hash (null-terminated string, or null on failure). @@ -176,6 +178,22 @@ impl Default for FfiTransferResult { } } +impl FfiTransferResult { + #[must_use] + /// Casting valid results hash into bytes. Effectively frees `FfiTransferResult`. + /// + /// # Safety + /// Field `tx_hash` must be a valid pointer into transaction hash. + pub unsafe fn tx_hash_bytes(self) -> FfiBytes32 { + let cstring = unsafe { CString::from_raw(self.tx_hash) }; + let rstring = cstring.into_string().expect("Must be a valid Rust string"); + + let hash_val = HashType::from_str(&rstring).expect("Must be a valid hex string"); + + FfiBytes32 { data: hash_val.0 } + } +} + // Helper functions to convert between Rust and FFI types impl FfiBytes32 { diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index e12f4655..08bdecf3 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -669,6 +669,24 @@ enum WalletFfiError wallet_ffi_send_generic_private_transaction(struct WalletHan const struct FfiProgramWithDependencies *program_with_dependencies, struct FfiTransactionResult *out_result); +/** + * Poll transaction for its status. + * + * # Parameters + * - `handle`: Valid pointer to wallet handle. + * - `tx_hash`: Bytes of a transaction hash, + * - `transaction_status`: Valid pointer into `bool`. + * + * # Returns + * - `true` if seen included, `false` othervise. + * + * # Safety + * - `handle` must be a valid pointer. + */ +enum WalletFfiError wallet_ffi_poll_transaction_status(struct WalletHandle *handle, + struct FfiBytes32 tx_hash, + bool *transaction_status); + /** * Free a transaction result returned by `wallet_ffi_send_generic_public_transaction` or * `wallet_ffi_send_generic_private_transaction`.