What changed, and why it matters
This commit only adds new unit tests and one test-tooling exclusion. It does not change any production code, so it cannot introduce or fix a security vulnerability on its own.
No security action needed; treat as routine test/maintenance commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds test coverage for CompactSizeEncoder::len() and for the check_encode/check_encoder test helpers, plus an exclusion for cargo-mutants to avoid an infinite-loop mutation in EncoderStatus::has_more. No library or consensus-encoding logic is modified.
Changed components
consensus_encoding/tests/compact_size.rsconsensus_encoding/tests/encode.rs.cargo/mutants.tomlInspect captured patch +68 / −3
diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml
index 3d68ec64..f1cca3bc 100644
--- a/.cargo/mutants.toml
+++ b/.cargo/mutants.toml
@@ -74,6 +74,7 @@ exclude_re = [
"consensus_encoding/.* decode_from_read", # Mutations cause an infinite loop
"consensus_encoding/.* <impl Decoder for .*>::push_bytes", # Mutations cause an infinite loop
"consensus_encoding/.* <impl Encoder for .*>::advance", # Replacing the return with true causes an infinite loop.
+ "consensus_encoding/.* EncoderStatus::has_more", # Replacing with true causes an infinite loop
"consensus_encoding/.* delete ! in drain_to_vec", # Causes an infinite loop.
"consensus_encoding/.* delete ! in drain_to_writer", # Causes an infinite loop.
]
diff --git a/consensus_encoding/tests/compact_size.rs b/consensus_encoding/tests/compact_size.rs
index b5384e91..13b0f9e6 100644
--- a/consensus_encoding/tests/compact_size.rs
+++ b/consensus_encoding/tests/compact_size.rs
@@ -4,7 +4,7 @@
use bitcoin_consensus_encoding::{
check_encode, decode_from_slice, CompactSizeDecoderError, CompactSizeEncoder,
- CompactSizeU64Decoder, Decode, Encode,
+ CompactSizeU64Decoder, Decode, Encode, ExactSizeEncoder,
};
use bitcoin_consensus_encoding::{CompactSizeDecoder, Decoder};
@@ -307,3 +307,21 @@ fn decoder_compact_size_end_incomplete_nine_byte() {
let err = decoder.end().unwrap_err();
assert!(matches!(err, bitcoin_consensus_encoding::CompactSizeDecoderError { .. }));
}
+
+#[test]
+fn encoder_compact_size_len_matches_encoded_bytes() {
+ // Test that CompactSizeEncoder::len() returns the correct length for various values
+ let test_cases = [
+ (0usize, 1), // Single byte: 0x00
+ (252usize, 1), // Single byte: 0xFC
+ (253usize, 3), // Three bytes: 0xFD + 2 bytes
+ (0xFFFF_usize, 3), // Three bytes: 0xFD + 2 bytes
+ (0x1_0000_usize, 5), // Five bytes: 0xFE + 4 bytes
+ (0xFFFF_FFFF_usize, 5), // Five bytes: 0xFE + 4 bytes
+ ];
+
+ for (value, expected_len) in test_cases {
+ let encoder = CompactSizeEncoder::new(value);
+ assert_eq!(encoder.len(), expected_len, "CompactSizeEncoder::len() mismatch for value {}", value);
+ }
+}
diff --git a/consensus_encoding/tests/encode.rs b/consensus_encoding/tests/encode.rs
index c91a3256..35800b1e 100644
--- a/consensus_encoding/tests/encode.rs
+++ b/consensus_encoding/tests/encode.rs
@@ -6,8 +6,9 @@
use std::io::{Cursor, Write};
use bitcoin_consensus_encoding::{
- ArrayEncoder, ArrayRefEncoder, BytesEncoder, check_encoder, Encode, Encoder, Encoder2, Encoder3,
- Encoder4, Encoder6, EncoderByteIter, ExactSizeEncoder, SliceEncoder,
+ check_encode, check_encoder, ArrayEncoder, ArrayRefEncoder, BytesEncoder, Encode, Encoder,
+ Encoder2, Encoder3, Encoder4, Encoder6, EncoderByteIter, ExactSizeEncoder,
+ SliceEncoder,
};
struct TestBytes<'a>(&'a [u8]);
@@ -430,3 +431,48 @@ fn iter_encoder() {
assert_eq!(iter.len(), 0);
assert!(iter.next().is_none());
}
+
+#[test]
+#[should_panic(expected = "encoder did not yield expected bytes")]
+fn check_encode_detects_mismatched_bytes() {
+ let test_array = TestArray([0x01, 0x02, 0x03, 0x04]);
+ check_encode(&test_array, &[0xFF, 0xFF, 0xFF, 0xFF]);
+}
+
+#[test]
+#[should_panic(expected = "did not yield enough bytes")]
+fn check_encode_detects_too_few_bytes() {
+ let test_array = TestArray([0x01, 0x02, 0x03, 0x04]);
+ check_encode(&test_array, &[0x01, 0x02, 0x03, 0x04, 0x05]);
+}
+
+#[test]
+fn check_encoder_with_multiple_chunks() {
+ let mut encoder = Encoder3::new(
+ BytesEncoder::without_length_prefix(&[0x01]),
+ BytesEncoder::without_length_prefix(&[0x02, 0x03]),
+ BytesEncoder::without_length_prefix(&[0x04, 0x05, 0x06]),
+ );
+ check_encoder(&mut encoder, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
+}
+
+#[test]
+#[should_panic(expected = "difference in chunk #1")]
+fn check_encoder_detects_error_in_chunk() {
+ let mut encoder = Encoder3::new(
+ BytesEncoder::without_length_prefix(&[0x01]),
+ BytesEncoder::without_length_prefix(&[0xFF, 0xFF]),
+ BytesEncoder::without_length_prefix(&[0x04, 0x05, 0x06]),
+ );
+ check_encoder(&mut encoder, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
+}
+
+#[test]
+#[should_panic(expected = "after 1 bytes")]
+fn check_encoder_detects_error_byte_offset() {
+ let mut encoder = Encoder2::new(
+ BytesEncoder::without_length_prefix(&[0x01]),
+ BytesEncoder::without_length_prefix(&[0xFF, 0x03]),
+ );
+ check_encoder(&mut encoder, &[0x01, 0x02, 0x03]);
+}
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.