Skip to content
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

Add channel scoring to get_route #1124

Merged

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Oct 14, 2021

Failed payments may be retried, but calling get_route may return a Route with the same failing path. Add a routing::Score trait used to parameterize get_route, which it calls to determine how much a channel should be penalized in terms of msats willing to pay to avoid the channel.

Also, add a Scorer struct that implements routing::Score with a constant penalty. Subsequent changes will allow for more robust scoring by feeding back payment path success and failure to the scorer via event handling.

@codecov
Copy link

codecov bot commented Oct 14, 2021

Codecov Report

Merging #1124 (84c0a48) into main (da498d7) will increase coverage by 0.76%.
The diff coverage is 91.52%.

❗ Current head 84c0a48 differs from pull request most recent head e15a18a. Consider uploading reports for the commit e15a18a to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1124      +/-   ##
==========================================
+ Coverage   90.60%   91.36%   +0.76%     
==========================================
  Files          66       67       +1     
  Lines       34474    37698    +3224     
==========================================
+ Hits        31235    34443    +3208     
- Misses       3239     3255      +16     
Impacted Files Coverage Δ
lightning/src/routing/scorer.rs 57.14% <57.14%> (ø)
lightning/src/routing/router.rs 95.70% <91.78%> (-0.30%) ⬇️
lightning-invoice/src/utils.rs 84.26% <100.00%> (+0.17%) ⬆️
lightning/src/ln/channelmanager.rs 85.14% <100.00%> (+0.01%) ⬆️
lightning/src/ln/functional_test_utils.rs 97.32% <100.00%> (+2.24%) ⬆️
lightning/src/ln/functional_tests.rs 97.93% <100.00%> (+0.53%) ⬆️
lightning/src/ln/shutdown_tests.rs 95.87% <100.00%> (+<0.01%) ⬆️
lightning/src/chain/mod.rs 50.00% <0.00%> (-8.83%) ⬇️
lightning-background-processor/src/lib.rs 94.38% <0.00%> (+0.15%) ⬆️
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update da498d7...e15a18a. Read the comment docs.

@TheBlueMatt TheBlueMatt added this to the 0.0.102 milestone Oct 14, 2021
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks are probably about what I'd expect given the expansion of of in-cmp logic. Something to look into eventually, probably:

Latest git

test ln::channelmanager::bench::bench_sends               ... bench:   9,128,177 ns/iter (+/- 1,349,843)
test routing::network_graph::benches::read_network_graph  ... bench: 1,777,566,078 ns/iter (+/- 23,897,528)
test routing::network_graph::benches::write_network_graph ... bench: 159,355,206 ns/iter (+/- 5,292,557)
test routing::router::benches::generate_mpp_routes        ... bench:  74,852,606 ns/iter (+/- 55,662,508)
test routing::router::benches::generate_routes            ... bench:  72,321,963 ns/iter (+/- 56,925,293)
test bench::bench_sends ... bench: 211,670,987 ns/iter (+/- 188,695,358)

This PR:

test ln::channelmanager::bench::bench_sends               ... bench:   8,284,024 ns/iter (+/- 443,276)
test routing::network_graph::benches::read_network_graph  ... bench: 1,614,888,492 ns/iter (+/- 2,280,811)
test routing::network_graph::benches::write_network_graph ... bench: 143,246,612 ns/iter (+/- 458,059)
test routing::router::benches::generate_mpp_routes        ... bench:  96,742,965 ns/iter (+/- 52,848,627)
test routing::router::benches::generate_routes            ... bench:  96,963,520 ns/iter (+/- 62,975,254)
test bench::bench_sends ... bench: 190,069,734 ns/iter (+/- 168,900,474)

@TheBlueMatt
Copy link
Collaborator

Note for comparison bench for 0.0.101 was (there's obviously a ton of noise in these numbers from github CI).

test ln::channelmanager::bench::bench_sends               ... bench:   7,587,098 ns/iter (+/- 1,026,747)
test routing::network_graph::benches::read_network_graph  ... bench: 2,195,464,293 ns/iter (+/- 116,402,530)
test routing::network_graph::benches::write_network_graph ... bench: 132,630,621 ns/iter (+/- 11,344,574)
test routing::router::benches::generate_mpp_routes        ... bench:  82,229,661 ns/iter (+/- 48,137,908)
test routing::router::benches::generate_routes            ... bench:  83,419,028 ns/iter (+/- 62,531,148)
test bench::bench_sends ... bench: 175,605,757 ns/iter (+/- 148,418,861)

@jkczyz
Copy link
Contributor Author

jkczyz commented Oct 14, 2021

All good feedback! Thanks for divining into benchmarks. All comments addressed.

@TheBlueMatt
Copy link
Collaborator

Github CI now says (which is, uhmmmm, faster than latest git? yea, no, that's just cause github's CI machines are clearly very heterogeneous....last time I look at that output.........)

test ln::channelmanager::bench::bench_sends               ... bench:   8,363,865 ns/iter (+/- 1,362,103)
test routing::network_graph::benches::read_network_graph  ... bench: 1,626,468,025 ns/iter (+/- 90,985,613)
test routing::network_graph::benches::write_network_graph ... bench: 142,291,900 ns/iter (+/- 6,987,924)
test routing::router::benches::generate_mpp_routes        ... bench:  57,154,598 ns/iter (+/- 32,542,178)
test routing::router::benches::generate_routes            ... bench:  57,344,572 ns/iter (+/- 39,866,175)
test bench::bench_sends ... bench: 197,672,739 ns/iter (+/- 170,050,060)

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs tests but otherwise good.

@@ -163,12 +164,19 @@ struct RouteGraphNode {
/// The effective htlc_minimum_msat at this hop. If a later hop on the path had a higher HTLC
/// minimum, we use it, plus the fees required at each earlier hop to meet it.
path_htlc_minimum_msat: u64,
/// All penalties incurred *after* this hop on the way to the destination, as calculated using
/// channel scoring.
next_hops_penalty_msat: u64,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use of next_hops in these confused me somewhat - next_hops_fee_msat (both in a PathBuildingHop and in the add_node macro) refers to only fee after the hop but not including the fee charged at the hop, whereas here we're using it to include the penalty charged at a hop. Maybe path_penalty_msat or so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. I was a little confused by the naming, but now I see the pattern. Should be consistent now.

@@ -25,6 +25,7 @@ use ln::{chan_utils, onion_utils};
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
use routing::network_graph::{NetworkUpdate, RoutingFees};
use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route};
use routing::scorer::DefaultScorer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use either a TestScorer in tests or a DefaultScorer with a score of 0? I find it a bit...awkward? to change all of our routing parameters in all our tests, even if they still pass it kinda makes me nervous.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing. I removed TestScorer and renamed DefaultScorer to Scorer, using it with 0 in all tests excepted for the newly added one.

impl Default for DefaultScorer {
/// Creates a new scorer using 100 msat as the channel penalty.
fn default() -> Self {
DefaultScorer::new(100)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think default should be 500, just kinda gut-feel. Lots of "good" nodes charge 1 sat per payment, and we should probably be willing to prefer that over two-three "free" hops IMO. Maybe even 1k. We can tune this later, but 100 just seems very low.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to 500 msats.

Copy link
Contributor Author

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test but was getting a little confused with the fee_msat assertions. Let me know if it looks sane. I could add more tests if needed.

@@ -163,12 +164,19 @@ struct RouteGraphNode {
/// The effective htlc_minimum_msat at this hop. If a later hop on the path had a higher HTLC
/// minimum, we use it, plus the fees required at each earlier hop to meet it.
path_htlc_minimum_msat: u64,
/// All penalties incurred *after* this hop on the way to the destination, as calculated using
/// channel scoring.
next_hops_penalty_msat: u64,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. I was a little confused by the naming, but now I see the pattern. Should be consistent now.

@@ -25,6 +25,7 @@ use ln::{chan_utils, onion_utils};
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
use routing::network_graph::{NetworkUpdate, RoutingFees};
use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route};
use routing::scorer::DefaultScorer;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing. I removed TestScorer and renamed DefaultScorer to Scorer, using it with 0 in all tests excepted for the newly added one.

impl Default for DefaultScorer {
/// Creates a new scorer using 100 msat as the channel penalty.
fn default() -> Self {
DefaultScorer::new(100)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to 500 msats.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to get an initial version of this in!

//!
//! # Example
//!
//! TODO: Fill in example once interface has been finalized.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good to fill this in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Not much to it yet, but could expand if we add more configurations. Also, may want to have an example with InvoicePayer for event handling once that is in. Or at least reference those docs.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@TheBlueMatt
Copy link
Collaborator

Can you squash :).

Failed payments may be retried, but calling get_route may return a Route
with the same failing path. Add a routing::Score trait used to
parameterize get_route, which it calls to determine how much a channel
should be penalized in terms of msats willing to pay to avoid the
channel.

Also, add a Scorer struct that implements routing::Score with a constant
constant penalty. Subsequent changes will allow for more robust scoring
by feeding back payment path success and failure to the scorer via event
handling.
@jkczyz jkczyz force-pushed the 2021-10-default-channel-scoring branch from 84c0a48 to e15a18a Compare October 15, 2021 20:31
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diff since val's ack was just a squash:

$ git diff-tree -U1 84c0a486e e15a18a50 
$

@TheBlueMatt TheBlueMatt merged commit 2398f17 into lightningdevkit:main Oct 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants