Add BOLT12 support to LSPS2 via custom Router implementation#4463
Add BOLT12 support to LSPS2 via custom Router implementation#4463tnull wants to merge 11 commits intolightningdevkit:mainfrom
Router implementation#4463Conversation
|
👋 Thanks for assigning @jkczyz 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
|
After thorough re-review of the entire PR diff and the affected files in the codebase, I've compared all findings against my 20+ prior inline comments. The vast majority of previously flagged issues have been resolved in this revision:
No new bugs, security vulnerabilities, or logic errors found in this pass. Review Summary — PR #4463 (Re-review pass 8)No new issues found. Previously Flagged Issues — StatusAll significant bugs from prior passes have been resolved. Two minor nits remain from prior passes (already posted, not re-posted):
|
5786409 to
98a9e9d
Compare
8800d48 to
7ca886d
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4463 +/- ##
==========================================
+ Coverage 84.55% 86.99% +2.43%
==========================================
Files 135 164 +29
Lines 76569 109094 +32525
Branches 76569 109094 +32525
==========================================
+ Hits 64745 94909 +30164
- Misses 9783 11703 +1920
- Partials 2041 2482 +441
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
071aa74 to
c37392e
Compare
5d148e9 to
101a176
Compare
733d3f2 to
9f95a94
Compare
9f95a94 to
b20cf2c
Compare
|
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. |
TheBlueMatt
left a comment
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Ah, I assumed we did, esp. given Jeff brought it up above ^^
We extend the `OnionMessenger` capabilities to also intercept onion messages if they are for unknown SCIDs. 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
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
b20cf2c to
bf0ead8
Compare
Squashed commits, reworded some commit messages to reflect the current state. |
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>
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
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>
bf0ead8 to
ff32073
Compare
|
|
||
| /// An identifier for an [`Offer`] built using [`DerivedMetadata`]. | ||
| #[derive(Clone, Copy, Eq, PartialEq)] | ||
| #[derive(Clone, Copy, Eq, Hash, PartialEq)] |
| /// For **payment** blinded paths (in invoices), it returns the intercept SCID as the forwarding | ||
| /// hop so that the LSP can intercept the HTLC and open a JIT channel. | ||
| /// For **payment** blinded paths (in invoices), it appends paths using the intercept SCID as the | ||
| /// forwarding hop so that the LSP can intercept the HTLC and open a JIT channel. Paths from the | ||
| /// inner router (e.g., through pre-existing channels) are included as well, allowing payers to | ||
| /// use existing inbound liquidity when available. |
There was a problem hiding this comment.
Seeing this in f538153. Should it belong in an earlier commit?
| pub struct TestMessageRouter<'a> { | ||
| pub inner: TestMessageRouterInternal<'a>, | ||
| pub peers_override: Mutex<Vec<PublicKey>>, | ||
| pub forward_node_scid_override: Mutex<HashMap<PublicKey, u64>>, |
There was a problem hiding this comment.
Is the TestMessageRouter changed needed anymore?
| /// Override closure type for [`TestRouter::override_create_blinded_payment_paths`]. | ||
| /// | ||
| /// This closure is called instead of the default [`Router::create_blinded_payment_paths`] | ||
| /// implementation when set, receiving the actual [`ReceiveTlvs`] so tests can construct custom | ||
| /// blinded payment paths using the same TLVs the caller generated. | ||
| pub type BlindedPaymentPathOverrideFn = Box< | ||
| dyn Fn( | ||
| PublicKey, | ||
| ReceiveAuthKey, | ||
| Vec<ChannelDetails>, | ||
| ReceiveTlvs, | ||
| Option<u64>, | ||
| ) -> Result<Vec<BlindedPaymentPath>, ()> | ||
| + Send | ||
| + Sync, | ||
| >; |
There was a problem hiding this comment.
Hmm... maybe we can have TestRouter wrap a dyn Router instead and provide a way to override it?
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