Skip to content

Commit 972776a

Browse files
committed
Merge rust-bitcoin#5326: Backport checked div by weight to 0.32.x
e8ce988 Add checked weight division to Amount (yancy) Pull request description: For comparison, in units 1.0 rc, the counterpart test is here: https://github.com/rust-bitcoin/rust-bitcoin/blob/master/units/src/fee.rs#L190 ACKs for top commit: apoelstra: ACK e8ce988; successfully ran local tests; noting that `div_ceil` is not yet available in the 0.32.x MSRV tcharding: ACK e8ce988 Tree-SHA512: 381b57c2dcc3c46ca61f88a16caa0d8a396efa5be7b0f13ad1dc30412da14dd35cb7afbaca140970193eeb44f952bd2aea50b4bb0f519a6336db196bad62400d
2 parents 57ab8fc + e8ce988 commit 972776a

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

units/src/amount.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ use ::serde::{Deserialize, Serialize};
1818
use internals::error::InputString;
1919
use internals::write_err;
2020

21+
#[cfg(feature = "alloc")]
22+
use crate::{FeeRate, Weight};
23+
2124
/// A set of denominations in which amounts can be expressed.
2225
///
2326
/// # Examples
@@ -1039,6 +1042,34 @@ impl Amount {
10391042
/// Returns [None] if overflow occurred.
10401043
pub fn checked_div(self, rhs: u64) -> Option<Amount> { self.0.checked_div(rhs).map(Amount) }
10411044

1045+
/// Checked weight ceiling division.
1046+
///
1047+
/// Be aware that integer division loses the remainder if no exact division
1048+
/// can be made. This method rounds up ensuring the transaction fee-rate is
1049+
/// sufficient. See also [`Amount::div_by_weight_floor`].
1050+
///
1051+
/// [`None`] is returned if an overflow occurred.
1052+
#[cfg(feature = "alloc")]
1053+
pub fn div_by_weight_ceil(self, rhs: Weight) -> Option<FeeRate> {
1054+
let sats = self.0.checked_mul(1000)?;
1055+
let wu = rhs.to_wu();
1056+
1057+
let fee_rate = sats.checked_add(wu.checked_sub(1)?)?.checked_div(wu)?;
1058+
Some(FeeRate::from_sat_per_kwu(fee_rate))
1059+
}
1060+
1061+
/// Checked weight floor division.
1062+
///
1063+
/// Be aware that integer division loses the remainder if no exact division
1064+
/// can be made. See also [`Amount::div_by_weight_ceil`].
1065+
///
1066+
/// Returns [`None`] if overflow occurred.
1067+
#[cfg(feature = "alloc")]
1068+
pub fn div_by_weight_floor(self, rhs: Weight) -> Option<FeeRate> {
1069+
let fee_rate = self.0.checked_mul(1_000)?.checked_div(rhs.to_wu())?;
1070+
Some(FeeRate::from_sat_per_kwu(fee_rate))
1071+
}
1072+
10421073
/// Checked remainder.
10431074
///
10441075
/// Returns [None] if overflow occurred.
@@ -2117,6 +2148,48 @@ mod tests {
21172148
assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3)));
21182149
}
21192150

2151+
#[cfg(feature = "alloc")]
2152+
#[test]
2153+
fn amount_checked_div_by_weight_ceil() {
2154+
let weight = Weight::from_kwu(1).unwrap();
2155+
let fee_rate = Amount::from_sat(1).div_by_weight_ceil(weight).unwrap();
2156+
// 1 sats / 1,000 wu = 1 sats/kwu
2157+
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
2158+
2159+
let weight = Weight::from_wu(381);
2160+
let fee_rate = Amount::from_sat(329).div_by_weight_ceil(weight).unwrap();
2161+
// 329 sats / 381 wu = 863.5 sats/kwu
2162+
// round up to 864
2163+
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864));
2164+
2165+
let fee_rate = Amount::MAX.div_by_weight_ceil(weight);
2166+
assert!(fee_rate.is_none());
2167+
2168+
let fee_rate = Amount::ONE_SAT.div_by_weight_ceil(Weight::ZERO);
2169+
assert!(fee_rate.is_none());
2170+
}
2171+
2172+
#[cfg(feature = "alloc")]
2173+
#[test]
2174+
fn amount_checked_div_by_weight_floor() {
2175+
let weight = Weight::from_kwu(1).unwrap();
2176+
let fee_rate = Amount::from_sat(1).div_by_weight_floor(weight).unwrap();
2177+
// 1 sats / 1,000 wu = 1 sats/kwu
2178+
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
2179+
2180+
let weight = Weight::from_wu(381);
2181+
let fee_rate = Amount::from_sat(329).div_by_weight_floor(weight).unwrap();
2182+
// 329 sats / 381 wu = 863.5 sats/kwu
2183+
// round down to 863
2184+
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
2185+
2186+
let fee_rate = Amount::MAX.div_by_weight_floor(weight);
2187+
assert!(fee_rate.is_none());
2188+
2189+
let fee_rate = Amount::ONE_SAT.div_by_weight_floor(Weight::ZERO);
2190+
assert!(fee_rate.is_none());
2191+
}
2192+
21202193
#[test]
21212194
#[cfg(not(debug_assertions))]
21222195
fn unchecked_amount_add() {

0 commit comments

Comments
 (0)