@@ -38,13 +38,14 @@ use bitcoin::address::NetworkUnchecked;
3838use bitcoin:: hashes:: sha256:: Hash as Sha256Hash ;
3939use bitcoin:: hashes:: Hash ;
4040use bitcoin:: { Address , Amount , ScriptBuf } ;
41-
4241use log:: LevelFilter ;
4342
4443use std:: collections:: HashSet ;
4544use std:: str:: FromStr ;
4645use std:: sync:: Arc ;
4746
47+ use crate :: common:: TestStoreType ;
48+
4849#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
4950async fn channel_full_cycle ( ) {
5051 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
@@ -2602,3 +2603,121 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
26022603 Some ( 6 )
26032604 ) ;
26042605}
2606+
2607+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
2608+ async fn payment_persistence_after_restart ( ) {
2609+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
2610+ let chain_source = TestChainSource :: Esplora ( & electrsd) ;
2611+
2612+ // Setup nodes manually so we can restart node_a with the same config
2613+ println ! ( "== Node A ==" ) ;
2614+ let mut config_a = random_config ( true ) ;
2615+ config_a. store_type = TestStoreType :: Sqlite ;
2616+
2617+ let num_payments = 200 ;
2618+ let payment_amount_msat = 1_000_000 ; // 1000 sats per payment
2619+
2620+ {
2621+ let node_a = setup_node ( & chain_source, config_a. clone ( ) , None ) ;
2622+
2623+ println ! ( "\n == Node B ==" ) ;
2624+ let config_b = random_config ( true ) ;
2625+ let node_b = setup_node ( & chain_source, config_b, None ) ;
2626+
2627+ let addr_a = node_a. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2628+ let addr_b = node_b. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
2629+
2630+ // Premine sufficient funds for a large channel and many payments
2631+ let premine_amount_sat = 10_000_000 ;
2632+ premine_and_distribute_funds (
2633+ & bitcoind. client ,
2634+ & electrsd. client ,
2635+ vec ! [ addr_a, addr_b] ,
2636+ Amount :: from_sat ( premine_amount_sat) ,
2637+ )
2638+ . await ;
2639+ node_a. sync_wallets ( ) . unwrap ( ) ;
2640+ node_b. sync_wallets ( ) . unwrap ( ) ;
2641+ assert_eq ! ( node_a. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2642+ assert_eq ! ( node_b. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
2643+
2644+ // Open a large channel from node_a to node_b
2645+ let channel_amount_sat = 5_000_000 ;
2646+ open_channel ( & node_a, & node_b, channel_amount_sat, true , & electrsd) . await ;
2647+ generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
2648+ node_a. sync_wallets ( ) . unwrap ( ) ;
2649+ node_b. sync_wallets ( ) . unwrap ( ) ;
2650+ expect_channel_ready_event ! ( node_a, node_b. node_id( ) ) ;
2651+ expect_channel_ready_event ! ( node_b, node_a. node_id( ) ) ;
2652+
2653+ // Send 200 payments from node_a to node_b
2654+ println ! ( "\n Sending {} payments from A to B..." , num_payments) ;
2655+ let invoice_description =
2656+ Bolt11InvoiceDescription :: Direct ( Description :: new ( String :: from ( "test" ) ) . unwrap ( ) ) ;
2657+
2658+ for i in 0 ..num_payments {
2659+ let invoice = node_b
2660+ . bolt11_payment ( )
2661+ . receive ( payment_amount_msat, & invoice_description. clone ( ) . into ( ) , 3600 )
2662+ . unwrap ( ) ;
2663+ let payment_id = node_a. bolt11_payment ( ) . send ( & invoice, None ) . unwrap ( ) ;
2664+ expect_event ! ( node_a, PaymentSuccessful ) ;
2665+ expect_event ! ( node_b, PaymentReceived ) ;
2666+
2667+ if ( i + 1 ) % 50 == 0 {
2668+ println ! ( "Completed {} payments" , i + 1 ) ;
2669+ }
2670+
2671+ // Verify payment succeeded
2672+ assert_eq ! ( node_a. payment( & payment_id) . unwrap( ) . status, PaymentStatus :: Succeeded ) ;
2673+ }
2674+ println ! ( "All {} payments completed successfully" , num_payments) ;
2675+
2676+ // Verify node_a has 200 outbound Bolt11 payments before shutdown
2677+ let outbound_payments_before = node_a. list_payments_with_filter ( |p| {
2678+ p. direction == PaymentDirection :: Outbound
2679+ && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2680+ } ) ;
2681+ assert_eq ! ( outbound_payments_before. len( ) , num_payments) ;
2682+
2683+ // Shut down both nodes
2684+ println ! ( "\n Shutting down nodes..." ) ;
2685+ node_a. stop ( ) . unwrap ( ) ;
2686+ node_b. stop ( ) . unwrap ( ) ;
2687+ }
2688+
2689+ // Restart node_a with the same config
2690+ println ! ( "\n Restarting node A..." ) ;
2691+ let restarted_node_a = setup_node ( & chain_source, config_a, None ) ;
2692+
2693+ // Assert all 200 payments are still in the store
2694+ let outbound_payments_after = restarted_node_a. list_payments_with_filter ( |p| {
2695+ p. direction == PaymentDirection :: Outbound && matches ! ( p. kind, PaymentKind :: Bolt11 { .. } )
2696+ } ) ;
2697+ assert_eq ! (
2698+ outbound_payments_after. len( ) ,
2699+ num_payments,
2700+ "Expected {} payments after restart, found {}" ,
2701+ num_payments,
2702+ outbound_payments_after. len( )
2703+ ) ;
2704+
2705+ // Verify all payments have the correct status
2706+ for payment in & outbound_payments_after {
2707+ assert_eq ! (
2708+ payment. status,
2709+ PaymentStatus :: Succeeded ,
2710+ "Payment {:?} has unexpected status {:?}" ,
2711+ payment. id,
2712+ payment. status
2713+ ) ;
2714+ assert_eq ! ( payment. amount_msat, Some ( payment_amount_msat) ) ;
2715+ }
2716+
2717+ println ! (
2718+ "Successfully verified {} payments persisted after restart" ,
2719+ outbound_payments_after. len( )
2720+ ) ;
2721+
2722+ restarted_node_a. stop ( ) . unwrap ( ) ;
2723+ }
0 commit comments