-
Notifications
You must be signed in to change notification settings - Fork 370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Respect route hint max_htlc
in pathfinding
#2305
Respect route hint max_htlc
in pathfinding
#2305
Conversation
Also get rid of some trailing whitespace because my text editor likes to do that. We'll next add a new variant for max_htlc provided in route hints, which will be treated differently in scoring.
ded3373
to
5207fa4
Compare
Codecov ReportPatch coverage:
❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more. Additional details and impacted files@@ Coverage Diff @@
## main #2305 +/- ##
==========================================
- Coverage 90.87% 90.82% -0.06%
==========================================
Files 104 104
Lines 52789 55111 +2322
Branches 52789 55111 +2322
==========================================
+ Hits 47972 50052 +2080
- Misses 4817 5059 +242
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I found a bug where we'll fail to split a payment over two route hints if they have the same source node id and we have a direct channel to said source node id, hitting the |
Oh interesting. Probably good to include a test for that case. |
409402e
to
a773553
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -1033,6 +1033,11 @@ pub enum EffectiveCapacity { | |||
/// A capacity sufficient to route any payment, typically used for private channels provided by | |||
/// an invoice. | |||
Infinite, | |||
/// The maximum HTLC amount as provided by an invoice route hint. | |||
HintMaxHTLC { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is used for blinded paths as well, Hint
might not be the best name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, there's several places in the code already where we refer to (BlindedPayInfo, BlindedPath)
things from BOLT12 invoices as "blinded route hints" (and more in #2120).
That said, I'm not super happy with this name, so v open to alternatives
a773553
to
686f4e1
Compare
Sadly I found another similar bug. Fixed now. Hope it's for real this time. |
686f4e1
to
99a123b
Compare
Tacked on an additional commit because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran some benches (4 total, 2 local on notebook, 2 on server):
Notebook (Apple M1 Pro):
RUN #1:
BASE:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 128,331,316 ns/iter (+/- 34,214,124)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 105,527,599 ns/iter (+/- 51,893,095)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 122,811,941 ns/iter (+/- 28,075,038)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 102,338,695 ns/iter (+/- 83,908,199)
HEAD:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 128,418,933 ns/iter (+/- 21,778,847)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 98,440,187 ns/iter (+/- 63,266,719)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 124,550,208 ns/iter (+/- 21,458,416)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 99,894,774 ns/iter (+/- 72,989,638)
RUN #2:
BASE:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 123,992,545 ns/iter (+/- 24,040,908)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 100,857,441 ns/iter (+/- 65,807,407)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 118,328,937 ns/iter (+/- 13,768,676)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 102,168,212 ns/iter (+/- 48,080,466)
HEAD:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 119,668,829 ns/iter (+/- 32,563,378)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 101,001,891 ns/iter (+/- 51,208,428)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 120,288,587 ns/iter (+/- 34,034,627)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 98,606,216 ns/iter (+/- 59,330,778)
Server (Intel(R) Xeon(R) CPU E3-1226 v3 @ 3.30GHz):
RUN #1:
BASE:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 167,627,890 ns/iter (+/- 29,241,988)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 122,588,677 ns/iter (+/- 81,634,017)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 167,874,126 ns/iter (+/- 39,447,553)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 120,162,977 ns/iter (+/- 78,110,553)
HEAD:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 171,435,012 ns/iter (+/- 50,514,398)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 121,795,169 ns/iter (+/- 83,967,647)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 168,357,184 ns/iter (+/- 30,977,056)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 112,352,576 ns/iter (+/- 81,124,152)
RUN #2:
BASE:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 164,439,346 ns/iter (+/- 38,028,845)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 118,526,962 ns/iter (+/- 92,154,137)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 170,246,213 ns/iter (+/- 33,679,643)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 128,066,773 ns/iter (+/- 66,781,456)
HEAD:
test routing::router::benches::generate_mpp_routes_with_probabilistic_scorer ... bench: 173,568,800 ns/iter (+/- 31,740,701)
test routing::router::benches::generate_mpp_routes_with_zero_penalty_scorer ... bench: 115,892,727 ns/iter (+/- 48,088,270)
test routing::router::benches::generate_routes_with_probabilistic_scorer ... bench: 165,457,689 ns/iter (+/- 37,772,193)
test routing::router::benches::generate_routes_with_zero_penalty_scorer ... bench: 122,303,787 ns/iter (+/- 90,595,307)
I can't see any noticeable differences outside of standard deviation here.
99a123b
to
60987e0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, would be ready for the squash from my side.
60987e0
to
0d5ac88
Compare
Squashed with no diff. Thanks for the benching @tnull! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay.
lightning/src/routing/router.rs
Outdated
@@ -1863,14 +1886,17 @@ where L::Target: Logger { | |||
.saturating_add(1); | |||
|
|||
// Searching for a direct channel between last checked hop and first_hop_targets | |||
if let Some(first_channels) = first_hop_targets.get(&NodeId::from_pubkey(&prev_hop_id)) { | |||
let hint_candidate_contribution_msat = cmp::min(path_value_msat, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is also already done in add_entry
? It currently takes the $next_hops_value_contribution
argument (which is what this is passed as) and min's it with available_value_contribution_msat
, which is calculated as the effective_capacity
(via max_htlc_from_capacity
minus $next_hops_fee_msat
minus used_liquidity_msat
(if one exists).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need this because otherwise add_entry
assumes that the next hop (aka the route hint hop) can carry the full payment value, since available_value_contribution_msat
only factors in the current candidate's capacity, not the next hop candidate's. direct_channel_to_hints_with_max_htlc
fails without this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but I believe the current code doesn't actually propagate that backwards - if we have one hop that's limited we will "jump back" to the max value for an earlier hop that isn't limited (not that we can actually have such a path right now). It also doesn't consider any other limiting of the value, this patch seems to pass your test:
diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs
index da1ae9771..4d1d68242 100644
--- a/lightning/src/routing/router.rs
+++ b/lightning/src/routing/router.rs
@@ -1493,8 +1493,9 @@ where L::Target: Logger {
( $candidate: expr, $src_node_id: expr, $dest_node_id: expr, $next_hops_fee_msat: expr,
$next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr,
$next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr, $next_hops_path_length: expr ) => { {
- // We "return" whether we updated the path at the end, via this:
- let mut did_add_update_path_to_src_node = false;
+ // We "return" whether we updated the path at the end, and how much we can route via
+ // this channel via this:
+ let mut did_add_update_path_to_src_node = None;
// Channels to self should not be used. This is more of belt-and-suspenders, because in
// practice these cases should be caught earlier:
// - for regular channels at channel announcement (TODO)
@@ -1675,7 +1676,7 @@ where L::Target: Logger {
{
old_entry.value_contribution_msat = value_contribution_msat;
}
- did_add_update_path_to_src_node = true;
+ did_add_update_path_to_src_node = Some(value_contribution_msat);
} else if old_entry.was_processed && new_cost < old_cost {
#[cfg(all(not(ldk_bench), any(test, fuzzing)))]
{
@@ -1796,7 +1797,7 @@ where L::Target: Logger {
for details in first_channels {
let candidate = CandidateRouteHop::FirstHop { details };
let added = add_entry!(candidate, our_node_id, payee, 0, path_value_msat,
- 0, 0u64, 0, 0);
+ 0, 0u64, 0, 0).is_some();
log_trace!(logger, "{} direct route to payee via SCID {}",
if added { "Added" } else { "Skipped" }, candidate.short_channel_id());
}
@@ -1843,6 +1844,7 @@ where L::Target: Logger {
let mut aggregate_next_hops_path_penalty_msat: u64 = 0;
let mut aggregate_next_hops_cltv_delta: u32 = 0;
let mut aggregate_next_hops_path_length: u8 = 0;
+ let mut aggregate_path_contribution_msat = path_value_msat;
for (idx, (hop, prev_hop_id)) in hop_iter.zip(prev_hop_iter).enumerate() {
let source = NodeId::from_pubkey(&hop.src_node_id);
@@ -1856,10 +1858,13 @@ where L::Target: Logger {
})
.unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop });
- if !add_entry!(candidate, source, target, aggregate_next_hops_fee_msat,
- path_value_msat, aggregate_next_hops_path_htlc_minimum_msat,
+ if let Some(hop_used_msat) = add_entry!(candidate, source, target, aggregate_next_hops_fee_msat,
+ aggregate_path_contribution_msat,
+ aggregate_next_hops_path_htlc_minimum_msat,
aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta,
aggregate_next_hops_path_length) {
+ aggregate_path_contribution_msat = hop_used_msat;
+ } else {
// If this hop was not used then there is no use checking the preceding
// hops in the RouteHint. We can break by just searching for a direct
// channel between last checked hop and first_hop_targets.
@@ -1886,15 +1891,13 @@ where L::Target: Logger {
.saturating_add(1);
// Searching for a direct channel between last checked hop and first_hop_targets
- let hint_candidate_contribution_msat = cmp::min(path_value_msat,
- candidate.effective_capacity().as_msat().saturating_sub(used_liquidity_msat));
if let Some(first_channels) = first_hop_targets.get_mut(&NodeId::from_pubkey(&prev_hop_id)) {
sort_first_hop_channels(first_channels, &used_channel_liquidities,
recommended_value_msat, our_node_pubkey);
for details in first_channels {
let first_hop_candidate = CandidateRouteHop::FirstHop { details };
add_entry!(first_hop_candidate, our_node_id, NodeId::from_pubkey(&prev_hop_id),
- aggregate_next_hops_fee_msat, hint_candidate_contribution_msat,
+ aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length);
}
@@ -1937,7 +1940,7 @@ where L::Target: Logger {
add_entry!(first_hop_candidate, our_node_id,
NodeId::from_pubkey(&hop.src_node_id),
aggregate_next_hops_fee_msat,
- hint_candidate_contribution_msat,
+ aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat,
aggregate_next_hops_path_penalty_msat,
aggregate_next_hops_cltv_delta,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good point. Fixed and added test coverage.
0d5ac88
to
f130f95
Compare
We have an
htlc_maximum_msat
field inRouteHint
, but we've so far ignored it in pathfinding because BOLT 11 route hints don't provide this field. We'll need to start abiding by route hintmax_htlc
s when we add support for blinded pathfinding in #2120 because BOLT 12 route hints provide this field, so let's start incorporating it now.