mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-26 17:52:25 +01:00
routing: add TlvTrafficShaper to bandwidth hints
This commit is contained in:
parent
4a4724dc12
commit
57e975be6d
@ -1,10 +1,13 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
// bandwidthHints provides hints about the currently available balance in our
|
||||
@ -18,7 +21,36 @@ type bandwidthHints interface {
|
||||
// will be used. If the channel is unavailable, a zero amount is
|
||||
// returned.
|
||||
availableChanBandwidth(channelID uint64,
|
||||
amount lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool)
|
||||
amount lnwire.MilliSatoshi,
|
||||
htlcBlob fn.Option[tlv.Blob]) (lnwire.MilliSatoshi, bool)
|
||||
}
|
||||
|
||||
// TlvTrafficShaper is an interface that allows the sender to determine if a
|
||||
// payment should be carried by a channel based on the TLV records that may be
|
||||
// present in the `update_add_htlc` message or the channel commitment itself.
|
||||
type TlvTrafficShaper interface {
|
||||
// ShouldCarryPayment returns true if the provided tlv records indicate
|
||||
// that this channel may carry out the payment by utilizing external
|
||||
// mechanisms.
|
||||
ShouldCarryPayment(amt lnwire.MilliSatoshi, htlcTLV,
|
||||
channelBlob fn.Option[tlv.Blob]) bool
|
||||
|
||||
// HandleTraffic is called in order to check if the channel identified
|
||||
// by the provided channel ID may have external mechanisms that would
|
||||
// allow it to carry out the payment.
|
||||
HandleTraffic(cid lnwire.ShortChannelID) bool
|
||||
|
||||
AuxHtlcModifier
|
||||
}
|
||||
|
||||
// AuxHtlcModifier is an interface that allows the sender to modify the outgoing
|
||||
// HTLC of a payment by changing the amount or the wire message tlv records.
|
||||
type AuxHtlcModifier interface {
|
||||
// ProduceHtlcExtraData is a function that, based on the previous extra
|
||||
// data blob of an htlc, may produce a different blob or modify the
|
||||
// amount of bitcoin this htlc should carry.
|
||||
ProduceHtlcExtraData(htlcBlob tlv.Blob,
|
||||
chanID uint64) (btcutil.Amount, tlv.Blob, error)
|
||||
}
|
||||
|
||||
// getLinkQuery is the function signature used to lookup a link.
|
||||
@ -29,8 +61,9 @@ type getLinkQuery func(lnwire.ShortChannelID) (
|
||||
// uses the link lookup provided to query the link for our latest local channel
|
||||
// balances.
|
||||
type bandwidthManager struct {
|
||||
getLink getLinkQuery
|
||||
localChans map[lnwire.ShortChannelID]struct{}
|
||||
getLink getLinkQuery
|
||||
localChans map[lnwire.ShortChannelID]struct{}
|
||||
trafficShaper fn.Option[TlvTrafficShaper]
|
||||
}
|
||||
|
||||
// newBandwidthManager creates a bandwidth manager for the source node provided
|
||||
@ -40,11 +73,13 @@ type bandwidthManager struct {
|
||||
// allows us to reduce the number of extraneous attempts as we can skip channels
|
||||
// that are inactive, or just don't have enough bandwidth to carry the payment.
|
||||
func newBandwidthManager(graph routingGraph, sourceNode route.Vertex,
|
||||
linkQuery getLinkQuery) (*bandwidthManager, error) {
|
||||
linkQuery getLinkQuery,
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (*bandwidthManager, error) {
|
||||
|
||||
manager := &bandwidthManager{
|
||||
getLink: linkQuery,
|
||||
localChans: make(map[lnwire.ShortChannelID]struct{}),
|
||||
getLink: linkQuery,
|
||||
localChans: make(map[lnwire.ShortChannelID]struct{}),
|
||||
trafficShaper: trafficShaper,
|
||||
}
|
||||
|
||||
// First, we'll collect the set of outbound edges from the target
|
||||
@ -71,7 +106,8 @@ func newBandwidthManager(graph routingGraph, sourceNode route.Vertex,
|
||||
// queried is one of our local channels, so any failure to retrieve the link
|
||||
// is interpreted as the link being offline.
|
||||
func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
|
||||
amount lnwire.MilliSatoshi) lnwire.MilliSatoshi {
|
||||
amount lnwire.MilliSatoshi,
|
||||
htlcBlob fn.Option[tlv.Blob]) lnwire.MilliSatoshi {
|
||||
|
||||
link, err := b.getLink(cid)
|
||||
if err != nil {
|
||||
@ -89,16 +125,63 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
|
||||
return 0
|
||||
}
|
||||
|
||||
// If our link isn't currently in a state where it can add another
|
||||
// outgoing htlc, treat the link as unusable.
|
||||
if err := link.MayAddOutgoingHtlc(amount); err != nil {
|
||||
log.Warnf("ShortChannelID=%v: cannot add outgoing htlc: %v",
|
||||
cid, err)
|
||||
res := fn.MapOptionZ(b.trafficShaper, func(ts TlvTrafficShaper) bool {
|
||||
return ts.HandleTraffic(cid)
|
||||
})
|
||||
|
||||
// If response is no and we do have a traffic shaper, we can't handle
|
||||
// the traffic and return early.
|
||||
if !res && b.trafficShaper.IsSome() {
|
||||
log.Warnf("ShortChannelID=%v: can't handle traffic", cid)
|
||||
return 0
|
||||
}
|
||||
|
||||
// Otherwise, we'll return the current best estimate for the available
|
||||
// bandwidth for the link.
|
||||
channelBlob := link.ChannelCustomBlob()
|
||||
|
||||
// Run the wrapped traffic if it exists, otherwise return false.
|
||||
res = fn.MapOptionZ(b.trafficShaper, func(ts TlvTrafficShaper) bool {
|
||||
return ts.ShouldCarryPayment(amount, htlcBlob, channelBlob)
|
||||
})
|
||||
|
||||
// If the traffic shaper indicates that this channel can route the
|
||||
// payment, we immediatelly select this channel and return maximum
|
||||
// bandwidth as response.
|
||||
if res {
|
||||
// If the amount is zero, but the traffic shaper signaled that
|
||||
// the channel can carry the payment, we'll return the maximum
|
||||
// amount. A zero amount is used when we try to figure out if
|
||||
// enough balance exists for the payment to be carried out, but
|
||||
// at that point we don't know the payment amount in order to
|
||||
// return an exact value, so we signal a value that will
|
||||
// certainly satisfy the payment amount.
|
||||
if amount == 0 {
|
||||
// We don't want to just return the max uint64 as this
|
||||
// will overflow when further amounts are added
|
||||
// together.
|
||||
return lnwire.MaxMilliSatoshi / 2
|
||||
}
|
||||
|
||||
return amount
|
||||
}
|
||||
|
||||
// If the traffic shaper is present and it returned false, we want to
|
||||
// skip this channel.
|
||||
if b.trafficShaper.IsSome() {
|
||||
log.Warnf("ShortChannelID=%v: payment not allowed by traffic "+
|
||||
"shaper", cid)
|
||||
return 0
|
||||
}
|
||||
|
||||
// If our link isn't currently in a state where it can add
|
||||
// another outgoing htlc, treat the link as unusable.
|
||||
if err := link.MayAddOutgoingHtlc(amount); err != nil {
|
||||
log.Warnf("ShortChannelID=%v: cannot add outgoing "+
|
||||
"htlc: %v", cid, err)
|
||||
return 0
|
||||
}
|
||||
|
||||
// Otherwise, we'll return the current best estimate for the
|
||||
// available bandwidth for the link.
|
||||
return link.Bandwidth()
|
||||
}
|
||||
|
||||
@ -106,7 +189,8 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
|
||||
// and a bool indicating whether the channel hint was found. If the channel is
|
||||
// unavailable, a zero amount is returned.
|
||||
func (b *bandwidthManager) availableChanBandwidth(channelID uint64,
|
||||
amount lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool) {
|
||||
amount lnwire.MilliSatoshi,
|
||||
htlcBlob fn.Option[tlv.Blob]) (lnwire.MilliSatoshi, bool) {
|
||||
|
||||
shortID := lnwire.NewShortChanIDFromInt(channelID)
|
||||
_, ok := b.localChans[shortID]
|
||||
@ -114,5 +198,5 @@ func (b *bandwidthManager) availableChanBandwidth(channelID uint64,
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return b.getBandwidth(shortID, amount), true
|
||||
return b.getBandwidth(shortID, amount, htlcBlob), true
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
@ -42,6 +43,8 @@ type SessionSource struct {
|
||||
// PathFindingConfig defines global parameters that control the
|
||||
// trade-off in path finding between fees and probability.
|
||||
PathFindingConfig PathFindingConfig
|
||||
|
||||
TrafficShaper fn.Option[TlvTrafficShaper]
|
||||
}
|
||||
|
||||
// getRoutingGraph returns a routing graph and a clean-up function for
|
||||
@ -63,12 +66,14 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
|
||||
// view from Mission Control. An optional set of routing hints can be provided
|
||||
// in order to populate additional edges to explore when finding a path to the
|
||||
// payment's destination.
|
||||
func (m *SessionSource) NewPaymentSession(p *LightningPayment) (
|
||||
func (m *SessionSource) NewPaymentSession(p *LightningPayment,
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (
|
||||
PaymentSession, error) {
|
||||
|
||||
getBandwidthHints := func(graph routingGraph) (bandwidthHints, error) {
|
||||
return newBandwidthManager(
|
||||
graph, m.SourceNode.PubKeyBytes, m.GetLink,
|
||||
trafficShaper,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,8 @@ type PaymentSessionSource interface {
|
||||
// routes to the given target. An optional set of routing hints can be
|
||||
// provided in order to populate additional edges to explore when
|
||||
// finding a path to the payment's destination.
|
||||
NewPaymentSession(p *LightningPayment) (PaymentSession, error)
|
||||
NewPaymentSession(p *LightningPayment,
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (PaymentSession, error)
|
||||
|
||||
// NewPaymentSessionEmpty creates a new paymentSession instance that is
|
||||
// empty, and will be exhausted immediately. Used for failure reporting
|
||||
@ -411,6 +412,8 @@ type Config struct {
|
||||
// IsAlias returns whether a passed ShortChannelID is an alias. This is
|
||||
// only used for our local channels.
|
||||
IsAlias func(scid lnwire.ShortChannelID) bool
|
||||
|
||||
TrafficShaper fn.Option[TlvTrafficShaper]
|
||||
}
|
||||
|
||||
// EdgeLocator is a struct used to identify a specific edge.
|
||||
@ -2104,6 +2107,7 @@ func (r *ChannelRouter) FindRoute(req *RouteRequest) (*route.Route, float64,
|
||||
// eliminate certain routes early on in the path finding process.
|
||||
bandwidthHints, err := newBandwidthManager(
|
||||
r.cachedGraph, r.selfNode.PubKeyBytes, r.cfg.GetLink,
|
||||
r.cfg.TrafficShaper,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@ -2466,7 +2470,9 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) (
|
||||
// Before starting the HTLC routing attempt, we'll create a fresh
|
||||
// payment session which will report our errors back to mission
|
||||
// control.
|
||||
paySession, err := r.cfg.SessionSource.NewPaymentSession(payment)
|
||||
paySession, err := r.cfg.SessionSource.NewPaymentSession(
|
||||
payment, r.cfg.TrafficShaper,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -3115,6 +3121,7 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
|
||||
// the best outgoing channel to use in case no outgoing channel is set.
|
||||
bandwidthHints, err := newBandwidthManager(
|
||||
r.cachedGraph, r.selfNode.PubKeyBytes, r.cfg.GetLink,
|
||||
r.cfg.TrafficShaper,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -3211,7 +3218,9 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
|
||||
}
|
||||
|
||||
// Get an edge for the specific amount that we want to forward.
|
||||
edge := edgeUnifier.getEdge(runningAmt, bandwidthHints, 0)
|
||||
edge := edgeUnifier.getEdge(
|
||||
runningAmt, bandwidthHints, 0, fn.Option[[]byte]{},
|
||||
)
|
||||
if edge == nil {
|
||||
log.Errorf("Cannot find policy with amt=%v for node %v",
|
||||
runningAmt, fromNode)
|
||||
@ -3249,7 +3258,9 @@ func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
|
||||
// amount ranges re-checked.
|
||||
var pathEdges []*unifiedEdge
|
||||
for i, unifier := range unifiers {
|
||||
edge := unifier.getEdge(receiverAmt, bandwidthHints, 0)
|
||||
edge := unifier.getEdge(
|
||||
receiverAmt, bandwidthHints, 0, fn.Option[[]byte]{},
|
||||
)
|
||||
if edge == nil {
|
||||
fromNode := source
|
||||
if i > 0 {
|
||||
|
@ -6,9 +6,11 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
// nodeEdgeUnifier holds all edge unifiers for connections towards a node.
|
||||
@ -182,11 +184,12 @@ type edgeUnifier struct {
|
||||
// channels.
|
||||
func (u *edgeUnifier) getEdge(netAmtReceived lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints,
|
||||
nextOutFee lnwire.MilliSatoshi) *unifiedEdge {
|
||||
nextOutFee lnwire.MilliSatoshi,
|
||||
htlcBlob fn.Option[tlv.Blob]) *unifiedEdge {
|
||||
|
||||
if u.localChan {
|
||||
return u.getEdgeLocal(
|
||||
netAmtReceived, bandwidthHints, nextOutFee,
|
||||
netAmtReceived, bandwidthHints, nextOutFee, htlcBlob,
|
||||
)
|
||||
}
|
||||
|
||||
@ -214,7 +217,8 @@ func calcCappedInboundFee(edge *unifiedEdge, amt lnwire.MilliSatoshi,
|
||||
// connection given a specific amount to send.
|
||||
func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi,
|
||||
bandwidthHints bandwidthHints,
|
||||
nextOutFee lnwire.MilliSatoshi) *unifiedEdge {
|
||||
nextOutFee lnwire.MilliSatoshi,
|
||||
htlcBlob fn.Option[tlv.Blob]) *unifiedEdge {
|
||||
|
||||
var (
|
||||
bestEdge *unifiedEdge
|
||||
@ -251,7 +255,7 @@ func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi,
|
||||
// channel. The bandwidth hint is expected to be
|
||||
// available.
|
||||
bandwidth, ok := bandwidthHints.availableChanBandwidth(
|
||||
edge.policy.ChannelID, amt,
|
||||
edge.policy.ChannelID, amt, htlcBlob,
|
||||
)
|
||||
if !ok {
|
||||
log.Debugf("Cannot get bandwidth for edge %v, use max "+
|
||||
|
Loading…
x
Reference in New Issue
Block a user