btcd/txscript/engine_debug_test.go

179 lines
4.2 KiB
Go
Raw Normal View History

// Copyright (c) 2013-2023 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
import (
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/wire"
"github.com/stretchr/testify/require"
)
2024-03-25 14:44:25 +01:00
// TestDebugEngine checks that the StepCallback called during debug script
// execution contains the expected data.
func TestDebugEngine(t *testing.T) {
t.Parallel()
// We'll generate a private key and a signature for the tx.
privKey, err := btcec.NewPrivateKey()
require.NoError(t, err)
internalKey := privKey.PubKey()
// We use a simple script that will utilize both the stack and alt
// stack in order to test the step callback, and wrap it in a taproot
// witness script.
builder := NewScriptBuilder()
builder.AddData([]byte{0xab})
builder.AddOp(OP_TOALTSTACK)
builder.AddData(schnorr.SerializePubKey(internalKey))
builder.AddOp(OP_CHECKSIG)
builder.AddOp(OP_VERIFY)
builder.AddOp(OP_1)
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)
testTx := wire.NewMsgTx(2)
testTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Index: 1,
},
})
txOut := &wire.TxOut{
Value: 1e8, PkScript: p2trScript,
}
testTx.AddTxOut(txOut)
prevFetcher := NewCannedPrevOutputFetcher(
txOut.PkScript, txOut.Value,
)
sigHashes := NewTxSigHashes(testTx, prevFetcher)
sig, err := RawTxInTapscriptSignature(
testTx, sigHashes, 0, txOut.Value,
txOut.PkScript, tapLeaf,
SigHashDefault, privKey,
)
require.NoError(t, err)
// 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,
}
expCallback := []StepInfo{
// First callback is looking at the OP_1 witness version.
{
ScriptIndex: 1,
OpcodeIndex: 0,
Stack: [][]byte{},
AltStack: [][]byte{},
},
// The OP_1 witness version is pushed to stack,
{
ScriptIndex: 1,
OpcodeIndex: 1,
Stack: [][]byte{{0x01}},
AltStack: [][]byte{},
},
// Then the taproot script is being executed, starting with
// only the signature on the stacks.
{
ScriptIndex: 2,
OpcodeIndex: 0,
Stack: [][]byte{sig},
AltStack: [][]byte{},
},
// 0xab is pushed to the stack.
{
ScriptIndex: 2,
OpcodeIndex: 1,
Stack: [][]byte{sig, {0xab}},
AltStack: [][]byte{},
},
// 0xab is moved to the alt stack.
{
ScriptIndex: 2,
OpcodeIndex: 2,
Stack: [][]byte{sig},
AltStack: [][]byte{{0xab}},
},
// The public key is pushed to the stack.
{
ScriptIndex: 2,
OpcodeIndex: 3,
Stack: [][]byte{
sig,
schnorr.SerializePubKey(internalKey),
},
AltStack: [][]byte{{0xab}},
},
// OP_CHECKSIG is executed, resulting in 0x01 on the stack.
{
ScriptIndex: 2,
OpcodeIndex: 4,
Stack: [][]byte{
{0x01},
},
AltStack: [][]byte{{0xab}},
},
// OP_VERIFY pops and checks the top stack element.
{
ScriptIndex: 2,
OpcodeIndex: 5,
Stack: [][]byte{},
AltStack: [][]byte{{0xab}},
},
// A single OP_1 push completes the script execution (note that
// the alt stack is cleared when the script is "done").
{
ScriptIndex: 2,
OpcodeIndex: 6,
Stack: [][]byte{{0x01}},
AltStack: [][]byte{},
},
}
stepIndex := 0
callback := func(s *StepInfo) error {
require.Less(
t, stepIndex, len(expCallback), "unexpected callback",
)
require.Equal(t, &expCallback[stepIndex], s)
stepIndex++
return nil
}
// Run the debug engine.
vm, err := NewDebugEngine(
txOut.PkScript, txCopy, 0, StandardVerifyFlags,
nil, sigHashes, txOut.Value, prevFetcher,
callback,
)
require.NoError(t, err)
require.NoError(t, vm.Execute())
}