@@ -329,10 +329,14 @@ where
329
329
/// This builds a [`WebhookNotificationMethod::LSPS5PaymentIncoming`] webhook notification, signs it with your
330
330
/// node key, and enqueues HTTP POSTs to all registered webhook URLs for that client.
331
331
///
332
+ /// This may fail if a similar notification was sent too recently,
333
+ /// violating the notification cooldown period defined in [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`].
334
+ ///
332
335
/// # Parameters
333
336
/// - `client_id`: the client's node-ID whose webhooks should be invoked.
334
337
///
335
338
/// [`WebhookNotificationMethod::LSPS5PaymentIncoming`]: super::msgs::WebhookNotificationMethod::LSPS5PaymentIncoming
339
+ /// [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`]: super::service::DEFAULT_NOTIFICATION_COOLDOWN_HOURS
336
340
pub fn notify_payment_incoming ( & self , client_id : PublicKey ) -> Result < ( ) , LSPS5ProtocolError > {
337
341
let notification = WebhookNotification :: payment_incoming ( ) ;
338
342
self . send_notifications_to_client_webhooks ( client_id, notification)
@@ -346,11 +350,15 @@ where
346
350
/// the `timeout` block height, signs it, and enqueues HTTP POSTs to the client's
347
351
/// registered webhooks.
348
352
///
353
+ /// This may fail if a similar notification was sent too recently,
354
+ /// violating the notification cooldown period defined in [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`].
355
+ ///
349
356
/// # Parameters
350
357
/// - `client_id`: the client's node-ID whose webhooks should be invoked.
351
358
/// - `timeout`: the block height at which the channel contract will expire.
352
359
///
353
360
/// [`WebhookNotificationMethod::LSPS5ExpirySoon`]: super::msgs::WebhookNotificationMethod::LSPS5ExpirySoon
361
+ /// [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`]: super::service::DEFAULT_NOTIFICATION_COOLDOWN_HOURS
354
362
pub fn notify_expiry_soon (
355
363
& self , client_id : PublicKey , timeout : u32 ,
356
364
) -> Result < ( ) , LSPS5ProtocolError > {
@@ -364,10 +372,14 @@ where
364
372
/// liquidity for `client_id`. Builds a [`WebhookNotificationMethod::LSPS5LiquidityManagementRequest`] notification,
365
373
/// signs it, and sends it to all of the client's registered webhook URLs.
366
374
///
375
+ /// This may fail if a similar notification was sent too recently,
376
+ /// violating the notification cooldown period defined in [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`].
377
+ ///
367
378
/// # Parameters
368
379
/// - `client_id`: the client's node-ID whose webhooks should be invoked.
369
380
///
370
381
/// [`WebhookNotificationMethod::LSPS5LiquidityManagementRequest`]: super::msgs::WebhookNotificationMethod::LSPS5LiquidityManagementRequest
382
+ /// [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`]: super::service::DEFAULT_NOTIFICATION_COOLDOWN_HOURS
371
383
pub fn notify_liquidity_management_request (
372
384
& self , client_id : PublicKey ,
373
385
) -> Result < ( ) , LSPS5ProtocolError > {
@@ -381,10 +393,14 @@ where
381
393
/// for `client_id` while the client is offline. Builds a [`WebhookNotificationMethod::LSPS5OnionMessageIncoming`]
382
394
/// notification, signs it, and enqueues HTTP POSTs to each registered webhook.
383
395
///
396
+ /// This may fail if a similar notification was sent too recently,
397
+ /// violating the notification cooldown period defined in [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`].
398
+ ///
384
399
/// # Parameters
385
400
/// - `client_id`: the client's node-ID whose webhooks should be invoked.
386
401
///
387
402
/// [`WebhookNotificationMethod::LSPS5OnionMessageIncoming`]: super::msgs::WebhookNotificationMethod::LSPS5OnionMessageIncoming
403
+ /// [`DEFAULT_NOTIFICATION_COOLDOWN_HOURS`]: super::service::DEFAULT_NOTIFICATION_COOLDOWN_HOURS
388
404
pub fn notify_onion_message_incoming (
389
405
& self , client_id : PublicKey ,
390
406
) -> Result < ( ) , LSPS5ProtocolError > {
@@ -405,23 +421,34 @@ where
405
421
let now =
406
422
LSPSDateTime :: new_from_duration_since_epoch ( self . time_provider . duration_since_epoch ( ) ) ;
407
423
408
- for ( app_name , webhook ) in client_webhooks . iter_mut ( ) {
409
- if webhook
410
- . last_notification_sent
411
- . get ( & notification . method )
412
- . map ( |last_sent| now . clone ( ) . abs_diff ( & last_sent ) )
413
- . map_or ( true , |duration| duration >= DEFAULT_NOTIFICATION_COOLDOWN_HOURS . as_secs ( ) )
414
- {
415
- webhook . last_notification_sent . insert ( notification . method . clone ( ) , now. clone ( ) ) ;
416
- webhook . last_used = now . clone ( ) ;
417
- self . send_notification (
418
- client_id ,
419
- app_name . clone ( ) ,
420
- webhook . url . clone ( ) ,
421
- notification . clone ( ) ,
422
- ) ? ;
424
+ // We must avoid sending multiple notifications of the same method
425
+ // (other than lsps5.webhook_registered) close in time.
426
+ if notification . method != WebhookNotificationMethod :: LSPS5WebhookRegistered {
427
+ let rate_limit_applies = client_webhooks . iter ( ) . any ( | ( _ , webhook ) | {
428
+ webhook
429
+ . last_notification_sent
430
+ . get ( & notification . method )
431
+ . map ( |last_sent| now. abs_diff ( & last_sent ) )
432
+ . map_or ( false , |duration| {
433
+ duration < DEFAULT_NOTIFICATION_COOLDOWN_HOURS . as_secs ( )
434
+ } )
435
+ } ) ;
436
+
437
+ if rate_limit_applies {
438
+ return Err ( LSPS5ProtocolError :: SlowDownError ) ;
423
439
}
424
440
}
441
+
442
+ for ( app_name, webhook) in client_webhooks. iter_mut ( ) {
443
+ webhook. last_notification_sent . insert ( notification. method . clone ( ) , now. clone ( ) ) ;
444
+ webhook. last_used = now. clone ( ) ;
445
+ self . send_notification (
446
+ client_id,
447
+ app_name. clone ( ) ,
448
+ webhook. url . clone ( ) ,
449
+ notification. clone ( ) ,
450
+ ) ?;
451
+ }
425
452
Ok ( ( ) )
426
453
}
427
454
0 commit comments