peer: set and use aux htlc validator

When instantiating the lightning channel we now pass in the created HTLC
validator. This validator simply performs a bandwidth check and errors
out if that is insufficient.
This commit is contained in:
George Tsagkarelis committed 2026-03-04 20:02:07 +01:00
1 parent 773b2c1c30
commit b1701cf232
1 file changed
+110
+110
View File
@@ -52,6 +52,7 @@ import (
"github.com/lightningnetwork/lnd/pool"
"github.com/lightningnetwork/lnd/protofsm"
"github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/subscribe"
"github.com/lightningnetwork/lnd/ticker"
"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(
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))
})
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
// channels, so we can look it up later easily according to its channel
// ID.
@@ -5465,6 +5485,96 @@ func (p *Brontide) scaleTimeout(timeout time.Duration) time.Duration {
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
// to the caller.
type CoopCloseUpdates struct {