Merge pull request #10434 from GeorgeTsagk/extra-aux-band-check
Add `AuxHtlcValidator`
This commit is contained in:
4 files changed
+441
-3
No files matched your search
@@ -0,0 +1,31 @@
|
|||||||
|
package lnwallet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTestAuxHtlcDescriptor creates an AuxHtlcDescriptor for testing purposes.
|
||||||
|
// This function allows tests to create descriptors with specific commit heights
|
||||||
|
// and entry types, which are normally unexported fields.
|
||||||
|
func NewTestAuxHtlcDescriptor(chanID lnwire.ChannelID,
|
||||||
|
rHash PaymentHash, timeout uint32,
|
||||||
|
amount lnwire.MilliSatoshi, htlcIndex, parentIndex uint64,
|
||||||
|
entryType uint8, customRecords lnwire.CustomRecords,
|
||||||
|
addHeightLocal, addHeightRemote, removeHeightLocal,
|
||||||
|
removeHeightRemote uint64) AuxHtlcDescriptor {
|
||||||
|
|
||||||
|
return AuxHtlcDescriptor{
|
||||||
|
ChanID: chanID,
|
||||||
|
RHash: rHash,
|
||||||
|
Timeout: timeout,
|
||||||
|
Amount: amount,
|
||||||
|
HtlcIndex: htlcIndex,
|
||||||
|
ParentIndex: parentIndex,
|
||||||
|
EntryType: updateType(entryType),
|
||||||
|
CustomRecords: customRecords,
|
||||||
|
addCommitHeightLocal: addHeightLocal,
|
||||||
|
addCommitHeightRemote: addHeightRemote,
|
||||||
|
removeCommitHeightLocal: removeHeightLocal,
|
||||||
|
removeCommitHeightRemote: removeHeightRemote,
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
-3
@@ -833,6 +833,20 @@ type LightningChannel struct {
|
|||||||
// is created.
|
// is created.
|
||||||
type ChannelOpt func(*channelOpts)
|
type ChannelOpt func(*channelOpts)
|
||||||
|
|
||||||
|
// AuxHtlcValidator is an interface for validating whether an HTLC can be added
|
||||||
|
// to a custom channel. It is called during HTLC validation with the current
|
||||||
|
// channel state and HTLC details. This allows external components (like the
|
||||||
|
// traffic shaper) to perform final validation checks against the most
|
||||||
|
// up-to-date channel state before the HTLC is committed.
|
||||||
|
type AuxHtlcValidator interface {
|
||||||
|
// ValidateHtlc checks whether the given HTLC can be added to the
|
||||||
|
// channel given the current link bandwidth, custom records, and HTLC
|
||||||
|
// view.
|
||||||
|
ValidateHtlc(amount, linkBandwidth lnwire.MilliSatoshi,
|
||||||
|
customRecords lnwire.CustomRecords,
|
||||||
|
view AuxHtlcView) error
|
||||||
|
}
|
||||||
|
|
||||||
// channelOpts is the set of options used to create a new channel.
|
// channelOpts is the set of options used to create a new channel.
|
||||||
type channelOpts struct {
|
type channelOpts struct {
|
||||||
localNonce *musig2.Nonces
|
localNonce *musig2.Nonces
|
||||||
@@ -842,6 +856,10 @@ type channelOpts struct {
|
|||||||
auxSigner fn.Option[AuxSigner]
|
auxSigner fn.Option[AuxSigner]
|
||||||
auxResolver fn.Option[AuxContractResolver]
|
auxResolver fn.Option[AuxContractResolver]
|
||||||
|
|
||||||
|
// auxHtlcValidator is an optional validator that performs custom
|
||||||
|
// validation on HTLCs before they are added to the channel state.
|
||||||
|
auxHtlcValidator fn.Option[AuxHtlcValidator]
|
||||||
|
|
||||||
skipNonceInit bool
|
skipNonceInit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,6 +912,15 @@ func WithAuxResolver(resolver AuxContractResolver) ChannelOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithAuxHtlcValidator is used to specify a custom HTLC validator for the
|
||||||
|
// channel. This validator will be called during HTLC addition to perform
|
||||||
|
// final validation checks against the most up-to-date channel state.
|
||||||
|
func WithAuxHtlcValidator(validator AuxHtlcValidator) ChannelOpt {
|
||||||
|
return func(o *channelOpts) {
|
||||||
|
o.auxHtlcValidator = fn.Some(validator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// defaultChannelOpts returns the set of default options for a new channel.
|
// defaultChannelOpts returns the set of default options for a new channel.
|
||||||
func defaultChannelOpts() *channelOpts {
|
func defaultChannelOpts() *channelOpts {
|
||||||
return &channelOpts{}
|
return &channelOpts{}
|
||||||
@@ -2738,9 +2765,21 @@ func (lc *LightningChannel) FetchLatestAuxHTLCView() AuxHtlcView {
|
|||||||
lc.RLock()
|
lc.RLock()
|
||||||
defer lc.RUnlock()
|
defer lc.RUnlock()
|
||||||
|
|
||||||
return newAuxHtlcView(lc.fetchHTLCView(
|
nextHeight := lc.commitChains.Local.tip().height + 1
|
||||||
lc.updateLogs.Remote.logIndex, lc.updateLogs.Local.logIndex,
|
|
||||||
))
|
// We use the remote ACKed index from the last signed local commitment
|
||||||
|
// (tail) rather than the remote's latest log index. This ensures we
|
||||||
|
// only include remote HTLCs that have been locked into a signed
|
||||||
|
// commitment, giving the aux validator a stable, consistent view that
|
||||||
|
// matches the actual commitment state used for balance calculations.
|
||||||
|
remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote
|
||||||
|
view := lc.fetchHTLCView(
|
||||||
|
remoteACKedIndex, lc.updateLogs.Local.logIndex,
|
||||||
|
)
|
||||||
|
|
||||||
|
view.NextHeight = nextHeight
|
||||||
|
|
||||||
|
return newAuxHtlcView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchHTLCView returns all the candidate HTLC updates which should be
|
// fetchHTLCView returns all the candidate HTLC updates which should be
|
||||||
@@ -6065,6 +6104,52 @@ func (lc *LightningChannel) addHTLC(htlc *lnwire.UpdateAddHTLC,
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If an auxiliary HTLC validator is configured, call it now to perform
|
||||||
|
// custom validation checks against the current channel state. This is
|
||||||
|
// the final validation point before the HTLC is added to the update
|
||||||
|
// log, ensuring that the validator sees the most up-to-date state
|
||||||
|
// including all previously validated HTLCs in this batch.
|
||||||
|
//
|
||||||
|
// NOTE: This is called after the standard commitment sanity checks to
|
||||||
|
// ensure we only perform (potentially) expensive custom validation on
|
||||||
|
// HTLCs that have already passed the basic Lightning protocol
|
||||||
|
// constraints.
|
||||||
|
err := fn.MapOptionZ(
|
||||||
|
lc.opts.auxHtlcValidator,
|
||||||
|
func(validator AuxHtlcValidator) error {
|
||||||
|
// Fetch the current HTLC view which includes all
|
||||||
|
// pending HTLCs that haven't been committed yet. This
|
||||||
|
// provides the validator with the most accurate state.
|
||||||
|
commitChain := lc.commitChains.Local
|
||||||
|
remoteIndex := commitChain.tail().messageIndices.Remote
|
||||||
|
view := lc.fetchHTLCView(
|
||||||
|
remoteIndex, lc.updateLogs.Local.logIndex,
|
||||||
|
)
|
||||||
|
|
||||||
|
nextHeight := lc.commitChains.Local.tip().height + 1
|
||||||
|
view.NextHeight = nextHeight
|
||||||
|
|
||||||
|
lc.log.Debugf("Setting view nextheight=%v", nextHeight)
|
||||||
|
|
||||||
|
auxView := newAuxHtlcView(view)
|
||||||
|
|
||||||
|
// Get the current available balance for the link
|
||||||
|
// bandwidth check. This is needed for the balance
|
||||||
|
// validation in the traffic shaper. We use NoBuffer
|
||||||
|
// since the buffer check was already performed earlier,
|
||||||
|
// and assets don't pay on-chain fees.
|
||||||
|
linkBandwidth, _ := lc.availableBalance(NoBuffer)
|
||||||
|
|
||||||
|
return validator.ValidateHtlc(
|
||||||
|
pd.Amount, linkBandwidth, pd.CustomRecords,
|
||||||
|
auxView,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("aux HTLC validation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
lc.updateLogs.Local.appendHtlc(pd)
|
lc.updateLogs.Local.appendHtlc(pd)
|
||||||
|
|
||||||
return pd.HtlcIndex, nil
|
return pd.HtlcIndex, nil
|
||||||
@@ -6216,6 +6301,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC,
|
|||||||
// remote commitments.
|
// remote commitments.
|
||||||
func (lc *LightningChannel) validateAddHtlc(pd *paymentDescriptor,
|
func (lc *LightningChannel) validateAddHtlc(pd *paymentDescriptor,
|
||||||
buffer BufferType) error {
|
buffer BufferType) error {
|
||||||
|
|
||||||
// Make sure adding this HTLC won't violate any of the constraints we
|
// Make sure adding this HTLC won't violate any of the constraints we
|
||||||
// must keep on the commitment transactions.
|
// must keep on the commitment transactions.
|
||||||
remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote
|
remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/pool"
|
"github.com/lightningnetwork/lnd/pool"
|
||||||
"github.com/lightningnetwork/lnd/protofsm"
|
"github.com/lightningnetwork/lnd/protofsm"
|
||||||
"github.com/lightningnetwork/lnd/queue"
|
"github.com/lightningnetwork/lnd/queue"
|
||||||
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/subscribe"
|
"github.com/lightningnetwork/lnd/subscribe"
|
||||||
"github.com/lightningnetwork/lnd/ticker"
|
"github.com/lightningnetwork/lnd/ticker"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
@@ -1146,6 +1147,16 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
p.cfg.AuxTrafficShaper.WhenSome(
|
||||||
|
func(ts htlcswitch.AuxTrafficShaper) {
|
||||||
|
val := p.createHtlcValidator(dbChan, ts)
|
||||||
|
chanOpts = append(
|
||||||
|
chanOpts,
|
||||||
|
lnwallet.WithAuxHtlcValidator(val),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
lnChan, err := lnwallet.NewLightningChannel(
|
lnChan, err := lnwallet.NewLightningChannel(
|
||||||
p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts...,
|
p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts...,
|
||||||
)
|
)
|
||||||
@@ -5259,6 +5270,15 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error {
|
|||||||
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
|
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
p.cfg.AuxTrafficShaper.WhenSome(
|
||||||
|
func(ts htlcswitch.AuxTrafficShaper) {
|
||||||
|
val := p.createHtlcValidator(c.OpenChannel, ts)
|
||||||
|
chanOpts = append(
|
||||||
|
chanOpts, lnwallet.WithAuxHtlcValidator(val),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// If not already active, we'll add this channel to the set of active
|
// If not already active, we'll add this channel to the set of active
|
||||||
// channels, so we can look it up later easily according to its channel
|
// channels, so we can look it up later easily according to its channel
|
||||||
// ID.
|
// ID.
|
||||||
@@ -5465,6 +5485,96 @@ func (p *Brontide) scaleTimeout(timeout time.Duration) time.Duration {
|
|||||||
return timeout
|
return timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// auxHtlcValidator implements lnwallet.AuxHtlcValidator by checking HTLC
|
||||||
|
// bandwidth against the traffic shaper.
|
||||||
|
type auxHtlcValidator struct {
|
||||||
|
peer *Brontide
|
||||||
|
dbChan *channeldb.OpenChannel
|
||||||
|
ts htlcswitch.AuxTrafficShaper
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateHtlc performs final aux balance validation before an HTLC is added
|
||||||
|
// to the channel state. It calls into the traffic shaper's PaymentBandwidth
|
||||||
|
// method to check external balance against the most up-to-date channel state,
|
||||||
|
// preventing race conditions where multiple HTLCs could be approved based on
|
||||||
|
// stale bandwidth.
|
||||||
|
func (v *auxHtlcValidator) ValidateHtlc(amount,
|
||||||
|
linkBandwidth lnwire.MilliSatoshi,
|
||||||
|
customRecords lnwire.CustomRecords,
|
||||||
|
view lnwallet.AuxHtlcView) error {
|
||||||
|
|
||||||
|
// Get the short channel ID for logging.
|
||||||
|
scid := v.dbChan.ShortChannelID
|
||||||
|
|
||||||
|
// Extract the HTLC custom records to pass to the traffic shaper.
|
||||||
|
var htlcBlob fn.Option[tlv.Blob]
|
||||||
|
if len(customRecords) > 0 {
|
||||||
|
blob, err := customRecords.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to serialize "+
|
||||||
|
"custom records: %w", err)
|
||||||
|
}
|
||||||
|
htlcBlob = fn.Some(blob)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the funding and commitment blobs for this channel.
|
||||||
|
fundingBlob := v.dbChan.CustomBlob
|
||||||
|
commitmentBlob := v.dbChan.LocalCommitment.CustomBlob
|
||||||
|
|
||||||
|
// Check if this channel should be handled by the traffic shaper. If
|
||||||
|
// not, we skip the aux validation entirely and allow the HTLC to
|
||||||
|
// proceed through normal validation.
|
||||||
|
shouldHandle, err := v.ts.ShouldHandleTraffic(
|
||||||
|
scid, fundingBlob, htlcBlob,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("traffic shaper failed to decide "+
|
||||||
|
"whether to handle traffic: %w", err)
|
||||||
|
}
|
||||||
|
if !shouldHandle {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := route.NewVertex(v.peer.IdentityKey())
|
||||||
|
|
||||||
|
// Call the traffic shaper's PaymentBandwidth method with the current
|
||||||
|
// state. This performs the same bandwidth checks as during
|
||||||
|
// pathfinding/forwarding, but against the absolute latest channel
|
||||||
|
// state.
|
||||||
|
//
|
||||||
|
// The linkBandwidth is provided by the channel and represents the
|
||||||
|
// current available balance, which is used by the traffic shaper to
|
||||||
|
// ensure we don't dip below channel reserves.
|
||||||
|
bandwidth, err := v.ts.PaymentBandwidth(
|
||||||
|
fundingBlob, htlcBlob, commitmentBlob,
|
||||||
|
linkBandwidth, amount, view, peer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("traffic shaper bandwidth check "+
|
||||||
|
"failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if amount > bandwidth {
|
||||||
|
return fmt.Errorf("insufficient aux bandwidth: "+
|
||||||
|
"need %v, have %v (scid=%v)", amount,
|
||||||
|
bandwidth, scid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createHtlcValidator creates an HTLC validator that performs final aux balance
|
||||||
|
// validation before HTLCs are added to the channel state.
|
||||||
|
func (p *Brontide) createHtlcValidator(dbChan *channeldb.OpenChannel,
|
||||||
|
ts htlcswitch.AuxTrafficShaper) lnwallet.AuxHtlcValidator {
|
||||||
|
|
||||||
|
return &auxHtlcValidator{
|
||||||
|
peer: p,
|
||||||
|
dbChan: dbChan,
|
||||||
|
ts: ts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CoopCloseUpdates is a struct used to communicate updates for an active close
|
// CoopCloseUpdates is a struct used to communicate updates for an active close
|
||||||
// to the caller.
|
// to the caller.
|
||||||
type CoopCloseUpdates struct {
|
type CoopCloseUpdates struct {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
@@ -19,6 +20,9 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1466,3 +1470,210 @@ func TestRemovePendingChannel(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mockAuxTrafficShaper is a mock implementation of htlcswitch.AuxTrafficShaper
|
||||||
|
// for testing the createHtlcValidator function.
|
||||||
|
type mockAuxTrafficShaper struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldHandleTraffic returns the configured mock values.
|
||||||
|
func (m *mockAuxTrafficShaper) ShouldHandleTraffic(
|
||||||
|
cid lnwire.ShortChannelID,
|
||||||
|
fundingBlob, htlcBlob fn.Option[tlv.Blob]) (bool, error) {
|
||||||
|
|
||||||
|
args := m.Called(cid, fundingBlob, htlcBlob)
|
||||||
|
return args.Bool(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentBandwidth returns the configured mock values.
|
||||||
|
func (m *mockAuxTrafficShaper) PaymentBandwidth(fundingBlob, htlcBlob,
|
||||||
|
commitmentBlob fn.Option[tlv.Blob], linkBandwidth,
|
||||||
|
htlcAmt lnwire.MilliSatoshi, htlcView lnwallet.AuxHtlcView,
|
||||||
|
peer route.Vertex) (lnwire.MilliSatoshi, error) {
|
||||||
|
|
||||||
|
args := m.Called(
|
||||||
|
fundingBlob, htlcBlob, commitmentBlob, linkBandwidth,
|
||||||
|
htlcAmt, htlcView, peer,
|
||||||
|
)
|
||||||
|
|
||||||
|
bw, _ := args.Get(0).(lnwire.MilliSatoshi)
|
||||||
|
|
||||||
|
return bw, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProduceHtlcExtraData is part of the AuxTrafficShaper interface.
|
||||||
|
func (m *mockAuxTrafficShaper) ProduceHtlcExtraData(
|
||||||
|
totalAmount lnwire.MilliSatoshi,
|
||||||
|
htlcCustomRecords lnwire.CustomRecords,
|
||||||
|
peer route.Vertex) (lnwire.MilliSatoshi, lnwire.CustomRecords,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
args := m.Called(totalAmount, htlcCustomRecords, peer)
|
||||||
|
|
||||||
|
amt, _ := args.Get(0).(lnwire.MilliSatoshi)
|
||||||
|
records, _ := args.Get(1).(lnwire.CustomRecords)
|
||||||
|
|
||||||
|
return amt, records, args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCustomHTLC is part of the AuxTrafficShaper interface.
|
||||||
|
func (m *mockAuxTrafficShaper) IsCustomHTLC(
|
||||||
|
htlcRecords lnwire.CustomRecords) bool {
|
||||||
|
|
||||||
|
args := m.Called(htlcRecords)
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time check that mockAuxTrafficShaper implements AuxTrafficShaper.
|
||||||
|
var _ htlcswitch.AuxTrafficShaper = (*mockAuxTrafficShaper)(nil)
|
||||||
|
|
||||||
|
// TestCreateHtlcValidator tests that the HTLC validator created by
|
||||||
|
// createHtlcValidator respects the ShouldHandleTraffic check. When
|
||||||
|
// ShouldHandleTraffic returns false, the validator should return nil without
|
||||||
|
// calling PaymentBandwidth.
|
||||||
|
func TestCreateHtlcValidator(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a minimal Brontide with just the identity key set.
|
||||||
|
privKey, err := btcec.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peer := &Brontide{
|
||||||
|
cfg: Config{
|
||||||
|
Addr: &lnwire.NetAddress{
|
||||||
|
IdentityKey: privKey.PubKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a mock channel with minimal required fields.
|
||||||
|
dbChan := &channeldb.OpenChannel{
|
||||||
|
ShortChannelID: lnwire.NewShortChanIDFromInt(123),
|
||||||
|
}
|
||||||
|
|
||||||
|
anyArg := mock.Anything
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
setupMock func(*mockAuxTrafficShaper)
|
||||||
|
htlcAmount lnwire.MilliSatoshi
|
||||||
|
linkBw lnwire.MilliSatoshi
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "non-custom channel skips check",
|
||||||
|
setupMock: func(m *mockAuxTrafficShaper) {
|
||||||
|
m.On(
|
||||||
|
"ShouldHandleTraffic",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
).Return(false, nil)
|
||||||
|
},
|
||||||
|
htlcAmount: 1000,
|
||||||
|
linkBw: 5000,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sufficient bandwidth",
|
||||||
|
setupMock: func(m *mockAuxTrafficShaper) {
|
||||||
|
m.On(
|
||||||
|
"ShouldHandleTraffic",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
).Return(true, nil)
|
||||||
|
m.On(
|
||||||
|
"PaymentBandwidth",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg,
|
||||||
|
).Return(
|
||||||
|
lnwire.MilliSatoshi(10000),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
htlcAmount: 1000,
|
||||||
|
linkBw: 5000,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "insufficient bandwidth",
|
||||||
|
setupMock: func(m *mockAuxTrafficShaper) {
|
||||||
|
m.On(
|
||||||
|
"ShouldHandleTraffic",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
).Return(true, nil)
|
||||||
|
m.On(
|
||||||
|
"PaymentBandwidth",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg,
|
||||||
|
).Return(
|
||||||
|
lnwire.MilliSatoshi(500),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
htlcAmount: 1000,
|
||||||
|
linkBw: 5000,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ShouldHandleTraffic error",
|
||||||
|
setupMock: func(m *mockAuxTrafficShaper) {
|
||||||
|
m.On(
|
||||||
|
"ShouldHandleTraffic",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
).Return(
|
||||||
|
false,
|
||||||
|
fmt.Errorf("shaper error"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
htlcAmount: 1000,
|
||||||
|
linkBw: 5000,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PaymentBandwidth error",
|
||||||
|
setupMock: func(m *mockAuxTrafficShaper) {
|
||||||
|
m.On(
|
||||||
|
"ShouldHandleTraffic",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
).Return(true, nil)
|
||||||
|
m.On(
|
||||||
|
"PaymentBandwidth",
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg, anyArg, anyArg,
|
||||||
|
anyArg,
|
||||||
|
).Return(
|
||||||
|
lnwire.MilliSatoshi(0),
|
||||||
|
fmt.Errorf("bandwidth error"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
htlcAmount: 1000,
|
||||||
|
linkBw: 5000,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
m := &mockAuxTrafficShaper{}
|
||||||
|
tc.setupMock(m)
|
||||||
|
|
||||||
|
validator := peer.createHtlcValidator(
|
||||||
|
dbChan, m,
|
||||||
|
)
|
||||||
|
|
||||||
|
err := validator.ValidateHtlc(
|
||||||
|
tc.htlcAmount, tc.linkBw,
|
||||||
|
nil, lnwallet.AuxHtlcView{},
|
||||||
|
)
|
||||||
|
|
||||||
|
if tc.expectError {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user