- let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code);
-
- // indicate that payment parameter has failed and no need to
- // update Route object
- let payment_failed = match error_code & 0xff {
- 15|16|17|18|19|23 => true,
- _ => false,
- } && is_from_final_node; // PERM bit observed below even if this error is from the intermediate nodes
-
- let mut network_update = None;
- let mut short_channel_id = None;
-
- if error_code & BADONION == BADONION {
- // If the error code has the BADONION bit set, always blame the channel
- // from the node "originating" the error to its next hop. The
- // "originator" is ultimately actually claiming that its counterparty
- // is the one who is failing the HTLC.
- // If the "originator" here isn't lying we should really mark the
- // next-hop node as failed entirely, but we can't be confident in that,
- // as it would allow any node to get us to completely ban one of its
- // counterparties. Instead, we simply remove the channel in question.
- network_update = Some(NetworkUpdate::ChannelFailure {
- short_channel_id: failing_route_hop.short_channel_id,
- is_permanent: true,
- });
- } else if error_code & NODE == NODE {
- let is_permanent = error_code & PERM == PERM;
- network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent });
- short_channel_id = Some(route_hop.short_channel_id);
- } else if error_code & PERM == PERM {
- if !payment_failed {
- network_update = Some(NetworkUpdate::ChannelFailure {
- short_channel_id: failing_route_hop.short_channel_id,
- is_permanent: true,
- });
- short_channel_id = Some(failing_route_hop.short_channel_id);
- }
- } else if error_code & UPDATE == UPDATE {
- if let Some(update_len_slice) = err_packet.failuremsg.get(debug_field_size+2..debug_field_size+4) {
- let update_len = u16::from_be_bytes(update_len_slice.try_into().expect("len is 2")) as usize;
- if let Some(mut update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) {
- // Historically, the BOLTs were unclear if the message type
- // bytes should be included here or not. The BOLTs have now
- // been updated to indicate that they *are* included, but many
- // nodes still send messages without the type bytes, so we
- // support both here.
- // TODO: Switch to hard require the type prefix, as the current
- // permissiveness introduces the (although small) possibility
- // that we fail to decode legitimate channel updates that
- // happen to start with ChannelUpdate::TYPE, i.e., [0x01, 0x02].
- if update_slice.len() > 2 && update_slice[0..2] == msgs::ChannelUpdate::TYPE.to_be_bytes() {
- update_slice = &update_slice[2..];
- } else {
- log_trace!(logger, "Failure provided features a channel update without type prefix. Deprecated, but allowing for now.");
- }
- let update_opt = msgs::ChannelUpdate::read(&mut Cursor::new(&update_slice));
- if update_opt.is_ok() || update_slice.is_empty() {
- // if channel_update should NOT have caused the failure:
- // MAY treat the channel_update as invalid.
- let is_chan_update_invalid = match error_code & 0xff {
- 7 => false,
- 11 => update_opt.is_ok() &&
- amt_to_forward >
- update_opt.as_ref().unwrap().contents.htlc_minimum_msat,
- 12 => update_opt.is_ok() && amt_to_forward
- .checked_mul(update_opt.as_ref().unwrap()
- .contents.fee_proportional_millionths as u64)
- .map(|prop_fee| prop_fee / 1_000_000)
- .and_then(|prop_fee| prop_fee.checked_add(
- update_opt.as_ref().unwrap().contents.fee_base_msat as u64))
- .map(|fee_msats| route_hop.fee_msat >= fee_msats)
- .unwrap_or(false),
- 13 => update_opt.is_ok() &&
- route_hop.cltv_expiry_delta as u16 >=
- update_opt.as_ref().unwrap().contents.cltv_expiry_delta,
- 14 => false, // expiry_too_soon; always valid?
- 20 => update_opt.as_ref().unwrap().contents.flags & 2 == 0,
- _ => false, // unknown error code; take channel_update as valid
- };
- if is_chan_update_invalid {
- // This probably indicates the node which forwarded
- // to the node in question corrupted something.
- network_update = Some(NetworkUpdate::ChannelFailure {
- short_channel_id: route_hop.short_channel_id,
- is_permanent: true,
- });
- } else {
- if let Ok(chan_update) = update_opt {
- // Make sure the ChannelUpdate contains the expected
- // short channel id.
- if failing_route_hop.short_channel_id == chan_update.contents.short_channel_id {
- short_channel_id = Some(failing_route_hop.short_channel_id);
- } else {
- log_info!(logger, "Node provided a channel_update for which it was not authoritative, ignoring.");
- }
- network_update = Some(NetworkUpdate::ChannelUpdateMessage {
- msg: chan_update,
- })
- } else {
- network_update = Some(NetworkUpdate::ChannelFailure {
- short_channel_id: route_hop.short_channel_id,
- is_permanent: false,
- });
- }
- };
- } else {
- // If the channel_update had a non-zero length (i.e. was
- // present) but we couldn't read it, treat it as a total
- // node failure.
- log_info!(logger,
- "Failed to read a channel_update of len {} in an onion",
- update_slice.len());
- }
- }
- }
- if network_update.is_none() {
- // They provided an UPDATE which was obviously bogus, not worth
- // trying to relay through them anymore.
- network_update = Some(NetworkUpdate::NodeFailure {
- node_id: route_hop.pubkey,
- is_permanent: true,
- });
- }
- if short_channel_id.is_none() {
- short_channel_id = Some(route_hop.short_channel_id);
- }
- } else if payment_failed {
- // Only blame the hop when a value in the HTLC doesn't match the
- // corresponding value in the onion.
- short_channel_id = match error_code & 0xff {
- 18|19 => Some(route_hop.short_channel_id),
- _ => None,
- };
- } else {
- // We can't understand their error messages and they failed to
- // forward...they probably can't understand our forwards so its
- // really not worth trying any further.
- network_update = Some(NetworkUpdate::NodeFailure {
- node_id: route_hop.pubkey,
- is_permanent: true,
- });
- short_channel_id = Some(route_hop.short_channel_id);
- }
-
- res = Some((network_update, short_channel_id, !(error_code & PERM == PERM && is_from_final_node)));
-
- let (description, title) = errors::get_onion_error_description(error_code);
- if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
- log_info!(logger, "Onion Error[from {}: {}({:#x}) {}({})] {}", route_hop.pubkey, title, error_code, debug_field, log_bytes!(&err_packet.failuremsg[4..4+debug_field_size]), description);
- }
- else {
- log_info!(logger, "Onion Error[from {}: {}({:#x})] {}", route_hop.pubkey, title, error_code, description);
- }
- } else {
- // Useless packet that we can't use but it passed HMAC, so it
- // definitely came from the peer in question
- let network_update = Some(NetworkUpdate::NodeFailure {
- node_id: route_hop.pubkey,
- is_permanent: true,
- });
- let short_channel_id = Some(route_hop.short_channel_id);
- res = Some((network_update, short_channel_id, !is_from_final_node));
+ // Learnings from the HTLC failure to inform future payment retries and scoring.
+ struct FailureLearnings {
+ network_update: Option<NetworkUpdate>,
+ short_channel_id: Option<u64>,
+ payment_failed_permanently: bool,
+ failed_within_blinded_path: bool,
+ }
+ let mut res: Option<FailureLearnings> = None;
+ let mut htlc_msat = *first_hop_htlc_msat;
+ let mut error_code_ret = None;
+ let mut error_packet_ret = None;
+ let mut is_from_final_node = false;
+
+ const BADONION: u16 = 0x8000;
+ const PERM: u16 = 0x4000;
+ const NODE: u16 = 0x2000;
+ const UPDATE: u16 = 0x1000;
+
+ // Handle packed channel/node updates for passing back for the route handler
+ let callback = |shared_secret, _, _, route_hop_opt: Option<&RouteHop>, route_hop_idx| {
+ if res.is_some() {
+ return;
+ }
+
+ let route_hop = match route_hop_opt {
+ Some(hop) => hop,
+ None => {
+ // Got an error from within a blinded route.
+ error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
+ error_packet_ret = Some(vec![0; 32]);
+ res = Some(FailureLearnings {
+ network_update: None,
+ short_channel_id: None,
+ payment_failed_permanently: false,
+ failed_within_blinded_path: true,
+ });
+ return;
+ },
+ };
+
+ // The failing hop includes either the inbound channel to the recipient or the outbound channel
+ // from the current hop (i.e., the next hop's inbound channel).
+ let num_blinded_hops = path.blinded_tail.as_ref().map_or(0, |bt| bt.hops.len());
+ // For 1-hop blinded paths, the final `path.hops` entry is the recipient.
+ is_from_final_node = route_hop_idx + 1 == path.hops.len() && num_blinded_hops <= 1;
+ let failing_route_hop = if is_from_final_node {
+ route_hop
+ } else {
+ match path.hops.get(route_hop_idx + 1) {
+ Some(hop) => hop,
+ None => {
+ // The failing hop is within a multi-hop blinded path.
+ #[cfg(not(test))]
+ {
+ error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
+ error_packet_ret = Some(vec![0; 32]);
+ }
+ #[cfg(test)]
+ {
+ // Actually parse the onion error data in tests so we can check that blinded hops fail
+ // back correctly.
+ let err_packet =
+ decrypt_onion_error_packet(&mut encrypted_packet, shared_secret)
+ .unwrap();
+ error_code_ret = Some(u16::from_be_bytes(
+ err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap(),
+ ));
+ error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());