22
33mod common;
44
5- use common:: { create_service_and_client_nodes, get_lsps_message, LSPSNodes , LiquidityNode } ;
5+ use common:: {
6+ create_service_and_client_nodes_with_kv_stores, get_lsps_message, LSPSNodes , LiquidityNode ,
7+ } ;
68
79use lightning_liquidity:: events:: LiquidityEvent ;
810use lightning_liquidity:: lsps0:: ser:: LSPSDateTime ;
@@ -12,10 +14,11 @@ use lightning_liquidity::lsps2::event::LSPS2ServiceEvent;
1214use lightning_liquidity:: lsps2:: msgs:: LSPS2RawOpeningFeeParams ;
1315use lightning_liquidity:: lsps2:: service:: LSPS2ServiceConfig ;
1416use lightning_liquidity:: lsps2:: utils:: is_valid_opening_fee_params;
15- use lightning_liquidity:: utils:: time:: DefaultTimeProvider ;
16- use lightning_liquidity:: { LiquidityClientConfig , LiquidityServiceConfig } ;
17+ use lightning_liquidity:: utils:: time:: { DefaultTimeProvider , TimeProvider } ;
18+ use lightning_liquidity:: { LiquidityClientConfig , LiquidityManagerSync , LiquidityServiceConfig } ;
1719
18- use lightning:: ln:: channelmanager:: { InterceptId , MIN_FINAL_CLTV_EXPIRY_DELTA } ;
20+ use lightning:: chain:: { BestBlock , Filter } ;
21+ use lightning:: ln:: channelmanager:: { ChainParameters , InterceptId , MIN_FINAL_CLTV_EXPIRY_DELTA } ;
1922use lightning:: ln:: functional_test_utils:: {
2023 create_chanmon_cfgs, create_node_cfgs, create_node_chanmgrs,
2124} ;
@@ -26,6 +29,7 @@ use lightning::routing::router::{RouteHint, RouteHintHop};
2629use lightning:: sign:: NodeSigner ;
2730use lightning:: util:: errors:: APIError ;
2831use lightning:: util:: logger:: Logger ;
32+ use lightning:: util:: test_utils:: TestStore ;
2933
3034use lightning_invoice:: { Bolt11Invoice , InvoiceBuilder , RoutingFees } ;
3135
@@ -42,8 +46,8 @@ use std::time::Duration;
4246const MAX_PENDING_REQUESTS_PER_PEER : usize = 10 ;
4347const MAX_TOTAL_PENDING_REQUESTS : usize = 1000 ;
4448
45- fn setup_test_lsps2_nodes < ' a , ' b , ' c > (
46- nodes : Vec < Node < ' a , ' b , ' c > > ,
49+ fn setup_test_lsps2_nodes_with_kv_stores < ' a , ' b , ' c > (
50+ nodes : Vec < Node < ' a , ' b , ' c > > , service_kv_store : Arc < TestStore > , client_kv_store : Arc < TestStore > ,
4751) -> ( LSPSNodes < ' a , ' b , ' c > , [ u8 ; 32 ] ) {
4852 let promise_secret = [ 42 ; 32 ] ;
4953 let lsps2_service_config = LSPS2ServiceConfig { promise_secret } ;
@@ -61,16 +65,26 @@ fn setup_test_lsps2_nodes<'a, 'b, 'c>(
6165 lsps2_client_config : Some ( lsps2_client_config) ,
6266 lsps5_client_config : None ,
6367 } ;
64- let lsps_nodes = create_service_and_client_nodes (
68+ let lsps_nodes = create_service_and_client_nodes_with_kv_stores (
6569 nodes,
6670 service_config,
6771 client_config,
6872 Arc :: new ( DefaultTimeProvider ) ,
73+ service_kv_store,
74+ client_kv_store,
6975 ) ;
7076
7177 ( lsps_nodes, promise_secret)
7278}
7379
80+ fn setup_test_lsps2_nodes < ' a , ' b , ' c > (
81+ nodes : Vec < Node < ' a , ' b , ' c > > ,
82+ ) -> ( LSPSNodes < ' a , ' b , ' c > , [ u8 ; 32 ] ) {
83+ let service_kv_store = Arc :: new ( TestStore :: new ( false ) ) ;
84+ let client_kv_store = Arc :: new ( TestStore :: new ( false ) ) ;
85+ setup_test_lsps2_nodes_with_kv_stores ( nodes, service_kv_store, client_kv_store)
86+ }
87+
7488fn create_jit_invoice (
7589 node : & LiquidityNode < ' _ , ' _ , ' _ > , service_node_id : PublicKey , intercept_scid : u64 ,
7690 cltv_expiry_delta : u32 , payment_size_msat : Option < u64 > , description : & str , expiry_secs : u32 ,
@@ -887,3 +901,199 @@ fn opening_fee_params_menu_is_sorted_by_spec() {
887901 panic ! ( "Unexpected event" ) ;
888902 }
889903}
904+
905+ #[ test]
906+ fn lsps2_service_handler_persistence_across_restarts ( ) {
907+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
908+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
909+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
910+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
911+
912+ // Create shared KV store for service node that will persist across restarts
913+ let service_kv_store = Arc :: new ( TestStore :: new ( false ) ) ;
914+ let client_kv_store = Arc :: new ( TestStore :: new ( false ) ) ;
915+
916+ let promise_secret = [ 42 ; 32 ] ;
917+ let service_config = LiquidityServiceConfig {
918+ #[ cfg( lsps1_service) ]
919+ lsps1_service_config : None ,
920+ lsps2_service_config : Some ( LSPS2ServiceConfig { promise_secret } ) ,
921+ lsps5_service_config : None ,
922+ advertise_service : true ,
923+ } ;
924+ let time_provider: Arc < dyn TimeProvider + Send + Sync > = Arc :: new ( DefaultTimeProvider ) ;
925+
926+ // Variables to carry state between scopes
927+ let user_channel_id = 42 ;
928+ let cltv_expiry_delta = 144 ;
929+ let intercept_scid;
930+ let client_node_id;
931+
932+ // First scope: Setup, persistence, and dropping of all node objects
933+ {
934+ // Use the helper function with custom KV stores
935+ let ( lsps_nodes, _) = setup_test_lsps2_nodes_with_kv_stores (
936+ nodes,
937+ Arc :: clone ( & service_kv_store) ,
938+ client_kv_store,
939+ ) ;
940+ let LSPSNodes { service_node, client_node } = lsps_nodes;
941+
942+ let service_node_id = service_node. inner . node . get_our_node_id ( ) ;
943+ client_node_id = client_node. inner . node . get_our_node_id ( ) ;
944+
945+ let client_handler = client_node. liquidity_manager . lsps2_client_handler ( ) . unwrap ( ) ;
946+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
947+
948+ // Set up a JIT channel request to create state that needs persistence
949+ let _get_info_request_id = client_handler. request_opening_params ( service_node_id, None ) ;
950+ let get_info_request = get_lsps_message ! ( client_node, service_node_id) ;
951+ service_node
952+ . liquidity_manager
953+ . handle_custom_message ( get_info_request, client_node_id)
954+ . unwrap ( ) ;
955+
956+ let get_info_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
957+ let request_id = match get_info_event {
958+ LiquidityEvent :: LSPS2Service ( LSPS2ServiceEvent :: GetInfo { request_id, .. } ) => {
959+ request_id
960+ } ,
961+ _ => panic ! ( "Unexpected event" ) ,
962+ } ;
963+
964+ let raw_opening_params = LSPS2RawOpeningFeeParams {
965+ min_fee_msat : 100 ,
966+ proportional : 21 ,
967+ valid_until : LSPSDateTime :: from_str ( "2035-05-20T08:30:45Z" ) . unwrap ( ) ,
968+ min_lifetime : 144 ,
969+ max_client_to_self_delay : 128 ,
970+ min_payment_size_msat : 1 ,
971+ max_payment_size_msat : 100_000_000 ,
972+ } ;
973+
974+ service_handler
975+ . opening_fee_params_generated (
976+ & client_node_id,
977+ request_id. clone ( ) ,
978+ vec ! [ raw_opening_params] ,
979+ )
980+ . unwrap ( ) ;
981+
982+ let get_info_response = get_lsps_message ! ( service_node, client_node_id) ;
983+ client_node
984+ . liquidity_manager
985+ . handle_custom_message ( get_info_response, service_node_id)
986+ . unwrap ( ) ;
987+
988+ let opening_fee_params = match client_node. liquidity_manager . next_event ( ) . unwrap ( ) {
989+ LiquidityEvent :: LSPS2Client ( LSPS2ClientEvent :: OpeningParametersReady {
990+ opening_fee_params_menu,
991+ ..
992+ } ) => opening_fee_params_menu. first ( ) . unwrap ( ) . clone ( ) ,
993+ _ => panic ! ( "Unexpected event" ) ,
994+ } ;
995+
996+ // Client makes a buy request
997+ let payment_size_msat = Some ( 1_000_000 ) ;
998+ let buy_request_id = client_handler
999+ . select_opening_params ( service_node_id, payment_size_msat, opening_fee_params. clone ( ) )
1000+ . unwrap ( ) ;
1001+
1002+ let buy_request = get_lsps_message ! ( client_node, service_node_id) ;
1003+ service_node. liquidity_manager . handle_custom_message ( buy_request, client_node_id) . unwrap ( ) ;
1004+
1005+ let buy_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
1006+ if let LiquidityEvent :: LSPS2Service ( LSPS2ServiceEvent :: BuyRequest { request_id, .. } ) =
1007+ buy_event
1008+ {
1009+ assert_eq ! ( request_id, buy_request_id) ;
1010+ } else {
1011+ panic ! ( "Unexpected event" ) ;
1012+ }
1013+
1014+ // Service responds with invoice parameters, creating persistent channel state
1015+ intercept_scid = service_node. node . get_intercept_scid ( ) ;
1016+ let client_trusts_lsp = true ;
1017+
1018+ service_handler
1019+ . invoice_parameters_generated (
1020+ & client_node_id,
1021+ buy_request_id. clone ( ) ,
1022+ intercept_scid,
1023+ cltv_expiry_delta,
1024+ client_trusts_lsp,
1025+ user_channel_id,
1026+ )
1027+ . unwrap ( ) ;
1028+
1029+ let buy_response = get_lsps_message ! ( service_node, client_node_id) ;
1030+ client_node. liquidity_manager . handle_custom_message ( buy_response, service_node_id) . unwrap ( ) ;
1031+
1032+ let _invoice_params_event = client_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
1033+
1034+ // Trigger persistence by calling persist
1035+ service_node. liquidity_manager . persist ( ) . unwrap ( ) ;
1036+
1037+ // All node objects are dropped at the end of this scope
1038+ }
1039+
1040+ // Second scope: Recovery from persisted store and verification
1041+ {
1042+ // Create fresh node configurations for restart to avoid connection conflicts
1043+ let node_chanmgrs_restart = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1044+ let nodes_restart = create_network ( 2 , & node_cfgs, & node_chanmgrs_restart) ;
1045+
1046+ // Create a new LiquidityManager with the same configuration and KV store to simulate restart
1047+ let chain_params = ChainParameters {
1048+ network : Network :: Testnet ,
1049+ best_block : BestBlock :: from_network ( Network :: Testnet ) ,
1050+ } ;
1051+
1052+ let restarted_service_lm = LiquidityManagerSync :: new_with_custom_time_provider (
1053+ nodes_restart[ 0 ] . keys_manager ,
1054+ nodes_restart[ 0 ] . keys_manager ,
1055+ nodes_restart[ 0 ] . node ,
1056+ None :: < Arc < dyn Filter + Send + Sync > > ,
1057+ Some ( chain_params) ,
1058+ service_kv_store,
1059+ Some ( service_config) ,
1060+ None ,
1061+ time_provider,
1062+ )
1063+ . unwrap ( ) ;
1064+
1065+ let restarted_service_handler = restarted_service_lm. lsps2_service_handler ( ) . unwrap ( ) ;
1066+
1067+ // Verify the state was properly restored by checking if the channel exists
1068+ // We can do this by trying to call htlc_intercepted which should succeed if state was restored
1069+ let htlc_amount_msat = 1_000_000 ;
1070+ let intercept_id = InterceptId ( [ 0 ; 32 ] ) ;
1071+ let payment_hash = PaymentHash ( [ 1 ; 32 ] ) ;
1072+
1073+ let result = restarted_service_handler. htlc_intercepted (
1074+ intercept_scid,
1075+ intercept_id,
1076+ htlc_amount_msat,
1077+ payment_hash,
1078+ ) ;
1079+
1080+ // This should succeed if the channel state was properly restored
1081+ assert ! ( result. is_ok( ) , "HTLC interception should succeed with restored state" ) ;
1082+
1083+ // Check that we get an OpenChannel event, confirming the state was restored correctly
1084+ let event = restarted_service_lm. next_event ( ) ;
1085+ assert ! ( event. is_some( ) , "Should have an event after HTLC interception" ) ;
1086+
1087+ if let Some ( LiquidityEvent :: LSPS2Service ( LSPS2ServiceEvent :: OpenChannel {
1088+ user_channel_id : restored_channel_id,
1089+ intercept_scid : restored_scid,
1090+ ..
1091+ } ) ) = event
1092+ {
1093+ assert_eq ! ( restored_channel_id, user_channel_id) ;
1094+ assert_eq ! ( restored_scid, intercept_scid) ;
1095+ } else {
1096+ panic ! ( "Expected OpenChannel event after restart" ) ;
1097+ }
1098+ }
1099+ }
0 commit comments