diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index cb68c3543..60e151355 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -204,14 +204,15 @@ pub mod amount { #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] + pub use units::CheckedSum; + #[cfg(feature = "serde")] + pub use units::amount::serde; pub use units::amount::{ - Amount, CheckedSum, Denomination, Display, InvalidCharacterError, MissingDenominationError, + Amount, Denomination, Display, InvalidCharacterError, MissingDenominationError, MissingDigitsError, OutOfRangeError, ParseAmountError, ParseDenominationError, ParseError, PossiblyConfusingDenominationError, SignedAmount, TooPreciseError, UnknownDenominationError, }; - #[cfg(feature = "serde")] - pub use units::amount::serde; impl Decodable for Amount { #[inline] diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs index b08e6ebb4..19b4a9832 100644 --- a/units/src/amount/mod.rs +++ b/units/src/amount/mod.rs @@ -26,6 +26,7 @@ use core::str::FromStr; use arbitrary::{Arbitrary, Unstructured}; use self::error::{MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner}; +use crate::CheckedSum; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] @@ -593,13 +594,6 @@ enum DisplayStyle { DynamicDenomination, } -/// Calculates the sum over the iterator using checked arithmetic. -pub trait CheckedSum: sealed::Sealed { - /// Calculates the sum over the iterator using checked arithmetic. If an - /// overflow happens it returns [`None`]. - fn checked_sum(self) -> Option; -} - impl CheckedSum for T where T: Iterator, @@ -616,16 +610,6 @@ where } } -mod sealed { - use super::{Amount, SignedAmount}; - - /// Used to seal the `CheckedSum` trait - pub trait Sealed {} - - impl Sealed for T where T: Iterator {} - impl Sealed for T where T: Iterator {} -} - #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Denomination { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { diff --git a/units/src/lib.rs b/units/src/lib.rs index 06d24d718..643120223 100644 --- a/units/src/lib.rs +++ b/units/src/lib.rs @@ -69,3 +69,21 @@ pub(crate) use self::result::OptionExt; #[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")] #[doc(hidden)] pub type BlockInterval = BlockHeightInterval; + +/// Calculates the sum over the iterator using checked arithmetic. +pub trait CheckedSum: sealed::Sealed { + /// Calculates the sum over the iterator using checked arithmetic. If an + /// overflow happens it returns [`None`]. + fn checked_sum(self) -> Option; +} + +mod sealed { + use super::{Amount, SignedAmount, Weight}; + + /// Used to seal the `CheckedSum` trait + pub trait Sealed {} + + impl Sealed for T where T: Iterator {} + impl Sealed for T where T: Iterator {} + impl Sealed for T where T: Iterator {} +} diff --git a/units/src/weight.rs b/units/src/weight.rs index d801dd254..6649b4d64 100644 --- a/units/src/weight.rs +++ b/units/src/weight.rs @@ -10,6 +10,8 @@ use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::CheckedSum; + /// The factor that non-witness serialization data is multiplied by during weight calculation. pub const WITNESS_SCALE_FACTOR: usize = 4; @@ -241,6 +243,13 @@ impl ops::RemAssign for Weight { fn rem_assign(&mut self, rhs: u64) { *self = Weight::from_wu(self.to_wu() % rhs); } } +impl CheckedSum for T +where + T: Iterator, +{ + fn checked_sum(mut self) -> Option { self.try_fold(Weight::ZERO, Weight::checked_add) } +} + impl core::iter::Sum for Weight { fn sum(iter: I) -> Self where @@ -524,4 +533,16 @@ mod tests { weight %= 3; assert_eq!(weight, Weight::from_wu(1)); } + + #[test] + #[cfg(feature = "alloc")] + fn checked_sum_weights() { + assert_eq!([].into_iter().checked_sum(), Some(Weight::ZERO)); + + let sum = alloc::vec![0, 1, 2].iter().map(|&w| Weight::from_wu(w)).checked_sum().unwrap(); + assert_eq!(sum, Weight::from_wu(3)); + + let sum = alloc::vec![1, u64::MAX].iter().map(|&w| Weight::from_wu(w)).checked_sum(); + assert!(sum.is_none()); + } } diff --git a/units/tests/api.rs b/units/tests/api.rs index 33b60bc7b..c44025295 100644 --- a/units/tests/api.rs +++ b/units/tests/api.rs @@ -15,7 +15,7 @@ use arbitrary::{Arbitrary, Unstructured}; use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`. use bitcoin_units::{ amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, BlockMtp, - BlockMtpInterval, BlockTime, FeeRate, SignedAmount, Weight, + BlockMtpInterval, BlockTime, CheckedSum, FeeRate, SignedAmount, Weight, }; /// A struct that includes all public non-error enums. @@ -272,7 +272,8 @@ fn regression_default() { fn dyn_compatible() { // If this builds then traits are dyn compatible. struct Traits { - a: Box>, + a: Box>, + b: Box>, // These traits are explicitly not dyn compatible. // b: Box, // c: Box,