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
693 changes: 354 additions & 339 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ members = [
]

[workspace.dependencies]
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "991c6ebe24d7ea8ba7d900a052b25be8c5498409" }
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "afcff1566c81abb0a9cf64b99b0ee675169fddfc" }

tokio-metrics = "0.5"

Expand Down
26 changes: 26 additions & 0 deletions packages/rs-platform-wallet-ffi/src/core_wallet/addresses.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! FFI bindings for CoreWallet address derivation.

use super::transaction_builder::CoreAccountTypeFFI;
use crate::error::*;
use crate::handle::*;
use crate::runtime::runtime;
use crate::{check_ptr, unwrap_option_or_return, unwrap_result_or_return};
use key_wallet::wallet::managed_wallet_info::transaction_building::AccountTypePreference;
use std::ffi::CString;
use std::os::raw::c_char;

Expand Down Expand Up @@ -51,6 +53,30 @@ pub unsafe extern "C" fn core_wallet_next_change_address(
PlatformWalletFFIResult::ok()
}

/// Widen the gap limit for an account, generating the addresses the wider
/// limit now requires.
///
/// # Safety
/// `handle` must be a valid core-wallet handle.
#[no_mangle]
pub unsafe extern "C" fn core_wallet_set_gap_limit(
handle: Handle,
account_type: CoreAccountTypeFFI,
account_index: u32,
gap_limit: u32,
) -> PlatformWalletFFIResult {
let source: AccountTypePreference = account_type.into();

let option = CORE_WALLET_STORAGE.with_item(handle, |wallet| {
runtime().block_on(wallet.set_gap_limit(source, account_index, gap_limit))
});

let result = unwrap_option_or_return!(option);
unwrap_result_or_return!(result);

PlatformWalletFFIResult::ok()
}

/// Free an address string returned by `core_wallet_next_receive_address`
/// or `core_wallet_next_change_address`.
#[no_mangle]
Expand Down
122 changes: 12 additions & 110 deletions packages/rs-platform-wallet-ffi/src/core_wallet/broadcast.rs
Original file line number Diff line number Diff line change
@@ -1,136 +1,38 @@
//! FFI bindings for CoreWallet transaction building and broadcasting.
//! FFI bindings for CoreWallet transaction broadcasting.

use super::transaction_builder::FFICoreTransaction;
use crate::error::*;
use crate::handle::*;
use crate::runtime::runtime;
use crate::{check_ptr, unwrap_option_or_return, unwrap_result_or_return};
use rs_sdk_ffi::MnemonicResolverCoreSigner;
use rs_sdk_ffi::MnemonicResolverHandle;
use std::os::raw::c_char;
use std::str::FromStr;

/// Broadcast a signed transaction to the network.
/// Broadcast a transaction built by `core_wallet_tx_builder_build_signed`.
///
/// # Safety
/// `handle` must be a valid core-wallet handle; `tx` a valid;
/// `out_txid` must be writable.
#[no_mangle]
pub unsafe extern "C" fn core_wallet_broadcast_transaction(
handle: Handle,
tx_bytes: *const u8,
tx_bytes_len: usize,
tx: *const FFICoreTransaction,
out_txid: *mut *mut c_char,
) -> PlatformWalletFFIResult {
check_ptr!(tx_bytes);
check_ptr!(tx);
check_ptr!(out_txid);

let bytes = std::slice::from_raw_parts(tx_bytes, tx_bytes_len);
let tx: dashcore::Transaction =
unwrap_result_or_return!(dashcore::consensus::deserialize(bytes));
unwrap_result_or_return!(dashcore::consensus::deserialize((*tx).bytes()));

let option = CORE_WALLET_STORAGE.with_item(handle, |wallet| {
runtime().block_on(wallet.broadcast_transaction(&tx))
});

let result = unwrap_option_or_return!(option);

let txid = unwrap_result_or_return!(result);
let c_str = unwrap_result_or_return!(std::ffi::CString::new(txid.to_string()));
*out_txid = c_str.into_raw();
PlatformWalletFFIResult::ok()
}

/// Build, sign, and broadcast a payment to the given addresses via
/// an external mnemonic-resolver-backed signer.
///
/// The Swift caller supplies a [`MnemonicResolverHandle`] — the same
/// vtable shape used by
/// [`crate::dash_sdk_sign_with_mnemonic_resolver_and_path`] — which
/// the FFI wraps in a [`MnemonicResolverCoreSigner`] for the
/// lifetime of this call. Private keys never cross the FFI as raw
/// bytes; every signature is produced inside the signer's atomic
/// derive-and-sign step.
///
/// # Safety
/// - `core_signer_handle` must be a valid, non-destroyed
/// `*mut MnemonicResolverHandle`. Ownership is retained by the
/// caller — this function does NOT destroy it.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn core_wallet_send_to_addresses(
handle: Handle,
account_type: u32,
account_index: u32,
addresses: *const *const c_char,
amounts: *const u64,
count: usize,
core_signer_handle: *mut MnemonicResolverHandle,
out_tx_bytes: *mut *mut u8,
out_tx_len: *mut usize,
) -> PlatformWalletFFIResult {
check_ptr!(core_signer_handle);
if count > 0 {
check_ptr!(addresses);
check_ptr!(amounts);
}
check_ptr!(out_tx_bytes);
check_ptr!(out_tx_len);

let mut outputs = Vec::with_capacity(count);
let addr_ptrs = std::slice::from_raw_parts(addresses, count);
let amount_slice = std::slice::from_raw_parts(amounts, count);

for i in 0..count {
let c_str = unwrap_result_or_return!(std::ffi::CStr::from_ptr(addr_ptrs[i]).to_str());

let addr = unwrap_result_or_return!(dashcore::Address::from_str(c_str)).assume_checked();

outputs.push((addr, amount_slice[i]));
}

use key_wallet::account::account_type::StandardAccountType;
let std_account_type = match account_type {
0 => StandardAccountType::BIP44Account,
1 => StandardAccountType::BIP32Account,
_ => {
return PlatformWalletFFIResult::err(
PlatformWalletFFIResultCode::ErrorInvalidParameter,
format!("Unknown account type: {account_type}"),
);
}
};

let signer_addr = core_signer_handle as usize;

let option = CORE_WALLET_STORAGE.with_item(handle, |wallet| {
let wallet_id = wallet.wallet_id();
let network = wallet.network();
// SAFETY: the resolver handle is pinned alive for the duration
// of this FFI call (see fn-level safety doc). The
// `MnemonicResolverCoreSigner` lives on this stack frame and
// is dropped before the function returns.
let signer = unsafe {
MnemonicResolverCoreSigner::new(
signer_addr as *mut MnemonicResolverHandle,
wallet_id,
network,
)
};
runtime().block_on(wallet.send_to_addresses(
std_account_type,
account_index,
outputs,
&signer,
))
});
let result = unwrap_option_or_return!(option);
let tx = unwrap_result_or_return!(result);
let serialized = dashcore::consensus::serialize(&tx);
let len = serialized.len();
let boxed = serialized.into_boxed_slice();
*out_tx_bytes = Box::into_raw(boxed) as *mut u8;
*out_tx_len = len;
PlatformWalletFFIResult::ok()
}

/// Free transaction bytes returned by `core_wallet_send_to_addresses`.
#[no_mangle]
pub unsafe extern "C" fn core_wallet_free_tx_bytes(bytes: *mut u8, len: usize) {
if !bytes.is_null() && len > 0 {
let _ = Box::from_raw(std::ptr::slice_from_raw_parts_mut(bytes, len));
}
}
2 changes: 2 additions & 0 deletions packages/rs-platform-wallet-ffi/src/core_wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

mod addresses;
mod broadcast;
mod transaction_builder;
mod wallet;

pub use addresses::*;
pub use broadcast::*;
pub use transaction_builder::*;
pub use wallet::*;
Loading
Loading