@@ -19,9 +19,9 @@ use lightning::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelS
19
19
use lightning:: ln:: channelmanager;
20
20
use lightning:: ln:: msgs;
21
21
use lightning:: ln:: types:: ChannelId ;
22
- use lightning:: routing:: gossip:: { NetworkGraph , RoutingFees } ;
22
+ use lightning:: routing:: gossip:: { NetworkGraph , NodeId , RoutingFees } ;
23
23
use lightning:: routing:: router:: {
24
- find_route, PaymentParameters , RouteHint , RouteHintHop , RouteParameters ,
24
+ find_route, Payee , PaymentParameters , RouteHint , RouteHintHop , RouteParameters ,
25
25
} ;
26
26
use lightning:: routing:: scoring:: {
27
27
ProbabilisticScorer , ProbabilisticScoringDecayParameters , ProbabilisticScoringFeeParameters ,
@@ -296,7 +296,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
296
296
let final_value_msat = slice_to_be64( get_slice!( 8 ) ) ;
297
297
let final_cltv_expiry_delta = slice_to_be32( get_slice!( 4 ) ) ;
298
298
let route_params = $route_params( final_value_msat, final_cltv_expiry_delta, target) ;
299
- let _ = find_route(
299
+ let route = find_route(
300
300
& our_pubkey,
301
301
& route_params,
302
302
& net_graph,
@@ -309,6 +309,91 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
309
309
& ProbabilisticScoringFeeParameters :: default ( ) ,
310
310
& random_seed_bytes,
311
311
) ;
312
+ if let Ok ( route) = route {
313
+ // If we generated a route, check that it is valid
314
+ // TODO: Check CLTV deltas
315
+ assert_eq!( route. route_params. as_ref( ) , Some ( & route_params) ) ;
316
+ let graph = net_graph. read_only( ) ;
317
+ let mut blinded_path_payment_amts = new_hash_map( ) ;
318
+ let mut total_fee = 0 ;
319
+ let mut total_sent = 0 ;
320
+ for path in & route. paths {
321
+ total_fee += path. fee_msat( ) ;
322
+ total_sent += path. final_value_msat( ) ;
323
+ let unblinded_recipient = path. hops. last( ) . expect( "No hops" ) . pubkey;
324
+ let mut hops = path. hops. iter( ) . peekable( ) ;
325
+ let payee = & route_params. payment_params. payee;
326
+ ' path_check: while let Some ( hop) = hops. next( ) {
327
+ if let Some ( next) = hops. peek( ) . cloned( ) {
328
+ let amt_to_send: u64 = hops. clone( ) . map( |hop| hop. fee_msat) . sum( ) ;
329
+ if let Payee :: Clear { route_hints, .. } = payee {
330
+ // If we paid to an invoice with clear route hints, check
331
+ // whether we pulled from a route hint first, and if not fall
332
+ // back to searching through the public network graph.
333
+ for hint_path in route_hints. iter( ) {
334
+ let mut hint_hops = hint_path. 0 . iter( ) . peekable( ) ;
335
+ while let Some ( hint) = hint_hops. next( ) {
336
+ let next_hint_hop_key = hint_hops
337
+ . peek( )
338
+ . map( |hint_hop| hint_hop. src_node_id)
339
+ . unwrap_or( unblinded_recipient) ;
340
+
341
+ let matches_hint = hint. src_node_id == hop. pubkey
342
+ && hint. short_channel_id == next. short_channel_id
343
+ && next_hint_hop_key == next. pubkey;
344
+ let prop = hint. fees. proportional_millionths as u64 ;
345
+ let base = hint. fees. base_msat as u64 ;
346
+ let min_fee = amt_to_send * prop / 1_000_000 + base;
347
+ if matches_hint {
348
+ assert!( min_fee <= hop. fee_msat) ;
349
+ continue ' path_check;
350
+ }
351
+ }
352
+ }
353
+ }
354
+ let chan = graph. channel( hop. short_channel_id) . expect( "No chan" ) ;
355
+ assert!( chan. one_to_two. is_some( ) && chan. two_to_one. is_some( ) ) ;
356
+ let fees = if chan. node_one == NodeId :: from_pubkey( & hop. pubkey) {
357
+ chan. one_to_two. as_ref( ) . unwrap( ) . fees
358
+ } else {
359
+ chan. two_to_one. as_ref( ) . unwrap( ) . fees
360
+ } ;
361
+ let prop_fee = fees. proportional_millionths as u64 ;
362
+ let base_fee = fees. base_msat as u64 ;
363
+ let min_fee = amt_to_send * prop_fee / 1_000_000 + base_fee;
364
+ assert!( min_fee <= hop. fee_msat) ;
365
+ } else {
366
+ if let Payee :: Blinded { route_hints, .. } = payee {
367
+ let tail = path. blinded_tail. as_ref( ) . expect( "No blinded path" ) ;
368
+ if tail. hops. len( ) == 1 {
369
+ // We don't consider the payinfo for one-hop blinded paths
370
+ // since they're not "real" blinded paths.
371
+ continue ;
372
+ }
373
+ // TODO: We should add some kind of coverage of trampoline hops
374
+ assert!( tail. trampoline_hops. is_empty( ) ) ;
375
+ let hint_filter = |hint: &&BlindedPaymentPath | {
376
+ // We store a unique counter in each encrypted_payload.
377
+ let hint_id = & hint. blinded_hops( ) [ 0 ] . encrypted_payload;
378
+ * hint_id == tail. hops[ 0 ] . encrypted_payload
379
+ } ;
380
+ let mut matching_hints = route_hints. iter( ) . filter( hint_filter) ;
381
+ let used_hint = matching_hints. next( ) . unwrap( ) ;
382
+ assert!( matching_hints. next( ) . is_none( ) ) ;
383
+ let key = & tail. hops[ 0 ] . encrypted_payload;
384
+ let used = blinded_path_payment_amts. entry( key) . or_insert( 0u64 ) ;
385
+ let blind_intro_amt = tail. final_value_msat + hop. fee_msat;
386
+ * used += blind_intro_amt;
387
+ assert!( * used <= used_hint. payinfo. htlc_maximum_msat) ;
388
+ assert!( blind_intro_amt >= used_hint. payinfo. htlc_minimum_msat) ;
389
+ }
390
+ break ;
391
+ }
392
+ }
393
+ }
394
+ assert!( total_sent >= final_value_msat) ;
395
+ assert!( total_fee <= route_params. max_total_routing_fee_msat. unwrap( ) ) ;
396
+ }
312
397
}
313
398
} ;
314
399
}
@@ -383,7 +468,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
383
468
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
384
469
let last_hops: Vec < BlindedPaymentPath > = last_hops_unblinded
385
470
. into_iter ( )
386
- . map ( |hint| {
471
+ . enumerate ( )
472
+ . map ( |( hint_idx, hint) | {
387
473
let hop = & hint. 0 [ 0 ] ;
388
474
let payinfo = BlindedPayInfo {
389
475
fee_base_msat : hop. fees . base_msat ,
@@ -398,7 +484,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
398
484
for _ in 0 ..num_blinded_hops {
399
485
blinded_hops. push ( BlindedHop {
400
486
blinded_node_id : dummy_pk,
401
- encrypted_payload : Vec :: new ( ) ,
487
+ encrypted_payload : hint_idx . to_ne_bytes ( ) . to_vec ( ) ,
402
488
} ) ;
403
489
}
404
490
BlindedPaymentPath :: from_raw (
0 commit comments