Skip to content

Commit 7cfeac8

Browse files
committed
WIP: GroupKeyRevealV1 unit tests
1 parent 7aab0c7 commit 7cfeac8

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

asset/group_key_reveal_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package asset
2+
3+
import (
4+
"bytes"
5+
"crypto/rand"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/btcsuite/btcd/btcec/v2"
10+
"github.com/btcsuite/btcd/chaincfg/chainhash"
11+
"github.com/btcsuite/btcd/txscript"
12+
"github.com/lightninglabs/taproot-assets/fn"
13+
"github.com/lightninglabs/taproot-assets/internal/test"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
//// MockGKRRecord creates a TLV record for a GroupKeyReveal.
18+
//func MockGKRRecord(reveal *GroupKeyReveal) tlv.Record {
19+
// recordSize := func() uint64 {
20+
// if reveal == nil || *reveal == nil {
21+
// return 0
22+
// }
23+
// r := *reveal
24+
// return uint64(
25+
// btcec.PubKeyBytesLenCompressed + len(r.TapscriptRoot()),
26+
// )
27+
// }
28+
// return tlv.MakeDynamicRecord(
29+
// 0, reveal, recordSize, GroupKeyRevealEncoder, GroupKeyRevealDecoder,
30+
// )
31+
//}
32+
33+
type testCaseGkrEncodeDecode struct {
34+
testName string
35+
36+
internalKey btcec.PublicKey
37+
genesisAssetID ID
38+
customSubtreeRoot fn.Option[chainhash.Hash]
39+
}
40+
41+
// GroupKeyReveal generates a GroupKeyReveal instance from the test case.
42+
func (tc testCaseGkrEncodeDecode) GroupKeyReveal() (GroupKeyReveal, error) {
43+
gkr, err := NewGroupKeyRevealV1(
44+
tc.internalKey, tc.genesisAssetID, tc.customSubtreeRoot,
45+
).Unpack()
46+
47+
return &gkr, err
48+
}
49+
50+
func TestGroupKeyTapscriptEncodeDecode(t *testing.T) {
51+
t.Parallel()
52+
53+
// Create a random internal public key.
54+
internalKey := *(test.RandPubKey(t))
55+
56+
// Create a random genesis asset ID.
57+
randomAssetIdBytes := test.RandBytes(32)
58+
genesisAssetID := ID(randomAssetIdBytes)
59+
60+
testCases := []testCaseGkrEncodeDecode{
61+
{
62+
testName: "no custom root",
63+
64+
internalKey: internalKey,
65+
genesisAssetID: genesisAssetID,
66+
customSubtreeRoot: fn.None[chainhash.Hash](),
67+
},
68+
}
69+
70+
for _, tc := range testCases {
71+
t.Run(tc.testName, func(tt *testing.T) {
72+
gkr, err := tc.GroupKeyReveal()
73+
require.NoError(tt, err)
74+
75+
// Encode the GroupKeyReveal into buffer.
76+
var buffer bytes.Buffer
77+
var scratchBuffEncode [8]byte
78+
err = GroupKeyRevealEncoder(&buffer, &gkr, &scratchBuffEncode)
79+
require.NoError(tt, err)
80+
81+
// Decode the GroupKeyReveal from buffer.
82+
var gkrDecoded GroupKeyReveal
83+
var scratchBuffDecode [8]byte
84+
err = GroupKeyRevealDecoder(
85+
&buffer, &gkrDecoded, &scratchBuffDecode, uint64(buffer.Len()),
86+
)
87+
require.NoError(tt, err)
88+
89+
// Ensure the decoded GroupKeyReveal matches the original.
90+
require.Equal(
91+
tt, gkr, gkrDecoded,
92+
"decoded GroupKeyReveal does not match original",
93+
)
94+
})
95+
}
96+
}
97+
98+
func TestGroupKeyTapscript(t *testing.T) {
99+
t.Parallel()
100+
101+
// Construct an asset ID leaf.
102+
assetIDLeaf := txscript.NewBaseTapLeaf(
103+
[]byte("something something OP_RETURN <asset ID>"),
104+
)
105+
assetIDLeafHash := assetIDLeaf.TapHash()
106+
107+
// Construct a custom user script leaf. This is used to validate the
108+
// control block.
109+
customScriptLeaf := txscript.NewBaseTapLeaf(
110+
[]byte("I'm a custom user script"),
111+
)
112+
113+
// Construct a non-executable script leaf.
114+
noExecScript, err := txscript.NewScriptBuilder().
115+
AddOp(txscript.OP_RETURN).
116+
Script()
117+
require.NoError(t, err)
118+
119+
noExecScriptLeaf := txscript.TapLeaf{
120+
Script: noExecScript,
121+
LeafVersion: txscript.BaseLeafVersion,
122+
}
123+
124+
noExecScriptLeafHash := noExecScriptLeaf.TapHash()
125+
126+
// This is the user's custom tapscript tree root hash. It can be set or
127+
// unset, up to the user. If unset, using [32]byte{} as the hash will be
128+
// fine.
129+
//
130+
// It can also be a leaf hash or a branch hash.
131+
userNodeHash := customScriptLeaf.TapHash()
132+
133+
branchHash := TapBranchHash(noExecScriptLeafHash, userNodeHash)
134+
actualTapscriptRootHash4 := TapBranchHash(assetIDLeafHash, branchHash)
135+
fmt.Printf("Tapscript root hash with optional user tree root: %v\n",
136+
actualTapscriptRootHash4)
137+
138+
// Construct the user subtree control block.
139+
internalKey := RandInternalPubKey()
140+
outputKey := txscript.ComputeTaprootOutputKey(
141+
&internalKey, actualTapscriptRootHash4[:],
142+
)
143+
outputKeyIsOdd := outputKey.SerializeCompressed()[0] == 0x03
144+
145+
inclusionProof := bytes.Join(
146+
[][]byte{
147+
noExecScriptLeafHash[:],
148+
assetIDLeafHash[:],
149+
}, nil,
150+
)
151+
152+
userSubtreeControlBlock := txscript.ControlBlock{
153+
InternalKey: &internalKey,
154+
OutputKeyYIsOdd: outputKeyIsOdd,
155+
LeafVersion: txscript.BaseLeafVersion,
156+
InclusionProof: inclusionProof,
157+
}
158+
159+
// Ensure the custom script control block is correct by computing the
160+
// root hash given the control block and the custom script leaf.
161+
// Ensure the custom script control block is correct by computing the
162+
// root hash given the control block and the custom script leaf.
163+
rootCheckBytes := userSubtreeControlBlock.RootHash(
164+
customScriptLeaf.Script,
165+
)
166+
var rootCheck chainhash.Hash
167+
copy(rootCheck[:], rootCheckBytes)
168+
169+
require.Equal(t, actualTapscriptRootHash4, rootCheck)
170+
}
171+
172+
// RandInternalPubKey generates a random internal public key.
173+
func RandInternalPubKey() btcec.PublicKey {
174+
randBytes := make([]byte, 32)
175+
_, _ = rand.Read(randBytes)
176+
177+
privateKey, _ := btcec.PrivKeyFromBytes(randBytes)
178+
pubKey := privateKey.PubKey()
179+
return *pubKey
180+
}

0 commit comments

Comments
 (0)