Implement serde traits for absolute locktime types
What changed, and why it matters
This commit adds standard data serialization/deserialization support (serde) for two Bitcoin locktime types: block height and median-time-past. It is an API completeness change, not a security fix or vulnerability. The new code validates values during deserialization using the same existing constructors, so it does not introduce obvious weaknesses.
No security action required. Review as normal API completeness change; consider adding serde round-trip tests if not already present.
Security signals we found
No security-relevant signals in commit message or diff
New deserialization path uses existing validated constructors
No unsafe code, no cryptographic operations, no network/parsing surface expansion beyond serde feature
Evidence from the diff
The patch implements serde::Serialize and serde::Deserialize for the absolute locktime types Height and MedianTimePast in units/src/locktime/absolute/mod.rs. Serialization delegates to u32, and deserialization parses a u32 then calls the existing Self::from_u32 constructor, which enforces the crate’s existing validity rules and returns a custom error on invalid input. No logic changes were made to validation, arithmetic, or consensus-critical behavior.
Changed components
units/src/locktime/absolute/mod.rsHeightMedianTimePastserde featureInspect captured patch +46 / −0
diff --git a/units/src/locktime/absolute/mod.rs b/units/src/locktime/absolute/mod.rs
index 11720c22..a8c7430c 100644
--- a/units/src/locktime/absolute/mod.rs
+++ b/units/src/locktime/absolute/mod.rs
@@ -13,6 +13,8 @@ use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use internals::error::InputString;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
use self::error::ParseError;
#[cfg(doc)]
@@ -589,6 +591,28 @@ impl fmt::Display for Height {
parse_int::impl_parse_str!(Height, ParseHeightError, parser(Height::from_u32));
+#[cfg(feature = "serde")]
+impl Serialize for Height {
+ #[inline]
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ u32::serialize(&self.to_u32(), s)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> Deserialize<'de> for Height {
+ #[inline]
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Self::from_u32(u32::deserialize(d)?).map_err(serde::de::Error::custom)
+ }
+}
+
/// The median timestamp of 11 consecutive blocks, representing "the timestamp" of the
/// final block for locktime-checking purposes.
///
@@ -720,6 +744,28 @@ where
}
}
+#[cfg(feature = "serde")]
+impl Serialize for MedianTimePast {
+ #[inline]
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ u32::serialize(&self.to_u32(), s)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> Deserialize<'de> for MedianTimePast {
+ #[inline]
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Self::from_u32(u32::deserialize(d)?).map_err(serde::de::Error::custom)
+ }
+}
+
/// Returns true if `n` is a block height i.e., less than 500,000,000.
#[inline]
pub const fn is_block_height(n: u32) -> bool { n < LOCK_TIME_THRESHOLD }
Why this scored 18/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.