[wallet] Overhaul TxBuilder internals and externals
Fixes #251 TxBuilders are now not created directly but are created through the wallet with `build_tx` and `build_fee_bump`. The advantages of this realised in this commit are: 1. Normal tx creation and fee bumping use the code internally. The only difference between normal tx and fee bump is how the builder is created. 2. The TxBuilder now has a refernce to the wallet and can therefore lookup things as methods are called on it. `add_utxo` now uses this to look up UTXO deta when it is called (rather than having to do it and possibly error later on). To support these changes `get_utxo` and `get_descriptor_for_keychain` public methods have been added to Wallet. I could have kept them pub(crate) but they seem like fine APIs to have publicly.
This commit is contained in:
parent
f74f17e227
commit
7553b905c4
@ -87,7 +87,7 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
### Create a transaction
|
### Create a transaction
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use bdk::{FeeRate, TxBuilder, Wallet};
|
use bdk::{FeeRate, Wallet};
|
||||||
use bdk::database::MemoryDatabase;
|
use bdk::database::MemoryDatabase;
|
||||||
use bdk::blockchain::{noop_progress, ElectrumBlockchain};
|
use bdk::blockchain::{noop_progress, ElectrumBlockchain};
|
||||||
|
|
||||||
@ -108,12 +108,12 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
wallet.sync(noop_progress(), None)?;
|
wallet.sync(noop_progress(), None)?;
|
||||||
|
|
||||||
let send_to = wallet.get_new_address()?;
|
let send_to = wallet.get_new_address()?;
|
||||||
let (psbt, details) = wallet.create_tx(
|
let (psbt, details) = wallet.build_tx()
|
||||||
TxBuilder::with_recipients(vec![(send_to.script_pubkey(), 50_000)])
|
.add_recipient(send_to.script_pubkey(), 50_000)
|
||||||
.enable_rbf()
|
.enable_rbf()
|
||||||
.do_not_spend_change()
|
.do_not_spend_change()
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(5.0))
|
.fee_rate(FeeRate::from_sat_per_vb(5.0))
|
||||||
)?;
|
.finish()?;
|
||||||
|
|
||||||
println!("Transaction details: {:#?}", details);
|
println!("Transaction details: {:#?}", details);
|
||||||
println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
|
println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
|
||||||
|
@ -115,7 +115,7 @@
|
|||||||
//! ### Example
|
//! ### Example
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! use base64::decode;
|
//! use base64::decode;
|
||||||
//! use bdk::{FeeRate, TxBuilder, Wallet};
|
//! use bdk::{FeeRate, Wallet};
|
||||||
//! use bdk::database::MemoryDatabase;
|
//! use bdk::database::MemoryDatabase;
|
||||||
//! use bdk::blockchain::{noop_progress, ElectrumBlockchain};
|
//! use bdk::blockchain::{noop_progress, ElectrumBlockchain};
|
||||||
//!
|
//!
|
||||||
@ -136,12 +136,12 @@
|
|||||||
//! wallet.sync(noop_progress(), None)?;
|
//! wallet.sync(noop_progress(), None)?;
|
||||||
//!
|
//!
|
||||||
//! let send_to = wallet.get_new_address()?;
|
//! let send_to = wallet.get_new_address()?;
|
||||||
//! let (psbt, details) = wallet.create_tx(
|
//! let (psbt, details) = wallet.build_tx()
|
||||||
//! TxBuilder::with_recipients(vec![(send_to.script_pubkey(), 50_000)])
|
//! .add_recipient(send_to.script_pubkey(), 50_000)
|
||||||
//! .enable_rbf()
|
//! .enable_rbf()
|
||||||
//! .do_not_spend_change()
|
//! .do_not_spend_change()
|
||||||
//! .fee_rate(FeeRate::from_sat_per_vb(5.0))
|
//! .fee_rate(FeeRate::from_sat_per_vb(5.0))
|
||||||
//! )?;
|
//! .finish()?;
|
||||||
//!
|
//!
|
||||||
//! println!("Transaction details: {:#?}", details);
|
//! println!("Transaction details: {:#?}", details);
|
||||||
//! println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
|
//! println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
|
||||||
|
@ -127,7 +127,6 @@ mod test {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::wallet::test::{get_funded_wallet, get_test_wpkh};
|
use crate::wallet::test::{get_funded_wallet, get_test_wpkh};
|
||||||
use crate::wallet::TxBuilder;
|
|
||||||
|
|
||||||
struct TestValidator;
|
struct TestValidator;
|
||||||
impl AddressValidator for TestValidator {
|
impl AddressValidator for TestValidator {
|
||||||
@ -158,10 +157,9 @@ mod test {
|
|||||||
|
|
||||||
let addr = testutils!(@external descriptors, 10);
|
let addr = testutils!(@external descriptors, 10);
|
||||||
wallet
|
wallet
|
||||||
.create_tx(TxBuilder::with_recipients(vec![(
|
.build_tx()
|
||||||
addr.script_pubkey(),
|
.add_recipient(addr.script_pubkey(), 25_000)
|
||||||
25_000,
|
.finish()
|
||||||
)]))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,15 +27,12 @@
|
|||||||
//! This module provides the trait [`CoinSelectionAlgorithm`] that can be implemented to
|
//! This module provides the trait [`CoinSelectionAlgorithm`] that can be implemented to
|
||||||
//! define custom coin selection algorithms.
|
//! define custom coin selection algorithms.
|
||||||
//!
|
//!
|
||||||
//! The coin selection algorithm is not globally part of a [`Wallet`](super::Wallet), instead it
|
//! You can specify a custom coin selection algorithm through the [`coin_selection`] method on
|
||||||
//! is selected whenever a [`Wallet::create_tx`](super::Wallet::create_tx) call is made, through
|
//! [`TxBuilder`]. [`DefaultCoinSelectionAlgorithm`] aliases the coin selection algorithm that will
|
||||||
//! the use of the [`TxBuilder`] structure, specifically with
|
//! be used if it is not explicitly set.
|
||||||
//! [`TxBuilder::coin_selection`](super::tx_builder::TxBuilder::coin_selection) method.
|
|
||||||
//!
|
|
||||||
//! The [`DefaultCoinSelectionAlgorithm`] selects the default coin selection algorithm that
|
|
||||||
//! [`TxBuilder`] uses, if it's not explicitly overridden.
|
|
||||||
//!
|
//!
|
||||||
//! [`TxBuilder`]: super::tx_builder::TxBuilder
|
//! [`TxBuilder`]: super::tx_builder::TxBuilder
|
||||||
|
//! [`coin_selection`]: super::tx_builder::coin_selection
|
||||||
//!
|
//!
|
||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
@ -88,10 +85,9 @@
|
|||||||
//! // create wallet, sync, ...
|
//! // create wallet, sync, ...
|
||||||
//!
|
//!
|
||||||
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
//! let (psbt, details) = wallet.create_tx(
|
//! let (psbt, details) = wallet.build_tx().coin_selection(AlwaysSpendEverything)
|
||||||
//! TxBuilder::with_recipients(vec![(to_address.script_pubkey(), 50_000)])
|
//! .add_recipient(to_address.script_pubkey(), 50_000)
|
||||||
//! .coin_selection(AlwaysSpendEverything),
|
//! .finish()?;
|
||||||
//! )?;
|
|
||||||
//!
|
//!
|
||||||
//! // inspect, sign, broadcast, ...
|
//! // inspect, sign, broadcast, ...
|
||||||
//!
|
//!
|
||||||
|
1087
src/wallet/mod.rs
1087
src/wallet/mod.rs
File diff suppressed because it is too large
Load Diff
@ -32,14 +32,22 @@
|
|||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! # use bdk::wallet::tx_builder::CreateTx;
|
//! # use bdk::wallet::tx_builder::CreateTx;
|
||||||
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
//! // Create a transaction with one output to `to_address` of 50_000 satoshi, with a custom fee rate
|
//! # let wallet = doctest_wallet!();
|
||||||
//! // of 5.0 satoshi/vbyte, only spending non-change outputs and with RBF signaling
|
//! // create a TxBuilder from a wallet
|
||||||
//! // enabled
|
//! let mut tx_builder = wallet.build_tx();
|
||||||
//! let builder = TxBuilder::with_recipients(vec![(to_address.script_pubkey(), 50_000)])
|
//!
|
||||||
|
//! let (psbt, tx_details) = tx_builder
|
||||||
|
//! // Create a transaction with one output to `to_address` of 50_000 satoshi
|
||||||
|
//! .add_recipient(to_address.script_pubkey(), 50_000)
|
||||||
|
//! // With a custom fee rate of 5.0 satoshi/vbyte
|
||||||
//! .fee_rate(FeeRate::from_sat_per_vb(5.0))
|
//! .fee_rate(FeeRate::from_sat_per_vb(5.0))
|
||||||
|
//! // Only spend non-change outputs
|
||||||
//! .do_not_spend_change()
|
//! .do_not_spend_change()
|
||||||
//! .enable_rbf();
|
//! // Turn on RBF signaling
|
||||||
//! # let builder: TxBuilder<bdk::database::MemoryDatabase, _, CreateTx> = builder;
|
//! .enable_rbf()
|
||||||
|
//! .finish()?;
|
||||||
|
//!
|
||||||
|
//! # Ok::<(), bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -47,39 +55,92 @@ use std::collections::HashSet;
|
|||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
|
||||||
use bitcoin::{OutPoint, Script, SigHashType, Transaction};
|
use bitcoin::{OutPoint, Script, SigHashType, Transaction};
|
||||||
|
|
||||||
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
||||||
use crate::database::Database;
|
use crate::{database::BatchDatabase, Error, Wallet};
|
||||||
use crate::types::{FeeRate, KeychainKind, UTXO};
|
use crate::{
|
||||||
|
types::{FeeRate, KeychainKind, UTXO},
|
||||||
|
TransactionDetails,
|
||||||
|
};
|
||||||
/// Context in which the [`TxBuilder`] is valid
|
/// Context in which the [`TxBuilder`] is valid
|
||||||
pub trait TxBuilderContext: std::fmt::Debug + Default + Clone {}
|
pub trait TxBuilderContext: std::fmt::Debug + Default + Clone {}
|
||||||
|
|
||||||
/// [`Wallet::create_tx`](super::Wallet::create_tx) context
|
/// Marker type to indicate the [`TxBuilder`] is being used to create a new transaction (as opposed
|
||||||
|
/// to bumping the fee of an existing one).
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct CreateTx;
|
pub struct CreateTx;
|
||||||
impl TxBuilderContext for CreateTx {}
|
impl TxBuilderContext for CreateTx {}
|
||||||
|
|
||||||
/// [`Wallet::bump_fee`](super::Wallet::bump_fee) context
|
/// Marker type to indicate the [`TxBuilder`] is being used to bump the fee of an existing transaction.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct BumpFee;
|
pub struct BumpFee;
|
||||||
impl TxBuilderContext for BumpFee {}
|
impl TxBuilderContext for BumpFee {}
|
||||||
|
|
||||||
/// A transaction builder
|
/// A transaction builder
|
||||||
///
|
///
|
||||||
/// This structure contains the configuration that the wallet must follow to build a transaction.
|
/// A `TxBuilder` is initially created by calling [`build_tx`] or [`build_fee_bump`] on a wallet.
|
||||||
|
/// From there you set sepcific options on the builder until finally calling [`finish`] to get the transaction.
|
||||||
///
|
///
|
||||||
/// For an example see [this module](super::tx_builder)'s documentation;
|
/// Each method on TxBuilder takes and returns `&mut self` so you can use either use a chaining call
|
||||||
#[derive(Debug)]
|
/// or assign the builder and call normally as in the following example:
|
||||||
pub struct TxBuilder<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> {
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bdk::*;
|
||||||
|
/// # use bdk::wallet::tx_builder::*;
|
||||||
|
/// # use bitcoin::*;
|
||||||
|
/// # use core::str::FromStr;
|
||||||
|
/// # let wallet = doctest_wallet!();
|
||||||
|
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
|
/// # let addr2 = addr1.clone();
|
||||||
|
/// // chaining
|
||||||
|
/// let (psbt1, details) = wallet.build_tx()
|
||||||
|
/// .ordering(TxOrdering::Untouched)
|
||||||
|
/// .add_recipient(addr1.script_pubkey(), 50_000)
|
||||||
|
/// .add_recipient(addr2.script_pubkey(), 50_000)
|
||||||
|
/// .finish()?;
|
||||||
|
///
|
||||||
|
/// // non-chaining
|
||||||
|
/// let mut builder = wallet.build_tx();
|
||||||
|
/// for addr in &[addr1, addr2] {
|
||||||
|
/// builder.add_recipient(addr.script_pubkey(), 50_000);
|
||||||
|
/// }
|
||||||
|
/// let (psbt2, details) = builder.ordering(TxOrdering::Untouched).finish()?;
|
||||||
|
/// //
|
||||||
|
/// assert_eq!(psbt1.global.unsigned_tx.output[..2], psbt2.global.unsigned_tx.output[..2]);
|
||||||
|
/// # Ok::<(), bdk::Error>(())
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// At the moment [`coin_selection`] is an exception - it consumes `self`.
|
||||||
|
/// This means it is usually best to call [`coin_selection`] first before calling other methods.
|
||||||
|
///
|
||||||
|
/// Note that calling methods on the builder after calling [`finish`] will result in a panic.
|
||||||
|
///
|
||||||
|
/// For further examples see [this module](super::tx_builder)'s documentation;
|
||||||
|
///
|
||||||
|
/// [`build_tx`]: Self::build_tx
|
||||||
|
/// [`build_fee_bump`]: Self::build_fee_bump
|
||||||
|
/// [`finish`]: Self::finish
|
||||||
|
/// [`coin_selection`]: Self::coin_selection
|
||||||
|
pub struct TxBuilder<'a, B, D, Cs, Ctx> {
|
||||||
|
pub(crate) params: Option<TxParams>,
|
||||||
|
pub(crate) wallet: &'a Wallet<B, D>,
|
||||||
|
pub(crate) coin_selection: Option<Cs>,
|
||||||
|
pub(crate) phantom: PhantomData<Ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The parameters for transaction creation sans coin selection algorithm.
|
||||||
|
//TODO: TxParams should eventually be exposed publicly.
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub(crate) struct TxParams {
|
||||||
pub(crate) recipients: Vec<(Script, u64)>,
|
pub(crate) recipients: Vec<(Script, u64)>,
|
||||||
pub(crate) drain_wallet: bool,
|
pub(crate) drain_wallet: bool,
|
||||||
pub(crate) single_recipient: Option<Script>,
|
pub(crate) single_recipient: Option<Script>,
|
||||||
pub(crate) fee_policy: Option<FeePolicy>,
|
pub(crate) fee_policy: Option<FeePolicy>,
|
||||||
pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
||||||
pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
||||||
pub(crate) utxos: Vec<OutPoint>,
|
pub(crate) utxos: BTreeMap<OutPoint, (UTXO, usize)>,
|
||||||
pub(crate) unspendable: HashSet<OutPoint>,
|
pub(crate) unspendable: HashSet<OutPoint>,
|
||||||
pub(crate) manually_selected_only: bool,
|
pub(crate) manually_selected_only: bool,
|
||||||
pub(crate) sighash: Option<SigHashType>,
|
pub(crate) sighash: Option<SigHashType>,
|
||||||
@ -90,10 +151,14 @@ pub struct TxBuilder<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderC
|
|||||||
pub(crate) change_policy: ChangeSpendPolicy,
|
pub(crate) change_policy: ChangeSpendPolicy,
|
||||||
pub(crate) force_non_witness_utxo: bool,
|
pub(crate) force_non_witness_utxo: bool,
|
||||||
pub(crate) add_global_xpubs: bool,
|
pub(crate) add_global_xpubs: bool,
|
||||||
pub(crate) coin_selection: Cs,
|
|
||||||
pub(crate) include_output_redeem_witness_script: bool,
|
pub(crate) include_output_redeem_witness_script: bool,
|
||||||
|
pub(crate) bumping_fee: Option<PreviousFee>,
|
||||||
|
}
|
||||||
|
|
||||||
phantom: PhantomData<(D, Ctx)>,
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(crate) struct PreviousFee {
|
||||||
|
pub absolute: u64,
|
||||||
|
pub rate: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -108,58 +173,24 @@ impl std::default::Default for FeePolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately derive doesn't work with `PhantomData`: https://github.com/rust-lang/rust/issues/26925
|
|
||||||
impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> Default
|
|
||||||
for TxBuilder<D, Cs, Ctx>
|
|
||||||
where
|
|
||||||
Cs: Default,
|
|
||||||
{
|
|
||||||
fn default() -> Self {
|
|
||||||
TxBuilder {
|
|
||||||
recipients: Default::default(),
|
|
||||||
drain_wallet: Default::default(),
|
|
||||||
single_recipient: Default::default(),
|
|
||||||
fee_policy: Default::default(),
|
|
||||||
internal_policy_path: Default::default(),
|
|
||||||
external_policy_path: Default::default(),
|
|
||||||
utxos: Default::default(),
|
|
||||||
unspendable: Default::default(),
|
|
||||||
manually_selected_only: Default::default(),
|
|
||||||
sighash: Default::default(),
|
|
||||||
ordering: Default::default(),
|
|
||||||
locktime: Default::default(),
|
|
||||||
rbf: Default::default(),
|
|
||||||
version: Default::default(),
|
|
||||||
change_policy: Default::default(),
|
|
||||||
force_non_witness_utxo: Default::default(),
|
|
||||||
add_global_xpubs: Default::default(),
|
|
||||||
coin_selection: Default::default(),
|
|
||||||
include_output_redeem_witness_script: Default::default(),
|
|
||||||
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// methods supported by both contexts, but only for `DefaultCoinSelectionAlgorithm`
|
|
||||||
impl<D: Database, Ctx: TxBuilderContext> TxBuilder<D, DefaultCoinSelectionAlgorithm, Ctx> {
|
|
||||||
/// Create an empty builder
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
||||||
impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilder<D, Cs, Ctx> {
|
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
|
||||||
|
TxBuilder<'a, B, D, Cs, Ctx>
|
||||||
|
{
|
||||||
|
fn params(&mut self) -> &mut TxParams {
|
||||||
|
self.params
|
||||||
|
.as_mut()
|
||||||
|
.expect("method called on transaction builder after it was finalized")
|
||||||
|
}
|
||||||
/// Set a custom fee rate
|
/// Set a custom fee rate
|
||||||
pub fn fee_rate(mut self, fee_rate: FeeRate) -> Self {
|
pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
|
||||||
self.fee_policy = Some(FeePolicy::FeeRate(fee_rate));
|
self.params().fee_policy = Some(FeePolicy::FeeRate(fee_rate));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set an absolute fee
|
/// Set an absolute fee
|
||||||
pub fn fee_absolute(mut self, fee_amount: u64) -> Self {
|
pub fn fee_absolute(&mut self, fee_amount: u64) -> &mut Self {
|
||||||
self.fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
|
self.params().fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,96 +242,95 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use bdk::*;
|
/// # use bdk::*;
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
|
/// # let wallet = doctest_wallet!();
|
||||||
/// let mut path = BTreeMap::new();
|
/// let mut path = BTreeMap::new();
|
||||||
/// path.insert("aabbccdd".to_string(), vec![0, 1]);
|
/// path.insert("aabbccdd".to_string(), vec![0, 1]);
|
||||||
///
|
///
|
||||||
/// let builder = TxBuilder::with_recipients(vec![(to_address.script_pubkey(), 50_000)])
|
/// let builder = wallet.build_tx()
|
||||||
|
/// .add_recipient(to_address.script_pubkey(), 50_000)
|
||||||
/// .policy_path(path, KeychainKind::External);
|
/// .policy_path(path, KeychainKind::External);
|
||||||
/// # let builder: TxBuilder<bdk::database::MemoryDatabase, _, _> = builder;
|
///
|
||||||
|
/// # Ok::<(), bdk::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub fn policy_path(
|
pub fn policy_path(
|
||||||
mut self,
|
&mut self,
|
||||||
policy_path: BTreeMap<String, Vec<usize>>,
|
policy_path: BTreeMap<String, Vec<usize>>,
|
||||||
keychain: KeychainKind,
|
keychain: KeychainKind,
|
||||||
) -> Self {
|
) -> &mut Self {
|
||||||
let to_update = match keychain {
|
let to_update = match keychain {
|
||||||
KeychainKind::Internal => &mut self.internal_policy_path,
|
KeychainKind::Internal => &mut self.params().internal_policy_path,
|
||||||
KeychainKind::External => &mut self.external_policy_path,
|
KeychainKind::External => &mut self.params().external_policy_path,
|
||||||
};
|
};
|
||||||
|
|
||||||
*to_update = Some(policy_path);
|
*to_update = Some(policy_path);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the internal list of utxos that **must** be spent with a new list
|
|
||||||
///
|
|
||||||
/// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
|
|
||||||
/// the "utxos" and the "unspendable" list, it will be spent.
|
|
||||||
pub fn utxos(mut self, utxos: Vec<OutPoint>) -> Self {
|
|
||||||
self.utxos = utxos;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a utxo to the internal list of utxos that **must** be spent
|
/// Add a utxo to the internal list of utxos that **must** be spent
|
||||||
///
|
///
|
||||||
/// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
|
/// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
|
||||||
/// the "utxos" and the "unspendable" list, it will be spent.
|
/// the "utxos" and the "unspendable" list, it will be spent.
|
||||||
pub fn add_utxo(mut self, utxo: OutPoint) -> Self {
|
pub fn add_utxo(&mut self, outpoint: OutPoint) -> Result<&mut Self, Error> {
|
||||||
self.utxos.push(utxo);
|
if self.params().utxos.get(&outpoint).is_none() {
|
||||||
self
|
let deriv_ctx = crate::wallet::descriptor_to_pk_ctx(self.wallet.secp_ctx());
|
||||||
|
let utxo = self.wallet.get_utxo(outpoint)?.ok_or(Error::UnknownUTXO)?;
|
||||||
|
let descriptor = self.wallet.get_descriptor_for_keychain(utxo.keychain);
|
||||||
|
let satisfaction_weight = descriptor.max_satisfaction_weight(deriv_ctx).unwrap();
|
||||||
|
self.params()
|
||||||
|
.utxos
|
||||||
|
.insert(outpoint, (utxo, satisfaction_weight));
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only spend utxos added by [`add_utxo`] and [`utxos`].
|
/// Only spend utxos added by [`add_utxo`].
|
||||||
///
|
///
|
||||||
/// The wallet will **not** add additional utxos to the transaction even if they are needed to
|
/// The wallet will **not** add additional utxos to the transaction even if they are needed to
|
||||||
/// make the transaction valid.
|
/// make the transaction valid.
|
||||||
///
|
///
|
||||||
/// [`add_utxo`]: Self::add_utxo
|
/// [`add_utxo`]: Self::add_utxo
|
||||||
/// [`utxos`]: Self::utxos
|
pub fn manually_selected_only(&mut self) -> &mut Self {
|
||||||
pub fn manually_selected_only(mut self) -> Self {
|
self.params().manually_selected_only = true;
|
||||||
self.manually_selected_only = true;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the internal list of unspendable utxos with a new list
|
/// Replace the internal list of unspendable utxos with a new list
|
||||||
///
|
///
|
||||||
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::utxos`] and
|
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
|
||||||
/// [`TxBuilder::add_utxo`] have priority over these. See the docs of the two linked methods
|
/// have priority over these. See the docs of the two linked methods for more details.
|
||||||
/// for more details.
|
pub fn unspendable(&mut self, unspendable: Vec<OutPoint>) -> &mut Self {
|
||||||
pub fn unspendable(mut self, unspendable: Vec<OutPoint>) -> Self {
|
self.params().unspendable = unspendable.into_iter().collect();
|
||||||
self.unspendable = unspendable.into_iter().collect();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a utxo to the internal list of unspendable utxos
|
/// Add a utxo to the internal list of unspendable utxos
|
||||||
///
|
///
|
||||||
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::utxos`] and
|
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
|
||||||
/// [`TxBuilder::add_utxo`] have priority over this. See the docs of the two linked methods
|
/// have priority over this. See the docs of the two linked methods for more details.
|
||||||
/// for more details.
|
pub fn add_unspendable(&mut self, unspendable: OutPoint) -> &mut Self {
|
||||||
pub fn add_unspendable(mut self, unspendable: OutPoint) -> Self {
|
self.params().unspendable.insert(unspendable);
|
||||||
self.unspendable.insert(unspendable);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign with a specific sig hash
|
/// Sign with a specific sig hash
|
||||||
///
|
///
|
||||||
/// **Use this option very carefully**
|
/// **Use this option very carefully**
|
||||||
pub fn sighash(mut self, sighash: SigHashType) -> Self {
|
pub fn sighash(&mut self, sighash: SigHashType) -> &mut Self {
|
||||||
self.sighash = Some(sighash);
|
self.params().sighash = Some(sighash);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Choose the ordering for inputs and outputs of the transaction
|
/// Choose the ordering for inputs and outputs of the transaction
|
||||||
pub fn ordering(mut self, ordering: TxOrdering) -> Self {
|
pub fn ordering(&mut self, ordering: TxOrdering) -> &mut Self {
|
||||||
self.ordering = ordering;
|
self.params().ordering = ordering;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use a specific nLockTime while creating the transaction
|
/// Use a specific nLockTime while creating the transaction
|
||||||
///
|
///
|
||||||
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
|
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
|
||||||
pub fn nlocktime(mut self, locktime: u32) -> Self {
|
pub fn nlocktime(&mut self, locktime: u32) -> &mut Self {
|
||||||
self.locktime = Some(locktime);
|
self.params().locktime = Some(locktime);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,8 +338,8 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
///
|
///
|
||||||
/// The `version` should always be greater than `0` and greater than `1` if the wallet's
|
/// The `version` should always be greater than `0` and greater than `1` if the wallet's
|
||||||
/// descriptors contain an "older" (OP_CSV) operator.
|
/// descriptors contain an "older" (OP_CSV) operator.
|
||||||
pub fn version(mut self, version: i32) -> Self {
|
pub fn version(&mut self, version: i32) -> &mut Self {
|
||||||
self.version = Some(Version(version));
|
self.params().version = Some(Version(version));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,8 +347,8 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
///
|
///
|
||||||
/// This effectively adds all the change outputs to the "unspendable" list. See
|
/// This effectively adds all the change outputs to the "unspendable" list. See
|
||||||
/// [`TxBuilder::unspendable`].
|
/// [`TxBuilder::unspendable`].
|
||||||
pub fn do_not_spend_change(mut self) -> Self {
|
pub fn do_not_spend_change(&mut self) -> &mut Self {
|
||||||
self.change_policy = ChangeSpendPolicy::ChangeForbidden;
|
self.params().change_policy = ChangeSpendPolicy::ChangeForbidden;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,15 +356,15 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
///
|
///
|
||||||
/// This effectively adds all the non-change outputs to the "unspendable" list. See
|
/// This effectively adds all the non-change outputs to the "unspendable" list. See
|
||||||
/// [`TxBuilder::unspendable`].
|
/// [`TxBuilder::unspendable`].
|
||||||
pub fn only_spend_change(mut self) -> Self {
|
pub fn only_spend_change(&mut self) -> &mut Self {
|
||||||
self.change_policy = ChangeSpendPolicy::OnlyChange;
|
self.params().change_policy = ChangeSpendPolicy::OnlyChange;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a specific [`ChangeSpendPolicy`]. See [`TxBuilder::do_not_spend_change`] and
|
/// Set a specific [`ChangeSpendPolicy`]. See [`TxBuilder::do_not_spend_change`] and
|
||||||
/// [`TxBuilder::only_spend_change`] for some shortcuts.
|
/// [`TxBuilder::only_spend_change`] for some shortcuts.
|
||||||
pub fn change_policy(mut self, change_policy: ChangeSpendPolicy) -> Self {
|
pub fn change_policy(&mut self, change_policy: ChangeSpendPolicy) -> &mut Self {
|
||||||
self.change_policy = change_policy;
|
self.params().change_policy = change_policy;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,8 +372,8 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
/// descriptors.
|
/// descriptors.
|
||||||
///
|
///
|
||||||
/// This is useful for signers which always require it, like Trezor hardware wallets.
|
/// This is useful for signers which always require it, like Trezor hardware wallets.
|
||||||
pub fn force_non_witness_utxo(mut self) -> Self {
|
pub fn force_non_witness_utxo(&mut self) -> &mut Self {
|
||||||
self.force_non_witness_utxo = true;
|
self.params().force_non_witness_utxo = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,8 +381,8 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields.
|
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields.
|
||||||
///
|
///
|
||||||
/// This is useful for signers which always require it, like ColdCard hardware wallets.
|
/// This is useful for signers which always require it, like ColdCard hardware wallets.
|
||||||
pub fn include_output_redeem_witness_script(mut self) -> Self {
|
pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
|
||||||
self.include_output_redeem_witness_script = true;
|
self.params().include_output_redeem_witness_script = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,69 +391,57 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext> TxBuilde
|
|||||||
///
|
///
|
||||||
/// This is useful for offline signers that take part to a multisig. Some hardware wallets like
|
/// This is useful for offline signers that take part to a multisig. Some hardware wallets like
|
||||||
/// BitBox and ColdCard are known to require this.
|
/// BitBox and ColdCard are known to require this.
|
||||||
pub fn add_global_xpubs(mut self) -> Self {
|
pub fn add_global_xpubs(&mut self) -> &mut Self {
|
||||||
self.add_global_xpubs = true;
|
self.params().add_global_xpubs = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spend all the available inputs. This respects filters like [`TxBuilder::unspendable`] and the change policy.
|
/// Spend all the available inputs. This respects filters like [`TxBuilder::unspendable`] and the change policy.
|
||||||
pub fn drain_wallet(mut self) -> Self {
|
pub fn drain_wallet(&mut self) -> &mut Self {
|
||||||
self.drain_wallet = true;
|
self.params().drain_wallet = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Choose the coin selection algorithm
|
/// Choose the coin selection algorithm
|
||||||
///
|
///
|
||||||
/// Overrides the [`DefaultCoinSelectionAlgorithm`](super::coin_selection::DefaultCoinSelectionAlgorithm).
|
/// Overrides the [`DefaultCoinSelectionAlgorithm`](super::coin_selection::DefaultCoinSelectionAlgorithm).
|
||||||
|
///
|
||||||
|
/// Note that this function consumes the builder and returns it so it is usually best to put this as the first call on the builder.
|
||||||
pub fn coin_selection<P: CoinSelectionAlgorithm<D>>(
|
pub fn coin_selection<P: CoinSelectionAlgorithm<D>>(
|
||||||
self,
|
self,
|
||||||
coin_selection: P,
|
coin_selection: P,
|
||||||
) -> TxBuilder<D, P, Ctx> {
|
) -> TxBuilder<'a, B, D, P, Ctx> {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
recipients: self.recipients,
|
wallet: self.wallet,
|
||||||
drain_wallet: self.drain_wallet,
|
params: self.params,
|
||||||
single_recipient: self.single_recipient,
|
coin_selection: Some(coin_selection),
|
||||||
fee_policy: self.fee_policy,
|
|
||||||
internal_policy_path: self.internal_policy_path,
|
|
||||||
external_policy_path: self.external_policy_path,
|
|
||||||
utxos: self.utxos,
|
|
||||||
unspendable: self.unspendable,
|
|
||||||
manually_selected_only: self.manually_selected_only,
|
|
||||||
sighash: self.sighash,
|
|
||||||
ordering: self.ordering,
|
|
||||||
locktime: self.locktime,
|
|
||||||
rbf: self.rbf,
|
|
||||||
version: self.version,
|
|
||||||
change_policy: self.change_policy,
|
|
||||||
force_non_witness_utxo: self.force_non_witness_utxo,
|
|
||||||
add_global_xpubs: self.add_global_xpubs,
|
|
||||||
include_output_redeem_witness_script: self.include_output_redeem_witness_script,
|
|
||||||
coin_selection,
|
|
||||||
|
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// methods supported only by create_tx, and only for `DefaultCoinSelectionAlgorithm`
|
/// Finish the building the transaction.
|
||||||
impl<D: Database> TxBuilder<D, DefaultCoinSelectionAlgorithm, CreateTx> {
|
///
|
||||||
/// Create a builder starting from a list of recipients
|
/// Returns the [`BIP174`] "PSBT" and summary details about the transaction.
|
||||||
pub fn with_recipients(recipients: Vec<(Script, u64)>) -> Self {
|
///
|
||||||
Self::default().set_recipients(recipients)
|
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
||||||
|
pub fn finish(&mut self) -> Result<(PSBT, TransactionDetails), Error> {
|
||||||
|
self.wallet.create_tx(
|
||||||
|
self.coin_selection.take().unwrap(),
|
||||||
|
self.params.take().unwrap(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods supported only by create_tx, for any `CoinSelectionAlgorithm`
|
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, B, D, Cs, CreateTx> {
|
||||||
impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs, CreateTx> {
|
|
||||||
/// Replace the recipients already added with a new list
|
/// Replace the recipients already added with a new list
|
||||||
pub fn set_recipients(mut self, recipients: Vec<(Script, u64)>) -> Self {
|
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self {
|
||||||
self.recipients = recipients;
|
self.params().recipients = recipients;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a recipient to the internal list
|
/// Add a recipient to the internal list
|
||||||
pub fn add_recipient(mut self, script_pubkey: Script, amount: u64) -> Self {
|
pub fn add_recipient(&mut self, script_pubkey: Script, amount: u64) -> &mut Self {
|
||||||
self.recipients.push((script_pubkey, amount));
|
self.params().recipients.push((script_pubkey, amount));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,14 +454,14 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs, CreateTx> {
|
|||||||
/// It can only be used in conjunction with [`drain_wallet`](Self::drain_wallet) to send the
|
/// It can only be used in conjunction with [`drain_wallet`](Self::drain_wallet) to send the
|
||||||
/// entire content of the wallet (minus filters) to a single recipient or with a
|
/// entire content of the wallet (minus filters) to a single recipient or with a
|
||||||
/// list of manually selected UTXOs by enabling [`manually_selected_only`](Self::manually_selected_only)
|
/// list of manually selected UTXOs by enabling [`manually_selected_only`](Self::manually_selected_only)
|
||||||
/// and selecting them with [`utxos`](Self::utxos) or [`add_utxo`](Self::add_utxo).
|
/// and selecting them with or [`add_utxo`](Self::add_utxo).
|
||||||
///
|
///
|
||||||
/// When bumping the fees of a transaction made with this option, the user should remeber to
|
/// When bumping the fees of a transaction made with this option, the user should remeber to
|
||||||
/// add [`maintain_single_recipient`](Self::maintain_single_recipient) to correctly update the
|
/// add [`maintain_single_recipient`](Self::maintain_single_recipient) to correctly update the
|
||||||
/// single output instead of adding one more for the change.
|
/// single output instead of adding one more for the change.
|
||||||
pub fn set_single_recipient(mut self, recipient: Script) -> Self {
|
pub fn set_single_recipient(&mut self, recipient: Script) -> &mut Self {
|
||||||
self.single_recipient = Some(recipient);
|
self.params().single_recipient = Some(recipient);
|
||||||
self.recipients.clear();
|
self.params().recipients.clear();
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -451,8 +469,8 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs, CreateTx> {
|
|||||||
/// Enable signaling RBF
|
/// Enable signaling RBF
|
||||||
///
|
///
|
||||||
/// This will use the default nSequence value of `0xFFFFFFFD`.
|
/// This will use the default nSequence value of `0xFFFFFFFD`.
|
||||||
pub fn enable_rbf(mut self) -> Self {
|
pub fn enable_rbf(&mut self) -> &mut Self {
|
||||||
self.rbf = Some(RBFValue::Default);
|
self.params().rbf = Some(RBFValue::Default);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,17 +481,17 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs, CreateTx> {
|
|||||||
///
|
///
|
||||||
/// If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not
|
/// If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not
|
||||||
/// be a valid nSequence to signal RBF.
|
/// be a valid nSequence to signal RBF.
|
||||||
pub fn enable_rbf_with_sequence(mut self, nsequence: u32) -> Self {
|
pub fn enable_rbf_with_sequence(&mut self, nsequence: u32) -> &mut Self {
|
||||||
self.rbf = Some(RBFValue::Value(nsequence));
|
self.params().rbf = Some(RBFValue::Value(nsequence));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods supported only by bump_fee
|
// methods supported only by bump_fee
|
||||||
impl<D: Database> TxBuilder<D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
impl<'a, B, D: BatchDatabase> TxBuilder<'a, B, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
||||||
/// Bump the fees of a transaction made with [`set_single_recipient`](Self::set_single_recipient)
|
/// Bump the fees of a transaction made with [`set_single_recipient`](Self::set_single_recipient)
|
||||||
///
|
///
|
||||||
/// Unless extra inputs are specified with [`add_utxo`] or [`utxos`], this flag will make
|
/// Unless extra inputs are specified with [`add_utxo`], this flag will make
|
||||||
/// `bump_fee` reduce the value of the existing output, or fail if it would be consumed
|
/// `bump_fee` reduce the value of the existing output, or fail if it would be consumed
|
||||||
/// entirely given the higher new fee rate.
|
/// entirely given the higher new fee rate.
|
||||||
///
|
///
|
||||||
@ -483,9 +501,11 @@ impl<D: Database> TxBuilder<D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
|||||||
/// Fails if the transaction has more than one outputs.
|
/// Fails if the transaction has more than one outputs.
|
||||||
///
|
///
|
||||||
/// [`add_utxo`]: Self::add_utxo
|
/// [`add_utxo`]: Self::add_utxo
|
||||||
/// [`utxos`]: Self::utxos
|
pub fn maintain_single_recipient(&mut self) -> &mut Self {
|
||||||
pub fn maintain_single_recipient(mut self) -> Self {
|
let mut recipients = self.params().recipients.drain(..).collect::<Vec<_>>();
|
||||||
self.single_recipient = Some(Script::default());
|
assert_eq!(recipients.len(), 1, "maintain_single_recipient must not be called while bumping a transactions with more than one output");
|
||||||
|
self.params().single_recipient = Some(recipients.pop().unwrap().0);
|
||||||
|
// Since we are fee bumping and maintaining a single recipient we never want to add any more non-manual inputs.
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey(), 25_000)])).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey(), 25_000).finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
let tx = psbt.extract_tx();
|
let tx = psbt.extract_tx();
|
||||||
@ -334,7 +334,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey(), 25_000)])).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey(), 25_000).finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
let sent_txid = wallet.broadcast(psbt.extract_tx()).unwrap();
|
let sent_txid = wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -373,7 +373,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
|
|
||||||
let mut total_sent = 0;
|
let mut total_sent = 0;
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey().clone(), 5_000)])).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey(), 5_000).finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -405,7 +405,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey().clone(), 5_000)]).enable_rbf()).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey().clone(), 5_000).enable_rbf().finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -413,7 +413,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000);
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
let (new_psbt, new_details) = wallet.bump_fee(&details.txid, TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(2.1))).unwrap();
|
let (new_psbt, new_details) = wallet.build_fee_bump(details.txid).unwrap().fee_rate(FeeRate::from_sat_per_vb(2.1)).finish().unwrap();
|
||||||
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
@ -437,7 +437,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey().clone(), 49_000)]).enable_rbf()).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf().finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -445,8 +445,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees);
|
assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees);
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
let (new_psbt, new_details) = wallet.bump_fee(&details.txid, TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(5.0))).unwrap();
|
let (new_psbt, new_details) = wallet.build_fee_bump(details.txid).unwrap().fee_rate(FeeRate::from_sat_per_vb(5.0)).finish().unwrap();
|
||||||
|
|
||||||
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
@ -470,7 +469,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey().clone(), 49_000)]).enable_rbf()).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf().finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -478,8 +477,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
||||||
assert_eq!(details.received, 1_000 - details.fees);
|
assert_eq!(details.received, 1_000 - details.fees);
|
||||||
|
|
||||||
let (new_psbt, new_details) = wallet.bump_fee(&details.txid, TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(10.0))).unwrap();
|
let (new_psbt, new_details) = wallet.build_fee_bump(details.txid).unwrap().fee_rate(FeeRate::from_sat_per_vb(10.0)).finish().unwrap();
|
||||||
|
|
||||||
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
@ -501,7 +499,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
|
||||||
let (psbt, details) = wallet.create_tx(TxBuilder::with_recipients(vec![(node_addr.script_pubkey().clone(), 49_000)]).enable_rbf()).unwrap();
|
let (psbt, details) = wallet.build_tx().add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf().finish().unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
let (psbt, finalized) = wallet.sign(psbt, None).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
@ -509,7 +507,7 @@ pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream
|
|||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
||||||
assert_eq!(details.received, 1_000 - details.fees);
|
assert_eq!(details.received, 1_000 - details.fees);
|
||||||
|
|
||||||
let (new_psbt, new_details) = wallet.bump_fee(&details.txid, TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(123.0))).unwrap();
|
let (new_psbt, new_details) = wallet.build_fee_bump(details.txid).unwrap().fee_rate(FeeRate::from_sat_per_vb(123.0)).finish().unwrap();
|
||||||
println!("{:#?}", new_details);
|
println!("{:#?}", new_details);
|
||||||
|
|
||||||
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
let (new_psbt, finalized) = wallet.sign(new_psbt, None).unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user