Skip to content

Commit 5207fa4

Browse files
Respect route hint max_htlc in pathfinding
1 parent 88821cb commit 5207fa4

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

lightning/src/routing/gossip.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,11 @@ pub enum EffectiveCapacity {
10331033
/// A capacity sufficient to route any payment, typically used for private channels provided by
10341034
/// an invoice.
10351035
Infinite,
1036+
/// The maximum HTLC amount as provided by an invoice route hint.
1037+
HintMaxHTLC {
1038+
/// The maximum HTLC amount denominated in millisatoshi.
1039+
amount_msat: u64,
1040+
},
10361041
/// A capacity that is unknown possibly because either the chain state is unavailable to know
10371042
/// the total capacity or the `htlc_maximum_msat` was not advertised on the gossip network.
10381043
Unknown,
@@ -1049,6 +1054,7 @@ impl EffectiveCapacity {
10491054
EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
10501055
EffectiveCapacity::AdvertisedMaxHTLC { amount_msat } => *amount_msat,
10511056
EffectiveCapacity::Total { capacity_msat, .. } => *capacity_msat,
1057+
EffectiveCapacity::HintMaxHTLC { amount_msat } => *amount_msat,
10521058
EffectiveCapacity::Infinite => u64::max_value(),
10531059
EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
10541060
}

lightning/src/routing/router.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,10 @@ impl<'a> CandidateRouteHop<'a> {
951951
liquidity_msat: details.next_outbound_htlc_limit_msat,
952952
},
953953
CandidateRouteHop::PublicHop { info, .. } => info.effective_capacity(),
954-
CandidateRouteHop::PrivateHop { .. } => EffectiveCapacity::Infinite,
954+
CandidateRouteHop::PrivateHop { hint } => {
955+
hint.htlc_maximum_msat.map_or(EffectiveCapacity::Infinite,
956+
|max| EffectiveCapacity::HintMaxHTLC { amount_msat: max })
957+
},
955958
}
956959
}
957960
}
@@ -965,6 +968,7 @@ fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_po
965968
EffectiveCapacity::Unknown => EffectiveCapacity::Unknown.as_msat(),
966969
EffectiveCapacity::AdvertisedMaxHTLC { amount_msat } =>
967970
amount_msat.checked_shr(saturation_shift).unwrap_or(0),
971+
EffectiveCapacity::HintMaxHTLC { amount_msat } => amount_msat,
968972
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat } =>
969973
cmp::min(capacity_msat.checked_shr(saturation_shift).unwrap_or(0), htlc_maximum_msat),
970974
}
@@ -5906,6 +5910,57 @@ mod tests {
59065910
assert!(route.is_ok());
59075911
}
59085912

5913+
#[test]
5914+
fn respect_route_hint_max_htlc() {
5915+
// Make sure that any max_htlc provided in the route hints of the payment params is respected in
5916+
// the final route.
5917+
let (secp_ctx, network_graph, _, _, logger) = build_graph();
5918+
let netgraph = network_graph.read_only();
5919+
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
5920+
let scorer = ln_test_utils::TestScorer::new();
5921+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
5922+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
5923+
let config = UserConfig::default();
5924+
5925+
let max_htlc_msat = 50_000;
5926+
let route_hint_1 = RouteHint(vec![RouteHintHop {
5927+
src_node_id: nodes[2],
5928+
short_channel_id: 42,
5929+
fees: RoutingFees {
5930+
base_msat: 100,
5931+
proportional_millionths: 0,
5932+
},
5933+
cltv_expiry_delta: 10,
5934+
htlc_minimum_msat: None,
5935+
htlc_maximum_msat: Some(max_htlc_msat),
5936+
}]);
5937+
let dest_node_id = ln_test_utils::pubkey(42);
5938+
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
5939+
.with_route_hints(vec![route_hint_1.clone()]).unwrap()
5940+
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
5941+
5942+
// Make sure we'll error if our route hints don't have enough liquidity according to their
5943+
// max_htlc.
5944+
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id,
5945+
&payment_params, &netgraph, None, max_htlc_msat + 1, Arc::clone(&logger), &scorer, &(),
5946+
&random_seed_bytes)
5947+
{
5948+
assert_eq!(err, "Failed to find a sufficient route to the given destination");
5949+
} else { panic!(); }
5950+
5951+
// Make sure we'll split an MPP payment across route hints if their max_htlcs warrant it.
5952+
let mut route_hint_2 = route_hint_1.clone();
5953+
route_hint_2.0[0].short_channel_id = 43;
5954+
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
5955+
.with_route_hints(vec![route_hint_1, route_hint_2]).unwrap()
5956+
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
5957+
let route = get_route(&our_id, &payment_params, &netgraph, None, max_htlc_msat + 1,
5958+
Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
5959+
assert_eq!(route.paths.len(), 2);
5960+
assert!(route.paths[0].hops.last().unwrap().fee_msat <= max_htlc_msat);
5961+
assert!(route.paths[1].hops.last().unwrap().fee_msat <= max_htlc_msat);
5962+
}
5963+
59095964
#[test]
59105965
fn blinded_route_ser() {
59115966
let blinded_path_1 = BlindedPath {

lightning/src/routing/scoring.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,8 +1243,10 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
12431243

12441244
let mut anti_probing_penalty_msat = 0;
12451245
match usage.effective_capacity {
1246-
EffectiveCapacity::ExactLiquidity { liquidity_msat } => {
1247-
if usage.amount_msat > liquidity_msat {
1246+
EffectiveCapacity::ExactLiquidity { liquidity_msat: amount_msat } |
1247+
EffectiveCapacity::HintMaxHTLC { amount_msat } =>
1248+
{
1249+
if usage.amount_msat > amount_msat {
12481250
return u64::max_value();
12491251
} else {
12501252
return base_penalty_msat;

0 commit comments

Comments
 (0)