Skip to content

Refactor liquidity source to support multiple LSP nodes#792

Draft
Camillarhi wants to merge 1 commit intolightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Draft

Refactor liquidity source to support multiple LSP nodes#792
Camillarhi wants to merge 1 commit intolightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi
Copy link
Contributor

@Camillarhi Camillarhi commented Feb 12, 2026

Opening this as an early draft to get feedback on the overall API direction

The current setup ties you to a single LSP per protocol via set_liquidity_source_lsps1 / set_liquidity_source_lsps2. This refactor replaces that with a unified Vec<LspNode> model where LSP nodes are added via add_lsp() and protocol support is discovered at runtime through LSPS0 list_protocols. Multi-LSP support has been requested previously in #529.

  • Deprecated set_liquidity_source_lsps1 / set_liquidity_source_lsps2 in favor of add_lsp()
  • Replaced the per-protocol LSPS1Client / LSPS2Client with global pending request maps keyed by LSPSRequestId
  • Added LSPS0 protocol discovery with event handling for ListProtocolsResponse
  • Background discovery task spawns on Node::start()
  • LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically select the cheapest fee offer
  • Added request_channel_from_lsp() for explicit LSPS1 LSP selection
  • Updated event handling to use is_lsps_node() for multi-LSP counterparty checks

This sets the foundation for LSPS5 support currently being worked on in #729

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Feb 12, 2026

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

@Camillarhi
Copy link
Contributor Author

@tnull Early draft up for review, would appreciate feedback on the general API direction

@tnull tnull self-requested a review February 24, 2026 16:28
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Thanks! Some initial questions..

src/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Uh, no, I don't think we should just merge all into one. Especially given that we intend to add more logic on a per-spec basis, this will be will become even more confusing going forward. If anything, we should maybe start the refactoring by first moving the LSPS1/LSPS2 specific parts to src/liquidity/{lsps1,lsps2}.rs, or maybe even to client/service specific sub-modules like src/liquidity/{client,service}/{lsps1,lsps2}.rs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, I will move the LSPS1/LSPS2 specific parts

src/liquidity.rs Outdated
pub(crate) async fn lsps1_request_channel(
&self, lsp_balance_sat: u64, client_balance_sat: u64, channel_expiry_blocks: u32,
announce_channel: bool, refund_address: bitcoin::Address,
announce_channel: bool, refund_address: bitcoin::Address, node_id: &PublicKey,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I do wonder if it would make sense to create something like an struct ServiceProvider and move the API methods to there. Then, each registered LSP would have a corresponding ServiceProvider that exposes a bunch of public and internal APIs, which would make the modularization cleaner and would avoid having to give node_id everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I will have a look and see

@Camillarhi Camillarhi force-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55a Compare March 26, 2026 00:46
@Camillarhi Camillarhi requested a review from tnull March 26, 2026 00:47
@Camillarhi
Copy link
Contributor Author

Hello @tnull, apologies for the delay. This is ready for another round of review

Replace per-protocol single-LSP configuration `LSPS1Client, LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_lsp()` and protocol support is discovered at runtime via LSPS0
`list_protocols`.

- Replace separate `LSPS1Client/LSPS2Client` with global pending request
  maps keyed by `LSPSRequestId`
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
  handling for `ListProtocolsResponse`
- Update events to use is_lsps_node() for multi-LSP counterparty
  checks
- Deprecate `set_liquidity_source_lsps1/lsps2` builder methods in favor
  of `add_lsp()`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
  select the cheapest fee offer across all of them
- Add `request_channel_from_lsp()` for explicit LSPS1 LSP selection
- Spawn background discovery task on `Node::start()`
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Cool, already looks pretty good! Did a first higher-level pass, have yet to look into the details.

@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be good to structure this PR in a way that (as far as possible) makes any code moves dedicated commits that can be picked up by git diff --color-moved --patience, as otherwise reviewing this in detail will be very hard.

}
}

discovery_ls.discover_all_lsp_protocols().await;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it would be good to make this part of the background task above? Also, can we spawn the discovery tasks in parallel rather than doing them sequentially?

}

/// Configures the [`Node`] instance to source inbound liquidity from the given LSP at runtime,
/// without specifying the exact protocol used (e.g., LSPS1 or LSPS2).
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can drop the remark regarding 'without specifying the exact protocol' here and elsewhere, as the API already communicates that due to being generic. I do however wonder if we'd want to move this method to an API-extension object similar to what we do for the payment types? I.e., retrieve the API object via Node::liquidity()?

/// [`Bolt11Payment::receive_via_jit_channel`]: crate::payment::Bolt11Payment::receive_via_jit_channel
#[derive(Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
pub struct LSPS1Liquidity {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems this is currently not exposed in the API anymore?

/// The given `token` will be used by the LSP to authenticate the user.
///
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
#[deprecated(note = "Use `add_lsp` instead")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think so far we've been fine with just breaking the APIs without deprecating them first. If we find a better API I'd be fine with just dropping the old ones to clean up.

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.

3 participants