Skip to content

Commit

Permalink
Merge pull request #416 from ffranr/multiverse_register_issuance
Browse files Browse the repository at this point in the history
Multiverse fetch/register issuance
  • Loading branch information
ffranr authored Aug 9, 2023
2 parents edf59c6 + deda171 commit b4c8186
Show file tree
Hide file tree
Showing 10 changed files with 645 additions and 373 deletions.
42 changes: 42 additions & 0 deletions itest/universe_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package itest

import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2/schnorr"
tap "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/mssmt"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc"
Expand Down Expand Up @@ -162,6 +164,38 @@ func testUniverseSync(t *harnessTest) {
firstAssetUniProof, err := bob.QueryProof(ctxt, &firstAssetProofQuery)
require.NoError(t.t, err)

// Verify the multiverse inclusion proof for the first asset.
firstAssetUniMssmtRoot := unmarshalMerkleSumNode(
firstAssetUniProof.UniverseRoot.MssmtRoot,
)

multiverseRoot := unmarshalMerkleSumNode(
firstAssetUniProof.MultiverseRoot,
)

var compressedProof mssmt.CompressedProof
err = compressedProof.Decode(
bytes.NewReader(firstAssetUniProof.MultiverseInclusionProof),
)
require.NoError(t.t, err)

multiverseInclusionProof, err := compressedProof.Decompress()
require.NoError(t.t, err)

assetIdFixedSize := fn.ToArray[[32]byte](firstAssetID)

firstAssetAmount := rpcSimpleAssets[0].Amount
nodeHash := firstAssetUniMssmtRoot.NodeHash()
leaf := mssmt.NewLeafNode(
nodeHash[:], firstAssetAmount,
)

verifyProofResult := mssmt.VerifyMerkleProof(
assetIdFixedSize, leaf, multiverseInclusionProof,
multiverseRoot,
)
require.True(t.t, verifyProofResult)

firstAssetFromUni := firstAssetUniProof.AssetLeaf.Asset
firstAssetFromUni.PrevWitnesses = nil
assertAsset(t.t, rpcSimpleAssets[0], firstAssetFromUni)
Expand Down Expand Up @@ -211,6 +245,14 @@ func testUniverseSync(t *harnessTest) {
)
}

// unmarshalMerkleSumNode un-marshals a protobuf MerkleSumNode.
func unmarshalMerkleSumNode(root *unirpc.MerkleSumNode) mssmt.Node {
var nodeHash mssmt.NodeHash
copy(nodeHash[:], root.RootHash)

return mssmt.NewComputedBranch(nodeHash, uint64(root.RootSum))
}

// testUniverseREST tests that we're able to properly query the universe state
// via the REST interface.
func testUniverseREST(t *harnessTest) {
Expand Down
46 changes: 32 additions & 14 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2298,21 +2298,27 @@ func marshalUniID(id universe.Identifier) *unirpc.ID {
return &uniID
}

// marshalMssmtNode marshals a MS-SMT node into the RPC counterpart.
func marshalMssmtNode(node mssmt.Node) *unirpc.MerkleSumNode {
nodeHash := node.NodeHash()

return &unirpc.MerkleSumNode{
RootHash: nodeHash[:],
RootSum: int64(node.NodeSum()),
}
}

// marshallUniverseRoot marshals the universe root into the RPC counterpart.
func marshalUniverseRoot(node universe.BaseRoot) (*unirpc.UniverseRoot, error) {
// There was no old base root, so we'll just return a blank root.
if node.Node == nil {
return &unirpc.UniverseRoot{}, nil
}

nodeHash := node.Node.NodeHash()
mssmtRoot := marshalMssmtNode(node.Node)

return &unirpc.UniverseRoot{
Id: marshalUniID(node.ID),
MssmtRoot: &unirpc.MerkleSumNode{
RootHash: nodeHash[:],
RootSum: int64(node.Node.NodeSum()),
},
Id: marshalUniID(node.ID),
MssmtRoot: mssmtRoot,
AssetName: node.AssetName,
}, nil
}
Expand Down Expand Up @@ -2661,8 +2667,8 @@ func unmarshalLeafKey(key *unirpc.AssetKey) (universe.BaseKey, error) {
return baseKey, nil
}

// marshalUniverseProof marshals a universe proof into the RPC form.
func marshalUniverseProof(proof *mssmt.Proof) ([]byte, error) {
// marshalMssmtProof marshals a MS-SMT proof into the RPC form.
func marshalMssmtProof(proof *mssmt.Proof) ([]byte, error) {
compressedProof := proof.Compress()

var b bytes.Buffer
Expand All @@ -2678,7 +2684,7 @@ func (r *rpcServer) marshalIssuanceProof(ctx context.Context,
req *unirpc.UniverseKey,
proof *universe.IssuanceProof) (*unirpc.AssetProofResponse, error) {

uniProof, err := marshalUniverseProof(proof.InclusionProof)
uniProof, err := marshalMssmtProof(proof.InclusionProof)
if err != nil {
return nil, err
}
Expand All @@ -2698,11 +2704,23 @@ func (r *rpcServer) marshalIssuanceProof(ctx context.Context,
uniRoot.AssetName = assetLeaf.Asset.AssetGenesis.Name
uniRoot.Id = req.Id

// Marshal multiverse specific fields.
multiverseRoot := marshalMssmtNode(proof.MultiverseRoot)

multiverseProof, err := marshalMssmtProof(
proof.MultiverseInclusionProof,
)
if err != nil {
return nil, err
}

return &unirpc.AssetProofResponse{
Req: req,
UniverseRoot: uniRoot,
UniverseInclusionProof: uniProof,
AssetLeaf: assetLeaf,
Req: req,
UniverseRoot: uniRoot,
UniverseInclusionProof: uniProof,
AssetLeaf: assetLeaf,
MultiverseRoot: multiverseRoot,
MultiverseInclusionProof: multiverseProof,
}, nil
}

Expand Down
28 changes: 4 additions & 24 deletions tapdb/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package tapdb
import (
"bytes"
"context"
"crypto/sha256"
"database/sql"
"encoding/hex"
"errors"
"fmt"
"sync"
Expand Down Expand Up @@ -41,12 +39,6 @@ type (
UniverseLeaf = sqlc.QueryUniverseLeavesRow
)

var (
// ErrNoUniverseProofFound is returned when a user attempts to look up
// a key in the universe that actually points to the empty leaf.
ErrNoUniverseProofFound = fmt.Errorf("no universe proof found")
)

// BaseUniverseStore is the main interface for the Taproot Asset universe store.
// This is a composite of the capabilities to insert new asset genesis, update
// the SMT tree, and finally fetch a genesis. We then combine that with Universe
Expand Down Expand Up @@ -135,26 +127,14 @@ type BaseUniverseTree struct {
smtNamespace string
}

// idToNameSpace maps a universe ID to a string namespace.
func idToNameSpace(id universe.Identifier) string {
if id.GroupKey != nil {
h := sha256.Sum256(schnorr.SerializePubKey(id.GroupKey))
return hex.EncodeToString(h[:])
}

return hex.EncodeToString(id.AssetID[:])
}

// NewBaseUniverseTree creates a new base Universe tree.
func NewBaseUniverseTree(db BatchedUniverseTree,
id universe.Identifier) *BaseUniverseTree {

namespace := idToNameSpace(id)

return &BaseUniverseTree{
db: db,
id: id,
smtNamespace: namespace,
smtNamespace: id.String(),
}
}

Expand Down Expand Up @@ -349,7 +329,7 @@ func universeRegisterIssuance(ctx context.Context, dbTx BaseUniverseStore,
metaReveal *proof.MetaReveal) (*universe.IssuanceProof, mssmt.Node,
error) {

namespace := idToNameSpace(id)
namespace := id.String()

// With the tree store created, we'll now obtain byte representation of
// the minting key, as that'll be the key in the SMT itself.
Expand Down Expand Up @@ -489,7 +469,7 @@ func universeFetchIssuanceProof(ctx context.Context,
id universe.Identifier, universeKey universe.BaseKey,
dbTx BaseUniverseStore) ([]*universe.IssuanceProof, error) {

namespace := idToNameSpace(id)
namespace := id.String()

// Depending on the universeKey, we'll either be fetching the details of
// a specific issuance, or each issuance for that minting outpoint.
Expand Down Expand Up @@ -537,7 +517,7 @@ func universeFetchIssuanceProof(ctx context.Context,
}

if len(universeLeaves) == 0 {
return nil, ErrNoUniverseProofFound
return nil, universe.ErrNoUniverseProofFound
}

// Now that we have all the leaves we need to query, we'll look each up
Expand Down
Loading

0 comments on commit b4c8186

Please sign in to comment.