What changed, and why it matters
This is a routine code cleanup in the rust-bitcoin library. It wraps two error values inside new dedicated error types so callers can no longer directly read the raw number inside. The change does not alter what errors are produced, only how they are packaged. There is no security fix here.
No security action needed. Treat as normal refactoring/API cleanup.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit removes two TODO comments in units/src/locktime/relative/error.rs by introducing IncompatibleHeightError and IncompatibleTimeError structs. These wrap NumberOf512Seconds and NumberOfBlocks respectively, replacing the previous enum variants that exposed those types directly. Display and Error trait implementations are added, and the source() method now returns Some for the Incompatible variants. The public re-exports are updated and tests adjusted. No logic, validation, or behavior changes are present.
Changed components
units/src/locktime/relative/error.rsunits/src/locktime/relative/mod.rsInspect captured patch +56 / −18
diff --git a/units/src/locktime/relative/error.rs b/units/src/locktime/relative/error.rs
index 5283ac94..f5ddbcd9 100644
--- a/units/src/locktime/relative/error.rs
+++ b/units/src/locktime/relative/error.rs
@@ -78,8 +78,7 @@ pub enum IsSatisfiedByHeightError {
/// Satisfaction of the lock height value failed.
Satisfaction(InvalidHeightError),
/// Tried to satisfy a lock-by-height locktime using seconds.
- // TODO: Hide inner value in a new struct error type.
- Incompatible(NumberOf512Seconds),
+ Incompatible(IncompatibleHeightError),
}
impl From<Infallible> for IsSatisfiedByHeightError {
@@ -91,8 +90,7 @@ impl fmt::Display for IsSatisfiedByHeightError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Satisfaction(ref e) => write_err!(f, "satisfaction"; e),
- Self::Incompatible(time) =>
- write!(f, "tried to satisfy a lock-by-height locktime using seconds {}", time),
+ Self::Incompatible(ref e) => write_err!(f, "incompatible"; e),
}
}
}
@@ -103,19 +101,38 @@ impl std::error::Error for IsSatisfiedByHeightError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::Satisfaction(ref e) => Some(e),
- Self::Incompatible(_) => None,
+ Self::Incompatible(ref e) => Some(e),
}
}
}
+/// Error returned when `is_satisfied_by_height` fails with a block time.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct IncompatibleHeightError(pub(crate) NumberOf512Seconds);
+
+impl From<Infallible> for IncompatibleHeightError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for IncompatibleHeightError {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "tried to satisfy a lock-by-height locktime using seconds {}", self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for IncompatibleHeightError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+}
+
/// Error returned when `is_satisfied_by_time` fails.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IsSatisfiedByTimeError {
/// Satisfaction of the lock time value failed.
Satisfaction(InvalidTimeError),
/// Tried to satisfy a lock-by-time locktime using number of blocks.
- // TODO: Hide inner value in a new struct error type.
- Incompatible(NumberOfBlocks),
+ Incompatible(IncompatibleTimeError),
}
impl From<Infallible> for IsSatisfiedByTimeError {
@@ -127,8 +144,8 @@ impl fmt::Display for IsSatisfiedByTimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Satisfaction(ref e) => write_err!(f, "satisfaction"; e),
- Self::Incompatible(blocks) =>
- write!(f, "tried to satisfy a lock-by-time locktime using blocks {}", blocks),
+ Self::Incompatible(ref e) => write_err!(f, "incompatible"; e),
+
}
}
}
@@ -139,11 +156,31 @@ impl std::error::Error for IsSatisfiedByTimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::Satisfaction(ref e) => Some(e),
- Self::Incompatible(_) => None,
+ Self::Incompatible(ref e) => Some(e),
}
}
}
+/// Error returned when `is_satisfied_by_time` fails with a block height.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct IncompatibleTimeError(pub(crate) NumberOfBlocks);
+
+impl From<Infallible> for IncompatibleTimeError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for IncompatibleTimeError {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "tried to satisfy a lock-by-time locktime using blocks {}", self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for IncompatibleTimeError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+}
+
/// Error returned when the input time in seconds was too large to be encoded to a 16 bit 512 second interval.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimeOverflowError {
@@ -308,7 +345,7 @@ mod tests {
.unwrap_err();
assert!(!e.to_string().is_empty());
#[cfg(feature = "std")]
- assert!(e.source().is_none());
+ assert!(e.source().is_some());
// Satisfaction type
let e = height_lock
.is_satisfied_by_height(BlockHeight::from_u32(5), BlockHeight::from_u32(10))
@@ -324,7 +361,7 @@ mod tests {
.unwrap_err();
assert!(!e.to_string().is_empty());
#[cfg(feature = "std")]
- assert!(e.source().is_none());
+ assert!(e.source().is_some());
// Satisfaction type
let e = time_lock
.is_satisfied_by_time(BlockMtp::from_u32(5), BlockMtp::from_u32(10))
diff --git a/units/src/locktime/relative/mod.rs b/units/src/locktime/relative/mod.rs
index 777d3685..dde79d7a 100644
--- a/units/src/locktime/relative/mod.rs
+++ b/units/src/locktime/relative/mod.rs
@@ -22,8 +22,9 @@ use crate::{BlockHeight, BlockMtp, Sequence};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(no_inline)]
pub use self::error::{
- DisabledLockTimeError, InvalidHeightError, InvalidTimeError, IsSatisfiedByError,
- IsSatisfiedByHeightError, IsSatisfiedByTimeError, TimeOverflowError,
+ DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, InvalidHeightError,
+ InvalidTimeError, IsSatisfiedByError, IsSatisfiedByHeightError, IsSatisfiedByTimeError,
+ TimeOverflowError,
};
/// A relative lock time value, representing either a block height or time (512 second intervals).
@@ -236,7 +237,7 @@ impl LockTime {
Self::Blocks(blocks) => blocks
.is_satisfied_by(chain_tip, utxo_mined_at)
.map_err(IsSatisfiedByHeightError::Satisfaction),
- Self::Time(time) => Err(IsSatisfiedByHeightError::Incompatible(time)),
+ Self::Time(time) => Err(IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(time))),
}
}
@@ -258,7 +259,7 @@ impl LockTime {
Self::Time(time) => time
.is_satisfied_by(chain_tip, utxo_mined_at)
.map_err(IsSatisfiedByTimeError::Satisfaction),
- Self::Blocks(blocks) => Err(IsSatisfiedByTimeError::Incompatible(blocks)),
+ Self::Blocks(blocks) => Err(IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(blocks))),
}
}
@@ -838,7 +839,7 @@ mod tests {
let err = lock_by_time.is_satisfied_by_height(chain_tip, mined_at).unwrap_err();
let expected_time = NumberOf512Seconds::from_512_second_intervals(70);
- assert_eq!(err, IsSatisfiedByHeightError::Incompatible(expected_time));
+ assert_eq!(err, IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(expected_time)));
assert!(!format!("{}", err).is_empty());
}
@@ -866,7 +867,7 @@ mod tests {
let err = lock_by_height.is_satisfied_by_time(chain_tip, mined_at).unwrap_err();
let expected_height = NumberOfBlocks::from(10);
- assert_eq!(err, IsSatisfiedByTimeError::Incompatible(expected_height));
+ assert_eq!(err, IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(expected_height)));
assert!(!format!("{}", err).is_empty());
}
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.