From: Alekos Filini Date: Thu, 13 Aug 2020 14:51:27 +0000 (+0200) Subject: Implement RBF and add a few tests X-Git-Tag: 0.1.0-beta.1~11 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/struct.SegwitCodeLengthError.html?a=commitdiff_plain;h=fb689ed31bbed9f831cc38c8b4896c7e3a204579;p=bdk-cli Implement RBF and add a few tests --- diff --git a/src/cli.rs b/src/cli.rs index 164ae4a..3da4d49 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,7 +9,7 @@ use log::{debug, error, info, trace, LevelFilter}; use bitcoin::consensus::encode::{deserialize, serialize, serialize_hex}; use bitcoin::hashes::hex::{FromHex, ToHex}; use bitcoin::util::psbt::PartiallySignedTransaction; -use bitcoin::{Address, OutPoint}; +use bitcoin::{Address, OutPoint, Txid}; use crate::error::Error; use crate::types::ScriptType; @@ -83,6 +83,12 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> { .long("send_all") .help("Sends all the funds (or all the selected utxos). Requires only one addressees of value 0"), ) + .arg( + Arg::with_name("enable_rbf") + .short("rbf") + .long("enable_rbf") + .help("Enables Replace-By-Fee (BIP125)"), + ) .arg( Arg::with_name("utxos") .long("utxos") @@ -120,6 +126,53 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> { .number_of_values(1), ), ) + .subcommand( + SubCommand::with_name("bump_fee") + .about("Bumps the fees of an RBF transaction") + .arg( + Arg::with_name("txid") + .required(true) + .takes_value(true) + .short("txid") + .long("txid") + .help("TXID of the transaction to update"), + ) + .arg( + Arg::with_name("send_all") + .short("all") + .long("send_all") + .help("Allows the wallet to reduce the amount of the only output in order to increase fees. This is generally the expected behavior for transactions originally created with `send_all`"), + ) + .arg( + Arg::with_name("utxos") + .long("utxos") + .value_name("TXID:VOUT") + .help("Selects which utxos *must* be added to the tx. Unconfirmed utxos cannot be used") + .takes_value(true) + .number_of_values(1) + .multiple(true) + .validator(outpoint_validator), + ) + .arg( + Arg::with_name("unspendable") + .long("unspendable") + .value_name("TXID:VOUT") + .help("Marks an utxo as unspendable, in case more inputs are needed to cover the extra fees") + .takes_value(true) + .number_of_values(1) + .multiple(true) + .validator(outpoint_validator), + ) + .arg( + Arg::with_name("fee_rate") + .required(true) + .short("fee") + .long("fee_rate") + .value_name("SATS_VBYTE") + .help("The new targeted fee rate in sat/vbyte") + .takes_value(true), + ), + ) .subcommand( SubCommand::with_name("policies") .about("Returns the available spending policies for the descriptor") @@ -331,6 +384,9 @@ where if sub_matches.is_present("send_all") { tx_builder = tx_builder.send_all(); } + if sub_matches.is_present("enable_rbf") { + tx_builder = tx_builder.enable_rbf(); + } if let Some(fee_rate) = sub_matches.value_of("fee_rate") { let fee_rate = f32::from_str(fee_rate).map_err(|s| Error::Generic(s.to_string()))?; @@ -363,6 +419,40 @@ where result.1, base64::encode(&serialize(&result.0)) ))) + } else if let Some(sub_matches) = matches.subcommand_matches("bump_fee") { + let txid = Txid::from_str(sub_matches.value_of("txid").unwrap()) + .map_err(|s| Error::Generic(s.to_string()))?; + + let fee_rate = f32::from_str(sub_matches.value_of("fee_rate").unwrap()) + .map_err(|s| Error::Generic(s.to_string()))?; + let mut tx_builder = TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(fee_rate)); + + if sub_matches.is_present("send_all") { + tx_builder = tx_builder.send_all(); + } + + if let Some(utxos) = sub_matches.values_of("utxos") { + let utxos = utxos + .map(|i| parse_outpoint(i)) + .collect::, _>>() + .map_err(|s| Error::Generic(s.to_string()))?; + tx_builder = tx_builder.utxos(utxos); + } + + if let Some(unspendable) = sub_matches.values_of("unspendable") { + let unspendable = unspendable + .map(|i| parse_outpoint(i)) + .collect::, _>>() + .map_err(|s| Error::Generic(s.to_string()))?; + tx_builder = tx_builder.unspendable(unspendable); + } + + let result = wallet.bump_fee(&txid, tx_builder)?; + Ok(Some(format!( + "{:#?}\nPSBT: {}", + result.1, + base64::encode(&serialize(&result.0)) + ))) } else if let Some(_sub_matches) = matches.subcommand_matches("policies") { Ok(Some(format!( "External: {}\nInternal:{}",