Remove checked_ prefix from fee functions
The `Amount` and `FeeRate` types are not simple int wrappers, as such we do not need to mimic the stdlib too closely. Having the `checked_` prefix to the functions that do fee calcualtions adds no additional meaning. Note that I'm about to change the return type to use `NumOpResult` further justifying this change. Note we leave the 'Checked foo' in the functions docs title because the functions are still checked.
This commit is contained in:
parent
052514e6ff
commit
75106e6d82
|
@ -268,17 +268,17 @@ fn positive_sub() {
|
|||
#[test]
|
||||
fn amount_checked_div_by_weight_ceil() {
|
||||
let weight = Weight::from_kwu(1).unwrap();
|
||||
let fee_rate = sat(1).checked_div_by_weight_ceil(weight).unwrap();
|
||||
let fee_rate = sat(1).div_by_weight_ceil(weight).unwrap();
|
||||
// 1 sats / 1,000 wu = 1 sats/kwu
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||
|
||||
let weight = Weight::from_wu(381);
|
||||
let fee_rate = sat(329).checked_div_by_weight_ceil(weight).unwrap();
|
||||
let fee_rate = sat(329).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).unwrap());
|
||||
|
||||
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_ceil(Weight::ZERO);
|
||||
let fee_rate = Amount::ONE_SAT.div_by_weight_ceil(Weight::ZERO);
|
||||
assert!(fee_rate.is_none());
|
||||
}
|
||||
|
||||
|
@ -286,17 +286,17 @@ fn amount_checked_div_by_weight_ceil() {
|
|||
#[test]
|
||||
fn amount_checked_div_by_weight_floor() {
|
||||
let weight = Weight::from_kwu(1).unwrap();
|
||||
let fee_rate = sat(1).checked_div_by_weight_floor(weight).unwrap();
|
||||
let fee_rate = sat(1).div_by_weight_floor(weight).unwrap();
|
||||
// 1 sats / 1,000 wu = 1 sats/kwu
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||
|
||||
let weight = Weight::from_wu(381);
|
||||
let fee_rate = sat(329).checked_div_by_weight_floor(weight).unwrap();
|
||||
let fee_rate = sat(329).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).unwrap());
|
||||
|
||||
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_floor(Weight::ZERO);
|
||||
let fee_rate = Amount::ONE_SAT.div_by_weight_floor(Weight::ZERO);
|
||||
assert!(fee_rate.is_none());
|
||||
}
|
||||
|
||||
|
@ -307,31 +307,31 @@ fn amount_checked_div_by_fee_rate() {
|
|||
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
|
||||
// Test floor division
|
||||
let weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
||||
let weight = amount.div_by_fee_rate_floor(fee_rate).unwrap();
|
||||
// 1000 sats / (2 sats/kwu) = 500,000 wu
|
||||
assert_eq!(weight, Weight::from_wu(500_000));
|
||||
|
||||
// Test ceiling division
|
||||
let weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
let weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
assert_eq!(weight, Weight::from_wu(500_000)); // Same result for exact division
|
||||
|
||||
// Test truncation behavior
|
||||
let amount = sat(1000);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let floor_weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
||||
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
let floor_weight = amount.div_by_fee_rate_floor(fee_rate).unwrap();
|
||||
let ceil_weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
assert_eq!(floor_weight, Weight::from_wu(333_333));
|
||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||
|
||||
// Test division by zero
|
||||
let zero_fee_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
||||
assert!(amount.checked_div_by_fee_rate_floor(zero_fee_rate).is_none());
|
||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
||||
assert!(amount.div_by_fee_rate_floor(zero_fee_rate).is_none());
|
||||
assert!(amount.div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
||||
|
||||
// Test with maximum amount
|
||||
let max_amount = Amount::MAX;
|
||||
let small_fee_rate = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
let weight = max_amount.checked_div_by_fee_rate_floor(small_fee_rate).unwrap();
|
||||
let weight = max_amount.div_by_fee_rate_floor(small_fee_rate).unwrap();
|
||||
// 21_000_000_0000_0000 sats / (1 sat/kwu) = 2_100_000_000_000_000_000 wu
|
||||
assert_eq!(weight, Weight::from_wu(2_100_000_000_000_000_000));
|
||||
}
|
||||
|
|
|
@ -411,7 +411,7 @@ impl Amount {
|
|||
///
|
||||
/// Returns [`None`] if overflow occurred.
|
||||
#[must_use]
|
||||
pub const fn checked_div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
||||
pub const fn div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
||||
let wu = weight.to_wu();
|
||||
if wu == 0 {
|
||||
return None;
|
||||
|
@ -441,12 +441,12 @@ impl Amount {
|
|||
/// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
|
||||
/// let amount = Amount::from_sat(10)?;
|
||||
/// let weight = Weight::from_wu(300);
|
||||
/// let fee_rate = amount.checked_div_by_weight_ceil(weight);
|
||||
/// let fee_rate = amount.div_by_weight_ceil(weight);
|
||||
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
|
||||
/// # Ok::<_, amount::OutOfRangeError>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub const fn checked_div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
||||
pub const fn div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
||||
let wu = weight.to_wu();
|
||||
if wu == 0 {
|
||||
return None;
|
||||
|
@ -471,7 +471,7 @@ impl Amount {
|
|||
///
|
||||
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
||||
#[must_use]
|
||||
pub const fn checked_div_by_fee_rate_floor(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||
pub const fn div_by_fee_rate_floor(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||
if let Some(msats) = self.to_sat().checked_mul(1000) {
|
||||
if let Some(wu) = msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) {
|
||||
return Some(Weight::from_wu(wu));
|
||||
|
@ -487,7 +487,7 @@ impl Amount {
|
|||
///
|
||||
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
||||
#[must_use]
|
||||
pub const fn checked_div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||
pub const fn div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||
// Use ceil because result is used as the divisor.
|
||||
let rate = fee_rate.to_sat_per_kwu_ceil();
|
||||
if rate == 0 {
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
//! Either the weight or fee rate can be calculated if one knows the total fee and either of the
|
||||
//! other values. Note however that such calculations truncate (as for integer division).
|
||||
//!
|
||||
//! We provide `fee.checked_div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate
|
||||
//! We provide `fee.div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate
|
||||
//! required to pay at least `fee` for transaction with `weight`.
|
||||
//!
|
||||
//! We support various `core::ops` traits all of which return [`NumOpResult<T>`].
|
||||
//!
|
||||
//! For specific methods see:
|
||||
//!
|
||||
//! * [`Amount::checked_div_by_weight_floor`]
|
||||
//! * [`Amount::checked_div_by_weight_ceil`]
|
||||
//! * [`Amount::checked_div_by_fee_rate_floor`]
|
||||
//! * [`Amount::checked_div_by_fee_rate_ceil`]
|
||||
//! * [`Weight::checked_mul_by_fee_rate`]
|
||||
//! * [`FeeRate::checked_mul_by_weight`]
|
||||
//! * [`Amount::div_by_weight_floor`]
|
||||
//! * [`Amount::div_by_weight_ceil`]
|
||||
//! * [`Amount::div_by_fee_rate_floor`]
|
||||
//! * [`Amount::div_by_fee_rate_ceil`]
|
||||
//! * [`Weight::mul_by_fee_rate`]
|
||||
//! * [`FeeRate::mul_by_weight`]
|
||||
//! * [`FeeRate::to_fee`]
|
||||
|
||||
use core::ops;
|
||||
|
@ -33,7 +33,7 @@ crate::internal_macros::impl_op_for_references! {
|
|||
impl ops::Mul<FeeRate> for Weight {
|
||||
type Output = NumOpResult<Amount>;
|
||||
fn mul(self, rhs: FeeRate) -> Self::Output {
|
||||
match rhs.checked_mul_by_weight(self) {
|
||||
match rhs.mul_by_weight(self) {
|
||||
Some(amount) => R::Valid(amount),
|
||||
None => R::Error(E::while_doing(MathOp::Mul)),
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ crate::internal_macros::impl_op_for_references! {
|
|||
impl ops::Mul<Weight> for FeeRate {
|
||||
type Output = NumOpResult<Amount>;
|
||||
fn mul(self, rhs: Weight) -> Self::Output {
|
||||
match self.checked_mul_by_weight(rhs) {
|
||||
match self.mul_by_weight(rhs) {
|
||||
Some(amount) => R::Valid(amount),
|
||||
None => R::Error(E::while_doing(MathOp::Mul)),
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ crate::internal_macros::impl_op_for_references! {
|
|||
type Output = NumOpResult<FeeRate>;
|
||||
|
||||
fn div(self, rhs: Weight) -> Self::Output {
|
||||
self.checked_div_by_weight_floor(rhs).valid_or_error(MathOp::Div)
|
||||
self.div_by_weight_floor(rhs).valid_or_error(MathOp::Div)
|
||||
}
|
||||
}
|
||||
impl ops::Div<Weight> for NumOpResult<Amount> {
|
||||
|
@ -155,7 +155,7 @@ crate::internal_macros::impl_op_for_references! {
|
|||
type Output = NumOpResult<Weight>;
|
||||
|
||||
fn div(self, rhs: FeeRate) -> Self::Output {
|
||||
self.checked_div_by_fee_rate_floor(rhs).valid_or_error(MathOp::Div)
|
||||
self.div_by_fee_rate_floor(rhs).valid_or_error(MathOp::Div)
|
||||
}
|
||||
}
|
||||
impl ops::Div<FeeRate> for NumOpResult<Amount> {
|
||||
|
@ -211,25 +211,25 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn checked_weight_mul() {
|
||||
fn weight_mul() {
|
||||
let weight = Weight::from_vb(10).unwrap();
|
||||
let fee: Amount = FeeRate::from_sat_per_vb(10)
|
||||
.unwrap()
|
||||
.checked_mul_by_weight(weight)
|
||||
.mul_by_weight(weight)
|
||||
.expect("expected Amount");
|
||||
assert_eq!(Amount::from_sat_u32(100), fee);
|
||||
|
||||
let fee = FeeRate::from_sat_per_kwu(10).unwrap().checked_mul_by_weight(Weight::MAX);
|
||||
let fee = FeeRate::from_sat_per_kwu(10).unwrap().mul_by_weight(Weight::MAX);
|
||||
assert!(fee.is_none());
|
||||
|
||||
let weight = Weight::from_vb(3).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
||||
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
|
||||
let fee = fee_rate.mul_by_weight(weight).unwrap();
|
||||
assert_eq!(Amount::from_sat_u32(9), fee);
|
||||
|
||||
let weight = Weight::from_wu(381);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(864).unwrap();
|
||||
let fee = weight.checked_mul_by_fee_rate(fee_rate).unwrap();
|
||||
let fee = weight.mul_by_fee_rate(fee_rate).unwrap();
|
||||
// 381 * 0.864 yields 329.18.
|
||||
// The result is then rounded up to 330.
|
||||
assert_eq!(fee, Amount::from_sat_u32(330));
|
||||
|
@ -277,12 +277,12 @@ mod tests {
|
|||
assert_eq!(weight, Weight::from_wu(333_333));
|
||||
|
||||
// Verify that ceiling division gives different result
|
||||
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
let ceil_weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||
|
||||
// Test that division by zero returns None
|
||||
let zero_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
||||
assert!(amount.checked_div_by_fee_rate_floor(zero_rate).is_none());
|
||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_rate).is_none());
|
||||
assert!(amount.div_by_fee_rate_floor(zero_rate).is_none());
|
||||
assert!(amount.div_by_fee_rate_ceil(zero_rate).is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ impl FeeRate {
|
|||
/// wrapping.
|
||||
pub const fn to_fee(self, weight: Weight) -> Amount {
|
||||
// No `unwrap_or()` in const context.
|
||||
match self.checked_mul_by_weight(weight) {
|
||||
match self.mul_by_weight(weight) {
|
||||
Some(fee) => fee,
|
||||
None => Amount::MAX,
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ impl FeeRate {
|
|||
/// This is equivalent to `Self::checked_mul_by_weight()`.
|
||||
#[must_use]
|
||||
#[deprecated(since = "TBD", note = "use `to_fee()` instead")]
|
||||
pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) }
|
||||
pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.mul_by_weight(weight) }
|
||||
|
||||
/// Calculates the fee by multiplying this fee rate by weight, in virtual bytes, returning [`None`]
|
||||
/// if an overflow occurred.
|
||||
|
@ -227,7 +227,7 @@ impl FeeRate {
|
|||
///
|
||||
/// Returns [`None`] if overflow occurred.
|
||||
#[must_use]
|
||||
pub const fn checked_mul_by_weight(self, weight: Weight) -> Option<Amount> {
|
||||
pub const fn mul_by_weight(self, weight: Weight) -> Option<Amount> {
|
||||
let wu = weight.to_wu();
|
||||
if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) {
|
||||
// Bump by 999 to do ceil division using kwu.
|
||||
|
|
|
@ -171,8 +171,8 @@ impl Weight {
|
|||
///
|
||||
/// Returns [`None`] if overflow occurred.
|
||||
#[must_use]
|
||||
pub const fn checked_mul_by_fee_rate(self, fee_rate: FeeRate) -> Option<Amount> {
|
||||
fee_rate.checked_mul_by_weight(self)
|
||||
pub const fn mul_by_fee_rate(self, fee_rate: FeeRate) -> Option<Amount> {
|
||||
fee_rate.mul_by_weight(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue