@@ -426,7 +426,7 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
426426pub ( crate ) struct DecodedOnionFailure {
427427 pub ( crate ) network_update : Option < NetworkUpdate > ,
428428 pub ( crate ) short_channel_id : Option < u64 > ,
429- pub ( crate ) payment_retryable : bool ,
429+ pub ( crate ) payment_failed_permanently : bool ,
430430 #[ cfg( test) ]
431431 pub ( crate ) onion_error_code : Option < u16 > ,
432432 #[ cfg( test) ]
@@ -444,7 +444,14 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
444444 } = htlc_source {
445445 ( path, session_priv, first_hop_htlc_msat)
446446 } else { unreachable ! ( ) } ;
447- let mut res = None ;
447+
448+ // Learnings from the HTLC failure to inform future payment retries and scoring.
449+ struct FailureLearnings {
450+ network_update : Option < NetworkUpdate > ,
451+ short_channel_id : Option < u64 > ,
452+ payment_failed_permanently : bool ,
453+ }
454+ let mut res: Option < FailureLearnings > = None ;
448455 let mut htlc_msat = * first_hop_htlc_msat;
449456 let mut error_code_ret = None ;
450457 let mut error_packet_ret = None ;
@@ -467,11 +474,33 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
467474 // Got an error from within a blinded route.
468475 error_code_ret = Some ( BADONION | PERM | 24 ) ; // invalid_onion_blinding
469476 error_packet_ret = Some ( vec ! [ 0 ; 32 ] ) ;
470- is_from_final_node = false ;
477+ res = Some ( FailureLearnings {
478+ network_update : None , short_channel_id : None , payment_failed_permanently : false
479+ } ) ;
471480 return
472481 } ,
473482 } ;
474483
484+ // The failing hop includes either the inbound channel to the recipient or the outbound channel
485+ // from the current hop (i.e., the next hop's inbound channel).
486+ let num_blinded_hops = path. blinded_tail . as_ref ( ) . map_or ( 0 , |bt| bt. hops . len ( ) ) ;
487+ // For 1-hop blinded paths, the final `path.hops` entry is the recipient.
488+ is_from_final_node = route_hop_idx + 1 == path. hops . len ( ) && num_blinded_hops <= 1 ;
489+ let failing_route_hop = if is_from_final_node { route_hop } else {
490+ match path. hops . get ( route_hop_idx + 1 ) {
491+ Some ( hop) => hop,
492+ None => {
493+ // The failing hop is within a multi-hop blinded path.
494+ error_code_ret = Some ( BADONION | PERM | 24 ) ; // invalid_onion_blinding
495+ error_packet_ret = Some ( vec ! [ 0 ; 32 ] ) ;
496+ res = Some ( FailureLearnings {
497+ network_update : None , short_channel_id : None , payment_failed_permanently : false
498+ } ) ;
499+ return
500+ }
501+ }
502+ } ;
503+
475504 let amt_to_forward = htlc_msat - route_hop. fee_msat ;
476505 htlc_msat = amt_to_forward;
477506
@@ -483,11 +512,6 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
483512 chacha. process ( & packet_decrypted, & mut decryption_tmp[ ..] ) ;
484513 packet_decrypted = decryption_tmp;
485514
486- // The failing hop includes either the inbound channel to the recipient or the outbound channel
487- // from the current hop (i.e., the next hop's inbound channel).
488- is_from_final_node = route_hop_idx + 1 == path. hops . len ( ) ;
489- let failing_route_hop = if is_from_final_node { route_hop } else { & path. hops [ route_hop_idx + 1 ] } ;
490-
491515 let err_packet = match msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new ( & packet_decrypted) ) {
492516 Ok ( p) => p,
493517 Err ( _) => return
@@ -507,7 +531,9 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
507531 is_permanent : true ,
508532 } ) ;
509533 let short_channel_id = Some ( route_hop. short_channel_id ) ;
510- res = Some ( ( network_update, short_channel_id, !is_from_final_node) ) ;
534+ res = Some ( FailureLearnings {
535+ network_update, short_channel_id, payment_failed_permanently : is_from_final_node
536+ } ) ;
511537 return
512538 }
513539 } ;
@@ -615,8 +641,9 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
615641 } else {
616642 // The node in question intentionally encoded a 0-length channel update. This is
617643 // likely due to https://github.com/ElementsProject/lightning/issues/6200.
644+ short_channel_id = Some ( failing_route_hop. short_channel_id ) ;
618645 network_update = Some ( NetworkUpdate :: ChannelFailure {
619- short_channel_id : route_hop . short_channel_id ,
646+ short_channel_id : failing_route_hop . short_channel_id ,
620647 is_permanent : false ,
621648 } ) ;
622649 }
@@ -659,7 +686,10 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
659686 short_channel_id = Some ( route_hop. short_channel_id ) ;
660687 }
661688
662- res = Some ( ( network_update, short_channel_id, !( error_code & PERM == PERM && is_from_final_node) ) ) ;
689+ res = Some ( FailureLearnings {
690+ network_update, short_channel_id,
691+ payment_failed_permanently : error_code & PERM == PERM && is_from_final_node
692+ } ) ;
663693
664694 let ( description, title) = errors:: get_onion_error_description ( error_code) ;
665695 if debug_field_size > 0 && err_packet. failuremsg . len ( ) >= 4 + debug_field_size {
@@ -668,9 +698,11 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
668698 log_info ! ( logger, "Onion Error[from {}: {}({:#x})] {}" , route_hop. pubkey, title, error_code, description) ;
669699 }
670700 } ) . expect ( "Route that we sent via spontaneously grew invalid keys in the middle of it?" ) ;
671- if let Some ( ( network_update, short_channel_id, payment_retryable) ) = res {
701+ if let Some ( FailureLearnings {
702+ network_update, short_channel_id, payment_failed_permanently
703+ } ) = res {
672704 DecodedOnionFailure {
673- network_update, short_channel_id, payment_retryable ,
705+ network_update, short_channel_id, payment_failed_permanently ,
674706 #[ cfg( test) ]
675707 onion_error_code : error_code_ret,
676708 #[ cfg( test) ]
@@ -680,7 +712,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
680712 // only not set either packet unparseable or hmac does not match with any
681713 // payment not retryable only when garbage is from the final node
682714 DecodedOnionFailure {
683- network_update : None , short_channel_id : None , payment_retryable : ! is_from_final_node,
715+ network_update : None , short_channel_id : None , payment_failed_permanently : is_from_final_node,
684716 #[ cfg( test) ]
685717 onion_error_code : None ,
686718 #[ cfg( test) ]
@@ -824,7 +856,7 @@ impl HTLCFailReason {
824856 if let & HTLCSource :: OutboundRoute { ref path, .. } = htlc_source {
825857 DecodedOnionFailure {
826858 network_update : None ,
827- payment_retryable : true ,
859+ payment_failed_permanently : false ,
828860 short_channel_id : Some ( path. hops [ 0 ] . short_channel_id ) ,
829861 #[ cfg( test) ]
830862 onion_error_code : Some ( * failure_code) ,
0 commit comments