mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-27 18:22:24 +01:00
Merge branch '0-18-4-branch-rc2-9333' into 0-18-4-branch-rc2
This commit is contained in:
commit
b63ac70113
@ -35,6 +35,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/invoices"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
@ -46,7 +47,6 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet/rpcwallet"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
"github.com/lightningnetwork/lnd/msgmux"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/rpcperms"
|
||||
"github.com/lightningnetwork/lnd/signal"
|
||||
"github.com/lightningnetwork/lnd/sqldb"
|
||||
@ -165,7 +165,7 @@ type AuxComponents struct {
|
||||
|
||||
// TrafficShaper is an optional traffic shaper that can be used to
|
||||
// control the outgoing channel of a payment.
|
||||
TrafficShaper fn.Option[routing.TlvTrafficShaper]
|
||||
TrafficShaper fn.Option[htlcswitch.AuxTrafficShaper]
|
||||
|
||||
// MsgRouter is an optional message router that if set will be used in
|
||||
// place of a new blank default message router.
|
||||
|
@ -194,6 +194,11 @@ const (
|
||||
Outgoing LinkDirection = true
|
||||
)
|
||||
|
||||
// OptionalBandwidth is a type alias for the result of a bandwidth query that
|
||||
// may return a bandwidth value or fn.None if the bandwidth is not available or
|
||||
// not applicable.
|
||||
type OptionalBandwidth = fn.Option[lnwire.MilliSatoshi]
|
||||
|
||||
// ChannelLink is an interface which represents the subsystem for managing the
|
||||
// incoming htlc requests, applying the changes to the channel, and also
|
||||
// propagating/forwarding it to htlc switch.
|
||||
@ -255,10 +260,10 @@ type ChannelLink interface {
|
||||
// in order to signal to the source of the HTLC, the policy consistency
|
||||
// issue.
|
||||
CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
|
||||
amtToForward lnwire.MilliSatoshi,
|
||||
incomingTimeout, outgoingTimeout uint32,
|
||||
inboundFee models.InboundFee,
|
||||
heightNow uint32, scid lnwire.ShortChannelID) *LinkError
|
||||
amtToForward lnwire.MilliSatoshi, incomingTimeout,
|
||||
outgoingTimeout uint32, inboundFee models.InboundFee,
|
||||
heightNow uint32, scid lnwire.ShortChannelID,
|
||||
customRecords lnwire.CustomRecords) *LinkError
|
||||
|
||||
// CheckHtlcTransit should return a nil error if the passed HTLC details
|
||||
// satisfy the current channel policy. Otherwise, a LinkError with a
|
||||
@ -266,14 +271,15 @@ type ChannelLink interface {
|
||||
// the violation. This call is intended to be used for locally initiated
|
||||
// payments for which there is no corresponding incoming htlc.
|
||||
CheckHtlcTransit(payHash [32]byte, amt lnwire.MilliSatoshi,
|
||||
timeout uint32, heightNow uint32) *LinkError
|
||||
timeout uint32, heightNow uint32,
|
||||
customRecords lnwire.CustomRecords) *LinkError
|
||||
|
||||
// Stats return the statistics of channel link. Number of updates,
|
||||
// total sent/received milli-satoshis.
|
||||
Stats() (uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi)
|
||||
|
||||
// Peer returns the serialized public key of remote peer with which we
|
||||
// have the channel link opened.
|
||||
// PeerPubKey returns the serialized public key of remote peer with
|
||||
// which we have the channel link opened.
|
||||
PeerPubKey() [33]byte
|
||||
|
||||
// AttachMailBox delivers an active MailBox to the link. The MailBox may
|
||||
@ -290,9 +296,18 @@ type ChannelLink interface {
|
||||
// commitment of the channel that this link is associated with.
|
||||
CommitmentCustomBlob() fn.Option[tlv.Blob]
|
||||
|
||||
// Start/Stop are used to initiate the start/stop of the channel link
|
||||
// functioning.
|
||||
// AuxBandwidth returns the bandwidth that can be used for a channel,
|
||||
// expressed in milli-satoshi. This might be different from the regular
|
||||
// BTC bandwidth for custom channels. This will always return fn.None()
|
||||
// for a regular (non-custom) channel.
|
||||
AuxBandwidth(amount lnwire.MilliSatoshi, cid lnwire.ShortChannelID,
|
||||
htlcBlob fn.Option[tlv.Blob],
|
||||
ts AuxTrafficShaper) fn.Result[OptionalBandwidth]
|
||||
|
||||
// Start starts the channel link.
|
||||
Start() error
|
||||
|
||||
// Stop requests the channel link to be shut down.
|
||||
Stop()
|
||||
}
|
||||
|
||||
@ -428,7 +443,7 @@ type htlcNotifier interface {
|
||||
NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
|
||||
eventType HtlcEventType)
|
||||
|
||||
// NotifyIncomingLinkFailEvent notifies that a htlc has failed on our
|
||||
// NotifyLinkFailEvent notifies that a htlc has failed on our
|
||||
// incoming link. It takes an isReceive bool to differentiate between
|
||||
// our node's receives and forwards.
|
||||
NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
|
||||
@ -449,3 +464,36 @@ type htlcNotifier interface {
|
||||
NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||
info channeldb.FinalHtlcInfo)
|
||||
}
|
||||
|
||||
// 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(totalAmount lnwire.MilliSatoshi,
|
||||
htlcCustomRecords lnwire.CustomRecords) (lnwire.MilliSatoshi,
|
||||
lnwire.CustomRecords, error)
|
||||
}
|
||||
|
||||
// AuxTrafficShaper 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 AuxTrafficShaper interface {
|
||||
AuxHtlcModifier
|
||||
|
||||
// ShouldHandleTraffic 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.
|
||||
ShouldHandleTraffic(cid lnwire.ShortChannelID,
|
||||
fundingBlob fn.Option[tlv.Blob]) (bool, error)
|
||||
|
||||
// PaymentBandwidth returns the available bandwidth for a custom channel
|
||||
// decided by the given channel aux blob and HTLC blob. A return value
|
||||
// of 0 means there is no bandwidth available. To find out if a channel
|
||||
// is a custom channel that should be handled by the traffic shaper, the
|
||||
// ShouldHandleTraffic method should be called first.
|
||||
PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob],
|
||||
linkBandwidth,
|
||||
htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error)
|
||||
}
|
||||
|
@ -284,6 +284,10 @@ type ChannelLinkConfig struct {
|
||||
// MaxFeeExposure is the threshold in milli-satoshis after which we'll
|
||||
// restrict the flow of HTLCs and fee updates.
|
||||
MaxFeeExposure lnwire.MilliSatoshi
|
||||
|
||||
// AuxTrafficShaper is an optional auxiliary traffic shaper that can be
|
||||
// used to manage the bandwidth of the link.
|
||||
AuxTrafficShaper fn.Option[AuxTrafficShaper]
|
||||
}
|
||||
|
||||
// channelLink is the service which drives a channel's commitment update
|
||||
@ -3021,11 +3025,11 @@ func (l *channelLink) UpdateForwardingPolicy(
|
||||
// issue.
|
||||
//
|
||||
// NOTE: Part of the ChannelLink interface.
|
||||
func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
||||
incomingHtlcAmt, amtToForward lnwire.MilliSatoshi,
|
||||
incomingTimeout, outgoingTimeout uint32,
|
||||
inboundFee models.InboundFee,
|
||||
heightNow uint32, originalScid lnwire.ShortChannelID) *LinkError {
|
||||
func (l *channelLink) CheckHtlcForward(payHash [32]byte, incomingHtlcAmt,
|
||||
amtToForward lnwire.MilliSatoshi, incomingTimeout,
|
||||
outgoingTimeout uint32, inboundFee models.InboundFee,
|
||||
heightNow uint32, originalScid lnwire.ShortChannelID,
|
||||
customRecords lnwire.CustomRecords) *LinkError {
|
||||
|
||||
l.RLock()
|
||||
policy := l.cfg.FwrdingPolicy
|
||||
@ -3074,7 +3078,7 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
||||
// Check whether the outgoing htlc satisfies the channel policy.
|
||||
err := l.canSendHtlc(
|
||||
policy, payHash, amtToForward, outgoingTimeout, heightNow,
|
||||
originalScid,
|
||||
originalScid, customRecords,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -3110,8 +3114,8 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
|
||||
// the violation. This call is intended to be used for locally initiated
|
||||
// payments for which there is no corresponding incoming htlc.
|
||||
func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
||||
amt lnwire.MilliSatoshi, timeout uint32,
|
||||
heightNow uint32) *LinkError {
|
||||
amt lnwire.MilliSatoshi, timeout uint32, heightNow uint32,
|
||||
customRecords lnwire.CustomRecords) *LinkError {
|
||||
|
||||
l.RLock()
|
||||
policy := l.cfg.FwrdingPolicy
|
||||
@ -3122,6 +3126,7 @@ func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
||||
// to occur.
|
||||
return l.canSendHtlc(
|
||||
policy, payHash, amt, timeout, heightNow, hop.Source,
|
||||
customRecords,
|
||||
)
|
||||
}
|
||||
|
||||
@ -3129,7 +3134,8 @@ func (l *channelLink) CheckHtlcTransit(payHash [32]byte,
|
||||
// the channel's amount and time lock constraints.
|
||||
func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
|
||||
payHash [32]byte, amt lnwire.MilliSatoshi, timeout uint32,
|
||||
heightNow uint32, originalScid lnwire.ShortChannelID) *LinkError {
|
||||
heightNow uint32, originalScid lnwire.ShortChannelID,
|
||||
customRecords lnwire.CustomRecords) *LinkError {
|
||||
|
||||
// As our first sanity check, we'll ensure that the passed HTLC isn't
|
||||
// too small for the next hop. If so, then we'll cancel the HTLC
|
||||
@ -3187,8 +3193,38 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
|
||||
return NewLinkError(&lnwire.FailExpiryTooFar{})
|
||||
}
|
||||
|
||||
// We now check the available bandwidth to see if this HTLC can be
|
||||
// forwarded.
|
||||
availableBandwidth := l.Bandwidth()
|
||||
auxBandwidth, err := fn.MapOptionZ(
|
||||
l.cfg.AuxTrafficShaper,
|
||||
func(ts AuxTrafficShaper) fn.Result[OptionalBandwidth] {
|
||||
var htlcBlob fn.Option[tlv.Blob]
|
||||
blob, err := customRecords.Serialize()
|
||||
if err != nil {
|
||||
return fn.Err[OptionalBandwidth](
|
||||
fmt.Errorf("unable to serialize "+
|
||||
"custom records: %w", err))
|
||||
}
|
||||
|
||||
if len(blob) > 0 {
|
||||
htlcBlob = fn.Some(blob)
|
||||
}
|
||||
|
||||
return l.AuxBandwidth(amt, originalScid, htlcBlob, ts)
|
||||
},
|
||||
).Unpack()
|
||||
if err != nil {
|
||||
l.log.Errorf("Unable to determine aux bandwidth: %v", err)
|
||||
return NewLinkError(&lnwire.FailTemporaryNodeFailure{})
|
||||
}
|
||||
|
||||
auxBandwidth.WhenSome(func(bandwidth lnwire.MilliSatoshi) {
|
||||
availableBandwidth = bandwidth
|
||||
})
|
||||
|
||||
// Check to see if there is enough balance in this channel.
|
||||
if amt > l.Bandwidth() {
|
||||
if amt > availableBandwidth {
|
||||
l.log.Warnf("insufficient bandwidth to route htlc: %v is "+
|
||||
"larger than %v", amt, l.Bandwidth())
|
||||
cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
|
||||
@ -3203,6 +3239,48 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuxBandwidth returns the bandwidth that can be used for a channel, expressed
|
||||
// in milli-satoshi. This might be different from the regular BTC bandwidth for
|
||||
// custom channels. This will always return fn.None() for a regular (non-custom)
|
||||
// channel.
|
||||
func (l *channelLink) AuxBandwidth(amount lnwire.MilliSatoshi,
|
||||
cid lnwire.ShortChannelID, htlcBlob fn.Option[tlv.Blob],
|
||||
ts AuxTrafficShaper) fn.Result[OptionalBandwidth] {
|
||||
|
||||
unknownBandwidth := fn.None[lnwire.MilliSatoshi]()
|
||||
|
||||
fundingBlob := l.FundingCustomBlob()
|
||||
shouldHandle, err := ts.ShouldHandleTraffic(cid, fundingBlob)
|
||||
if err != nil {
|
||||
return fn.Err[OptionalBandwidth](fmt.Errorf("traffic shaper "+
|
||||
"failed to decide whether to handle traffic: %w", err))
|
||||
}
|
||||
|
||||
log.Debugf("ShortChannelID=%v: aux traffic shaper is handling "+
|
||||
"traffic: %v", cid, shouldHandle)
|
||||
|
||||
// If this channel isn't handled by the aux traffic shaper, we'll return
|
||||
// early.
|
||||
if !shouldHandle {
|
||||
return fn.Ok(unknownBandwidth)
|
||||
}
|
||||
|
||||
// Ask for a specific bandwidth to be used for the channel.
|
||||
commitmentBlob := l.CommitmentCustomBlob()
|
||||
auxBandwidth, err := ts.PaymentBandwidth(
|
||||
htlcBlob, commitmentBlob, l.Bandwidth(), amount,
|
||||
)
|
||||
if err != nil {
|
||||
return fn.Err[OptionalBandwidth](fmt.Errorf("failed to get "+
|
||||
"bandwidth from external traffic shaper: %w", err))
|
||||
}
|
||||
|
||||
log.Debugf("ShortChannelID=%v: aux traffic shaper reported available "+
|
||||
"bandwidth: %v", cid, auxBandwidth)
|
||||
|
||||
return fn.Ok(fn.Some(auxBandwidth))
|
||||
}
|
||||
|
||||
// Stats returns the statistics of channel link.
|
||||
//
|
||||
// NOTE: Part of the ChannelLink interface.
|
||||
|
@ -6240,9 +6240,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
var hash [32]byte
|
||||
|
||||
t.Run("satisfied", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||
200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1500, 1000, 200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if result != nil {
|
||||
t.Fatalf("expected policy to be satisfied")
|
||||
@ -6250,9 +6250,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("below minhtlc", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 100, 50,
|
||||
200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 100, 50, 200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailAmountBelowMinimum); !ok {
|
||||
t.Fatalf("expected FailAmountBelowMinimum failure code")
|
||||
@ -6260,9 +6260,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("above maxhtlc", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 1500, 1200,
|
||||
200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1500, 1200, 200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailTemporaryChannelFailure); !ok {
|
||||
t.Fatalf("expected FailTemporaryChannelFailure failure code")
|
||||
@ -6270,9 +6270,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("insufficient fee", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 1005, 1000,
|
||||
200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1005, 1000, 200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailFeeInsufficient); !ok {
|
||||
t.Fatalf("expected FailFeeInsufficient failure code")
|
||||
@ -6285,17 +6285,17 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 100005, 100000, 200,
|
||||
150, models.InboundFee{}, 0, lnwire.ShortChannelID{},
|
||||
hash, 100005, 100000, 200, 150, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
_, ok := result.WireMessage().(*lnwire.FailFeeInsufficient)
|
||||
require.True(t, ok, "expected FailFeeInsufficient failure code")
|
||||
})
|
||||
|
||||
t.Run("expiry too soon", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||
200, 150, models.InboundFee{}, 190,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1500, 1000, 200, 150, models.InboundFee{}, 190,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailExpiryTooSoon); !ok {
|
||||
t.Fatalf("expected FailExpiryTooSoon failure code")
|
||||
@ -6303,9 +6303,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("incorrect cltv expiry", func(t *testing.T) {
|
||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||
200, 190, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1500, 1000, 200, 190, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailIncorrectCltvExpiry); !ok {
|
||||
t.Fatalf("expected FailIncorrectCltvExpiry failure code")
|
||||
@ -6315,9 +6315,9 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
|
||||
t.Run("cltv expiry too far in the future", func(t *testing.T) {
|
||||
// Check that expiry isn't too far in the future.
|
||||
result := link.CheckHtlcForward(hash, 1500, 1000,
|
||||
10200, 10100, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{},
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1500, 1000, 10200, 10100, models.InboundFee{}, 0,
|
||||
lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if _, ok := result.WireMessage().(*lnwire.FailExpiryTooFar); !ok {
|
||||
t.Fatalf("expected FailExpiryTooFar failure code")
|
||||
@ -6327,9 +6327,11 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
t.Run("inbound fee satisfied", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := link.CheckHtlcForward(hash, 1000+10-2-1, 1000,
|
||||
200, 150, models.InboundFee{Base: -2, Rate: -1_000},
|
||||
0, lnwire.ShortChannelID{})
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1000+10-2-1, 1000, 200, 150,
|
||||
models.InboundFee{Base: -2, Rate: -1_000},
|
||||
0, lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
if result != nil {
|
||||
t.Fatalf("expected policy to be satisfied")
|
||||
}
|
||||
@ -6338,9 +6340,11 @@ func TestCheckHtlcForward(t *testing.T) {
|
||||
t.Run("inbound fee insufficient", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := link.CheckHtlcForward(hash, 1000+10-10-101-1, 1000,
|
||||
result := link.CheckHtlcForward(
|
||||
hash, 1000+10-10-101-1, 1000,
|
||||
200, 150, models.InboundFee{Base: -10, Rate: -100_000},
|
||||
0, lnwire.ShortChannelID{})
|
||||
0, lnwire.ShortChannelID{}, nil,
|
||||
)
|
||||
|
||||
msg := result.WireMessage()
|
||||
if _, ok := msg.(*lnwire.FailFeeInsufficient); !ok {
|
||||
|
@ -845,14 +845,14 @@ func (f *mockChannelLink) UpdateForwardingPolicy(_ models.ForwardingPolicy) {
|
||||
}
|
||||
func (f *mockChannelLink) CheckHtlcForward([32]byte, lnwire.MilliSatoshi,
|
||||
lnwire.MilliSatoshi, uint32, uint32, models.InboundFee, uint32,
|
||||
lnwire.ShortChannelID) *LinkError {
|
||||
lnwire.ShortChannelID, lnwire.CustomRecords) *LinkError {
|
||||
|
||||
return f.checkHtlcForwardResult
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) CheckHtlcTransit(payHash [32]byte,
|
||||
amt lnwire.MilliSatoshi, timeout uint32,
|
||||
heightNow uint32) *LinkError {
|
||||
heightNow uint32, _ lnwire.CustomRecords) *LinkError {
|
||||
|
||||
return f.checkHtlcTransitResult
|
||||
}
|
||||
@ -959,6 +959,17 @@ func (f *mockChannelLink) CommitmentCustomBlob() fn.Option[tlv.Blob] {
|
||||
return fn.None[tlv.Blob]()
|
||||
}
|
||||
|
||||
// AuxBandwidth returns the bandwidth that can be used for a channel,
|
||||
// expressed in milli-satoshi. This might be different from the regular
|
||||
// BTC bandwidth for custom channels. This will always return fn.None()
|
||||
// for a regular (non-custom) channel.
|
||||
func (f *mockChannelLink) AuxBandwidth(lnwire.MilliSatoshi,
|
||||
lnwire.ShortChannelID,
|
||||
fn.Option[tlv.Blob], AuxTrafficShaper) fn.Result[OptionalBandwidth] {
|
||||
|
||||
return fn.Ok(fn.None[lnwire.MilliSatoshi]())
|
||||
}
|
||||
|
||||
var _ ChannelLink = (*mockChannelLink)(nil)
|
||||
|
||||
func newDB() (*channeldb.DB, func(), error) {
|
||||
|
@ -916,6 +916,7 @@ func (s *Switch) getLocalLink(pkt *htlcPacket, htlc *lnwire.UpdateAddHTLC) (
|
||||
currentHeight := atomic.LoadUint32(&s.bestHeight)
|
||||
htlcErr := link.CheckHtlcTransit(
|
||||
htlc.PaymentHash, htlc.Amount, htlc.Expiry, currentHeight,
|
||||
htlc.CustomRecords,
|
||||
)
|
||||
if htlcErr != nil {
|
||||
log.Errorf("Link %v policy for local forward not "+
|
||||
@ -2886,10 +2887,9 @@ func (s *Switch) handlePacketAdd(packet *htlcPacket,
|
||||
failure = link.CheckHtlcForward(
|
||||
htlc.PaymentHash, packet.incomingAmount,
|
||||
packet.amount, packet.incomingTimeout,
|
||||
packet.outgoingTimeout,
|
||||
packet.inboundFee,
|
||||
currentHeight,
|
||||
packet.originalOutgoingChanID,
|
||||
packet.outgoingTimeout, packet.inboundFee,
|
||||
currentHeight, packet.originalOutgoingChanID,
|
||||
htlc.CustomRecords,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -400,6 +400,10 @@ type Config struct {
|
||||
// way contracts are resolved.
|
||||
AuxResolver fn.Option[lnwallet.AuxContractResolver]
|
||||
|
||||
// AuxTrafficShaper is an optional auxiliary traffic shaper that can be
|
||||
// used to manage the bandwidth of peer links.
|
||||
AuxTrafficShaper fn.Option[htlcswitch.AuxTrafficShaper]
|
||||
|
||||
// PongBuf is a slice we'll reuse instead of allocating memory on the
|
||||
// heap. Since only reads will occur and no writes, there is no need
|
||||
// for any synchronization primitives. As a result, it's safe to share
|
||||
@ -1319,6 +1323,7 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
|
||||
PreviouslySentShutdown: shutdownMsg,
|
||||
DisallowRouteBlinding: p.cfg.DisallowRouteBlinding,
|
||||
MaxFeeExposure: p.cfg.MaxFeeExposure,
|
||||
AuxTrafficShaper: p.cfg.AuxTrafficShaper,
|
||||
}
|
||||
|
||||
// Before adding our new link, purge the switch of any pending or live
|
||||
|
@ -29,39 +29,6 @@ type bandwidthHints interface {
|
||||
firstHopCustomBlob() fn.Option[tlv.Blob]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
AuxHtlcModifier
|
||||
|
||||
// ShouldHandleTraffic 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.
|
||||
ShouldHandleTraffic(cid lnwire.ShortChannelID,
|
||||
fundingBlob fn.Option[tlv.Blob]) (bool, error)
|
||||
|
||||
// PaymentBandwidth returns the available bandwidth for a custom channel
|
||||
// decided by the given channel aux blob and HTLC blob. A return value
|
||||
// of 0 means there is no bandwidth available. To find out if a channel
|
||||
// is a custom channel that should be handled by the traffic shaper, the
|
||||
// HandleTraffic method should be called first.
|
||||
PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob],
|
||||
linkBandwidth,
|
||||
htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error)
|
||||
}
|
||||
|
||||
// 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(totalAmount lnwire.MilliSatoshi,
|
||||
htlcCustomRecords lnwire.CustomRecords) (lnwire.MilliSatoshi,
|
||||
lnwire.CustomRecords, error)
|
||||
}
|
||||
|
||||
// getLinkQuery is the function signature used to lookup a link.
|
||||
type getLinkQuery func(lnwire.ShortChannelID) (
|
||||
htlcswitch.ChannelLink, error)
|
||||
@ -73,7 +40,7 @@ type bandwidthManager struct {
|
||||
getLink getLinkQuery
|
||||
localChans map[lnwire.ShortChannelID]struct{}
|
||||
firstHopBlob fn.Option[tlv.Blob]
|
||||
trafficShaper fn.Option[TlvTrafficShaper]
|
||||
trafficShaper fn.Option[htlcswitch.AuxTrafficShaper]
|
||||
}
|
||||
|
||||
// newBandwidthManager creates a bandwidth manager for the source node provided
|
||||
@ -84,13 +51,14 @@ type bandwidthManager struct {
|
||||
// that are inactive, or just don't have enough bandwidth to carry the payment.
|
||||
func newBandwidthManager(graph Graph, sourceNode route.Vertex,
|
||||
linkQuery getLinkQuery, firstHopBlob fn.Option[tlv.Blob],
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (*bandwidthManager, error) {
|
||||
ts fn.Option[htlcswitch.AuxTrafficShaper]) (*bandwidthManager,
|
||||
error) {
|
||||
|
||||
manager := &bandwidthManager{
|
||||
getLink: linkQuery,
|
||||
localChans: make(map[lnwire.ShortChannelID]struct{}),
|
||||
firstHopBlob: firstHopBlob,
|
||||
trafficShaper: trafficShaper,
|
||||
trafficShaper: ts,
|
||||
}
|
||||
|
||||
// First, we'll collect the set of outbound edges from the target
|
||||
@ -166,44 +134,15 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
|
||||
|
||||
result, err := fn.MapOptionZ(
|
||||
b.trafficShaper,
|
||||
func(ts TlvTrafficShaper) fn.Result[bandwidthResult] {
|
||||
fundingBlob := link.FundingCustomBlob()
|
||||
shouldHandle, err := ts.ShouldHandleTraffic(
|
||||
cid, fundingBlob,
|
||||
)
|
||||
if err != nil {
|
||||
return bandwidthErr(fmt.Errorf("traffic "+
|
||||
"shaper failed to decide whether to "+
|
||||
"handle traffic: %w", err))
|
||||
}
|
||||
|
||||
log.Debugf("ShortChannelID=%v: external traffic "+
|
||||
"shaper is handling traffic: %v", cid,
|
||||
shouldHandle)
|
||||
|
||||
// If this channel isn't handled by the external traffic
|
||||
// shaper, we'll return early.
|
||||
if !shouldHandle {
|
||||
return fn.Ok(bandwidthResult{})
|
||||
}
|
||||
|
||||
// Ask for a specific bandwidth to be used for the
|
||||
// channel.
|
||||
commitmentBlob := link.CommitmentCustomBlob()
|
||||
auxBandwidth, err := ts.PaymentBandwidth(
|
||||
b.firstHopBlob, commitmentBlob, linkBandwidth,
|
||||
amount,
|
||||
)
|
||||
func(s htlcswitch.AuxTrafficShaper) fn.Result[bandwidthResult] {
|
||||
auxBandwidth, err := link.AuxBandwidth(
|
||||
amount, cid, b.firstHopBlob, s,
|
||||
).Unpack()
|
||||
if err != nil {
|
||||
return bandwidthErr(fmt.Errorf("failed to get "+
|
||||
"bandwidth from external traffic "+
|
||||
"shaper: %w", err))
|
||||
"auxiliary bandwidth: %w", err))
|
||||
}
|
||||
|
||||
log.Debugf("ShortChannelID=%v: external traffic "+
|
||||
"shaper reported available bandwidth: %v", cid,
|
||||
auxBandwidth)
|
||||
|
||||
// We don't know the actual HTLC amount that will be
|
||||
// sent using the custom channel. But we'll still want
|
||||
// to make sure we can add another HTLC, using the
|
||||
@ -213,7 +152,7 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
|
||||
// the max number of HTLCs on the channel. A proper
|
||||
// balance check is done elsewhere.
|
||||
return fn.Ok(bandwidthResult{
|
||||
bandwidth: fn.Some(auxBandwidth),
|
||||
bandwidth: auxBandwidth,
|
||||
htlcAmount: fn.Some[lnwire.MilliSatoshi](0),
|
||||
})
|
||||
},
|
||||
|
@ -118,7 +118,9 @@ func TestBandwidthManager(t *testing.T) {
|
||||
m, err := newBandwidthManager(
|
||||
g, sourceNode.pubkey, testCase.linkQuery,
|
||||
fn.None[[]byte](),
|
||||
fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -107,7 +107,7 @@ var _ PaymentSessionSource = (*mockPaymentSessionSourceOld)(nil)
|
||||
|
||||
func (m *mockPaymentSessionSourceOld) NewPaymentSession(
|
||||
_ *LightningPayment, _ fn.Option[tlv.Blob],
|
||||
_ fn.Option[TlvTrafficShaper]) (PaymentSession, error) {
|
||||
_ fn.Option[htlcswitch.AuxTrafficShaper]) (PaymentSession, error) {
|
||||
|
||||
return &mockPaymentSessionOld{
|
||||
routes: m.routes,
|
||||
@ -635,7 +635,8 @@ var _ PaymentSessionSource = (*mockPaymentSessionSource)(nil)
|
||||
|
||||
func (m *mockPaymentSessionSource) NewPaymentSession(
|
||||
payment *LightningPayment, firstHopBlob fn.Option[tlv.Blob],
|
||||
tlvShaper fn.Option[TlvTrafficShaper]) (PaymentSession, error) {
|
||||
tlvShaper fn.Option[htlcswitch.AuxTrafficShaper]) (PaymentSession,
|
||||
error) {
|
||||
|
||||
args := m.Called(payment, firstHopBlob, tlvShaper)
|
||||
return args.Get(0).(PaymentSession), args.Error(1)
|
||||
@ -895,6 +896,19 @@ func (m *mockLink) Bandwidth() lnwire.MilliSatoshi {
|
||||
return m.bandwidth
|
||||
}
|
||||
|
||||
// AuxBandwidth returns the bandwidth that can be used for a channel,
|
||||
// expressed in milli-satoshi. This might be different from the regular
|
||||
// BTC bandwidth for custom channels. This will always return fn.None()
|
||||
// for a regular (non-custom) channel.
|
||||
func (m *mockLink) AuxBandwidth(lnwire.MilliSatoshi, lnwire.ShortChannelID,
|
||||
fn.Option[tlv.Blob],
|
||||
htlcswitch.AuxTrafficShaper) fn.Result[htlcswitch.OptionalBandwidth] {
|
||||
|
||||
return fn.Ok[htlcswitch.OptionalBandwidth](
|
||||
fn.None[lnwire.MilliSatoshi](),
|
||||
)
|
||||
}
|
||||
|
||||
// EligibleToForward returns the mock's configured eligibility.
|
||||
func (m *mockLink) EligibleToForward() bool {
|
||||
return !m.ineligible
|
||||
|
@ -761,7 +761,8 @@ func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error {
|
||||
// and apply its side effects to the UpdateAddHTLC message.
|
||||
result, err := fn.MapOptionZ(
|
||||
p.router.cfg.TrafficShaper,
|
||||
func(ts TlvTrafficShaper) fn.Result[extraDataRequest] {
|
||||
//nolint:ll
|
||||
func(ts htlcswitch.AuxTrafficShaper) fn.Result[extraDataRequest] {
|
||||
newAmt, newRecords, err := ts.ProduceHtlcExtraData(
|
||||
rt.TotalAmount, p.firstHopCustomRecords,
|
||||
)
|
||||
@ -774,7 +775,7 @@ func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error {
|
||||
return fn.Err[extraDataRequest](err)
|
||||
}
|
||||
|
||||
log.Debugf("TLV traffic shaper returned custom "+
|
||||
log.Debugf("Aux traffic shaper returned custom "+
|
||||
"records %v and amount %d msat for HTLC",
|
||||
spew.Sdump(newRecords), newAmt)
|
||||
|
||||
|
@ -30,7 +30,7 @@ func createTestPaymentLifecycle() *paymentLifecycle {
|
||||
quitChan := make(chan struct{})
|
||||
rt := &ChannelRouter{
|
||||
cfg: &Config{
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
},
|
||||
@ -83,7 +83,7 @@ func newTestPaymentLifecycle(t *testing.T) (*paymentLifecycle, *mockers) {
|
||||
Payer: mockPayer,
|
||||
Clock: mockClock,
|
||||
MissionControl: mockMissionControl,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
},
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"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"
|
||||
@ -53,7 +54,8 @@ type SessionSource struct {
|
||||
// payment's destination.
|
||||
func (m *SessionSource) NewPaymentSession(p *LightningPayment,
|
||||
firstHopBlob fn.Option[tlv.Blob],
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (PaymentSession, error) {
|
||||
trafficShaper fn.Option[htlcswitch.AuxTrafficShaper]) (PaymentSession,
|
||||
error) {
|
||||
|
||||
getBandwidthHints := func(graph Graph) (bandwidthHints, error) {
|
||||
return newBandwidthManager(
|
||||
|
@ -157,7 +157,7 @@ type PaymentSessionSource interface {
|
||||
// finding a path to the payment's destination.
|
||||
NewPaymentSession(p *LightningPayment,
|
||||
firstHopBlob fn.Option[tlv.Blob],
|
||||
trafficShaper fn.Option[TlvTrafficShaper]) (PaymentSession,
|
||||
ts fn.Option[htlcswitch.AuxTrafficShaper]) (PaymentSession,
|
||||
error)
|
||||
|
||||
// NewPaymentSessionEmpty creates a new paymentSession instance that is
|
||||
@ -297,7 +297,7 @@ type Config struct {
|
||||
|
||||
// TrafficShaper is an optional traffic shaper that can be used to
|
||||
// control the outgoing channel of a payment.
|
||||
TrafficShaper fn.Option[TlvTrafficShaper]
|
||||
TrafficShaper fn.Option[htlcswitch.AuxTrafficShaper]
|
||||
}
|
||||
|
||||
// EdgeLocator is a struct used to identify a specific edge.
|
||||
|
@ -164,7 +164,7 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
||||
Clock: clock.NewTestClock(time.Unix(1, 0)),
|
||||
ApplyChannelUpdate: graphBuilder.ApplyChannelUpdate,
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
})
|
||||
@ -2194,8 +2194,10 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
return 0, nil
|
||||
},
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
}}
|
||||
|
||||
// Register mockers with the expected method calls.
|
||||
@ -2279,8 +2281,10 @@ func TestSendToRouteSkipTempErrNonMPP(t *testing.T) {
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
return 0, nil
|
||||
},
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
}}
|
||||
|
||||
// Expect an error to be returned.
|
||||
@ -2335,8 +2339,10 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
return 0, nil
|
||||
},
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
}}
|
||||
|
||||
// Create the error to be returned.
|
||||
@ -2419,8 +2425,10 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
return 0, nil
|
||||
},
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
}}
|
||||
|
||||
// Create the error to be returned.
|
||||
@ -2507,8 +2515,10 @@ func TestSendToRouteTempFailure(t *testing.T) {
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
return 0, nil
|
||||
},
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}),
|
||||
ClosedSCIDs: mockClosedSCIDs,
|
||||
TrafficShaper: fn.Some[htlcswitch.AuxTrafficShaper](
|
||||
&mockTrafficShaper{},
|
||||
),
|
||||
}}
|
||||
|
||||
// Create the error to be returned.
|
||||
|
@ -4126,6 +4126,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
|
||||
MsgRouter: s.implCfg.MsgRouter,
|
||||
AuxChanCloser: s.implCfg.AuxChanCloser,
|
||||
AuxResolver: s.implCfg.AuxContractResolver,
|
||||
AuxTrafficShaper: s.implCfg.TrafficShaper,
|
||||
}
|
||||
|
||||
copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed())
|
||||
|
Loading…
x
Reference in New Issue
Block a user