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,20 +2091,312 @@ 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+ scriptKey []byte
2106+ }
2107+
2108+ type BalanceOption func (* balanceConfig )
2109+
2110+ func WithAssetID (assetID []byte ) BalanceOption {
2111+ return func (c * balanceConfig ) {
2112+ c .assetID = assetID
2113+ }
2114+ }
2115+
2116+ func WithGroupKey (groupKey []byte ) BalanceOption {
2117+ return func (c * balanceConfig ) {
2118+ c .groupKey = groupKey
2119+ }
2120+ }
2121+
2122+ func WithGroupedAssetBalance (groupedAssetBalance uint64 ) BalanceOption {
2123+ return func (c * balanceConfig ) {
2124+ c .groupedAssetBalance = groupedAssetBalance
2125+ }
2126+ }
2127+
2128+ func WithNumUtxos (numAssetUtxos uint32 ) BalanceOption {
2129+ return func (c * balanceConfig ) {
2130+ c .numAssetUtxos = numAssetUtxos
2131+ }
2132+ }
2133+
2134+ func WithNumAnchorUtxos (numAnchorUtxos uint32 ) BalanceOption {
2135+ return func (c * balanceConfig ) {
2136+ c .numAnchorUtxos = numAnchorUtxos
2137+ }
2138+ }
2139+
2140+ func WithIncludeLeased () BalanceOption {
2141+ return func (c * balanceConfig ) {
2142+ c .includeLeased = true
2143+ }
2144+ }
2145+
2146+ func WithAllScriptKeyTypes () BalanceOption {
2147+ return func (c * balanceConfig ) {
2148+ c .allScriptKeyTypes = true
2149+ }
2150+ }
2151+
2152+ func WithScriptKeyType (scriptKeyType asset.ScriptKeyType ) BalanceOption {
2153+ return func (c * balanceConfig ) {
2154+ c .scriptKeyType = & scriptKeyType
2155+ }
2156+ }
2157+
2158+ func WithScriptKey (scriptKey []byte ) BalanceOption {
2159+ return func (c * balanceConfig ) {
2160+ c .scriptKey = scriptKey
2161+
2162+ if len (scriptKey ) == 33 {
2163+ xOnlyKey , _ := schnorr .ParsePubKey (scriptKey [1 :])
2164+ c .scriptKey = xOnlyKey .SerializeCompressed ()
2165+ }
2166+ }
2167+ }
2168+
2169+ func AssertBalances (t * testing.T , client taprpc.TaprootAssetsClient ,
2170+ balance uint64 , opts ... BalanceOption ) {
2171+
2172+ t .Helper ()
2173+
2174+ config := & balanceConfig {}
2175+ for _ , opt := range opts {
2176+ opt (config )
2177+ }
2178+
2179+ var rpcTypeQuery * taprpc.ScriptKeyTypeQuery
2180+ switch {
2181+ case config .allScriptKeyTypes :
2182+ rpcTypeQuery = & taprpc.ScriptKeyTypeQuery {
2183+ Type : & taprpc.ScriptKeyTypeQuery_AllTypes {
2184+ AllTypes : true ,
2185+ },
2186+ }
2187+
2188+ case config .scriptKeyType != nil :
2189+ rpcTypeQuery = & taprpc.ScriptKeyTypeQuery {
2190+ Type : & taprpc.ScriptKeyTypeQuery_ExplicitType {
2191+ ExplicitType : taprpc .MarshalScriptKeyType (
2192+ * config .scriptKeyType ,
2193+ ),
2194+ },
2195+ }
2196+ }
2197+
2198+ balanceSum := func (resp * taprpc.ListBalancesResponse ,
2199+ group bool ) uint64 {
2200+
2201+ var totalBalance uint64
2202+
2203+ if group {
2204+ for _ , currentBalance := range resp .AssetGroupBalances {
2205+ totalBalance += currentBalance .Balance
2206+ }
2207+ } else {
2208+ for _ , currentBalance := range resp .AssetBalances {
2209+ totalBalance += currentBalance .Balance
2210+ }
2211+ }
2212+
2213+ return totalBalance
2214+ }
2215+
2216+ ctxb := context .Background ()
2217+ ctxt , cancel := context .WithTimeout (ctxb , defaultWaitTimeout )
2218+ defer cancel ()
2219+
2220+ // First, we'll ensure that we're able to get the balances of all the
2221+ // assets grouped by their asset IDs.
2222+ assetIDBalances , err := client .ListBalances (
2223+ ctxt , & taprpc.ListBalancesRequest {
2224+ GroupBy : groupBalancesByAssetID ,
2225+ IncludeLeased : config .includeLeased ,
2226+ ScriptKeyType : rpcTypeQuery ,
2227+ AssetFilter : config .assetID ,
2228+ GroupKeyFilter : config .groupKey ,
2229+ },
2230+ )
2231+ require .NoError (t , err )
2232+
2233+ // Spent assets (burns/tombstones) are never included in the balance,
2234+ // even if we specifically query for their type. So we skip the balance
2235+ // check in that case. We also skip if we're looking for a specific
2236+ // script key.
2237+ checkBalance := (config .scriptKeyType == nil ||
2238+ (* config .scriptKeyType != asset .ScriptKeyBurn &&
2239+ * config .scriptKeyType != asset .ScriptKeyTombstone )) &&
2240+ len (config .scriptKey ) == 0
2241+ if checkBalance {
2242+ require .Equal (
2243+ t , balance , balanceSum (assetIDBalances , false ),
2244+ "asset balance, wanted %d, got: %v" , balance ,
2245+ toJSON (t , assetIDBalances ),
2246+ )
2247+ }
2248+
2249+ // Next, we do the same but grouped by group keys (if requested, since
2250+ // this only returns the balances for actually grouped assets).
2251+ if config .groupedAssetBalance > 0 {
2252+ assetGroupBalances , err := client .ListBalances (
2253+ ctxt , & taprpc.ListBalancesRequest {
2254+ GroupBy : groupBalancesByGroupKey ,
2255+ IncludeLeased : config .includeLeased ,
2256+ ScriptKeyType : rpcTypeQuery ,
2257+ AssetFilter : config .assetID ,
2258+ GroupKeyFilter : config .groupKey ,
2259+ },
2260+ )
2261+ require .NoError (t , err )
2262+
2263+ // Spent assets (burns/tombstones) are never included in the
2264+ // balance, even if we specifically query for their type. So we
2265+ // skip the balance check in that case.
2266+ if checkBalance {
2267+ require .Equalf (
2268+ t , config .groupedAssetBalance ,
2269+ balanceSum (assetGroupBalances , true ),
2270+ "grouped balance, wanted %d, got: %v" , balance ,
2271+ toJSON (t , assetGroupBalances ),
2272+ )
2273+ }
2274+ }
2275+
2276+ // Finally, we assert that the sum of all assets queried with the given
2277+ // query parameters matches the expected balance.
2278+ assetList , err := client .ListAssets (ctxt , & taprpc.ListAssetRequest {
2279+ IncludeLeased : config .includeLeased ,
2280+ ScriptKeyType : rpcTypeQuery ,
2281+ })
2282+ require .NoError (t , err )
2283+
2284+ var (
2285+ totalBalance uint64
2286+ numUtxos uint32
2287+ )
2288+ for _ , a := range assetList .Assets {
2289+ if len (config .assetID ) > 0 {
2290+ if ! bytes .Equal (
2291+ a .AssetGenesis .AssetId , config .assetID ,
2292+ ) {
2293+
2294+ continue
2295+ }
2296+ }
2297+
2298+ if len (config .groupKey ) > 0 {
2299+ if a .AssetGroup == nil {
2300+ continue
2301+ }
2302+
2303+ if ! bytes .Equal (
2304+ a .AssetGroup .TweakedGroupKey , config .groupKey ,
2305+ ) {
2306+
2307+ continue
2308+ }
2309+ }
2310+
2311+ if len (config .scriptKey ) > 0 {
2312+ if ! bytes .Equal (a .ScriptKey , config .scriptKey ) {
2313+ continue
2314+ }
2315+ }
2316+
2317+ totalBalance += a .Amount
2318+ numUtxos ++
2319+ }
2320+ require .Equalf (
2321+ t , balance , totalBalance , "ListAssets balance, wanted %d, " +
2322+ "got: %v" , balance , toJSON (t , assetList ),
2323+ )
2324+
2325+ // The number of UTXOs means asset outputs in this case. We check the
2326+ // BTC-level UTXOs below as well, but that's just to make sure the
2327+ // output of that RPC lists the same assets.
2328+ if config .numAssetUtxos > 0 {
2329+ require .Equal (
2330+ t , config .numAssetUtxos , numUtxos , "ListAssets num " +
2331+ "asset utxos" ,
2332+ )
2333+ }
2334+
2335+ utxoList , err := client .ListUtxos (ctxt , & taprpc.ListUtxosRequest {
2336+ IncludeLeased : config .includeLeased ,
2337+ ScriptKeyType : rpcTypeQuery ,
2338+ })
2339+ require .NoError (t , err )
2340+
2341+ // Make sure the ListUtxos call returns the same number of (asset) UTXOs
2342+ // as the ListAssets call.
2343+ var numAnchorUtxos uint32
2344+ totalBalance = 0
2345+ numUtxos = 0
2346+ for _ , btcUtxo := range utxoList .ManagedUtxos {
2347+ numAnchorUtxos ++
2348+ for _ , assetUtxo := range btcUtxo .Assets {
2349+ if len (config .assetID ) > 0 {
2350+ if ! bytes .Equal (
2351+ assetUtxo .AssetGenesis .AssetId ,
2352+ config .assetID ,
2353+ ) {
2354+
2355+ continue
2356+ }
2357+ }
2358+
2359+ if len (config .groupKey ) > 0 {
2360+ if assetUtxo .AssetGroup == nil {
2361+ continue
2362+ }
2363+
2364+ if ! bytes .Equal (
2365+ assetUtxo .AssetGroup .TweakedGroupKey ,
2366+ config .groupKey ,
2367+ ) {
2368+
2369+ continue
2370+ }
21032371 }
2372+
2373+ if len (config .scriptKey ) > 0 {
2374+ if ! bytes .Equal (
2375+ assetUtxo .ScriptKey , config .scriptKey ,
2376+ ) {
2377+
2378+ continue
2379+ }
2380+ }
2381+
2382+ totalBalance += assetUtxo .Amount
2383+ numUtxos ++
21042384 }
21052385 }
2386+ require .Equal (t , balance , totalBalance , "ListUtxos balance" )
2387+
2388+ if config .numAnchorUtxos > 0 {
2389+ require .Equal (
2390+ t , config .numAnchorUtxos , numAnchorUtxos , "num anchor " +
2391+ "utxos" ,
2392+ )
2393+ }
2394+ if config .numAssetUtxos > 0 {
2395+ require .Equal (
2396+ t , config .numAssetUtxos , numUtxos , "ListUtxos num " +
2397+ "asset utxos" ,
2398+ )
2399+ }
21062400}
21072401
21082402func assertGroups (t * testing.T , client taprpc.TaprootAssetsClient ,
0 commit comments