2
2
3
3
mod common;
4
4
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
+ } ;
6
8
7
9
use lightning_liquidity:: events:: LiquidityEvent ;
8
10
use lightning_liquidity:: lsps0:: ser:: LSPSDateTime ;
@@ -12,10 +14,11 @@ use lightning_liquidity::lsps2::event::LSPS2ServiceEvent;
12
14
use lightning_liquidity:: lsps2:: msgs:: LSPS2RawOpeningFeeParams ;
13
15
use lightning_liquidity:: lsps2:: service:: LSPS2ServiceConfig ;
14
16
use 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 } ;
17
19
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 } ;
19
22
use lightning:: ln:: functional_test_utils:: {
20
23
create_chanmon_cfgs, create_node_cfgs, create_node_chanmgrs,
21
24
} ;
@@ -26,6 +29,7 @@ use lightning::routing::router::{RouteHint, RouteHintHop};
26
29
use lightning:: sign:: NodeSigner ;
27
30
use lightning:: util:: errors:: APIError ;
28
31
use lightning:: util:: logger:: Logger ;
32
+ use lightning:: util:: test_utils:: TestStore ;
29
33
30
34
use lightning_invoice:: { Bolt11Invoice , InvoiceBuilder , RoutingFees } ;
31
35
@@ -42,8 +46,8 @@ use std::time::Duration;
42
46
const MAX_PENDING_REQUESTS_PER_PEER : usize = 10 ;
43
47
const MAX_TOTAL_PENDING_REQUESTS : usize = 1000 ;
44
48
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 > ,
47
51
) -> ( LSPSNodes < ' a , ' b , ' c > , [ u8 ; 32 ] ) {
48
52
let promise_secret = [ 42 ; 32 ] ;
49
53
let lsps2_service_config = LSPS2ServiceConfig { promise_secret } ;
@@ -61,16 +65,26 @@ fn setup_test_lsps2_nodes<'a, 'b, 'c>(
61
65
lsps2_client_config : Some ( lsps2_client_config) ,
62
66
lsps5_client_config : None ,
63
67
} ;
64
- let lsps_nodes = create_service_and_client_nodes (
68
+ let lsps_nodes = create_service_and_client_nodes_with_kv_stores (
65
69
nodes,
66
70
service_config,
67
71
client_config,
68
72
Arc :: new ( DefaultTimeProvider ) ,
73
+ service_kv_store,
74
+ client_kv_store,
69
75
) ;
70
76
71
77
( lsps_nodes, promise_secret)
72
78
}
73
79
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
+
74
88
fn create_jit_invoice (
75
89
node : & LiquidityNode < ' _ , ' _ , ' _ > , service_node_id : PublicKey , intercept_scid : u64 ,
76
90
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() {
887
901
panic ! ( "Unexpected event" ) ;
888
902
}
889
903
}
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