@@ -16,7 +16,8 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS
16
16
use crate :: chain:: transaction:: OutPoint ;
17
17
use crate :: chain:: keysinterface:: { EntropySource , KeysInterface } ;
18
18
use crate :: ln:: channel:: EXPIRE_PREV_CONFIG_TICKS ;
19
- use crate :: ln:: channelmanager:: { self , BREAKDOWN_TIMEOUT , ChannelManager , MPP_TIMEOUT_TICKS , MIN_CLTV_EXPIRY_DELTA , PaymentId , PaymentSendFailure , IDEMPOTENCY_TIMEOUT_TICKS } ;
19
+ use crate :: ln:: channelmanager:: { self , BREAKDOWN_TIMEOUT , ChannelManager , MPP_TIMEOUT_TICKS , MIN_CLTV_EXPIRY_DELTA , PaymentId , PaymentSendFailure , IDEMPOTENCY_TIMEOUT_TICKS , Retry } ;
20
+ use crate :: ln:: features:: InvoiceFeatures ;
20
21
use crate :: ln:: msgs;
21
22
use crate :: ln:: msgs:: ChannelMessageHandler ;
22
23
use crate :: routing:: gossip:: RoutingFees ;
@@ -34,6 +35,11 @@ use crate::prelude::*;
34
35
35
36
use crate :: ln:: functional_test_utils:: * ;
36
37
use crate :: routing:: gossip:: NodeId ;
38
+ #[ cfg( feature = "std" ) ]
39
+ use {
40
+ crate :: util:: time:: tests:: SinceEpoch ,
41
+ std:: time:: { SystemTime , Duration }
42
+ } ;
37
43
38
44
#[ test]
39
45
fn retry_single_path_payment ( ) {
@@ -1564,3 +1570,187 @@ fn do_test_intercepted_payment(test: InterceptTest) {
1564
1570
assert_eq ! ( unknown_intercept_id_err , APIError :: APIMisuseError { err: format!( "Payment with intercept id {} not found" , log_bytes!( intercept_id. 0 ) ) } ) ;
1565
1571
}
1566
1572
}
1573
+
1574
+ #[ derive( PartialEq ) ]
1575
+ enum AutoRetry {
1576
+ Success ,
1577
+ FailAttempts ,
1578
+ FailTimeout ,
1579
+ FailOnRestart ,
1580
+ }
1581
+
1582
+ #[ test]
1583
+ fn automatic_retries ( ) {
1584
+ do_automatic_retries ( AutoRetry :: Success ) ;
1585
+ do_automatic_retries ( AutoRetry :: FailAttempts ) ;
1586
+ do_automatic_retries ( AutoRetry :: FailTimeout ) ;
1587
+ do_automatic_retries ( AutoRetry :: FailOnRestart ) ;
1588
+ }
1589
+ fn do_automatic_retries ( test : AutoRetry ) {
1590
+ // Test basic automatic payment retries in ChannelManager. See individual `test` variant comments
1591
+ // below.
1592
+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1593
+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1594
+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1595
+
1596
+ let persister;
1597
+ let new_chain_monitor;
1598
+ let node_0_deserialized;
1599
+
1600
+ let mut nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1601
+ let channel_id_1 = create_announced_chan_between_nodes ( & nodes, 0 , 1 , channelmanager:: provided_init_features ( ) , channelmanager:: provided_init_features ( ) ) . 2 ;
1602
+ let channel_id_2 = create_announced_chan_between_nodes ( & nodes, 2 , 1 , channelmanager:: provided_init_features ( ) , channelmanager:: provided_init_features ( ) ) . 2 ;
1603
+
1604
+ // Marshall data to send the payment
1605
+ #[ cfg( feature = "std" ) ]
1606
+ let payment_expiry_secs = SystemTime :: UNIX_EPOCH . elapsed ( ) . unwrap ( ) . as_secs ( ) + 60 * 60 ;
1607
+ #[ cfg( not( feature = "std" ) ) ]
1608
+ let payment_expiry_secs = 60 * 60 ;
1609
+ let amt_msat = 1000 ;
1610
+ let mut invoice_features = InvoiceFeatures :: empty ( ) ;
1611
+ invoice_features. set_variable_length_onion_required ( ) ;
1612
+ invoice_features. set_payment_secret_required ( ) ;
1613
+ invoice_features. set_basic_mpp_optional ( ) ;
1614
+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] . node . get_our_node_id ( ) )
1615
+ . with_expiry_time ( payment_expiry_secs as u64 )
1616
+ . with_features ( invoice_features) ;
1617
+ let route_params = RouteParameters {
1618
+ payment_params,
1619
+ final_value_msat : amt_msat,
1620
+ final_cltv_expiry_delta : TEST_FINAL_CLTV ,
1621
+ } ;
1622
+ let ( _, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash ! ( nodes[ 0 ] , nodes[ 2 ] , amt_msat) ;
1623
+
1624
+ macro_rules! pass_failed_attempt_with_retry_along_path {
1625
+ ( $failing_channel_id: expr) => {
1626
+ // Send a payment attempt that fails due to lack of liquidity on the second hop
1627
+ check_added_monitors!( nodes[ 0 ] , 1 ) ;
1628
+ let update_0 = get_htlc_update_msgs!( nodes[ 0 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
1629
+ let mut update_add = update_0. update_add_htlcs[ 0 ] . clone( ) ;
1630
+ nodes[ 1 ] . node. handle_update_add_htlc( & nodes[ 0 ] . node. get_our_node_id( ) , & update_add) ;
1631
+ commitment_signed_dance!( nodes[ 1 ] , nodes[ 0 ] , & update_0. commitment_signed, false , true ) ;
1632
+ expect_pending_htlcs_forwardable_ignore!( nodes[ 1 ] ) ;
1633
+ nodes[ 1 ] . node. process_pending_htlc_forwards( ) ;
1634
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!( nodes[ 1 ] ,
1635
+ vec![ HTLCDestination :: NextHopChannel {
1636
+ node_id: Some ( nodes[ 2 ] . node. get_our_node_id( ) ) ,
1637
+ channel_id: $failing_channel_id,
1638
+ } ] ) ;
1639
+ nodes[ 1 ] . node. process_pending_htlc_forwards( ) ;
1640
+ let update_1 = get_htlc_update_msgs!( nodes[ 1 ] , nodes[ 0 ] . node. get_our_node_id( ) ) ;
1641
+ check_added_monitors!( & nodes[ 1 ] , 1 ) ;
1642
+ assert!( update_1. update_fail_htlcs. len( ) == 1 ) ;
1643
+ let fail_msg = update_1. update_fail_htlcs[ 0 ] . clone( ) ;
1644
+
1645
+ nodes[ 0 ] . node. handle_update_fail_htlc( & nodes[ 1 ] . node. get_our_node_id( ) , & fail_msg) ;
1646
+ commitment_signed_dance!( nodes[ 0 ] , nodes[ 1 ] , update_1. commitment_signed, false ) ;
1647
+
1648
+ // Ensure the attempt fails and a new PendingHTLCsForwardable event is generated for the retry
1649
+ let mut events = nodes[ 0 ] . node. get_and_clear_pending_events( ) ;
1650
+ assert_eq!( events. len( ) , 2 ) ;
1651
+ match events[ 0 ] {
1652
+ Event :: PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently, .. } => {
1653
+ assert_eq!( payment_hash, ev_payment_hash) ;
1654
+ assert_eq!( payment_failed_permanently, false ) ;
1655
+ } ,
1656
+ _ => panic!( "Unexpected event" ) ,
1657
+ }
1658
+ match events[ 1 ] {
1659
+ Event :: PendingHTLCsForwardable { .. } => { } ,
1660
+ _ => panic!( "Unexpected event" ) ,
1661
+ }
1662
+ }
1663
+ }
1664
+
1665
+ if test == AutoRetry :: Success {
1666
+ // Test that we can succeed on the first retry.
1667
+ nodes[ 0 ] . node . send_payment_with_retry ( payment_hash, & Some ( payment_secret) , PaymentId ( payment_hash. 0 ) , route_params, Retry :: Attempts ( 1 ) ) . unwrap ( ) ;
1668
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_2) ;
1669
+
1670
+ // Open a new channel with liquidity on the second hop so we can find a route for the retry
1671
+ // attempt, since the initial second hop channel will be excluded from pathfinding
1672
+ create_announced_chan_between_nodes ( & nodes, 1 , 2 , channelmanager:: provided_init_features ( ) , channelmanager:: provided_init_features ( ) ) ;
1673
+
1674
+ // We retry payments in `process_pending_htlc_forwards`
1675
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1676
+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
1677
+ let mut msg_events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
1678
+ assert_eq ! ( msg_events. len( ) , 1 ) ;
1679
+ pass_along_path ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , amt_msat, payment_hash, Some ( payment_secret) , msg_events. pop ( ) . unwrap ( ) , true , None ) ;
1680
+ claim_payment_along_route ( & nodes[ 0 ] , & [ & [ & nodes[ 1 ] , & nodes[ 2 ] ] ] , false , payment_preimage) ;
1681
+ } else if test == AutoRetry :: FailAttempts {
1682
+ // Ensure ChannelManager will not retry a payment if it has run out of payment attempts.
1683
+ nodes[ 0 ] . node . send_payment_with_retry ( payment_hash, & Some ( payment_secret) , PaymentId ( payment_hash. 0 ) , route_params, Retry :: Attempts ( 1 ) ) . unwrap ( ) ;
1684
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_2) ;
1685
+
1686
+ // Open a new channel with no liquidity on the second hop so we can find a (bad) route for
1687
+ // the retry attempt, since the initial second hop channel will be excluded from pathfinding
1688
+ let channel_id_3 = create_announced_chan_between_nodes ( & nodes, 2 , 1 , channelmanager:: provided_init_features ( ) , channelmanager:: provided_init_features ( ) ) . 2 ;
1689
+
1690
+ // We retry payments in `process_pending_htlc_forwards`
1691
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1692
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_3) ;
1693
+
1694
+ // Ensure we won't retry a second time.
1695
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1696
+ nodes[ 0 ] . logger . assert_log_regex ( "lightning::ln::channelmanager" . to_string ( ) , regex:: Regex :: new ( r"Payment [0-9a-f]* is not retryable" ) . unwrap ( ) , 1 ) ;
1697
+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1698
+ assert_eq ! ( events. len( ) , 1 ) ;
1699
+ match events[ 0 ] {
1700
+ Event :: PaymentFailed { payment_hash : ref ev_payment_hash, payment_id : ref ev_payment_id } => {
1701
+ assert_eq ! ( payment_hash, * ev_payment_hash) ;
1702
+ assert_eq ! ( PaymentId ( payment_hash. 0 ) , * ev_payment_id) ;
1703
+ } ,
1704
+ _ => panic ! ( "Unexpected event" ) ,
1705
+ }
1706
+ } else if test == AutoRetry :: FailTimeout {
1707
+ #[ cfg( not( feature = "no-std" ) ) ] {
1708
+ // Ensure ChannelManager will not retry a payment if it times out due to Retry::Timeout.
1709
+ nodes[ 0 ] . node . send_payment_with_retry ( payment_hash, & Some ( payment_secret) , PaymentId ( payment_hash. 0 ) , route_params, Retry :: Timeout ( Duration :: from_secs ( 60 ) ) ) . unwrap ( ) ;
1710
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_2) ;
1711
+
1712
+ // Advance the time so the second attempt fails due to timeout.
1713
+ SinceEpoch :: advance ( Duration :: from_secs ( 61 ) ) ;
1714
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1715
+ nodes[ 0 ] . logger . assert_log_regex ( "lightning::ln::channelmanager" . to_string ( ) , regex:: Regex :: new ( r"Payment [0-9a-f]* is not retryable" ) . unwrap ( ) , 1 ) ;
1716
+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1717
+ assert_eq ! ( events. len( ) , 1 ) ;
1718
+ match events[ 0 ] {
1719
+ Event :: PaymentFailed { payment_hash : ref ev_payment_hash, payment_id : ref ev_payment_id } => {
1720
+ assert_eq ! ( payment_hash, * ev_payment_hash) ;
1721
+ assert_eq ! ( PaymentId ( payment_hash. 0 ) , * ev_payment_id) ;
1722
+ } ,
1723
+ _ => panic ! ( "Unexpected event" ) ,
1724
+ }
1725
+ }
1726
+ } else if test == AutoRetry :: FailOnRestart {
1727
+ // Ensure ChannelManager will not retry a payment after restart, even if there were retry
1728
+ // attempts remaining prior to restart.
1729
+ nodes[ 0 ] . node . send_payment_with_retry ( payment_hash, & Some ( payment_secret) , PaymentId ( payment_hash. 0 ) , route_params, Retry :: Attempts ( 2 ) ) . unwrap ( ) ;
1730
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_2) ;
1731
+
1732
+ // Open a new channel with no liquidity on the second hop so we can find a (bad) route for
1733
+ // the retry attempt, since the initial second hop channel will be excluded from pathfinding
1734
+ let channel_id_3 = create_announced_chan_between_nodes ( & nodes, 2 , 1 , channelmanager:: provided_init_features ( ) , channelmanager:: provided_init_features ( ) ) . 2 ;
1735
+
1736
+ // Ensure the first retry attempt fails, with 1 retry attempt remaining
1737
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1738
+ pass_failed_attempt_with_retry_along_path ! ( channel_id_3) ;
1739
+
1740
+ // Restart the node and ensure that ChannelManager does not use its remaining retry attempt
1741
+ let node_encoded = nodes[ 0 ] . node . encode ( ) ;
1742
+ let chan_1_monitor_serialized = get_monitor ! ( nodes[ 0 ] , channel_id_1) . encode ( ) ;
1743
+ reload_node ! ( nodes[ 0 ] , node_encoded, & [ & chan_1_monitor_serialized] , persister, new_chain_monitor, node_0_deserialized) ;
1744
+ nodes[ 0 ] . node . process_pending_htlc_forwards ( ) ;
1745
+ nodes[ 0 ] . logger . assert_log_regex ( "lightning::ln::channelmanager" . to_string ( ) , regex:: Regex :: new ( r"Payment [0-9a-f]* is not retryable" ) . unwrap ( ) , 1 ) ;
1746
+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1747
+ assert_eq ! ( events. len( ) , 1 ) ;
1748
+ match events[ 0 ] {
1749
+ Event :: PaymentFailed { payment_hash : ref ev_payment_hash, payment_id : ref ev_payment_id } => {
1750
+ assert_eq ! ( payment_hash, * ev_payment_hash) ;
1751
+ assert_eq ! ( PaymentId ( payment_hash. 0 ) , * ev_payment_id) ;
1752
+ } ,
1753
+ _ => panic ! ( "Unexpected event" ) ,
1754
+ }
1755
+ }
1756
+ }
0 commit comments