Skip to content

Commit bc2dde3

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 41ab23f commit bc2dde3

15 files changed

Lines changed: 1733 additions & 1289 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: 63 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,35 @@ 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 accept 0-confirmation channels opened by this
449+
/// LSP. Note this supersedes [`Config::trusted_peers_0conf`] for this peer.
450+
///
451+
/// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored.
452452
///
453-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
454-
pub fn set_liquidity_source_lsps1(
453+
/// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md
454+
pub fn add_liquidity_source(
455455
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
456+
trust_peer_0conf: bool,
456457
) -> &mut Self {
457-
// Mark the LSP as trusted for 0conf
458-
self.config.trusted_peers_0conf.push(node_id.clone());
459-
460458
let liquidity_source_config =
461459
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-
}
466460

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());
461+
if liquidity_source_config.lsp_nodes.iter().any(|n| n.node_id == node_id) {
462+
return self;
463+
}
480464

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);
465+
liquidity_source_config.lsp_nodes.push(LspConfig {
466+
node_id,
467+
address,
468+
token,
469+
trust_peer_0conf,
470+
});
485471
self
486472
}
487473

@@ -491,12 +477,12 @@ impl NodeBuilder {
491477
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
492478
///
493479
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
494-
pub fn set_liquidity_provider_lsps2(
495-
&mut self, service_config: LSPS2ServiceConfig,
480+
pub fn enable_liquidity_provider(
481+
&mut self, lsps2_service_config: LSPS2ServiceConfig,
496482
) -> &mut Self {
497483
let liquidity_source_config =
498484
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
499-
liquidity_source_config.lsps2_service = Some(service_config);
485+
liquidity_source_config.lsps2_service = Some(lsps2_service_config);
500486
self
501487
}
502488

@@ -964,32 +950,28 @@ impl ArcedNodeBuilder {
964950
self.inner.write().expect("lock").set_pathfinding_scores_source(url);
965951
}
966952

967-
/// Configures the [`Node`] instance to source inbound liquidity from the given
968-
/// [bLIP-51 / LSPS1] service.
953+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
969954
///
970-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
955+
/// The node will discover the LSP's supported protocols (LSPS1/LSPS2) on startup via [bLIP-50 / LSPS0]
956+
/// and select the appropriate protocol per request automatically.
971957
///
972958
/// The given `token` will be used by the LSP to authenticate the user.
959+
/// `trust_peer_0conf` controls whether the node will accept 0-confirmation channels opened by this
960+
/// LSP. Note this supersedes [`Config::trusted_peers_0conf`] for this peer.
973961
///
974-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
975-
pub fn set_liquidity_source_lsps1(
976-
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
977-
) {
978-
self.inner.write().expect("lock").set_liquidity_source_lsps1(node_id, address, token);
979-
}
980-
981-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
982-
/// [bLIP-52 / LSPS2] service.
983-
///
984-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
985-
///
986-
/// The given `token` will be used by the LSP to authenticate the user.
962+
/// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored.
987963
///
988-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
989-
pub fn set_liquidity_source_lsps2(
964+
/// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md
965+
pub fn add_liquidity_source(
990966
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
967+
trust_peer_0conf: bool,
991968
) {
992-
self.inner.write().expect("lock").set_liquidity_source_lsps2(node_id, address, token);
969+
self.inner.write().expect("lock").add_liquidity_source(
970+
node_id,
971+
address,
972+
token,
973+
trust_peer_0conf,
974+
);
993975
}
994976

995977
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -998,8 +980,8 @@ impl ArcedNodeBuilder {
998980
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
999981
///
1000982
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
1001-
pub fn set_liquidity_provider_lsps2(&self, service_config: LSPS2ServiceConfig) {
1002-
self.inner.write().expect("lock").set_liquidity_provider_lsps2(service_config);
983+
pub fn enable_liquidity_provider(&self, lsps2_service_config: LSPS2ServiceConfig) {
984+
self.inner.write().expect("lock").enable_liquidity_provider(lsps2_service_config);
1003985
}
1004986

1005987
/// Sets the used storage directory path.
@@ -1846,33 +1828,19 @@ fn build_with_store_internal(
18461828
},
18471829
};
18481830

1849-
let (liquidity_source, custom_message_handler) =
1850-
if let Some(lsc) = liquidity_source_config.as_ref() {
1851-
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1852-
Arc::clone(&wallet),
1853-
Arc::clone(&channel_manager),
1854-
Arc::clone(&keys_manager),
1855-
Arc::clone(&tx_broadcaster),
1856-
Arc::clone(&kv_store),
1857-
Arc::clone(&config),
1858-
Arc::clone(&logger),
1859-
);
1860-
1861-
lsc.lsps1_client.as_ref().map(|config| {
1862-
liquidity_source_builder.lsps1_client(
1863-
config.node_id,
1864-
config.address.clone(),
1865-
config.token.clone(),
1866-
)
1867-
});
1831+
let (liquidity_source, custom_message_handler) = {
1832+
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1833+
Arc::clone(&wallet),
1834+
Arc::clone(&channel_manager),
1835+
Arc::clone(&keys_manager),
1836+
Arc::clone(&tx_broadcaster),
1837+
Arc::clone(&kv_store),
1838+
Arc::clone(&config),
1839+
Arc::clone(&logger),
1840+
);
18681841

1869-
lsc.lsps2_client.as_ref().map(|config| {
1870-
liquidity_source_builder.lsps2_client(
1871-
config.node_id,
1872-
config.address.clone(),
1873-
config.token.clone(),
1874-
)
1875-
});
1842+
if let Some(lsc) = liquidity_source_config.as_ref() {
1843+
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());
18761844

18771845
let promise_secret = {
18781846
let lsps_xpriv = derive_xprv(
@@ -1886,15 +1854,15 @@ fn build_with_store_internal(
18861854
lsc.lsps2_service.as_ref().map(|config| {
18871855
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
18881856
});
1857+
}
18891858

1890-
let liquidity_source = runtime
1891-
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1892-
let custom_message_handler =
1893-
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1894-
(Some(liquidity_source), custom_message_handler)
1895-
} else {
1896-
(None, Arc::new(NodeCustomMessageHandler::new_ignoring()))
1897-
};
1859+
let liquidity_source = runtime
1860+
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1861+
let custom_message_handler =
1862+
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1863+
1864+
(liquidity_source, custom_message_handler)
1865+
};
18981866

18991867
let msg_handler = match gossip_source.as_gossip_sync() {
19001868
GossipSync::P2P(p2p_gossip_sync) => MessageHandler {
@@ -1943,7 +1911,7 @@ fn build_with_store_internal(
19431911
}));
19441912
}
19451913

1946-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
1914+
liquidity_source.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager));
19471915

19481916
let connection_manager = Arc::new(ConnectionManager::new(
19491917
Arc::clone(&peer_manager),

0 commit comments

Comments
 (0)