Skip to content

Commit 45c0b77

Browse files
committed
Allow configuring LSPS2 service via builders
We add the capability to configure LSPS2 service mode in `Builder` and `LiquiditySourceBuilder`.
1 parent 8878ab0 commit 45c0b77

File tree

2 files changed

+117
-31
lines changed

2 files changed

+117
-31
lines changed

src/builder.rs

+89-29
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvide
7777

7878
const VSS_HARDENED_CHILD_INDEX: u32 = 877;
7979
const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138;
80+
const LSPS_HARDENED_CHILD_INDEX: u32 = 577;
8081

8182
#[derive(Debug, Clone)]
8283
enum ChainDataSourceConfig {
@@ -103,6 +104,8 @@ struct LiquiditySourceConfig {
103104
lsps1_client: Option<LSPS1ClientConfig>,
104105
// Act as an LSPS2 client connecting to the given service.
105106
lsps2_client: Option<LSPS2ClientConfig>,
107+
// Act as an LSPS2 service.
108+
lsps2_service: Option<LSPS2ServiceConfig>,
106109
}
107110

108111
#[derive(Debug, Clone)]
@@ -119,6 +122,12 @@ struct LSPS2ClientConfig {
119122
token: Option<String>,
120123
}
121124

125+
#[derive(Debug, Clone)]
126+
struct LSPS2ServiceConfig {
127+
token: Option<String>,
128+
advertise_service: bool,
129+
}
130+
122131
#[derive(Clone)]
123132
enum LogWriterConfig {
124133
File { log_file_path: Option<String>, log_level: Option<LogLevel> },
@@ -354,6 +363,26 @@ impl NodeBuilder {
354363
self
355364
}
356365

366+
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
367+
/// channels to clients.
368+
///
369+
/// If a `token` is provided, only requests matching this token will be accepted.
370+
///
371+
/// If `advertise_service` is set, the LSPS service will be announced via the gossip network.
372+
///
373+
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
374+
///
375+
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
376+
pub fn set_liquidity_provider_lsps2(
377+
&mut self, token: Option<String>, advertise_service: bool,
378+
) -> &mut Self {
379+
let liquidity_source_config =
380+
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
381+
let lsps2_service_config = LSPS2ServiceConfig { token, advertise_service };
382+
liquidity_source_config.lsps2_service = Some(lsps2_service_config);
383+
self
384+
}
385+
357386
/// Sets the used storage directory path.
358387
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
359388
self.config.storage_dir_path = storage_dir_path;
@@ -706,6 +735,20 @@ impl ArcedNodeBuilder {
706735
self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token);
707736
}
708737

738+
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
739+
/// channels to clients.
740+
///
741+
/// If a `token` is provided, only requests matching this token will be accepted.
742+
///
743+
/// If `advertise_service` is set, the LSPS service will be announced via the gossip network.
744+
///
745+
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
746+
///
747+
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
748+
pub fn set_liquidity_provider_lsps2(&self, token: Option<String>, advertise_service: bool) {
749+
self.inner.write().unwrap().set_liquidity_provider_lsps2(token, advertise_service);
750+
}
751+
709752
/// Sets the used storage directory path.
710753
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
711754
self.inner.write().unwrap().set_storage_dir_path(storage_dir_path);
@@ -1181,39 +1224,56 @@ fn build_with_store_internal(
11811224
},
11821225
};
11831226

1184-
let liquidity_source = liquidity_source_config.as_ref().map(|lsc| {
1185-
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1186-
Arc::clone(&channel_manager),
1187-
Arc::clone(&keys_manager),
1188-
Arc::clone(&chain_source),
1189-
Arc::clone(&config),
1190-
Arc::clone(&logger),
1191-
);
1227+
let (liquidity_source, custom_message_handler) =
1228+
if let Some(lsc) = liquidity_source_config.as_ref() {
1229+
let mut liquidity_source_builder = LiquiditySourceBuilder::new(
1230+
Arc::clone(&channel_manager),
1231+
Arc::clone(&keys_manager),
1232+
Arc::clone(&chain_source),
1233+
Arc::clone(&config),
1234+
Arc::clone(&logger),
1235+
);
11921236

1193-
lsc.lsps1_client.as_ref().map(|config| {
1194-
liquidity_source_builder.lsps1_client(
1195-
config.node_id,
1196-
config.address.clone(),
1197-
config.token.clone(),
1198-
)
1199-
});
1237+
lsc.lsps1_client.as_ref().map(|config| {
1238+
liquidity_source_builder.lsps1_client(
1239+
config.node_id,
1240+
config.address.clone(),
1241+
config.token.clone(),
1242+
)
1243+
});
12001244

1201-
lsc.lsps2_client.as_ref().map(|config| {
1202-
liquidity_source_builder.lsps2_client(
1203-
config.node_id,
1204-
config.address.clone(),
1205-
config.token.clone(),
1206-
)
1207-
});
1245+
lsc.lsps2_client.as_ref().map(|config| {
1246+
liquidity_source_builder.lsps2_client(
1247+
config.node_id,
1248+
config.address.clone(),
1249+
config.token.clone(),
1250+
)
1251+
});
12081252

1209-
Arc::new(liquidity_source_builder.build())
1210-
});
1253+
let promise_secret = {
1254+
let lsps_xpriv = derive_xprv(
1255+
Arc::clone(&config),
1256+
&seed_bytes,
1257+
LSPS_HARDENED_CHILD_INDEX,
1258+
Arc::clone(&logger),
1259+
)?;
1260+
lsps_xpriv.private_key.secret_bytes()
1261+
};
1262+
lsc.lsps2_service.as_ref().map(|config| {
1263+
liquidity_source_builder.lsps2_service(
1264+
promise_secret,
1265+
config.token.clone(),
1266+
config.advertise_service,
1267+
)
1268+
});
12111269

1212-
let custom_message_handler = if let Some(liquidity_source) = liquidity_source.as_ref() {
1213-
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)))
1214-
} else {
1215-
Arc::new(NodeCustomMessageHandler::new_ignoring())
1216-
};
1270+
let liquidity_source = Arc::new(liquidity_source_builder.build());
1271+
let custom_message_handler =
1272+
Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source)));
1273+
(Some(liquidity_source), custom_message_handler)
1274+
} else {
1275+
(None, Arc::new(NodeCustomMessageHandler::new_ignoring()))
1276+
};
12171277

12181278
let msg_handler = match gossip_source.as_gossip_sync() {
12191279
GossipSync::P2P(p2p_gossip_sync) => MessageHandler {

src/liquidity.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ use lightning_liquidity::lsps1::msgs::{ChannelInfo, LSPS1Options, OrderId, Order
2525
use lightning_liquidity::lsps2::client::LSPS2ClientConfig;
2626
use lightning_liquidity::lsps2::event::LSPS2ClientEvent;
2727
use lightning_liquidity::lsps2::msgs::OpeningFeeParams;
28+
use lightning_liquidity::lsps2::service::LSPS2ServiceConfig;
2829
use lightning_liquidity::lsps2::utils::compute_opening_fee;
29-
use lightning_liquidity::LiquidityClientConfig;
30+
use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig};
3031

3132
use bitcoin::hashes::{sha256, Hash};
3233
use bitcoin::secp256k1::{PublicKey, Secp256k1};
@@ -61,12 +62,19 @@ struct LSPS2Client {
6162
pending_buy_requests: Mutex<HashMap<RequestId, oneshot::Sender<LSPS2BuyResponse>>>,
6263
}
6364

65+
struct LSPS2Service {
66+
token: Option<String>,
67+
service_config: LSPS2ServiceConfig,
68+
advertise_service: bool,
69+
}
70+
6471
pub(crate) struct LiquiditySourceBuilder<L: Deref>
6572
where
6673
L::Target: LdkLogger,
6774
{
6875
lsps1_client: Option<LSPS1Client>,
6976
lsps2_client: Option<LSPS2Client>,
77+
lsps2_service: Option<LSPS2Service>,
7078
channel_manager: Arc<ChannelManager>,
7179
keys_manager: Arc<KeysManager>,
7280
chain_source: Arc<ChainSource>,
@@ -84,9 +92,11 @@ where
8492
) -> Self {
8593
let lsps1_client = None;
8694
let lsps2_client = None;
95+
let lsps2_service = None;
8796
Self {
8897
lsps1_client,
8998
lsps2_client,
99+
lsps2_service,
90100
channel_manager,
91101
keys_manager,
92102
chain_source,
@@ -132,7 +142,21 @@ where
132142
self
133143
}
134144

145+
pub(crate) fn lsps2_service(
146+
&mut self, promise_secret: [u8; 32], token: Option<String>, advertise_service: bool,
147+
) -> &mut Self {
148+
let service_config = LSPS2ServiceConfig { promise_secret };
149+
self.lsps2_service = Some(LSPS2Service { token, service_config, advertise_service });
150+
self
151+
}
152+
135153
pub(crate) fn build(self) -> LiquiditySource<L> {
154+
let liquidity_service_config = self.lsps2_service.as_ref().map(|s| {
155+
let lsps2_service_config = Some(s.service_config.clone());
156+
let advertise_service = s.advertise_service;
157+
LiquidityServiceConfig { lsps2_service_config, advertise_service }
158+
});
159+
136160
let lsps1_client_config = self.lsps1_client.as_ref().map(|s| s.client_config.clone());
137161
let lsps2_client_config = self.lsps2_client.as_ref().map(|s| s.client_config.clone());
138162
let liquidity_client_config =
@@ -143,13 +167,14 @@ where
143167
Arc::clone(&self.channel_manager),
144168
Some(Arc::clone(&self.chain_source)),
145169
None,
146-
None,
170+
liquidity_service_config,
147171
liquidity_client_config,
148172
));
149173

150174
LiquiditySource {
151175
lsps1_client: self.lsps1_client,
152176
lsps2_client: self.lsps2_client,
177+
lsps2_service: self.lsps2_service,
153178
channel_manager: self.channel_manager,
154179
keys_manager: self.keys_manager,
155180
liquidity_manager,
@@ -165,6 +190,7 @@ where
165190
{
166191
lsps1_client: Option<LSPS1Client>,
167192
lsps2_client: Option<LSPS2Client>,
193+
lsps2_service: Option<LSPS2Service>,
168194
channel_manager: Arc<ChannelManager>,
169195
keys_manager: Arc<KeysManager>,
170196
liquidity_manager: Arc<LiquidityManager>,

0 commit comments

Comments
 (0)