routing: account for blinded routes in fee calculation

When we introduce blinded routes, some of our hops are expected
to have zero amounts to forward in their hop payload. This commit
updates our hop fee logic to attribute the full blinded route's
fees to the introduction node. We can't actually know where/how
these fees are distributed, so we collect them all at the
introduction node.
This commit is contained in:
Carla Kirk-Cohen 2022-12-20 14:00:27 -05:00 committed by Olaoluwa Osuntokun
parent 940b491051
commit 11a007dc16
2 changed files with 94 additions and 2 deletions

View File

@ -408,6 +408,42 @@ func (r *Route) Copy() *Route {
} }
// HopFee returns the fee charged by the route hop indicated by hopIndex. // HopFee returns the fee charged by the route hop indicated by hopIndex.
//
// This calculation takes into account the possibility that the route contains
// some blinded hops, that will not have the amount to forward set. We take
// note of various points in the blinded route.
//
// Given the following route where Carol is the introduction node and B2 is
// the recipient, Carol and B1's hops will not have an amount to forward set:
// Alice --- Bob ---- Carol (introduction) ----- B1 ----- B2
//
// We locate ourselves in the route as follows:
// * Regular Hop (eg Alice - Bob):
//
// incomingAmt !=0
// outgoingAmt !=0
// -> Fee = incomingAmt - outgoingAmt
//
// * Introduction Hop (eg Bob - Carol):
//
// incomingAmt !=0
// outgoingAmt = 0
// -> Fee = incomingAmt - receiverAmt
//
// This has the impact of attributing the full fees for the blinded route to
// the introduction node.
//
// * Blinded Intermediate Hop (eg Carol - B1):
//
// incomingAmt = 0
// outgoingAmt = 0
// -> Fee = 0
//
// * Final Blinded Hop (B1 - B2):
//
// incomingAmt = 0
// outgoingAmt !=0
// -> Fee = 0
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi { func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
var incomingAmt lnwire.MilliSatoshi var incomingAmt lnwire.MilliSatoshi
if hopIndex == 0 { if hopIndex == 0 {
@ -416,8 +452,25 @@ func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
incomingAmt = r.Hops[hopIndex-1].AmtToForward incomingAmt = r.Hops[hopIndex-1].AmtToForward
} }
// Fee is calculated as difference between incoming and outgoing amount. outgoingAmt := r.Hops[hopIndex].AmtToForward
return incomingAmt - r.Hops[hopIndex].AmtToForward
switch {
// If both incoming and outgoing amounts are set, we're in a normal
// hop
case incomingAmt != 0 && outgoingAmt != 0:
return incomingAmt - outgoingAmt
// If the incoming amount is zero, we're at an intermediate hop in
// a blinded route, so the fee is zero.
case incomingAmt == 0:
return 0
// If we have a non-zero incoming amount and a zero outgoing amount,
// we're at the introduction hop so we express the fees for the full
// blinded route at this hop.
default:
return incomingAmt - r.ReceiverAmt()
}
} }
// TotalFees is the sum of the fees paid at each hop within the final route. In // TotalFees is the sum of the fees paid at each hop within the final route. In

View File

@ -296,3 +296,42 @@ func testPayloadSize(t *testing.T, hops []*Hop) {
} }
} }
} }
// TestBlindedHopFee tests calculation of hop fees for blinded routes.
func TestBlindedHopFee(t *testing.T) {
t.Parallel()
route := &Route{
TotalAmount: 1500,
Hops: []*Hop{
// Start with two un-blinded hops.
{
AmtToForward: 1450,
},
{
AmtToForward: 1300,
},
{
// Our introduction node will have a zero
// forward amount.
AmtToForward: 0,
},
{
// An intermediate blinded hop will also have
// a zero forward amount.
AmtToForward: 0,
},
{
// Our final blinded hop should have a forward
// amount set.
AmtToForward: 1000,
},
},
}
require.Equal(t, lnwire.MilliSatoshi(50), route.HopFee(0))
require.Equal(t, lnwire.MilliSatoshi(150), route.HopFee(1))
require.Equal(t, lnwire.MilliSatoshi(300), route.HopFee(2))
require.Equal(t, lnwire.MilliSatoshi(0), route.HopFee(3))
require.Equal(t, lnwire.MilliSatoshi(0), route.HopFee(4))
}