@@ -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