|
10 | 10 | //! |
11 | 11 | //! We provide `fee.checked_div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate |
12 | 12 | //! required to pay at least `fee` for transaction with `weight`. |
| 13 | +//! |
| 14 | +//! We support various `core::ops` traits all of which return [`NumOpResult<T>`]. |
| 15 | +//! |
| 16 | +//! For specific methods see: |
| 17 | +//! |
| 18 | +//! * [`Amount::checked_div_by_weight_floor`] |
| 19 | +//! * [`Amount::checked_div_by_weight_ceil`] |
| 20 | +//! * [`Amount::checked_div_by_fee_rate_floor`] |
| 21 | +//! * [`Amount::checked_div_by_fee_rate_ceil`] |
| 22 | +//! * [`Weight::checked_mul_by_fee_rate`] |
| 23 | +//! * [`FeeRate::checked_mul_by_weight`] |
| 24 | +//! * [`FeeRate::to_fee`] |
13 | 25 |
|
14 | 26 | use core::ops; |
15 | 27 |
|
16 | 28 | use NumOpResult as R; |
17 | 29 |
|
18 | 30 | use crate::{Amount, FeeRate, MathOp, NumOpError as E, NumOpResult, OptionExt, Weight}; |
19 | 31 |
|
20 | | -impl Amount { |
21 | | - /// Checked weight floor division. |
22 | | - /// |
23 | | - /// Be aware that integer division loses the remainder if no exact division |
24 | | - /// can be made. See also [`Self::checked_div_by_weight_ceil`]. |
25 | | - /// |
26 | | - /// Returns [`None`] if overflow occurred. |
27 | | - #[must_use] |
28 | | - pub const fn checked_div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> { |
29 | | - let wu = weight.to_wu(); |
30 | | - if wu == 0 { |
31 | | - return None; |
32 | | - } |
33 | | - |
34 | | - // Mul by 1,000 because we use per/kwu. |
35 | | - match self.to_sat().checked_mul(1_000) { |
36 | | - Some(sats) => { |
37 | | - let fee_rate = sats / wu; |
38 | | - FeeRate::from_sat_per_kwu(fee_rate) |
39 | | - } |
40 | | - None => None, |
41 | | - } |
42 | | - } |
43 | | - |
44 | | - /// Checked weight ceiling division. |
45 | | - /// |
46 | | - /// Be aware that integer division loses the remainder if no exact division |
47 | | - /// can be made. This method rounds up ensuring the transaction fee rate is |
48 | | - /// sufficient. See also [`Self::checked_div_by_weight_floor`]. |
49 | | - /// |
50 | | - /// Returns [`None`] if overflow occurred. |
51 | | - /// |
52 | | - /// # Examples |
53 | | - /// |
54 | | - /// ``` |
55 | | - /// # use bitcoin_units::{amount, Amount, FeeRate, Weight}; |
56 | | - /// let amount = Amount::from_sat(10)?; |
57 | | - /// let weight = Weight::from_wu(300); |
58 | | - /// let fee_rate = amount.checked_div_by_weight_ceil(weight); |
59 | | - /// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34)); |
60 | | - /// # Ok::<_, amount::OutOfRangeError>(()) |
61 | | - /// ``` |
62 | | - #[must_use] |
63 | | - pub const fn checked_div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> { |
64 | | - let wu = weight.to_wu(); |
65 | | - if wu == 0 { |
66 | | - return None; |
67 | | - } |
68 | | - |
69 | | - // Mul by 1,000 because we use per/kwu. |
70 | | - if let Some(sats) = self.to_sat().checked_mul(1_000) { |
71 | | - // No need to used checked arithmetic because wu is non-zero. |
72 | | - if let Some(bump) = sats.checked_add(wu - 1) { |
73 | | - let fee_rate = bump / wu; |
74 | | - return FeeRate::from_sat_per_kwu(fee_rate); |
75 | | - } |
76 | | - } |
77 | | - None |
78 | | - } |
79 | | - |
80 | | - /// Checked fee rate floor division. |
81 | | - /// |
82 | | - /// Computes the maximum weight that would result in a fee less than or equal to this amount |
83 | | - /// at the given `fee_rate`. Uses floor division to ensure the resulting weight doesn't cause |
84 | | - /// the fee to exceed the amount. |
85 | | - /// |
86 | | - /// Returns [`None`] if overflow occurred or if `fee_rate` is zero. |
87 | | - #[must_use] |
88 | | - pub const fn checked_div_by_fee_rate_floor(self, fee_rate: FeeRate) -> Option<Weight> { |
89 | | - if let Some(msats) = self.to_sat().checked_mul(1000) { |
90 | | - if let Some(wu) = msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) { |
91 | | - return Some(Weight::from_wu(wu)); |
92 | | - } |
93 | | - } |
94 | | - None |
95 | | - } |
96 | | - |
97 | | - /// Checked fee rate ceiling division. |
98 | | - /// |
99 | | - /// Computes the minimum weight that would result in a fee greater than or equal to this amount |
100 | | - /// at the given `fee_rate`. Uses ceiling division to ensure the resulting weight is sufficient. |
101 | | - /// |
102 | | - /// Returns [`None`] if overflow occurred or if `fee_rate` is zero. |
103 | | - #[must_use] |
104 | | - pub const fn checked_div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> Option<Weight> { |
105 | | - // Use ceil because result is used as the divisor. |
106 | | - let rate = fee_rate.to_sat_per_kwu_ceil(); |
107 | | - if rate == 0 { |
108 | | - return None; |
109 | | - } |
110 | | - |
111 | | - if let Some(msats) = self.to_sat().checked_mul(1000) { |
112 | | - // No need to used checked arithmetic because rate is non-zero. |
113 | | - if let Some(bump) = msats.checked_add(rate - 1) { |
114 | | - let wu = bump / rate; |
115 | | - return Some(Weight::from_wu(wu)); |
116 | | - } |
117 | | - } |
118 | | - None |
119 | | - } |
120 | | -} |
121 | | - |
122 | | -impl FeeRate { |
123 | | - /// Calculates the fee by multiplying this fee rate by weight. |
124 | | - /// |
125 | | - /// Computes the absolute fee amount for a given [`Weight`] at this fee rate. When the resulting |
126 | | - /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is |
127 | | - /// enough instead of falling short if rounded down. |
128 | | - /// |
129 | | - /// If the calculation would overflow we saturate to [`Amount::MAX`]. Since such a fee can never |
130 | | - /// be paid this is meaningful as an error case while still removing the possibility of silently |
131 | | - /// wrapping. |
132 | | - pub const fn to_fee(self, weight: Weight) -> Amount { |
133 | | - // No `unwrap_or()` in const context. |
134 | | - match self.checked_mul_by_weight(weight) { |
135 | | - Some(fee) => fee, |
136 | | - None => Amount::MAX, |
137 | | - } |
138 | | - } |
139 | | - |
140 | | - /// Calculates the fee by multiplying this fee rate by weight, in weight units, returning [`None`] |
141 | | - /// if an overflow occurred. |
142 | | - /// |
143 | | - /// This is equivalent to `Self::checked_mul_by_weight()`. |
144 | | - #[must_use] |
145 | | - #[deprecated(since = "TBD", note = "use `to_fee()` instead")] |
146 | | - pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) } |
147 | | - |
148 | | - /// Calculates the fee by multiplying this fee rate by weight, in virtual bytes, returning [`None`] |
149 | | - /// if an overflow occurred. |
150 | | - /// |
151 | | - /// This is equivalent to converting `vb` to [`Weight`] using [`Weight::from_vb`] and then calling |
152 | | - /// `Self::fee_wu(weight)`. |
153 | | - #[must_use] |
154 | | - #[deprecated(since = "TBD", note = "use Weight::from_vb and then `to_fee()` instead")] |
155 | | - pub fn fee_vb(self, vb: u64) -> Option<Amount> { Weight::from_vb(vb).map(|w| self.to_fee(w)) } |
156 | | - |
157 | | - /// Checked weight multiplication. |
158 | | - /// |
159 | | - /// Computes the absolute fee amount for a given [`Weight`] at this fee rate. When the resulting |
160 | | - /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is |
161 | | - /// enough instead of falling short if rounded down. |
162 | | - /// |
163 | | - /// Returns [`None`] if overflow occurred. |
164 | | - #[must_use] |
165 | | - pub const fn checked_mul_by_weight(self, weight: Weight) -> Option<Amount> { |
166 | | - let wu = weight.to_wu(); |
167 | | - if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) { |
168 | | - // Bump by 999 to do ceil division using kwu. |
169 | | - if let Some(bump) = fee_kwu.checked_add(999) { |
170 | | - let fee = bump / 1_000; |
171 | | - if let Ok(fee_amount) = Amount::from_sat(fee) { |
172 | | - return Some(fee_amount); |
173 | | - } |
174 | | - } |
175 | | - } |
176 | | - None |
177 | | - } |
178 | | -} |
179 | | - |
180 | 32 | crate::internal_macros::impl_op_for_references! { |
181 | 33 | impl ops::Mul<FeeRate> for Weight { |
182 | 34 | type Output = NumOpResult<Amount>; |
@@ -341,20 +193,6 @@ crate::internal_macros::impl_op_for_references! { |
341 | 193 | } |
342 | 194 | } |
343 | 195 |
|
344 | | -impl Weight { |
345 | | - /// Checked fee rate multiplication. |
346 | | - /// |
347 | | - /// Computes the absolute fee amount for a given [`FeeRate`] at this weight. When the resulting |
348 | | - /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is |
349 | | - /// enough instead of falling short if rounded down. |
350 | | - /// |
351 | | - /// Returns [`None`] if overflow occurred. |
352 | | - #[must_use] |
353 | | - pub const fn checked_mul_by_fee_rate(self, fee_rate: FeeRate) -> Option<Amount> { |
354 | | - fee_rate.checked_mul_by_weight(self) |
355 | | - } |
356 | | -} |
357 | | - |
358 | 196 | #[cfg(test)] |
359 | 197 | mod tests { |
360 | 198 | use super::*; |
|
0 commit comments