Skip to content

Commit

Permalink
universe: update MultiverseRoot to use a single db query
Browse files Browse the repository at this point in the history
In this commit, we modify the `MultiverseRoot` method to use a single DB
query. If we don't have anything to filter, then we'll fetch the global
root for that proof type.

Otherwise, we'll query the DB for the leaves that match the set of IDs,
then insert those into our in-memory tree.

This saves us N DB queries where N is the size of the set of Identifier.
  • Loading branch information
Roasbeef authored and guggero committed Dec 12, 2023
1 parent 6744d9c commit 9d55839
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 47 deletions.
9 changes: 6 additions & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3005,9 +3005,12 @@ func (r *rpcServer) MultiverseRoot(ctx context.Context,
err)
}

return &unirpc.MultiverseRootResponse{
MultiverseRoot: marshalMssmtNode(rootNode),
}, nil
var resp unirpc.MultiverseRootResponse
rootNode.WhenSome(func(node universe.MultiverseRoot) {
resp.MultiverseRoot = marshalMssmtNode(node)
})

return &resp, nil
}

// AssetRoots queries for the known Universe roots associated with each known
Expand Down
83 changes: 45 additions & 38 deletions universe/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,63 +139,70 @@ func (a *Archive) RootNodes(ctx context.Context,
// proof type. If the given list of universe IDs is non-empty, then the root
// will be calculated just for those universes.
func (a *Archive) MultiverseRoot(ctx context.Context, proofType ProofType,
filterByIDs []Identifier) (mssmt.Node, error) {
filterByIDs []Identifier) (fn.Option[MultiverseRoot], error) {

log.Debugf("Fetching multiverse root for proof type: %v", proofType)

leaveIDs, err := a.cfg.Multiverse.FetchLeaves(ctx, nil, nil, proofType)
if err != nil {
return nil, fmt.Errorf("unable to fetch multiverse leaves: %w",
err)
}
none := fn.None[MultiverseRoot]()

// If a filter list is provided, then we'll only include the leaves
// that are in the filter list.
includeUniverse := func(id Identifier) bool {
if len(filterByIDs) == 0 {
return true
// If we don't have any IDs, then we'll return the multiverse root for
// the given proof type.
if len(filterByIDs) == 0 {
rootNode, err := a.cfg.Multiverse.MultiverseRootNode(
ctx, proofType,
)
if err != nil {
return none, err
}

for _, filterID := range filterByIDs {
if id.IsEqual(filterID) {
return true
}
return rootNode, nil
}

// Otherwise, we'll run the query to fetch the multiverse leaf for each
// of the specified assets.
uniTargets := make([]MultiverseLeafDesc, len(filterByIDs))
for idx, id := range filterByIDs {
if id.GroupKey != nil {
uniTargets[idx] = fn.NewRight[asset.ID](*id.GroupKey)
} else {
uniTargets[idx] = fn.NewLeft[asset.ID, btcec.PublicKey](
id.AssetID,
)
}
}

return false
multiverseLeaves, err := a.cfg.Multiverse.FetchLeaves(
ctx, uniTargets, proofType,
)
if err != nil {
return none, fmt.Errorf("unable to fetch multiverse "+
"leaves: %w", err)
}

// Now that we have the leaves, we'll insert them into an in-memory
// tree, so we can obtain the root for this unique combination.
memStore := mssmt.NewDefaultStore()
tree := mssmt.NewCompactedTree(memStore)

for _, id := range leaveIDs {
// Only include the universe if it's in the filter list (given
// the filter list is non-empty).
if !includeUniverse(id) {
continue
}

uniRoot, err := a.cfg.Multiverse.UniverseRootNode(ctx, id)
for _, leaf := range multiverseLeaves {
_, err = tree.Insert(ctx, leaf.ID.Bytes(), leaf.LeafNode)
if err != nil {
return nil, fmt.Errorf("unable to fetch universe "+
"root: %w", err)
return none, fmt.Errorf("unable to insert "+
"leaf: %w", err)
}
}

rootHash := uniRoot.NodeHash()
rootSum := uniRoot.NodeSum()

if id.ProofType == ProofTypeIssuance {
rootSum = 1
}
customRoot, err := tree.Root(ctx)
if err != nil {
return none, fmt.Errorf("unable to obtain root: %w", err)
}

uniLeaf := mssmt.NewLeafNode(rootHash[:], rootSum)
_, err = tree.Insert(ctx, id.Bytes(), uniLeaf)
if err != nil {
return nil, fmt.Errorf("unable to insert leaf: %w", err)
}
multiverseRoot := MultiverseRoot{
ProofType: proofType,
Node: customRoot,
}

return tree.Root(ctx)
return fn.Some(multiverseRoot), nil
}

// UpsertProofLeaf attempts to upsert a proof for an asset issuance or transfer
Expand Down
16 changes: 10 additions & 6 deletions universe/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,12 +428,16 @@ type MultiverseArchive interface {
UniverseLeafKeys(ctx context.Context,
q UniverseLeafKeysQuery) ([]LeafKey, error)

// FetchLeaves returns the set of multiverse leaves for the given proof
// type, asset ID, and group key. If both asset ID and group key is nil,
// all leaves for the given proof type will be returned.
FetchLeaves(ctx context.Context, assetID *asset.ID,
groupKey *btcec.PublicKey,
proofType ProofType) ([]Identifier, error)
// FetchLeaves returns the set of multiverse leaves that satisfy the set
// of universe targets. If the set of targets is empty, all leaves for
// the given proof type will be returned.
FetchLeaves(ctx context.Context, universeTargets []MultiverseLeafDesc,
proofType ProofType) ([]MultiverseLeaf, error)

// MultiverseRootNode returns the Multiverse root node for the given
// proof type.
MultiverseRootNode(ctx context.Context,
proofType ProofType) (fn.Option[MultiverseRoot], error)
}

// Registrar is an interface that allows a caller to upsert a proof leaf in a
Expand Down

0 comments on commit 9d55839

Please sign in to comment.