Merge rust-bitcoin/rust-bitcoin#3587: Split checked_div_by_weight into floor and ceiling version

8b928a1515 Split checked_div_by_weight into floor and ceiling version (yancy)

Pull request description:

  closes https://github.com/rust-bitcoin/rust-bitcoin/issues/3563

ACKs for top commit:
  tcharding:
    ACK 8b928a1515
  apoelstra:
    ACK 8b928a151515b00d44e0c13a866eda874c73dbf6; successfully ran local tests

Tree-SHA512: 1f5a669b24dd5896cf5b13ecdb299fc1e4d449e379e5ae811a66a2efaf2565b7c7edc3e70275cde3b5e0aa5e29c1bbf180be6514a0de78ffd00ac929dbabbb87
This commit is contained in:
merge-script 2024-11-08 15:58:36 +00:00
commit 7df5e7c1bc
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 42 additions and 9 deletions

View File

@ -140,22 +140,43 @@ fn checked_arithmetic() {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[test] #[test]
fn amount_checked_div_by_weight() { fn amount_checked_div_by_weight_ceil() {
let weight = Weight::from_kwu(1).unwrap(); let weight = Weight::from_kwu(1).unwrap();
let fee_rate = Amount::from_sat(1).checked_div_by_weight(weight).unwrap(); let fee_rate = Amount::from_sat(1).checked_div_by_weight_ceil(weight).unwrap();
// 1 sats / 1,000 wu = 1 sats/kwu // 1 sats / 1,000 wu = 1 sats/kwu
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1)); assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
let weight = Weight::from_wu(381); let weight = Weight::from_wu(381);
let fee_rate = Amount::from_sat(329).checked_div_by_weight(weight).unwrap(); let fee_rate = Amount::from_sat(329).checked_div_by_weight_ceil(weight).unwrap();
// 329 sats / 381 wu = 863.5 sats/kwu // 329 sats / 381 wu = 863.5 sats/kwu
// round up to 864 // round up to 864
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864)); assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864));
let fee_rate = Amount::MAX.checked_div_by_weight(weight); let fee_rate = Amount::MAX.checked_div_by_weight_ceil(weight);
assert!(fee_rate.is_none()); assert!(fee_rate.is_none());
let fee_rate = Amount::ONE_SAT.checked_div_by_weight(Weight::ZERO); let fee_rate = Amount::ONE_SAT.checked_div_by_weight_ceil(Weight::ZERO);
assert!(fee_rate.is_none());
}
#[cfg(feature = "alloc")]
#[test]
fn amount_checked_div_by_weight_floor() {
let weight = Weight::from_kwu(1).unwrap();
let fee_rate = Amount::from_sat(1).checked_div_by_weight_floor(weight).unwrap();
// 1 sats / 1,000 wu = 1 sats/kwu
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
let weight = Weight::from_wu(381);
let fee_rate = Amount::from_sat(329).checked_div_by_weight_floor(weight).unwrap();
// 329 sats / 381 wu = 863.5 sats/kwu
// round down to 863
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
let fee_rate = Amount::MAX.checked_div_by_weight_floor(weight);
assert!(fee_rate.is_none());
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_floor(Weight::ZERO);
assert!(fee_rate.is_none()); assert!(fee_rate.is_none());
} }

View File

@ -231,22 +231,34 @@ impl Amount {
/// Returns [`None`] if overflow occurred. /// Returns [`None`] if overflow occurred.
pub fn checked_div(self, rhs: u64) -> Option<Amount> { self.0.checked_div(rhs).map(Amount) } pub fn checked_div(self, rhs: u64) -> Option<Amount> { self.0.checked_div(rhs).map(Amount) }
/// Checked weight division. /// Checked weight ceiling division.
/// ///
/// Be aware that integer division loses the remainder if no exact division /// Be aware that integer division loses the remainder if no exact division
/// can be made. This method rounds up ensuring the transaction fee-rate is /// can be made. This method rounds up ensuring the transaction fee-rate is
/// sufficient. If you wish to round down use `amount / weight`. /// sufficient. See also [`Amount::checked_div_by_weight_floor`].
/// ///
/// Returns [`None`] if overflow occurred. /// Returns [`None`] if overflow occurred.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn checked_div_by_weight(self, rhs: Weight) -> Option<FeeRate> { pub fn checked_div_by_weight_ceil(self, rhs: Weight) -> Option<FeeRate> {
let sats = self.0.checked_mul(1000)?; let sats = self.0.checked_mul(1_000)?;
let wu = rhs.to_wu(); let wu = rhs.to_wu();
let fee_rate = sats.checked_add(wu.checked_sub(1)?)?.checked_div(wu)?; let fee_rate = sats.checked_add(wu.checked_sub(1)?)?.checked_div(wu)?;
Some(FeeRate::from_sat_per_kwu(fee_rate)) Some(FeeRate::from_sat_per_kwu(fee_rate))
} }
/// Checked weight floor division.
///
/// Be aware that integer division loses the remainder if no exact division
/// can be made. See also [`Amount::checked_div_by_weight_ceil`].
///
/// Returns [`None`] if overflow occurred.
#[cfg(feature = "alloc")]
pub fn checked_div_by_weight_floor(self, rhs: Weight) -> Option<FeeRate> {
let fee_rate = self.0.checked_mul(1_000)?.checked_div(rhs.to_wu())?;
Some(FeeRate::from_sat_per_kwu(fee_rate))
}
/// Checked remainder. /// Checked remainder.
/// ///
/// Returns [`None`] if overflow occurred. /// Returns [`None`] if overflow occurred.