Skip to content

Commit 4045def

Browse files
committed
itest: add new balance assertions to multiple tests
We add a new AssetBalances function that makes sure that the output from all the following RPCs is aligned: - ListAssets - ListBalances (both grouped by asset ID or group key) - ListUtxos
1 parent 50bb8ec commit 4045def

File tree

6 files changed

+439
-56
lines changed

6 files changed

+439
-56
lines changed

itest/assertions.go

Lines changed: 315 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ var (
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

21082402
func assertGroups(t *testing.T, client taprpc.TaprootAssetsClient,

0 commit comments

Comments
 (0)