diff --git a/client/src/client.rs b/client/src/client.rs index c5ed266e..c1491495 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -269,6 +269,19 @@ pub trait RpcApi: Sized { self.call("addmultisigaddress", handle_defaults(&mut args, &[into_json("")?, null()])).await } + async fn bump_fee( + &self, + txid: &bitcoin::Txid, + options: Option<&json::BumpFeeOptions>, + ) -> Result { + let opts = match options { + Some(options) => Some(options.to_serializable(self.version().await?)), + None => None, + }; + let mut args = [into_json(txid)?, opt_into_json(opts)?]; + self.call("bumpfee", handle_defaults(&mut args, &[null()])).await + } + async fn load_wallet(&self, wallet: &str) -> Result { self.call("loadwallet", &[wallet.into()]).await } diff --git a/json/src/lib.rs b/json/src/lib.rs index 7733e3a6..6b69eb42 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -36,9 +36,38 @@ use bitcoin::{ use serde::de::Error as SerdeError; use serde::{Deserialize, Serialize}; 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 to_sat_per_vbyte(&self) -> f64 { + // multiply by the number of decimals to get sat + self.0.to_sat() as f64 + } + + pub fn to_btc_per_kvbyte(&self) -> f64 { + // divide by 10^8 to get btc/vbyte, then multiply by 10^3 to get btc/kbyte + self.0.to_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. @@ -1985,6 +2014,69 @@ pub struct FundRawTransactionResult { pub change_position: i32, } +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct BumpFeeOptions { + /// Confirmation target in blocks. + pub conf_target: Option, + /// Specify a fee rate instead of relying on the built-in fee estimator. + pub fee_rate: Option, + /// Whether this transaction could be replaced due to BIP125 (replace-by-fee) + pub replaceable: Option, + /// The fee estimate mode + pub estimate_mode: Option, +} + +impl BumpFeeOptions { + pub fn to_serializable(&self, version: usize) -> SerializableBumpFeeOptions { + let fee_rate = self.fee_rate.map(|x| { + if version < 210000 { + x.to_btc_per_kvbyte() + } else { + x.to_sat_per_vbyte() + } + }); + + SerializableBumpFeeOptions { + fee_rate, + conf_target: self.conf_target, + replaceable: self.replaceable, + estimate_mode: self.estimate_mode, + } + } +} + +#[derive(Serialize, Clone, PartialEq, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct SerializableBumpFeeOptions { + #[serde(rename = "conf_target", skip_serializing_if = "Option::is_none")] + /// Confirmation target in blocks. + pub conf_target: Option, + /// Specify a fee rate instead of relying on the built-in fee estimator. + #[serde(rename = "fee_rate")] + pub fee_rate: Option, + /// Whether this transaction could be replaced due to BIP125 (replace-by-fee) + #[serde(skip_serializing_if = "Option::is_none")] + pub replaceable: Option, + /// The fee estimate mode + #[serde(rename = "estimate_mode", skip_serializing_if = "Option::is_none")] + pub estimate_mode: Option, +} + +#[derive(Deserialize, Clone, PartialEq, Eq, Debug)] +#[serde(rename_all = "camelCase")] +pub struct BumpFeeResult { + /// The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled. + pub psbt: Option, + /// The id of the new transaction. Only returned when wallet private keys are enabled. + pub txid: Option, + #[serde(with = "bitcoin::amount::serde::as_btc")] + pub origfee: Amount, + #[serde(with = "bitcoin::amount::serde::as_btc")] + pub fee: Amount, + /// Errors encountered during processing. + pub errors: Vec, +} + #[derive(Deserialize, Clone, PartialEq, Eq, Debug)] pub struct GetBalancesResultEntry { #[serde(with = "bitcoin::amount::serde::as_btc")]