#[cfg(feature = "serde")]
pub mod serde;
use core::num::NonZeroU64;
use core::ops;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use NumOpResult as R;
use crate::result::{MathErrorKind, MathOp, NumOpError as E, NumOpResult};
use crate::{Amount, Weight};
mod encapsulate {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FeeRate(u64);
impl FeeRate {
#[inline]
pub(crate) const fn from_sat_per_mvb(sat_mvb: u64) -> Self { Self(sat_mvb) }
#[inline]
pub(crate) const fn to_sat_per_mvb(self) -> u64 { self.0 }
}
}
#[doc(inline)]
pub use encapsulate::FeeRate;
use internals::const_casts;
impl FeeRate {
pub const ZERO: Self = Self::from_sat_per_mvb(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self::from_sat_per_mvb(u64::MAX);
#[inline]
pub const fn from_sat_per_kwu(sat_kwu: u64) -> NumOpResult<Self> {
match sat_kwu.checked_mul(4_000) {
Some(fee_rate) => R::Valid(Self::from_sat_per_mvb(fee_rate)),
None => R::Error(E::while_doing(MathErrorKind::Overflow {
op: MathOp::Mul,
is_negative: false,
})),
}
}
#[inline]
pub const fn from_sat_per_kwu_u32(sat_kwu: u32) -> Self {
let fee_rate = (const_casts::u32_to_u64(sat_kwu)) * 4_000;
Self::from_sat_per_mvb(fee_rate)
}
#[inline]
pub const fn from_per_kwu(rate: Amount) -> Self {
Self::from_sat_per_mvb(rate.to_sat() * 4_000)
}
#[inline]
pub const fn from_sat_per_vb(sat_vb: u64) -> NumOpResult<Self> {
match sat_vb.checked_mul(1_000_000) {
Some(fee_rate) => R::Valid(Self::from_sat_per_mvb(fee_rate)),
None => R::Error(E::while_doing(MathErrorKind::Overflow {
op: MathOp::Mul,
is_negative: false,
})),
}
}
#[inline]
pub const fn from_sat_per_vb_u32(sat_vb: u32) -> Self {
let fee_rate = (const_casts::u32_to_u64(sat_vb)) * 1_000_000;
Self::from_sat_per_mvb(fee_rate)
}
#[inline]
pub const fn from_per_vb(rate: Amount) -> NumOpResult<Self> {
match rate.to_sat().checked_mul(1_000_000) {
Some(per_mvb) => R::Valid(Self::from_sat_per_mvb(per_mvb)),
None => R::Error(E::while_doing(MathErrorKind::Overflow {
op: MathOp::Mul,
is_negative: false,
})),
}
}
#[inline]
pub const fn from_sat_per_kvb(sat_kvb: u64) -> NumOpResult<Self> {
match sat_kvb.checked_mul(1_000) {
Some(fee_rate) => R::Valid(Self::from_sat_per_mvb(fee_rate)),
None => R::Error(E::while_doing(MathErrorKind::Overflow {
op: MathOp::Mul,
is_negative: false,
})),
}
}
#[inline]
pub const fn from_sat_per_kvb_u32(sat_kvb: u32) -> Self {
let fee_rate = (const_casts::u32_to_u64(sat_kvb)) * 1_000;
Self::from_sat_per_mvb(fee_rate)
}
#[inline]
pub const fn from_per_kvb(rate: Amount) -> Self {
Self::from_sat_per_mvb(rate.to_sat() * 1_000)
}
#[inline]
pub const fn to_sat_per_kwu_floor(self) -> u64 { self.to_sat_per_mvb() / 4_000 }
#[inline]
pub const fn to_sat_per_kwu_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(4_000) }
#[inline]
pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000_000 }
#[inline]
pub const fn to_sat_per_vb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000_000) }
#[inline]
pub const fn to_sat_per_kvb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000 }
#[inline]
pub const fn to_sat_per_kvb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000) }
#[inline]
#[must_use]
pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
let Some(sat_mvb) = self.to_sat_per_mvb().checked_mul(rhs) else { return None };
Some(Self::from_sat_per_mvb(sat_mvb))
}
#[inline]
#[must_use]
pub const fn checked_div(self, rhs: u64) -> Option<Self> {
let Some(sat_mvb) = self.to_sat_per_mvb().checked_div(rhs) else { return None };
Some(Self::from_sat_per_mvb(sat_mvb))
}
#[inline]
#[must_use]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let Some(sat_mvb) = self.to_sat_per_mvb().checked_add(rhs.to_sat_per_mvb()) else {
return None;
};
Some(Self::from_sat_per_mvb(sat_mvb))
}
#[inline]
#[must_use]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let Some(sat_mvb) = self.to_sat_per_mvb().checked_sub(rhs.to_sat_per_mvb()) else {
return None;
};
Some(Self::from_sat_per_mvb(sat_mvb))
}
#[inline]
#[must_use]
pub const fn saturating_mul(self, rhs: u64) -> Self {
Self::from_sat_per_mvb(self.to_sat_per_mvb().saturating_mul(rhs))
}
#[inline]
#[must_use]
pub const fn saturating_add(self, rhs: Self) -> Self {
Self::from_sat_per_mvb(self.to_sat_per_mvb().saturating_add(rhs.to_sat_per_mvb()))
}
#[inline]
#[must_use]
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self::from_sat_per_mvb(self.to_sat_per_mvb().saturating_sub(rhs.to_sat_per_mvb()))
}
#[inline]
pub const fn to_fee(self, weight: Weight) -> Amount {
match self.mul_by_weight(weight) {
NumOpResult::Valid(fee) => fee,
NumOpResult::Error(_) => Amount::MAX,
}
}
#[inline]
pub const fn mul_by_weight(self, weight: Weight) -> NumOpResult<Amount> {
let numerator = const_casts::u64_to_u128(self.to_sat_per_mvb())
* const_casts::u64_to_u128(weight.to_wu());
let denominator = 4_000_000_u128;
let quotient = numerator / denominator;
let fee_sat = if numerator % denominator == 0 { quotient } else { quotient + 1 };
if fee_sat <= const_casts::u64_to_u128(Amount::MAX.to_sat()) {
if let Ok(fee_amount) = Amount::from_sat(fee_sat as u64) {
return NumOpResult::Valid(fee_amount);
}
}
NumOpResult::Error(E::while_doing(MathErrorKind::Overflow {
op: MathOp::Mul,
is_negative: false,
}))
}
}
crate::internal_macros::impl_op_for_references! {
impl ops::Add<FeeRate> for FeeRate {
type Output = FeeRate;
#[track_caller]
fn add(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() + rhs.to_sat_per_mvb()) }
}
impl ops::Sub<FeeRate> for FeeRate {
type Output = FeeRate;
#[track_caller]
fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() - rhs.to_sat_per_mvb()) }
}
impl ops::Div<NonZeroU64> for FeeRate {
type Output = FeeRate;
fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_sat_per_mvb(self.to_sat_per_mvb() / rhs) }
}
}
crate::internal_macros::impl_add_assign!(FeeRate);
crate::internal_macros::impl_sub_assign!(FeeRate);
impl core::iter::Sum for FeeRate {
#[inline]
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
Self::from_sat_per_mvb(iter.map(Self::to_sat_per_mvb).sum())
}
}
impl<'a> core::iter::Sum<&'a Self> for FeeRate {
#[inline]
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
Self::from_sat_per_mvb(iter.map(|f| Self::to_sat_per_mvb(*f)).sum())
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for FeeRate {
#[inline]
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let choice = u.int_in_range(0..=4)?;
match choice {
0 => Ok(Self::MIN),
1 => Ok(Self::from_sat_per_vb_u32(1)),
2 => Ok(Self::from_sat_per_vb_u32(3)),
3 => Ok(Self::MAX),
_ => Ok(Self::from_sat_per_mvb(u64::arbitrary(u)?)),
}
}
}
#[cfg(test)]
mod tests {
use core::num::NonZeroU64;
use super::*;
const ONE: FeeRate = FeeRate::from_sat_per_kwu_u32(1);
const TWO: FeeRate = FeeRate::from_sat_per_kwu_u32(2);
const THREE: FeeRate = FeeRate::from_sat_per_kwu_u32(3);
const TEN: FeeRate = FeeRate::from_sat_per_kwu_u32(10);
const ONE_HUNDRED: FeeRate = FeeRate::from_sat_per_kwu_u32(100);
#[test]
#[allow(clippy::op_ref)]
fn feerate_div_nonzero() {
let rate = FeeRate::from_sat_per_kwu_u32(200);
let divisor = NonZeroU64::new(2).unwrap();
assert_eq!(rate / divisor, ONE_HUNDRED);
assert_eq!(&rate / &divisor, ONE_HUNDRED);
}
#[test]
#[allow(clippy::op_ref)]
fn addition() {
assert!(ONE + TWO == THREE);
assert!(&ONE + TWO == THREE);
assert!(ONE + &TWO == THREE);
assert!(&ONE + &TWO == THREE);
}
#[test]
#[allow(clippy::op_ref)]
fn subtract() {
assert_eq!(THREE - TWO, ONE);
assert_eq!(&THREE - TWO, ONE);
assert_eq!(THREE - &TWO, ONE);
assert_eq!(&THREE - &TWO, ONE);
}
#[test]
fn add_assign() {
let mut f = ONE;
f += TWO;
assert_eq!(f, THREE);
let mut f = ONE;
f += &TWO;
assert_eq!(f, THREE);
let mut f = NumOpResult::Valid(ONE);
f += TWO;
assert_eq!(f, NumOpResult::Valid(THREE));
let mut f = NumOpResult::Valid(ONE);
f += NumOpResult::Valid(TWO);
assert_eq!(f, NumOpResult::Valid(THREE));
}
#[test]
fn sub_assign() {
let mut f = THREE;
f -= TWO;
assert_eq!(f, ONE);
let mut f = THREE;
f -= &TWO;
assert_eq!(f, ONE);
let mut f = NumOpResult::Valid(THREE);
f -= TWO;
assert_eq!(f, NumOpResult::Valid(ONE));
let mut f = NumOpResult::Valid(THREE);
f -= NumOpResult::Valid(TWO);
assert_eq!(f, NumOpResult::Valid(ONE));
}
#[test]
fn checked_add() {
assert_eq!(ONE.checked_add(TWO).unwrap(), THREE);
let _ = FeeRate::from_sat_per_kvb_u32(u32::MAX).checked_add(ONE).unwrap();
let fee_rate = FeeRate::from_sat_per_mvb(u64::MAX).checked_add(ONE);
assert!(fee_rate.is_none());
}
#[test]
fn checked_sub() {
assert_eq!(THREE.checked_sub(TWO).unwrap(), ONE);
let fee_rate = FeeRate::ZERO.checked_sub(ONE);
assert!(fee_rate.is_none());
}
#[test]
fn fee_rate_const() {
assert_eq!(FeeRate::ZERO.to_sat_per_kwu_floor(), 0);
assert_eq!(FeeRate::MIN.to_sat_per_kwu_floor(), u64::MIN);
assert_eq!(FeeRate::MAX.to_sat_per_kwu_floor(), u64::MAX / 4_000);
}
#[test]
fn fee_rate_from_sat_per_vb() {
let fee_rate = FeeRate::from_sat_per_vb(10).unwrap();
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu_u32(2500));
}
#[test]
fn fee_rate_from_sat_per_vb_u32() {
let fee_rate = FeeRate::from_sat_per_vb_u32(10);
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu_u32(2500));
}
#[test]
fn fee_rate_from_sat_per_kvb() {
let fee_rate = FeeRate::from_sat_per_kvb(11).unwrap();
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
let fee_rate = FeeRate::from_sat_per_kvb(16_800_000_000_000_000).unwrap();
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(16_800_000_000_000_000_000));
}
#[test]
fn fee_rate_from_sat_per_kvb_u32() {
let fee_rate = FeeRate::from_sat_per_kvb_u32(11);
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
}
#[test]
fn fee_rate_from_per_kvb() {
let fee_rate = FeeRate::from_per_kvb(Amount::from_sat_u32(11));
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
}
#[test]
fn fee_rate_from_sat_per_kwu() {
let fee_rate = FeeRate::from_sat_per_kwu(4_200_000_000_000_000).unwrap();
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(16_800_000_000_000_000_000));
}
#[test]
fn fee_rate_to_sat_per_x() {
let fee_rate = FeeRate::from_sat_per_mvb(2_000_400);
assert_eq!(fee_rate.to_sat_per_kwu_floor(), 500);
assert_eq!(fee_rate.to_sat_per_kwu_ceil(), 501);
assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
assert_eq!(fee_rate.to_sat_per_kvb_floor(), 2_000);
assert_eq!(fee_rate.to_sat_per_kvb_ceil(), 2_001);
let max = FeeRate::MAX;
assert_eq!(max.to_sat_per_kwu_ceil(), u64::MAX / 4_000 + 1);
assert_eq!(max.to_sat_per_vb_ceil(), u64::MAX / 1_000_000 + 1);
assert_eq!(max.to_sat_per_kvb_ceil(), u64::MAX / 1_000 + 1);
}
#[test]
fn fee_rate_from_per_x() {
let from_per_kwu = FeeRate::from_per_kwu(Amount::MAX);
assert_eq!(from_per_kwu, FeeRate::from_sat_per_mvb(8_400_000_000_000_000_000));
let from_per_kvb = FeeRate::from_per_kvb(Amount::MAX);
assert_eq!(from_per_kvb, FeeRate::from_sat_per_mvb(2_100_000_000_000_000_000));
assert!(FeeRate::from_per_vb(Amount::MAX).is_error());
let amount = Amount::from_sat(18_446_744_073_709).unwrap();
let from_per_vb = FeeRate::from_per_vb(amount).unwrap();
assert_eq!(from_per_vb, FeeRate::from_sat_per_mvb(18_446_744_073_709_000_000));
}
#[test]
fn checked_mul() {
let fee_rate = TEN.checked_mul(10).expect("expected feerate in sat/kwu");
assert_eq!(fee_rate, ONE_HUNDRED);
let fee_rate = TEN.checked_mul(u64::MAX);
assert!(fee_rate.is_none());
}
#[test]
fn checked_div() {
let fee_rate = TEN.checked_div(10).expect("expected feerate in sat/kwu");
assert_eq!(fee_rate, ONE);
let fee_rate = TEN.checked_div(0);
assert!(fee_rate.is_none());
}
#[test]
fn saturating_mul() {
assert_eq!(TEN.saturating_mul(10), ONE_HUNDRED);
assert_eq!(TEN.saturating_mul(0), FeeRate::ZERO);
assert_eq!(FeeRate::MAX.saturating_mul(2), FeeRate::MAX);
assert_eq!(TEN.saturating_mul(u64::MAX), FeeRate::MAX);
}
#[test]
fn saturating_add() {
assert_eq!(ONE.saturating_add(TWO), THREE);
assert_eq!(FeeRate::MAX.saturating_add(ONE), FeeRate::MAX);
assert_eq!(FeeRate::ZERO.saturating_add(FeeRate::ZERO), FeeRate::ZERO);
}
#[test]
fn saturating_sub() {
assert_eq!(THREE.saturating_sub(TWO), ONE);
assert_eq!(FeeRate::ZERO.saturating_sub(ONE), FeeRate::ZERO);
assert_eq!(ONE.saturating_sub(THREE), FeeRate::ZERO);
}
#[test]
fn mvb() {
let fee_rate = FeeRate::from_sat_per_mvb(1_234_567);
let got = fee_rate.to_sat_per_mvb();
assert_eq!(got, 1_234_567);
}
#[test]
fn mul_by_weight_round_up() {
let fee_rate = FeeRate::from_sat_per_kvb_u32(10);
let weight = Weight::from_wu(500);
let fee = fee_rate.mul_by_weight(weight).expect("expected fee amount");
assert_eq!(fee, Amount::from_sat(2).unwrap());
}
#[test]
fn mul_by_weight_preserves_mvb_precision() {
let fee_rate = FeeRate::from_sat_per_kvb_u32(101);
let weight = Weight::from_wu(4_000);
assert_eq!(fee_rate.mul_by_weight(weight).unwrap(), Amount::from_sat(101).unwrap());
}
}