Skip to content

Commit d501a20

Browse files
committed
Randomize candidate paths during route selection.
1 parent 5ed2985 commit d501a20

File tree

1 file changed

+31
-12
lines changed

1 file changed

+31
-12
lines changed

lightning/src/routing/router.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -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 {
645652
pub(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>
650657
where 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

Comments
 (0)