},
};
+ // 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.
+ 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
+ });
+ return
+ }
+ }
+ };
+
let amt_to_forward = htlc_msat - route_hop.fee_msat;
htlc_msat = amt_to_forward;
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
packet_decrypted = decryption_tmp;
- // 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).
- is_from_final_node = route_hop_idx + 1 == path.hops.len();
- let failing_route_hop = if is_from_final_node { route_hop } else { &path.hops[route_hop_idx + 1] };
-
let err_packet = match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
Ok(p) => p,
Err(_) => return