Change the internal representation of FeeRate
To get more precision use sats per million virtual bytes. To make review easier keep most calls in tests using `FeeRate::from_sats_per_kwu` and just unwrap. These can likely be cleaned up later on if we want to. For `serde` just change the module to `_floor` and leave it at that. The serde stuff likely needs re-visiting before release anyways.
This commit is contained in:
parent
2e0b88ba76
commit
b27d8e5819
|
@ -290,7 +290,7 @@ crate::internal_macros::define_extension_trait! {
|
||||||
///
|
///
|
||||||
/// [`minimal_non_dust`]: Script::minimal_non_dust
|
/// [`minimal_non_dust`]: Script::minimal_non_dust
|
||||||
fn minimal_non_dust_custom(&self, dust_relay: FeeRate) -> Option<Amount> {
|
fn minimal_non_dust_custom(&self, dust_relay: FeeRate) -> Option<Amount> {
|
||||||
self.minimal_non_dust_internal(dust_relay.to_sat_per_kvb())
|
self.minimal_non_dust_internal(dust_relay.to_sat_per_kvb_ceil())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counts the sigops for this Script using accurate counting.
|
/// Counts the sigops for this Script using accurate counting.
|
||||||
|
|
|
@ -1687,7 +1687,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn effective_value_happy_path() {
|
fn effective_value_happy_path() {
|
||||||
let value = "1 cBTC".parse::<Amount>().unwrap();
|
let value = "1 cBTC".parse::<Amount>().unwrap();
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(10);
|
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap();
|
||||||
let effective_value = effective_value(fee_rate, InputWeightPrediction::P2WPKH_MAX, value);
|
let effective_value = effective_value(fee_rate, InputWeightPrediction::P2WPKH_MAX, value);
|
||||||
|
|
||||||
// 10 sat/kwu * 272 wu = 4 sats (rounding up)
|
// 10 sat/kwu * 272 wu = 4 sats (rounding up)
|
||||||
|
|
|
@ -1356,9 +1356,15 @@ mod tests {
|
||||||
use crate::Sequence;
|
use crate::Sequence;
|
||||||
|
|
||||||
/// Fee rate in sat/kwu for a high-fee PSBT with an input=5_000_000_000_000, output=1000
|
/// Fee rate in sat/kwu for a high-fee PSBT with an input=5_000_000_000_000, output=1000
|
||||||
const ABSURD_FEE_RATE: FeeRate = FeeRate::from_sat_per_kwu(15_060_240_960_843);
|
const ABSURD_FEE_RATE: FeeRate = match FeeRate::from_sat_per_kwu(15_060_240_960_843) {
|
||||||
/// Fee rate which is just below absurd threshold (1 sat/kwu less)
|
Some(fee_rate) => fee_rate,
|
||||||
const JUST_BELOW_ABSURD_FEE_RATE: FeeRate = FeeRate::from_sat_per_kwu(15_060_240_960_842);
|
None => panic!("unreachable - no unwrap in Rust 1.63 in const"),
|
||||||
|
};
|
||||||
|
const JUST_BELOW_ABSURD_FEE_RATE: FeeRate = match FeeRate::from_sat_per_kwu(15_060_240_960_842)
|
||||||
|
{
|
||||||
|
Some(fee_rate) => fee_rate,
|
||||||
|
None => panic!("unreachable - no unwrap in Rust 1.63 in const"),
|
||||||
|
};
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn hex_psbt(s: &str) -> Result<Psbt, crate::psbt::error::Error> {
|
pub fn hex_psbt(s: &str) -> Result<Psbt, crate::psbt::error::Error> {
|
||||||
|
@ -1475,7 +1481,7 @@ mod tests {
|
||||||
ExtractTxError::AbsurdFeeRate { fee_rate, .. } => fee_rate,
|
ExtractTxError::AbsurdFeeRate { fee_rate, .. } => fee_rate,
|
||||||
_ => panic!(""),
|
_ => panic!(""),
|
||||||
}),
|
}),
|
||||||
Err(FeeRate::from_sat_per_kwu(6250003)) // 6250000 is 25k sat/vbyte
|
Err(FeeRate::from_sat_per_kwu(6250003).unwrap()) // 6250000 is 25k sat/vbyte
|
||||||
);
|
);
|
||||||
|
|
||||||
// Lowering the input satoshis by 1 lowers the sat/kwu by 3
|
// Lowering the input satoshis by 1 lowers the sat/kwu by 3
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn do_test(data: &[u8]) {
|
||||||
let _ = script.count_sigops_legacy();
|
let _ = script.count_sigops_legacy();
|
||||||
let _ = script.minimal_non_dust();
|
let _ = script.minimal_non_dust();
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(consume_u64(&mut new_data));
|
let fee_rate = FeeRate::from_sat_per_kwu(consume_u64(&mut new_data)).unwrap();
|
||||||
let _ = script.minimal_non_dust_custom(fee_rate);
|
let _ = script.minimal_non_dust_custom(fee_rate);
|
||||||
|
|
||||||
let mut b = script::Builder::new();
|
let mut b = script::Builder::new();
|
||||||
|
|
|
@ -270,13 +270,13 @@ fn amount_checked_div_by_weight_ceil() {
|
||||||
let weight = Weight::from_kwu(1).unwrap();
|
let weight = Weight::from_kwu(1).unwrap();
|
||||||
let fee_rate = sat(1).checked_div_by_weight_ceil(weight).unwrap();
|
let fee_rate = 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).unwrap());
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = sat(329).checked_div_by_weight_ceil(weight).unwrap();
|
let fee_rate = 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).unwrap());
|
||||||
|
|
||||||
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_ceil(Weight::ZERO);
|
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_ceil(Weight::ZERO);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
|
@ -288,13 +288,13 @@ fn amount_checked_div_by_weight_floor() {
|
||||||
let weight = Weight::from_kwu(1).unwrap();
|
let weight = Weight::from_kwu(1).unwrap();
|
||||||
let fee_rate = sat(1).checked_div_by_weight_floor(weight).unwrap();
|
let fee_rate = sat(1).checked_div_by_weight_floor(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).unwrap());
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = sat(329).checked_div_by_weight_floor(weight).unwrap();
|
let fee_rate = sat(329).checked_div_by_weight_floor(weight).unwrap();
|
||||||
// 329 sats / 381 wu = 863.5 sats/kwu
|
// 329 sats / 381 wu = 863.5 sats/kwu
|
||||||
// round down to 863
|
// round down to 863
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(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.checked_div_by_weight_floor(Weight::ZERO);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
|
@ -304,7 +304,7 @@ fn amount_checked_div_by_weight_floor() {
|
||||||
#[test]
|
#[test]
|
||||||
fn amount_checked_div_by_fee_rate() {
|
fn amount_checked_div_by_fee_rate() {
|
||||||
let amount = sat(1000);
|
let amount = sat(1000);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(2);
|
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
|
|
||||||
// Test floor division
|
// Test floor division
|
||||||
let weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
let weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
||||||
|
@ -317,20 +317,20 @@ fn amount_checked_div_by_fee_rate() {
|
||||||
|
|
||||||
// Test truncation behavior
|
// Test truncation behavior
|
||||||
let amount = sat(1000);
|
let amount = sat(1000);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(3);
|
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
let floor_weight = amount.checked_div_by_fee_rate_floor(fee_rate).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 ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||||
assert_eq!(floor_weight, Weight::from_wu(333_333));
|
assert_eq!(floor_weight, Weight::from_wu(333_333));
|
||||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||||
|
|
||||||
// Test division by zero
|
// Test division by zero
|
||||||
let zero_fee_rate = FeeRate::from_sat_per_kwu(0);
|
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_floor(zero_fee_rate).is_none());
|
||||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
assert!(amount.checked_div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
||||||
|
|
||||||
// Test with maximum amount
|
// Test with maximum amount
|
||||||
let max_amount = Amount::MAX;
|
let max_amount = Amount::MAX;
|
||||||
let small_fee_rate = FeeRate::from_sat_per_kwu(1);
|
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.checked_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
|
// 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));
|
assert_eq!(weight, Weight::from_wu(2_100_000_000_000_000_000));
|
||||||
|
|
|
@ -26,14 +26,15 @@ impl Amount {
|
||||||
/// Returns [`None`] if overflow occurred.
|
/// Returns [`None`] if overflow occurred.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
pub const fn checked_div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
||||||
// No `?` operator in const context.
|
let wu = weight.to_wu();
|
||||||
match self.to_sat().checked_mul(1_000) {
|
if wu == 0 {
|
||||||
Some(res) => match res.checked_div(weight.to_wu()) {
|
return None;
|
||||||
Some(fee_rate) => Some(FeeRate::from_sat_per_kwu(fee_rate)),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sats = self.to_sat() * 1_000; // Because we use per/kwu.
|
||||||
|
let fee_rate = sats / wu;
|
||||||
|
|
||||||
|
FeeRate::from_sat_per_kwu(fee_rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checked weight ceiling division.
|
/// Checked weight ceiling division.
|
||||||
|
@ -51,23 +52,20 @@ impl Amount {
|
||||||
/// let amount = Amount::from_sat(10)?;
|
/// let amount = Amount::from_sat(10)?;
|
||||||
/// let weight = Weight::from_wu(300);
|
/// let weight = Weight::from_wu(300);
|
||||||
/// let fee_rate = amount.checked_div_by_weight_ceil(weight);
|
/// let fee_rate = amount.checked_div_by_weight_ceil(weight);
|
||||||
/// assert_eq!(fee_rate, Some(FeeRate::from_sat_per_kwu(34)));
|
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
|
||||||
/// # Ok::<_, amount::OutOfRangeError>(())
|
/// # Ok::<_, amount::OutOfRangeError>(())
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn checked_div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
pub fn checked_div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
||||||
let wu = weight.to_wu();
|
let wu = weight.to_wu();
|
||||||
// No `?` operator in const context.
|
if wu == 0 {
|
||||||
if let Some(sats) = self.to_sat().checked_mul(1_000) {
|
return None;
|
||||||
if let Some(wu_minus_one) = wu.checked_sub(1) {
|
|
||||||
if let Some(sats_plus_wu_minus_one) = sats.checked_add(wu_minus_one) {
|
|
||||||
if let Some(fee_rate) = sats_plus_wu_minus_one.checked_div(wu) {
|
|
||||||
return Some(FeeRate::from_sat_per_kwu(fee_rate));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
let sats = self.to_sat() * 1_000; // Because we use per/kwu.
|
||||||
}
|
let fee_rate = (sats + wu - 1) / wu;
|
||||||
None
|
|
||||||
|
FeeRate::from_sat_per_kwu(fee_rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checked fee rate floor division.
|
/// Checked fee rate floor division.
|
||||||
|
@ -346,7 +344,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fee_rate_div_by_weight() {
|
fn fee_rate_div_by_weight() {
|
||||||
let fee_rate = (Amount::from_sat_u32(329) / Weight::from_wu(381)).unwrap();
|
let fee_rate = (Amount::from_sat_u32(329) / Weight::from_wu(381)).unwrap();
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -365,7 +363,7 @@ mod tests {
|
||||||
.expect("expected Amount");
|
.expect("expected Amount");
|
||||||
assert_eq!(Amount::from_sat_u32(100), fee);
|
assert_eq!(Amount::from_sat_u32(100), fee);
|
||||||
|
|
||||||
let fee = FeeRate::from_sat_per_kwu(10).checked_mul_by_weight(Weight::MAX);
|
let fee = FeeRate::from_sat_per_kwu(10).unwrap().checked_mul_by_weight(Weight::MAX);
|
||||||
assert!(fee.is_none());
|
assert!(fee.is_none());
|
||||||
|
|
||||||
let weight = Weight::from_vb(3).unwrap();
|
let weight = Weight::from_vb(3).unwrap();
|
||||||
|
@ -374,7 +372,7 @@ mod tests {
|
||||||
assert_eq!(Amount::from_sat_u32(9), fee);
|
assert_eq!(Amount::from_sat_u32(9), fee);
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(864);
|
let fee_rate = FeeRate::from_sat_per_kwu(864).unwrap();
|
||||||
let fee = weight.checked_mul_by_fee_rate(fee_rate).unwrap();
|
let fee = weight.checked_mul_by_fee_rate(fee_rate).unwrap();
|
||||||
// 381 * 0.864 yields 329.18.
|
// 381 * 0.864 yields 329.18.
|
||||||
// The result is then rounded up to 330.
|
// The result is then rounded up to 330.
|
||||||
|
@ -401,7 +399,7 @@ mod tests {
|
||||||
fn amount_div_by_fee_rate() {
|
fn amount_div_by_fee_rate() {
|
||||||
// Test exact division
|
// Test exact division
|
||||||
let amount = Amount::from_sat_u32(1000);
|
let amount = Amount::from_sat_u32(1000);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(2);
|
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
let weight = (amount / fee_rate).unwrap();
|
let weight = (amount / fee_rate).unwrap();
|
||||||
assert_eq!(weight, Weight::from_wu(500_000));
|
assert_eq!(weight, Weight::from_wu(500_000));
|
||||||
|
|
||||||
|
@ -415,7 +413,7 @@ mod tests {
|
||||||
|
|
||||||
// Test truncation behavior
|
// Test truncation behavior
|
||||||
let amount = Amount::from_sat_u32(1000);
|
let amount = Amount::from_sat_u32(1000);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(3);
|
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
let weight = (amount / fee_rate).unwrap();
|
let weight = (amount / fee_rate).unwrap();
|
||||||
// 1000 * 1000 = 1,000,000 msats
|
// 1000 * 1000 = 1,000,000 msats
|
||||||
// 1,000,000 / 3 = 333,333.33... wu
|
// 1,000,000 / 3 = 333,333.33... wu
|
||||||
|
@ -427,7 +425,7 @@ mod tests {
|
||||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||||
|
|
||||||
// Test that division by zero returns None
|
// Test that division by zero returns None
|
||||||
let zero_rate = FeeRate::from_sat_per_kwu(0);
|
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_floor(zero_rate).is_none());
|
||||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_rate).is_none());
|
assert!(amount.checked_div_by_fee_rate_ceil(zero_rate).is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ impl FeeRate {
|
||||||
/// 0 sat/kwu.
|
/// 0 sat/kwu.
|
||||||
///
|
///
|
||||||
/// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
|
/// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
|
||||||
pub const ZERO: FeeRate = FeeRate::from_sat_per_kwu(0);
|
pub const ZERO: FeeRate = FeeRate::from_sat_per_mvb(0);
|
||||||
|
|
||||||
/// Minimum possible value (0 sat/kwu).
|
/// Minimum possible value (0 sat/kwu).
|
||||||
///
|
///
|
||||||
|
@ -30,7 +30,7 @@ impl FeeRate {
|
||||||
pub const MIN: FeeRate = FeeRate::ZERO;
|
pub const MIN: FeeRate = FeeRate::ZERO;
|
||||||
|
|
||||||
/// Maximum possible value.
|
/// Maximum possible value.
|
||||||
pub const MAX: FeeRate = FeeRate::from_sat_per_kwu(u64::MAX / 4_000);
|
pub const MAX: FeeRate = FeeRate::from_sat_per_mvb(u64::MAX);
|
||||||
|
|
||||||
/// Minimum fee rate required to broadcast a transaction.
|
/// Minimum fee rate required to broadcast a transaction.
|
||||||
///
|
///
|
||||||
|
@ -40,46 +40,66 @@ impl FeeRate {
|
||||||
/// Fee rate used to compute dust amount.
|
/// 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_u32(3);
|
||||||
|
|
||||||
|
/// Constructs a new [`FeeRate`] from satoshis per 1,000,000 virtual bytes.
|
||||||
|
pub(crate) const fn from_sat_per_mvb(sat_mvb: u64) -> Self { Self(sat_mvb) }
|
||||||
|
|
||||||
/// Constructs a new [`FeeRate`] from satoshis per 1000 weight units.
|
/// Constructs a new [`FeeRate`] from satoshis per 1000 weight units.
|
||||||
pub const fn from_sat_per_kwu(sat_kwu: u64) -> Self { FeeRate(sat_kwu) }
|
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 virtual bytes.
|
/// Constructs a new [`FeeRate`] from satoshis per virtual bytes.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns [`None`] on arithmetic overflow.
|
/// Returns [`None`] on arithmetic overflow.
|
||||||
pub fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
|
pub const fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
|
||||||
// 1 vb == 4 wu
|
// No `map()` in const context.
|
||||||
// 1 sat/vb == 1/4 sat/wu
|
match sat_vb.checked_mul(1_000_000) {
|
||||||
// sat/vb * 1000 / 4 == sat/kwu
|
Some(fee_rate) => Some(FeeRate::from_sat_per_mvb(fee_rate)),
|
||||||
Some(FeeRate::from_sat_per_kwu(sat_vb.checked_mul(1000 / 4)?))
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new [`FeeRate`] from satoshis per virtual bytes.
|
/// Constructs a new [`FeeRate`] from satoshis per virtual bytes.
|
||||||
pub const fn from_sat_per_vb_u32(sat_vb: u32) -> Self {
|
pub const fn from_sat_per_vb_u32(sat_vb: u32) -> Self {
|
||||||
let sat_vb = sat_vb as u64; // No `Into` in const context.
|
let sat_vb = sat_vb as u64; // No `Into` in const context.
|
||||||
FeeRate::from_sat_per_kwu(sat_vb * (1000 / 4))
|
FeeRate::from_sat_per_mvb(sat_vb * 1_000_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
|
/// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
|
||||||
pub const fn from_sat_per_kvb(sat_kvb: u64) -> Self { FeeRate::from_sat_per_kwu(sat_kvb / 4) }
|
pub const fn from_sat_per_kvb(sat_kvb: u64) -> Option<Self> {
|
||||||
|
// No `map()` in const context.
|
||||||
/// Converts to sat/kwu rounding down.
|
match sat_kvb.checked_mul(1_000) {
|
||||||
pub const fn to_sat_per_kwu_floor(self) -> u64 { self.0 }
|
Some(fee_rate) => Some(FeeRate::from_sat_per_mvb(fee_rate)),
|
||||||
|
None => None,
|
||||||
/// Converts to sat/kwu rounding up.
|
}
|
||||||
pub const fn to_sat_per_kwu_ceil(self) -> u64 { self.0 }
|
|
||||||
|
|
||||||
/// Converts to sat/vB rounding down.
|
|
||||||
pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_kwu_floor() / (1000 / 4) }
|
|
||||||
|
|
||||||
/// Converts to sat/vB rounding up.
|
|
||||||
pub const fn to_sat_per_vb_ceil(self) -> u64 {
|
|
||||||
(self.to_sat_per_kwu_floor() + (1000 / 4 - 1)) / (1000 / 4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts to sat/kvb.
|
/// Converts to sat/MvB.
|
||||||
pub const fn to_sat_per_kvb(self) -> u64 { self.to_sat_per_kwu_floor() * 4 }
|
pub(crate) const fn to_sat_per_mvb(self) -> u64 { self.0 }
|
||||||
|
|
||||||
|
/// Converts to sat/kwu rounding down.
|
||||||
|
pub const fn to_sat_per_kwu_floor(self) -> u64 { self.to_sat_per_mvb() / 4_000 }
|
||||||
|
|
||||||
|
/// Converts to sat/kwu rounding up.
|
||||||
|
pub const fn to_sat_per_kwu_ceil(self) -> u64 { (self.to_sat_per_mvb() + 3_999) / 4_000 }
|
||||||
|
|
||||||
|
/// Converts to sat/vB rounding down.
|
||||||
|
pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000_000 }
|
||||||
|
|
||||||
|
/// Converts to sat/vB rounding up.
|
||||||
|
pub const fn to_sat_per_vb_ceil(self) -> u64 { (self.to_sat_per_mvb() + 999_999) / 1_000_000 }
|
||||||
|
|
||||||
|
/// Converts to sat/kvb rounding down.
|
||||||
|
pub const fn to_sat_per_kvb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000 }
|
||||||
|
|
||||||
|
/// Converts to sat/kvb rounding up.
|
||||||
|
pub const fn to_sat_per_kvb_ceil(self) -> u64 { (self.to_sat_per_mvb() + 999) / 1_000 }
|
||||||
|
|
||||||
/// Checked multiplication.
|
/// Checked multiplication.
|
||||||
///
|
///
|
||||||
|
@ -87,8 +107,8 @@ impl FeeRate {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
|
pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
|
||||||
// No `map()` in const context.
|
// No `map()` in const context.
|
||||||
match self.to_sat_per_kwu_floor().checked_mul(rhs) {
|
match self.to_sat_per_mvb().checked_mul(rhs) {
|
||||||
Some(res) => Some(Self::from_sat_per_kwu(res)),
|
Some(res) => Some(Self::from_sat_per_mvb(res)),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +119,8 @@ impl FeeRate {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div(self, rhs: u64) -> Option<Self> {
|
pub const fn checked_div(self, rhs: u64) -> Option<Self> {
|
||||||
// No `map()` in const context.
|
// No `map()` in const context.
|
||||||
match self.to_sat_per_kwu_floor().checked_div(rhs) {
|
match self.to_sat_per_mvb().checked_div(rhs) {
|
||||||
Some(res) => Some(Self::from_sat_per_kwu(res)),
|
Some(res) => Some(Self::from_sat_per_mvb(res)),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,8 +131,8 @@ impl FeeRate {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_add(self, rhs: FeeRate) -> Option<Self> {
|
pub const fn checked_add(self, rhs: FeeRate) -> Option<Self> {
|
||||||
// No `map()` in const context.
|
// No `map()` in const context.
|
||||||
match self.to_sat_per_kwu_floor().checked_add(rhs.to_sat_per_kwu_floor()) {
|
match self.to_sat_per_mvb().checked_add(rhs.to_sat_per_mvb()) {
|
||||||
Some(res) => Some(Self::from_sat_per_kwu(res)),
|
Some(res) => Some(Self::from_sat_per_mvb(res)),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,8 +143,8 @@ impl FeeRate {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_sub(self, rhs: FeeRate) -> Option<Self> {
|
pub const fn checked_sub(self, rhs: FeeRate) -> Option<Self> {
|
||||||
// No `map()` in const context.
|
// No `map()` in const context.
|
||||||
match self.to_sat_per_kwu_floor().checked_sub(rhs.to_sat_per_kwu_floor()) {
|
match self.to_sat_per_mvb().checked_sub(rhs.to_sat_per_mvb()) {
|
||||||
Some(res) => Some(Self::from_sat_per_kwu(res)),
|
Some(res) => Some(Self::from_sat_per_mvb(res)),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,19 +154,19 @@ crate::internal_macros::impl_op_for_references! {
|
||||||
impl ops::Add<FeeRate> for FeeRate {
|
impl ops::Add<FeeRate> for FeeRate {
|
||||||
type Output = FeeRate;
|
type Output = FeeRate;
|
||||||
|
|
||||||
fn add(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_kwu(self.to_sat_per_kwu_floor() + rhs.to_sat_per_kwu_floor()) }
|
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 {
|
impl ops::Sub<FeeRate> for FeeRate {
|
||||||
type Output = FeeRate;
|
type Output = FeeRate;
|
||||||
|
|
||||||
fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_kwu(self.to_sat_per_kwu_floor() - rhs.to_sat_per_kwu_floor()) }
|
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 {
|
impl ops::Div<NonZeroU64> for FeeRate {
|
||||||
type Output = FeeRate;
|
type Output = FeeRate;
|
||||||
|
|
||||||
fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_sat_per_kwu(self.to_sat_per_kwu_floor() / rhs.get()) }
|
fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_sat_per_mvb(self.to_sat_per_mvb() / rhs.get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::internal_macros::impl_add_assign!(FeeRate);
|
crate::internal_macros::impl_add_assign!(FeeRate);
|
||||||
|
@ -157,7 +177,7 @@ impl core::iter::Sum for FeeRate {
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Self>,
|
I: Iterator<Item = Self>,
|
||||||
{
|
{
|
||||||
FeeRate::from_sat_per_kwu(iter.map(FeeRate::to_sat_per_kwu_floor).sum())
|
FeeRate::from_sat_per_mvb(iter.map(FeeRate::to_sat_per_mvb).sum())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +186,7 @@ impl<'a> core::iter::Sum<&'a FeeRate> for FeeRate {
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a FeeRate>,
|
I: Iterator<Item = &'a FeeRate>,
|
||||||
{
|
{
|
||||||
FeeRate::from_sat_per_kwu(iter.map(|f| FeeRate::to_sat_per_kwu_floor(*f)).sum())
|
FeeRate::from_sat_per_mvb(iter.map(|f| FeeRate::to_sat_per_mvb(*f)).sum())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +199,7 @@ impl<'a> Arbitrary<'a> for FeeRate {
|
||||||
1 => Ok(FeeRate::BROADCAST_MIN),
|
1 => Ok(FeeRate::BROADCAST_MIN),
|
||||||
2 => Ok(FeeRate::DUST),
|
2 => Ok(FeeRate::DUST),
|
||||||
3 => Ok(FeeRate::MAX),
|
3 => Ok(FeeRate::MAX),
|
||||||
_ => Ok(FeeRate::from_sat_per_kwu(u64::arbitrary(u)?)),
|
_ => Ok(FeeRate::from_sat_per_mvb(u64::arbitrary(u)?)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,18 +213,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::op_ref)]
|
#[allow(clippy::op_ref)]
|
||||||
fn feerate_div_nonzero() {
|
fn feerate_div_nonzero() {
|
||||||
let rate = FeeRate::from_sat_per_kwu(200);
|
let rate = FeeRate::from_sat_per_kwu(200).unwrap();
|
||||||
let divisor = NonZeroU64::new(2).unwrap();
|
let divisor = NonZeroU64::new(2).unwrap();
|
||||||
assert_eq!(rate / divisor, FeeRate::from_sat_per_kwu(100));
|
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).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::op_ref)]
|
#[allow(clippy::op_ref)]
|
||||||
fn addition() {
|
fn addition() {
|
||||||
let one = FeeRate::from_sat_per_kwu(1);
|
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
let two = FeeRate::from_sat_per_kwu(2);
|
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
let three = FeeRate::from_sat_per_kwu(3);
|
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
|
|
||||||
assert!(one + two == three);
|
assert!(one + two == three);
|
||||||
assert!(&one + two == three);
|
assert!(&one + two == three);
|
||||||
|
@ -215,9 +235,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::op_ref)]
|
#[allow(clippy::op_ref)]
|
||||||
fn subtract() {
|
fn subtract() {
|
||||||
let three = FeeRate::from_sat_per_kwu(3);
|
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
let seven = FeeRate::from_sat_per_kwu(7);
|
let seven = FeeRate::from_sat_per_kwu(7).unwrap();
|
||||||
let ten = FeeRate::from_sat_per_kwu(10);
|
let ten = FeeRate::from_sat_per_kwu(10).unwrap();
|
||||||
|
|
||||||
assert_eq!(ten - seven, three);
|
assert_eq!(ten - seven, three);
|
||||||
assert_eq!(&ten - seven, three);
|
assert_eq!(&ten - seven, three);
|
||||||
|
@ -227,43 +247,44 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_assign() {
|
fn add_assign() {
|
||||||
let mut f = FeeRate::from_sat_per_kwu(1);
|
let mut f = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
f += FeeRate::from_sat_per_kwu(2);
|
f += FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3));
|
assert_eq!(f, FeeRate::from_sat_per_kwu(3).unwrap());
|
||||||
|
|
||||||
let mut f = FeeRate::from_sat_per_kwu(1);
|
let mut f = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
f += &FeeRate::from_sat_per_kwu(2);
|
f += &FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
assert_eq!(f, FeeRate::from_sat_per_kwu(3));
|
assert_eq!(f, FeeRate::from_sat_per_kwu(3).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sub_assign() {
|
fn sub_assign() {
|
||||||
let mut f = FeeRate::from_sat_per_kwu(3);
|
let mut f = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
f -= FeeRate::from_sat_per_kwu(2);
|
f -= FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1));
|
assert_eq!(f, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||||
|
|
||||||
let mut f = FeeRate::from_sat_per_kwu(3);
|
let mut f = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
f -= &FeeRate::from_sat_per_kwu(2);
|
f -= &FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
assert_eq!(f, FeeRate::from_sat_per_kwu(1));
|
assert_eq!(f, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_add() {
|
fn checked_add() {
|
||||||
let one = FeeRate::from_sat_per_kwu(1);
|
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
let two = FeeRate::from_sat_per_kwu(2);
|
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
let three = FeeRate::from_sat_per_kwu(3);
|
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
|
|
||||||
assert_eq!(one.checked_add(two).unwrap(), three);
|
assert_eq!(one.checked_add(two).unwrap(), three);
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(u64::MAX).checked_add(one);
|
assert!(FeeRate::from_sat_per_kvb(u64::MAX).is_none()); // sanity check.
|
||||||
|
let fee_rate = FeeRate::from_sat_per_mvb(u64::MAX).checked_add(one);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_sub() {
|
fn checked_sub() {
|
||||||
let one = FeeRate::from_sat_per_kwu(1);
|
let one = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
let two = FeeRate::from_sat_per_kwu(2);
|
let two = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
let three = FeeRate::from_sat_per_kwu(3);
|
let three = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
assert_eq!(three.checked_sub(two).unwrap(), one);
|
assert_eq!(three.checked_sub(two).unwrap(), one);
|
||||||
|
|
||||||
let fee_rate = FeeRate::ZERO.checked_sub(one);
|
let fee_rate = FeeRate::ZERO.checked_sub(one);
|
||||||
|
@ -282,13 +303,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fee_rate_from_sat_per_vb() {
|
fn fee_rate_from_sat_per_vb() {
|
||||||
let fee_rate = FeeRate::from_sat_per_vb(10).expect("expected feerate in sat/kwu");
|
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));
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fee_rate_from_sat_per_kvb() {
|
fn fee_rate_from_sat_per_kvb() {
|
||||||
let fee_rate = FeeRate::from_sat_per_kvb(11);
|
let fee_rate = FeeRate::from_sat_per_kvb(11).unwrap();
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2));
|
assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -300,7 +321,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn from_sat_per_vb_u32() {
|
fn from_sat_per_vb_u32() {
|
||||||
let fee_rate = FeeRate::from_sat_per_vb_u32(10);
|
let fee_rate = FeeRate::from_sat_per_vb_u32(10);
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500));
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -309,7 +330,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn raw_feerate() {
|
fn raw_feerate() {
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(749);
|
let fee_rate = FeeRate::from_sat_per_kwu(749).unwrap();
|
||||||
assert_eq!(fee_rate.to_sat_per_kwu_floor(), 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_floor(), 2);
|
||||||
assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
|
assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
|
||||||
|
@ -317,21 +338,32 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_mul() {
|
fn checked_mul() {
|
||||||
let fee_rate =
|
let fee_rate = FeeRate::from_sat_per_kwu(10)
|
||||||
FeeRate::from_sat_per_kwu(10).checked_mul(10).expect("expected feerate in sat/kwu");
|
.unwrap()
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100));
|
.checked_mul(10)
|
||||||
|
.expect("expected feerate in sat/kwu");
|
||||||
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100).unwrap());
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(10).checked_mul(u64::MAX);
|
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap().checked_mul(u64::MAX);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_div() {
|
fn checked_div() {
|
||||||
let fee_rate =
|
let fee_rate = FeeRate::from_sat_per_kwu(10)
|
||||||
FeeRate::from_sat_per_kwu(10).checked_div(10).expect("expected feerate in sat/kwu");
|
.unwrap()
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
|
.checked_div(10)
|
||||||
|
.expect("expected feerate in sat/kwu");
|
||||||
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(10).checked_div(0);
|
let fee_rate = FeeRate::from_sat_per_kwu(10).unwrap().checked_div(0);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
//!
|
//!
|
||||||
//! #[derive(Serialize, Deserialize)]
|
//! #[derive(Serialize, Deserialize)]
|
||||||
//! pub struct Foo {
|
//! pub struct Foo {
|
||||||
//! #[serde(with = "fee_rate::serde::as_sat_per_kwu")]
|
//! #[serde(with = "fee_rate::serde::as_sat_per_kwu_floor")]
|
||||||
//! pub fee_rate: FeeRate,
|
//! pub fee_rate: FeeRate,
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
pub mod as_sat_per_kwu {
|
pub mod as_sat_per_kwu_floor {
|
||||||
//! Serialize and deserialize [`FeeRate`] denominated in satoshis per 1000 weight units.
|
//! Serialize and deserialize [`FeeRate`] denominated in satoshis per 1000 weight units.
|
||||||
//!
|
//!
|
||||||
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu")]`.
|
//! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu")]`.
|
||||||
|
@ -40,7 +40,8 @@ pub mod as_sat_per_kwu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
|
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
|
||||||
Ok(FeeRate::from_sat_per_kwu(u64::deserialize(d)?))
|
FeeRate::from_sat_per_kwu(u64::deserialize(d)?)
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("overflowed sats/kwu"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod opt {
|
pub mod opt {
|
||||||
|
|
|
@ -37,13 +37,13 @@ struct Serde {
|
||||||
vb_floor: FeeRate,
|
vb_floor: FeeRate,
|
||||||
#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil")]
|
#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil")]
|
||||||
vb_ceil: FeeRate,
|
vb_ceil: FeeRate,
|
||||||
#[serde(with = "fee_rate::serde::as_sat_per_kwu")]
|
#[serde(with = "fee_rate::serde::as_sat_per_kwu_floor")]
|
||||||
kwu: FeeRate,
|
kwu: FeeRate,
|
||||||
#[serde(with = "fee_rate::serde::as_sat_per_vb_floor::opt")]
|
#[serde(with = "fee_rate::serde::as_sat_per_vb_floor::opt")]
|
||||||
opt_vb_floor: Option<FeeRate>,
|
opt_vb_floor: Option<FeeRate>,
|
||||||
#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil::opt")]
|
#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil::opt")]
|
||||||
opt_vb_ceil: Option<FeeRate>,
|
opt_vb_ceil: Option<FeeRate>,
|
||||||
#[serde(with = "fee_rate::serde::as_sat_per_kwu::opt")]
|
#[serde(with = "fee_rate::serde::as_sat_per_kwu_floor::opt")]
|
||||||
opt_kwu: Option<FeeRate>,
|
opt_kwu: Option<FeeRate>,
|
||||||
|
|
||||||
a: BlockHeight,
|
a: BlockHeight,
|
||||||
|
|
Loading…
Reference in New Issue