Implement create_psbt for Wallet#297
Implement create_psbt for Wallet#297ValuedMammal wants to merge 12 commits intobitcoindevkit:masterfrom
create_psbt for Wallet#297Conversation
673d602 to
77e1d20
Compare
Pull Request Test Coverage Report for Build 19016316365Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
|
I like the What do you envision the API for replace-by-fee transactions look like in this new bdk-tx world? I'm picturing something like |
2c54be1 to
6f525d1
Compare
6f525d1 to
4493cbd
Compare
create_psbt for Walletcreate_psbt for Wallet
thunderbiscuit
left a comment
There was a problem hiding this comment.
So this is more of a conceptual review/question set, as I'm getting acquainted with the PR, and it also requires knowledge and understanding of the bdk-tx crate/workflow.
One general question I have is do you think is missing in functionality between this and the current TxBuilder? Can we make a todo list that compares functionality with the current TxBuilder to better visualize how close of a replacement this is, or if it only provides part of the functionality for now (and if so which parts)?
I haven't had time to look/test the examples and my day is over, but I'll come back to this on Monday.
src/psbt/params.rs
Outdated
| } | ||
|
|
||
| /// Set the definite descriptor used for generating the change output. | ||
| pub fn change_descriptor(&mut self, desc: DefiniteDescriptor) -> &mut Self { |
There was a problem hiding this comment.
Is it correct to think that if not defined here, the change just goes to:
- The next index on Keychain::Internal if available
- If no internal keychain is available, the next index on the KeychainKind::External
There was a problem hiding this comment.
Ok awesome thanks for confirming. In this case, I'd just mention this in the docs (that this method is basically "if you want to send change elsewhere than your default change location").
src/psbt/params.rs
Outdated
| /// `ReplaceParams` provides a thin wrapper around [`PsbtParams`] and is intended for | ||
| /// crafting Replace-By-Fee transactions (RBF). | ||
| #[derive(Debug, Default)] | ||
| pub struct ReplaceParams { |
There was a problem hiding this comment.
Can you tell me more about why we want this new ReplaceParams type? Naively I would have thought you'd just call PsbtParams::replace and you'd get a sort of pre-populated PsbtParams ready for replacing your tx, but I assume this doesn't quite work?
There was a problem hiding this comment.
Naively I would have thought you'd just call
PsbtParams::replaceand you'd get a sort of pre-populated PsbtParams ready for replacing your tx
That works too.
There was a problem hiding this comment.
If that works that's my preferred approach, unless there is something I'm not seeing.
There was a problem hiding this comment.
Sorry I didn't elaborate before. We want to provide some separation to make it difficult to misuse the API. Once you've committed to replacing a tx, you can't go back and fiddle with the params, instead you're limited to doing only what is permitted by the ReplaceParams, at least that's the general idea. This prevents a situation where some params override others and the implementation becomes unwieldy.
Right now the key difference is that you're not allowed to add more utxos (outpoints) in addition the ones being replaced, because it may lead to creating an invalid tx. Still open to suggestions for improvement.
There was a problem hiding this comment.
Removed ReplaceParams. Now calling PsbtParams::replace_txs will return a new PsbtParams that is marked by the "RBF" context. I think it's a cleaner approach overall without compromising type safety.
src/wallet/mod.rs
Outdated
| /// # Ok::<_, anyhow::Error>(()) | ||
| /// ``` | ||
| #[cfg(feature = "std")] | ||
| pub fn create_psbt(&self, params: PsbtParams) -> Result<(Psbt, Finalizer), CreatePsbtError> { |
There was a problem hiding this comment.
Why do we return this Finalizer? I'd like to see that expanded upon in the docs. When looking into it I see that the Finalizer is
pub struct Finalizer {
pub(crate) plans: HashMap<OutPoint, Plan>,
}so probably something that helps the signers figure out what to sign and how to sign once you give them a psbt, but I'm not sure. The docs on Finalizer are also a bit... brief 😂😂
There was a problem hiding this comment.
I still need to take a deeper look into bdk-tx, but I think the name could be misleading with Input Finalizer from the PSBT BIP-174 (if it's not following the same proposed idea).
There was a problem hiding this comment.
It is meant to handle the role of a PSBT input finalizer as described in BIP174.
oleonardolima
left a comment
There was a problem hiding this comment.
I did an initial review, with simple comments/questions. Still need to take a deeper look into bdk-tx and do a more thorough review here.
| /// No Bnb solution. | ||
| Bnb(bdk_coin_select::NoBnbSolution), | ||
| /// Non-sufficient funds | ||
| InsufficientFunds(bdk_coin_select::InsufficientFunds), |
There was a problem hiding this comment.
Can't we have a generic CS error instead ?
There was a problem hiding this comment.
Can you elaborate on what you mean by generic CS error?
src/wallet/mod.rs
Outdated
| /// # Ok::<_, anyhow::Error>(()) | ||
| /// ``` | ||
| #[cfg(feature = "std")] | ||
| pub fn create_psbt(&self, params: PsbtParams) -> Result<(Psbt, Finalizer), CreatePsbtError> { |
There was a problem hiding this comment.
I still need to take a deeper look into bdk-tx, but I think the name could be misleading with Input Finalizer from the PSBT BIP-174 (if it's not following the same proposed idea).
|
Thank you for the quick review @thunderbiscuit @oleonardolima. |
TxBuilder vs PsbtParams feature comparisonsubject to change
N/A: |
4493cbd to
a7afecb
Compare
|
3d7bd2e to
5285937
Compare
I think a straightforward approach is to set a target feerate of I can see it being relevant to CPFP, since there we're less concerned about the feerate of an individual tx and more so with the fee needed to bump a package to a target feerate. |
f00d309 to
3988abb
Compare
d98d8f1 to
14c1d2c
Compare
cc0dcd8 to
73a7db9
Compare
|
The |
661cfc8 to
7c7e59e
Compare
| /// Optional assets for creating a spend plan. | ||
| pub(crate) assets: Option<Assets>, | ||
| /// Fee targeting strategy. | ||
| pub(crate) fee_strategy: FeeStrategy, |
There was a problem hiding this comment.
I think it's a better to include BOTH a min-feerate target and a min-absolute-fee target and not have the caller choose either-or.
Payjoin (BIP 78): The payjoin proposal MUST NOT decrease the absolute fee of the original transaction.
However, the receiver might also want a feerate target that ensures the payment gets received in the next block.
c.c. @DanGould
This feature should be upstreamed to bdk_coin_select and bdk_tx (I'm currently working on it!).
There was a problem hiding this comment.
This is what our API looks like to solve our fee problems for receivers: https://docs.rs/payjoin/1.0.0-rc.1/payjoin/receive/v2/struct.Receiver.html#impl-Receiver%3CWantsFeeRange%3E
Internally we enforce the minimum absolute fee and we enable a receiver to specify their minimum fee rate when the initialize the receiver (though I suppose in time it should really be a function rather than a value) as well as a maximum fee rate to prevent overspending by a counterparty who insists on a higher feerate.
very much a nit, but '"ensures" the [transaction] gets received in the next block' is necessarily interactive, and still only a best effort, since all we can do is estimate in this function afaiu.
`TxOrdering` is made generic by exposing the generic from `TxSort` function. The benefit is that we're no longer limited to ordering lists of only `TxIn` or `TxOut`. We use bitcoin `TxIn` and `TxOut` as the default type parameter to maintain backward compatibility. Add `sort_with_rng` for `TxOrdering<In, Out>` for sorting two generic mutable slices. This will be useful in a later commit by applying a `TxOrdering` to the Input/Output sets of a `Selection`.
We add the `psbt::params` module along with new types including `PsbtParams` and `SelectionStrategy`. `PsbtParams` is mostly inspired by `TxParams` from `tx_builder.rs`, except that we've removed support for `policy_path` in favor of `add_assets` API. Further enhancements include `.utxo_filter` and `.canonical_params` member fields. `PsbtParams<C>` contains a type parameter `C` indicating the context in which the parameters can be used. Methods related to PSBT creation exist within the `CreateTx` context, and methods related to replacements (RBF) exist within the `ReplaceTx` context. In `lib.rs` re-export everything under `psbt` module. - deps: Add `bdk_tx` 0.1.0 - deps: Add `bdk_coin_select` 0.4.1
We use the new `PsbtParams` to add methods on `Wallet` for creating PSBTs, including RBF transactions. `Wallet::create_psbt` and `Wallet::replace_by_fee` each have no-std counterparts that take an additional `impl RngCore` parameter. Also adds a high level convenience method `Wallet::replace_by_fee_and_recipients` that exposes the minimum information needed to create an RBF. This commit re-introduces the `Wallet::insert_tx` API for adding transaction data to the wallet that may or may not be canonical from the point of view of the TxGraph. Added `Wallet::transactions_with_params` that allows customizing the internal canonicalization logic. Added new errors to `wallet::errors` - `CreatePsbtError` - `ReplaceByFeeError`
Adds additional tests to exercise `PsbtParams` logic and PSBT creation. - test-utils: Add `insert_tx_anchor` test helper for adding a transaction to the wallet with associated anchor block.
- `examples/psbt.rs` - `examples/rbf.rs`
Made a number of API changes to `PsbtParams` - Add `PsbtParams::sighash_type` - psbt: Change `change_descriptor` to `change_script` - Add `PsbtParams::change_policy` - psbt: Change `feerate` to `fee` using `FeeStrategy` - Remove `longterm_feerate` TODO: - This should be updated to depend on `bdk_tx` version 0.2.0 which is still unreleased.
This enables the corresponding option in `bdk_tx::PsbtParams`. By default the feature is disabled, and allows opting in to LockTime- or Sequence-based (taproot only) anti-fee-sniping protection as specified in BIP326.
doc: Add special consideration regarding time-based absolute timelocks By default when no locktime is specified for the transaction, the wallet uses the height of the current chain tip as a fallback. However this will clash with a time-based absolute timelock of a planned input when deciding the locktime to put in the transaction. To clarify the behavior we update the docs to state that if spending coins locked to a certain timestamp, the `PsbtParams::locktime` must also be time-based. The highest available locktime is the one that is used.
7c7e59e to
4afed0e
Compare
| /// If adding a time-based absolute timelock here, you must also set a [`PsbtParams::locktime`] | ||
| /// which is time-based (i.e. UNIX timestamp) to use as a fallback, otherwise a | ||
| /// `LockTypeMismatch` error will occur. |
There was a problem hiding this comment.
I find this slightly annoying and there could be an alternative way to handle it. Rationale in 4afed0e.
e6579cb to
151a96e
Compare
Description
Use the new
bdk_txtransaction building library to create PSBTs in BDK Wallet. Primary benefits include the use ofbdk_coin_selectas well asminiscript::planmodule under the hood.fix #164
fix #204
Notes to the reviewers
It may help to look over the high level design and rationale which is available here https://hackmd.io/@bdk/r11JIeIjxl. See also a chart of how the features compare with
TxBuilder#297 (comment).Changelog notice
Changed
TxOrdering::Customis generic over the input and output types.Added
psbt::paramsmodulePsbtParamsstructCoinSelectionStrategyenumUtxoFilterstructAdded
pubmethods onWalletWallet::create_psbt{_with_rng}Wallet::replace_by_fee{_with_rng}Wallet::replace_by_fee_and_recipientsWallet::transactions_with_paramsWallet::insert_txexamples/psbt.rsexamples/rbf.rsChecklists
All Submissions:
New Features:
Bugfixes:
This pull request breaks the existing API