@@ -2131,6 +2131,8 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
2131
2131
let network_nodes = network_graph. nodes ( ) ;
2132
2132
2133
2133
for path in route. paths . iter_mut ( ) {
2134
+ if path. blinded_tail . as_ref ( ) . map_or ( false , |tail| tail. path . blinded_hops . len ( ) > 1 ) { continue }
2135
+
2134
2136
let mut shadow_ctlv_expiry_delta_offset: u32 = 0 ;
2135
2137
2136
2138
// Remember the last three nodes of the random walk and avoid looping back on them.
@@ -2199,7 +2201,10 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
2199
2201
shadow_ctlv_expiry_delta_offset = cmp:: min ( shadow_ctlv_expiry_delta_offset, max_path_offset) ;
2200
2202
2201
2203
// Add 'shadow' CLTV offset to the final hop
2202
- if let Some ( last_hop) = path. hops . last_mut ( ) {
2204
+ if let Some ( tail) = path. blinded_tail . as_mut ( ) {
2205
+ tail. cltv_expiry_delta = tail. cltv_expiry_delta
2206
+ . checked_add ( shadow_ctlv_expiry_delta_offset) . unwrap_or ( tail. cltv_expiry_delta ) ;
2207
+ } else if let Some ( last_hop) = path. hops . last_mut ( ) {
2203
2208
last_hop. cltv_expiry_delta = last_hop. cltv_expiry_delta
2204
2209
. checked_add ( shadow_ctlv_expiry_delta_offset) . unwrap_or ( last_hop. cltv_expiry_delta ) ;
2205
2210
}
@@ -2287,7 +2292,7 @@ fn build_route_from_hops_internal<L: Deref>(
2287
2292
2288
2293
#[ cfg( test) ]
2289
2294
mod tests {
2290
- use crate :: blinded_path:: BlindedPath ;
2295
+ use crate :: blinded_path:: { BlindedHop , BlindedPath } ;
2291
2296
use crate :: routing:: gossip:: { NetworkGraph , P2PGossipSync , NodeId , EffectiveCapacity } ;
2292
2297
use crate :: routing:: utxo:: UtxoResult ;
2293
2298
use crate :: routing:: router:: { get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
@@ -5818,6 +5823,45 @@ mod tests {
5818
5823
assert_eq ! ( * inflight_htlcs. 0 . get( & ( 42 , true ) ) . unwrap( ) , 301 ) ;
5819
5824
assert_eq ! ( * inflight_htlcs. 0 . get( & ( 43 , false ) ) . unwrap( ) , 201 ) ;
5820
5825
}
5826
+
5827
+ #[ test]
5828
+ fn blinded_path_cltv_shadow_offset ( ) {
5829
+ // Don't add a shadow offset to blinded paths with more than 1 hop.
5830
+ let mut route = Route { paths : vec ! [ Path {
5831
+ hops: vec![ RouteHop {
5832
+ pubkey: ln_test_utils:: pubkey( 42 ) ,
5833
+ node_features: NodeFeatures :: empty( ) ,
5834
+ short_channel_id: 42 ,
5835
+ channel_features: ChannelFeatures :: empty( ) ,
5836
+ fee_msat: 100 ,
5837
+ cltv_expiry_delta: 0 ,
5838
+ } ] ,
5839
+ blinded_tail: Some ( BlindedTail {
5840
+ path: BlindedPath {
5841
+ introduction_node_id: ln_test_utils:: pubkey( 43 ) ,
5842
+ blinding_point: ln_test_utils:: pubkey( 44 ) ,
5843
+ blinded_hops: vec![
5844
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 45 ) , encrypted_payload: Vec :: new( ) } ,
5845
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 46 ) , encrypted_payload: Vec :: new( ) }
5846
+ ] ,
5847
+ } ,
5848
+ intro_node_scid: 43 ,
5849
+ fee_msat: 1 ,
5850
+ cltv_expiry_delta: 0 ,
5851
+ final_value_msat: 200 ,
5852
+ } ) ,
5853
+ } ] , payment_params : None } ;
5854
+
5855
+ let payment_params = PaymentParameters :: from_node_id ( ln_test_utils:: pubkey ( 47 ) , 18 ) ;
5856
+ let ( _, network_graph, _, _, _) = build_line_graph ( ) ;
5857
+ add_random_cltv_offset ( & mut route, & payment_params, & network_graph. read_only ( ) , & [ 0 ; 32 ] ) ;
5858
+ assert_eq ! ( route. paths[ 0 ] . blinded_tail. as_ref( ) . unwrap( ) . cltv_expiry_delta, 0 ) ;
5859
+
5860
+ // Add a shadow offset if we're sending to a 1-hop blinded path.
5861
+ route. paths [ 0 ] . blinded_tail . as_mut ( ) . unwrap ( ) . path . blinded_hops . pop ( ) ;
5862
+ add_random_cltv_offset ( & mut route, & payment_params, & network_graph. read_only ( ) , & [ 0 ; 32 ] ) ;
5863
+ assert_eq ! ( route. paths[ 0 ] . blinded_tail. as_ref( ) . unwrap( ) . cltv_expiry_delta, 40 ) ;
5864
+ }
5821
5865
}
5822
5866
5823
5867
#[ cfg( all( test, not( feature = "no-std" ) ) ) ]
0 commit comments