@@ -3372,36 +3372,20 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
33723372 heightNow uint32 , originalScid lnwire.ShortChannelID ,
33733373 customRecords lnwire.CustomRecords ) * LinkError {
33743374
3375- // As our first sanity check, we'll ensure that the passed HTLC isn't
3376- // too small for the next hop. If so, then we'll cancel the HTLC
3377- // directly.
3378- if amt < policy .MinHTLCOut {
3379- l .log .Warnf ("outgoing htlc(%x) is too small: min_htlc=%v, " +
3380- "htlc_value=%v" , payHash [:], policy .MinHTLCOut ,
3381- amt )
3375+ var (
3376+ auxBandwidth OptionalBandwidth
33823377
3383- // As part of the returned error, we'll send our latest routing
3384- // policy so the sending node obtains the most up to date data.
3385- cb := func (upd * lnwire.ChannelUpdate1 ) lnwire.FailureMessage {
3386- return lnwire .NewAmountBelowMinimum (amt , * upd )
3387- }
3388- failure := l .createFailureWithUpdate (false , originalScid , cb )
3389- return NewLinkError (failure )
3390- }
3391-
3392- // Next, ensure that the passed HTLC isn't too large. If so, we'll
3393- // cancel the HTLC directly.
3394- if policy .MaxHTLC != 0 && amt > policy .MaxHTLC {
3395- l .log .Warnf ("outgoing htlc(%x) is too large: max_htlc=%v, " +
3396- "htlc_value=%v" , payHash [:], policy .MaxHTLC , amt )
3378+ // externalErr is an error that is returned by the aux traffic
3379+ // shaper.
3380+ externalErr error
3381+ )
33973382
3398- // As part of the returned error, we'll send our latest routing
3399- // policy so the sending node obtains the most up-to-date data.
3400- cb := func (upd * lnwire.ChannelUpdate1 ) lnwire.FailureMessage {
3401- return lnwire .NewTemporaryChannelFailure (upd )
3402- }
3403- failure := l .createFailureWithUpdate (false , originalScid , cb )
3404- return NewDetailedLinkError (failure , OutgoingFailureHTLCExceedsMax )
3383+ // Validate HTLC amount against policy limits.
3384+ linkErr := l .validateHtlcAmount (
3385+ policy , payHash , amt , originalScid , customRecords ,
3386+ )
3387+ if linkErr != nil {
3388+ return linkErr
34053389 }
34063390
34073391 // We want to avoid offering an HTLC which will expire in the near
@@ -3416,6 +3400,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
34163400 return lnwire .NewExpiryTooSoon (* upd )
34173401 }
34183402 failure := l .createFailureWithUpdate (false , originalScid , cb )
3403+
34193404 return NewLinkError (failure )
34203405 }
34213406
@@ -3431,7 +3416,8 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
34313416 // We now check the available bandwidth to see if this HTLC can be
34323417 // forwarded.
34333418 availableBandwidth := l .Bandwidth ()
3434- auxBandwidth , err := fn .MapOptionZ (
3419+
3420+ auxBandwidth , externalErr = fn .MapOptionZ (
34353421 l .cfg .AuxTrafficShaper ,
34363422 func (ts AuxTrafficShaper ) fn.Result [OptionalBandwidth ] {
34373423 var htlcBlob fn.Option [tlv.Blob ]
@@ -3449,8 +3435,10 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
34493435 return l .AuxBandwidth (amt , originalScid , htlcBlob , ts )
34503436 },
34513437 ).Unpack ()
3452- if err != nil {
3453- l .log .Errorf ("Unable to determine aux bandwidth: %v" , err )
3438+ if externalErr != nil {
3439+ l .log .Errorf ("Unable to determine aux bandwidth: %v" ,
3440+ externalErr )
3441+
34543442 return NewLinkError (& lnwire.FailTemporaryNodeFailure {})
34553443 }
34563444
@@ -3470,6 +3458,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
34703458 return lnwire .NewTemporaryChannelFailure (upd )
34713459 }
34723460 failure := l .createFailureWithUpdate (false , originalScid , cb )
3461+
34733462 return NewDetailedLinkError (
34743463 failure , OutgoingFailureInsufficientBalance ,
34753464 )
@@ -4579,3 +4568,71 @@ func (l *channelLink) CommitmentCustomBlob() fn.Option[tlv.Blob] {
45794568
45804569 return l .channel .LocalCommitmentBlob ()
45814570}
4571+
4572+ // validateHtlcAmount checks if the HTLC amount is within the policy's
4573+ // minimum and maximum limits. Returns a LinkError if validation fails.
4574+ func (l * channelLink ) validateHtlcAmount (policy models.ForwardingPolicy ,
4575+ payHash [32 ]byte , amt lnwire.MilliSatoshi ,
4576+ originalScid lnwire.ShortChannelID ,
4577+ customRecords lnwire.CustomRecords ) * LinkError {
4578+
4579+ // In case we are dealing with a custom HTLC, we don't need to validate
4580+ // the HTLC constraints.
4581+ //
4582+ // NOTE: Custom HTLCs are only locally sourced and will use custom
4583+ // channels which are not routable channels and should have their policy
4584+ // not restricted in the first place. However to be sure we skip this
4585+ // check otherwise we might end up in a loop of sending to the same
4586+ // route again and again because link errors are not persisted in
4587+ // mission control.
4588+ if fn .MapOptionZ (
4589+ l .cfg .AuxTrafficShaper ,
4590+ func (ts AuxTrafficShaper ) bool {
4591+ return ts .IsCustomHTLC (customRecords )
4592+ },
4593+ ) {
4594+
4595+ l .log .Debugf ("skipping htlc amount policy validation for " +
4596+ "custom htlc" )
4597+
4598+ return nil
4599+ }
4600+
4601+ // As our first sanity check, we'll ensure that the passed HTLC isn't
4602+ // too small for the next hop. If so, then we'll cancel the HTLC
4603+ // directly.
4604+ if amt < policy .MinHTLCOut {
4605+ l .log .Warnf ("outgoing htlc(%x) is too small: min_htlc=%v, " +
4606+ "htlc_value=%v" , payHash [:], policy .MinHTLCOut ,
4607+ amt )
4608+
4609+ // As part of the returned error, we'll send our latest routing
4610+ // policy so the sending node obtains the most up to date data.
4611+ cb := func (upd * lnwire.ChannelUpdate1 ) lnwire.FailureMessage {
4612+ return lnwire .NewAmountBelowMinimum (amt , * upd )
4613+ }
4614+ failure := l .createFailureWithUpdate (false , originalScid , cb )
4615+
4616+ return NewLinkError (failure )
4617+ }
4618+
4619+ // Next, ensure that the passed HTLC isn't too large. If so, we'll
4620+ // cancel the HTLC directly.
4621+ if policy .MaxHTLC != 0 && amt > policy .MaxHTLC {
4622+ l .log .Warnf ("outgoing htlc(%x) is too large: max_htlc=%v, " +
4623+ "htlc_value=%v" , payHash [:], policy .MaxHTLC , amt )
4624+
4625+ // As part of the returned error, we'll send our latest routing
4626+ // policy so the sending node obtains the most up-to-date data.
4627+ cb := func (upd * lnwire.ChannelUpdate1 ) lnwire.FailureMessage {
4628+ return lnwire .NewTemporaryChannelFailure (upd )
4629+ }
4630+ failure := l .createFailureWithUpdate (false , originalScid , cb )
4631+
4632+ return NewDetailedLinkError (
4633+ failure , OutgoingFailureHTLCExceedsMax ,
4634+ )
4635+ }
4636+
4637+ return nil
4638+ }
0 commit comments