Merge rust-bitcoin/rust-bitcoin#4506: units: Manually implement serde traits

e2d03fef72 units: Manually implement serde traits (Tobin C. Harding)

Pull request description:

  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`

ACKs for top commit:
  apoelstra:
    ACK e2d03fef72f4fcbdacd1cbecc0c2dddfbdeb3031; successfully ran local tests

Tree-SHA512: 6d3cc4106e44e08d59f8da6f8f3285923408ed05d1c0b2f6f2d83a22718744a38bf67381177856b5713810a5f5318d929daa2d12447c722628e0576fcd075e3d
This commit is contained in:
merge-script 2025-05-14 16:13:10 +00:00
commit 1b709eb460
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
3 changed files with 93 additions and 11 deletions

View File

@ -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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
u16::serialize(&self.to_height(), s)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for NumberOfBlocks {
#[inline]
fn deserialize<D>(d: D) -> Result<Self, D::Error>
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<S>(&self, s: S) -> Result<S::Ok, S::Error>
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: D) -> Result<Self, D::Error>
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 {

View File

@ -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: <https://en.bitcoin.it/wiki/Block_timestamp>
#[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<BlockTime> for u32 {
fn from(t: BlockTime) -> Self { t.to_u32() }
}
#[cfg(feature = "serde")]
impl Serialize for BlockTime {
#[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 BlockTime {
#[inline]
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::from_u32(u32::deserialize(d)?))
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for BlockTime {
#[inline]

View File

@ -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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
u64::serialize(&self.to_wu(), s)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Weight {
#[inline]
fn deserialize<D>(d: D) -> Result<Self, D::Error>
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<Self> {