Skip to content

Add inbound BOLT12 recurrence tracking - #1205

Open
shaavan wants to merge 7 commits into
lightningdevkit:mainfrom
shaavan:inbound-recurrence
Open

shaavan wants to merge 7 commits into
lightningdevkit:mainfrom
shaavan:inbound-recurrence

Conversation

@shaavan

@shaavan shaavan commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Overview

This PR adds receiver-side tracking for recurring BOLT 12 payments to LDK Node, based on lightning/bolts#1240.

rust-lightning handles the recurrence protocol and provides the offer terms and invoice timing through payment events. LDK Node saves each offer-and-payer relationship, decides whether an incoming payment can be accepted, and records its outcome.

This prevents different payments from claiming the same counter and lets applications inspect recurrence state.

Scope

  • Persistent state: Save recurrence terms, timing, status, pending reservations, and completed payments separately from individual payment records.
  • Payment acceptance: Reserve the expected counter before claiming funds. Reject competing payments and new payments for cancelled recurrences. Leave invoice-request window checks to LDK.
  • Successful payments and replay: Record success only on PaymentClaimed. Recognize repeated confirmations without advancing twice or changing newer progress and reservations. Return ReplayEvent when a write fails.
  • Cancellation: Persist cancellation under the same lock as acceptance. Block new payments while allowing an already accepted payment to finish without clearing the cancelled status.
  • Lookup and listing: Expose saved recurrence details through Rust APIs that return copies without allowing applications to change stored state.
  • Tests: Add focused coverage for storage, APIs, acceptance, payment progress, replay, concurrent claims, and cancellation in separate test commits.

Deferred work

The following work is deferred to follow-up PRs.

  • Restart reconciliation: Recover unfinished reservations when no event is available to replay. This requires reconciling recurrence records, payment records, and restored LDK state.
  • Failed-payment reservation release: Allow another attempt only after the whole inbound payment is known to have failed. A single failed HTLC is not enough proof to release its counter.
  • Missed-window tracking: Add scheduling to mark recurrences missed when their payment window closes.
  • Preventing future invoice issuance: Integrate cancellation with LDK’s earlier invoice-request handling. The current implementation controls payment acceptance after an invoice has been issued.
  • UniFFI exposure: Add language bindings as part of the shared bindings work.

Update LDK Node's Cargo.toml to use recurrence-aware versions of
rust-lightning and bitcoin-payment-instructions.

This makes the recurrence APIs available for implementing recurrence
support in subsequent commits.

Co-authored-by: GLaDOS
Inbound recurrence tracking needs durable relationship state separate
from individual payments. Persist terms, status, timing, and payment
progress for each offer and payer signing public key pair.

Co-Authored-By: GLaDOS
@ldk-reviews-bot

ldk-reviews-bot commented Oct 23, 2026 •

Copy link
Copy Markdown

I've assigned @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

Verify that the builder restores pending ownership, ordered payment
history, and inactive relationships through the public lookup APIs.
Changing either the offer or payer must select a separate relationship.
Seed snapshots directly so this coverage needs no claim event handling.

Co-Authored-By: GLaDOS
Reserve each counter for one payment before claiming it. Move to the
next counter only after LDK confirms success. Record rejected payments
as failed where safe, leaving accepted payment records unchanged.

This prevents two payments from claiming the same counter and keeps
the saved payment status consistent with acceptance or rejection.

Co-Authored-By: GLaDOS
Check context validation, counter ownership, confirmed progress, and
historical replay without disturbing a newer pending payment. Exercise
absolute period limits and overflow rather than only counter limits.

Force competing reservations to overlap at persistence. Inject failures
at each claim write boundary and rebuild from saved bytes before retrying,
so partial writes cannot silently release ownership or advance twice.

Co-Authored-By: GLaDOS
Mark the existing recurrence as cancelled under the same lock used
to accept payments. Preserve any accepted payment so it can finish,
and keep the cancelled status when recording its success.

This blocks new payments after cancellation without interrupting
a payment that was already accepted.

Co-Authored-By: GLaDOS
Check that cancellation blocks a new reservation while allowing an
already accepted payment to finish without reactivating the relationship.
Repeated notifications must preserve ownership and successful history.

Cover unknown relationships and failed cancellation writes so the event
remains replayable until cancellation can be saved safely.

Co-Authored-By: GLaDOS
@shaavan
shaavan force-pushed the inbound-recurrence branch from 275f3d8 to 2a30d2b Compare October 23, 2026 17:29
@shaavan
shaavan marked this pull request as ready for review October 23, 2026 17:30
@ldk-reviews-bot
ldk-reviews-bot requested a review from tnull October 23, 2026 17:31
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.

2 participants