+ log_info!(logger, "Onion Error[from {}: {}({:#x})] {}", route_hop.pubkey, title, error_code, description);
+ }
+ }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
+ if let Some(FailureLearnings {
+ network_update, short_channel_id, payment_failed_permanently
+ }) = res {
+ DecodedOnionFailure {
+ network_update, short_channel_id, payment_failed_permanently,
+ #[cfg(test)]
+ onion_error_code: error_code_ret,
+ #[cfg(test)]
+ onion_error_data: error_packet_ret
+ }
+ } else {
+ // only not set either packet unparseable or hmac does not match with any
+ // payment not retryable only when garbage is from the final node
+ DecodedOnionFailure {
+ network_update: None, short_channel_id: None, payment_failed_permanently: is_from_final_node,
+ #[cfg(test)]
+ onion_error_code: None,
+ #[cfg(test)]
+ onion_error_data: None
+ }
+ }
+}
+
+#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
+pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
+
+#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
+enum HTLCFailReasonRepr {
+ LightningError {
+ err: msgs::OnionErrorPacket,
+ },
+ Reason {
+ failure_code: u16,
+ data: Vec<u8>,
+ }
+}
+
+impl core::fmt::Debug for HTLCFailReason {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ match self.0 {
+ HTLCFailReasonRepr::Reason { ref failure_code, .. } => {
+ write!(f, "HTLC error code {}", failure_code)
+ },
+ HTLCFailReasonRepr::LightningError { .. } => {
+ write!(f, "pre-built LightningError")
+ }
+ }
+ }
+}
+
+impl Writeable for HTLCFailReason {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
+ self.0.write(writer)
+ }
+}
+impl Readable for HTLCFailReason {
+ fn read<R: Read>(reader: &mut R) -> Result<Self, msgs::DecodeError> {
+ Ok(Self(Readable::read(reader)?))
+ }
+}
+
+impl_writeable_tlv_based_enum!(HTLCFailReasonRepr,
+ (0, LightningError) => {
+ (0, err, required),
+ },
+ (1, Reason) => {
+ (0, failure_code, required),
+ (2, data, required_vec),
+ },
+;);
+
+impl HTLCFailReason {
+ pub(super) fn reason(failure_code: u16, data: Vec<u8>) -> Self {
+ const BADONION: u16 = 0x8000;
+ const PERM: u16 = 0x4000;
+ const NODE: u16 = 0x2000;
+ const UPDATE: u16 = 0x1000;
+
+ if failure_code == 1 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 2 | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 2 | PERM | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 3 | PERM | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 4 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 5 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 6 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 7 | UPDATE {
+ debug_assert_eq!(data.len() - 2, u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize) }
+ else if failure_code == 8 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 9 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 10 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 11 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 8, u16::from_be_bytes(data[8..10].try_into().unwrap()) as usize) }
+ else if failure_code == 12 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 8, u16::from_be_bytes(data[8..10].try_into().unwrap()) as usize) }
+ else if failure_code == 13 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 4, u16::from_be_bytes(data[4..6].try_into().unwrap()) as usize) }
+ else if failure_code == 14 | UPDATE {
+ debug_assert_eq!(data.len() - 2, u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize) }
+ else if failure_code == 15 | PERM { debug_assert_eq!(data.len(), 12) }
+ else if failure_code == 18 { debug_assert_eq!(data.len(), 4) }
+ else if failure_code == 19 { debug_assert_eq!(data.len(), 8) }
+ else if failure_code == 20 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 2, u16::from_be_bytes(data[2..4].try_into().unwrap()) as usize) }
+ else if failure_code == 21 { debug_assert!(data.is_empty()) }
+ else if failure_code == 22 | PERM { debug_assert!(data.len() <= 11) }
+ else if failure_code == 23 { debug_assert!(data.is_empty()) }
+ else if failure_code & BADONION != 0 {
+ // We set some bogus BADONION failure codes in test, so ignore unknown ones.
+ }
+ else { debug_assert!(false, "Unknown failure code: {}", failure_code) }
+
+ Self(HTLCFailReasonRepr::Reason { failure_code, data })
+ }
+
+ pub(super) fn from_failure_code(failure_code: u16) -> Self {
+ Self::reason(failure_code, Vec::new())
+ }
+
+ pub(super) fn from_msg(msg: &msgs::UpdateFailHTLC) -> Self {
+ Self(HTLCFailReasonRepr::LightningError { err: msg.reason.clone() })
+ }
+
+ pub(super) fn get_encrypted_failure_packet(&self, incoming_packet_shared_secret: &[u8; 32], phantom_shared_secret: &Option<[u8; 32]>)
+ -> msgs::OnionErrorPacket {
+ match self.0 {
+ HTLCFailReasonRepr::Reason { ref failure_code, ref data } => {
+ if let Some(phantom_ss) = phantom_shared_secret {
+ let phantom_packet = build_failure_packet(phantom_ss, *failure_code, &data[..]).encode();
+ let encrypted_phantom_packet = encrypt_failure_packet(phantom_ss, &phantom_packet);
+ encrypt_failure_packet(incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
+ } else {
+ let packet = build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..]).encode();
+ encrypt_failure_packet(incoming_packet_shared_secret, &packet)
+ }
+ },
+ HTLCFailReasonRepr::LightningError { ref err } => {
+ encrypt_failure_packet(incoming_packet_shared_secret, &err.data)
+ }