Skip to content

Commit 3b92c82

Browse files
authored
Merge pull request #1119 from lightninglabs/asset-balance-alignment
Add 'include_leased' flag to ListBalances
2 parents 0d64598 + fad2631 commit 3b92c82

File tree

14 files changed

+1156
-684
lines changed

14 files changed

+1156
-684
lines changed

cmd/tapcli/assets.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var (
4848
assetShowWitnessName = "show_witness"
4949
assetShowSpentName = "show_spent"
5050
assetShowLeasedName = "show_leased"
51+
assetIncludeLeasedName = "include_leased"
5152
assetShowUnconfMintsName = "show_unconfirmed_mints"
5253
assetGroupKeyName = "group_key"
5354
assetGroupAnchorName = "group_anchor"
@@ -642,6 +643,10 @@ var listAssetBalancesCommand = cli.Command{
642643
Name: groupByGroupName,
643644
Usage: "Group asset balances by group key",
644645
},
646+
cli.BoolFlag{
647+
Name: assetIncludeLeasedName,
648+
Usage: "Include leased assets in balances",
649+
},
645650
cli.StringFlag{
646651
Name: assetIDName,
647652
Usage: "A specific asset ID to run the balance query " +
@@ -663,7 +668,9 @@ func listAssetBalances(ctx *cli.Context) error {
663668

664669
var err error
665670

666-
req := &taprpc.ListBalancesRequest{}
671+
req := &taprpc.ListBalancesRequest{
672+
IncludeLeased: ctx.Bool(assetIncludeLeasedName),
673+
}
667674

668675
if !ctx.Bool(groupByGroupName) {
669676
req.GroupBy = &taprpc.ListBalancesRequest_AssetId{

itest/assertions.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,7 +1886,7 @@ func AssertGenesisOutput(t *testing.T, output *taprpc.ManagedUtxo,
18861886
}
18871887

18881888
func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
1889-
simpleAssets, issuableAssets []*taprpc.Asset) {
1889+
simpleAssets, issuableAssets []*taprpc.Asset, includeLeased bool) {
18901890

18911891
t.Helper()
18921892

@@ -1901,7 +1901,8 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
19011901
}
19021902
assetIDBalances, err := client.ListBalances(
19031903
ctxt, &taprpc.ListBalancesRequest{
1904-
GroupBy: balanceReq,
1904+
GroupBy: balanceReq,
1905+
IncludeLeased: includeLeased,
19051906
},
19061907
)
19071908
require.NoError(t, err)
@@ -1912,7 +1913,9 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
19121913

19131914
require.Equal(t, len(allAssets), len(assetIDBalances.AssetBalances))
19141915

1916+
var totalBalance uint64
19151917
for _, balance := range assetIDBalances.AssetBalances {
1918+
totalBalance += balance.Balance
19161919
for _, rpcAsset := range allAssets {
19171920
balanceGen := balance.AssetGenesis
19181921
targetGen := rpcAsset.AssetGenesis
@@ -1931,6 +1934,20 @@ func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient,
19311934
}
19321935
}
19331936

1937+
// We should also ensure that the total balance returned by
1938+
// `ListBalances` matches the total balance returned by `ListAssets`.
1939+
assetList, err := client.ListAssets(ctxt, &taprpc.ListAssetRequest{
1940+
IncludeLeased: includeLeased,
1941+
})
1942+
require.NoError(t, err)
1943+
1944+
var totalAssetListBalance uint64
1945+
for _, asset := range assetList.Assets {
1946+
totalAssetListBalance += asset.Amount
1947+
}
1948+
1949+
require.Equal(t, totalBalance, totalAssetListBalance)
1950+
19341951
// We'll also ensure that we're able to get the balance by key group
19351952
// for all the assets that have one specified.
19361953
groupBalanceReq := &taprpc.ListBalancesRequest_GroupKey{

itest/assets_test.go

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/lightninglabs/taproot-assets/internal/test"
2020
"github.com/lightninglabs/taproot-assets/proof"
2121
"github.com/lightninglabs/taproot-assets/taprpc"
22+
wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
2223
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
2324
"github.com/lightninglabs/taproot-assets/taprpc/tapdevrpc"
2425
"github.com/lightningnetwork/lnd/lnrpc"
@@ -109,7 +110,9 @@ func testMintAssets(t *harnessTest) {
109110
// Now that all our assets have been issued, we'll use the balance
110111
// calls to ensure that we're able to retrieve the proper balance for
111112
// them all.
112-
AssertAssetBalances(t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets)
113+
AssertAssetBalances(
114+
t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets, false,
115+
)
113116

114117
// Check that we can retrieve the group keys for the issuable assets.
115118
assertGroups(t.t, t.tapd, issuableAssets)
@@ -441,7 +444,9 @@ func testMintAssetsWithTapscriptSibling(t *harnessTest) {
441444
rpcIssuableAssets := MintAssetsConfirmBatch(
442445
t.t, t.lndHarness.Miner.Client, t.tapd, issuableAssets,
443446
)
444-
AssertAssetBalances(t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets)
447+
AssertAssetBalances(
448+
t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets, false,
449+
)
445450

446451
// Filter the managed UTXOs to select the genesis UTXO with the
447452
// tapscript sibling.
@@ -617,3 +622,77 @@ func testMintBatchAndTransfer(t *harnessTest) {
617622

618623
require.True(t.t, proto.Equal(originalBatch, afterBatch))
619624
}
625+
626+
// testAssetBalances tests the balance retrieval functionality for issued
627+
// assets. The function mints two batches of assets and asserts if the tapcli
628+
// `assets balance` returns the correct balances. It then funds a vPSBT, putting
629+
// a lease on one of the two batches. It then asserts whether the endpoint still
630+
// returns the correct balances, taking into account the `include_leased` flag.
631+
func testAssetBalances(t *harnessTest) {
632+
ctxb := context.Background()
633+
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout)
634+
defer cancel()
635+
636+
rpcSimpleAssets := MintAssetsConfirmBatch(
637+
t.t, t.lndHarness.Miner.Client, t.tapd, simpleAssets,
638+
)
639+
rpcIssuableAssets := MintAssetsConfirmBatch(
640+
t.t, t.lndHarness.Miner.Client, t.tapd, issuableAssets,
641+
)
642+
targetAsset := rpcSimpleAssets[0]
643+
644+
// Now that all our assets have been issued, we'll use the balance
645+
// calls to ensure that we're able to retrieve the proper balance for
646+
// them all.
647+
AssertAssetBalances(
648+
t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets, false,
649+
)
650+
651+
var (
652+
targetAssetGenesis = targetAsset.AssetGenesis
653+
aliceTapd = t.tapd
654+
bobLnd = t.lndHarness.Bob
655+
)
656+
657+
// We create a second tapd node that will be used to simulate a second
658+
// party in the test. This tapd node is connected to lnd "Bob".
659+
bobTapd := setupTapdHarness(t.t, t, bobLnd, t.universeServer)
660+
defer func() {
661+
require.NoError(t.t, bobTapd.stop(!*noDelete))
662+
}()
663+
664+
const assetsToSend = 1000
665+
bobAddr, err := bobTapd.NewAddr(ctxt, &taprpc.NewAddrRequest{
666+
AssetId: targetAssetGenesis.AssetId,
667+
Amt: assetsToSend,
668+
})
669+
require.NoError(t.t, err)
670+
671+
// Now we can create our virtual transaction and ask Alice's tapd to
672+
// fund it.
673+
recipients := map[string]uint64{
674+
bobAddr.Encoded: bobAddr.Amount,
675+
}
676+
_, err = aliceTapd.FundVirtualPsbt(
677+
ctxt, &wrpc.FundVirtualPsbtRequest{
678+
Template: &wrpc.FundVirtualPsbtRequest_Raw{
679+
Raw: &wrpc.TxTemplate{
680+
Recipients: recipients,
681+
},
682+
},
683+
},
684+
)
685+
require.NoError(t.t, err)
686+
687+
// With a transaction funding should have led to a lease on the simple
688+
// assets, we'll use the balance calls to ensure that we're able to
689+
// retrieve the proper balances.
690+
rpcEmptyAssets := []*taprpc.Asset{}
691+
AssertAssetBalances(
692+
t.t, t.tapd, rpcEmptyAssets, rpcIssuableAssets, false,
693+
)
694+
695+
AssertAssetBalances(
696+
t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets, true,
697+
)
698+
}

itest/test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ var testCases = []*testCase{
1717
name: "mint batch and transfer",
1818
test: testMintBatchAndTransfer,
1919
},
20+
{
21+
name: "asset balances",
22+
test: testAssetBalances,
23+
},
2024
{
2125
name: "asset meta validation",
2226
test: testAssetMeta,

rpcserver.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ func (r *rpcServer) checkBalanceOverflow(ctx context.Context,
922922
case assetID != nil:
923923
// Retrieve the current asset balance.
924924
balances, err := r.cfg.AssetStore.QueryBalancesByAsset(
925-
ctx, assetID,
925+
ctx, assetID, true,
926926
)
927927
if err != nil {
928928
return fmt.Errorf("unable to query asset balance: %w",
@@ -938,7 +938,7 @@ func (r *rpcServer) checkBalanceOverflow(ctx context.Context,
938938
case groupPubKey != nil:
939939
// Retrieve the current balance of the group.
940940
balances, err := r.cfg.AssetStore.QueryAssetBalancesByGroup(
941-
ctx, groupPubKey,
941+
ctx, groupPubKey, true,
942942
)
943943
if err != nil {
944944
return fmt.Errorf("unable to query group balance: %w",
@@ -1106,9 +1106,12 @@ func (r *rpcServer) MarshalChainAsset(ctx context.Context, a *asset.ChainAsset,
11061106
}
11071107

11081108
func (r *rpcServer) listBalancesByAsset(ctx context.Context,
1109-
assetID *asset.ID) (*taprpc.ListBalancesResponse, error) {
1109+
assetID *asset.ID, includeLeased bool) (*taprpc.ListBalancesResponse,
1110+
error) {
11101111

1111-
balances, err := r.cfg.AssetStore.QueryBalancesByAsset(ctx, assetID)
1112+
balances, err := r.cfg.AssetStore.QueryBalancesByAsset(
1113+
ctx, assetID, includeLeased,
1114+
)
11121115
if err != nil {
11131116
return nil, fmt.Errorf("unable to list balances: %w", err)
11141117
}
@@ -1138,10 +1141,11 @@ func (r *rpcServer) listBalancesByAsset(ctx context.Context,
11381141
}
11391142

11401143
func (r *rpcServer) listBalancesByGroupKey(ctx context.Context,
1141-
groupKey *btcec.PublicKey) (*taprpc.ListBalancesResponse, error) {
1144+
groupKey *btcec.PublicKey,
1145+
includeLeased bool) (*taprpc.ListBalancesResponse, error) {
11421146

11431147
balances, err := r.cfg.AssetStore.QueryAssetBalancesByGroup(
1144-
ctx, groupKey,
1148+
ctx, groupKey, includeLeased,
11451149
)
11461150
if err != nil {
11471151
return nil, fmt.Errorf("unable to list balances: %w", err)
@@ -1293,7 +1297,7 @@ func (r *rpcServer) ListBalances(ctx context.Context,
12931297
copy(assetID[:], req.AssetFilter)
12941298
}
12951299

1296-
return r.listBalancesByAsset(ctx, assetID)
1300+
return r.listBalancesByAsset(ctx, assetID, req.IncludeLeased)
12971301

12981302
case *taprpc.ListBalancesRequest_GroupKey:
12991303
if !groupBy.GroupKey {
@@ -1310,7 +1314,9 @@ func (r *rpcServer) ListBalances(ctx context.Context,
13101314
}
13111315
}
13121316

1313-
return r.listBalancesByGroupKey(ctx, groupKey)
1317+
return r.listBalancesByGroupKey(
1318+
ctx, groupKey, req.IncludeLeased,
1319+
)
13141320

13151321
default:
13161322
return nil, fmt.Errorf("invalid group_by")

0 commit comments

Comments
 (0)