Skip to content

Commit 6337ea4

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 03da893 commit 6337ea4

12 files changed

Lines changed: 1775 additions & 1281 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 {
@@ -279,6 +278,7 @@ dictionary LSPS1OrderStatus {
279278
LSPS1OrderParams order_params;
280279
LSPS1PaymentInfo payment_options;
281280
LSPS1ChannelInfo? channel_state;
281+
PublicKey counterparty_node_id;
282282
};
283283

284284
[Remote]

src/builder.rs

Lines changed: 54 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ use crate::io::{
6969
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
7070
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
7171
};
72-
use crate::liquidity::{
73-
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
74-
};
72+
use crate::liquidity::{LSPS2ServiceConfig, LiquiditySourceBuilder, LspConfig};
7573
use crate::lnurl_auth::LnurlAuth;
7674
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7775
use crate::message_handler::NodeCustomMessageHandler;
@@ -124,10 +122,8 @@ struct PathfindingScoresSyncConfig {
124122

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

447-
/// Configures the [`Node`] instance to source inbound liquidity from the given
448-
/// [bLIP-51 / LSPS1] service.
449-
///
450-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
443+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP, without specifying
444+
/// the exact protocol used (e.g., LSPS1 or LSPS2).
451445
///
452446
/// The given `token` will be used by the LSP to authenticate the user.
453-
///
454-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
455-
pub fn set_liquidity_source_lsps1(
447+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
448+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
449+
/// appropriate protocol supported by the LSP.
450+
pub fn add_liquidity_source(
456451
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
452+
trust_peer_0conf: bool,
457453
) -> &mut Self {
458-
// Mark the LSP as trusted for 0conf
459-
self.config.trusted_peers_0conf.push(node_id.clone());
460-
461454
let liquidity_source_config =
462455
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
463-
let lsps1_client_config = LSPS1ClientConfig { node_id, address, token };
464-
liquidity_source_config.lsps1_client = Some(lsps1_client_config);
465-
self
466-
}
467-
468-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
469-
/// [bLIP-52 / LSPS2] service.
470-
///
471-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
472-
///
473-
/// The given `token` will be used by the LSP to authenticate the user.
474-
///
475-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
476-
pub fn set_liquidity_source_lsps2(
477-
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
478-
) -> &mut Self {
479-
// Mark the LSP as trusted for 0conf
480-
self.config.trusted_peers_0conf.push(node_id.clone());
481-
482-
let liquidity_source_config =
483-
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
484-
let lsps2_client_config = LSPS2ClientConfig { node_id, address, token };
485-
liquidity_source_config.lsps2_client = Some(lsps2_client_config);
456+
liquidity_source_config.lsp_nodes.push(LspConfig {
457+
node_id,
458+
address,
459+
token,
460+
trust_peer_0conf,
461+
});
486462
self
487463
}
488464

@@ -492,12 +468,12 @@ impl NodeBuilder {
492468
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
493469
///
494470
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
495-
pub fn set_liquidity_provider_lsps2(
496-
&mut self, service_config: LSPS2ServiceConfig,
471+
pub fn enable_liquidity_provider(
472+
&mut self, lsps2_service_config: LSPS2ServiceConfig,
497473
) -> &mut Self {
498474
let liquidity_source_config =
499475
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
500-
liquidity_source_config.lsps2_service = Some(service_config);
476+
liquidity_source_config.lsps2_service = Some(lsps2_service_config);
501477
self
502478
}
503479

@@ -965,32 +941,24 @@ impl ArcedNodeBuilder {
965941
self.inner.write().expect("lock").set_pathfinding_scores_source(url);
966942
}
967943

968-
/// Configures the [`Node`] instance to source inbound liquidity from the given
969-
/// [bLIP-51 / LSPS1] service.
970-
///
971-
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
972-
///
973-
/// The given `token` will be used by the LSP to authenticate the user.
974-
///
975-
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
976-
pub fn set_liquidity_source_lsps1(
977-
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
978-
) {
979-
self.inner.write().expect("lock").set_liquidity_source_lsps1(node_id, address, token);
980-
}
981-
982-
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
983-
/// [bLIP-52 / LSPS2] service.
944+
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
984945
///
985946
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
986947
///
987948
/// The given `token` will be used by the LSP to authenticate the user.
988-
///
989-
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
990-
pub fn set_liquidity_source_lsps2(
949+
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
950+
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
951+
/// appropriate protocol supported by the LSP.
952+
pub fn add_liquidity_source(
991953
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
954+
trust_peer_0conf: bool,
992955
) {
993-
self.inner.write().expect("lock").set_liquidity_source_lsps2(node_id, address, token);
956+
self.inner.write().expect("lock").add_liquidity_source(
957+
node_id,
958+
address,
959+
token,
960+
trust_peer_0conf,
961+
);
994962
}
995963

996964
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -999,8 +967,8 @@ impl ArcedNodeBuilder {
999967
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
1000968
///
1001969
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
1002-
pub fn set_liquidity_provider_lsps2(&self, service_config: LSPS2ServiceConfig) {
1003-
self.inner.write().expect("lock").set_liquidity_provider_lsps2(service_config);
970+
pub fn enable_liquidity_provider(&self, lsps2_service_config: LSPS2ServiceConfig) {
971+
self.inner.write().expect("lock").enable_liquidity_provider(lsps2_service_config);
1004972
}
1005973

1006974
/// Sets the used storage directory path.
@@ -1857,33 +1825,19 @@ fn build_with_store_internal(
18571825
},
18581826
};
18591827

1860-
let (liquidity_source, custom_message_handler) =
1861-
if let Some(lsc) = liquidity_source_config.as_ref() {
1862-
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1863-
Arc::clone(&wallet),
1864-
Arc::clone(&channel_manager),
1865-
Arc::clone(&keys_manager),
1866-
Arc::clone(&tx_broadcaster),
1867-
Arc::clone(&kv_store),
1868-
Arc::clone(&config),
1869-
Arc::clone(&logger),
1870-
);
1871-
1872-
lsc.lsps1_client.as_ref().map(|config| {
1873-
liquidity_source_builder.lsps1_client(
1874-
config.node_id,
1875-
config.address.clone(),
1876-
config.token.clone(),
1877-
)
1878-
});
1828+
let (liquidity_source, custom_message_handler) = {
1829+
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1830+
Arc::clone(&wallet),
1831+
Arc::clone(&channel_manager),
1832+
Arc::clone(&keys_manager),
1833+
Arc::clone(&tx_broadcaster),
1834+
Arc::clone(&kv_store),
1835+
Arc::clone(&config),
1836+
Arc::clone(&logger),
1837+
);
18791838

1880-
lsc.lsps2_client.as_ref().map(|config| {
1881-
liquidity_source_builder.lsps2_client(
1882-
config.node_id,
1883-
config.address.clone(),
1884-
config.token.clone(),
1885-
)
1886-
});
1839+
if let Some(lsc) = liquidity_source_config.as_ref() {
1840+
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());
18871841

18881842
let promise_secret = {
18891843
let lsps_xpriv = derive_xprv(
@@ -1897,15 +1851,15 @@ fn build_with_store_internal(
18971851
lsc.lsps2_service.as_ref().map(|config| {
18981852
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
18991853
});
1854+
}
19001855

1901-
let liquidity_source = runtime
1902-
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1903-
let custom_message_handler =
1904-
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1905-
(Some(liquidity_source), custom_message_handler)
1906-
} else {
1907-
(None, Arc::new(NodeCustomMessageHandler::new_ignoring()))
1908-
};
1856+
let liquidity_source = runtime
1857+
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
1858+
let custom_message_handler =
1859+
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1860+
1861+
(liquidity_source, custom_message_handler)
1862+
};
19091863

19101864
let msg_handler = match gossip_source.as_gossip_sync() {
19111865
GossipSync::P2P(p2p_gossip_sync) => MessageHandler {
@@ -1949,7 +1903,7 @@ fn build_with_store_internal(
19491903
*guard = Some(Arc::downgrade(&peer_manager));
19501904
}
19511905

1952-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
1906+
liquidity_source.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager));
19531907

19541908
let connection_manager = Arc::new(ConnectionManager::new(
19551909
Arc::clone(&peer_manager),

0 commit comments

Comments
 (0)