github.com/btcsuite/btcd@v0.24.0/txscript/engine_debug_test.go (about)

     1  // Copyright (c) 2013-2023 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package txscript
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/btcsuite/btcd/btcec/v2"
    11  	"github.com/btcsuite/btcd/btcec/v2/schnorr"
    12  	"github.com/btcsuite/btcd/wire"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  // TestDebugEngine checks that the StepCallbck called during debug script
    17  // execution contains the expected data.
    18  func TestDebugEngine(t *testing.T) {
    19  	t.Parallel()
    20  
    21  	// We'll generate a private key and a signature for the tx.
    22  	privKey, err := btcec.NewPrivateKey()
    23  	require.NoError(t, err)
    24  
    25  	internalKey := privKey.PubKey()
    26  
    27  	// We use a simple script that will utilize both the stack and alt
    28  	// stack in order to test the step callback, and wrap it in a taproot
    29  	// witness script.
    30  	builder := NewScriptBuilder()
    31  	builder.AddData([]byte{0xab})
    32  	builder.AddOp(OP_TOALTSTACK)
    33  	builder.AddData(schnorr.SerializePubKey(internalKey))
    34  	builder.AddOp(OP_CHECKSIG)
    35  	builder.AddOp(OP_VERIFY)
    36  	builder.AddOp(OP_1)
    37  	pkScript, err := builder.Script()
    38  	require.NoError(t, err)
    39  
    40  	tapLeaf := NewBaseTapLeaf(pkScript)
    41  	tapScriptTree := AssembleTaprootScriptTree(tapLeaf)
    42  
    43  	ctrlBlock := tapScriptTree.LeafMerkleProofs[0].ToControlBlock(
    44  		internalKey,
    45  	)
    46  
    47  	tapScriptRootHash := tapScriptTree.RootNode.TapHash()
    48  	outputKey := ComputeTaprootOutputKey(
    49  		internalKey, tapScriptRootHash[:],
    50  	)
    51  	p2trScript, err := PayToTaprootScript(outputKey)
    52  	require.NoError(t, err)
    53  
    54  	testTx := wire.NewMsgTx(2)
    55  	testTx.AddTxIn(&wire.TxIn{
    56  		PreviousOutPoint: wire.OutPoint{
    57  			Index: 1,
    58  		},
    59  	})
    60  	txOut := &wire.TxOut{
    61  		Value: 1e8, PkScript: p2trScript,
    62  	}
    63  	testTx.AddTxOut(txOut)
    64  
    65  	prevFetcher := NewCannedPrevOutputFetcher(
    66  		txOut.PkScript, txOut.Value,
    67  	)
    68  	sigHashes := NewTxSigHashes(testTx, prevFetcher)
    69  
    70  	sig, err := RawTxInTapscriptSignature(
    71  		testTx, sigHashes, 0, txOut.Value,
    72  		txOut.PkScript, tapLeaf,
    73  		SigHashDefault, privKey,
    74  	)
    75  	require.NoError(t, err)
    76  
    77  	// Now that we have the sig, we'll make a valid witness
    78  	// including the control block.
    79  	ctrlBlockBytes, err := ctrlBlock.ToBytes()
    80  	require.NoError(t, err)
    81  	txCopy := testTx.Copy()
    82  	txCopy.TxIn[0].Witness = wire.TxWitness{
    83  		sig, pkScript, ctrlBlockBytes,
    84  	}
    85  
    86  	expCallback := []StepInfo{
    87  		// First callback is looking at the OP_1 witness version.
    88  		{
    89  			ScriptIndex: 1,
    90  			OpcodeIndex: 0,
    91  			Stack:       [][]byte{},
    92  			AltStack:    [][]byte{},
    93  		},
    94  		// The OP_1 witness version is pushed to stack,
    95  		{
    96  			ScriptIndex: 1,
    97  			OpcodeIndex: 1,
    98  			Stack:       [][]byte{{0x01}},
    99  			AltStack:    [][]byte{},
   100  		},
   101  		// Then the taproot script is being executed, starting with
   102  		// only the signature on the stacks.
   103  		{
   104  			ScriptIndex: 2,
   105  			OpcodeIndex: 0,
   106  			Stack:       [][]byte{sig},
   107  			AltStack:    [][]byte{},
   108  		},
   109  		// 0xab is pushed to the stack.
   110  		{
   111  			ScriptIndex: 2,
   112  			OpcodeIndex: 1,
   113  			Stack:       [][]byte{sig, {0xab}},
   114  			AltStack:    [][]byte{},
   115  		},
   116  		// 0xab is moved to the alt stack.
   117  		{
   118  			ScriptIndex: 2,
   119  			OpcodeIndex: 2,
   120  			Stack:       [][]byte{sig},
   121  			AltStack:    [][]byte{{0xab}},
   122  		},
   123  		// The public key is pushed to the stack.
   124  		{
   125  			ScriptIndex: 2,
   126  			OpcodeIndex: 3,
   127  			Stack: [][]byte{
   128  				sig,
   129  				schnorr.SerializePubKey(internalKey),
   130  			},
   131  			AltStack: [][]byte{{0xab}},
   132  		},
   133  		// OP_CHECKSIG is executed, resulting in 0x01 on the stack.
   134  		{
   135  			ScriptIndex: 2,
   136  			OpcodeIndex: 4,
   137  			Stack: [][]byte{
   138  				{0x01},
   139  			},
   140  			AltStack: [][]byte{{0xab}},
   141  		},
   142  		// OP_VERIFY pops and checks the top stack element.
   143  		{
   144  			ScriptIndex: 2,
   145  			OpcodeIndex: 5,
   146  			Stack:       [][]byte{},
   147  			AltStack:    [][]byte{{0xab}},
   148  		},
   149  		// A single OP_1 push completes the script execution (note that
   150  		// the alt stack is cleared when the script is "done").
   151  		{
   152  			ScriptIndex: 2,
   153  			OpcodeIndex: 6,
   154  			Stack:       [][]byte{{0x01}},
   155  			AltStack:    [][]byte{},
   156  		},
   157  	}
   158  
   159  	stepIndex := 0
   160  	callback := func(s *StepInfo) error {
   161  		require.Less(
   162  			t, stepIndex, len(expCallback), "unexpected callback",
   163  		)
   164  
   165  		require.Equal(t, &expCallback[stepIndex], s)
   166  		stepIndex++
   167  		return nil
   168  	}
   169  
   170  	// Run the debug engine.
   171  	vm, err := NewDebugEngine(
   172  		txOut.PkScript, txCopy, 0, StandardVerifyFlags,
   173  		nil, sigHashes, txOut.Value, prevFetcher,
   174  		callback,
   175  	)
   176  	require.NoError(t, err)
   177  	require.NoError(t, vm.Execute())
   178  }