Skip to content

Commit c8e9948

Browse files
Remove peerState if there are no pending requests with that peer
1 parent 76eec66 commit c8e9948

File tree

2 files changed

+117
-30
lines changed

2 files changed

+117
-30
lines changed

lightning-liquidity/src/lsps5/client.rs

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ impl PeerState {
5656
pending_remove_webhook_requests: BoundedMap::new(MAX_PENDING_REQUESTS),
5757
}
5858
}
59+
60+
fn is_empty(&self) -> bool {
61+
self.pending_set_webhook_requests.is_empty()
62+
&& self.pending_list_webhooks_requests.is_empty()
63+
&& self.pending_remove_webhook_requests.is_empty()
64+
}
5965
}
6066

6167
/// Client-side handler for the LSPS5 (bLIP-55) webhook registration protocol.
@@ -345,6 +351,19 @@ where
345351
}
346352
};
347353
self.with_peer_state(*counterparty_node_id, handle_response);
354+
355+
let mut outer_state_lock = self.per_peer_state.write().unwrap();
356+
let should_remove =
357+
if let Some(peer_state_mutex) = outer_state_lock.get(counterparty_node_id) {
358+
let peer_state = peer_state_mutex.lock().unwrap();
359+
peer_state.is_empty()
360+
} else {
361+
false
362+
};
363+
364+
if should_remove {
365+
outer_state_lock.remove(counterparty_node_id);
366+
}
348367
result
349368
}
350369
}
@@ -461,36 +480,6 @@ mod tests {
461480
}
462481
}
463482

464-
#[test]
465-
fn test_handle_response_clears_pending_state() {
466-
let (client, _, _, peer, _) = setup_test_client();
467-
468-
let req_id = client
469-
.set_webhook(peer, "test-app".to_string(), "https://example.com/hook".to_string())
470-
.unwrap();
471-
472-
let response = LSPS5Response::SetWebhook(SetWebhookResponse {
473-
num_webhooks: 1,
474-
max_webhooks: 5,
475-
no_change: false,
476-
});
477-
let response_msg = LSPS5Message::Response(req_id.clone(), response);
478-
479-
{
480-
let outer_state_lock = client.per_peer_state.read().unwrap();
481-
let peer_state = outer_state_lock.get(&peer).unwrap().lock().unwrap();
482-
assert!(peer_state.pending_set_webhook_requests.contains_key(&req_id));
483-
}
484-
485-
client.handle_message(response_msg, &peer).unwrap();
486-
487-
{
488-
let outer_state_lock = client.per_peer_state.read().unwrap();
489-
let peer_state = outer_state_lock.get(&peer).unwrap().lock().unwrap();
490-
assert!(!peer_state.pending_set_webhook_requests.contains_key(&req_id));
491-
}
492-
}
493-
494483
#[test]
495484
fn test_unknown_request_id_handling() {
496485
let (client, _message_queue, _, peer, _) = setup_test_client();
@@ -552,4 +541,68 @@ mod tests {
552541
assert!(peer_state.pending_set_webhook_requests.contains_key(&new_req_id));
553542
}
554543
}
544+
545+
#[test]
546+
fn test_peer_state_cleanup_and_recreation() {
547+
let (client, _, _, peer, _) = setup_test_client();
548+
549+
let set_webhook_req_id = client
550+
.set_webhook(peer, "test-app".to_string(), "https://example.com/hook".to_string())
551+
.unwrap();
552+
553+
let list_webhooks_req_id = client.list_webhooks(peer);
554+
555+
{
556+
let state = client.per_peer_state.read().unwrap();
557+
assert!(state.contains_key(&peer));
558+
let peer_state = state.get(&peer).unwrap().lock().unwrap();
559+
assert!(peer_state.pending_set_webhook_requests.contains_key(&set_webhook_req_id));
560+
assert!(peer_state.pending_list_webhooks_requests.contains_key(&list_webhooks_req_id));
561+
}
562+
563+
let set_webhook_response = LSPS5Response::SetWebhook(SetWebhookResponse {
564+
num_webhooks: 1,
565+
max_webhooks: 5,
566+
no_change: false,
567+
});
568+
let response_msg = LSPS5Message::Response(set_webhook_req_id.clone(), set_webhook_response);
569+
// trigger cleanup but there is still a pending request
570+
// so the peer state should not be removed
571+
client.handle_message(response_msg, &peer).unwrap();
572+
573+
{
574+
let state = client.per_peer_state.read().unwrap();
575+
assert!(state.contains_key(&peer));
576+
let peer_state = state.get(&peer).unwrap().lock().unwrap();
577+
assert!(!peer_state.pending_set_webhook_requests.contains_key(&set_webhook_req_id));
578+
assert!(peer_state.pending_list_webhooks_requests.contains_key(&list_webhooks_req_id));
579+
}
580+
581+
let list_webhooks_response =
582+
LSPS5Response::ListWebhooks(crate::lsps5::msgs::ListWebhooksResponse {
583+
app_names: vec![],
584+
max_webhooks: 5,
585+
});
586+
let response_msg = LSPS5Message::Response(list_webhooks_req_id, list_webhooks_response);
587+
588+
// now the pending request is handled, so the peer state should be removed
589+
client.handle_message(response_msg, &peer).unwrap();
590+
591+
{
592+
let state = client.per_peer_state.read().unwrap();
593+
assert!(!state.contains_key(&peer));
594+
}
595+
596+
// check that it's possible to recreate the peer state by sending a new request
597+
let new_req_id = client
598+
.set_webhook(peer, "test-app-2".to_string(), "https://example.com/hook2".to_string())
599+
.unwrap();
600+
601+
{
602+
let state = client.per_peer_state.read().unwrap();
603+
assert!(state.contains_key(&peer));
604+
let peer_state = state.get(&peer).unwrap().lock().unwrap();
605+
assert!(peer_state.pending_set_webhook_requests.contains_key(&new_req_id));
606+
}
607+
}
555608
}

lightning-liquidity/src/utils/bounded_map.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ where
6666
pub(crate) fn contains_key(&self, key: &K) -> bool {
6767
self.map.contains_key(key)
6868
}
69+
70+
/// Check whether the map is empty.
71+
pub(crate) fn is_empty(&self) -> bool {
72+
self.map.is_empty()
73+
}
6974
}
7075

7176
#[cfg(test)]
@@ -76,20 +81,35 @@ mod tests {
7681
fn new_empty() {
7782
let m: BoundedMap<&str, i32> = BoundedMap::new(3);
7883
assert_eq!(m.len(), 0);
84+
assert!(m.is_empty());
7985
assert!(m.get(&"key").is_none());
8086
}
8187

8288
#[test]
8389
fn insert_within_capacity() {
8490
let mut m = BoundedMap::new(2);
91+
assert!(m.is_empty());
8592
m.insert("a", 1);
8693
m.insert("b", 2);
8794

95+
assert!(!m.is_empty());
8896
assert_eq!(m.len(), 2);
8997
assert_eq!(m.get(&"a"), Some(&1));
9098
assert_eq!(m.get(&"b"), Some(&2));
9199
}
92100

101+
#[test]
102+
fn is_empty() {
103+
let mut m: BoundedMap<&str, i32> = BoundedMap::new(3);
104+
assert!(m.is_empty());
105+
106+
m.insert("a", 1);
107+
assert!(!m.is_empty());
108+
109+
m.remove(&"a");
110+
assert!(m.is_empty());
111+
}
112+
93113
#[test]
94114
fn eviction_fifo() {
95115
let mut m = BoundedMap::new(2);
@@ -98,6 +118,7 @@ mod tests {
98118
m.insert("third", 3); // evicts "first"
99119

100120
assert_eq!(m.len(), 2);
121+
assert!(!m.is_empty());
101122
assert!(m.get(&"first").is_none());
102123
assert_eq!(m.get(&"second"), Some(&2));
103124
assert_eq!(m.get(&"third"), Some(&3));
@@ -111,6 +132,7 @@ mod tests {
111132
m.insert("old", 10); // update moves "old" to back
112133
m.insert("newer", 3); // should evict "new", not "old"
113134

135+
assert!(!m.is_empty());
114136
assert_eq!(m.get(&"old"), Some(&10));
115137
assert!(m.get(&"new").is_none());
116138
assert_eq!(m.get(&"newer"), Some(&3));
@@ -121,38 +143,50 @@ mod tests {
121143
let mut m = BoundedMap::new(2);
122144
m.insert("keep", 1);
123145
m.insert("remove", 2);
146+
assert!(!m.is_empty());
124147

125148
assert_eq!(m.remove(&"remove"), Some(2));
126149
assert_eq!(m.len(), 1);
150+
assert!(!m.is_empty());
127151
assert!(m.get(&"remove").is_none());
128152
assert_eq!(m.get(&"keep"), Some(&1));
153+
154+
m.remove(&"keep");
155+
assert!(m.is_empty());
129156
}
130157

131158
#[test]
132159
fn remove_nonexistent() {
133160
let mut m = BoundedMap::new(2);
134161
m.insert("exists", 1);
162+
assert!(!m.is_empty());
135163

136164
assert_eq!(m.remove(&"missing"), None);
137165
assert_eq!(m.len(), 1);
166+
assert!(!m.is_empty());
138167
}
139168

140169
#[test]
141170
fn zero_capacity() {
142171
let mut m = BoundedMap::new(0);
172+
assert!(m.is_empty());
143173
m.insert("any", 42);
144174

145175
assert_eq!(m.len(), 0);
176+
assert!(m.is_empty());
146177
assert!(m.get(&"any").is_none());
147178
}
148179

149180
#[test]
150181
fn capacity_one() {
151182
let mut m = BoundedMap::new(1);
183+
assert!(m.is_empty());
152184
m.insert("first", 1);
185+
assert!(!m.is_empty());
153186
assert_eq!(m.get(&"first"), Some(&1));
154187

155188
m.insert("second", 2);
189+
assert!(!m.is_empty());
156190
assert!(m.get(&"first").is_none());
157191
assert_eq!(m.get(&"second"), Some(&2));
158192
}

0 commit comments

Comments
 (0)