Skip to content

Commit e8ce988

Browse files
committed
Add checked weight division to Amount
Enable Amount type to be divided by weight. Without this change, amount and weight types need to be cast to a common type before division can take place. A floor and ceiling version are added depending on how rounding should occur during division.
1 parent 99233f1 commit e8ce988

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)