Skip to content

Commit fba3056

Browse files
Remove peerState if there are no pending requests with that peer
1 parent af746fd commit fba3056

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,21 +81,36 @@ 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!(m.contains_key(&"a"));
9098
assert_eq!(m.get(&"a"), Some(&1));
9199
assert_eq!(m.get(&"b"), Some(&2));
92100
}
93101

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

101121
assert_eq!(m.len(), 2);
122+
assert!(!m.is_empty());
102123
assert!(m.get(&"first").is_none());
103124
assert_eq!(m.get(&"second"), Some(&2));
104125
assert_eq!(m.get(&"third"), Some(&3));
@@ -112,6 +133,7 @@ mod tests {
112133
m.insert("old", 10); // update moves "old" to back
113134
m.insert("newer", 3); // should evict "new", not "old"
114135

136+
assert!(!m.is_empty());
115137
assert_eq!(m.get(&"old"), Some(&10));
116138
assert!(m.get(&"new").is_none());
117139
assert_eq!(m.get(&"newer"), Some(&3));
@@ -122,38 +144,50 @@ mod tests {
122144
let mut m = BoundedMap::new(2);
123145
m.insert("keep", 1);
124146
m.insert("remove", 2);
147+
assert!(!m.is_empty());
125148

126149
assert_eq!(m.remove(&"remove"), Some(2));
127150
assert_eq!(m.len(), 1);
151+
assert!(!m.is_empty());
128152
assert!(m.get(&"remove").is_none());
129153
assert_eq!(m.get(&"keep"), Some(&1));
154+
155+
m.remove(&"keep");
156+
assert!(m.is_empty());
130157
}
131158

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

137165
assert_eq!(m.remove(&"missing"), None);
138166
assert_eq!(m.len(), 1);
167+
assert!(!m.is_empty());
139168
}
140169

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

146176
assert_eq!(m.len(), 0);
177+
assert!(m.is_empty());
147178
assert!(m.get(&"any").is_none());
148179
}
149180

150181
#[test]
151182
fn capacity_one() {
152183
let mut m = BoundedMap::new(1);
184+
assert!(m.is_empty());
153185
m.insert("first", 1);
186+
assert!(!m.is_empty());
154187
assert_eq!(m.get(&"first"), Some(&1));
155188

156189
m.insert("second", 2);
190+
assert!(!m.is_empty());
157191
assert!(m.get(&"first").is_none());
158192
assert_eq!(m.get(&"second"), Some(&2));
159193
}

0 commit comments

Comments
 (0)