@@ -174,18 +174,40 @@ func (s *AuxTrafficShaper) PaymentBandwidth(htlcBlob,
174174 return 0 , fmt .Errorf ("error decoding HTLC blob: %w" , err )
175175 }
176176
177- // localBalance is the total number of local asset units.
178- localBalance := cmsg . OutputSum ( commitment . LocalOutputs ())
179-
180- // There either already is an amount set in the HTLC (which would
181- // indicate it to be a direct-channel keysend payment that just sends
182- // assets to the direct peer with no conversion), in which case we don't
183- // need an RFQ ID as we can just compare the local balance and the
184- // required HTLC amount. If there is no amount set, we need to look up
185- // the RFQ ID in the HTLC blob and use the accepted quote to determine
186- // the amount .
177+ // With the help of the latest HtlcView, let's calculate a more precise
178+ // local balance. This is useful in order to not forward HTLCs that may
179+ // never be settled. Other HTLCs that may also call into this method are
180+ // not yet registered to the commitment, so we need to account for them
181+ // manually.
182+ computedLocal := ComputeLocalBalance ( * commitment )
183+
184+ // If the HTLC carries asset units (keysend, forwarding), then there's
185+ // no need to do any RFQ related math. We can directly compare the asset
186+ // units of the HTLC with those in our local balance .
187187 htlcAssetAmount := htlc .Amounts .Val .Sum ()
188- if htlcAssetAmount != 0 && htlcAssetAmount <= localBalance {
188+ if htlcAssetAmount != 0 {
189+ return paymentBandwidthAssetUnits (
190+ htlcAssetAmount , computedLocal , linkBandwidth , htlcAmt ,
191+ )
192+ }
193+
194+ // Otherwise, we derive the available bandwidth from the HTLC's RFQ and
195+ // the asset units in our local balance.
196+ return s .paymentBandwidth (
197+ htlc , computedLocal , linkBandwidth , minHtlcAmt ,
198+ )
199+ }
200+
201+ // paymentBandwidthAssetUnits includes the asset unit related checks between the
202+ // HTLC carrying the units and the asset balance of our channel. The response
203+ // will either be infinite or zero bandwidth, as we can't really map the amount
204+ // to msats without an RFQ, and it's also not needed.
205+ func paymentBandwidthAssetUnits (htlcAssetAmount , computedLocal uint64 ,
206+ linkBandwidth ,
207+ htlcAmt lnwire.MilliSatoshi ) (lnwire.MilliSatoshi , error ) {
208+
209+ switch {
210+ case htlcAssetAmount <= computedLocal :
189211 // Check if the current link bandwidth can afford sending out
190212 // the htlc amount without dipping into the channel reserve. If
191213 // it goes below the reserve, we report zero bandwidth as we
@@ -204,10 +226,30 @@ func (s *AuxTrafficShaper) PaymentBandwidth(htlcBlob,
204226 // matter too much here, we just want to signal that this
205227 // channel _does_ have available bandwidth.
206228 return lnwire .NewMSatFromSatoshis (btcutil .MaxSatoshi ), nil
229+
230+ case htlcAssetAmount > computedLocal :
231+ // The asset balance of the channel is simply not enough to
232+ // route the asset units, we report 0 bandwidth in order for the
233+ // HTLC to fail back.
234+ return 0 , nil
235+
236+ default :
237+ // We shouldn't reach this case, we add it only for the function
238+ // to always return something and the compiler to be happy.
239+ return 0 , fmt .Errorf ("should not reach this, invalid htlc " +
240+ "asset amount or computed local balance" )
207241 }
242+ }
243+
244+ // paymentBandwidth returns the available payment bandwidth of the channel based
245+ // on the asset rate of the RFQ quote that is included in the HTLC and the asset
246+ // units of the local balance.
247+ func (s * AuxTrafficShaper ) paymentBandwidth (htlc * rfqmsg.Htlc ,
248+ localBalance uint64 , linkBandwidth ,
249+ minHtlcAmt lnwire.MilliSatoshi ) (lnwire.MilliSatoshi , error ) {
208250
209- // If the HTLC doesn't have an asset amount and RFQ ID, it's incomplete,
210- // and we cannot determine what channel to use .
251+ // If the HTLC doesn't have an RFQ ID, it's incomplete, and we cannot
252+ // determine the bandwidth .
211253 if htlc .RfqID .ValOpt ().IsNone () {
212254 log .Tracef ("No RFQ ID in HTLC, cannot determine matching " +
213255 "outgoing channel" )
@@ -261,6 +303,16 @@ func (s *AuxTrafficShaper) PaymentBandwidth(htlcBlob,
261303 return availableBalanceMsat , nil
262304}
263305
306+ // ComputeLocalBalance combines the given commitment state with the HtlcView to
307+ // produce the available local balance with accuracy.
308+ func ComputeLocalBalance (commitment cmsg.Commitment ) uint64 {
309+ // Let's get the current local asset balance of the channel as reported
310+ // by the latest commitment.
311+ localBalance := cmsg .OutputSum (commitment .LocalOutputs ())
312+
313+ return localBalance
314+ }
315+
264316// ProduceHtlcExtraData is a function that, based on the previous custom record
265317// blob of an HTLC, may produce a different blob or modify the amount of bitcoin
266318// this HTLC should carry.
0 commit comments