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

Add a bLIP for backwards-compatible inbound fees #22

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

TheBlueMatt
Copy link
Contributor

@TheBlueMatt TheBlueMatt commented Jan 9, 2023

This provides an alternative to #18. In comparison, it:

  • is backwards compatible in that average nodes on the network do not have to take any action in order to apply the fee changes,
  • allows for positive inbound fees,
  • requires substantially less total code changed (at least in LDK) as it doesn't touch gossip or path-finding,
  • avoids the quagmire/incentive of nodes to rapidly update channel_update messages due to negative fees being a function of channel balance by clamping the fee to zero at the protocol level.

@renepickhardt
Copy link

Disclaimer: I might cross post this to the mailinglist.

Let's say Alice charges 200 ppm on the channel with Bob and Bob wishes to request an inbound fee of 100. It is my understanding that upon receiving inbound_fees_update Alice is supposed to send out a channel_update with a ppm of 300. In particular the network will understand this channel to be of 300ppm.

During fee computation in forwarding Alice would only reduce the amt_to_forward only by a ppm 200 such that Bob could keep the remaining 100 ppm.

Here my questions:

  • Assuming the total routing fee on the channel is 300 ppm (as stated above). Why should Bob only get 100ppm and Alice 200ppm?
  • What prevents Bob from asking Alice for an inbound fee of 300ppm and forcing her to charge 0ppm?
  • Vice versa: What prevents Alice from increasing her fees afterwards to 400 and waiting for Bob now to reduce his inbound fees again resulting in 0 ppm inbound fees and 300 ppm outbound fees.
  • How do the two very greedy and selfish actors Alice and Bob find an agreement how to share the revenue.

What I am trying to say. Just having one node initially suggest a price for routing and heaving the channel partner make a counter suggestion by either adding to or subtracting from the amount and then keeping or paying the difference seems extremely unstable.

While I very much like from this proposal that this conflict may not be directly visible to the remainder of the network I think we need some form of protocol for Alice and Bob to decide how to split the routing fees on the channel (similar to how we do onchain fee negotaion in mutual closes) and define a point after which Alice or Bob would fail the channel because they can't find agreement.

Luckily various dialects of this problem have been studied in game theory. E.G:

There for some similar dialect of the problem it seems that the nash equlibrium is to split the revenue in a 50/50 way. Thus if we don't know any better it seems very reasonable to have a round based communication protocol (similar to channel closing) by which Alice and Bob negotiate the total routing fees which then would be split 50/50 betweetn them.

btw: As mentioned of Twitter I have previously informed the Lightning Labs team about the fact that the question how to split the routing revenue between the partners needs to be addressed in any form of inbound fees as we would otherwise create instability in the network.

Also let me be clear. To the best of my knowledge price signals (as routing fees are) are known to be a poor mechanism for flow control in fluid networks (like the electric grid or gas / water networks) as they usually do not provide stability. Valves that I have previously suggested to use tend to work much better for flow and congestion control. However also the valves approach would benefit form a communication protocol for the peers to agree on how much to open the valve in which direction. Similar to the negotiation of the routing fee one could have a more general communication protocol to negotiate channel meta data like fees / valves and other features.

Conclusion: Maybe the current discussion and disagreement could be resolved by thinking about channel meta data negotiation protocols (which btw could also include some form of information sharing as proposed in lightning/bolts#780 ) and could be used even during channel opening negotiation.

@TheBlueMatt
Copy link
Contributor Author

Assuming the total routing fee on the channel is 300 ppm (as stated above). Why should Bob only get 100ppm and Alice 200ppm?

The HTLC will fail.

How do the two very greedy and selfish actors Alice and Bob find an agreement how to share the revenue.

You're assuming they have a target total fee for the channel. I don't think that's a given - rather, a node may wish to receive X for flows inbound over a channel, irrespective of their peer's fee. We could also take your questions further - you could make a similar argument between a peer and the average (or min/max or whatever) fees its peer charges for outbound forwards. Nodes are in effect "splitting the revenue" of an HTLC through the full path, not just on one single hop.

@TheBlueMatt
Copy link
Contributor Author

More generally, however, see the motivation section of the bLIP here - even if we assume routing nodes don't want this, nodes may want it on private channels for LSP clients.

blip-0019.md Outdated Show resolved Hide resolved
Copy link
Contributor

@t-bast t-bast left a comment

Choose a reason for hiding this comment

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

While I'm still not sure whether inbound fees make sense or not (and how much instability they'd create), I like this proposal better than #18 and it's really trivial to implement, which is a good argument at least for a prototyping phase.

blip-0019.md Outdated Show resolved Hide resolved
@cdecker
Copy link

cdecker commented Jan 10, 2023

I share the concern of others wondering if inbound fees actually solve a problem, and don't just cater to the wishes to have more flexibility for the sake of it, but this proposal is definitely better and less infectious than #18. I'm still pretty convinced that the scenarios used to show the necessity if inbound fees can be solved by emulating them with existing parameters too, or perceived issues in the scenarios are not real (net income is likely unaffected by a node leeching all the outbound balance through a direct peering).

blip-0019.md Outdated Show resolved Hide resolved
blip-0019.md Show resolved Hide resolved
blip-0019.md Outdated Show resolved Hide resolved
blip-0019.md Outdated

## Universality

This bLIP describes a mechanism for inbound fees which is completely transparent to the broader lightning network. Only the two nodes involved in the channel are aware of the inbound fees. More importantly, due to the above concerns expressed over the general applicability of inbound routing fees, it is anticipated that not all lightning implementations will support this. Users wishing to charge inbound fees may seek out peers which support it, and LSP clients may wish to use this when working with an LSP to receive a fee discount.

Choose a reason for hiding this comment

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

This means that a routing node can only charge an inbound fee if its peer cooperates and may cause existing peering relationships to change. Especially if a node wants to charge inbound fees across the board in order to reduce outbound fees, all of its peers need to support this.

With #18, you can charge inbound fees as you like independent of your peers. Of course the limitation there is that senders need to support it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not really sure I get this - a user may want to charge an inbound fee today, but they don't. Sure, once you have an implementation you may also ask your peer to support it, but it doesn't mean your relationship changes with your peer, unless of course they also support this feature. You may choose to prefer peers which do support this feature, sure, but isn't that true for any feature? Even for #18 you may prefer to use peers that use negative fees to give a discount in line with the second suggested motivation here.

Choose a reason for hiding this comment

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

I'm just saying that there is a difference between requiring your peer to support a feature vs requiring the sender of a payment to support a feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, one is local and one is global :)

* MUST increase the amount forwarded in an HTLC by the advertised inbound fees,
* however SHOULD delay increasing the amount forwarded by an updated inbound
fee until it has a chance to broadcast a new `channel_update` after
rate-limits.

Choose a reason for hiding this comment

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

How can the node know what the rate limits are? They may be applied anywhere on the network. Or just a grace period?

Then if there are still senders who haven't received the update after the grace period, the node will start returning fee_insufficient and still see its reputation damaged because of its peer inbound fee update? Not sure if the incentives are properly aligned here.

Does the delaying add complexity in the implementation because you need to keep track of history - potentially across restarts?

The situation that I described in the comment would be useful to add here too so that implementers understand why this is important. In the BOLTS I find that there isn't always enough rationale recorded, and it gets forgotten.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How can the node know what the rate limits are? They may be applied anywhere on the network. Or just a grace period?

That is up to individual nodes. Its deliberately phrased in terms of channel_update to indicate to nodes that they should use their existing rate-limiting logic they already have in place.

Then if there are still senders who haven't received the update after the grace period, the node will start returning fee_insufficient and still see its reputation damaged because of its peer inbound fee update? Not sure if the incentives are properly aligned here.

Hmm? No, this is not the case, the downstream node now is the one that gets to send the failure message (as its the one that is spamming updates) the upstream node forwarded the HTLC with stale parameters so that it wasnt blamed.

Does the delaying add complexity in the implementation because you need to keep track of history - potentially across restarts?

No more than existing delays in channel_update, basically. I mostly reused the tracking logic from there.

The situation that I described in the comment would be useful to add here too so that implementers understand why this is important. In the BOLTS I find that there isn't always enough rationale recorded, and it gets forgotten.

Good point, added more details.

Choose a reason for hiding this comment

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

That is up to individual nodes. Its deliberately phrased in terms of channel_update to indicate to nodes that they should use their existing rate-limiting logic they already have in place.

What I mean is that a node's existing rate-limiting logic may not match what other nodes on the network do. The node broadcasts its channel_update, but then it is held back somewhere on the network between this node and a potential sender of a payment. How do you implement this concretely?

Hmm? No, this is not the case, the downstream node now is the one that gets to send the failure message (as its the one that is spamming updates) the upstream node forwarded the HTLC with stale parameters so that it wasnt blamed.

Let's take the example A->B->C->D again. D set an inbound fee, C broadcasted a new channel_update.

Then at some point, C receives an htlc to forward with an insufficient fee. There are two reasons why this could be:

  1. Channel update hasn't reached the sender yet
  2. Node B is not forwarding enough.

Now what is C going to do? Return fee_insufficient and potentially damage its reputation because D wanted to change its inbound fee? Or forward an insufficient amount to D and punish D while B is at fault really.

If no node ever wants to return fee_insufficient, they can just keep forwarding an amount that is too small until the final destination is reached. Seems that things get a bit strange then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What I mean is that a node's existing rate-limiting logic may not match what other nodes on the network do. The node broadcasts its channel_update, but then it is held back somewhere on the network between this node and a potential sender of a payment. How do you implement this concretely?

This is no different from any other fee on the network today. Yes, there's active discussion in setting a more common rate-limit across the network, but in general senders have to be tolerant of this and recipients should expect it sometimes.

Now what is C going to do? Return fee_insufficient and potentially damage its reputation because D wanted to change its inbound fee? Or forward an insufficient amount to D and punish D while B is at fault really.

C can tell the difference, though! If B didn't forward enough to C, C will note that it didn't get enough fee compared to the forwarding instructions in the onion, and will then fail back with fee_insufficient.

blip-0019.md Outdated

## Universality

This bLIP describes a mechanism for inbound fees which is completely transparent to the broader lightning network. Only the two nodes involved in the channel are aware of the inbound fees. More importantly, due to the above concerns expressed over the general applicability of inbound routing fees, it is anticipated that not all lightning implementations will support this. Users wishing to charge inbound fees may seek out peers which support it, and LSP clients may wish to use this when working with an LSP to receive a fee discount.

Choose a reason for hiding this comment

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

I'm just saying that there is a difference between requiring your peer to support a feature vs requiring the sender of a payment to support a feature.

inbound HTLC amount *before* the inbound fees are subtracted. This includes
received payments as well as forwarded ones.
* SHOULD delay enforcing updated inbound fees for some time, congruous with
the delay applied when enforcing new fees after sending a `channel_update`.
Copy link

@joostjager joostjager Jan 11, 2023

Choose a reason for hiding this comment

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

The observed delay is also influenced by the peer that is charging the inbound fee for this node (grace period). If those periods don't line up, this node is going to return fee_insufficient and damage its reputation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, luckily there's a super trivial solution - wait until you also see the channel_update. I mentioned that now.

Choose a reason for hiding this comment

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

I don't think it is that trivial at all. You can wait until you see the channel_update, but that doesn't tell you for how long your peer will underpay you to account for propagation of that update to the sender.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, it is trivial - the forwarder should wait to enforce it until it has a chance to broadcast a new channel_update, at which point it MUST enforce the update. The recipient/inbound-fee-setter should delay enforcement until it sees the channel_update + 10 minutes (which is congruous with what it does for channel_updates normally). That shouldn't have any disagreement. If you prefer we could also apply the 10 minute timer on the forwarder's side, but its not a huge deal to send back the updated channel_update when failing.

@joostjager
Copy link

Another fee model that has been talked about in the past is 'pair-wise' fees. In that model, a routing node can assign a specific fee for each combination of in and out channel. With the gossip approach this is mostly an extension of #18 with a more advanced data format.

Is there any way to do pair-wise fees with the proposal in this PR?

@joostjager
Copy link

The proposal specifies that inbound fees must be paid to the final hop too. If two routing nodes decide to open a direct channel to route traffic with inbound fees (charged to a distant sender), they can no longer use this channel to make 'free' payments between them. For example two custodial wallet nodes that route traffic but also make direct payments to each other. This looks to me like something to not step over too easily.

@TheBlueMatt
Copy link
Contributor Author

Another fee model that has been talked about in the past is 'pair-wise' fees. In that model, a routing node can assign a specific fee for each combination of in and out channel. With the gossip approach this is mostly an extension of #18 with a more advanced data format.

Please for the love of god do not make channel_update messages N^2 in size.

Is there any way to do pair-wise fees with the proposal in this PR?

No, for the same reason as above :)

The proposal specifies that inbound fees must be paid to the final hop too. If two routing nodes decide to open a direct channel to route traffic with inbound fees (charged to a distant sender), they can no longer use this channel to make 'free' payments between them.

This is not a change from today. If a node decides it wants to charge its peer for sending it payments it can do so by creating fake hops in invoices.

@joostjager
Copy link

Please for the love of god do not make channel_update messages N^2 in size.

That's the naive way to do it. But there surely are different options. For example: every channel can be assigned a group (id) that is communicated as part of the channel update. For pair-wise fees, you can then reference a whole group of channels and set a different fee only for that group. Not saying that this is how it should be, but I do value the extensibility that the gossip model offers if a need for this would arise in the future.

This is not a change from today. If a node decides it wants to charge its peer for sending it payments it can do so by creating fake hops in invoices.

I am not saying that they can't do so today. It's the opposite. If the mechanism proposed in this PR would be implemented, then routing nodes that charge inbound fees for traffic that they forward wouldn't be able to do zero-fee payments with direct peers anymore across those channels.

@TheBlueMatt
Copy link
Contributor Author

< Not saying that this is how it should be, but I do value the extensibility that the gossip model offers if a need for this would arise in the future.

If we end up deciding we need that feature, we can implement it any way we want to at that point :) I don't want to implement a router that lets nodes turn themselves into a bunch of virtual nodes in the graph, though :p.

I am not saying that they can't do so today. It's the opposite. If the mechanism proposed in this PR would be implemented, then routing nodes that charge inbound fees for traffic that they forward wouldn't be able to do zero-fee payments with direct peers anymore across those channels.

Sure they can! They can negotiate any additional optional protocol they want :). "If invoice is to me, feel free to pay a fake invoice with funds less by the inbound fee amount", problem solved :). By making this a thing that is negotiated between two peers the two peers can negotiate any additional anything they want.

@cdecker
Copy link

cdecker commented Jan 16, 2023

Listening to the discussion during the call one thing came to mind: we are handing one peer a way to trigger a channel_update on their behalf by modifying our own inbound fee (we update our fee, they just add it to their fee and broadcast a new channel_update). This may run into issues with nodes that throttle updates or have a time-based limit not forwarding any more updates for our peer, and the malicious node has caused the victim to no longer be update that channel going forward, or severely slow down the propagation of those updates.

Then again, they are always free to reject anything that doesn't match their own policy, but that can cause more failed payments (the malicious node is also hurting themself, so 🤷 )

Didn't want to bring this up since the discussion was on a more fundamental level :-)

@TheBlueMatt
Copy link
Contributor Author

Yes, the node asking for a fee update has to wait for their peer to send back (and thus broadcast) an updated channel_update before they can enforce the new fees (as written in the bLIP now). That may effectively cut in half how often you can update the inbound fees vs your outbound fees, but its not a huge change otherwise - as you point out your peer can already do any number of other things to cause HTLCs to not be forwarded through the channel y'all have, there's not much we can do to prevent that :)

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.

6 participants