Skip to main content

bitcoin_units/
weight.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Implements `Weight` and associated features.
4
5use core::num::NonZeroU64;
6use core::{fmt, ops};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::{Arbitrary, Unstructured};
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
14use crate::{Amount, FeeRate, NumOpResult};
15
16/// The factor that non-witness serialization data is multiplied by during weight calculation.
17pub const WITNESS_SCALE_FACTOR: usize = 4;
18
19mod encapsulate {
20    /// The weight of a transaction or block.
21    ///
22    /// This is an integer newtype representing weight in weight units. It provides protection
23    /// against mixing up the types, conversion functions, and basic formatting.
24    #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
25    pub struct Weight(u64);
26
27    impl Weight {
28        /// Constructs a new [`Weight`] from weight units.
29        #[inline]
30        pub const fn from_wu(wu: u64) -> Self { Self(wu) }
31
32        /// Returns raw weight units.
33        ///
34        /// Can be used instead of `into()` to avoid inference issues.
35        #[inline]
36        pub const fn to_wu(self) -> u64 { self.0 }
37    }
38}
39#[doc(inline)]
40pub use encapsulate::Weight;
41
42impl Weight {
43    /// Zero weight units (wu).
44    ///
45    /// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
46    pub const ZERO: Self = Self::from_wu(0);
47
48    /// Minimum possible value (0 wu).
49    ///
50    /// Equivalent to [`ZERO`](Self::ZERO), may better express intent in some contexts.
51    pub const MIN: Self = Self::from_wu(u64::MIN);
52
53    /// Maximum possible value.
54    pub const MAX: Self = Self::from_wu(u64::MAX);
55
56    /// The factor that non-witness serialization data is multiplied by during weight calculation.
57    pub const WITNESS_SCALE_FACTOR: u64 = WITNESS_SCALE_FACTOR as u64; // this value is 4
58
59    /// The maximum allowed weight for a block, see BIP-0141 (network rule).
60    pub const MAX_BLOCK: Self = Self::from_wu(4_000_000);
61
62    /// The minimum transaction weight for a valid serialized transaction.
63    pub const MIN_TRANSACTION: Self = Self::from_wu(Self::WITNESS_SCALE_FACTOR * 60);
64
65    /// Constructs a new [`Weight`] from kilo weight units returning [`None`] if an overflow occurred.
66    #[inline]
67    pub const fn from_kwu(wu: u64) -> Option<Self> {
68        // No `map()` in const context.
69        match wu.checked_mul(1000) {
70            Some(wu) => Some(Self::from_wu(wu)),
71            None => None,
72        }
73    }
74
75    /// Constructs a new [`Weight`] from virtual bytes, returning [`None`] if an overflow occurred.
76    #[inline]
77    pub const fn from_vb(vb: u64) -> Option<Self> {
78        // No `map()` in const context.
79        match vb.checked_mul(Self::WITNESS_SCALE_FACTOR) {
80            Some(wu) => Some(Self::from_wu(wu)),
81            None => None,
82        }
83    }
84
85    /// Constructs a new [`Weight`] from virtual bytes panicking if an overflow occurred.
86    ///
87    /// # Panics
88    ///
89    /// If the conversion from virtual bytes overflows.
90    #[deprecated(since = "1.0.0-rc.0", note = "use `from_vb_unchecked` instead")]
91    pub const fn from_vb_unwrap(vb: u64) -> Self {
92        match vb.checked_mul(Self::WITNESS_SCALE_FACTOR) {
93            Some(weight) => Self::from_wu(weight),
94            None => panic!("checked_mul overflowed"),
95        }
96    }
97
98    /// Constructs a new [`Weight`] from virtual bytes without an overflow check.
99    #[inline]
100    pub const fn from_vb_unchecked(vb: u64) -> Self {
101        Self::from_wu(vb * Self::WITNESS_SCALE_FACTOR)
102    }
103
104    /// Constructs a new [`Weight`] from witness size.
105    #[deprecated(since = "1.0.0-rc.1", note = "use `from_wu` instead")]
106    pub const fn from_witness_data_size(witness_size: u64) -> Self { Self::from_wu(witness_size) }
107
108    /// Constructs a new [`Weight`] from non-witness size.
109    ///
110    /// # Panics
111    ///
112    /// If the conversion from virtual bytes overflows.
113    #[deprecated(since = "1.0.0-rc.1", note = "use `from_vb` or `from_vb_unchecked` instead")]
114    pub const fn from_non_witness_data_size(non_witness_size: u64) -> Self {
115        Self::from_wu(non_witness_size * Self::WITNESS_SCALE_FACTOR)
116    }
117
118    /// Constructs a new `Weight` from a prefixed hex string.
119    ///
120    /// # Errors
121    ///
122    /// If the input string is not a valid hex representation of a weight in weight units or it
123    /// does not include the `0x` prefix.
124    #[inline]
125    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
126        let weight = parse_int::hex_u64_prefixed(s)?;
127        Ok(Self::from_wu(weight))
128    }
129
130    /// Constructs a new `Weight` from an unprefixed hex string.
131    ///
132    /// # Errors
133    ///
134    /// If the input string is not a valid hex representation of a weight in weight units or if
135    /// it includes the `0x` prefix.
136    #[inline]
137    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
138        let weight = parse_int::hex_u64_unprefixed(s)?;
139        Ok(Self::from_wu(weight))
140    }
141
142    /// Converts to kilo weight units rounding down.
143    #[inline]
144    pub const fn to_kwu_floor(self) -> u64 { self.to_wu() / 1000 }
145
146    /// Converts to kilo weight units rounding up.
147    #[inline]
148    pub const fn to_kwu_ceil(self) -> u64 { self.to_wu().div_ceil(1_000) }
149
150    /// Converts to vB rounding down.
151    #[inline]
152    pub const fn to_vbytes_floor(self) -> u64 { self.to_wu() / Self::WITNESS_SCALE_FACTOR }
153
154    /// Converts to vB rounding up.
155    #[inline]
156    pub const fn to_vbytes_ceil(self) -> u64 { self.to_wu().div_ceil(Self::WITNESS_SCALE_FACTOR) }
157
158    /// Checked addition.
159    ///
160    /// Computes `self + rhs` returning [`None`] if an overflow occurred.
161    #[inline]
162    #[must_use]
163    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
164        // No `map()` in const context.
165        match self.to_wu().checked_add(rhs.to_wu()) {
166            Some(wu) => Some(Self::from_wu(wu)),
167            None => None,
168        }
169    }
170
171    /// Checked subtraction.
172    ///
173    /// Computes `self - rhs` returning [`None`] if an overflow occurred.
174    #[inline]
175    #[must_use]
176    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
177        // No `map()` in const context.
178        match self.to_wu().checked_sub(rhs.to_wu()) {
179            Some(wu) => Some(Self::from_wu(wu)),
180            None => None,
181        }
182    }
183
184    /// Checked multiplication.
185    ///
186    /// Computes `self * rhs` returning [`None`] if an overflow occurred.
187    #[inline]
188    #[must_use]
189    pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
190        // No `map()` in const context.
191        match self.to_wu().checked_mul(rhs) {
192            Some(wu) => Some(Self::from_wu(wu)),
193            None => None,
194        }
195    }
196
197    /// Checked division.
198    ///
199    /// Computes `self / rhs` returning [`None`] if `rhs == 0`.
200    #[inline]
201    #[must_use]
202    pub const fn checked_div(self, rhs: u64) -> Option<Self> {
203        // No `map()` in const context.
204        match self.to_wu().checked_div(rhs) {
205            Some(wu) => Some(Self::from_wu(wu)),
206            None => None,
207        }
208    }
209
210    /// Checked fee rate multiplication.
211    ///
212    /// Computes the absolute fee amount for a given [`FeeRate`] at this weight. When the resulting
213    /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is
214    /// enough instead of falling short if rounded down.
215    #[inline]
216    pub const fn mul_by_fee_rate(self, fee_rate: FeeRate) -> NumOpResult<Amount> {
217        fee_rate.mul_by_weight(self)
218    }
219}
220
221crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(Weight, to_wu);
222
223/// Alternative will display the unit.
224impl fmt::Display for Weight {
225    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        if f.alternate() {
227            write!(f, "{} wu", self.to_wu())
228        } else {
229            fmt::Display::fmt(&self.to_wu(), f)
230        }
231    }
232}
233
234impl From<Weight> for u64 {
235    #[inline]
236    fn from(value: Weight) -> Self { value.to_wu() }
237}
238
239crate::internal_macros::impl_op_for_references! {
240    impl ops::Add<Weight> for Weight {
241        type Output = Weight;
242
243        fn add(self, rhs: Weight) -> Self::Output { Weight::from_wu(self.to_wu() + rhs.to_wu()) }
244    }
245    impl ops::Sub<Weight> for Weight {
246        type Output = Weight;
247
248        fn sub(self, rhs: Weight) -> Self::Output { Weight::from_wu(self.to_wu() - rhs.to_wu()) }
249    }
250
251    impl ops::Mul<u64> for Weight {
252        type Output = Weight;
253
254        fn mul(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() * rhs) }
255    }
256    impl ops::Mul<Weight> for u64 {
257        type Output = Weight;
258
259        fn mul(self, rhs: Weight) -> Self::Output { Weight::from_wu(self * rhs.to_wu()) }
260    }
261    impl ops::Div<u64> for Weight {
262        type Output = Weight;
263
264        fn div(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() / rhs) }
265    }
266    impl ops::Div<Weight> for Weight {
267        type Output = u64;
268
269        fn div(self, rhs: Weight) -> Self::Output { self.to_wu() / rhs.to_wu() }
270    }
271    impl ops::Rem<u64> for Weight {
272        type Output = Weight;
273
274        fn rem(self, rhs: u64) -> Self::Output { Weight::from_wu(self.to_wu() % rhs) }
275    }
276    impl ops::Rem<Weight> for Weight {
277        type Output = u64;
278
279        fn rem(self, rhs: Weight) -> Self::Output { self.to_wu() % rhs.to_wu() }
280    }
281    impl ops::Div<NonZeroU64> for Weight {
282        type Output = Weight;
283
284        fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_wu(self.to_wu() / rhs.get()) }
285    }
286}
287crate::internal_macros::impl_add_assign!(Weight);
288crate::internal_macros::impl_sub_assign!(Weight);
289
290impl ops::MulAssign<u64> for Weight {
291    #[inline]
292    fn mul_assign(&mut self, rhs: u64) { *self = Self::from_wu(self.to_wu() * rhs); }
293}
294
295impl ops::DivAssign<u64> for Weight {
296    #[inline]
297    fn div_assign(&mut self, rhs: u64) { *self = Self::from_wu(self.to_wu() / rhs); }
298}
299
300impl ops::RemAssign<u64> for Weight {
301    #[inline]
302    fn rem_assign(&mut self, rhs: u64) { *self = Self::from_wu(self.to_wu() % rhs); }
303}
304
305impl core::iter::Sum for Weight {
306    #[inline]
307    fn sum<I>(iter: I) -> Self
308    where
309        I: Iterator<Item = Self>,
310    {
311        Self::from_wu(iter.map(Self::to_wu).sum())
312    }
313}
314
315impl<'a> core::iter::Sum<&'a Self> for Weight {
316    #[inline]
317    fn sum<I>(iter: I) -> Self
318    where
319        I: Iterator<Item = &'a Self>,
320    {
321        iter.copied().sum()
322    }
323}
324
325parse_int::impl_parse_str_from_int_infallible!(Weight, u64, from_wu);
326
327#[cfg(feature = "serde")]
328impl Serialize for Weight {
329    #[inline]
330    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
331    where
332        S: Serializer,
333    {
334        u64::serialize(&self.to_wu(), s)
335    }
336}
337
338#[cfg(feature = "serde")]
339impl<'de> Deserialize<'de> for Weight {
340    #[inline]
341    fn deserialize<D>(d: D) -> Result<Self, D::Error>
342    where
343        D: Deserializer<'de>,
344    {
345        Ok(Self::from_wu(u64::deserialize(d)?))
346    }
347}
348
349#[cfg(feature = "arbitrary")]
350impl<'a> Arbitrary<'a> for Weight {
351    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
352        let w = u64::arbitrary(u)?;
353        Ok(Self::from_wu(w))
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use core::num::NonZeroU64;
360
361    use super::*;
362
363    const ONE: Weight = Weight::from_wu(1);
364    const TWO: Weight = Weight::from_wu(2);
365    const FOUR: Weight = Weight::from_wu(4);
366
367    #[test]
368    fn sanity_check() {
369        assert_eq!(Weight::MIN_TRANSACTION, Weight::from_wu(240));
370    }
371
372    #[test]
373    #[allow(clippy::op_ref)]
374    fn weight_div_nonzero() {
375        let w = Weight::from_wu(100);
376        let divisor = NonZeroU64::new(4).unwrap();
377        assert_eq!(w / divisor, Weight::from_wu(25));
378        // for borrowed variants
379        assert_eq!(&w / &divisor, Weight::from_wu(25));
380        assert_eq!(w / &divisor, Weight::from_wu(25));
381    }
382
383    #[test]
384    fn from_kwu() {
385        let got = Weight::from_kwu(1).unwrap();
386        let want = Weight::from_wu(1_000);
387        assert_eq!(got, want);
388    }
389
390    #[test]
391    fn from_kwu_overflows() { assert!(Weight::from_kwu(u64::MAX).is_none()) }
392
393    #[test]
394    fn from_vb() {
395        let got = Weight::from_vb(1).unwrap();
396        let want = Weight::from_wu(4);
397        assert_eq!(got, want);
398    }
399
400    #[test]
401    fn from_vb_overflows() {
402        assert!(Weight::from_vb(u64::MAX).is_none());
403    }
404
405    #[test]
406    fn from_vb_unchecked() {
407        let got = Weight::from_vb_unchecked(1);
408        let want = Weight::from_wu(4);
409        assert_eq!(got, want);
410    }
411
412    #[test]
413    #[cfg(debug_assertions)]
414    #[should_panic = "attempt to multiply with overflow"]
415    fn from_vb_unchecked_panic() { Weight::from_vb_unchecked(u64::MAX); }
416
417    #[test]
418    #[allow(deprecated)] // tests the deprecated function
419    #[allow(deprecated_in_future)]
420    fn from_witness_data_size() {
421        let witness_data_size = 1;
422        let got = Weight::from_witness_data_size(witness_data_size);
423        let want = Weight::from_wu(witness_data_size);
424        assert_eq!(got, want);
425    }
426
427    #[test]
428    #[allow(deprecated)] // tests the deprecated function
429    #[allow(deprecated_in_future)]
430    fn from_non_witness_data_size() {
431        let non_witness_data_size = 1;
432        let got = Weight::from_non_witness_data_size(non_witness_data_size);
433        let want = Weight::from_wu(non_witness_data_size * 4);
434        assert_eq!(got, want);
435    }
436
437    #[test]
438    #[cfg(feature = "alloc")]
439    fn try_from_string() {
440        let weight_value: alloc::string::String = "10".into();
441        let got = Weight::try_from(weight_value).unwrap();
442        let want = Weight::from_wu(10);
443        assert_eq!(got, want);
444
445        // Only base-10 integers should parse
446        let weight_value: alloc::string::String = "0xab".into();
447        assert!(Weight::try_from(weight_value).is_err());
448        let weight_value: alloc::string::String = "10.123".into();
449        assert!(Weight::try_from(weight_value).is_err());
450    }
451
452    #[test]
453    #[cfg(feature = "alloc")]
454    fn try_from_box() {
455        let weight_value: alloc::boxed::Box<str> = "10".into();
456        let got = Weight::try_from(weight_value).unwrap();
457        let want = Weight::from_wu(10);
458        assert_eq!(got, want);
459
460        // Only base-10 integers should parse
461        let weight_value: alloc::boxed::Box<str> = "0xab".into();
462        assert!(Weight::try_from(weight_value).is_err());
463        let weight_value: alloc::boxed::Box<str> = "10.123".into();
464        assert!(Weight::try_from(weight_value).is_err());
465    }
466
467    #[test]
468    fn to_kwu_floor() {
469        assert_eq!(Weight::from_wu(5_000).to_kwu_floor(), 5);
470        assert_eq!(Weight::from_wu(5_999).to_kwu_floor(), 5);
471    }
472
473    #[test]
474    fn to_kwu_ceil() {
475        assert_eq!(Weight::from_wu(1_000).to_kwu_ceil(), 1);
476        assert_eq!(Weight::from_wu(1_001).to_kwu_ceil(), 2);
477        assert_eq!(Weight::MAX.to_kwu_ceil(), u64::MAX / 1_000 + 1);
478    }
479
480    #[test]
481    fn to_vb_floor() {
482        assert_eq!(Weight::from_wu(8).to_vbytes_floor(), 2);
483        assert_eq!(Weight::from_wu(9).to_vbytes_floor(), 2);
484    }
485
486    #[test]
487    fn to_vb_ceil() {
488        assert_eq!(Weight::from_wu(4).to_vbytes_ceil(), 1);
489        assert_eq!(Weight::from_wu(5).to_vbytes_ceil(), 2);
490        assert_eq!(Weight::MAX.to_vbytes_ceil(), u64::MAX / Weight::WITNESS_SCALE_FACTOR + 1);
491    }
492
493    #[test]
494    fn checked_add() {
495        assert_eq!(ONE.checked_add(ONE).unwrap(), TWO);
496    }
497
498    #[test]
499    fn checked_add_overflows() { assert!(Weight::MAX.checked_add(ONE).is_none()) }
500
501    #[test]
502    fn checked_sub() {
503        assert_eq!(TWO.checked_sub(ONE).unwrap(), ONE);
504    }
505
506    #[test]
507    fn checked_sub_overflows() { assert!(Weight::ZERO.checked_sub(ONE).is_none()) }
508
509    #[test]
510    fn checked_mul() {
511        assert_eq!(TWO.checked_mul(1).unwrap(), TWO);
512        assert_eq!(TWO.checked_mul(2).unwrap(), FOUR);
513    }
514
515    #[test]
516    fn checked_mul_overflows() { assert!(Weight::MAX.checked_mul(2).is_none()) }
517
518    #[test]
519    fn checked_div() {
520        assert_eq!(FOUR.checked_div(2).unwrap(), TWO);
521        assert_eq!(TWO.checked_div(1).unwrap(), TWO);
522    }
523
524    #[test]
525    fn checked_div_overflows() { assert!(TWO.checked_div(0).is_none()) }
526
527    #[test]
528    #[allow(clippy::op_ref)]
529    fn addition() {
530        let one = Weight::from_wu(1);
531        let two = Weight::from_wu(2);
532        let three = Weight::from_wu(3);
533
534        assert!(one + two == three);
535        assert!(&one + two == three);
536        assert!(one + &two == three);
537        assert!(&one + &two == three);
538    }
539
540    #[test]
541    #[allow(clippy::op_ref)]
542    fn subtract() {
543        let ten = Weight::from_wu(10);
544        let seven = Weight::from_wu(7);
545        let three = Weight::from_wu(3);
546
547        assert_eq!(ten - seven, three);
548        assert_eq!(&ten - seven, three);
549        assert_eq!(ten - &seven, three);
550        assert_eq!(&ten - &seven, three);
551    }
552
553    #[test]
554    #[allow(clippy::op_ref)]
555    fn multiply() {
556        let two = Weight::from_wu(2);
557        let six = Weight::from_wu(6);
558
559        assert_eq!(3_u64 * two, six);
560        assert_eq!(two * 3_u64, six);
561    }
562
563    #[test]
564    fn divide() {
565        let eight = Weight::from_wu(8);
566        let four = Weight::from_wu(4);
567
568        assert_eq!(eight / four, 2_u64);
569        assert_eq!(eight / 4_u64, Weight::from_wu(2));
570    }
571
572    #[test]
573    fn add_assign() {
574        let mut f = Weight::from_wu(1);
575        f += Weight::from_wu(2);
576        assert_eq!(f, Weight::from_wu(3));
577
578        let mut f = Weight::from_wu(1);
579        f += &Weight::from_wu(2);
580        assert_eq!(f, Weight::from_wu(3));
581    }
582
583    #[test]
584    fn sub_assign() {
585        let mut f = Weight::from_wu(3);
586        f -= Weight::from_wu(2);
587        assert_eq!(f, Weight::from_wu(1));
588
589        let mut f = Weight::from_wu(3);
590        f -= &Weight::from_wu(2);
591        assert_eq!(f, Weight::from_wu(1));
592    }
593
594    #[test]
595    fn mul_assign() {
596        let mut w = Weight::from_wu(3);
597        w *= 2_u64;
598        assert_eq!(w, Weight::from_wu(6));
599    }
600
601    #[test]
602    fn div_assign() {
603        let mut w = Weight::from_wu(8);
604        w /= Weight::from_wu(4).into();
605        assert_eq!(w, Weight::from_wu(2));
606    }
607
608    #[test]
609    fn remainder() {
610        let weight10 = Weight::from_wu(10);
611        let weight3 = Weight::from_wu(3);
612
613        let remainder = weight10 % weight3;
614        assert_eq!(remainder, 1);
615
616        let remainder = weight10 % 3;
617        assert_eq!(remainder, Weight::from_wu(1));
618    }
619
620    #[test]
621    fn remainder_assign() {
622        let mut weight = Weight::from_wu(10);
623        weight %= 3;
624        assert_eq!(weight, Weight::from_wu(1));
625    }
626
627    #[test]
628    fn iter_sum() {
629        let values = [
630            Weight::from_wu(10),
631            Weight::from_wu(50),
632            Weight::from_wu(30),
633            Weight::from_wu(5),
634            Weight::from_wu(5),
635        ];
636        let got: Weight = values.into_iter().sum();
637        let want = Weight::from_wu(100);
638        assert_eq!(got, want);
639    }
640
641    #[test]
642    fn iter_sum_ref() {
643        let values = [
644            Weight::from_wu(10),
645            Weight::from_wu(50),
646            Weight::from_wu(30),
647            Weight::from_wu(5),
648            Weight::from_wu(5),
649        ];
650        let got: Weight = values.iter().sum();
651        let want = Weight::from_wu(100);
652        assert_eq!(got, want);
653    }
654
655    #[test]
656    fn iter_sum_empty() {
657        let values: [Weight; 0] = [];
658        let got: Weight = values.into_iter().sum();
659        let want = Weight::from_wu(0);
660        assert_eq!(got, want);
661    }
662}