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

Track in-flight HTLCs across payments when routing #1643

Conversation

jurvis
Copy link
Contributor

@jurvis jurvis commented Aug 1, 2022

Addresses #1267.

Purpose

As a sender, we want to keep track of all the HTLCs that are currently in flight. We currently do that between pathfinding attempts in find_route. However, that information is not currently shared across payments, leading to some inaccuracies.

Proposed Implementation

Accounting for the HTLCs will consist of two efforts: keeping track of them, and using them.

Keep Track

In pay_internal, we want to respond to specific results of send_payment which will tell us when we'll need to store the path information for use later.

Path information is then stored in payment_cache of our payer.

Using

Before find_route is called, we retrieve all the paths we stored, and loop through them to construct a HashMap (HashMap<(u64, bool), u64>) mapping a channel's short channel id and direction with the amount being sent.

This map is wrapped with our user-supplied score in aAccountForInFlightHtlcs struct, which will serve to delegate Score functions to its underlying scorer. It will also use our new liquidity map to construct a ChannelUsage accounting for the inflight HTLCs to be passed to channel_penalty_msat.

Last Edited: 2022-08-15T05:27:11.602Z

Other Notes

Future opportunities:

  • @ariard: track CLTV deltas to know when HTLCs are expiring/stuck
  • @jkczyz: have router not need to depend on scorer (possibly in a future commit of this PR)

@jurvis jurvis changed the title Jurvis/2022 07 inflights htlcs across payments Track in-flight HTLCs across payments Aug 1, 2022
@jurvis jurvis marked this pull request as draft August 1, 2022 06:26
@jurvis jurvis changed the title Track in-flight HTLCs across payments Track in-flight HTLCs across payments when routing Aug 1, 2022
@jkczyz jkczyz self-requested a review August 1, 2022 15:33
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from fa440fa to d03d6a8 Compare August 5, 2022 05:57
@jkczyz jkczyz requested a review from TheBlueMatt August 6, 2022 01:26
@jkczyz
Copy link
Contributor

jkczyz commented Aug 6, 2022

@TheBlueMatt Would good to have some early feedback on this PR. Upcoming commits will use in-flight HTLC tracking during routing. Follow-up PR will deal with persistence.

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.

As for the followup to do the tracking, I don't think we need a new interface in the router at all, I think we can do it entirely by wrapping the scorer and changing the amounts we pass through to the user-provided scorer.

@dunxen
Copy link
Contributor

dunxen commented Aug 8, 2022

Going to be using this for review club session 5. Nice to dig into payment logic a bit. :)

@jurvis
Copy link
Contributor Author

jurvis commented Aug 8, 2022

@TheBlueMatt

As for the followup to do the tracking, I don't think we need a new interface in the router at all, I think we can do it entirely by wrapping the scorer and changing the amounts we pass through to the user-provided scorer.

hm, I think we do need to pass in the inflight htlcs map into get_route because we do account for used_channel_liquidities when pathfinding here as well.

@TheBlueMatt
Copy link
Collaborator

hm, I think we do need to pass in the inflight htlcs map into get_route because we do account for used_channel_liquidities when pathfinding here as well.

Yea, if we want to exactly match the behavior, but I'm not sure that is neccessary - it'll result in a few suboptimal decisions, but we can do it only in the scorer and we'll still get most of the way there, without more complexity in the router itself.

@jkczyz
Copy link
Contributor

jkczyz commented Aug 8, 2022

Yea, if we want to exactly match the behavior, but I'm not sure that is neccessary - it'll result in a few suboptimal decisions, but we can do it only in the scorer and we'll still get most of the way there, without more complexity in the router itself.

There may be another case when computing available_value_contribution_msat:

.map_or(0, |used_liquidity_msat| {
available_value_contribution_msat = available_value_contribution_msat
.saturating_sub(*used_liquidity_msat);

@TheBlueMatt
Copy link
Collaborator

There may be another case when computing available_value_contribution_msat:

Yea, that's generally what I was referring to - we'll not make the same decisions we'd like to around how much we'd maximally want to send over a channel. I do kinda now wonder if we shouldn't have made that a method on the scorer, but either way, I'm not sure its totally critical that we get it exactly right here - at worse we'll end up refusing to re-use channels that we already have pending payments over where we may have been willing to re-use some small part of their capacity.

@jurvis
Copy link
Contributor Author

jurvis commented Aug 9, 2022

I do kinda now wonder if we shouldn't have made that a method on the scorer

which method? channel_penalty_msat? is your suggestion to pass our map of inflight htlcs to the scorer and just have it subtracted when calculating capacity_msat?

let capacity_msat = usage.effective_capacity.as_msat()
.saturating_sub(usage.inflight_htlc_msat);

@jkczyz
Copy link
Contributor

jkczyz commented Aug 9, 2022

I do kinda now wonder if we shouldn't have made that a method on the scorer

which method? channel_penalty_msat? is your suggestion to pass our map of inflight htlcs to the scorer and just have it subtracted when calculating capacity_msat?

let capacity_msat = usage.effective_capacity.as_msat()
.saturating_sub(usage.inflight_htlc_msat);

@TheBlueMatt Did you mean should have not shouldn't have?

@TheBlueMatt
Copy link
Collaborator

@TheBlueMatt Did you mean should have not shouldn't have?

Err, yes lol.

@jkczyz
Copy link
Contributor

jkczyz commented Aug 9, 2022

Another thing to keep in mind is that we'd eventually like to move the LockableScore from InvoicePayer to DefaultRouter since a remote Router wouldn't need a scorer locally. So we may need to pass the in-flight HTLC map to Router::find_route even if it is not used by the standalone find_route function that DefaultRoute::find_route calls. But I think the change would be fairly trivial (i.e., moving the use of the AccountForInFlightHtlcs wrapper into DefaultRoute::find_route).

@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from f827add to b184bee Compare August 18, 2022 06:19
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch 2 times, most recently from e8e05fd to e657138 Compare August 21, 2022 17:22
@jurvis jurvis marked this pull request as ready for review August 21, 2022 17:22
Copy link
Contributor

@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.

LGTM other than a few nits.

@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from 033028b to 9911955 Compare August 26, 2022 16:04
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch 2 times, most recently from 1abd3df to a434146 Compare August 28, 2022 06:08
Copy link
Contributor

@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.

Ok, I'm happy with this other than one test change to make it more realistic.

@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from a434146 to 08130b7 Compare August 30, 2022 01:55
Copy link
Contributor

@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.

LGTM. @TheBlueMatt could you take another look?

Introduces a new `PaymentInfo` struct that contains both the previous
`attempts` count that was tracked as well as the paths that are also
currently inflight.
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from 08130b7 to 51b90ab Compare August 30, 2022 05:59
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let route = TestRouter::route_for_value(final_value_msat);
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());

Choose a reason for hiding this comment

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

Are these RefCells really needed? Wouldn't just creating TestScorers work generally?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe this is because InvoicePayer expects the scorer to implement LockableScore.

TestScorer here is really just a mocked version of a scorer that we'd expect users to implement on their own

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, a scorer needs to be locked to (a) use its mutable interface in InvoicePayer's event handling and (b) use its immutable interface when routing such that its state doesn't change between calls. Additionally, another reference to it may be needed for serialization.

Copy link

@adamritter adamritter Aug 31, 2022

Choose a reason for hiding this comment

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

I see, thanks! MinCostFlowRouter pull request is at a point where it needs to be tested together with InvoicePayer and tracking in-flight HTLCs, but in a conversion with @TheBlueMatt, he said that as MinCostFlowRouter needs to access liquidity tracking, but not a custom scorer provided by LDK users, LiquidityTracker should be a separate trait implemented by the scorers. Another option in my opinion would be keeping liquidity tracking from scoring totally separate, and DefaultRouter should request the estimated liquidity information and provide it to the immutable Scorer (that can be customized by LDK user, while liquidity tracking implementation probably shouldn't be surfaced in the API).

What do you guys think?

Here's a link to the comments:

#1668 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think we want at least two follow-ups to this PR:

(1) Move the LockableScore type parameter to DefaultRouter since some routers (e.g., remote server routing) don't need a scorer.
(2) Move liquidity tracking to either the Payer trait or a dedicated one, having InvoicePayer query it and pass the liquidity information to Router akin to how it currently passes a scorer.

The latter comes from an observation that I made in review club. ChannelManager actually knows all the liquidity information given it is the one producing PaymentPathFailed and PaymentPathSuccessful events. It also would make liquidity tracking persist across restarts.

See also #1157 for exposing pending payments more generally, which should be considered when making this change.

Choose a reason for hiding this comment

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

I see, thanks, that makes a lot of sense.

@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from 51b90ab to c43843e Compare August 31, 2022 03:08
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from c43843e to e1fddf1 Compare August 31, 2022 20:39
TheBlueMatt
TheBlueMatt previously approved these changes Aug 31, 2022
jurvis added 2 commits August 31, 2022 18:50
Added two methods, `process_path_inflight_htlcs` and
`remove_path_inflight_htlcs`, that updates that `payment_cache` map with
path information that may have failed, succeeded, or have been given up
on.

Introduced `AccountForInflightHtlcs`, which will wrap our user-provided
scorer. We move the `S:Score` type parameterization from the `Router` to
`find_route`, so we can use our newly introduced
`AccountForInflightHtlcs`.

`AccountForInflightHtlcs` keeps track of a map of inflight HTLCs by
their short channel id, direction, and give us the value that is being
used up.

This map will in turn be populated prior to calling `find_route`, where
we’ll use `create_inflight_map`, to generate a current map of all
inflight HTLCs based on what was stored in `payment_cache`.
Made sure that every hop has a unique receipient. When we simulate
calling `channel_penalty_msat` in `TestRouter`’s find route, use
actual previous node ids instead of just using the payer’s.
@jurvis jurvis force-pushed the jurvis/2022-07-inflights-htlcs-across-payments branch from e1fddf1 to 80daf94 Compare September 1, 2022 01:50
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.

7 participants