diff --git a/src/builder.rs b/src/builder.rs index fadfed84c..8e6781ac6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -8,7 +8,7 @@ use crate::fee_estimator::OnchainFeeEstimator; use crate::gossip::GossipSource; use crate::io; use crate::io::sqlite_store::SqliteStore; -use crate::liquidity::LiquiditySource; +use crate::liquidity::LiquiditySourceBuilder; use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; use crate::message_handler::NodeCustomMessageHandler; use crate::payment::store::PaymentStore; @@ -42,9 +42,6 @@ use lightning_persister::fs_store::FilesystemStore; use lightning_transaction_sync::EsploraSyncClient; -use lightning_liquidity::lsps2::client::LSPS2ClientConfig; -use lightning_liquidity::{LiquidityClientConfig, LiquidityManager}; - #[cfg(any(vss, vss_test))] use crate::io::vss_store::VssStore; use bdk::bitcoin::secp256k1::Secp256k1; @@ -89,13 +86,15 @@ enum GossipSourceConfig { #[derive(Debug, Clone)] struct LiquiditySourceConfig { + // LSPS1 service's (node_id, address, token) + lsps1_service: Option<(PublicKey, SocketAddress, Option)>, // LSPS2 service's (node_id, address, token) lsps2_service: Option<(PublicKey, SocketAddress, Option)>, } impl Default for LiquiditySourceConfig { fn default() -> Self { - Self { lsps2_service: None } + Self { lsps1_service: None, lsps2_service: None } } } @@ -247,7 +246,26 @@ impl NodeBuilder { self } - /// Configures the [`Node`] instance to source its inbound liquidity from the given + /// Configures the [`Node`] instance to source inbound liquidity from the given + /// [LSPS1](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS1/README.md) + /// 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. + pub fn set_liquidity_source_lsps1( + &mut self, node_id: PublicKey, address: SocketAddress, token: Option, + ) -> &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()); + liquidity_source_config.lsps1_service = Some((node_id, address, token)); + self + } + + /// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given /// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md) /// service. /// @@ -459,7 +477,20 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_gossip_source_rgs(rgs_server_url); } - /// Configures the [`Node`] instance to source its inbound liquidity from the given + /// Configures the [`Node`] instance to source inbound liquidity from the given + /// [LSPS1](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS1/README.md) + /// 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. + pub fn set_liquidity_source_lsps1( + &self, node_id: PublicKey, address: SocketAddress, token: Option, + ) { + 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 /// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md) /// service. /// @@ -817,29 +848,24 @@ fn build_with_store_internal( }, }; - let liquidity_source = liquidity_source_config.as_ref().and_then(|lsc| { + let liquidity_source = liquidity_source_config.as_ref().map(|lsc| { + let mut liquidity_source_builder = LiquiditySourceBuilder::new( + Arc::clone(&channel_manager), + Arc::clone(&keys_manager), + Arc::clone(&tx_sync), + Arc::clone(&config), + Arc::clone(&logger), + ); + + lsc.lsps1_service.as_ref().map(|(node_id, address, token)| { + liquidity_source_builder.lsps1_service(*node_id, address.clone(), token.clone()) + }); + lsc.lsps2_service.as_ref().map(|(node_id, address, token)| { - let lsps2_client_config = Some(LSPS2ClientConfig {}); - let liquidity_client_config = Some(LiquidityClientConfig { lsps2_client_config }); - let liquidity_manager = Arc::new(LiquidityManager::new( - Arc::clone(&keys_manager), - Arc::clone(&channel_manager), - Some(Arc::clone(&tx_sync)), - None, - None, - liquidity_client_config, - )); - Arc::new(LiquiditySource::new_lsps2( - *node_id, - address.clone(), - token.clone(), - Arc::clone(&channel_manager), - Arc::clone(&keys_manager), - liquidity_manager, - Arc::clone(&config), - Arc::clone(&logger), - )) - }) + liquidity_source_builder.lsps2_service(*node_id, address.clone(), token.clone()) + }); + + Arc::new(liquidity_source_builder.build()) }); let custom_message_handler = if let Some(liquidity_source) = liquidity_source.as_ref() { diff --git a/src/liquidity.rs b/src/liquidity.rs index 71beecb35..1d78cae80 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -1,5 +1,5 @@ use crate::logger::{log_debug, log_error, log_info, Logger}; -use crate::types::{ChannelManager, KeysManager, LiquidityManager, PeerManager}; +use crate::types::{ChainSource, ChannelManager, KeysManager, LiquidityManager, PeerManager}; use crate::{Config, Error}; use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA; @@ -8,9 +8,12 @@ use lightning::routing::router::{RouteHint, RouteHintHop}; use lightning_invoice::{Bolt11Invoice, InvoiceBuilder, RoutingFees}; use lightning_liquidity::events::Event; use lightning_liquidity::lsps0::ser::RequestId; +use lightning_liquidity::lsps1::client::LSPS1ClientConfig; +use lightning_liquidity::lsps2::client::LSPS2ClientConfig; use lightning_liquidity::lsps2::event::LSPS2ClientEvent; use lightning_liquidity::lsps2::msgs::OpeningFeeParams; use lightning_liquidity::lsps2::utils::compute_opening_fee; +use lightning_liquidity::LiquidityClientConfig; use bitcoin::hashes::{sha256, Hash}; use bitcoin::secp256k1::{PublicKey, Secp256k1}; @@ -24,47 +27,126 @@ use std::time::Duration; const LIQUIDITY_REQUEST_TIMEOUT_SECS: u64 = 5; +struct LSPS1Service { + node_id: PublicKey, + address: SocketAddress, + token: Option, + client_config: LSPS1ClientConfig, +} + struct LSPS2Service { node_id: PublicKey, address: SocketAddress, token: Option, + client_config: LSPS2ClientConfig, pending_fee_requests: Mutex>>, pending_buy_requests: Mutex>>, } -pub(crate) struct LiquiditySource +pub(crate) struct LiquiditySourceBuilder where L::Target: Logger, { + lsps1_service: Option, lsps2_service: Option, channel_manager: Arc, keys_manager: Arc, - liquidity_manager: Arc, + tx_sync: Arc, config: Arc, logger: L, } -impl LiquiditySource +impl LiquiditySourceBuilder where L::Target: Logger, { - pub(crate) fn new_lsps2( - node_id: PublicKey, address: SocketAddress, token: Option, + pub(crate) fn new( channel_manager: Arc, keys_manager: Arc, - liquidity_manager: Arc, config: Arc, logger: L, + tx_sync: Arc, config: Arc, logger: L, ) -> Self { + let lsps1_service = None; + let lsps2_service = None; + Self { + lsps1_service, + lsps2_service, + channel_manager, + keys_manager, + tx_sync, + config, + logger, + } + } + + pub(crate) fn lsps1_service( + &mut self, node_id: PublicKey, address: SocketAddress, token: Option, + ) -> &mut Self { + // TODO: allow to set max_channel_fees_msat + let client_config = LSPS1ClientConfig { max_channel_fees_msat: None }; + self.lsps1_service = Some(LSPS1Service { node_id, address, token, client_config }); + self + } + + pub(crate) fn lsps2_service( + &mut self, node_id: PublicKey, address: SocketAddress, token: Option, + ) -> &mut Self { + let client_config = LSPS2ClientConfig {}; let pending_fee_requests = Mutex::new(HashMap::new()); let pending_buy_requests = Mutex::new(HashMap::new()); - let lsps2_service = Some(LSPS2Service { + self.lsps2_service = Some(LSPS2Service { node_id, address, token, + client_config, pending_fee_requests, pending_buy_requests, }); - Self { lsps2_service, channel_manager, keys_manager, liquidity_manager, config, logger } + self } + pub(crate) fn build(self) -> LiquiditySource { + let lsps1_client_config = self.lsps1_service.as_ref().map(|s| s.client_config.clone()); + let lsps2_client_config = self.lsps2_service.as_ref().map(|s| s.client_config.clone()); + let liquidity_client_config = + Some(LiquidityClientConfig { lsps1_client_config, lsps2_client_config }); + + let liquidity_manager = Arc::new(LiquidityManager::new( + Arc::clone(&self.keys_manager), + Arc::clone(&self.channel_manager), + Some(Arc::clone(&self.tx_sync)), + None, + None, + liquidity_client_config, + )); + + LiquiditySource { + lsps1_service: self.lsps1_service, + lsps2_service: self.lsps2_service, + channel_manager: self.channel_manager, + keys_manager: self.keys_manager, + liquidity_manager, + config: self.config, + logger: self.logger, + } + } +} + +pub(crate) struct LiquiditySource +where + L::Target: Logger, +{ + lsps1_service: Option, + lsps2_service: Option, + channel_manager: Arc, + keys_manager: Arc, + liquidity_manager: Arc, + config: Arc, + logger: L, +} + +impl LiquiditySource +where + L::Target: Logger, +{ pub(crate) fn set_peer_manager(&self, peer_manager: Arc) { let process_msgs_callback = move || peer_manager.process_events(); self.liquidity_manager.set_process_msgs_callback(process_msgs_callback); @@ -74,7 +156,11 @@ where self.liquidity_manager.as_ref() } - pub(crate) fn get_liquidity_source_details(&self) -> Option<(PublicKey, SocketAddress)> { + pub(crate) fn get_lsps1_service_details(&self) -> Option<(PublicKey, SocketAddress)> { + self.lsps1_service.as_ref().map(|s| (s.node_id, s.address.clone())) + } + + pub(crate) fn get_lsps2_service_details(&self) -> Option<(PublicKey, SocketAddress)> { self.lsps2_service.as_ref().map(|s| (s.node_id, s.address.clone())) } diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index 3641d6870..7cd9fde98 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -550,7 +550,7 @@ impl Bolt11Payment { self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?; let (node_id, address) = liquidity_source - .get_liquidity_source_details() + .get_lsps2_service_details() .ok_or(Error::LiquiditySourceUnavailable)?; let rt_lock = self.runtime.read().unwrap();