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

Allow nodes to overshoot final htlc amount and expiry #1032

Merged
merged 2 commits into from
Nov 8, 2022

Conversation

t-bast
Copy link
Collaborator

@t-bast t-bast commented Oct 11, 2022

When nodes receive HTLCs, they verify that the contents of those HTLCs match the intructions that the sender provided in the onion. It is important to ensure that intermediate nodes and final nodes have similar requirements, otherwise a malicious intermediate node could easily probe whether the next node is the final recipient or not.

Unfortunately, the requirements for intermediate nodes were more lenient than the requirements for final nodes. Intermediate nodes allowed overpaying and increasing the CLTV expiry, whereas final nodes required a perfect equality between the HTLC values and the onion values.

This provided a trivial way of probing: when relaying an HTLC, nodes could relay 1 msat more than what the onion instructed (or increase the outgoing expiry by 1). If the next node was an intermediate node, they would accept this HTLC, but if the next node was the recipient, they would reject it.

We update those requirements to fix this probing attack vector. We also rename the min_final_cltv_expiry field, which is actually an expiry delta.

@@ -292,7 +292,7 @@ The writer:
- otherwise:
- MUST set `total_msat` to the amount it wishes to pay.
- MUST ensure that the total `amount_msat` of the HTLC set which arrives at the payee
is equal to `total_msat`.
is equal to or greater than `total_msat`.
Copy link
Collaborator

@joostjager joostjager Oct 11, 2022

Choose a reason for hiding this comment

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

I don't think this change is necessary anymore after the change to allow htlc_amt > amt_to_fwd in the later commit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think we have to keep it, don't we? Because if we overpay one HTLC in the set, that means that the total HTLC set will overpay the total_amount?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think so, because you only count amt_to_forward towards total_msat. The overpayment is already absorbed at the htlc level.

Copy link
Collaborator Author

@t-bast t-bast Oct 11, 2022

Choose a reason for hiding this comment

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

I don't think so, because you only count amt_to_forward towards total_msat. The overpayment is already absorbed at the htlc level.

I see what you mean, that depends on what you use to compute your "HTLC set amount". Currently the spec does it using amount_msat, which is the value from update_add_htlc: in that case, we do need to explicitly allow over-paying.

What you suggest is that we should define the "HTLC set amount" to be the sum of the onion's amt_to_fwd, in which case we don't necessarily need to allow over-paying at this layer.

That would be a bit awkward for the scenario highlighted in #1031 though. Let's imagine that you retry a partial payment of 500 msat and are forced to pay 510 msat to satisfy min_htlc constraints in the route. With your proposal, the sender would be forced to put 500 msat in the onion amt_to_fwd, which means giving the additional fee to intermediate nodes instead of the final node (and feels a bit weird, doesn't it?). Whereas if we allow over-paying the total_amount_msat, the sender can decide whether they want to give that extra fee to intermediate nodes or to the final node.

I understand your point of view, but my current feeling is to go for the extra flexibility of allowing the total_amount_msat over-payment. Let's see what others prefer, I can change that if that's the consensus.

Copy link
Collaborator

@joostjager joostjager Oct 11, 2022

Choose a reason for hiding this comment

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

Let's imagine that you retry a partial payment of 500 msat and are forced to pay 510 msat to satisfy min_htlc constraints in the route. With your proposal, the sender would be forced to put 500 msat in the onion amt_to_fwd, which means giving the additional fee to intermediate nodes instead of the final node.

I don't think you are giving it to the intermediate node straight away? The instruction is still to forward 510 sat to the final node. Only if the intermediate node would first try to pay lower amounts, they could steal some of those sats.

Also I think this only applies to the final shard. All the other shards would just put 510 msat in the onion amt_to_fwd? It satisfies min_htlc and makes sure that a maximum of sats is delivered to the final destination. The overshoot can be compensated in the final shard to end up with the exact pre-determined total_msat.

What remains then is a sender being forced to overpay on the final shard because of a min_htlc constraint and the possibility of a routing node trying to steal the difference. It wouldn't increase the total cost of the payment though, because all prior shards were already 'optimal' in that they don't allow any fee stealing. The difference is just in where that final overpayment ends up IF the routing node decides to probe.

Even if we fix this for the final hop, it is still possible to shift the fee distribution for the intermediate hops.

If senders would always assume that overpayment of total_msat is acceptable, there wouldn't be a way anymore to reject such payments. You can fail the htlc set if it overpays, but the sender wouldn't know what's wrong. As mentioned before, overpayment isn't always desirable and can complicate accounting.

Copy link
Collaborator Author

@t-bast t-bast Oct 13, 2022

Choose a reason for hiding this comment

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

if we would let the per-hop payload hmac also cover the required incoming htlc amount

That would just be an alternative from adding a required_incoming_htlc_amt field, right? But that removes all possibility of giving some leeway for intermediate nodes to decide to forward more to pay an inbound fee for example.

Also, this would require a feature bit to signal the change in the sphinx mac logic and would require the whole path to support it, which is annoying.

Copy link
Collaborator

@joostjager joostjager Oct 13, 2022

Choose a reason for hiding this comment

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

Yes, a zero bytes alternative indeed for required_incoming_htlc_amt. Why would an intermediate node need leeway to pay for example an inbound fee? I'd think this is all decided upfront by the sender. It's the leeway that we don't want because it allows the probing. So if there is leeway, it is bad?

The feature bit is required indeed, but does the whole path need to support this? I thought the sender can calculate the hmac for each hop and cover the required incoming amt under the hmac only if the feature bit is present.

Copy link
Contributor

Choose a reason for hiding this comment

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

Starting to magically include more data that's not be present in the HMAC sounds like something that would make similar changes harder later.

What's bad about just adding an extra (optional) field require_incoming_amount and as more nodes start checking this, probing gets harder and harder. I still thinks this is the cleanest way of ensuring intermediate nodes can't do anything other than what the sender tells them to, which sounds like it would fix most of these probing vectors (maybe even some we haven't thought of).

Should also be totally backwards compatible IIUC.

I agree with t-bast that this would give intermediate nodes less leeway to overpay (for some reason), but as Joost is saying, shouldn't senders really be in charge of deciding that?

Copy link
Collaborator Author

@t-bast t-bast Oct 16, 2022

Choose a reason for hiding this comment

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

Why would an intermediate node need leeway to pay for example an inbound fee? I'd think this is all decided upfront by the sender.

I thought it could be interesting to let nodes only tell their own peers about inbound fees, so that the sender doesn't have to know about it. But that's a separate topic, so maybe we should discuss it somewhere else or later.

Starting to magically include more data that's not be present in the HMAC sounds like something that would make similar changes harder later.

Agreed.

I agree with t-bast that this would give intermediate nodes less leeway to overpay (for some reason), but as Joost is saying, shouldn't senders really be in charge of deciding that?

I think that whatever senders do, if it doesn't align with the right incentives, it will eventually be ignored: that's why I think the required_incoming_htlc_amt would not be useful in practice. As @thomash-acinq correctly points out, as an intermediate node or a final node, if I receive more money than expected, I will take it, regardless of what is inside the onion. If I receive less money than I expect, I will reject it. Anything else doesn't really matter to my decision to accept or reject an HTLC. The current spec correctly does that for intermediate nodes, but incorrectly restricts final nodes, which doesn't align with their incentives: that's what this PR is fixing (and it incidentally fixes probing vectors, except in rare scenarios of recently updated fees as @joostjager pointed out).

Copy link
Collaborator

Choose a reason for hiding this comment

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

as an intermediate node or a final node, if I receive more money than expected, I will take it

Another way to look at this is that failing could lead to more money on the longer term. By failing you're teaching your predecessor that there is no point in trying to steal anything and next time they might just pay the instructed (higher) amount?

- if it is the final node:
- MUST treat `total_msat` as if it were equal to `amt_to_forward` if it
is not present.
- MUST return an error if:
- incoming `amount_msat` != `amt_to_forward`.
- incoming `cltv_expiry` != `cltv_expiry_delta`.
- incoming `amount_msat` < `amt_to_forward`.
Copy link
Contributor

Choose a reason for hiding this comment

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

If we relax this field we must also start counting payment_amt = SUM(htlcs.amt_to_forward) at the final node. If we don't, a second-to-last node can overpay the htlc amount and mess with the tracking of MPP shards (that would lead to a premature settle of the HTLC set).

This would become even worse with the relaxation of the total_msat check, as it would get very easy to cause such a premature settle for a malicious node.

I think the ultimate solution would be what is mentioned in https://github.com/lightning/bolts/pull/1032/files#r993085640, adding arequired_incoming_htlc_amt field. This would restrict all sorts of funny business routing nodes can do to the HTLC amt they send to the next hop, and would require them to always forward exactly amt_to_fwd. To me that sounds like it would solve all the probing issues also, as there would not be possible for a routing node to under- nor overpay, regardless of the next hop is the final or not.

Copy link
Collaborator Author

@t-bast t-bast Oct 13, 2022

Choose a reason for hiding this comment

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

I think you're over-complicating this. From the recipient's point of view, they should be happy as soon as they receive at least total_amount_msat: they've been paid what they wanted and should release the preimage. From the sender's point of view, they should be happy as soon as they receive the preimage while paying at most total_amount_msat.

If intermediate nodes overpay an HTLC, neither the sender not the recipient are negatively impacted. The sender may even get the preimage for less than what they intended to pay: this is great, why would they prevent that?

as there would not be possible for a routing node to under- nor overpay

It should never be possible for an intermediate node to underpay, I agree with that. However, I don't see how overpaying can be an issue, and it's actually necessary to allow overpaying if you want to enable inbound fees?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with the theory, but are implementations properly doing this in practice? I guess it should be handled anyway, since there are never any guarantees all shards will be settled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's at least how eclair handles it ;)

@thomash-acinq
Copy link

The objections to this change seem to only consider the privacy aspect: preventing a malicious node from learning more information than it should, assuming all other nodes follow the spec. However for this to work, we need all other nodes to follow the spec, and for that we need to make sure that incentives are aligned. Currently, the spec asks us to refuse free money to protect the privacy of others. Any economically rational actor should take the free money as there is no downside for them, the downside is only for the privacy of the others.
This changes makes it so that economically rational actors would now follow the spec, which is in itself a sufficient reason to accept it. In addition, it also improves privacy, but only because now everyone is incentivized to actually follow the spec.

04-onion-routing.md Outdated Show resolved Hide resolved
t-bast added a commit that referenced this pull request Oct 20, 2022
Note that this depends on #1032 which clarifies the final node requirements
for non-blinded payments.
When nodes receive HTLCs, they verify that the contents of those HTLCs
match the intructions that the sender provided in the onion. It is
important to ensure that intermediate nodes and final nodes have similar
requirements, otherwise a malicious intermediate node could easily probe
whether the next node is the final recipient or not.

Unfortunately, the requirements for intermediate nodes were more lenient
than the requirements for final nodes. Intermediate nodes allowed overpaying
and increasing the CLTV expiry, whereas final nodes required a perfect
equality between the HTLC values and the onion values.

This provided a trivial way of probing: when relaying an HTLC, nodes could
relay 1 msat more than what the onion instructed (or increase the outgoing
expiry by 1). If the next node was an intermediate node, they would accept
this HTLC, but if the next node was the recipient, they would reject it.

We update those requirements to fix this probing attack vector.
This is actually a cltv_expiry_delta, not an absolute cltv_expiry, so the
field name should reflect that.

Recipients require incoming HTLC expiry to comply with that expiry delta.
@t-bast
Copy link
Collaborator Author

t-bast commented Oct 24, 2022

Rebased on master.

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.

ACK 391ed84

t-bast added a commit to ACINQ/lightning-kmp that referenced this pull request Oct 25, 2022
Recipients must allow HTLC values greater than what the onion payload
contains, otherwise they can be trivially identified as being the
final recipient.

See lightning/bolts#1032 for more details.
t-bast added a commit to ACINQ/eclair that referenced this pull request Oct 25, 2022
When nodes receive HTLCs, they verify that the contents of those HTLCs
match the intructions that the sender provided in the onion. It is
important to ensure that intermediate nodes and final nodes have similar
requirements, otherwise a malicious intermediate node could easily probe
whether the next node is the final recipient or not.

Unfortunately, the requirements for intermediate nodes were more lenient
than the requirements for final nodes. Intermediate nodes allowed overpaying
and increasing the CLTV expiry, whereas final nodes required a perfect
equality between the HTLC values and the onion values.

This provided a trivial way of probing: when relaying an HTLC, nodes could
relay 1 msat more than what the onion instructed (or increase the outgoing
expiry by 1). If the next node was an intermediate node, they would accept
this HTLC, but if the next node was the recipient, they would reject it.

We update those requirements to fix this probing attack vector.

See lightning/bolts#1032
@joostjager
Copy link
Collaborator

joostjager commented Oct 25, 2022

I am not so sure about this change anymore, #1031 (comment)

I know the spec already allows overpayment, but it's undeniable that in the traditional world overpayments can lead to problems. Companies not knowing what to do with the excess, requiring refunds, perhaps tax implications even, etc.

@t-bast
Copy link
Collaborator Author

t-bast commented Oct 26, 2022

I am not so sure about this change anymore, #1031 (comment)

As was pointed out in response to your comment, I believe this isn't a valid reason to reject the PR as we already support over-payment.

We can add a recipient feature to require strict payment amounts (at the expense of privacy) in future work, but that shouldn't have any impact on acceptance of this PR.

@joostjager
Copy link
Collaborator

joostjager commented Oct 26, 2022

We can add a recipient feature to require strict payment amounts (at the expense of privacy) in future work, but that shouldn't have any impact on acceptance of this PR.

I am wondering if we can have strict payment amounts without sacrificing privacy.

I think we can have that if we'd go the other previously discussed way and instead of relaxing the final amt_to_fwd requirement (this pr), we'd tighten acceptance for routing nodes via a new onion field or inclusion of the amount in the hmac'ed data.

The downside of that is, and we also discussed this before, is that an economically rational routing node would ignore the accept instruction in the onion and maximize profit without caring about privacy, which brings us back to the current situation.

So yes, maybe the outcome is that there is no other option than relaxing indeed. Unless anyone has a new creative idea?

@t-bast
Copy link
Collaborator Author

t-bast commented Oct 26, 2022

I think that strict payments should not be the default as they're not what an economically rational actor would want. But there's room for making this an opt-in feature, signaled in the invoice, most likely at the expense of privacy, and maybe this is a trade-off that some nodes are ok with?

Anyway implementing strict payments is a separate proposal, and as you say hopefully by the time we really commit to implementing it we'll have found ways of doing it that doesn't sacrifice too much privacy.

@TheBlueMatt
Copy link
Collaborator

If our understanding is that we cannot easily get route-privacy with strict payments then I don't think we should ever add strict payments. The recipient deciding that the sender is not allowed to have privacy is not a good tradeoff, IMO.

t-bast added a commit to ACINQ/eclair that referenced this pull request Nov 4, 2022
When nodes receive HTLCs, they verify that the contents of those HTLCs
match the intructions that the sender provided in the onion. It is
important to ensure that intermediate nodes and final nodes have similar
requirements, otherwise a malicious intermediate node could easily probe
whether the next node is the final recipient or not.

Unfortunately, the requirements for intermediate nodes were more lenient
than the requirements for final nodes. Intermediate nodes allowed overpaying
and increasing the CLTV expiry, whereas final nodes required a perfect
equality between the HTLC values and the onion values.

This provided a trivial way of probing: when relaying an HTLC, nodes could
relay 1 msat more than what the onion instructed (or increase the outgoing
expiry by 1). If the next node was an intermediate node, they would accept
this HTLC, but if the next node was the recipient, they would reject it.

We update those requirements to fix this probing attack vector.

See lightning/bolts#1032
t-bast added a commit to ACINQ/lightning-kmp that referenced this pull request Nov 7, 2022
Recipients must allow HTLC values greater than what the onion payload
contains, otherwise they can be trivially identified as being the
final recipient.

See lightning/bolts#1032 for more details.
@rustyrussell
Copy link
Collaborator

Yes. Consistency FTW.

Ack.

Copy link
Collaborator

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

@t-bast t-bast merged commit b38156b into lightning:master Nov 8, 2022
@t-bast t-bast deleted the recipient-overshoot branch November 8, 2022 07:38
t-bast added a commit to t-bast/bolts that referenced this pull request Nov 18, 2022
These requirements were missed when integrating lightning#1032
t-bast added a commit that referenced this pull request Nov 22, 2022
* Use onion amount in MPP set calculation

The sender chooses the amounts that are set in the onion payload
(`amt_to_forward`) but cannot predict what amounts will be set in the
HTLCs (`amount_msat`) since intermediate nodes are allowed to overpay.

* Fix error requirements for final node

These requirements were missed when integrating #1032
t-bast added a commit that referenced this pull request Jan 9, 2023
Note that this depends on #1032 which clarifies the final node requirements
for non-blinded payments.
TheBlueMatt added a commit to TheBlueMatt/lightning-rfc that referenced this pull request Mar 28, 2023
In lightning#1032 we allowed overshooting the final amount and expiry, but
forgot to update the onion error descriptions which make reference
thereto.
t-bast pushed a commit that referenced this pull request Apr 11, 2023
In #1032 we allowed overshooting the final amount and expiry, but
forgot to update the onion error descriptions which make reference
thereto.
t-bast added a commit to ACINQ/lightning-kmp that referenced this pull request Feb 7, 2024
Recipients must allow HTLC values greater than what the onion payload
contains, otherwise they can be trivially identified as being the
final recipient.

See lightning/bolts#1032 for more details.
t-bast added a commit to ACINQ/lightning-kmp that referenced this pull request Feb 8, 2024
Recipients must allow HTLC values greater than what the onion payload
contains, otherwise they can be trivially identified as being the
final recipient.

See lightning/bolts#1032 for more details.
t-bast added a commit to ACINQ/lightning-kmp that referenced this pull request Feb 8, 2024
Recipients must allow HTLC values greater than what the onion payload
contains, otherwise they can be trivially identified as being the
final recipient.

See lightning/bolts#1032 for more details.
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