@@ -95,6 +95,12 @@ impl PeerState {
9595 None
9696 }
9797 }
98+
99+ fn is_empty ( & self ) -> bool {
100+ self . pending_set_webhook_requests . is_empty ( )
101+ && self . pending_list_webhooks_requests . is_empty ( )
102+ && self . pending_remove_webhook_requests . is_empty ( )
103+ }
98104}
99105
100106/// Client-side handler for the LSPS5 (bLIP-55) webhook registration protocol.
@@ -389,8 +395,26 @@ where
389395 }
390396 } ;
391397 self . with_peer_state ( * counterparty_node_id, handle_response) ;
398+
399+ self . check_and_remove_empty_peer_state ( counterparty_node_id) ;
400+
392401 result
393402 }
403+
404+ fn check_and_remove_empty_peer_state ( & self , counterparty_node_id : & PublicKey ) {
405+ let mut outer_state_lock = self . per_peer_state . write ( ) . unwrap ( ) ;
406+ let should_remove =
407+ if let Some ( peer_state_mutex) = outer_state_lock. get ( counterparty_node_id) {
408+ let peer_state = peer_state_mutex. lock ( ) . unwrap ( ) ;
409+ peer_state. is_empty ( )
410+ } else {
411+ false
412+ } ;
413+
414+ if should_remove {
415+ outer_state_lock. remove ( counterparty_node_id) ;
416+ }
417+ }
394418}
395419
396420impl < ES : Deref > LSPSProtocolMessageHandler for LSPS5ClientHandler < ES >
@@ -515,36 +539,6 @@ mod tests {
515539 }
516540 }
517541
518- #[ test]
519- fn test_handle_response_clears_pending_state ( ) {
520- let ( client, _, _, peer, _) = setup_test_client ( ) ;
521-
522- let req_id = client
523- . set_webhook ( peer, "test-app" . to_string ( ) , "https://example.com/hook" . to_string ( ) )
524- . unwrap ( ) ;
525-
526- let response = LSPS5Response :: SetWebhook ( SetWebhookResponse {
527- num_webhooks : 1 ,
528- max_webhooks : 5 ,
529- no_change : false ,
530- } ) ;
531- let response_msg = LSPS5Message :: Response ( req_id. clone ( ) , response) ;
532-
533- {
534- let outer_state_lock = client. per_peer_state . read ( ) . unwrap ( ) ;
535- let peer_state = outer_state_lock. get ( & peer) . unwrap ( ) . lock ( ) . unwrap ( ) ;
536- assert ! ( peer_state. pending_set_webhook_requests. contains_key( & req_id) ) ;
537- }
538-
539- client. handle_message ( response_msg, & peer) . unwrap ( ) ;
540-
541- {
542- let outer_state_lock = client. per_peer_state . read ( ) . unwrap ( ) ;
543- let peer_state = outer_state_lock. get ( & peer) . unwrap ( ) . lock ( ) . unwrap ( ) ;
544- assert ! ( !peer_state. pending_set_webhook_requests. contains_key( & req_id) ) ;
545- }
546- }
547-
548542 #[ test]
549543 fn test_unknown_request_id_handling ( ) {
550544 let ( client, _message_queue, _, peer, _) = setup_test_client ( ) ;
@@ -612,4 +606,77 @@ mod tests {
612606 . any( |( id, _) | id == & new_req_id) ) ;
613607 }
614608 }
609+
610+ #[ test]
611+ fn test_peer_state_cleanup_and_recreation ( ) {
612+ let ( client, _, _, peer, _) = setup_test_client ( ) ;
613+
614+ let set_webhook_req_id = client
615+ . set_webhook ( peer, "test-app" . to_string ( ) , "https://example.com/hook" . to_string ( ) )
616+ . unwrap ( ) ;
617+
618+ let list_webhooks_req_id = client. list_webhooks ( peer) ;
619+
620+ {
621+ let state = client. per_peer_state . read ( ) . unwrap ( ) ;
622+ assert ! ( state. contains_key( & peer) ) ;
623+ let peer_state = state. get ( & peer) . unwrap ( ) . lock ( ) . unwrap ( ) ;
624+ assert ! ( peer_state
625+ . pending_set_webhook_requests
626+ . iter( )
627+ . any( |( id, _) | id == & set_webhook_req_id) ) ;
628+ assert ! ( peer_state. pending_list_webhooks_requests. contains( & list_webhooks_req_id) ) ;
629+ }
630+
631+ let set_webhook_response = LSPS5Response :: SetWebhook ( SetWebhookResponse {
632+ num_webhooks : 1 ,
633+ max_webhooks : 5 ,
634+ no_change : false ,
635+ } ) ;
636+ let response_msg = LSPS5Message :: Response ( set_webhook_req_id. clone ( ) , set_webhook_response) ;
637+ // trigger cleanup but there is still a pending request
638+ // so the peer state should not be removed
639+ client. handle_message ( response_msg, & peer) . unwrap ( ) ;
640+
641+ {
642+ let state = client. per_peer_state . read ( ) . unwrap ( ) ;
643+ assert ! ( state. contains_key( & peer) ) ;
644+ let peer_state = state. get ( & peer) . unwrap ( ) . lock ( ) . unwrap ( ) ;
645+ assert ! ( !peer_state
646+ . pending_set_webhook_requests
647+ . iter( )
648+ . any( |( id, _) | id == & set_webhook_req_id) ) ;
649+ assert ! ( peer_state. pending_list_webhooks_requests. contains( & list_webhooks_req_id) ) ;
650+ }
651+
652+ let list_webhooks_response =
653+ LSPS5Response :: ListWebhooks ( crate :: lsps5:: msgs:: ListWebhooksResponse {
654+ app_names : vec ! [ ] ,
655+ max_webhooks : 5 ,
656+ } ) ;
657+ let response_msg = LSPS5Message :: Response ( list_webhooks_req_id, list_webhooks_response) ;
658+
659+ // now the pending request is handled, so the peer state should be removed
660+ client. handle_message ( response_msg, & peer) . unwrap ( ) ;
661+
662+ {
663+ let state = client. per_peer_state . read ( ) . unwrap ( ) ;
664+ assert ! ( !state. contains_key( & peer) ) ;
665+ }
666+
667+ // check that it's possible to recreate the peer state by sending a new request
668+ let new_req_id = client
669+ . set_webhook ( peer, "test-app-2" . to_string ( ) , "https://example.com/hook2" . to_string ( ) )
670+ . unwrap ( ) ;
671+
672+ {
673+ let state = client. per_peer_state . read ( ) . unwrap ( ) ;
674+ assert ! ( state. contains_key( & peer) ) ;
675+ let peer_state = state. get ( & peer) . unwrap ( ) . lock ( ) . unwrap ( ) ;
676+ assert ! ( peer_state
677+ . pending_set_webhook_requests
678+ . iter( )
679+ . any( |( id, _) | id == & new_req_id) ) ;
680+ }
681+ }
615682}
0 commit comments