Severity: Medium (fund-stranding / UX integrity)
Wallet::send_to_address in ldk-node builds, signs, and persists the transaction, then calls broadcast_transactions and unconditionally returns Ok(txid) (ldk-node/src/wallet/mod.rs:1915). The broadcaster is a fire-and-forget try_send onto a 50-slot queue; a full queue is only logged (ldk-node/src/tx_broadcaster.rs:47-52). The queue consumer POSTs each tx to esplora and only logs failures (timeouts, 5xx, non-"already known" 400s), with no retry and no error path back to the caller (ldk-node/src/chain/esplora.rs:482-551).
The app therefore shows the success screen as soon as Ok(txid) returns (SendConfirmationView.performPayment -> .success), whether or not the tx reached the network. If it never propagates, the tx stays in the wallet store with its inputs spent and the funds appear locked. The only downstream signal is OnchainTransactionEvicted on a later sync (CoreService.swift:693, handleOnchainTransactionEvicted). There are no cancelTx or rebroadcast call sites in the app, and ldk-node never re-broadcasts unconfirmed wallet transactions. The same queue also carries RBF bumps and CPFP accelerations.
Steps to reproduce:
- Send an on-chain payment while the configured esplora endpoint is unreachable (cut connectivity right after confirm, or point the app at a dead esplora URL).
- The app shows success with a txid; the tx never appears on chain.
- Balance stays reduced until a later sync evicts the tx.
Suggested fix:
- Make
broadcast_transactions return a result the payment call can await, or have the queue consumer emit a per-txid success/failure event.
- Periodically re-broadcast unconfirmed wallet transactions.
- When
OnchainTransactionEvicted fires for a user-initiated send, offer "retry broadcast" and "cancel (RBF-to-self)" actions in the UI.
Sibling issue: synonymdev/bitkit-android#1143 (same root cause in ldk-node).
Severity: Medium (fund-stranding / UX integrity)
Wallet::send_to_addressin ldk-node builds, signs, and persists the transaction, then callsbroadcast_transactionsand unconditionally returnsOk(txid)(ldk-node/src/wallet/mod.rs:1915). The broadcaster is a fire-and-forgettry_sendonto a 50-slot queue; a full queue is only logged (ldk-node/src/tx_broadcaster.rs:47-52). The queue consumer POSTs each tx to esplora and only logs failures (timeouts, 5xx, non-"already known" 400s), with no retry and no error path back to the caller (ldk-node/src/chain/esplora.rs:482-551).The app therefore shows the success screen as soon as
Ok(txid)returns (SendConfirmationView.performPayment->.success), whether or not the tx reached the network. If it never propagates, the tx stays in the wallet store with its inputs spent and the funds appear locked. The only downstream signal isOnchainTransactionEvictedon a later sync (CoreService.swift:693,handleOnchainTransactionEvicted). There are nocancelTxor rebroadcast call sites in the app, and ldk-node never re-broadcasts unconfirmed wallet transactions. The same queue also carries RBF bumps and CPFP accelerations.Steps to reproduce:
Suggested fix:
broadcast_transactionsreturn a result the payment call can await, or have the queue consumer emit a per-txid success/failure event.OnchainTransactionEvictedfires for a user-initiated send, offer "retry broadcast" and "cancel (RBF-to-self)" actions in the UI.Sibling issue: synonymdev/bitkit-android#1143 (same root cause in ldk-node).