@@ -357,23 +357,25 @@ impl Writeable for Route {
357
357
fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
358
358
write_ver_prefix ! ( writer, SERIALIZATION_VERSION , MIN_SERIALIZATION_VERSION ) ;
359
359
( self . paths . len ( ) as u64 ) . write ( writer) ?;
360
- let mut blinded_tails = None ;
360
+ let mut blinded_tails = Vec :: new ( ) ;
361
361
for path in self . paths . iter ( ) {
362
362
( path. hops . len ( ) as u8 ) . write ( writer) ?;
363
- for hop in path. hops . iter ( ) {
363
+ for ( idx , hop) in path. hops . iter ( ) . enumerate ( ) {
364
364
hop. write ( writer) ?;
365
365
if let Some ( blinded_tail) = & path. blinded_tail {
366
- if blinded_tails. is_none ( ) { blinded_tails = Some ( Vec :: new ( ) ) ; }
367
- blinded_tails. as_mut ( ) . map ( |tails| tails. push ( blinded_tail) ) ;
368
- }
366
+ if blinded_tails. is_empty ( ) {
367
+ blinded_tails = Vec :: with_capacity ( path. hops . len ( ) ) ;
368
+ for _ in 0 ..idx {
369
+ blinded_tails. push ( None ) ;
370
+ }
371
+ }
372
+ blinded_tails. push ( Some ( blinded_tail) ) ;
373
+ } else if !blinded_tails. is_empty ( ) { blinded_tails. push ( None ) ; }
369
374
}
370
375
}
371
- if blinded_tails. is_some ( ) && blinded_tails. as_ref ( ) . unwrap ( ) . len ( ) != self . paths . len ( ) {
372
- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "Missing blinded tail for path (if blinded tails are included, there must be 1 set per path)" ) )
373
- }
374
376
write_tlv_fields ! ( writer, {
375
377
( 1 , self . payment_params, option) ,
376
- ( 2 , blinded_tails, option ) ,
378
+ ( 2 , blinded_tails, optional_vec ) ,
377
379
} ) ;
378
380
Ok ( ( ) )
379
381
}
@@ -399,13 +401,13 @@ impl Readable for Route {
399
401
}
400
402
_init_and_read_tlv_fields ! ( reader, {
401
403
( 1 , payment_params, ( option: ReadableArgs , min_final_cltv_expiry_delta) ) ,
402
- ( 2 , blinded_tails, vec_type ) ,
404
+ ( 2 , blinded_tails, optional_vec ) ,
403
405
} ) ;
404
406
let blinded_tails = blinded_tails. unwrap_or ( Vec :: new ( ) ) ;
405
407
if blinded_tails. len ( ) != 0 {
406
408
if blinded_tails. len ( ) != paths. len ( ) { return Err ( DecodeError :: InvalidValue ) }
407
- for ( mut path, blinded_tail ) in paths. iter_mut ( ) . zip ( blinded_tails. into_iter ( ) ) {
408
- path. blinded_tail = Some ( blinded_tail) ;
409
+ for ( mut path, blinded_tail_opt ) in paths. iter_mut ( ) . zip ( blinded_tails. into_iter ( ) ) {
410
+ if let Some ( blinded_tail ) = blinded_tail_opt { path. blinded_tail = Some ( blinded_tail) ; }
409
411
}
410
412
}
411
413
Ok ( Route { paths, payment_params } )
@@ -2260,10 +2262,11 @@ fn build_route_from_hops_internal<L: Deref>(
2260
2262
2261
2263
#[ cfg( test) ]
2262
2264
mod tests {
2265
+ use crate :: blinded_path:: { BlindedHop , BlindedPath } ;
2263
2266
use crate :: routing:: gossip:: { NetworkGraph , P2PGossipSync , NodeId , EffectiveCapacity } ;
2264
2267
use crate :: routing:: utxo:: UtxoResult ;
2265
2268
use crate :: routing:: router:: { get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
2266
- Path , PaymentParameters , Route , RouteHint , RouteHintHop , RouteHop , RoutingFees ,
2269
+ BlindedTail , Path , PaymentParameters , Route , RouteHint , RouteHintHop , RouteHop , RoutingFees ,
2267
2270
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA , MAX_PATH_LENGTH_ESTIMATE } ;
2268
2271
use crate :: routing:: scoring:: { ChannelUsage , FixedPenaltyScorer , Score , ProbabilisticScorer , ProbabilisticScoringParameters } ;
2269
2272
use crate :: routing:: test_utils:: { add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel} ;
@@ -2275,8 +2278,9 @@ mod tests {
2275
2278
use crate :: util:: config:: UserConfig ;
2276
2279
use crate :: util:: test_utils as ln_test_utils;
2277
2280
use crate :: util:: chacha20:: ChaCha20 ;
2281
+ use crate :: util:: ser:: { Readable , Writeable } ;
2278
2282
#[ cfg( c_bindings) ]
2279
- use crate :: util:: ser:: { Writeable , Writer } ;
2283
+ use crate :: util:: ser:: Writer ;
2280
2284
2281
2285
use bitcoin:: hashes:: Hash ;
2282
2286
use bitcoin:: network:: constants:: Network ;
@@ -2290,6 +2294,7 @@ mod tests {
2290
2294
use bitcoin:: secp256k1:: { PublicKey , SecretKey } ;
2291
2295
use bitcoin:: secp256k1:: Secp256k1 ;
2292
2296
2297
+ use crate :: io:: Cursor ;
2293
2298
use crate :: prelude:: * ;
2294
2299
use crate :: sync:: Arc ;
2295
2300
@@ -5760,6 +5765,68 @@ mod tests {
5760
5765
let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None , 100 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) ;
5761
5766
assert ! ( route. is_ok( ) ) ;
5762
5767
}
5768
+
5769
+ #[ test]
5770
+ fn blinded_route_ser ( ) {
5771
+ let blinded_path_1 = BlindedPath {
5772
+ introduction_node_id : ln_test_utils:: pubkey ( 42 ) ,
5773
+ blinding_point : ln_test_utils:: pubkey ( 43 ) ,
5774
+ blinded_hops : vec ! [
5775
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 44 ) , encrypted_payload: Vec :: new( ) } ,
5776
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 45 ) , encrypted_payload: Vec :: new( ) }
5777
+ ] ,
5778
+ } ;
5779
+ let blinded_path_2 = BlindedPath {
5780
+ introduction_node_id : ln_test_utils:: pubkey ( 46 ) ,
5781
+ blinding_point : ln_test_utils:: pubkey ( 47 ) ,
5782
+ blinded_hops : vec ! [
5783
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 48 ) , encrypted_payload: Vec :: new( ) } ,
5784
+ BlindedHop { blinded_node_id: ln_test_utils:: pubkey( 49 ) , encrypted_payload: Vec :: new( ) }
5785
+ ] ,
5786
+ } ;
5787
+ // (De)serialize a Route with 1 blinded path out of two total paths.
5788
+ let mut route = Route { paths : vec ! [ Path {
5789
+ hops: vec![ RouteHop {
5790
+ pubkey: ln_test_utils:: pubkey( 50 ) ,
5791
+ node_features: NodeFeatures :: empty( ) ,
5792
+ short_channel_id: 42 ,
5793
+ channel_features: ChannelFeatures :: empty( ) ,
5794
+ fee_msat: 100 ,
5795
+ cltv_expiry_delta: 0 ,
5796
+ } ] ,
5797
+ blinded_tail: Some ( BlindedTail {
5798
+ hops: blinded_path_1. blinded_hops,
5799
+ blinding_point: blinded_path_1. blinding_point,
5800
+ final_cltv_expiry_delta: 40 ,
5801
+ final_value_msat: 100 ,
5802
+ } ) } , Path {
5803
+ hops: vec![ RouteHop {
5804
+ pubkey: ln_test_utils:: pubkey( 51 ) ,
5805
+ node_features: NodeFeatures :: empty( ) ,
5806
+ short_channel_id: 43 ,
5807
+ channel_features: ChannelFeatures :: empty( ) ,
5808
+ fee_msat: 100 ,
5809
+ cltv_expiry_delta: 0 ,
5810
+ } ] , blinded_tail: None } ] ,
5811
+ payment_params : None ,
5812
+ } ;
5813
+ let encoded_route = route. encode ( ) ;
5814
+ let decoded_route: Route = Readable :: read ( & mut Cursor :: new ( & encoded_route[ ..] ) ) . unwrap ( ) ;
5815
+ assert_eq ! ( decoded_route. paths[ 0 ] . blinded_tail, route. paths[ 0 ] . blinded_tail) ;
5816
+ assert_eq ! ( decoded_route. paths[ 1 ] . blinded_tail, route. paths[ 1 ] . blinded_tail) ;
5817
+
5818
+ // (De)serialize a Route with two paths, each containing a blinded tail.
5819
+ route. paths [ 1 ] . blinded_tail = Some ( BlindedTail {
5820
+ hops : blinded_path_2. blinded_hops ,
5821
+ blinding_point : blinded_path_2. blinding_point ,
5822
+ final_cltv_expiry_delta : 41 ,
5823
+ final_value_msat : 101 ,
5824
+ } ) ;
5825
+ let encoded_route = route. encode ( ) ;
5826
+ let decoded_route: Route = Readable :: read ( & mut Cursor :: new ( & encoded_route[ ..] ) ) . unwrap ( ) ;
5827
+ assert_eq ! ( decoded_route. paths[ 0 ] . blinded_tail, route. paths[ 0 ] . blinded_tail) ;
5828
+ assert_eq ! ( decoded_route. paths[ 1 ] . blinded_tail, route. paths[ 1 ] . blinded_tail) ;
5829
+ }
5763
5830
}
5764
5831
5765
5832
#[ cfg( all( test, not( feature = "no-std" ) ) ) ]
0 commit comments