Skip to content

Commit a338f0e

Browse files
committed
asset+test+vm: extract common tapscript test code
1 parent d01237d commit a338f0e

File tree

3 files changed

+190
-42
lines changed

3 files changed

+190
-42
lines changed

asset/mock.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,109 @@ func SignVirtualTx(priv *btcec.PrivateKey, signDesc *lndclient.SignDescriptor,
309309
return sig, nil
310310
}
311311

312+
// AssetCustomGroupKey constructs a new asset group key and anchor asset from a
313+
// given asset genesis. The asset group key may also commit to a Tapscript tree
314+
// root. The tree used in that case includes a hash lock and signature lock.
315+
// The validity of that Tapscript tree is set by the caller.
316+
//
317+
// The following group key derivation methods are supported:
318+
//
319+
// BIP86: The group key commits to an empty tapscript tree. Assets can only be
320+
// added to the group with a valid signature from the tweaked group key.
321+
//
322+
// Key-spend: The group key commits to a tapscript tree root, but the witness
323+
// for the group anchor will be a signature using the tweaked group key. Assets
324+
// could later be added to the group with either a signature from the tweaked
325+
// group key or a valid witness for a script in the committed tapscript tree.
326+
//
327+
// Script-spend: The group key commits to a tapscript tree root, and the witness
328+
// for the group anchor is a valid script witness for a script in the tapscript
329+
// tree. Assets could later be added to the group with either a signature from
330+
// the tweaked group key or a valid witness for a script in the committed
331+
// tapscript tree.
332+
func AssetCustomGroupKey(t *testing.T, useHashLock, BIP86, keySpend,
333+
validScriptWitness bool, gen Genesis) *Asset {
334+
335+
t.Helper()
336+
337+
// Sanity check the custom group key request. If both flags are false,
338+
// the script-spend path will be used.
339+
if BIP86 && keySpend {
340+
require.Fail(t, "Cannot have both BIP 86 and key spend group "+
341+
"key types")
342+
}
343+
344+
var (
345+
groupKey *GroupKey
346+
err error
347+
)
348+
349+
genID := gen.ID()
350+
scriptKey := RandScriptKey(t)
351+
protoAsset := RandAssetWithValues(t, gen, nil, scriptKey)
352+
353+
groupPrivKey := test.RandPrivKey(t)
354+
groupInternalKey := groupPrivKey.PubKey()
355+
genSigner := NewMockGenesisSigner(groupPrivKey)
356+
genBuilder := MockGroupTxBuilder{}
357+
358+
// Manually create and use the singly tweaked key here, to match the
359+
// signing behavior later when using the signing descriptor.
360+
groupSinglyTweakedKey := input.TweakPubKeyWithTweak(
361+
groupInternalKey, genID[:],
362+
)
363+
364+
// Populate the initial parameters for the group key request.
365+
groupReq := GroupKeyRequest{
366+
RawKey: test.PubToKeyDesc(groupInternalKey),
367+
AnchorGen: gen,
368+
NewAsset: protoAsset,
369+
}
370+
371+
// Update the group key request and group key derivation arguments
372+
// to match the requested group key type.
373+
switch {
374+
// Use an empty tapscript and script witness.
375+
case BIP86:
376+
groupKey, err = DeriveCustomGroupKey(
377+
genSigner, &genBuilder, groupReq, nil, nil,
378+
)
379+
380+
// Derive a tapscipt root using the default tapscript tree used for
381+
// testing, but use a signature as a witness.
382+
case keySpend:
383+
tapRootHash := test.BuildTapscriptTreeNoReveal(
384+
t, groupSinglyTweakedKey,
385+
)
386+
387+
groupReq.TapscriptRoot = tapRootHash
388+
groupKey, err = DeriveCustomGroupKey(
389+
genSigner, &genBuilder, groupReq, nil, nil,
390+
)
391+
392+
// For a script spend, we derive a tapscript root, and create the needed
393+
// tapscript and script witness.
394+
default:
395+
_, _, tapLeaf, tapRootHash, witness := test.BuildTapscriptTree(
396+
t, useHashLock, validScriptWitness,
397+
groupSinglyTweakedKey,
398+
)
399+
400+
groupReq.TapscriptRoot = tapRootHash
401+
groupKey, err = DeriveCustomGroupKey(
402+
genSigner, &genBuilder, groupReq, tapLeaf, witness,
403+
)
404+
}
405+
406+
require.NoError(t, err)
407+
408+
return NewAssetNoErr(
409+
t, gen, protoAsset.Amount, protoAsset.LockTime,
410+
protoAsset.RelativeLockTime, scriptKey, groupKey,
411+
WithAssetVersion(protoAsset.Version),
412+
)
413+
}
414+
312415
// RandScriptKey creates a random script key for testing.
313416
func RandScriptKey(t testing.TB) ScriptKey {
314417
return NewScriptKey(test.RandPrivKey(t).PubKey())

internal/test/helpers.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import (
1313
"github.com/btcsuite/btcd/btcec/v2"
1414
"github.com/btcsuite/btcd/btcec/v2/schnorr"
1515
"github.com/btcsuite/btcd/btcutil"
16+
"github.com/btcsuite/btcd/btcutil/psbt"
1617
"github.com/btcsuite/btcd/chaincfg/chainhash"
1718
"github.com/btcsuite/btcd/txscript"
1819
"github.com/btcsuite/btcd/wire"
20+
"github.com/btcsuite/btcwallet/waddrmgr"
1921
"github.com/lightningnetwork/lnd/input"
2022
"github.com/lightningnetwork/lnd/keychain"
2123
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
@@ -389,3 +391,86 @@ func ReadTestDataFile(t *testing.T, fileName string) string {
389391

390392
return string(fileBytes)
391393
}
394+
395+
// BuildTapscriptTree builds a Tapscript tree with two leaves, a hash lock
396+
// script and a signature verification script. It returns only the tapscript
397+
// tree root.
398+
func BuildTapscriptTreeNoReveal(t *testing.T,
399+
internalKey *btcec.PublicKey) []byte {
400+
401+
hashLockWitness := []byte("foobar")
402+
hashLockLeaf := ScriptHashLock(t, hashLockWitness)
403+
sigLeaf := ScriptSchnorrSig(t, internalKey)
404+
405+
tree := txscript.AssembleTaprootScriptTree(hashLockLeaf, sigLeaf)
406+
rootHash := tree.RootNode.TapHash()
407+
408+
return rootHash[:]
409+
}
410+
411+
// BuildTapscriptTree builds a Tapscript tree with two leaves, a hash lock
412+
// script and a signature verification script. It also returns the data needed
413+
// to satisfy one of the two leaves.
414+
func BuildTapscriptTree(t *testing.T, useHashLock, valid bool,
415+
internalKey *btcec.PublicKey) (*txscript.TapLeaf, *waddrmgr.Tapscript,
416+
*psbt.TaprootTapLeafScript, []byte, []byte) {
417+
418+
// Let's create a taproot asset script now. This is a hash lock with a
419+
// simple preimage of "foobar".
420+
hashLockWitness := []byte("foobar")
421+
invalidHashLockWitness := []byte("not-foobar")
422+
hashLockLeaf := ScriptHashLock(t, hashLockWitness)
423+
424+
// Let's add a second script output as well to test the partial reveal.
425+
sigLeaf := ScriptSchnorrSig(t, internalKey)
426+
invalidSigWitness := make([]byte, 64)
427+
428+
var (
429+
usedLeaf *txscript.TapLeaf
430+
testTapScript *waddrmgr.Tapscript
431+
scriptWitness []byte
432+
)
433+
434+
if useHashLock {
435+
usedLeaf = &hashLockLeaf
436+
inclusionProof := sigLeaf.TapHash()
437+
testTapScript = input.TapscriptPartialReveal(
438+
internalKey, hashLockLeaf, inclusionProof[:],
439+
)
440+
scriptWitness = hashLockWitness
441+
442+
if !valid {
443+
scriptWitness = invalidHashLockWitness
444+
}
445+
} else {
446+
usedLeaf = &sigLeaf
447+
inclusionProof := hashLockLeaf.TapHash()
448+
testTapScript = input.TapscriptPartialReveal(
449+
internalKey, sigLeaf, inclusionProof[:],
450+
)
451+
452+
// If we leave the scriptWitness nil, the genTaprootScriptSpend
453+
// function will automatically create a signature for us.
454+
// We only need to create a witness if we want an invalid
455+
// signature.
456+
if !valid {
457+
scriptWitness = invalidSigWitness
458+
}
459+
}
460+
461+
// Compute the final tapscript root and leaf script needed to create a
462+
// key that includes the above tapscript tree.
463+
tapTweak := testTapScript.ControlBlock.RootHash(
464+
testTapScript.RevealedScript,
465+
)
466+
controlBlockBytes, err := testTapScript.ControlBlock.ToBytes()
467+
require.NoError(t, err)
468+
469+
tapLeaf := &psbt.TaprootTapLeafScript{
470+
ControlBlock: controlBlockBytes,
471+
Script: usedLeaf.Script,
472+
LeafVersion: usedLeaf.LeafVersion,
473+
}
474+
475+
return usedLeaf, testTapScript, tapLeaf, tapTweak, scriptWitness
476+
}

vm/vm_test.go

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ import (
1010
"github.com/btcsuite/btcd/btcec/v2/schnorr"
1111
"github.com/btcsuite/btcd/txscript"
1212
"github.com/btcsuite/btcd/wire"
13-
"github.com/btcsuite/btcwallet/waddrmgr"
1413
"github.com/lightninglabs/taproot-assets/asset"
1514
"github.com/lightninglabs/taproot-assets/commitment"
1615
"github.com/lightninglabs/taproot-assets/internal/test"
1716
"github.com/lightninglabs/taproot-assets/tapscript"
18-
"github.com/lightningnetwork/lnd/input"
1917
"github.com/stretchr/testify/require"
2018
"golang.org/x/exp/maps"
2119
)
@@ -450,47 +448,9 @@ func scriptTreeSpendStateTransition(t *testing.T, useHashLock,
450448
valid bool, sigHashType txscript.SigHashType) stateTransitionFunc {
451449

452450
scriptPrivKey := test.RandPrivKey(t)
453-
scriptInternalKey := scriptPrivKey.PubKey()
454-
455-
// Let's create a taproot asset script now. This is a hash lock with a
456-
// simple preimage of "foobar".
457-
leaf1 := test.ScriptHashLock(t, []byte("foobar"))
458-
459-
// Let's add a second script output as well to test the partial reveal.
460-
leaf2 := test.ScriptSchnorrSig(t, scriptInternalKey)
461-
462-
var (
463-
usedLeaf *txscript.TapLeaf
464-
testTapScript *waddrmgr.Tapscript
465-
scriptWitness []byte
451+
usedLeaf, testTapScript, _, _, scriptWitness := test.BuildTapscriptTree(
452+
t, useHashLock, valid, scriptPrivKey.PubKey(),
466453
)
467-
if useHashLock {
468-
usedLeaf = &leaf1
469-
inclusionProof := leaf2.TapHash()
470-
testTapScript = input.TapscriptPartialReveal(
471-
scriptInternalKey, leaf1, inclusionProof[:],
472-
)
473-
scriptWitness = []byte("foobar")
474-
475-
if !valid {
476-
scriptWitness = []byte("not-foobar")
477-
}
478-
} else {
479-
usedLeaf = &leaf2
480-
inclusionProof := leaf1.TapHash()
481-
testTapScript = input.TapscriptPartialReveal(
482-
scriptInternalKey, leaf2, inclusionProof[:],
483-
)
484-
485-
// If we leave the scriptWitness nil, the genTaprootScriptSpend
486-
// function will automatically create a signature for us.
487-
// We only need to create a witness if we want an invalid
488-
// signature.
489-
if !valid {
490-
scriptWitness = make([]byte, 64)
491-
}
492-
}
493-
494454
scriptKey, err := testTapScript.TaprootKey()
495455
require.NoError(t, err)
496456

0 commit comments

Comments
 (0)