Add BOLT12 support to LSPS2 via custom Router implementation#4463
Add BOLT12 support to LSPS2 via custom Router implementation#4463tnull wants to merge 21 commits intolightningdevkit:mainfrom
Router implementation#4463Conversation
|
👋 Thanks for assigning @TheBlueMatt as a reviewer! |
2cb0546 to
25ab3bc
Compare
|
🔔 1st Reminder Hey @jkczyz! This PR has been waiting for your review. |
|
🔔 2nd Reminder Hey @jkczyz! This PR has been waiting for your review. |
25ab3bc to
5786409
Compare
|
I've completed a thorough review of the current state of the diff. All critical bugs identified in prior review passes (12 passes) have been confirmed fixed in this revision. The code is in good shape. Review Summary — PR #4463 (Review pass 13)No new bugs or security issues found. All critical issues from prior passes are confirmed resolved. Verified fixes from prior passes
Previously posted nits still applicable (not reposted per instructions)
Changelog improvementThe backward compatibility note ( No new inline comments posted this pass. |
5786409 to
98a9e9d
Compare
8800d48 to
7ca886d
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4463 +/- ##
==========================================
+ Coverage 87.08% 87.10% +0.01%
==========================================
Files 161 162 +1
Lines 109255 109716 +461
Branches 109255 109716 +461
==========================================
+ Hits 95147 95564 +417
- Misses 11627 11669 +42
- Partials 2481 2483 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
7ca886d to
2ff16d7
Compare
bcc4e10 to
5602e07
Compare
ea05389 to
3acf915
Compare
|
🔔 1st Reminder Hey @TheBlueMatt! This PR has been waiting for your review. |
|
🔔 2nd Reminder Hey @TheBlueMatt! This PR has been waiting for your review. |
|
🔔 3rd Reminder Hey @TheBlueMatt! This PR has been waiting for your review. |
| pub fn register_intercept_scid( | ||
| &self, intercept_scid: u64, invoice_params: LSPS2Bolt12InvoiceParameters, | ||
| ) -> Option<LSPS2Bolt12InvoiceParameters> { | ||
| debug_assert_eq!(intercept_scid, invoice_params.intercept_scid); |
There was a problem hiding this comment.
lol why on earth are we providing both just to assert that they're the same?
There was a problem hiding this comment.
Because I found it preferable to still communicate that the parameters will be keyed by intercept SCID, especially since we also want to remove them by intercept SCID. And dropping the intercept SCID from the parameters is also not great.
We extend the `OnionMessenger` capabilities to also intercept onion messages if they are for unknown SCIDs. Co-Authored-By: HAL 9000
Add an `intercept_for_unknown_scids: bool` parameter to `OnionMessenger::new_with_offline_peer_interception`. When false, we preserve the pre-0.3 behavior of dropping onion messages whose next hop is a `ShortChannelId` that cannot be resolved. When true, we generate an `Event::OnionMessageIntercepted` with a `NextMessageHop::ShortChannelId` next hop. This protects downgrade compatibility for users persisting `OnionMessageIntercepted` events who may need to roll back to LDK 0.2, since the `ShortChannelId` variant of the event is not deserializable there. Co-Authored-By: HAL 9000
Replace the `debug_assert!(false, ...)` + `Ok(None)` branch in `Event::OnionMessageIntercepted` deserialization with a plain `ok_or(DecodeError::InvalidValue)?`. Returning an error on malformed input matches the conventions used elsewhere in this file and avoids silently dropping events that were written with neither a `peer_node_id` nor a `next_hop` TLV. Co-Authored-By: HAL 9000
Add `intercept_unknown_scid_oms` test that verifies the `OnionMessenger` correctly generates `OnionMessageIntercepted` events with a `ShortChannelId` next hop when a blinded path uses an unresolvable SCID. This complements the existing `intercept_offline_peer_oms` test which only covers the `NodeId` variant (offline peer case). Co-Authored-By: HAL 9000
The unknown-SCID interception is now gated on `intercept_for_unknown_scids`, so the test must opt in via a new `with_unknown_scid_interception` helper. Co-Authored-By: HAL 9000
Add backwards compatibility tests for `Event::OnionMessageIntercepted` serialization to verify that: - Events serialized by LDK 0.2 (with `peer_node_id` in TLV field 0) can be deserialized by the current version as `NextMessageHop::NodeId`. - Events with `NodeId` next hop serialized by the current version can be deserialized by LDK 0.2 (which reads `peer_node_id` from field 0). - Events with `ShortChannelId` next hop (which omit TLV field 0) correctly fail to deserialize in LDK 0.2, since the `peer_node_id` field is required there. Co-Authored-By: HAL 9000
Introduce `LSPS2BOLT12Router` to allow registering LSPS2 invoice parameters and build blinded payment paths through the negotiated intercept SCIDs. All other routing behavior still delegates to the wrapped router. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer <dev@tnull.de>
Intercept SCIDs are only unique on a per-node basis, so two different peers can legitimately hand out the same SCID. Keying the registration map on the SCID alone would let the second registration silently overwrite the first. Co-Authored-By: HAL 9000
The bounds were never required by the struct or its Router impl. Co-Authored-By: HAL 9000
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 registered LSPS2 parameters produce 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
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
Replace the blocking `await_async_receive_offer(Duration)` with an `async fn await_async_receive_offer(&self) -> Result<Offer, ()>` and a new `get_async_receive_offer_ready_future() -> Future` helper. Async callers can `.await` the offer directly; synchronous callers can fetch the underlying [`Future`] and call `wait_timeout` on it, which lets them combine with whatever timing primitive their runtime provides. Co-Authored-By: HAL 9000
`Notifier`, `Future`, and `.await` are all available in no-std; only `Future::wait_timeout` requires std. Stop gating the notifier field and callers on `std`, and also revert the incidental blank-line removals that crept into the async-receive-offer refresh changes. Co-Authored-By: HAL 9000
Now that `get_async_receive_offer_ready_future` and `await_async_receive_offer` are available in no-std, their rustdoc intra-doc links to `Future::wait_timeout` (which is itself still std-only) break the no-std rustdoc build. Wrap the sentence and reference definition that mention `Future::wait_timeout` in `cfg_attr(feature = "std", doc = ...)` so the links only appear when they can resolve. 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. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer <dev@tnull.de>
Reword the backwards-compatibility note to call out that the downgrade risk is limited to users who (1) manually persist `Event::OnionMessageIntercepted` events and (2) opt in to the new `intercept_for_unknown_scids` flag on `OnionMessenger::new_with_offline_peer_interception`. LDK itself does not persist these events, so anyone who doesn't persist them manually is unaffected regardless of the flag. Co-Authored-By: HAL 9000
a0bcfcf to
0cef8dc
Compare
|
Addressed pending comments, also had to rebase as we now had a minor conflict. Let me know if I can squash. |
Closes #4272.
This is an alternative approach to #4394 which leverages a custom
Routerimplementation on the client side to inject the respective.LDK Node integration PR over at lightningdevkit/ldk-node#817