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

pay: respect maxfeepercent when choosing a shadow route #3693

Merged

Conversation

darosior
Copy link
Contributor

@darosior darosior commented May 2, 2020

We claim to do so in the manpage. I was wrong

fixes #3684

@darosior darosior force-pushed the pay_shadow_small_amounts branch from 771b64b to 3ad208b Compare May 2, 2020 12:24
@darosior darosior marked this pull request as ready for review May 2, 2020 12:25
@darosior darosior requested a review from cdecker as a code owner May 2, 2020 12:25
@darosior darosior force-pushed the pay_shadow_small_amounts branch from 3ad208b to aea9467 Compare May 2, 2020 13:46
@kirelagin
Copy link

kirelagin commented May 3, 2020

I gave the docs a more thorough read, and, it turns out, they are actually correct:

Route randomization will never exceed maxfeepercent of the payment. Route randomization and shadow routing will not take routes that would exceed maxdelay.

So, it only claims that route randomisation respects maxfeepercent, not shadow routing.

Anyway, docs aside, there are two things here:

  1. No way of disabling shadow routing. As I understand now, this was intentional, however I think it would still be good to have an option for disabling it (e.g. my use-case is that I control multiple nodes connected to each other and I would like to be able to distribute liquidity among them through direct channels, and it is easier when the amounts being transferred are not fuzzed).
  2. Fuzzing small amounts breaks the transfers as the payee rejects them.

I think that this PR is definitely an improvement on (1), as it makes the behaviour more predictable.

It also resolves (2), assuming that maxfeepercent cannot be set to more than 100. The receiving node rejects the transfer if the amount is twice that of the invoice. However, I am worried that it effectively completely disables shadow routing for small amounts, although, I suppose, it can be desirable in certain cases. Therefore, probably, not as part of this PR, but I think it is worth considering lifting the restriction on “twice the amount” on the receiving side for small payments (the RFC only says SHOULD, not MUST fail).

@darosior
Copy link
Contributor Author

darosior commented May 3, 2020

So, it only claims that route randomisation respects maxfeepercent, not shadow routing.

Right, amended the OP.

No way of disabling shadow routing.

Actually you can in developer mode this PR hid a disabling parameter (use_shadow) behind #if DEVELOPER :-).

It also resolves (2), assuming that maxfeepercent cannot be set to more than 100

I think it can, but It does default to 50%.

I am worried that it effectively completely disables shadow routing for small amounts, although

It doesn't if the receiving peer has channel signaling a base_fee which is less than amount * maxfeepercent.
However it does in most cases and I'm looking forward to Rusty and/or Christian feedback on this : it seems a lesser evil than effectively paying more than twice the requested amount.

I think it is worth considering lifting the restriction on “twice the amount” on the receiving side for small payments

Maybe, or put a treshold for it to take effect ? Like >10sat.

@darosior
Copy link
Contributor Author

darosior commented May 3, 2020

Don't know if I introduced it with this patch, but test_peerinfo seems flaky on Travis : I had to restart the VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=3 TEST_GROUP_COUNT=12 SOURCE_CHECK_ONLY=false job twice (and another one once).

@darosior
Copy link
Contributor Author

darosior commented May 3, 2020

Hmm I'm thinking that maybe we should still add the CLTV delta in this case, just not the amount.

@rustyrussell
Copy link
Contributor

OK, so the test sometimes passes before the changes anyway, so let's make it firmer.

Otherwise, excellent. Needs "Changelog-Fixed: pay: don't send occasional wrong amount for tiny amounts" and "Changelog-Changed: pay: respect maxfeepercent when creating shadow routes" though.

diff --git a/tests/test_pay.py b/tests/test_pay.py
index c1373652f..af37eb0f9 100644
--- a/tests/test_pay.py
+++ b/tests/test_pay.py
@@ -462,18 +462,22 @@ def test_pay_maxfee_shadow(node_factory):
         lambda: len(l1.rpc.listchannels(source=l2.info["id"])["channels"]) > 1
     )
 
-    # A tiny amount, we must not add the base_fee between l2 and l3
-    amount = 2
-    bolt11 = l2.rpc.invoice(amount, "tiny", "tiny")["bolt11"]
-    pay_status = l1.rpc.pay(bolt11)
-    assert pay_status["amount_msat"] == Millisatoshi(amount)
-
-    # A bigger amount, shadow routing could have been used but we set a low
-    # maxfeepercent.
-    amount = 20000
-    bolt11 = l2.rpc.invoice(amount, "bigger", "bigger")["bolt11"]
-    pay_status = l1.rpc.pay(bolt11, maxfeepercent="0.000001")
-    assert pay_status["amount_msat"] == Millisatoshi(amount)
+    # shadow routes are a bit random, so run multiple times.
+    for i in range(5):
+        # A tiny amount, we must not add the base_fee between l2 and l3
+        amount = 2
+        bolt11 = l2.rpc.invoice(amount, "tiny.{}".format(i), "tiny")["bolt11"]
+        pay_status = l1.rpc.pay(bolt11)
+        assert pay_status["amount_msat"] == Millisatoshi(amount)
+
+    # shadow routes are a bit random, so run multiple times.
+    for i in range(5):
+        # A bigger amount, shadow routing could have been used but we set a low
+        # maxfeepercent.
+        amount = 20000
+        bolt11 = l2.rpc.invoice(amount, "big.{}".format(i), "bigger")["bolt11"]
+        pay_status = l1.rpc.pay(bolt11, maxfeepercent="0.000001")
+        assert pay_status["amount_msat"] == Millisatoshi(amount)
 
 
 def test_sendpay(node_factory):

@rustyrussell
Copy link
Contributor

1. No way of disabling shadow routing. As I understand now, [this was intentional](https://github.com/ElementsProject/lightning/pull/3212#discussion_r342386943), however I think it would still be good to have an option for disabling it (e.g. my use-case is that I control multiple nodes connected to each other and I would like to be able to distribute liquidity among them through direct channels, and it is easier when the amounts being transferred are not fuzzed).

You can always use the lowlevel getroute and sendpay APIs if you want finer control (they can literally be chained together), or if they're direct peers you can make a route pretty easily without getroute at all?

@darosior
Copy link
Contributor Author

darosior commented May 4, 2020

OK, so the test sometimes passes before the changes anyway, so let's make it firmer.

Ok, I usually comment out the randomization to test shadow routing :)

lightning/plugins/pay.c

Lines 1107 to 1108 in 34ed209

if (pseudorand(2) == 0)
return start_pay_attempt(cmd, pc, "Initial attempt");

darosior added 2 commits May 4, 2020 08:58
Fixed-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
And the percentage of the initial amount, not the constently increasing
one !

Changelog-Fixed: pay: we now respect maxfeepercent, even for tiny amounts.
Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
@darosior darosior force-pushed the pay_shadow_small_amounts branch from aea9467 to 7993285 Compare May 4, 2020 06:59
@kirelagin
Copy link

You can always use the lowlevel getroute and sendpay APIs if you want finer control (they can literally be chained together),

Yes, that’s what I was considering, but I would also need to waitsendpay and, in addition to that, I’m not sure if there are any potential unexpected transient failures that pay would retry for me.

or if they're direct peers you can make a route pretty easily without getroute at all?

For that I'd need to know the parameters of the channel, and rather than keeping track of them, it’s much easier to just cal getroute to be honest.

@rustyrussell
Copy link
Contributor

Yes, pay will retry, but via a different path (if any available) which is probably not what you want? It won't wait for peers to come online or anything.

Copy link
Contributor

@rustyrussell rustyrussell left a comment

Choose a reason for hiding this comment

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

Ack 7993285

@rustyrussell rustyrussell merged commit 4bb7b46 into ElementsProject:master May 5, 2020
@darosior darosior deleted the pay_shadow_small_amounts branch May 5, 2020 09:53
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.

pay tries to pay an invoice with a wrong amount
3 participants