diff --git a/src/coin_selector.rs b/src/coin_selector.rs index 944332a..469ef56 100644 --- a/src/coin_selector.rs +++ b/src/coin_selector.rs @@ -247,7 +247,10 @@ impl<'a> CoinSelector<'a> { } fn implied_fee_from_feerate(&self, target: Target, drain_weights: DrainWeights) -> u64 { - (self.weight(target.outputs, drain_weights) as f32 * target.fee.rate.spwu()).ceil() as u64 + target + .fee + .rate + .implied_fee(self.weight(target.outputs, drain_weights)) } /// The actual fee the selection would pay if it was used in a transaction that had diff --git a/src/feerate.rs b/src/feerate.rs index e7570e5..9168c7f 100644 --- a/src/feerate.rs +++ b/src/feerate.rs @@ -78,6 +78,12 @@ impl FeeRate { pub fn spwu(&self) -> f32 { self.0 .0 } + + /// The fee that the transaction with weight `tx_weight` should pay in order to satisfy the fee rate given by `self`. + pub fn implied_fee(&self, tx_weight: u64) -> u64 { + // The fee rate is applied to the rounded-up vbytes. + ((tx_weight as f32 / 4.0).ceil() * self.as_sat_vb()).ceil() as u64 + } } impl Add for FeeRate { diff --git a/src/target.rs b/src/target.rs index 0dddf7b..6de3cb0 100644 --- a/src/target.rs +++ b/src/target.rs @@ -136,8 +136,9 @@ impl Replace { /// /// [RBF rule 4]: https://github.com/bitcoin/bitcoin/blob/master/doc/policy/mempool-replacements.md#current-replace-by-fee-policy pub fn min_fee_to_do_replacement(&self, replacing_tx_weight: u64) -> u64 { - let min_fee_increment = - (replacing_tx_weight as f32 * self.incremental_relay_feerate.spwu()).ceil() as u64; - self.fee + min_fee_increment + self.fee + + self + .incremental_relay_feerate + .implied_fee(replacing_tx_weight) } } diff --git a/tests/rbf.rs b/tests/rbf.rs index 64539f6..f89f1a0 100644 --- a/tests/rbf.rs +++ b/tests/rbf.rs @@ -5,65 +5,78 @@ fn run_bitcoin_core_rbf_tests() { // see rbf_tests.cpp // // https://github.com/bitcoin/bitcoin/blob/e69796c79c0aa202087a13ba62d9fbcc1c8754d4/src/test/rbf_tests.cpp#L151 - const CENT: u64 = 100_000; // no clue why this would be called CENT 😕 + const CENT: u64 = 1_000_000; let low_fee = CENT / 100; let _normal_fee = CENT / 10; let high_fee = CENT; let incremental_relay_feerate = FeeRate::DEFUALT_RBF_INCREMENTAL_RELAY; let higher_relay_feerate = FeeRate::from_sat_per_vb(2.0); + let very_high_relay_feerate = FeeRate::from_sat_per_vb(10.0); - assert!(pays_for_rbf(high_fee, high_fee, 1, FeeRate::ZERO)); - assert!(!pays_for_rbf(high_fee, high_fee - 1, 1, FeeRate::ZERO)); - assert!(!pays_for_rbf(high_fee + 1, high_fee, 1, FeeRate::ZERO)); + assert!(pays_for_rbf(high_fee, high_fee, 4, FeeRate::ZERO)); + assert!(!pays_for_rbf(high_fee, high_fee - 1, 4, FeeRate::ZERO)); + assert!(!pays_for_rbf(high_fee + 1, high_fee, 4, FeeRate::ZERO)); assert!(!pays_for_rbf( high_fee, high_fee + 1, - 2, + 8, incremental_relay_feerate )); assert!(pays_for_rbf( high_fee, high_fee + 2, - 2, + 8, incremental_relay_feerate )); assert!(!pays_for_rbf( high_fee, high_fee + 2, - 2, + 8, higher_relay_feerate )); assert!(pays_for_rbf( high_fee, high_fee + 4, - 2, + 8, higher_relay_feerate )); assert!(!pays_for_rbf( low_fee, high_fee, - 99999999, + 99999999 * 4, incremental_relay_feerate )); assert!(pays_for_rbf( low_fee, high_fee + 99999999, - 99999999, + 99999999 * 4, incremental_relay_feerate )); + assert!(!pays_for_rbf( + low_fee, + low_fee + 29, + 8 + 1, + very_high_relay_feerate + )); + assert!(pays_for_rbf( + low_fee, + low_fee + 30, // 30 = (10 * (9/4).ceil()) + 8 + 1, + very_high_relay_feerate + )); } fn pays_for_rbf( original_fees: u64, replacement_fees: u64, - replacement_vsize: u64, + replacement_weight: u64, relay_fee: FeeRate, ) -> bool { let min_fee = Replace { fee: original_fees, incremental_relay_feerate: relay_fee, } - .min_fee_to_do_replacement(replacement_vsize * 4); + .min_fee_to_do_replacement(replacement_weight); replacement_fees >= min_fee }