2023-08-01 14:54:44 +02:00
|
|
|
// 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
|
2023-08-01 14:54:44 +02:00
|
|
|
// 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())
|
|
|
|
}
|