What changed, and why it matters
This is a routine Rust code refactor that adds the ability to encode fixed-size and borrowed slices using the same logic already used for vectors. It does not fix a vulnerability or introduce a known security weakness; it simply makes the encoding API more convenient and consistent.
No security action required. Review as normal code-quality/API-consistency change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors the consensus encoding implementation in rust-bitcoin. Previously, only Vec
Changed components
bitcoin/src/consensus/encode.rsInspect captured patch +79 / −15
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index ff669c7f..ed4486d4 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -469,21 +469,7 @@ impl Encodable for [u16; 8] {
impl<T: Encodable + 'static> Encodable for Vec<T> {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- if TypeId::of::<T>() == TypeId::of::<u8>() {
- let len = self.len();
- let ptr = self.as_ptr();
-
- // unsafe: We've just checked that T is `u8`.
- let v = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), len) };
- consensus_encode_with_size(v, w)
- } else {
- let mut len = 0;
- len += w.emit_compact_size(self.len())?;
- for c in self.iter() {
- len += c.consensus_encode(w)?;
- }
- Ok(len)
- }
+ self[..].consensus_encode(w)
}
}
@@ -569,6 +555,33 @@ impl Decodable for Box<[u8]> {
}
}
+impl<T: Encodable + 'static> Encodable for [T] {
+ #[inline]
+ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
+ (&self).consensus_encode(w)
+ }
+}
+
+impl<T: Encodable + 'static> Encodable for &[T] {
+ #[inline]
+ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
+ if TypeId::of::<T>() == TypeId::of::<u8>() {
+ let len = self.len();
+ let ptr = self.as_ptr();
+
+ // unsafe: We've just checked that T is `u8`.
+ let v = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), len) };
+ consensus_encode_with_size(v, w)
+ } else {
+ let mut len = w.emit_compact_size(self.len())?;
+ for c in self.iter() {
+ len += c.consensus_encode(w)?;
+ }
+ Ok(len)
+ }
+ }
+}
+
impl<T: Encodable> Encodable for &'_ T {
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
(**self).consensus_encode(w)
@@ -733,6 +746,57 @@ mod tests {
(&input[..]).read_compact_size()
}
+ #[test]
+ fn encode_t_slice() {
+ // Multi-element u8 case
+ let enc_buf = serialize(&[1u8, 2, 3, 4].as_slice());
+ assert_eq!(enc_buf, [4u8, 1, 2, 3, 4]);
+
+ // Empty u64 case
+ let enc_buf = serialize::<&[u64]>(&[0u64; 0].as_slice());
+ assert_eq!(enc_buf, [0u8]);
+
+ // multi-element u32 case
+ let enc_buf = serialize(&[654321u32, 123456].as_slice());
+ assert_eq!(enc_buf, [2u8, 241, 251, 9, 0, 64, 226, 1, 0])
+ }
+
+ #[test]
+ fn encode_u8_slice() {
+ // Multi-element case
+ let enc_buf = serialize([1u8, 2, 3, 4].as_slice());
+ assert_eq!(enc_buf, [4u8, 1, 2, 3, 4]);
+
+ // Empty case
+ let enc_buf = serialize::<[u8]>([0u8; 0].as_slice());
+ assert_eq!(enc_buf, [0u8]);
+
+ // Single-element case
+ let enc_buf = serialize([42u8].as_slice());
+ assert_eq!(enc_buf, [1u8, 42]);
+ }
+
+ #[test]
+ fn encode_u8_slice_matches_vec() {
+ let assert_vec_eq = |data: Vec<u8>| {
+ let enc_buf = serialize::<[u8]>(data.as_slice());
+ let vec_enc_buf = serialize::<Vec<u8>>(&data);
+ assert_eq!(enc_buf, vec_enc_buf);
+ };
+
+ // Multi-element case
+ let data = vec![1u8, 2, 3, 4];
+ assert_vec_eq(data);
+
+ // Empty case
+ let data = Vec::new();
+ assert_vec_eq(data);
+
+ // Single-element case
+ let data = vec![42u8];
+ assert_vec_eq(data);
+ }
+
#[test]
fn serialize_varint() {
fn encode(v: u64) -> Vec<u8> {
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.