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
376 changes: 276 additions & 100 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ qrcode = "0.14.1"
nix = { version = "0.31.1", features = ["signal"] }
eframe = { version = "0.33.3", features = ["persistence"] }
base64 = "0.22.1"
dash-sdk = { git = "https://github.com/dashpay/platform", rev = "806322247caf1037c6d73d3620804e27a2170a3d", features = [
dash-sdk = { git = "https://github.com/dashpay/platform", rev = "060515987a", features = [
"core_key_wallet",
"core_key_wallet_manager",
"core_bincode",
Expand Down
21 changes: 10 additions & 11 deletions src/backend_task/core/create_asset_lock.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use crate::backend_task::BackendTaskSuccessResult;
use crate::context::AppContext;
use crate::model::wallet::Wallet;
use dash_sdk::dashcore_rpc::RpcApi;
use dash_sdk::dpp::balances::credits::CREDITS_PER_DUFF;
use dash_sdk::dpp::fee::Credits;
use std::sync::{Arc, RwLock};

impl AppContext {
pub fn create_registration_asset_lock(
pub async fn create_registration_asset_lock(
&self,
wallet: Arc<RwLock<Wallet>>,
amount: Credits,
Expand Down Expand Up @@ -39,10 +38,8 @@ impl AppContext {
}

// Broadcast the transaction
self.core_client
.read()
.expect("Core client lock was poisoned")
.send_raw_transaction(&asset_lock_transaction)
self.broadcast_raw_transaction(&asset_lock_transaction)
.await
.map_err(|e| format!("Failed to broadcast asset lock transaction: {}", e))?;

// Update wallet UTXOs
Expand All @@ -59,6 +56,8 @@ impl AppContext {
.drop_utxo(utxo, &self.network.to_string())
.map_err(|e| e.to_string())?;
}

wallet_guard.recalculate_affected_address_balances(&used_utxos, self)?;
}

Ok(BackendTaskSuccessResult::Message(format!(
Expand All @@ -67,7 +66,7 @@ impl AppContext {
)))
}

pub fn create_top_up_asset_lock(
pub async fn create_top_up_asset_lock(
&self,
wallet: Arc<RwLock<Wallet>>,
amount: Credits,
Expand Down Expand Up @@ -101,10 +100,8 @@ impl AppContext {
}

// Broadcast the transaction
self.core_client
.read()
.expect("Core client lock was poisoned")
.send_raw_transaction(&asset_lock_transaction)
self.broadcast_raw_transaction(&asset_lock_transaction)
.await
.map_err(|e| format!("Failed to broadcast asset lock transaction: {}", e))?;

// Update wallet UTXOs
Expand All @@ -121,6 +118,8 @@ impl AppContext {
.drop_utxo(utxo, &self.network.to_string())
.map_err(|e| e.to_string())?;
}

wallet_guard.recalculate_affected_address_balances(&used_utxos, self)?;
}

Ok(BackendTaskSuccessResult::Message(format!(
Expand Down
9 changes: 8 additions & 1 deletion src/backend_task/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,11 @@ impl AppContext {
.map(|_| BackendTaskSuccessResult::None),
CoreTask::CreateRegistrationAssetLock(wallet, amount, identity_index) => self
.create_registration_asset_lock(wallet, amount, true, identity_index)
.await
.map_err(|e| format!("Error creating asset lock: {}", e)),
CoreTask::CreateTopUpAssetLock(wallet, amount, identity_index, top_up_index) => self
.create_top_up_asset_lock(wallet, amount, true, identity_index, top_up_index)
.await
.map_err(|e| format!("Error creating top up asset lock: {}", e)),
CoreTask::SendWalletPayment { wallet, request } => {
self.send_wallet_payment(wallet, request).await
Expand Down Expand Up @@ -483,7 +485,12 @@ impl AppContext {
const FALLBACK_STEP: u64 = 100;

let network = self.wallet_network_key();
let current_height = wm.current_height();
let current_height = self
.spv_manager()
.status()
.sync_progress
.map(|p| p.header_height)
.ok_or("Cannot build transaction: SPV sync height is not yet known")?;
let total_amount: u64 = recipients.iter().map(|(_, amt)| *amt).sum();
let mut scale_factor = 1.0f64;
let mut attempted_fallback = false;
Expand Down
2 changes: 1 addition & 1 deletion src/backend_task/dashpay/incoming_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ pub fn match_transaction_to_contact(
}

/// Process an incoming transaction that was detected by SPV
/// This should be called when SpvEvent::TransactionDetected is received
/// This should be called when WalletEvent::TransactionReceived is received
pub async fn process_incoming_payment(
app_context: &Arc<AppContext>,
tx_id: &str,
Expand Down
146 changes: 40 additions & 106 deletions src/backend_task/identity/register_identity.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::backend_task::identity::{IdentityRegistrationInfo, RegisterIdentityFundingMethod};
use crate::backend_task::{BackendTaskSuccessResult, FeeResult};
use crate::context::{AppContext, get_transaction_info_via_dapi};
use crate::context::{AppContext, get_transaction_info};
use crate::model::fee_estimation::PlatformFeeEstimator;
use crate::model::proof_log_item::{ProofLogItem, RequestType};
use crate::model::qualified_identity::{IdentityStatus, IdentityType, QualifiedIdentity};
use crate::spv::CoreBackendMode;
use dash_sdk::dash_spv::Network;
use dash_sdk::dashcore_rpc::RpcApi;
use dash_sdk::dpp::ProtocolError;
use dash_sdk::dpp::address_funds::PlatformAddress;
use dash_sdk::dpp::block::extended_epoch_info::ExtendedEpochInfo;
Expand All @@ -22,7 +22,6 @@ use dash_sdk::platform::{Fetch, FetchMany, Identity};
use dash_sdk::query_types::AddressInfo;
use dash_sdk::{Error, Sdk};
use std::collections::BTreeMap;
use std::time::Duration;

impl AppContext {
pub(super) async fn register_identity(
Expand Down Expand Up @@ -65,7 +64,7 @@ impl AppContext {
asset_lock_proof.as_ref()
{
// we need to make sure the instant send asset lock is recent
let tx_info = get_transaction_info_via_dapi(&sdk, &tx_id).await?;
let tx_info = get_transaction_info(&sdk, &tx_id).await?;

if tx_info.is_chain_locked && tx_info.height > 0 && tx_info.confirmations > 8 {
// Transaction is old enough that instant lock may have expired
Expand Down Expand Up @@ -107,24 +106,34 @@ impl AppContext {
Some(self),
) {
Ok(transaction) => transaction,
Err(_) => {
wallet
.reload_utxos(
&self
.core_client
.read()
.expect("Core client lock was poisoned"),
self.network,
Some(self),
)
.map_err(|e| e.to_string())?;
wallet.registration_asset_lock_transaction(
sdk.network,
amount,
true,
identity_index,
Some(self),
)?
Err(e) => {
match self.core_backend_mode() {
CoreBackendMode::Rpc => {
wallet
.reload_utxos(
&self
.core_client
.read()
.expect("Core client lock was poisoned"),
self.network,
Some(self),
)
.map_err(|e| e.to_string())?;
wallet.registration_asset_lock_transaction(
sdk.network,
amount,
true,
identity_index,
Some(self),
)?
}
CoreBackendMode::Spv => {
// SPV wallet state is authoritative — UTXOs are synced
// continuously via compact block filters. No Core RPC
// fallback available.
return Err(e);
}
}
}
}
Comment thread
PastaPastaPasta marked this conversation as resolved.
};
Expand All @@ -136,11 +145,8 @@ impl AppContext {
proofs.insert(tx_id, None);
}

self.core_client
.read()
.expect("Core client lock was poisoned")
.send_raw_transaction(&asset_lock_transaction)
.map_err(|e| e.to_string())?;
self.broadcast_raw_transaction(&asset_lock_transaction)
.await?;

// Store the asset lock transaction in the database immediately after sending.
// This ensures it's tracked even if the proof times out or identity creation fails.
Expand Down Expand Up @@ -172,47 +178,10 @@ impl AppContext {
.map_err(|e| e.to_string())?;
}

// Update address_balances for affected addresses
let affected_addresses: std::collections::BTreeSet<_> =
used_utxos.values().map(|(_, addr)| addr.clone()).collect();
for address in affected_addresses {
// Recalculate balance from remaining UTXOs for this address
let new_balance = wallet
.utxos
.get(&address)
.map(|utxo_map| utxo_map.values().map(|tx_out| tx_out.value).sum())
.unwrap_or(0);
let _ = wallet.update_address_balance(&address, new_balance, self);
}
wallet.recalculate_affected_address_balances(&used_utxos, self)?;
}

// Wait for asset lock proof with timeout (2 minutes)
const ASSET_LOCK_PROOF_TIMEOUT: Duration = Duration::from_secs(120);
let asset_lock_proof = match tokio::time::timeout(ASSET_LOCK_PROOF_TIMEOUT, async {
loop {
{
let proofs = self.transactions_waiting_for_finality.lock().unwrap();
if let Some(Some(proof)) = proofs.get(&tx_id) {
return proof.clone();
}
}
tokio::time::sleep(Duration::from_millis(200)).await;
}
})
.await
{
Ok(proof) => proof,
Err(_) => {
// Clean up on timeout
let mut proofs = self.transactions_waiting_for_finality.lock().unwrap();
proofs.remove(&tx_id);
return Err(format!(
"Timeout waiting for asset lock proof after {} seconds. \
The transaction may not have been confirmed by the network.",
ASSET_LOCK_PROOF_TIMEOUT.as_secs()
));
}
};
let asset_lock_proof = self.wait_for_asset_lock_proof(tx_id).await?;

(asset_lock_proof, asset_lock_proof_private_key, tx_id)
}
Expand Down Expand Up @@ -287,11 +256,8 @@ impl AppContext {
proofs.insert(tx_id, None);
}

self.core_client
.read()
.expect("Core client lock was poisoned")
.send_raw_transaction(&asset_lock_transaction)
.map_err(|e| e.to_string())?;
self.broadcast_raw_transaction(&asset_lock_transaction)
.await?;

// Store the asset lock transaction in the database immediately after sending.
// This ensures it's tracked even if the proof times out or identity creation fails.
Expand All @@ -317,42 +283,10 @@ impl AppContext {
.drop_utxo(&utxo, &self.network.to_string())
.map_err(|e| e.to_string())?;

// Update address_balance for the affected address
let new_balance = wallet
.utxos
.get(&input_address)
.map(|utxo_map| utxo_map.values().map(|tx_out| tx_out.value).sum())
.unwrap_or(0);
let _ = wallet.update_address_balance(&input_address, new_balance, self);
wallet.recalculate_address_balance(&input_address, self)?;
}

// Wait for asset lock proof with timeout (2 minutes)
const ASSET_LOCK_PROOF_TIMEOUT: Duration = Duration::from_secs(120);
let asset_lock_proof = match tokio::time::timeout(ASSET_LOCK_PROOF_TIMEOUT, async {
loop {
{
let proofs = self.transactions_waiting_for_finality.lock().unwrap();
if let Some(Some(proof)) = proofs.get(&tx_id) {
return proof.clone();
}
}
tokio::time::sleep(Duration::from_millis(200)).await;
}
})
.await
{
Ok(proof) => proof,
Err(_) => {
// Clean up on timeout
let mut proofs = self.transactions_waiting_for_finality.lock().unwrap();
proofs.remove(&tx_id);
return Err(format!(
"Timeout waiting for asset lock proof after {} seconds. \
The transaction may not have been confirmed by the network.",
ASSET_LOCK_PROOF_TIMEOUT.as_secs()
));
}
};
let asset_lock_proof = self.wait_for_asset_lock_proof(tx_id).await?;

(asset_lock_proof, asset_lock_proof_private_key, tx_id)
}
Expand Down Expand Up @@ -481,7 +415,7 @@ impl AppContext {
|| e.contains("wasn't created recently")
{
// Try to use chain asset lock proof instead
let tx_info = get_transaction_info_via_dapi(&sdk, &tx_id).await?;
let tx_info = get_transaction_info(&sdk, &tx_id).await?;

if tx_info.is_chain_locked && tx_info.height > 0 {
let tx_block_height = tx_info.height;
Expand Down
Loading
Loading