mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-30 15:40:59 +02:00
routing: add inbound fee support to pathfinding
Add sender-side support for inbound fees in pathfinding and route building.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
@@ -21,6 +23,9 @@ type nodeEdgeUnifier struct {
|
||||
// toNode is the node for which the edge unifiers are instantiated.
|
||||
toNode route.Vertex
|
||||
|
||||
// useInboundFees indicates whether to take inbound fees into account.
|
||||
useInboundFees bool
|
||||
|
||||
// outChanRestr is an optional outgoing channel restriction for the
|
||||
// local channel to use.
|
||||
outChanRestr map[uint64]struct{}
|
||||
@@ -28,14 +33,15 @@ type nodeEdgeUnifier struct {
|
||||
|
||||
// newNodeEdgeUnifier instantiates a new nodeEdgeUnifier object. Channel
|
||||
// policies can be added to this object.
|
||||
func newNodeEdgeUnifier(sourceNode, toNode route.Vertex,
|
||||
func newNodeEdgeUnifier(sourceNode, toNode route.Vertex, useInboundFees bool,
|
||||
outChanRestr map[uint64]struct{}) *nodeEdgeUnifier {
|
||||
|
||||
return &nodeEdgeUnifier{
|
||||
edgeUnifiers: make(map[route.Vertex]*edgeUnifier),
|
||||
toNode: toNode,
|
||||
sourceNode: sourceNode,
|
||||
outChanRestr: outChanRestr,
|
||||
edgeUnifiers: make(map[route.Vertex]*edgeUnifier),
|
||||
toNode: toNode,
|
||||
useInboundFees: useInboundFees,
|
||||
sourceNode: sourceNode,
|
||||
outChanRestr: outChanRestr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +50,8 @@ func newNodeEdgeUnifier(sourceNode, toNode route.Vertex,
|
||||
// graceful shutdown if it is not provided as this indicates that edges are
|
||||
// incorrectly specified.
|
||||
func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
||||
edge *models.CachedEdgePolicy, capacity btcutil.Amount,
|
||||
hopPayloadSizeFn PayloadSizeFunc) {
|
||||
edge *models.CachedEdgePolicy, inboundFee models.InboundFee,
|
||||
capacity btcutil.Amount, hopPayloadSizeFn PayloadSizeFunc) {
|
||||
|
||||
localChan := fromNode == u.sourceNode
|
||||
|
||||
@@ -75,10 +81,16 @@ func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
||||
return
|
||||
}
|
||||
|
||||
// Zero inbound fee for exit hops.
|
||||
if !u.useInboundFees {
|
||||
inboundFee = models.InboundFee{}
|
||||
}
|
||||
|
||||
unifier.edges = append(unifier.edges, &unifiedEdge{
|
||||
policy: edge,
|
||||
capacity: capacity,
|
||||
hopPayloadSizeFn: hopPayloadSizeFn,
|
||||
inboundFees: inboundFee,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,9 +109,13 @@ func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
|
||||
// to the clear hop payload size function because
|
||||
// `addGraphPolicies` is only used for cleartext intermediate
|
||||
// hops in a route.
|
||||
inboundFee := models.NewInboundFeeFromWire(
|
||||
channel.InboundFee,
|
||||
)
|
||||
|
||||
u.addPolicy(
|
||||
channel.OtherNode, channel.InPolicy, channel.Capacity,
|
||||
defaultHopPayloadSize,
|
||||
channel.OtherNode, channel.InPolicy, inboundFee,
|
||||
channel.Capacity, defaultHopPayloadSize,
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -112,8 +128,9 @@ func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
|
||||
// unifiedEdge is the individual channel data that is kept inside an edgeUnifier
|
||||
// object.
|
||||
type unifiedEdge struct {
|
||||
policy *models.CachedEdgePolicy
|
||||
capacity btcutil.Amount
|
||||
policy *models.CachedEdgePolicy
|
||||
capacity btcutil.Amount
|
||||
inboundFees models.InboundFee
|
||||
|
||||
// hopPayloadSize supplies an edge with the ability to calculate the
|
||||
// exact payload size if this edge would be included in a route. This
|
||||
@@ -163,20 +180,41 @@ type edgeUnifier struct {
|
||||
// getEdge returns the optimal unified edge to use for this connection given a
|
||||
// specific amount to send. It differentiates between local and network
|
||||
// channels.
|
||||
func (u *edgeUnifier) getEdge(amt lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints) *unifiedEdge {
|
||||
func (u *edgeUnifier) getEdge(netAmtReceived lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints,
|
||||
nextOutFee lnwire.MilliSatoshi) *unifiedEdge {
|
||||
|
||||
if u.localChan {
|
||||
return u.getEdgeLocal(amt, bandwidthHints)
|
||||
return u.getEdgeLocal(
|
||||
netAmtReceived, bandwidthHints, nextOutFee,
|
||||
)
|
||||
}
|
||||
|
||||
return u.getEdgeNetwork(amt)
|
||||
return u.getEdgeNetwork(netAmtReceived, nextOutFee)
|
||||
}
|
||||
|
||||
// calcCappedInboundFee calculates the inbound fee for a channel, taking into
|
||||
// account the total node fee for the "to" node.
|
||||
func calcCappedInboundFee(edge *unifiedEdge, amt lnwire.MilliSatoshi,
|
||||
nextOutFee lnwire.MilliSatoshi) int64 {
|
||||
|
||||
// Calculate the inbound fee charged for the amount that passes over the
|
||||
// channel.
|
||||
inboundFee := edge.inboundFees.CalcFee(amt)
|
||||
|
||||
// Take into account that the total node fee cannot be negative.
|
||||
if inboundFee < -int64(nextOutFee) {
|
||||
inboundFee = -int64(nextOutFee)
|
||||
}
|
||||
|
||||
return inboundFee
|
||||
}
|
||||
|
||||
// getEdgeLocal returns the optimal unified edge to use for this local
|
||||
// connection given a specific amount to send.
|
||||
func (u *edgeUnifier) getEdgeLocal(amt lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints) *unifiedEdge {
|
||||
func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints,
|
||||
nextOutFee lnwire.MilliSatoshi) *unifiedEdge {
|
||||
|
||||
var (
|
||||
bestEdge *unifiedEdge
|
||||
@@ -184,10 +222,20 @@ func (u *edgeUnifier) getEdgeLocal(amt lnwire.MilliSatoshi,
|
||||
)
|
||||
|
||||
for _, edge := range u.edges {
|
||||
// Calculate the inbound fee charged at the receiving node.
|
||||
inboundFee := calcCappedInboundFee(
|
||||
edge, netAmtReceived, nextOutFee,
|
||||
)
|
||||
|
||||
// Add inbound fee to get to the amount that is sent over the
|
||||
// local channel.
|
||||
amt := netAmtReceived + lnwire.MilliSatoshi(inboundFee)
|
||||
|
||||
// Check valid amount range for the channel.
|
||||
if !edge.amtInRange(amt) {
|
||||
log.Debugf("Amount %v not in range for edge %v",
|
||||
amt, edge.policy.ChannelID)
|
||||
netAmtReceived, edge.policy.ChannelID)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -249,6 +297,7 @@ func (u *edgeUnifier) getEdgeLocal(amt lnwire.MilliSatoshi,
|
||||
policy: edge.policy,
|
||||
capacity: edge.capacity,
|
||||
hopPayloadSizeFn: edge.hopPayloadSizeFn,
|
||||
inboundFees: edge.inboundFees,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,16 +308,27 @@ func (u *edgeUnifier) getEdgeLocal(amt lnwire.MilliSatoshi,
|
||||
// given a specific amount to send. The goal is to return a unified edge with a
|
||||
// policy that maximizes the probability of a successful forward in a non-strict
|
||||
// forwarding context.
|
||||
func (u *edgeUnifier) getEdgeNetwork(amt lnwire.MilliSatoshi) *unifiedEdge {
|
||||
func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
||||
nextOutFee lnwire.MilliSatoshi) *unifiedEdge {
|
||||
|
||||
var (
|
||||
bestPolicy *models.CachedEdgePolicy
|
||||
maxFee lnwire.MilliSatoshi
|
||||
bestPolicy *unifiedEdge
|
||||
maxFee int64 = math.MinInt64
|
||||
maxTimelock uint16
|
||||
maxCapMsat lnwire.MilliSatoshi
|
||||
hopPayloadSizeFn PayloadSizeFunc
|
||||
)
|
||||
|
||||
for _, edge := range u.edges {
|
||||
// Calculate the inbound fee charged at the receiving node.
|
||||
inboundFee := calcCappedInboundFee(
|
||||
edge, netAmtReceived, nextOutFee,
|
||||
)
|
||||
|
||||
// Add inbound fee to get to the amount that is sent over the
|
||||
// channel.
|
||||
amt := netAmtReceived + lnwire.MilliSatoshi(inboundFee)
|
||||
|
||||
// Check valid amount range for the channel.
|
||||
if !edge.amtInRange(amt) {
|
||||
log.Debugf("Amount %v not in range for edge %v",
|
||||
@@ -302,9 +362,12 @@ func (u *edgeUnifier) getEdgeNetwork(amt lnwire.MilliSatoshi) *unifiedEdge {
|
||||
maxTimelock = lntypes.Max(
|
||||
maxTimelock, edge.policy.TimeLockDelta,
|
||||
)
|
||||
|
||||
outboundFee := int64(edge.policy.ComputeFee(amt))
|
||||
fee := outboundFee + inboundFee
|
||||
|
||||
// Use the policy that results in the highest fee for this
|
||||
// specific amount.
|
||||
fee := edge.policy.ComputeFee(amt)
|
||||
if fee < maxFee {
|
||||
log.Debugf("Skipped edge %v due to it produces less "+
|
||||
"fee: fee=%v, maxFee=%v",
|
||||
@@ -314,7 +377,11 @@ func (u *edgeUnifier) getEdgeNetwork(amt lnwire.MilliSatoshi) *unifiedEdge {
|
||||
}
|
||||
maxFee = fee
|
||||
|
||||
bestPolicy = edge.policy
|
||||
bestPolicy = &unifiedEdge{
|
||||
policy: edge.policy,
|
||||
inboundFees: edge.inboundFees,
|
||||
}
|
||||
|
||||
// The payload size function for edges to a connected peer is
|
||||
// always the same hence there is not need to find the maximum.
|
||||
// This also counts for blinded edges where we only have one
|
||||
@@ -337,8 +404,11 @@ func (u *edgeUnifier) getEdgeNetwork(amt lnwire.MilliSatoshi) *unifiedEdge {
|
||||
// get forwarded. Because we penalize pair-wise, there won't be a second
|
||||
// chance for this node pair. But this is all only needed for nodes that
|
||||
// have distinct policies for channels to the same peer.
|
||||
policyCopy := *bestPolicy
|
||||
modifiedEdge := unifiedEdge{policy: &policyCopy}
|
||||
policyCopy := *bestPolicy.policy
|
||||
modifiedEdge := unifiedEdge{
|
||||
policy: &policyCopy,
|
||||
inboundFees: bestPolicy.inboundFees,
|
||||
}
|
||||
modifiedEdge.policy.TimeLockDelta = maxTimelock
|
||||
modifiedEdge.capacity = maxCapMsat.ToSatoshis()
|
||||
modifiedEdge.hopPayloadSizeFn = hopPayloadSizeFn
|
||||
|
Reference in New Issue
Block a user