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

MPP on the router side #646

Merged
merged 6 commits into from
Mar 3, 2021

Conversation

naumenkogs
Copy link
Contributor

@naumenkogs naumenkogs commented Jun 21, 2020

I've implemented MPP for path finding part with minimum complexity overhead over given code. Closes #563.

Seeking approach ACK for now.

@codecov
Copy link

codecov bot commented Jun 21, 2020

Codecov Report

Merging #646 (e0600e5) into master (74cd96f) will increase coverage by 1.98%.
The diff coverage is 93.75%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #646      +/-   ##
==========================================
+ Coverage   91.23%   93.21%   +1.98%     
==========================================
  Files          37       49      +12     
  Lines       22623    33943   +11320     
==========================================
+ Hits        20639    31639   +11000     
- Misses       1984     2304     +320     
Impacted Files Coverage Δ
lightning/src/routing/router.rs 97.61% <ø> (+1.71%) ⬆️
lightning/src/routing/network_graph.rs 92.21% <80.00%> (+0.01%) ⬆️
lightning/src/ln/channel.rs 92.10% <100.00%> (+4.64%) ⬆️
lightning/src/ln/functional_tests.rs 98.67% <100.00%> (+1.50%) ⬆️
lightning/src/util/errors.rs 71.42% <0.00%> (-9.97%) ⬇️
lightning/src/util/enforcing_trait_impls.rs 97.76% <0.00%> (-2.24%) ⬇️
lightning/src/util/macro_logger.rs 88.33% <0.00%> (-0.96%) ⬇️
lightning/src/ln/onion_route_tests.rs 96.85% <0.00%> (-0.69%) ⬇️
lightning/src/ln/features.rs 98.63% <0.00%> (-0.17%) ⬇️
... and 28 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 74cd96f...e0600e5. Read the comment docs.

@TheBlueMatt
Copy link
Collaborator

TheBlueMatt commented Jun 23, 2020 via email

@naumenkogs
Copy link
Contributor Author

My initial intention was to expose how many paths shall be found to the caller (see max_paths var in my impl). It seems you suggesting we decide for them?

Then, what do you think about this plan:

  • we should be comparing HTLC value to channel capacity even for the first path (so first commit will just modify the existing path finding)
  • then we see if any of the channel capacities is less than 0.25 * HTLC (or something), and based on that may choose to MPP
  • then if we chose to MPP, use the same value comparison heuristic to drop selected channels and A* again

@TheBlueMatt
Copy link
Collaborator

TheBlueMatt commented Jun 24, 2020

That sounds like a reasonable approach to me, there's also the htlc_maximum_msat field.

@naumenkogs
Copy link
Contributor Author

naumenkogs commented Aug 4, 2020

@TheBlueMatt I sketched an implementation, please take a look. It has a bunch of TODOs, and tests are not fixed against the changes.

@TheBlueMatt
Copy link
Collaborator

Hmm, right, if I'm reading this right it does seem to be the right approach, but there's a ton of complexity that isn't clear how to solve. eg you can't just say "using_low_capacity_channel" as a boolean, you need to track the value you're limited to, and then figure out how many paths you need to reach that value. Also, really annoyingly, I don't think its practical to do this in a perfectly-lowest-cost way, because once you calculate a path (but find out that its value-limited), you have to go back along that path and recalculate fees with the new value (and thus new proportional_msat fees at each hop). Thus, you may end up finding a path that is really low value that would have a low fee if it wasnt low value, but which actually has a high fee because its mostly static fees.

One approach, I think, would be to just find a number of paths and then at the end select the best paths until you have enough value - only add channels who's values are "too low" to channels_to_avoid as you go, then at the end of the path go back and calculate the real fee. Reapeat until you have a number of candidate paths and mix + match the lowest fees until you find enough total value. Once you have a candidate paths list, you should be able to calcualte, for each, a absolute and relative feerate total, and then you can shift the balance towards the relative paths.

@naumenkogs
Copy link
Contributor Author

@TheBlueMatt if we do MPP, how do we communicate the suggested path<->amount mapping? Unless I'm missing something I should modify Route struct?

@TheBlueMatt
Copy link
Collaborator

Correct - Route is now a list of "paths", and each RouteHop in each Path must have its value set to include the fees taken by that path, etc. So the algorithm needs to figure out how to split the value across different paths and then fill in the fees/value for each.

@naumenkogs
Copy link
Contributor Author

naumenkogs commented Aug 27, 2020

alright, another draft :)
@TheBlueMatt Do you mind looking at the approach here? There are a couple of little todos left, and then tests to fix.

So the idea is:

  • find a lot of paths. More specifically, find enough to aggregately cover the target amount 4 times (or if made too much attempts to find them). Be aware of the maximum capacity: at the end of finding a path, recompute amounts/fees to match the minimum hop and not overpay. Also, make sure different paths don't "share"/rely on capacities of the same channels — this is not necessarily a good idea???
  • try to randomly combine those paths into routes that cover the target amount
  • select the best route by total fee

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.

I think this is moving in the right direction, but sans-comments its pretty hard to read. Not that that's really your fault, it definitely could have been better commented when originally written, but it would be nice at least for the new parts.

@naumenkogs
Copy link
Contributor Author

Added a lot of comments

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

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

Just dumb questions for now. I'm still grasping the problem magnitude, I've to confess I wasn't a Routing Algorithms CS major :p

Maybe we should split up get_route in multiple logical chunks first ? We will succeed to merge this current PR but next time we touch about this code we or future reviewers may be lost again.

@naumenkogs
Copy link
Contributor Author

Maybe we should split up get_route in multiple logical chunks first ? We will succeed to merge this current PR but next time we touch about this code we or future reviewers may be lost again.

The two macros are logically heavy. We can make them separate functions, sure, but they will never be reused outside get_route, so a macro seems to be a perfect fit?

Other than that, I think the algorithm reads sequentially and it's already logically split.

I agree it's a difficult place in the codebase, but I'm not sure how to make it easier.

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.

At a high level the algorithm looks good! I didn't try to review too carefully for breakage, because I assume you'll find plenty of stuff when it gets tests, but I figure tests is a good next step :).

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

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

Concept ACK.

I think that's an endless debate to try to get an efficient MPP algo, get_route refactoring, clear high-level documentation and good test coverage all at once :)

I'm good enough if we have simple tests demonstrating MPP is working.

Note, I've tried to refactor get_route by extracting macro : https://github.com/ariard/rust-lightning/pull/new/2020-09-refactor-routing. I think that's the right way to go, but let's get your stuff merged first and then I can try to split routing algorithm by logical components, even if they're only used once, code maintainability matters.

@ariard
Copy link

ariard commented Sep 12, 2020

Here a rough model of pathfinding I've sketched for my own understanding. I don't propose it, it's more to help evaluating correctness of implemented algorithm.

We have the following entities:

  • a set of hops
  • a set of channels (attributes of hops)
  • a per-channel relay-fee (weight)
  • a per-channel capacity higher/lower bounds
  • a per-channel CLTV

We assume we are the only node routing on the network and thus can't anticipate
other outgoing routing by other network participants.

The problem we're solving is given a source S, a destination D and an amount A,
find a valid route. Note that amount A is not the one we effectively spend as
we have to cover relay fees. A route is a set of M payment paths, where each
payment path is a unique conjunction of channels between source S and destination
D.

A payment path weight is (sum of per-channel relay-fee) / (number of path channels).

Conditions of payment path validity :

  • the sum of each hop's cltv is under max network
  • the channel/node advertised routing features needed
  • each payment path must have an advertised capacity superior to amount A

If we have multiple valid payment paths candidates, we choose the one with
the lowest weight.

An additional condition (Multi-Payment-Path), if we don't have at least one payment
path succeeding above conditions, we can split amount A in N chunks and M can be
superior to 1. Thus a valid solution is a set of payment paths where each N chunks
is allocated to a payment path with enough capacity.

We observe a weak interdependency in case of M > 1 payment paths. In case of
paths intersections, we have to allocate advertised capacity between contenders.

AFAICT, how to allocate doesn't matter, the contended capacity is offered as the
same relay-fee between both payment paths. Picking randomly a payment path winner
doesn't degrade the total score of the payment paths combination.

As a proposed routing algorithm :

  1. Find all available payment paths
  2. Tie-break paths intersections
  3. Prune out paths invalid due to 2.
  4. Compute weights of valid paths
  5. If we don't have any valid paths, split amount A by 2 (divide factor)
  6. Prune out paths invalid (lower bound) due to 5.
  7. Iterate over all valid paths, select M lowest-weight ones until sum of capacities equal or superior
    to sum of N chunks
  8. If we fail , go back to 6, increase divide factor by 2
  9. If we obtain a set of valid paths, return success
  10. If we prune out all potential paths, retturn failure

@naumenkogs naumenkogs force-pushed the 2020-06-router-mpp branch 2 times, most recently from 11cb677 to b03b73a Compare September 17, 2020 09:10
@naumenkogs
Copy link
Contributor Author

I've addressed all the suggestions, now I'll finish some TODOs left and then start working on tests to see what it is capable of.

@ariard I have hard times mapping your explanation to my algorithm...
Maybe updated code will make it better for you to understand?

@ariard ariard added the MOAR TEST PLZ NEEDS MOAR TEST label Sep 21, 2020
@naumenkogs
Copy link
Contributor Author

Seems all passing except assert_eq!(sent_msat, value_msat); which is unaware that we might have to pay more on the last hop to hit htlc_minimum_msat

@TheBlueMatt
Copy link
Collaborator

Chugging along, I'm still working on fixing the failing tests (though take a look at the https://github.com/TheBlueMatt/rust-lightning/commits/646-moar-test branch again, it should be right now, but still fails a few cases), but it managed to find its way into the unreachable!() at lightning/src/routing/router.rs:290:17. The input is https://pastebin.com/sSWGM00x

@naumenkogs
Copy link
Contributor Author

@TheBlueMatt I fixed the specific unreachable issue you mentioned

@naumenkogs naumenkogs force-pushed the 2020-06-router-mpp branch 2 times, most recently from bb811de to 86196bd Compare February 25, 2021 20:35
@TheBlueMatt
Copy link
Collaborator

TheBlueMatt commented Feb 25, 2021

Spent some time tracking down more errors - the same branch, above, is rebased on this PR and has one additional fix commit (which I dont believe is a regression in this PR, but breaking the fuzzer nonetheless). It has two commented out checks that both fail in the top commit. One with the input from https://pastebin.com/fRWGnSxZ the other with the input from https://pastebin.com/wxzbppjr. Given I don't think the top commit is a regression, I'd say leave it off and I (or you) can PR it with an additional test for that case.

@naumenkogs
Copy link
Contributor Author

I agree. I updated one mistaken comment from earlier, added your commit and commented that we should do the same htlc_minimum_msat check in update_value_[...].

I think this can be merged now, although I didn't understand the point of this rename:

-				let last_hop_htlc_minimum_msat: u64 = match hop.htlc_minimum_msat {
+				let last_path_htlc_minimum_msat: u64 = match hop.htlc_minimum_msat {

@naumenkogs naumenkogs force-pushed the 2020-06-router-mpp branch 2 times, most recently from 465734c to 8896492 Compare February 26, 2021 09:20
@naumenkogs
Copy link
Contributor Author

Also, see one more commit addressing that TODO. Let me know if it causes some your test to fail, we can drop this code and leave for later.

@TheBlueMatt TheBlueMatt added this to the 0.0.13 milestone Feb 26, 2021
@naumenkogs naumenkogs force-pushed the 2020-06-router-mpp branch 3 times, most recently from 6b8d794 to 05d5955 Compare March 1, 2021 12:13
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.

Only nits, feel free to ignore unless there are other substantive comments. Lets land this thing!

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

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

I think the changes with my latest ack version of the branch are correct, though I would be glad retouching new comments.

new_entry = match dist.remove(&ordered_hops.last().unwrap().route_hop.pubkey) {
Some(payment_hop) => payment_hop,
// We can't arrive at None because of how we traverse with add_entry.
None => unreachable!(),
Copy link

Choose a reason for hiding this comment

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

@TheBlueMatt Is this "our (rather strange) modifications to dijkstras that might well be unique" you were mentioning on IRC ? If yes I would be glad to have more explications with a better comment :) Or maybe I should go on the slack thread between you and gleb

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wrote up a long comment to be added in 815 at ef88cb0

At truncating the overpaid value, if we fall below
htlc_minimum_msat, reach it by increasing fees.
We could have possibly constructed a slightly inconsistent
path: since we reduce value being transferred all the way, we
could have violated htlc_minimum_msat on some channels
we already passed (assuming dest->source direction). Here,
we recompute the fees again, so that if that's the case, we
match the currently underpaid htlc_minimum_msat with fees.
@naumenkogs naumenkogs force-pushed the 2020-06-router-mpp branch from 05d5955 to e0600e5 Compare March 2, 2021 19:40
@ariard
Copy link

ariard commented Mar 2, 2021

Code Review ACK e0600e5, thanks for the great comments.
We can commit ef88cb0 in a follow-up.

@TheBlueMatt TheBlueMatt merged commit 6f8d6ae into lightningdevkit:master Mar 3, 2021
TheBlueMatt added a commit that referenced this pull request Mar 3, 2021
@TheBlueMatt TheBlueMatt mentioned this pull request May 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Router MPP
4 participants