Skip to content

Commit 0ed44a6

Browse files
committed
Refactor liquidity source to support multiple LSP nodes
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
1 parent c16340f commit 0ed44a6

12 files changed

Lines changed: 1838 additions & 1293 deletions

File tree

bindings/ldk_node.udl

Lines changed: 4 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_liquidity_source(PublicKey node_id, SocketAddress address, string? token, boolean trust_peer_0conf);
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]
@@ -165,7 +164,7 @@ interface FeeRate {
165164

166165
typedef interface UnifiedPayment;
167166

168-
typedef interface LSPS1Liquidity;
167+
typedef interface Liquidity;
169168

170169
[Error]
171170
enum NodeError {
@@ -273,6 +272,7 @@ dictionary LSPS1OrderStatus {
273272
LSPS1OrderParams order_params;
274273
LSPS1PaymentInfo payment_options;
275274
LSPS1ChannelInfo? channel_state;
275+
PublicKey counterparty_node_id;
276276
};
277277

278278
[Remote]

src/builder.rs

Lines changed: 65 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ use crate::io::{
6868
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
6969
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
7070
};
71-
use crate::liquidity::{
72-
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
73-
};
71+
use crate::liquidity::{LSPS2ServiceConfig, LiquiditySourceBuilder, LspConfig};
7472
use crate::lnurl_auth::LnurlAuth;
7573
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7674
use crate::message_handler::NodeCustomMessageHandler;
@@ -123,10 +121,8 @@ struct PathfindingScoresSyncConfig {
123121

124122
#[derive(Debug, Clone, Default)]
125123
struct LiquiditySourceConfig {
126-
// Act as an LSPS1 client connecting to the given service.
127-
lsps1_client: Option<LSPS1ClientConfig>,
128-
// Act as an LSPS2 client connecting to the given service.
129-
lsps2_client: Option<LSPS2ClientConfig>,
124+
// Acts for both LSPS1 and LSPS2 clients connecting to the given service.
125+
lsp_nodes: Vec<LspConfig>,
130126
// Act as an LSPS2 service.
131127
lsps2_service: Option<LSPS2ServiceConfig>,
132128
}
@@ -443,45 +439,36 @@ impl NodeBuilder {
443439
self
444440
}
445441

446-
/// Configures the [`Node`] instance to source inbound liquidity from the given
447-
/// [bLIP-51 / LSPS1] service.
442+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
448443
///
449-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
444+
/// The node will discover the LSP's supported protocols (LSPS1/LSPS2) on startup via [bLIP-50 / LSPS0]
445+
/// and select the appropriate protocol per request automatically.
450446
///
451447
/// The given `token` will be used by the LSP to authenticate the user.
448+
/// `trust_peer_0conf` controls whether the node will additionally accept
449+
/// 0-confirmation channels opened by this LSP. If `false`, 0-confirmation
450+
/// acceptance for this peer falls back to [`Config::trusted_peers_0conf`].
451+
///
452+
/// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored.
452453
///
453-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
454-
pub fn set_liquidity_source_lsps1(
454+
/// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md
455+
pub fn add_liquidity_source(
455456
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
457+
trust_peer_0conf: bool,
456458
) -> &mut Self {
457-
// Mark the LSP as trusted for 0conf
458-
self.config.trusted_peers_0conf.push(node_id.clone());
459-
460459
let liquidity_source_config =
461460
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
462-
let lsps1_client_config = LSPS1ClientConfig { node_id, address, token };
463-
liquidity_source_config.lsps1_client = Some(lsps1_client_config);
464-
self
465-
}
466461

467-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
468-
/// [bLIP-52 / LSPS2] service.
469-
///
470-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
471-
///
472-
/// The given `token` will be used by the LSP to authenticate the user.
473-
///
474-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
475-
pub fn set_liquidity_source_lsps2(
476-
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
477-
) -> &mut Self {
478-
// Mark the LSP as trusted for 0conf
479-
self.config.trusted_peers_0conf.push(node_id.clone());
462+
if liquidity_source_config.lsp_nodes.iter().any(|n| n.node_id == node_id) {
463+
return self;
464+
}
480465

481-
let liquidity_source_config =
482-
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
483-
let lsps2_client_config = LSPS2ClientConfig { node_id, address, token };
484-
liquidity_source_config.lsps2_client = Some(lsps2_client_config);
466+
liquidity_source_config.lsp_nodes.push(LspConfig {
467+
node_id,
468+
address,
469+
token,
470+
trust_peer_0conf,
471+
});
485472
self
486473
}
487474

@@ -491,12 +478,12 @@ impl NodeBuilder {
491478
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
492479
///
493480
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
494-
pub fn set_liquidity_provider_lsps2(
495-
&mut self, service_config: LSPS2ServiceConfig,
481+
pub fn enable_liquidity_provider(
482+
&mut self, lsps2_service_config: LSPS2ServiceConfig,
496483
) -> &mut Self {
497484
let liquidity_source_config =
498485
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
499-
liquidity_source_config.lsps2_service = Some(service_config);
486+
liquidity_source_config.lsps2_service = Some(lsps2_service_config);
500487
self
501488
}
502489

@@ -978,32 +965,29 @@ impl ArcedNodeBuilder {
978965
self.inner.write().expect("lock").set_pathfinding_scores_source(url);
979966
}
980967

981-
/// Configures the [`Node`] instance to source inbound liquidity from the given
982-
/// [bLIP-51 / LSPS1] service.
968+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
983969
///
984-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
970+
/// The node will discover the LSP's supported protocols (LSPS1/LSPS2) on startup via [bLIP-50 / LSPS0]
971+
/// and select the appropriate protocol per request automatically.
985972
///
986973
/// The given `token` will be used by the LSP to authenticate the user.
974+
/// `trust_peer_0conf` controls whether the node will additionally accept
975+
/// 0-confirmation channels opened by this LSP. If `false`, 0-confirmation
976+
/// acceptance for this peer falls back to [`Config::trusted_peers_0conf`].
987977
///
988-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
989-
pub fn set_liquidity_source_lsps1(
990-
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
991-
) {
992-
self.inner.write().expect("lock").set_liquidity_source_lsps1(node_id, address, token);
993-
}
994-
995-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
996-
/// [bLIP-52 / LSPS2] service.
997-
///
998-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
999-
///
1000-
/// The given `token` will be used by the LSP to authenticate the user.
978+
/// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored.
1001979
///
1002-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
1003-
pub fn set_liquidity_source_lsps2(
980+
/// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md
981+
pub fn add_liquidity_source(
1004982
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
983+
trust_peer_0conf: bool,
1005984
) {
1006-
self.inner.write().expect("lock").set_liquidity_source_lsps2(node_id, address, token);
985+
self.inner.write().expect("lock").add_liquidity_source(
986+
node_id,
987+
address,
988+
token,
989+
trust_peer_0conf,
990+
);
1007991
}
1008992

1009993
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -1012,8 +996,8 @@ impl ArcedNodeBuilder {
1012996
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
1013997
///
1014998
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
1015-
pub fn set_liquidity_provider_lsps2(&self, service_config: LSPS2ServiceConfig) {
1016-
self.inner.write().expect("lock").set_liquidity_provider_lsps2(service_config);
999+
pub fn enable_liquidity_provider(&self, lsps2_service_config: LSPS2ServiceConfig) {
1000+
self.inner.write().expect("lock").enable_liquidity_provider(lsps2_service_config);
10171001
}
10181002

10191003
/// Sets the used storage directory path.
@@ -1860,33 +1844,19 @@ fn build_with_store_internal(
18601844
},
18611845
};
18621846

1863-
let (liquidity_source, custom_message_handler) =
1864-
if let Some(lsc) = liquidity_source_config.as_ref() {
1865-
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1866-
Arc::clone(&wallet),
1867-
Arc::clone(&channel_manager),
1868-
Arc::clone(&keys_manager),
1869-
Arc::clone(&tx_broadcaster),
1870-
Arc::clone(&kv_store),
1871-
Arc::clone(&config),
1872-
Arc::clone(&logger),
1873-
);
1874-
1875-
lsc.lsps1_client.as_ref().map(|config| {
1876-
liquidity_source_builder.lsps1_client(
1877-
config.node_id,
1878-
config.address.clone(),
1879-
config.token.clone(),
1880-
)
1881-
});
1847+
let (liquidity_source, custom_message_handler) = {
1848+
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1849+
Arc::clone(&wallet),
1850+
Arc::clone(&channel_manager),
1851+
Arc::clone(&keys_manager),
1852+
Arc::clone(&tx_broadcaster),
1853+
Arc::clone(&kv_store),
1854+
Arc::clone(&config),
1855+
Arc::clone(&logger),
1856+
);
18821857

1883-
lsc.lsps2_client.as_ref().map(|config| {
1884-
liquidity_source_builder.lsps2_client(
1885-
config.node_id,
1886-
config.address.clone(),
1887-
config.token.clone(),
1888-
)
1889-
});
1858+
if let Some(lsc) = liquidity_source_config.as_ref() {
1859+
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());
18901860

18911861
let promise_secret = {
18921862
let lsps_xpriv = derive_xprv(
@@ -1900,15 +1870,15 @@ fn build_with_store_internal(
19001870
lsc.lsps2_service.as_ref().map(|config| {
19011871
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
19021872
});
1873+
}
19031874

1904-
let liquidity_source = runtime
1905-
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1906-
let custom_message_handler =
1907-
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1908-
(Some(liquidity_source), custom_message_handler)
1909-
} else {
1910-
(None, Arc::new(NodeCustomMessageHandler::new_ignoring()))
1911-
};
1875+
let liquidity_source = runtime
1876+
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1877+
let custom_message_handler =
1878+
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1879+
1880+
(liquidity_source, custom_message_handler)
1881+
};
19121882

19131883
let msg_handler = match gossip_source.as_gossip_sync() {
19141884
GossipSync::P2P(p2p_gossip_sync) => MessageHandler {
@@ -1957,7 +1927,7 @@ fn build_with_store_internal(
19571927
}));
19581928
}
19591929

1960-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
1930+
liquidity_source.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager));
19611931

19621932
let connection_manager = Arc::new(ConnectionManager::new(
19631933
Arc::clone(&peer_manager),

0 commit comments

Comments
 (0)