From 48f42678987c99c079a32e5b29a040c2df6afc30 Mon Sep 17 00:00:00 2001 From: kaldun-tech Date: Tue, 12 May 2026 17:10:28 -0600 Subject: [PATCH] taprpc+tapdb: add GenesisInfo to AssetGroupBalance in ListBalances Added GenesisInfo and DecimalDisplay fields to the AssetGroupBalance message, making the group_by=group_key mode of ListBalances useful for clients that need asset metadata alongside balances. The SQL query was modified to join genesis_info_view and return genesis data for a representative issuance per group (selected via MIN to get the earliest-inserted genesis, which is the anchor for locally-minted assets). Fixes #2066. Signed-off-by: kaldun-tech --- docs/release-notes/release-notes-0.8.0.md | 6 + itest/multi_asset_group_test.go | 26 ++- rpcserver/rpcserver.go | 41 +++- tapdb/assets_store.go | 40 +++- tapdb/sqlc/assets.sql.go | 49 +++- tapdb/sqlc/queries/assets.sql | 32 ++- taprpc/taprootassets.pb.go | 267 ++++++++++++---------- taprpc/taprootassets.proto | 9 + taprpc/taprootassets.swagger.json | 8 + 9 files changed, 340 insertions(+), 138 deletions(-) diff --git a/docs/release-notes/release-notes-0.8.0.md b/docs/release-notes/release-notes-0.8.0.md index 954472d313..1f91562528 100644 --- a/docs/release-notes/release-notes-0.8.0.md +++ b/docs/release-notes/release-notes-0.8.0.md @@ -348,6 +348,12 @@ clients to distinguish grouped fungible burns from grouped collectible burns. +- [PR#2130](https://github.com/lightninglabs/taproot-assets/pull/2130) + Add `asset_genesis` and `decimal_display` fields to `AssetGroupBalance` + in `ListBalances`. When using `group_by=group_key` mode, clients now + receive asset metadata (name, type, decimal display) alongside grouped + balances without requiring additional RPC calls. + - [PR#2100](https://github.com/lightninglabs/taproot-assets/pull/2100) Add pagination support (offset, limit, direction) to the `AssetLeaves` RPC endpoint, and add `MaxPageSize` validation to `AssetRoots`. diff --git a/itest/multi_asset_group_test.go b/itest/multi_asset_group_test.go index df86fa3170..95d90ee965 100644 --- a/itest/multi_asset_group_test.go +++ b/itest/multi_asset_group_test.go @@ -58,16 +58,40 @@ func testMintMultiAssetGroups(t *harnessTest) { require.NoError(t.t, err) // For each group minted, we check that the total balance for each - // group matches our minting requests. + // group matches our minting requests, and that genesis info is + // populated correctly. var singleAssetGroupKey, normalGroupKey, collectGroupKey string for groupKey, groupBalance := range balancesResp.AssetGroupBalances { + // Verify genesis info is populated for all group balances. + require.NotNil(t.t, groupBalance.AssetGenesis, + "group %s missing genesis info", groupKey) + require.NotEmpty(t.t, groupBalance.AssetGenesis.Name, + "group %s missing asset name", groupKey) + require.NotEmpty(t.t, groupBalance.AssetGenesis.AssetId, + "group %s missing asset ID", groupKey) + require.NotEmpty(t.t, groupBalance.AssetGenesis.GenesisPoint, + "group %s missing genesis point", groupKey) + switch groupBalance.Balance { case issuableAsset.Asset.Amount: singleAssetGroupKey = groupKey + case normalGroupSum: normalGroupKey = groupKey + + // Verify the normal group has NORMAL asset type. + require.Equal(t.t, taprpc.AssetType_NORMAL, + groupBalance.AssetGenesis.AssetType, + "normal group has wrong asset type") + case collectGroupSum: collectGroupKey = groupKey + + // Verify the collectible group has COLLECTIBLE type. + require.Equal(t.t, taprpc.AssetType_COLLECTIBLE, + groupBalance.AssetGenesis.AssetType, + "collectible group has wrong asset type") + default: t.t.Fatalf("minted group %v has unexpected balance %v", groupKey, groupBalance.Balance) diff --git a/rpcserver/rpcserver.go b/rpcserver/rpcserver.go index 6b195ab3a5..2feaa5645a 100644 --- a/rpcserver/rpcserver.go +++ b/rpcserver/rpcserver.go @@ -27,6 +27,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog/v2" "github.com/btcsuite/btcwallet/wtxmgr" "github.com/davecgh/go-spew/spew" proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" @@ -1483,11 +1484,45 @@ func (r *RPCServer) listBalancesByGroupKey(ctx context.Context, groupKey = balance.GroupKey.SerializeCompressed() } + // Create the genesis info for the representative issuance. + genesisInfo := &taprpc.GenesisInfo{ + GenesisPoint: balance.GenesisPoint.String(), + AssetType: taprpc.AssetType(balance.Type), + Name: balance.Tag, + MetaHash: balance.MetaHash[:], + AssetId: balance.ID[:], + OutputIndex: balance.OutputIndex, + } + groupKeyString := hex.EncodeToString(groupKey) - resp.AssetGroupBalances[groupKeyString] = &taprpc.AssetGroupBalance{ - GroupKey: groupKey, - Balance: balance.Balance, + groupBalance := &taprpc.AssetGroupBalance{ + GroupKey: groupKey, + Balance: balance.Balance, + AssetGenesis: genesisInfo, + } + + // Fetch the decimal display for this asset ID. We don't fail + // the request if this lookup fails since decimal display is + // optional metadata. + decDisplay, err := r.cfg.AddrBook.DecDisplayForAssetID( + ctx, balance.ID, + ) + if err == nil { + groupBalance.DecimalDisplay = fn.MapOptionZ( + decDisplay, + func(d uint32) *taprpc.DecimalDisplay { + return &taprpc.DecimalDisplay{ + DecimalDisplay: d, + } + }, + ) + } else { + rpcsLog.DebugS(ctx, "Failed to fetch decimal display", + btclog.Fmt("asset_id", "%x", balance.ID[:]), + "error", err) } + + resp.AssetGroupBalances[groupKeyString] = groupBalance } // We will also report the number of unconfirmed transfers. This is diff --git a/tapdb/assets_store.go b/tapdb/assets_store.go index 52b9adb498..65d1e18e25 100644 --- a/tapdb/assets_store.go +++ b/tapdb/assets_store.go @@ -412,11 +412,20 @@ type AssetBalance struct { GroupKey []byte } -// AssetGroupBalance holds abalance query result for a particular asset group -// or all asset groups tracked by this daemon. +// AssetGroupBalance holds a balance query result for a particular asset group +// or all asset groups tracked by this daemon. It includes the genesis info of +// a representative issuance in the group. type AssetGroupBalance struct { GroupKey *btcec.PublicKey Balance uint64 + + // Genesis info fields for the representative issuance. + ID asset.ID + Tag string + MetaHash [asset.MetaHashLen]byte + Type asset.Type + GenesisPoint wire.OutPoint + OutputIndex uint32 } // cacheableTimestamp is a wrapper around an int32 that can be used as a @@ -1219,11 +1228,30 @@ func (a *AssetStore) QueryAssetBalancesByGroup(ctx context.Context, } } - serializedKey := asset.ToSerialized(groupKey) - balances[serializedKey] = AssetGroupBalance{ - GroupKey: groupKey, - Balance: uint64(groupBalance.Balance), + assetGroupBalance := AssetGroupBalance{ + GroupKey: groupKey, + Balance: uint64(groupBalance.Balance), + Tag: groupBalance.AssetTag, + Type: asset.Type(groupBalance.AssetType), + OutputIndex: uint32(groupBalance.OutputIndex), } + + // Parse the genesis point from the raw bytes. + err = readOutPoint( + bytes.NewReader(groupBalance.PrevOut), + 0, 0, &assetGroupBalance.GenesisPoint, + ) + if err != nil { + return err + } + + // Copy the asset ID and meta hash. + copy(assetGroupBalance.ID[:], groupBalance.AssetID) + copy(assetGroupBalance.MetaHash[:], + groupBalance.MetaHash) + + serializedKey := asset.ToSerialized(groupKey) + balances[serializedKey] = assetGroupBalance } return err diff --git a/tapdb/sqlc/assets.sql.go b/tapdb/sqlc/assets.sql.go index b02dcb0c2c..a61d681012 100644 --- a/tapdb/sqlc/assets.sql.go +++ b/tapdb/sqlc/assets.sql.go @@ -2517,20 +2517,43 @@ func (q *Queries) QueryAssetBalancesByAsset(ctx context.Context, arg QueryAssetB } const QueryAssetBalancesByGroup = `-- name: QueryAssetBalancesByGroup :many +WITH group_anchor AS ( + -- Select a representative genesis for each group. We use MIN(gen_asset_id) + -- as a deterministic way to pick one genesis per group. For fungible assets + -- (the primary use case), all tranches in a group share the same name and + -- type, so any genesis provides correct metadata. This is a heuristic that + -- works for locally-minted assets where the anchor is inserted first. + SELECT + tweaked_group_key, + MIN(gen_asset_id) as anchor_gen_id + FROM key_group_info_view + GROUP BY tweaked_group_key +) SELECT - key_group_info_view.tweaked_group_key, SUM(amount) balance + key_group_info_view.tweaked_group_key, + SUM(amount) balance, + genesis_info_view.asset_id, + genesis_info_view.asset_tag, + genesis_info_view.meta_hash, + genesis_info_view.asset_type, + genesis_info_view.output_index, + genesis_info_view.prev_out FROM assets JOIN key_group_info_view ON assets.genesis_id = key_group_info_view.gen_asset_id AND (key_group_info_view.tweaked_group_key = $1 OR $1 IS NULL) +JOIN group_anchor + ON key_group_info_view.tweaked_group_key = group_anchor.tweaked_group_key +JOIN genesis_info_view + ON group_anchor.anchor_gen_id = genesis_info_view.gen_asset_id JOIN managed_utxos utxos ON assets.anchor_utxo_id = utxos.utxo_id AND CASE WHEN $2 = true THEN (utxos.lease_owner IS NOT NULL AND utxos.lease_expiry > $3) WHEN $2 = false THEN - (utxos.lease_owner IS NULL OR + (utxos.lease_owner IS NULL OR utxos.lease_expiry IS NULL OR utxos.lease_expiry <= $3) ELSE TRUE @@ -2542,7 +2565,10 @@ WHERE spent = FALSE AND -- query will return no results. COALESCE(script_keys.key_type, 0) IN (/*SLICE:script_key_type*/?) -GROUP BY key_group_info_view.tweaked_group_key +GROUP BY key_group_info_view.tweaked_group_key, + genesis_info_view.asset_id, genesis_info_view.asset_tag, + genesis_info_view.meta_hash, genesis_info_view.asset_type, + genesis_info_view.output_index, genesis_info_view.prev_out ` type QueryAssetBalancesByGroupParams struct { @@ -2555,6 +2581,12 @@ type QueryAssetBalancesByGroupParams struct { type QueryAssetBalancesByGroupRow struct { TweakedGroupKey []byte Balance int64 + AssetID []byte + AssetTag string + MetaHash []byte + AssetType int16 + OutputIndex int32 + PrevOut []byte } func (q *Queries) QueryAssetBalancesByGroup(ctx context.Context, arg QueryAssetBalancesByGroupParams) ([]QueryAssetBalancesByGroupRow, error) { @@ -2579,7 +2611,16 @@ func (q *Queries) QueryAssetBalancesByGroup(ctx context.Context, arg QueryAssetB var items []QueryAssetBalancesByGroupRow for rows.Next() { var i QueryAssetBalancesByGroupRow - if err := rows.Scan(&i.TweakedGroupKey, &i.Balance); err != nil { + if err := rows.Scan( + &i.TweakedGroupKey, + &i.Balance, + &i.AssetID, + &i.AssetTag, + &i.MetaHash, + &i.AssetType, + &i.OutputIndex, + &i.PrevOut, + ); err != nil { return nil, err } items = append(items, i) diff --git a/tapdb/sqlc/queries/assets.sql b/tapdb/sqlc/queries/assets.sql index 562a9e94b6..3d978223c5 100644 --- a/tapdb/sqlc/queries/assets.sql +++ b/tapdb/sqlc/queries/assets.sql @@ -362,20 +362,43 @@ GROUP BY assets.genesis_id, genesis_info_view.asset_id, genesis_info_view.prev_out, key_group_info_view.tweaked_group_key; -- name: QueryAssetBalancesByGroup :many +WITH group_anchor AS ( + -- Select a representative genesis for each group. We use MIN(gen_asset_id) + -- as a deterministic way to pick one genesis per group. For fungible assets + -- (the primary use case), all tranches in a group share the same name and + -- type, so any genesis provides correct metadata. This is a heuristic that + -- works for locally-minted assets where the anchor is inserted first. + SELECT + tweaked_group_key, + MIN(gen_asset_id) as anchor_gen_id + FROM key_group_info_view + GROUP BY tweaked_group_key +) SELECT - key_group_info_view.tweaked_group_key, SUM(amount) balance + key_group_info_view.tweaked_group_key, + SUM(amount) balance, + genesis_info_view.asset_id, + genesis_info_view.asset_tag, + genesis_info_view.meta_hash, + genesis_info_view.asset_type, + genesis_info_view.output_index, + genesis_info_view.prev_out FROM assets JOIN key_group_info_view ON assets.genesis_id = key_group_info_view.gen_asset_id AND (key_group_info_view.tweaked_group_key = sqlc.narg('key_group_filter') OR sqlc.narg('key_group_filter') IS NULL) +JOIN group_anchor + ON key_group_info_view.tweaked_group_key = group_anchor.tweaked_group_key +JOIN genesis_info_view + ON group_anchor.anchor_gen_id = genesis_info_view.gen_asset_id JOIN managed_utxos utxos ON assets.anchor_utxo_id = utxos.utxo_id AND CASE WHEN sqlc.narg('leased') = true THEN (utxos.lease_owner IS NOT NULL AND utxos.lease_expiry > @now) WHEN sqlc.narg('leased') = false THEN - (utxos.lease_owner IS NULL OR + (utxos.lease_owner IS NULL OR utxos.lease_expiry IS NULL OR utxos.lease_expiry <= @now) ELSE TRUE @@ -387,7 +410,10 @@ WHERE spent = FALSE AND -- query will return no results. COALESCE(script_keys.key_type, 0) IN (sqlc.slice('script_key_type')/*SLICE:script_key_type*/) -GROUP BY key_group_info_view.tweaked_group_key; +GROUP BY key_group_info_view.tweaked_group_key, + genesis_info_view.asset_id, genesis_info_view.asset_tag, + genesis_info_view.meta_hash, genesis_info_view.asset_type, + genesis_info_view.output_index, genesis_info_view.prev_out; -- name: FetchGroupedAssets :many SELECT diff --git a/taprpc/taprootassets.pb.go b/taprpc/taprootassets.pb.go index dea9a04962..ade9e66b00 100644 --- a/taprpc/taprootassets.pb.go +++ b/taprpc/taprootassets.pb.go @@ -2979,9 +2979,16 @@ type AssetGroupBalance struct { // The group key or nil aggregating assets that don't have a group. GroupKey []byte `protobuf:"bytes,1,opt,name=group_key,json=groupKey,proto3" json:"group_key,omitempty"` // The total balance of the assets in the group. - Balance uint64 `protobuf:"varint,2,opt,name=balance,proto3" json:"balance,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Balance uint64 `protobuf:"varint,2,opt,name=balance,proto3" json:"balance,omitempty"` + // The genesis info of a representative issuance in the group. For fungible + // assets, all tranches share the same name and type, so this provides the + // canonical metadata for the group. + AssetGenesis *GenesisInfo `protobuf:"bytes,3,opt,name=asset_genesis,json=assetGenesis,proto3" json:"asset_genesis,omitempty"` + // The decimal display value for the asset group, extracted from the + // representative issuance's metadata. + DecimalDisplay *DecimalDisplay `protobuf:"bytes,4,opt,name=decimal_display,json=decimalDisplay,proto3" json:"decimal_display,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AssetGroupBalance) Reset() { @@ -3028,6 +3035,20 @@ func (x *AssetGroupBalance) GetBalance() uint64 { return 0 } +func (x *AssetGroupBalance) GetAssetGenesis() *GenesisInfo { + if x != nil { + return x.AssetGenesis + } + return nil +} + +func (x *AssetGroupBalance) GetDecimalDisplay() *DecimalDisplay { + if x != nil { + return x.DecimalDisplay + } + return nil +} + type ListBalancesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The map of asset balances, where the key is the asset ID and the value @@ -7670,10 +7691,12 @@ const file_taprootassets_proto_rawDesc = "" + "\fAssetBalance\x128\n" + "\rasset_genesis\x18\x01 \x01(\v2\x13.taprpc.GenesisInfoR\fassetGenesis\x12\x18\n" + "\abalance\x18\x03 \x01(\x04R\abalance\x12\x1b\n" + - "\tgroup_key\x18\x04 \x01(\fR\bgroupKey\"J\n" + + "\tgroup_key\x18\x04 \x01(\fR\bgroupKey\"\xc5\x01\n" + "\x11AssetGroupBalance\x12\x1b\n" + "\tgroup_key\x18\x01 \x01(\fR\bgroupKey\x12\x18\n" + - "\abalance\x18\x02 \x01(\x04R\abalance\"\xc5\x03\n" + + "\abalance\x18\x02 \x01(\x04R\abalance\x128\n" + + "\rasset_genesis\x18\x03 \x01(\v2\x13.taprpc.GenesisInfoR\fassetGenesis\x12?\n" + + "\x0fdecimal_display\x18\x04 \x01(\v2\x16.taprpc.DecimalDisplayR\x0edecimalDisplay\"\xc5\x03\n" + "\x14ListBalancesResponse\x12V\n" + "\x0easset_balances\x18\x01 \x03(\v2/.taprpc.ListBalancesResponse.AssetBalancesEntryR\rassetBalances\x12f\n" + "\x14asset_group_balances\x18\x02 \x03(\v24.taprpc.ListBalancesResponse.AssetGroupBalancesEntryR\x12assetGroupBalances\x123\n" + @@ -8256,122 +8279,124 @@ var file_taprootassets_proto_depIdxs = []int32{ 99, // 31: taprpc.ListGroupsResponse.groups:type_name -> taprpc.ListGroupsResponse.GroupsEntry 56, // 32: taprpc.ListBalancesRequest.script_key_type:type_name -> taprpc.ScriptKeyTypeQuery 16, // 33: taprpc.AssetBalance.asset_genesis:type_name -> taprpc.GenesisInfo - 100, // 34: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry - 101, // 35: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry - 44, // 36: taprpc.ListTransfersResponse.transfers:type_name -> taprpc.AssetTransfer - 45, // 37: taprpc.AssetTransfer.inputs:type_name -> taprpc.TransferInput - 47, // 38: taprpc.AssetTransfer.outputs:type_name -> taprpc.TransferOutput - 43, // 39: taprpc.AssetTransfer.anchor_tx_block_hash:type_name -> taprpc.ChainHash - 0, // 40: taprpc.TransferInput.asset_type:type_name -> taprpc.AssetType - 46, // 41: taprpc.TransferOutput.anchor:type_name -> taprpc.TransferOutputAnchor - 3, // 42: taprpc.TransferOutput.output_type:type_name -> taprpc.OutputType - 2, // 43: taprpc.TransferOutput.asset_version:type_name -> taprpc.AssetVersion - 4, // 44: taprpc.TransferOutput.proof_delivery_status:type_name -> taprpc.ProofDeliveryStatus - 0, // 45: taprpc.TransferOutput.asset_type:type_name -> taprpc.AssetType - 0, // 46: taprpc.Addr.asset_type:type_name -> taprpc.AssetType - 2, // 47: taprpc.Addr.asset_version:type_name -> taprpc.AssetVersion - 5, // 48: taprpc.Addr.address_version:type_name -> taprpc.AddrVersion - 52, // 49: taprpc.QueryAddrResponse.addrs:type_name -> taprpc.Addr - 57, // 50: taprpc.NewAddrRequest.script_key:type_name -> taprpc.ScriptKey - 59, // 51: taprpc.NewAddrRequest.internal_key:type_name -> taprpc.KeyDescriptor - 2, // 52: taprpc.NewAddrRequest.asset_version:type_name -> taprpc.AssetVersion - 5, // 53: taprpc.NewAddrRequest.address_version:type_name -> taprpc.AddrVersion - 6, // 54: taprpc.ScriptKeyTypeQuery.explicit_type:type_name -> taprpc.ScriptKeyType - 59, // 55: taprpc.ScriptKey.key_desc:type_name -> taprpc.KeyDescriptor - 6, // 56: taprpc.ScriptKey.type:type_name -> taprpc.ScriptKeyType - 58, // 57: taprpc.KeyDescriptor.key_loc:type_name -> taprpc.KeyLocator - 61, // 58: taprpc.TapscriptFullTree.all_leaves:type_name -> taprpc.TapLeaf - 26, // 59: taprpc.DecodedProof.asset:type_name -> taprpc.Asset - 10, // 60: taprpc.DecodedProof.meta_reveal:type_name -> taprpc.AssetMeta - 24, // 61: taprpc.DecodedProof.genesis_reveal:type_name -> taprpc.GenesisReveal - 23, // 62: taprpc.DecodedProof.group_key_reveal:type_name -> taprpc.GroupKeyReveal - 65, // 63: taprpc.VerifyProofResponse.decoded_proof:type_name -> taprpc.DecodedProof - 65, // 64: taprpc.DecodeProofResponse.decoded_proof:type_name -> taprpc.DecodedProof - 103, // 65: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint - 52, // 66: taprpc.AddrEvent.addr:type_name -> taprpc.Addr - 7, // 67: taprpc.AddrEvent.status:type_name -> taprpc.AddrEventStatus - 7, // 68: taprpc.AddrReceivesRequest.filter_status:type_name -> taprpc.AddrEventStatus - 104, // 69: taprpc.AddrReceivesRequest.direction:type_name -> taprpc.SortDirection - 72, // 70: taprpc.AddrReceivesResponse.events:type_name -> taprpc.AddrEvent - 76, // 71: taprpc.SendAssetRequest.addresses_with_amounts:type_name -> taprpc.AddressWithAmount - 44, // 72: taprpc.SendAssetResponse.transfer:type_name -> taprpc.AssetTransfer - 1, // 73: taprpc.FetchAssetMetaResponse.type:type_name -> taprpc.AssetMetaType - 102, // 74: taprpc.FetchAssetMetaResponse.unknown_odd_types:type_name -> taprpc.FetchAssetMetaResponse.UnknownOddTypesEntry - 11, // 75: taprpc.BurnAssetRequest.asset_specifier:type_name -> taprpc.AssetSpecifier - 44, // 76: taprpc.BurnAssetResponse.burn_transfer:type_name -> taprpc.AssetTransfer - 65, // 77: taprpc.BurnAssetResponse.burn_proof:type_name -> taprpc.DecodedProof - 65, // 78: taprpc.BurnAssetResponse.burn_proofs:type_name -> taprpc.DecodedProof - 0, // 79: taprpc.AssetBurn.asset_type:type_name -> taprpc.AssetType - 86, // 80: taprpc.ListBurnsResponse.burns:type_name -> taprpc.AssetBurn - 52, // 81: taprpc.ReceiveEvent.address:type_name -> taprpc.Addr - 7, // 82: taprpc.ReceiveEvent.status:type_name -> taprpc.AddrEventStatus - 9, // 83: taprpc.SendEvent.parcel_type:type_name -> taprpc.ParcelType - 52, // 84: taprpc.SendEvent.addresses:type_name -> taprpc.Addr - 92, // 85: taprpc.SendEvent.anchor_transaction:type_name -> taprpc.AnchorTransaction - 44, // 86: taprpc.SendEvent.transfer:type_name -> taprpc.AssetTransfer - 103, // 87: taprpc.AnchorTransaction.lnd_locked_utxos:type_name -> taprpc.OutPoint - 103, // 88: taprpc.RegisterTransferRequest.outpoint:type_name -> taprpc.OutPoint - 26, // 89: taprpc.RegisterTransferResponse.registered_asset:type_name -> taprpc.Asset - 95, // 90: taprpc.BakeMacaroonRequest.permissions:type_name -> taprpc.MacaroonPermission - 31, // 91: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo - 35, // 92: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets - 38, // 93: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance - 39, // 94: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance - 14, // 95: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest - 12, // 96: taprpc.TaprootAssets.FetchAsset:input_type -> taprpc.FetchAssetRequest - 30, // 97: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest - 33, // 98: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest - 37, // 99: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest - 41, // 100: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest - 48, // 101: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest - 50, // 102: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest - 53, // 103: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest - 55, // 104: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest - 63, // 105: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest - 73, // 106: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest - 64, // 107: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile - 67, // 108: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest - 69, // 109: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest - 70, // 110: taprpc.TaprootAssets.UnpackProofFile:input_type -> taprpc.UnpackProofFileRequest - 75, // 111: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest - 83, // 112: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest - 85, // 113: taprpc.TaprootAssets.ListBurns:input_type -> taprpc.ListBurnsRequest - 79, // 114: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest - 96, // 115: taprpc.TaprootAssets.BakeMacaroon:input_type -> taprpc.BakeMacaroonRequest - 81, // 116: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest - 88, // 117: taprpc.TaprootAssets.SubscribeReceiveEvents:input_type -> taprpc.SubscribeReceiveEventsRequest - 90, // 118: taprpc.TaprootAssets.SubscribeSendEvents:input_type -> taprpc.SubscribeSendEventsRequest - 93, // 119: taprpc.TaprootAssets.RegisterTransfer:input_type -> taprpc.RegisterTransferRequest - 29, // 120: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse - 13, // 121: taprpc.TaprootAssets.FetchAsset:output_type -> taprpc.FetchAssetResponse - 32, // 122: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse - 36, // 123: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse - 40, // 124: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse - 42, // 125: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse - 49, // 126: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse - 51, // 127: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse - 54, // 128: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse - 52, // 129: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr - 52, // 130: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr - 74, // 131: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse - 66, // 132: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse - 68, // 133: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse - 64, // 134: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile - 71, // 135: taprpc.TaprootAssets.UnpackProofFile:output_type -> taprpc.UnpackProofFileResponse - 78, // 136: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse - 84, // 137: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse - 87, // 138: taprpc.TaprootAssets.ListBurns:output_type -> taprpc.ListBurnsResponse - 80, // 139: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse - 97, // 140: taprpc.TaprootAssets.BakeMacaroon:output_type -> taprpc.BakeMacaroonResponse - 82, // 141: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.FetchAssetMetaResponse - 89, // 142: taprpc.TaprootAssets.SubscribeReceiveEvents:output_type -> taprpc.ReceiveEvent - 91, // 143: taprpc.TaprootAssets.SubscribeSendEvents:output_type -> taprpc.SendEvent - 94, // 144: taprpc.TaprootAssets.RegisterTransfer:output_type -> taprpc.RegisterTransferResponse - 120, // [120:145] is the sub-list for method output_type - 95, // [95:120] is the sub-list for method input_type - 95, // [95:95] is the sub-list for extension type_name - 95, // [95:95] is the sub-list for extension extendee - 0, // [0:95] is the sub-list for field type_name + 16, // 34: taprpc.AssetGroupBalance.asset_genesis:type_name -> taprpc.GenesisInfo + 25, // 35: taprpc.AssetGroupBalance.decimal_display:type_name -> taprpc.DecimalDisplay + 100, // 36: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry + 101, // 37: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry + 44, // 38: taprpc.ListTransfersResponse.transfers:type_name -> taprpc.AssetTransfer + 45, // 39: taprpc.AssetTransfer.inputs:type_name -> taprpc.TransferInput + 47, // 40: taprpc.AssetTransfer.outputs:type_name -> taprpc.TransferOutput + 43, // 41: taprpc.AssetTransfer.anchor_tx_block_hash:type_name -> taprpc.ChainHash + 0, // 42: taprpc.TransferInput.asset_type:type_name -> taprpc.AssetType + 46, // 43: taprpc.TransferOutput.anchor:type_name -> taprpc.TransferOutputAnchor + 3, // 44: taprpc.TransferOutput.output_type:type_name -> taprpc.OutputType + 2, // 45: taprpc.TransferOutput.asset_version:type_name -> taprpc.AssetVersion + 4, // 46: taprpc.TransferOutput.proof_delivery_status:type_name -> taprpc.ProofDeliveryStatus + 0, // 47: taprpc.TransferOutput.asset_type:type_name -> taprpc.AssetType + 0, // 48: taprpc.Addr.asset_type:type_name -> taprpc.AssetType + 2, // 49: taprpc.Addr.asset_version:type_name -> taprpc.AssetVersion + 5, // 50: taprpc.Addr.address_version:type_name -> taprpc.AddrVersion + 52, // 51: taprpc.QueryAddrResponse.addrs:type_name -> taprpc.Addr + 57, // 52: taprpc.NewAddrRequest.script_key:type_name -> taprpc.ScriptKey + 59, // 53: taprpc.NewAddrRequest.internal_key:type_name -> taprpc.KeyDescriptor + 2, // 54: taprpc.NewAddrRequest.asset_version:type_name -> taprpc.AssetVersion + 5, // 55: taprpc.NewAddrRequest.address_version:type_name -> taprpc.AddrVersion + 6, // 56: taprpc.ScriptKeyTypeQuery.explicit_type:type_name -> taprpc.ScriptKeyType + 59, // 57: taprpc.ScriptKey.key_desc:type_name -> taprpc.KeyDescriptor + 6, // 58: taprpc.ScriptKey.type:type_name -> taprpc.ScriptKeyType + 58, // 59: taprpc.KeyDescriptor.key_loc:type_name -> taprpc.KeyLocator + 61, // 60: taprpc.TapscriptFullTree.all_leaves:type_name -> taprpc.TapLeaf + 26, // 61: taprpc.DecodedProof.asset:type_name -> taprpc.Asset + 10, // 62: taprpc.DecodedProof.meta_reveal:type_name -> taprpc.AssetMeta + 24, // 63: taprpc.DecodedProof.genesis_reveal:type_name -> taprpc.GenesisReveal + 23, // 64: taprpc.DecodedProof.group_key_reveal:type_name -> taprpc.GroupKeyReveal + 65, // 65: taprpc.VerifyProofResponse.decoded_proof:type_name -> taprpc.DecodedProof + 65, // 66: taprpc.DecodeProofResponse.decoded_proof:type_name -> taprpc.DecodedProof + 103, // 67: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint + 52, // 68: taprpc.AddrEvent.addr:type_name -> taprpc.Addr + 7, // 69: taprpc.AddrEvent.status:type_name -> taprpc.AddrEventStatus + 7, // 70: taprpc.AddrReceivesRequest.filter_status:type_name -> taprpc.AddrEventStatus + 104, // 71: taprpc.AddrReceivesRequest.direction:type_name -> taprpc.SortDirection + 72, // 72: taprpc.AddrReceivesResponse.events:type_name -> taprpc.AddrEvent + 76, // 73: taprpc.SendAssetRequest.addresses_with_amounts:type_name -> taprpc.AddressWithAmount + 44, // 74: taprpc.SendAssetResponse.transfer:type_name -> taprpc.AssetTransfer + 1, // 75: taprpc.FetchAssetMetaResponse.type:type_name -> taprpc.AssetMetaType + 102, // 76: taprpc.FetchAssetMetaResponse.unknown_odd_types:type_name -> taprpc.FetchAssetMetaResponse.UnknownOddTypesEntry + 11, // 77: taprpc.BurnAssetRequest.asset_specifier:type_name -> taprpc.AssetSpecifier + 44, // 78: taprpc.BurnAssetResponse.burn_transfer:type_name -> taprpc.AssetTransfer + 65, // 79: taprpc.BurnAssetResponse.burn_proof:type_name -> taprpc.DecodedProof + 65, // 80: taprpc.BurnAssetResponse.burn_proofs:type_name -> taprpc.DecodedProof + 0, // 81: taprpc.AssetBurn.asset_type:type_name -> taprpc.AssetType + 86, // 82: taprpc.ListBurnsResponse.burns:type_name -> taprpc.AssetBurn + 52, // 83: taprpc.ReceiveEvent.address:type_name -> taprpc.Addr + 7, // 84: taprpc.ReceiveEvent.status:type_name -> taprpc.AddrEventStatus + 9, // 85: taprpc.SendEvent.parcel_type:type_name -> taprpc.ParcelType + 52, // 86: taprpc.SendEvent.addresses:type_name -> taprpc.Addr + 92, // 87: taprpc.SendEvent.anchor_transaction:type_name -> taprpc.AnchorTransaction + 44, // 88: taprpc.SendEvent.transfer:type_name -> taprpc.AssetTransfer + 103, // 89: taprpc.AnchorTransaction.lnd_locked_utxos:type_name -> taprpc.OutPoint + 103, // 90: taprpc.RegisterTransferRequest.outpoint:type_name -> taprpc.OutPoint + 26, // 91: taprpc.RegisterTransferResponse.registered_asset:type_name -> taprpc.Asset + 95, // 92: taprpc.BakeMacaroonRequest.permissions:type_name -> taprpc.MacaroonPermission + 31, // 93: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo + 35, // 94: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets + 38, // 95: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance + 39, // 96: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance + 14, // 97: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest + 12, // 98: taprpc.TaprootAssets.FetchAsset:input_type -> taprpc.FetchAssetRequest + 30, // 99: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest + 33, // 100: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest + 37, // 101: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest + 41, // 102: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest + 48, // 103: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest + 50, // 104: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest + 53, // 105: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest + 55, // 106: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest + 63, // 107: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest + 73, // 108: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest + 64, // 109: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile + 67, // 110: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest + 69, // 111: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest + 70, // 112: taprpc.TaprootAssets.UnpackProofFile:input_type -> taprpc.UnpackProofFileRequest + 75, // 113: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest + 83, // 114: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest + 85, // 115: taprpc.TaprootAssets.ListBurns:input_type -> taprpc.ListBurnsRequest + 79, // 116: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest + 96, // 117: taprpc.TaprootAssets.BakeMacaroon:input_type -> taprpc.BakeMacaroonRequest + 81, // 118: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest + 88, // 119: taprpc.TaprootAssets.SubscribeReceiveEvents:input_type -> taprpc.SubscribeReceiveEventsRequest + 90, // 120: taprpc.TaprootAssets.SubscribeSendEvents:input_type -> taprpc.SubscribeSendEventsRequest + 93, // 121: taprpc.TaprootAssets.RegisterTransfer:input_type -> taprpc.RegisterTransferRequest + 29, // 122: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse + 13, // 123: taprpc.TaprootAssets.FetchAsset:output_type -> taprpc.FetchAssetResponse + 32, // 124: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse + 36, // 125: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse + 40, // 126: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse + 42, // 127: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse + 49, // 128: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse + 51, // 129: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse + 54, // 130: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse + 52, // 131: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr + 52, // 132: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr + 74, // 133: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse + 66, // 134: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse + 68, // 135: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse + 64, // 136: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile + 71, // 137: taprpc.TaprootAssets.UnpackProofFile:output_type -> taprpc.UnpackProofFileResponse + 78, // 138: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse + 84, // 139: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse + 87, // 140: taprpc.TaprootAssets.ListBurns:output_type -> taprpc.ListBurnsResponse + 80, // 141: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse + 97, // 142: taprpc.TaprootAssets.BakeMacaroon:output_type -> taprpc.BakeMacaroonResponse + 82, // 143: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.FetchAssetMetaResponse + 89, // 144: taprpc.TaprootAssets.SubscribeReceiveEvents:output_type -> taprpc.ReceiveEvent + 91, // 145: taprpc.TaprootAssets.SubscribeSendEvents:output_type -> taprpc.SendEvent + 94, // 146: taprpc.TaprootAssets.RegisterTransfer:output_type -> taprpc.RegisterTransferResponse + 122, // [122:147] is the sub-list for method output_type + 97, // [97:122] is the sub-list for method input_type + 97, // [97:97] is the sub-list for extension type_name + 97, // [97:97] is the sub-list for extension extendee + 0, // [0:97] is the sub-list for field type_name } func init() { file_taprootassets_proto_init() } diff --git a/taprpc/taprootassets.proto b/taprpc/taprootassets.proto index 9a50826da9..4789314e36 100644 --- a/taprpc/taprootassets.proto +++ b/taprpc/taprootassets.proto @@ -827,6 +827,15 @@ message AssetGroupBalance { // The total balance of the assets in the group. uint64 balance = 2; + + // The genesis info of a representative issuance in the group. For fungible + // assets, all tranches share the same name and type, so this provides the + // canonical metadata for the group. + GenesisInfo asset_genesis = 3; + + // The decimal display value for the asset group, extracted from the + // representative issuance's metadata. + DecimalDisplay decimal_display = 4; } message ListBalancesResponse { diff --git a/taprpc/taprootassets.swagger.json b/taprpc/taprootassets.swagger.json index 0ce2b859ca..19ccd9ab9a 100644 --- a/taprpc/taprootassets.swagger.json +++ b/taprpc/taprootassets.swagger.json @@ -1890,6 +1890,14 @@ "type": "string", "format": "uint64", "description": "The total balance of the assets in the group." + }, + "asset_genesis": { + "$ref": "#/definitions/taprpcGenesisInfo", + "description": "The genesis info of a representative issuance in the group. For fungible\nassets, all tranches share the same name and type, so this provides the\ncanonical metadata for the group." + }, + "decimal_display": { + "$ref": "#/definitions/taprpcDecimalDisplay", + "description": "The decimal display value for the asset group, extracted from the\nrepresentative issuance's metadata." } } },