blips icon indicating copy to clipboard operation
blips copied to clipboard

Inbound routing fees

Open joostjager opened this issue 2 years ago • 47 comments

The Lightning BOLTs define a routing fee that is based on the policy of the outgoing channel. This makes it impossible for routing node operators to differentiate in fees between incoming channels.

Incoming traffic from some peers may be more desired than from others, for example because it balances out traffic in the opposite direction.

This bLIP defines an optional channel_update message field to express an inbound fee discount. Senders who are able to read this field can benefit from lower routing fees using those channels.

Alternative: pairwise fees

An alternative scheme for inbound fees are the so-called pairwise fees, where a distinct fee can be set for each combination of incoming and outgoing channel. This can also be implemented as a discount to remain backwards compatible.

For this BLIP, I chose to start simple with a plain single inbound fee. A single fee only requires trivial changes to pathfinding algorithms and there is no potential for quadratic growth of gossip data.

If pairwise fees prove to be required, an additional BLIP can be defined. It can again be formulated as another discount on the general inbound discount, to keep all options open for implementers.

Additional information

  • https://github.com/lightning/bolts/issues/835
  • https://github.com/lightningnetwork/lnd/pull/6703
  • https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-July/003643.html

joostjager avatar Aug 29 '22 14:08 joostjager

IMO this should not go in the channel_update message, but rather a new point-to-point message to tell the counterparty to decrease their outbound fee by this much. That way you don't have (as much of a) chicken and eh problem when routing, but rather can let individual peer pairs upgrade and then routing nodes will all get the discount.

TheBlueMatt avatar Aug 29 '22 14:08 TheBlueMatt

I don't see how that achieves the same goal. I am looking to make senders aware of the discount so that they can choose routes that are better for them. How will a sender become aware of that new p2p message if it is only exchanged between two remote nodes?

joostjager avatar Aug 29 '22 14:08 joostjager

A channel participant can inform their counterparty that they wish to take a negative inbound fee for the channel, then their counterparty can subtract the given inbound fee from their announced outbound fees, which all existing nodes will honor. That way no one except the two channel participants needs to update any software at all. As-is, I think its a bit of a stretch to suggest this has no broad-upgrade implication.

TheBlueMatt avatar Aug 29 '22 15:08 TheBlueMatt

Interesting idea. I suppose it's up to your counterparty to actually lower their outbound fee and not something that can be enforced. Which simplifies the implementation.

What's left is just the ability to tell your peer to send you fewer sats.

It may be a problem for final nodes though that also route. If they are accepting a payment as the final destination, they'll also receive less and this can't be offset by an outgoing fee. The counterparty doesn't know which position in the route they are.

joostjager avatar Aug 29 '22 15:08 joostjager

I suppose it's up to your counterparty to actually lower their outbound fee and not something that can be enforced.

Sure, they could also increase their fee corresponding to your negative fees in the current implementation too :).

It may be a problem for final nodes though that also route. If they are accepting a payment as the final destination, they'll also receive less and this can't be offset by an outgoing fee.

Hmm, yea, neither the existing nor the new proposal really properly lets you assign positive fees on inbound edges (by offsetting negative fees with outbound fee increases) for this reason, I suppose. I guess the "easy" way to fix that is to use something like phantom nodes and assign the fee offset to the fake hop.

TheBlueMatt avatar Aug 29 '22 15:08 TheBlueMatt

Well in the existing proposal, the sender is aware of the final hop. This BLIP says that no inbound fee needs to be paid to the final node, and the sender can take that into account when building the route.

Yes, a fake node can be used to let the payer pay for the discount they got :)

With the new p2p proposal, it may not be necessary anymore to do negative fees. That was just for backwards compatibility. I think it can be reformulated to positive fees. Basically forcing your peer to share part of their outbound fee with you.

For the final node, this would be a little better I guess because at least your getting the invoice amount.

joostjager avatar Aug 29 '22 16:08 joostjager

With the new p2p proposal, it may not be necessary anymore to do negative fees. That was just for backwards compatibility. I think it can be reformulated to positive fees. Basically forcing your peer to share part of their outbound fee with you.

That's true, but it complicates the backwards-compatibility story - why would I ever deploy a change that results in my counterparty increasing the fee on a channel of mine, that just results in less routing revenue for me. Sure, a node may prefer channels with peers that support the new option, but I'm not sure if anyone will prefer that so strongly that they'll reject other new inbound channels.

TheBlueMatt avatar Aug 29 '22 17:08 TheBlueMatt

I think that not having the ability to charge inbound fees is at this moment already a strong reason for node operators to reject / close certain inbound channels.

I can see this option becoming a requirement for those nodes. The win is that they can keep channels open that they would otherwise close.

joostjager avatar Aug 29 '22 18:08 joostjager

If you assume very broad deployment of that feature, then yes, I think I'd agree, but in that case I don't think the feature is really a decent candidate for being a bLIP, but rather needs to be a BOLT change :p.

TheBlueMatt avatar Aug 29 '22 19:08 TheBlueMatt

Need to think about what to do with the insufficient fee failure message. Maybe extend with the channel update message for the incoming channel?

joostjager avatar Aug 29 '22 21:08 joostjager

I don't think anything needs to change? The first node compares the fee paid according to the onion to what they announced (including the discount). The discount only applies at the update_add_htlc layer, so no changes needed.

TheBlueMatt avatar Aug 29 '22 22:08 TheBlueMatt

Ah, this was a comment about the channel_update extension proposal. In the p2p solution, nothing like that is needed indeed.

One thing that worries me about the p2p proposal is that it probably requires changes to the channel state machine so that it will be clear from what point onwards exactly the new fee applies.

joostjager avatar Aug 30 '22 06:08 joostjager

I'm not sure how this differs - either way you have a new argument to your logic that checks an HTLC pays the appropriate fees. There is a slight difference on your peer's side in that they have to subtract the fee amount rather than seeing it in the onion, but that's really rather trivial as well.

I'm kinda unsure why we're arguing so much about this one - one proposal requires network-wide upgrade in order for your fee to be considered (the ultimate goal of anyone providing a fee discount), one does not. Given there was some debate about this being not-widely-deployed that seems like a rather massive advantage.

TheBlueMatt avatar Aug 30 '22 15:08 TheBlueMatt

I did a PoC for the p2p inbound fee: https://github.com/lightningnetwork/lnd/pull/6878

Overall a fairly straight-forward change. A real functional difference only seems to exist for nodes that are both routing nodes and a payment destination.

A fake hop was suggested in https://github.com/lightning/blips/pull/18#issuecomment-1230505010, but this does add complexity to the setup and it won't be possible to cancel out the discount exactly.

joostjager avatar Sep 01 '22 12:09 joostjager

One downside of the p2p approach is that if the incoming node already charges zero fees, they can't lower their fee any further. So giving them a discount isn't going to make a difference. If there are multiple zero or low fee peers, it won't be possible to have distinct inbound fee schedules for these peers.

joostjager avatar Sep 01 '22 17:09 joostjager

Right, but in those same cases routing algorithms would need to be modified to support negative fees, which AFAIK no existing node supports. I think negative fees are somewhat of an orthogonal discussion.

TheBlueMatt avatar Sep 02 '22 20:09 TheBlueMatt

Yes, that's true. Over time, when senders are mostly upgraded, you could switch to positive inbound fees to get around the pathfinding limitation.

Although for p2p this may be uncomfortable for peers, because they need to monitor the inbound fee closely and adjust their fees accordingly to make sure they charge as much as they need to pay p2p to the next node and keep it net zero.

joostjager avatar Sep 02 '22 22:09 joostjager

Although for p2p this may be uncomfortable for peers, because they need to monitor the inbound fee closely and adjust their fees accordingly to make sure they charge as much as they need to pay p2p to the next node and keep it net zero.

I'm not sure why this is different from today, really? Peers have to watch for channel updates from their peers and track updated routing fees, now they just also have to broadcast their own updates in response to a new message that's similar in nature.

TheBlueMatt avatar Sep 04 '22 16:09 TheBlueMatt

I think this is a great proposal. I love the fact that by charging negative inbound fees (and potentially raising the outbound fees) this becomes a backward compatible change.

There's two suggestions I would like to make.

  1. Do not allow destination nodes to charge inbound fees (they could charge more for the service they provide instead)
  2. More intuitive fee calculation

I think both above points can be addressed by changing the way the total routing fee is calculated.

outbound_fee(amt_to_forward) = inbound_fee.base_msat + outbound_fee.base_msat + (inbound_fee.proportional_millionths + outbound_fee.proportional_millionths) * amt_to_forward / 1e6

Let me give an example. A routing node X charges 100ppm inbound fees from A and 200ppm outbound fees to B. Say X forwards 1,000,000 sats to B. In the proposal in this PR, the fee X gets is calculated as follows:

outbound fee = 1,000,000 * 200ppm = 200 sat
inbound fee = (1,000,000 + 200 sat outbound fee) * 100 ppm = 100.02 sat
total fee = 300.02 sat

The effective ppm is 300.02 ppm. Intuitively though, if a routing node charges 100ppm + 200ppm routing fees you'd expect the fee to be 300ppm.

So I'd suggest calculating the fees over the outgoing amount only. By adding the the inbound + outbound fee policies together and applying that to the outgoing amount:

total fee = 1,000,000 * (100ppm inbound fee + 200ppm outbound fee) = 300 sat

An advantage of this technique is it also addresses point 1). Because since fees are calculated over the outgoing amount. This way routing nodes charge fees for forwards. But a destination node does not charge a fee for receiving a payment. This way you're effectively not charging an inbound fee, but raising/decreasing the outbound fee based on where the payment is coming from. I believe that's more suited to the problem we're trying to solve.

JssDWt avatar Sep 08 '22 09:09 JssDWt

I like the idea of the more intuitive fee calculation. Because we're talking about fees paid over fees, the difference is small. But it does simplify things on the implementation side. In particular the complicated inverse calculation that can now be dropped. Tested it, and it works without any unexpected challenges in pathfinding.

The other suggestion to not allow payment destinations to charge inbound fees is I think independent of this. For both ways of calculating, we can choose whether to make the final hop inbound-fee-free or not.

Charging inbound fees as a destination is already possible today by setting up a gateway node in front of the final destination. So in that regard there is not much fundamental change.

Perhaps this decision is better informed by looking at routing nodes that are also a payment destination. Suppose they charge inbound fees on some channels for forwards. Would it be desirable for them to charge the same fees if they receive a payment? I'd think that it is beneficial to be able to apply the same traffic shaping as for forwarded traffic, but interested to hear other's opinions.

joostjager avatar Sep 09 '22 13:09 joostjager

So I'd suggest calculating the fees over the outgoing amount only. By adding the the inbound + outbound fee policies together and applying that to the outgoing amount:

total fee = 1,000,000 * (100ppm inbound fee + 200ppm outbound fee) = 300 sat

It's important to note that the policies first need to be added together. If applied separately, rounding differences may occur.

Many route building algorithms work "node by node". For implementation it may be easier to specify the total fee required as inbound_fee + outbound_fee, rounding each component separately.

Another decision to make it is how to round negative fees. A normal integer division rounds towards zero, meaning that positive fees are rounded down and negative fees rounded up. Not sure if this is the most intuitive for negative fees.

Perhaps for all of this, the leading principle should be that senders may always round in their favor.

joostjager avatar Sep 13 '22 08:09 joostjager

~@JssDWt one potential downside of calculation inbound fees based on the downstream outgoing amount is that it may be difficult to use bolt11 route hints as is.~

~If the inbound fee was calculated over the upstream incoming amount, it may have been possible to fold the outbound and inbound fee for a particular channel into a single base fee and fee rate and communicate that to the sender. Not sure though if this is really possible.~

Actually I think it isn't possible to do that.

joostjager avatar Sep 21 '22 10:09 joostjager

@bitromortac suggested a third way to calculate the inbound fees: to base them on the outgoing htlc amount plus the outbound fee.

Example: inbound fee = 1 + 10%, outbound fee = 7 + 3%, outgoing htlc amount = 100

--- 122 ---> (in fee: 12) NODE (out fee: 10) --- 100 --->

In this case, the amount that the inbound fee is based on is 100+10=110. The inbound fee is 1+10%(110)=12.

I've created a Google Sheet that shows the three fee models side-by-side.

The advantage of this latest variation is that:

  • No awkward inverse fee calculation required
  • Fits better with pathfinding algorithms because the outgoing htlc amount doesn't need to be kept available.

joostjager avatar Sep 28 '22 20:09 joostjager

Updated PR to reflect newest fee calculation described above.

joostjager avatar Oct 04 '22 12:10 joostjager

Regarding charging fees for incoming payments: It seems like this is desirable to enable. If I'm a merchant, I could end up with some incoming channels being used more than others (perhaps because they are towards a well-connected routing node), so it would be nice to incentivise senders to use different channels by giving them a rebate.

halseth avatar Oct 06 '22 10:10 halseth

I am not so sure anymore about charging inbound fees for exit hops. Some nodes may not want to do that because it can be perceived as a hidden charge to the payer. If they decide to not set inbound fees, they also can't use the feature to shape forwarding traffic.

It seems most flexible to not charge inbound fees for exit hops. If they then still want it, they can set up a secondary node for payments only or use a virtual channel for example via lnmux.

Thoughts? Wisdom of the crowd: https://twitter.com/joostjgr/status/1582795532115664897

joostjager avatar Oct 19 '22 18:10 joostjager

Its kinda awkward, but the poll indicates people are split, so I'm not sure there's a "right" answer here - that said, keeping it backwards-compatible (and allowing both positive and negative fees, rather than only negative) seems like a huge win. I'm not sure why it made sense to move away from the "tell your peer to charge more" design - designing a protocol just to make lnd implementation details happy seems like a really strange way to go about it :(.

TheBlueMatt avatar Oct 24 '22 21:10 TheBlueMatt

The main reason for dropping exit hops fees is that routing nodes that use inbound fees for forwards, can't turn these off if they are a payment destination. And exit hops fees can also be implemented using a virtual/phantom/lnmux channel.

I don't think this is compatible with "tell your peer to charge more", because they'd learn whether they are the second to last node.

joostjager avatar Oct 25 '22 07:10 joostjager

For totally flexibility with regards to the exit hop fees, we can do a follow-up with a new optional channel_update tlv record that defines a base fee and rate specifically for senders to pay to the last hop.

joostjager avatar Oct 26 '22 14:10 joostjager

I don't buy that that is a sufficient reason to complicate the network more for normal nodes, nor that its worth the restriction that the inbound fees are no longer allowed to be positive. While explicit negative inbound fees are cool, (a) the time required for people to deploy them is annoying, and (b) they are much more limited in use - if you have very, very low (or zero) outbound fees, then you cannot use inbound fees at all (unless you want to pay people to route through you). This is sufficiently limiting that I'm not sure its worth it. If we go the "counterparty adds fee" route we don't have that limitation, and just end up with recipients charging fees. That's a bit awkward, sure, but recipients can already charge fees if they want to, so its not like its a new issue?

TheBlueMatt avatar Oct 29 '22 18:10 TheBlueMatt