Skip to content

Commit

Permalink
Merge rust-bitcoin#3587: Split checked_div_by_weight into floor and c…
Browse files Browse the repository at this point in the history
…eiling version

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

Pull request description:

  closes rust-bitcoin#3563

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

Tree-SHA512: 1f5a669b24dd5896cf5b13ecdb299fc1e4d449e379e5ae811a66a2efaf2565b7c7edc3e70275cde3b5e0aa5e29c1bbf180be6514a0de78ffd00ac929dbabbb87
  • Loading branch information
apoelstra committed Nov 8, 2024
2 parents 9d9f1d8 + 8b928a1 commit 7df5e7c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
31 changes: 26 additions & 5 deletions units/src/amount/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,22 +140,43 @@ fn checked_arithmetic() {

#[cfg(feature = "alloc")]
#[test]
fn amount_checked_div_by_weight() {
fn amount_checked_div_by_weight_ceil() {
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
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(weight).unwrap();
let fee_rate = Amount::from_sat(329).checked_div_by_weight_ceil(weight).unwrap();
// 329 sats / 381 wu = 863.5 sats/kwu
// round up to 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());

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());
}

Expand Down
20 changes: 16 additions & 4 deletions units/src/amount/unsigned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,34 @@ impl Amount {
/// Returns [`None`] if overflow occurred.
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
/// 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.
#[cfg(feature = "alloc")]
pub fn checked_div_by_weight(self, rhs: Weight) -> Option<FeeRate> {
let sats = self.0.checked_mul(1000)?;
pub fn checked_div_by_weight_ceil(self, rhs: Weight) -> Option<FeeRate> {
let sats = self.0.checked_mul(1_000)?;
let wu = rhs.to_wu();

let fee_rate = sats.checked_add(wu.checked_sub(1)?)?.checked_div(wu)?;
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.
///
/// Returns [`None`] if overflow occurred.
Expand Down

0 comments on commit 7df5e7c

Please sign in to comment.