txscript: add tests for RawTxInTaprootSignature and RawTxInTapscriptSignature

In this commit, we add tests for the public functions used to generate
keyspend and tapscript signatures. Without the prior commit, these tests
will fail as the keyspend function won't properly add the sighash bytes
for things that aren't sighash default.
This commit is contained in:
Olaoluwa Osuntokun 2023-01-23 20:14:53 -08:00
parent 5e0de1c539
commit d6efaa7208
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306

View File

@ -10,10 +10,12 @@ import (
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/stretchr/testify/require"
)
type addressToKey struct {
@ -1692,3 +1694,205 @@ nexttest:
}
}
}
// TestRawTxInTaprootSignature tests that the RawTxInTaprootSignature function
// generates valid signatures for all relevant sighash types.
func TestRawTxInTaprootSignature(t *testing.T) {
t.Parallel()
privKey, err := btcec.NewPrivateKey()
require.NoError(t, err)
pubKey := ComputeTaprootKeyNoScript(privKey.PubKey())
pkScript, err := PayToTaprootScript(pubKey)
require.NoError(t, err)
// We'll reuse this simple transaction for the tests below. It ends up
// spending from a bip86 P2TR output.
testTx := wire.NewMsgTx(2)
testTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Index: 1,
},
})
txOut := &wire.TxOut{
Value: 1e8, PkScript: pkScript,
}
testTx.AddTxOut(txOut)
tests := []struct {
sigHashType SigHashType
}{
{
sigHashType: SigHashDefault,
},
{
sigHashType: SigHashAll,
},
{
sigHashType: SigHashNone,
},
{
sigHashType: SigHashSingle,
},
{
sigHashType: SigHashSingle | SigHashAnyOneCanPay,
},
{
sigHashType: SigHashNone | SigHashAnyOneCanPay,
},
{
sigHashType: SigHashAll | SigHashAnyOneCanPay,
},
}
for _, test := range tests {
name := fmt.Sprintf("sighash=%v", test.sigHashType)
t.Run(name, func(t *testing.T) {
prevFetcher := NewCannedPrevOutputFetcher(
txOut.PkScript, txOut.Value,
)
sigHashes := NewTxSigHashes(testTx, prevFetcher)
sig, err := RawTxInTaprootSignature(
testTx, sigHashes, 0, txOut.Value, txOut.PkScript,
nil, test.sigHashType, privKey,
)
require.NoError(t, err)
// If this isn't sighash default, then a sighash should be
// applied. Otherwise, it should be a normal sig.
expectedLen := schnorr.SignatureSize
if test.sigHashType != SigHashDefault {
expectedLen += 1
}
require.Len(t, sig, expectedLen)
// Finally, ensure that the signature produced is valid.
txCopy := testTx.Copy()
txCopy.TxIn[0].Witness = wire.TxWitness{sig}
vm, err := NewEngine(
txOut.PkScript, txCopy, 0, StandardVerifyFlags,
nil, sigHashes, txOut.Value, prevFetcher,
)
require.NoError(t, err)
require.NoError(t, vm.Execute())
})
}
}
// TestRawTxInTapscriptSignature thats that we're able to produce valid schnorr
// signatures for a simple tapscript spend, for various sighash types.
func TestRawTxInTapscriptSignature(t *testing.T) {
t.Parallel()
privKey, err := btcec.NewPrivateKey()
require.NoError(t, err)
internalKey := privKey.PubKey()
// Our script will be a simple OP_CHECKSIG as the sole leaf of a
// tapscript tree. We'll also re-use the internal key as the key in the
// leaf.
builder := NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(internalKey))
builder.AddOp(OP_CHECKSIG)
pkScript, err := builder.Script()
require.NoError(t, err)
tapLeaf := NewBaseTapLeaf(pkScript)
tapScriptTree := AssembleTaprootScriptTree(tapLeaf)
ctrlBlock := tapScriptTree.LeafMerkleProofs[0].ToControlBlock(
internalKey,
)
tapScriptRootHash := tapScriptTree.RootNode.TapHash()
outputKey := ComputeTaprootOutputKey(
internalKey, tapScriptRootHash[:],
)
p2trScript, err := PayToTaprootScript(outputKey)
require.NoError(t, err)
// We'll reuse this simple transaction for the tests below. It ends up
// spending from a bip86 P2TR output.
testTx := wire.NewMsgTx(2)
testTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Index: 1,
},
})
txOut := &wire.TxOut{
Value: 1e8, PkScript: p2trScript,
}
testTx.AddTxOut(txOut)
tests := []struct {
sigHashType SigHashType
}{
{
sigHashType: SigHashDefault,
},
{
sigHashType: SigHashAll,
},
{
sigHashType: SigHashNone,
},
{
sigHashType: SigHashSingle,
},
{
sigHashType: SigHashSingle | SigHashAnyOneCanPay,
},
{
sigHashType: SigHashNone | SigHashAnyOneCanPay,
},
{
sigHashType: SigHashAll | SigHashAnyOneCanPay,
},
}
for _, test := range tests {
name := fmt.Sprintf("sighash=%v", test.sigHashType)
t.Run(name, func(t *testing.T) {
prevFetcher := NewCannedPrevOutputFetcher(
txOut.PkScript, txOut.Value,
)
sigHashes := NewTxSigHashes(testTx, prevFetcher)
sig, err := RawTxInTapscriptSignature(
testTx, sigHashes, 0, txOut.Value,
txOut.PkScript, tapLeaf, test.sigHashType,
privKey,
)
require.NoError(t, err)
// If this isn't sighash default, then a sighash should
// be applied. Otherwise, it should be a normal sig.
expectedLen := schnorr.SignatureSize
if test.sigHashType != SigHashDefault {
expectedLen += 1
}
require.Len(t, sig, expectedLen)
// Now that we have the sig, we'll make a valid witness
// including the control block.
ctrlBlockBytes, err := ctrlBlock.ToBytes()
require.NoError(t, err)
txCopy := testTx.Copy()
txCopy.TxIn[0].Witness = wire.TxWitness{
sig, pkScript, ctrlBlockBytes,
}
// Finally, ensure that the signature produced is valid.
vm, err := NewEngine(
txOut.PkScript, txCopy, 0, StandardVerifyFlags,
nil, sigHashes, txOut.Value, prevFetcher,
)
require.NoError(t, err)
require.NoError(t, vm.Execute())
})
}
}