5252			AllTypes: true,
5353		},
5454	}
55+ 
56+ 	groupBalancesByAssetID = &taprpc.ListBalancesRequest_AssetId{
57+ 		AssetId: true,
58+ 	}
59+ 
60+ 	groupBalancesByGroupKey = &taprpc.ListBalancesRequest_GroupKey{
61+ 		GroupKey: true,
62+ 	}
5563)
5664
5765// tapClient is an interface that covers all currently available RPC interfaces
@@ -2010,7 +2018,7 @@ func AssertGenesisOutput(t *testing.T, output *taprpc.ManagedUtxo,
20102018	require.Equal(t, expectedMerkleRoot[:], output.MerkleRoot)
20112019}
20122020
2013- func AssertAssetBalances (t *testing.T, client taprpc.TaprootAssetsClient,
2021+ func AssertMintedAssetBalances (t *testing.T, client taprpc.TaprootAssetsClient,
20142022	simpleAssets, issuableAssets []*taprpc.Asset, includeLeased bool) {
20152023
20162024	t.Helper()
@@ -2021,12 +2029,9 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
20212029
20222030	// First, we'll ensure that we're able to get the balances of all the
20232031	// assets grouped by their asset IDs.
2024- 	balanceReq := &taprpc.ListBalancesRequest_AssetId{
2025- 		AssetId: true,
2026- 	}
20272032	assetIDBalances, err := client.ListBalances(
20282033		ctxt, &taprpc.ListBalancesRequest{
2029- 			GroupBy:       balanceReq ,
2034+ 			GroupBy:       groupBalancesByAssetID ,
20302035			IncludeLeased: includeLeased,
20312036		},
20322037	)
@@ -2067,20 +2072,17 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
20672072	require.NoError(t, err)
20682073
20692074	var totalAssetListBalance uint64
2070- 	for _, asset  := range assetList.Assets {
2071- 		totalAssetListBalance += asset .Amount
2075+ 	for _, a  := range assetList.Assets {
2076+ 		totalAssetListBalance += a .Amount
20722077	}
20732078
20742079	require.Equal(t, totalBalance, totalAssetListBalance)
20752080
20762081	// We'll also ensure that we're able to get the balance by key group
20772082	// for all the assets that have one specified.
2078- 	groupBalanceReq := &taprpc.ListBalancesRequest_GroupKey{
2079- 		GroupKey: true,
2080- 	}
20812083	assetGroupBalances, err := client.ListBalances(
20822084		ctxt, &taprpc.ListBalancesRequest{
2083- 			GroupBy: groupBalanceReq ,
2085+ 			GroupBy: groupBalancesByGroupKey ,
20842086		},
20852087	)
20862088	require.NoError(t, err)
@@ -2089,19 +2091,274 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
20892091		t, len(issuableAssets),
20902092		len(assetGroupBalances.AssetGroupBalances),
20912093	)
2094+ }
20922095
2093- 	for _, balance := range assetGroupBalances.AssetBalances {
2094- 		for _, rpcAsset := range issuableAssets {
2095- 			if balance.AssetGenesis.Name == rpcAsset.AssetGenesis.Name {
2096- 				require.Equal(
2097- 					t, balance.Balance, rpcAsset.Amount,
2098- 				)
2099- 				require.Equal(
2100- 					t, balance.AssetGenesis,
2101- 					rpcAsset.AssetGenesis,
2102- 				)
2096+ type balanceConfig struct {
2097+ 	assetID             []byte
2098+ 	groupKey            []byte
2099+ 	groupedAssetBalance uint64
2100+ 	numAssetUtxos       uint32
2101+ 	numAnchorUtxos      uint32
2102+ 	includeLeased       bool
2103+ 	allScriptKeyTypes   bool
2104+ 	scriptKeyType       *asset.ScriptKeyType
2105+ }
2106+ 
2107+ type BalanceOption func(*balanceConfig)
2108+ 
2109+ func WithAssetID(assetID []byte) BalanceOption {
2110+ 	return func(c *balanceConfig) {
2111+ 		c.assetID = assetID
2112+ 	}
2113+ }
2114+ 
2115+ func WithGroupKey(groupKey []byte) BalanceOption {
2116+ 	return func(c *balanceConfig) {
2117+ 		c.groupKey = groupKey
2118+ 	}
2119+ }
2120+ 
2121+ func WithGroupedAssetBalance(groupedAssetBalance uint64) BalanceOption {
2122+ 	return func(c *balanceConfig) {
2123+ 		c.groupedAssetBalance = groupedAssetBalance
2124+ 	}
2125+ }
2126+ 
2127+ func WithNumUtxos(numAssetUtxos uint32) BalanceOption {
2128+ 	return func(c *balanceConfig) {
2129+ 		c.numAssetUtxos = numAssetUtxos
2130+ 	}
2131+ }
2132+ 
2133+ func WithNumAnchorUtxos(numAnchorUtxos uint32) BalanceOption {
2134+ 	return func(c *balanceConfig) {
2135+ 		c.numAnchorUtxos = numAnchorUtxos
2136+ 	}
2137+ }
2138+ 
2139+ func WithIncludeLeased() BalanceOption {
2140+ 	return func(c *balanceConfig) {
2141+ 		c.includeLeased = true
2142+ 	}
2143+ }
2144+ 
2145+ func WithAllScriptKeyTypes() BalanceOption {
2146+ 	return func(c *balanceConfig) {
2147+ 		c.allScriptKeyTypes = true
2148+ 	}
2149+ }
2150+ 
2151+ func WithScriptKeyType(scriptKeyType asset.ScriptKeyType) BalanceOption {
2152+ 	return func(c *balanceConfig) {
2153+ 		c.scriptKeyType = &scriptKeyType
2154+ 	}
2155+ }
2156+ 
2157+ func AssertBalances(t *testing.T, client taprpc.TaprootAssetsClient,
2158+ 	balance uint64, opts ...BalanceOption) {
2159+ 
2160+ 	t.Helper()
2161+ 
2162+ 	config := &balanceConfig{}
2163+ 	for _, opt := range opts {
2164+ 		opt(config)
2165+ 	}
2166+ 
2167+ 	var rpcTypeQuery *taprpc.ScriptKeyTypeQuery
2168+ 	switch {
2169+ 	case config.allScriptKeyTypes:
2170+ 		rpcTypeQuery = &taprpc.ScriptKeyTypeQuery{
2171+ 			Type: &taprpc.ScriptKeyTypeQuery_AllTypes{
2172+ 				AllTypes: true,
2173+ 			},
2174+ 		}
2175+ 
2176+ 	case config.scriptKeyType != nil:
2177+ 		rpcTypeQuery = &taprpc.ScriptKeyTypeQuery{
2178+ 			Type: &taprpc.ScriptKeyTypeQuery_ExplicitType{
2179+ 				ExplicitType: taprpc.MarshalScriptKeyType(
2180+ 					*config.scriptKeyType,
2181+ 				),
2182+ 			},
2183+ 		}
2184+ 	}
2185+ 
2186+ 	balanceSum := func(resp *taprpc.ListBalancesResponse,
2187+ 		group bool) uint64 {
2188+ 
2189+ 		var totalBalance uint64
2190+ 
2191+ 		if group {
2192+ 			for _, currentBalance := range resp.AssetGroupBalances {
2193+ 				totalBalance += currentBalance.Balance
2194+ 			}
2195+ 		} else {
2196+ 			for _, currentBalance := range resp.AssetBalances {
2197+ 				totalBalance += currentBalance.Balance
2198+ 			}
2199+ 		}
2200+ 
2201+ 		return totalBalance
2202+ 	}
2203+ 
2204+ 	ctxb := context.Background()
2205+ 	ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout)
2206+ 	defer cancel()
2207+ 
2208+ 	// First, we'll ensure that we're able to get the balances of all the
2209+ 	// assets grouped by their asset IDs.
2210+ 	assetIDBalances, err := client.ListBalances(
2211+ 		ctxt, &taprpc.ListBalancesRequest{
2212+ 			GroupBy:        groupBalancesByAssetID,
2213+ 			IncludeLeased:  config.includeLeased,
2214+ 			ScriptKeyType:  rpcTypeQuery,
2215+ 			AssetFilter:    config.assetID,
2216+ 			GroupKeyFilter: config.groupKey,
2217+ 		},
2218+ 	)
2219+ 	require.NoError(t, err)
2220+ 
2221+ 	// Spent assets (burns/tombstones) are never included in the balance,
2222+ 	// even if we specifically query for their type. So we skip the balance
2223+ 	// check in that case.
2224+ 	checkBalance := config.scriptKeyType == nil ||
2225+ 		(*config.scriptKeyType != asset.ScriptKeyBurn &&
2226+ 			*config.scriptKeyType != asset.ScriptKeyTombstone)
2227+ 	if checkBalance {
2228+ 		require.Equal(
2229+ 			t, balance, balanceSum(assetIDBalances, false),
2230+ 			"asset balance, wanted %d, got: %v", balance,
2231+ 			toJSON(t, assetIDBalances),
2232+ 		)
2233+ 	}
2234+ 
2235+ 	// Next, we do the same but grouped by group keys (if requested, since
2236+ 	// this only returns the balances for actually grouped assets).
2237+ 	if config.groupedAssetBalance > 0 {
2238+ 		assetGroupBalances, err := client.ListBalances(
2239+ 			ctxt, &taprpc.ListBalancesRequest{
2240+ 				GroupBy:        groupBalancesByGroupKey,
2241+ 				IncludeLeased:  config.includeLeased,
2242+ 				ScriptKeyType:  rpcTypeQuery,
2243+ 				AssetFilter:    config.assetID,
2244+ 				GroupKeyFilter: config.groupKey,
2245+ 			},
2246+ 		)
2247+ 		require.NoError(t, err)
2248+ 
2249+ 		// Spent assets (burns/tombstones) are never included in the
2250+ 		// balance, even if we specifically query for their type. So we
2251+ 		// skip the balance check in that case.
2252+ 		if checkBalance {
2253+ 			require.Equalf(
2254+ 				t, config.groupedAssetBalance,
2255+ 				balanceSum(assetGroupBalances, true),
2256+ 				"grouped balance, wanted %d, got: %v", balance,
2257+ 				toJSON(t, assetGroupBalances),
2258+ 			)
2259+ 		}
2260+ 	}
2261+ 
2262+ 	// Finally, we assert that the sum of all assets queried with the given
2263+ 	// query parameters matches the expected balance.
2264+ 	assetList, err := client.ListAssets(ctxt, &taprpc.ListAssetRequest{
2265+ 		IncludeLeased: config.includeLeased,
2266+ 		ScriptKeyType: rpcTypeQuery,
2267+ 	})
2268+ 	require.NoError(t, err)
2269+ 
2270+ 	var (
2271+ 		totalBalance uint64
2272+ 		numUtxos     uint32
2273+ 	)
2274+ 	for _, a := range assetList.Assets {
2275+ 		if len(config.assetID) > 0 {
2276+ 			if !bytes.Equal(
2277+ 				a.AssetGenesis.AssetId, config.assetID,
2278+ 			) {
2279+ 
2280+ 				continue
2281+ 			}
2282+ 		}
2283+ 
2284+ 		if len(config.groupKey) > 0 {
2285+ 			if !bytes.Equal(
2286+ 				a.AssetGroup.TweakedGroupKey, config.groupKey,
2287+ 			) {
2288+ 
2289+ 				continue
21032290			}
21042291		}
2292+ 
2293+ 		totalBalance += a.Amount
2294+ 		numUtxos++
2295+ 	}
2296+ 	require.Equalf(
2297+ 		t, balance, totalBalance, "ListAssets balance, wanted %d, "+
2298+ 			"got: %v", balance, toJSON(t, assetList),
2299+ 	)
2300+ 
2301+ 	// The number of UTXOs means asset outputs in this case. We check the
2302+ 	// BTC-level UTXOs below as well, but that's just to make sure the
2303+ 	// output of that RPC lists the same assets.
2304+ 	if config.numAssetUtxos > 0 {
2305+ 		require.Equal(
2306+ 			t, config.numAssetUtxos, numUtxos, "ListAssets num "+
2307+ 				"asset utxos",
2308+ 		)
2309+ 	}
2310+ 
2311+ 	utxoList, err := client.ListUtxos(ctxt, &taprpc.ListUtxosRequest{
2312+ 		IncludeLeased: config.includeLeased,
2313+ 		ScriptKeyType: rpcTypeQuery,
2314+ 	})
2315+ 	require.NoError(t, err)
2316+ 
2317+ 	// Make sure the ListUtxos call returns the same number of (asset) UTXOs
2318+ 	// as the ListAssets call.
2319+ 	var numAnchorUtxos uint32
2320+ 	totalBalance = 0
2321+ 	numUtxos = 0
2322+ 	for _, btcUtxo := range utxoList.ManagedUtxos {
2323+ 		numAnchorUtxos++
2324+ 		for _, assetUtxo := range btcUtxo.Assets {
2325+ 			if len(config.assetID) > 0 {
2326+ 				if !bytes.Equal(
2327+ 					assetUtxo.AssetGenesis.AssetId,
2328+ 					config.assetID,
2329+ 				) {
2330+ 
2331+ 					continue
2332+ 				}
2333+ 			}
2334+ 
2335+ 			if len(config.groupKey) > 0 {
2336+ 				if !bytes.Equal(
2337+ 					btcUtxo.TaprootAssetRoot,
2338+ 					config.groupKey,
2339+ 				) {
2340+ 
2341+ 					continue
2342+ 				}
2343+ 			}
2344+ 
2345+ 			totalBalance += assetUtxo.Amount
2346+ 			numUtxos++
2347+ 		}
2348+ 	}
2349+ 	require.Equal(t, balance, totalBalance, "ListUtxos balance")
2350+ 
2351+ 	if config.numAnchorUtxos > 0 {
2352+ 		require.Equal(
2353+ 			t, config.numAnchorUtxos, numAnchorUtxos, "num anchor "+
2354+ 				"utxos",
2355+ 		)
2356+ 	}
2357+ 	if config.numAssetUtxos > 0 {
2358+ 		require.Equal(
2359+ 			t, config.numAssetUtxos, numUtxos, "ListUtxos num "+
2360+ 				"asset utxos",
2361+ 		)
21052362	}
21062363}
21072364
0 commit comments