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