Skip to content

Commit 0ae45a2

Browse files
committed
Test that PaymentIds are idempotency keys until abandon_payment
1 parent 548f3f8 commit 0ae45a2

File tree

2 files changed

+81
-14
lines changed

2 files changed

+81
-14
lines changed

lightning/src/ln/functional_test_utils.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,22 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
19051905
}
19061906

19071907
pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
1908+
let expected_payment_id = pass_failed_payment_back_no_abandon(origin_node, expected_paths_slice, skip_last, our_payment_hash);
1909+
if !skip_last {
1910+
origin_node.node.abandon_payment(expected_payment_id.unwrap());
1911+
let events = origin_node.node.get_and_clear_pending_events();
1912+
assert_eq!(events.len(), 1);
1913+
match events[0] {
1914+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1915+
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1916+
assert_eq!(*payment_id, expected_payment_id.unwrap());
1917+
}
1918+
_ => panic!("Unexpected second event"),
1919+
}
1920+
}
1921+
}
1922+
1923+
pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) -> Option<PaymentId> {
19081924
let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
19091925
check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
19101926

@@ -1928,6 +1944,8 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
19281944
per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
19291945
expected_paths.sort_unstable_by(|path_a, path_b| path_a[path_a.len() - 2].node.get_our_node_id().cmp(&path_b[path_b.len() - 2].node.get_our_node_id()));
19301946

1947+
let mut expected_payment_id = None;
1948+
19311949
for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
19321950
let mut next_msgs = Some(path_msgs);
19331951
let mut expected_next_node = next_hop;
@@ -1976,7 +1994,7 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
19761994
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
19771995
let events = origin_node.node.get_and_clear_pending_events();
19781996
assert_eq!(events.len(), 1);
1979-
let expected_payment_id = match events[0] {
1997+
expected_payment_id = Some(match events[0] {
19801998
Event::PaymentPathFailed { payment_hash, payment_failed_permanently, all_paths_failed, ref path, ref payment_id, .. } => {
19811999
assert_eq!(payment_hash, our_payment_hash);
19822000
assert!(payment_failed_permanently);
@@ -1987,19 +2005,7 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
19872005
payment_id.unwrap()
19882006
},
19892007
_ => panic!("Unexpected event"),
1990-
};
1991-
if i == expected_paths.len() - 1 {
1992-
origin_node.node.abandon_payment(expected_payment_id);
1993-
let events = origin_node.node.get_and_clear_pending_events();
1994-
assert_eq!(events.len(), 1);
1995-
match events[0] {
1996-
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1997-
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1998-
assert_eq!(*payment_id, expected_payment_id);
1999-
}
2000-
_ => panic!("Unexpected second event"),
2001-
}
2002-
}
2008+
});
20032009
}
20042010
}
20052011

@@ -2008,6 +2014,8 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
20082014
assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_events().is_empty());
20092015
assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_msg_events().is_empty());
20102016
check_added_monitors!(expected_paths[0].last().unwrap(), 0);
2017+
2018+
expected_payment_id
20112019
}
20122020

20132021
pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) {

lightning/src/ln/payment_tests.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,3 +1326,62 @@ fn claimed_send_payment_idempotent() {
13261326
pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
13271327
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
13281328
}
1329+
1330+
#[test]
1331+
fn abandoned_send_payment_idempotent() {
1332+
// Tests that `send_payment` (and friends) allow duplicate PaymentIds immediately after
1333+
// abandon_payment.
1334+
let chanmon_cfgs = create_chanmon_cfgs(2);
1335+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1336+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1337+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1338+
1339+
create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
1340+
1341+
let (route, second_payment_hash, second_payment_preimage, second_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000);
1342+
let (_, first_payment_hash, _, payment_id) = send_along_route(&nodes[0], route.clone(), &[&nodes[1]], 100_000);
1343+
1344+
macro_rules! check_send_rejected {
1345+
() => {
1346+
// If we try to resend a new payment with a different payment_hash but with the same
1347+
// payment_id, it should be rejected.
1348+
let send_result = nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id);
1349+
match send_result {
1350+
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1351+
_ => panic!("Unexpected send result: {:?}", send_result),
1352+
}
1353+
1354+
// Further, if we try to send a spontaneous payment with the same payment_id it should
1355+
// also be rejected.
1356+
let send_result = nodes[0].node.send_spontaneous_payment(&route, None, payment_id);
1357+
match send_result {
1358+
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1359+
_ => panic!("Unexpected send result: {:?}", send_result),
1360+
}
1361+
}
1362+
}
1363+
1364+
check_send_rejected!();
1365+
1366+
nodes[1].node.fail_htlc_backwards(&first_payment_hash);
1367+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::FailedPayment { payment_hash: first_payment_hash }]);
1368+
1369+
pass_failed_payment_back_no_abandon(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash);
1370+
check_send_rejected!();
1371+
1372+
// Until we abandon the payment, no matter how many timer ticks pass, we still cannot reuse the
1373+
// PaymentId.
1374+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1375+
nodes[0].node.timer_tick_occurred();
1376+
}
1377+
check_send_rejected!();
1378+
1379+
nodes[0].node.abandon_payment(payment_id);
1380+
get_event!(nodes[0], Event::PaymentFailed);
1381+
1382+
// However, we can reuse the PaymentId immediately after we `abandon_payment`.
1383+
nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id).unwrap();
1384+
check_added_monitors!(nodes[0], 1);
1385+
pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
1386+
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
1387+
}

0 commit comments

Comments
 (0)