Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ interface Builder {
void set_gossip_source_p2p();
void set_gossip_source_rgs(string rgs_server_url);
void set_pathfinding_scores_source(string url);
void set_liquidity_source_lsps1(PublicKey node_id, SocketAddress address, string? token);
void set_liquidity_source_lsps2(PublicKey node_id, SocketAddress address, string? token);
void add_lsp(PublicKey node_id, SocketAddress address, string? token);
void set_storage_dir_path(string storage_dir_path);
void set_filesystem_logger(string? log_file_path, LogLevel? max_log_level);
void set_log_facade_logger();
Expand Down Expand Up @@ -97,7 +96,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
UnifiedPayment unified_payment();
LSPS1Liquidity lsps1_liquidity();
Liquidity liquidity();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand Down Expand Up @@ -161,7 +160,7 @@ interface FeeRate {

typedef interface UnifiedPayment;

typedef interface LSPS1Liquidity;
typedef interface Liquidity;

[Error]
enum NodeError {
Expand Down
95 changes: 21 additions & 74 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ use crate::io::{
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
use crate::liquidity::{
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
};
use crate::liquidity::{LSPS2ServiceConfig, LiquiditySourceBuilder, LspConfig};
use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
Expand Down Expand Up @@ -120,10 +118,8 @@ struct PathfindingScoresSyncConfig {

#[derive(Debug, Clone, Default)]
struct LiquiditySourceConfig {
// Act as an LSPS1 client connecting to the given service.
lsps1_client: Option<LSPS1ClientConfig>,
// Act as an LSPS2 client connecting to the given service.
lsps2_client: Option<LSPS2ClientConfig>,
// Acts for both LSPS1 and LSPS2 clients connecting to the given service.
lsp_nodes: Vec<LspConfig>,
// Act as an LSPS2 service.
lsps2_service: Option<LSPS2ServiceConfig>,
}
Expand Down Expand Up @@ -432,45 +428,24 @@ impl NodeBuilder {
self
}

/// Configures the [`Node`] instance to source inbound liquidity from the given
/// [bLIP-51 / LSPS1] service.
///
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
///
/// The given `token` will be used by the LSP to authenticate the user.
///
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
pub fn set_liquidity_source_lsps1(
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
) -> &mut Self {
// Mark the LSP as trusted for 0conf
self.config.trusted_peers_0conf.push(node_id.clone());

let liquidity_source_config =
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
let lsps1_client_config = LSPS1ClientConfig { node_id, address, token };
liquidity_source_config.lsps1_client = Some(lsps1_client_config);
self
}

/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
/// [bLIP-52 / LSPS2] service.
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP, without specifying
/// the exact protocol used (e.g., LSPS1 or LSPS2).
///
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
///
/// The given `token` will be used by the LSP to authenticate the user.
///
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
pub fn set_liquidity_source_lsps2(
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
/// appropriate protocol supported by the LSP.
pub fn add_lsp(
&mut self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
) -> &mut Self {
// Mark the LSP as trusted for 0conf
self.config.trusted_peers_0conf.push(node_id.clone());
self.config.trusted_peers_0conf.push(node_id);

let liquidity_source_config =
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
let lsps2_client_config = LSPS2ClientConfig { node_id, address, token };
liquidity_source_config.lsps2_client = Some(lsps2_client_config);
liquidity_source_config.lsp_nodes.push(LspConfig { node_id, address, token });
self
}

Expand Down Expand Up @@ -953,32 +928,16 @@ impl ArcedNodeBuilder {
self.inner.write().unwrap().set_pathfinding_scores_source(url);
}

/// Configures the [`Node`] instance to source inbound liquidity from the given
/// [bLIP-51 / LSPS1] service.
/// Configures the [`Node`] instance to source inbound liquidity from the given LSP.
///
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
///
/// The given `token` will be used by the LSP to authenticate the user.
///
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
pub fn set_liquidity_source_lsps1(
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
) {
self.inner.write().unwrap().set_liquidity_source_lsps1(node_id, address, token);
}

/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
/// [bLIP-52 / LSPS2] service.
///
/// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
///
/// The given `token` will be used by the LSP to authenticate the user.
///
/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
pub fn set_liquidity_source_lsps2(
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
) {
self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token);
/// This method is useful when the user wants to connect to an LSP but does not want to be concerned with
/// the specific protocol used for liquidity provision. The node will automatically detect and use the
/// appropriate protocol supported by the LSP.
pub fn add_lsp(&self, node_id: PublicKey, address: SocketAddress, token: Option<String>) {
self.inner.write().unwrap().add_lsp(node_id, address, token);
}

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

lsc.lsps1_client.as_ref().map(|config| {
liquidity_source_builder.lsps1_client(
config.node_id,
config.address.clone(),
config.token.clone(),
)
});

lsc.lsps2_client.as_ref().map(|config| {
liquidity_source_builder.lsps2_client(
config.node_id,
config.address.clone(),
config.token.clone(),
)
});
liquidity_source_builder.set_lsp_nodes(lsc.lsp_nodes.clone());

let promise_secret = {
let lsps_xpriv = derive_xprv(
Expand Down Expand Up @@ -1885,7 +1830,9 @@ fn build_with_store_internal(
}
}));

liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
liquidity_source
.as_ref()
.map(|l| l.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager)));

let connection_manager = Arc::new(ConnectionManager::new(
Arc::clone(&peer_manager),
Expand Down
28 changes: 18 additions & 10 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,15 +581,15 @@ where
Ok(final_tx) => {
let needs_manual_broadcast =
self.liquidity_source.as_ref().map_or(false, |ls| {
ls.as_ref().lsps2_channel_needs_manual_broadcast(
ls.as_ref().lsps2_service().lsps2_channel_needs_manual_broadcast(
counterparty_node_id,
user_channel_id,
)
});

let result = if needs_manual_broadcast {
self.liquidity_source.as_ref().map(|ls| {
ls.lsps2_store_funding_transaction(
ls.lsps2_service().lsps2_store_funding_transaction(
user_channel_id,
counterparty_node_id,
final_tx.clone(),
Expand Down Expand Up @@ -653,7 +653,8 @@ where
},
LdkEvent::FundingTxBroadcastSafe { user_channel_id, counterparty_node_id, .. } => {
self.liquidity_source.as_ref().map(|ls| {
ls.lsps2_funding_tx_broadcast_safe(user_channel_id, counterparty_node_id);
ls.lsps2_service()
.lsps2_funding_tx_broadcast_safe(user_channel_id, counterparty_node_id);
});
},
LdkEvent::PaymentClaimable {
Expand Down Expand Up @@ -1139,7 +1140,10 @@ where
LdkEvent::ProbeFailed { .. } => {},
LdkEvent::HTLCHandlingFailed { failure_type, .. } => {
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
liquidity_source.handle_htlc_handling_failed(failure_type).await;
liquidity_source
.lsps2_service()
.handle_htlc_handling_failed(failure_type)
.await;
}
},
LdkEvent::SpendableOutputs { outputs, channel_id, counterparty_node_id } => {
Expand Down Expand Up @@ -1238,14 +1242,15 @@ where
let user_channel_id: u128 = u128::from_ne_bytes(
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
);
let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id);
let mut channel_override_config = None;
if let Some((lsp_node_id, _)) = self
let is_lsp_node = self
.liquidity_source
.as_ref()
.and_then(|ls| ls.as_ref().get_lsps2_lsp_details())
{
if lsp_node_id == counterparty_node_id {
.map_or(false, |ls| ls.as_ref().is_lsps_node(&counterparty_node_id));
let allow_0conf =
self.config.trusted_peers_0conf.contains(&counterparty_node_id) || is_lsp_node;
let mut channel_override_config = None;
if let Some(ls) = self.liquidity_source.as_ref() {
if ls.as_ref().is_lsps_node(&counterparty_node_id) {
// When we're an LSPS2 client, allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
// check that they don't take too much before claiming.
//
Expand Down Expand Up @@ -1390,6 +1395,7 @@ where
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
let skimmed_fee_msat = skimmed_fee_msat.unwrap_or(0);
liquidity_source
.lsps2_service()
.handle_payment_forwarded(Some(next_htlc.channel_id), skimmed_fee_msat)
.await;
}
Expand Down Expand Up @@ -1499,6 +1505,7 @@ where

if let Some(liquidity_source) = self.liquidity_source.as_ref() {
liquidity_source
.lsps2_service()
.handle_channel_ready(user_channel_id, &channel_id, &counterparty_node_id)
.await;
}
Expand Down Expand Up @@ -1570,6 +1577,7 @@ where
} => {
if let Some(liquidity_source) = self.liquidity_source.as_ref() {
liquidity_source
.lsps2_service()
.handle_htlc_intercepted(
requested_next_hop_scid,
intercept_id,
Expand Down
63 changes: 52 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ use lightning_background_processor::process_events_async;
pub use lightning_invoice;
pub use lightning_liquidity;
pub use lightning_types;
use liquidity::{LSPS1Liquidity, LiquiditySource};
use liquidity::LiquiditySource;
use lnurl_auth::LnurlAuth;
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
Expand All @@ -180,6 +180,7 @@ use types::{
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
pub use vss_client;

use crate::liquidity::Liquidity;
use crate::scoring::setup_background_pathfinding_scores_sync;
use crate::wallet::FundingAmount;

Expand Down Expand Up @@ -658,7 +659,51 @@ impl Node {
let mut stop_liquidity_handler = self.stop_sender.subscribe();
let liquidity_handler = Arc::clone(&liquidity_source);
let liquidity_logger = Arc::clone(&self.logger);
let discovery_ls = Arc::clone(&liquidity_source);
let discovery_cm = Arc::clone(&self.connection_manager);
self.runtime.spawn_background_task(async move {
let discovery_logger = Arc::clone(&liquidity_logger);
tokio::spawn(async move {
let mut set = tokio::task::JoinSet::new();
for (node_id, address) in discovery_ls.get_all_lsp_details() {
let cm = Arc::clone(&discovery_cm);
let logger = Arc::clone(&discovery_logger);
let ls = Arc::clone(&discovery_ls);
set.spawn(async move {
if let Err(e) =
cm.connect_peer_if_necessary(node_id, address.clone()).await
{
log_error!(
logger,
"Failed to connect to LSP {} for protocol discovery: {}",
node_id,
e
);
return;
}
match ls.discover_lsp_protocols(&node_id).await {
Ok(protocols) => {
log_info!(
logger,
"Discovered protocols for LSP {}: {:?}",
node_id,
protocols
);
},
Err(e) => {
log_error!(
logger,
"Failed to discover protocols for LSP {}: {:?}",
node_id,
e
);
},
}
});
}
while set.join_next().await.is_some() {}
});

loop {
tokio::select! {
_ = stop_liquidity_handler.changed() => {
Expand Down Expand Up @@ -1039,12 +1084,10 @@ impl Node {
})
}

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

/// Returns a liquidity handler allowing to request channels via the [bLIP-51 / LSPS1] protocol.
///
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
/// Returns a liquidity handler allowing to manage LSP connections and request channels.
#[cfg(feature = "uniffi")]
pub fn lsps1_liquidity(&self) -> Arc<LSPS1Liquidity> {
Arc::new(LSPS1Liquidity::new(
pub fn liquidity(&self) -> Arc<Liquidity> {
Arc::new(Liquidity::new(
Arc::clone(&self.runtime),
Arc::clone(&self.wallet),
Arc::clone(&self.connection_manager),
Expand Down
Loading
Loading