/// as they will result in over-/re-payment. These HTLCs all either successfully sent (in the
/// case of Ok(())) or will send once channel_monitor_updated is called on the next-hop channel
/// with the latest update_id.
- PartialFailure(Vec<Result<(), APIError>>),
+ PartialFailure {
+ /// The errors themselves, in the same order as the route hops.
+ results: Vec<Result<(), APIError>>,
+ /// If some paths failed without irrevocably committing to the new HTLC(s), this will
+ /// contain a [`RouteParameters`] object which can be used to calculate a new route that
+ /// will pay all remaining unpaid balance.
+ failed_paths_retry: Option<RouteParameters>,
+ /// The payment id for the payment, which is now at least partially pending.
+ payment_id: PaymentId,
+ },
}
macro_rules! handle_error {
res
} };
($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr) => {
- handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, Vec::new());
+ handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, Vec::new())
}
}
}
let mut has_ok = false;
let mut has_err = false;
- for res in results.iter() {
+ let mut pending_amt_unsent = 0;
+ let mut max_unsent_cltv_delta = 0;
+ for (res, path) in results.iter().zip(route.paths.iter()) {
if res.is_ok() { has_ok = true; }
if res.is_err() { has_err = true; }
if let &Err(APIError::MonitorUpdateFailed) = res {
// PartialFailure.
has_err = true;
has_ok = true;
- break;
+ } else if res.is_err() {
+ pending_amt_unsent += path.last().unwrap().fee_msat;
+ max_unsent_cltv_delta = cmp::max(max_unsent_cltv_delta, path.last().unwrap().cltv_expiry_delta);
}
}
if has_err && has_ok {
- Err(PaymentSendFailure::PartialFailure(results))
+ Err(PaymentSendFailure::PartialFailure {
+ results,
+ payment_id,
+ failed_paths_retry: if pending_amt_unsent != 0 {
+ if let Some(payee) = &route.payee {
+ Some(RouteParameters {
+ payee: payee.clone(),
+ final_value_msat: pending_amt_unsent,
+ final_cltv_expiry_delta: max_unsent_cltv_delta,
+ })
+ } else { None }
+ } else { None },
+ })
} else if has_err {
Err(PaymentSendFailure::AllFailedRetrySafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
} else {
use ln::msgs;
use ln::msgs::ChannelMessageHandler;
use routing::router::{Payee, RouteParameters, find_route};
- use routing::scorer::Scorer;
use util::errors::APIError;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
+ use util::test_utils;
#[cfg(feature = "std")]
#[test]
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, InitFeatures::known(), InitFeatures::known());
- let scorer = Scorer::with_fixed_penalty(0);
+ let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// To start (1), send a regular payment but don't claim it.
let expected_route = [&nodes[1]];
final_cltv_expiry_delta: TEST_FINAL_CLTV,
};
let route = find_route(
- &nodes[0].node.get_our_node_id(), ¶ms,
- &nodes[0].net_graph_msg_handler.network_graph, None, nodes[0].logger, &scorer
+ &nodes[0].node.get_our_node_id(), ¶ms, nodes[0].network_graph, None,
+ nodes[0].logger, &scorer
).unwrap();
nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
// To start (2), send a keysend payment but don't claim it.
let payment_preimage = PaymentPreimage([42; 32]);
let route = find_route(
- &nodes[0].node.get_our_node_id(), ¶ms,
- &nodes[0].net_graph_msg_handler.network_graph, None, nodes[0].logger, &scorer
+ &nodes[0].node.get_our_node_id(), ¶ms, nodes[0].network_graph, None,
+ nodes[0].logger, &scorer
).unwrap();
let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
final_value_msat: 10000,
final_cltv_expiry_delta: 40,
};
- let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
+ let network_graph = nodes[0].network_graph;
let first_hops = nodes[0].node.list_usable_channels();
- let scorer = Scorer::with_fixed_penalty(0);
+ let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer
final_value_msat: 10000,
final_cltv_expiry_delta: 40,
};
- let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
+ let network_graph = nodes[0].network_graph;
let first_hops = nodes[0].node.list_usable_channels();
- let scorer = Scorer::with_fixed_penalty(0);
+ let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer
macro_rules! send_payment {
($node_a: expr, $node_b: expr) => {
let usable_channels = $node_a.list_usable_channels();
- let payee = Payee::new($node_b.get_our_node_id())
+ let payee = Payee::from_node_id($node_b.get_our_node_id())
.with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let route = get_route(&$node_a.get_our_node_id(), &payee, &dummy_graph,