Skip to content

Commit d2c7c66

Browse files
Get rid of TimeProvider on LSPS5/client.
Client will no longer use a time provider to expire pending requests. Instead, it will just keep the X most recent requests (coming in next commit).
1 parent 61e5819 commit d2c7c66

File tree

2 files changed

+36
-177
lines changed

2 files changed

+36
-177
lines changed

lightning-liquidity/src/lsps5/client.rs

Lines changed: 33 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
1212
use crate::alloc::string::ToString;
1313
use crate::events::EventQueue;
14-
use crate::lsps0::ser::{LSPSDateTime, LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId};
14+
use crate::lsps0::ser::{LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId};
1515
use crate::lsps5::event::LSPS5ClientEvent;
1616
use crate::lsps5::msgs::{
1717
LSPS5Message, LSPS5Request, LSPS5Response, ListWebhooksRequest, RemoveWebhookRequest,
@@ -22,7 +22,6 @@ use crate::message_queue::MessageQueue;
2222
use crate::prelude::{new_hash_map, HashMap};
2323
use crate::sync::{Arc, Mutex, RwLock};
2424
use crate::utils::generate_request_id;
25-
use crate::utils::time::TimeProvider;
2625

2726
use super::msgs::{LSPS5AppName, LSPS5Error, LSPS5WebhookUrl};
2827

@@ -35,76 +34,25 @@ use lightning::util::logger::Level;
3534
use alloc::string::String;
3635

3736
use core::ops::Deref;
38-
use core::time::Duration;
3937

40-
/// Default maximum age in seconds for cached responses (1 hour).
41-
pub const DEFAULT_RESPONSE_MAX_AGE_SECS: u64 = 3600;
42-
43-
#[derive(Debug, Clone)]
38+
#[derive(Debug, Clone, Copy, Default)]
4439
/// Configuration for the LSPS5 client
45-
pub struct LSPS5ClientConfig {
46-
/// Maximum age in seconds for cached responses (default: [`DEFAULT_RESPONSE_MAX_AGE_SECS`]).
47-
pub response_max_age_secs: Duration,
48-
}
40+
pub struct LSPS5ClientConfig {}
4941

50-
impl Default for LSPS5ClientConfig {
51-
fn default() -> Self {
52-
Self { response_max_age_secs: Duration::from_secs(DEFAULT_RESPONSE_MAX_AGE_SECS) }
53-
}
42+
struct PeerState {
43+
pending_set_webhook_requests: HashMap<LSPSRequestId, (LSPS5AppName, LSPS5WebhookUrl)>,
44+
pending_list_webhooks_requests: HashMap<LSPSRequestId, ()>,
45+
pending_remove_webhook_requests: HashMap<LSPSRequestId, LSPS5AppName>,
5446
}
5547

56-
struct PeerState<TP: Deref + Clone>
57-
where
58-
TP::Target: TimeProvider,
59-
{
60-
pending_set_webhook_requests:
61-
HashMap<LSPSRequestId, (LSPS5AppName, LSPS5WebhookUrl, LSPSDateTime)>,
62-
pending_list_webhooks_requests: HashMap<LSPSRequestId, LSPSDateTime>,
63-
pending_remove_webhook_requests: HashMap<LSPSRequestId, (LSPS5AppName, LSPSDateTime)>,
64-
last_cleanup: Option<LSPSDateTime>,
65-
max_age_secs: Duration,
66-
time_provider: TP,
67-
}
68-
69-
impl<TP: Deref + Clone> PeerState<TP>
70-
where
71-
TP::Target: TimeProvider,
72-
{
73-
fn new(max_age_secs: Duration, time_provider: TP) -> Self {
48+
impl PeerState {
49+
fn new() -> Self {
7450
Self {
7551
pending_set_webhook_requests: new_hash_map(),
7652
pending_list_webhooks_requests: new_hash_map(),
7753
pending_remove_webhook_requests: new_hash_map(),
78-
last_cleanup: None,
79-
max_age_secs,
80-
time_provider,
8154
}
8255
}
83-
84-
fn cleanup_expired_responses(&mut self) {
85-
let now =
86-
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());
87-
// Only run cleanup once per minute to avoid excessive processing
88-
const CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
89-
if let Some(last_cleanup) = &self.last_cleanup {
90-
let time_since_last_cleanup = Duration::from_secs(now.abs_diff(&last_cleanup));
91-
if time_since_last_cleanup < CLEANUP_INTERVAL {
92-
return;
93-
}
94-
}
95-
96-
self.last_cleanup = Some(now.clone());
97-
98-
self.pending_set_webhook_requests.retain(|_, (_, _, timestamp)| {
99-
Duration::from_secs(timestamp.abs_diff(&now)) < self.max_age_secs
100-
});
101-
self.pending_list_webhooks_requests.retain(|_, timestamp| {
102-
Duration::from_secs(timestamp.abs_diff(&now)) < self.max_age_secs
103-
});
104-
self.pending_remove_webhook_requests.retain(|_, (_, timestamp)| {
105-
Duration::from_secs(timestamp.abs_diff(&now)) < self.max_age_secs
106-
});
107-
}
10856
}
10957

11058
/// Client-side handler for the LSPS5 (bLIP-55) webhook registration protocol.
@@ -128,51 +76,44 @@ where
12876
/// [`lsps5.list_webhooks`]: super::msgs::LSPS5Request::ListWebhooks
12977
/// [`lsps5.remove_webhook`]: super::msgs::LSPS5Request::RemoveWebhook
13078
/// [`LSPS5Validator`]: super::validator::LSPS5Validator
131-
pub struct LSPS5ClientHandler<ES: Deref, TP: Deref + Clone>
79+
pub struct LSPS5ClientHandler<ES: Deref>
13280
where
13381
ES::Target: EntropySource,
134-
TP::Target: TimeProvider,
13582
{
13683
pending_messages: Arc<MessageQueue>,
13784
pending_events: Arc<EventQueue>,
13885
entropy_source: ES,
139-
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState<TP>>>>,
140-
config: LSPS5ClientConfig,
141-
time_provider: TP,
86+
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
87+
_config: LSPS5ClientConfig,
14288
}
14389

144-
impl<ES: Deref, TP: Deref + Clone> LSPS5ClientHandler<ES, TP>
90+
impl<ES: Deref> LSPS5ClientHandler<ES>
14591
where
14692
ES::Target: EntropySource,
147-
TP::Target: TimeProvider,
14893
{
14994
/// Constructs an `LSPS5ClientHandler`.
150-
pub(crate) fn new_with_time_provider(
95+
pub(crate) fn new(
15196
entropy_source: ES, pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>,
152-
config: LSPS5ClientConfig, time_provider: TP,
97+
_config: LSPS5ClientConfig,
15398
) -> Self {
15499
Self {
155100
pending_messages,
156101
pending_events,
157102
entropy_source,
158103
per_peer_state: RwLock::new(new_hash_map()),
159-
config,
160-
time_provider,
104+
_config,
161105
}
162106
}
163107

164108
fn with_peer_state<F, R>(&self, counterparty_node_id: PublicKey, f: F) -> R
165109
where
166-
F: FnOnce(&mut PeerState<TP>) -> R,
110+
F: FnOnce(&mut PeerState) -> R,
167111
{
168112
let mut outer_state_lock = self.per_peer_state.write().unwrap();
169-
let inner_state_lock = outer_state_lock.entry(counterparty_node_id).or_insert(Mutex::new(
170-
PeerState::new(self.config.response_max_age_secs, self.time_provider.clone()),
171-
));
113+
let inner_state_lock =
114+
outer_state_lock.entry(counterparty_node_id).or_insert(Mutex::new(PeerState::new()));
172115
let mut peer_state_lock = inner_state_lock.lock().unwrap();
173116

174-
peer_state_lock.cleanup_expired_responses();
175-
176117
f(&mut *peer_state_lock)
177118
}
178119

@@ -212,16 +153,9 @@ where
212153
let request_id = generate_request_id(&self.entropy_source);
213154

214155
self.with_peer_state(counterparty_node_id, |peer_state| {
215-
peer_state.pending_set_webhook_requests.insert(
216-
request_id.clone(),
217-
(
218-
app_name.clone(),
219-
lsps_webhook_url.clone(),
220-
LSPSDateTime::new_from_duration_since_epoch(
221-
self.time_provider.duration_since_epoch(),
222-
),
223-
),
224-
);
156+
peer_state
157+
.pending_set_webhook_requests
158+
.insert(request_id.clone(), (app_name.clone(), lsps_webhook_url.clone()));
225159
});
226160

227161
let request =
@@ -251,11 +185,9 @@ where
251185
/// [`LSPS5Response::ListWebhooks`]: super::msgs::LSPS5Response::ListWebhooks
252186
pub fn list_webhooks(&self, counterparty_node_id: PublicKey) -> LSPSRequestId {
253187
let request_id = generate_request_id(&self.entropy_source);
254-
let now =
255-
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());
256188

257189
self.with_peer_state(counterparty_node_id, |peer_state| {
258-
peer_state.pending_list_webhooks_requests.insert(request_id.clone(), now);
190+
peer_state.pending_list_webhooks_requests.insert(request_id.clone(), ());
259191
});
260192

261193
let request = LSPS5Request::ListWebhooks(ListWebhooksRequest {});
@@ -290,13 +222,9 @@ where
290222
let app_name = LSPS5AppName::from_string(app_name)?;
291223

292224
let request_id = generate_request_id(&self.entropy_source);
293-
let now =
294-
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());
295225

296226
self.with_peer_state(counterparty_node_id, |peer_state| {
297-
peer_state
298-
.pending_remove_webhook_requests
299-
.insert(request_id.clone(), (app_name.clone(), now));
227+
peer_state.pending_remove_webhook_requests.insert(request_id.clone(), app_name.clone());
300228
});
301229

302230
let request = LSPS5Request::RemoveWebhook(RemoveWebhookRequest { app_name });
@@ -326,8 +254,8 @@ where
326254
action: ErrorAction::IgnoreAndLog(Level::Debug),
327255
});
328256
let event_queue_notifier = self.pending_events.notifier();
329-
let handle_response = |peer_state: &mut PeerState<TP>| {
330-
if let Some((app_name, webhook_url, _)) =
257+
let handle_response = |peer_state: &mut PeerState| {
258+
if let Some((app_name, webhook_url)) =
331259
peer_state.pending_set_webhook_requests.remove(&request_id)
332260
{
333261
match &response {
@@ -378,7 +306,7 @@ where
378306
});
379307
},
380308
}
381-
} else if let Some((app_name, _)) =
309+
} else if let Some(app_name) =
382310
peer_state.pending_remove_webhook_requests.remove(&request_id)
383311
{
384312
match &response {
@@ -418,10 +346,9 @@ where
418346
}
419347
}
420348

421-
impl<ES: Deref, TP: Deref + Clone> LSPSProtocolMessageHandler for LSPS5ClientHandler<ES, TP>
349+
impl<ES: Deref> LSPSProtocolMessageHandler for LSPS5ClientHandler<ES>
422350
where
423351
ES::Target: EntropySource,
424-
TP::Target: TimeProvider,
425352
{
426353
type ProtocolMessage = LSPS5Message;
427354
const PROTOCOL_NUMBER: Option<u16> = Some(5);
@@ -435,17 +362,15 @@ where
435362

436363
#[cfg(all(test, feature = "time"))]
437364
mod tests {
438-
use core::time::Duration;
439365

440366
use super::*;
441367
use crate::{
442368
lsps0::ser::LSPSRequestId, lsps5::msgs::SetWebhookResponse, tests::utils::TestEntropy,
443-
utils::time::DefaultTimeProvider,
444369
};
445370
use bitcoin::{key::Secp256k1, secp256k1::SecretKey};
446371

447372
fn setup_test_client() -> (
448-
LSPS5ClientHandler<Arc<TestEntropy>, Arc<DefaultTimeProvider>>,
373+
LSPS5ClientHandler<Arc<TestEntropy>>,
449374
Arc<MessageQueue>,
450375
Arc<EventQueue>,
451376
PublicKey,
@@ -454,12 +379,11 @@ mod tests {
454379
let test_entropy_source = Arc::new(TestEntropy {});
455380
let message_queue = Arc::new(MessageQueue::new());
456381
let event_queue = Arc::new(EventQueue::new());
457-
let client = LSPS5ClientHandler::new_with_time_provider(
382+
let client = LSPS5ClientHandler::new(
458383
test_entropy_source,
459384
Arc::clone(&message_queue),
460385
Arc::clone(&event_queue),
461386
LSPS5ClientConfig::default(),
462-
Arc::new(DefaultTimeProvider),
463387
);
464388

465389
let secp = Secp256k1::new();
@@ -510,18 +434,14 @@ mod tests {
510434
let peer_state = outer_state_lock.get(&peer).unwrap().lock().unwrap();
511435
assert_eq!(
512436
peer_state.pending_set_webhook_requests.get(&set_req_id).unwrap(),
513-
&(
514-
lsps5_app_name.clone(),
515-
lsps5_webhook_url,
516-
peer_state.pending_set_webhook_requests.get(&set_req_id).unwrap().2.clone()
517-
)
437+
&(lsps5_app_name.clone(), lsps5_webhook_url)
518438
);
519439

520440
assert!(peer_state.pending_list_webhooks_requests.contains_key(&list_req_id));
521441

522442
assert_eq!(
523-
peer_state.pending_remove_webhook_requests.get(&remove_req_id).unwrap().0,
524-
lsps5_app_name
443+
peer_state.pending_remove_webhook_requests.get(&remove_req_id).unwrap(),
444+
&lsps5_app_name
525445
);
526446
}
527447
}
@@ -556,66 +476,6 @@ mod tests {
556476
}
557477
}
558478

559-
#[test]
560-
fn test_cleanup_expired_responses() {
561-
let (client, _, _, _, _) = setup_test_client();
562-
let time_provider = &client.time_provider;
563-
const OLD_APP_NAME: &str = "test-app-old";
564-
const NEW_APP_NAME: &str = "test-app-new";
565-
const WEBHOOK_URL: &str = "https://example.com/hook";
566-
let lsps5_old_app_name = LSPS5AppName::from_string(OLD_APP_NAME.to_string()).unwrap();
567-
let lsps5_new_app_name = LSPS5AppName::from_string(NEW_APP_NAME.to_string()).unwrap();
568-
let lsps5_webhook_url = LSPS5WebhookUrl::from_string(WEBHOOK_URL.to_string()).unwrap();
569-
let now = time_provider.duration_since_epoch();
570-
let mut peer_state = PeerState::<Arc<DefaultTimeProvider>>::new(
571-
Duration::from_secs(1800),
572-
Arc::clone(time_provider),
573-
);
574-
peer_state.last_cleanup = Some(LSPSDateTime::new_from_duration_since_epoch(
575-
now.checked_sub(Duration::from_secs(120)).unwrap(),
576-
));
577-
578-
let old_request_id = LSPSRequestId("test:request:old".to_string());
579-
let new_request_id = LSPSRequestId("test:request:new".to_string());
580-
581-
// Add an old request (should be removed during cleanup)
582-
peer_state.pending_set_webhook_requests.insert(
583-
old_request_id.clone(),
584-
(
585-
lsps5_old_app_name,
586-
lsps5_webhook_url.clone(),
587-
LSPSDateTime::new_from_duration_since_epoch(
588-
now.checked_sub(Duration::from_secs(7200)).unwrap(),
589-
),
590-
), // 2 hours old
591-
);
592-
593-
// Add a recent request (should be kept)
594-
peer_state.pending_set_webhook_requests.insert(
595-
new_request_id.clone(),
596-
(
597-
lsps5_new_app_name,
598-
lsps5_webhook_url,
599-
LSPSDateTime::new_from_duration_since_epoch(
600-
now.checked_sub(Duration::from_secs(600)).unwrap(),
601-
),
602-
), // 10 minutes old
603-
);
604-
605-
peer_state.cleanup_expired_responses();
606-
607-
assert!(!peer_state.pending_set_webhook_requests.contains_key(&old_request_id));
608-
assert!(peer_state.pending_set_webhook_requests.contains_key(&new_request_id));
609-
610-
let cleanup_age = if let Some(last_cleanup) = peer_state.last_cleanup {
611-
LSPSDateTime::new_from_duration_since_epoch(time_provider.duration_since_epoch())
612-
.abs_diff(&last_cleanup)
613-
} else {
614-
0
615-
};
616-
assert!(cleanup_age < 10);
617-
}
618-
619479
#[test]
620480
fn test_unknown_request_id_handling() {
621481
let (client, _message_queue, _, peer, _) = setup_test_client();

lightning-liquidity/src/manager.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ pub struct LiquidityManager<
181181
lsps2_service_handler: Option<LSPS2ServiceHandler<CM>>,
182182
lsps2_client_handler: Option<LSPS2ClientHandler<ES>>,
183183
lsps5_service_handler: Option<LSPS5ServiceHandler<CM, NS, TP>>,
184-
lsps5_client_handler: Option<LSPS5ClientHandler<ES, TP>>,
184+
lsps5_client_handler: Option<LSPS5ClientHandler<ES>>,
185185
service_config: Option<LiquidityServiceConfig>,
186186
_client_config: Option<LiquidityClientConfig>,
187187
best_block: RwLock<Option<BestBlock>>,
@@ -276,12 +276,11 @@ where
276276

277277
let lsps5_client_handler = client_config.as_ref().and_then(|config| {
278278
config.lsps5_client_config.as_ref().map(|config| {
279-
LSPS5ClientHandler::new_with_time_provider(
279+
LSPS5ClientHandler::new(
280280
entropy_source.clone(),
281281
Arc::clone(&pending_messages),
282282
Arc::clone(&pending_events),
283283
config.clone(),
284-
time_provider.clone(),
285284
)
286285
})
287286
});
@@ -411,7 +410,7 @@ where
411410
/// Returns a reference to the LSPS5 client-side handler.
412411
///
413412
/// The returned hendler allows to initiate the LSPS5 client-side flow. That is, it allows to
414-
pub fn lsps5_client_handler(&self) -> Option<&LSPS5ClientHandler<ES, TP>> {
413+
pub fn lsps5_client_handler(&self) -> Option<&LSPS5ClientHandler<ES>> {
415414
self.lsps5_client_handler.as_ref()
416415
}
417416

0 commit comments

Comments
 (0)