Skip to content

Commit 2c73f30

Browse files
committed
rfq+rpcserver: return better error for non-match
If there are multiple assets in a channel, we don't want the user to be able to use the --asset_id flag, as the send logic can't guarantee a specific asset UTXO is moved within a channel when an HTLC goes out. So to inform the user about this fact, we return a more useful error message.
1 parent b12f0af commit 2c73f30

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

rfq/manager.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ func (m *Manager) addScidAlias(scidAlias uint64, assetSpecifier asset.Specifier,
579579
continue
580580
}
581581

582-
match, err := m.ChannelCompatible(
582+
match, err := m.ChannelMatchesFully(
583583
ctxb, assetData, assetSpecifier,
584584
)
585585
if err != nil {
@@ -1019,11 +1019,11 @@ func (m *Manager) GetPriceDeviationPpm() uint64 {
10191019
return m.cfg.AcceptPriceDeviationPpm
10201020
}
10211021

1022-
// ChannelCompatible checks a channel's assets against an asset specifier. If
1023-
// the specifier is an asset ID, then all assets must be of that specific ID,
1024-
// if the specifier is a group key, then all assets in the channel must belong
1025-
// to that group.
1026-
func (m *Manager) ChannelCompatible(ctx context.Context,
1022+
// ChannelMatchesFully checks a channel's assets against an asset specifier. If
1023+
// the specifier is an asset ID, then all asset UTXOs must be of that specific
1024+
// ID, if the specifier is a group key, then all assets in the channel must
1025+
// belong to that group.
1026+
func (m *Manager) ChannelMatchesFully(ctx context.Context,
10271027
jsonChannel rfqmsg.JsonAssetChannel, specifier asset.Specifier) (bool,
10281028
error) {
10291029

rpcserver.go

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8395,12 +8395,29 @@ func (r *rpcServer) rfqChannel(ctx context.Context, specifier asset.Specifier,
83958395
peerPubKey *route.Vertex,
83968396
intention chanIntention) (*channelWithSpecifier, error) {
83978397

8398-
balances, err := r.computeChannelAssetBalance(ctx, specifier)
8398+
balances, haveGroupChans, err := r.computeCompatibleChannelAssetBalance(
8399+
ctx, specifier,
8400+
)
83998401
if err != nil {
84008402
return nil, fmt.Errorf("error computing available asset "+
84018403
"channel balance: %w", err)
84028404
}
84038405

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.
84048421
if len(balances) == 0 {
84058422
return nil, fmt.Errorf("no asset channel balance found for %s",
84068423
&specifier)
@@ -8484,17 +8501,27 @@ type channelWithSpecifier struct {
84848501
assetInfo rfqmsg.JsonAssetChannel
84858502
}
84868503

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) {
84918514

84928515
activeChannels, err := r.cfg.Lnd.Client.ListChannels(ctx, true, false)
84938516
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)
84958519
}
84968520

8497-
channels := make([]channelWithSpecifier, 0)
8521+
var (
8522+
channels = make([]channelWithSpecifier, 0)
8523+
haveGroupedAssetChannels bool
8524+
)
84988525
for chanIdx := range activeChannels {
84998526
openChan := activeChannels[chanIdx]
85008527
if len(openChan.CustomChannelData) == 0 {
@@ -8504,17 +8531,21 @@ func (r *rpcServer) computeChannelAssetBalance(ctx context.Context,
85048531
var assetData rfqmsg.JsonAssetChannel
85058532
err = json.Unmarshal(openChan.CustomChannelData, &assetData)
85068533
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
85098540
}
85108541

85118542
// Check if the assets of this channel match the provided
85128543
// specifier.
8513-
pass, err := r.cfg.RfqManager.ChannelCompatible(
8544+
pass, err := r.cfg.RfqManager.ChannelMatchesFully(
85148545
ctx, assetData, specifier,
85158546
)
85168547
if err != nil {
8517-
return nil, err
8548+
return nil, false, err
85188549
}
85198550

85208551
if pass {
@@ -8526,7 +8557,7 @@ func (r *rpcServer) computeChannelAssetBalance(ctx context.Context,
85268557
}
85278558
}
85288559

8529-
return channels, nil
8560+
return channels, haveGroupedAssetChannels, nil
85308561
}
85318562

85328563
// getInboundPolicy returns the policy of the given channel that points towards

0 commit comments

Comments
 (0)