From dfbe771e6293f5ad95d282808d30556a82280b28 Mon Sep 17 00:00:00 2001 From: Sander Bosma Date: Fri, 19 Aug 2022 10:08:54 +0200 Subject: [PATCH] move, document and test the FeeRate struct --- integration_test/src/main.rs | 17 ++++----- json/src/lib.rs | 67 ++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/integration_test/src/main.rs b/integration_test/src/main.rs index d2e6cf9f..7039ed45 100644 --- a/integration_test/src/main.rs +++ b/integration_test/src/main.rs @@ -27,8 +27,8 @@ use bitcoin::hashes::hex::{FromHex, ToHex}; use bitcoin::hashes::Hash; use bitcoin::secp256k1; use bitcoin::{ - Address, Amount, Network, OutPoint, PrivateKey, Script, EcdsaSighashType, SignedAmount, Transaction, - TxIn, TxOut, Txid, Witness, + Address, Amount, EcdsaSighashType, Network, OutPoint, PrivateKey, Script, SignedAmount, + Transaction, TxIn, TxOut, Txid, Witness, }; use bitcoincore_rpc::bitcoincore_rpc_json::{ GetBlockTemplateModes, GetBlockTemplateRules, ScanTxOutRequest, @@ -597,8 +597,9 @@ fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { }], }; - let res = - cl.sign_raw_transaction_with_key(&tx, &[sk], None, Some(EcdsaSighashType::All.into())).unwrap(); + let res = cl + .sign_raw_transaction_with_key(&tx, &[sk], None, Some(EcdsaSighashType::All.into())) + .unwrap(); assert!(res.complete); let _ = cl.send_raw_transaction(&res.transaction().unwrap()).unwrap(); } @@ -687,7 +688,7 @@ fn test_bump_fee(cl: &Client) { // bump with explicit fee rate let amount_per_vbyte = Amount::from_sat(500); - let new_fee_rate = json::FeeRate::new(amount_per_vbyte); + let new_fee_rate = json::FeeRate::per_vbyte(amount_per_vbyte); let options = json::BumpFeeOptions { fee_rate: Some(new_fee_rate), replaceable: Some(true), @@ -1103,11 +1104,7 @@ fn test_add_ban(cl: &Client) { let res = cl.list_banned().unwrap(); assert_eq!(res.len(), 0); - assert_error_message!( - cl.add_ban("INVALID_STRING", 0, false), - -30, - "Error: Invalid IP/Subnet" - ); + assert_error_message!(cl.add_ban("INVALID_STRING", 0, false), -30, "Error: Invalid IP/Subnet"); } fn test_set_network_active(cl: &Client) { diff --git a/json/src/lib.rs b/json/src/lib.rs index fb7be174..530d3884 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -35,6 +35,37 @@ use std::fmt; //TODO(stevenroose) consider using a Time type +/// A representation of a fee rate. Bitcoin Core uses different units in different +/// versions. To avoid burdening the user with using the correct unit, this struct +/// provides an umambiguous way to represent the fee rate, and the lib will perform +/// the necessary conversions. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +pub struct FeeRate(Amount); + +impl FeeRate { + /// Construct FeeRate from the amount per vbyte + pub fn per_vbyte(amount_per_vbyte: Amount) -> Self { + // internal representation is amount per vbyte + Self(amount_per_vbyte) + } + + /// Construct FeeRate from the amount per kilo-vbyte + pub fn per_kvbyte(amount_per_kvbyte: Amount) -> Self { + // internal representation is amount per vbyte, so divide by 1000 + Self::per_vbyte(amount_per_kvbyte / 1000) + } + + pub fn as_sat_per_vbyte(&self) -> f64 { + // multiply by the number of decimals to get sat + self.0.as_sat() as f64 + } + + pub fn as_btc_per_kvbyte(&self) -> f64 { + // divide by 10^8 to get btc/vbyte, then multiply by 10^3 to get btc/kbyte + self.0.as_sat() as f64 / 100_000.0 + } +} + /// A module used for serde serialization of bytes in hexadecimal format. /// /// The module is compatible with the serde attribute. @@ -1827,9 +1858,9 @@ impl BumpFeeOptions { pub fn to_serializable(&self, version: usize) -> SerializableBumpFeeOptions { let fee_rate = self.fee_rate.map(|x| { if version < 210000 { - x.btc_per_kvbyte() + x.as_btc_per_kvbyte() } else { - x.sat_per_vbyte() + x.as_sat_per_vbyte() } }); @@ -1842,23 +1873,6 @@ impl BumpFeeOptions { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] -pub struct FeeRate(Amount); - -impl FeeRate { - pub fn new(amount_per_vbyte: Amount) -> Self { - Self(amount_per_vbyte) - } - pub fn sat_per_vbyte(&self) -> f64 { - // multiply by the number of decimals to get sat - self.0.as_sat() as f64 - } - pub fn btc_per_kvbyte(&self) -> f64 { - // divide by 10^8 to get btc/vbyte, then multiply by 10^3 to get btc/kbyte - self.0.as_sat() as f64 / 100_000.0 - } -} - #[derive(Serialize, Clone, PartialEq, Debug, Default)] #[serde(rename_all = "camelCase")] pub struct SerializableBumpFeeOptions { @@ -2072,3 +2086,18 @@ where } Ok(Some(res)) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fee_rate_conversion() { + let rate_1 = FeeRate::per_kvbyte(Amount::from_sat(10_000)); + let rate_2 = FeeRate::per_vbyte(Amount::from_sat(10)); + assert_eq!(rate_1, rate_2); + + assert_eq!(rate_1.as_sat_per_vbyte(), 10.0); + assert_eq!(rate_1.as_btc_per_kvbyte(), 10.0 * 1e3 / 1e8); + } +}