PendingOutboundPayment::InvoiceReceived { .. } => { debug_assert!(false); false },
};
if remove_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
- let path = path.expect("Fulfilling a payment should always come with a path");
+ if let PendingOutboundPayment::Retryable {
+ ref mut pending_amt_msat, ref mut pending_fee_msat,
+ ref mut remaining_max_total_routing_fee_msat, ..
+ } = self {
+ let path = path.expect("Removing a failed payment should always come with a path");
*pending_amt_msat -= path.final_value_msat();
+ let path_fee_msat = path.fee_msat();
if let Some(fee_msat) = pending_fee_msat.as_mut() {
- *fee_msat -= path.fee_msat();
+ *fee_msat -= path_fee_msat;
+ }
+
+ if let Some(max_total_routing_fee_msat) = remaining_max_total_routing_fee_msat.as_mut() {
+ *max_total_routing_fee_msat = max_total_routing_fee_msat.saturating_add(path_fee_msat);
}
}
}
PendingOutboundPayment::Abandoned { .. } => false,
};
if insert_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
- *pending_amt_msat += path.final_value_msat();
- if let Some(fee_msat) = pending_fee_msat.as_mut() {
- *fee_msat += path.fee_msat();
- }
+ if let PendingOutboundPayment::Retryable {
+ ref mut pending_amt_msat, ref mut pending_fee_msat,
+ ref mut remaining_max_total_routing_fee_msat, ..
+ } = self {
+ *pending_amt_msat += path.final_value_msat();
+ let path_fee_msat = path.fee_msat();
+ if let Some(fee_msat) = pending_fee_msat.as_mut() {
+ *fee_msat += path_fee_msat;
+ }
+
+ if let Some(max_total_routing_fee_msat) = remaining_max_total_routing_fee_msat.as_mut() {
+ *max_total_routing_fee_msat = max_total_routing_fee_msat.saturating_sub(path_fee_msat);
+ }
}
}
insert_res
}
}
- let route = router.find_route_with_id(
+ let mut route = router.find_route_with_id(
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
payment_hash, payment_id,
RetryableSendFailure::RouteNotFound
})?;
+ if let Some(route_route_params) = route.route_params.as_mut() {
+ if route_route_params.final_value_msat != route_params.final_value_msat {
+ debug_assert!(false,
+ "Routers are expected to return a route which includes the requested final_value_msat");
+ route_route_params.final_value_msat = route_params.final_value_msat;
+ }
+ }
+
let onion_session_privs = self.add_new_pending_payment(payment_hash,
recipient_onion.clone(), payment_id, keysend_preimage, &route, Some(retry_strategy),
Some(route_params.payment_params.clone()), entropy_source, best_block_height)
}
}
- let route = match router.find_route_with_id(
+ let mut route = match router.find_route_with_id(
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
payment_hash, payment_id,
return
}
};
+
+ if let Some(route_route_params) = route.route_params.as_mut() {
+ if route_route_params.final_value_msat != route_params.final_value_msat {
+ debug_assert!(false,
+ "Routers are expected to return a route which includes the requested final_value_msat");
+ route_route_params.final_value_msat = route_params.final_value_msat;
+ }
+ }
+
for path in route.paths.iter() {
if path.hops.len() == 0 {
log_error!(logger, "Unusable path in route (path.hops.len() must be at least 1");
}
let mut has_ok = false;
let mut has_err = false;
- let mut pending_amt_unsent = 0;
+ let mut has_unsent = false;
let mut total_ok_fees_msat = 0;
+ let mut total_ok_amt_sent_msat = 0;
for (res, path) in results.iter().zip(route.paths.iter()) {
if res.is_ok() {
has_ok = true;
total_ok_fees_msat += path.fee_msat();
+ total_ok_amt_sent_msat += path.final_value_msat();
}
if res.is_err() { has_err = true; }
if let &Err(APIError::MonitorUpdateInProgress) = res {
has_err = true;
has_ok = true;
total_ok_fees_msat += path.fee_msat();
+ total_ok_amt_sent_msat += path.final_value_msat();
} else if res.is_err() {
- pending_amt_unsent += path.final_value_msat();
+ has_unsent = true;
}
}
if has_err && has_ok {
Err(PaymentSendFailure::PartialFailure {
results,
payment_id,
- failed_paths_retry: if pending_amt_unsent != 0 {
+ failed_paths_retry: if has_unsent {
if let Some(route_params) = &route.route_params {
let mut route_params = route_params.clone();
// We calculate the leftover fee budget we're allowed to spend by
// subtracting the used fee from the total fee budget.
route_params.max_total_routing_fee_msat = route_params
.max_total_routing_fee_msat.map(|m| m.saturating_sub(total_ok_fees_msat));
- route_params.final_value_msat = pending_amt_unsent;
+ // We calculate the remaining target amount by subtracting the succeded
+ // path values.
+ route_params.final_value_msat = route_params.final_value_msat
+ .saturating_sub(total_ok_amt_sent_msat);
Some(route_params)
} else { None }
} else { None },
) -> bool where L::Target: Logger {
#[cfg(test)]
let DecodedOnionFailure {
- network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data
+ network_update, short_channel_id, payment_failed_permanently, onion_error_code,
+ onion_error_data
} = onion_error.decode_onion_failure(secp_ctx, logger, &source);
#[cfg(not(test))]
- let DecodedOnionFailure { network_update, short_channel_id, payment_retryable } =
+ let DecodedOnionFailure { network_update, short_channel_id, payment_failed_permanently } =
onion_error.decode_onion_failure(secp_ctx, logger, &source);
let payment_is_probe = payment_is_probe(payment_hash, &payment_id, probing_cookie_secret);
payment.get_mut().insert_previously_failed_scid(scid);
}
- if payment_is_probe || !is_retryable_now || !payment_retryable {
- let reason = if !payment_retryable {
+ if payment_is_probe || !is_retryable_now || payment_failed_permanently {
+ let reason = if payment_failed_permanently {
PaymentFailureReason::RecipientRejected
} else {
PaymentFailureReason::RetriesExhausted
is_retryable_now = false;
}
if payment.get().remaining_parts() == 0 {
- if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. }= payment.get() {
+ if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = payment.get() {
if !payment_is_probe {
full_failure_ev = Some(events::Event::PaymentFailed {
payment_id: *payment_id,
let path_failure = {
if payment_is_probe {
- if !payment_retryable {
+ if payment_failed_permanently {
events::Event::ProbeSuccessful {
payment_id: *payment_id,
payment_hash: payment_hash.clone(),
events::Event::PaymentPathFailed {
payment_id: Some(*payment_id),
payment_hash: payment_hash.clone(),
- payment_failed_permanently: !payment_retryable,
+ payment_failed_permanently,
failure: events::PathFailure::OnPath { network_update },
path: path.clone(),
short_channel_id,
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
- );
- assert!(outbound_payments.has_pending_payments());
-
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
+ assert!(outbound_payments.add_new_awaiting_invoice(
+ payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000))
+ .is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
router.expect_find_route(
RouteParameters::from_payment_params_and_value(
PaymentParameters::from_bolt12_invoice(&invoice),
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
- );
- assert!(outbound_payments.has_pending_payments());
-
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
+ assert!(outbound_payments.add_new_awaiting_invoice(
+ payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000))
+ .is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
let route_params = RouteParameters::from_payment_params_and_value(
PaymentParameters::from_bolt12_invoice(&invoice),
invoice.amount_msats(),