@@ -51,7 +51,8 @@ use common::{
5151 expect_splice_pending_event, generate_blocks_and_wait, open_channel, open_channel_push_amt,
5252 premine_and_distribute_funds, premine_blocks, prepare_rbf, random_config,
5353 random_listening_addresses, setup_bitcoind_and_electrsd, setup_builder, setup_node,
54- setup_node_for_async_payments, setup_two_nodes, wait_for_tx, TestChainSource , TestSyncStore ,
54+ setup_node_for_async_payments, setup_two_nodes, wait_for_tx, TestChainSource , TestStoreType ,
55+ TestSyncStore ,
5556} ;
5657use ldk_node:: config:: { AsyncPaymentsRole , EsploraSyncConfig } ;
5758use ldk_node:: liquidity:: LSPS2ServiceConfig ;
@@ -2617,3 +2618,121 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
26172618 Some ( 6 )
26182619 ) ;
26192620}
2621+
2622+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
2623+ async fn payment_persistence_after_restart ( ) {
2624+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
2625+ let chain_source = TestChainSource :: Esplora ( & electrsd) ;
2626+
2627+ // Setup nodes manually so we can restart node_a with the same config
2628+ println ! ( "== Node A ==" ) ;
2629+ let mut config_a = random_config ( true ) ;
2630+ config_a. store_type = TestStoreType :: Sqlite ;
2631+
2632+ let num_payments = 200 ;
2633+ let payment_amount_msat = 1_000_000 ; // 1000 sats per payment
2634+
2635+ {
2636+ let node_a = setup_node ( & chain_source, config_a. clone ( ) ) ;
2637+
2638+ println ! ( "\n == Node B ==" ) ;
2639+ let config_b = random_config ( true ) ;
2640+ let node_b = setup_node ( & chain_source, config_b) ;
2641+
2642+ let addr_a = node_a. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2643+ let addr_b = node_b. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2644+
2645+ // Premine sufficient funds for a large channel and many payments
2646+ let premine_amount_sat = 10_000_000 ;
2647+ premine_and_distribute_funds (
2648+ & bitcoind. client ,
2649+ & electrsd. client ,
2650+ vec ! [ addr_a, addr_b] ,
2651+ Amount :: from_sat ( premine_amount_sat) ,
2652+ )
2653+ . await ;
2654+ node_a. sync_wallets ( ) . unwrap ( ) ;
2655+ node_b. sync_wallets ( ) . unwrap ( ) ;
2656+ assert_eq ! ( node_a. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2657+ assert_eq ! ( node_b. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2658+
2659+ // Open a large channel from node_a to node_b
2660+ let channel_amount_sat = 5_000_000 ;
2661+ open_channel ( & node_a, & node_b, channel_amount_sat, true , & electrsd) . await ;
2662+ generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
2663+ node_a. sync_wallets ( ) . unwrap ( ) ;
2664+ node_b. sync_wallets ( ) . unwrap ( ) ;
2665+ expect_channel_ready_event ! ( node_a, node_b. node_id( ) ) ;
2666+ expect_channel_ready_event ! ( node_b, node_a. node_id( ) ) ;
2667+
2668+ // Send 200 payments from node_a to node_b
2669+ println ! ( "\n Sending {} payments from A to B..." , num_payments) ;
2670+ let invoice_description =
2671+ Bolt11InvoiceDescription :: Direct ( Description :: new ( String :: from ( "test" ) ) . unwrap ( ) ) ;
2672+
2673+ for i in 0 ..num_payments {
2674+ let invoice = node_b
2675+ . bolt11_payment ( )
2676+ . receive ( payment_amount_msat, & invoice_description. clone ( ) . into ( ) , 3600 )
2677+ . unwrap ( ) ;
2678+ let payment_id = node_a. bolt11_payment ( ) . send ( & invoice, None ) . unwrap ( ) ;
2679+ expect_event ! ( node_a, PaymentSuccessful ) ;
2680+ expect_event ! ( node_b, PaymentReceived ) ;
2681+
2682+ if ( i + 1 ) % 50 == 0 {
2683+ println ! ( "Completed {} payments" , i + 1 ) ;
2684+ }
2685+
2686+ // Verify payment succeeded
2687+ assert_eq ! ( node_a. payment( & payment_id) . unwrap( ) . status, PaymentStatus :: Succeeded ) ;
2688+ }
2689+ println ! ( "All {} payments completed successfully" , num_payments) ;
2690+
2691+ // Verify node_a has 200 outbound Bolt11 payments before shutdown
2692+ let outbound_payments_before = node_a. list_payments_with_filter ( |p| {
2693+ p. direction == PaymentDirection :: Outbound
2694+ && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2695+ } ) ;
2696+ assert_eq ! ( outbound_payments_before. len( ) , num_payments) ;
2697+
2698+ // Shut down both nodes
2699+ println ! ( "\n Shutting down nodes..." ) ;
2700+ node_a. stop ( ) . unwrap ( ) ;
2701+ node_b. stop ( ) . unwrap ( ) ;
2702+ }
2703+
2704+ // Restart node_a with the same config
2705+ println ! ( "\n Restarting node A..." ) ;
2706+ let restarted_node_a = setup_node ( & chain_source, config_a) ;
2707+
2708+ // Assert all 200 payments are still in the store
2709+ let outbound_payments_after = restarted_node_a. list_payments_with_filter ( |p| {
2710+ p. direction == PaymentDirection :: Outbound && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2711+ } ) ;
2712+ assert_eq ! (
2713+ outbound_payments_after. len( ) ,
2714+ num_payments,
2715+ "Expected {} payments after restart, found {}" ,
2716+ num_payments,
2717+ outbound_payments_after. len( )
2718+ ) ;
2719+
2720+ // Verify all payments have the correct status
2721+ for payment in & outbound_payments_after {
2722+ assert_eq ! (
2723+ payment. status,
2724+ PaymentStatus :: Succeeded ,
2725+ "Payment {:?} has unexpected status {:?}" ,
2726+ payment. id,
2727+ payment. status
2728+ ) ;
2729+ assert_eq ! ( payment. amount_msat, Some ( payment_amount_msat) ) ;
2730+ }
2731+
2732+ println ! (
2733+ "Successfully verified {} payments persisted after restart" ,
2734+ outbound_payments_after. len( )
2735+ ) ;
2736+
2737+ restarted_node_a. stop ( ) . unwrap ( ) ;
2738+ }
0 commit comments