// SPDX-License-Identifier: CC0-1.0 //! Implements `Weight` and associated features. use core::{fmt, ops}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; /// 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 { /// Constructs a new [`Weight`] from weight units. pub const fn from_wu(wu: u64) -> Self { Weight(wu) } /// Returns raw weight units. /// /// Can be used instead of `into()` to avoid inference issues. pub const fn to_wu(self) -> u64 { self.0 } } } #[doc(inline)] pub use encapsulate::Weight; impl Weight { /// 0 wu. /// /// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts. pub const ZERO: Weight = Weight::from_wu(0); /// Minimum possible value (0 wu). /// /// Equivalent to [`ZERO`](Self::ZERO), may better express intent in some contexts. pub const MIN: Weight = Weight::from_wu(u64::MIN); /// Maximum possible value. pub const MAX: Weight = Weight::from_wu(u64::MAX); /// The factor that non-witness serialization data is multiplied by during weight calculation. pub const WITNESS_SCALE_FACTOR: u64 = WITNESS_SCALE_FACTOR as u64; /// The maximum allowed weight for a block, see BIP 141 (network rule). pub const MAX_BLOCK: Weight = Weight::from_wu(4_000_000); /// The minimum transaction weight for a valid serialized transaction. pub const MIN_TRANSACTION: Weight = Weight::from_wu(Self::WITNESS_SCALE_FACTOR * 60); /// Constructs a new [`Weight`] from kilo weight units returning [`None`] if an overflow occurred. pub const fn from_kwu(wu: u64) -> Option { // No `map()` in const context. match wu.checked_mul(1000) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } /// Constructs a new [`Weight`] from virtual bytes, returning [`None`] if an overflow occurred. pub const fn from_vb(vb: u64) -> Option { // No `map()` in const context. match vb.checked_mul(Self::WITNESS_SCALE_FACTOR) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } /// Constructs a new [`Weight`] from virtual bytes panicking if an overflow occurred. /// /// # Panics /// /// If the conversion from virtual bytes overflows. #[deprecated(since = "TBD", note = "use `from_vb_unchecked` instead")] pub const fn from_vb_unwrap(vb: u64) -> Weight { match vb.checked_mul(Self::WITNESS_SCALE_FACTOR) { Some(weight) => Weight::from_wu(weight), None => panic!("checked_mul overflowed"), } } /// Constructs a new [`Weight`] from virtual bytes without an overflow check. pub const fn from_vb_unchecked(vb: u64) -> Self { Weight::from_wu(vb * 4) } /// Constructs a new [`Weight`] from witness size. pub const fn from_witness_data_size(witness_size: u64) -> Self { Weight::from_wu(witness_size) } /// Constructs a new [`Weight`] from non-witness size. pub const fn from_non_witness_data_size(non_witness_size: u64) -> Self { Weight::from_wu(non_witness_size * Self::WITNESS_SCALE_FACTOR) } /// Converts to kilo weight units rounding down. pub const fn to_kwu_floor(self) -> u64 { self.to_wu() / 1000 } /// Converts to kilo weight units rounding up. pub const fn to_kwu_ceil(self) -> u64 { (self.to_wu() + 999) / 1000 } /// Converts to vB rounding down. pub const fn to_vbytes_floor(self) -> u64 { self.to_wu() / Self::WITNESS_SCALE_FACTOR } /// Converts to vB rounding up. pub const fn to_vbytes_ceil(self) -> u64 { (self.to_wu() + Self::WITNESS_SCALE_FACTOR - 1) / Self::WITNESS_SCALE_FACTOR } /// Checked addition. /// /// Computes `self + rhs` returning [`None`] if an overflow occurred. #[must_use] pub const fn checked_add(self, rhs: Self) -> Option { // No `map()` in const context. match self.to_wu().checked_add(rhs.to_wu()) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } /// Checked subtraction. /// /// Computes `self - rhs` returning [`None`] if an overflow occurred. #[must_use] pub const fn checked_sub(self, rhs: Self) -> Option { // No `map()` in const context. match self.to_wu().checked_sub(rhs.to_wu()) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } /// Checked multiplication. /// /// Computes `self * rhs` returning [`None`] if an overflow occurred. #[must_use] pub const fn checked_mul(self, rhs: u64) -> Option { // No `map()` in const context. match self.to_wu().checked_mul(rhs) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } /// Checked division. /// /// Computes `self / rhs` returning [`None`] if `rhs == 0`. #[must_use] pub const fn checked_div(self, rhs: u64) -> Option { // No `map()` in const context. match self.to_wu().checked_div(rhs) { Some(wu) => Some(Weight::from_wu(wu)), None => None, } } } /// Alternative will display the unit. impl fmt::Display for Weight { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if f.alternate() { write!(f, "{} wu", self.to_wu()) } else { fmt::Display::fmt(&self.to_wu(), f) } } } impl From for u64 { fn from(value: Weight) -> Self { value.to_wu() } } crate::internal_macros::impl_op_for_references! { impl ops::Add for Weight { type Output = Weight; fn add(self, rhs: Weight) -> Self::Output { Weight::from_wu(self.to_wu() + rhs.to_wu()) } } impl ops::Sub for Weight { type Output = Weight; fn sub(self, rhs: Weight) -> Self::Output { Weight::from_wu(self.to_wu() - rhs.to_wu()) } } impl ops::Mul for Weight { type Output = Weight; fn mul(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() * rhs) } } impl ops::Mul for u64 { type Output = Weight; fn mul(self, rhs: Weight) -> Self::Output { Weight::from_wu(self * rhs.to_wu()) } } impl ops::Div for Weight { type Output = Weight; fn div(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() / rhs) } } impl ops::Div for Weight { type Output = u64; fn div(self, rhs: Weight) -> Self::Output { self.to_wu() / rhs.to_wu() } } impl ops::Rem for Weight { type Output = Weight; fn rem(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() % rhs) } } impl ops::Rem for Weight { type Output = u64; fn rem(self, rhs: Weight) -> Self::Output { self.to_wu() % rhs.to_wu() } } } crate::internal_macros::impl_add_assign!(Weight); crate::internal_macros::impl_sub_assign!(Weight); impl ops::MulAssign for Weight { fn mul_assign(&mut self, rhs: u64) { *self = Weight::from_wu(self.to_wu() * rhs); } } impl ops::DivAssign for Weight { fn div_assign(&mut self, rhs: u64) { *self = Weight::from_wu(self.to_wu() / rhs ); } } impl ops::RemAssign for Weight { fn rem_assign(&mut self, rhs: u64) { *self = Weight::from_wu(self.to_wu() % rhs); } } impl core::iter::Sum for Weight { fn sum(iter: I) -> Self where I: Iterator, { Weight::from_wu(iter.map(Weight::to_wu).sum()) } } impl<'a> core::iter::Sum<&'a Weight> for Weight { fn sum(iter: I) -> Self where I: Iterator, { iter.copied().sum() } } crate::impl_parse_str_from_int_infallible!(Weight, u64, from_wu); #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Weight { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let w = u64::arbitrary(u)?; Ok(Weight::from_wu(w)) } } #[cfg(test)] mod tests { use super::*; const ONE: Weight = Weight::from_wu(1); const TWO: Weight = Weight::from_wu(2); const FOUR: Weight = Weight::from_wu(4); #[test] fn sanity_check() { assert_eq!(Weight::MIN_TRANSACTION, Weight::from_wu(240)); } #[test] fn from_kwu() { let got = Weight::from_kwu(1).unwrap(); let want = Weight::from_wu(1_000); assert_eq!(got, want); } #[test] fn from_kwu_overflows() { assert!(Weight::from_kwu(u64::MAX).is_none()) } #[test] fn from_vb() { let got = Weight::from_vb(1).unwrap(); let want = Weight::from_wu(4); assert_eq!(got, want); } #[test] fn from_vb_overflows() { assert!(Weight::from_vb(u64::MAX).is_none()); } #[test] fn from_vb_unchecked() { let got = Weight::from_vb_unchecked(1); let want = Weight::from_wu(4); assert_eq!(got, want); } #[test] #[cfg(debug_assertions)] #[should_panic = "attempt to multiply with overflow"] fn from_vb_unchecked_panic() { Weight::from_vb_unchecked(u64::MAX); } #[test] fn from_witness_data_size() { let witness_data_size = 1; let got = Weight::from_witness_data_size(witness_data_size); let want = Weight::from_wu(witness_data_size); assert_eq!(got, want); } #[test] fn from_non_witness_data_size() { let non_witness_data_size = 1; let got = Weight::from_non_witness_data_size(non_witness_data_size); let want = Weight::from_wu(non_witness_data_size * 4); assert_eq!(got, want); } #[test] fn to_kwu_floor() { assert_eq!(Weight::from_wu(5_000).to_kwu_floor(), 5); assert_eq!(Weight::from_wu(5_999).to_kwu_floor(), 5); } #[test] fn to_kwu_ceil() { assert_eq!(Weight::from_wu(1_000).to_kwu_ceil(), 1); assert_eq!(Weight::from_wu(1_001).to_kwu_ceil(), 2); } #[test] fn to_vb_floor() { assert_eq!(Weight::from_wu(8).to_vbytes_floor(), 2); assert_eq!(Weight::from_wu(9).to_vbytes_floor(), 2); } #[test] fn to_vb_ceil() { assert_eq!(Weight::from_wu(4).to_vbytes_ceil(), 1); assert_eq!(Weight::from_wu(5).to_vbytes_ceil(), 2); } #[test] fn checked_add() { assert_eq!(ONE.checked_add(ONE).unwrap(), TWO); } #[test] fn checked_add_overflows() { assert!(Weight::MAX.checked_add(ONE).is_none()) } #[test] fn checked_sub() { assert_eq!(TWO.checked_sub(ONE).unwrap(), ONE); } #[test] fn checked_sub_overflows() { assert!(Weight::ZERO.checked_sub(ONE).is_none()) } #[test] fn checked_mul() { assert_eq!(TWO.checked_mul(1).unwrap(), TWO); assert_eq!(TWO.checked_mul(2).unwrap(), FOUR); } #[test] fn checked_mul_overflows() { assert!(Weight::MAX.checked_mul(2).is_none()) } #[test] fn checked_div() { assert_eq!(FOUR.checked_div(2).unwrap(), TWO); assert_eq!(TWO.checked_div(1).unwrap(), TWO); } #[test] fn checked_div_overflows() { assert!(TWO.checked_div(0).is_none()) } #[test] #[allow(clippy::op_ref)] fn addition() { let one = Weight::from_wu(1); let two = Weight::from_wu(2); let three = Weight::from_wu(3); assert!(one + two == three); assert!(&one + two == three); assert!(one + &two == three); assert!(&one + &two == three); } #[test] #[allow(clippy::op_ref)] fn subtract() { let ten = Weight::from_wu(10); let seven = Weight::from_wu(7); let three = Weight::from_wu(3); assert_eq!(ten - seven, three); assert_eq!(&ten - seven, three); assert_eq!(ten - &seven, three); assert_eq!(&ten - &seven, three); } #[test] #[allow(clippy::op_ref)] fn multiply() { let two = Weight::from_wu(2); let six = Weight::from_wu(6); assert_eq!(3_u64 * two, six); assert_eq!(two * 3_u64, six); } #[test] fn divide() { let eight = Weight::from_wu(8); let four = Weight::from_wu(4); assert_eq!(eight / four, 2_u64); assert_eq!(eight / 4_u64, Weight::from_wu(2)); } #[test] fn add_assign() { let mut f = Weight::from_wu(1); f += Weight::from_wu(2); assert_eq!(f, Weight::from_wu(3)); let mut f = Weight::from_wu(1); f += &Weight::from_wu(2); assert_eq!(f, Weight::from_wu(3)); } #[test] fn sub_assign() { let mut f = Weight::from_wu(3); f -= Weight::from_wu(2); assert_eq!(f, Weight::from_wu(1)); let mut f = Weight::from_wu(3); f -= &Weight::from_wu(2); assert_eq!(f, Weight::from_wu(1)); } #[test] fn mul_assign() { let mut w = Weight::from_wu(3); w *= 2_u64; assert_eq!(w, Weight::from_wu(6)); } #[test] fn div_assign() { let mut w = Weight::from_wu(8); w /= Weight::from_wu(4).into(); assert_eq!(w, Weight::from_wu(2)); } #[test] fn remainder() { let weight10 = Weight::from_wu(10); let weight3 = Weight::from_wu(3); let remainder = weight10 % weight3; assert_eq!(remainder, 1); let remainder = weight10 % 3; assert_eq!(remainder, Weight::from_wu(1)); } #[test] fn remainder_assign() { let mut weight = Weight::from_wu(10); weight %= 3; assert_eq!(weight, Weight::from_wu(1)); } }