Make FeeRate from sat constructors infallible
We now have constructors that take an arbitrary size fee rate (`Amount`). The `from_sat_per_foo` constructors can be made infallible by taking a `u32` instead of `u64`. This makes the API more ergonomic but limits the fee rate to just under 42 BTC which is plenty. Note we just delete the `from_sat_per_vb_u32` function because it is unreleased, in the past we had `from_sat_per_vb_unchecked` so we could put that back in if we wanted to be a bit more kind to downstream. Can be done later, we likely want to go over the public API before release and add a few things back in that we forgot to deprecate or could not for some reason during dev. Fuzz with a new function that consumes a `u32`.
This commit is contained in:
parent
3b0286bd56
commit
0ff8d82193
|
@ -751,7 +751,7 @@ fn default_dust_value() {
|
|||
assert!(script_p2wpkh.is_p2wpkh());
|
||||
assert_eq!(script_p2wpkh.minimal_non_dust(), Amount::from_sat_u32(294));
|
||||
assert_eq!(
|
||||
script_p2wpkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_u32(6)),
|
||||
script_p2wpkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb(6)),
|
||||
Some(Amount::from_sat_u32(588))
|
||||
);
|
||||
|
||||
|
@ -765,7 +765,7 @@ fn default_dust_value() {
|
|||
assert!(script_p2pkh.is_p2pkh());
|
||||
assert_eq!(script_p2pkh.minimal_non_dust(), Amount::from_sat_u32(546));
|
||||
assert_eq!(
|
||||
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_u32(6)),
|
||||
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb(6)),
|
||||
Some(Amount::from_sat_u32(1092))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1689,7 +1689,7 @@ mod tests {
|
|||
#[test]
|
||||
fn effective_value_happy_path() {
|
||||
let value = "1 cBTC".parse::<Amount>().unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10);
|
||||
let effective_value = effective_value(fee_rate, InputWeightPrediction::P2WPKH_MAX, value);
|
||||
|
||||
// 10 sat/kwu * 272 wu = 3 sats (rounding up)
|
||||
|
|
|
@ -127,7 +127,7 @@ impl Psbt {
|
|||
/// 1000 sats/vByte. 25k sats/vByte is obviously a mistake at this point.
|
||||
///
|
||||
/// [`extract_tx`]: Psbt::extract_tx
|
||||
pub const DEFAULT_MAX_FEE_RATE: FeeRate = FeeRate::from_sat_per_vb_u32(25_000);
|
||||
pub const DEFAULT_MAX_FEE_RATE: FeeRate = FeeRate::from_sat_per_vb(25_000);
|
||||
|
||||
/// An alias for [`extract_tx_fee_rate_limit`].
|
||||
///
|
||||
|
@ -1437,7 +1437,7 @@ mod tests {
|
|||
// Large fee rate errors if we pass in 1 sat/vb so just use this to get the error fee rate returned.
|
||||
let error_fee_rate = psbt
|
||||
.clone()
|
||||
.extract_tx_with_fee_rate_limit(FeeRate::from_sat_per_vb_u32(1))
|
||||
.extract_tx_with_fee_rate_limit(FeeRate::from_sat_per_vb(1))
|
||||
.map_err(|e| match e {
|
||||
ExtractTxError::AbsurdFeeRate { fee_rate, .. } => fee_rate,
|
||||
_ => panic!(""),
|
||||
|
@ -1475,7 +1475,7 @@ mod tests {
|
|||
ExtractTxError::AbsurdFeeRate { fee_rate, .. } => fee_rate,
|
||||
_ => panic!(""),
|
||||
}),
|
||||
Err(FeeRate::from_sat_per_kwu(6250003).unwrap()) // 6250000 is 25k sat/vbyte
|
||||
Err(FeeRate::from_sat_per_kwu(6250003)) // 6250000 is 25k sat/vbyte
|
||||
);
|
||||
|
||||
// Lowering the input satoshis by 1 lowers the sat/kwu by 3
|
||||
|
|
|
@ -2,7 +2,7 @@ use bitcoin::address::Address;
|
|||
use bitcoin::consensus::encode;
|
||||
use bitcoin::script::{self, ScriptExt as _};
|
||||
use bitcoin::{FeeRate, Network};
|
||||
use bitcoin_fuzz::fuzz_utils::{consume_random_bytes, consume_u64};
|
||||
use bitcoin_fuzz::fuzz_utils::{consume_random_bytes, consume_u32};
|
||||
use honggfuzz::fuzz;
|
||||
|
||||
fn do_test(data: &[u8]) {
|
||||
|
@ -17,7 +17,7 @@ fn do_test(data: &[u8]) {
|
|||
let _ = script.count_sigops_legacy();
|
||||
let _ = script.minimal_non_dust();
|
||||
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(consume_u64(&mut new_data)).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(consume_u32(&mut new_data));
|
||||
let _ = script.minimal_non_dust_custom(fee_rate);
|
||||
|
||||
let mut b = script::Builder::new();
|
||||
|
|
|
@ -35,3 +35,16 @@ pub fn consume_u64(data: &mut &[u8]) -> u64 {
|
|||
u64_bytes[7],
|
||||
])
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn consume_u32(data: &mut &[u8]) -> u32 {
|
||||
// We need at least 4 bytes to read a u32
|
||||
if data.len() < 4 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let (u32_bytes, rest) = data.split_at(4);
|
||||
*data = rest;
|
||||
|
||||
u32::from_le_bytes([u32_bytes[0], u32_bytes[1], u32_bytes[2], u32_bytes[3]])
|
||||
}
|
||||
|
|
|
@ -270,13 +270,13 @@ fn amount_checked_div_by_weight_ceil() {
|
|||
let weight = Weight::from_kwu(1).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());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
|
||||
|
||||
let weight = Weight::from_wu(381);
|
||||
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());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864));
|
||||
|
||||
let fee_rate = Amount::ONE_SAT.div_by_weight_ceil(Weight::ZERO);
|
||||
assert!(fee_rate.is_error());
|
||||
|
@ -288,13 +288,13 @@ fn amount_checked_div_by_weight_floor() {
|
|||
let weight = Weight::from_kwu(1).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());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
|
||||
|
||||
let weight = Weight::from_wu(381);
|
||||
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());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
|
||||
|
||||
let fee_rate = Amount::ONE_SAT.div_by_weight_floor(Weight::ZERO);
|
||||
assert!(fee_rate.is_error());
|
||||
|
@ -304,7 +304,7 @@ fn amount_checked_div_by_weight_floor() {
|
|||
#[test]
|
||||
fn amount_checked_div_by_fee_rate() {
|
||||
let amount = sat(1000);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(2);
|
||||
|
||||
// Test floor division
|
||||
let weight = amount.div_by_fee_rate_floor(fee_rate).unwrap();
|
||||
|
@ -317,20 +317,20 @@ fn amount_checked_div_by_fee_rate() {
|
|||
|
||||
// Test truncation behavior
|
||||
let amount = sat(1000);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(3);
|
||||
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();
|
||||
let zero_fee_rate = FeeRate::from_sat_per_kwu(0);
|
||||
assert!(amount.div_by_fee_rate_floor(zero_fee_rate).is_error());
|
||||
assert!(amount.div_by_fee_rate_ceil(zero_fee_rate).is_error());
|
||||
|
||||
// Test with maximum amount
|
||||
let max_amount = Amount::MAX;
|
||||
let small_fee_rate = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
let small_fee_rate = FeeRate::from_sat_per_kwu(1);
|
||||
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));
|
||||
|
|
|
@ -439,7 +439,7 @@ impl Amount {
|
|||
/// let amount = Amount::from_sat(10)?;
|
||||
/// let weight = Weight::from_wu(300);
|
||||
/// let fee_rate = amount.div_by_weight_ceil(weight).expect("valid fee rate");
|
||||
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34).expect("valid fee rate"));
|
||||
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
|
||||
/// # Ok::<_, amount::OutOfRangeError>(())
|
||||
/// ```
|
||||
pub const fn div_by_weight_ceil(self, weight: Weight) -> NumOpResult<FeeRate> {
|
||||
|
|
|
@ -190,12 +190,12 @@ mod tests {
|
|||
#[test]
|
||||
fn fee_rate_div_by_weight() {
|
||||
let fee_rate = (Amount::from_sat_u32(329) / Weight::from_wu(381)).unwrap();
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863).unwrap());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fee_wu() {
|
||||
let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_vb(2);
|
||||
let weight = Weight::from_vb(3).unwrap();
|
||||
assert_eq!(fee_rate.to_fee(weight), Amount::from_sat_u32(6));
|
||||
}
|
||||
|
@ -204,19 +204,19 @@ mod tests {
|
|||
fn weight_mul() {
|
||||
let weight = Weight::from_vb(10).unwrap();
|
||||
let fee: Amount =
|
||||
FeeRate::from_sat_per_vb(10).unwrap().mul_by_weight(weight).expect("expected Amount");
|
||||
FeeRate::from_sat_per_vb(10).mul_by_weight(weight).expect("expected Amount");
|
||||
assert_eq!(Amount::from_sat_u32(100), fee);
|
||||
|
||||
let fee = FeeRate::from_sat_per_kwu(10).unwrap().mul_by_weight(Weight::MAX);
|
||||
let fee = FeeRate::from_sat_per_kwu(10).mul_by_weight(Weight::MAX);
|
||||
assert!(fee.is_error());
|
||||
|
||||
let weight = Weight::from_vb(3).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_vb(3);
|
||||
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_rate = FeeRate::from_sat_per_kwu(864);
|
||||
let fee = weight.mul_by_fee_rate(fee_rate).unwrap();
|
||||
// 381 * 0.864 yields 329.18.
|
||||
// The result is then rounded up to 330.
|
||||
|
@ -226,7 +226,7 @@ mod tests {
|
|||
#[test]
|
||||
#[allow(clippy::op_ref)]
|
||||
fn multiply() {
|
||||
let two = FeeRate::from_sat_per_vb(2).unwrap();
|
||||
let two = FeeRate::from_sat_per_vb(2);
|
||||
let three = Weight::from_vb(3).unwrap();
|
||||
let six = Amount::from_sat_u32(6);
|
||||
|
||||
|
@ -243,9 +243,9 @@ mod tests {
|
|||
fn amount_div_by_fee_rate() {
|
||||
// Test exact division
|
||||
let amount = Amount::from_sat_u32(1000);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
let weight = (amount / fee_rate).unwrap();
|
||||
assert_eq!(weight, Weight::from_wu(500_000));
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(2);
|
||||
let weight = amount / fee_rate;
|
||||
assert_eq!(weight.unwrap(), Weight::from_wu(500_000));
|
||||
|
||||
// Test reference division
|
||||
let weight_ref = (&amount / fee_rate).unwrap();
|
||||
|
@ -257,19 +257,19 @@ mod tests {
|
|||
|
||||
// Test truncation behavior
|
||||
let amount = Amount::from_sat_u32(1000);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let weight = (amount / fee_rate).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(3);
|
||||
let weight = amount / fee_rate;
|
||||
// 1000 * 1000 = 1,000,000 msats
|
||||
// 1,000,000 / 3 = 333,333.33... wu
|
||||
// Should truncate down to 333,333 wu
|
||||
assert_eq!(weight, Weight::from_wu(333_333));
|
||||
assert_eq!(weight.unwrap(), Weight::from_wu(333_333));
|
||||
|
||||
// Verify that ceiling division gives different result
|
||||
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();
|
||||
let zero_rate = FeeRate::from_sat_per_kwu(0);
|
||||
assert!(amount.div_by_fee_rate_floor(zero_rate).is_error());
|
||||
assert!(amount.div_by_fee_rate_ceil(zero_rate).is_error());
|
||||
}
|
||||
|
|
|
@ -50,19 +50,15 @@ impl FeeRate {
|
|||
/// The minimum fee rate required to broadcast a transaction.
|
||||
///
|
||||
/// The value matches the default Bitcoin Core policy at the time of library release.
|
||||
pub const BROADCAST_MIN: FeeRate = FeeRate::from_sat_per_vb_u32(1);
|
||||
pub const BROADCAST_MIN: FeeRate = FeeRate::from_sat_per_vb(1);
|
||||
|
||||
/// The fee rate used to compute dust amount.
|
||||
pub const DUST: FeeRate = FeeRate::from_sat_per_vb_u32(3);
|
||||
pub const DUST: FeeRate = FeeRate::from_sat_per_vb(3);
|
||||
|
||||
/// Constructs a new [`FeeRate`] from satoshis per 1000 weight units,
|
||||
/// returning `None` if overflow occurred.
|
||||
pub const fn from_sat_per_kwu(sat_kwu: u64) -> Option<Self> {
|
||||
// No `map()` in const context.
|
||||
match sat_kwu.checked_mul(4_000) {
|
||||
Some(fee_rate) => Some(FeeRate::from_sat_per_mvb(fee_rate)),
|
||||
None => None,
|
||||
}
|
||||
/// Constructs a new [`FeeRate`] from satoshis per 1000 weight units.
|
||||
pub const fn from_sat_per_kwu(sat_kwu: u32) -> Self {
|
||||
let fee_rate = (sat_kwu as u64) * 4_000; // No `Into` in const context.
|
||||
FeeRate::from_sat_per_mvb(fee_rate)
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from amount per 1000 weight units.
|
||||
|
@ -74,14 +70,10 @@ impl FeeRate {
|
|||
}
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from satoshis per virtual byte,
|
||||
/// returning `None` if overflow occurred.
|
||||
pub const fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
|
||||
// No `map()` in const context.
|
||||
match sat_vb.checked_mul(1_000_000) {
|
||||
Some(fee_rate) => Some(FeeRate::from_sat_per_mvb(fee_rate)),
|
||||
None => None,
|
||||
}
|
||||
/// Constructs a new [`FeeRate`] from satoshis per virtual byte.
|
||||
pub const fn from_sat_per_vb(sat_vb: u32) -> Self {
|
||||
let fee_rate = (sat_vb as u64) * 1_000_000; // No `Into` in const context.
|
||||
FeeRate::from_sat_per_mvb(fee_rate)
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from amount per virtual byte.
|
||||
|
@ -93,20 +85,10 @@ impl FeeRate {
|
|||
}
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from satoshis per virtual bytes.
|
||||
pub const fn from_sat_per_vb_u32(sat_vb: u32) -> Self {
|
||||
let sat_vb = sat_vb as u64; // No `Into` in const context.
|
||||
FeeRate::from_sat_per_mvb(sat_vb * 1_000_000)
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes),
|
||||
/// returning `None` if overflow occurred.
|
||||
pub const fn from_sat_per_kvb(sat_kvb: u64) -> Option<Self> {
|
||||
// No `map()` in const context.
|
||||
match sat_kvb.checked_mul(1_000) {
|
||||
Some(fee_rate) => Some(FeeRate::from_sat_per_mvb(fee_rate)),
|
||||
None => None,
|
||||
}
|
||||
/// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
|
||||
pub const fn from_sat_per_kvb(sat_kvb: u32) -> Self {
|
||||
let fee_rate = (sat_kvb as u64) * 1_000; // No `Into` in const context.
|
||||
FeeRate::from_sat_per_mvb(fee_rate)
|
||||
}
|
||||
|
||||
/// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
|
||||
|
@ -301,18 +283,18 @@ mod tests {
|
|||
#[test]
|
||||
#[allow(clippy::op_ref)]
|
||||
fn feerate_div_nonzero() {
|
||||
let rate = FeeRate::from_sat_per_kwu(200).unwrap();
|
||||
let rate = FeeRate::from_sat_per_kwu(200);
|
||||
let divisor = NonZeroU64::new(2).unwrap();
|
||||
assert_eq!(rate / divisor, FeeRate::from_sat_per_kwu(100).unwrap());
|
||||
assert_eq!(&rate / &divisor, FeeRate::from_sat_per_kwu(100).unwrap());
|
||||
assert_eq!(rate / divisor, FeeRate::from_sat_per_kwu(100));
|
||||
assert_eq!(&rate / &divisor, FeeRate::from_sat_per_kwu(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::op_ref)]
|
||||
fn addition() {
|
||||
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let one = FeeRate::from_sat_per_kwu(1);
|
||||
let two = FeeRate::from_sat_per_kwu(2);
|
||||
let three = FeeRate::from_sat_per_kwu(3);
|
||||
|
||||
assert!(one + two == three);
|
||||
assert!(&one + two == three);
|
||||
|
@ -323,9 +305,9 @@ mod tests {
|
|||
#[test]
|
||||
#[allow(clippy::op_ref)]
|
||||
fn subtract() {
|
||||
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let seven = FeeRate::from_sat_per_kwu(7).unwrap();
|
||||
let ten = FeeRate::from_sat_per_kwu(10).unwrap();
|
||||
let three = FeeRate::from_sat_per_kwu(3);
|
||||
let seven = FeeRate::from_sat_per_kwu(7);
|
||||
let ten = FeeRate::from_sat_per_kwu(10);
|
||||
|
||||
assert_eq!(ten - seven, three);
|
||||
assert_eq!(&ten - seven, three);
|
||||
|
@ -335,44 +317,45 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn add_assign() {
|
||||
let mut f = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
f += FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3).unwrap());
|
||||
let mut f = FeeRate::from_sat_per_kwu(1);
|
||||
f += FeeRate::from_sat_per_kwu(2);
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3));
|
||||
|
||||
let mut f = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
f += &FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3).unwrap());
|
||||
let mut f = FeeRate::from_sat_per_kwu(1);
|
||||
f += &FeeRate::from_sat_per_kwu(2);
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_assign() {
|
||||
let mut f = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
f -= FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||
let mut f = FeeRate::from_sat_per_kwu(3);
|
||||
f -= FeeRate::from_sat_per_kwu(2);
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1));
|
||||
|
||||
let mut f = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
f -= &FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||
let mut f = FeeRate::from_sat_per_kwu(3);
|
||||
f -= &FeeRate::from_sat_per_kwu(2);
|
||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_add() {
|
||||
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let one = FeeRate::from_sat_per_kwu(1);
|
||||
let two = FeeRate::from_sat_per_kwu(2);
|
||||
let three = FeeRate::from_sat_per_kwu(3);
|
||||
|
||||
assert_eq!(one.checked_add(two).unwrap(), three);
|
||||
|
||||
assert!(FeeRate::from_sat_per_kvb(u64::MAX).is_none()); // sanity check.
|
||||
// Sanity check - no overflow adding one to per kvb max.
|
||||
let _ = FeeRate::from_sat_per_kvb(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() {
|
||||
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||
let one = FeeRate::from_sat_per_kwu(1);
|
||||
let two = FeeRate::from_sat_per_kwu(2);
|
||||
let three = FeeRate::from_sat_per_kwu(3);
|
||||
assert_eq!(three.checked_sub(two).unwrap(), one);
|
||||
|
||||
let fee_rate = FeeRate::ZERO.checked_sub(one);
|
||||
|
@ -390,35 +373,25 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn fee_rate_from_sat_per_vb() {
|
||||
let fee_rate = FeeRate::from_sat_per_vb(10).expect("expected feerate in sat/kwu");
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500).unwrap());
|
||||
let fee_rate = FeeRate::from_sat_per_vb(10);
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fee_rate_from_sat_per_kvb() {
|
||||
let fee_rate = FeeRate::from_sat_per_kvb(11).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kvb(11);
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fee_rate_from_sat_per_vb_overflow() {
|
||||
let fee_rate = FeeRate::from_sat_per_vb(u64::MAX);
|
||||
assert!(fee_rate.is_none());
|
||||
fn from_sat_per_vb() {
|
||||
let fee_rate = FeeRate::from_sat_per_vb(10);
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sat_per_vb_u32() {
|
||||
let fee_rate = FeeRate::from_sat_per_vb_u32(10);
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn from_sat_per_vb_u32_cannot_panic() { FeeRate::from_sat_per_vb_u32(u32::MAX); }
|
||||
|
||||
#[test]
|
||||
fn raw_feerate() {
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(749).unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(749);
|
||||
assert_eq!(fee_rate.to_sat_per_kwu_floor(), 749);
|
||||
assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
|
||||
assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
|
||||
|
@ -427,24 +400,22 @@ mod tests {
|
|||
#[test]
|
||||
fn checked_mul() {
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10)
|
||||
.unwrap()
|
||||
.checked_mul(10)
|
||||
.expect("expected feerate in sat/kwu");
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100).unwrap());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100));
|
||||
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap().checked_mul(u64::MAX);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10).checked_mul(u64::MAX);
|
||||
assert!(fee_rate.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div() {
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10)
|
||||
.unwrap()
|
||||
.checked_div(10)
|
||||
.expect("expected feerate in sat/kwu");
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
|
||||
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap().checked_div(0);
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10).checked_div(0);
|
||||
assert!(fee_rate.is_none());
|
||||
}
|
||||
|
||||
|
|
|
@ -33,15 +33,19 @@ pub mod as_sat_per_kwu_floor {
|
|||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::FeeRate;
|
||||
use crate::{Amount, FeeRate};
|
||||
|
||||
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
|
||||
u64::serialize(&f.to_sat_per_kwu_floor(), s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
|
||||
FeeRate::from_sat_per_kwu(u64::deserialize(d)?)
|
||||
.ok_or_else(|| serde::de::Error::custom("overflowed sats/kwu"))
|
||||
let sat = u64::deserialize(d)?;
|
||||
FeeRate::from_per_kwu(
|
||||
Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
|
||||
)
|
||||
.into_result()
|
||||
.map_err(|_| serde::de::Error::custom("fee rate too big for sats/kwu"))
|
||||
}
|
||||
|
||||
pub mod opt {
|
||||
|
@ -100,8 +104,7 @@ pub mod as_sat_per_vb_floor {
|
|||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::fee_rate::serde::OverflowError;
|
||||
use crate::fee_rate::FeeRate;
|
||||
use crate::{Amount, FeeRate};
|
||||
|
||||
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
|
||||
u64::serialize(&f.to_sat_per_vb_floor(), s)
|
||||
|
@ -109,9 +112,12 @@ pub mod as_sat_per_vb_floor {
|
|||
|
||||
// Errors on overflow.
|
||||
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
|
||||
FeeRate::from_sat_per_vb(u64::deserialize(d)?)
|
||||
.ok_or(OverflowError)
|
||||
.map_err(serde::de::Error::custom)
|
||||
let sat = u64::deserialize(d)?;
|
||||
FeeRate::from_per_vb(
|
||||
Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
|
||||
)
|
||||
.into_result()
|
||||
.map_err(|_| serde::de::Error::custom("fee rate too big for sats/vb"))
|
||||
}
|
||||
|
||||
pub mod opt {
|
||||
|
@ -171,8 +177,7 @@ pub mod as_sat_per_vb_ceil {
|
|||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::fee_rate::serde::OverflowError;
|
||||
use crate::fee_rate::FeeRate;
|
||||
use crate::{Amount, FeeRate};
|
||||
|
||||
pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
|
||||
u64::serialize(&f.to_sat_per_vb_ceil(), s)
|
||||
|
@ -180,9 +185,12 @@ pub mod as_sat_per_vb_ceil {
|
|||
|
||||
// Errors on overflow.
|
||||
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
|
||||
FeeRate::from_sat_per_vb(u64::deserialize(d)?)
|
||||
.ok_or(OverflowError)
|
||||
.map_err(serde::de::Error::custom)
|
||||
let sat = u64::deserialize(d)?;
|
||||
FeeRate::from_per_vb(
|
||||
Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
|
||||
)
|
||||
.into_result()
|
||||
.map_err(|_| serde::de::Error::custom("fee rate too big for sats/vb"))
|
||||
}
|
||||
|
||||
pub mod opt {
|
||||
|
|
|
@ -58,7 +58,7 @@ use crate::{Amount, FeeRate, SignedAmount, Weight};
|
|||
/// let a = Amount::from_sat(123).expect("valid amount");
|
||||
/// let b = Amount::from_sat(467).expect("valid amount");
|
||||
/// // Fee rate for transaction.
|
||||
/// let fee_rate = FeeRate::from_sat_per_vb(1).unwrap();
|
||||
/// let fee_rate = FeeRate::from_sat_per_vb(1);
|
||||
///
|
||||
/// // Somewhat contrived example to show addition operator chained with division.
|
||||
/// let max_fee = a + b;
|
||||
|
|
Loading…
Reference in New Issue