Skip to content

Commit fff2e07

Browse files
committed
Randomize candidate paths during route selection.
1 parent c244c78 commit fff2e07

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

lightning/src/routing/router.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ where L::Target: Logger {
645645
pub(crate) fn get_route<L: Deref, S: Score>(
646646
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
647647
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
648-
logger: L, scorer: &S, _random_seed_bytes: &[u8; 32]
648+
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]
649649
) -> Result<Route, LightningError>
650650
where L::Target: Logger {
651651
let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey);
@@ -833,7 +833,7 @@ where L::Target: Logger {
833833
.entry(short_channel_id)
834834
.or_insert_with(|| $candidate.effective_capacity().as_msat());
835835

836-
// It is tricky to substract $next_hops_fee_msat from available liquidity here.
836+
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
837837
// It may be misleading because we might later choose to reduce the value transferred
838838
// over these channels, and the channel which was insufficient might become sufficient.
839839
// Worst case: we drop a good channel here because it can't cover the high following
@@ -1449,14 +1449,31 @@ where L::Target: Logger {
14491449

14501450
// Draw multiple sufficient routes by randomly combining the selected paths.
14511451
let mut drawn_routes = Vec::new();
1452-
for i in 0..payment_paths.len() {
1452+
let mut prng = ChaCha20::new(random_seed_bytes, &[0u8; 12]);
1453+
let mut random_index_bytes = [0u8; ::core::mem::size_of::<usize>()];
1454+
1455+
let num_permutations = payment_paths.len();
1456+
let mut seen_permutation_scids: HashSet<Vec<Vec<u64>>> = HashSet::new();
1457+
for _ in 0..num_permutations {
14531458
let mut cur_route = Vec::<PaymentPath>::new();
14541459
let mut aggregate_route_value_msat = 0;
14551460

14561461
// 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();
1462+
// Do a Fisher-Yates shuffle to create a unique permutation of the payment paths
1463+
let cur_payment_paths = loop {
1464+
let mut cur_perm = payment_paths.clone();
1465+
for cur_index in (1..cur_perm.len()).rev() {
1466+
prng.process_in_place(&mut random_index_bytes);
1467+
let random_index = usize::from_be_bytes(random_index_bytes).wrapping_rem(cur_index+1);
1468+
cur_perm.swap(cur_index, random_index);
1469+
}
1470+
let cur_perm_scids = cur_perm.iter().map(|path|
1471+
path.hops.iter().map(|(hop,_)| hop.candidate.short_channel_id()).collect::<Vec<u64>>()
1472+
).collect::<Vec<Vec<u64>>>();
1473+
if seen_permutation_scids.insert(cur_perm_scids) {
1474+
break cur_perm;
1475+
}
1476+
};
14601477

14611478
// Step (7).
14621479
for payment_path in cur_payment_paths {
@@ -1469,12 +1486,17 @@ where L::Target: Logger {
14691486
// also makes routing more reliable.
14701487
let mut overpaid_value_msat = aggregate_route_value_msat - final_value_msat;
14711488

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
1489+
// First, we drop some expensive low-value paths entirely if possible.
1490+
// In order to do so, we pre-sort by total fees paid, so that in case of equal
1491+
// values we prefer lower cost paths.
1492+
// (Descending order, so we drop higher-fee paths first)
1493+
cur_route.sort_by_key(|path| path.get_total_fee_paid_msat());
1494+
cur_route.reverse();
1495+
1496+
// Then sort by value so that we drop many really-low values first, since
14741497
// 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.
14771498
cur_route.sort_by_key(|path| path.get_value_msat());
1499+
14781500
// We should make sure that at least 1 path left.
14791501
let mut paths_left = cur_route.len();
14801502
cur_route.retain(|path| {
@@ -1498,13 +1520,14 @@ where L::Target: Logger {
14981520
assert!(cur_route.len() > 0);
14991521

15001522
// Step (8).
1501-
// Now, substract the overpaid value from the most-expensive path.
1523+
// Now, subtract the overpaid value from the most-expensive path.
15021524
// TODO: this could also be optimized by also sorting by feerate_per_sat_routed,
15031525
// so that the sender pays less fees overall. And also htlc_minimum_msat.
15041526
cur_route.sort_by_key(|path| { path.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::<u64>() });
15051527
let expensive_payment_path = cur_route.first_mut().unwrap();
1506-
// We already dropped all the small channels above, meaning all the
1507-
// remaining channels are larger than remaining overpaid_value_msat.
1528+
1529+
// We already dropped all the small value paths above, meaning all the
1530+
// remaining paths are larger than remaining overpaid_value_msat.
15081531
// Thus, this can't be negative.
15091532
let expensive_path_new_value_msat = expensive_payment_path.get_value_msat() - overpaid_value_msat;
15101533
expensive_payment_path.update_value_and_recompute_fees(expensive_path_new_value_msat);

0 commit comments

Comments
 (0)