@@ -148,12 +148,21 @@ impl<B: TransactionBroadcaster + ?Sized> AssetLockManager<B> {
148148 /// Called from the recovery layer when `put_to_platform` fails with
149149 /// `InvalidInstantAssetLockProofSignature`. If the TX is already
150150 /// chain-locked, constructs the proof immediately. Otherwise, **waits**
151- /// for a ChainLock via SPV events (up to 10 minutes) so the caller
152- /// doesn't see a failure — just a longer wait.
151+ /// for a ChainLock via SPV events so the caller doesn't see a failure —
152+ /// just a longer wait.
153+ ///
154+ /// `timeout` is `Option<Duration>`: `None` waits **indefinitely**. A
155+ /// ChainLock is deterministic finality that will eventually cover any
156+ /// broadcast asset-lock tx, so the user-facing funding flows
157+ /// (identity registration / top-up, platform-address top-up, shielded
158+ /// funding) pass `None` — a broadcast lock is pending, never failed.
159+ /// The only bounded caller is the shielded seed pool, where a
160+ /// `FinalityTimeout` is a deliberate pacing signal for the
161+ /// unconfirmed-ancestor stall (see `CL_FALLBACK_TIMEOUT`).
153162 pub ( crate ) async fn upgrade_to_chain_lock_proof (
154163 & self ,
155164 out_point : & OutPoint ,
156- timeout : Duration ,
165+ timeout : Option < Duration > ,
157166 ) -> Result < dpp:: prelude:: AssetLockProof , PlatformWalletError > {
158167 use dpp:: identity:: state_transition:: asset_lock_proof:: chain:: ChainAssetLockProof ;
159168 use key_wallet:: transaction_checking:: TransactionContext ;
@@ -253,16 +262,18 @@ impl<B: TransactionBroadcaster + ?Sized> AssetLockManager<B> {
253262 /// Wait for a ChainLock that covers the given transaction.
254263 ///
255264 /// Subscribes to SPV events and waits until the transaction's block
256- /// is chain-locked.
265+ /// is chain-locked. `timeout` is `Option<Duration>`: `None` waits
266+ /// **indefinitely** (a ChainLock is guaranteed finality that will
267+ /// eventually arrive, so a broadcast lock is pending, not failed).
257268 async fn wait_for_chain_lock (
258269 & self ,
259270 account_index : u32 ,
260271 out_point : & OutPoint ,
261- timeout : Duration ,
272+ timeout : Option < Duration > ,
262273 ) -> Result < u32 , PlatformWalletError > {
263274 use key_wallet:: transaction_checking:: TransactionContext ;
264275
265- let deadline = tokio:: time:: Instant :: now ( ) + timeout ;
276+ let deadline = timeout . map ( |t| tokio:: time:: Instant :: now ( ) + t ) ;
266277
267278 loop {
268279 // Arm the `Notify` future BEFORE the state check, closing
@@ -304,18 +315,26 @@ impl<B: TransactionBroadcaster + ?Sized> AssetLockManager<B> {
304315 }
305316 }
306317
307- let remaining = deadline. saturating_duration_since ( tokio:: time:: Instant :: now ( ) ) ;
308- if remaining. is_zero ( ) {
309- return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
310- }
311-
312- // Wait for a lock event notification or timeout. The
313- // `notified` future is the one we armed above, so any
314- // CL/IS event since then is already buffered into it.
315- tokio:: select! {
316- _ = & mut notified => continue ,
317- _ = tokio:: time:: sleep( remaining) => {
318- return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
318+ // Wait for a lock event notification (or timeout, when one is
319+ // configured). The `notified` future is the one we armed above,
320+ // so any CL/IS event since then is already buffered into it.
321+ match deadline {
322+ Some ( dl) => {
323+ let remaining = dl. saturating_duration_since ( tokio:: time:: Instant :: now ( ) ) ;
324+ if remaining. is_zero ( ) {
325+ return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
326+ }
327+ tokio:: select! {
328+ _ = & mut notified => continue ,
329+ _ = tokio:: time:: sleep( remaining) => {
330+ return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
331+ }
332+ }
333+ }
334+ // No deadline: wait indefinitely for the next lock event.
335+ None => {
336+ notified. as_mut ( ) . await ;
337+ continue ;
319338 }
320339 }
321340 }
@@ -330,17 +349,23 @@ impl<B: TransactionBroadcaster + ?Sized> AssetLockManager<B> {
330349 ///
331350 /// Returns a properly-constructed `AssetLockProof` on success, or
332351 /// `FinalityTimeout` if the timeout elapses first.
352+ ///
353+ /// `timeout` is `Option<Duration>`: `None` waits **indefinitely** for
354+ /// either an InstantSend or a ChainLock proof. Bounded callers use the
355+ /// deadline as an InstantSend-preference window — on expiry they get a
356+ /// `FinalityTimeout` and fall back to an (unbounded) ChainLock wait via
357+ /// [`Self::upgrade_to_chain_lock_proof`].
333358 pub ( in crate :: wallet:: asset_lock) async fn wait_for_proof (
334359 & self ,
335360 out_point : & OutPoint ,
336- timeout : Duration ,
361+ timeout : Option < Duration > ,
337362 ) -> Result < dpp:: prelude:: AssetLockProof , PlatformWalletError > {
338363 use dpp:: identity:: state_transition:: asset_lock_proof:: chain:: ChainAssetLockProof ;
339364 use dpp:: identity:: state_transition:: asset_lock_proof:: InstantAssetLockProof ;
340365 use key_wallet:: transaction_checking:: TransactionContext ;
341366
342367 tracing:: info!( outpoint = %out_point, ?timeout, "wait_for_proof: entered" ) ;
343- let deadline = tokio:: time:: Instant :: now ( ) + timeout ;
368+ let deadline = timeout . map ( |t| tokio:: time:: Instant :: now ( ) + t ) ;
344369 let mut iter: u32 = 0 ;
345370
346371 // Read account_index and transaction from the tracked lock.
@@ -520,18 +545,26 @@ impl<B: TransactionBroadcaster + ?Sized> AssetLockManager<B> {
520545 }
521546 }
522547
523- let remaining = deadline. saturating_duration_since ( tokio:: time:: Instant :: now ( ) ) ;
524- if remaining. is_zero ( ) {
525- return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
526- }
527-
528- // Wait for a lock event notification or timeout. The
529- // `notified` future is the one we armed above, so any
530- // IS/CL event since then is already buffered into it.
531- tokio:: select! {
532- _ = & mut notified => continue ,
533- _ = tokio:: time:: sleep( remaining) => {
534- return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
548+ // Wait for a lock event notification (or timeout, when one is
549+ // configured). The `notified` future is the one we armed above,
550+ // so any IS/CL event since then is already buffered into it.
551+ match deadline {
552+ Some ( dl) => {
553+ let remaining = dl. saturating_duration_since ( tokio:: time:: Instant :: now ( ) ) ;
554+ if remaining. is_zero ( ) {
555+ return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
556+ }
557+ tokio:: select! {
558+ _ = & mut notified => continue ,
559+ _ = tokio:: time:: sleep( remaining) => {
560+ return Err ( PlatformWalletError :: FinalityTimeout ( * out_point) ) ;
561+ }
562+ }
563+ }
564+ // No deadline: wait indefinitely for the next lock event.
565+ None => {
566+ notified. as_mut ( ) . await ;
567+ continue ;
535568 }
536569 }
537570 }
0 commit comments