bitcoin: add "extreme object size" deserialization unit test
What changed, and why it matters
This commit only adds a new unit test to the rust-bitcoin library. It does not change any production code. The test checks that the library can still serialize and deserialize very large Bitcoin transactions, witnesses, and script signatures. There is no bug fix or security patch here.
No action required. This is a test-only change. Reviewers may optionally confirm the test passes and that the large allocations are acceptable in CI.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a single test function, deserialize_extreme_tx, in bitcoin/src/consensus/encode.rs. It constructs transactions with up to 4 million witness items, a 4 MB witness element, and an 8 MB scriptSig, then verifies round-trip serialization/deserialization. No decoding logic is modified. The commit message explicitly frames this as adding test coverage, not fixing a vulnerability.
Changed components
bitcoin/src/consensus/encode.rs (test module only)Inspect captured patch +41 / −0
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 23c2bc26..f9ddb6c4 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -1058,4 +1058,45 @@ mod tests {
FromHexError::Decode(DecodeError::Unconsumed)
));
}
+
+ #[test]
+ fn deserialize_extreme_tx() {
+ use crate::{ScriptSigBuf, Witness};
+
+ // Start with transaction from `deserialize_tx_hex`
+ let hex = include_str!("../../tests/data/previous_tx_0_hex"); // An arbitrary transaction.
+ let tx = deserialize_hex::<Transaction>(hex).unwrap();
+
+ assert_eq!(tx.inputs.len(), 1);
+ assert_eq!(tx.outputs.len(), 2);
+ assert_eq!(tx.inputs[0].witness.len(), 2);
+
+ // 1. Test with 4 million witnesses.
+ let mut tx_copy = tx.clone();
+ tx_copy.inputs[0].witness = Witness::from_slice(&vec![vec![]; 4_000_000]);
+ let roundtrip = deserialize(&serialize(&tx_copy)).unwrap();
+ assert_eq!(tx_copy, roundtrip);
+
+ // 2. Test with a single large witness. (Size of 4 megs, including length prefix)
+ let mut tx_copy = tx.clone();
+ tx_copy.inputs[0].witness = Witness::from_slice(&vec![vec![0; 4_000_000 - 9]; 1]);
+ let roundtrip = deserialize(&serialize(&tx_copy)).unwrap();
+ assert_eq!(tx_copy, roundtrip);
+
+ // 3. Combine these; with the witness stack we can exceed a total size of 4M but
+ // only by a tiny bit. (It is not part of our API guarantee that such things
+ // will round-trip, but we unit test them anyway to help notice changes.)
+ let mut tx_copy = tx.clone();
+ tx_copy.inputs[0].witness = Witness::from_slice(&vec![vec![0; 997]; 4_000]);
+ let roundtrip = deserialize(&serialize(&tx_copy)).unwrap();
+ assert_eq!(tx_copy, roundtrip);
+
+ // 4. Test with a large script sig. With scriptsigs there is no limit on how large
+ // an object we can parse, which is inconsistent with witnesses. Also not an
+ // API guarantee.
+ let mut tx_copy = tx.clone();
+ tx_copy.inputs[0].script_sig = ScriptSigBuf::from(vec![0; 8_000_001]);
+ let roundtrip = deserialize(&serialize(&tx_copy)).unwrap();
+ assert_eq!(tx_copy, roundtrip);
+ }
}
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.