Skip to content

Commit 5bb9f9b

Browse files
committed
Prefactor: Simplify last_notification_sent tracking
While bLIP-55 describes that the service should wait at least some cooldown between sending notifications per individual `method`, there is nothing that keeps us from simplifying our approach to apply the cooldown to *any* notifications sent, especially since we just reduced the cooldown period to 1 minute elsewhere. Here, we therefore simplify the `last_notification_sent` field to just be a `Option<LSPSDateTime>`.
1 parent e6ca208 commit 5bb9f9b

File tree

2 files changed

+35
-39
lines changed

2 files changed

+35
-39
lines changed

lightning-liquidity/src/lsps5/service.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ struct Webhook {
5454
// Timestamp used for tracking when the webhook was created / updated, or when the last notification was sent.
5555
// This is used to determine if the webhook is stale and should be pruned.
5656
last_used: LSPSDateTime,
57-
// Map of last notification sent timestamps for each notification method.
58-
// This is used to enforce notification cooldowns.
59-
last_notification_sent: HashMap<WebhookNotificationMethod, LSPSDateTime>,
57+
// Timestamp when we last sent a notification to the client. This is used to enforce
58+
// notification cooldowns.
59+
last_notification_sent: Option<LSPSDateTime>,
6060
}
6161

6262
/// Server-side configuration options for LSPS5 Webhook Registration.
@@ -184,11 +184,8 @@ where
184184
match client_webhooks.entry(params.app_name.clone()) {
185185
Entry::Occupied(mut entry) => {
186186
no_change = entry.get().url == params.webhook;
187-
let (last_used, last_notification_sent) = if no_change {
188-
(entry.get().last_used, entry.get().last_notification_sent.clone())
189-
} else {
190-
(now, new_hash_map())
191-
};
187+
let last_used = if no_change { entry.get().last_used } else { now };
188+
let last_notification_sent = entry.get().last_notification_sent;
192189
entry.insert(Webhook {
193190
_app_name: params.app_name.clone(),
194191
url: params.webhook.clone(),
@@ -217,7 +214,7 @@ where
217214
url: params.webhook.clone(),
218215
_counterparty_node_id: counterparty_node_id,
219216
last_used: now,
220-
last_notification_sent: new_hash_map(),
217+
last_notification_sent: None,
221218
});
222219
},
223220
}
@@ -425,11 +422,9 @@ where
425422
// (other than lsps5.webhook_registered) close in time.
426423
if notification.method != WebhookNotificationMethod::LSPS5WebhookRegistered {
427424
let rate_limit_applies = client_webhooks.iter().any(|(_, webhook)| {
428-
webhook
429-
.last_notification_sent
430-
.get(&notification.method)
431-
.map(|last_sent| now.duration_since(&last_sent))
432-
.map_or(false, |duration| duration < NOTIFICATION_COOLDOWN_TIME)
425+
webhook.last_notification_sent.as_ref().map_or(false, |last_sent| {
426+
now.duration_since(&last_sent) < NOTIFICATION_COOLDOWN_TIME
427+
})
433428
});
434429

435430
if rate_limit_applies {
@@ -438,8 +433,8 @@ where
438433
}
439434

440435
for (app_name, webhook) in client_webhooks.iter_mut() {
441-
webhook.last_notification_sent.insert(notification.method.clone(), now);
442436
webhook.last_used = now;
437+
webhook.last_notification_sent = Some(now);
443438
self.send_notification(
444439
client_id,
445440
app_name.clone(),
@@ -527,7 +522,7 @@ where
527522
let mut webhooks = self.webhooks.lock().unwrap();
528523
if let Some(client_webhooks) = webhooks.get_mut(counterparty_node_id) {
529524
for webhook in client_webhooks.values_mut() {
530-
webhook.last_notification_sent.clear();
525+
webhook.last_notification_sent = None;
531526
}
532527
}
533528
}

lightning-liquidity/tests/lsps5_integration_tests.rs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,13 @@ fn webhook_error_handling_test() {
411411

412412
#[test]
413413
fn webhook_notification_delivery_test() {
414+
let mock_time_provider = Arc::new(MockTimeProvider::new(1000));
415+
let time_provider = Arc::<MockTimeProvider>::clone(&mock_time_provider);
414416
let chanmon_cfgs = create_chanmon_cfgs(2);
415417
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
416418
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
417419
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
418-
let (lsps_nodes, validator) = lsps5_test_setup(nodes, Arc::new(DefaultTimeProvider));
420+
let (lsps_nodes, validator) = lsps5_test_setup(nodes, time_provider);
419421
let LSPSNodes { service_node, client_node } = lsps_nodes;
420422
let service_node_id = service_node.inner.node.get_our_node_id();
421423
let client_node_id = client_node.inner.node.get_our_node_id();
@@ -499,6 +501,8 @@ fn webhook_notification_delivery_test() {
499501
"No event should be emitted due to cooldown"
500502
);
501503

504+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
505+
502506
let timeout_block = 700000; // Some future block height
503507
let _ = service_handler.notify_expiry_soon(client_node_id, timeout_block);
504508

@@ -719,11 +723,13 @@ fn idempotency_set_webhook_test() {
719723

720724
#[test]
721725
fn replay_prevention_test() {
726+
let mock_time_provider = Arc::new(MockTimeProvider::new(1000));
727+
let time_provider = Arc::<MockTimeProvider>::clone(&mock_time_provider);
722728
let chanmon_cfgs = create_chanmon_cfgs(2);
723729
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
724730
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
725731
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
726-
let (lsps_nodes, validator) = lsps5_test_setup(nodes, Arc::new(DefaultTimeProvider));
732+
let (lsps_nodes, validator) = lsps5_test_setup(nodes, time_provider);
727733
let LSPSNodes { service_node, client_node } = lsps_nodes;
728734
let service_node_id = service_node.inner.node.get_our_node_id();
729735
let client_node_id = client_node.inner.node.get_our_node_id();
@@ -774,6 +780,9 @@ fn replay_prevention_test() {
774780

775781
// Fill up the validator's signature cache to push out the original signature.
776782
for i in 0..MAX_RECENT_SIGNATURES {
783+
// Advance time, allowing for another notification
784+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
785+
777786
let timeout_block = 700000 + i as u32;
778787
let _ = service_handler.notify_expiry_soon(client_node_id, timeout_block);
779788
let event = service_node.liquidity_manager.next_event().unwrap();
@@ -871,11 +880,13 @@ fn stale_webhooks() {
871880

872881
#[test]
873882
fn test_all_notifications() {
883+
let mock_time_provider = Arc::new(MockTimeProvider::new(1000));
884+
let time_provider = Arc::<MockTimeProvider>::clone(&mock_time_provider);
874885
let chanmon_cfgs = create_chanmon_cfgs(2);
875886
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
876887
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
877888
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
878-
let (lsps_nodes, validator) = lsps5_test_setup(nodes, Arc::new(DefaultTimeProvider));
889+
let (lsps_nodes, validator) = lsps5_test_setup(nodes, time_provider);
879890
let LSPSNodes { service_node, client_node } = lsps_nodes;
880891
let service_node_id = service_node.inner.node.get_our_node_id();
881892
let client_node_id = client_node.inner.node.get_our_node_id();
@@ -894,9 +905,16 @@ fn test_all_notifications() {
894905
// consume initial SendWebhookNotification
895906
let _ = service_node.liquidity_manager.next_event().unwrap();
896907

908+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
897909
let _ = service_handler.notify_onion_message_incoming(client_node_id);
910+
911+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
898912
let _ = service_handler.notify_payment_incoming(client_node_id);
913+
914+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
899915
let _ = service_handler.notify_expiry_soon(client_node_id, 1000);
916+
917+
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
900918
let _ = service_handler.notify_liquidity_management_request(client_node_id);
901919

902920
let expected_notifications = vec![
@@ -1101,24 +1119,7 @@ fn test_send_notifications_and_peer_connected_resets_cooldown() {
11011119
"Should not emit event due to cooldown"
11021120
);
11031121

1104-
// 3. Notification of a different method CAN be sent
1105-
let timeout_block = 424242;
1106-
let _ = service_handler.notify_expiry_soon(client_node_id, timeout_block);
1107-
let event = service_node.liquidity_manager.next_event().unwrap();
1108-
match event {
1109-
LiquidityEvent::LSPS5Service(LSPS5ServiceEvent::SendWebhookNotification {
1110-
notification,
1111-
..
1112-
}) => {
1113-
assert!(matches!(
1114-
notification.method,
1115-
WebhookNotificationMethod::LSPS5ExpirySoon { timeout } if timeout == timeout_block
1116-
));
1117-
},
1118-
_ => panic!("Expected SendWebhookNotification event for expiry_soon"),
1119-
}
1120-
1121-
// 4. Advance time past cooldown and ensure payment_incoming can be sent again
1122+
// 3. Advance time past cooldown and ensure payment_incoming can be sent again
11221123
mock_time_provider.advance_time(NOTIFICATION_COOLDOWN_TIME.as_secs() + 1);
11231124

11241125
let _ = service_handler.notify_payment_incoming(client_node_id);
@@ -1133,7 +1134,7 @@ fn test_send_notifications_and_peer_connected_resets_cooldown() {
11331134
_ => panic!("Expected SendWebhookNotification event after cooldown"),
11341135
}
11351136

1136-
// 5. Can't send payment_incoming notification again immediately after cooldown
1137+
// 4. Can't send payment_incoming notification again immediately after cooldown
11371138
let result = service_handler.notify_payment_incoming(client_node_id);
11381139

11391140
let error = result.unwrap_err();
@@ -1144,7 +1145,7 @@ fn test_send_notifications_and_peer_connected_resets_cooldown() {
11441145
"Should not emit event due to cooldown"
11451146
);
11461147

1147-
// 6. After peer_connected, notification should be sent again immediately
1148+
// 5. After peer_connected, notification should be sent again immediately
11481149
let init_msg = Init {
11491150
features: lightning_types::features::InitFeatures::empty(),
11501151
remote_network_address: None,

0 commit comments

Comments
 (0)