@@ -18,7 +18,9 @@ use crate::gossip::GossipSource;
18
18
use crate :: io:: sqlite_store:: SqliteStore ;
19
19
use crate :: io:: utils:: { read_node_metrics, write_node_metrics} ;
20
20
use crate :: io:: vss_store:: VssStore ;
21
- use crate :: liquidity:: LiquiditySourceBuilder ;
21
+ use crate :: liquidity:: {
22
+ LSPS1ClientConfig , LSPS2ClientConfig , LSPS2ServiceConfig , LiquiditySourceBuilder ,
23
+ } ;
22
24
use crate :: logger:: { log_error, log_info, LdkLogger , LogLevel , LogWriter , Logger } ;
23
25
use crate :: message_handler:: NodeCustomMessageHandler ;
24
26
use crate :: payment:: store:: PaymentStore ;
@@ -75,6 +77,10 @@ use std::sync::{Arc, Mutex, RwLock};
75
77
use std:: time:: SystemTime ;
76
78
use vss_client:: headers:: { FixedHeaders , LnurlAuthToJwtProvider , VssHeaderProvider } ;
77
79
80
+ const VSS_HARDENED_CHILD_INDEX : u32 = 877 ;
81
+ const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX : u32 = 138 ;
82
+ const LSPS_HARDENED_CHILD_INDEX : u32 = 577 ;
83
+
78
84
#[ derive( Debug , Clone ) ]
79
85
enum ChainDataSourceConfig {
80
86
Esplora { server_url : String , sync_config : Option < EsploraSyncConfig > } ,
@@ -94,18 +100,14 @@ enum GossipSourceConfig {
94
100
RapidGossipSync ( String ) ,
95
101
}
96
102
97
- #[ derive( Debug , Clone ) ]
103
+ #[ derive( Debug , Clone , Default ) ]
98
104
struct LiquiditySourceConfig {
99
- // LSPS1 service's (node_id, address, token)
100
- lsps1_service : Option < ( PublicKey , SocketAddress , Option < String > ) > ,
101
- // LSPS2 service's (node_id, address, token)
102
- lsps2_service : Option < ( PublicKey , SocketAddress , Option < String > ) > ,
103
- }
104
-
105
- impl Default for LiquiditySourceConfig {
106
- fn default ( ) -> Self {
107
- Self { lsps1_service : None , lsps2_service : None }
108
- }
105
+ // Act as an LSPS1 client connecting to the given service.
106
+ lsps1_client : Option < LSPS1ClientConfig > ,
107
+ // Act as an LSPS2 client connecting to the given service.
108
+ lsps2_client : Option < LSPS2ClientConfig > ,
109
+ // Act as an LSPS2 service.
110
+ lsps2_service : Option < LSPS2ServiceConfig > ,
109
111
}
110
112
111
113
#[ derive( Clone ) ]
@@ -319,7 +321,8 @@ impl NodeBuilder {
319
321
320
322
let liquidity_source_config =
321
323
self . liquidity_source_config . get_or_insert ( LiquiditySourceConfig :: default ( ) ) ;
322
- liquidity_source_config. lsps1_service = Some ( ( node_id, address, token) ) ;
324
+ let lsps1_client_config = LSPS1ClientConfig { node_id, address, token } ;
325
+ liquidity_source_config. lsps1_client = Some ( lsps1_client_config) ;
323
326
self
324
327
}
325
328
@@ -339,7 +342,23 @@ impl NodeBuilder {
339
342
340
343
let liquidity_source_config =
341
344
self . liquidity_source_config . get_or_insert ( LiquiditySourceConfig :: default ( ) ) ;
342
- liquidity_source_config. lsps2_service = Some ( ( node_id, address, token) ) ;
345
+ let lsps2_client_config = LSPS2ClientConfig { node_id, address, token } ;
346
+ liquidity_source_config. lsps2_client = Some ( lsps2_client_config) ;
347
+ self
348
+ }
349
+
350
+ /// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
351
+ /// channels to clients.
352
+ ///
353
+ /// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
354
+ ///
355
+ /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
356
+ pub fn set_liquidity_provider_lsps2 (
357
+ & mut self , service_config : LSPS2ServiceConfig ,
358
+ ) -> & mut Self {
359
+ let liquidity_source_config =
360
+ self . liquidity_source_config . get_or_insert ( LiquiditySourceConfig :: default ( ) ) ;
361
+ liquidity_source_config. lsps2_service = Some ( service_config) ;
343
362
self
344
363
}
345
364
@@ -471,10 +490,14 @@ impl NodeBuilder {
471
490
472
491
let config = Arc :: new ( self . config . clone ( ) ) ;
473
492
474
- let vss_xprv = derive_vss_xprv ( config, & seed_bytes, Arc :: clone ( & logger) ) ?;
493
+ let vss_xprv =
494
+ derive_xprv ( config, & seed_bytes, VSS_HARDENED_CHILD_INDEX , Arc :: clone ( & logger) ) ?;
475
495
476
496
let lnurl_auth_xprv = vss_xprv
477
- . derive_priv ( & Secp256k1 :: new ( ) , & [ ChildNumber :: Hardened { index : 138 } ] )
497
+ . derive_priv (
498
+ & Secp256k1 :: new ( ) ,
499
+ & [ ChildNumber :: Hardened { index : VSS_LNURL_AUTH_HARDENED_CHILD_INDEX } ] ,
500
+ )
478
501
. map_err ( |e| {
479
502
log_error ! ( logger, "Failed to derive VSS secret: {}" , e) ;
480
503
BuildError :: KVStoreSetupFailed
@@ -536,7 +559,12 @@ impl NodeBuilder {
536
559
537
560
let config = Arc :: new ( self . config . clone ( ) ) ;
538
561
539
- let vss_xprv = derive_vss_xprv ( config. clone ( ) , & seed_bytes, Arc :: clone ( & logger) ) ?;
562
+ let vss_xprv = derive_xprv (
563
+ config. clone ( ) ,
564
+ & seed_bytes,
565
+ VSS_HARDENED_CHILD_INDEX ,
566
+ Arc :: clone ( & logger) ,
567
+ ) ?;
540
568
541
569
let vss_seed_bytes: [ u8 ; 32 ] = vss_xprv. private_key . secret_bytes ( ) ;
542
570
@@ -691,6 +719,16 @@ impl ArcedNodeBuilder {
691
719
self . inner . write ( ) . unwrap ( ) . set_liquidity_source_lsps2 ( node_id, address, token) ;
692
720
}
693
721
722
+ /// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
723
+ /// channels to clients.
724
+ ///
725
+ /// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
726
+ ///
727
+ /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
728
+ pub fn set_liquidity_provider_lsps2 ( & self , service_config : LSPS2ServiceConfig ) {
729
+ self . inner . write ( ) . unwrap ( ) . set_liquidity_provider_lsps2 ( service_config) ;
730
+ }
731
+
694
732
/// Sets the used storage directory path.
695
733
pub fn set_storage_dir_path ( & self , storage_dir_path : String ) {
696
734
self . inner . write ( ) . unwrap ( ) . set_storage_dir_path ( storage_dir_path) ;
@@ -1039,7 +1077,7 @@ fn build_with_store_internal(
1039
1077
} ;
1040
1078
1041
1079
let mut user_config = default_user_config ( & config) ;
1042
- if liquidity_source_config. and_then ( |lsc| lsc. lsps2_service . as_ref ( ) ) . is_some ( ) {
1080
+ if liquidity_source_config. and_then ( |lsc| lsc. lsps2_client . as_ref ( ) ) . is_some ( ) {
1043
1081
// Generally allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
1044
1082
// check that they don't take too much before claiming.
1045
1083
user_config. channel_config . accept_underpaying_htlcs = true ;
@@ -1051,6 +1089,12 @@ fn build_with_store_internal(
1051
1089
100 ;
1052
1090
}
1053
1091
1092
+ if liquidity_source_config. and_then ( |lsc| lsc. lsps2_service . as_ref ( ) ) . is_some ( ) {
1093
+ // If we act as an LSPS2 service, we need to to be able to intercept HTLCs and forward the
1094
+ // information to the service handler.
1095
+ user_config. accept_intercept_htlcs = true ;
1096
+ }
1097
+
1054
1098
let message_router =
1055
1099
Arc :: new ( MessageRouter :: new ( Arc :: clone ( & network_graph) , Arc :: clone ( & keys_manager) ) ) ;
1056
1100
@@ -1171,31 +1215,53 @@ fn build_with_store_internal(
1171
1215
} ,
1172
1216
} ;
1173
1217
1174
- let liquidity_source = liquidity_source_config. as_ref ( ) . map ( |lsc| {
1175
- let mut liquidity_source_builder = LiquiditySourceBuilder :: new (
1176
- Arc :: clone ( & channel_manager) ,
1177
- Arc :: clone ( & keys_manager) ,
1178
- Arc :: clone ( & chain_source) ,
1179
- Arc :: clone ( & config) ,
1180
- Arc :: clone ( & logger) ,
1181
- ) ;
1182
-
1183
- lsc. lsps1_service . as_ref ( ) . map ( |( node_id, address, token) | {
1184
- liquidity_source_builder. lsps1_service ( * node_id, address. clone ( ) , token. clone ( ) )
1185
- } ) ;
1218
+ let ( liquidity_source, custom_message_handler) =
1219
+ if let Some ( lsc) = liquidity_source_config. as_ref ( ) {
1220
+ let mut liquidity_source_builder = LiquiditySourceBuilder :: new (
1221
+ Arc :: clone ( & wallet) ,
1222
+ Arc :: clone ( & channel_manager) ,
1223
+ Arc :: clone ( & keys_manager) ,
1224
+ Arc :: clone ( & chain_source) ,
1225
+ Arc :: clone ( & config) ,
1226
+ Arc :: clone ( & logger) ,
1227
+ ) ;
1186
1228
1187
- lsc. lsps2_service . as_ref ( ) . map ( |( node_id, address, token) | {
1188
- liquidity_source_builder. lsps2_service ( * node_id, address. clone ( ) , token. clone ( ) )
1189
- } ) ;
1229
+ lsc. lsps1_client . as_ref ( ) . map ( |config| {
1230
+ liquidity_source_builder. lsps1_client (
1231
+ config. node_id ,
1232
+ config. address . clone ( ) ,
1233
+ config. token . clone ( ) ,
1234
+ )
1235
+ } ) ;
1190
1236
1191
- Arc :: new ( liquidity_source_builder. build ( ) )
1192
- } ) ;
1237
+ lsc. lsps2_client . as_ref ( ) . map ( |config| {
1238
+ liquidity_source_builder. lsps2_client (
1239
+ config. node_id ,
1240
+ config. address . clone ( ) ,
1241
+ config. token . clone ( ) ,
1242
+ )
1243
+ } ) ;
1193
1244
1194
- let custom_message_handler = if let Some ( liquidity_source) = liquidity_source. as_ref ( ) {
1195
- Arc :: new ( NodeCustomMessageHandler :: new_liquidity ( Arc :: clone ( & liquidity_source) ) )
1196
- } else {
1197
- Arc :: new ( NodeCustomMessageHandler :: new_ignoring ( ) )
1198
- } ;
1245
+ let promise_secret = {
1246
+ let lsps_xpriv = derive_xprv (
1247
+ Arc :: clone ( & config) ,
1248
+ & seed_bytes,
1249
+ LSPS_HARDENED_CHILD_INDEX ,
1250
+ Arc :: clone ( & logger) ,
1251
+ ) ?;
1252
+ lsps_xpriv. private_key . secret_bytes ( )
1253
+ } ;
1254
+ lsc. lsps2_service . as_ref ( ) . map ( |config| {
1255
+ liquidity_source_builder. lsps2_service ( promise_secret, config. clone ( ) )
1256
+ } ) ;
1257
+
1258
+ let liquidity_source = Arc :: new ( liquidity_source_builder. build ( ) ) ;
1259
+ let custom_message_handler =
1260
+ Arc :: new ( NodeCustomMessageHandler :: new_liquidity ( Arc :: clone ( & liquidity_source) ) ) ;
1261
+ ( Some ( liquidity_source) , custom_message_handler)
1262
+ } else {
1263
+ ( None , Arc :: new ( NodeCustomMessageHandler :: new_ignoring ( ) ) )
1264
+ } ;
1199
1265
1200
1266
let msg_handler = match gossip_source. as_gossip_sync ( ) {
1201
1267
GossipSync :: P2P ( p2p_gossip_sync) => MessageHandler {
@@ -1397,8 +1463,8 @@ fn seed_bytes_from_config(
1397
1463
}
1398
1464
}
1399
1465
1400
- fn derive_vss_xprv (
1401
- config : Arc < Config > , seed_bytes : & [ u8 ; 64 ] , logger : Arc < Logger > ,
1466
+ fn derive_xprv (
1467
+ config : Arc < Config > , seed_bytes : & [ u8 ; 64 ] , hardened_child_index : u32 , logger : Arc < Logger > ,
1402
1468
) -> Result < Xpriv , BuildError > {
1403
1469
use bitcoin:: key:: Secp256k1 ;
1404
1470
@@ -1407,10 +1473,11 @@ fn derive_vss_xprv(
1407
1473
BuildError :: InvalidSeedBytes
1408
1474
} ) ?;
1409
1475
1410
- xprv. derive_priv ( & Secp256k1 :: new ( ) , & [ ChildNumber :: Hardened { index : 877 } ] ) . map_err ( |e| {
1411
- log_error ! ( logger, "Failed to derive VSS secret: {}" , e) ;
1412
- BuildError :: KVStoreSetupFailed
1413
- } )
1476
+ xprv. derive_priv ( & Secp256k1 :: new ( ) , & [ ChildNumber :: Hardened { index : hardened_child_index } ] )
1477
+ . map_err ( |e| {
1478
+ log_error ! ( logger, "Failed to derive hardened child secret: {}" , e) ;
1479
+ BuildError :: InvalidSeedBytes
1480
+ } )
1414
1481
}
1415
1482
1416
1483
/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
0 commit comments