use lightning::routing::utxo::UtxoLookup;
use lightning::routing::router::Router;
use lightning::routing::scoring::{Score, WriteableScore};
-use lightning::util::events::{Event, EventHandler, EventsProvider};
+use lightning::util::events::{Event, EventHandler, EventsProvider, PathFailure};
use lightning::util::logger::Logger;
use lightning::util::persist::Persister;
use lightning_rapid_gossip_sync::RapidGossipSync;
fn handle_network_graph_update<L: Deref>(
network_graph: &NetworkGraph<L>, event: &Event
) where L::Target: Logger {
- if let Event::PaymentPathFailed { ref network_update, .. } = event {
- if let Some(network_update) = network_update {
- network_graph.handle_network_update(&network_update);
- }
+ if let Event::PaymentPathFailed {
+ failure: PathFailure::OnPath { network_update: Some(ref upd) }, .. } = event
+ {
+ network_graph.handle_network_update(upd);
}
}
use lightning::routing::router::{DefaultRouter, RouteHop};
use lightning::routing::scoring::{ChannelUsage, Score};
use lightning::util::config::UserConfig;
- use lightning::util::events::{Event, MessageSendEventsProvider, MessageSendEvent};
+ use lightning::util::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::util::ser::Writeable;
use lightning::util::test_utils;
use lightning::util::persist::KVStorePersister;
payment_id: None,
payment_hash: PaymentHash([42; 32]),
payment_failed_permanently: false,
- network_update: None,
+ failure: PathFailure::OnPath { network_update: None },
path: path.clone(),
short_channel_id: Some(scored_scid),
retry: None,
payment_id: None,
payment_hash: PaymentHash([42; 32]),
payment_failed_permanently: true,
- network_update: None,
+ failure: PathFailure::OnPath { network_update: None },
path: path.clone(),
short_channel_id: None,
retry: None,
use crate::util::scid_utils;
use crate::util::test_utils;
use crate::util::test_utils::{panicking, TestChainMonitor};
-use crate::util::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
+use crate::util::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose};
use crate::util::errors::APIError;
use crate::util::config::UserConfig;
use crate::util::ser::{ReadableArgs, Writeable};
) {
if conditions.expected_mpp_parts_remain { assert_eq!(payment_failed_events.len(), 1); } else { assert_eq!(payment_failed_events.len(), 2); }
let expected_payment_id = match &payment_failed_events[0] {
- Event::PaymentPathFailed { payment_hash, payment_failed_permanently, path, retry, payment_id, network_update, short_channel_id,
+ Event::PaymentPathFailed { payment_hash, payment_failed_permanently, path, retry, payment_id, failure, short_channel_id,
#[cfg(test)]
error_code,
#[cfg(test)]
}
if let Some(chan_closed) = conditions.expected_blamed_chan_closed {
- match network_update {
- Some(NetworkUpdate::ChannelUpdateMessage { ref msg }) if !chan_closed => {
- if let Some(scid) = conditions.expected_blamed_scid {
- assert_eq!(msg.contents.short_channel_id, scid);
- }
- const CHAN_DISABLED_FLAG: u8 = 2;
- assert_eq!(msg.contents.flags & CHAN_DISABLED_FLAG, 0);
- },
- Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent }) if chan_closed => {
- if let Some(scid) = conditions.expected_blamed_scid {
- assert_eq!(*short_channel_id, scid);
- }
- assert!(is_permanent);
- },
- Some(_) => panic!("Unexpected update type"),
- None => panic!("Expected update"),
- }
+ if let PathFailure::OnPath { network_update: Some(upd) } = failure {
+ match upd {
+ NetworkUpdate::ChannelUpdateMessage { ref msg } if !chan_closed => {
+ if let Some(scid) = conditions.expected_blamed_scid {
+ assert_eq!(msg.contents.short_channel_id, scid);
+ }
+ const CHAN_DISABLED_FLAG: u8 = 2;
+ assert_eq!(msg.contents.flags & CHAN_DISABLED_FLAG, 0);
+ },
+ NetworkUpdate::ChannelFailure { short_channel_id, is_permanent } if chan_closed => {
+ if let Some(scid) = conditions.expected_blamed_scid {
+ assert_eq!(*short_channel_id, scid);
+ }
+ assert!(is_permanent);
+ },
+ _ => panic!("Unexpected update type"),
+ }
+ } else { panic!("Expected network update"); }
}
payment_id.unwrap()
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::enforcing_trait_impls::EnforcingSigner;
use crate::util::test_utils;
-use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason, HTLCDestination};
+use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination};
use crate::util::errors::APIError;
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::config::UserConfig;
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 6);
match events[0] {
- Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
+ Event::PaymentPathFailed { ref payment_hash, ref failure, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
// If we delivered B's RAA we got an unknown preimage error, not something
// that we should update our routing table for.
if !deliver_bs_raa {
- assert!(network_update.is_some());
+ if let PathFailure::OnPath { network_update: Some(_) } = failure { } else { panic!("Unexpected path failure") }
}
},
_ => panic!("Unexpected event"),
_ => panic!("Unexpected event"),
}
match events[2] {
- Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
+ Event::PaymentPathFailed { ref payment_hash, failure: PathFailure::OnPath { network_update: Some(_) }, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
- assert!(network_update.is_some());
},
_ => panic!("Unexpected event"),
}
_ => panic!("Unexpected event"),
}
match events[4] {
- Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
+ Event::PaymentPathFailed { ref payment_hash, failure: PathFailure::OnPath { network_update: Some(_) }, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
- assert!(network_update.is_some());
},
_ => panic!("Unexpected event"),
}
let mut as_failds = HashSet::new();
let mut as_updates = 0;
for event in as_events.iter() {
- if let &Event::PaymentPathFailed { ref payment_hash, ref payment_failed_permanently, ref network_update, .. } = event {
+ if let &Event::PaymentPathFailed { ref payment_hash, ref payment_failed_permanently, ref failure, .. } = event {
assert!(as_failds.insert(*payment_hash));
if *payment_hash != payment_hash_2 {
assert_eq!(*payment_failed_permanently, deliver_last_raa);
} else {
assert!(!payment_failed_permanently);
}
- if network_update.is_some() {
+ if let PathFailure::OnPath { network_update: Some(_) } = failure {
as_updates += 1;
}
} else if let &Event::PaymentFailed { .. } = event {
let mut bs_failds = HashSet::new();
let mut bs_updates = 0;
for event in bs_events.iter() {
- if let &Event::PaymentPathFailed { ref payment_hash, ref payment_failed_permanently, ref network_update, .. } = event {
+ if let &Event::PaymentPathFailed { ref payment_hash, ref payment_failed_permanently, ref failure, .. } = event {
assert!(bs_failds.insert(*payment_hash));
if *payment_hash != payment_hash_1 && *payment_hash != payment_hash_5 {
assert_eq!(*payment_failed_permanently, deliver_last_raa);
} else {
assert!(!payment_failed_permanently);
}
- if network_update.is_some() {
+ if let PathFailure::OnPath { network_update: Some(_) } = failure {
bs_updates += 1;
}
} else if let &Event::PaymentFailed { .. } = event {
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match &events[0] {
- &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref short_channel_id, .. } => {
+ &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, failure: PathFailure::OnPath { network_update: None }, ref short_channel_id, .. } => {
assert_eq!(PaymentId(our_payment_hash.0), *payment_id.as_ref().unwrap());
assert_eq!(our_payment_hash.clone(), *payment_hash);
assert_eq!(*payment_failed_permanently, false);
- assert_eq!(*network_update, None);
assert_eq!(*short_channel_id, Some(route.paths[0][0].short_channel_id));
},
_ => panic!("Unexpected event"),
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match &events[0] {
- &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref short_channel_id, .. } => {
+ &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, failure: PathFailure::OnPath { network_update: None }, ref short_channel_id, .. } => {
assert_eq!(payment_id_2, *payment_id.as_ref().unwrap());
assert_eq!(payment_hash_2.clone(), *payment_hash);
assert_eq!(*payment_failed_permanently, false);
- assert_eq!(*network_update, None);
assert_eq!(*short_channel_id, Some(route_2.paths[0][0].short_channel_id));
},
_ => panic!("Unexpected event"),
// Expect a PaymentPathFailed event with a ChannelFailure network update for the channel between
// the node originating the error to its next hop.
match events_5[0] {
- Event::PaymentPathFailed { network_update:
- Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent }), error_code, ..
+ Event::PaymentPathFailed { error_code, failure: PathFailure::OnPath { network_update: Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent }) }, ..
} => {
assert_eq!(short_channel_id, chan_2.0.contents.short_channel_id);
assert!(is_permanent);
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate};
use crate::ln::wire::Encode;
-use crate::util::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
+use crate::util::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure};
use crate::util::ser::{Writeable, Writer};
use crate::util::test_utils;
use crate::util::config::{UserConfig, ChannelConfig};
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
- if let &Event::PaymentPathFailed { ref payment_failed_permanently, ref network_update, ref short_channel_id, ref error_code, .. } = &events[0] {
+ if let &Event::PaymentPathFailed { ref payment_failed_permanently, ref short_channel_id, ref error_code, failure: PathFailure::OnPath { ref network_update }, .. } = &events[0] {
assert_eq!(*payment_failed_permanently, !expected_retryable);
assert_eq!(*error_code, expected_error_code);
if expected_channel_update.is_some() {
payment_id: Some(payment_id),
payment_hash,
payment_failed_permanently: false,
- network_update: None,
+ failure: events::PathFailure::InitialSend { err: e },
path,
short_channel_id: failed_scid,
retry: None,
payment_id: Some(*payment_id),
payment_hash: payment_hash.clone(),
payment_failed_permanently: !payment_retryable,
- network_update,
+ failure: events::PathFailure::OnPath { network_update },
path: path.clone(),
short_channel_id,
retry,
use crate::routing::gossip::{EffectiveCapacity, RoutingFees};
use crate::routing::router::{get_route, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RouteParameters};
use crate::routing::scoring::ChannelUsage;
-use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
+use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure};
use crate::util::test_utils;
use crate::util::errors::APIError;
use crate::util::ser::Writeable;
assert_eq!(events.len(), 1);
match events[0] {
Event::PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently: false,
- network_update: None, short_channel_id: Some(expected_scid), .. } => {
+ failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { err: ref err_msg }},
+ short_channel_id: Some(expected_scid), .. } =>
+ {
assert_eq!(payment_hash, ev_payment_hash);
assert_eq!(expected_scid, route.paths[1][0].short_channel_id);
+ assert!(err_msg.contains("max HTLC"));
},
_ => panic!("Unexpected event"),
}
assert_eq!(events.len(), 1);
match events[0] {
Event::PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently: false,
- network_update: None, short_channel_id: Some(expected_scid), .. } => {
+ failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { err: ref err_msg }},
+ short_channel_id: Some(expected_scid), .. } =>
+ {
assert_eq!(payment_hash, ev_payment_hash);
assert_eq!(expected_scid, route.paths[1][0].short_channel_id);
+ assert!(err_msg.contains("max HTLC"));
},
_ => panic!("Unexpected event"),
}
use crate::ln::msgs;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::routing::gossip::NetworkUpdate;
+use crate::util::errors::APIError;
use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength};
use crate::routing::router::{RouteHop, RouteParameters};
(2, SpontaneousPayment)
);
+/// When the payment path failure took place and extra details about it. [`PathFailure::OnPath`] may
+/// contain a [`NetworkUpdate`] that needs to be applied to the [`NetworkGraph`].
+///
+/// [`NetworkUpdate`]: crate::routing::gossip::NetworkUpdate
+/// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum PathFailure {
+ /// We failed to initially send the payment and no HTLC was committed to. Contains the relevant
+ /// error.
+ InitialSend {
+ /// The error surfaced from initial send.
+ err: APIError,
+ },
+ /// A hop on the path failed to forward our payment.
+ OnPath {
+ /// If present, this [`NetworkUpdate`] should be applied to the [`NetworkGraph`] so that routing
+ /// decisions can take into account the update.
+ ///
+ /// [`NetworkUpdate`]: crate::routing::gossip::NetworkUpdate
+ /// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph
+ network_update: Option<NetworkUpdate>,
+ },
+}
+
+impl_writeable_tlv_based_enum_upgradable!(PathFailure,
+ (0, OnPath) => {
+ (0, network_update, upgradable_option),
+ },
+ (2, InitialSend) => {
+ (0, err, upgradable_required),
+ },
+);
+
#[derive(Clone, Debug, PartialEq, Eq)]
/// The reason the channel was closed. See individual variants more details.
pub enum ClosureReason {
fee_paid_msat: Option<u64>,
},
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
- /// provide failure information for each MPP part in the payment.
+ /// provide failure information for each path attempt in the payment, including retries.
///
/// This event is provided once there are no further pending HTLCs for the payment and the
/// payment is no longer retryable, due either to the [`Retry`] provided or
/// the payment has failed, not just the route in question. If this is not set, the payment may
/// be retried via a different route.
payment_failed_permanently: bool,
- /// Any failure information conveyed via the Onion return packet by a node along the failed
- /// payment route.
- ///
- /// Should be applied to the [`NetworkGraph`] so that routing decisions can take into
- /// account the update.
+ /// Extra error details based on the failure type. May contain an update that needs to be
+ /// applied to the [`NetworkGraph`].
///
/// [`NetworkGraph`]: crate::routing::gossip::NetworkGraph
- network_update: Option<NetworkUpdate>,
+ failure: PathFailure,
/// The payment path that failed.
path: Vec<RouteHop>,
/// The channel responsible for the failed payment path.
});
},
&Event::PaymentPathFailed {
- ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update,
+ ref payment_id, ref payment_hash, ref payment_failed_permanently, ref failure,
ref path, ref short_channel_id, ref retry,
#[cfg(test)]
ref error_code,
error_data.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_hash, required),
- (1, network_update, option),
+ (1, None::<NetworkUpdate>, option), // network_update in LDK versions prior to 0.0.114
(2, payment_failed_permanently, required),
(3, false, required), // all_paths_failed in LDK versions prior to 0.0.114
(5, *path, vec_type),
(7, short_channel_id, option),
(9, retry, option),
(11, payment_id, option),
+ (13, failure, required),
});
},
&Event::PendingHTLCsForwardable { time_forwardable: _ } => {
let mut short_channel_id = None;
let mut retry = None;
let mut payment_id = None;
+ let mut failure_opt = None;
read_tlv_fields!(reader, {
(0, payment_hash, required),
(1, network_update, upgradable_option),
(7, short_channel_id, option),
(9, retry, option),
(11, payment_id, option),
+ (13, failure_opt, upgradable_option),
});
+ let failure = failure_opt.unwrap_or_else(|| PathFailure::OnPath { network_update });
Ok(Some(Event::PaymentPathFailed {
payment_id,
payment_hash,
payment_failed_permanently,
- network_update,
+ failure,
path: path.unwrap(),
short_channel_id,
retry,
## API Updates
+- `Event::PaymentPathFailed::network_update` has been replaced by a new `Failure` enum, which may
+ contain the `network_update` within it. See `Event::PaymentPathFailed::failure` and `Failure` docs
+ for more
- `Event::PaymentPathFailed::all_paths_failed` has been removed, as we've dropped support for manual
payment retries.
## Backwards Compatibility
+- If downgrading from 0.0.114 to a previous version, `Event::PaymentPathFailed::network_update` will
+ always be `None`.
- If downgrading from 0.0.114 to a previous version, `Event::PaymentPathFailed::all_paths_failed`
will always be set to `false`. Users who wish to support downgrading and currently rely on the
field should should first migrate to always calling `ChannelManager::abandon_payment` and awaiting