//! serialization ordering between ChannelManager/ChannelMonitors and ensuring we can still retry
//! payments thereafter.
-use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch};
-use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
-use chain::transaction::OutPoint;
-use chain::keysinterface::KeysInterface;
-use ln::channel::EXPIRE_PREV_CONFIG_TICKS;
-use ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure};
-use ln::msgs;
-use ln::msgs::ChannelMessageHandler;
-use routing::router::{PaymentParameters, get_route};
-use util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
-use util::test_utils;
-use util::errors::APIError;
-use util::enforcing_trait_impls::EnforcingSigner;
-use util::ser::{ReadableArgs, Writeable};
-use io;
+use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
+use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
+use crate::chain::transaction::OutPoint;
+use crate::chain::keysinterface::KeysInterface;
+use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
+use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
+use crate::ln::msgs;
+use crate::ln::msgs::ChannelMessageHandler;
+use crate::routing::router::{PaymentParameters, get_route};
+use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
+use crate::util::test_utils;
+use crate::util::errors::APIError;
+use crate::util::enforcing_trait_impls::EnforcingSigner;
+use crate::util::ser::{ReadableArgs, Writeable};
+use crate::io;
use bitcoin::{Block, BlockHeader, BlockHash, TxMerkleNode};
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
-use prelude::*;
+use crate::prelude::*;
-use ln::functional_test_utils::*;
+use crate::ln::functional_test_utils::*;
#[test]
fn retry_single_path_payment() {
send_payment(&nodes[1], &vec!(&nodes[2])[..], 2_000_000);
// Make sure the payment fails on the first hop.
- let payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ let payment_id = PaymentId(payment_hash.0);
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
route.paths[1][1].short_channel_id = chan_4_update.contents.short_channel_id;
// Initiate the MPP payment.
- let payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ let payment_id = PaymentId(payment_hash.0);
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
check_added_monitors!(nodes[0], 2); // one monitor per path
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2);
route.paths[1][1].short_channel_id = chan_4_update.contents.short_channel_id;
// Initiate the MPP payment.
- let _ = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 2); // one monitor per path
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2);
send_payment(&nodes[1], &vec!(&nodes[2])[..], 2_000_000);
// Make sure the payment fails on the first hop.
- let payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
connect_blocks(&nodes[0], 3);
// Retry the payment and make sure it errors as expected.
- if let Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err })) = nodes[0].node.retry_payment(&route, payment_id) {
+ if let Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err })) = nodes[0].node.retry_payment(&route, PaymentId(payment_hash.0)) {
assert!(err.contains("not found"));
} else {
panic!("Unexpected error");
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
- unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)),
+ unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)),
true, APIError::ChannelUnavailable { ref err },
assert_eq!(err, "Peer for first hop currently disconnected/pending monitor update!"));
// out and retry.
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
let (payment_preimage_1, payment_hash_1, _, payment_id_1) = send_along_route(&nodes[0], route.clone(), &[&nodes[1], &nodes[2]], 1_000_000);
- let payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
nodes_0_deserialized = nodes_0_deserialized_tmp;
assert!(nodes_0_read.is_empty());
- assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ assert_eq!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor),
+ ChannelMonitorUpdateStatus::Completed);
nodes[0].node = &nodes_0_deserialized;
check_added_monitors!(nodes[0], 1);
}
assert!(nodes[0].node.retry_payment(&new_route, payment_id_1).is_err()); // Shouldn't be allowed to retry a fulfilled payment
- nodes[0].node.retry_payment(&new_route, payment_id).unwrap();
+ nodes[0].node.retry_payment(&new_route, PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
$chan_manager = nodes_0_deserialized_tmp;
assert!(nodes_0_read.is_empty());
- assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ assert_eq!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor),
+ ChannelMonitorUpdateStatus::Completed);
if !chan_1_monitor_serialized.0.is_empty() {
let funding_txo = chan_1_monitor.as_ref().unwrap().get_funding_txo().0;
- assert!(nodes[0].chain_monitor.watch_channel(funding_txo, chan_1_monitor.unwrap()).is_ok());
+ assert_eq!(nodes[0].chain_monitor.watch_channel(funding_txo, chan_1_monitor.unwrap()),
+ ChannelMonitorUpdateStatus::Completed);
}
nodes[0].node = &$chan_manager;
check_added_monitors!(nodes[0], if !chan_1_monitor_serialized.0.is_empty() { 2 } else { 1 });
}
// Now connect the HTLC claim transaction with the ChainMonitor-generated ChannelMonitor update
- // returning TemporaryFailure. This should cause the claim event to never make its way to the
+ // returning InProgress. This should cause the claim event to never make its way to the
// ChannelManager.
chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clear();
- chanmon_cfgs[0].persister.set_update_ret(Err(ChannelMonitorUpdateErr::TemporaryFailure));
+ chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
if payment_timeout {
connect_blocks(&nodes[0], 1);
// Now persist the ChannelMonitor and inform the ChainMonitor that we're done, generating the
// payment sent event.
- chanmon_cfgs[0].persister.set_update_ret(Ok(()));
+ chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
get_monitor!(nodes[0], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
for update in mon_updates {
};
nodes_0_deserialized = nodes_0_deserialized_tmp;
- assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ assert_eq!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor),
+ ChannelMonitorUpdateStatus::Completed);
check_added_monitors!(nodes[0], 1);
nodes[0].node = &nodes_0_deserialized;
};
nodes_1_deserialized = nodes_1_deserialized_tmp;
- assert!(nodes[1].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ assert_eq!(nodes[1].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor),
+ ChannelMonitorUpdateStatus::Completed);
check_added_monitors!(nodes[1], 1);
nodes[1].node = &nodes_1_deserialized;
&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(),
Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
- let _payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
// Make sure to use `get_payment_preimage`
}
assert!(found_probe_failed);
}
+
+#[test]
+fn claimed_send_payment_idempotent() {
+ // Tests that `send_payment` (and friends) are (reasonably) idempotent.
+ 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_preimage, _, _, 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!();
+
+ // Claim the payment backwards, but note that the PaymentSent event is still pending and has
+ // not been seen by the user. At this point, from the user perspective nothing has changed, so
+ // we must remain just as idempotent as we were before.
+ do_claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, first_payment_preimage);
+
+ for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
+ nodes[0].node.timer_tick_occurred();
+ }
+
+ check_send_rejected!();
+
+ // Once the user sees and handles the `PaymentSent` event, we expect them to no longer call
+ // `send_payment`, and our idempotency guarantees are off - they should have atomically marked
+ // the payment complete. However, they could have called `send_payment` while the event was
+ // being processed, leading to a race in our idempotency guarantees. Thus, even immediately
+ // after the event is handled a duplicate payment should sitll be rejected.
+ expect_payment_sent!(&nodes[0], first_payment_preimage, Some(0));
+ check_send_rejected!();
+
+ // If relatively little time has passed, a duplicate payment should still fail.
+ nodes[0].node.timer_tick_occurred();
+ check_send_rejected!();
+
+ // However, after some time has passed (at least more than the one timer tick above), a
+ // duplicate payment should go through, as ChannelManager should no longer have any remaining
+ // references to the old payment data.
+ for _ in 0..IDEMPOTENCY_TIMEOUT_TICKS {
+ nodes[0].node.timer_tick_occurred();
+ }
+
+ 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);
+}