@@ -3,7 +3,9 @@ use crate::fee_estimator::OnchainFeeEstimator;
33use crate :: gossip:: GossipSource ;
44use crate :: io;
55use crate :: io:: sqlite_store:: SqliteStore ;
6+ use crate :: liquidity:: LiquiditySource ;
67use crate :: logger:: { log_error, FilesystemLogger , Logger } ;
8+ use crate :: message_handler:: NodeCustomMessageHandler ;
79use crate :: payment_store:: PaymentStore ;
810use crate :: peer_store:: PeerStore ;
911use crate :: sweep:: OutputSweeper ;
@@ -40,6 +42,9 @@ use lightning_persister::fs_store::FilesystemStore;
4042
4143use lightning_transaction_sync:: EsploraSyncClient ;
4244
45+ use lightning_liquidity:: lsps2:: client:: LSPS2ClientConfig ;
46+ use lightning_liquidity:: { LiquidityClientConfig , LiquidityManager } ;
47+
4348#[ cfg( any( vss, vss_test) ) ]
4449use crate :: io:: vss_store:: VssStore ;
4550use bdk:: bitcoin:: secp256k1:: Secp256k1 ;
@@ -49,6 +54,7 @@ use bdk::template::Bip84;
4954
5055use bip39:: Mnemonic ;
5156
57+ use bitcoin:: secp256k1:: PublicKey ;
5258use bitcoin:: { BlockHash , Network } ;
5359
5460#[ cfg( any( vss, vss_test) ) ]
@@ -80,6 +86,18 @@ enum GossipSourceConfig {
8086 RapidGossipSync ( String ) ,
8187}
8288
89+ #[ derive( Debug , Clone ) ]
90+ struct LiquiditySourceConfig {
91+ // LSPS2 service's (address, node_id, token)
92+ lsps2_service : Option < ( SocketAddress , PublicKey , Option < String > ) > ,
93+ }
94+
95+ impl Default for LiquiditySourceConfig {
96+ fn default ( ) -> Self {
97+ Self { lsps2_service : None }
98+ }
99+ }
100+
83101/// An error encountered during building a [`Node`].
84102///
85103/// [`Node`]: crate::Node
@@ -146,16 +164,14 @@ pub struct NodeBuilder {
146164 entropy_source_config : Option < EntropySourceConfig > ,
147165 chain_data_source_config : Option < ChainDataSourceConfig > ,
148166 gossip_source_config : Option < GossipSourceConfig > ,
167+ liquidity_source_config : Option < LiquiditySourceConfig > ,
149168}
150169
151170impl NodeBuilder {
152171 /// Creates a new builder instance with the default configuration.
153172 pub fn new ( ) -> Self {
154173 let config = Config :: default ( ) ;
155- let entropy_source_config = None ;
156- let chain_data_source_config = None ;
157- let gossip_source_config = None ;
158- Self { config, entropy_source_config, chain_data_source_config, gossip_source_config }
174+ Self :: from_config ( config)
159175 }
160176
161177 /// Creates a new builder instance from an [`Config`].
@@ -164,7 +180,14 @@ impl NodeBuilder {
164180 let entropy_source_config = None ;
165181 let chain_data_source_config = None ;
166182 let gossip_source_config = None ;
167- Self { config, entropy_source_config, chain_data_source_config, gossip_source_config }
183+ let liquidity_source_config = None ;
184+ Self {
185+ config,
186+ entropy_source_config,
187+ chain_data_source_config,
188+ gossip_source_config,
189+ liquidity_source_config,
190+ }
168191 }
169192
170193 /// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
@@ -218,6 +241,25 @@ impl NodeBuilder {
218241 self
219242 }
220243
244+ /// Configures the [`Node`] instance to source its inbound liquidity from the given
245+ /// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md)
246+ /// service.
247+ ///
248+ /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
249+ ///
250+ /// The given `token` will be used by the LSP to authenticate the user.
251+ pub fn set_liquidity_source_lsps2 (
252+ & mut self , address : SocketAddress , node_id : PublicKey , token : Option < String > ,
253+ ) -> & mut Self {
254+ // Mark the LSP as trusted for 0conf
255+ self . config . trusted_peers_0conf . push ( node_id. clone ( ) ) ;
256+
257+ let liquidity_source_config =
258+ self . liquidity_source_config . get_or_insert ( LiquiditySourceConfig :: default ( ) ) ;
259+ liquidity_source_config. lsps2_service = Some ( ( address, node_id, token) ) ;
260+ self
261+ }
262+
221263 /// Sets the used storage directory path.
222264 pub fn set_storage_dir_path ( & mut self , storage_dir_path : String ) -> & mut Self {
223265 self . config . storage_dir_path = storage_dir_path;
@@ -318,6 +360,7 @@ impl NodeBuilder {
318360 config,
319361 self . chain_data_source_config . as_ref ( ) ,
320362 self . gossip_source_config . as_ref ( ) ,
363+ self . liquidity_source_config . as_ref ( ) ,
321364 seed_bytes,
322365 logger,
323366 vss_store,
@@ -340,6 +383,7 @@ impl NodeBuilder {
340383 config,
341384 self . chain_data_source_config . as_ref ( ) ,
342385 self . gossip_source_config . as_ref ( ) ,
386+ self . liquidity_source_config . as_ref ( ) ,
343387 seed_bytes,
344388 logger,
345389 kv_store,
@@ -413,6 +457,19 @@ impl ArcedNodeBuilder {
413457 self . inner . write ( ) . unwrap ( ) . set_gossip_source_rgs ( rgs_server_url) ;
414458 }
415459
460+ /// Configures the [`Node`] instance to source its inbound liquidity from the given
461+ /// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md)
462+ /// service.
463+ ///
464+ /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`].
465+ ///
466+ /// The given `token` will be used by the LSP to authenticate the user.
467+ pub fn set_liquidity_source_lsps2 (
468+ & self , address : SocketAddress , node_id : PublicKey , token : Option < String > ,
469+ ) {
470+ self . inner . write ( ) . unwrap ( ) . set_liquidity_source_lsps2 ( address, node_id, token) ;
471+ }
472+
416473 /// Sets the used storage directory path.
417474 pub fn set_storage_dir_path ( & self , storage_dir_path : String ) {
418475 self . inner . write ( ) . unwrap ( ) . set_storage_dir_path ( storage_dir_path) ;
@@ -463,7 +520,8 @@ impl ArcedNodeBuilder {
463520/// Builds a [`Node`] instance according to the options previously configured.
464521fn build_with_store_internal < K : KVStore + Sync + Send + ' static > (
465522 config : Arc < Config > , chain_data_source_config : Option < & ChainDataSourceConfig > ,
466- gossip_source_config : Option < & GossipSourceConfig > , seed_bytes : [ u8 ; 64 ] ,
523+ gossip_source_config : Option < & GossipSourceConfig > ,
524+ liquidity_source_config : Option < & LiquiditySourceConfig > , seed_bytes : [ u8 ; 64 ] ,
467525 logger : Arc < FilesystemLogger > , kv_store : Arc < K > ,
468526) -> Result < Node < K > , BuildError > {
469527 // Initialize the on-chain wallet and chain access
@@ -636,6 +694,12 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
636694 // generating the events otherwise.
637695 user_config. manually_accept_inbound_channels = true ;
638696 }
697+
698+ if liquidity_source_config. is_some ( ) {
699+ // Generally allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
700+ // check that they don't take too much before claiming.
701+ user_config. channel_config . accept_underpaying_htlcs = true ;
702+ }
639703 let channel_manager = {
640704 if let Ok ( res) = kv_store. read (
641705 CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
@@ -746,20 +810,51 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
746810 }
747811 } ;
748812
813+ let liquidity_source = liquidity_source_config. as_ref ( ) . and_then ( |lsc| {
814+ lsc. lsps2_service . as_ref ( ) . map ( |( address, node_id, token) | {
815+ let lsps2_client_config = Some ( LSPS2ClientConfig { } ) ;
816+ let liquidity_client_config = Some ( LiquidityClientConfig { lsps2_client_config } ) ;
817+ let liquidity_manager = Arc :: new ( LiquidityManager :: new (
818+ Arc :: clone ( & keys_manager) ,
819+ Arc :: clone ( & channel_manager) ,
820+ Some ( Arc :: clone ( & tx_sync) ) ,
821+ None ,
822+ None ,
823+ liquidity_client_config,
824+ ) ) ;
825+ Arc :: new ( LiquiditySource :: new_lsps2 (
826+ address. clone ( ) ,
827+ * node_id,
828+ token. clone ( ) ,
829+ Arc :: clone ( & channel_manager) ,
830+ Arc :: clone ( & keys_manager) ,
831+ liquidity_manager,
832+ Arc :: clone ( & config) ,
833+ Arc :: clone ( & logger) ,
834+ ) )
835+ } )
836+ } ) ;
837+
838+ let custom_message_handler = if let Some ( liquidity_source) = liquidity_source. as_ref ( ) {
839+ Arc :: new ( NodeCustomMessageHandler :: new_liquidity ( Arc :: clone ( & liquidity_source) ) )
840+ } else {
841+ Arc :: new ( NodeCustomMessageHandler :: new_ignoring ( ) )
842+ } ;
843+
749844 let msg_handler = match gossip_source. as_gossip_sync ( ) {
750845 GossipSync :: P2P ( p2p_gossip_sync) => MessageHandler {
751846 chan_handler : Arc :: clone ( & channel_manager) ,
752847 route_handler : Arc :: clone ( & p2p_gossip_sync)
753848 as Arc < dyn RoutingMessageHandler + Sync + Send > ,
754849 onion_message_handler : onion_messenger,
755- custom_message_handler : IgnoringMessageHandler { } ,
850+ custom_message_handler,
756851 } ,
757852 GossipSync :: Rapid ( _) => MessageHandler {
758853 chan_handler : Arc :: clone ( & channel_manager) ,
759854 route_handler : Arc :: new ( IgnoringMessageHandler { } )
760855 as Arc < dyn RoutingMessageHandler + Sync + Send > ,
761856 onion_message_handler : onion_messenger,
762- custom_message_handler : IgnoringMessageHandler { } ,
857+ custom_message_handler,
763858 } ,
764859 GossipSync :: None => {
765860 unreachable ! ( "We must always have a gossip sync!" ) ;
@@ -782,6 +877,8 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
782877 Arc :: clone ( & keys_manager) ,
783878 ) ) ;
784879
880+ liquidity_source. as_ref ( ) . map ( |l| l. set_peer_manager ( Arc :: clone ( & peer_manager) ) ) ;
881+
785882 // Init payment info storage
786883 let payment_store = match io:: utils:: read_payments ( Arc :: clone ( & kv_store) , Arc :: clone ( & logger) ) {
787884 Ok ( payments) => {
@@ -853,6 +950,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
853950 keys_manager,
854951 network_graph,
855952 gossip_source,
953+ liquidity_source,
856954 kv_store,
857955 logger,
858956 _router : router,
0 commit comments