What changed, and why it matters
This commit only adds a new test file. It does not change any production code. The test checks that the PSBT parser rejects transaction fields that have extra trailing bytes. Because no actual parser logic is modified, this commit by itself does not fix or introduce a security issue.
No action needed for this commit alone. Review whether the existing production parser already enforces the strictness being tested, or whether a follow-up commit is required to implement the behavior this test assumes.
Security signals we found
Regression test added for strict parsing of transaction-valued PSBT fields
No production code changes
Evidence from the diff
The diff creates psbt/strict_tx_values_test.go, which adds regression tests verifying that NewFromRawBytes returns ErrInvalidPsbtFormat when the global unsigned transaction or an input non-witness UTXO field contains a valid serialized transaction followed by trailing data. The test constructs minimal PSBTs by hand and appends an extra 0x00 byte to the serialized transaction bytes. No library source code is changed.
Changed components
psbt/strict_tx_values_test.goInspect captured patch +117 / −0
diff --git a/psbt/strict_tx_values_test.go b/psbt/strict_tx_values_test.go
new file mode 100644
index 0000000..5552eac
--- /dev/null
+++ b/psbt/strict_tx_values_test.go
@@ -0,0 +1,117 @@
+package psbt
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/btcsuite/btcd/wire/v2"
+ "github.com/stretchr/testify/require"
+)
+
+// strictnessTxPair returns a minimal unsigned transaction and the previous
+// transaction provided as its non-witness UTXO.
+func strictnessTxPair(t *testing.T) (*wire.MsgTx, *wire.MsgTx) {
+ t.Helper()
+
+ prevTx := wire.NewMsgTx(2)
+ prevTx.AddTxIn(&wire.TxIn{
+ PreviousOutPoint: wire.OutPoint{Index: wire.MaxPrevOutIndex},
+ Sequence: wire.MaxTxInSequenceNum,
+ })
+ prevTx.AddTxOut(&wire.TxOut{
+ Value: 12345,
+ PkScript: []byte{0x51},
+ })
+
+ unsignedTx := wire.NewMsgTx(2)
+ unsignedTx.AddTxIn(&wire.TxIn{
+ PreviousOutPoint: wire.OutPoint{
+ Hash: prevTx.TxHash(),
+ Index: 0,
+ },
+ Sequence: wire.MaxTxInSequenceNum,
+ })
+ unsignedTx.AddTxOut(&wire.TxOut{
+ Value: 1000,
+ PkScript: []byte{0x51},
+ })
+
+ return unsignedTx, prevTx
+}
+
+// serializeTxForStrictness serializes tx in the PSBT form required by the
+// field under test.
+func serializeTxForStrictness(t *testing.T, tx *wire.MsgTx,
+ noWitness bool) []byte {
+
+ t.Helper()
+
+ var buf bytes.Buffer
+ var err error
+ if noWitness {
+ err = tx.SerializeNoWitness(&buf)
+ } else {
+ err = tx.Serialize(&buf)
+ }
+ require.NoError(t, err)
+
+ return buf.Bytes()
+}
+
+// strictnessPSBT builds a minimal PSBT using the supplied transaction-valued
+// fields verbatim.
+func strictnessPSBT(t *testing.T, unsignedTx,
+ nonWitnessUtxo []byte) []byte {
+
+ t.Helper()
+
+ var buf bytes.Buffer
+ _, err := buf.Write(psbtMagic[:])
+ require.NoError(t, err)
+
+ require.NoError(t, serializeKVPairWithType(
+ &buf, byte(UnsignedTxType), nil, unsignedTx,
+ ))
+ require.NoError(t, buf.WriteByte(0x00))
+
+ require.NoError(t, serializeKVPairWithType(
+ &buf, byte(NonWitnessUtxoType), nil, nonWitnessUtxo,
+ ))
+ require.NoError(t, buf.WriteByte(0x00))
+ require.NoError(t, buf.WriteByte(0x00))
+
+ return buf.Bytes()
+}
+
+// TestRejectsTrailingDataInTransactionValues verifies that PSBT transaction
+// values must contain exactly one serialized transaction.
+func TestRejectsTrailingDataInTransactionValues(t *testing.T) {
+ unsignedTx, prevTx := strictnessTxPair(t)
+ unsignedTxBytes := serializeTxForStrictness(t, unsignedTx, true)
+ prevTxBytes := serializeTxForStrictness(t, prevTx, false)
+
+ testCases := []struct {
+ name string
+ unsignedTx []byte
+ nonWitnessUtxo []byte
+ }{{
+ name: "global unsigned tx",
+ unsignedTx: append(unsignedTxBytes, 0x00),
+ nonWitnessUtxo: prevTxBytes,
+ }, {
+ name: "input non-witness utxo",
+ unsignedTx: unsignedTxBytes,
+ nonWitnessUtxo: append(prevTxBytes, 0x00),
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ _, err := NewFromRawBytes(bytes.NewReader(
+ strictnessPSBT(
+ t, tc.unsignedTx, tc.nonWitnessUtxo,
+ ),
+ ), false)
+ require.ErrorIs(t, err, ErrInvalidPsbtFormat)
+ })
+ }
+}
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.