Skip to content

Add BOLT12 support to LSPS2 via custom Router implementation#4463

Open
tnull wants to merge 23 commits intolightningdevkit:mainfrom
tnull:2026-03-lsps2-bolt12-alt
Open

Add BOLT12 support to LSPS2 via custom Router implementation#4463
tnull wants to merge 23 commits intolightningdevkit:mainfrom
tnull:2026-03-lsps2-bolt12-alt

Conversation

@tnull
Copy link
Copy Markdown
Contributor

@tnull tnull commented Mar 5, 2026

Closes #4272.

This is an alternative approach to #4394 which leverages a custom Router implementation on the client side to inject the respective.

LDK Node integration PR over at lightningdevkit/ldk-node#817

@tnull tnull requested review from TheBlueMatt and jkczyz March 5, 2026 13:36
@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Mar 5, 2026

👋 Thanks for assigning @jkczyz 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.

@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 2cb0546 to 25ab3bc Compare March 5, 2026 14:05
@tnull tnull moved this to Goal: Merge in Weekly Goals Mar 5, 2026
@tnull tnull self-assigned this Mar 5, 2026
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 25ab3bc to 5786409 Compare March 24, 2026 14:34
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented Mar 24, 2026

Review Summary — PR #4463 (Re-review pass 6)

No new issues found in this review pass.

All issues identified in prior review passes have either been fixed or remain as previously-posted inline comments:

Previously Flagged Issues — Still Open (not re-posted)

These were posted as inline comments in prior review passes and remain applicable:

  1. lightning-liquidity/src/lsps2/router.rs:55 — Unused [Event::HTLCIntercepted] rustdoc link definition (defined but never referenced in doc comments)
  2. lightning-liquidity/src/lsps2/router.rs:70-73 — Redundant intercept_scid parameter alongside invoice_params.intercept_scid (mitigated by debug_assert_eq!)
  3. lightning/src/ln/channelmanager.rs:7595-7620 — Race condition window between pending_released_async_htlcs check and pending_intercepted_htlcs insert (HTLC can get stuck if handle_release_held_htlc runs between the two lock acquisitions; DROPME commit)

Issues Confirmed Fixed Since Last Review

  • cleanup_intercept_scids dead code and associated deadlock: removed
  • ? in loop discarding valid paths: now uses continue
  • Empty paths guard: added
  • from_context_data method: removed
  • Type mismatches in tests: fixed (u16 throughout)
  • Unused BestBlock import: removed
  • InvoiceRequestReceived even event number: event removed entirely
  • events/mod.rs grammar ("should always been set"): fixed to "should always be set"
  • messenger.rs "messager" typo: fixed
  • HashMap<PublicKey, u64> message path collision: MessageRouter impl removed

@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 5786409 to 98a9e9d Compare March 24, 2026 14:50
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch 2 times, most recently from 8800d48 to 7ca886d Compare March 24, 2026 15:14
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 77.89116% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.10%. Comparing base (12edb7d) to head (3ada94e).
⚠️ Report is 44 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/offers/flow.rs 26.31% 42 Missing ⚠️
lightning/src/events/mod.rs 20.00% 11 Missing and 1 partial ⚠️
lightning-liquidity/src/lsps2/router.rs 96.90% 1 Missing and 5 partials ⚠️
lightning/src/offers/invoice_request.rs 0.00% 3 Missing ⚠️
lightning/src/onion_message/messenger.rs 81.81% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4463      +/-   ##
==========================================
+ Coverage   86.20%   87.10%   +0.90%     
==========================================
  Files         160      164       +4     
  Lines      107545   109018    +1473     
  Branches   107545   109018    +1473     
==========================================
+ Hits        92707    94958    +2251     
+ Misses      12214    11570     -644     
+ Partials     2624     2490     -134     
Flag Coverage Δ
fuzzing 40.11% <0.00%> (?)
tests 86.19% <77.89%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 7ca886d to 2ff16d7 Compare March 25, 2026 08:23
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch 4 times, most recently from bcc4e10 to 5602e07 Compare March 25, 2026 12:27
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch 2 times, most recently from ea05389 to 3acf915 Compare March 25, 2026 13:24
@tnull
Copy link
Copy Markdown
Contributor Author

tnull commented Apr 6, 2026

Now included two commits that were necessary to make the async-payments-via-LSPS2-BOLT12-JIT-flow work. The latter commit is #4542 to make review independent/easier. Validated this works in async_payment_via_lsps2_jit_channel test over at lightningdevkit/ldk-node#817, though there we'll still have to handle persistence to actually allow peers to re-register the same intercept scids after restart.

Copy link
Copy Markdown
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

assuming it works on ldk node this seems like the right design to me.

Leave blinded onion-message routing to the application's normal MessageRouter integration so LSPS2 only overrides the HTLC path needed for JIT channels.

Co-Authored-By: HAL 9000
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 071aa74 to c37392e Compare April 7, 2026 13:22
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from c37392e to 5d148e9 Compare April 8, 2026 13:01
tnull added 8 commits April 8, 2026 15:09
Signed-off-by: Elias Rohrer <dev@tnull.de>
Describe how `InvoiceParametersReady` feeds both the existing `BOLT11`
route-hint flow and the new `LSPS2BOLT12Router` registration path for
`BOLT12` offers.

Co-Authored-By: HAL 9000
Exercise the LSPS2 buy flow and assert that a registered `OfferId`
produces a blinded payment path whose first forwarding hop uses the
negotiated intercept `SCID`.

Co-Authored-By: HAL 9000
Signed-off-by: Elias Rohrer <dev@tnull.de>
Allow tests to inject a custom `create_blinded_payment_paths` hook while
preserving the normal `ReceiveTlvs` bindings. This makes it possible to
exercise LSPS2-specific `BOLT12` path construction in integration tests.

Co-Authored-By: HAL 9000
Cover the full offer-payment flow from onion-message invoice exchange
through HTLC interception, JIT channel opening, and settlement. This
confirms the LSPS2 router and service handler work together in the
integrated path.

Co-Authored-By: HAL 9000
Keep exercising SCID-based InvoiceRequest interception while removing the now-unused message-router constructor wiring from the LSPS2 helper setup.

Co-Authored-By: HAL 9000
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 5d148e9 to 101a176 Compare April 8, 2026 13:10
tnull added 4 commits April 8, 2026 16:21
Test the full end-to-end flow of an async payment through an LSPS2 JIT
channel: static invoice server setup, LSPS2BOLT12Router registration,
async payment onion message exchange (HeldHtlcAvailable/ReleaseHeldHtlc),
HTLC interception, JIT channel open, and payment claim.

Co-Authored-By: HAL 9000
Signed-off-by: Elias Rohrer <dev@tnull.de>
To enable suppoort for async payments via LSPS2 JIT channels, we expose
explicit async receive-offer refresh and readiness waiting so
integrators can sequence external setup before relying on a ready async
offer, instead of polling timer ticks.

Generated with AI assistance.

Co-Authored-By: HAL 9000
The BOLT 7 wire format defines `cltv_expiry_delta` as a 2-byte field,
and LDK uses u16 for it everywhere (`RouteHintHop`, `ChannelUpdateInfo`,
`UnsignedChannelUpdate`). Align the LSPS2 types accordingly.

serde_json will reject values exceeding `u16::MAX` during
deserialization, so a counterparty sending an out-of-range value is
handled gracefully without a custom deserializer.

Co-Authored-By: HAL 9000
Signed-off-by: Elias Rohrer <dev@tnull.de>
Handle `ReleaseHeldHtlc` messages that arrive before the sender-side LSP
has even queued the held HTLC for onion decoding. Unlike lightningdevkit#4106, which
covers releases arriving after the HTLC is in `decode_update_add_htlcs`
but before it reaches `pending_intercepted_htlcs`, this preserves
releases that arrive one step earlier and would otherwise be dropped as
HTLC not found.

Co-Authored-By: HAL 9000
Signed-off-by: Elias Rohrer <dev@tnull.de>
@tnull tnull force-pushed the 2026-03-lsps2-bolt12-alt branch from 9f95a94 to b20cf2c Compare April 8, 2026 14:22
@tnull tnull requested review from TheBlueMatt and jkczyz April 8, 2026 14:22
@tnull
Copy link
Copy Markdown
Contributor Author

tnull commented Apr 8, 2026

Addressed pending comments. Let me know if I can squash. This/actually making it work in LDK Node is still blocked on #4542 (or rather the replacement for it). Let me know if you prefer me dropping the DROPME commit here, so that we can land this independently.

Copy link
Copy Markdown
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Yes, feel free to squash. IMO we should drop the last commit and land this to get it out of the way and we can land the fix commit separately.

// triggering channel open. We however also keep the inner paths so the payer can use
// pre-existing inbound liquidity when available rather than always triggering a JIT
// channel open. As BOLT12 specifies that paths should be ordered by preference, adding
// JIT-paths to the end of the list *should* have the payee prefer pre-existing channels.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL lol. We definitely don't do this and I cannot imagine anyone else has implemented a router with preference that dominates fee or success probabilities to the introduction point. Maybe we should do this at least for "introduction point the same", but in that case it shouldn't matter cause the LSP should use what's already open....in any case, it might be nice to document this a bit forcefully at the struct level but otherwise not much we can do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Goal: Merge

Development

Successfully merging this pull request may close these issues.

BOLT 12 support for bLIP-52/LSPS2

5 participants