Skip to content

Commit e478ffb

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 0ed14ba commit e478ffb

12 files changed

Lines changed: 1838 additions & 1291 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

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

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

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

10181002
/// Sets the used storage directory path.
@@ -1859,33 +1843,19 @@ fn build_with_store_internal(
18591843
},
18601844
};
18611845

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

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

18901860
let promise_secret = {
18911861
let lsps_xpriv = derive_xprv(
@@ -1899,15 +1869,15 @@ fn build_with_store_internal(
18991869
lsc.lsps2_service.as_ref().map(|config| {
19001870
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
19011871
});
1872+
}
19021873

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

19121882
let msg_handler = match gossip_source.as_gossip_sync() {
19131883
GossipSync::P2P(p2p_gossip_sync) => MessageHandler {
@@ -1956,7 +1926,7 @@ fn build_with_store_internal(
19561926
}));
19571927
}
19581928

1959-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
1929+
liquidity_source.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager));
19601930

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

0 commit comments

Comments
 (0)