-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
routing: fix mc blinded path behaviour. #9316
routing: fix mc blinded path behaviour. #9316
Conversation
Important Review skippedAuto reviews are limited to specific labels. 🏷️ Labels to auto review (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
d2c031e
to
caee111
Compare
c74d2c0
to
2facd58
Compare
maybe we should add an itest ? |
routing/result_interpretation.go
Outdated
// route we swap the target pubkey for a general one | ||
// so that multi-path payment can be attempted. However |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it isnt super clear to me from the comment what the issue is/was. Is it that we only want to penalise one path instead of all the paths? why not just use the real (pre swap) final key & penalise that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently do use the real-path-pubkey, the problem arises that we will not use this information during pathfinding hence we will try to reattempt paths which already failed with the invalid blinding error. So that's why I propose penalizing the whole path which makes it bullet-proof.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the problem yet, could you specify a reproduction scenario (and logs with amounts)? The last hop (swapped) pubkey should be stable over a payment, right? So penalizing the last hop pair should be in effect even if further partial attempts are made. I can see though that the amount will be split often and the route will be re-attempted until the min shard size is reached
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the setup is pretty simple, just create a blinded path where the payment fails in the blinded path. LND will retry the payment until timeout because the wrong pair is penalized in the MC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LND will also not split the amount because it always find the same route.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, yes I see it now thanks for the examples, in pathfinding we use the swapped pubkey T for Z (X, T), but when reporting to mission control we use the original route and therefore we punish (X, Z). I think we can fix it like it is done in this PR.
However, as noted in the comments in the lines above this change, we would fail a payment that has for example two single-hop hop hints after we fail to send over one of them. As noted in the comment, this was ok for non-mpp scenarios, but we support mpp now. So I think the cleanest fix would be to add a dummy hop to the single-hop hints such that we have an extension like [(X,Z), (Z,T)] and a search a path to T. If we make T static, we could even reuse those mc reports for the same blinded route (not sure if there's an application). The dummy hop would need to be transported via the route to reporting of mission control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That actually seems like the quick non-mpp solution, swap in a static pubkey before pathfinding and swap it in again when reporting a failure of the blinded route?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested the current setup for a multipart payment, but we need something more, because with failPairBalance
we actually take the previous hop's amtToFwd
, which is set to zero for blinded hops. This leads to an over-penalization, we basically say that we can't forward anything by setting a failure amount of zero. To fix that, I'd suggest to add and use:
// failBlindedRoute marks a blinded route as failed for the specific amount to
// send.
func (i *interpretedResult) failBlindedRoute(rt *mcRoute) {
// We fail the last pair of the route, in order to fail the complete
// blinded route. This is because the combination of ephemeral pubkeys
// is unique to the route. We fail the last pair in order to not punish
// the introduction node, since we don't want to disincentivize them
// from providing that service.
pair, _ := getPair(rt, len(rt.hops.Val)-1)
// Since all the hops along a blinded path don't have any amount set, we
// extract the minimal amount to punish from the value that is tried to
// be sent to the receiver.
amt := rt.hops.Val[len(rt.hops.Val)-1].amtToFwd.Val
i.pairResults[pair] = failPairResult(amt)
}
After a discussion with Elle we decided to only punish the first PubKey after the intropoint and create an issue which makes sure blinded route mc data is not persistent in the db but rather only held in memory and removed as soon as the payment is done. |
2facd58
to
bff6e2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm! 🙏
some logs of the problem, so the problem is the wrong channel-pair is punished and we will always use the same path:
and from the cli view:
|
same for a bigger amount:
|
After discussions with @bitromortac we decided to change the approach: We are going to introduce a static target key (via a nums Point) so that no node can come up with the corresponding private key to create valid routes. When searching for paths or reporting an error in a blinded route we will use this key to persist the change. This will make it easy for us to clear the current MC-Store from blinded path data when we introduce the new blinded path logic where we won't persist blinded path MC data. |
bff6e2c
to
b235942
Compare
After trying to implement the option of a static target public key, I realised we cannot do this because the problem are rpc calls like SendToRoute. We do not want to swap out the public key for predefined routes, because there is no MPP payment happening. So we should keep it as is, and refactor the whole logic of Mission Control results in another PR. Moreover because it came up in a discussion, I added a testcase where the blinded path has duplicated hops, to make sure the pathfinding logic can handle this case. Blinded routes can have duplicated hops we can either abort the payment when parsing the invoice, but because we currently don't do it a testcase was created to verify that this does not cause any trouble with the pathfinding logic. |
Nice 🙏! In the current state of the PR, where we penalize the hop starting at the introduction node, this will penalize the introduction node (as the "node probability" will start to decline if the starting node of the hop accrues a lot of failures). We originally tried to avoid that to not disincentivize nodes from routing blinded payments. So the problem is that we can't penalize the first hop after the intro node, because we don't want the intro node to get penalized for its service and we can't penalize the last hop because the target node pubkey was replaced. I think that the conclusive fix is to append a dummy hop (real target, dummy target) to the blinded paths and search a path to the dummy target. When penalizing we would then keep the penalization as is, using the real blinded route's last hop. This is then also compatible with somebody doing external pathfinding, using mission control data produced by |
Ahh ok did not think about this, will come up with the dummy hop solution then. |
f4bc889
to
65aed69
Compare
The approach changed now, we are still penalizing the last hop but when sending a payment we add a NUMS Target key for blinded paths and as soon as the route is found before creating the final route we drop it again. The CI passed lmk what you think, thank you @bitromortac for the ideas 🙏 |
3236a92
to
d5b0af4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your reviews. PTAL
d5b0af4
to
5390ede
Compare
a366d40
to
34291a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Almost there I think. One blocking comment from me and then will ACK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tACK, LGTM ⚡ (one test needs a small change)
34291a7
to
963678b
Compare
ed70a51
to
878cc2f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! one micro nit - but non-blocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like there is a panic in the new deep copy code @ziggie1984
0d2e225
to
16ea1a9
Compare
To be able to do MPP payment to multiple blinded routes we need to add a constant dummy hop as a final hop to every blined path. This is used when sending or querying a blinded path, to let the pathfinder be able to send MPP payments over different blinded routes. For this dummy final hop we use a NUMS key so that we are sure no other node can use this blinded pubkey either in a normal or blinded route. Moreover this helps us handling the mission control data for blinded paths correctly because we always consider the blinded pubkey pairs which are registered with mission control when a payment to a blinded path fails.
Fixes a bug and makes the function more robust. Before we would always return the encrypted data size of last hop of the last path. Now we return the greatest last hop payload not always the one of the last path.
We add a test where we add duplicate hops in a route and verify that the pathfinding engine can handle this edge case.
When reporting an error or a success case of a payment to a blinded paths, the amounts to forward for intermediate hops are set to 0 so we need to use the receiver amount instead.
16ea1a9
to
101311d
Compare
Because we hot swap the target public key when sending to a
blinded path we need to penalize all nodes after the introduction
point.
Approach changed, see comment below.
We could also just penalize the first node after the introduction node, however that would mean we would do a bit of more pathfinding work and only fail after considering the rest of the blinded path. I think it is not super critical to have this bit more data persistent in memory.