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
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

[workspace.dependencies]
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "ea33cbc84179666c25515dfc817ce32210953037" }
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe9ea394306e5f9282505b24d09092ab9a33738" }
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Optimize heavy crypto crates even in dev/test builds so that
# Halo 2 proof generation and verification run at near-release speed.
Expand Down
55 changes: 41 additions & 14 deletions packages/rs-platform-wallet-ffi/src/core_wallet_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,18 +361,39 @@ fn account_index_of(at: &key_wallet::account::AccountType) -> u32 {
}
}

/// Per-account balance entry returned by the query FFI. Carries the
/// same `AccountTypeTagFFI` discriminants as `AccountSpecFFI` plus
/// four balance fields from `WalletCoreBalance`.
#[repr(C)]
pub struct AccountBalanceEntryFFI {
pub type_tag: crate::wallet_restore_types::AccountTypeTagFFI,
pub standard_tag: crate::wallet_restore_types::StandardAccountTypeTagFFI,
pub index: u32,
pub registration_index: u32,
pub key_class: u32,
pub user_identity_id: [u8; 32],
pub friend_identity_id: [u8; 32],
pub confirmed: u64,
pub unconfirmed: u64,
pub immature: u64,
pub locked: u64,
}

/// Subset of [`crate::wallet_restore_types::AccountSpecFFI`] carrying
/// only the tag/discriminator fields — no xpub. Used by the
/// changeset emit path to populate
/// [`AccountChangeSetFFI`]'s typed tags so the Swift persister can
/// upsert on the same composite key the load path uses.
struct AccountChangeSetTags {
type_tag: crate::wallet_restore_types::AccountTypeTagFFI,
standard_tag: crate::wallet_restore_types::StandardAccountTypeTagFFI,
registration_index: u32,
key_class: u32,
user_identity_id: [u8; 32],
friend_identity_id: [u8; 32],
/// upsert on the same composite key the load path uses. Also used by
/// [`AccountBalanceEntryFFI`] to carry per-account routing context
/// for balance queries.
pub struct AccountChangeSetTags {
pub type_tag: crate::wallet_restore_types::AccountTypeTagFFI,
pub standard_tag: crate::wallet_restore_types::StandardAccountTypeTagFFI,
pub index: u32,
pub registration_index: u32,
pub key_class: u32,
pub user_identity_id: [u8; 32],
pub friend_identity_id: [u8; 32],
}

/// Project an upstream [`AccountType`] into the flat FFI tag layout.
Expand All @@ -381,30 +402,33 @@ struct AccountChangeSetTags {
/// match arms but emits only the tag/discriminator fields — the
/// xpub is load-path-only and not relevant on the changeset emit
/// path.
fn account_type_to_tags(at: &key_wallet::account::AccountType) -> AccountChangeSetTags {
pub fn account_type_to_tags(at: &key_wallet::account::AccountType) -> AccountChangeSetTags {
use crate::wallet_restore_types::{AccountTypeTagFFI, StandardAccountTypeTagFFI};
use key_wallet::account::{AccountType, StandardAccountType};
let mut tags = AccountChangeSetTags {
type_tag: AccountTypeTagFFI::Standard,
standard_tag: StandardAccountTypeTagFFI::Bip44,
index: 0,
registration_index: 0,
key_class: 0,
user_identity_id: [0u8; 32],
friend_identity_id: [0u8; 32],
};
match at {
AccountType::Standard {
index,
standard_account_type,
..
} => {
tags.index = *index;
tags.type_tag = AccountTypeTagFFI::Standard;
tags.standard_tag = match standard_account_type {
StandardAccountType::BIP44Account => StandardAccountTypeTagFFI::Bip44,
StandardAccountType::BIP32Account => StandardAccountTypeTagFFI::Bip32,
};
}
AccountType::CoinJoin { .. } => {
AccountType::CoinJoin { index } => {
tags.type_tag = AccountTypeTagFFI::CoinJoin;
tags.index = *index;
}
AccountType::IdentityRegistration => {
tags.type_tag = AccountTypeTagFFI::IdentityRegistration;
Expand Down Expand Up @@ -438,25 +462,28 @@ fn account_type_to_tags(at: &key_wallet::account::AccountType) -> AccountChangeS
tags.type_tag = AccountTypeTagFFI::ProviderPlatformKeys;
}
AccountType::DashpayReceivingFunds {
index,
user_identity_id,
friend_identity_id,
..
} => {
tags.type_tag = AccountTypeTagFFI::DashpayReceivingFunds;
tags.index = *index;
tags.user_identity_id = *user_identity_id;
tags.friend_identity_id = *friend_identity_id;
}
AccountType::DashpayExternalAccount {
index,
user_identity_id,
friend_identity_id,
..
} => {
tags.type_tag = AccountTypeTagFFI::DashpayExternalAccount;
tags.index = *index;
tags.user_identity_id = *user_identity_id;
tags.friend_identity_id = *friend_identity_id;
}
AccountType::PlatformPayment { key_class, .. } => {
AccountType::PlatformPayment { account, key_class } => {
tags.type_tag = AccountTypeTagFFI::PlatformPayment;
tags.index = *account;
tags.key_class = *key_class;
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-platform-wallet-ffi/src/persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ fn build_wallet_start_state(
// unlock path builds a signing wallet from the mnemonic.
let wallet = Wallet::new_watch_only(network, entry.wallet_id, accounts);

let wallet_info = ManagedWalletInfo::from_wallet(&wallet);
let wallet_info = ManagedWalletInfo::from_wallet(&wallet, 0);

let mut per_account = PerWalletPlatformAddressState::new();
for (&account_key, account) in &wallet.accounts.platform_payment_accounts {
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-platform-wallet-ffi/src/platform_wallet_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub unsafe extern "C" fn platform_wallet_info_create_from_seed(
};

// Create PlatformWalletInfo from the wallet
let platform_wallet = PlatformWalletInfo::from_wallet(&wallet);
let platform_wallet = PlatformWalletInfo::from_wallet(&wallet, 0);

// Store in handle storage
let handle = WALLET_INFO_STORAGE.insert(platform_wallet);
Expand Down Expand Up @@ -235,7 +235,7 @@ pub unsafe extern "C" fn platform_wallet_info_create_from_mnemonic(
};

// Create PlatformWalletInfo from the wallet
let platform_wallet = PlatformWalletInfo::from_wallet(&wallet);
let platform_wallet = PlatformWalletInfo::from_wallet(&wallet, 0);

// Store in handle storage
let handle = WALLET_INFO_STORAGE.insert(platform_wallet);
Expand Down
78 changes: 78 additions & 0 deletions packages/rs-platform-wallet-ffi/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,84 @@ pub unsafe extern "C" fn platform_wallet_load_and_apply_persisted(
.unwrap_or(PlatformWalletFFIResult::ErrorInvalidHandle)
}

/// Query per-account balances from the in-memory `WalletManager`.
///
/// Returns an array of [`AccountBalanceEntryFFI`] — one per account
/// in the wallet's `ManagedAccountCollection`. The caller owns the
/// returned array and must free it via
/// [`platform_wallet_manager_free_account_balances`].
///
/// `out_entries` receives a pointer to the heap-allocated array;
/// `out_count` receives the element count. Both are set to
/// null / 0 when the wallet is not found.
///
/// Reads the wallet manager lock via `blocking_read` — must not be
/// called from within a tokio async context.
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_manager_get_account_balances(
manager_handle: Handle,
wallet_id: *const u8,
out_entries: *mut *const crate::core_wallet_types::AccountBalanceEntryFFI,
out_count: *mut usize,
_out_error: *mut PlatformWalletFFIError,
) -> PlatformWalletFFIResult {
if wallet_id.is_null() || out_entries.is_null() || out_count.is_null() {
return PlatformWalletFFIResult::ErrorNullPointer;
}

let wid: [u8; 32] = std::ptr::read(wallet_id as *const [u8; 32]);

PLATFORM_WALLET_MANAGER_STORAGE
.with_item(manager_handle, |manager| {
let balances = manager.account_balances_blocking(&wid);
let entries: Vec<crate::core_wallet_types::AccountBalanceEntryFFI> = balances
.into_iter()
.map(|(account_type, balance)| {
let tags = crate::core_wallet_types::account_type_to_tags(&account_type);
crate::core_wallet_types::AccountBalanceEntryFFI {
type_tag: tags.type_tag,
standard_tag: tags.standard_tag,
index: tags.index,
registration_index: tags.registration_index,
key_class: tags.key_class,
user_identity_id: tags.user_identity_id,
friend_identity_id: tags.friend_identity_id,
confirmed: balance.confirmed(),
unconfirmed: balance.unconfirmed(),
immature: balance.immature(),
locked: balance.locked(),
}
})
.collect();
let count = entries.len();
if count == 0 {
*out_entries = std::ptr::null();
*out_count = 0;
return PlatformWalletFFIResult::Success;
}
let boxed = entries.into_boxed_slice();
*out_entries = Box::into_raw(boxed) as *const _;
*out_count = count;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
PlatformWalletFFIResult::Success
})
.unwrap_or_else(|| {
*out_entries = std::ptr::null();
*out_count = 0;
PlatformWalletFFIResult::ErrorInvalidHandle
})
}

/// Free an array returned by [`platform_wallet_manager_get_account_balances`].
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_manager_free_account_balances(
entries: *mut crate::core_wallet_types::AccountBalanceEntryFFI,
count: usize,
) {
if !entries.is_null() && count > 0 {
let _ = Box::from_raw(std::slice::from_raw_parts_mut(entries, count));
}
}

/// Destroy a PlatformWallet handle.
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_destroy(
Expand Down
Loading
Loading