From 4e6888aea222d1ff2965eb7b37a7c64198f1de42 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 5 Jul 2026 18:14:33 +0700 Subject: [PATCH] fix(platform-wallet): wait indefinitely for asset-lock ChainLock finality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Platform funding operation (identity registration/top-up, platform- address top-up, shielded funding) that broadcast its asset lock but did not obtain an InstantSend proof fell back to a 180s ChainLock wait (CL_FALLBACK_TIMEOUT). On testnet, ChainLocks can take ~15min, so the bounded wait elapsed and the flow returned FinalityTimeout — surfaced to the user as "Top Up failed" even though the asset lock is committed on-chain and merely pending finality. A ChainLock is deterministic finality that will eventually cover any broadcast asset-lock tx, so there is no correct point at which to declare failure. Make the ChainLock wait unbounded: - wait_for_chain_lock / upgrade_to_chain_lock_proof / wait_for_proof / resume_asset_lock now take Option; None waits indefinitely. - All user-facing funding flows pass None at their ChainLock fallback. - The 300s build/resume wait is kept as an InstantSend-preference window (not a finality timeout): on expiry it falls through to the unbounded ChainLock wait rather than failing. The shielded seed pool is the one deliberate exception: its FinalityTimeout is a pacing signal for the unconfirmed-ancestor stall, so it keeps a bounded Some(CL_FALLBACK_TIMEOUT) via a new cl_wait parameter on shielded_fund_from_asset_lock. CL_FALLBACK_TIMEOUT is now shielded- gated as its only remaining consumer. The two asset-lock resume/catch-up FFI entry points treat timeout_secs==0 as unbounded. No exported C symbol or signature changed. Co-Authored-By: Claude Opus 4.8 --- .../src/asset_lock/sync.rs | 11 ++- .../src/shielded_send.rs | 6 ++ .../src/wallet/asset_lock/build.rs | 8 +- .../src/wallet/asset_lock/orchestration.rs | 28 ++++-- .../src/wallet/asset_lock/sync/proof.rs | 97 +++++++++++++------ .../src/wallet/asset_lock/sync/recovery.rs | 7 +- .../wallet/identity/network/registration.rs | 19 ++-- .../fund_from_asset_lock.rs | 29 +++--- .../wallet/shielded/fund_from_asset_lock.rs | 17 +++- .../src/wallet/shielded/seed_pool.rs | 5 +- 10 files changed, 153 insertions(+), 74 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/src/asset_lock/sync.rs b/packages/rs-platform-wallet-ffi/src/asset_lock/sync.rs index 82f790a798a..5b840f9d039 100644 --- a/packages/rs-platform-wallet-ffi/src/asset_lock/sync.rs +++ b/packages/rs-platform-wallet-ffi/src/asset_lock/sync.rs @@ -58,7 +58,9 @@ pub unsafe extern "C" fn asset_lock_manager_resume( check_ptr!(out_derivation_path); let out_point = parse_outpoint(txid, vout); - let timeout = Duration::from_secs(timeout_secs); + // `timeout_secs == 0` requests an unbounded wait (a ChainLock is + // guaranteed finality; a broadcast lock is pending, never failed). + let timeout = (timeout_secs != 0).then(|| Duration::from_secs(timeout_secs)); let option = ASSET_LOCK_MANAGER_STORAGE.with_item(handle, |manager| { runtime().block_on(manager.resume_asset_lock(&out_point, timeout)) @@ -92,7 +94,8 @@ pub unsafe extern "C" fn asset_lock_manager_resume( /// Returns `ok` on a successful proof resolution, an error on /// timeout / wait failure. The Swift caller is expected to schedule /// this on a background queue — `runtime().block_on(...)` parks the -/// calling thread for up to `timeout_secs`. +/// calling thread for up to `timeout_secs` (or **indefinitely** when +/// `timeout_secs == 0`, since a ChainLock is guaranteed finality). #[no_mangle] pub unsafe extern "C" fn asset_lock_manager_catch_up_blocking( handle: Handle, @@ -103,7 +106,9 @@ pub unsafe extern "C" fn asset_lock_manager_catch_up_blocking( check_ptr!(txid); let out_point = parse_outpoint(txid, vout); - let timeout = Duration::from_secs(timeout_secs); + // `timeout_secs == 0` requests an unbounded wait (a ChainLock is + // guaranteed finality; a broadcast lock is pending, never failed). + let timeout = (timeout_secs != 0).then(|| Duration::from_secs(timeout_secs)); tracing::info!( outpoint = %out_point, diff --git a/packages/rs-platform-wallet-ffi/src/shielded_send.rs b/packages/rs-platform-wallet-ffi/src/shielded_send.rs index 54f6edaa711..94fbbf1269a 100644 --- a/packages/rs-platform-wallet-ffi/src/shielded_send.rs +++ b/packages/rs-platform-wallet-ffi/src/shielded_send.rs @@ -918,6 +918,9 @@ pub unsafe extern "C" fn platform_wallet_manager_shielded_fund_from_asset_lock( // pool-seeding path uses its own dedicated FFI entry point). 0, None, + // User-facing funding: wait for the ChainLock indefinitely — + // a broadcast asset lock is pending finality, never failed. + None, ) .await }); @@ -1061,6 +1064,9 @@ pub unsafe extern "C" fn platform_wallet_manager_shielded_resume_fund_from_asset // Resuming a single-note fund (not a seeding batch). 0, None, + // User-facing funding: wait for the ChainLock indefinitely — + // a broadcast asset lock is pending finality, never failed. + None, ) .await }); diff --git a/packages/rs-platform-wallet/src/wallet/asset_lock/build.rs b/packages/rs-platform-wallet/src/wallet/asset_lock/build.rs index 887aa08dc53..5a3444a3159 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/build.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/build.rs @@ -386,9 +386,13 @@ impl AssetLockManager { .await?; self.queue_asset_lock_changeset(cs_broadcast); - // 5. Wait for proof via SPV events. + // 5. Wait for proof via SPV events. The 300s bound is an + // InstantSend-preference window, NOT a finality timeout: on + // expiry the resolver falls back to an unbounded ChainLock wait + // (`upgrade_to_chain_lock_proof(None)`), so a broadcast lock is + // never surfaced as "failed" just because IS was slow. let proof = self - .wait_for_proof(&out_point, Duration::from_secs(300)) + .wait_for_proof(&out_point, Some(Duration::from_secs(300))) .await?; // 5b. If we got an IS-lock proof, check whether the transaction is diff --git a/packages/rs-platform-wallet/src/wallet/asset_lock/orchestration.rs b/packages/rs-platform-wallet/src/wallet/asset_lock/orchestration.rs index 932efcc7113..953d687c182 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/orchestration.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/orchestration.rs @@ -46,11 +46,23 @@ use crate::wallet::asset_lock::manager::AssetLockManager; // Timeout policy // --------------------------------------------------------------------------- -/// Time we will wait for a ChainLock to materialise after an IS-lock -/// fallback is triggered. 180s mirrors the existing fallback shape and -/// is roughly the worst-case ChainLock latency we've observed in -/// testnet operation. Promoted to a constant so the registration, -/// top-up, and address-funding flows can't drift apart on this number. +/// Bounded ChainLock wait used *only* by the shielded seed pool, where a +/// `FinalityTimeout` is a deliberate pacing signal — rapid back-to-back +/// batches chain unconfirmed L1 change outputs, and around core's +/// unconfirmed-ancestor depth limit IS/CL proofs stop arriving until a +/// block lands; the seed pool catches the timeout, pauses, and resumes +/// the tracked lock (see `shielded/seed_pool.rs`). +/// +/// The user-facing funding flows (identity registration / top-up, +/// platform-address top-up, and user-initiated shielded funding) do NOT +/// use this: they wait for a ChainLock **indefinitely** +/// (`upgrade_to_chain_lock_proof(None)`), because a ChainLock is +/// deterministic finality that will eventually cover any broadcast +/// asset-lock tx — so a broadcast lock is *pending*, never *failed*. +/// +/// Only the shielded seed pool consumes this, so it is `shielded`-gated +/// to avoid a dead-code warning in builds without that feature. +#[cfg(feature = "shielded")] pub(crate) const CL_FALLBACK_TIMEOUT: Duration = Duration::from_secs(180); /// Delay between retries when Platform rejected with CL-height-too-low. @@ -408,8 +420,12 @@ impl AssetLockManager { } } AssetLockFunding::FromExistingAssetLock { out_point } => { + // 300s is an InstantSend-preference window, not a finality + // timeout: on expiry the caller falls back to an unbounded + // ChainLock wait, so a resumed broadcast lock never fails + // just because IS was slow. match self - .resume_asset_lock(&out_point, Duration::from_secs(300)) + .resume_asset_lock(&out_point, Some(Duration::from_secs(300))) .await { Ok((proof, path)) => Ok(FundingResolution::Resolved(ResolvedFunding { diff --git a/packages/rs-platform-wallet/src/wallet/asset_lock/sync/proof.rs b/packages/rs-platform-wallet/src/wallet/asset_lock/sync/proof.rs index ae9ab5b6b93..be590f5f84d 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/sync/proof.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/sync/proof.rs @@ -148,12 +148,21 @@ impl AssetLockManager { /// Called from the recovery layer when `put_to_platform` fails with /// `InvalidInstantAssetLockProofSignature`. If the TX is already /// chain-locked, constructs the proof immediately. Otherwise, **waits** - /// for a ChainLock via SPV events (up to 10 minutes) so the caller - /// doesn't see a failure — just a longer wait. + /// for a ChainLock via SPV events so the caller doesn't see a failure — + /// just a longer wait. + /// + /// `timeout` is `Option`: `None` waits **indefinitely**. A + /// ChainLock is deterministic finality that will eventually cover any + /// broadcast asset-lock tx, so the user-facing funding flows + /// (identity registration / top-up, platform-address top-up, shielded + /// funding) pass `None` — a broadcast lock is pending, never failed. + /// The only bounded caller is the shielded seed pool, where a + /// `FinalityTimeout` is a deliberate pacing signal for the + /// unconfirmed-ancestor stall (see `CL_FALLBACK_TIMEOUT`). pub(crate) async fn upgrade_to_chain_lock_proof( &self, out_point: &OutPoint, - timeout: Duration, + timeout: Option, ) -> Result { use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; use key_wallet::transaction_checking::TransactionContext; @@ -253,16 +262,18 @@ impl AssetLockManager { /// Wait for a ChainLock that covers the given transaction. /// /// Subscribes to SPV events and waits until the transaction's block - /// is chain-locked. + /// is chain-locked. `timeout` is `Option`: `None` waits + /// **indefinitely** (a ChainLock is guaranteed finality that will + /// eventually arrive, so a broadcast lock is pending, not failed). async fn wait_for_chain_lock( &self, account_index: u32, out_point: &OutPoint, - timeout: Duration, + timeout: Option, ) -> Result { use key_wallet::transaction_checking::TransactionContext; - let deadline = tokio::time::Instant::now() + timeout; + let deadline = timeout.map(|t| tokio::time::Instant::now() + t); loop { // Arm the `Notify` future BEFORE the state check, closing @@ -304,18 +315,26 @@ impl AssetLockManager { } } - let remaining = deadline.saturating_duration_since(tokio::time::Instant::now()); - if remaining.is_zero() { - return Err(PlatformWalletError::FinalityTimeout(*out_point)); - } - - // Wait for a lock event notification or timeout. The - // `notified` future is the one we armed above, so any - // CL/IS event since then is already buffered into it. - tokio::select! { - _ = &mut notified => continue, - _ = tokio::time::sleep(remaining) => { - return Err(PlatformWalletError::FinalityTimeout(*out_point)); + // Wait for a lock event notification (or timeout, when one is + // configured). The `notified` future is the one we armed above, + // so any CL/IS event since then is already buffered into it. + match deadline { + Some(dl) => { + let remaining = dl.saturating_duration_since(tokio::time::Instant::now()); + if remaining.is_zero() { + return Err(PlatformWalletError::FinalityTimeout(*out_point)); + } + tokio::select! { + _ = &mut notified => continue, + _ = tokio::time::sleep(remaining) => { + return Err(PlatformWalletError::FinalityTimeout(*out_point)); + } + } + } + // No deadline: wait indefinitely for the next lock event. + None => { + notified.as_mut().await; + continue; } } } @@ -330,17 +349,23 @@ impl AssetLockManager { /// /// Returns a properly-constructed `AssetLockProof` on success, or /// `FinalityTimeout` if the timeout elapses first. + /// + /// `timeout` is `Option`: `None` waits **indefinitely** for + /// either an InstantSend or a ChainLock proof. Bounded callers use the + /// deadline as an InstantSend-preference window — on expiry they get a + /// `FinalityTimeout` and fall back to an (unbounded) ChainLock wait via + /// [`Self::upgrade_to_chain_lock_proof`]. pub(in crate::wallet::asset_lock) async fn wait_for_proof( &self, out_point: &OutPoint, - timeout: Duration, + timeout: Option, ) -> Result { use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; use key_wallet::transaction_checking::TransactionContext; tracing::info!(outpoint = %out_point, ?timeout, "wait_for_proof: entered"); - let deadline = tokio::time::Instant::now() + timeout; + let deadline = timeout.map(|t| tokio::time::Instant::now() + t); let mut iter: u32 = 0; // Read account_index and transaction from the tracked lock. @@ -520,18 +545,26 @@ impl AssetLockManager { } } - let remaining = deadline.saturating_duration_since(tokio::time::Instant::now()); - if remaining.is_zero() { - return Err(PlatformWalletError::FinalityTimeout(*out_point)); - } - - // Wait for a lock event notification or timeout. The - // `notified` future is the one we armed above, so any - // IS/CL event since then is already buffered into it. - tokio::select! { - _ = &mut notified => continue, - _ = tokio::time::sleep(remaining) => { - return Err(PlatformWalletError::FinalityTimeout(*out_point)); + // Wait for a lock event notification (or timeout, when one is + // configured). The `notified` future is the one we armed above, + // so any IS/CL event since then is already buffered into it. + match deadline { + Some(dl) => { + let remaining = dl.saturating_duration_since(tokio::time::Instant::now()); + if remaining.is_zero() { + return Err(PlatformWalletError::FinalityTimeout(*out_point)); + } + tokio::select! { + _ = &mut notified => continue, + _ = tokio::time::sleep(remaining) => { + return Err(PlatformWalletError::FinalityTimeout(*out_point)); + } + } + } + // No deadline: wait indefinitely for the next lock event. + None => { + notified.as_mut().await; + continue; } } } diff --git a/packages/rs-platform-wallet/src/wallet/asset_lock/sync/recovery.rs b/packages/rs-platform-wallet/src/wallet/asset_lock/sync/recovery.rs index e08ae860c2d..64e709c0799 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/sync/recovery.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/sync/recovery.rs @@ -206,10 +206,15 @@ impl AssetLockManager { /// registration or top-up via the `_with_signer` SDK methods. The /// caller passes `derivation_path` to the same signer used for the /// build phase when the credit output is later consumed on Platform. + /// + /// `timeout` is `Option` and is only consulted when the lock + /// still needs a proof (`Built` / `Broadcast`): `None` waits + /// **indefinitely** for finality. For `InstantSendLocked` / `ChainLocked` + /// the proof already exists and no wait happens, so the value is moot. pub async fn resume_asset_lock( &self, out_point: &OutPoint, - timeout: Duration, + timeout: Option, ) -> Result<(dpp::prelude::AssetLockProof, DerivationPath), PlatformWalletError> { tracing::info!(outpoint = %out_point, ?timeout, "resume_asset_lock: entered"); diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs b/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs index 0e9f76b8c38..5d146591519 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs @@ -68,7 +68,6 @@ use dash_sdk::platform::transition::top_up_identity::TopUpIdentity; use crate::error::{is_instant_lock_proof_invalid, PlatformWalletError}; use crate::wallet::asset_lock::orchestration::{ out_point_from_proof, submit_with_cl_height_retry, FundingResolution, ResolvedFunding, - CL_FALLBACK_TIMEOUT, }; use crate::wallet::asset_lock::AssetLockFunding; @@ -184,7 +183,7 @@ impl IdentityWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; // Recover the credit-output derivation path. The // asset lock is now CL-attached (status advanced by @@ -193,10 +192,7 @@ impl IdentityWallet { // proof branch and just re-derives the path. This is // cheap (no SPV wait) and avoids duplicating the // path-derivation logic here. - let (_, path) = self - .asset_locks - .resume_asset_lock(&out_point, CL_FALLBACK_TIMEOUT) - .await?; + let (_, path) = self.asset_locks.resume_asset_lock(&out_point, None).await?; ResolvedFunding { proof: chain_proof, path, @@ -248,7 +244,7 @@ impl IdentityWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; submit_with_cl_height_retry(settings, |s| { placeholder.put_to_platform_and_wait_for_response_with_signer( @@ -404,12 +400,9 @@ impl IdentityWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) - .await?; - let (_, path) = self - .asset_locks - .resume_asset_lock(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; + let (_, path) = self.asset_locks.resume_asset_lock(&out_point, None).await?; ResolvedFunding { proof: chain_proof, path, @@ -445,7 +438,7 @@ impl IdentityWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; submit_with_cl_height_retry(settings, |s| { identity.top_up_identity_with_signer( diff --git a/packages/rs-platform-wallet/src/wallet/platform_addresses/fund_from_asset_lock.rs b/packages/rs-platform-wallet/src/wallet/platform_addresses/fund_from_asset_lock.rs index d33b1690c26..61dfab2095a 100644 --- a/packages/rs-platform-wallet/src/wallet/platform_addresses/fund_from_asset_lock.rs +++ b/packages/rs-platform-wallet/src/wallet/platform_addresses/fund_from_asset_lock.rs @@ -27,7 +27,7 @@ use crate::wallet::asset_lock::orchestration::{ out_point_from_proof, submit_with_cl_height_retry, AssetLockFunding, FundingResolution, - ResolvedFunding, CL_FALLBACK_TIMEOUT, + ResolvedFunding, }; use crate::wallet::PlatformAddressWallet; use crate::{error::is_instant_lock_proof_invalid, PlatformAddressChangeSet, PlatformWalletError}; @@ -86,12 +86,17 @@ impl PlatformAddressWallet { /// /// # Latency budget /// - /// Worst-case wall time stacks at ~690s on the IS-rejection - /// branch: + /// There is intentionally **no ceiling** on this call: a broadcast + /// asset lock is committed on-chain, and its ChainLock is + /// deterministic finality that will eventually arrive, so the flow + /// waits for finality however long it takes rather than reporting a + /// spurious failure. The components are: /// - 300s IS-wait inside the resolver's /// `create_funded_asset_lock_proof` (`AssetLockManager`'s - /// fixed window before falling back to ChainLock). - /// - 180s CL fallback (`CL_FALLBACK_TIMEOUT`) per `upgrade_to_chain_lock_proof` call. + /// InstantSend-preference window before falling back to ChainLock). + /// - **Unbounded** ChainLock fallback + /// (`upgrade_to_chain_lock_proof(None)`) — waits for the ChainLock + /// indefinitely (testnet ChainLocks can take ~15min). /// - 210s CL-height retry budget (`CL_HEIGHT_RETRY_BUDGET`) per /// `submit_with_cl_height_retry` wrapper. /// - Up to two passes through the submit wrapper on the @@ -100,7 +105,10 @@ impl PlatformAddressWallet { /// /// Happy-path wall time on a healthy testnet is single-digit /// seconds (IS-lock typically arrives within 3s of broadcast, - /// CL-height retry never fires). + /// CL-height retry never fires). The unbounded wait only bites when + /// InstantSend never propagates and the caller must wait out the + /// slower ChainLock. Callers run this off the main thread and never + /// cancel it (see the Cancellation note below). /// /// # Cancellation /// @@ -173,16 +181,13 @@ impl PlatformAddressWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; // Re-derive the credit-output path. The lock is now // CL-attached; `resume_asset_lock` short-circuits to // the existing-proof branch and just hands the path // back. - let (_, path) = self - .asset_locks - .resume_asset_lock(&out_point, CL_FALLBACK_TIMEOUT) - .await?; + let (_, path) = self.asset_locks.resume_asset_lock(&out_point, None).await?; ResolvedFunding { proof: chain_proof, path, @@ -220,7 +225,7 @@ impl PlatformAddressWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, None) .await?; // Advance the tracked status from `InstantSendLocked` // to `ChainLocked` with the upgraded proof attached diff --git a/packages/rs-platform-wallet/src/wallet/shielded/fund_from_asset_lock.rs b/packages/rs-platform-wallet/src/wallet/shielded/fund_from_asset_lock.rs index 7b118c9125a..ff6c71fff39 100644 --- a/packages/rs-platform-wallet/src/wallet/shielded/fund_from_asset_lock.rs +++ b/packages/rs-platform-wallet/src/wallet/shielded/fund_from_asset_lock.rs @@ -37,10 +37,12 @@ use key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundin use crate::wallet::asset_lock::tracked::TrackedAssetLock; +use std::time::Duration; + use crate::error::is_instant_lock_proof_invalid; use crate::wallet::asset_lock::orchestration::{ out_point_from_proof, submit_with_cl_height_retry, AssetLockFunding, FundingResolution, - ResolvedFunding, CL_FALLBACK_TIMEOUT, + ResolvedFunding, }; use crate::wallet::PlatformWallet; use crate::PlatformWalletError; @@ -119,6 +121,12 @@ impl PlatformWallet { /// even though each attempt re-signs a freshly-randomized bundle. /// * `settings` — Optional `PutSettings`; `user_fee_increase` is /// bumped by the CL-height retry wrapper on consensus 10506. + /// * `cl_wait` — ChainLock-fallback wait policy (`Option`). + /// User-facing funding passes `None` to wait for the ChainLock + /// **indefinitely** (a broadcast lock is pending, never failed). The + /// shielded seed pool passes `Some(CL_FALLBACK_TIMEOUT)` so a + /// `FinalityTimeout` surfaces as its unconfirmed-ancestor pacing + /// signal (see `seed_pool.rs`). #[cfg(feature = "shielded")] #[allow(clippy::too_many_arguments)] pub async fn shielded_fund_from_asset_lock( @@ -131,6 +139,7 @@ impl PlatformWallet { surplus_output: Option, dummy_outputs: usize, settings: Option, + cl_wait: Option, ) -> Result<(), PlatformWalletError> where AS: ::key_wallet::signer::Signer + Send + Sync, @@ -216,11 +225,11 @@ impl PlatformWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, cl_wait) .await?; let (_, path) = self .asset_locks - .resume_asset_lock(&out_point, CL_FALLBACK_TIMEOUT) + .resume_asset_lock(&out_point, cl_wait) .await?; ResolvedFunding { proof: chain_proof, @@ -370,7 +379,7 @@ impl PlatformWallet { ); let chain_proof = self .asset_locks - .upgrade_to_chain_lock_proof(&out_point, CL_FALLBACK_TIMEOUT) + .upgrade_to_chain_lock_proof(&out_point, cl_wait) .await?; let cs = self .asset_locks diff --git a/packages/rs-platform-wallet/src/wallet/shielded/seed_pool.rs b/packages/rs-platform-wallet/src/wallet/shielded/seed_pool.rs index 2f43d7daf75..5404f864e59 100644 --- a/packages/rs-platform-wallet/src/wallet/shielded/seed_pool.rs +++ b/packages/rs-platform-wallet/src/wallet/shielded/seed_pool.rs @@ -32,7 +32,7 @@ use dash_sdk::platform::types::shielded::fetch_shielded_notes_count; use dpp::address_funds::OrchardAddress; use dpp::balances::credits::CREDITS_PER_DUFF; -use crate::wallet::asset_lock::orchestration::AssetLockFunding; +use crate::wallet::asset_lock::orchestration::{AssetLockFunding, CL_FALLBACK_TIMEOUT}; use crate::wallet::shielded::fund_from_asset_lock::shield_from_asset_lock_num_actions; use crate::wallet::shielded::CachedOrchardProver; use crate::wallet::PlatformWallet; @@ -285,6 +285,9 @@ impl PlatformWallet { None, dummy_outputs, settings, + // Bounded: a ChainLock timeout here is the deliberate + // unconfirmed-ancestor pacing signal this loop retries on. + Some(CL_FALLBACK_TIMEOUT), ) .await {