@@ -23,7 +23,8 @@ use common::{
2323 expect_splice_pending_event, generate_blocks_and_wait, open_channel, open_channel_push_amt,
2424 premine_and_distribute_funds, premine_blocks, prepare_rbf, random_config,
2525 random_listening_addresses, setup_bitcoind_and_electrsd, setup_builder, setup_node,
26- setup_node_for_async_payments, setup_two_nodes, wait_for_tx, TestChainSource , TestSyncStore ,
26+ setup_node_for_async_payments, setup_two_nodes, wait_for_tx, TestChainSource , TestStoreType ,
27+ TestSyncStore ,
2728} ;
2829use ldk_node:: config:: { AsyncPaymentsRole , EsploraSyncConfig } ;
2930use ldk_node:: liquidity:: LSPS2ServiceConfig ;
@@ -2317,3 +2318,121 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
23172318 Some ( 6 )
23182319 ) ;
23192320}
2321+
2322+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
2323+ async fn payment_persistence_after_restart ( ) {
2324+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
2325+ let chain_source = TestChainSource :: Esplora ( & electrsd) ;
2326+
2327+ // Setup nodes manually so we can restart node_a with the same config
2328+ println ! ( "== Node A ==" ) ;
2329+ let mut config_a = random_config ( true ) ;
2330+ config_a. store_type = TestStoreType :: Sqlite ;
2331+
2332+ let num_payments = 200 ;
2333+ let payment_amount_msat = 1_000_000 ; // 1000 sats per payment
2334+
2335+ {
2336+ let node_a = setup_node ( & chain_source, config_a. clone ( ) ) ;
2337+
2338+ println ! ( "\n == Node B ==" ) ;
2339+ let config_b = random_config ( true ) ;
2340+ let node_b = setup_node ( & chain_source, config_b) ;
2341+
2342+ let addr_a = node_a. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2343+ let addr_b = node_b. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2344+
2345+ // Premine sufficient funds for a large channel and many payments
2346+ let premine_amount_sat = 10_000_000 ;
2347+ premine_and_distribute_funds (
2348+ & bitcoind. client ,
2349+ & electrsd. client ,
2350+ vec ! [ addr_a, addr_b] ,
2351+ Amount :: from_sat ( premine_amount_sat) ,
2352+ )
2353+ . await ;
2354+ node_a. sync_wallets ( ) . unwrap ( ) ;
2355+ node_b. sync_wallets ( ) . unwrap ( ) ;
2356+ assert_eq ! ( node_a. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2357+ assert_eq ! ( node_b. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2358+
2359+ // Open a large channel from node_a to node_b
2360+ let channel_amount_sat = 5_000_000 ;
2361+ open_channel ( & node_a, & node_b, channel_amount_sat, true , & electrsd) . await ;
2362+ generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
2363+ node_a. sync_wallets ( ) . unwrap ( ) ;
2364+ node_b. sync_wallets ( ) . unwrap ( ) ;
2365+ expect_channel_ready_event ! ( node_a, node_b. node_id( ) ) ;
2366+ expect_channel_ready_event ! ( node_b, node_a. node_id( ) ) ;
2367+
2368+ // Send 200 payments from node_a to node_b
2369+ println ! ( "\n Sending {} payments from A to B..." , num_payments) ;
2370+ let invoice_description =
2371+ Bolt11InvoiceDescription :: Direct ( Description :: new ( String :: from ( "test" ) ) . unwrap ( ) ) ;
2372+
2373+ for i in 0 ..num_payments {
2374+ let invoice = node_b
2375+ . bolt11_payment ( )
2376+ . receive ( payment_amount_msat, & invoice_description. clone ( ) . into ( ) , 3600 )
2377+ . unwrap ( ) ;
2378+ let payment_id = node_a. bolt11_payment ( ) . send ( & invoice, None ) . unwrap ( ) ;
2379+ expect_event ! ( node_a, PaymentSuccessful ) ;
2380+ expect_event ! ( node_b, PaymentReceived ) ;
2381+
2382+ if ( i + 1 ) % 50 == 0 {
2383+ println ! ( "Completed {} payments" , i + 1 ) ;
2384+ }
2385+
2386+ // Verify payment succeeded
2387+ assert_eq ! ( node_a. payment( & payment_id) . unwrap( ) . status, PaymentStatus :: Succeeded ) ;
2388+ }
2389+ println ! ( "All {} payments completed successfully" , num_payments) ;
2390+
2391+ // Verify node_a has 200 outbound Bolt11 payments before shutdown
2392+ let outbound_payments_before = node_a. list_payments_with_filter ( |p| {
2393+ p. direction == PaymentDirection :: Outbound
2394+ && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2395+ } ) ;
2396+ assert_eq ! ( outbound_payments_before. len( ) , num_payments) ;
2397+
2398+ // Shut down both nodes
2399+ println ! ( "\n Shutting down nodes..." ) ;
2400+ node_a. stop ( ) . unwrap ( ) ;
2401+ node_b. stop ( ) . unwrap ( ) ;
2402+ }
2403+
2404+ // Restart node_a with the same config
2405+ println ! ( "\n Restarting node A..." ) ;
2406+ let restarted_node_a = setup_node ( & chain_source, config_a) ;
2407+
2408+ // Assert all 200 payments are still in the store
2409+ let outbound_payments_after = restarted_node_a. list_payments_with_filter ( |p| {
2410+ p. direction == PaymentDirection :: Outbound && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2411+ } ) ;
2412+ assert_eq ! (
2413+ outbound_payments_after. len( ) ,
2414+ num_payments,
2415+ "Expected {} payments after restart, found {}" ,
2416+ num_payments,
2417+ outbound_payments_after. len( )
2418+ ) ;
2419+
2420+ // Verify all payments have the correct status
2421+ for payment in & outbound_payments_after {
2422+ assert_eq ! (
2423+ payment. status,
2424+ PaymentStatus :: Succeeded ,
2425+ "Payment {:?} has unexpected status {:?}" ,
2426+ payment. id,
2427+ payment. status
2428+ ) ;
2429+ assert_eq ! ( payment. amount_msat, Some ( payment_amount_msat) ) ;
2430+ }
2431+
2432+ println ! (
2433+ "Successfully verified {} payments persisted after restart" ,
2434+ outbound_payments_after. len( )
2435+ ) ;
2436+
2437+ restarted_node_a. stop ( ) . unwrap ( ) ;
2438+ }
0 commit comments