Skip to content
Open
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
67 changes: 67 additions & 0 deletions packages/rs-platform-wallet-ffi/src/dashpay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,73 @@ pub unsafe extern "C" fn platform_wallet_fetch_sent_contact_requests(
// Send payment
// ---------------------------------------------------------------------------

/// Reserve a fresh contact Core address for a Platform or shielded withdrawal.
/// The address is durably consumed before success; callers must not reuse it.
/// This does not submit a payment or record payment history.
///
/// # Safety
/// - Identity pointers must each reference 32 readable bytes.
/// - `core_signer_handle` must remain valid throughout this synchronous call.
/// - `out_address` must point to writable pointer storage. On success, free
/// the returned string with `platform_wallet_string_free`.
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_reserve_dashpay_payment_address(
wallet_handle: Handle,
from_identity_id: *const u8,
to_contact_identity_id: *const u8,
core_signer_handle: *mut MnemonicResolverHandle,
out_address: *mut *mut c_char,
) -> PlatformWalletFFIResult {
check_ptr!(core_signer_handle);
check_ptr!(out_address);
*out_address = std::ptr::null_mut();
Comment on lines +609 to +611

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Null the string out-param before the signer null-check

platform_wallet_reserve_dashpay_payment_address nulls *out_address only after check_ptr!(core_signer_handle), so a null-signer call returns ErrorNullPointer leaving *out_address untouched. The documented contract (free only on success) and the Swift caller (nil-initialized) are safe, but a C cleanup-on-error caller that did not pre-initialize would free garbage. The file's own convention in platform_wallet_fetch_sent_contact_requests publishes the empty sentinel before any fallible work. Match it here.

Suggested change
check_ptr!(core_signer_handle);
check_ptr!(out_address);
*out_address = std::ptr::null_mut();
check_ptr!(out_address);
*out_address = std::ptr::null_mut();
check_ptr!(core_signer_handle);

source: muse-spark-1.3-contributor (phase2-reviewer: ffi-engineer)

let from_id = unwrap_result_or_return!(read_identifier(from_identity_id));
let to_id = unwrap_result_or_return!(read_identifier(to_contact_identity_id));
let signer_addr = core_signer_handle as usize;
// Look the identity up under the registry guard, but wait outside it:
// the reservation waits on the manager write lock, the contact-payment
// gate, and the host store + flush, and a registry read guard held
// across those would stall `platform_wallet_destroy` and, through
// parking_lot's writer preference, every other registry reader.
let option = PLATFORM_WALLET_STORAGE.with_item(wallet_handle, |wallet| {
Comment thread
PastaPastaPasta marked this conversation as resolved.
(
wallet.identity().clone(),
wallet.wallet_id(),
wallet.network(),
)
});
let (identity, wallet_id, network) = unwrap_option_or_return!(option);
// SAFETY: `signer_addr` came from `core_signer_handle`, which the caller
// pins alive for the duration of this synchronous call; the provider is
// dropped when the worker task completes, before this call returns.
let provider = unsafe {
resolver_contact_crypto_provider(
signer_addr as *mut MnemonicResolverHandle,
wallet_id,
network,
)
};
let result = block_on_worker(async move {
identity
.dashpay()
.reserve_payment_address(&from_id, &to_id, &provider)
.await
});
let address = match result {
Ok(address) => address,
Err(e @ platform_wallet::PlatformWalletError::SeedMismatch { .. }) => {
return PlatformWalletFFIResult::err(
PlatformWalletFFIResultCode::ErrorInvalidParameter,
e.to_string(),
);
}
Err(e) => return e.into(),
};
let c_str = unwrap_result_or_return!(std::ffi::CString::new(address.to_string()));
*out_address = c_str.into_raw();
PlatformWalletFFIResult::ok()
}

/// Send a Dash payment from `from_identity_id` to `to_contact_identity_id`.
///
/// The funding inputs are signed through the supplied
Expand Down
Loading
Loading