Skip to content

Commit c430e51

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 67e6f0e commit c430e51

File tree

12 files changed

+1945
-1455
lines changed

12 files changed

+1945
-1455
lines changed

bindings/ldk_node.udl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ interface Builder {
4343
void set_gossip_source_p2p();
4444
void set_gossip_source_rgs(string rgs_server_url);
4545
void set_pathfinding_scores_source(string url);
46-
void set_liquidity_source_lsps1(PublicKey node_id, SocketAddress address, string? token);
47-
void set_liquidity_source_lsps2(PublicKey node_id, SocketAddress address, string? token);
46+
void add_lsp(PublicKey node_id, SocketAddress address, string? token);
4847
void set_storage_dir_path(string storage_dir_path);
4948
void set_filesystem_logger(string? log_file_path, LogLevel? max_log_level);
5049
void set_log_facade_logger();
@@ -97,7 +96,7 @@ interface Node {
9796
SpontaneousPayment spontaneous_payment();
9897
OnchainPayment onchain_payment();
9998
UnifiedPayment unified_payment();
100-
LSPS1Liquidity lsps1_liquidity();
99+
Liquidity liquidity();
101100
[Throws=NodeError]
102101
void lnurl_auth(string lnurl);
103102
[Throws=NodeError]
@@ -161,7 +160,7 @@ interface FeeRate {
161160

162161
typedef interface UnifiedPayment;
163162

164-
typedef interface LSPS1Liquidity;
163+
typedef interface Liquidity;
165164

166165
[Error]
167166
enum NodeError {

src/builder.rs

Lines changed: 21 additions & 74 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::lnurl_auth::LnurlAuth;
7270
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7371
use crate::message_handler::NodeCustomMessageHandler;
@@ -120,10 +118,8 @@ struct PathfindingScoresSyncConfig {
120118

121119
#[derive(Debug, Clone, Default)]
122120
struct LiquiditySourceConfig {
123-
// Act as an LSPS1 client connecting to the given service.
124-
lsps1_client: Option<LSPS1ClientConfig>,
125-
// Act as an LSPS2 client connecting to the given service.
126-
lsps2_client: Option<LSPS2ClientConfig>,
121+
// Acts for both LSPS1 and LSPS2 clients connecting to the given service.
122+
lsp_nodes: Vec<LspConfig>,
127123
// Act as an LSPS2 service.
128124
lsps2_service: Option<LSPS2ServiceConfig>,
129125
}
@@ -432,45 +428,24 @@ impl NodeBuilder {
432428
self
433429
}
434430

435-
/// Configures the [`Node`] instance to source inbound liquidity from the given
436-
/// [bLIP-51 / LSPS1] service.
437-
///
438-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
439-
///
440-
/// The given `token` will be used by the LSP to authenticate the user.
441-
///
442-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
443-
pub fn set_liquidity_source_lsps1(
444-
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
445-
) -> &mut Self {
446-
// Mark the LSP as trusted for 0conf
447-
self.config.trusted_peers_0conf.push(node_id.clone());
448-
449-
let liquidity_source_config =
450-
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
451-
let lsps1_client_config = LSPS1ClientConfig { node_id, address, token };
452-
liquidity_source_config.lsps1_client = Some(lsps1_client_config);
453-
self
454-
}
455-
456-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
457-
/// [bLIP-52 / LSPS2] service.
431+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP, without specifying
432+
/// the exact protocol used (e.g., LSPS1 or LSPS2).
458433
///
459434
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
460435
///
461436
/// The given `token` will be used by the LSP to authenticate the user.
462-
///
463-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
464-
pub fn set_liquidity_source_lsps2(
437+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
438+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
439+
/// appropriate protocol supported by the LSP.
440+
pub fn add_lsp(
465441
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
466442
) -> &mut Self {
467443
// Mark the LSP as trusted for 0conf
468-
self.config.trusted_peers_0conf.push(node_id.clone());
444+
self.config.trusted_peers_0conf.push(node_id);
469445

470446
let liquidity_source_config =
471447
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
472-
let lsps2_client_config = LSPS2ClientConfig { node_id, address, token };
473-
liquidity_source_config.lsps2_client = Some(lsps2_client_config);
448+
liquidity_source_config.lsp_nodes.push(LspConfig { node_id, address, token });
474449
self
475450
}
476451

@@ -953,32 +928,16 @@ impl ArcedNodeBuilder {
953928
self.inner.write().unwrap().set_pathfinding_scores_source(url);
954929
}
955930

956-
/// Configures the [`Node`] instance to source inbound liquidity from the given
957-
/// [bLIP-51 / LSPS1] service.
931+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
958932
///
959933
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
960934
///
961935
/// The given `token` will be used by the LSP to authenticate the user.
962-
///
963-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
964-
pub fn set_liquidity_source_lsps1(
965-
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
966-
) {
967-
self.inner.write().unwrap().set_liquidity_source_lsps1(node_id, address, token);
968-
}
969-
970-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
971-
/// [bLIP-52 / LSPS2] service.
972-
///
973-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
974-
///
975-
/// The given `token` will be used by the LSP to authenticate the user.
976-
///
977-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
978-
pub fn set_liquidity_source_lsps2(
979-
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
980-
) {
981-
self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token);
936+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
937+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
938+
/// appropriate protocol supported by the LSP.
939+
pub fn add_lsp(&self, node_id: PublicKey, address: SocketAddress, token: Option<String>) {
940+
self.inner.write().unwrap().add_lsp(node_id, address, token);
982941
}
983942

984943
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -1802,21 +1761,7 @@ fn build_with_store_internal(
18021761
Arc::clone(&logger),
18031762
);
18041763

1805-
lsc.lsps1_client.as_ref().map(|config| {
1806-
liquidity_source_builder.lsps1_client(
1807-
config.node_id,
1808-
config.address.clone(),
1809-
config.token.clone(),
1810-
)
1811-
});
1812-
1813-
lsc.lsps2_client.as_ref().map(|config| {
1814-
liquidity_source_builder.lsps2_client(
1815-
config.node_id,
1816-
config.address.clone(),
1817-
config.token.clone(),
1818-
)
1819-
});
1764+
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());
18201765

18211766
let promise_secret = {
18221767
let lsps_xpriv = derive_xprv(
@@ -1885,7 +1830,9 @@ fn build_with_store_internal(
18851830
}
18861831
}));
18871832

1888-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
1833+
liquidity_source
1834+
.as_ref()
1835+
.map(|l| l.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager)));
18891836

18901837
let connection_manager = Arc::new(ConnectionManager::new(
18911838
Arc::clone(&peer_manager),

src/event.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -581,15 +581,15 @@ where
581581
Ok(final_tx) => {
582582
let needs_manual_broadcast =
583583
self.liquidity_source.as_ref().map_or(false, |ls| {
584-
ls.as_ref().lsps2_channel_needs_manual_broadcast(
584+
ls.as_ref().lsps2_service().lsps2_channel_needs_manual_broadcast(
585585
counterparty_node_id,
586586
user_channel_id,
587587
)
588588
});
589589

590590
let result = if needs_manual_broadcast {
591591
self.liquidity_source.as_ref().map(|ls| {
592-
ls.lsps2_store_funding_transaction(
592+
ls.lsps2_service().lsps2_store_funding_transaction(
593593
user_channel_id,
594594
counterparty_node_id,
595595
final_tx.clone(),
@@ -653,7 +653,8 @@ where
653653
},
654654
LdkEvent::FundingTxBroadcastSafe { user_channel_id, counterparty_node_id, .. } => {
655655
self.liquidity_source.as_ref().map(|ls| {
656-
ls.lsps2_funding_tx_broadcast_safe(user_channel_id, counterparty_node_id);
656+
ls.lsps2_service()
657+
.lsps2_funding_tx_broadcast_safe(user_channel_id, counterparty_node_id);
657658
});
658659
},
659660
LdkEvent::PaymentClaimable {
@@ -1139,7 +1140,10 @@ where
11391140
LdkEvent::ProbeFailed { .. } => {},
11401141
LdkEvent::HTLCHandlingFailed { failure_type, .. } => {
11411142
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
1142-
liquidity_source.handle_htlc_handling_failed(failure_type).await;
1143+
liquidity_source
1144+
.lsps2_service()
1145+
.handle_htlc_handling_failed(failure_type)
1146+
.await;
11431147
}
11441148
},
11451149
LdkEvent::SpendableOutputs { outputs, channel_id, counterparty_node_id } => {
@@ -1238,14 +1242,15 @@ where
12381242
let user_channel_id: u128 = u128::from_ne_bytes(
12391243
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
12401244
);
1241-
let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id);
1242-
let mut channel_override_config = None;
1243-
if let Some((lsp_node_id, _)) = self
1245+
let is_lsp_node = self
12441246
.liquidity_source
12451247
.as_ref()
1246-
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())
1247-
{
1248-
if lsp_node_id == counterparty_node_id {
1248+
.map_or(false, |ls| ls.as_ref().is_lsps_node(&counterparty_node_id));
1249+
let allow_0conf =
1250+
self.config.trusted_peers_0conf.contains(&counterparty_node_id) || is_lsp_node;
1251+
let mut channel_override_config = None;
1252+
if let Some(ls) = self.liquidity_source.as_ref() {
1253+
if ls.as_ref().is_lsps_node(&counterparty_node_id) {
12491254
// When we're an LSPS2 client, allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
12501255
// check that they don't take too much before claiming.
12511256
//
@@ -1390,6 +1395,7 @@ where
13901395
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
13911396
let skimmed_fee_msat = skimmed_fee_msat.unwrap_or(0);
13921397
liquidity_source
1398+
.lsps2_service()
13931399
.handle_payment_forwarded(Some(next_htlc.channel_id), skimmed_fee_msat)
13941400
.await;
13951401
}
@@ -1499,6 +1505,7 @@ where
14991505

15001506
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
15011507
liquidity_source
1508+
.lsps2_service()
15021509
.handle_channel_ready(user_channel_id, &channel_id, &counterparty_node_id)
15031510
.await;
15041511
}
@@ -1570,6 +1577,7 @@ where
15701577
} => {
15711578
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
15721579
liquidity_source
1580+
.lsps2_service()
15731581
.handle_htlc_intercepted(
15741582
requested_next_hop_scid,
15751583
intercept_id,

src/lib.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ use lightning_background_processor::process_events_async;
160160
pub use lightning_invoice;
161161
pub use lightning_liquidity;
162162
pub use lightning_types;
163-
use liquidity::{LSPS1Liquidity, LiquiditySource};
163+
use liquidity::LiquiditySource;
164164
use lnurl_auth::LnurlAuth;
165165
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
166166
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
@@ -180,6 +180,7 @@ use types::{
180180
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
181181
pub use vss_client;
182182

183+
use crate::liquidity::Liquidity;
183184
use crate::scoring::setup_background_pathfinding_scores_sync;
184185
use crate::wallet::FundingAmount;
185186

@@ -658,7 +659,51 @@ impl Node {
658659
let mut stop_liquidity_handler = self.stop_sender.subscribe();
659660
let liquidity_handler = Arc::clone(&liquidity_source);
660661
let liquidity_logger = Arc::clone(&self.logger);
662+
let discovery_ls = Arc::clone(&liquidity_source);
663+
let discovery_cm = Arc::clone(&self.connection_manager);
661664
self.runtime.spawn_background_task(async move {
665+
let discovery_logger = Arc::clone(&liquidity_logger);
666+
tokio::spawn(async move {
667+
let mut set = tokio::task::JoinSet::new();
668+
for (node_id, address) in discovery_ls.get_all_lsp_details() {
669+
let cm = Arc::clone(&discovery_cm);
670+
let logger = Arc::clone(&discovery_logger);
671+
let ls = Arc::clone(&discovery_ls);
672+
set.spawn(async move {
673+
if let Err(e) =
674+
cm.connect_peer_if_necessary(node_id, address.clone()).await
675+
{
676+
log_error!(
677+
logger,
678+
"Failed to connect to LSP {} for protocol discovery: {}",
679+
node_id,
680+
e
681+
);
682+
return;
683+
}
684+
match ls.discover_lsp_protocols(&node_id).await {
685+
Ok(protocols) => {
686+
log_info!(
687+
logger,
688+
"Discovered protocols for LSP {}: {:?}",
689+
node_id,
690+
protocols
691+
);
692+
},
693+
Err(e) => {
694+
log_error!(
695+
logger,
696+
"Failed to discover protocols for LSP {}: {:?}",
697+
node_id,
698+
e
699+
);
700+
},
701+
}
702+
});
703+
}
704+
while set.join_next().await.is_some() {}
705+
});
706+
662707
loop {
663708
tokio::select! {
664709
_ = stop_liquidity_handler.changed() => {
@@ -1039,12 +1084,10 @@ impl Node {
10391084
})
10401085
}
10411086

1042-
/// Returns a liquidity handler allowing to request channels via the [bLIP-51 / LSPS1] protocol.
1043-
///
1044-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
1087+
/// Returns a liquidity handler allowing to manage LSP connections and request channels.
10451088
#[cfg(not(feature = "uniffi"))]
1046-
pub fn lsps1_liquidity(&self) -> LSPS1Liquidity {
1047-
LSPS1Liquidity::new(
1089+
pub fn liquidity(&self) -> Liquidity {
1090+
Liquidity::new(
10481091
Arc::clone(&self.runtime),
10491092
Arc::clone(&self.wallet),
10501093
Arc::clone(&self.connection_manager),
@@ -1053,12 +1096,10 @@ impl Node {
10531096
)
10541097
}
10551098

1056-
/// Returns a liquidity handler allowing to request channels via the [bLIP-51 / LSPS1] protocol.
1057-
///
1058-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
1099+
/// Returns a liquidity handler allowing to manage LSP connections and request channels.
10591100
#[cfg(feature = "uniffi")]
1060-
pub fn lsps1_liquidity(&self) -> Arc<LSPS1Liquidity> {
1061-
Arc::new(LSPS1Liquidity::new(
1101+
pub fn liquidity(&self) -> Arc<Liquidity> {
1102+
Arc::new(Liquidity::new(
10621103
Arc::clone(&self.runtime),
10631104
Arc::clone(&self.wallet),
10641105
Arc::clone(&self.connection_manager),

0 commit comments

Comments
 (0)