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
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions packages/rs-platform-wallet-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ bincode = { version = "=2.0.1" }
# Hex used for error diagnostics that include a wallet_id.
hex = "0.4"

# anyhow surfaces from `KeyType::try_from` / `Purpose::try_from`
# / `SecurityLevel::try_from` in dpp; we need the From impl in
# `error.rs` so `unwrap_result_or_return!` can absorb it.
anyhow = { version = "1.0.81" }

# Used by group-action queries to build the JSON response shape that
# Swift decodes via Codable. See `tokens/group_queries.rs`.
serde_json = "1.0"
Expand Down
171 changes: 59 additions & 112 deletions packages/rs-platform-wallet-ffi/src/asset_lock/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::error::*;
use crate::handle::*;
use crate::runtime::runtime;
use crate::{check_ptr, unwrap_option_or_return, unwrap_result_or_return};

/// Build an asset lock transaction.
///
Expand All @@ -21,54 +22,34 @@ pub unsafe extern "C" fn asset_lock_manager_build_transaction(
out_tx_bytes: *mut *mut u8,
out_tx_len: *mut usize,
out_private_key: *mut [u8; 32],
out_error: *mut PlatformWalletFFIError,
) -> PlatformWalletFFIResult {
if out_tx_bytes.is_null() || out_tx_len.is_null() || out_private_key.is_null() {
return PlatformWalletFFIResult::ErrorNullPointer;
}
check_ptr!(out_tx_bytes);
check_ptr!(out_tx_len);
check_ptr!(out_private_key);

let option = parse_funding_type(funding_type);
let funding = unwrap_option_or_return!(option);

let option = ASSET_LOCK_MANAGER_STORAGE.with_item(handle, |manager| {
runtime().block_on(manager.build_asset_lock_transaction(
amount_duffs,
account_index,
funding,
identity_index,
))
});
let result = unwrap_option_or_return!(option);
let (tx, key) = 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;
*out_private_key = key.inner.secret_bytes();

let funding = match parse_funding_type(funding_type) {
Some(f) => f,
None => {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorInvalidParameter,
format!("Unknown funding type: {}", funding_type),
);
}
return PlatformWalletFFIResult::ErrorInvalidParameter;
}
};

ASSET_LOCK_MANAGER_STORAGE
.with_item(handle, |manager| {
match runtime().block_on(manager.build_asset_lock_transaction(
amount_duffs,
account_index,
funding,
identity_index,
)) {
Ok((tx, key)) => {
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;
*out_private_key = key.inner.secret_bytes();
PlatformWalletFFIResult::Success
}
Err(e) => {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorWalletOperation,
e.to_string(),
);
}
PlatformWalletFFIResult::ErrorWalletOperation
}
}
})
.unwrap_or(PlatformWalletFFIResult::ErrorInvalidHandle)
PlatformWalletFFIResult::ok()
}

/// Build, broadcast, and wait for an asset lock proof.
Expand All @@ -91,74 +72,40 @@ pub unsafe extern "C" fn asset_lock_manager_create_funded_proof(
out_proof_len: *mut usize,
out_private_key: *mut [u8; 32],
out_txid: *mut [u8; 32],
out_error: *mut PlatformWalletFFIError,
) -> PlatformWalletFFIResult {
if out_proof_bytes.is_null()
|| out_proof_len.is_null()
|| out_private_key.is_null()
|| out_txid.is_null()
{
return PlatformWalletFFIResult::ErrorNullPointer;
}
check_ptr!(out_proof_bytes);
check_ptr!(out_proof_len);
check_ptr!(out_private_key);
check_ptr!(out_txid);

let funding = unwrap_option_or_return!(parse_funding_type(funding_type));

let option = ASSET_LOCK_MANAGER_STORAGE.with_item(handle, |manager| {
runtime().block_on(manager.create_funded_asset_lock_proof(
amount_duffs,
account_index,
funding,
identity_index,
))
});
let result = unwrap_option_or_return!(option);
let (proof, key, out_point) = unwrap_result_or_return!(result);

let bytes = unwrap_result_or_return!(dpp::bincode::encode_to_vec(
&proof,
dpp::bincode::config::standard()
));

let len = bytes.len();
let boxed = bytes.into_boxed_slice();
*out_proof_bytes = Box::into_raw(boxed) as *mut u8;
*out_proof_len = len;
*out_private_key = key.inner.secret_bytes();
let mut txid_bytes = [0u8; 32];
txid_bytes.copy_from_slice(&out_point.txid[..]);
*out_txid = txid_bytes;

let funding = match parse_funding_type(funding_type) {
Some(f) => f,
None => {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorInvalidParameter,
format!("Unknown funding type: {}", funding_type),
);
}
return PlatformWalletFFIResult::ErrorInvalidParameter;
}
};

ASSET_LOCK_MANAGER_STORAGE
.with_item(handle, |manager| {
match runtime().block_on(manager.create_funded_asset_lock_proof(
amount_duffs,
account_index,
funding,
identity_index,
)) {
Ok((proof, key, out_point)) => {
// Serialize proof with bincode
match dpp::bincode::encode_to_vec(&proof, dpp::bincode::config::standard()) {
Ok(bytes) => {
let len = bytes.len();
let boxed = bytes.into_boxed_slice();
*out_proof_bytes = Box::into_raw(boxed) as *mut u8;
*out_proof_len = len;
*out_private_key = key.inner.secret_bytes();
let mut txid_bytes = [0u8; 32];
txid_bytes.copy_from_slice(&out_point.txid[..]);
*out_txid = txid_bytes;
PlatformWalletFFIResult::Success
}
Err(e) => {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorSerialization,
format!("Failed to serialize proof: {}", e),
);
}
PlatformWalletFFIResult::ErrorSerialization
}
}
}
Err(e) => {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorWalletOperation,
e.to_string(),
);
}
PlatformWalletFFIResult::ErrorWalletOperation
}
}
})
.unwrap_or(PlatformWalletFFIResult::ErrorInvalidHandle)
PlatformWalletFFIResult::ok()
}

/// Free transaction bytes.
Expand Down
107 changes: 47 additions & 60 deletions packages/rs-platform-wallet-ffi/src/asset_lock/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::error::*;
use crate::handle::*;
use crate::runtime::runtime;
use crate::{check_ptr, unwrap_option_or_return};

/// C-compatible tracked asset lock entry.
#[repr(C)]
Expand All @@ -28,12 +29,9 @@ pub struct TrackedAssetLockFFI {

/// Destroy an AssetLockManager handle.
#[no_mangle]
pub unsafe extern "C" fn asset_lock_manager_destroy(
handle: Handle,
_out_error: *mut PlatformWalletFFIError,
) -> PlatformWalletFFIResult {
pub unsafe extern "C" fn asset_lock_manager_destroy(handle: Handle) -> PlatformWalletFFIResult {
ASSET_LOCK_MANAGER_STORAGE.remove(handle);
PlatformWalletFFIResult::Success
PlatformWalletFFIResult::ok()
}

/// List all tracked asset locks.
Expand All @@ -45,65 +43,54 @@ pub unsafe extern "C" fn asset_lock_manager_list_tracked_locks(
handle: Handle,
out_locks: *mut *mut TrackedAssetLockFFI,
out_count: *mut usize,
out_error: *mut PlatformWalletFFIError,
) -> PlatformWalletFFIResult {
if out_locks.is_null() || out_count.is_null() {
return PlatformWalletFFIResult::ErrorNullPointer;
}
check_ptr!(out_locks);
check_ptr!(out_count);

ASSET_LOCK_MANAGER_STORAGE
.with_item(handle, |manager| {
use platform_wallet::AssetLockStatus;
let option = ASSET_LOCK_MANAGER_STORAGE.with_item(handle, |manager| {
use platform_wallet::AssetLockStatus;

let locks = runtime().block_on(manager.list_tracked_locks());
let entries: Vec<TrackedAssetLockFFI> = locks
.iter()
.map(|lock| {
let mut txid = [0u8; 32];
txid.copy_from_slice(&lock.out_point.txid[..]);
TrackedAssetLockFFI {
txid,
vout: lock.out_point.vout,
account_index: lock.account_index,
funding_type: match lock.funding_type {
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityRegistration => 0,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityTopUp => 1,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityTopUpNotBound => 2,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityInvitation => 3,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::AssetLockAddressTopUp => 4,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::AssetLockShieldedAddressTopUp => 5,
},
identity_index: lock.identity_index,
amount: lock.amount,
status: match lock.status {
AssetLockStatus::Built => 0,
AssetLockStatus::Broadcast => 1,
AssetLockStatus::InstantSendLocked => 2,
AssetLockStatus::ChainLocked => 3,
},
has_proof: lock.proof.is_some(),
}
})
.collect();
let locks = runtime().block_on(manager.list_tracked_locks());
let entries: Vec<TrackedAssetLockFFI> = locks
.iter()
.map(|lock| {
let mut txid = [0u8; 32];
txid.copy_from_slice(&lock.out_point.txid[..]);
TrackedAssetLockFFI {
txid,
vout: lock.out_point.vout,
account_index: lock.account_index,
funding_type: match lock.funding_type {
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityRegistration => 0,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityTopUp => 1,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityTopUpNotBound => 2,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::IdentityInvitation => 3,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::AssetLockAddressTopUp => 4,
key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType::AssetLockShieldedAddressTopUp => 5,
},
identity_index: lock.identity_index,
amount: lock.amount,
status: match lock.status {
AssetLockStatus::Built => 0,
AssetLockStatus::Broadcast => 1,
AssetLockStatus::InstantSendLocked => 2,
AssetLockStatus::ChainLocked => 3,
},
has_proof: lock.proof.is_some(),
}
})
.collect();
entries
});
let entries = unwrap_option_or_return!(option);

*out_count = entries.len();
if entries.is_empty() {
*out_locks = std::ptr::null_mut();
} else {
*out_locks =
Box::into_raw(entries.into_boxed_slice()) as *mut TrackedAssetLockFFI;
}
PlatformWalletFFIResult::Success
})
.unwrap_or_else(|| {
if !out_error.is_null() {
*out_error = PlatformWalletFFIError::new(
PlatformWalletFFIResult::ErrorInvalidHandle,
"Invalid asset-lock manager handle",
);
}
PlatformWalletFFIResult::ErrorInvalidHandle
})
*out_count = entries.len();
if entries.is_empty() {
*out_locks = std::ptr::null_mut();
} else {
*out_locks = Box::into_raw(entries.into_boxed_slice()) as *mut TrackedAssetLockFFI;
}
PlatformWalletFFIResult::ok()
}

/// Free tracked locks array.
Expand Down
Loading
Loading