diff --git a/crates/wallet/README.md b/crates/wallet/README.md index 3b5422b63..937176e04 100644 --- a/crates/wallet/README.md +++ b/crates/wallet/README.md @@ -174,7 +174,6 @@ println!("Your new receive address is: {}", receive_address.address); - diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 6d687f825..fcca5cc11 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -1611,8 +1611,7 @@ impl Wallet { /// let mut psbt = { /// let mut builder = wallet.build_tx(); /// builder - /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)) - /// .enable_rbf(); + /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); /// builder.finish()? /// }; /// let _ = wallet.sign(&mut psbt, SignOptions::default())?; diff --git a/crates/wallet/src/wallet/tx_builder.rs b/crates/wallet/src/wallet/tx_builder.rs index 08b0f3249..60e4a2888 100644 --- a/crates/wallet/src/wallet/tx_builder.rs +++ b/crates/wallet/src/wallet/tx_builder.rs @@ -31,9 +31,7 @@ //! // With a custom fee rate of 5.0 satoshi/vbyte //! .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) //! // Only spend non-change outputs -//! .do_not_spend_change() -//! // Turn on RBF signaling -//! .enable_rbf(); +//! .do_not_spend_change(); //! let psbt = tx_builder.finish()?; //! # Ok::<(), anyhow::Error>(()) //! ``` @@ -120,7 +118,7 @@ pub struct TxBuilder<'a, Cs> { /// The parameters for transaction creation sans coin selection algorithm. //TODO: TxParams should eventually be exposed publicly. -#[derive(Default, Debug, Clone)] +#[derive(Debug, Clone)] pub(crate) struct TxParams { pub(crate) recipients: Vec<(ScriptBuf, Amount)>, pub(crate) drain_wallet: bool, @@ -145,6 +143,34 @@ pub(crate) struct TxParams { pub(crate) allow_dust: bool, } +impl Default for TxParams { + fn default() -> Self { + Self { + recipients: Vec::new(), + drain_wallet: false, + drain_to: None, + fee_policy: None, + internal_policy_path: None, + external_policy_path: None, + utxos: Vec::new(), + unspendable: HashSet::new(), + manually_selected_only: false, + sighash: None, + ordering: TxOrdering::default(), + locktime: None, + rbf: Some(RbfValue::Default), + version: None, + change_policy: ChangeSpendPolicy::default(), + only_witness_utxo: false, + add_global_xpubs: false, + include_output_redeem_witness_script: false, + bumping_fee: None, + current_height: None, + allow_dust: false, + } + } +} + #[derive(Clone, Copy, Debug)] pub(crate) struct PreviousFee { pub absolute: Amount, @@ -554,7 +580,7 @@ impl<'a, Cs> TxBuilder<'a, Cs> { } } - /// Enable signaling RBF + /// Enable RBF signaling /// /// This will use the default nSequence value of `0xFFFFFFFD`. pub fn enable_rbf(&mut self) -> &mut Self { @@ -562,7 +588,7 @@ impl<'a, Cs> TxBuilder<'a, Cs> { self } - /// Enable signaling RBF with a specific nSequence value + /// Enable RBF signaling with a specific nSequence value /// /// This can cause conflicts if the wallet's descriptors contain an "older" (OP_CSV) operator /// and the given `nsequence` is lower than the CSV value. @@ -574,6 +600,12 @@ impl<'a, Cs> TxBuilder<'a, Cs> { self } + /// Disable RBF signaling + pub fn disable_rbf(&mut self) -> &mut Self { + self.params.rbf = None; + self + } + /// Set the current blockchain height. /// /// This will be used to: @@ -654,8 +686,7 @@ impl<'a, Cs> TxBuilder<'a, Cs> { /// .drain_wallet() /// // Send the excess (which is all the coins minus the fee) to this address. /// .drain_to(to_address.script_pubkey()) - /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) - /// .enable_rbf(); + /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")); /// let psbt = tx_builder.finish()?; /// # Ok::<(), anyhow::Error>(()) /// ``` diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs index 637593780..11923ba19 100644 --- a/crates/wallet/tests/wallet.rs +++ b/crates/wallet/tests/wallet.rs @@ -654,9 +654,7 @@ fn test_create_tx_with_default_rbf_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); let addr = wallet.next_unused_address(KeychainKind::External); let mut builder = wallet.build_tx(); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); // When CSV is enabled it takes precedence over the rbf value (unless forced by the user). // It will be set to the OP_CSV value, in this case 6 @@ -682,6 +680,7 @@ fn test_create_tx_no_rbf_cltv() { let addr = wallet.next_unused_address(KeychainKind::External); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); + builder.disable_rbf(); let psbt = builder.finish().unwrap(); assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE)); @@ -743,7 +742,7 @@ fn test_create_tx_default_sequence() { builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); - assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE)); + assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD)); } macro_rules! check_fee { @@ -1328,7 +1327,7 @@ fn test_create_tx_policy_path_no_csv() { .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); - assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF)); + assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD)); } #[test] @@ -1370,7 +1369,7 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() { .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); - assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE)); + assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD)); } #[test] @@ -1797,6 +1796,7 @@ fn test_bump_fee_irreplaceable_tx() { let addr = wallet.next_unused_address(KeychainKind::External); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); + builder.disable_rbf(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1869,9 +1869,7 @@ fn test_bump_fee_low_abs() { let (mut wallet, _) = get_funded_wallet_wpkh(); let addr = wallet.next_unused_address(KeychainKind::External); let mut builder = wallet.build_tx(); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1891,9 +1889,7 @@ fn test_bump_fee_zero_abs() { let (mut wallet, _) = get_funded_wallet_wpkh(); let addr = wallet.next_unused_address(KeychainKind::External); let mut builder = wallet.build_tx(); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1913,9 +1909,7 @@ fn test_bump_fee_reduce_change() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let original_sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); @@ -1928,7 +1922,7 @@ fn test_bump_fee_reduce_change() { let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb let mut builder = wallet.build_fee_bump(txid).unwrap(); - builder.fee_rate(feerate).enable_rbf(); + builder.fee_rate(feerate); let psbt = builder.finish().unwrap(); let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); @@ -1964,7 +1958,6 @@ fn test_bump_fee_reduce_change() { let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_absolute(Amount::from_sat(200)); - builder.enable_rbf(); let psbt = builder.finish().unwrap(); let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); @@ -2011,10 +2004,7 @@ fn test_bump_fee_reduce_single_recipient() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder - .drain_to(addr.script_pubkey()) - .drain_wallet() - .enable_rbf(); + builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); let tx = psbt.clone().extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2058,10 +2048,7 @@ fn test_bump_fee_absolute_reduce_single_recipient() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder - .drain_to(addr.script_pubkey()) - .drain_wallet() - .enable_rbf(); + builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); let original_fee = check_fee!(wallet, psbt); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -2135,8 +2122,7 @@ fn test_bump_fee_drain_wallet() { vout: 0, }) .unwrap() - .manually_selected_only() - .enable_rbf(); + .manually_selected_only(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2201,8 +2187,7 @@ fn test_bump_fee_remove_output_manually_selected_only() { .drain_to(addr.script_pubkey()) .add_utxo(outpoint) .unwrap() - .manually_selected_only() - .enable_rbf(); + .manually_selected_only(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2247,9 +2232,7 @@ fn test_bump_fee_add_input() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_details = wallet.sent_and_received(&tx); @@ -2303,9 +2286,7 @@ fn test_bump_fee_absolute_add_input() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2366,8 +2347,7 @@ fn test_bump_fee_no_change_add_input_and_change() { .drain_to(addr.script_pubkey()) .add_utxo(op) .unwrap() - .manually_selected_only() - .enable_rbf(); + .manually_selected_only(); let psbt = builder.finish().unwrap(); let original_sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); @@ -2428,9 +2408,7 @@ fn test_bump_fee_add_input_change_dust() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); let psbt = builder.finish().unwrap(); let original_sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); @@ -2504,9 +2482,7 @@ fn test_bump_fee_force_add_input() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2569,9 +2545,7 @@ fn test_bump_fee_absolute_force_add_input() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); - builder - .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) - .enable_rbf(); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); @@ -2641,10 +2615,7 @@ fn test_bump_fee_unconfirmed_inputs_only() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder - .drain_wallet() - .drain_to(addr.script_pubkey()) - .enable_rbf(); + builder.drain_wallet().drain_to(addr.script_pubkey()); let psbt = builder.finish().unwrap(); // Now we receive one transaction with 0 confirmations. We won't be able to use that for // fee bumping, as it's still unconfirmed! @@ -2680,10 +2651,7 @@ fn test_bump_fee_unconfirmed_input() { // in the drain tx. receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0)); let mut builder = wallet.build_tx(); - builder - .drain_wallet() - .drain_to(addr.script_pubkey()) - .enable_rbf(); + builder.drain_wallet().drain_to(addr.script_pubkey()); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); @@ -2727,7 +2695,6 @@ fn test_fee_amount_negative_drain_val() { .add_recipient(send_to.script_pubkey(), Amount::from_sat(8630)) .add_utxo(incoming_op) .unwrap() - .enable_rbf() .fee_rate(fee_rate); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt);