Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet): add TxBuilder::replace_tx #1799

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ValuedMammal
Copy link
Contributor

@ValuedMammal ValuedMammal commented Jan 13, 2025

This PR adds a new method TxBuilder::replace_tx that offers a simple way of creating replacements without relying on the mechanics of build_fee_bump. See #1374 for context and motivating discussion.

In summary, there are a few limitations with the current build_fee_bump:

  1. It doesn't prevent the wallet from accidentally selecting the outputs of the replaced tx as inputs to the replacement
  2. The UX is somewhat rigid because it assumes all of the same inputs and outputs should appear in the replacement. (however despite this second point we somehow neglect to reuse the original drain script?)

In contrast replace_tx is useful when we only need to create a conflict and flexible enough to further tweak the parameters as desired.

Notes to the reviewers

  • TxBuilder::add_utxos is modified to look for potentially spent outputs, provided that the builder is in a bump-fee context.

Changelog notice

  • Added method TxBuilder::replace_tx
  • Added method TxBuilder::previous_fee

Checklists

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

Comment on lines 389 to 394
// add previous fee
if let Ok(absolute) = self.wallet.calculate_fee(&tx) {
let rate = absolute / tx.weight();
let previous_fee = PreviousFee { absolute, rate };
self.params.bumping_fee = Some(previous_fee);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we still allow replacement if we can't determine the previous tx's fee/feerate?

Copy link
Member

@evanlinjin evanlinjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConceptACK, ApproachACK.

Just need to make sure we don't use descendant outputs from tx we are trying to replace.

It's out of the scope of this PR, the inability to set both feerate/fee amount constraints together really bothers me. We should probably switch to the bdk_coin_select crate soon.

crates/wallet/src/wallet/tx_builder.rs Outdated Show resolved Hide resolved
crates/wallet/tests/wallet.rs Outdated Show resolved Hide resolved
/// - If none of the inputs are owned by this wallet
///
/// [`finish`]: TxBuilder::finish
pub fn replace_tx(&mut self, txid: Txid) -> Result<&mut Self, ReplaceTxError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably warn that we don't help keep the original tx's recipient.

@notmandatory notmandatory added this to the 1.1.0 milestone Jan 14, 2025
///
/// # Note
///
/// Aside from reusing one of the inputs, the method makes no assumptions about the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be wary of leaving this caveat up to documentation. The way I see it, one could either declare the recipient when calling replace_tx as a parameter or a new variant on the error path could be added for NoRecipient such that set_recipient must be called before replace_tx.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean, but isn't this basically true of all of the builder methods - that we don't know whether a recipient has been added until the user calls finish?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Forgive my ignorance but what happens if the recipient list is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally it will be a NoRecipients error, unless you're doing a sweep using drain_wallet.

- Add method `TxBuilder::previous_fee` for getting the previous
fee / feerate of the replaced tx.
@ValuedMammal
Copy link
Contributor Author

I made a few small changes including a test for unspendable descendants. I'm still open to discussing the right UX for replace_tx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

4 participants