consensus_encoding: composite error types
What changed, and why it matters
This commit is a routine internal refactoring of how composite decoders report errors in the rust-bitcoin library. It replaces a generic error-mapping scheme with fixed error types for decoders that combine 2, 3, 4, or 6 sub-decoders. There is no indication this fixes a security bug or changes how data is parsed; it only changes the shape of error values returned when parsing fails.
No security action required. Review as normal code-quality/maintenance change; ensure downstream consumers of the new public error types are updated if they depend on the old generic API.
Security signals we found
No security-relevant keywords in commit title or message
No changes to input validation, length checks, or cryptographic operations
Refactoring only: error-type plumbing in decoder composition
No advisory, CVE, or vendor security disclosure referenced
Evidence from the diff
The patch removes the Err type parameter and From trait bounds from Decoder2/3/4/6 and introduces concrete Decoder2Error, Decoder3Error, Decoder4Error, and Decoder6Error enums. Call sites in primitives/src/block.rs and primitives/src/transaction.rs are updated to map these composite errors into their own domain-specific error types via explicit match arms rather than From impls. Tests are updated to reflect the new error types. No parsing logic, bounds checks, or consensus rules are altered.
Changed components
consensus_encoding/src/decode/decoders.rsconsensus_encoding/src/lib.rsconsensus_encoding/tests/composition.rsconsensus_encoding/tests/encode.rsprimitives/src/block.rsprimitives/src/transaction.rsInspect captured patch +352 / −233
diff --git a/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs
index 3c8d14e8..502696bb 100644
--- a/consensus_encoding/src/decode/decoders.rs
+++ b/consensus_encoding/src/decode/decoders.rs
@@ -6,10 +6,8 @@
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::convert::Infallible;
-use core::marker::PhantomData;
use core::{fmt, mem};
-#[cfg(feature = "alloc")]
use internals::write_err;
#[cfg(feature = "alloc")]
@@ -278,14 +276,12 @@ impl<const N: usize> Decoder for ArrayDecoder<N> {
}
/// A decoder which wraps two inner decoders and returns the output of both.
-/// The error types of the inner decoders are mapped to a common type.
-pub struct Decoder2<A, B, Err>
+pub struct Decoder2<A, B>
where
A: Decoder,
B: Decoder,
{
state: Decoder2State<A, B>,
- _error: PhantomData<Err>,
}
enum Decoder2State<A: Decoder, B: Decoder> {
@@ -297,31 +293,28 @@ enum Decoder2State<A: Decoder, B: Decoder> {
Errored,
}
-impl<A, B, Err> Decoder2<A, B, Err>
+impl<A, B> Decoder2<A, B>
where
A: Decoder,
B: Decoder,
{
/// Constructs a new composite decoder.
- pub fn new(first: A, second: B) -> Self {
- Self { state: Decoder2State::First(first, second), _error: PhantomData }
- }
+ pub fn new(first: A, second: B) -> Self { Self { state: Decoder2State::First(first, second) } }
}
-impl<A, B, Err> Decoder for Decoder2<A, B, Err>
+impl<A, B> Decoder for Decoder2<A, B>
where
A: Decoder,
B: Decoder,
- Err: From<A::Error> + From<B::Error>,
{
type Output = (A::Output, B::Output);
- type Error = Err;
+ type Error = Decoder2Error<A::Error, B::Error>;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
loop {
match &mut self.state {
Decoder2State::First(first_decoder, _) => {
- if first_decoder.push_bytes(bytes).map_err(Err::from)? {
+ if first_decoder.push_bytes(bytes).map_err(Decoder2Error::First)? {
// First decoder wants more data.
return Ok(true);
}
@@ -331,7 +324,7 @@ where
// remains in an Errored state.
match mem::replace(&mut self.state, Decoder2State::Errored) {
Decoder2State::First(first, second) => {
- let first_result = first.end()?;
+ let first_result = first.end().map_err(Decoder2Error::First)?;
self.state = Decoder2State::Second(first_result, second);
}
_ => unreachable!("we know we're in First state"),
@@ -340,7 +333,7 @@ where
Decoder2State::Second(_, second_decoder) => {
return second_decoder.push_bytes(bytes).map_err(|error| {
self.state = Decoder2State::Errored;
- Err::from(error)
+ Decoder2Error::Second(error)
});
}
Decoder2State::Errored => {
@@ -357,12 +350,12 @@ where
// This branch is most likely an error since the decoder
// never got to the second one. But letting the error bubble
// up naturally from the child decoders.
- let first_result = first_decoder.end().map_err(Err::from)?;
- let second_result = second_decoder.end().map_err(Err::from)?;
+ let first_result = first_decoder.end().map_err(Decoder2Error::First)?;
+ let second_result = second_decoder.end().map_err(Decoder2Error::Second)?;
Ok((first_result, second_result))
}
Decoder2State::Second(first_result, second_decoder) => {
- let second_result = second_decoder.end().map_err(Err::from)?;
+ let second_result = second_decoder.end().map_err(Decoder2Error::Second)?;
Ok((first_result, second_result))
}
Decoder2State::Errored => {
@@ -383,48 +376,54 @@ where
}
/// A decoder which decodes three objects, one after the other.
-pub struct Decoder3<A, B, C, Err>
+pub struct Decoder3<A, B, C>
where
A: Decoder,
B: Decoder,
C: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error>,
{
- inner: Decoder2<Decoder2<A, B, Err>, C, Err>,
- _error: PhantomData<Err>,
+ inner: Decoder2<Decoder2<A, B>, C>,
}
-impl<A, B, C, Err> Decoder3<A, B, C, Err>
+impl<A, B, C> Decoder3<A, B, C>
where
A: Decoder,
B: Decoder,
C: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error>,
{
/// Constructs a new composite decoder.
pub fn new(dec_1: A, dec_2: B, dec_3: C) -> Self {
- Self { inner: Decoder2::new(Decoder2::new(dec_1, dec_2), dec_3), _error: PhantomData }
+ Self { inner: Decoder2::new(Decoder2::new(dec_1, dec_2), dec_3) }
}
}
-impl<A, B, C, Err> Decoder for Decoder3<A, B, C, Err>
+impl<A, B, C> Decoder for Decoder3<A, B, C>
where
A: Decoder,
B: Decoder,
C: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error>,
{
type Output = (A::Output, B::Output, C::Output);
- type Error = Err;
+ type Error = Decoder3Error<A::Error, B::Error, C::Error>;
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.inner.push_bytes(bytes)
+ self.inner.push_bytes(bytes).map_err(|error| match error {
+ Decoder2Error::First(Decoder2Error::First(a)) => Decoder3Error::First(a),
+ Decoder2Error::First(Decoder2Error::Second(b)) => Decoder3Error::Second(b),
+ Decoder2Error::Second(c) => Decoder3Error::Third(c),
+ })
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let ((first, second), third) = self.inner.end()?;
+ let result = self.inner.end().map_err(|error| match error {
+ Decoder2Error::First(Decoder2Error::First(a)) => Decoder3Error::First(a),
+ Decoder2Error::First(Decoder2Error::Second(b)) => Decoder3Error::Second(b),
+ Decoder2Error::Second(c) => Decoder3Error::Third(c),
+ })?;
+
+ let ((first, second), third) = result;
Ok((first, second, third))
}
@@ -433,54 +432,59 @@ where
}
/// A decoder which decodes four objects, one after the other.
-pub struct Decoder4<A, B, C, D, Err>
+pub struct Decoder4<A, B, C, D>
where
A: Decoder,
B: Decoder,
C: Decoder,
D: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error> + From<D::Error>,
{
- inner: Decoder2<Decoder2<A, B, Err>, Decoder2<C, D, Err>, Err>,
- _error: PhantomData<Err>,
+ inner: Decoder2<Decoder2<A, B>, Decoder2<C, D>>,
}
-impl<A, B, C, D, Err> Decoder4<A, B, C, D, Err>
+impl<A, B, C, D> Decoder4<A, B, C, D>
where
A: Decoder,
B: Decoder,
C: Decoder,
D: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error> + From<D::Error>,
{
/// Constructs a new composite decoder.
pub fn new(dec_1: A, dec_2: B, dec_3: C, dec_4: D) -> Self {
- Self {
- inner: Decoder2::new(Decoder2::new(dec_1, dec_2), Decoder2::new(dec_3, dec_4)),
- _error: PhantomData,
- }
+ Self { inner: Decoder2::new(Decoder2::new(dec_1, dec_2), Decoder2::new(dec_3, dec_4)) }
}
}
-impl<A, B, C, D, Err> Decoder for Decoder4<A, B, C, D, Err>
+impl<A, B, C, D> Decoder for Decoder4<A, B, C, D>
where
A: Decoder,
B: Decoder,
C: Decoder,
D: Decoder,
- Err: From<A::Error> + From<B::Error> + From<C::Error> + From<D::Error>,
{
type Output = (A::Output, B::Output, C::Output, D::Output);
- type Error = Err;
+ type Error = Decoder4Error<A::Error, B::Error, C::Error, D::Error>;
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.inner.push_bytes(bytes)
+ self.inner.push_bytes(bytes).map_err(|error| match error {
+ Decoder2Error::First(Decoder2Error::First(a)) => Decoder4Error::First(a),
+ Decoder2Error::First(Decoder2Error::Second(b)) => Decoder4Error::Second(b),
+ Decoder2Error::Second(Decoder2Error::First(c)) => Decoder4Error::Third(c),
+ Decoder2Error::Second(Decoder2Error::Second(d)) => Decoder4Error::Fourth(d),
+ })
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let ((first, second), (third, fourth)) = self.inner.end()?;
+ let result = self.inner.end().map_err(|error| match error {
+ Decoder2Error::First(Decoder2Error::First(a)) => Decoder4Error::First(a),
+ Decoder2Error::First(Decoder2Error::Second(b)) => Decoder4Error::Second(b),
+ Decoder2Error::Second(Decoder2Error::First(c)) => Decoder4Error::Third(c),
+ Decoder2Error::Second(Decoder2Error::Second(d)) => Decoder4Error::Fourth(d),
+ })?;
+
+ let ((first, second), (third, fourth)) = result;
Ok((first, second, third, fourth))
}
@@ -490,7 +494,7 @@ where
/// A decoder which decodes six objects, one after the other.
#[allow(clippy::type_complexity)] // Nested composition is easier than flattened alternatives.
-pub struct Decoder6<A, B, C, D, E, F, Err>
+pub struct Decoder6<A, B, C, D, E, F>
where
A: Decoder,
B: Decoder,
@@ -498,18 +502,11 @@ where
D: Decoder,
E: Decoder,
F: Decoder,
- Err: From<A::Error>
- + From<B::Error>
- + From<C::Error>
- + From<D::Error>
- + From<E::Error>
- + From<F::Error>,
{
- inner: Decoder2<Decoder3<A, B, C, Err>, Decoder3<D, E, F, Err>, Err>,
- _error: PhantomData<Err>,
+ inner: Decoder2<Decoder3<A, B, C>, Decoder3<D, E, F>>,
}
-impl<A, B, C, D, E, F, Err> Decoder6<A, B, C, D, E, F, Err>
+impl<A, B, C, D, E, F> Decoder6<A, B, C, D, E, F>
where
A: Decoder,
B: Decoder,
@@ -517,12 +514,6 @@ where
D: Decoder,
E: Decoder,
F: Decoder,
- Err: From<A::Error>
- + From<B::Error>
- + From<C::Error>
- + From<D::Error>
- + From<E::Error>
- + From<F::Error>,
{
/// Constructs a new composite decoder.
pub fn new(dec_1: A, dec_2: B, dec_3: C, dec_4: D, dec_5: E, dec_6: F) -> Self {
@@ -531,12 +522,11 @@ where
Decoder3::new(dec_1, dec_2, dec_3),
Decoder3::new(dec_4, dec_5, dec_6),
),
- _error: PhantomData,
}
}
}
-impl<A, B, C, D, E, F, Err> Decoder for Decoder6<A, B, C, D, E, F, Err>
+impl<A, B, C, D, E, F> Decoder for Decoder6<A, B, C, D, E, F>
where
A: Decoder,
B: Decoder,
@@ -544,24 +534,34 @@ where
D: Decoder,
E: Decoder,
F: Decoder,
- Err: From<A::Error>
- + From<B::Error>
- + From<C::Error>
- + From<D::Error>
- + From<E::Error>
- + From<F::Error>,
{
type Output = (A::Output, B::Output, C::Output, D::Output, E::Output, F::Output);
- type Error = Err;
+ type Error = Decoder6Error<A::Error, B::Error, C::Error, D::Error, E::Error, F::Error>;
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.inner.push_bytes(bytes)
+ self.inner.push_bytes(bytes).map_err(|error| match error {
+ Decoder2Error::First(Decoder3Error::First(a)) => Decoder6Error::First(a),
+ Decoder2Error::First(Decoder3Error::Second(b)) => Decoder6Error::Second(b),
+ Decoder2Error::First(Decoder3Error::Third(c)) => Decoder6Error::Third(c),
+ Decoder2Error::Second(Decoder3Error::First(d)) => Decoder6Error::Fourth(d),
+ Decoder2Error::Second(Decoder3Error::Second(e)) => Decoder6Error::Fifth(e),
+ Decoder2Error::Second(Decoder3Error::Third(f)) => Decoder6Error::Sixth(f),
+ })
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let ((first, second, third), (fourth, fifth, sixth)) = self.inner.end()?;
+ let result = self.inner.end().map_err(|error| match error {
+ Decoder2Error::First(Decoder3Error::First(a)) => Decoder6Error::First(a),
+ Decoder2Error::First(Decoder3Error::Second(b)) => Decoder6Error::Second(b),
+ Decoder2Error::First(Decoder3Error::Third(c)) => Decoder6Error::Third(c),
+ Decoder2Error::Second(Decoder3Error::First(d)) => Decoder6Error::Fourth(d),
+ Decoder2Error::Second(Decoder3Error::Second(e)) => Decoder6Error::Fifth(e),
+ Decoder2Error::Second(Decoder3Error::Third(f)) => Decoder6Error::Sixth(f),
+ })?;
+
+ let ((first, second, third), (fourth, fifth, sixth)) = result;
Ok((first, second, third, fourth, fifth, sixth))
}
@@ -693,10 +693,12 @@ impl fmt::Display for CompactSizeDecoderError {
use CompactSizeDecoderErrorInner as E;
match self.0 {
- E::UnexpectedEof { required: 1, received: 0 } =>
- write!(f, "required at least one byte but the input is empty"),
- E::UnexpectedEof { required, received: 0 } =>
- write!(f, "required at least {} bytes but the input is empty", required),
+ E::UnexpectedEof { required: 1, received: 0 } => {
+ write!(f, "required at least one byte but the input is empty")
+ }
+ E::UnexpectedEof { required, received: 0 } => {
+ write!(f, "required at least {} bytes but the input is empty", required)
+ }
E::UnexpectedEof { required, received } => write!(
f,
"required at least {} bytes but only {} bytes were received",
@@ -854,6 +856,192 @@ impl fmt::Display for UnexpectedEofError {
#[cfg(feature = "std")]
impl std::error::Error for UnexpectedEofError {}
+/// Error type for [`Decoder2`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Decoder2Error<A, B> {
+ /// Error from the first decoder.
+ First(A),
+ /// Error from the second decoder.
+ Second(B),
+}
+
+impl<A, B> fmt::Display for Decoder2Error<A, B>
+where
+ A: fmt::Display,
+ B: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Decoder2Error::First(ref e) => write_err!(f, "first decoder error"; e),
+ Decoder2Error::Second(ref e) => write_err!(f, "second decoder error"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<A, B> std::error::Error for Decoder2Error<A, B>
+where
+ A: std::error::Error + 'static,
+ B: std::error::Error + 'static,
+{
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Decoder2Error::First(ref e) => Some(e),
+ Decoder2Error::Second(ref e) => Some(e),
+ }
+ }
+}
+
+/// Error type for [`Decoder3`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Decoder3Error<A, B, C> {
+ /// Error from the first decoder.
+ First(A),
+ /// Error from the second decoder.
+ Second(B),
+ /// Error from the third decoder.
+ Third(C),
+}
+
+impl<A, B, C> fmt::Display for Decoder3Error<A, B, C>
+where
+ A: fmt::Display,
+ B: fmt::Display,
+ C: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Decoder3Error::First(ref e) => write_err!(f, "first decoder error"; e),
+ Decoder3Error::Second(ref e) => write_err!(f, "second decoder error"; e),
+ Decoder3Error::Third(ref e) => write_err!(f, "third decoder error"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<A, B, C> std::error::Error for Decoder3Error<A, B, C>
+where
+ A: std::error::Error + 'static,
+ B: std::error::Error + 'static,
+ C: std::error::Error + 'static,
+{
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Decoder3Error::First(ref e) => Some(e),
+ Decoder3Error::Second(ref e) => Some(e),
+ Decoder3Error::Third(ref e) => Some(e),
+ }
+ }
+}
+
+/// Error type for [`Decoder4`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Decoder4Error<A, B, C, D> {
+ /// Error from the first decoder.
+ First(A),
+ /// Error from the second decoder.
+ Second(B),
+ /// Error from the third decoder.
+ Third(C),
+ /// Error from the fourth decoder.
+ Fourth(D),
+}
+
+impl<A, B, C, D> fmt::Display for Decoder4Error<A, B, C, D>
+where
+ A: fmt::Display,
+ B: fmt::Display,
+ C: fmt::Display,
+ D: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Decoder4Error::First(ref e) => write_err!(f, "first decoder error"; e),
+ Decoder4Error::Second(ref e) => write_err!(f, "second decoder error"; e),
+ Decoder4Error::Third(ref e) => write_err!(f, "third decoder error"; e),
+ Decoder4Error::Fourth(ref e) => write_err!(f, "fourth decoder error"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<A, B, C, D> std::error::Error for Decoder4Error<A, B, C, D>
+where
+ A: std::error::Error + 'static,
+ B: std::error::Error + 'static,
+ C: std::error::Error + 'static,
+ D: std::error::Error + 'static,
+{
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Decoder4Error::First(ref e) => Some(e),
+ Decoder4Error::Second(ref e) => Some(e),
+ Decoder4Error::Third(ref e) => Some(e),
+ Decoder4Error::Fourth(ref e) => Some(e),
+ }
+ }
+}
+
+/// Error type for [`Decoder6`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Decoder6Error<A, B, C, D, E, F> {
+ /// Error from the first decoder.
+ First(A),
+ /// Error from the second decoder.
+ Second(B),
+ /// Error from the third decoder.
+ Third(C),
+ /// Error from the fourth decoder.
+ Fourth(D),
+ /// Error from the fifth decoder.
+ Fifth(E),
+ /// Error from the sixth decoder.
+ Sixth(F),
+}
+
+impl<A, B, C, D, E, F> fmt::Display for Decoder6Error<A, B, C, D, E, F>
+where
+ A: fmt::Display,
+ B: fmt::Display,
+ C: fmt::Display,
+ D: fmt::Display,
+ E: fmt::Display,
+ F: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Decoder6Error::First(ref e) => write_err!(f, "first decoder error"; e),
+ Decoder6Error::Second(ref e) => write_err!(f, "second decoder error"; e),
+ Decoder6Error::Third(ref e) => write_err!(f, "third decoder error"; e),
+ Decoder6Error::Fourth(ref e) => write_err!(f, "fourth decoder error"; e),
+ Decoder6Error::Fifth(ref e) => write_err!(f, "fifth decoder error"; e),
+ Decoder6Error::Sixth(ref e) => write_err!(f, "sixth decoder error"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<A, B, C, D, E, F> std::error::Error for Decoder6Error<A, B, C, D, E, F>
+where
+ A: std::error::Error + 'static,
+ B: std::error::Error + 'static,
+ C: std::error::Error + 'static,
+ D: std::error::Error + 'static,
+ E: std::error::Error + 'static,
+ F: std::error::Error + 'static,
+{
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Decoder6Error::First(ref e) => Some(e),
+ Decoder6Error::Second(ref e) => Some(e),
+ Decoder6Error::Third(ref e) => Some(e),
+ Decoder6Error::Fourth(ref e) => Some(e),
+ Decoder6Error::Fifth(ref e) => Some(e),
+ Decoder6Error::Sixth(ref e) => Some(e),
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs
index 0ebee30d..7a500053 100644
--- a/consensus_encoding/src/lib.rs
+++ b/consensus_encoding/src/lib.rs
@@ -28,8 +28,8 @@ pub use self::decode::decoders::{
VecDecoder, VecDecoderError,
};
pub use self::decode::decoders::{
- ArrayDecoder, CompactSizeDecoder, CompactSizeDecoderError, Decoder2, Decoder3, Decoder4,
- Decoder6, UnexpectedEofError,
+ ArrayDecoder, CompactSizeDecoder, CompactSizeDecoderError, Decoder2, Decoder2Error, Decoder3,
+ Decoder3Error, Decoder4, Decoder4Error, Decoder6, Decoder6Error, UnexpectedEofError,
};
#[cfg(feature = "std")]
pub use self::decode::{
diff --git a/consensus_encoding/tests/composition.rs b/consensus_encoding/tests/composition.rs
index ba59f5dd..2ea95ef8 100644
--- a/consensus_encoding/tests/composition.rs
+++ b/consensus_encoding/tests/composition.rs
@@ -3,8 +3,8 @@
//! Test composition of encoders and decoders.
use bitcoin_consensus_encoding::{
- ArrayDecoder, ArrayEncoder, Decodable, Decoder, Decoder2, Decoder6, Encodable, Encoder,
- Encoder2, Encoder6, UnexpectedEofError,
+ ArrayDecoder, ArrayEncoder, Decodable, Decoder, Decoder2, Decoder2Error, Decoder6, Encodable,
+ Encoder, Encoder2, Encoder6, UnexpectedEofError,
};
const EMPTY: &[u8] = &[];
@@ -47,7 +47,7 @@ impl core::fmt::Display for CompositeError {
/// A wrapper decoder that converts the tuple output to [`CompositeData`].
struct CompositeDataDecoder {
- inner: Decoder2<ArrayDecoder<4>, ArrayDecoder<2>, CompositeError>,
+ inner: Decoder2<ArrayDecoder<4>, ArrayDecoder<2>>,
}
impl CompositeDataDecoder {
@@ -61,11 +61,15 @@ impl Decoder for CompositeDataDecoder {
type Error = CompositeError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.inner.push_bytes(bytes)
+ self.inner.push_bytes(bytes).map_err(|error| match error {
+ Decoder2Error::First(e) | Decoder2Error::Second(e) => CompositeError::Eof(e),
+ })
}
fn end(self) -> Result<Self::Output, Self::Error> {
- let (first, second) = self.inner.end()?;
+ let (first, second) = self.inner.end().map_err(|error| match error {
+ Decoder2Error::First(e) | Decoder2Error::Second(e) => CompositeError::Eof(e),
+ })?;
Ok(CompositeData { first, second })
}
@@ -117,7 +121,7 @@ fn composition_nested() {
}
assert_eq!(encoded_bytes, data);
- let mut decoder6: Decoder6<_, _, _, _, _, _, UnexpectedEofError> = Decoder6::new(
+ let mut decoder6: Decoder6<_, _, _, _, _, _> = Decoder6::new(
ArrayDecoder::<1>::new(),
ArrayDecoder::<1>::new(),
ArrayDecoder::<1>::new(),
@@ -141,7 +145,7 @@ fn composition_nested() {
#[test]
fn composition_extra_bytes() {
// Test that Decoder2 consumes exactly what it needs and leaves extra bytes unconsumed.
- let mut decoder2: Decoder2<_, _, UnexpectedEofError> =
+ let mut decoder2: Decoder2<_, _> =
Decoder2::new(ArrayDecoder::<2>::new(), ArrayDecoder::<3>::new());
let mut bytes = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08][..];
let original_len = bytes.len();
@@ -198,7 +202,7 @@ fn composition_error_unification() {
/// A test composite decoder.
struct HeaderDecoder {
- inner: Decoder2<ArrayDecoder<1>, ArrayDecoder<1>, NestedError>,
+ inner: Decoder2<ArrayDecoder<1>, ArrayDecoder<1>>,
}
impl HeaderDecoder {
@@ -212,11 +216,15 @@ fn composition_error_unification() {
type Error = NestedError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.inner.push_bytes(bytes)
+ self.inner.push_bytes(bytes).map_err(|error| match error {
+ Decoder2Error::First(e) | Decoder2Error::Second(e) => NestedError::from(e),
+ })
}
fn end(self) -> Result<Self::Output, Self::Error> {
- let (first, second) = self.inner.end()?;
+ let (first, second) = self.inner.end().map_err(|error| match error {
+ Decoder2Error::First(e) | Decoder2Error::Second(e) => NestedError::from(e),
+ })?;
Ok((first, second))
}
@@ -278,15 +286,7 @@ fn composition_error_unification() {
}
// A multi-layer, nested, decoder structure with a unified top level error type.
- let mut nested_decoder: Decoder6<
- HeaderDecoder,
- PayloadDecoder,
- ArrayDecoder<1>,
- HeaderDecoder,
- PayloadDecoder,
- ArrayDecoder<2>,
- TopLevelError,
- > = Decoder6::new(
+ let mut nested_decoder = Decoder6::new(
HeaderDecoder::new(),
PayloadDecoder::new(),
ArrayDecoder::<1>::new(),
@@ -303,18 +303,15 @@ fn composition_error_unification() {
assert!(end_result.is_ok(), "end should succeed, got error: {:?}", end_result.err());
// Test error during decoding.
- let mut failing_decoder: Decoder2<FailingDecoder, ArrayDecoder<1>, TopLevelError> =
+ let mut failing_decoder: Decoder2<FailingDecoder, ArrayDecoder<1>> =
Decoder2::new(FailingDecoder::new(true), ArrayDecoder::<1>::new());
let test_data = b"ab";
let mut bytes = &test_data[..];
let push_result = failing_decoder.push_bytes(&mut bytes);
assert!(push_result.is_err(), "push_bytes should fail when first decoder fails in end()");
assert!(
- matches!(
- push_result.as_ref().unwrap_err(),
- TopLevelError::Validation(NestedError::BadChecksum)
- ),
- "Expected TopLevelError::Validation(NestedError::BadChecksum), got {:?}",
+ matches!(push_result.as_ref().unwrap_err(), Decoder2Error::First(NestedError::BadChecksum)),
+ "Expected Decoder2Error::First(NestedError::BadChecksum), got {:?}",
push_result.unwrap_err()
);
}
diff --git a/consensus_encoding/tests/encode.rs b/consensus_encoding/tests/encode.rs
index 3409a2c9..c6b157ce 100644
--- a/consensus_encoding/tests/encode.rs
+++ b/consensus_encoding/tests/encode.rs
@@ -5,9 +5,9 @@
#[cfg(feature = "std")]
use std::io::{Cursor, Write};
-use bitcoin_consensus_encoding::{ArrayEncoder, BytesEncoder, Encoder};
#[cfg(feature = "alloc")]
-use bitcoin_consensus_encoding::{Encodable};
+use bitcoin_consensus_encoding::Encodable;
+use bitcoin_consensus_encoding::{ArrayEncoder, BytesEncoder, Encoder};
// Simple test type that implements Encodable.
#[cfg(feature = "alloc")]
diff --git a/primitives/src/block.rs b/primitives/src/block.rs
index 7f28ade9..66bc5a70 100644
--- a/primitives/src/block.rs
+++ b/primitives/src/block.rs
@@ -305,19 +305,33 @@ impl Encodable for Header {
}
}
+#[cfg(feature = "alloc")]
+type HeaderInnerDecoder = Decoder6<
+ VersionDecoder,
+ BlockHashDecoder,
+ TxMerkleNodeDecoder,
+ BlockTimeDecoder,
+ CompactTargetDecoder,
+ encoding::ArrayDecoder<4>, // Nonce
+>;
+
/// The decoder for the [`Header`] type.
#[cfg(feature = "alloc")]
-pub struct HeaderDecoder(
- Decoder6<
- VersionDecoder,
- BlockHashDecoder,
- TxMerkleNodeDecoder,
- BlockTimeDecoder,
- CompactTargetDecoder,
- encoding::ArrayDecoder<4>, // Nonce
- HeaderDecoderError,
- >,
-);
+pub struct HeaderDecoder(HeaderInnerDecoder);
+
+#[cfg(feature = "alloc")]
+impl HeaderDecoder {
+ fn from_inner(e: <HeaderInnerDecoder as Decoder>::Error) -> HeaderDecoderError {
+ match e {
+ encoding::Decoder6Error::First(e) => HeaderDecoderError::Version(e),
+ encoding::Decoder6Error::Second(e) => HeaderDecoderError::PrevBlockhash(e),
+ encoding::Decoder6Error::Third(e) => HeaderDecoderError::MerkleRoot(e),
+ encoding::Decoder6Error::Fourth(e) => HeaderDecoderError::Time(e),
+ encoding::Decoder6Error::Fifth(e) => HeaderDecoderError::Bits(e),
+ encoding::Decoder6Error::Sixth(e) => HeaderDecoderError::Nonce(e),
+ }
+ }
+}
#[cfg(feature = "alloc")]
impl Decoder for HeaderDecoder {
@@ -326,12 +340,13 @@ impl Decoder for HeaderDecoder {
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.0.push_bytes(bytes)
+ self.0.push_bytes(bytes).map_err(Self::from_inner)
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let (version, prev_blockhash, merkle_root, time, bits, nonce) = self.0.end()?;
+ let (version, prev_blockhash, merkle_root, time, bits, nonce) =
+ self.0.end().map_err(Self::from_inner)?;
let nonce = u32::from_le_bytes(nonce);
Ok(Header { version, prev_blockhash, merkle_root, time, bits, nonce })
}
@@ -379,36 +394,6 @@ impl From<Infallible> for HeaderDecoderError {
fn from(never: Infallible) -> Self { match never {} }
}
-#[cfg(feature = "alloc")]
-impl From<VersionDecoderError> for HeaderDecoderError {
- fn from(e: VersionDecoderError) -> Self { Self::Version(e) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<BlockHashDecoderError> for HeaderDecoderError {
- fn from(e: BlockHashDecoderError) -> Self { Self::PrevBlockhash(e) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<TxMerkleNodeDecoderError> for HeaderDecoderError {
- fn from(e: TxMerkleNodeDecoderError) -> Self { Self::MerkleRoot(e) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<BlockTimeDecoderError> for HeaderDecoderError {
- fn from(e: BlockTimeDecoderError) -> Self { Self::Time(e) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<CompactTargetDecoderError> for HeaderDecoderError {
- fn from(e: CompactTargetDecoderError) -> Self { Self::Bits(e) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<encoding::UnexpectedEofError> for HeaderDecoderError {
- fn from(e: encoding::UnexpectedEofError) -> Self { Self::Nonce(e) }
-}
-
#[cfg(feature = "alloc")]
impl fmt::Display for HeaderDecoderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs
index b3456074..06308241 100644
--- a/primitives/src/transaction.rs
+++ b/primitives/src/transaction.rs
@@ -34,17 +34,15 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use units::parse_int;
#[cfg(feature = "alloc")]
-use crate::amount::{AmountDecoder, AmountDecoderError, AmountEncoder};
+use crate::amount::{AmountDecoder, AmountEncoder};
#[cfg(feature = "alloc")]
use crate::locktime::absolute::{LockTimeDecoder, LockTimeDecoderError, LockTimeEncoder};
#[cfg(feature = "alloc")]
use crate::prelude::Vec;
#[cfg(feature = "alloc")]
-use crate::script::{
- ScriptBufDecoderError, ScriptEncoder, ScriptPubKeyBufDecoder, ScriptSigBufDecoder,
-};
+use crate::script::{ScriptEncoder, ScriptPubKeyBufDecoder, ScriptSigBufDecoder};
#[cfg(feature = "alloc")]
-use crate::sequence::{SequenceDecoder, SequenceDecoderError, SequenceEncoder};
+use crate::sequence::{SequenceDecoder, SequenceEncoder};
#[cfg(feature = "alloc")]
use crate::witness::{WitnessDecoder, WitnessDecoderError, WitnessEncoder};
#[cfg(feature = "alloc")]
@@ -669,8 +667,9 @@ impl fmt::Display for TransactionDecoderError {
match self.0 {
E::Version(ref e) => write_err!(f, "transaction decoder error"; e),
- E::UnsupportedSegwitFlag(v) =>
- write!(f, "we only support segwit flag value 0x01: {}", v),
+ E::UnsupportedSegwitFlag(v) => {
+ write!(f, "we only support segwit flag value 0x01: {}", v)
+ }
E::Inputs(ref e) => write_err!(f, "transaction decoder error"; e),
E::Outputs(ref e) => write_err!(f, "transaction decoder error"; e),
E::Witness(ref e) => write_err!(f, "transaction decoder error"; e),
@@ -819,11 +818,12 @@ impl Encoder for WitnessesEncoder<'_> {
}
}
+#[cfg(feature = "alloc")]
+type TxInInnerDecoder = Decoder3<OutPointDecoder, ScriptSigBufDecoder, SequenceDecoder>;
+
/// The decoder for the [`TxIn`] type.
#[cfg(feature = "alloc")]
-pub struct TxInDecoder(
- Decoder3<OutPointDecoder, ScriptSigBufDecoder, SequenceDecoder, TxInDecoderError>,
-);
+pub struct TxInDecoder(TxInInnerDecoder);
#[cfg(feature = "alloc")]
impl Decoder for TxInDecoder {
@@ -832,12 +832,12 @@ impl Decoder for TxInDecoder {
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.0.push_bytes(bytes)
+ self.0.push_bytes(bytes).map_err(TxInDecoderError)
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let (previous_output, script_sig, sequence) = self.0.end()?;
+ let (previous_output, script_sig, sequence) = self.0.end().map_err(TxInDecoderError)?;
Ok(TxIn { previous_output, script_sig, sequence, witness: Witness::default() })
}
@@ -860,48 +860,20 @@ impl Decodable for TxIn {
/// An error consensus decoding a `TxIn`.
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct TxInDecoderError(TxInDecoderErrorInner);
-
-#[cfg(feature = "alloc")]
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum TxInDecoderErrorInner {
- /// Error while decoding the `previous_output`.
- PreviousOutput(OutPointDecoderError),
- /// Error while decoding the `script_sig`.
- ScriptSig(ScriptBufDecoderError),
- /// Error while decoding the `sequence`.
- Sequence(SequenceDecoderError),
-}
+pub struct TxInDecoderError(<TxInInnerDecoder as Decoder>::Error);
#[cfg(feature = "alloc")]
impl From<Infallible> for TxInDecoderError {
fn from(never: Infallible) -> Self { match never {} }
}
-#[cfg(feature = "alloc")]
-impl From<OutPointDecoderError> for TxInDecoderError {
- fn from(e: OutPointDecoderError) -> Self { Self(TxInDecoderErrorInner::PreviousOutput(e)) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<ScriptBufDecoderError> for TxInDecoderError {
- fn from(e: ScriptBufDecoderError) -> Self { Self(TxInDecoderErrorInner::ScriptSig(e)) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<SequenceDecoderError> for TxInDecoderError {
- fn from(e: SequenceDecoderError) -> Self { Self(TxInDecoderErrorInner::Sequence(e)) }
-}
-
#[cfg(feature = "alloc")]
impl fmt::Display for TxInDecoderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use TxInDecoderErrorInner as E;
-
- match self.0 {
- E::PreviousOutput(ref e) => write_err!(f, "txin decoder error"; e),
- E::ScriptSig(ref e) => write_err!(f, "txin decoder error"; e),
- E::Sequence(ref e) => write_err!(f, "txin decoder error"; e),
+ match &self.0 {
+ encoding::Decoder3Error::First(ref e) => write_err!(f, "txin decoder error"; e),
+ encoding::Decoder3Error::Second(ref e) => write_err!(f, "txin decoder error"; e),
+ encoding::Decoder3Error::Third(ref e) => write_err!(f, "txin decoder error"; e),
}
}
}
@@ -910,12 +882,10 @@ impl fmt::Display for TxInDecoderError {
#[cfg(feature = "std")]
impl std::error::Error for TxInDecoderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- use TxInDecoderErrorInner as E;
-
- match self.0 {
- E::PreviousOutput(ref e) => Some(e),
- E::ScriptSig(ref e) => Some(e),
- E::Sequence(ref e) => Some(e),
+ match &self.0 {
+ encoding::Decoder3Error::First(ref e) => Some(e),
+ encoding::Decoder3Error::Second(ref e) => Some(e),
+ encoding::Decoder3Error::Third(ref e) => Some(e),
}
}
}
@@ -958,9 +928,12 @@ impl Encodable for TxOut {
}
}
+#[cfg(feature = "alloc")]
+type TxOutInnerDecoder = Decoder2<AmountDecoder, ScriptPubKeyBufDecoder>;
+
/// The decoder for the [`TxOut`] type.
#[cfg(feature = "alloc")]
-pub struct TxOutDecoder(Decoder2<AmountDecoder, ScriptPubKeyBufDecoder, TxOutDecoderError>);
+pub struct TxOutDecoder(TxOutInnerDecoder);
#[cfg(feature = "alloc")]
impl Decoder for TxOutDecoder {
@@ -969,12 +942,12 @@ impl Decoder for TxOutDecoder {
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- Ok(self.0.push_bytes(bytes)?)
+ self.0.push_bytes(bytes).map_err(TxOutDecoderError)
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
- let (amount, script_pubkey) = self.0.end()?;
+ let (amount, script_pubkey) = self.0.end().map_err(TxOutDecoderError)?;
Ok(TxOut { amount, script_pubkey })
}
@@ -993,41 +966,19 @@ impl Decodable for TxOut {
/// An error consensus decoding a `TxOut`.
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct TxOutDecoderError(TxOutDecoderErrorInner);
-
-/// An error consensus decoding a `TxOut`.
-#[cfg(feature = "alloc")]
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum TxOutDecoderErrorInner {
- /// Error while decoding the `amount`.
- Amount(AmountDecoderError),
- /// Error while decoding the `script_pubkey`.
- ScriptPubKey(ScriptBufDecoderError),
-}
+pub struct TxOutDecoderError(<TxOutInnerDecoder as Decoder>::Error);
#[cfg(feature = "alloc")]
impl From<Infallible> for TxOutDecoderError {
fn from(never: Infallible) -> Self { match never {} }
}
-#[cfg(feature = "alloc")]
-impl From<AmountDecoderError> for TxOutDecoderError {
- fn from(e: AmountDecoderError) -> Self { Self(TxOutDecoderErrorInner::Amount(e)) }
-}
-
-#[cfg(feature = "alloc")]
-impl From<ScriptBufDecoderError> for TxOutDecoderError {
- fn from(e: ScriptBufDecoderError) -> Self { Self(TxOutDecoderErrorInner::ScriptPubKey(e)) }
-}
-
#[cfg(feature = "alloc")]
impl fmt::Display for TxOutDecoderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use TxOutDecoderErrorInner as E;
-
- match self.0 {
- E::Amount(ref e) => write_err!(f, "txout decoder error"; e),
- E::ScriptPubKey(ref e) => write_err!(f, "txout decoder error"; e),
+ match &self.0 {
+ encoding::Decoder2Error::First(ref e) => write_err!(f, "txout decoder error"; e),
+ encoding::Decoder2Error::Second(ref e) => write_err!(f, "txout decoder error"; e),
}
}
}
@@ -1035,11 +986,9 @@ impl fmt::Display for TxOutDecoderError {
#[cfg(feature = "std")]
impl std::error::Error for TxOutDecoderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- use TxOutDecoderErrorInner as E;
-
- match self.0 {
- E::Amount(ref e) => Some(e),
- E::ScriptPubKey(ref e) => Some(e),
+ match &self.0 {
+ encoding::Decoder2Error::First(ref e) => Some(e),
+ encoding::Decoder2Error::Second(ref e) => Some(e),
}
}
}
Why this scored 17/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.