Skip to content

Commit db41b87

Browse files
authored
Merge pull request #2604 from TheBlueMatt/2023-09-route-overpay-limit
Try to overpay the recipient if we fail to find a path at all and limit overpay
2 parents 082a19b + fa48df6 commit db41b87

File tree

1 file changed

+82
-14
lines changed

1 file changed

+82
-14
lines changed

lightning/src/routing/router.rs

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,9 +1622,13 @@ where L::Target: Logger {
16221622
|info| info.features.supports_basic_mpp()))
16231623
} else { false };
16241624

1625-
log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
1626-
LoggedPayeePubkey(payment_params.payee.node_id()), if allow_mpp { "with" } else { "without" },
1627-
first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " });
1625+
let max_total_routing_fee_msat = route_params.max_total_routing_fee_msat.unwrap_or(u64::max_value());
1626+
1627+
log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph with a fee limit of {} msat",
1628+
our_node_pubkey, LoggedPayeePubkey(payment_params.payee.node_id()),
1629+
if allow_mpp { "with" } else { "without" },
1630+
first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " },
1631+
max_total_routing_fee_msat);
16281632

16291633
// Step (1).
16301634
// Prepare the data we'll use for payee-to-payer search by
@@ -1788,9 +1792,9 @@ where L::Target: Logger {
17881792
#[allow(unused_comparisons)] // $next_hops_path_htlc_minimum_msat is 0 in some calls so rustc complains
17891793
let may_overpay_to_meet_path_minimum_msat =
17901794
((amount_to_transfer_over_msat < $candidate.htlc_minimum_msat() &&
1791-
recommended_value_msat > $candidate.htlc_minimum_msat()) ||
1795+
recommended_value_msat >= $candidate.htlc_minimum_msat()) ||
17921796
(amount_to_transfer_over_msat < $next_hops_path_htlc_minimum_msat &&
1793-
recommended_value_msat > $next_hops_path_htlc_minimum_msat));
1797+
recommended_value_msat >= $next_hops_path_htlc_minimum_msat));
17941798

17951799
let payment_failed_on_this_channel = scid_opt.map_or(false,
17961800
|scid| payment_params.previously_failed_channels.contains(&scid));
@@ -1890,7 +1894,6 @@ where L::Target: Logger {
18901894
}
18911895

18921896
// Ignore hops if augmenting the current path to them would put us over `max_total_routing_fee_msat`
1893-
let max_total_routing_fee_msat = route_params.max_total_routing_fee_msat.unwrap_or(u64::max_value());
18941897
if total_fee_msat > max_total_routing_fee_msat {
18951898
if should_log_candidate {
18961899
log_trace!(logger, "Ignoring {} due to exceeding max total routing fee limit.", LoggedCandidateHop(&$candidate));
@@ -2454,6 +2457,9 @@ where L::Target: Logger {
24542457
// because we deterministically terminated the search due to low liquidity.
24552458
if !found_new_path && channel_saturation_pow_half != 0 {
24562459
channel_saturation_pow_half = 0;
2460+
} else if !found_new_path && hit_minimum_limit && already_collected_value_msat < final_value_msat && path_value_msat != recommended_value_msat {
2461+
log_trace!(logger, "Failed to collect enough value, but running again to collect extra paths with a potentially higher limit.");
2462+
path_value_msat = recommended_value_msat;
24572463
} else if already_collected_value_msat >= recommended_value_msat || !found_new_path {
24582464
log_trace!(logger, "Have now collected {} msat (seeking {} msat) in paths. Last path loop {} a new path.",
24592465
already_collected_value_msat, recommended_value_msat, if found_new_path { "found" } else { "did not find" });
@@ -2625,21 +2631,22 @@ where L::Target: Logger {
26252631
// Make sure we would never create a route with more paths than we allow.
26262632
debug_assert!(paths.len() <= payment_params.max_path_count.into());
26272633

2628-
// Make sure we would never create a route whose total fees exceed max_total_routing_fee_msat.
2629-
if let Some(max_total_routing_fee_msat) = route_params.max_total_routing_fee_msat {
2630-
if paths.iter().map(|p| p.fee_msat()).sum::<u64>() > max_total_routing_fee_msat {
2631-
return Err(LightningError{err: format!("Failed to find route that adheres to the maximum total fee limit of {}msat",
2632-
max_total_routing_fee_msat), action: ErrorAction::IgnoreError});
2633-
}
2634-
}
2635-
26362634
if let Some(node_features) = payment_params.payee.node_features() {
26372635
for path in paths.iter_mut() {
26382636
path.hops.last_mut().unwrap().node_features = node_features.clone();
26392637
}
26402638
}
26412639

26422640
let route = Route { paths, route_params: Some(route_params.clone()) };
2641+
2642+
// Make sure we would never create a route whose total fees exceed max_total_routing_fee_msat.
2643+
if let Some(max_total_routing_fee_msat) = route_params.max_total_routing_fee_msat {
2644+
if route.get_total_fees() > max_total_routing_fee_msat {
2645+
return Err(LightningError{err: format!("Failed to find route that adheres to the maximum total fee limit of {}msat",
2646+
max_total_routing_fee_msat), action: ErrorAction::IgnoreError});
2647+
}
2648+
}
2649+
26432650
log_info!(logger, "Got route: {}", log_route!(route));
26442651
Ok(route)
26452652
}
@@ -3223,6 +3230,67 @@ mod tests {
32233230
assert_eq!(fees, 5_000);
32243231
}
32253232

3233+
#[test]
3234+
fn htlc_minimum_recipient_overpay_test() {
3235+
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
3236+
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
3237+
let config = UserConfig::default();
3238+
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
3239+
let scorer = ln_test_utils::TestScorer::new();
3240+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
3241+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
3242+
3243+
// Route to node2 over a single path which requires overpaying the recipient themselves.
3244+
3245+
// First disable all paths except the us -> node1 -> node2 path
3246+
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
3247+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3248+
short_channel_id: 13,
3249+
timestamp: 2,
3250+
flags: 3,
3251+
cltv_expiry_delta: 0,
3252+
htlc_minimum_msat: 0,
3253+
htlc_maximum_msat: 0,
3254+
fee_base_msat: 0,
3255+
fee_proportional_millionths: 0,
3256+
excess_data: Vec::new()
3257+
});
3258+
3259+
// Set channel 4 to free but with a high htlc_minimum_msat
3260+
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
3261+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3262+
short_channel_id: 4,
3263+
timestamp: 2,
3264+
flags: 0,
3265+
cltv_expiry_delta: 0,
3266+
htlc_minimum_msat: 15_000,
3267+
htlc_maximum_msat: MAX_VALUE_MSAT,
3268+
fee_base_msat: 0,
3269+
fee_proportional_millionths: 0,
3270+
excess_data: Vec::new()
3271+
});
3272+
3273+
// Now check that we'll fail to find a path if we fail to find a path if the htlc_minimum
3274+
// is overrun. Note that the fees are actually calculated on 3*payment amount as that's
3275+
// what we try to find a route for, so this test only just happens to work out to exactly
3276+
// the fee limit.
3277+
let mut route_params = RouteParameters::from_payment_params_and_value(
3278+
payment_params.clone(), 5_000);
3279+
route_params.max_total_routing_fee_msat = Some(9_999);
3280+
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id,
3281+
&route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer,
3282+
&Default::default(), &random_seed_bytes) {
3283+
assert_eq!(err, "Failed to find route that adheres to the maximum total fee limit of 9999msat");
3284+
} else { panic!(); }
3285+
3286+
let mut route_params = RouteParameters::from_payment_params_and_value(
3287+
payment_params.clone(), 5_000);
3288+
route_params.max_total_routing_fee_msat = Some(10_000);
3289+
let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
3290+
Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap();
3291+
assert_eq!(route.get_total_fees(), 10_000);
3292+
}
3293+
32263294
#[test]
32273295
fn disable_channels_test() {
32283296
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();

0 commit comments

Comments
 (0)