From e2d03fef72f4fcbdacd1cbecc0c2dddfbdeb3031 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 14 May 2025 14:19:56 +1000 Subject: [PATCH] units: Manually implement serde traits For types that have private inner fields we would like to manually implement `serde` traits instead of deriving them. This hardens the crate against making future mistakes if we want to change the inner fields. Do so for: - `Weight` - `BlockTime` - `NumberOfBlocks` - `NumberOf512seconds` --- units/src/locktime/relative.rs | 48 +++++++++++++++++++++++++++++++--- units/src/time.rs | 28 +++++++++++++++++--- units/src/weight.rs | 28 +++++++++++++++++--- 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/units/src/locktime/relative.rs b/units/src/locktime/relative.rs index 6bd5e6c13..4aa330710 100644 --- a/units/src/locktime/relative.rs +++ b/units/src/locktime/relative.rs @@ -8,7 +8,7 @@ use core::fmt; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[deprecated(since = "TBD", note = "use `NumberOfBlocks` instead")] #[doc(hidden)] @@ -16,7 +16,6 @@ pub type Height = NumberOfBlocks; /// A relative lock time lock-by-blockheight value. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct NumberOfBlocks(u16); impl NumberOfBlocks { @@ -92,6 +91,28 @@ impl fmt::Display for NumberOfBlocks { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } +#[cfg(feature = "serde")] +impl Serialize for NumberOfBlocks { + #[inline] + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + u16::serialize(&self.to_height(), s) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for NumberOfBlocks { + #[inline] + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self::from_height(u16::deserialize(d)?)) + } +} + #[deprecated(since = "TBD", note = "use `NumberOf512Seconds` instead")] #[doc(hidden)] pub type Time = NumberOf512Seconds; @@ -100,7 +121,6 @@ pub type Time = NumberOf512Seconds; /// /// For BIP 68 relative lock-by-blocktime locks, time is measured in 512 second intervals. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct NumberOf512Seconds(u16); impl NumberOf512Seconds { @@ -213,6 +233,28 @@ impl fmt::Display for NumberOf512Seconds { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } +#[cfg(feature = "serde")] +impl Serialize for NumberOf512Seconds { + #[inline] + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + u16::serialize(&self.to_512_second_intervals(), s) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for NumberOf512Seconds { + #[inline] + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self::from_512_second_intervals(u16::deserialize(d)?)) + } +} + /// 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 { diff --git a/units/src/time.rs b/units/src/time.rs index 2534763b9..c58c3ae69 100644 --- a/units/src/time.rs +++ b/units/src/time.rs @@ -9,11 +9,10 @@ #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; mod encapsulate { - #[cfg(feature = "serde")] - use serde::{Deserialize, Serialize}; - /// A Bitcoin block timestamp. /// /// > Each block contains a Unix time timestamp. In addition to serving as a source of variation for @@ -27,7 +26,6 @@ mod encapsulate { /// /// ref: #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BlockTime(u32); impl BlockTime { @@ -53,6 +51,28 @@ impl From for u32 { fn from(t: BlockTime) -> Self { t.to_u32() } } +#[cfg(feature = "serde")] +impl Serialize for BlockTime { + #[inline] + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + u32::serialize(&self.to_u32(), s) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for BlockTime { + #[inline] + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self::from_u32(u32::deserialize(d)?)) + } +} + #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for BlockTime { #[inline] diff --git a/units/src/weight.rs b/units/src/weight.rs index 87b981053..269332592 100644 --- a/units/src/weight.rs +++ b/units/src/weight.rs @@ -7,21 +7,19 @@ use core::{fmt, ops}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// The factor that non-witness serialization data is multiplied by during weight calculation. pub const WITNESS_SCALE_FACTOR: usize = 4; mod encapsulate { - #[cfg(feature = "serde")] - use serde::{Deserialize, Serialize}; /// The weight of a transaction or block. /// /// This is an integer newtype representing [`Weight`] in `wu`. It provides protection /// against mixing up types as well as basic formatting features. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - #[cfg_attr(feature = "serde", serde(transparent))] pub struct Weight(u64); impl Weight { @@ -263,6 +261,28 @@ impl<'a> core::iter::Sum<&'a Weight> for Weight { crate::impl_parse_str_from_int_infallible!(Weight, u64, from_wu); +#[cfg(feature = "serde")] +impl Serialize for Weight { + #[inline] + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + u64::serialize(&self.to_wu(), s) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Weight { + #[inline] + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self::from_wu(u64::deserialize(d)?)) + } +} + #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Weight { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result {