add wallet ffi auth-transfer deshielded method

This commit is contained in:
Sergio Chouhy 2026-02-11 18:06:20 -03:00
parent c3ca6c7563
commit 2c89e17896
2 changed files with 160 additions and 1 deletions

View File

@ -109,6 +109,14 @@ unsafe extern "C" {
out_result: *mut FfiTransferResult,
) -> error::WalletFfiError;
fn wallet_ffi_transfer_deshielded(
handle: *mut WalletHandle,
from: *const FfiBytes32,
to: *const FfiBytes32,
amount: *const [u8; 16],
out_result: *mut FfiTransferResult,
) -> error::WalletFfiError;
fn wallet_ffi_free_transfer_result(result: *mut FfiTransferResult);
fn wallet_ffi_register_public_account(
@ -901,3 +909,66 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
Ok(())
}
#[test]
fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
let ctx = BlockingTestContext::new().unwrap();
let home = tempfile::tempdir().unwrap();
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
let from: FfiBytes32 = (&ACC_SENDER_PRIVATE.parse::<AccountId>().unwrap()).into();
let to = FfiBytes32::from_bytes([37; 32]);
let amount: [u8; 16] = 100u128.to_le_bytes();
let mut transfer_result = FfiTransferResult::default();
unsafe {
wallet_ffi_transfer_deshielded(
wallet_ffi_handle,
(&from) as *const FfiBytes32,
(&to) as *const FfiBytes32,
(&amount) as *const [u8; 16],
(&mut transfer_result) as *mut FfiTransferResult,
);
}
info!("Waiting for next block creation");
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
// Sync private account local storage with onchain encrypted state
unsafe {
let mut current_height = 0;
wallet_ffi_get_current_block_height(wallet_ffi_handle, (&mut current_height) as *mut u64);
wallet_ffi_sync_to_block(wallet_ffi_handle, current_height);
};
let from_balance = unsafe {
let mut out_balance: [u8; 16] = [0; 16];
let _result = wallet_ffi_get_balance(
wallet_ffi_handle,
(&from) as *const FfiBytes32,
false,
(&mut out_balance) as *mut [u8; 16],
);
u128::from_le_bytes(out_balance)
};
let to_balance = unsafe {
let mut out_balance: [u8; 16] = [0; 16];
let _result = wallet_ffi_get_balance(
wallet_ffi_handle,
(&to) as *const FfiBytes32,
true,
(&mut out_balance) as *mut [u8; 16],
);
u128::from_le_bytes(out_balance)
};
assert_eq!(from_balance, 9900);
assert_eq!(to_balance, 100);
unsafe {
wallet_ffi_free_transfer_result((&mut transfer_result) as *mut FfiTransferResult);
wallet_ffi_destroy(wallet_ffi_handle);
}
Ok(())
}

View File

@ -102,7 +102,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_public(
}
}
/// Send a token transfer shielded transfer.
/// Send a shielded token transfer.
///
/// Transfers tokens from one private account to another on the network.
///
@ -199,6 +199,94 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
}
}
/// Send a deshielded token transfer.
///
/// Transfers tokens from one private account to another on the network.
///
/// # Parameters
/// - `handle`: Valid wallet handle
/// - `from`: Source account ID (must be owned by this wallet)
/// - `to`: Destination account ID
/// - `amount`: Amount to transfer as little-endian [u8; 16]
/// - `out_result`: Output pointer for transfer result
///
/// # Returns
/// - `Success` if the transfer was submitted successfully
/// - `InsufficientFunds` if the source account doesn't have enough balance
/// - `KeyNotFound` if the source account's signing key is not in this wallet
/// - Error code on other failures
///
/// # Memory
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
///
/// # Safety
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
/// - `from` must be a valid pointer to a `FfiBytes32` struct
/// - `to` must be a valid pointer to a `FfiBytes32` struct
/// - `amount` must be a valid pointer to a `[u8; 16]` array
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
#[no_mangle]
pub unsafe extern "C" fn wallet_ffi_transfer_deshielded(
handle: *mut WalletHandle,
from: *const FfiBytes32,
to: *const FfiBytes32,
amount: *const [u8; 16],
out_result: *mut FfiTransferResult,
) -> WalletFfiError {
let wrapper = match get_wallet(handle) {
Ok(w) => w,
Err(e) => return e,
};
if from.is_null() || to.is_null() || amount.is_null() || out_result.is_null() {
print_error("Null pointer argument");
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {}", e));
return WalletFfiError::InternalError;
}
};
let from_id = AccountId::new(unsafe { (*from).data });
let to_id = AccountId::new(unsafe { (*to).data });
let amount = u128::from_le_bytes(unsafe { *amount });
let transfer = NativeTokenTransfer(&wallet);
match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount)) {
Ok(Ok((response, _shared_key))) => {
let tx_hash = CString::new(response.tx_hash)
.map(|s| s.into_raw())
.unwrap_or(ptr::null_mut());
unsafe {
(*out_result).tx_hash = tx_hash;
(*out_result).success = true;
}
WalletFfiError::Success
}
Ok(Err(e)) => {
print_error(format!("Transfer failed: {:?}", e));
unsafe {
(*out_result).tx_hash = ptr::null_mut();
(*out_result).success = false;
}
match e {
ExecutionFailureKind::InsufficientFundsError => WalletFfiError::InsufficientFunds,
ExecutionFailureKind::KeyNotFoundError => WalletFfiError::KeyNotFound,
ExecutionFailureKind::SequencerError => WalletFfiError::NetworkError,
ExecutionFailureKind::SequencerClientError(_) => WalletFfiError::NetworkError,
_ => WalletFfiError::InternalError,
}
}
Err(e) => e,
}
}
/// Register a public account on the network.
///
/// This initializes a public account on the blockchain. The account must be