Skip to content

Commit c5772f3

Browse files
committed
Wire swap-in-potentiam handlers into LiquidityManager via ldk-node
Add set_sip_lsp() builder method that configures SIP client support, pass SIP config through LiquiditySourceBuilder to LiquidityManager, and handle SIP client/service events in the liquidity event loop. The LSP is automatically added as a trusted 0conf peer. Co-Authored-By: HAL 9000
1 parent 5f9d6ea commit c5772f3

File tree

4 files changed

+176
-6
lines changed

4 files changed

+176
-6
lines changed

src/builder.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ use crate::io::{
6767
};
6868
use crate::liquidity::{
6969
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
70+
SIPClientConfig, SIPServiceBuilderConfig,
7071
};
7172
use crate::lnurl_auth::LnurlAuth;
7273
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
@@ -118,14 +119,30 @@ struct PathfindingScoresSyncConfig {
118119
url: String,
119120
}
120121

121-
#[derive(Debug, Clone, Default)]
122+
#[derive(Debug, Clone)]
122123
struct LiquiditySourceConfig {
123124
// Act as an LSPS1 client connecting to the given service.
124125
lsps1_client: Option<LSPS1ClientConfig>,
125126
// Act as an LSPS2 client connecting to the given service.
126127
lsps2_client: Option<LSPS2ClientConfig>,
127128
// Act as an LSPS2 service.
128129
lsps2_service: Option<LSPS2ServiceConfig>,
130+
// Act as a SIP client connecting to the given LSP.
131+
sip_client: Option<SIPClientConfig>,
132+
// Act as a SIP service (LSP).
133+
sip_service: Option<SIPServiceBuilderConfig>,
134+
}
135+
136+
impl Default for LiquiditySourceConfig {
137+
fn default() -> Self {
138+
Self {
139+
lsps1_client: None,
140+
lsps2_client: None,
141+
lsps2_service: None,
142+
sip_client: None,
143+
sip_service: None,
144+
}
145+
}
129146
}
130147

131148
#[derive(Clone)]
@@ -492,6 +509,21 @@ impl NodeBuilder {
492509
self
493510
}
494511

512+
/// Configures the [`Node`] instance to use swap-in-potentiam with the given LSP.
513+
///
514+
/// SIP allows the node to receive on-chain funds at a shared address and instantly swap them
515+
/// into a Lightning channel. The LSP at `node_id` must support the SIP protocol.
516+
pub fn set_sip_lsp(
517+
&mut self, node_id: PublicKey, address: SocketAddress,
518+
) -> &mut Self {
519+
self.config.trusted_peers_0conf.push(node_id.clone());
520+
521+
let liquidity_source_config =
522+
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
523+
liquidity_source_config.sip_client = Some(SIPClientConfig { node_id, address });
524+
self
525+
}
526+
495527
/// Sets the used storage directory path.
496528
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
497529
self.config.storage_dir_path = storage_dir_path;
@@ -1835,6 +1867,21 @@ fn build_with_store_internal(
18351867
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
18361868
});
18371869

1870+
lsc.sip_client.as_ref().map(|config| {
1871+
liquidity_source_builder.sip_client(config.node_id, config.address.clone())
1872+
});
1873+
1874+
lsc.sip_service.as_ref().map(|config| {
1875+
let ldk_config = lightning_liquidity::sip::service::SIPServiceConfig {
1876+
server_pubkey: config.server_pubkey,
1877+
csv_delay: config.csv_delay,
1878+
min_swap_amount_sat: config.min_swap_amount_sat,
1879+
max_swap_amount_sat: config.max_swap_amount_sat,
1880+
min_confirmations: config.min_confirmations,
1881+
};
1882+
liquidity_source_builder.sip_service(ldk_config)
1883+
});
1884+
18381885
let liquidity_source = runtime
18391886
.block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?;
18401887
let custom_message_handler =

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub mod payment;
103103
mod peer_store;
104104
mod runtime;
105105
mod scoring;
106-
mod sip;
106+
pub mod sip;
107107
mod tx_broadcaster;
108108
mod types;
109109
mod wallet;

src/liquidity.rs

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
3434
use lightning_liquidity::lsps2::msgs::{LSPS2OpeningFeeParams, LSPS2RawOpeningFeeParams};
3535
use lightning_liquidity::lsps2::service::LSPS2ServiceConfig as LdkLSPS2ServiceConfig;
3636
use lightning_liquidity::lsps2::utils::compute_opening_fee;
37+
use lightning_liquidity::sip::client::SIPClientConfig as LdkSIPClientConfig;
38+
use lightning_liquidity::sip::event::{SIPClientEvent, SIPServiceEvent};
39+
use lightning_liquidity::sip::service::SIPServiceConfig as LdkSIPServiceConfig;
3740
use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig};
3841
use lightning_types::payment::PaymentHash;
3942
use tokio::sync::oneshot;
@@ -148,13 +151,32 @@ pub struct LSPS2ServiceConfig {
148151
pub allow_client_0reserve: bool,
149152
}
150153

154+
/// Client-side configuration for connecting to an LSP's SIP service.
155+
#[derive(Debug, Clone)]
156+
pub(crate) struct SIPClientConfig {
157+
pub node_id: PublicKey,
158+
pub address: SocketAddress,
159+
}
160+
161+
/// Service-side configuration for offering a SIP service.
162+
#[derive(Debug, Clone)]
163+
pub(crate) struct SIPServiceBuilderConfig {
164+
pub server_pubkey: PublicKey,
165+
pub csv_delay: u16,
166+
pub min_swap_amount_sat: u64,
167+
pub max_swap_amount_sat: u64,
168+
pub min_confirmations: u16,
169+
}
170+
151171
pub(crate) struct LiquiditySourceBuilder<L: Deref>
152172
where
153173
L::Target: LdkLogger,
154174
{
155175
lsps1_client: Option<LSPS1Client>,
156176
lsps2_client: Option<LSPS2Client>,
157177
lsps2_service: Option<LSPS2Service>,
178+
sip_client: Option<SIPClientConfig>,
179+
sip_service_config: Option<LdkSIPServiceConfig>,
158180
wallet: Arc<Wallet>,
159181
channel_manager: Arc<ChannelManager>,
160182
keys_manager: Arc<KeysManager>,
@@ -175,10 +197,14 @@ where
175197
let lsps1_client = None;
176198
let lsps2_client = None;
177199
let lsps2_service = None;
200+
let sip_client = None;
201+
let sip_service_config = None;
178202
Self {
179203
lsps1_client,
180204
lsps2_client,
181205
lsps2_service,
206+
sip_client,
207+
sip_service_config,
182208
wallet,
183209
channel_manager,
184210
keys_manager,
@@ -234,6 +260,18 @@ where
234260
self
235261
}
236262

263+
pub(crate) fn sip_client(
264+
&mut self, lsp_node_id: PublicKey, lsp_address: SocketAddress,
265+
) -> &mut Self {
266+
self.sip_client = Some(SIPClientConfig { node_id: lsp_node_id, address: lsp_address });
267+
self
268+
}
269+
270+
pub(crate) fn sip_service(&mut self, config: LdkSIPServiceConfig) -> &mut Self {
271+
self.sip_service_config = Some(config);
272+
self
273+
}
274+
237275
pub(crate) async fn build(self) -> Result<LiquiditySource<L>, BuildError> {
238276
let liquidity_service_config = self.lsps2_service.as_ref().map(|s| {
239277
let lsps2_service_config = Some(s.ldk_service_config.clone());
@@ -243,19 +281,21 @@ where
243281
lsps1_service_config: None,
244282
lsps2_service_config,
245283
lsps5_service_config,
246-
sip_service_config: None,
284+
sip_service_config: self.sip_service_config.clone(),
247285
advertise_service,
248286
}
249287
});
250288

251289
let lsps1_client_config = self.lsps1_client.as_ref().map(|s| s.ldk_client_config.clone());
252290
let lsps2_client_config = self.lsps2_client.as_ref().map(|s| s.ldk_client_config.clone());
253291
let lsps5_client_config = None;
292+
let sip_client_config =
293+
self.sip_client.as_ref().map(|_| LdkSIPClientConfig {});
254294
let liquidity_client_config = Some(LiquidityClientConfig {
255295
lsps1_client_config,
256296
lsps2_client_config,
257297
lsps5_client_config,
258-
sip_client_config: None,
298+
sip_client_config,
259299
});
260300

261301
let liquidity_manager = Arc::new(
@@ -276,6 +316,7 @@ where
276316
lsps1_client: self.lsps1_client,
277317
lsps2_client: self.lsps2_client,
278318
lsps2_service: self.lsps2_service,
319+
sip_client: self.sip_client,
279320
wallet: self.wallet,
280321
channel_manager: self.channel_manager,
281322
peer_manager: RwLock::new(None),
@@ -294,6 +335,7 @@ where
294335
lsps1_client: Option<LSPS1Client>,
295336
lsps2_client: Option<LSPS2Client>,
296337
lsps2_service: Option<LSPS2Service>,
338+
sip_client: Option<SIPClientConfig>,
297339
wallet: Arc<Wallet>,
298340
channel_manager: Arc<ChannelManager>,
299341
peer_manager: RwLock<Option<Weak<PeerManager>>>,
@@ -323,6 +365,22 @@ where
323365
self.lsps2_client.as_ref().map(|s| (s.lsp_node_id, s.lsp_address.clone()))
324366
}
325367

368+
/// Returns the SIP LSP details if configured.
369+
pub(crate) fn get_sip_lsp_details(&self) -> Option<(PublicKey, SocketAddress)> {
370+
self.sip_client.as_ref().map(|s| (s.node_id, s.address.clone()))
371+
}
372+
373+
/// Request the LSP's SIP parameters via the `sip.get_info` protocol message.
374+
pub(crate) fn sip_request_info(&self) -> Result<(), Error> {
375+
let sip_client = self.sip_client.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
376+
let handler = self.liquidity_manager.sip_client_handler().ok_or_else(|| {
377+
log_error!(self.logger, "SIP client handler was not configured.");
378+
Error::LiquiditySourceUnavailable
379+
})?;
380+
handler.request_sip_info(sip_client.node_id);
381+
Ok(())
382+
}
383+
326384
pub(crate) fn lsps2_channel_needs_manual_broadcast(
327385
&self, counterparty_node_id: PublicKey, user_channel_id: u128,
328386
) -> bool {
@@ -935,6 +993,69 @@ where
935993
);
936994
}
937995
},
996+
LiquidityEvent::SIPClient(SIPClientEvent::GetInfoResponse {
997+
lsp_node_id,
998+
server_pubkey,
999+
csv_delay,
1000+
..
1001+
}) => {
1002+
log_info!(
1003+
self.logger,
1004+
"Received SIP info from LSP {}: server_pubkey={}, csv_delay={}",
1005+
lsp_node_id,
1006+
server_pubkey,
1007+
csv_delay,
1008+
);
1009+
},
1010+
LiquidityEvent::SIPClient(SIPClientEvent::UtxoRegistered {
1011+
lsp_node_id,
1012+
outpoint,
1013+
}) => {
1014+
log_info!(
1015+
self.logger,
1016+
"SIP UTXO {} registered with LSP {}",
1017+
outpoint,
1018+
lsp_node_id,
1019+
);
1020+
},
1021+
LiquidityEvent::SIPClient(SIPClientEvent::SwapAccepted {
1022+
lsp_node_id,
1023+
channel_id,
1024+
..
1025+
}) => {
1026+
log_info!(
1027+
self.logger,
1028+
"SIP swap accepted by LSP {}: channel_id={}",
1029+
lsp_node_id,
1030+
channel_id,
1031+
);
1032+
},
1033+
LiquidityEvent::SIPService(SIPServiceEvent::UtxoRegistered {
1034+
counterparty_node_id,
1035+
outpoint,
1036+
value_sat,
1037+
..
1038+
}) => {
1039+
log_info!(
1040+
self.logger,
1041+
"SIP UTXO {} ({} sat) registered by client {}",
1042+
outpoint,
1043+
value_sat,
1044+
counterparty_node_id,
1045+
);
1046+
},
1047+
LiquidityEvent::SIPService(SIPServiceEvent::SwapRequested {
1048+
counterparty_node_id,
1049+
utxos,
1050+
..
1051+
}) => {
1052+
log_info!(
1053+
self.logger,
1054+
"SIP swap requested by client {} with {} UTXOs",
1055+
counterparty_node_id,
1056+
utxos.len(),
1057+
);
1058+
},
9381059
e => {
9391060
log_error!(self.logger, "Received unexpected liquidity event: {:?}", e);
9401061
},

src/sip/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
//! and transaction building for refund and cooperative spends.
1414
1515
pub(crate) mod coin_selection;
16-
pub(crate) mod state;
17-
pub(crate) mod wallet;
16+
/// UTXO state machine for swap-in-potentiam.
17+
pub mod state;
18+
/// SIP wallet with BIP32 key derivation and UTXO tracking.
19+
pub mod wallet;
1820

1921
use std::sync::Arc;
2022

0 commit comments

Comments
 (0)