Skip to content

Commit

Permalink
Merge pull request #553 from lightninglabs/mint-rpc-fixes
Browse files Browse the repository at this point in the history
Improve clarity on asset mint and universe stats RPC responses
  • Loading branch information
Roasbeef authored Oct 11, 2023
2 parents abdfae2 + 975a969 commit 9330a5d
Show file tree
Hide file tree
Showing 14 changed files with 797 additions and 407 deletions.
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 {
// 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
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,
})
}

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
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

0 comments on commit 9330a5d

Please sign in to comment.