Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Refactor liquidity source to support multiple LSP nodes by Camillarhi · Pull Request #792 · lightningdevkit/ldk-node · GitHub
Skip to content

Refactor liquidity source to support multiple LSP nodes - #792

Merged
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support
Jun 12, 2026
Merged

Refactor liquidity source to support multiple LSP nodes#792
tnull merged 9 commits into
lightningdevkit:mainfrom
Camillarhi:multi-lsp-support

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Feb 12, 2026

Copy link
Copy Markdown

👋 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
Copy Markdown
ContributorAuthor

@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

@tnulltnull left a comment

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.

Thanks! Some initial questions..

Comment threadsrc/builder.rs Outdated
Comment threadsrc/liquidity.rs Outdated
supported_protocols: Mutex::new(None),
})
.collect(),
pending_lsps1_opening_params_requests: Mutex::new(HashMap::new()),

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.

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
Copy Markdown
ContributorAuthor

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

Comment threadsrc/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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 3cda7f7 to 8bbf55aCompareMarch 26, 2026 00:46
@Camillarhi
Camillarhi requested a review from tnullMarch 26, 2026 00:47
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

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

@tnulltnull left a comment

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.

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

Comment threadsrc/liquidity.rs
@@ -1,1542 +0,0 @@
// This file is Copyright its original authors, visible in version control history.

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.

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.

Comment threadsrc/lib.rs Outdated
}
}

discovery_ls.discover_all_lsp_protocols().await;

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.

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?

Comment threadsrc/lib.rs Outdated
}

/// 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
Copy Markdown
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()?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks. I will remove the remark and move this to a Node::liquidity() API-extension

Comment threadsrc/liquidity/client/lsps1.rs Outdated
/// [`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
Copy Markdown
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?

Comment threadsrc/builder.rs Outdated
/// 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
Copy Markdown
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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 6061c6d to a560764CompareMarch 30, 2026 16:32
@Camillarhi
Camillarhi requested a review from tnullMarch 30, 2026 16:32

@tnulltnull left a comment

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.

Cool. Docs CI is currently failing.

Would be great if you try once more to breakup the second commit into smaller chunks, but let me know if it's too cumbersome/impossible.

Feel free to mark ready for review then.

@Camillarhi
Camillarhi marked this pull request as ready for review March 31, 2026 16:07
@Camillarhi
Camillarhi requested a review from tnullMarch 31, 2026 16:45
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@valentinewallace! 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 @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull@valentinewallace! 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

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull@valentinewallace! 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

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull@valentinewallace! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 22da16c to 2cda868CompareApril 9, 2026 15:16
@valentinewallace
valentinewallace removed their request for review April 9, 2026 15:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! 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

🔔 10th Reminder

Hey @tnull! 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

🔔 11th Reminder

Hey @tnull! 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.

@Camillarhi
Camillarhiforce-pushed the multi-lsp-support branch 2 times, most recently from 43c5ccc to 7ceb385CompareJune 2, 2026 12:12
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! 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

🔔 13th Reminder

Hey @tnull! 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

🔔 14th Reminder

Hey @tnull! 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

🔔 15th Reminder

Hey @tnull! 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.

Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.
- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
builder methods with a single `add_liquidity_source()` that takes a
`trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.

@tnulltnull left a comment

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.

Thanks! Excuse the considerable delay here! I'm going ahead landing this, we can address / revisit minor things in follow-ups (or as part of #900 for instance).

Comment threadsrc/event.rs
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())

// If the peer is a configured LSP node, additionally honor its trust_peer_0conf flag.
if let Some(lsp) =

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.

Codex:

  • [P1] trust_peer_0conf is only honored for LSPS2-discovered LSPs: /home/tnull/worktrees/ldk-node/pr-792/src/event.rs:1323. add_liquidity_source(..., true) documents generic 0-conf trust for the LSP (/home/tnull/worktrees/ldk-node/pr-792/src/builder.rs:448, /home/tnull/worktrees/ldk-node/pr-
    792/src/liquidity/mod.rs:117), but inbound acceptance only looks up protocol 2. LSPS1-only LSPs, or trusted LSPs before/after failed LSPS0 discovery, now fall through to accept_inbound_channel instead of accept_inbound_channel_from_trusted_peer. The trust lookup should be by node id
    independent of protocol; keep the LSPS2 protocol check only for accept_underpaying_htlcs.

where
L::Target: Logger,
{
Ignoring,

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.

Hmm, in conjunction with #900 we might want to revisit this, i.e., whether we'll want to allow to disable LSP functionalities based on a feature entirely.

pub(crate) mod lsps1;
pub(crate) mod lsps2;

pub use lsps1::LSPS1OrderStatus;

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.

nit:

  • [P3] Three new files have CRLF line endings, so git diff --check upstream/main...HEAD reports trailing whitespace on every line: /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/client/mod.rs:1, /home/tnull/worktrees/ldk-node/pr-792/src/liquidity/service/mod.rs:1, /home/tnull/worktrees/
    ldk-node/pr-792/src/liquidity/service/lsps2.rs:1. cargo fmt tolerates it, but diff whitespace checks will fail. Normalize those files to LF.

@tnull
tnull merged commit 926c18a into lightningdevkit:mainJun 12, 2026
@tnull

Copy link
Copy Markdown
Collaborator

Now documented them over at #936.

@tnulltnull mentioned this pull request Jun 12, 2026
@Camillarhi

Copy link
Copy Markdown
ContributorAuthor

Thanks for landing it, and no worries about the delay! I'll pick up the follow-ups in #936

Sign up for freeto 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.

4 participants

@Camillarhi@ldk-reviews-bot@tnull@f3r10