diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index c4dad4f76..cf427199f 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -14,132 +14,29 @@ //! scripts come with an opcode decode, hashes are big-endian, numbers are //! typically big-endian decimals, etc.) -use core::{fmt, mem}; +use core::mem; use hashes::{sha256, sha256d, GeneralHash, Hash}; -use hex::error::{InvalidCharError, OddLengthStringError}; -use internals::{compact_size, write_err, ToU64}; +use hex::DisplayHex as _; +use internals::{compact_size, ToU64}; use io::{BufRead, Cursor, Read, Write}; +use super::IterReader; use crate::bip152::{PrefilledTransaction, ShortId}; use crate::bip158::{FilterHash, FilterHeader}; use crate::block::{self, BlockHash}; -use crate::consensus::{DecodeError, IterReader}; use crate::merkle_tree::TxMerkleNode; #[cfg(feature = "std")] use crate::p2p::{ address::{AddrV2Message, Address}, message_blockdata::Inventory, }; -use crate::prelude::{rc, sync, Box, Cow, DisplayHex, String, Vec}; +use crate::prelude::{rc, sync, Box, Cow, String, Vec}; use crate::taproot::TapLeafHash; use crate::transaction::{Transaction, TxIn, TxOut}; -/// Encoding error. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// And I/O error. - Io(io::Error), - /// 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], - }, - /// VarInt was encoded in a non-minimal way. - NonMinimalVarInt, - /// Parsing error. - ParseFailed(&'static str), - /// Unsupported Segwit flag. - UnsupportedSegwitFlag(u8), -} - -internals::impl_from_infallible!(Error); - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - - match *self { - Io(ref e) => write_err!(f, "IO error"; e), - OversizedVectorAllocation { requested: ref r, max: ref m } => - write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m), - InvalidChecksum { expected: ref e, actual: ref a } => - write!(f, "invalid checksum: expected {:x}, actual {:x}", e.as_hex(), a.as_hex()), - NonMinimalVarInt => write!(f, "non-minimal varint"), - ParseFailed(ref s) => write!(f, "parse failed: {}", s), - UnsupportedSegwitFlag(ref swflag) => - write!(f, "unsupported segwit version: {}", swflag), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use Error::*; - - match self { - Io(e) => Some(e), - OversizedVectorAllocation { .. } - | InvalidChecksum { .. } - | NonMinimalVarInt - | ParseFailed(_) - | UnsupportedSegwitFlag(_) => None, - } - } -} - -impl From for Error { - fn from(error: io::Error) -> Self { Error::Io(error) } -} - -/// Hex deserialization error. -#[derive(Debug)] -pub enum FromHexError { - /// Purported hex string had odd length. - OddLengthString(OddLengthStringError), - /// Decoding error. - Decode(DecodeError), -} - -impl fmt::Display for FromHexError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use FromHexError::*; - - match *self { - OddLengthString(ref e) => - write_err!(f, "odd length, failed to create bytes from hex"; e), - 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)> { - use FromHexError::*; - - match *self { - OddLengthString(ref e) => Some(e), - Decode(ref e) => Some(e), - } - } -} - -impl From for FromHexError { - #[inline] - fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) } -} +#[rustfmt::skip] // Keep public re-exports separate. +pub use super::{Error, FromHexError}; /// Encodes an object into a vector. pub fn serialize(data: &T) -> Vec { @@ -859,6 +756,7 @@ impl Decodable for TapLeafHash { #[cfg(test)] mod tests { + use core::fmt; use core::mem::discriminant; use super::*; diff --git a/bitcoin/src/consensus/error.rs b/bitcoin/src/consensus/error.rs new file mode 100644 index 000000000..181b8d310 --- /dev/null +++ b/bitcoin/src/consensus/error.rs @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Consensus encoding errors. + +use core::fmt; + +use hex::error::{InvalidCharError, OddLengthStringError}; +use hex::DisplayHex as _; +use internals::write_err; + +#[cfg(doc)] +use super::IterReader; + +/// Error when consensus decoding from an `[IterReader]`. +#[derive(Debug)] +pub enum DecodeError { + /// Attempted to decode an object from an iterator that yielded too many bytes. + TooManyBytes, + /// Invalid consensus encoding. + Consensus(Error), + /// Other decoding error. + Other(E), +} + +internals::impl_from_infallible!(DecodeError); + +impl fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DecodeError::*; + + match *self { + TooManyBytes => + write!(f, "attempted to decode object from an iterator that yielded too many bytes"), + Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e), + Other(ref other) => write!(f, "other decoding error: {:?}", other), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DecodeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DecodeError::*; + + match *self { + TooManyBytes => None, + Consensus(ref e) => Some(e), + Other(_) => None, // TODO: Is this correct? + } + } +} + +/// Encoding error. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// And I/O error. + Io(io::Error), + /// 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], + }, + /// VarInt was encoded in a non-minimal way. + NonMinimalVarInt, + /// Parsing error. + ParseFailed(&'static str), + /// Unsupported Segwit flag. + UnsupportedSegwitFlag(u8), +} + +internals::impl_from_infallible!(Error); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Error::*; + + match *self { + Io(ref e) => write_err!(f, "IO error"; e), + OversizedVectorAllocation { requested: ref r, max: ref m } => + write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m), + InvalidChecksum { expected: ref e, actual: ref a } => + write!(f, "invalid checksum: expected {:x}, actual {:x}", e.as_hex(), a.as_hex()), + NonMinimalVarInt => write!(f, "non-minimal varint"), + ParseFailed(ref s) => write!(f, "parse failed: {}", s), + UnsupportedSegwitFlag(ref swflag) => + write!(f, "unsupported segwit version: {}", swflag), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use Error::*; + + match self { + Io(e) => Some(e), + OversizedVectorAllocation { .. } + | InvalidChecksum { .. } + | NonMinimalVarInt + | ParseFailed(_) + | UnsupportedSegwitFlag(_) => None, + } + } +} + +impl From for Error { + fn from(error: io::Error) -> Self { Error::Io(error) } +} + +/// Hex deserialization error. +#[derive(Debug)] +pub enum FromHexError { + /// Purported hex string had odd length. + OddLengthString(OddLengthStringError), + /// Decoding error. + Decode(DecodeError), +} + +impl fmt::Display for FromHexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use FromHexError::*; + + match *self { + OddLengthString(ref e) => + write_err!(f, "odd length, failed to create bytes from hex"; e), + 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)> { + use FromHexError::*; + + match *self { + OddLengthString(ref e) => Some(e), + Decode(ref e) => Some(e), + } + } +} + +impl From for FromHexError { + #[inline] + fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) } +} diff --git a/bitcoin/src/consensus/mod.rs b/bitcoin/src/consensus/mod.rs index 1150af014..a12b22e13 100644 --- a/bitcoin/src/consensus/mod.rs +++ b/bitcoin/src/consensus/mod.rs @@ -6,12 +6,12 @@ //! conform to Bitcoin consensus. pub mod encode; +mod error; #[cfg(feature = "serde")] pub mod serde; use core::fmt; -use internals::write_err; use io::{BufRead, Read}; use crate::consensus; @@ -20,6 +20,7 @@ use crate::consensus; #[doc(inline)] pub use self::{ encode::{deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt}, + error::{Error, FromHexError, DecodeError}, }; struct IterReader>> { @@ -102,42 +103,3 @@ impl>> BufRead for IterReader { - /// Attempted to decode an object from an iterator that yielded too many bytes. - TooManyBytes, - /// Invalid consensus encoding. - Consensus(consensus::encode::Error), - /// Other decoding error. - Other(E), -} - -internals::impl_from_infallible!(DecodeError); - -impl fmt::Display for DecodeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use DecodeError::*; - - match *self { - TooManyBytes => - write!(f, "attempted to decode object from an iterator that yielded too many bytes"), - Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e), - Other(ref other) => write!(f, "other decoding error: {:?}", other), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for DecodeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use DecodeError::*; - - match *self { - TooManyBytes => None, - Consensus(ref e) => Some(e), - Other(_) => None, // TODO: Is this correct? - } - } -}