Skip to content

Commit 979a30f

Browse files
Test ChannelManager automatic retries
TODO: more tests
1 parent fcf4c32 commit 979a30f

File tree

1 file changed

+191
-1
lines changed

1 file changed

+191
-1
lines changed

lightning/src/ln/payment_tests.rs

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS
1616
use crate::chain::transaction::OutPoint;
1717
use crate::chain::keysinterface::KeysInterface;
1818
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;
2021
use crate::ln::msgs;
2122
use crate::ln::msgs::ChannelMessageHandler;
2223
use crate::routing::gossip::RoutingFees;
@@ -34,6 +35,11 @@ use crate::prelude::*;
3435

3536
use crate::ln::functional_test_utils::*;
3637
use crate::routing::gossip::NodeId;
38+
#[cfg(feature = "std")]
39+
use {
40+
crate::util::time::tests::SinceEpoch,
41+
std::time::{SystemTime, Duration}
42+
};
3743

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

0 commit comments

Comments
 (0)