- let mut events = origin_node.node.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), expected_paths.len());
- for (path_idx, (ev, expected_route)) in events.drain(..).zip(expected_paths.iter()).enumerate() {
- let mut payment_event = SendEvent::from_event(ev);
- let mut prev_node = origin_node;
-
- for (idx, &node) in expected_route.iter().enumerate() {
- assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
-
- node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
- check_added_monitors!(node, 0);
- commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
-
- expect_pending_htlcs_forwardable!(node);
-
- if idx == expected_route.len() - 1 {
- let events_2 = node.node.get_and_clear_pending_events();
- // Once we've gotten through all the HTLCs, the last one should result in a
- // PaymentReceived (but each previous one should not!).
- if path_idx == expected_paths.len() - 1 {
- assert_eq!(events_2.len(), 1);
- match events_2[0] {
- Event::PaymentReceived { ref payment_hash, ref payment_secret, amt } => {
- assert_eq!(our_payment_hash, *payment_hash);
- assert_eq!(our_payment_secret, *payment_secret);
- assert_eq!(amt, recv_value);
+impl<'a> PaymentFailedConditions<'a> {
+ pub fn new() -> Self {
+ Self {
+ expected_htlc_error_data: None,
+ expected_blamed_scid: None,
+ expected_blamed_chan_closed: None,
+ expected_mpp_parts_remain: false,
+ }
+ }
+ pub fn mpp_parts_remain(mut self) -> Self {
+ self.expected_mpp_parts_remain = true;
+ self
+ }
+ pub fn blamed_scid(mut self, scid: u64) -> Self {
+ self.expected_blamed_scid = Some(scid);
+ self
+ }
+ pub fn blamed_chan_closed(mut self, closed: bool) -> Self {
+ self.expected_blamed_chan_closed = Some(closed);
+ self
+ }
+ pub fn expected_htlc_error_data(mut self, code: u16, data: &'a [u8]) -> Self {
+ self.expected_htlc_error_data = Some((code, data));
+ self
+ }
+}
+
+#[cfg(test)]
+macro_rules! expect_payment_failed_with_update {
+ ($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $scid: expr, $chan_closed: expr) => {
+ expect_payment_failed_conditions!($node, $expected_payment_hash, $rejected_by_dest,
+ $crate::ln::functional_test_utils::PaymentFailedConditions::new().blamed_scid($scid).blamed_chan_closed($chan_closed));
+ }
+}
+
+#[cfg(test)]
+macro_rules! expect_payment_failed {
+ ($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
+ #[allow(unused_mut)]
+ let mut conditions = $crate::ln::functional_test_utils::PaymentFailedConditions::new();
+ $(
+ conditions = conditions.expected_htlc_error_data($expected_error_code, &$expected_error_data);
+ )*
+ expect_payment_failed_conditions!($node, $expected_payment_hash, $rejected_by_dest, conditions);
+ };
+}
+
+#[cfg(test)]
+macro_rules! expect_payment_failed_conditions {
+ ($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $conditions: expr) => {
+ let events = $node.node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ let expected_payment_id = match events[0] {
+ Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref payment_id, ref network_update, .. } => {
+ assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
+ assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
+ assert!(retry.is_some(), "expected retry.is_some()");
+ assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path");
+ assert_eq!(retry.as_ref().unwrap().payment_params.payee_pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
+
+ assert!(error_code.is_some(), "expected error_code.is_some() = true");
+ assert!(error_data.is_some(), "expected error_data.is_some() = true");
+ if let Some((code, data)) = $conditions.expected_htlc_error_data {
+ assert_eq!(error_code.unwrap(), code, "unexpected error code");
+ assert_eq!(&error_data.as_ref().unwrap()[..], data, "unexpected error data");
+ }
+
+ if let Some(chan_closed) = $conditions.expected_blamed_chan_closed {
+ match network_update {
+ &Some($crate::routing::network_graph::NetworkUpdate::ChannelUpdateMessage { ref msg }) if !chan_closed => {
+ if let Some(scid) = $conditions.expected_blamed_scid {
+ assert_eq!(msg.contents.short_channel_id, scid);
+ }
+ assert_eq!(msg.contents.flags & 2, 0);
+ },
+ &Some($crate::routing::network_graph::NetworkUpdate::ChannelClosed { 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);