@@ -426,7 +426,7 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
426
426
pub ( crate ) struct DecodedOnionFailure {
427
427
pub ( crate ) network_update : Option < NetworkUpdate > ,
428
428
pub ( crate ) short_channel_id : Option < u64 > ,
429
- pub ( crate ) payment_retryable : bool ,
429
+ pub ( crate ) payment_failed_permanently : bool ,
430
430
#[ cfg( test) ]
431
431
pub ( crate ) onion_error_code : Option < u16 > ,
432
432
#[ cfg( test) ]
@@ -444,7 +444,14 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
444
444
} = htlc_source {
445
445
( path, session_priv, first_hop_htlc_msat)
446
446
} 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 ;
448
455
let mut htlc_msat = * first_hop_htlc_msat;
449
456
let mut error_code_ret = None ;
450
457
let mut error_packet_ret = None ;
@@ -467,11 +474,33 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
467
474
// Got an error from within a blinded route.
468
475
error_code_ret = Some ( BADONION | PERM | 24 ) ; // invalid_onion_blinding
469
476
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
+ } ) ;
471
480
return
472
481
} ,
473
482
} ;
474
483
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
+
475
504
let amt_to_forward = htlc_msat - route_hop. fee_msat ;
476
505
htlc_msat = amt_to_forward;
477
506
@@ -483,11 +512,6 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
483
512
chacha. process ( & packet_decrypted, & mut decryption_tmp[ ..] ) ;
484
513
packet_decrypted = decryption_tmp;
485
514
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
-
491
515
let err_packet = match msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new ( & packet_decrypted) ) {
492
516
Ok ( p) => p,
493
517
Err ( _) => return
@@ -507,7 +531,9 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
507
531
is_permanent : true ,
508
532
} ) ;
509
533
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
+ } ) ;
511
537
return
512
538
}
513
539
} ;
@@ -615,8 +641,9 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
615
641
} else {
616
642
// The node in question intentionally encoded a 0-length channel update. This is
617
643
// likely due to https://github.com/ElementsProject/lightning/issues/6200.
644
+ short_channel_id = Some ( failing_route_hop. short_channel_id ) ;
618
645
network_update = Some ( NetworkUpdate :: ChannelFailure {
619
- short_channel_id : route_hop . short_channel_id ,
646
+ short_channel_id : failing_route_hop . short_channel_id ,
620
647
is_permanent : false ,
621
648
} ) ;
622
649
}
@@ -659,7 +686,10 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
659
686
short_channel_id = Some ( route_hop. short_channel_id ) ;
660
687
}
661
688
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
+ } ) ;
663
693
664
694
let ( description, title) = errors:: get_onion_error_description ( error_code) ;
665
695
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>(
668
698
log_info ! ( logger, "Onion Error[from {}: {}({:#x})] {}" , route_hop. pubkey, title, error_code, description) ;
669
699
}
670
700
} ) . 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 {
672
704
DecodedOnionFailure {
673
- network_update, short_channel_id, payment_retryable ,
705
+ network_update, short_channel_id, payment_failed_permanently ,
674
706
#[ cfg( test) ]
675
707
onion_error_code : error_code_ret,
676
708
#[ cfg( test) ]
@@ -680,7 +712,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
680
712
// only not set either packet unparseable or hmac does not match with any
681
713
// payment not retryable only when garbage is from the final node
682
714
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,
684
716
#[ cfg( test) ]
685
717
onion_error_code : None ,
686
718
#[ cfg( test) ]
@@ -824,7 +856,7 @@ impl HTLCFailReason {
824
856
if let & HTLCSource :: OutboundRoute { ref path, .. } = htlc_source {
825
857
DecodedOnionFailure {
826
858
network_update : None ,
827
- payment_retryable : true ,
859
+ payment_failed_permanently : false ,
828
860
short_channel_id : Some ( path. hops [ 0 ] . short_channel_id ) ,
829
861
#[ cfg( test) ]
830
862
onion_error_code : Some ( * failure_code) ,
0 commit comments