renepay: accomodate fees in the payment flow - #6893
Merged
rustyrussell merged 1 commit intoJan 29, 2024
Merged
Conversation
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
November 27, 2023 09:48
221a7b6 to
c03168d
Compare
Lagrang3
marked this pull request as ready for review
November 27, 2023 09:53
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
November 29, 2023 10:52
65fcc77 to
5de111b
Compare
Contributor
|
Notes:
|
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
December 15, 2023 15:42
de94391 to
025d047
Compare
Lagrang3
marked this pull request as draft
December 16, 2023 09:42
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
December 16, 2023 09:53
025d047 to
3fc82bf
Compare
Collaborator
Author
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
December 16, 2023 10:45
3fc82bf to
0822bb6
Compare
Collaborator
Author
|
Added an error check in uncertainty_network_update_from_listpeerchannels and rebased and squashed commits. |
Lagrang3
marked this pull request as ready for review
December 16, 2023 10:48
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
December 18, 2023 14:02
910f2f9 to
a49806d
Compare
Contributor
|
I tried to rebase this, but it was non-trivial and the tests failed. So I'd ask you to do it please? |
rustyrussell
approved these changes
Jan 24, 2024
rustyrussell
left a comment
Contributor
There was a problem hiding this comment.
Trivial feedback only... this approach looks much nicer than my random hack!!!
Rebase needed though
|
|
||
| /* Try to increase the value we send until we fail to fulfill the | ||
| * fee inequality. It takes only one iteration though. */ | ||
| for(int i=0;i<10;++i) |
Contributor
There was a problem hiding this comment.
nitpick: I prefer size_t i if it's a simple loop like this
| = amount_msat_min( | ||
| channel_liquidity(gossmap,chan_extra_map,flow->path[0],flow->dirs[0]), | ||
| channel_max_htlc(flow->path[0],flow->dirs[0])); | ||
| for(int i=1;i<tal_count(flow->path);++i) |
| * probability. If the deficit is very small with respect to the amount | ||
| * each flow carries then optimization here will not make much | ||
| * difference. */ | ||
| for(int i=0;i<tal_count(flows) && !amount_msat_zero(deficit);++i) |
Min. Cost Flow does not take into account fees when computing a flow
with liquidity constraints.
This is a work-around solution that reduces the amount on every route to
respect the liquidity bound. The deficity in the delivered amount is
solved by running MCF once again.
Changes:
1. the function `flow_complete` allocates amounts to send over the set of routes
computed by the MCF algorithm, but it does not allocate more than liquidity
bound of the route. For this reason `minflow` returns a set of routes that
satisfy the liquidity bounds but it is not guaranteed that the total payment
reaches the destination therefore there could a deficit in the delivery:
`deficit = amount_to_deliver - delivering`.
2. in the function `add_payflows` after `minflow` returns a set of routes we
call `flows_fit_amount` that tries to a allocate the `deficit` in the routes
that the MCF have computed.
3. if the resulting flows pass all payment constraints then we update
`amount_to_deliver = amount_to_deliver - delivering`, and the loop
repeats as long as `amount_to_deliver` is not zero.
In other words, the excess amount, beyond the liquidity bound,
in the routes is removed and then we try to allocate it
into known routes, otherwise we do a whole MCF again just for the
remaining amount.
Fixes issue ElementsProject#6599
Lagrang3
force-pushed
the
renepay_fee_reserve
branch
from
January 27, 2024 09:49
a49806d to
8d87c19
Compare
Collaborator
Author
|
I have rebased and fixed the conflicts. If the CI test pass, this will be ready for merging. |
Contributor
|
Flake is unrelated, opened PR #7021 for that. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Min. Cost Flow (MCF) does not resolve the correct amount in channel forwarding
because the algorithm dismisses the amounts paid as fees to the routing
nodes. The root of this problem is that flow conservation is assumed for
simplicity such that the sender is the only source and the recepient is the
only sink. This in reality is not true, because also forwarding nodes are
sinks since they consume fees.
When real amounts including fees are then added to the payment routes by
the function
flow_complete, some channels might be requested to forward anamount which could be greater than
known_max - htlc_total.For remote channels this issue is likely inadverted because we are anyways
caping the probability of success down to 5% in our linearization scheme,
see this line,
ie. 5% of the channel probable liquidity is reserved which could be in most
cases sufficient to let fees go through.
However, if the conditional capacity (ie.
know_max - known_min) is smallthen its 5% may not be sufficient to let fees go through, specially if the MCF
has saturated the available liquidity of that particular channel.
The easiest way this might happen is with local channels, because
known_max-knon_min = 0therefore there is 0 breath space for fees, furthermorethe MCF is always favorable to send as much flow as possible through local
channels because their fee and uncertainty cost is zero.
This was reported in issue #6599.
A temporary fix #6601 was merged that puts a fee reserve of 1% in local
channels.
This commit is a more robust solution that works-around the problem by reducing
a portion of the amount in those routes that exceed the liquidity bound.
Then the deficit in the deliver amount is either sent along the other routes
or sent through a new set of flows.
Liquidity bound in a channel
The liquidity bound in a channel is defined as
known_max - htlc_total, itrepresents the maximum amount we can forward through this channel given our
current knowledge. This is implemented in this function:
Maximum forward
Bolt 7 specifies that a channel should forward an amount
outif thefeeis greater equal toIf there is a limit
into the amount we can pay to a node to forward a payment onour behalf (through a specific channel) there will be a limit to the amount that
the channel can forward for us. Since we would like to send as much as possible we
need to solve the following problem:
given the value
in, find the maximum valueoutsuch that:Which is equivalent to
Let's denote the bound in the right hand side as
B(in,out),which depends on
outand that makes the fee equation difficult to invert.But consider another simpler expression
B_simple(in)One can quickly verify that
B_simple(in) <= B(in,out)so that one could useB_simple(in)as an upper bound forout.In fact we can even show that
for every value of
out.Therefore if we set
we will automatically satisfy Bolt7 inequality.
We can also try if
out+1also satisfies the inequality, if it does then thatnumber is the maximum value we can forward.
The function
computes the maximum value of
outsuch thatusing the technique we explained here.
Liquidity bound of a route
Another component of this PR is the liquidity bound of a given route.
It is defined as the maximum amount the last node can receive considering the
liquidity bound of each channel and the fees that are paid.
It can be computed greedly in a single pass from the source to the destination.
The implementation is in the function:
For completeness we also constrain the liquidity of the route by taking into account the
htlc_max.Changes
the function
flow_completeallocates amounts to send over the set of routescomputed by the MCF algorithm, but it does not allocate more than liquidity
bound of the route. For this reason
minflowreturns a set of routes thatsatisfy the liquidity bounds but it is not guaranteed that the total payment
reaches the destination therefore there could a deficit in the delivery:
deficit = amount_to_deliver - delivering.in the function
add_payflowsafterminflowreturns a set of routes wecall
flows_fit_amountthat tries to a allocate thedeficitin the routesthat the MCF have computed.
if the resulting flows pass all payment constraints then we update
amount_to_deliver = amount_to_deliver - delivering, and the looprepeats as long as
amount_to_deliveris not zero.In other words, the excess amount, beyond the liquidity bound,
in the routes is removed and then we try to allocate it
into known routes, otherwise we do a whole MCF again just for the
remaining amount.
Task list