Drop the consensus module from bitcoin
What changed, and why it matters
This commit removes the old `bitcoin::consensus` module from the rust-bitcoin library because its functionality has already been replaced by a newer `consensus_encoding` module. It is a cleanup change that deletes unused code; no security vulnerability is introduced or fixed.
No security action required. Treat as routine refactoring/dead-code removal. Verify downstream consumers no longer import `bitcoin::consensus` before upgrading.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit deletes the entire bitcoin/src/consensus/ directory (encode.rs, error.rs, mod.rs, serde.rs, verification.rs) and removes the pub mod consensus; declaration from bitcoin/src/lib.rs. The commit message states that all uses of the old module were previously migrated to consensus_encoding. No logic changes, bug fixes, or security-relevant modifications are present in the diff.
Changed components
bitcoin/src/consensus/encode.rsbitcoin/src/consensus/error.rsbitcoin/src/consensus/mod.rsbitcoin/src/consensus/serde.rsbitcoin/src/consensus/verification.rsbitcoin/src/lib.rsInspect captured patch +0 / −2062
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
deleted file mode 100644
index ca95e6a1..00000000
--- a/bitcoin/src/consensus/encode.rs
+++ /dev/null
@@ -1,1141 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Bitcoin consensus-encodable types.
-//!
-//! This is basically a replacement of the `Encodable` trait which does
-//! normalization of endianness etc., to ensure that the encoding matches
-//! the network consensus encoding.
-//!
-//! Essentially, anything that must go on the _disk_ or _network_ must be
-//! encoded using the `Encodable` trait, since this data must be the same for
-//! all systems. Any data going to the _user_ e.g., over JSONRPC, should use the
-//! ordinary `Encodable` trait. (This should also be the same across systems, of
-//! course, but has some critical differences from the network format e.g.,
-//! scripts come with an opcode decode, hashes are big-endian, numbers are
-//! typically big-endian decimals, etc.)
-
-use core::any::TypeId;
-use core::{cmp, mem, slice};
-
-use encoding::{CompactSizeEncoder, Encoder};
-use hashes::{sha256, sha256d, Hash};
-use hex::DisplayHex as _;
-use io::{BufRead, Cursor, Read, Write};
-
-use super::IterReader;
-use crate::prelude::{rc, sync, Box, Cow, String, Vec};
-use crate::taproot::TapLeafHash;
-use crate::ToU64;
-
-#[rustfmt::skip] // Keep public re-exports separate.
-pub use super::{Error, FromHexError, ParseError, DeserializeError};
-
-/// Encodes an object into a vector.
-pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
- let mut encoder = Vec::new();
- let len = data.consensus_encode(&mut encoder).expect("in-memory writers don't error");
- debug_assert_eq!(len, encoder.len());
- encoder
-}
-
-/// Encodes an object into a hex-encoded string.
-pub fn serialize_hex<T: Encodable + ?Sized>(data: &T) -> String {
- serialize(data).to_lower_hex_string()
-}
-
-/// Deserializes an object from a vector, will error if said deserialization
-/// doesn't consume the entire vector.
-pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, DeserializeError> {
- let (rv, consumed) = deserialize_partial(data)?;
-
- // Fail if data are not consumed entirely.
- if consumed == data.len() {
- Ok(rv)
- } else {
- Err(DeserializeError::Unconsumed)
- }
-}
-
-/// Deserializes any decodable type from a hex string, will error if said deserialization
-/// doesn't consume the entire vector.
-pub fn deserialize_hex<T: Decodable>(hex: &str) -> Result<T, FromHexError> {
- let iter = hex::HexSliceToBytesIter::new(hex)?;
- let reader = IterReader::new(iter);
- Ok(reader.decode().map_err(FromHexError::Decode)?)
-}
-
-/// Deserializes an object from a vector, but will not report an error if said deserialization
-/// doesn't consume the entire vector.
-pub fn deserialize_partial<T: Decodable>(data: &[u8]) -> Result<(T, usize), ParseError> {
- let mut decoder = Cursor::new(data);
-
- let rv = match Decodable::consensus_decode_from_finite_reader(&mut decoder) {
- Ok(rv) => rv,
- Err(Error::Parse(e)) => return Err(e),
- Err(Error::Io(_)) =>
- unreachable!("consensus_decode code never returns an I/O error for in-memory reads"),
- };
- let consumed = decoder.position() as usize;
-
- Ok((rv, consumed))
-}
-
-/// Extensions of `Write` to encode data as per Bitcoin consensus.
-pub trait WriteExt: Write {
- /// Outputs a 64-bit unsigned integer.
- fn emit_u64(&mut self, v: u64) -> Result<(), io::Error>;
- /// Outputs a 32-bit unsigned integer.
- fn emit_u32(&mut self, v: u32) -> Result<(), io::Error>;
- /// Outputs a 16-bit unsigned integer.
- fn emit_u16(&mut self, v: u16) -> Result<(), io::Error>;
- /// Outputs an 8-bit unsigned integer.
- fn emit_u8(&mut self, v: u8) -> Result<(), io::Error>;
-
- /// Outputs a 64-bit signed integer.
- fn emit_i64(&mut self, v: i64) -> Result<(), io::Error>;
- /// Outputs a 32-bit signed integer.
- fn emit_i32(&mut self, v: i32) -> Result<(), io::Error>;
- /// Outputs a 16-bit signed integer.
- fn emit_i16(&mut self, v: i16) -> Result<(), io::Error>;
- /// Outputs an 8-bit signed integer.
- fn emit_i8(&mut self, v: i8) -> Result<(), io::Error>;
-
- /// Outputs a variable sized integer ([`CompactSize`]).
- ///
- /// [`CompactSize`]: <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>
- fn emit_compact_size(&mut self, v: impl ToU64) -> Result<usize, io::Error>;
-
- /// Outputs a boolean.
- fn emit_bool(&mut self, v: bool) -> Result<(), io::Error>;
-
- /// Outputs a byte slice.
- fn emit_slice(&mut self, v: &[u8]) -> Result<usize, io::Error>;
-}
-
-/// Extensions of `Read` to decode data as per Bitcoin consensus.
-pub trait ReadExt: Read {
- /// Reads a 64-bit unsigned integer.
- fn read_u64(&mut self) -> Result<u64, Error>;
- /// Reads a 32-bit unsigned integer.
- fn read_u32(&mut self) -> Result<u32, Error>;
- /// Reads a 16-bit unsigned integer.
- fn read_u16(&mut self) -> Result<u16, Error>;
- /// Reads an 8-bit unsigned integer.
- fn read_u8(&mut self) -> Result<u8, Error>;
-
- /// Reads a 64-bit signed integer.
- fn read_i64(&mut self) -> Result<i64, Error>;
- /// Reads a 32-bit signed integer.
- fn read_i32(&mut self) -> Result<i32, Error>;
- /// Reads a 16-bit signed integer.
- fn read_i16(&mut self) -> Result<i16, Error>;
- /// Reads an 8-bit signed integer.
- fn read_i8(&mut self) -> Result<i8, Error>;
-
- /// Reads a variable sized integer ([`CompactSize`]).
- ///
- /// [`CompactSize`]: <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>
- fn read_compact_size(&mut self) -> Result<u64, Error>;
-
- /// Reads a boolean.
- fn read_bool(&mut self) -> Result<bool, Error>;
-
- /// Reads a byte slice.
- fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), Error>;
-}
-
-macro_rules! encoder_fn {
- ($name:ident, $val_type:ty) => {
- #[inline]
- fn $name(&mut self, v: $val_type) -> core::result::Result<(), io::Error> {
- self.write_all(&v.to_le_bytes())
- }
- };
-}
-
-macro_rules! decoder_fn {
- ($name:ident, $val_type:ty, $byte_len: expr) => {
- #[inline]
- fn $name(&mut self) -> core::result::Result<$val_type, Error> {
- let mut val = [0; $byte_len];
- self.read_exact(&mut val[..])?;
- Ok(<$val_type>::from_le_bytes(val))
- }
- };
-}
-
-impl<W: Write + ?Sized> WriteExt for W {
- encoder_fn!(emit_u64, u64);
- encoder_fn!(emit_u32, u32);
- encoder_fn!(emit_u16, u16);
- encoder_fn!(emit_i64, i64);
- encoder_fn!(emit_i32, i32);
- encoder_fn!(emit_i16, i16);
-
- #[inline]
- fn emit_i8(&mut self, v: i8) -> Result<(), io::Error> { self.write_all(&[v as u8]) }
- #[inline]
- fn emit_u8(&mut self, v: u8) -> Result<(), io::Error> { self.write_all(&[v]) }
- #[inline]
- fn emit_bool(&mut self, v: bool) -> Result<(), io::Error> { self.write_all(&[v as u8]) }
- #[inline]
- fn emit_slice(&mut self, v: &[u8]) -> Result<usize, io::Error> {
- self.write_all(v)?;
- Ok(v.len())
- }
- #[inline]
- fn emit_compact_size(&mut self, v: impl ToU64) -> Result<usize, io::Error> {
- let encoder = CompactSizeEncoder::new(v.to_u64().try_into().unwrap_or(usize::MAX));
- let encoded = encoder.current_chunk();
- self.emit_slice(encoded)?;
- Ok(encoded.len())
- }
-}
-
-impl<R: Read + ?Sized> ReadExt for R {
- decoder_fn!(read_u64, u64, 8);
- decoder_fn!(read_u32, u32, 4);
- decoder_fn!(read_u16, u16, 2);
- decoder_fn!(read_i64, i64, 8);
- decoder_fn!(read_i32, i32, 4);
- decoder_fn!(read_i16, i16, 2);
-
- #[inline]
- fn read_u8(&mut self) -> Result<u8, Error> {
- let mut slice = [0u8; 1];
- self.read_exact(&mut slice)?;
- Ok(slice[0])
- }
- #[inline]
- fn read_i8(&mut self) -> Result<i8, Error> {
- let mut slice = [0u8; 1];
- self.read_exact(&mut slice)?;
- Ok(slice[0] as i8)
- }
- #[inline]
- fn read_bool(&mut self) -> Result<bool, Error> { ReadExt::read_i8(self).map(|bit| bit != 0) }
- #[inline]
- fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), Error> { Ok(self.read_exact(slice)?) }
- #[inline]
- fn read_compact_size(&mut self) -> Result<u64, Error> { read_compact_size_internal(self, true) }
-}
-
-#[rustfmt::skip] // Formatter munges code comments below.
-fn read_compact_size_internal<R: Read + ?Sized>(r: &mut R, range_check: bool)-> Result<u64, Error> {
- let x = match r.read_u8()? {
- 0xFF => {
- let x = r.read_u64()?;
- if x < 0x1_0000_0000 { // I.e., would have fit in a `u32`.
- return Err(ParseError::NonMinimalCompactSize.into());
- } else {
- x
- }
- }
- 0xFE => {
- let x = r.read_u32()?;
- if x < 0x1_0000 { // I.e., would have fit in a `u16`.
- return Err(ParseError::NonMinimalCompactSize.into());
- } else {
- x as u64
- }
- }
- 0xFD => {
- let x = r.read_u16()?;
- if x < 0xFD { // Could have been encoded as a `u8`.
- return Err(ParseError::NonMinimalCompactSize.into());
- } else {
- x as u64
- }
- }
- n => n as u64,
- };
- if range_check && x > MAX_COMPACT_SIZE as u64 {
- Err(ParseError::OversizedCompactSize.into())
- } else {
- Ok(x)
- }
- }
-
-/// Maximum size, in bytes, of a vector we are allowed to decode.
-pub const MAX_VEC_SIZE: usize = 4_000_000;
-
-/// The maximum size of a serialized object in bytes or number of elements
-/// (for eg vectors) when the size is encoded as CompactSize.
-/// <https://github.com/bitcoin/bitcoin/blob/a7c29df0e5ace05b6186612671d6103c112ec922/src/serialize.h#L32>
-pub const MAX_COMPACT_SIZE: usize = 0x02000000;
-
-/// Data which can be encoded in a consensus-consistent way.
-pub trait Encodable {
- /// Encodes an object with a well-defined format.
- ///
- /// # Returns
- ///
- /// The number of bytes written on success. The only errors returned are errors propagated from
- /// the writer.
- fn consensus_encode<W: Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error>;
-}
-
-/// Data which can be decoded in a consensus-consistent way.
-pub trait Decodable: Sized {
- /// Decodes `Self` from a size-limited reader.
- ///
- /// Like `consensus_decode` but relies on the reader being limited in the amount of data it
- /// returns, e.g. by being wrapped in [`std::io::Take`].
- ///
- /// Failing to abide to this requirement might lead to memory exhaustion caused by malicious
- /// inputs.
- ///
- /// Users should default to `consensus_decode`, but when data to be decoded is already in a byte
- /// vector of a limited size, calling this function directly might be marginally faster (due to
- /// avoiding extra checks).
- ///
- /// # Rules for trait implementations
- ///
- /// * Simple types that have a fixed size (own and member fields), don't have to overwrite
- /// this method, or be concern with it.
- /// * Types that deserialize using externally provided length should implement it:
- /// * Make `consensus_decode` forward to `consensus_decode_from_finite_reader` with the
- /// reader wrapped by `Take`. Failure to do so, without other forms of memory exhaustion
- /// protection might lead to resource exhaustion vulnerability.
- /// * Put a max cap on things like `Vec::with_capacity` to avoid oversized allocations, and
- /// rely on the reader running out of data, and collections reallocating on a legitimately
- /// oversized input data, instead of trying to enforce arbitrary length limits.
- /// * Types that contain other types that implement custom
- /// `consensus_decode_from_finite_reader`, should also implement it applying same rules, and
- /// in addition make sure to call `consensus_decode_from_finite_reader` on all members, to
- /// avoid creating redundant `Take` wrappers. Failure to do so might result only in a tiny
- /// performance hit.
- #[inline]
- fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
- reader: &mut R,
- ) -> Result<Self, Error> {
- // This method is always strictly less general than, `consensus_decode`, so it's safe and
- // make sense to default to just calling it. This way most types, that don't care about
- // protecting against resource exhaustion due to malicious input, can just ignore it.
- Self::consensus_decode(reader)
- }
-
- /// Decodes an object with a well-defined format.
- ///
- /// This is the method that should be implemented for a typical, fixed sized type
- /// implementing this trait. Default implementation is wrapping the reader
- /// in [`crate::io::Take`] to limit the input size to [`MAX_VEC_SIZE`], and forwards the call to
- /// [`Self::consensus_decode_from_finite_reader`], which is convenient
- /// for types that override [`Self::consensus_decode_from_finite_reader`]
- /// instead.
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(reader: &mut R) -> Result<Self, Error> {
- Self::consensus_decode_from_finite_reader(&mut reader.take(MAX_VEC_SIZE.to_u64()))
- }
-}
-
-// Primitive types
-macro_rules! impl_int_encodable {
- ($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
- impl Decodable for $ty {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(
- r: &mut R,
- ) -> core::result::Result<Self, Error> {
- ReadExt::$meth_dec(r)
- }
- }
- impl Encodable for $ty {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(
- &self,
- w: &mut W,
- ) -> core::result::Result<usize, io::Error> {
- w.$meth_enc(*self)?;
- Ok(mem::size_of::<$ty>())
- }
- }
- };
-}
-
-impl_int_encodable!(u8, read_u8, emit_u8);
-impl_int_encodable!(u16, read_u16, emit_u16);
-impl_int_encodable!(u32, read_u32, emit_u32);
-impl_int_encodable!(u64, read_u64, emit_u64);
-impl_int_encodable!(i8, read_i8, emit_i8);
-impl_int_encodable!(i16, read_i16, emit_i16);
-impl_int_encodable!(i32, read_i32, emit_i32);
-impl_int_encodable!(i64, read_i64, emit_i64);
-
-/// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
-#[deprecated(
- since = "0.33.0",
- note = "use `consensus_encoding::CompactSizeEncoder::encoded_size` instead"
-)]
-#[inline]
-pub const fn varint_size_u64(v: u64) -> usize {
- const LIMIT: u64 = if core::mem::size_of::<usize>() <= 8 {
- usize::MAX as u64 // Cast is ok, because usize is <= the size of u64
- } else {
- u64::MAX
- };
-
- #[allow(unreachable_patterns)] // This is only reachable on < 64 bit platforms
- match v {
- 0..=LIMIT => encoding::CompactSizeEncoder::encoded_size(v as usize), // cast is ok because we just checked bounds
- _ => encoding::CompactSizeEncoder::encoded_size(usize::MAX),
- }
-}
-
-/// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
-#[deprecated(
- since = "0.33.0",
- note = "use `consensus_encoding::CompactSizeEncoder::encoded_size` instead"
-)]
-#[inline]
-pub fn varint_size(v: impl ToU64) -> usize {
- encoding::CompactSizeEncoder::encoded_size(v.to_u64().try_into().unwrap_or(usize::MAX))
-}
-
-impl Encodable for bool {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- w.emit_bool(*self)?;
- Ok(1)
- }
-}
-
-impl Decodable for bool {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- ReadExt::read_bool(r)
- }
-}
-
-impl Encodable for String {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- consensus_encode_with_size(self.as_bytes(), w)
- }
-}
-
-impl Decodable for String {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- Self::from_utf8(Decodable::consensus_decode(r)?)
- .map_err(|_| super::parse_failed_error("String was not valid UTF8"))
- }
-}
-
-impl Encodable for Cow<'static, str> {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- consensus_encode_with_size(self.as_bytes(), w)
- }
-}
-
-impl Decodable for Cow<'static, str> {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- String::from_utf8(Decodable::consensus_decode(r)?)
- .map_err(|_| super::parse_failed_error("String was not valid UTF8"))
- .map(Cow::Owned)
- }
-}
-
-macro_rules! impl_array {
- ( $size:literal ) => {
- impl Encodable for [u8; $size] {
- #[inline]
- fn consensus_encode<W: WriteExt + ?Sized>(
- &self,
- w: &mut W,
- ) -> core::result::Result<usize, io::Error> {
- let n = w.emit_slice(&self[..])?;
- Ok(n)
- }
- }
-
- impl Decodable for [u8; $size] {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(
- r: &mut R,
- ) -> core::result::Result<Self, Error> {
- let mut ret = [0; $size];
- r.read_slice(&mut ret)?;
- Ok(ret)
- }
- }
- };
-}
-
-impl_array!(2);
-impl_array!(4);
-impl_array!(6);
-impl_array!(8);
-impl_array!(10);
-impl_array!(12);
-impl_array!(16);
-impl_array!(32);
-impl_array!(33);
-
-impl Decodable for [u16; 8] {
- #[inline]
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- let mut res = [0; 8];
- for item in &mut res {
- *item = Decodable::consensus_decode(r)?;
- }
- Ok(res)
- }
-}
-
-impl Encodable for [u16; 8] {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- for c in self.iter() {
- c.consensus_encode(w)?;
- }
- Ok(16)
- }
-}
-
-impl<T: Encodable + 'static> Encodable for Vec<T> {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- self[..].consensus_encode(w)
- }
-}
-
-impl<T: Decodable + 'static> Decodable for Vec<T> {
- #[inline]
- fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- if TypeId::of::<T>() == TypeId::of::<u8>() {
- let len = r.read_compact_size()? as usize;
- // most real-world vec of bytes data, wouldn't be larger than 128KiB
- let opts = ReadBytesFromFiniteReaderOpts { len, chunk_size: 128 * 1024 };
- let bytes = read_bytes_from_finite_reader(r, opts)?;
-
- // unsafe: We've just checked that T is `u8` so the transmute here is a no-op.
- unsafe { Ok(mem::transmute::<Vec<u8>, Self>(bytes)) }
- } else {
- let len = r.read_compact_size()?;
- // Limit the initial vec allocation to at most 8,000 bytes, which is
- // sufficient for most use cases. We don't allocate more space upfront
- // than this, since `len` is an untrusted allocation capacity. If the
- // vector does overflow the initial capacity `push` will just reallocate.
- // Note: OOM protection relies on reader eventually running out of
- // data to feed us.
- let max_init_capacity = 8000 / mem::size_of::<T>();
- let mut ret = Self::with_capacity(cmp::min(len as usize, max_init_capacity));
- for _ in 0..len {
- ret.push(Decodable::consensus_decode_from_finite_reader(r)?);
- }
- Ok(ret)
- }
- }
-}
-
-pub(crate) fn consensus_encode_with_size<W: Write + ?Sized>(
- data: &[u8],
- w: &mut W,
-) -> Result<usize, io::Error> {
- Ok(w.emit_compact_size(data.len())? + w.emit_slice(data)?)
-}
-
-struct ReadBytesFromFiniteReaderOpts {
- len: usize,
- chunk_size: usize,
-}
-
-/// Read `opts.len` bytes from reader, where `opts.len` could potentially be malicious.
-///
-/// This function relies on reader being bound in amount of data
-/// it returns for OOM protection. See [`Decodable::consensus_decode_from_finite_reader`].
-#[inline]
-fn read_bytes_from_finite_reader<D: Read + ?Sized>(
- d: &mut D,
- mut opts: ReadBytesFromFiniteReaderOpts,
-) -> Result<Vec<u8>, Error> {
- let mut ret = vec![];
-
- assert_ne!(opts.chunk_size, 0);
-
- while opts.len > 0 {
- let chunk_start = ret.len();
- let chunk_size = core::cmp::min(opts.len, opts.chunk_size);
- let chunk_end = chunk_start + chunk_size;
- ret.resize(chunk_end, 0u8);
- d.read_slice(&mut ret[chunk_start..chunk_end])?;
- opts.len -= chunk_size;
- }
-
- Ok(ret)
-}
-
-impl Encodable for Box<[u8]> {
- #[inline]
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- consensus_encode_with_size(self, w)
- }
-}
-
-impl Decodable for Box<[u8]> {
- #[inline]
- fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- <Vec<u8>>::consensus_decode_from_finite_reader(r).map(From::from)
- }
-}
-
-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)
- }
-}
-
-impl<T: Encodable> Encodable for &'_ mut T {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- (**self).consensus_encode(w)
- }
-}
-
-impl<T: Encodable> Encodable for rc::Rc<T> {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- (**self).consensus_encode(w)
- }
-}
-
-/// Note: This will fail to compile on old Rust for targets that don't support atomics
-#[cfg(target_has_atomic = "ptr")]
-impl<T: Encodable> Encodable for sync::Arc<T> {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- (**self).consensus_encode(w)
- }
-}
-
-macro_rules! tuple_encode {
- ($($x:ident),*) => {
- impl <$($x: Encodable),*> Encodable for ($($x),*) {
- #[inline]
- #[allow(non_snake_case)]
- fn consensus_encode<W: Write + ?Sized>(
- &self,
- w: &mut W,
- ) -> core::result::Result<usize, io::Error> {
- let &($(ref $x),*) = self;
- let mut len = 0;
- $(len += $x.consensus_encode(w)?;)*
- Ok(len)
- }
- }
-
- impl<$($x: Decodable),*> Decodable for ($($x),*) {
- #[inline]
- #[allow(non_snake_case)]
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> core::result::Result<Self, Error> {
- Ok(($({let $x = Decodable::consensus_decode(r)?; $x }),*))
- }
- }
- };
-}
-
-tuple_encode!(T0, T1);
-tuple_encode!(T0, T1, T2);
-tuple_encode!(T0, T1, T2, T3);
-tuple_encode!(T0, T1, T2, T3, T4);
-tuple_encode!(T0, T1, T2, T3, T4, T5);
-tuple_encode!(T0, T1, T2, T3, T4, T5, T6);
-tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
-
-impl Encodable for sha256d::Hash {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- self.as_byte_array().consensus_encode(w)
- }
-}
-
-impl Decodable for sha256d::Hash {
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
- }
-}
-
-impl Encodable for sha256::Hash {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- self.as_byte_array().consensus_encode(w)
- }
-}
-
-impl Decodable for sha256::Hash {
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
- }
-}
-
-impl Encodable for TapLeafHash {
- fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- self.as_byte_array().consensus_encode(w)
- }
-}
-
-impl Decodable for TapLeafHash {
- fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
- Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use alloc::string::ToString;
- use core::fmt;
- use core::mem::discriminant;
-
- use super::*;
- use crate::prelude::{Cow, Vec};
-
- #[test]
- fn serialize_int() {
- // bool
- assert_eq!(serialize(&false), [0u8]);
- assert_eq!(serialize(&true), [1u8]);
- // u8
- assert_eq!(serialize(&1u8), [1u8]);
- assert_eq!(serialize(&0u8), [0u8]);
- assert_eq!(serialize(&255u8), [255u8]);
- // u16
- assert_eq!(serialize(&1u16), [1u8, 0]);
- assert_eq!(serialize(&256u16), [0u8, 1]);
- assert_eq!(serialize(&5000u16), [136u8, 19]);
- // u32
- assert_eq!(serialize(&1u32), [1u8, 0, 0, 0]);
- assert_eq!(serialize(&256u32), [0u8, 1, 0, 0]);
- assert_eq!(serialize(&5000u32), [136u8, 19, 0, 0]);
- assert_eq!(serialize(&500000u32), [32u8, 161, 7, 0]);
- assert_eq!(serialize(&168430090u32), [10u8, 10, 10, 10]);
- // i32
- assert_eq!(serialize(&-1i32), [255u8, 255, 255, 255]);
- assert_eq!(serialize(&-256i32), [0u8, 255, 255, 255]);
- assert_eq!(serialize(&-5000i32), [120u8, 236, 255, 255]);
- assert_eq!(serialize(&-500000i32), [224u8, 94, 248, 255]);
- assert_eq!(serialize(&-168430090i32), [246u8, 245, 245, 245]);
- assert_eq!(serialize(&1i32), [1u8, 0, 0, 0]);
- assert_eq!(serialize(&256i32), [0u8, 1, 0, 0]);
- assert_eq!(serialize(&5000i32), [136u8, 19, 0, 0]);
- assert_eq!(serialize(&500000i32), [32u8, 161, 7, 0]);
- assert_eq!(serialize(&168430090i32), [10u8, 10, 10, 10]);
- // u64
- assert_eq!(serialize(&1u64), [1u8, 0, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&256u64), [0u8, 1, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&5000u64), [136u8, 19, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&500000u64), [32u8, 161, 7, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&723401728380766730u64), [10u8, 10, 10, 10, 10, 10, 10, 10]);
- // i64
- assert_eq!(serialize(&-1i64), [255u8, 255, 255, 255, 255, 255, 255, 255]);
- assert_eq!(serialize(&-256i64), [0u8, 255, 255, 255, 255, 255, 255, 255]);
- assert_eq!(serialize(&-5000i64), [120u8, 236, 255, 255, 255, 255, 255, 255]);
- assert_eq!(serialize(&-500000i64), [224u8, 94, 248, 255, 255, 255, 255, 255]);
- assert_eq!(serialize(&-723401728380766730i64), [246u8, 245, 245, 245, 245, 245, 245, 245]);
- assert_eq!(serialize(&1i64), [1u8, 0, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&256i64), [0u8, 1, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&5000i64), [136u8, 19, 0, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&500000i64), [32u8, 161, 7, 0, 0, 0, 0, 0]);
- assert_eq!(serialize(&723401728380766730i64), [10u8, 10, 10, 10, 10, 10, 10, 10]);
- }
-
- fn test_varint_encode(n: u8, x: &[u8]) -> Result<u64, Error> {
- let mut input = [0u8; 9];
- input[0] = n;
- input[1..x.len() + 1].copy_from_slice(x);
- read_compact_size_internal(&mut &input[..], false)
- }
-
- #[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> {
- let mut buf = Vec::new();
- buf.emit_compact_size(v).unwrap();
- buf
- }
-
- assert_eq!(encode(10), [10u8]);
- assert_eq!(encode(0xFC), [0xFCu8]);
- assert_eq!(encode(0xFD), [0xFDu8, 0xFD, 0]);
- assert_eq!(encode(0xFFF), [0xFDu8, 0xFF, 0xF]);
- assert_eq!(encode(0xF0F0F0F), [0xFEu8, 0xF, 0xF, 0xF, 0xF]);
- #[cfg(target_pointer_width = "64")]
- assert_eq!(encode(0xF0F0F0F0F0E0), vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0],);
- assert_eq!(test_varint_encode(0xFF, &0x100000000_u64.to_le_bytes()).unwrap(), 0x100000000,);
- assert_eq!(test_varint_encode(0xFE, &0x10000_u64.to_le_bytes()).unwrap(), 0x10000);
- assert_eq!(test_varint_encode(0xFD, &0xFD_u64.to_le_bytes()).unwrap(), 0xFD);
-
- // Test that length calc is working correctly
- fn test_varint_len(varint: usize, expected: usize) {
- let mut encoder = vec![];
- assert_eq!(encoder.emit_compact_size(varint).unwrap(), expected);
- assert_eq!(encoding::CompactSizeEncoder::encoded_size(varint), expected);
- }
- test_varint_len(0, 1);
- test_varint_len(0xFC, 1);
- test_varint_len(0xFD, 3);
- test_varint_len(0xFFFF, 3);
- test_varint_len(0x10000, 5);
- test_varint_len(0xFFFFFFFF, 5);
- #[cfg(target_pointer_width = "64")]
- {
- test_varint_len(0xFFFFFFFF + 1, 9);
- test_varint_len(u64::MAX as usize, 9);
- }
- }
-
- #[test]
- fn deserialize_compact_size_too_large() {
- // MAX_COMPACT_SIZE (0x02000000) should succeed
- assert_eq!(test_varint_encode(0xFE, &(0x02000000_u64).to_le_bytes()).unwrap(), 0x02000000);
- // MAX_COMPACT_SIZE + 1 should fail with range check enabled
- let mut input = [0u8; 9];
- input[0] = 0xFE;
- input[1..5].copy_from_slice(&(0x02000001_u32).to_le_bytes());
- assert_eq!(
- discriminant(&(&mut &input[..]).read_compact_size().unwrap_err()),
- discriminant(&ParseError::OversizedCompactSize.into())
- );
- // Same value without range check should succeed
- assert_eq!(read_compact_size_internal(&mut &input[..], false).unwrap(), 0x02000001);
- }
-
- #[test]
- fn deserialize_nonminimal_vec() {
- // Check the edges for variant int
- assert_eq!(
- discriminant(
- &test_varint_encode(0xFF, &(0x100000000_u64 - 1).to_le_bytes()).unwrap_err()
- ),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&test_varint_encode(0xFE, &(0x10000_u64 - 1).to_le_bytes()).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&test_varint_encode(0xFD, &(0xFD_u64 - 1).to_le_bytes()).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
-
- assert_eq!(
- discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]).unwrap_err()),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(
- &deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
- .unwrap_err()
- ),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
- assert_eq!(
- discriminant(
- &deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00])
- .unwrap_err()
- ),
- discriminant(&ParseError::NonMinimalCompactSize.into())
- );
-
- let mut vec_256 = vec![0; 259];
- vec_256[0] = 0xfd;
- vec_256[1] = 0x00;
- vec_256[2] = 0x01;
- assert!(deserialize::<Vec<u8>>(&vec_256).is_ok());
-
- let mut vec_253 = vec![0; 256];
- vec_253[0] = 0xfd;
- vec_253[1] = 0xfd;
- vec_253[2] = 0x00;
- assert!(deserialize::<Vec<u8>>(&vec_253).is_ok());
- }
-
- #[test]
- fn serialize_vector() {
- assert_eq!(serialize(&vec![1u8, 2, 3]), [3u8, 1, 2, 3]);
- }
-
- #[test]
- fn serialize_strbuf() {
- assert_eq!(serialize(&"Andrew".to_string()), [6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]);
- }
-
- #[test]
- fn deserialize_int() {
- // bool
- assert!((deserialize(&[58u8, 0]) as Result<bool, _>).is_err());
- assert_eq!(deserialize(&[58u8]).ok(), Some(true));
- assert_eq!(deserialize(&[1u8]).ok(), Some(true));
- assert_eq!(deserialize(&[0u8]).ok(), Some(false));
- assert!((deserialize(&[0u8, 1]) as Result<bool, _>).is_err());
-
- // u8
- assert_eq!(deserialize(&[58u8]).ok(), Some(58u8));
-
- // u16
- assert_eq!(deserialize(&[0x01u8, 0x02]).ok(), Some(0x0201u16));
- assert_eq!(deserialize(&[0xABu8, 0xCD]).ok(), Some(0xCDABu16));
- assert_eq!(deserialize(&[0xA0u8, 0x0D]).ok(), Some(0xDA0u16));
- let failure16: Result<u16, _> = deserialize(&[1u8]);
- assert!(failure16.is_err());
-
- // i16
- assert_eq!(deserialize(&[0x32_u8, 0xF4]).ok(), Some(-0x0bce_i16));
- assert_eq!(deserialize(&[0xFF_u8, 0xFE]).ok(), Some(-0x0101_i16));
- assert_eq!(deserialize(&[0x00_u8, 0x00]).ok(), Some(-0_i16));
- assert_eq!(deserialize(&[0xFF_u8, 0xFA]).ok(), Some(-0x0501_i16));
-
- // u32
- assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABu32));
- assert_eq!(deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD]).ok(), Some(0xCDAB0DA0u32));
-
- let failure32: Result<u32, _> = deserialize(&[1u8, 2, 3]);
- assert!(failure32.is_err());
-
- // i32
- assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0]).ok(), Some(0xCDABi32));
- assert_eq!(deserialize(&[0xA0u8, 0x0D, 0xAB, 0x2D]).ok(), Some(0x2DAB0DA0i32));
-
- assert_eq!(deserialize(&[0, 0, 0, 0]).ok(), Some(-0_i32));
- assert_eq!(deserialize(&[0, 0, 0, 0]).ok(), Some(0_i32));
-
- assert_eq!(deserialize(&[0xFF, 0xFF, 0xFF, 0xFF]).ok(), Some(-1_i32));
- assert_eq!(deserialize(&[0xFE, 0xFF, 0xFF, 0xFF]).ok(), Some(-2_i32));
- assert_eq!(deserialize(&[0x01, 0xFF, 0xFF, 0xFF]).ok(), Some(-255_i32));
- assert_eq!(deserialize(&[0x02, 0xFF, 0xFF, 0xFF]).ok(), Some(-254_i32));
-
- let failurei32: Result<i32, _> = deserialize(&[1u8, 2, 3]);
- assert!(failurei32.is_err());
-
- // u64
- assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(), Some(0xCDABu64));
- assert_eq!(
- deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
- Some(0x99000099CDAB0DA0u64)
- );
- let failure64: Result<u64, _> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
- assert!(failure64.is_err());
-
- // i64
- assert_eq!(deserialize(&[0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]).ok(), Some(0xCDABi64));
- assert_eq!(
- deserialize(&[0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]).ok(),
- Some(-0x66ffff663254f260i64)
- );
- assert_eq!(
- deserialize(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).ok(),
- Some(-1_i64)
- );
- assert_eq!(
- deserialize(&[0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).ok(),
- Some(-2_i64)
- );
- assert_eq!(
- deserialize(&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).ok(),
- Some(-255_i64)
- );
- assert_eq!(
- deserialize(&[0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).ok(),
- Some(-254_i64)
- );
-
- let failurei64: Result<i64, _> = deserialize(&[1u8, 2, 3, 4, 5, 6, 7]);
- assert!(failurei64.is_err());
- }
-
- #[test]
- fn deserialize_vec() {
- assert_eq!(deserialize(&[3u8, 2, 3, 4]).ok(), Some(vec![2u8, 3, 4]));
- assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>, _>).is_err());
- // found by cargo fuzz
- assert!(deserialize::<Vec<u64>>(&[
- 0xff, 0xff, 0xff, 0xff, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b,
- 0x6b, 0x6b, 0xa, 0xa, 0x3a
- ])
- .is_err());
-
- test_len_is_max_vec::<u8>();
- test_len_is_max_vec::<Vec<u8>>();
- test_len_is_max_vec::<u64>();
- }
-
- fn test_len_is_max_vec<T>()
- where
- Vec<T>: Decodable,
- T: fmt::Debug,
- {
- let mut buf = Vec::new();
- buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::<T>()).unwrap();
- let err = deserialize::<Vec<T>>(&buf).unwrap_err();
- assert!(matches!(err, DeserializeError::Parse(ParseError::MissingData)));
- }
-
- #[test]
- fn deserialize_strbuf() {
- assert_eq!(
- deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
- Some("Andrew".to_string())
- );
- assert_eq!(
- deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
- Some(Cow::Borrowed("Andrew"))
- );
- }
-
- #[test]
- fn limit_read() {
- let witness = vec![vec![0u8; 3_999_999]; 2];
- let ser = serialize(&witness);
- let mut reader = io::Cursor::new(ser);
- let err = Vec::<Vec<u8>>::consensus_decode(&mut reader);
- assert!(err.is_err());
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn serialization_round_trips() {
- use secp256k1::rand::{self, Rng};
-
- macro_rules! round_trip {
- ($($val_type:ty),*) => {
- $(
- let r: $val_type = rand::rng().random();
- assert_eq!(deserialize::<$val_type>(&serialize(&r)).unwrap(), r);
- )*
- };
- }
- macro_rules! round_trip_bytes {
- ($(($val_type:ty, $data:expr)),*) => {
- $(
- rand::rng().fill(&mut $data[..]);
- assert_eq!(deserialize::<$val_type>(&serialize(&$data)).unwrap()[..], $data[..]);
- )*
- };
- }
-
- let mut data = Vec::with_capacity(256);
- let mut data64 = Vec::with_capacity(256);
- for _ in 0..10 {
- round_trip! {bool, i8, u8, i16, u16, i32, u32, i64, u64,
- (bool, i8, u16, i32), (u64, i64, u32, i32, u16, i16), (i8, u8, i16, u16, i32, u32, i64, u64),
- [u8; 2], [u8; 4], [u8; 8], [u8; 12], [u8; 16], [u8; 32]};
-
- data.clear();
- data64.clear();
- let len = rand::rng().random_range(1..256);
- data.resize(len, 0u8);
- data64.resize(len, 0u64);
- let mut arr33 = [0u8; 33];
- let mut arr16 = [0u16; 8];
- round_trip_bytes! {(Vec<u8>, data), ([u8; 33], arr33), ([u16; 8], arr16), (Vec<u64>, data64)};
- }
- }
-
- #[test]
- fn test_read_bytes_from_finite_reader() {
- let data: Vec<u8> = (0..10).collect();
-
- for chunk_size in 1..20 {
- assert_eq!(
- read_bytes_from_finite_reader(
- &mut io::Cursor::new(&data),
- ReadBytesFromFiniteReaderOpts { len: data.len(), chunk_size }
- )
- .unwrap(),
- data
- );
- }
- }
-}
diff --git a/bitcoin/src/consensus/error.rs b/bitcoin/src/consensus/error.rs
deleted file mode 100644
index f6d7ec24..00000000
--- a/bitcoin/src/consensus/error.rs
+++ /dev/null
@@ -1,290 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Consensus encoding errors.
-
-use core::convert::Infallible;
-use core::fmt;
-
-use hex::error::{InvalidCharError, OddLengthStringError};
-use internals::write_err;
-
-#[cfg(doc)]
-use super::IterReader;
-
-/// Error deserializing from a slice.
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum DeserializeError {
- /// Error parsing encoded object.
- Parse(ParseError),
- /// Data unconsumed error.
- Unconsumed,
-}
-
-impl From<Infallible> for DeserializeError {
- fn from(never: Infallible) -> Self { match never {} }
-}
-
-impl fmt::Display for DeserializeError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
- Self::Unconsumed => write!(f, "data not consumed entirely when deserializing"),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for DeserializeError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Parse(ref e) => Some(e),
- Self::Unconsumed => None,
- }
- }
-}
-
-impl From<ParseError> for DeserializeError {
- fn from(e: ParseError) -> Self { Self::Parse(e) }
-}
-
-/// Error when consensus decoding from an `[IterReader]`.
-///
-/// This is the same as a `DeserializeError` with an additional variant to return any error yielded
-/// by the inner bytes iterator.
-#[derive(Debug)]
-pub enum DecodeError<E> {
- /// Invalid consensus encoding.
- Parse(ParseError),
- /// Data unconsumed error.
- Unconsumed,
- /// Other decoding error.
- Other(E), // Yielded by the inner iterator.
-}
-
-impl<E> From<Infallible> for DecodeError<E> {
- fn from(never: Infallible) -> Self { match never {} }
-}
-
-impl<E: fmt::Debug> fmt::Display for DecodeError<E> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
- Self::Unconsumed => write!(f, "data not consumed entirely when deserializing"),
- Self::Other(ref other) => write!(f, "other decoding error: {:?}", other),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl<E: fmt::Debug + std::error::Error + 'static> std::error::Error for DecodeError<E> {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Parse(ref e) => Some(e),
- Self::Unconsumed => None,
- Self::Other(ref e) => Some(e),
- }
- }
-}
-
-/// Encoding error.
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum Error {
- /// An I/O error.
- Io(io::Error),
- /// Error parsing encoded object.
- Parse(ParseError),
-}
-
-impl From<Infallible> for Error {
- fn from(never: Infallible) -> Self { match never {} }
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Io(ref e) => write_err!(f, "I/O error"; e),
- Self::Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for Error {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Io(ref e) => Some(e),
- Self::Parse(ref e) => Some(e),
- }
- }
-}
-
-impl From<io::Error> for Error {
- fn from(e: io::Error) -> Self {
- use io::ErrorKind;
-
- match e.kind() {
- ErrorKind::UnexpectedEof => Self::Parse(ParseError::MissingData),
- _ => Self::Io(e),
- }
- }
-}
-
-impl From<ParseError> for Error {
- fn from(e: ParseError) -> Self { Self::Parse(e) }
-}
-
-/// Encoding is invalid.
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum ParseError {
- /// Missing data (early end of file or slice too short).
- MissingData, // TODO: Can we add more context?
- /// Tried to allocate an oversized vector.
- OversizedVectorAllocation {
- /// The capacity requested.
- requested: usize,
- /// The maximum capacity.
- max: usize,
- },
- /// Checksum was invalid.
- InvalidChecksum {
- /// The expected checksum.
- expected: [u8; 4],
- /// The invalid checksum.
- actual: [u8; 4],
- },
- /// CompactSize was encoded in a non-minimal way.
- NonMinimalCompactSize,
- /// CompactSize value exceeds the maximum allowed size.
- OversizedCompactSize,
- /// Parsing error.
- ParseFailed(&'static str),
- /// Unsupported SegWit flag.
- UnsupportedSegwitFlag(u8),
- /// Witness decoding error.
- Witness(io::ReadError<primitives::witness::WitnessDecoderError>),
-}
-
-impl From<Infallible> for ParseError {
- fn from(never: Infallible) -> Self { match never {} }
-}
-
-impl fmt::Display for ParseError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::MissingData => write!(f, "missing data (early end of file or slice too short)"),
- Self::OversizedVectorAllocation { requested: ref r, max: ref m } =>
- write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m),
- Self::InvalidChecksum { expected: ref e, actual: ref a } => write!(
- f,
- "invalid checksum: expected {:02x}{:02x}{:02x}{:02x}, actual {:02x}{:02x}{:02x}{:02x}",
- e[0], e[1], e[2], e[3], a[0], a[1], a[2], a[3],
- ),
- Self::NonMinimalCompactSize => write!(f, "non-minimal compact size"),
- Self::OversizedCompactSize => write!(f, "value exceeds the maximum allowed compact size"),
- Self::ParseFailed(ref s) => write!(f, "parse failed: {}", s),
- Self::UnsupportedSegwitFlag(ref swflag) =>
- write!(f, "unsupported SegWit version: {}", swflag),
- Self::Witness(ref e) => write_err!(f, "witness"; e),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for ParseError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Witness(ref e) => Some(e),
- Self::MissingData
- | Self::OversizedVectorAllocation { .. }
- | Self::InvalidChecksum { .. }
- | Self::NonMinimalCompactSize
- | Self::OversizedCompactSize
- | Self::ParseFailed(_)
- | Self::UnsupportedSegwitFlag(_) => None,
- }
- }
-}
-
-/// Hex deserialization error.
-#[derive(Debug)]
-pub enum FromHexError {
- /// Purported hex string had odd length.
- OddLengthString(OddLengthStringError),
- /// Decoding error.
- Decode(DecodeError<InvalidCharError>),
-}
-
-impl fmt::Display for FromHexError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::OddLengthString(ref e) =>
- write_err!(f, "odd length, failed to create bytes from hex"; e),
- Self::Decode(ref e) => write_err!(f, "decoding error"; e),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for FromHexError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::OddLengthString(ref e) => Some(e),
- Self::Decode(ref e) => Some(e),
- }
- }
-}
-
-impl From<OddLengthStringError> for FromHexError {
- #[inline]
- fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) }
-}
-
-/// Constructs a new `Error::ParseFailed` error.
-// This whole variant should go away because of the inner string.
-pub(crate) fn parse_failed_error(msg: &'static str) -> Error {
- Error::Parse(ParseError::ParseFailed(msg))
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn invalid_checksum_display() {
- let e = ParseError::InvalidChecksum {
- expected: [0xde, 0xad, 0xbe, 0xef],
- actual: [0xca, 0xfe, 0xba, 0xbe],
- };
-
- let want = "invalid checksum: expected deadbeef, actual cafebabe";
- let got = format!("{}", e);
- assert_eq!(got, want);
- }
-
- #[test]
- fn invalid_checksum_display_expected_leading_zeros() {
- let e = ParseError::InvalidChecksum {
- expected: [0x00, 0x00, 0x00, 0x0f],
- actual: [0xca, 0xfe, 0xba, 0xbe],
- };
-
- let want = "invalid checksum: expected 0000000f, actual cafebabe";
- let got = format!("{}", e);
- assert_eq!(got, want);
- }
-
- #[test]
- fn invalid_checksum_display_actual_leading_zeros() {
- let e = ParseError::InvalidChecksum {
- expected: [0xde, 0xad, 0xbe, 0xef],
- actual: [0x00, 0x00, 0x00, 0x0e],
- };
-
- let want = "invalid checksum: expected deadbeef, actual 0000000e";
- let got = format!("{}", e);
- assert_eq!(got, want);
- }
-}
diff --git a/bitcoin/src/consensus/mod.rs b/bitcoin/src/consensus/mod.rs
deleted file mode 100644
index 49ffe4ef..00000000
--- a/bitcoin/src/consensus/mod.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Bitcoin consensus.
-//!
-//! This module defines structures, functions, and traits that are needed to
-//! conform to Bitcoin consensus.
-
-pub mod encode;
-pub mod error;
-#[cfg(feature = "serde")]
-pub mod serde;
-#[cfg(kani)]
-mod verification;
-
-use core::fmt;
-
-use io::{BufRead, Read};
-
-use crate::consensus;
-
-#[rustfmt::skip] // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
- encode::{deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt},
-};
-pub(crate) use self::error::parse_failed_error;
-#[doc(no_inline)]
-pub use self::error::{DecodeError, DeserializeError, Error, FromHexError, ParseError};
-
-struct IterReader<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> {
- iterator: core::iter::Fuse<I>,
- buf: Option<u8>,
- error: Option<E>,
-}
-
-impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> IterReader<E, I> {
- pub(crate) fn new(iterator: I) -> Self {
- Self { iterator: iterator.fuse(), buf: None, error: None }
- }
-
- fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
- let result = T::consensus_decode(&mut self);
- match (result, self.error) {
- (Ok(_), None) if self.iterator.next().is_some() => Err(DecodeError::Unconsumed),
- (Ok(value), None) => Ok(value),
- (Ok(_), Some(error)) => panic!("{} silently ate the error: {:?}", core::any::type_name::<T>(), error),
-
- (Err(consensus::encode::Error::Io(io_error)), Some(de_error)) if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() => Err(DecodeError::Other(de_error)),
- (Err(consensus::encode::Error::Parse(parse_error)), None) => Err(DecodeError::Parse(parse_error)),
- (Err(consensus::encode::Error::Io(io_error)), de_error) => panic!("unexpected I/O error {:?} returned from {}::consensus_decode(), deserialization error: {:?}", io_error, core::any::type_name::<T>(), de_error),
- (Err(consensus_error), Some(de_error)) => panic!("{} should've returned `Other` I/O error because of deserialization error {:?} but it returned consensus error {:?} instead", core::any::type_name::<T>(), de_error, consensus_error),
- }
- }
-}
-
-impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> Read for IterReader<E, I> {
- fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
- let mut count = 0;
- if buf.is_empty() {
- return Ok(0);
- }
-
- if let Some(first) = self.buf.take() {
- buf[0] = first;
- buf = &mut buf[1..];
- count += 1;
- }
- for (dst, src) in buf.iter_mut().zip(&mut self.iterator) {
- match src {
- Ok(byte) => *dst = byte,
- Err(error) => {
- self.error = Some(error);
- return Err(io::ErrorKind::Other.into());
- }
- }
- // bounded by the length of buf
- count += 1;
- }
- Ok(count)
- }
-}
-
-impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> BufRead for IterReader<E, I> {
- fn fill_buf(&mut self) -> Result<&[u8], io::Error> {
- // matching on reference rather than using `ref` confuses borrow checker
- if let Some(ref byte) = self.buf {
- Ok(core::slice::from_ref(byte))
- } else {
- match self.iterator.next() {
- Some(Ok(byte)) => {
- self.buf = Some(byte);
- Ok(core::slice::from_ref(self.buf.as_ref().expect("we've just filled it")))
- }
- Some(Err(error)) => {
- self.error = Some(error);
- Err(io::ErrorKind::Other.into())
- }
- None => Ok(&[]),
- }
- }
- }
-
- fn consume(&mut self, len: usize) {
- debug_assert!(len <= 1);
- if len > 0 {
- self.buf = None;
- }
- }
-}
diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs
deleted file mode 100644
index 08f5cead..00000000
--- a/bitcoin/src/consensus/serde.rs
+++ /dev/null
@@ -1,488 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Serde serialization via consensus encoding
-//!
-//! This provides functions for (de)serializing any type as consensus-encoded bytes.
-//! For human-readable formats it serializes as a string with a consumer-supplied encoding, for
-//! binary formats it serializes as a sequence of bytes (not `serialize_bytes` to avoid allocations).
-//!
-//! The string encoding has to be specified using a marker type implementing the encoding strategy.
-//! This crate provides hex encoding via `Hex<Upper>` and `Hex<Lower>`
-
-use core::fmt;
-use core::marker::PhantomData;
-
-use io::Write;
-use serde::de::{SeqAccess, Unexpected, Visitor};
-use serde::ser::SerializeSeq;
-use serde::{Deserializer, Serializer};
-
-use super::{Decodable, Encodable, ParseError};
-use crate::consensus::{DecodeError, IterReader};
-
-/// Hex-encoding strategy
-pub struct Hex<Case = hex::Lower>(PhantomData<Case>)
-where
- Case: hex::Case;
-
-impl<C: hex::Case> Default for Hex<C> {
- fn default() -> Self { Self(Default::default()) }
-}
-
-impl<C: hex::Case> ByteEncoder for Hex<C> {
- type Encoder = hex::Encoder<C>;
-}
-
-/// Implements hex encoding.
-pub mod hex {
- use core::fmt;
- use core::marker::PhantomData;
-
- use hex::buf_encoder::BufEncoder;
-
- /// Marker for upper/lower case type-level flags ("type-level enum").
- ///
- /// You may use this trait in bounds only.
- pub trait Case: sealed::Case {}
- impl<T: sealed::Case> Case for T {}
-
- /// Marker for using lower-case hex encoding.
- pub enum Lower {}
- /// Marker for using upper-case hex encoding.
- pub enum Upper {}
-
- mod sealed {
- pub trait Case {
- /// Internal detail, don't depend on it!!!
- const INTERNAL_CASE: hex::Case;
- }
-
- impl Case for super::Lower {
- const INTERNAL_CASE: hex::Case = hex::Case::Lower;
- }
-
- impl Case for super::Upper {
- const INTERNAL_CASE: hex::Case = hex::Case::Upper;
- }
- }
-
- // We just guessed at a reasonably sane value.
- const HEX_BUF_SIZE: usize = 512;
-
- /// Hex byte encoder.
- // We wrap `BufEncoder` to not leak internal representation.
- pub struct Encoder<C: Case>(BufEncoder<{ HEX_BUF_SIZE }>, PhantomData<C>);
-
- impl<C: Case> From<super::Hex<C>> for Encoder<C> {
- fn from(_: super::Hex<C>) -> Self {
- Self(BufEncoder::new(C::INTERNAL_CASE), Default::default())
- }
- }
-
- impl<C: Case> super::EncodeBytes for Encoder<C> {
- fn encode_chunk<W: fmt::Write>(&mut self, writer: &mut W, mut bytes: &[u8]) -> fmt::Result {
- while !bytes.is_empty() {
- if self.0.is_full() {
- self.flush(writer)?;
- }
- bytes = self.0.put_bytes_min(bytes);
- }
- Ok(())
- }
-
- fn flush<W: fmt::Write>(&mut self, writer: &mut W) -> fmt::Result {
- writer.write_str(self.0.as_str())?;
- self.0.clear();
- Ok(())
- }
- }
-
- // Newtypes to hide internal details.
-
- /// Error returned when a hex string decoder can't be created.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct DecodeInitError(hex::OddLengthStringError);
-
- /// Error returned when a hex string contains invalid characters.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct DecodeError(hex::InvalidCharError);
-
- /// Hex decoder state.
- pub struct Decoder<'a>(hex::HexSliceToBytesIter<'a>);
-
- impl<'a> Decoder<'a> {
- fn new(s: &'a str) -> Result<Self, DecodeInitError> {
- match hex::HexSliceToBytesIter::new(s) {
- Ok(iter) => Ok(Decoder(iter)),
- Err(error) => Err(DecodeInitError(error)),
- }
- }
- }
-
- impl Iterator for Decoder<'_> {
- type Item = Result<u8, DecodeError>;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(|result| result.map_err(DecodeError))
- }
- }
-
- impl<'a, C: Case> super::ByteDecoder<'a> for super::Hex<C> {
- type InitError = DecodeInitError;
- type DecodeError = DecodeError;
- type Decoder = Decoder<'a>;
-
- fn from_str(s: &'a str) -> Result<Self::Decoder, Self::InitError> { Decoder::new(s) }
- }
-
- impl super::IntoDeError for DecodeInitError {
- fn into_de_error<E: serde::de::Error>(self) -> E { serde::de::Error::custom(self.0) }
- }
-
- impl super::IntoDeError for DecodeError {
- fn into_de_error<E: serde::de::Error>(self) -> E { serde::de::Error::custom(self.0) }
- }
-}
-
-struct DisplayWrapper<'a, T: 'a + Encodable, E>(&'a T, PhantomData<E>);
-
-impl<'a, T: 'a + Encodable, E: ByteEncoder> fmt::Display for DisplayWrapper<'a, T, E> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut writer = IoWrapper::<'_, _, E::Encoder>::new(f, E::default().into());
- self.0.consensus_encode(&mut writer).map_err(|error| {
- #[cfg(debug_assertions)]
- {
- if error.kind() != io::ErrorKind::Other
- || error.get_ref().is_some()
- || !writer.writer.was_error
- {
- panic!(
- "{} returned an unexpected error: {:?}",
- core::any::type_name::<T>(),
- error
- );
- }
- }
- #[cfg(not(debug_assertions))]
- let _ = error;
- fmt::Error
- })?;
- let result = writer.actually_flush();
- if result.is_err() {
- writer.writer.assert_was_error::<E>();
- }
- result
- }
-}
-
-struct ErrorTrackingWriter<W: fmt::Write> {
- writer: W,
- #[cfg(debug_assertions)]
- was_error: bool,
-}
-
-impl<W: fmt::Write> ErrorTrackingWriter<W> {
- fn new(writer: W) -> Self {
- Self {
- writer,
- #[cfg(debug_assertions)]
- was_error: false,
- }
- }
-
- #[track_caller]
- fn assert_no_error(&self, fun: &str) {
- #[cfg(debug_assertions)]
- {
- if self.was_error {
- panic!("`{}` called on errored writer", fun);
- }
- }
- #[cfg(not(debug_assertions))]
- let _ = fun;
- }
-
- fn assert_was_error<Offender>(&self) {
- #[cfg(debug_assertions)]
- {
- if !self.was_error {
- panic!("{} returned an error unexpectedly", core::any::type_name::<Offender>());
- }
- }
- }
-
- fn set_error(&mut self, was: bool) {
- #[cfg(debug_assertions)]
- {
- self.was_error |= was;
- }
- #[cfg(not(debug_assertions))]
- let _ = was;
- }
-
- fn check_err<T, E>(&mut self, result: Result<T, E>) -> Result<T, E> {
- self.set_error(result.is_err());
- result
- }
-}
-
-impl<W: fmt::Write> fmt::Write for ErrorTrackingWriter<W> {
- fn write_str(&mut self, s: &str) -> fmt::Result {
- self.assert_no_error("write_str");
- let result = self.writer.write_str(s);
- self.check_err(result)
- }
-
- fn write_char(&mut self, c: char) -> fmt::Result {
- self.assert_no_error("write_char");
- let result = self.writer.write_char(c);
- self.check_err(result)
- }
-}
-
-struct IoWrapper<'a, W: fmt::Write, E: EncodeBytes> {
- writer: ErrorTrackingWriter<&'a mut W>,
- encoder: E,
-}
-
-impl<'a, W: fmt::Write, E: EncodeBytes> IoWrapper<'a, W, E> {
- fn new(writer: &'a mut W, encoder: E) -> Self {
- IoWrapper { writer: ErrorTrackingWriter::new(writer), encoder }
- }
-
- fn actually_flush(&mut self) -> fmt::Result { self.encoder.flush(&mut self.writer) }
-}
-
-impl<W: fmt::Write, E: EncodeBytes> Write for IoWrapper<'_, W, E> {
- fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
- match self.encoder.encode_chunk(&mut self.writer, bytes) {
- Ok(()) => Ok(bytes.len()),
- Err(fmt::Error) => {
- self.writer.assert_was_error::<E>();
- Err(io::Error::from(io::ErrorKind::Other))
- }
- }
- }
- // we intentionally ignore flushes because we will do a single flush at the end.
- fn flush(&mut self) -> io::Result<()> { Ok(()) }
-}
-
-/// Provides an instance of byte-to-string encoder.
-///
-/// This is basically a type constructor used in places where value arguments are not accepted.
-/// Such as the generic `serialize`.
-pub trait ByteEncoder: Default {
- /// The encoder state.
- type Encoder: EncodeBytes + From<Self>;
-}
-
-/// Transforms given bytes and writes to the writer.
-///
-/// The encoder is allowed to be buffered (and probably should be).
-/// The design passing writer each time bypasses the need for GAT.
-pub trait EncodeBytes {
- /// Transform the provided slice and write to the writer.
- ///
- /// This is similar to the `write_all` method on `io::Write`.
- fn encode_chunk<W: fmt::Write>(&mut self, writer: &mut W, bytes: &[u8]) -> fmt::Result;
-
- /// Write data in buffer (if any) to the writer.
- fn flush<W: fmt::Write>(&mut self, writer: &mut W) -> fmt::Result;
-}
-
-/// Provides an instance of string-to-byte decoder.
-///
-/// This is basically a type constructor used in places where value arguments are not accepted.
-/// Such as the generic `serialize`.
-pub trait ByteDecoder<'a> {
- /// Error returned when decoder can't be created.
- ///
- /// This is typically returned when string length is invalid.
- type InitError: IntoDeError + fmt::Debug;
-
- /// Error returned when decoding fails.
- ///
- /// This is typically returned when the input string contains malformed chars.
- type DecodeError: IntoDeError + fmt::Debug;
-
- /// The decoder state.
- type Decoder: Iterator<Item = Result<u8, Self::DecodeError>>;
-
- /// Constructs a new decoder from string.
- fn from_str(s: &'a str) -> Result<Self::Decoder, Self::InitError>;
-}
-
-/// Converts error into a type implementing `serde::de::Error`
-pub trait IntoDeError {
- /// Performs the conversion.
- fn into_de_error<E: serde::de::Error>(self) -> E;
-}
-
-struct BinWriter<S: SerializeSeq> {
- serializer: S,
- error: Option<S::Error>,
-}
-
-impl<S: SerializeSeq> Write for BinWriter<S> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.write_all(buf).map(|_| buf.len()) }
-
- fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
- for byte in buf {
- if let Err(error) = self.serializer.serialize_element(byte) {
- self.error = Some(error);
- return Err(io::ErrorKind::Other.into());
- }
- }
- Ok(())
- }
-
- fn flush(&mut self) -> io::Result<()> { Ok(()) }
-}
-
-struct DisplayExpected<D: fmt::Display>(D);
-
-impl<D: fmt::Display> serde::de::Expected for DisplayExpected<D> {
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.0, formatter)
- }
-}
-
-// not a trait impl because we panic on some variants
-fn consensus_error_into_serde<E: serde::de::Error>(error: ParseError) -> E {
- match error {
- ParseError::MissingData => E::custom("missing data (early end of file or slice too short)"),
- ParseError::OversizedVectorAllocation { requested, max } => E::custom(format_args!(
- "the requested allocation of {} items exceeds maximum of {}",
- requested, max
- )),
- ParseError::InvalidChecksum { expected, actual } => E::invalid_value(
- Unexpected::Bytes(&actual),
- &DisplayExpected(format_args!(
- "checksum {:02x}{:02x}{:02x}{:02x}",
- expected[0], expected[1], expected[2], expected[3]
- )),
- ),
- ParseError::NonMinimalCompactSize =>
- E::custom(format_args!("compact size was not encoded minimally")),
- ParseError::OversizedCompactSize =>
- E::custom(format_args!("compact size value exceeds the maximum allowed size")),
- ParseError::ParseFailed(msg) => E::custom(msg),
- ParseError::UnsupportedSegwitFlag(flag) =>
- E::invalid_value(Unexpected::Unsigned(flag.into()), &"segwit version 1 flag"),
- ParseError::Witness(e) => E::custom(e),
- }
-}
-
-impl<E> DecodeError<E>
-where
- E: serde::de::Error,
-{
- fn unify(self) -> E {
- match self {
- Self::Other(error) => error,
- Self::Unconsumed => E::custom(format_args!("got more bytes than expected")),
- Self::Parse(e) => consensus_error_into_serde(e),
- }
- }
-}
-
-impl<E> IntoDeError for DecodeError<E>
-where
- E: IntoDeError,
-{
- fn into_de_error<DE: serde::de::Error>(self) -> DE {
- match self {
- Self::Other(error) => error.into_de_error(),
- Self::Unconsumed => DE::custom(format_args!("got more bytes than expected")),
- Self::Parse(e) => consensus_error_into_serde(e),
- }
- }
-}
-
-/// Helper for `#[serde(with = "")]`.
-pub struct With<E>(PhantomData<E>);
-
-impl<E> With<E> {
- /// Serializes the value as consensus-encoded
- pub fn serialize<T: Encodable, S: Serializer>(
- value: &T,
- serializer: S,
- ) -> Result<S::Ok, S::Error>
- where
- E: ByteEncoder,
- {
- if serializer.is_human_readable() {
- serializer.collect_str(&DisplayWrapper::<'_, _, E>(value, Default::default()))
- } else {
- let serializer = serializer.serialize_seq(None)?;
- let mut writer = BinWriter { serializer, error: None };
-
- let result = value.consensus_encode(&mut writer);
- match (result, writer.error) {
- (Ok(_), None) => writer.serializer.end(),
- (Ok(_), Some(error)) =>
- panic!("{} silently ate an I/O error: {:?}", core::any::type_name::<T>(), error),
- (Err(io_error), Some(ser_error))
- if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() =>
- Err(ser_error),
- (Err(io_error), ser_error) => panic!(
- "{} returned an unexpected I/O error: {:?} serialization error: {:?}",
- core::any::type_name::<T>(),
- io_error,
- ser_error
- ),
- }
- }
- }
-
- /// Deserializes the value as consensus-encoded
- pub fn deserialize<'d, T: Decodable, D: Deserializer<'d>>(
- deserializer: D,
- ) -> Result<T, D::Error>
- where
- for<'a> E: ByteDecoder<'a>,
- {
- if deserializer.is_human_readable() {
- deserializer.deserialize_str(HRVisitor::<_, E>(Default::default()))
- } else {
- deserializer.deserialize_seq(BinVisitor(Default::default()))
- }
- }
-}
-
-struct HRVisitor<T: Decodable, D: for<'a> ByteDecoder<'a>>(PhantomData<fn() -> (T, D)>);
-
-impl<T: Decodable, D: for<'a> ByteDecoder<'a>> Visitor<'_> for HRVisitor<T, D> {
- type Value = T;
-
- fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str("bytes encoded as a hex string")
- }
-
- fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<T, E> {
- let decoder = D::from_str(s).map_err(IntoDeError::into_de_error)?;
- IterReader::new(decoder).decode().map_err(IntoDeError::into_de_error)
- }
-}
-
-struct BinVisitor<T: Decodable>(PhantomData<fn() -> T>);
-
-impl<'de, T: Decodable> Visitor<'de> for BinVisitor<T> {
- type Value = T;
-
- fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str("a sequence of bytes")
- }
-
- fn visit_seq<S: SeqAccess<'de>>(self, s: S) -> Result<T, S::Error> {
- IterReader::new(SeqIterator(s, Default::default())).decode().map_err(DecodeError::unify)
- }
-}
-
-struct SeqIterator<'a, S: serde::de::SeqAccess<'a>>(S, PhantomData<&'a ()>);
-
-impl<'a, S: serde::de::SeqAccess<'a>> Iterator for SeqIterator<'a, S> {
- type Item = Result<u8, S::Error>;
-
- fn next(&mut self) -> Option<Self::Item> { self.0.next_element::<u8>().transpose() }
-}
diff --git a/bitcoin/src/consensus/verification.rs b/bitcoin/src/consensus/verification.rs
deleted file mode 100644
index ffa469a2..00000000
--- a/bitcoin/src/consensus/verification.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-use crate::consensus::encode::{ReadExt, WriteExt, MAX_COMPACT_SIZE};
-use crate::consensus::{Error, ParseError};
-use crate::io::Cursor;
-
-#[kani::unwind(10)] // Unwind recursion for read/write operations
-#[kani::proof]
-fn check_compact_size_roundtrip() {
- let x: u32 = kani::any();
- kani::assume(x <= MAX_COMPACT_SIZE as u32);
- let mut bytes = [0u8; 9];
- let mut cursor = Cursor::new(&mut bytes[..]);
- cursor.emit_compact_size(x).unwrap();
- cursor.set_position(0);
- let y = cursor.read_compact_size().unwrap();
- assert_eq!(u64::from(x), y);
-}
-
-#[kani::unwind(10)]
-#[kani::proof]
-fn check_oversized_compact_size_is_rejected() {
- let x: u64 = kani::any();
- kani::assume(x > MAX_COMPACT_SIZE as u64);
- let mut bytes = [0u8; 9];
- let mut cursor = Cursor::new(&mut bytes[..]);
- cursor.emit_compact_size(x).unwrap();
- cursor.set_position(0);
- assert!(matches!(
- cursor.read_compact_size(),
- Err(Error::Parse(ParseError::OversizedCompactSize))
- ));
-}
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 56e8811a..8d08d9da 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -119,7 +119,6 @@ pub mod ext {
pub mod address;
pub mod bip158;
pub mod blockdata;
-pub mod consensus;
#[cfg(feature = "bitcoinconsensus")]
pub mod consensus_validation;
// Private until we either make this a crate or flatten it - still to be decided.
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.