Implement From<Infallible> for all public error types
What changed, and why it matters
This commit adds standard Rust trait implementations that let the library's error types be automatically converted from an 'impossible' error type (Infallible). It is a routine API-ergonomics improvement with no security relevance: Infallible can never actually be produced at runtime, so these conversions can never be triggered by an attacker.
No security action required. Treat as a normal API-quality commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds impl From<core::convert::Infallible> for ... for 31 public error types across consensus_encoding, primitives, and units crates. In Rust, Infallible is an uninhabited type (it has no values), so From<Infallible> conversions are idiomatic, compile-time-only plumbing that allows generic code using ? or Result combinators to unify error types. The implementations are all match never {} and introduce no runtime behavior, no parsing changes, no state mutation, and no new attack surface.
Changed components
consensus_encoding/src/compact_size.rsconsensus_encoding/src/decode/decoders.rsprimitives/src/transaction.rsprimitives/src/witness.rsunits/src/amount/error.rsunits/src/block.rsunits/src/locktime/absolute/error.rsunits/src/locktime/relative/error.rsunits/src/parse_int.rsunits/src/result.rsInspect captured patch +119 / −2
diff --git a/consensus_encoding/src/compact_size.rs b/consensus_encoding/src/compact_size.rs
index 4fa169ce..47df41b5 100644
--- a/consensus_encoding/src/compact_size.rs
+++ b/consensus_encoding/src/compact_size.rs
@@ -6,6 +6,8 @@
//! Bitcoin consensus protocol to usually to encode collection lengths. However,
//! there are also some unique non-length use cases.
+use core::convert::Infallible;
+
use internals::array_vec::ArrayVec;
use crate::decode::Decoder;
@@ -347,6 +349,10 @@ enum CompactSizeDecoderErrorInner {
ValueExceedsLimit(LengthPrefixExceedsMaxError),
}
+impl From<Infallible> for CompactSizeDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl core::fmt::Display for CompactSizeDecoderError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use internals::write_err;
@@ -391,6 +397,10 @@ pub struct LengthPrefixExceedsMaxError {
value: u64,
}
+impl From<Infallible> for LengthPrefixExceedsMaxError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl core::fmt::Display for LengthPrefixExceedsMaxError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "decoded length {} exceeds maximum allowed {}", self.value, self.limit)
diff --git a/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs
index 0afa46a3..fcf19840 100644
--- a/consensus_encoding/src/decode/decoders.rs
+++ b/consensus_encoding/src/decode/decoders.rs
@@ -4,7 +4,6 @@
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
-#[cfg(feature = "alloc")]
use core::convert::Infallible;
use core::{fmt, mem};
@@ -779,6 +778,10 @@ pub struct UnexpectedEofError {
missing: usize,
}
+impl From<Infallible> for UnexpectedEofError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for UnexpectedEofError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "not enough bytes for decoder, {} more bytes required", self.missing)
diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs
index b7a4c4e1..ee09e147 100644
--- a/primitives/src/transaction.rs
+++ b/primitives/src/transaction.rs
@@ -1229,6 +1229,10 @@ impl encoding::Decodable for OutPoint {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutPointDecoderError(UnexpectedEofError);
+impl From<Infallible> for OutPointDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl core::fmt::Display for OutPointDecoderError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write_err!(f, "out point decoder error"; self.0)
diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs
index f103b6bd..fc0924e8 100644
--- a/primitives/src/witness.rs
+++ b/primitives/src/witness.rs
@@ -866,6 +866,10 @@ pub struct UnexpectedEofError {
missing_elements: usize,
}
+impl From<Infallible> for UnexpectedEofError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl core::fmt::Display for UnexpectedEofError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "not enough witness elements for decoder, missing {}", self.missing_elements)
diff --git a/units/src/amount/error.rs b/units/src/amount/error.rs
index 87c9246a..96830c24 100644
--- a/units/src/amount/error.rs
+++ b/units/src/amount/error.rs
@@ -133,6 +133,10 @@ pub struct OutOfRangeError {
pub(super) is_greater_than_max: bool,
}
+impl From<Infallible> for OutOfRangeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl OutOfRangeError {
/// Returns the minimum and maximum allowed values for the type that was parsed.
///
@@ -190,6 +194,10 @@ pub struct TooPreciseError {
pub(super) position: usize,
}
+impl From<Infallible> for TooPreciseError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for TooPreciseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.position {
@@ -212,6 +220,10 @@ pub struct InputTooLargeError {
pub(super) len: usize,
}
+impl From<Infallible> for InputTooLargeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for InputTooLargeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.len - INPUT_STRING_LEN_LIMIT {
@@ -240,6 +252,10 @@ pub struct MissingDigitsError {
pub(super) kind: MissingDigitsKind,
}
+impl From<Infallible> for MissingDigitsError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for MissingDigitsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
@@ -266,6 +282,10 @@ pub struct InvalidCharacterError {
pub(super) position: usize,
}
+impl From<Infallible> for InvalidCharacterError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for InvalidCharacterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.invalid_char {
@@ -290,6 +310,10 @@ pub struct BadPositionError {
pub(super) position: usize,
}
+impl From<Infallible> for BadPositionError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for BadPositionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.char {
@@ -347,11 +371,19 @@ impl std::error::Error for ParseDenominationError {
#[non_exhaustive]
pub struct MissingDenominationError;
+impl From<Infallible> for MissingDenominationError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
/// Error returned when parsing an unknown denomination.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UnknownDenominationError(pub(super) InputString);
+impl From<Infallible> for UnknownDenominationError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for UnknownDenominationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.unknown_variant("bitcoin denomination", f)
@@ -368,6 +400,10 @@ impl std::error::Error for UnknownDenominationError {
#[non_exhaustive]
pub struct PossiblyConfusingDenominationError(pub(super) InputString);
+impl From<Infallible> for PossiblyConfusingDenominationError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for PossiblyConfusingDenominationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: possibly confusing denomination - we intentionally do not support 'M' and 'P' so as to not confuse mega/milli and peta/pico", self.0.display_cannot_parse("bitcoin denomination"))
diff --git a/units/src/block.rs b/units/src/block.rs
index 1969d670..80999895 100644
--- a/units/src/block.rs
+++ b/units/src/block.rs
@@ -11,7 +11,6 @@
//! The difference between these types and the locktime types is that these types are thin wrappers
//! whereas the locktime types contain more complex locktime specific abstractions.
-#[cfg(feature = "encoding")]
use core::convert::Infallible;
use core::{fmt, ops};
@@ -495,6 +494,10 @@ impl From<relative::NumberOf512Seconds> for BlockMtpInterval {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TooBigForRelativeHeightError(u32);
+impl From<Infallible> for TooBigForRelativeHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for TooBigForRelativeHeightError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
diff --git a/units/src/locktime/absolute/error.rs b/units/src/locktime/absolute/error.rs
index eb1794dd..8fe6d581 100644
--- a/units/src/locktime/absolute/error.rs
+++ b/units/src/locktime/absolute/error.rs
@@ -43,6 +43,10 @@ pub struct IncompatibleHeightError {
pub(super) incompatible: Height,
}
+impl From<Infallible> for IncompatibleHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl IncompatibleHeightError {
/// Returns the value of the lock-by-time lock.
pub fn lock(&self) -> MedianTimePast { self.lock }
@@ -74,6 +78,10 @@ pub struct IncompatibleTimeError {
pub(super) incompatible: MedianTimePast,
}
+impl From<Infallible> for IncompatibleTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl IncompatibleTimeError {
/// Returns the value of the lock-by-height lock.
pub fn lock(&self) -> Height { self.lock }
@@ -154,6 +162,14 @@ impl From<Infallible> for ParseError {
fn from(never: Infallible) -> Self { match never {} }
}
+impl From<Infallible> for ParseHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl From<Infallible> for ParseTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl ParseError {
pub(super) fn invalid_int<S: Into<InputString>>(
s: S,
@@ -258,6 +274,10 @@ pub struct ConversionError {
input: u32,
}
+impl From<Infallible> for ConversionError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl ConversionError {
/// Constructs a new `ConversionError` from an invalid `n` when expecting a height value.
pub(super) const fn invalid_height(n: u32) -> Self {
diff --git a/units/src/locktime/relative/error.rs b/units/src/locktime/relative/error.rs
index 5b77f7bf..c8243e6e 100644
--- a/units/src/locktime/relative/error.rs
+++ b/units/src/locktime/relative/error.rs
@@ -2,6 +2,7 @@
//! Error types for the relative locktime module.
+use core::convert::Infallible;
use core::fmt;
use internals::write_err;
@@ -13,6 +14,10 @@ use super::{NumberOf512Seconds, NumberOfBlocks};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DisabledLockTimeError(pub(super) u32);
+impl From<Infallible> for DisabledLockTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl DisabledLockTimeError {
/// Accessor for the `u32` whose "disable" flag was set, preventing
/// it from being parsed as a relative locktime.
@@ -39,6 +44,10 @@ pub enum IsSatisfiedByError {
Time(InvalidTimeError),
}
+impl From<Infallible> for IsSatisfiedByError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for IsSatisfiedByError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -69,6 +78,10 @@ pub enum IsSatisfiedByHeightError {
Incompatible(NumberOf512Seconds),
}
+impl From<Infallible> for IsSatisfiedByHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for IsSatisfiedByHeightError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -100,6 +113,10 @@ pub enum IsSatisfiedByTimeError {
Incompatible(NumberOfBlocks),
}
+impl From<Infallible> for IsSatisfiedByTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for IsSatisfiedByTimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -129,6 +146,10 @@ pub struct TimeOverflowError {
pub(crate) seconds: u32,
}
+impl From<Infallible> for TimeOverflowError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for TimeOverflowError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
@@ -151,6 +172,10 @@ pub struct InvalidHeightError {
pub(crate) utxo_mined_at: crate::BlockHeight,
}
+impl From<Infallible> for InvalidHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for InvalidHeightError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "is_satisfied_by arguments invalid (probably the wrong way around) chain_tip: {} utxo_mined_at: {}", self.chain_tip, self.utxo_mined_at
@@ -170,6 +195,10 @@ pub struct InvalidTimeError {
pub(crate) utxo_mined_at: crate::BlockMtp,
}
+impl From<Infallible> for InvalidTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for InvalidTimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "is_satisfied_by arguments invalid (probably the wrong way around) chain_tip: {} utxo_mined_at: {}", self.chain_tip, self.utxo_mined_at
diff --git a/units/src/parse_int.rs b/units/src/parse_int.rs
index ebb68d8e..8b115d43 100644
--- a/units/src/parse_int.rs
+++ b/units/src/parse_int.rs
@@ -31,6 +31,10 @@ pub struct ParseIntError {
pub(crate) source: core::num::ParseIntError,
}
+impl From<Infallible> for ParseIntError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl fmt::Display for ParseIntError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let signed = if self.is_signed { "signed" } else { "unsigned" };
diff --git a/units/src/result.rs b/units/src/result.rs
index d0b74fd7..0362c48b 100644
--- a/units/src/result.rs
+++ b/units/src/result.rs
@@ -331,6 +331,10 @@ impl_opt_ext!(Amount, SignedAmount, u64, i64, FeeRate, Weight);
#[non_exhaustive]
pub struct NumOpError(MathOp);
+impl From<Infallible> for NumOpError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
impl NumOpError {
/// Constructs a [`NumOpError`] caused by `op`.
pub(crate) const fn while_doing(op: MathOp) -> Self { Self(op) }
Why this scored 19/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.