Skip to content

Commit

Permalink
Merge pull request #1050 from lightninglabs/proof_decode_read_metareveal
Browse files Browse the repository at this point in the history
taprpc: marshal with metareveal of issuance proof
  • Loading branch information
guggero authored Jul 25, 2024
2 parents 054a66c + 21c1148 commit 0dfdb12
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 23 deletions.
9 changes: 9 additions & 0 deletions itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ func AssetVersionCheck(version taprpc.AssetVersion) AssetCheck {
// non-nil decimal display value.
func AssetDecimalDisplayCheck(decDisplay uint32) AssetCheck {
return func(a *taprpc.Asset) error {
// If we didn't set a decimal display in the mint request, we
// don't expect one to be set.
if decDisplay == 0 &&
(a.DecimalDisplay == nil ||
a.DecimalDisplay.DecimalDisplay == 0) {

return nil
}

if a.DecimalDisplay == nil {
return fmt.Errorf("asset decimal display is nil")
}
Expand Down
4 changes: 2 additions & 2 deletions proof/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ func DecodeMetaJSON(jBytes []byte) (map[string]interface{}, error) {
// These bytes must match our metadata size constraints.
err := IsValidMetaSize(jBytes, MetaDataMaxSizeBytes)
if err != nil {
return nil, err
return nil, fmt.Errorf("%w: %s", ErrInvalidJSON, err.Error())
}

// Unmarshal checks internally if the JSON is valid.
err = json.Unmarshal(jBytes, &jMeta)
if err != nil {
return nil, err
return nil, fmt.Errorf("%w: %s", ErrInvalidJSON, err.Error())
}

return jMeta, nil
Expand Down
37 changes: 37 additions & 0 deletions proof/meta_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package proof

import (
"bytes"
"encoding/hex"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

var (
// proofInvalidJsonHexFileName is the name of the file that contains the
// hex proof data for a proof where the meta type is declared as JSON
// but the data is not valid JSON.
proofInvalidJsonHexFileName = filepath.Join(
testDataFileName, "proof-invalid-json-meta-reveal.hex",
)
)

func TestValidateMetaReveal(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -78,3 +92,26 @@ func TestValidateMetaReveal(t *testing.T) {
})
}
}

// TestProofInvalidJsonMetaReveal tests that a proof with a meta reveal that
// is declared as JSON but is not valid JSON will return the correct error when
// trying to decode the decimal display.
func TestProofInvalidJsonMetaReveal(t *testing.T) {
proofHex, err := os.ReadFile(proofInvalidJsonHexFileName)
require.NoError(t, err)

proofBytes, err := hex.DecodeString(
strings.Trim(string(proofHex), "\n"),
)
require.NoError(t, err)

p := &Proof{}
err = p.Decode(bytes.NewReader(proofBytes))
require.NoError(t, err)

require.NotNil(t, p.MetaReveal)

_, decDisplay, err := p.MetaReveal.GetDecDisplay()
require.ErrorIs(t, err, ErrInvalidJSON)
require.Zero(t, decDisplay)
}
1 change: 1 addition & 0 deletions proof/testdata/proof-invalid-json-meta-reveal.hex

Large diffs are not rendered by default.

63 changes: 43 additions & 20 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ func (r *rpcServer) fetchRpcAssets(ctx context.Context, withWitness,
rpcAssets := make([]*taprpc.Asset, len(assets))
for i, a := range assets {
rpcAssets[i], err = r.MarshalChainAsset(
ctx, a, withWitness, r.cfg.AddrBook,
ctx, a, nil, withWitness, r.cfg.AddrBook,
)
if err != nil {
return nil, fmt.Errorf("unable to marshal asset: %w",
Expand All @@ -1048,16 +1048,28 @@ func (r *rpcServer) fetchRpcAssets(ctx context.Context, withWitness,

// MarshalChainAsset marshals the given chain asset into an RPC asset.
func (r *rpcServer) MarshalChainAsset(ctx context.Context, a *asset.ChainAsset,
withWitness bool, keyRing taprpc.KeyLookup) (*taprpc.Asset, error) {
meta *proof.MetaReveal, withWitness bool,
keyRing taprpc.KeyLookup) (*taprpc.Asset, error) {

decDisplay, err := r.DecDisplayForAssetID(ctx, a.ID())
var (
decDisplay fn.Option[uint32]
err error
)

// If the asset metadata is provided, we don't need to look it up from
// the database when decoding a decimal display value.
switch {
case meta != nil:
decDisplay, err = getDecimalDisplayNonStrict(meta)
default:
decDisplay, err = r.DecDisplayForAssetID(ctx, a.ID())
}
if err != nil {
return nil, err
}

rpcAsset, err := taprpc.MarshalAsset(
ctx, a.Asset, a.IsSpent, withWitness, keyRing,
fn.Some(decDisplay),
ctx, a.Asset, a.IsSpent, withWitness, keyRing, decDisplay,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1748,7 +1760,7 @@ func (r *rpcServer) marshalProof(ctx context.Context, p *proof.Proof,
AnchorInternalKey: p.InclusionProof.InternalKey,
AnchorMerkleRoot: merkleRoot[:],
AnchorTapscriptSibling: tsSibling,
}, withPrevWitnesses, r.cfg.AddrBook)
}, p.MetaReveal, withPrevWitnesses, r.cfg.AddrBook)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -4997,7 +5009,7 @@ func (r *rpcServer) AssetLeaves(ctx context.Context,
}

resp.Leaves[i], err = r.marshalAssetLeaf(
ctx, &assetLeaf, fn.Some(decDisplay),
ctx, &assetLeaf, decDisplay,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -5100,9 +5112,7 @@ func (r *rpcServer) marshalUniverseProofLeaf(ctx context.Context,
return nil, err
}

assetLeaf, err := r.marshalAssetLeaf(
ctx, proof.Leaf, fn.Some(decDisplay),
)
assetLeaf, err := r.marshalAssetLeaf(ctx, proof.Leaf, decDisplay)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -5519,7 +5529,7 @@ func (r *rpcServer) marshalUniverseDiff(ctx context.Context,
}

leaves[i], err = r.marshalAssetLeaf(
ctx, leaf, fn.Some(decDisplay),
ctx, leaf, decDisplay,
)
if err != nil {
return err
Expand Down Expand Up @@ -5603,8 +5613,8 @@ func marshalUniverseServer(
// of the local Universe server. These servers are used to push out new proofs,
// and also periodically call sync new proofs from the remote server.
func (r *rpcServer) ListFederationServers(ctx context.Context,
_ *unirpc.ListFederationServersRequest,
) (*unirpc.ListFederationServersResponse, error) {
_ *unirpc.ListFederationServersRequest) (
*unirpc.ListFederationServersResponse, error) {

uniServers, err := r.cfg.FederationDB.UniverseServers(ctx)
if err != nil {
Expand Down Expand Up @@ -6791,30 +6801,43 @@ func encodeVirtualPackets(packets []*tappsbt.VPacket) ([][]byte, error) {
// DecDisplayForAssetID attempts to fetch the meta reveal for a specific asset
// ID and extract the decimal display value from it.
func (r *rpcServer) DecDisplayForAssetID(ctx context.Context,
id asset.ID) (uint32, error) {
id asset.ID) (fn.Option[uint32], error) {

meta, err := r.cfg.AssetStore.FetchAssetMetaForAsset(
ctx, id,
)
if err != nil {
return 0, fmt.Errorf("unable to fetch asset meta "+
"for asset_id=%v :%v", id, err)
return fn.None[uint32](), fmt.Errorf("unable to fetch asset "+
"meta for asset_id=%v :%v", id, err)
}

return getDecimalDisplayNonStrict(meta)
}

// getDecimalDisplayNonStrict attempts to decode a decimal display value from
// metadata. If no custom decimal display value is decoded, the default value of
// 0 is returned without error.
func getDecimalDisplayNonStrict(
meta *proof.MetaReveal) (fn.Option[uint32], error) {

_, decDisplay, err := meta.GetDecDisplay()
switch {
// If it isn't JSON, or doesn't have a dec display, we'll just return 0
// below.
case errors.Is(err, proof.ErrNotJSON):
fallthrough
case errors.Is(err, proof.ErrInvalidJSON):
fallthrough
case errors.Is(err, proof.ErrDecDisplayMissing):
fallthrough
case errors.Is(err, proof.ErrDecDisplayInvalidType):
break
// We can't determine if there is a decimal display value set.
return fn.None[uint32](), nil

case err != nil:
return 0, fmt.Errorf("unable to extract decimal "+
"display for asset_id=%v :%v", id, err)
return fn.None[uint32](), fmt.Errorf("unable to extract "+
"decimal display: %v", err)
}

return decDisplay, nil
return fn.Some(decDisplay), nil
}
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const (
AppMinor uint = 4

// AppPatch defines the application patch for this binary.
AppPatch uint = 0
AppPatch uint = 1

// AppStatus defines the release status of this binary (e.g. beta).
AppStatus = "alpha"
Expand Down

0 comments on commit 0dfdb12

Please sign in to comment.