Skip to content

Commit c0a3f16

Browse files
committed
tapchannel: check quote vs. channel compatibility
During pathfinding, when an HTLC doesn't have an asset ID or group key encoded, we can only find out if a channel is compatible after looking at the specifier of the quote. We add that to make sure pathfinding doesn't give false positives for channels that then can't be used because they're not compatible.
1 parent abdf42f commit c0a3f16

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

tapchannel/aux_traffic_shaper.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tapchannel
22

33
import (
4+
"context"
45
"fmt"
56
"sync"
67

@@ -228,7 +229,7 @@ func (s *AuxTrafficShaper) PaymentBandwidth(fundingBlob, htlcBlob,
228229
// Otherwise, we derive the available bandwidth from the HTLC's RFQ and
229230
// the asset units in our local balance.
230231
return s.paymentBandwidth(
231-
htlc, computedLocal, linkBandwidth, minHtlcAmt,
232+
htlc, computedLocal, linkBandwidth, minHtlcAmt, fundingChan,
232233
)
233234
}
234235

@@ -279,8 +280,8 @@ func paymentBandwidthAssetUnits(htlcAssetAmount, computedLocal uint64,
279280
// on the asset rate of the RFQ quote that is included in the HTLC and the asset
280281
// units of the local balance.
281282
func (s *AuxTrafficShaper) paymentBandwidth(htlc *rfqmsg.Htlc,
282-
localBalance uint64, linkBandwidth,
283-
minHtlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) {
283+
localBalance uint64, linkBandwidth, minHtlcAmt lnwire.MilliSatoshi,
284+
fundingChan *cmsg.OpenChannel) (lnwire.MilliSatoshi, error) {
284285

285286
// If the HTLC doesn't have an RFQ ID, it's incomplete, and we cannot
286287
// determine the bandwidth.
@@ -301,19 +302,54 @@ func (s *AuxTrafficShaper) paymentBandwidth(htlc *rfqmsg.Htlc,
301302
sellQuote, isSellQuote := acceptedSellQuotes[rfqID.Scid()]
302303
buyQuote, isBuyQuote := acceptedBuyQuotes[rfqID.Scid()]
303304

304-
var rate rfqmsg.AssetRate
305+
var (
306+
rate rfqmsg.AssetRate
307+
specifier asset.Specifier
308+
)
305309
switch {
306310
case isSellQuote:
307311
rate = sellQuote.AssetRate
312+
specifier = sellQuote.Request.AssetSpecifier
308313

309314
case isBuyQuote:
310315
rate = buyQuote.AssetRate
316+
specifier = buyQuote.Request.AssetSpecifier
311317

312318
default:
313319
return 0, fmt.Errorf("no accepted quote found for RFQ ID "+
314320
"%x (SCID %d)", rfqID[:], rfqID.Scid())
315321
}
316322

323+
// Now that we found the quote, we can determine if this quote is even
324+
// compatible with this channel. If not, we cannot forward the HTLC
325+
// and should return 0 bandwidth.
326+
for _, b := range fundingChan.FundedAssets.Val.Outputs {
327+
// We define compatibility by making sure that each asset in the
328+
// channel matches the specifier of the RFQ quote. This means
329+
// if the quote was created for a single asset in a grouped
330+
// asset channel with multiple tranches, then the check will
331+
// return false, because the group key needs to be used in that
332+
// case. But this matches the behavior in other areas, where we
333+
// also use AssetMatchesSpecifier.
334+
match, err := s.cfg.RfqManager.AssetMatchesSpecifier(
335+
context.Background(), specifier, b.AssetID.Val,
336+
)
337+
if err != nil {
338+
return 0, fmt.Errorf("error checking if asset ID %x "+
339+
"matches specifier %s: %w", b.AssetID.Val[:],
340+
specifier.String(), err)
341+
}
342+
343+
// One of the asset IDs in the channel does not match the quote,
344+
// we don't want to route this HTLC over this channel.
345+
if !match {
346+
log.Tracef("Quote with ID %x (SCID %d) not compatible "+
347+
"with channel assets, returning 0 bandwidth",
348+
rfqID[:], rfqID.Scid())
349+
return 0, nil
350+
}
351+
}
352+
317353
// Calculate the local available balance in the local asset unit,
318354
// expressed in milli-satoshis.
319355
localBalanceFp := rfqmath.NewBigIntFixedPoint(localBalance, 0)

0 commit comments

Comments
 (0)