Skip to content

Commit fd8f2fa

Browse files
WIP
1 parent 4e8664a commit fd8f2fa

File tree

5 files changed

+165
-65
lines changed

5 files changed

+165
-65
lines changed

lightning-liquidity/src/lsps5/client.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,15 @@ impl PeerState {
137137
///
138138
/// # Core Capabilities
139139
///
140-
/// - `set_webhook(peer, app_name, url)` -> register or update a webhook (`lsps5.set_webhook`)
141-
/// - `list_webhooks(peer)` -> retrieve all registered webhooks (`lsps5.list_webhooks`)
142-
/// - `remove_webhook(peer, name)` -> delete a webhook (`lsps5.remove_webhook`)
140+
/// - `set_webhook(peer, app_name, url)` -> register or update a webhook [`lsps5.set_webhook`]
141+
/// - `list_webhooks(peer)` -> retrieve all registered webhooks [`lsps5.list_webhooks`]
142+
/// - `remove_webhook(peer, name)` -> delete a webhook [`lsps5.remove_webhook`]
143143
/// - `parse_webhook_notification(...)` -> verify signature, timestamp, replay, and emit event
144144
///
145145
/// [`bLIP-55 / LSPS5 specification`]: https://github.com/lightning/blips/pull/55/files
146+
/// [`lsps5.set_webhook`]: super::msgs::LSPS5Request::SetWebhook
147+
/// [`lsps5.list_webhooks`]: super::msgs::LSPS5Request::ListWebhooks
148+
/// [`lsps5.remove_webhook`]: super::msgs::LSPS5Request::RemoveWebhook
146149
pub struct LSPS5ClientHandler<ES: Deref>
147150
where
148151
ES::Target: EntropySource,
@@ -205,11 +208,11 @@ where
205208
/// A unique `LSPSRequestId` for correlating the asynchronous response.
206209
///
207210
/// Response from the LSP peer will be provided asynchronously through a
208-
/// [`LSPS5Response::SetWebhook`] or [`LSPS5Response::SetWebhookError`] message, and the this client
211+
/// [`LSPS5Response::SetWebhook`] or [`LSPS5Response::SetWebhookError`] message, and this client
209212
/// will then enqueue either a [`WebhookRegistered`] or [`WebhookRegistrationFailed`] event.
210213
///
211214
/// **Note**: Ensure the app name is valid and its length does not exceed [`MAX_APP_NAME_LENGTH`].
212-
/// **Note**: Ensure the URL is valid and its length does not exceed [`MAX_WEBHOOK_URL_LENGTH`]
215+
/// Also ensure the URL is valid, has HTTPS protocol, its length does not exceed [`MAX_WEBHOOK_URL_LENGTH`]
213216
/// and that the URL points to a public host.
214217
///
215218
/// [`MAX_WEBHOOK_URL_LENGTH`]: super::msgs::MAX_WEBHOOK_URL_LENGTH
@@ -259,9 +262,14 @@ where
259262
/// # Returns
260263
/// A unique `LSPSRequestId` for correlating the asynchronous response.
261264
///
265+
/// Response from the LSP peer will be provided asynchronously through a
266+
/// [`LSPS5Response::ListWebhooks`] or [`LSPS5Response::ListWebhooksError`] message, and this client
267+
/// will then enqueue either a [`WebhooksListed`] or [`WebhooksListFailed`] event.
262268
///
263-
/// Response will be provided asynchronously through the event queue as a
264-
/// WebhooksListed or WebhooksListFailed event.
269+
/// [`WebhooksListed`]: super::event::LSPS5ClientEvent::WebhooksListed
270+
/// [`WebhooksListFailed`]: super::event::LSPS5ClientEvent::WebhooksListFailed
271+
/// [`LSPS5Response::ListWebhooks`]: super::msgs::LSPS5Response::ListWebhooks
272+
/// [`LSPS5Response::ListWebhooksError`]: super::msgs::LSPS5Response::ListWebhooksError
265273
pub fn list_webhooks(
266274
&self, counterparty_node_id: PublicKey,
267275
) -> Result<LSPSRequestId, LSPS5Error> {
@@ -291,8 +299,14 @@ where
291299
/// # Returns
292300
/// A unique `LSPSRequestId` for correlating the asynchronous response.
293301
///
294-
/// Response will be provided asynchronously through the event queue as a
295-
/// WebhookRemoved or WebhookRemovalFailed event.
302+
/// Response from the LSP peer will be provided asynchronously through a
303+
/// [`LSPS5Response::RemoveWebhook`] or [`LSPS5Response::RemoveWebhookError`] message, and this client
304+
/// will then enqueue either a [`WebhookRemoved`] or [`WebhookRemovalFailed`] event.
305+
///
306+
/// [`WebhookRemoved`]: super::event::LSPS5ClientEvent::WebhookRemoved
307+
/// [`WebhookRemovalFailed`]: super::event::LSPS5ClientEvent::WebhookRemovalFailed
308+
/// [`LSPS5Response::RemoveWebhook`]: super::msgs::LSPS5Response::RemoveWebhook
309+
/// [`LSPS5Response::RemoveWebhookError`]: super::msgs::LSPS5Response::RemoveWebhookError
296310
pub fn remove_webhook(
297311
&self, counterparty_node_id: PublicKey, app_name: String,
298312
) -> Result<LSPSRequestId, LSPS5Error> {
@@ -512,18 +526,22 @@ where
512526
/// - `signature`: the zbase32-encoded LN signature over timestamp+body.
513527
/// - `notification_json`: the JSON string of the JSON-RPC notification object.
514528
///
515-
/// On success, emits `LSPS5ClientEvent::WebhookNotificationReceived`
516-
/// and returns the parsed `WebhookNotification`.
529+
/// On success, emits [`LSPS5ClientEvent::WebhookNotificationReceived`]
530+
/// and returns the parsed [`WebhookNotification`].
517531
///
518532
/// Failure reasons include:
519533
/// - Timestamp too old (drift > 10 minutes)
520534
/// - Replay attack detected (signature reused)
521535
/// - Invalid signature (crypto check fails)
522536
/// - JSON parse errors for malformed `notification_json`
523537
///
524-
/// Clients should call this method upon receiving a SendWebhookNotifications
538+
/// Clients should call this method upon receiving a [`LSPS5ServiceEvent::SendWebhookNotification`]
525539
/// event, before taking action on the notification. This guarantees that only authentic,
526540
/// non-replayed notifications reach your application.
541+
///
542+
/// [`LSPS5ClientEvent::WebhookNotificationReceived`]: super::event::LSPS5ClientEvent::WebhookNotificationReceived
543+
/// [`LSPS5ServiceEvent::SendWebhookNotification`]: super::event::LSPS5ServiceEvent::SendWebhookNotification
544+
/// [`WebhookNotification`]: super::msgs::WebhookNotification
527545
pub fn parse_webhook_notification(
528546
&self, counterparty_node_id: PublicKey, timestamp: &LSPSDateTime, signature: &str,
529547
notification_json: &str,

lightning-liquidity/src/lsps5/event.rs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,20 @@ pub enum LSPS5ServiceEvent {
6262
/// Client node ID that requested their webhooks.
6363
counterparty_node_id: PublicKey,
6464
/// App names with registered webhooks for this client.
65+
///
66+
/// Each [`app_name`] in this list corresponds to a registered webhook.
67+
///
68+
/// [`app_name`]: super::msgs::LSPS5AppName
6569
app_names: Vec<LSPS5AppName>,
6670
/// The identifier of the issued bLIP-55 / LSPS5 webhook listing request.
6771
///
6872
/// This can be used to track which request this event corresponds to.
6973
request_id: LSPSRequestId,
7074
/// Maximum number of webhooks allowed by LSP per client.
75+
///
76+
/// This is the value defined in [`max_webhooks_per_client`] within the service configuration.
77+
///
78+
/// [`max_webhooks_per_client`]: super::service::LSPS5ServiceConfig::max_webhooks_per_client
7179
max_webhooks: u32,
7280
},
7381

@@ -78,6 +86,12 @@ pub enum LSPS5ServiceEvent {
7886
/// Client node ID that removed the webhook.
7987
counterparty_node_id: PublicKey,
8088
/// App name that was removed.
89+
///
90+
/// This identifies the webhook that was removed.
91+
///
92+
/// **Note**: The [`app_name`] must have been previously registered via `lsps5.set_webhook`.
93+
///
94+
/// [`app_name`]: super::msgs::LSPS5AppName
8195
app_name: LSPS5AppName,
8296
/// The identifier of the issued bLIP-55 / LSPS5 webhook removal request.
8397
///
@@ -96,18 +110,37 @@ pub enum LSPS5ServiceEvent {
96110
/// When the client receives this notification, they will process it and generate a
97111
/// `WebhookNotificationReceived` event on their side. The client will validate the
98112
/// signature using the LSP's node ID to ensure the notification is authentic.
99-
SendWebhookNotifications {
113+
SendWebhookNotification {
100114
/// Client node ID to be notified.
101115
counterparty_node_id: PublicKey,
102116
/// App name to be notified.
117+
///
118+
/// This identifies which webhook registration should be notified.
119+
///
120+
/// **Note**: The [`app_name`] must have been previously registered via `lsps5.set_webhook`.
121+
///
122+
/// [`app_name`]: super::msgs::LSPS5AppName
103123
app_name: LSPS5AppName,
104124
/// URL that to be contacted.
125+
///
126+
/// This is the webhook URL (HTTPS) provided by the client during registration.
127+
///
128+
/// **Note**: The URL must be a valid HTTPS URL that points to a public host.
129+
///
130+
/// [`url`]: super::msgs::LSPS5WebhookUrl
105131
url: LSPS5WebhookUrl,
106132
/// Notification method with its parameters.
133+
///
134+
/// This contains the type of notification and any associated data to be sent to the client.
107135
notification: WebhookNotification,
108136
/// Timestamp of the notification.
137+
///
138+
/// This timestamp is used for signing the notification and must be within 10 minutes
139+
/// of the client's local time for the signature to be accepted.
109140
timestamp: LSPSDateTime,
110141
/// Signature of the notification using the LSP's node ID.
142+
///
143+
/// This signature helps the client verify that the notification was sent by the LSP.
111144
signature: String,
112145
/// Headers to be included in the HTTP POST request.
113146
///
@@ -124,7 +157,14 @@ pub enum LSPS5ClientEvent {
124157
/// A webhook was successfully registered with the LSP.
125158
///
126159
/// This event is triggered when the LSP confirms successful registration
127-
/// of a webhook via `lsps5.set_webhook`.
160+
/// of a webhook via `lsps5.set_webhook`. The [`app_name`] and [`url`]
161+
/// both must respect maximum lengths of [`MAX_APP_NAME_LENGTH`] and
162+
/// [`MAX_WEBHOOK_URL_LENGTH`] respectively, and the [`url`] must use HTTPS.
163+
///
164+
/// [`app_name`]: super::msgs::LSPS5AppName
165+
/// [`url`]: super::msgs::LSPS5WebhookUrl
166+
/// [`MAX_APP_NAME_LENGTH`]: super::msgs::MAX_APP_NAME_LENGTH
167+
/// [`MAX_WEBHOOK_URL_LENGTH`]: super::msgs::MAX_WEBHOOK_URL_LENGTH
128168
WebhookRegistered {
129169
/// The node id of the LSP that confirmed the registration.
130170
counterparty_node_id: PublicKey,
@@ -145,11 +185,22 @@ pub enum LSPS5ClientEvent {
145185
request_id: LSPSRequestId,
146186
},
147187

148-
/// A webhook registration attempt failed
188+
/// A webhook registration attempt failed.
149189
///
150190
/// This event is triggered when the LSP rejects a webhook registration
151-
/// via `lsps5.set_webhook`. This can happen if the app_name or URL is too long,
152-
/// the URL uses an unsupported protocol, or the maximum number of webhooks is reached.
191+
/// via `lsps5.set_webhook`. This can happen if the [`app_name`]
192+
/// exceeds [`MAX_APP_NAME_LENGTH`], the [`url`] exceeds
193+
/// [`MAX_WEBHOOK_URL_LENGTH`], the [`url`] uses an unsupported protocol
194+
/// (HTTPS is required), or the maximum number of webhooks
195+
/// (`max_webhooks_per_client`) has been reached.
196+
///
197+
/// See also [`LSPS5Error::AppNameTooLong`], [`LSPS5Error::WebhookUrlTooLong`],
198+
/// [`LSPS5Error::UnsupportedProtocol`], [`LSPS5Error::TooManyWebhooks`].
199+
///
200+
/// [`app_name`]: super::msgs::LSPS5AppName
201+
/// [`url`]: super::msgs::LSPS5WebhookUrl
202+
/// [`MAX_APP_NAME_LENGTH`]: super::msgs::MAX_APP_NAME_LENGTH
203+
/// [`MAX_WEBHOOK_URL_LENGTH`]: super::msgs::MAX_WEBHOOK_URL_LENGTH
153204
WebhookRegistrationFailed {
154205
/// The node id of the LSP that rejected the registration.
155206
counterparty_node_id: PublicKey,
@@ -167,7 +218,8 @@ pub enum LSPS5ClientEvent {
167218

168219
/// The list of registered webhooks was successfully retrieved.
169220
///
170-
/// This event is triggered when the LSP responds to a `lsps5.list_webhooks` request.
221+
/// This event is triggered when the LSP responds to a
222+
/// `lsps5.list_webhooks` request.
171223
WebhooksListed {
172224
/// The node id of the LSP that provided the list.
173225
counterparty_node_id: PublicKey,
@@ -183,7 +235,8 @@ pub enum LSPS5ClientEvent {
183235

184236
/// The attempt to list webhooks failed.
185237
///
186-
/// This event is triggered when the LSP rejects a `lsps5.list_webhooks` request.
238+
/// This event is triggered when the LSP rejects a
239+
/// `lsps5.list_webhooks` request.
187240
WebhooksListFailed {
188241
/// The node id of the LSP that rejected the request.
189242
counterparty_node_id: PublicKey,
@@ -213,7 +266,13 @@ pub enum LSPS5ClientEvent {
213266
/// A webhook removal attempt failed.
214267
///
215268
/// This event is triggered when the LSP rejects a webhook removal
216-
/// via `lsps5.remove_webhook`. The most common error is app_name_not_found (1010).
269+
/// via `lsps5.remove_webhook`. The most common error is
270+
/// [`LSPS5Error::AppNameNotFound`] (error code [`LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE`]),
271+
/// which indicates the given [`app_name`] was not found.
272+
///
273+
/// [`LSPS5Error::AppNameNotFound`]: super::msgs::LSPS5Error::AppNameNotFound
274+
/// [`LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE`]: super::msgs::LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE
275+
/// [`app_name`]: super::msgs::LSPS5AppName
217276
WebhookRemovalFailed {
218277
/// The node id of the LSP that rejected the removal.
219278
counterparty_node_id: PublicKey,
@@ -230,8 +289,7 @@ pub enum LSPS5ClientEvent {
230289
/// A webhook notification was received from the LSP.
231290
///
232291
/// This event is triggered when the client receives a webhook notification
233-
/// from the LSP. This can happen for various reasons such as incoming payment,
234-
/// expiring HTLCs, liquidity management requests, or incoming onion messages.
292+
/// from the LSP. The signature and timestamp must be verified before use.
235293
WebhookNotificationReceived {
236294
/// LSP node ID that sent the notification.
237295
counterparty_node_id: PublicKey,

lightning-liquidity/src/lsps5/msgs.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ pub const MAX_APP_NAME_LENGTH: usize = 64;
3434
/// Maximum allowed length for a webhook URL (in characters).
3535
pub const MAX_WEBHOOK_URL_LENGTH: usize = 1024;
3636

37-
pub(crate) const LSPS5_TOO_LONG_ERROR_CODE: i32 = 500;
38-
pub(crate) const LSPS5_URL_PARSE_ERROR_CODE: i32 = 501;
39-
pub(crate) const LSPS5_UNSUPPORTED_PROTOCOL_ERROR_CODE: i32 = 502;
40-
pub(crate) const LSPS5_TOO_MANY_WEBHOOKS_ERROR_CODE: i32 = 503;
41-
pub(crate) const LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE: i32 = 1010;
37+
/// Either the app name or the webhook URL is too long.
38+
pub const LSPS5_TOO_LONG_ERROR_CODE: i32 = 500;
39+
/// The provided URL could not be parsed.
40+
pub const LSPS5_URL_PARSE_ERROR_CODE: i32 = 501;
41+
/// The provided URL is not HTTPS.
42+
pub const LSPS5_UNSUPPORTED_PROTOCOL_ERROR_CODE: i32 = 502;
43+
/// The client has too many webhooks registered.
44+
pub const LSPS5_TOO_MANY_WEBHOOKS_ERROR_CODE: i32 = 503;
45+
/// The app name was not found.
46+
pub const LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE: i32 = 1010;
4247

4348
pub(crate) const LSPS5_SET_WEBHOOK_METHOD_NAME: &str = "lsps5.set_webhook";
4449
pub(crate) const LSPS5_LIST_WEBHOOKS_METHOD_NAME: &str = "lsps5.list_webhooks";

0 commit comments

Comments
 (0)