Skip to content

Commit 4a959b3

Browse files
committed
Refactor liquidity source to support multiple LSP nodes
Replace per-protocol single-LSP configuration `LSPS1Client, LSPS2Client` with a unified `Vec<LspNode>` model where users configure LSP nodes via `add_lsp()` and protocol support is discovered at runtime via LSPS0 `list_protocols`. - Replace separate `LSPS1Client/LSPS2Client` with global pending request maps keyed by `LSPSRequestId` - Add LSPS0 protocol discovery `discover_lsp_protocols` with event handling for `ListProtocolsResponse` - Update events to use is_lsps_node() for multi-LSP counterparty checks - Deprecate `set_liquidity_source_lsps1/lsps2` builder methods in favor of `add_lsp()` - LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically select the cheapest fee offer across all of them - Add `request_channel_from_lsp()` for explicit LSPS1 LSP selection - Spawn background discovery task on `Node::start()`
1 parent 80fb49b commit 4a959b3

File tree

6 files changed

+452
-243
lines changed

6 files changed

+452
-243
lines changed

bindings/ldk_node.udl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ interface Builder {
116116
void set_pathfinding_scores_source(string url);
117117
void set_liquidity_source_lsps1(PublicKey node_id, SocketAddress address, string? token);
118118
void set_liquidity_source_lsps2(PublicKey node_id, SocketAddress address, string? token);
119+
void add_lsp(PublicKey node_id, SocketAddress address, string? token);
119120
void set_storage_dir_path(string storage_dir_path);
120121
void set_filesystem_logger(string? log_file_path, LogLevel? max_log_level);
121122
void set_log_facade_logger();
@@ -299,6 +300,8 @@ interface LSPS1Liquidity {
299300
[Throws=NodeError]
300301
LSPS1OrderStatus request_channel(u64 lsp_balance_sat, u64 client_balance_sat, u32 channel_expiry_blocks, boolean announce_channel);
301302
[Throws=NodeError]
303+
LSPS1OrderStatus request_channel_from_lsp(u64 lsp_balance_sat, u64 client_balance_sat, u32 channel_expiry_blocks, boolean announce_channel, PublicKey node_id);
304+
[Throws=NodeError]
302305
LSPS1OrderStatus check_order_status(LSPS1OrderId order_id);
303306
};
304307

src/builder.rs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ use crate::io::{
6565
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
6666
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
6767
};
68-
use crate::liquidity::{
69-
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
70-
};
68+
use crate::liquidity::{LSPS2ServiceConfig, LiquiditySourceBuilder, LspConfig};
7169
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7270
use crate::message_handler::NodeCustomMessageHandler;
7371
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
@@ -119,10 +117,8 @@ struct PathfindingScoresSyncConfig {
119117

120118
#[derive(Debug, Clone, Default)]
121119
struct LiquiditySourceConfig {
122-
// Act as an LSPS1 client connecting to the given service.
123-
lsps1_client: Option<LSPS1ClientConfig>,
124-
// Act as an LSPS2 client connecting to the given service.
125-
lsps2_client: Option<LSPS2ClientConfig>,
120+
// Acts for both LSPS1 and LSPS2 clients connecting to the given service.
121+
lsp_nodes: Vec<LspConfig>,
126122
// Act as an LSPS2 service.
127123
lsps2_service: Option<LSPS2ServiceConfig>,
128124
}
@@ -401,17 +397,12 @@ impl NodeBuilder {
401397
/// The given `token` will be used by the LSP to authenticate the user.
402398
///
403399
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
400+
#[deprecated(note = "Use `add_lsp` instead")]
401+
#[allow(dead_code)]
404402
pub fn set_liquidity_source_lsps1(
405403
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
406404
) -> &mut Self {
407-
// Mark the LSP as trusted for 0conf
408-
self.config.trusted_peers_0conf.push(node_id.clone());
409-
410-
let liquidity_source_config =
411-
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
412-
let lsps1_client_config = LSPS1ClientConfig { node_id, address, token };
413-
liquidity_source_config.lsps1_client = Some(lsps1_client_config);
414-
self
405+
self.add_lsp(node_id, address, token)
415406
}
416407

417408
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
@@ -422,16 +413,32 @@ impl NodeBuilder {
422413
/// The given `token` will be used by the LSP to authenticate the user.
423414
///
424415
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
416+
#[deprecated(note = "Use `add_lsp` instead")]
417+
#[allow(dead_code)]
425418
pub fn set_liquidity_source_lsps2(
426419
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
420+
) -> &mut Self {
421+
self.add_lsp(node_id, address, token)
422+
}
423+
424+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP, without specifying
425+
/// the exact protocol used (e.g., LSPS1 or LSPS2).
426+
///
427+
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
428+
///
429+
/// The given `token` will be used by the LSP to authenticate the user.
430+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
431+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
432+
/// appropriate protocol supported by the LSP.
433+
pub fn add_lsp(
434+
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
427435
) -> &mut Self {
428436
// Mark the LSP as trusted for 0conf
429-
self.config.trusted_peers_0conf.push(node_id.clone());
437+
self.config.trusted_peers_0conf.push(node_id);
430438

431439
let liquidity_source_config =
432440
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
433-
let lsps2_client_config = LSPS2ClientConfig { node_id, address, token };
434-
liquidity_source_config.lsps2_client = Some(lsps2_client_config);
441+
liquidity_source_config.lsp_nodes.push(LspConfig { node_id, address, token });
435442
self
436443
}
437444

@@ -824,7 +831,7 @@ impl ArcedNodeBuilder {
824831
pub fn set_liquidity_source_lsps1(
825832
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
826833
) {
827-
self.inner.write().unwrap().set_liquidity_source_lsps1(node_id, address, token);
834+
self.inner.write().unwrap().add_lsp(node_id, address, token);
828835
}
829836

830837
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
@@ -838,7 +845,20 @@ impl ArcedNodeBuilder {
838845
pub fn set_liquidity_source_lsps2(
839846
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
840847
) {
841-
self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token);
848+
self.inner.write().unwrap().add_lsp(node_id, address, token);
849+
}
850+
851+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP, without specifying
852+
/// the exact protocol used (e.g., LSPS1 or LSPS2).
853+
///
854+
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
855+
///
856+
/// The given `token` will be used by the LSP to authenticate the user.
857+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
858+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
859+
/// appropriate protocol supported by the LSP.
860+
pub fn add_lsp(&self, node_id: PublicKey, address: SocketAddress, token: Option<String>) {
861+
self.inner.write().unwrap().add_lsp(node_id, address, token);
842862
}
843863

844864
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -1598,21 +1618,7 @@ fn build_with_store_internal(
15981618
Arc::clone(&logger),
15991619
);
16001620

1601-
lsc.lsps1_client.as_ref().map(|config| {
1602-
liquidity_source_builder.lsps1_client(
1603-
config.node_id,
1604-
config.address.clone(),
1605-
config.token.clone(),
1606-
)
1607-
});
1608-
1609-
lsc.lsps2_client.as_ref().map(|config| {
1610-
liquidity_source_builder.lsps2_client(
1611-
config.node_id,
1612-
config.address.clone(),
1613-
config.token.clone(),
1614-
)
1615-
});
1621+
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());
16161622

16171623
let promise_secret = {
16181624
let lsps_xpriv = derive_xprv(

src/event.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,12 +1221,8 @@ where
12211221
let user_channel_id: u128 = rng().random();
12221222
let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id);
12231223
let mut channel_override_config = None;
1224-
if let Some((lsp_node_id, _)) = self
1225-
.liquidity_source
1226-
.as_ref()
1227-
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())
1228-
{
1229-
if lsp_node_id == counterparty_node_id {
1224+
if let Some(ls) = self.liquidity_source.as_ref() {
1225+
if ls.as_ref().is_lsps_node(&counterparty_node_id) {
12301226
// When we're an LSPS2 client, allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
12311227
// check that they don't take too much before claiming.
12321228
//

src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,29 @@ impl Node {
661661
});
662662
}
663663

664+
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
665+
let discovery_ls = Arc::clone(&liquidity_source);
666+
let discovery_cm = Arc::clone(&self.connection_manager);
667+
let discovery_logger = Arc::clone(&self.logger);
668+
self.runtime.spawn_background_task(async move {
669+
for (node_id, address) in discovery_ls.get_all_lsp_details() {
670+
if let Err(e) =
671+
discovery_cm.connect_peer_if_necessary(node_id, address.clone()).await
672+
{
673+
log_error!(
674+
discovery_logger,
675+
"Failed to connect to LSP {} for protocol discovery: {}",
676+
node_id,
677+
e
678+
);
679+
continue;
680+
}
681+
}
682+
683+
discovery_ls.discover_all_lsp_protocols().await;
684+
});
685+
}
686+
664687
log_info!(self.logger, "Startup complete.");
665688
*is_running_lock = true;
666689
Ok(())

0 commit comments

Comments
 (0)