mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
Test that PaymentIds are idempotency keys until abandon_payment
This commit is contained in:
parent
548f3f8416
commit
0ae45a2578
2 changed files with 81 additions and 14 deletions
|
@ -1905,6 +1905,22 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
|
|||
}
|
||||
|
||||
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) {
|
||||
let expected_payment_id = pass_failed_payment_back_no_abandon(origin_node, expected_paths_slice, skip_last, our_payment_hash);
|
||||
if !skip_last {
|
||||
origin_node.node.abandon_payment(expected_payment_id.unwrap());
|
||||
let events = origin_node.node.get_and_clear_pending_events();
|
||||
assert_eq!(events.len(), 1);
|
||||
match events[0] {
|
||||
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
|
||||
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
|
||||
assert_eq!(*payment_id, expected_payment_id.unwrap());
|
||||
}
|
||||
_ => panic!("Unexpected second event"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
|
||||
check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
|
||||
|
||||
|
@ -1928,6 +1944,8 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
|
|||
per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
|
||||
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()));
|
||||
|
||||
let mut expected_payment_id = None;
|
||||
|
||||
for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
|
||||
let mut next_msgs = Some(path_msgs);
|
||||
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
|
|||
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
|
||||
let events = origin_node.node.get_and_clear_pending_events();
|
||||
assert_eq!(events.len(), 1);
|
||||
let expected_payment_id = match events[0] {
|
||||
expected_payment_id = Some(match events[0] {
|
||||
Event::PaymentPathFailed { payment_hash, payment_failed_permanently, all_paths_failed, ref path, ref payment_id, .. } => {
|
||||
assert_eq!(payment_hash, our_payment_hash);
|
||||
assert!(payment_failed_permanently);
|
||||
|
@ -1987,19 +2005,7 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
|
|||
payment_id.unwrap()
|
||||
},
|
||||
_ => panic!("Unexpected event"),
|
||||
};
|
||||
if i == expected_paths.len() - 1 {
|
||||
origin_node.node.abandon_payment(expected_payment_id);
|
||||
let events = origin_node.node.get_and_clear_pending_events();
|
||||
assert_eq!(events.len(), 1);
|
||||
match events[0] {
|
||||
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
|
||||
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
|
||||
assert_eq!(*payment_id, expected_payment_id);
|
||||
}
|
||||
_ => panic!("Unexpected second event"),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2008,6 +2014,8 @@ pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
|
|||
assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_events().is_empty());
|
||||
assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_msg_events().is_empty());
|
||||
check_added_monitors!(expected_paths[0].last().unwrap(), 0);
|
||||
|
||||
expected_payment_id
|
||||
}
|
||||
|
||||
pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) {
|
||||
|
|
|
@ -1326,3 +1326,62 @@ fn claimed_send_payment_idempotent() {
|
|||
pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
|
||||
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abandoned_send_payment_idempotent() {
|
||||
// Tests that `send_payment` (and friends) allow duplicate PaymentIds immediately after
|
||||
// abandon_payment.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
|
||||
create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
|
||||
|
||||
let (route, second_payment_hash, second_payment_preimage, second_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000);
|
||||
let (_, first_payment_hash, _, payment_id) = send_along_route(&nodes[0], route.clone(), &[&nodes[1]], 100_000);
|
||||
|
||||
macro_rules! check_send_rejected {
|
||||
() => {
|
||||
// If we try to resend a new payment with a different payment_hash but with the same
|
||||
// payment_id, it should be rejected.
|
||||
let send_result = nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id);
|
||||
match send_result {
|
||||
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
|
||||
_ => panic!("Unexpected send result: {:?}", send_result),
|
||||
}
|
||||
|
||||
// Further, if we try to send a spontaneous payment with the same payment_id it should
|
||||
// also be rejected.
|
||||
let send_result = nodes[0].node.send_spontaneous_payment(&route, None, payment_id);
|
||||
match send_result {
|
||||
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
|
||||
_ => panic!("Unexpected send result: {:?}", send_result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_send_rejected!();
|
||||
|
||||
nodes[1].node.fail_htlc_backwards(&first_payment_hash);
|
||||
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::FailedPayment { payment_hash: first_payment_hash }]);
|
||||
|
||||
pass_failed_payment_back_no_abandon(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash);
|
||||
check_send_rejected!();
|
||||
|
||||
// Until we abandon the payment, no matter how many timer ticks pass, we still cannot reuse the
|
||||
// PaymentId.
|
||||
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
|
||||
nodes[0].node.timer_tick_occurred();
|
||||
}
|
||||
check_send_rejected!();
|
||||
|
||||
nodes[0].node.abandon_payment(payment_id);
|
||||
get_event!(nodes[0], Event::PaymentFailed);
|
||||
|
||||
// However, we can reuse the PaymentId immediately after we `abandon_payment`.
|
||||
nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
|
||||
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue