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

rpc+routing: add support for fee limits when finding routes for payments #1113

Merged
merged 8 commits into from
Jun 13, 2018

Conversation

wpaulino
Copy link
Contributor

This PR adds support for fee limits when finding possible routes for payments.

The fee limit can be specified in two ways: either as a fixed amount of satoshis or as a percentage of the payment's amount.

Replaces #714 and fixes #278.

Copy link
Contributor

@halseth halseth left a comment

Choose a reason for hiding this comment

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

Nice job! This feature is a much needed one, as at the moment you might end up paying excessive amounts of fees with no way of controlling it! 🚔

A few comments, main one that we should add test for the new RPC.

Thanks to @djseeds and @sebastiandelgado for getting this started, and @danrobinson for initial review! 👏

rpcserver.go Outdated
btcutil.Amount(feeLimit.GetFixed()),
)
case *lnrpc.FeeLimit_Percent:
return amount * lnwire.MilliSatoshi(feeLimit.GetPercent()/100)
Copy link
Contributor

Choose a reason for hiding this comment

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

generally best to move the division last to avoid loss of precision, as I think this will become 0 by integer division.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

rpcserver.go Outdated
// payment amount as an upper bound since there might be some
// routes where you pay more in fees than the amount of the
// payment.
return maxPaymentMSat
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should restrict this to 2x payment amount or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was thinking this too. Not sure if we want to enforce this without adding a no_fee_limit flag though.

Copy link
Contributor

Choose a reason for hiding this comment

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

If the caller really wants no fee limit, she can set the fee limit to a ridiculously high number.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still think we need to have a sane default value here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot to address this - should be good now.

Copy link
Contributor

Choose a reason for hiding this comment

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

I gave this a spin on testnet and seems o be working as advertised! Only thing I realized is that very small payments might be impossible to get through with this default value, since fees might always be higher. This might be okay though, as you can always increase your fee limit.

Also, for large payments, you might end up paying 2x the amount if the only route is a high fee route (value + fee when fee=value). This might lead to some unpleasant surprises. Should we set a default, hard coded max value? We could even consider doing this at the lncli level. Atm the default value appears to be 0 if you look at lncli payinvoice -h. We could change this to a reasonable value?

err error
amount int64
)
return sendPaymentRequest(ctx, req)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice refactor! Can you add a short comment why we short circuit in case pay_req is set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -992,8 +1004,10 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {

// Should still be able to find the routes, and the info should be
// updated.
routes, err = ctx.router.FindRoutes(targetNode, paymentAmt,
defaultNumRoutes, DefaultFinalCLTVDelta)
routes, err = ctx.router.FindRoutes(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could add a short test here for the fee limit exceeded case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

rpcserver.go Outdated
@@ -1832,6 +1852,10 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
p.cltvDelta = uint16(nextPayment.FinalCltvDelta)
}

p.feeLimit = calculateFeeLimit(
Copy link
Contributor

Choose a reason for hiding this comment

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

Make an addition to the integration tests to check that percentage and absolute fee limits are working correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

lnd_test.go Outdated
}

// Wait for Alice to receive the channel update.
time.Sleep(5 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

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

we should try to eliminate sleeps during integration tests, as they are becoming quite time consuming. Is there any way you could subscribe to graph updates to determine when Alice has seen the policy update? (maybe the policy update test does this).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

for _, channel := range listResp.Channels {
switch channel.RemotePubkey {
case net.Alice.PubKeyStr:
aliceBobChanID = channel.ChanId
Copy link
Contributor

Choose a reason for hiding this comment

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

You could get these from openChannelAndAssert

Copy link
Contributor

Choose a reason for hiding this comment

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

EDIT: Since these are short channel IDs I realized you probably can't that easily, so this approach is fine :)

Copy link
Contributor

@halseth halseth left a comment

Choose a reason for hiding this comment

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

Starting to look very good to me! One final comment about shortening the test, then I will do a final review after squash an rebase 👍

lnd_test.go Outdated

// waitForChanUpdate is a helper closure that will wait for an update
// for a specific channel and assert its routing policy.
waitForChanUpdate := func(graphUpdates chan *lnrpc.GraphTopologyUpdate,
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a waitForChannelUpdate elsewhere in this file. Is it possible to unify them, or are they checking different criteria?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

lnd_test.go Outdated
}

// assertChannelPolicy asserts that the passed node's known channel
// policy for the passed chanPoint is consistent with Bob's current
Copy link
Contributor

Choose a reason for hiding this comment

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

Bob

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.


// waitForChannelUpdate waits for a node to receive updates from the advertising
// node for the specified channels.
func waitForChannelUpdate(t *harnessTest, graphUpdates chan *lnrpc.GraphTopologyUpdate,
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

for _, channel := range listResp.Channels {
switch channel.RemotePubkey {
case net.Alice.PubKeyStr:
aliceBobChanID = channel.ChanId
Copy link
Contributor

Choose a reason for hiding this comment

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

EDIT: Since these are short channel IDs I realized you probably can't that easily, so this approach is fine :)

@halseth
Copy link
Contributor

halseth commented May 7, 2018

Still think we need a default fee cutoff set. Otherwise this LGTM 👍

@wpaulino wpaulino force-pushed the fee-cutoff branch 3 times, most recently from 7b9faf0 to 0388447 Compare May 7, 2018 20:51
@wpaulino wpaulino force-pushed the fee-cutoff branch 2 times, most recently from 68182b0 to 980ba6b Compare June 1, 2018 00:21
@Roasbeef
Copy link
Member

Roasbeef commented Jun 2, 2018

Needs a rebase to address proto conflicts.

@wpaulino
Copy link
Contributor Author

wpaulino commented Jun 7, 2018

Rebased.

@halseth
Copy link
Contributor

halseth commented Jun 7, 2018

Needs rebase and probably some tweaking again after sendToRoute merge 😅

@wpaulino
Copy link
Contributor Author

Rebased.

Roasbeef
Roasbeef previously approved these changes Jun 13, 2018
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 👾

Can you try to rebase s.t, the binaries are never even added in the first place?

Also, FWIW the current impl is a bit off, as it'll just error out, rather then seeking the set of paths that respect the feel limit. However, with #1321, we'll have the opportunity to implement a proper greedy solution.

@@ -2547,6 +2614,8 @@ func queryRoutes(ctx *cli.Context) error {
return fmt.Errorf("amt argument missing")
}

feeLimit = calculateFeeLimit(ctx, amt)

Copy link
Member

Choose a reason for hiding this comment

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

The debug binaries have been committed.

@Roasbeef Roasbeef merged commit 26636ce into lightningnetwork:master Jun 13, 2018
@wpaulino wpaulino deleted the fee-cutoff branch June 13, 2018 02:38
nothingmuch referenced this pull request in BlueWallet/LndHub Mar 20, 2019
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.

Add fee-based cut-off within SendPayment of the ChannelRouter
4 participants