What changed, and why it matters
This commit only adds new test code for the PSBT (Partially Signed Bitcoin Transaction) package. It does not change any production code. The tests verify that WitnessUtxo fields are parsed strictly and correctly, including rejecting extra trailing bytes and handling multi-byte script lengths. There is no direct security fix here, but the tests appear to be added in support of an existing or upcoming strict-parsing behavior.
No immediate action is required for this commit because it is test-only. Reviewers should confirm the corresponding production parsing code already enforces the strict behavior being tested, and consider whether additional edge cases (e.g., zero-length scripts, malformed CompactSize) should also be covered.
Security signals we found
Strict parsing tests for WitnessUtxo transaction outputs
Rejection of trailing data after serialized txOut
Correct handling of multi-byte CompactSize script lengths
Evidence from the diff
The diff adds helper functions and two test cases to psbt/strict_tx_values_test.go. The tests construct minimal PSBT packets with a WitnessUtxo field and assert: (1) a valid txOut parses correctly and a txOut with a trailing byte is rejected with ErrInvalidPsbtFormat; (2) a pkScript requiring a multi-byte CompactSize length (300 bytes, encoded as 0xfd 0x2c 0x01) is parsed without folding the length prefix into the script. No library logic is modified.
Changed components
btcd/psbt/strict_tx_values_test.goInspect captured patch +79 / −0
diff --git a/psbt/strict_tx_values_test.go b/psbt/strict_tx_values_test.go
index 33837f0..9c7d3ae 100644
--- a/psbt/strict_tx_values_test.go
+++ b/psbt/strict_tx_values_test.go
@@ -83,6 +83,45 @@ func strictnessPSBT(t *testing.T, unsignedTx,
return buf.Bytes()
}
+// serializeTxOutForStrictness serializes txOut in the PSBT form required by
+// the WitnessUtxo field.
+func serializeTxOutForStrictness(t *testing.T, txOut *wire.TxOut) []byte {
+ t.Helper()
+
+ var buf bytes.Buffer
+ require.NoError(t, wire.WriteTxOut(&buf, 0, 0, txOut))
+
+ return buf.Bytes()
+}
+
+// strictnessPSBTWithWitnessUtxo builds a minimal PSBT using the supplied
+// WitnessUtxo value verbatim.
+func strictnessPSBTWithWitnessUtxo(t *testing.T,
+ witnessUtxo []byte) []byte {
+
+ t.Helper()
+
+ unsignedTx, _ := strictnessTxPair(t)
+
+ var buf bytes.Buffer
+ _, err := buf.Write(psbtMagic[:])
+ require.NoError(t, err)
+
+ require.NoError(t, serializeKVPairWithType(
+ &buf, byte(UnsignedTxType), nil,
+ serializeTxForStrictness(t, unsignedTx, true),
+ ))
+ require.NoError(t, buf.WriteByte(0x00))
+
+ require.NoError(t, serializeKVPairWithType(
+ &buf, byte(WitnessUtxoType), nil, witnessUtxo,
+ ))
+ 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) {
@@ -131,3 +170,43 @@ func TestRejectsTrailingDataAfterPacket(t *testing.T) {
)
require.ErrorIs(t, err, ErrInvalidPsbtFormat)
}
+
+// TestParsesWitnessUtxoTxOutStrictly verifies that WitnessUtxo values are
+// parsed as exact transaction outputs.
+func TestParsesWitnessUtxoTxOutStrictly(t *testing.T) {
+ pkScript := bytes.Repeat([]byte{0x51}, 253)
+ txOutBytes := serializeTxOutForStrictness(t, &wire.TxOut{
+ Value: 1234,
+ PkScript: pkScript,
+ })
+
+ packet, err := NewFromRawBytes(bytes.NewReader(
+ strictnessPSBTWithWitnessUtxo(t, txOutBytes),
+ ), false)
+ require.NoError(t, err)
+ require.Equal(t, pkScript, packet.Inputs[0].WitnessUtxo.PkScript)
+
+ malformedTxOut := append(append([]byte{}, txOutBytes...), 0x00)
+ _, err = NewFromRawBytes(bytes.NewReader(
+ strictnessPSBTWithWitnessUtxo(t, malformedTxOut),
+ ), false)
+ require.ErrorIs(t, err, ErrInvalidPsbtFormat)
+}
+
+// TestParsesWitnessUtxoTxOutCompactSizeScriptLength verifies that WitnessUtxo
+// scripts with multi-byte CompactSize lengths are parsed without folding the
+// length bytes into the script.
+func TestParsesWitnessUtxoTxOutCompactSizeScriptLength(t *testing.T) {
+ pkScript := bytes.Repeat([]byte{0x51}, 300)
+ txOutBytes := serializeTxOutForStrictness(t, &wire.TxOut{
+ Value: 1234,
+ PkScript: pkScript,
+ })
+ require.Equal(t, byte(0xfd), txOutBytes[8])
+
+ packet, err := NewFromRawBytes(bytes.NewReader(
+ strictnessPSBTWithWitnessUtxo(t, txOutBytes),
+ ), false)
+ require.NoError(t, err)
+ require.Equal(t, pkScript, packet.Inputs[0].WitnessUtxo.PkScript)
+}
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.