What changed, and why it matters
This commit adds a new optional feature to a low-level byte-encoding helper in the rust-bitcoin library. It lets callers prepend a Bitcoin-style 'compact size' length prefix when encoding a byte slice. The change is purely additive, includes tests, and does not alter existing behavior of the 'without length prefix' constructor. There is no indication it fixes a security bug or introduces a vulnerability.
No security action required. Review as normal feature/refactoring code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch extends BytesEncoder in consensus_encoding/src/encode/encoders.rs with a with_length_prefix constructor. It stores an ArrayVec<u8, SIZE> containing the compact-size encoding of the slice length, returns that as the first chunk, and then returns the slice itself. without_length_prefix behavior is unchanged. The crate gains a dependency on bitcoin-internals for ArrayVec and compact_size. Two unit tests verify correct encoding for non-empty and empty slices. The commit message frames this as preparatory work for script encoding in primitives.
Changed components
consensus_encoding/src/encode/encoders.rsconsensus_encoding/Cargo.tomlInspect captured patch +56 / −6
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index 9d77d46b..ce193674 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -209,6 +209,7 @@ dependencies = [
name = "consensus-encoding"
version = "0.0.0"
dependencies = [
+ "bitcoin-internals",
"bitcoin_hashes 0.16.0",
]
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index e14b1908..6f190a46 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -211,6 +211,7 @@ dependencies = [
name = "consensus-encoding"
version = "0.0.0"
dependencies = [
+ "bitcoin-internals",
"bitcoin_hashes 0.16.0",
]
diff --git a/consensus_encoding/Cargo.toml b/consensus_encoding/Cargo.toml
index fcb7b85e..03eeb715 100644
--- a/consensus_encoding/Cargo.toml
+++ b/consensus_encoding/Cargo.toml
@@ -14,11 +14,12 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
-std = ["alloc"]
-alloc = []
+std = ["alloc", "internals/std"]
+alloc = ["internals/alloc"]
[dependencies]
hashes = { package = "bitcoin_hashes", path = "../hashes", default-features = false }
+internals = { package = "bitcoin-internals", path = "../internals" }
[package.metadata.docs.rs]
all-features = true
diff --git a/consensus_encoding/src/encode/encoders.rs b/consensus_encoding/src/encode/encoders.rs
index fcec3305..1ddcec94 100644
--- a/consensus_encoding/src/encode/encoders.rs
+++ b/consensus_encoding/src/encode/encoders.rs
@@ -11,24 +11,49 @@
//! For implementing these newtypes, we provide the [`encoder_newtype`] macro.
//!
+use internals::array_vec::ArrayVec;
+use internals::compact_size;
+
use super::Encoder;
+/// The maximum length of a compact size encoding.
+const SIZE: usize = compact_size::MAX_ENCODING_SIZE;
+
/// An encoder for a single byte slice.
pub struct BytesEncoder<'sl> {
sl: Option<&'sl [u8]>,
+ compact_size: Option<ArrayVec<u8, SIZE>>,
}
impl<'sl> BytesEncoder<'sl> {
/// Constructs a byte encoder which encodes the given byte slice, with no length prefix.
- pub fn without_length_prefix(sl: &'sl [u8]) -> Self { Self { sl: Some(sl) } }
+ pub fn without_length_prefix(sl: &'sl [u8]) -> Self {
+ Self { sl: Some(sl), compact_size: None }
+ }
+
+ /// Constructs a byte encoder which encodes the given byte slice, with the length prefix.
+ pub fn with_length_prefix(sl: &'sl [u8]) -> Self {
+ Self { sl: Some(sl), compact_size: Some(compact_size::encode(sl.len())) }
+ }
}
impl<'e, 'sl> Encoder<'e> for BytesEncoder<'sl> {
- fn current_chunk(&self) -> Option<&[u8]> { self.sl }
+ fn current_chunk(&self) -> Option<&[u8]> {
+ if let Some(compact_size) = self.compact_size.as_ref() {
+ Some(compact_size)
+ } else {
+ self.sl
+ }
+ }
fn advance(&mut self) -> bool {
- self.sl = None;
- false
+ if self.compact_size.is_some() {
+ self.compact_size = None;
+ true
+ } else {
+ self.sl = None;
+ false
+ }
}
}
@@ -192,4 +217,26 @@ mod tests {
assert_eq!(got, obj);
}
+
+ #[test]
+ fn encode_byte_slice_with_prefix() {
+ let obj = [1u8, 2, 3];
+
+ let encoder = BytesEncoder::with_length_prefix(&obj);
+ let got = run_encoder(encoder);
+
+ let want = [3u8, 1, 2, 3];
+ assert_eq!(got, want);
+ }
+
+ #[test]
+ fn encode_empty_byte_slice_with_prefix() {
+ let obj = [];
+
+ let encoder = BytesEncoder::with_length_prefix(&obj);
+ let got = run_encoder(encoder);
+
+ let want = [0u8];
+ assert_eq!(got, want);
+ }
}
Why this scored 18/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.