consensus_encoding: add standard I/O drivers
What changed, and why it matters
This commit adds two new helper functions to a Bitcoin library's encoding module: one that turns an object into a byte vector, and another that writes an object's bytes to a standard output destination. It also adds tests for these helpers. There is no indication of a security bug or fix in the code or commit message.
No security action required. This is a routine feature addition with tests. Reviewers may optionally verify that the new public API surface is intentional and that feature gating (`alloc`/`std`) matches crate policy.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces encode_to_vec (gated behind the alloc feature) and encode_to_writer (gated behind the std feature) in consensus_encoding. These are straightforward convenience wrappers around the existing Encodable/Encoder trait machinery. They iterate over encoder chunks and either append them to a Vec or write them via std::io::Write::write_all. The implementation correctly propagates I/O errors and handles empty encoders. No unsafe code, no cryptographic changes, no parsing/validation logic, and no security-relevant behavior changes are present.
Changed components
consensus_encoding/src/encode/mod.rsconsensus_encoding/src/lib.rsconsensus_encoding/tests/encode.rsInspect captured patch +145 / −0
diff --git a/consensus_encoding/src/encode/mod.rs b/consensus_encoding/src/encode/mod.rs
index 12121107..9d39cdef 100644
--- a/consensus_encoding/src/encode/mod.rs
+++ b/consensus_encoding/src/encode/mod.rs
@@ -2,6 +2,9 @@
//! Consensus Encoding Traits
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
pub mod encoders;
/// A Bitcoin object which can be consensus-encoded.
@@ -74,3 +77,40 @@ pub fn encode_to_hash_engine<T: Encodable, H: hashes::HashEngine>(object: &T, mu
}
engine
}
+
+/// Encodes an object into a vector.
+#[cfg(feature = "alloc")]
+pub fn encode_to_vec<T: Encodable>(object: &T) -> Vec<u8> {
+ let mut encoder = object.encoder();
+ let mut vec = Vec::new();
+ while let Some(chunk) = encoder.current_chunk() {
+ vec.extend_from_slice(chunk);
+ encoder.advance();
+ }
+ vec
+}
+
+/// Encodes an object to a standard I/O writer.
+///
+/// # Performance
+///
+/// This method writes data in potentially small chunks based on the encoder's
+/// internal chunking strategy. For optimal performance with unbuffered writers
+/// (like [`std::fs::File`] or [`std::net::TcpStream`]), consider wrapping your
+/// writer with [`std::io::BufWriter`].
+///
+/// # Errors
+///
+/// Returns any I/O error encountered while writing to the writer.
+#[cfg(feature = "std")]
+pub fn encode_to_writer<T: Encodable, W: std::io::Write>(
+ object: &T,
+ mut writer: W,
+) -> Result<(), std::io::Error> {
+ let mut encoder = object.encoder();
+ while let Some(chunk) = encoder.current_chunk() {
+ writer.write_all(chunk)?;
+ encoder.advance();
+ }
+ Ok(())
+}
diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs
index f8190886..0e2fa737 100644
--- a/consensus_encoding/src/lib.rs
+++ b/consensus_encoding/src/lib.rs
@@ -14,8 +14,17 @@
#![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))]
+#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "std")]
+extern crate std;
+
mod encode;
+#[cfg(feature = "alloc")]
+pub use self::encode::encode_to_vec;
+#[cfg(feature = "std")]
+pub use self::encode::encode_to_writer;
pub use self::encode::encoders::{
ArrayEncoder, BytesEncoder, Encoder2, Encoder3, Encoder4, Encoder6,
};
diff --git a/consensus_encoding/tests/encode.rs b/consensus_encoding/tests/encode.rs
new file mode 100644
index 00000000..e8531155
--- /dev/null
+++ b/consensus_encoding/tests/encode.rs
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Tests for encoder free functions.
+
+#[cfg(feature = "std")]
+use std::io::{Cursor, Write};
+
+use consensus_encoding::{ArrayEncoder, Encodable};
+
+// Simple test type that implements Encodable.
+struct TestData(u32);
+
+impl Encodable for TestData {
+ type Encoder<'s>
+ = ArrayEncoder<4>
+ where
+ Self: 's;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ ArrayEncoder::without_length_prefix(self.0.to_le_bytes())
+ }
+}
+
+// Test with a type that creates an empty encoder.
+struct EmptyData;
+
+impl Encodable for EmptyData {
+ type Encoder<'s>
+ = ArrayEncoder<0>
+ where
+ Self: 's;
+
+ fn encoder(&self) -> Self::Encoder<'_> { ArrayEncoder::without_length_prefix([]) }
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn encode_std_writer() {
+ let data = TestData(0x1234_5678);
+
+ let mut cursor = Cursor::new(Vec::new());
+ consensus_encoding::encode_to_writer(&data, &mut cursor).unwrap();
+
+ let result = cursor.into_inner();
+ assert_eq!(result, vec![0x78, 0x56, 0x34, 0x12]);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn encode_vec() {
+ let data = TestData(0xDEAD_BEEF);
+ let vec = consensus_encoding::encode_to_vec(&data);
+ assert_eq!(vec, vec![0xEF, 0xBE, 0xAD, 0xDE]);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn encode_vec_empty_data() {
+ let data = EmptyData;
+ let result = consensus_encoding::encode_to_vec(&data);
+ assert!(result.is_empty());
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn encode_std_writer_empty_data() {
+ let data = EmptyData;
+ let mut cursor = Cursor::new(Vec::new());
+ consensus_encoding::encode_to_writer(&data, &mut cursor).unwrap();
+
+ let result = cursor.into_inner();
+ assert!(result.is_empty());
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn encode_std_writer_io_error() {
+ // Test writer that always fails.
+ struct FailingWriter;
+
+ impl Write for FailingWriter {
+ fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
+ Err(std::io::Error::other("test error"))
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
+ }
+
+ let data = TestData(0x1234_5678);
+ let mut writer = FailingWriter;
+
+ let result = consensus_encoding::encode_to_writer(&data, &mut writer);
+
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().kind(), std::io::ErrorKind::Other);
+}
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.