@@ -3,7 +3,9 @@ use crate::fee_estimator::OnchainFeeEstimator;
3
3
use crate :: gossip:: GossipSource ;
4
4
use crate :: io;
5
5
use crate :: io:: sqlite_store:: SqliteStore ;
6
+ use crate :: liquidity:: LiquiditySource ;
6
7
use crate :: logger:: { log_error, FilesystemLogger , Logger } ;
8
+ use crate :: message_handler:: NodeCustomMessageHandler ;
7
9
use crate :: payment_store:: PaymentStore ;
8
10
use crate :: peer_store:: PeerStore ;
9
11
use crate :: sweep:: OutputSweeper ;
@@ -40,6 +42,9 @@ use lightning_persister::fs_store::FilesystemStore;
40
42
41
43
use lightning_transaction_sync:: EsploraSyncClient ;
42
44
45
+ use lightning_liquidity:: lsps2:: client:: LSPS2ClientConfig ;
46
+ use lightning_liquidity:: { LiquidityClientConfig , LiquidityManager } ;
47
+
43
48
#[ cfg( any( vss, vss_test) ) ]
44
49
use crate :: io:: vss_store:: VssStore ;
45
50
use bdk:: bitcoin:: secp256k1:: Secp256k1 ;
@@ -49,6 +54,7 @@ use bdk::template::Bip84;
49
54
50
55
use bip39:: Mnemonic ;
51
56
57
+ use bitcoin:: secp256k1:: PublicKey ;
52
58
use bitcoin:: { BlockHash , Network } ;
53
59
54
60
#[ cfg( any( vss, vss_test) ) ]
@@ -80,6 +86,18 @@ enum GossipSourceConfig {
80
86
RapidGossipSync ( String ) ,
81
87
}
82
88
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
+
83
101
/// An error encountered during building a [`Node`].
84
102
///
85
103
/// [`Node`]: crate::Node
@@ -146,16 +164,14 @@ pub struct NodeBuilder {
146
164
entropy_source_config : Option < EntropySourceConfig > ,
147
165
chain_data_source_config : Option < ChainDataSourceConfig > ,
148
166
gossip_source_config : Option < GossipSourceConfig > ,
167
+ liquidity_source_config : Option < LiquiditySourceConfig > ,
149
168
}
150
169
151
170
impl NodeBuilder {
152
171
/// Creates a new builder instance with the default configuration.
153
172
pub fn new ( ) -> Self {
154
173
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)
159
175
}
160
176
161
177
/// Creates a new builder instance from an [`Config`].
@@ -164,7 +180,14 @@ impl NodeBuilder {
164
180
let entropy_source_config = None ;
165
181
let chain_data_source_config = None ;
166
182
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
+ }
168
191
}
169
192
170
193
/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
@@ -218,6 +241,25 @@ impl NodeBuilder {
218
241
self
219
242
}
220
243
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
+
221
263
/// Sets the used storage directory path.
222
264
pub fn set_storage_dir_path ( & mut self , storage_dir_path : String ) -> & mut Self {
223
265
self . config . storage_dir_path = storage_dir_path;
@@ -318,6 +360,7 @@ impl NodeBuilder {
318
360
config,
319
361
self . chain_data_source_config . as_ref ( ) ,
320
362
self . gossip_source_config . as_ref ( ) ,
363
+ self . liquidity_source_config . as_ref ( ) ,
321
364
seed_bytes,
322
365
logger,
323
366
vss_store,
@@ -340,6 +383,7 @@ impl NodeBuilder {
340
383
config,
341
384
self . chain_data_source_config . as_ref ( ) ,
342
385
self . gossip_source_config . as_ref ( ) ,
386
+ self . liquidity_source_config . as_ref ( ) ,
343
387
seed_bytes,
344
388
logger,
345
389
kv_store,
@@ -413,6 +457,19 @@ impl ArcedNodeBuilder {
413
457
self . inner . write ( ) . unwrap ( ) . set_gossip_source_rgs ( rgs_server_url) ;
414
458
}
415
459
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
+
416
473
/// Sets the used storage directory path.
417
474
pub fn set_storage_dir_path ( & self , storage_dir_path : String ) {
418
475
self . inner . write ( ) . unwrap ( ) . set_storage_dir_path ( storage_dir_path) ;
@@ -463,7 +520,8 @@ impl ArcedNodeBuilder {
463
520
/// Builds a [`Node`] instance according to the options previously configured.
464
521
fn build_with_store_internal < K : KVStore + Sync + Send + ' static > (
465
522
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 ] ,
467
525
logger : Arc < FilesystemLogger > , kv_store : Arc < K > ,
468
526
) -> Result < Node < K > , BuildError > {
469
527
// Initialize the on-chain wallet and chain access
@@ -636,6 +694,12 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
636
694
// generating the events otherwise.
637
695
user_config. manually_accept_inbound_channels = true ;
638
696
}
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
+ }
639
703
let channel_manager = {
640
704
if let Ok ( res) = kv_store. read (
641
705
CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
@@ -746,20 +810,51 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
746
810
}
747
811
} ;
748
812
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
+
749
844
let msg_handler = match gossip_source. as_gossip_sync ( ) {
750
845
GossipSync :: P2P ( p2p_gossip_sync) => MessageHandler {
751
846
chan_handler : Arc :: clone ( & channel_manager) ,
752
847
route_handler : Arc :: clone ( & p2p_gossip_sync)
753
848
as Arc < dyn RoutingMessageHandler + Sync + Send > ,
754
849
onion_message_handler : onion_messenger,
755
- custom_message_handler : IgnoringMessageHandler { } ,
850
+ custom_message_handler,
756
851
} ,
757
852
GossipSync :: Rapid ( _) => MessageHandler {
758
853
chan_handler : Arc :: clone ( & channel_manager) ,
759
854
route_handler : Arc :: new ( IgnoringMessageHandler { } )
760
855
as Arc < dyn RoutingMessageHandler + Sync + Send > ,
761
856
onion_message_handler : onion_messenger,
762
- custom_message_handler : IgnoringMessageHandler { } ,
857
+ custom_message_handler,
763
858
} ,
764
859
GossipSync :: None => {
765
860
unreachable ! ( "We must always have a gossip sync!" ) ;
@@ -782,6 +877,8 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
782
877
Arc :: clone ( & keys_manager) ,
783
878
) ) ;
784
879
880
+ liquidity_source. as_ref ( ) . map ( |l| l. set_peer_manager ( Arc :: clone ( & peer_manager) ) ) ;
881
+
785
882
// Init payment info storage
786
883
let payment_store = match io:: utils:: read_payments ( Arc :: clone ( & kv_store) , Arc :: clone ( & logger) ) {
787
884
Ok ( payments) => {
@@ -853,6 +950,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
853
950
keys_manager,
854
951
network_graph,
855
952
gossip_source,
953
+ liquidity_source,
856
954
kv_store,
857
955
logger,
858
956
_router : router,
0 commit comments