@@ -495,6 +495,13 @@ impl<'a> PaymentPath<'a> {
495495 self . hops . last ( ) . unwrap ( ) . 0 . fee_msat
496496 }
497497
498+ fn get_path_penalty_msat ( & self ) -> u64 {
499+ if self . hops . len ( ) < 1 {
500+ return u64:: max_value ( ) ;
501+ }
502+ self . hops . first ( ) . unwrap ( ) . 0 . path_penalty_msat
503+ }
504+
498505 fn get_total_fee_paid_msat ( & self ) -> u64 {
499506 if self . hops . len ( ) < 1 {
500507 return 0 ;
@@ -645,7 +652,7 @@ where L::Target: Logger {
645652pub ( crate ) fn get_route < L : Deref , S : Score > (
646653 our_node_pubkey : & PublicKey , payment_params : & PaymentParameters , network_graph : & ReadOnlyNetworkGraph ,
647654 first_hops : Option < & [ & ChannelDetails ] > , final_value_msat : u64 , final_cltv_expiry_delta : u32 ,
648- logger : L , scorer : & S , _random_seed_bytes : & [ u8 ; 32 ]
655+ logger : L , scorer : & S , random_seed_bytes : & [ u8 ; 32 ]
649656) -> Result < Route , LightningError >
650657where L :: Target : Logger {
651658 let payee_node_id = NodeId :: from_pubkey ( & payment_params. payee_pubkey ) ;
@@ -1449,17 +1456,24 @@ where L::Target: Logger {
14491456
14501457 // Draw multiple sufficient routes by randomly combining the selected paths.
14511458 let mut drawn_routes = Vec :: new ( ) ;
1452- for i in 0 ..payment_paths. len ( ) {
1459+ let mut prng = ChaCha20 :: new ( random_seed_bytes, & [ 0u8 ; 12 ] ) ;
1460+ let mut random_index_bytes = [ 0u8 ; :: core:: mem:: size_of :: < usize > ( ) ] ;
1461+
1462+ let num_permutations = payment_paths. len ( ) ;
1463+ for _ in 0 ..num_permutations {
14531464 let mut cur_route = Vec :: < PaymentPath > :: new ( ) ;
14541465 let mut aggregate_route_value_msat = 0 ;
14551466
14561467 // Step (6).
1457- // TODO: real random shuffle
1458- // Currently just starts with i_th and goes up to i-1_th in a looped way.
1459- let cur_payment_paths = [ & payment_paths[ i..] , & payment_paths[ ..i] ] . concat ( ) ;
1468+ // Do a Fisher-Yates shuffle to create a random permutation of the payment paths
1469+ for cur_index in ( 1 ..payment_paths. len ( ) ) . rev ( ) {
1470+ prng. process_in_place ( & mut random_index_bytes) ;
1471+ let random_index = usize:: from_be_bytes ( random_index_bytes) . wrapping_rem ( cur_index+1 ) ;
1472+ payment_paths. swap ( cur_index, random_index) ;
1473+ }
14601474
14611475 // Step (7).
1462- for payment_path in cur_payment_paths {
1476+ for payment_path in & payment_paths {
14631477 cur_route. push ( payment_path. clone ( ) ) ;
14641478 aggregate_route_value_msat += payment_path. get_value_msat ( ) ;
14651479 if aggregate_route_value_msat > final_value_msat {
@@ -1469,12 +1483,17 @@ where L::Target: Logger {
14691483 // also makes routing more reliable.
14701484 let mut overpaid_value_msat = aggregate_route_value_msat - final_value_msat;
14711485
1472- // First, drop some expensive low-value paths entirely if possible.
1473- // Sort by value so that we drop many really-low values first, since
1474- // fewer paths is better: the payment is less likely to fail.
1475- // TODO: this could also be optimized by also sorting by feerate_per_sat_routed,
1476- // so that the sender pays less fees overall. And also htlc_minimum_msat.
1477- cur_route. sort_by_key ( |path| path. get_value_msat ( ) ) ;
1486+ // First, we drop some expensive low-value paths entirely if possible, since fewer
1487+ // paths is better: the payment is less likely to fail. In order to do so, we sort
1488+ // by value and fall back to total fees paid, i.e., in case of equal values values
1489+ // we prefer lower cost paths.
1490+ cur_route. sort_unstable_by ( |a, b| {
1491+ a. get_value_msat ( ) . cmp ( & b. get_value_msat ( ) )
1492+ // Reverse ordering for fees, so we drop higher-fee paths first
1493+ . then_with ( || b. get_total_fee_paid_msat ( ) . saturating_add ( b. get_path_penalty_msat ( ) )
1494+ . cmp ( & a. get_total_fee_paid_msat ( ) . saturating_add ( a. get_path_penalty_msat ( ) ) ) )
1495+ } ) ;
1496+
14781497 // We should make sure that at least 1 path left.
14791498 let mut paths_left = cur_route. len ( ) ;
14801499 cur_route. retain ( |path| {
0 commit comments