From cf67d1fd0cc8b09c5bd19eac7b3943f173370757 Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 20 Nov 2024 18:00:44 -0700 Subject: [PATCH] tapdb: Update AssetQueryFilters to handle additional filter options --- tapdb/assets_store.go | 47 ++++++++++++++++++++++++++++++++--- tapdb/sqlc/assets.sql.go | 17 +++++++------ tapdb/sqlc/queries/assets.sql | 1 + tapfreighter/interface.go | 3 +++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/tapdb/assets_store.go b/tapdb/assets_store.go index d38da9d7e..a10ee060e 100644 --- a/tapdb/assets_store.go +++ b/tapdb/assets_store.go @@ -868,7 +868,7 @@ func (a *AssetStore) dbAssetsToChainAssets(dbAssets []ConfirmedAsset, // constraintsToDbFilter maps application level constraints to the set of // filters we use in the SQL queries. func (a *AssetStore) constraintsToDbFilter( - query *AssetQueryFilters) QueryAssetFilters { + query *AssetQueryFilters) (QueryAssetFilters, error) { assetFilter := QueryAssetFilters{ Now: sql.NullTime{ @@ -883,12 +883,41 @@ func (a *AssetStore) constraintsToDbFilter( Valid: true, } } + + if query.MaxAmt != 0 { + assetFilter.MaxAmt = sql.NullInt64{ + Int64: int64(query.MaxAmt), + Valid: true, + } + } else { + // If MaxAmt is unspecified, use valid: false to ignore + // the amount option in the SQL query + assetFilter.MaxAmt = sql.NullInt64{ + Valid: false, + } + } + if query.MinAnchorHeight != 0 { assetFilter.MinAnchorHeight = sqlInt32( query.MinAnchorHeight, ) } + if query.ScriptKey != nil { + key := query.ScriptKey.PubKey + assetFilter.TweakedScriptKey = key.SerializeCompressed() + } + + if query.AnchorPoint != nil { + anchorPointBytes, err := encodeOutpoint(*query.AnchorPoint) + if err != nil { + return QueryAssetFilters{}, fmt.Errorf("unable to encode "+ + "outpoint: %w", err) + } + + assetFilter.AnchorPoint = anchorPointBytes + } + // Add asset ID bytes and group key bytes to the filter. These // byte arrays are empty if the asset ID or group key is not // specified in the query. @@ -908,7 +937,7 @@ func (a *AssetStore) constraintsToDbFilter( } } - return assetFilter + return assetFilter, nil } // specificAssetFilter maps the given asset parameters to the set of filters @@ -980,6 +1009,10 @@ type AssetQueryFilters struct { // MinAnchorHeight is the minimum block height the asset's anchor tx // must have been confirmed at. MinAnchorHeight int32 + + ScriptKey *asset.ScriptKey + + AnchorPoint *wire.OutPoint } // QueryBalancesByAsset queries the balances for assets or alternatively @@ -1199,7 +1232,10 @@ func (a *AssetStore) FetchAllAssets(ctx context.Context, includeSpent, // We'll now map the application level filtering to the type of // filtering our database query understands. - assetFilter := a.constraintsToDbFilter(query) + assetFilter, err := a.constraintsToDbFilter(query) + if err != nil { + return nil, err + } // By default, the spent boolean is null, which means we'll fetch all // assets. Only if we should exclude spent assets, we'll set the spent @@ -1991,9 +2027,12 @@ func (a *AssetStore) ListEligibleCoins(ctx context.Context, // First, we'll map the commitment constraints to our database query // filters. - assetFilter := a.constraintsToDbFilter(&AssetQueryFilters{ + assetFilter, err := a.constraintsToDbFilter(&AssetQueryFilters{ CommitmentConstraints: constraints, }) + if err != nil { + return nil, err + } // We only want to select unspent and non-leased commitments. assetFilter.Spent = sqlBool(false) diff --git a/tapdb/sqlc/assets.sql.go b/tapdb/sqlc/assets.sql.go index a518ebbc3..2ce9bdc0b 100644 --- a/tapdb/sqlc/assets.sql.go +++ b/tapdb/sqlc/assets.sql.go @@ -2262,14 +2262,15 @@ JOIN chain_txns txns COALESCE(txns.block_height, 0) >= COALESCE($6, txns.block_height, 0) WHERE ( assets.amount >= COALESCE($7, assets.amount) AND - assets.spent = COALESCE($8, assets.spent) AND - (key_group_info_view.tweaked_group_key = $9 OR - $9 IS NULL) AND - assets.anchor_utxo_id = COALESCE($10, assets.anchor_utxo_id) AND - assets.genesis_id = COALESCE($11, assets.genesis_id) AND - assets.script_key_id = COALESCE($12, assets.script_key_id) AND + assets.amount <= COALESCE($8, assets.amount) AND + assets.spent = COALESCE($9, assets.spent) AND + (key_group_info_view.tweaked_group_key = $10 OR + $10 IS NULL) AND + assets.anchor_utxo_id = COALESCE($11, assets.anchor_utxo_id) AND + assets.genesis_id = COALESCE($12, assets.genesis_id) AND + assets.script_key_id = COALESCE($13, assets.script_key_id) AND COALESCE(length(script_keys.tweak), 0) = (CASE - WHEN cast($13 as bool) = TRUE + WHEN cast($14 as bool) = TRUE THEN 0 ELSE COALESCE(length(script_keys.tweak), 0) END) @@ -2284,6 +2285,7 @@ type QueryAssetsParams struct { Now sql.NullTime MinAnchorHeight sql.NullInt32 MinAmt sql.NullInt64 + MaxAmt sql.NullInt64 Spent sql.NullBool KeyGroupFilter []byte AnchorUtxoID sql.NullInt64 @@ -2352,6 +2354,7 @@ func (q *Queries) QueryAssets(ctx context.Context, arg QueryAssetsParams) ([]Que arg.Now, arg.MinAnchorHeight, arg.MinAmt, + arg.MaxAmt, arg.Spent, arg.KeyGroupFilter, arg.AnchorUtxoID, diff --git a/tapdb/sqlc/queries/assets.sql b/tapdb/sqlc/queries/assets.sql index 78bfa313b..35ed8da05 100644 --- a/tapdb/sqlc/queries/assets.sql +++ b/tapdb/sqlc/queries/assets.sql @@ -490,6 +490,7 @@ JOIN chain_txns txns -- specified. WHERE ( assets.amount >= COALESCE(sqlc.narg('min_amt'), assets.amount) AND + assets.amount <= COALESCE(sqlc.narg('max_amt'), assets.amount) AND assets.spent = COALESCE(sqlc.narg('spent'), assets.spent) AND (key_group_info_view.tweaked_group_key = sqlc.narg('key_group_filter') OR sqlc.narg('key_group_filter') IS NULL) AND diff --git a/tapfreighter/interface.go b/tapfreighter/interface.go index 3649b4194..bfee9fbe1 100644 --- a/tapfreighter/interface.go +++ b/tapfreighter/interface.go @@ -35,6 +35,9 @@ type CommitmentConstraints struct { // to satisfy the constraints. MinAmt uint64 + // MaxAmt specifies the maximum amount of minted assets to be selected. + MaxAmt uint64 + // CoinSelectType is the type of coins that should be selected. CoinSelectType tapsend.CoinSelectType }