Skip to content

Commit f10e6fd

Browse files
committed
Add LSPS5 (bLIP-55) webhook notification support
Implement the bLIP-55 / LSPS5 webhook registration protocol on top of the multi-LSP liquidity module (src/liquidity/{client,service}). Client side, exposed via Node::liquidity().lsps5(): - set_webhook / list_webhooks / remove_webhook to manage webhook registrations with an LSP. - When no node_id is given, set_webhook and remove_webhook fan out to every LSPS5-capable LSP so a webhook can be configured once across all configured LSPs; set_webhook returns one result per LSP that accepted the registration and remove_webhook returns the LSPs it was removed from. Service side, enabled via Builder::enable_liquidity_provider_lsps5(): - Deliver outgoing webhook notifications over HTTP in response to LSPS5ServiceEvent::SendWebhookNotification. - Automatically send an onion-message-incoming notification when an intercepted onion message targets a client that is currently offline (wired from LdkEvent::OnionMessageIntercepted, gated on peer connectivity). Wires the feature through the UniFFI bindings and adds LSPS5-specific Error variants.
1 parent d9b8f7b commit f10e6fd

12 files changed

Lines changed: 1191 additions & 17 deletions

File tree

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ enum NodeError {
249249
"InvalidLnurl",
250250
"ChainSourceNotSupported",
251251
"InvalidPayerProof",
252+
"LiquiditySetWebhookFailed",
253+
"LiquidityRemoveWebhookFailed",
254+
"LiquidityListWebhooksFailed",
255+
"LiquidityNotifyWebhookFailed"
252256
};
253257

254258
typedef dictionary NodeStatus;

src/builder.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ use crate::runtime::{Runtime, RuntimeSpawner};
8383
use crate::tx_broadcaster::TransactionBroadcaster;
8484
use crate::types::{
8585
AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreRef, DynStoreWrapper,
86-
GossipSync, Graph, HRNResolver, KeysManager, MessageRouter, OnionMessenger, PaymentStore,
87-
PeerManager, PendingPaymentStore,
86+
GossipSync, Graph, HRNResolver, KeysManager, LSPS5ServiceConfig, MessageRouter, OnionMessenger,
87+
PaymentStore, PeerManager, PendingPaymentStore,
8888
};
8989
use crate::wallet::persist::{read_address_pool, KVStoreWalletPersister};
9090
use crate::wallet::Wallet;
@@ -127,10 +127,12 @@ struct PathfindingScoresSyncConfig {
127127

128128
#[derive(Debug, Clone, Default)]
129129
struct LiquiditySourceConfig {
130-
// Acts for both LSPS1 and LSPS2 clients connecting to the given service.
130+
// Acts for LSPS1, LSPS2 and LSPS5 clients connecting to the given service.
131131
lsp_nodes: Vec<LspConfig>,
132132
// Act as an LSPS2 service.
133133
lsps2_service: Option<LSPS2ServiceConfig>,
134+
// Act as an LSPS5 service.
135+
lsps5_service: Option<LSPS5ServiceConfig>,
134136
}
135137

136138
#[derive(Clone)]
@@ -522,6 +524,21 @@ impl NodeBuilder {
522524
self
523525
}
524526

527+
/// Configures the [`Node`] instance to provide an [bLIP-55 / LSPS5] service, enabling clients
528+
/// to register webhooks for push notifications.
529+
///
530+
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
531+
///
532+
/// [bLIP-55 / LSPS5]: https://github.com/lightning/blips/blob/master/blip-0055.md
533+
pub fn enable_liquidity_provider_lsps5(
534+
&mut self, lsps5_service_config: LSPS5ServiceConfig,
535+
) -> &mut Self {
536+
let liquidity_source_config =
537+
self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default());
538+
liquidity_source_config.lsps5_service = Some(lsps5_service_config);
539+
self
540+
}
541+
525542
/// Sets the used storage directory path.
526543
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
527544
self.config.storage_dir_path = storage_dir_path;
@@ -1122,6 +1139,16 @@ impl ArcedNodeBuilder {
11221139
self.inner.write().expect("lock").enable_liquidity_provider(lsps2_service_config);
11231140
}
11241141

1142+
/// Configures the [`Node`] instance to provide an [bLIP-55 / LSPS5] service, enabling clients
1143+
/// to register webhooks for push notifications.
1144+
///
1145+
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature.
1146+
///
1147+
/// [bLIP-55 / LSPS5]: https://github.com/lightning/blips/blob/master/blip-0055.md
1148+
pub fn enable_liquidity_provider_lsps5(&self, lsps5_service_config: LSPS5ServiceConfig) {
1149+
self.inner.write().expect("lock").enable_liquidity_provider_lsps5(lsps5_service_config);
1150+
}
1151+
11251152
/// Sets the used storage directory path.
11261153
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
11271154
self.inner.write().expect("lock").set_storage_dir_path(storage_dir_path);
@@ -2148,6 +2175,7 @@ fn build_with_store_internal(
21482175
Arc::clone(&tx_broadcaster),
21492176
Arc::clone(&kv_store),
21502177
Arc::clone(&config),
2178+
Arc::clone(&runtime),
21512179
Arc::clone(&logger),
21522180
);
21532181

@@ -2166,6 +2194,10 @@ fn build_with_store_internal(
21662194
lsc.lsps2_service.as_ref().map(|config| {
21672195
liquidity_source_builder.lsps2_service(promise_secret, config.clone())
21682196
});
2197+
2198+
lsc.lsps5_service
2199+
.as_ref()
2200+
.map(|config| liquidity_source_builder.lsps5_service(config.clone()));
21692201
}
21702202

21712203
let liquidity_source = runtime
@@ -2225,6 +2257,8 @@ fn build_with_store_internal(
22252257

22262258
liquidity_source.lsps2_service().set_peer_manager(Arc::downgrade(&peer_manager));
22272259

2260+
liquidity_source.lsps5_service().set_peer_manager(Arc::downgrade(&peer_manager));
2261+
22282262
let connection_manager = Arc::new(ConnectionManager::new(
22292263
Arc::clone(&peer_manager),
22302264
config.tor_config.clone(),

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
152152
// thereafter until every configured LSP has been discovered.
153153
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);
154154

155+
// The timeout after which we abort a LSPS5 webhook notification operation.
156+
pub(crate) const LSPS5_WEBHOOK_TIMEOUT_SECS: u64 = 30;
157+
158+
// The maximum size of a response body we'll accept when delivering an LSPS5 webhook notification.
159+
pub(crate) const LSPS5_WEBHOOK_MAX_RESPONSE_SIZE: usize = 64 * 1024;
160+
155161
#[derive(Debug, Clone)]
156162
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
157163
/// Represents the configuration of an [`Node`] instance.

src/error.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ pub enum Error {
143143
ChainSourceNotSupported,
144144
/// The provided payer proof is invalid.
145145
InvalidPayerProof,
146+
/// Failed to set a webhook with the LSP.
147+
LiquiditySetWebhookFailed,
148+
/// Failed to remove a webhook with the LSP.
149+
LiquidityRemoveWebhookFailed,
150+
/// Failed to list webhooks with the LSP.
151+
LiquidityListWebhooksFailed,
152+
/// Failed to send a webhook notification to a client.
153+
LiquidityNotifyWebhookFailed,
146154
}
147155

148156
impl fmt::Display for Error {
@@ -233,6 +241,18 @@ impl fmt::Display for Error {
233241
write!(f, "The configured chain source is not supported.")
234242
},
235243
Self::InvalidPayerProof => write!(f, "The provided payer proof is invalid."),
244+
Self::LiquiditySetWebhookFailed => {
245+
write!(f, "Failed to set a webhook with the LSP.")
246+
},
247+
Self::LiquidityRemoveWebhookFailed => {
248+
write!(f, "Failed to remove a webhook with the LSP.")
249+
},
250+
Self::LiquidityListWebhooksFailed => {
251+
write!(f, "Failed to list webhooks with the LSP.")
252+
},
253+
Self::LiquidityNotifyWebhookFailed => {
254+
write!(f, "Failed to send a webhook notification to a client.")
255+
},
236256
}
237257
}
238258
}

src/event.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,8 @@ where
20222022
debug_assert!(false, "We currently don't handle BOLT12 invoices manually, so this event should never be emitted.");
20232023
},
20242024
LdkEvent::ConnectionNeeded { node_id, addresses } => {
2025+
self.liquidity_source.lsps5_service().notify_onion_message_incoming(node_id);
2026+
20252027
let spawn_logger = self.logger.clone();
20262028
let spawn_cm = Arc::clone(&self.connection_manager);
20272029
let future = async move {
@@ -2080,6 +2082,9 @@ where
20802082
"Onion message intercepted, but no onion message mailbox available"
20812083
);
20822084
}
2085+
self.liquidity_source
2086+
.lsps5_service()
2087+
.notify_onion_message_incoming(peer_node_id);
20832088
} else {
20842089
log_error!(self.logger, "Onion message intercepted for unknown SCID");
20852090
}

0 commit comments

Comments
 (0)