Skip to content

[bug]: Confirmed sweep dropped from ListSweeps when an RBF'd sweep's original (pre-replacement) tx confirms #10946

Description

@onepabz

Pre-Submission Checklist

  • I have searched the existing issues and believe this is a new bug.
  • I am not asking a question about how to use lnd, but reporting a bug.

LND Version

0.20.1-beta (commit v0.20.1-beta-1-g20a93e8e9)

LND Configuration

Standard config; sweeper using default fee-bumping / RBF behaviour. Nodes sweep to_remote outputs after remote-initiated force-closes.

Backend Version

bitcoind (full node)

Backend Configuration

Default.

OS/Distribution

Linux (containerised).

Bug Details & Steps to Reproduce

We noticed some sweep transactions being reported as receives by downstream tooling (Faraday), and traced it to sweeps disappearing from ListSweeps.

The pattern occurs when a peer force-closes a channel and our node sweeps its balance back to its own wallet:

  1. Sweeper broadcasts sweep tx A at a low feerate.
  2. The fee bumper RBFs it, producing replacement B at a higher feerate. handleBumpEventTxReplaced calls Store.DeleteTx(A) and then stores B.
  3. Due to a mempool/propagation race, the original tx A is the one that confirms — not replacement B.
  4. The input is marked swept via the spend notification. TxConfirmed is not handled in handleBumpEvent (comment: "we already subscribe to the input's spending event"), and markInputsSwept does not call StoreTx.

Net effect: no code path ever re-adds the confirmed tx A to the sweeper store. The store is left holding only B, which never confirmed.

Example (mainnet), txids redacted — happy to share the specific transactions and fuller logs privately:

SWPR: Created initial sweep tx=<A>  feerate=750 sat/kw, fee=365 sats
SWPR: RBFed tx=<A> (fee=365, 750 sat/kw) with new tx=<B> (fee=606, 1246 sat/kw)
CNCT: commitSweepResolver(...): local commitment output was swept by remote party via <A>

The original tx <A> confirmed on-chain; the replacement <B> never did. Because <A> was deleted from the store on RBF, IsOurTx(<A>) returns false — producing the "swept by remote party" log for our own tx — and <A> is absent from ListSweeps.

Expected Behavior

After an RBF where the original/replaced tx is the one that confirms, the confirmed sweep txid should still be discoverable — ListSweeps should include it and IsOurTx should return true for it.

Actual Behavior

The confirmed sweep txid is permanently missing from ListSweeps and IsOurTx returns false, because it was removed from the store on the RBF replacement and never re-added. Funds are resolved correctly on-chain (this is purely a bookkeeping/labeling issue), but ListSweeps consumers misclassify the sweep.

Possible fix directions (happy to open a PR in whatever shape you'd prefer):

  • On the input's spend notification, if the confirming txid isn't already in the store, record it (reconcile the store to the actually-confirmed tx); or
  • Defer deleting the replaced tx until the input is fully resolved, so whichever variant confirms remains discoverable.

Debug Information

Relevant SWPR / CNCT sweeper log excerpts above; fuller logs available on request.

Environment

lnd 0.20.1-beta, bitcoind backend, Linux.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions