consensus_encoding: add ::end tests for decoders
What changed, and why it matters
This commit only adds new test cases for existing decoder code. It checks that various decoders correctly report an error when their input data ends unexpectedly. No production code was changed, so there is no direct security fix or vulnerability introduced here.
No action required; this is a test-only change. Continue normal review and testing practices.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds unit tests exercising the end() method on CompactSizeDecoder, Decoder2, ByteVecDecoder, and VecDecoder with truncated inputs. All assertions verify that the decoders return the expected error variants. The implementation being tested is unchanged.
Changed components
consensus_encoding/tests/compact_size.rsconsensus_encoding/tests/decode.rsInspect captured patch +143 / −0
diff --git a/consensus_encoding/tests/compact_size.rs b/consensus_encoding/tests/compact_size.rs
index a740cba8..640d5ad7 100644
--- a/consensus_encoding/tests/compact_size.rs
+++ b/consensus_encoding/tests/compact_size.rs
@@ -299,3 +299,51 @@ fn decoder_compact_size_zero() {
let got = decoder.end().unwrap();
assert_eq!(got, 0);
}
+
+#[test]
+fn decoder_compact_size_end_incomplete_one_byte() {
+ let encoded = [0xFD, 0x05];
+
+ let mut slice = &encoded[..];
+ let mut decoder = CompactSizeDecoder::new();
+ assert!(decoder.push_bytes(&mut slice).unwrap());
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::CompactSizeDecoderError { .. }));
+}
+
+#[test]
+fn decoder_compact_size_end_incomplete_three_byte() {
+ let encoded = [0xFD];
+
+ let mut slice = &encoded[..];
+ let mut decoder = CompactSizeDecoder::new();
+ assert!(decoder.push_bytes(&mut slice).unwrap());
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::CompactSizeDecoderError { .. }));
+}
+
+#[test]
+fn decoder_compact_size_end_incomplete_five_byte() {
+ let encoded = [0xFE, 0x01, 0x02];
+
+ let mut slice = &encoded[..];
+ let mut decoder = CompactSizeDecoder::new();
+ assert!(decoder.push_bytes(&mut slice).unwrap());
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::CompactSizeDecoderError { .. }));
+}
+
+#[test]
+fn decoder_compact_size_end_incomplete_nine_byte() {
+ let encoded = [0xFF, 0x01, 0x02, 0x03];
+
+ let mut slice = &encoded[..];
+ let mut decoder = CompactSizeDecoder::new();
+ assert!(decoder.push_bytes(&mut slice).unwrap());
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::CompactSizeDecoderError { .. }));
+}
diff --git a/consensus_encoding/tests/decode.rs b/consensus_encoding/tests/decode.rs
index 34fceffb..ae6927c5 100644
--- a/consensus_encoding/tests/decode.rs
+++ b/consensus_encoding/tests/decode.rs
@@ -132,6 +132,51 @@ fn decode_decoder2_read_limit_with_exhausted() {
assert_eq!(decoder2.read_limit(), 3);
}
+#[test]
+fn decode_decoder2_end_with_first_decoder_incomplete() {
+ // Test calling end() when first decoder is incomplete.
+ let mut decoder = Decoder2::new(ArrayDecoder::<5>::new(), ArrayDecoder::<3>::new());
+
+ let mut data = &[0x01, 0x02][..];
+ let _ = decoder.push_bytes(&mut data);
+ let err = decoder.end().unwrap_err();
+
+ assert!(matches!(
+ err,
+ bitcoin_consensus_encoding::Decoder2Error::First(UnexpectedEofError { .. })
+ ));
+}
+
+#[test]
+fn decode_decoder2_end_with_second_decoder_incomplete() {
+ // Test calling end() when second decoder is incomplete.
+ let mut decoder = Decoder2::new(ArrayDecoder::<2>::new(), ArrayDecoder::<5>::new());
+
+ let mut data = &[0x01, 0x02, 0x03][..];
+ let _ = decoder.push_bytes(&mut data);
+ let err = decoder.end().unwrap_err();
+
+ assert!(matches!(
+ err,
+ bitcoin_consensus_encoding::Decoder2Error::Second(UnexpectedEofError { .. })
+ ));
+}
+
+#[test]
+fn decode_decoder2_with_zero_sized_first_decoder_end() {
+ // Test edge case where first decoder needs 0 bytes.
+ let mut decoder = Decoder2::new(ArrayDecoder::<0>::new(), ArrayDecoder::<3>::new());
+
+ let mut data = &[0x42][..];
+ let _ = decoder.push_bytes(&mut data);
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(
+ err,
+ bitcoin_consensus_encoding::Decoder2Error::Second(UnexpectedEofError { .. })
+ ));
+}
+
#[test]
#[cfg(feature = "alloc")]
fn decode_byte_vec_decoder_empty() {
@@ -582,3 +627,53 @@ fn decode_vec_from_read_unbuffered_success() {
let want = Test(vec![Inner(0xDEAD_BEEF)]);
assert_eq!(got, want);
}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn decode_byte_vec_decoder_end_incomplete_length_prefix() {
+ let mut decoder = ByteVecDecoder::new();
+ let mut data = &[0xFD, 0x05][..];
+ let needs_more = decoder.push_bytes(&mut data).unwrap();
+ assert!(needs_more);
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::ByteVecDecoderError { .. }));
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn decode_byte_vec_decoder_end_incomplete_data() {
+ // Length=5 but only 2 bytes of data.
+ let mut decoder = ByteVecDecoder::new();
+ let mut data = &[0x05, 0xAA, 0xBB][..];
+ let needs_more = decoder.push_bytes(&mut data).unwrap();
+ assert!(needs_more);
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::ByteVecDecoderError { .. }));
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn decode_vec_decoder_end_incomplete_length_prefix() {
+ let mut decoder = VecDecoder::<Inner>::new();
+ let mut data = &[0xFD, 0x05][..];
+ let needs_more = decoder.push_bytes(&mut data).unwrap();
+ assert!(needs_more);
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::VecDecoderError { .. }));
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn decode_vec_decoder_end_incomplete_item() {
+ // Length=3 but only 2 bytes of data.
+ let mut decoder = VecDecoder::<Inner>::new();
+ let mut data = &[0x03, 0xAA, 0xBB][..];
+ let needs_more = decoder.push_bytes(&mut data).unwrap();
+ assert!(needs_more);
+
+ let err = decoder.end().unwrap_err();
+ assert!(matches!(err, bitcoin_consensus_encoding::VecDecoderError { .. }));
+}
Why this scored 15/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.