Merge rust-bitcoin/rust-bitcoin#3666: Implement serde modules for `FeeRate`

f5eb8f4747 api: Run just check-api (Tobin C. Harding)
472b1d3ff3 units: Add serde regression test (Tobin C. Harding)
dedae8acf2 Implement custom serde modules for FeeRate (Tobin C. Harding)
d94e5f03e6 Move fee_rate.rs to module (Tobin C. Harding)
c3c1f6f82d Add missing license comment to test file (Tobin C. Harding)

Pull request description:

  Implement and enforce explicit unit when serializing. This is as we do for `Amount` (see #3672 for similar).

  To test it, and as part of the 1.0 effort; add regression tests for `serde` stuff in `units`.

  With this applied one must use attributes to serialize `FeeRate`.

  ```rust
      #[derive(Serialize, Deserialize)]
      pub struct Foo {
          #[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_kwu")]
          pub fee_rate: FeeRate,
      }
  ```

ACKs for top commit:
  apoelstra:
    ACK f5eb8f4747a7cd303cad2b7f8f442bb31862c52a; successfully ran local tests; great idea!

Tree-SHA512: 0968ead568b1e3142efd4c0e856192ddde0f441de84215cbb0950b60a56922f1abaf6d4ccfe243b722a6883c0a927d26bcfba979acf3ca84c4f21baba73af764
This commit is contained in:
merge-script 2025-01-03 16:31:34 +00:00
commit 6501b0d781
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
9 changed files with 398 additions and 8 deletions

View File

@ -129,6 +129,7 @@ name = "bitcoin-units"
version = "0.2.0"
dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
"serde",
"serde_json",

View File

@ -128,6 +128,7 @@ name = "bitcoin-units"
version = "0.2.0"
dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
"serde",
"serde_json",

View File

@ -3,6 +3,7 @@
#[non_exhaustive] pub struct bitcoin_units::amount::MissingDenominationError
#[non_exhaustive] pub struct bitcoin_units::amount::PossiblyConfusingDenominationError(_)
#[non_exhaustive] pub struct bitcoin_units::amount::UnknownDenominationError(_)
#[non_exhaustive] pub struct bitcoin_units::fee_rate::serde::OverflowError
#[non_exhaustive] pub struct bitcoin_units::locktime::absolute::ConversionError
#[non_exhaustive] pub struct bitcoin_units::parse::ParseIntError
impl bitcoin_units::Amount
@ -52,6 +53,7 @@ impl core::clone::Clone for bitcoin_units::block::BlockHeight
impl core::clone::Clone for bitcoin_units::block::BlockInterval
impl core::clone::Clone for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::clone::Clone for bitcoin_units::fee_rate::FeeRate
impl core::clone::Clone for bitcoin_units::fee_rate::serde::OverflowError
impl core::clone::Clone for bitcoin_units::locktime::absolute::ConversionError
impl core::clone::Clone for bitcoin_units::locktime::absolute::Height
impl core::clone::Clone for bitcoin_units::locktime::absolute::ParseHeightError
@ -82,6 +84,7 @@ impl core::cmp::Eq for bitcoin_units::block::BlockHeight
impl core::cmp::Eq for bitcoin_units::block::BlockInterval
impl core::cmp::Eq for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::cmp::Eq for bitcoin_units::fee_rate::FeeRate
impl core::cmp::Eq for bitcoin_units::fee_rate::serde::OverflowError
impl core::cmp::Eq for bitcoin_units::locktime::absolute::ConversionError
impl core::cmp::Eq for bitcoin_units::locktime::absolute::Height
impl core::cmp::Eq for bitcoin_units::locktime::absolute::ParseHeightError
@ -122,6 +125,7 @@ impl core::cmp::PartialEq for bitcoin_units::block::BlockHeight
impl core::cmp::PartialEq for bitcoin_units::block::BlockInterval
impl core::cmp::PartialEq for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::cmp::PartialEq for bitcoin_units::fee_rate::FeeRate
impl core::cmp::PartialEq for bitcoin_units::fee_rate::serde::OverflowError
impl core::cmp::PartialEq for bitcoin_units::locktime::absolute::ConversionError
impl core::cmp::PartialEq for bitcoin_units::locktime::absolute::Height
impl core::cmp::PartialEq for bitcoin_units::locktime::absolute::ParseHeightError
@ -170,6 +174,7 @@ impl core::convert::From<bitcoin_units::weight::Weight> for u64
impl core::convert::From<core::convert::Infallible> for bitcoin_units::amount::ParseAmountError
impl core::convert::From<core::convert::Infallible> for bitcoin_units::amount::ParseDenominationError
impl core::convert::From<core::convert::Infallible> for bitcoin_units::amount::ParseError
impl core::convert::From<core::convert::Infallible> for bitcoin_units::fee_rate::serde::OverflowError
impl core::convert::From<core::convert::Infallible> for bitcoin_units::parse::PrefixedHexError
impl core::convert::From<core::convert::Infallible> for bitcoin_units::parse::UnprefixedHexError
impl core::convert::From<u16> for bitcoin_units::locktime::relative::Height
@ -218,6 +223,7 @@ impl core::error::Error for bitcoin_units::amount::PossiblyConfusingDenomination
impl core::error::Error for bitcoin_units::amount::TooPreciseError
impl core::error::Error for bitcoin_units::amount::UnknownDenominationError
impl core::error::Error for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::error::Error for bitcoin_units::fee_rate::serde::OverflowError
impl core::error::Error for bitcoin_units::locktime::absolute::ConversionError
impl core::error::Error for bitcoin_units::locktime::absolute::ParseHeightError
impl core::error::Error for bitcoin_units::locktime::absolute::ParseTimeError
@ -244,6 +250,7 @@ impl core::fmt::Debug for bitcoin_units::block::BlockHeight
impl core::fmt::Debug for bitcoin_units::block::BlockInterval
impl core::fmt::Debug for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::fmt::Debug for bitcoin_units::fee_rate::FeeRate
impl core::fmt::Debug for bitcoin_units::fee_rate::serde::OverflowError
impl core::fmt::Debug for bitcoin_units::locktime::absolute::ConversionError
impl core::fmt::Debug for bitcoin_units::locktime::absolute::Height
impl core::fmt::Debug for bitcoin_units::locktime::absolute::ParseHeightError
@ -274,6 +281,7 @@ impl core::fmt::Display for bitcoin_units::block::BlockHeight
impl core::fmt::Display for bitcoin_units::block::BlockInterval
impl core::fmt::Display for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::fmt::Display for bitcoin_units::fee_rate::FeeRate
impl core::fmt::Display for bitcoin_units::fee_rate::serde::OverflowError
impl core::fmt::Display for bitcoin_units::locktime::absolute::ConversionError
impl core::fmt::Display for bitcoin_units::locktime::absolute::Height
impl core::fmt::Display for bitcoin_units::locktime::absolute::ParseHeightError
@ -333,6 +341,7 @@ impl core::marker::Freeze for bitcoin_units::block::BlockHeight
impl core::marker::Freeze for bitcoin_units::block::BlockInterval
impl core::marker::Freeze for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::marker::Freeze for bitcoin_units::fee_rate::FeeRate
impl core::marker::Freeze for bitcoin_units::fee_rate::serde::OverflowError
impl core::marker::Freeze for bitcoin_units::locktime::absolute::ConversionError
impl core::marker::Freeze for bitcoin_units::locktime::absolute::Height
impl core::marker::Freeze for bitcoin_units::locktime::absolute::ParseHeightError
@ -364,6 +373,7 @@ impl core::marker::Send for bitcoin_units::block::BlockHeight
impl core::marker::Send for bitcoin_units::block::BlockInterval
impl core::marker::Send for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::marker::Send for bitcoin_units::fee_rate::FeeRate
impl core::marker::Send for bitcoin_units::fee_rate::serde::OverflowError
impl core::marker::Send for bitcoin_units::locktime::absolute::ConversionError
impl core::marker::Send for bitcoin_units::locktime::absolute::Height
impl core::marker::Send for bitcoin_units::locktime::absolute::ParseHeightError
@ -394,6 +404,7 @@ impl core::marker::StructuralPartialEq for bitcoin_units::block::BlockHeight
impl core::marker::StructuralPartialEq for bitcoin_units::block::BlockInterval
impl core::marker::StructuralPartialEq for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::marker::StructuralPartialEq for bitcoin_units::fee_rate::FeeRate
impl core::marker::StructuralPartialEq for bitcoin_units::fee_rate::serde::OverflowError
impl core::marker::StructuralPartialEq for bitcoin_units::locktime::absolute::ConversionError
impl core::marker::StructuralPartialEq for bitcoin_units::locktime::absolute::Height
impl core::marker::StructuralPartialEq for bitcoin_units::locktime::absolute::ParseHeightError
@ -425,6 +436,7 @@ impl core::marker::Sync for bitcoin_units::block::BlockHeight
impl core::marker::Sync for bitcoin_units::block::BlockInterval
impl core::marker::Sync for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::marker::Sync for bitcoin_units::fee_rate::FeeRate
impl core::marker::Sync for bitcoin_units::fee_rate::serde::OverflowError
impl core::marker::Sync for bitcoin_units::locktime::absolute::ConversionError
impl core::marker::Sync for bitcoin_units::locktime::absolute::Height
impl core::marker::Sync for bitcoin_units::locktime::absolute::ParseHeightError
@ -456,6 +468,7 @@ impl core::marker::Unpin for bitcoin_units::block::BlockHeight
impl core::marker::Unpin for bitcoin_units::block::BlockInterval
impl core::marker::Unpin for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::marker::Unpin for bitcoin_units::fee_rate::FeeRate
impl core::marker::Unpin for bitcoin_units::fee_rate::serde::OverflowError
impl core::marker::Unpin for bitcoin_units::locktime::absolute::ConversionError
impl core::marker::Unpin for bitcoin_units::locktime::absolute::Height
impl core::marker::Unpin for bitcoin_units::locktime::absolute::ParseHeightError
@ -556,6 +569,7 @@ impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::block::BlockHeig
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::block::BlockInterval
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::fee_rate::FeeRate
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::fee_rate::serde::OverflowError
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::locktime::absolute::ConversionError
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::locktime::absolute::Height
impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_units::locktime::absolute::ParseHeightError
@ -587,6 +601,7 @@ impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::block::BlockHeight
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::block::BlockInterval
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::block::TooBigForRelativeBlockHeightError
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::fee_rate::FeeRate
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::fee_rate::serde::OverflowError
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::locktime::absolute::ConversionError
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::locktime::absolute::Height
impl core::panic::unwind_safe::UnwindSafe for bitcoin_units::locktime::absolute::ParseHeightError
@ -612,7 +627,6 @@ impl core::str::traits::FromStr for bitcoin_units::locktime::relative::Time
impl core::str::traits::FromStr for bitcoin_units::weight::Weight
impl serde::ser::Serialize for bitcoin_units::block::BlockHeight
impl serde::ser::Serialize for bitcoin_units::block::BlockInterval
impl serde::ser::Serialize for bitcoin_units::fee_rate::FeeRate
impl serde::ser::Serialize for bitcoin_units::locktime::absolute::Height
impl serde::ser::Serialize for bitcoin_units::locktime::absolute::Time
impl serde::ser::Serialize for bitcoin_units::locktime::relative::Height
@ -644,7 +658,6 @@ impl<'a> core::ops::arith::Sub<&'a bitcoin_units::fee_rate::FeeRate> for &bitcoi
impl<'a> core::ops::arith::Sub<&'a bitcoin_units::weight::Weight> for &bitcoin_units::weight::Weight
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::block::BlockHeight
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::block::BlockInterval
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::fee_rate::FeeRate
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::locktime::absolute::Height
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::locktime::absolute::Time
impl<'de> serde::de::Deserialize<'de> for bitcoin_units::locktime::relative::Height
@ -1034,7 +1047,6 @@ pub fn bitcoin_units::fee_rate::FeeRate::add_assign(&mut self, rhs: bitcoin_unit
pub fn bitcoin_units::fee_rate::FeeRate::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_units::fee_rate::FeeRate::clone(&self) -> bitcoin_units::fee_rate::FeeRate
pub fn bitcoin_units::fee_rate::FeeRate::cmp(&self, other: &bitcoin_units::fee_rate::FeeRate) -> core::cmp::Ordering
pub fn bitcoin_units::fee_rate::FeeRate::deserialize<__D>(__deserializer: __D) -> core::result::Result<Self, <__D as serde::de::Deserializer>::Error> where __D: serde::de::Deserializer<'de>
pub fn bitcoin_units::fee_rate::FeeRate::eq(&self, other: &bitcoin_units::fee_rate::FeeRate) -> bool
pub fn bitcoin_units::fee_rate::FeeRate::fee_vb(self, vb: u64) -> core::option::Option<bitcoin_units::Amount>
pub fn bitcoin_units::fee_rate::FeeRate::fee_wu(self, weight: bitcoin_units::weight::Weight) -> core::option::Option<bitcoin_units::Amount>
@ -1044,7 +1056,6 @@ pub fn bitcoin_units::fee_rate::FeeRate::from_str(s: &str) -> core::result::Resu
pub fn bitcoin_units::fee_rate::FeeRate::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
pub fn bitcoin_units::fee_rate::FeeRate::mul(self, rhs: bitcoin_units::weight::Weight) -> Self::Output
pub fn bitcoin_units::fee_rate::FeeRate::partial_cmp(&self, other: &bitcoin_units::fee_rate::FeeRate) -> core::option::Option<core::cmp::Ordering>
pub fn bitcoin_units::fee_rate::FeeRate::serialize<__S>(&self, __serializer: __S) -> core::result::Result<<__S as serde::ser::Serializer>::Ok, <__S as serde::ser::Serializer>::Error> where __S: serde::ser::Serializer
pub fn bitcoin_units::fee_rate::FeeRate::sub(self, rhs: &bitcoin_units::fee_rate::FeeRate) -> Self::Output
pub fn bitcoin_units::fee_rate::FeeRate::sub(self, rhs: bitcoin_units::fee_rate::FeeRate) -> Self::Output
pub fn bitcoin_units::fee_rate::FeeRate::sub_assign(&mut self, rhs: &bitcoin_units::fee_rate::FeeRate)
@ -1054,6 +1065,22 @@ pub fn bitcoin_units::fee_rate::FeeRate::sum<I>(iter: I) -> Self where I: core::
pub fn bitcoin_units::fee_rate::FeeRate::try_from(s: &str) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::fee_rate::FeeRate::try_from(s: alloc::boxed::Box<str>) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::fee_rate::FeeRate::try_from(s: alloc::string::String) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::fee_rate::serde::OverflowError::clone(&self) -> bitcoin_units::fee_rate::serde::OverflowError
pub fn bitcoin_units::fee_rate::serde::OverflowError::eq(&self, other: &bitcoin_units::fee_rate::serde::OverflowError) -> bool
pub fn bitcoin_units::fee_rate::serde::OverflowError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
pub fn bitcoin_units::fee_rate::serde::OverflowError::from(never: core::convert::Infallible) -> Self
pub fn bitcoin_units::fee_rate::serde::as_sat_per_kwu::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<bitcoin_units::fee_rate::FeeRate, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_kwu::opt::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<core::option::Option<bitcoin_units::fee_rate::FeeRate>, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_kwu::opt::serialize<S: serde::ser::Serializer>(f: &core::option::Option<bitcoin_units::fee_rate::FeeRate>, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_kwu::serialize<S: serde::ser::Serializer>(f: &bitcoin_units::fee_rate::FeeRate, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<bitcoin_units::fee_rate::FeeRate, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::opt::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<core::option::Option<bitcoin_units::fee_rate::FeeRate>, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::opt::serialize<S: serde::ser::Serializer>(f: &core::option::Option<bitcoin_units::fee_rate::FeeRate>, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::serialize<S: serde::ser::Serializer>(f: &bitcoin_units::fee_rate::FeeRate, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<bitcoin_units::fee_rate::FeeRate, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::opt::deserialize<'d, D: serde::de::Deserializer<'d>>(d: D) -> core::result::Result<core::option::Option<bitcoin_units::fee_rate::FeeRate>, <D as serde::de::Deserializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::opt::serialize<S: serde::ser::Serializer>(f: &core::option::Option<bitcoin_units::fee_rate::FeeRate>, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::serialize<S: serde::ser::Serializer>(f: &bitcoin_units::fee_rate::FeeRate, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::locktime::absolute::ConversionError::clone(&self) -> bitcoin_units::locktime::absolute::ConversionError
pub fn bitcoin_units::locktime::absolute::ConversionError::eq(&self, other: &bitcoin_units::locktime::absolute::ConversionError) -> bool
pub fn bitcoin_units::locktime::absolute::ConversionError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
@ -1208,6 +1235,13 @@ pub mod bitcoin_units::amount::serde::as_str
pub mod bitcoin_units::amount::serde::as_str::opt
pub mod bitcoin_units::block
pub mod bitcoin_units::fee_rate
pub mod bitcoin_units::fee_rate::serde
pub mod bitcoin_units::fee_rate::serde::as_sat_per_kwu
pub mod bitcoin_units::fee_rate::serde::as_sat_per_kwu::opt
pub mod bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil
pub mod bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::opt
pub mod bitcoin_units::fee_rate::serde::as_sat_per_vb_floor
pub mod bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::opt
pub mod bitcoin_units::locktime
pub mod bitcoin_units::locktime::absolute
pub mod bitcoin_units::locktime::relative

View File

@ -25,6 +25,7 @@ arbitrary = { version = "1.4", optional = true }
[dev-dependencies]
internals = { package = "bitcoin-internals", version = "0.4.0", features = ["test-serde"] }
bincode = "1.3.1"
serde_test = "1.0"
serde_json = "1.0"

View File

@ -2,12 +2,13 @@
//! Implements `FeeRate` and assoctiated features.
#[cfg(feature = "serde")]
pub mod serde;
use core::{fmt, ops};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::amount::Amount;
use crate::weight::Weight;
@ -17,8 +18,6 @@ use crate::weight::Weight;
/// This is an integer newtype representing fee rate in `sat/kwu`. It provides protection against mixing
/// up the 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 FeeRate(u64);
impl FeeRate {

255
units/src/fee_rate/serde.rs Normal file
View File

@ -0,0 +1,255 @@
// SPDX-License-Identifier: CC0-1.0
// Module implements standardized serde-specific trait methods.
#![allow(missing_docs)]
#![allow(clippy::trivially_copy_pass_by_ref)]
//! This module adds serde serialization and deserialization support for amounts.
//!
//! Since there is not a default way to serialize and deserialize Amounts, multiple
//! ways are supported and it's up to the user to decide which serialiation to use.
//!
//! The provided modules can be used as follows:
//!
//! ```
//! use serde::{Serialize, Deserialize};
//! use bitcoin_units::FeeRate;
//!
//! #[derive(Serialize, Deserialize)]
//! pub struct Foo {
//! #[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_kwu")]
//! pub fee_rate: FeeRate,
//! }
//! ```
use core::fmt;
pub mod as_sat_per_kwu {
//! Serialize and deserialize [`FeeRate`] denominated in satoshis per 1000 weight units.
//!
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu")]`.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::FeeRate;
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
u64::serialize(&f.to_sat_per_kwu(), s)
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
Ok(FeeRate::from_sat_per_kwu(u64::deserialize(d)?))
}
pub mod opt {
//! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per 1000 weight units.
//!
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu::opt")]`.
use core::fmt;
use serde::{de, Deserialize, Deserializer, Serializer};
use crate::FeeRate;
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
match *f {
Some(f) => s.serialize_some(&f.to_sat_per_kwu()),
None => s.serialize_none(),
}
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
struct VisitOpt;
impl<'de> de::Visitor<'de> for VisitOpt {
type Value = Option<FeeRate>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An Option<FeeRate>")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(FeeRate::from_sat_per_kwu(u64::deserialize(d)?)))
}
}
d.deserialize_option(VisitOpt)
}
}
}
pub mod as_sat_per_vb_floor {
//! Serialize and deserialize [`FeeRate`] denominated in satoshis per virtual byte.
//!
//! When serializing use floor division to convert per kwu to per virtual byte.
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_floor")]`.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::fee_rate::serde::OverflowError;
use crate::fee_rate::FeeRate;
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
u64::serialize(&f.to_sat_per_vb_floor(), s)
}
// Errors on overflow.
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
Ok(FeeRate::from_sat_per_vb(u64::deserialize(d)?)
.ok_or(OverflowError)
.map_err(serde::de::Error::custom)?)
}
pub mod opt {
//! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per virtual byte.
//!
//! When serializing use floor division to convert per kwu to per virtual byte.
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_floor::opt")]`.
use core::fmt;
use serde::{de, Deserialize, Deserializer, Serializer};
use crate::fee_rate::serde::OverflowError;
use crate::fee_rate::FeeRate;
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
match *f {
Some(f) => s.serialize_some(&f.to_sat_per_vb_floor()),
None => s.serialize_none(),
}
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
struct VisitOpt;
impl<'de> de::Visitor<'de> for VisitOpt {
type Value = Option<FeeRate>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An Option<FeeRate>")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
FeeRate::from_sat_per_vb(u64::deserialize(d)?)
.ok_or(OverflowError)
.map_err(serde::de::Error::custom)?,
))
}
}
d.deserialize_option(VisitOpt)
}
}
}
pub mod as_sat_per_vb_ceil {
//! Serialize and deserialize [`FeeRate`] denominated in satoshis per virtual byte.
//!
//! When serializing use ceil division to convert per kwu to per virtual byte.
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb")]`.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::fee_rate::serde::OverflowError;
use crate::fee_rate::FeeRate;
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
u64::serialize(&f.to_sat_per_vb_ceil(), s)
}
// Errors on overflow.
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
Ok(FeeRate::from_sat_per_vb(u64::deserialize(d)?)
.ok_or(OverflowError)
.map_err(serde::de::Error::custom)?)
}
pub mod opt {
//! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per virtual byte.
//!
//! When serializing use ceil division to convert per kwu to per virtual byte.
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil::opt")]`.
use core::fmt;
use serde::{de, Deserialize, Deserializer, Serializer};
use crate::fee_rate::serde::OverflowError;
use crate::fee_rate::FeeRate;
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
match *f {
Some(f) => s.serialize_some(&f.to_sat_per_vb_ceil()),
None => s.serialize_none(),
}
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
struct VisitOpt;
impl<'de> de::Visitor<'de> for VisitOpt {
type Value = Option<FeeRate>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An Option<FeeRate>")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
FeeRate::from_sat_per_vb(u64::deserialize(d)?)
.ok_or(OverflowError)
.map_err(serde::de::Error::custom)?,
))
}
}
d.deserialize_option(VisitOpt)
}
}
}
/// Overflow occurred while deserializing fee rate per virtual byte.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct OverflowError;
internals::impl_from_infallible!(OverflowError);
impl fmt::Display for OverflowError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "overflow occurred while deserializing fee rate per virtual byte")
}
}
#[cfg(feature = "std")]
impl std::error::Error for OverflowError {}

View File

@ -1,3 +1,5 @@
// SPDX-License-Identifier: CC0-1.0
//! Test the API surface of `units`.
//!
//! The point of these tests are to check the API surface as opposed to test the API functionality.

Binary file not shown.

97
units/tests/serde.rs Normal file
View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: CC0-1.0
//! Test the `serde` implementations for types in `units`.
#![cfg(feature = "alloc")]
#![cfg(feature = "serde")]
use bincode::serialize;
use bitcoin_units::locktime::{absolute, relative};
use bitcoin_units::{Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight};
use serde::{Deserialize, Serialize};
/// A struct that includes all the types that implement or support `serde` traits.
#[derive(Debug, Serialize, Deserialize)]
struct Serde {
#[serde(with = "bitcoin_units::amount::serde::as_sat")]
unsigned_as_sat: Amount,
#[serde(with = "bitcoin_units::amount::serde::as_btc")]
unsigned_as_btc: Amount,
#[serde(with = "bitcoin_units::amount::serde::as_sat::opt")]
unsigned_opt_as_sat: Option<Amount>,
#[serde(with = "bitcoin_units::amount::serde::as_btc::opt")]
unsigned_opt_as_btc: Option<Amount>,
#[serde(with = "bitcoin_units::amount::serde::as_sat")]
signed_as_sat: SignedAmount,
#[serde(with = "bitcoin_units::amount::serde::as_btc")]
signed_as_btc: SignedAmount,
#[serde(with = "bitcoin_units::amount::serde::as_sat::opt")]
signed_opt_as_sat: Option<SignedAmount>,
#[serde(with = "bitcoin_units::amount::serde::as_btc::opt")]
signed_opt_as_btc: Option<SignedAmount>,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_vb_floor")]
vb_floor: FeeRate,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil")]
vb_ceil: FeeRate,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_kwu")]
kwu: FeeRate,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_vb_floor::opt")]
opt_vb_floor: Option<FeeRate>,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_vb_ceil::opt")]
opt_vb_ceil: Option<FeeRate>,
#[serde(with = "bitcoin_units::fee_rate::serde::as_sat_per_kwu::opt")]
opt_kwu: Option<FeeRate>,
a: BlockHeight,
b: BlockInterval,
c: absolute::Height,
d: absolute::Time,
e: relative::Height,
f: relative::Time,
g: Weight,
}
impl Serde {
/// Constructs an arbitrary instance.
fn new() -> Self {
Self {
unsigned_as_sat: Amount::MAX,
unsigned_as_btc: Amount::MAX,
unsigned_opt_as_sat: Some(Amount::MAX),
unsigned_opt_as_btc: Some(Amount::MAX),
signed_as_sat: SignedAmount::MAX,
signed_as_btc: SignedAmount::MAX,
signed_opt_as_sat: Some(SignedAmount::MAX),
signed_opt_as_btc: Some(SignedAmount::MAX),
vb_floor: FeeRate::BROADCAST_MIN,
vb_ceil: FeeRate::BROADCAST_MIN,
kwu: FeeRate::BROADCAST_MIN,
opt_vb_floor: Some(FeeRate::BROADCAST_MIN),
opt_vb_ceil: Some(FeeRate::BROADCAST_MIN),
opt_kwu: Some(FeeRate::BROADCAST_MIN),
a: BlockHeight::MAX,
b: BlockInterval::MAX,
c: absolute::Height::MAX,
d: absolute::Time::MAX,
e: relative::Height::MAX,
f: relative::Time::MAX,
g: Weight::MAX,
}
}
}
#[test]
fn serde_regression() {
let t = Serde::new();
let got = serialize(&t).unwrap();
let want = include_bytes!("data/serde_bincode");
assert_eq!(got, want);
}