@@ -8395,12 +8395,29 @@ func (r *rpcServer) rfqChannel(ctx context.Context, specifier asset.Specifier,
8395
8395
peerPubKey * route.Vertex ,
8396
8396
intention chanIntention ) (* channelWithSpecifier , error ) {
8397
8397
8398
- balances , err := r .computeChannelAssetBalance (ctx , specifier )
8398
+ balances , haveGroupChans , err := r .computeCompatibleChannelAssetBalance (
8399
+ ctx , specifier ,
8400
+ )
8399
8401
if err != nil {
8400
8402
return nil , fmt .Errorf ("error computing available asset " +
8401
8403
"channel balance: %w" , err )
8402
8404
}
8403
8405
8406
+ // If the user uses the asset ID to specify what asset to use, that will
8407
+ // not work for asset channels that have multiple UTXOs of grouped
8408
+ // assets. The result wouldn't be deterministic anyway (meaning, it's
8409
+ // not guaranteed that a specific asset ID is moved within a channel
8410
+ // when an HTLC is sent, the allocation logic decides which actual UTXO
8411
+ // is used). So we tell the user to use the group key instead, at least
8412
+ // for channels that have multiple UTXOs of grouped assets.
8413
+ if specifier .HasId () && len (balances ) == 0 && haveGroupChans {
8414
+ return nil , fmt .Errorf ("no compatible asset channel found for " +
8415
+ "%s, make sure to use group key for grouped asset " +
8416
+ "channels" , & specifier )
8417
+ }
8418
+
8419
+ // If the above special case didn't apply, it just means we don't have
8420
+ // the specific asset in a channel that can be used.
8404
8421
if len (balances ) == 0 {
8405
8422
return nil , fmt .Errorf ("no asset channel balance found for %s" ,
8406
8423
& specifier )
@@ -8484,17 +8501,27 @@ type channelWithSpecifier struct {
8484
8501
assetInfo rfqmsg.JsonAssetChannel
8485
8502
}
8486
8503
8487
- // computeChannelAssetBalance computes the total local and remote balance for
8488
- // each asset channel that matches the provided asset specifier.
8489
- func (r * rpcServer ) computeChannelAssetBalance (ctx context.Context ,
8490
- specifier asset.Specifier ) ([]channelWithSpecifier , error ) {
8504
+ // computeCompatibleChannelAssetBalance computes the total local and remote
8505
+ // balance for each asset channel that matches the provided asset specifier.
8506
+ // For a channel to match an asset specifier, all asset UTXOs in the channel
8507
+ // must match the specifier. That means a channel is seen as _not_ compatible if
8508
+ // it contains multiple asset IDs but the specifier only specifies one of them.
8509
+ // The user should use the group key in that case instead. To help inform the
8510
+ // user about this, the returned boolean value indicates if there are any active
8511
+ // channels that have grouped assets.
8512
+ func (r * rpcServer ) computeCompatibleChannelAssetBalance (ctx context.Context ,
8513
+ specifier asset.Specifier ) ([]channelWithSpecifier , bool , error ) {
8491
8514
8492
8515
activeChannels , err := r .cfg .Lnd .Client .ListChannels (ctx , true , false )
8493
8516
if err != nil {
8494
- return nil , fmt .Errorf ("unable to fetch channels: %w" , err )
8517
+ return nil , false , fmt .Errorf ("unable to fetch channels: %w" ,
8518
+ err )
8495
8519
}
8496
8520
8497
- channels := make ([]channelWithSpecifier , 0 )
8521
+ var (
8522
+ channels = make ([]channelWithSpecifier , 0 )
8523
+ haveGroupedAssetChannels bool
8524
+ )
8498
8525
for chanIdx := range activeChannels {
8499
8526
openChan := activeChannels [chanIdx ]
8500
8527
if len (openChan .CustomChannelData ) == 0 {
@@ -8504,17 +8531,21 @@ func (r *rpcServer) computeChannelAssetBalance(ctx context.Context,
8504
8531
var assetData rfqmsg.JsonAssetChannel
8505
8532
err = json .Unmarshal (openChan .CustomChannelData , & assetData )
8506
8533
if err != nil {
8507
- return nil , fmt .Errorf ("unable to unmarshal asset " +
8508
- "data: %w" , err )
8534
+ return nil , false , fmt .Errorf ("unable to unmarshal " +
8535
+ "asset data: %w" , err )
8536
+ }
8537
+
8538
+ if len (assetData .GroupKey ) > 0 {
8539
+ haveGroupedAssetChannels = true
8509
8540
}
8510
8541
8511
8542
// Check if the assets of this channel match the provided
8512
8543
// specifier.
8513
- pass , err := r .cfg .RfqManager .ChannelCompatible (
8544
+ pass , err := r .cfg .RfqManager .ChannelMatchesFully (
8514
8545
ctx , assetData , specifier ,
8515
8546
)
8516
8547
if err != nil {
8517
- return nil , err
8548
+ return nil , false , err
8518
8549
}
8519
8550
8520
8551
if pass {
@@ -8526,7 +8557,7 @@ func (r *rpcServer) computeChannelAssetBalance(ctx context.Context,
8526
8557
}
8527
8558
}
8528
8559
8529
- return channels , nil
8560
+ return channels , haveGroupedAssetChannels , nil
8530
8561
}
8531
8562
8532
8563
// getInboundPolicy returns the policy of the given channel that points towards
0 commit comments