Apply RBF replacements to the wallet before sync - #1124
ram0verflow wants to merge 2 commits into
Conversation
|
👋 Thanks for assigning @jkczyz as a reviewer! |
Bumping the same on-chain payment twice left the first replacement unmapped. The payment id is derived from the original txid and each bump sets the payment's txid to the newest one, so an earlier replacement is found only through the pending-store entry's conflicting txids. The bump never added the replaced txid to that list, so wallet sync's TxReplaced event for an earlier replacement found no payment, logged an error, and skipped it. The list stayed incomplete until the newest transaction was itself replaced or the earlier one confirmed, when the TxReplaced event for the newest one listed every conflict and repaired it. The bump now records the replaced txid in the pending-store entry, so the event resolves to the payment and the error log stops. Developed with assistance from Claude Code. Co-Authored-By: Claude Fable 5.1 <[email protected]>
| if let Some(previous) = self.pending_payment_store.get(&payment_id).await? { | ||
| conflict_txids.extend(previous.conflicting_txids); | ||
| } | ||
|
|
||
| conflict_txids.push(txid); | ||
| conflict_txids.sort_unstable(); | ||
| conflict_txids.dedup(); |
There was a problem hiding this comment.
No. The conflicts in TxReplaced come from direct_conflicts, which returns every tx in the graph spending the same outpoints, and each RBF round keeps the original inputs, so earlier rounds are already there. I hadn't checked it properly and assumed earlier entries could get lost. Dropped.
| .get(&payment_id) | ||
| .await? | ||
| .map(|p| p.conflicting_txids) | ||
| .unwrap_or_default(); |
There was a problem hiding this comment.
This is independent of #1117. A session of mine found this a little while ago, so I took the opportunity to clean it up. Could you base your PR on https://github.com/jkczyz/ldk-node/commits/2026-09-rbf-middle-round-conflict-list? Then you can drop this and the push/sort/dedup below.
Worth updating your commit's message to include something like:
Once the bump applies the replacement to the wallet, sync no longer emits TxReplaced for the replaced transaction (BDK derives events from a before/after diff of the canonical set), so nothing else populates conflicting_txids after a bump until a later eviction or confirmation flips the canonical set.
There was a problem hiding this comment.
Done, rebased on your branch and dropped the conflict handling here. Added the paragraph to the commit message
| locked_wallet.apply_unconfirmed_txs([( | ||
| fee_bumped_tx.clone(), | ||
| seen_at.max(previous_seen_at.saturating_add(1)), | ||
| )]); |
There was a problem hiding this comment.
Worth adding a one-line comment on why + 1 is needed.
eebb200 to
30fc02b
Compare
bump_fee_rbf updated the payment store but not the BDK wallet, so a second bump before sync failed with InvalidPaymentId (or panicked on debug). Apply the replacement to the wallet immediately, with a seen-at after the replaced round. Once the bump applies the replacement to the wallet, sync no longer emits TxReplaced for the replaced transaction (BDK derives events from a before/after diff of the canonical set), so nothing else populates conflicting_txids after a bump until a later eviction or confirmation flips the canonical set. Fixes lightningdevkit#1117. AI assistance: OpenAI Codex, Claude Code.
30fc02b to
a08f1fd
Compare
Fixes #1117.
bump_fee_rbfwrote the replacement to the payment store but never applied it to the BDK wallet. A second bump before the next sync returnedInvalidPaymentId(release) or panicked on thedebug_assert!(debug).apply_unconfirmed_txsbeforetake_staged(), seen-atmax(now, previous_last_seen + 1).conflicting_txidsacross rounds inbump_fee_rbfand theTxReplacedhandler. The pending record is read before the persister/wallet locks.Testing:
onchain_fee_bump_rbf_twice_before_sync: fails onmain, passes with this change.onchain_fee_bump_rbfandonchain_fee_bump_rbf_respects_anchor_reservepass.cargo fmt --checkclean; no new clippy warnings.AI assistance: OpenAI Codex, Claude Code.