@@ -18,7 +18,9 @@ use crate::gossip::GossipSource;
1818use crate :: io:: sqlite_store:: SqliteStore ;
1919use crate :: io:: utils:: { read_node_metrics, write_node_metrics} ;
2020use crate :: io:: vss_store:: VssStore ;
21- use crate :: liquidity:: LiquiditySourceBuilder ;
21+ use crate :: liquidity:: {
22+ LSPS1ClientConfig , LSPS2ClientConfig , LSPS2ServiceConfig , LiquiditySourceBuilder ,
23+ } ;
2224use crate :: logger:: { log_error, log_info, LdkLogger , LogLevel , LogWriter , Logger } ;
2325use crate :: message_handler:: NodeCustomMessageHandler ;
2426use crate :: payment:: store:: PaymentStore ;
@@ -75,6 +77,10 @@ use std::sync::{Arc, Mutex, RwLock};
7577use std:: time:: SystemTime ;
7678use vss_client:: headers:: { FixedHeaders , LnurlAuthToJwtProvider , VssHeaderProvider } ;
7779
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+
7884#[ derive( Debug , Clone ) ]
7985enum ChainDataSourceConfig {
8086 Esplora { server_url : String , sync_config : Option < EsploraSyncConfig > } ,
@@ -94,18 +100,14 @@ enum GossipSourceConfig {
94100 RapidGossipSync ( String ) ,
95101}
96102
97- #[ derive( Debug , Clone ) ]
103+ #[ derive( Debug , Clone , Default ) ]
98104struct 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 > ,
109111}
110112
111113#[ derive( Clone ) ]
@@ -319,7 +321,8 @@ impl NodeBuilder {
319321
320322 let liquidity_source_config =
321323 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) ;
323326 self
324327 }
325328
@@ -339,7 +342,23 @@ impl NodeBuilder {
339342
340343 let liquidity_source_config =
341344 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) ;
343362 self
344363 }
345364
@@ -471,10 +490,14 @@ impl NodeBuilder {
471490
472491 let config = Arc :: new ( self . config . clone ( ) ) ;
473492
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) ) ?;
475495
476496 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+ )
478501 . map_err ( |e| {
479502 log_error ! ( logger, "Failed to derive VSS secret: {}" , e) ;
480503 BuildError :: KVStoreSetupFailed
@@ -536,7 +559,12 @@ impl NodeBuilder {
536559
537560 let config = Arc :: new ( self . config . clone ( ) ) ;
538561
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+ ) ?;
540568
541569 let vss_seed_bytes: [ u8 ; 32 ] = vss_xprv. private_key . secret_bytes ( ) ;
542570
@@ -691,6 +719,16 @@ impl ArcedNodeBuilder {
691719 self . inner . write ( ) . unwrap ( ) . set_liquidity_source_lsps2 ( node_id, address, token) ;
692720 }
693721
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+
694732 /// Sets the used storage directory path.
695733 pub fn set_storage_dir_path ( & self , storage_dir_path : String ) {
696734 self . inner . write ( ) . unwrap ( ) . set_storage_dir_path ( storage_dir_path) ;
@@ -1039,7 +1077,7 @@ fn build_with_store_internal(
10391077 } ;
10401078
10411079 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 ( ) {
10431081 // Generally allow claiming underpaying HTLCs as the LSP will skim off some fee. We'll
10441082 // check that they don't take too much before claiming.
10451083 user_config. channel_config . accept_underpaying_htlcs = true ;
@@ -1051,6 +1089,12 @@ fn build_with_store_internal(
10511089 100 ;
10521090 }
10531091
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+
10541098 let message_router =
10551099 Arc :: new ( MessageRouter :: new ( Arc :: clone ( & network_graph) , Arc :: clone ( & keys_manager) ) ) ;
10561100
@@ -1171,31 +1215,53 @@ fn build_with_store_internal(
11711215 } ,
11721216 } ;
11731217
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+ ) ;
11861228
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+ } ) ;
11901236
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+ } ) ;
11931244
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+ } ;
11991265
12001266 let msg_handler = match gossip_source. as_gossip_sync ( ) {
12011267 GossipSync :: P2P ( p2p_gossip_sync) => MessageHandler {
@@ -1397,8 +1463,8 @@ fn seed_bytes_from_config(
13971463 }
13981464}
13991465
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 > ,
14021468) -> Result < Xpriv , BuildError > {
14031469 use bitcoin:: key:: Secp256k1 ;
14041470
@@ -1407,10 +1473,11 @@ fn derive_vss_xprv(
14071473 BuildError :: InvalidSeedBytes
14081474 } ) ?;
14091475
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+ } )
14141481}
14151482
14161483/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
0 commit comments