Skip to content

fix: only fee-bump transactions the wallet broadcast - #1140

Merged
ovitrif merged 5 commits into
masterfrom
fix/bump-own-txs-only
Oct 28, 2026
Merged

ovitrif merged 5 commits into
masterfrom
fix/bump-own-txs-only

Conversation

@ovitrif

@ovitrif ovitrif commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Twin: synonymdev/bitkit-android#1706
Refs: synonymdev/ldk-node#129

This PR updates LDK Node to 0.7.0-rc.78, which contains synonymdev/ldk-node#129, so Boost only replaces outgoing transactions this wallet sent.

Description

  • Updates LDK Node to 0.7.0-rc.78 in project.pbxproj and Package.resolved. After it, bumpFeeByRbf replaces only transactions the wallet built and broadcast. Any other transaction ID returns the new NodeError.TransactionNotBroadcastByWallet before a replacement is built.
  • Maps NodeError.TransactionNotBroadcastByWallet in Errors.swift to "No broadcast record for this transaction". The NodeError switch there is exhaustive, so the new case is required to compile.
  • Boosting a received transaction (CPFP) is unchanged.

Out of Scope

  • Bitkit/Views: a dedicated Boost error message. The new error reaches the user through the existing LDK Node error text.
  • Sends made before this update: LDK Node holds no broadcast record for them, so an unconfirmed send from before the update cannot be boosted by replacement after it.
  • Channel force closes with the LSP: the closing transaction is not broadcast when the peer is trusted. That needs a separate LDK Node change, not part of fix: only fee-bump transactions the wallet broadcast ldk-node#129.

Design

N/A — no UI changes.

Preview

N/A — no user-visible UI change.

QA Notes

Setup

Regtest with on-chain funds, on a build of this PR.

Journeys

  • journeys/boost/rbf-own-send.xml — boost an unconfirmed send this wallet made: the replacement keeps the amount, raises the fee, gets a new TXID and confirms — passed at f0bca98 (@jvsena42's run)
  • journeys/boost/cpfp-receive.xml — boost an unconfirmed incoming payment: a Boost Fee child appears, the receive keeps its TXID and both confirm — passed at f0bca98 (@jvsena42's run)

Manual Tests

  • 1. Upgrade from a build on 0.7.0-rc.77 with an existing wallet and channels → the node starts, channels reconnect and balances match — upgrade path not in Capabilities

Automated Checks

  • ran an arm64 simulator build against 0.7.0-rc.78 resolved from a clean package cache; it builds, including the new NodeError.TransactionNotBroadcastByWallet case.

Gaps

  • Manual test 1 has not run on a device yet.

@ovitrif
ovitrif marked this pull request as ready for review October 27, 2026 17:28
@ovitrif
ovitrif requested review from a team, ben-kaufman and pwltr and removed request for a team October 27, 2026 17:28
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-10-27T17:28:50.421952Z 7477475 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ovitrif
ovitrif requested review from piotr-iohk and removed request for pwltr October 27, 2026 17:28

@talosmachina talosmachina left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No findings. Pins LDK Node 0.7.0-rc.78, so bumpFeeByRbf refuses transactions the wallet has no broadcast record for, and maps the new NodeError.TransactionNotBroadcastByWallet case in AppError. Reviewed 7477475, full tier (dependency pin). Build skipped: iOS does not build on Linux, so this review is reasoned from the code, the pinned release and CI.

What I checked, and 5 candidates I ruled out

Read in full: Bitkit/Utilities/Errors.swift (the NodeError mapping), CoreService.boostOnchainTransaction, LightningService.bumpFeeByRbf / accelerateByCpfp / send, OnchainSendAttemptService.prepareOnchainSend
Pin: Package.resolved revision 1976754 is the commit v0.7.0-rc.78 points to, it contains synonymdev/ldk-node#129 (merge e794b35 is an ancestor), and the release's LDKNodeFFI.xcframework.zip hashes to the Package.swift checksum 2a003f8a…f285.
Upstream delta rc.77..rc.78: #129 only, plus the release commit. All three wallet broadcast routes record the txid before dispatching: send_to_address, PreparedOnchainSend::broadcast, and the RBF/CPFP replacements.
CI: SwiftFormat, validate and detect-changes green. Run Tests, Run Integration Tests and build-local were still pending when I reviewed.

Ruled out

  • Bitkit sends that skip the record: both on-chain send paths in the app (sendToAddressWithBroadcastResult/sendAllToAddressWithBroadcastResult, and prepareSend* then prepared.broadcast()) go through recording routes. Hardware-wallet sends are already gated out of Boost by isHardwareWallet.
  • PersistenceFailed from broadcast() misclassified by the Paykit "definitely not sent" check: ldk-node returns it before dispatch when the record write fails, so classifying it as not sent is correct.
  • Exhaustive NodeError switches elsewhere: every other switch over NodeError has a default. Errors.swift is the only exhaustive one, and the diff covers it.
  • New VSS dependency on the send path: the record goes to the node's existing KV store, which persist_all() already writes before every send, so sends gain no new failure mode.
  • Unconfirmed sends from before the upgrade cannot be boosted: deliberate, and listed under Out of Scope in the PR.

Merge confidence: 4/5, no findings, but the unit, integration and build jobs had not finished and manual tests 1 to 3 have not run on a device.

@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 251f596: adds journeys/boost/rbf-own-send.xml and journeys/boost/cpfp-receive.xml, the same Boost journeys synonymdev/bitkit-android#1706 adds, with iOS identifiers and commands. The QA notes now list them, and the upgrade from 0.7.0-rc.77 stays the only manual test.

@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed f0bca98: journeys/boost/rbf-own-send.xml now taps ContinueAmount and checks SendConfirm before swiping, matching synonymdev/bitkit-android#1706.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code scan: no HIGH or MEDIUM.

  • Every legitimate bump still works: RBF on your own sends (all app sends go through the prepared broadcast, including recovery re-preparation), CPFP on received transactions, and bumps after a restore, since the broadcast record lives in the VSS store.
  • Hardware-wallet sends and transfers stay excluded from Boost.
  • A counterparty-crafted RBF is refused with nothing queued.
  • Sends made before the upgrade (v3.0.0 ships ldk-node rc.74) can no longer be bumped, which the PR body acknowledges.

Performance-relevant: ldk-node rc.78 adds a synchronous VSS write (record_broadcast_txid) to every send and boost. That is one extra round trip normally, and up to 180 s of retries when VSS is degraded.

Device gate (partial): not run yet. Pending: boost/rbf-own-send.xml and boost/cpfp-receive.xml, and timing a send against master on the same network.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The code scan had no HIGH or MEDIUM, and the device gate passed.

Device gate: f0bca98, iPhone 17 simulator, regtest on staging, compared with master 68bede6.

  • boost/rbf-own-send.xml: pass. The fee went from 143 to 422, the TXID changed, RBFBoosted is listed, and it confirmed after mining.
  • boost/cpfp-receive.xml: pass. The Boost Fee was -2 190, the receive kept its TXID, CPFPBoosted is listed, and both confirmed after mining.
    • The success toast came after an 11 s sync and wasn't captured; the log confirms it with Boost transaction completed successfully.
    • The Boosting status wasn't seen because a shared staging block confirmed the parent first.
  • Negative check: an incoming unconfirmed transaction offers only CPFP, and an own send offers only RBF.
  • Perf: "Created new transaction" to "Successfully broadcast transaction" had a median of 112 ms against 59 ms on master, 6 sends each. The extra ~53 ms is the new ldk-node rc.78 broadcast-record write, and Android showed +65 ms on synonymdev/bitkit-android#1706. One run here took 2.9 s at that step with no log line in the gap.

⚠️ Not run: the case of a send broadcast before the upgrade. On Android it fails RBF with "no broadcast record" and the generic Boost Failed toast, which the PR lists as out of scope.

@ovitrif
ovitrif merged commit 682acde into master Oct 28, 2026
18 checks passed
@ovitrif
ovitrif deleted the fix/bump-own-txs-only branch October 28, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants