Skip to content

Support BOLT12 LSPS2 JIT-channel receives#990

Draft
tnull wants to merge 29 commits into
lightningdevkit:mainfrom
tnull:2026-07-bolt12-lsps2-ng
Draft

Support BOLT12 LSPS2 JIT-channel receives#990
tnull wants to merge 29 commits into
lightningdevkit:mainfrom
tnull:2026-07-bolt12-lsps2-ng

Conversation

@tnull

@tnull tnull commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

This adds LSPS2 JIT-channel support for long-lived BOLT12 offers without tying offers to expiring intercept SCIDs. LDK Node persists pending offers and safely reusable payment leases, restores sufficiently long-lived leases at startup, negotiates and replenishes them on demand, and consumes each lease at most once when responding to an invoice request. The same lease machinery is shared with BOLT11, including multi-LSP selection, retry and failover handling, and opening-fee validation.

Unlike #817 negotiated parameters are not embedded into an offer’s blinded message paths. Unlike #964 the implementation does not apply one globally cached parameter set to every BOLT12 receive (which is non-spec compliant and doesn't support fixed amount offers); leases are selected by LSP and payment mode or amount, consumed once, and refreshed independently while offers remain node-ID-addressed.

Based on https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/4809

@tnull tnull added this to the 0.8 milestone Jul 16, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Jul 16, 2026

Copy link
Copy Markdown

👋 Hi! This PR is now in draft status.
I'll wait to assign reviewers until you mark it as ready for review.
Just convert it out of draft status when you're ready for review!

@tnull
tnull marked this pull request as draft July 16, 2026 16:56
tnull added 27 commits July 16, 2026 18:57
Prepare the LSPS2 client for dedicated state and storage modules.

Co-Authored-By: HAL 9000
Track negotiated parameters as expiring leases so intercept

identifiers cannot be reused across payments.

Co-Authored-By: HAL 9000
Load cached leases in parallel at startup and remove entries that

are expired or too close to expiry for safe reuse.

Co-Authored-By: HAL 9000
Remove unusable leases hourly so persisted cache data cannot grow

without bound.

Co-Authored-By: HAL 9000
Persist negotiated parameters before making them available and consume

them before constructing existing BOLT11 invoices.

Co-Authored-By: HAL 9000
Prefer the cheapest eligible cached lease across configured LSPs and

negotiate only when the cache has no suitable parameters.

Co-Authored-By: HAL 9000
Delete an available lease from durable storage before removing its
in-memory copy. A failed store removal now leaves the lease available
for retry instead of allowing it to reappear only after restart.

Co-Authored-By: HAL 9000
Build against the Rust Lightning revision providing deferred BOLT12
invoice responses and LSPS2 routing support. Keep manual invoice
requests disabled until the node response handler lands.

Co-Authored-By: HAL 9000
Retain long-lived JIT offer descriptors across restarts so invoice
requests can negotiate fresh payment parameters independently of offer
creation.

Bound the registry with configurable LRU eviction and remove expired
entries during startup and periodic maintenance.

Co-Authored-By: HAL 9000
Build JIT offers with the standard node-addressed message paths and
persist only a receive descriptor keyed by OfferId. This keeps offer
validity independent from short-lived LSPS2 payment parameters and
avoids contacting an LSP during offer construction.

Co-Authored-By: HAL 9000
Move shared payment metadata out of the BOLT11 module.

This lets other protocols reuse it without a BOLT11 dependency.

Co-Authored-By: HAL 9000
Define metadata for single-use BOLT12 lease routing.

Retain fee limits for payment-claim validation.

Co-Authored-By: HAL 9000
Install the LSPS2-aware router in node construction.

Allow BOLT12 responses to append negotiated JIT paths.

Co-Authored-By: HAL 9000
Reuse one cached-or-negotiated lease path across invoice formats.

Keep BOLT11 selection and fee-limit behavior unchanged.

Co-Authored-By: HAL 9000
Consume one lease for each deferred JIT invoice response.

Preserve normal offer responses while LSPS2 negotiation runs in a
separate task.

Co-Authored-By: HAL 9000
Keep built-in service parameters usable beyond the client cache safety
margin so freshly negotiated leases are not rejected due to timing.

Co-Authored-By: HAL 9000
Read fee limits from BOLT12 payment context metadata and reject
unsupported withholding. Record accepted fees on inbound offer payments.

Co-Authored-By: HAL 9000
Make same-amount and variable callers wait for one in-flight LSPS2
request, then recheck the shared cache before negotiating.

Co-Authored-By: HAL 9000
Keep one usable lease ready after fixed or variable receive flows
consume cached parameters. Foreground callers share the refill lock
and reuse its result when it completes.

Co-Authored-By: HAL 9000
After startup discovery, schedule refills for persisted BOLT12 offer
descriptors. Reuse valid persisted leases and renegotiate only missing
or stale cache entries.

Co-Authored-By: HAL 9000
Retry transient LSPS2 request failures for foreground acquisition and
background cache refills. Preserve immediate errors for fee limits and
unavailable liquidity sources.

Co-Authored-By: HAL 9000
Keep JIT offers independent of ephemeral LSPS2 leases and available
when the receiver does not yet have any channels. Negotiate payment
paths only after an invoice request arrives.

Co-Authored-By: HAL 9000
Base proportional fee limits on the amount sent by the payer. The
claimable amount excludes the LSP's withheld fee and otherwise rejects
a valid fee at the configured limit.

Co-Authored-By: HAL 9000
Cover fixed and variable BOLT12 offers through a real LSPS2 service.
Verify node-ID offer addressing, fee withholding, fresh JIT channels,
and the variable-amount single-path behavior.

Co-Authored-By: HAL 9000
Extend the order-independent fee-selection scenario through a real
BOLT12 payment. Also prove that BOLT11 consumption and BOLT12 response
handling share the same replenished lease pool.

Co-Authored-By: HAL 9000
Rebuild the receiver from its persisted store and pay the same
long-lived offer again. Verify the pending offer survives and the exact
cached lease is consumed before its replacement is negotiated.

Co-Authored-By: HAL 9000
Restart the selected provider after offer creation and prove that the
variable-amount invoice request and JIT payment still complete. This
retains the restart coverage from the earlier integration approach.

Co-Authored-By: HAL 9000
tnull added 2 commits July 16, 2026 18:57
Try the next eligible provider when a selected LSP rejects or times out
during the buy request. Skip remaining fee-menu entries from a failed
provider while preserving bounded whole-round retries.

Co-Authored-By: HAL 9000
Pin the direct lease serialization format and ensure fixed-amount and
variable-amount caches remain isolated. These focused checks complement
the end-to-end BOLT11 and BOLT12 coverage.

Co-Authored-By: HAL 9000
@tnull
tnull force-pushed the 2026-07-bolt12-lsps2-ng branch from 251706d to 27c34ef Compare July 16, 2026 17:00
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