Merge rust-bitcoin/rust-bitcoin#2582: fix: Manually implement serde traits
911f8cbd6a
fix: Manually implement serde traits (Tobin C. Harding) Pull request description: Currently we are deriving the serde traits for the `absolute::{Height, Time}` types, this is incorrect because we maintain an invariant on the inner `u32` of both types that it is above or below the threshold. Manually implement the serde traits and pass the deserialized `u32` to `from_consensus` to maintain the invariant. Close: #2559 ACKs for top commit: apoelstra: ACK911f8cbd6a
sanket1729: ACK911f8cbd6a
. Tree-SHA512: 28f9f3e23b2016e00216e5e94f35e22a5bfa6b29e1dd30fa3351575eafc2e47dbd98aa8e51631de2f09eec8d881b0e5e79231185c7a293c1ba0170a90fbbd19d
This commit is contained in:
commit
51bb6a2d69
|
@ -33,6 +33,10 @@ extern crate std;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub extern crate serde;
|
pub extern crate serde;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
mod test_macros;
|
||||||
|
|
||||||
pub mod amount;
|
pub mod amount;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod locktime;
|
pub mod locktime;
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use internals::write_err;
|
use internals::write_err;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
@ -28,7 +25,6 @@ pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
|
||||||
|
|
||||||
/// An absolute block height, guaranteed to always contain a valid height value.
|
/// An absolute block height, guaranteed to always contain a valid height value.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct Height(u32);
|
pub struct Height(u32);
|
||||||
|
|
||||||
impl Height {
|
impl Height {
|
||||||
|
@ -100,13 +96,33 @@ impl From<ParseError> for ParseHeightError {
|
||||||
fn from(value: ParseError) -> Self { Self(value) }
|
fn from(value: ParseError) -> Self { Self(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for Height {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let u = u32::deserialize(deserializer)?;
|
||||||
|
Ok(Height::from_consensus(u).map_err(serde::de::Error::custom)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for Height {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.to_consensus_u32().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
|
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
|
||||||
///
|
///
|
||||||
/// Note that there is no manipulation of the inner value during construction or when using
|
/// Note that there is no manipulation of the inner value during construction or when using
|
||||||
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
|
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
|
||||||
/// threshold) seconds since epoch'.
|
/// threshold) seconds since epoch'.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct Time(u32);
|
pub struct Time(u32);
|
||||||
|
|
||||||
impl Time {
|
impl Time {
|
||||||
|
@ -155,6 +171,27 @@ impl fmt::Display for Time {
|
||||||
|
|
||||||
crate::impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus));
|
crate::impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus));
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for Time {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let u = u32::deserialize(deserializer)?;
|
||||||
|
Ok(Time::from_consensus(u).map_err(serde::de::Error::custom)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for Time {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.to_consensus_u32().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Error returned when parsing block time fails.
|
/// Error returned when parsing block time fails.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct ParseTimeError(ParseError);
|
pub struct ParseTimeError(ParseError);
|
||||||
|
@ -364,4 +401,18 @@ mod tests {
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub fn encode_decode_height() {
|
||||||
|
serde_round_trip!(Height::ZERO);
|
||||||
|
serde_round_trip!(Height::MIN);
|
||||||
|
serde_round_trip!(Height::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub fn encode_decode_time() {
|
||||||
|
serde_round_trip!(Time::MIN);
|
||||||
|
serde_round_trip!(Time::MAX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
//! Bitcoin serde macros.
|
||||||
|
//!
|
||||||
|
//! This module provides internal macros used for unit tests.
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
macro_rules! serde_round_trip (
|
||||||
|
($var:expr) => ({
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
let encoded = serde_json::to_value(&$var).unwrap();
|
||||||
|
let decoded = serde_json::from_value(encoded).unwrap();
|
||||||
|
assert_eq!($var, decoded);
|
||||||
|
})
|
||||||
|
);
|
Loading…
Reference in New Issue