Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve clarity on asset mint and universe stats RPC responses #553

Merged
merged 6 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,7 @@ func AssertUniverseKeysEqual(t *testing.T, uniIDs []*unirpc.ID,
}

func AssertUniverseStats(t *testing.T, client unirpc.UniverseClient,
numProofs, numSyncs, numAssets int) {
numProofs, numSyncs, numAssets, numGroups int) {

err := wait.NoError(func() error {
uniStats, err := client.UniverseStats(
Expand All @@ -1232,6 +1232,10 @@ func AssertUniverseStats(t *testing.T, client unirpc.UniverseClient,
return fmt.Errorf("expected %v assets, got %v",
numAssets, uniStats.NumTotalAssets)
}
if numGroups != int(uniStats.NumTotalGroups) {
return fmt.Errorf("expected %v groups, got %v",
numGroups, uniStats.NumTotalGroups)
}

return nil
}, defaultTimeout)
Expand All @@ -1247,27 +1251,31 @@ func AssertUniverseAssetStats(t *testing.T, node *tapdHarness,
require.Len(t, assetStats.AssetStats, len(assets))

for _, assetStat := range assetStats.AssetStats {
found := fn.Any(
assets, func(a *taprpc.Asset) bool {
groupKeyEqual := true
if a.AssetGroup != nil {
groupKeyEqual = bytes.Equal(
assetStat.GroupKey,
a.AssetGroup.TweakedGroupKey,
)
}
var statAsset *unirpc.AssetStatsAsset
if assetStat.GroupAnchor != nil {
statAsset = assetStat.GroupAnchor
} else {
statAsset = assetStat.Asset
}

return groupKeyEqual && bytes.Equal(
assetStat.AssetId,
a.AssetGenesis.AssetId,
found := fn.Any(assets, func(a *taprpc.Asset) bool {
groupKeyEqual := true
if a.AssetGroup != nil {
groupKeyEqual = bytes.Equal(
assetStat.GroupKey,
a.AssetGroup.TweakedGroupKey,
)
},
)
}

return groupKeyEqual && bytes.Equal(
statAsset.AssetId, a.AssetGenesis.AssetId,
)
})
require.True(t, found)

require.NotZero(t, assetStat.GenesisHeight)
require.NotZero(t, assetStat.GenesisTimestamp)
require.NotEmpty(t, assetStat.GenesisPoint)
require.NotZero(t, statAsset.GenesisHeight)
require.NotZero(t, statAsset.GenesisTimestamp)
require.NotEmpty(t, statAsset.GenesisPoint)
}

eventStats, err := node.QueryEvents(ctxb, &unirpc.QueryEventsRequest{})
Expand Down
4 changes: 2 additions & 2 deletions itest/universe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func testUniverseFederation(t *harnessTest) {

// Bob's Universe stats should show that he now has a single asset. We
// should also be able to query for stats specifically for the asset.
AssertUniverseStats(t.t, bob, 1, 0, 1)
AssertUniverseStats(t.t, bob, 1, 0, 1, 0)

// Test the content of the universe info call.
info, err := bob.Info(ctxt, &unirpc.InfoRequest{})
Expand Down Expand Up @@ -478,7 +478,7 @@ func testUniverseFederation(t *harnessTest) {

// Bob's stats should also now show that there're three total asset as
// well as three proofs.
AssertUniverseStats(t.t, bob, 3, 0, 3)
AssertUniverseStats(t.t, bob, 3, 0, 3, 1)

// We should be able to find both the new assets in the set of universe
// stats for an asset.
Expand Down
120 changes: 98 additions & 22 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2262,15 +2262,48 @@ func marshalMintingBatch(batch *tapgarden.MintingBatch,
return rpcBatch, nil
}

rpcBatch.Assets = make([]*mintrpc.MintAsset, 0, len(batch.Seedlings))
for _, seedling := range batch.Seedlings {
// When we have sprouts, then they represent the same assets as the
// seedlings but in a more "grown up" state. So in that case we only
// marshal the sprouts.
switch {
guggero marked this conversation as resolved.
Show resolved Hide resolved
// We have sprouts, ignore seedlings.
case batch.RootAssetCommitment != nil &&
len(batch.RootAssetCommitment.CommittedAssets()) > 0:

rpcBatch.Assets = marshalSprouts(
batch.RootAssetCommitment.CommittedAssets(),
batch.AssetMetas,
)

// No sprouts, so we marshal the seedlings.
case len(batch.Seedlings) > 0:
rpcBatch.Assets, err = marshalSeedlings(batch.Seedlings)
if err != nil {
return nil, err
}
}

return rpcBatch, nil
}

// marshalSeedlings marshals the seedlings into the RPC counterpart.
func marshalSeedlings(
seedlings map[string]*tapgarden.Seedling) ([]*mintrpc.MintAsset, error) {

rpcAssets := make([]*mintrpc.MintAsset, 0, len(seedlings))
for _, seedling := range seedlings {
var groupKeyBytes []byte
if seedling.HasGroupKey() {
groupKey := seedling.GroupInfo.GroupKey
groupPubKey := groupKey.GroupPubKey
groupKeyBytes = groupPubKey.SerializeCompressed()
}

var groupAnchor string
guggero marked this conversation as resolved.
Show resolved Hide resolved
if seedling.GroupAnchor != nil {
groupAnchor = *seedling.GroupAnchor
}

var seedlingMeta *taprpc.AssetMeta
if seedling.Meta != nil {
seedlingMeta = &taprpc.AssetMeta{
Expand All @@ -2289,17 +2322,55 @@ func marshalMintingBatch(batch *tapgarden.MintingBatch,
return nil, err
}

rpcBatch.Assets = append(rpcBatch.Assets, &mintrpc.MintAsset{
rpcAssets = append(rpcAssets, &mintrpc.MintAsset{
AssetType: taprpc.AssetType(seedling.AssetType),
AssetVersion: assetVersion,
Name: seedling.AssetName,
AssetMeta: seedlingMeta,
Amount: seedling.Amount,
GroupKey: groupKeyBytes,
GroupAnchor: groupAnchor,
guggero marked this conversation as resolved.
Show resolved Hide resolved
})
}

return rpcBatch, nil
return rpcAssets, nil
}

// marshalSprouts marshals the sprouts into the RPC counterpart.
func marshalSprouts(sprouts []*asset.Asset,
metas tapgarden.AssetMetas) []*mintrpc.MintAsset {

rpcAssets := make([]*mintrpc.MintAsset, 0, len(sprouts))
for _, sprout := range sprouts {
scriptKey := asset.ToSerialized(sprout.ScriptKey.PubKey)

var assetMeta *taprpc.AssetMeta
if metas != nil {
if m, ok := metas[scriptKey]; ok && m != nil {
assetMeta = &taprpc.AssetMeta{
MetaHash: fn.ByteSlice(m.MetaHash()),
Data: m.Data,
Type: taprpc.AssetMetaType(m.Type),
}
}
}

var groupKeyBytes []byte
if sprout.GroupKey != nil {
gpk := sprout.GroupKey.GroupPubKey
groupKeyBytes = gpk.SerializeCompressed()
}

rpcAssets = append(rpcAssets, &mintrpc.MintAsset{
AssetType: taprpc.AssetType(sprout.Type),
Name: sprout.Tag,
AssetMeta: assetMeta,
Amount: sprout.Amount,
GroupKey: groupKeyBytes,
})
}

return rpcAssets
}

// marshalBatchState converts the batch state field into its RPC counterpart.
Expand Down Expand Up @@ -3410,32 +3481,40 @@ func (r *rpcServer) UniverseStats(ctx context.Context,

return &unirpc.StatsResponse{
NumTotalAssets: int64(universeStats.NumTotalAssets),
NumTotalGroups: int64(universeStats.NumTotalGroups),
NumTotalSyncs: int64(universeStats.NumTotalSyncs),
NumTotalProofs: int64(universeStats.NumTotalProofs),
}, nil
}

// marshalAssetSyncSnapshot maps a universe asset sync stat snapshot to the RPC
// counterpart.
func marshalAssetSyncSnapshot(
func (r *rpcServer) marshalAssetSyncSnapshot(ctx context.Context,
a universe.AssetSyncSnapshot) *unirpc.AssetStatsSnapshot {

var groupKey []byte
if a.GroupKey != nil {
groupKey = a.GroupKey.SerializeCompressed()
resp := &unirpc.AssetStatsSnapshot{
TotalSyncs: int64(a.TotalSyncs),
TotalProofs: int64(a.TotalProofs),
GroupSupply: int64(a.GroupSupply),
}
rpcAsset := &unirpc.AssetStatsAsset{
AssetId: a.AssetID[:],
GenesisPoint: a.GenesisPoint.String(),
AssetName: a.AssetName,
AssetType: taprpc.AssetType(a.AssetType),
TotalSupply: int64(a.TotalSupply),
GenesisHeight: int32(a.GenesisHeight),
GenesisTimestamp: r.getBlockTimestamp(ctx, a.GenesisHeight),
}

return &unirpc.AssetStatsSnapshot{
AssetId: a.AssetID[:],
GroupKey: groupKey,
GenesisPoint: a.GenesisPoint.String(),
AssetName: a.AssetName,
AssetType: taprpc.AssetType(a.AssetType),
TotalSupply: int64(a.TotalSupply),
GenesisHeight: int32(a.GenesisHeight),
TotalSyncs: int64(a.TotalSyncs),
TotalProofs: int64(a.TotalProofs),
if a.GroupKey != nil {
resp.GroupKey = a.GroupKey.SerializeCompressed()
resp.GroupAnchor = rpcAsset
} else {
resp.Asset = rpcAsset
}

return resp
}

// QueryAssetStats returns a set of statistics for a given set of assets.
Expand Down Expand Up @@ -3479,10 +3558,7 @@ func (r *rpcServer) QueryAssetStats(ctx context.Context,
),
}
for idx, snapshot := range assetStats.SyncStats {
resp.AssetStats[idx] = marshalAssetSyncSnapshot(snapshot)
resp.AssetStats[idx].GenesisTimestamp = r.getBlockTimestamp(
ctx, snapshot.GenesisHeight,
)
resp.AssetStats[idx] = r.marshalAssetSyncSnapshot(ctx, snapshot)
}

return resp, nil
Expand Down
3 changes: 2 additions & 1 deletion tapdb/sqlc/migrations/000002_assets.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ CREATE VIEW genesis_info_view AS
CREATE VIEW key_group_info_view AS
SELECT
witness_id, gen_asset_id, witness_stack, tapscript_root,
tweaked_group_key, raw_key, key_index, key_family
tweaked_group_key, raw_key, key_index, key_family,
substr(tweaked_group_key, 2) AS x_only_group_key
FROM asset_group_witnesses wit
JOIN asset_groups groups
ON wit.group_key_id = groups.group_id
Expand Down
1 change: 1 addition & 0 deletions tapdb/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 52 additions & 8 deletions tapdb/sqlc/queries/universe.sql
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,45 @@ INSERT INTO universe_events (
);

-- name: QueryUniverseStats :one
WITH num_assets As (
SELECT COUNT(*) AS num_assets
WITH stats AS (
SELECT total_asset_syncs, total_asset_proofs
FROM universe_stats
), group_ids AS (
SELECT id
FROM universe_roots
WHERE group_key IS NOT NULL
), asset_keys AS (
SELECT hash_key
FROM mssmt_nodes nodes
JOIN mssmt_roots roots
ON nodes.hash_key = roots.root_hash AND
nodes.namespace = roots.namespace
JOIN universe_roots uroots
ON roots.namespace = uroots.namespace_root
), aggregated AS (
SELECT COALESCE(SUM(stats.total_asset_syncs), 0) AS total_syncs,
COALESCE(SUM(stats.total_asset_proofs), 0) AS total_proofs,
0 AS total_num_groups,
0 AS total_num_assets
FROM stats
UNION ALL
SELECT 0 AS total_syncs,
0 AS total_proofs,
COALESCE(COUNT(group_ids.id), 0) AS total_num_groups,
0 AS total_num_assets
FROM group_ids
UNION ALL
SELECT 0 AS total_syncs,
0 AS total_proofs,
0 AS total_num_groups,
COALESCE(COUNT(asset_keys.hash_key), 0) AS total_num_assets
FROM asset_keys
)
SELECT COALESCE(SUM(universe_stats.total_asset_syncs), 0) AS total_syncs,
COALESCE(SUM(universe_stats.total_asset_proofs), 0) AS total_proofs,
COUNT(num_assets) AS total_num_assets
FROM universe_stats, num_assets;
SELECT SUM(total_syncs) AS total_syncs,
SUM(total_proofs) AS total_proofs,
SUM(total_num_groups) AS total_num_groups,
SUM(total_num_assets) AS total_num_assets
FROM aggregated;

-- TODO(roasbeef): use the universe id instead for the grouping? so namespace
-- root, simplifies queries
Expand All @@ -181,8 +212,17 @@ WITH asset_supply AS (
JOIN genesis_info_view gen
ON leaves.asset_genesis_id = gen.gen_asset_id
GROUP BY gen.asset_id
), group_supply AS (
SELECT sum AS num_assets, uroots.group_key AS group_key
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
FROM mssmt_nodes nodes
JOIN mssmt_roots roots
ON nodes.hash_key = roots.root_hash AND
nodes.namespace = roots.namespace
JOIN universe_roots uroots
ON roots.namespace = uroots.namespace_root
), asset_info AS (
SELECT asset_supply.supply, gen.asset_id AS asset_id,
SELECT asset_supply.supply, group_supply.num_assets AS group_supply,
gen.asset_id AS asset_id,
gen.asset_tag AS asset_name, gen.asset_type AS asset_type,
gen.block_height AS genesis_height, gen.prev_out AS genesis_prev_out,
group_info.tweaked_group_key AS group_key
Expand All @@ -194,11 +234,15 @@ WITH asset_supply AS (
-- doesn't have a group key.
LEFT JOIN key_group_info_view group_info
ON gen.gen_asset_id = group_info.gen_asset_id
LEFT JOIN group_supply
ON group_supply.group_key = group_info.x_only_group_key
WHERE (gen.asset_tag = sqlc.narg('asset_name') OR sqlc.narg('asset_name') IS NULL) AND
(gen.asset_type = sqlc.narg('asset_type') OR sqlc.narg('asset_type') IS NULL) AND
(gen.asset_id = sqlc.narg('asset_id') OR sqlc.narg('asset_id') IS NULL)
)
SELECT asset_info.supply AS asset_supply, asset_info.asset_name AS asset_name,
SELECT asset_info.supply AS asset_supply,
asset_info.group_supply AS group_supply,
asset_info.asset_name AS asset_name,
asset_info.asset_type AS asset_type, asset_info.asset_id AS asset_id,
asset_info.genesis_height AS genesis_height,
asset_info.genesis_prev_out AS genesis_prev_out,
Expand Down
Loading