Skip to content

Commit

Permalink
WIP: GroupKeyRevealV1 unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ffranr committed Dec 17, 2024
1 parent 9b54dc3 commit 6d27d92
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions asset/group_key_reveal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package asset

import (
"bytes"
"crypto/rand"
"fmt"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/internal/test"
"github.com/stretchr/testify/require"
)

//// MockGKRRecord creates a TLV record for a GroupKeyReveal.
//func MockGKRRecord(reveal *GroupKeyReveal) tlv.Record {

Check failure on line 18 in asset/group_key_reveal_test.go

View workflow job for this annotation

GitHub Actions / Lint check

commentFormatting: put a space between `//` and comment text (gocritic)
// recordSize := func() uint64 {
// if reveal == nil || *reveal == nil {
// return 0
// }
// r := *reveal
// return uint64(
// btcec.PubKeyBytesLenCompressed + len(r.TapscriptRoot()),
// )
// }
// return tlv.MakeDynamicRecord(
// 0, reveal, recordSize, GroupKeyRevealEncoder, GroupKeyRevealDecoder,
// )
//}

type testCaseGkrEncodeDecode struct {
testName string

internalKey btcec.PublicKey
genesisAssetID ID
customSubtreeRoot fn.Option[chainhash.Hash]
}

// GroupKeyReveal generates a GroupKeyReveal instance from the test case.
func (tc testCaseGkrEncodeDecode) GroupKeyReveal() (GroupKeyReveal, error) {
gkr, err := NewGroupKeyRevealV1(
tc.internalKey, tc.genesisAssetID, tc.customSubtreeRoot,
).Unpack()

return &gkr, err
}

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

// Create a random internal public key.
internalKey := *(test.RandPubKey(t))

// Create a random genesis asset ID.
randomAssetIdBytes := test.RandBytes(32)
genesisAssetID := ID(randomAssetIdBytes)

testCases := []testCaseGkrEncodeDecode{
{
testName: "no custom root",

internalKey: internalKey,
genesisAssetID: genesisAssetID,
customSubtreeRoot: fn.None[chainhash.Hash](),
},
}

for _, tc := range testCases {
t.Run(tc.testName, func(tt *testing.T) {
gkr, err := tc.GroupKeyReveal()
require.NoError(tt, err)

// Encode the GroupKeyReveal into buffer.
var buffer bytes.Buffer
var scratchBuffEncode [8]byte
err = GroupKeyRevealEncoder(&buffer, &gkr, &scratchBuffEncode)
require.NoError(tt, err)

// Decode the GroupKeyReveal from buffer.
var gkrDecoded GroupKeyReveal
var scratchBuffDecode [8]byte
err = GroupKeyRevealDecoder(
&buffer, &gkrDecoded, &scratchBuffDecode, uint64(buffer.Len()),
)
require.NoError(tt, err)

// Ensure the decoded GroupKeyReveal matches the original.
require.Equal(
tt, gkr, gkrDecoded,
"decoded GroupKeyReveal does not match original",
)
})
}
}

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

// Construct an asset ID leaf.
assetIDLeaf := txscript.NewBaseTapLeaf(
[]byte("something something OP_RETURN <asset ID>"),
)
assetIDLeafHash := assetIDLeaf.TapHash()

// Construct a custom user script leaf. This is used to validate the
// control block.
customScriptLeaf := txscript.NewBaseTapLeaf(
[]byte("I'm a custom user script"),
)

// Construct a non-executable script leaf.
noExecScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_RETURN).
Script()
require.NoError(t, err)

noExecScriptLeaf := txscript.TapLeaf{
Script: noExecScript,
LeafVersion: txscript.BaseLeafVersion,
}

noExecScriptLeafHash := noExecScriptLeaf.TapHash()

// This is the user's custom tapscript tree root hash. It can be set or
// unset, up to the user. If unset, using [32]byte{} as the hash will be
// fine.
//
// It can also be a leaf hash or a branch hash.
userNodeHash := customScriptLeaf.TapHash()

branchHash := TapBranchHash(noExecScriptLeafHash, userNodeHash)
actualTapscriptRootHash4 := TapBranchHash(assetIDLeafHash, branchHash)
fmt.Printf("Tapscript root hash with optional user tree root: %v\n",
actualTapscriptRootHash4)

// Construct the user subtree control block.
internalKey := RandInternalPubKey()
outputKey := txscript.ComputeTaprootOutputKey(
&internalKey, actualTapscriptRootHash4[:],
)
outputKeyIsOdd := outputKey.SerializeCompressed()[0] == 0x03

inclusionProof := bytes.Join(
[][]byte{
noExecScriptLeafHash[:],
assetIDLeafHash[:],
}, nil,
)

userSubtreeControlBlock := txscript.ControlBlock{
InternalKey: &internalKey,
OutputKeyYIsOdd: outputKeyIsOdd,
LeafVersion: txscript.BaseLeafVersion,
InclusionProof: inclusionProof,
}

// Ensure the custom script control block is correct by computing the
// root hash given the control block and the custom script leaf.
// Ensure the custom script control block is correct by computing the
// root hash given the control block and the custom script leaf.
rootCheckBytes := userSubtreeControlBlock.RootHash(
customScriptLeaf.Script,
)
var rootCheck chainhash.Hash
copy(rootCheck[:], rootCheckBytes)

require.Equal(t, actualTapscriptRootHash4, rootCheck)
}

// RandInternalPubKey generates a random internal public key.
func RandInternalPubKey() btcec.PublicKey {
randBytes := make([]byte, 32)
_, _ = rand.Read(randBytes)

privateKey, _ := btcec.PrivKeyFromBytes(randBytes)
pubKey := privateKey.PubKey()
return *pubKey
}

0 comments on commit 6d27d92

Please sign in to comment.