2020-08-31 11:26:36 +02:00
|
|
|
// Magical Bitcoin Library
|
|
|
|
// Written in 2020 by
|
|
|
|
// Alekos Filini <alekos.filini@gmail.com>
|
|
|
|
//
|
|
|
|
// Copyright (c) 2020 Magical Bitcoin
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
|
|
// copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
// SOFTWARE.
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
//! Transaction builder
|
|
|
|
//!
|
|
|
|
//! ## Example
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! # use std::str::FromStr;
|
|
|
|
//! # use bitcoin::*;
|
2020-09-14 14:25:38 +02:00
|
|
|
//! # use bdk::*;
|
2020-10-28 10:37:47 +01:00
|
|
|
//! # use bdk::wallet::tx_builder::CreateTx;
|
2020-09-04 15:45:11 +02:00
|
|
|
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
2021-01-01 13:35:05 +11:00
|
|
|
//! # let wallet = doctest_wallet!();
|
|
|
|
//! // create a TxBuilder from a wallet
|
|
|
|
//! let mut tx_builder = wallet.build_tx();
|
|
|
|
//!
|
|
|
|
//! 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
|
2020-09-04 15:45:11 +02:00
|
|
|
//! .fee_rate(FeeRate::from_sat_per_vb(5.0))
|
2021-01-01 13:35:05 +11:00
|
|
|
//! // Only spend non-change outputs
|
2020-09-04 15:45:11 +02:00
|
|
|
//! .do_not_spend_change()
|
2021-01-01 13:35:05 +11:00
|
|
|
//! // Turn on RBF signaling
|
|
|
|
//! .enable_rbf()
|
|
|
|
//! .finish()?;
|
|
|
|
//!
|
|
|
|
//! # Ok::<(), bdk::Error>(())
|
2020-09-04 15:45:11 +02:00
|
|
|
//! ```
|
|
|
|
|
2020-08-06 13:09:39 +02:00
|
|
|
use std::collections::BTreeMap;
|
2020-10-21 15:53:55 +11:00
|
|
|
use std::collections::HashSet;
|
2020-08-07 15:35:14 +02:00
|
|
|
use std::default::Default;
|
2020-10-14 15:21:22 +02:00
|
|
|
use std::marker::PhantomData;
|
2020-08-06 13:09:39 +02:00
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
|
2020-09-04 15:45:11 +02:00
|
|
|
use bitcoin::{OutPoint, Script, SigHashType, Transaction};
|
2020-08-06 13:09:39 +02:00
|
|
|
|
2020-08-06 16:56:41 +02:00
|
|
|
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
2021-01-01 13:35:05 +11:00
|
|
|
use crate::{database::BatchDatabase, Error, Wallet};
|
|
|
|
use crate::{
|
|
|
|
types::{FeeRate, KeychainKind, UTXO},
|
|
|
|
TransactionDetails,
|
|
|
|
};
|
2020-10-28 10:37:47 +01:00
|
|
|
/// Context in which the [`TxBuilder`] is valid
|
|
|
|
pub trait TxBuilderContext: std::fmt::Debug + Default + Clone {}
|
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Marker type to indicate the [`TxBuilder`] is being used to create a new transaction (as opposed
|
|
|
|
/// to bumping the fee of an existing one).
|
2020-10-28 10:37:47 +01:00
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
pub struct CreateTx;
|
|
|
|
impl TxBuilderContext for CreateTx {}
|
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Marker type to indicate the [`TxBuilder`] is being used to bump the fee of an existing transaction.
|
2020-10-28 10:37:47 +01:00
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
pub struct BumpFee;
|
|
|
|
impl TxBuilderContext for BumpFee {}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// A transaction builder
|
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// 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.
|
2020-09-04 15:45:11 +02:00
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Each method on TxBuilder takes and returns `&mut self` so you can use either use a chaining call
|
|
|
|
/// or assign the builder and call normally as in the following example:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # 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;
|
|
|
|
///
|
2021-01-05 08:46:23 +11:00
|
|
|
/// [`build_tx`]: Wallet::build_tx
|
|
|
|
/// [`build_fee_bump`]: Wallet::build_fee_bump
|
2021-01-01 13:35:05 +11:00
|
|
|
/// [`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 {
|
2020-09-04 15:45:11 +02:00
|
|
|
pub(crate) recipients: Vec<(Script, u64)>,
|
2020-10-28 10:37:47 +01:00
|
|
|
pub(crate) drain_wallet: bool,
|
|
|
|
pub(crate) single_recipient: Option<Script>,
|
2020-10-20 18:10:59 +02:00
|
|
|
pub(crate) fee_policy: Option<FeePolicy>,
|
2020-11-10 15:06:14 +01:00
|
|
|
pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
|
|
|
pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
2021-01-01 13:35:05 +11:00
|
|
|
pub(crate) utxos: BTreeMap<OutPoint, (UTXO, usize)>,
|
2020-10-21 15:53:55 +11:00
|
|
|
pub(crate) unspendable: HashSet<OutPoint>,
|
2020-10-22 12:07:51 +11:00
|
|
|
pub(crate) manually_selected_only: bool,
|
2020-08-06 16:56:41 +02:00
|
|
|
pub(crate) sighash: Option<SigHashType>,
|
2020-08-07 15:35:14 +02:00
|
|
|
pub(crate) ordering: TxOrdering,
|
|
|
|
pub(crate) locktime: Option<u32>,
|
2020-12-07 14:48:17 +01:00
|
|
|
pub(crate) rbf: Option<RBFValue>,
|
2020-08-10 17:16:47 +02:00
|
|
|
pub(crate) version: Option<Version>,
|
2020-08-07 19:40:13 +02:00
|
|
|
pub(crate) change_policy: ChangeSpendPolicy,
|
2020-08-08 12:06:40 +02:00
|
|
|
pub(crate) force_non_witness_utxo: bool,
|
2020-11-30 15:13:33 +01:00
|
|
|
pub(crate) add_global_xpubs: bool,
|
2020-11-16 16:25:16 -06:00
|
|
|
pub(crate) include_output_redeem_witness_script: bool,
|
2021-01-01 13:35:05 +11:00
|
|
|
pub(crate) bumping_fee: Option<PreviousFee>,
|
|
|
|
}
|
2020-10-14 15:21:22 +02:00
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub(crate) struct PreviousFee {
|
|
|
|
pub absolute: u64,
|
|
|
|
pub rate: f32,
|
2020-08-06 13:09:39 +02:00
|
|
|
}
|
|
|
|
|
2020-10-20 18:10:59 +02:00
|
|
|
#[derive(Debug)]
|
2020-10-28 10:37:47 +01:00
|
|
|
pub(crate) enum FeePolicy {
|
2020-10-20 18:10:59 +02:00
|
|
|
FeeRate(FeeRate),
|
|
|
|
FeeAmount(u64),
|
|
|
|
}
|
|
|
|
|
2020-10-22 09:11:58 +02:00
|
|
|
impl std::default::Default for FeePolicy {
|
|
|
|
fn default() -> Self {
|
|
|
|
FeePolicy::FeeRate(FeeRate::default_min_relay_fee())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
|
|
|
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
|
|
|
|
TxBuilder<'a, B, D, Cs, Ctx>
|
2020-10-14 15:21:22 +02:00
|
|
|
{
|
2021-01-01 13:35:05 +11:00
|
|
|
fn params(&mut self) -> &mut TxParams {
|
|
|
|
self.params
|
|
|
|
.as_mut()
|
|
|
|
.expect("method called on transaction builder after it was finalized")
|
2020-10-14 15:21:22 +02:00
|
|
|
}
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Set a custom fee rate
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
|
|
|
|
self.params().fee_policy = Some(FeePolicy::FeeRate(fee_rate));
|
2020-10-20 18:10:59 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set an absolute fee
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn fee_absolute(&mut self, fee_amount: u64) -> &mut Self {
|
|
|
|
self.params().fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
|
2020-08-06 13:09:39 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-12-15 08:39:19 +11:00
|
|
|
/// Set the policy path to use while creating the transaction for a given keychain.
|
2020-09-04 15:45:11 +02:00
|
|
|
///
|
|
|
|
/// This method accepts a map where the key is the policy node id (see
|
|
|
|
/// [`Policy::id`](crate::descriptor::Policy::id)) and the value is the list of the indexes of
|
|
|
|
/// the items that are intended to be satisfied from the policy node (see
|
|
|
|
/// [`SatisfiableItem::Thresh::items`](crate::descriptor::policy::SatisfiableItem::Thresh::items)).
|
2020-11-10 15:06:14 +01:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// An example of when the policy path is needed is the following descriptor:
|
|
|
|
/// `wsh(thresh(2,pk(A),sj:and_v(v:pk(B),n:older(6)),snj:and_v(v:pk(C),after(630000))))`,
|
|
|
|
/// derived from the miniscript policy `thresh(2,pk(A),and(pk(B),older(6)),and(pk(C),after(630000)))`.
|
|
|
|
/// It declares three descriptor fragments, and at the top level it uses `thresh()` to
|
|
|
|
/// ensure that at least two of them are satisfied. The individual fragments are:
|
|
|
|
///
|
|
|
|
/// 1. `pk(A)`
|
|
|
|
/// 2. `and(pk(B),older(6))`
|
|
|
|
/// 3. `and(pk(C),after(630000))`
|
|
|
|
///
|
|
|
|
/// When those conditions are combined in pairs, it's clear that the transaction needs to be created
|
|
|
|
/// differently depending on how the user intends to satisfy the policy afterwards:
|
|
|
|
///
|
|
|
|
/// * If fragments `1` and `2` are used, the transaction will need to use a specific
|
|
|
|
/// `n_sequence` in order to spend an `OP_CSV` branch.
|
|
|
|
/// * If fragments `1` and `3` are used, the transaction will need to use a specific `locktime`
|
|
|
|
/// in order to spend an `OP_CLTV` branch.
|
|
|
|
/// * If fragments `2` and `3` are used, the transaction will need both.
|
|
|
|
///
|
|
|
|
/// When the spending policy is represented as a tree (see
|
|
|
|
/// [`Wallet::policies`](super::Wallet::policies)), every node
|
|
|
|
/// is assigned a unique identifier that can be used in the policy path to specify which of
|
|
|
|
/// the node's children the user intends to satisfy: for instance, assuming the `thresh()`
|
|
|
|
/// root node of this example has an id of `aabbccdd`, the policy path map would look like:
|
|
|
|
///
|
|
|
|
/// `{ "aabbccdd" => [0, 1] }`
|
|
|
|
///
|
|
|
|
/// where the key is the node's id, and the value is a list of the children that should be
|
|
|
|
/// used, in no particular order.
|
|
|
|
///
|
|
|
|
/// If a particularly complex descriptor has multiple ambiguous thresholds in its structure,
|
|
|
|
/// multiple entries can be added to the map, one for each node that requires an explicit path.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use std::str::FromStr;
|
|
|
|
/// # use std::collections::BTreeMap;
|
|
|
|
/// # use bitcoin::*;
|
|
|
|
/// # use bdk::*;
|
|
|
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
2021-01-01 13:35:05 +11:00
|
|
|
/// # let wallet = doctest_wallet!();
|
2020-11-10 15:06:14 +01:00
|
|
|
/// let mut path = BTreeMap::new();
|
|
|
|
/// path.insert("aabbccdd".to_string(), vec![0, 1]);
|
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// let builder = wallet.build_tx()
|
|
|
|
/// .add_recipient(to_address.script_pubkey(), 50_000)
|
2020-12-14 17:14:24 +01:00
|
|
|
/// .policy_path(path, KeychainKind::External);
|
2021-01-01 13:35:05 +11:00
|
|
|
///
|
|
|
|
/// # Ok::<(), bdk::Error>(())
|
2020-11-10 15:06:14 +01:00
|
|
|
/// ```
|
|
|
|
pub fn policy_path(
|
2021-01-01 13:35:05 +11:00
|
|
|
&mut self,
|
2020-11-10 15:06:14 +01:00
|
|
|
policy_path: BTreeMap<String, Vec<usize>>,
|
2020-12-14 17:14:24 +01:00
|
|
|
keychain: KeychainKind,
|
2021-01-01 13:35:05 +11:00
|
|
|
) -> &mut Self {
|
2020-12-14 17:14:24 +01:00
|
|
|
let to_update = match keychain {
|
2021-01-01 13:35:05 +11:00
|
|
|
KeychainKind::Internal => &mut self.params().internal_policy_path,
|
|
|
|
KeychainKind::External => &mut self.params().external_policy_path,
|
2020-11-10 15:06:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
*to_update = Some(policy_path);
|
2020-08-06 13:09:39 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// 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
|
|
|
|
/// the "utxos" and the "unspendable" list, it will be spent.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn add_utxo(&mut self, outpoint: OutPoint) -> Result<&mut Self, Error> {
|
|
|
|
if self.params().utxos.get(&outpoint).is_none() {
|
|
|
|
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)
|
2020-10-22 12:07:51 +11:00
|
|
|
}
|
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Only spend utxos added by [`add_utxo`].
|
2020-10-22 12:07:51 +11:00
|
|
|
///
|
|
|
|
/// The wallet will **not** add additional utxos to the transaction even if they are needed to
|
|
|
|
/// make the transaction valid.
|
|
|
|
///
|
|
|
|
/// [`add_utxo`]: Self::add_utxo
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn manually_selected_only(&mut self) -> &mut Self {
|
|
|
|
self.params().manually_selected_only = true;
|
2020-08-06 13:09:39 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Replace the internal list of unspendable utxos with a new list
|
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
|
|
|
|
/// have priority over these. See the docs of the two linked methods for more details.
|
|
|
|
pub fn unspendable(&mut self, unspendable: Vec<OutPoint>) -> &mut Self {
|
|
|
|
self.params().unspendable = unspendable.into_iter().collect();
|
2020-08-06 13:09:39 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Add a utxo to the internal list of unspendable utxos
|
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
|
|
|
|
/// have priority over this. See the docs of the two linked methods for more details.
|
|
|
|
pub fn add_unspendable(&mut self, unspendable: OutPoint) -> &mut Self {
|
|
|
|
self.params().unspendable.insert(unspendable);
|
2020-08-06 13:09:39 +02:00
|
|
|
self
|
|
|
|
}
|
2020-08-06 16:56:41 +02:00
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Sign with a specific sig hash
|
|
|
|
///
|
|
|
|
/// **Use this option very carefully**
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn sighash(&mut self, sighash: SigHashType) -> &mut Self {
|
|
|
|
self.params().sighash = Some(sighash);
|
2020-08-06 16:56:41 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Choose the ordering for inputs and outputs of the transaction
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn ordering(&mut self, ordering: TxOrdering) -> &mut Self {
|
|
|
|
self.params().ordering = ordering;
|
2020-08-07 15:35:14 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Use a specific nLockTime while creating the transaction
|
|
|
|
///
|
|
|
|
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn nlocktime(&mut self, locktime: u32) -> &mut Self {
|
|
|
|
self.params().locktime = Some(locktime);
|
2020-08-06 16:56:41 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Build a transaction with a specific version
|
|
|
|
///
|
|
|
|
/// The `version` should always be greater than `0` and greater than `1` if the wallet's
|
|
|
|
/// descriptors contain an "older" (OP_CSV) operator.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn version(&mut self, version: i32) -> &mut Self {
|
|
|
|
self.params().version = Some(Version(version));
|
2020-08-07 16:30:19 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Do not spend change outputs
|
|
|
|
///
|
|
|
|
/// This effectively adds all the change outputs to the "unspendable" list. See
|
|
|
|
/// [`TxBuilder::unspendable`].
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn do_not_spend_change(&mut self) -> &mut Self {
|
|
|
|
self.params().change_policy = ChangeSpendPolicy::ChangeForbidden;
|
2020-08-07 19:40:13 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Only spend change outputs
|
|
|
|
///
|
|
|
|
/// This effectively adds all the non-change outputs to the "unspendable" list. See
|
|
|
|
/// [`TxBuilder::unspendable`].
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn only_spend_change(&mut self) -> &mut Self {
|
|
|
|
self.params().change_policy = ChangeSpendPolicy::OnlyChange;
|
2020-08-07 19:40:13 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Set a specific [`ChangeSpendPolicy`]. See [`TxBuilder::do_not_spend_change`] and
|
|
|
|
/// [`TxBuilder::only_spend_change`] for some shortcuts.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn change_policy(&mut self, change_policy: ChangeSpendPolicy) -> &mut Self {
|
|
|
|
self.params().change_policy = change_policy;
|
2020-08-08 12:06:40 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Fill-in the [`psbt::Input::non_witness_utxo`](bitcoin::util::psbt::Input::non_witness_utxo) field even if the wallet only has SegWit
|
|
|
|
/// descriptors.
|
|
|
|
///
|
|
|
|
/// This is useful for signers which always require it, like Trezor hardware wallets.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn force_non_witness_utxo(&mut self) -> &mut Self {
|
|
|
|
self.params().force_non_witness_utxo = true;
|
2020-08-08 12:06:40 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-11-30 15:13:33 +01:00
|
|
|
/// Fill-in the [`psbt::Output::redeem_script`](bitcoin::util::psbt::Output::redeem_script) and
|
|
|
|
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields.
|
|
|
|
///
|
|
|
|
/// This is useful for signers which always require it, like ColdCard hardware wallets.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
|
|
|
|
self.params().include_output_redeem_witness_script = true;
|
2020-11-30 15:13:33 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Fill-in the `PSBT_GLOBAL_XPUB` field with the extended keys contained in both the external
|
|
|
|
/// and internal descriptors
|
|
|
|
///
|
|
|
|
/// This is useful for offline signers that take part to a multisig. Some hardware wallets like
|
|
|
|
/// BitBox and ColdCard are known to require this.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn add_global_xpubs(&mut self) -> &mut Self {
|
|
|
|
self.params().add_global_xpubs = true;
|
2020-11-30 15:13:33 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-12-02 16:54:49 -08:00
|
|
|
/// Spend all the available inputs. This respects filters like [`TxBuilder::unspendable`] and the change policy.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn drain_wallet(&mut self) -> &mut Self {
|
|
|
|
self.params().drain_wallet = true;
|
2020-10-28 10:37:47 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Choose the coin selection algorithm
|
|
|
|
///
|
|
|
|
/// Overrides the [`DefaultCoinSelectionAlgorithm`](super::coin_selection::DefaultCoinSelectionAlgorithm).
|
2021-01-01 13:35:05 +11:00
|
|
|
///
|
|
|
|
/// 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.
|
2020-10-14 15:21:22 +02:00
|
|
|
pub fn coin_selection<P: CoinSelectionAlgorithm<D>>(
|
|
|
|
self,
|
|
|
|
coin_selection: P,
|
2021-01-01 13:35:05 +11:00
|
|
|
) -> TxBuilder<'a, B, D, P, Ctx> {
|
2020-08-06 16:56:41 +02:00
|
|
|
TxBuilder {
|
2021-01-01 13:35:05 +11:00
|
|
|
wallet: self.wallet,
|
|
|
|
params: self.params,
|
|
|
|
coin_selection: Some(coin_selection),
|
2020-10-14 15:21:22 +02:00
|
|
|
phantom: PhantomData,
|
2020-08-06 16:56:41 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-07 15:35:14 +02:00
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Finish the building the transaction.
|
|
|
|
///
|
|
|
|
/// Returns the [`BIP174`] "PSBT" and summary details about the transaction.
|
|
|
|
///
|
|
|
|
/// [`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(),
|
|
|
|
)
|
2020-10-28 10:37:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 13:35:05 +11:00
|
|
|
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, B, D, Cs, CreateTx> {
|
2020-10-28 10:37:47 +01:00
|
|
|
/// Replace the recipients already added with a new list
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self {
|
|
|
|
self.params().recipients = recipients;
|
2020-10-28 10:37:47 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a recipient to the internal list
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn add_recipient(&mut self, script_pubkey: Script, amount: u64) -> &mut Self {
|
|
|
|
self.params().recipients.push((script_pubkey, amount));
|
2020-10-28 10:37:47 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a single recipient that will get all the selected funds minus the fee. No change will
|
|
|
|
/// be created
|
|
|
|
///
|
|
|
|
/// This method overrides any recipient set with [`set_recipients`](Self::set_recipients) or
|
|
|
|
/// [`add_recipient`](Self::add_recipient).
|
|
|
|
///
|
|
|
|
/// 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
|
|
|
|
/// list of manually selected UTXOs by enabling [`manually_selected_only`](Self::manually_selected_only)
|
2021-01-01 13:35:05 +11:00
|
|
|
/// and selecting them with or [`add_utxo`](Self::add_utxo).
|
2020-10-28 10:37:47 +01:00
|
|
|
///
|
|
|
|
/// 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
|
|
|
|
/// single output instead of adding one more for the change.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn set_single_recipient(&mut self, recipient: Script) -> &mut Self {
|
|
|
|
self.params().single_recipient = Some(recipient);
|
|
|
|
self.params().recipients.clear();
|
2020-10-28 10:37:47 +01:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Enable signaling RBF
|
|
|
|
///
|
|
|
|
/// This will use the default nSequence value of `0xFFFFFFFD`.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn enable_rbf(&mut self) -> &mut Self {
|
|
|
|
self.params().rbf = Some(RBFValue::Default);
|
2020-12-07 14:48:17 +01:00
|
|
|
self
|
2020-10-28 10:37:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Enable signaling RBF 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.
|
|
|
|
///
|
|
|
|
/// If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not
|
|
|
|
/// be a valid nSequence to signal RBF.
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn enable_rbf_with_sequence(&mut self, nsequence: u32) -> &mut Self {
|
|
|
|
self.params().rbf = Some(RBFValue::Value(nsequence));
|
2020-10-28 10:37:47 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// methods supported only by bump_fee
|
2021-01-01 13:35:05 +11:00
|
|
|
impl<'a, B, D: BatchDatabase> TxBuilder<'a, B, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
2020-10-28 10:37:47 +01:00
|
|
|
/// Bump the fees of a transaction made with [`set_single_recipient`](Self::set_single_recipient)
|
|
|
|
///
|
2021-01-01 13:35:05 +11:00
|
|
|
/// Unless extra inputs are specified with [`add_utxo`], this flag will make
|
2020-10-28 10:37:47 +01:00
|
|
|
/// `bump_fee` reduce the value of the existing output, or fail if it would be consumed
|
|
|
|
/// entirely given the higher new fee rate.
|
|
|
|
///
|
|
|
|
/// If extra inputs are added and they are not entirely consumed in fees, a change output will not
|
|
|
|
/// be added; the existing output will simply grow in value.
|
|
|
|
///
|
|
|
|
/// Fails if the transaction has more than one outputs.
|
|
|
|
///
|
|
|
|
/// [`add_utxo`]: Self::add_utxo
|
2021-01-01 13:35:05 +11:00
|
|
|
pub fn maintain_single_recipient(&mut self) -> &mut Self {
|
|
|
|
let mut recipients = self.params().recipients.drain(..).collect::<Vec<_>>();
|
|
|
|
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.
|
2020-10-28 10:37:47 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Ordering of the transaction's inputs and outputs
|
2020-08-10 17:16:47 +02:00
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
2020-08-07 15:35:14 +02:00
|
|
|
pub enum TxOrdering {
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Randomized (default)
|
2020-08-07 15:35:14 +02:00
|
|
|
Shuffle,
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Unchanged
|
2020-08-07 15:35:14 +02:00
|
|
|
Untouched,
|
2020-09-04 15:45:11 +02:00
|
|
|
/// BIP69 / Lexicographic
|
2020-08-07 15:35:14 +02:00
|
|
|
BIP69Lexicographic,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TxOrdering {
|
|
|
|
fn default() -> Self {
|
|
|
|
TxOrdering::Shuffle
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TxOrdering {
|
2020-12-11 14:10:11 -08:00
|
|
|
/// Sort transaction inputs and outputs by [`TxOrdering`] variant
|
2020-08-31 10:49:44 +02:00
|
|
|
pub fn sort_tx(&self, tx: &mut Transaction) {
|
2020-08-07 15:35:14 +02:00
|
|
|
match self {
|
|
|
|
TxOrdering::Untouched => {}
|
|
|
|
TxOrdering::Shuffle => {
|
|
|
|
use rand::seq::SliceRandom;
|
|
|
|
#[cfg(test)]
|
|
|
|
use rand::SeedableRng;
|
|
|
|
|
|
|
|
#[cfg(not(test))]
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
#[cfg(test)]
|
|
|
|
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
|
|
|
|
|
|
|
|
tx.output.shuffle(&mut rng);
|
|
|
|
}
|
|
|
|
TxOrdering::BIP69Lexicographic => {
|
|
|
|
tx.input.sort_unstable_by_key(|txin| {
|
|
|
|
(txin.previous_output.txid, txin.previous_output.vout)
|
|
|
|
});
|
|
|
|
tx.output
|
|
|
|
.sort_unstable_by_key(|txout| (txout.value, txout.script_pubkey.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Transaction version
|
|
|
|
///
|
|
|
|
/// Has a default value of `1`
|
2020-08-10 17:16:47 +02:00
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
2020-10-09 12:03:47 +02:00
|
|
|
pub(crate) struct Version(pub(crate) i32);
|
2020-08-07 16:30:19 +02:00
|
|
|
|
|
|
|
impl Default for Version {
|
|
|
|
fn default() -> Self {
|
|
|
|
Version(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-07 14:48:17 +01:00
|
|
|
/// RBF nSequence value
|
|
|
|
///
|
|
|
|
/// Has a default value of `0xFFFFFFFD`
|
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
|
|
|
pub(crate) enum RBFValue {
|
|
|
|
Default,
|
|
|
|
Value(u32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RBFValue {
|
|
|
|
pub(crate) fn get_value(&self) -> u32 {
|
|
|
|
match self {
|
|
|
|
RBFValue::Default => 0xFFFFFFFD,
|
|
|
|
RBFValue::Value(v) => *v,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Policy regarding the use of change outputs when creating a transaction
|
2020-08-10 17:16:47 +02:00
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
2020-08-07 19:40:13 +02:00
|
|
|
pub enum ChangeSpendPolicy {
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Use both change and non-change outputs (default)
|
2020-08-07 19:40:13 +02:00
|
|
|
ChangeAllowed,
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Only use change outputs (see [`TxBuilder::only_spend_change`])
|
2020-08-07 19:40:13 +02:00
|
|
|
OnlyChange,
|
2020-09-04 15:45:11 +02:00
|
|
|
/// Only use non-change outputs (see [`TxBuilder::do_not_spend_change`])
|
2020-08-07 19:40:13 +02:00
|
|
|
ChangeForbidden,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ChangeSpendPolicy {
|
|
|
|
fn default() -> Self {
|
|
|
|
ChangeSpendPolicy::ChangeAllowed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChangeSpendPolicy {
|
2020-10-21 15:53:00 +11:00
|
|
|
pub(crate) fn is_satisfied_by(&self, utxo: &UTXO) -> bool {
|
2020-08-07 19:40:13 +02:00
|
|
|
match self {
|
2020-10-21 15:53:00 +11:00
|
|
|
ChangeSpendPolicy::ChangeAllowed => true,
|
2020-12-14 17:14:24 +01:00
|
|
|
ChangeSpendPolicy::OnlyChange => utxo.keychain == KeychainKind::Internal,
|
|
|
|
ChangeSpendPolicy::ChangeForbidden => utxo.keychain == KeychainKind::External,
|
2020-08-07 19:40:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-07 15:35:14 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
const ORDERING_TEST_TX: &'static str = "0200000003c26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f54\
|
|
|
|
85d1fd600f0100000000ffffffffc26f3eb7932f7acddc5ddd26602b77e75160\
|
|
|
|
79b03090a16e2c2f5485d1fd600f0000000000ffffffff571fb3e02278217852\
|
|
|
|
dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e0500000000ffffffff\
|
|
|
|
03e80300000000000002aaeee80300000000000001aa200300000000000001ff\
|
|
|
|
00000000";
|
|
|
|
macro_rules! ordering_test_tx {
|
|
|
|
() => {
|
|
|
|
deserialize::<bitcoin::Transaction>(&Vec::<u8>::from_hex(ORDERING_TEST_TX).unwrap())
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
use bitcoin::consensus::deserialize;
|
|
|
|
use bitcoin::hashes::hex::FromHex;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2020-08-10 17:16:47 +02:00
|
|
|
#[test]
|
|
|
|
fn test_output_ordering_default_shuffle() {
|
|
|
|
assert_eq!(TxOrdering::default(), TxOrdering::Shuffle);
|
|
|
|
}
|
|
|
|
|
2020-08-07 15:35:14 +02:00
|
|
|
#[test]
|
|
|
|
fn test_output_ordering_untouched() {
|
|
|
|
let original_tx = ordering_test_tx!();
|
|
|
|
let mut tx = original_tx.clone();
|
|
|
|
|
2020-08-31 10:49:44 +02:00
|
|
|
TxOrdering::Untouched.sort_tx(&mut tx);
|
2020-08-07 15:35:14 +02:00
|
|
|
|
|
|
|
assert_eq!(original_tx, tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_output_ordering_shuffle() {
|
|
|
|
let original_tx = ordering_test_tx!();
|
|
|
|
let mut tx = original_tx.clone();
|
|
|
|
|
2020-08-31 10:49:44 +02:00
|
|
|
TxOrdering::Shuffle.sort_tx(&mut tx);
|
2020-08-07 15:35:14 +02:00
|
|
|
|
|
|
|
assert_eq!(original_tx.input, tx.input);
|
|
|
|
assert_ne!(original_tx.output, tx.output);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_output_ordering_bip69() {
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
let original_tx = ordering_test_tx!();
|
|
|
|
let mut tx = original_tx.clone();
|
|
|
|
|
2020-08-31 10:49:44 +02:00
|
|
|
TxOrdering::BIP69Lexicographic.sort_tx(&mut tx);
|
2020-08-07 15:35:14 +02:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
tx.input[0].previous_output,
|
|
|
|
bitcoin::OutPoint::from_str(
|
|
|
|
"0e53ec5dfb2cb8a71fec32dc9a634a35b7e24799295ddd5278217822e0b31f57:5"
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
tx.input[1].previous_output,
|
|
|
|
bitcoin::OutPoint::from_str(
|
|
|
|
"0f60fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2:0"
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
tx.input[2].previous_output,
|
|
|
|
bitcoin::OutPoint::from_str(
|
|
|
|
"0f60fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2:1"
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(tx.output[0].value, 800);
|
|
|
|
assert_eq!(tx.output[1].script_pubkey, From::from(vec![0xAA]));
|
|
|
|
assert_eq!(tx.output[2].script_pubkey, From::from(vec![0xAA, 0xEE]));
|
|
|
|
}
|
2020-08-10 17:16:47 +02:00
|
|
|
|
|
|
|
fn get_test_utxos() -> Vec<UTXO> {
|
|
|
|
vec![
|
|
|
|
UTXO {
|
|
|
|
outpoint: OutPoint {
|
|
|
|
txid: Default::default(),
|
|
|
|
vout: 0,
|
|
|
|
},
|
|
|
|
txout: Default::default(),
|
2020-12-14 17:14:24 +01:00
|
|
|
keychain: KeychainKind::External,
|
2020-08-10 17:16:47 +02:00
|
|
|
},
|
|
|
|
UTXO {
|
|
|
|
outpoint: OutPoint {
|
|
|
|
txid: Default::default(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
txout: Default::default(),
|
2020-12-14 17:14:24 +01:00
|
|
|
keychain: KeychainKind::Internal,
|
2020-08-10 17:16:47 +02:00
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_change_spend_policy_default() {
|
|
|
|
let change_spend_policy = ChangeSpendPolicy::default();
|
2020-10-21 15:53:00 +11:00
|
|
|
let filtered = get_test_utxos()
|
|
|
|
.into_iter()
|
|
|
|
.filter(|u| change_spend_policy.is_satisfied_by(u))
|
|
|
|
.collect::<Vec<_>>();
|
2020-08-10 17:16:47 +02:00
|
|
|
|
|
|
|
assert_eq!(filtered.len(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_change_spend_policy_no_internal() {
|
|
|
|
let change_spend_policy = ChangeSpendPolicy::ChangeForbidden;
|
2020-10-21 15:53:00 +11:00
|
|
|
let filtered = get_test_utxos()
|
|
|
|
.into_iter()
|
|
|
|
.filter(|u| change_spend_policy.is_satisfied_by(u))
|
|
|
|
.collect::<Vec<_>>();
|
2020-08-10 17:16:47 +02:00
|
|
|
|
|
|
|
assert_eq!(filtered.len(), 1);
|
2020-12-14 17:14:24 +01:00
|
|
|
assert_eq!(filtered[0].keychain, KeychainKind::External);
|
2020-08-10 17:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_change_spend_policy_only_internal() {
|
|
|
|
let change_spend_policy = ChangeSpendPolicy::OnlyChange;
|
2020-10-21 15:53:00 +11:00
|
|
|
let filtered = get_test_utxos()
|
|
|
|
.into_iter()
|
|
|
|
.filter(|u| change_spend_policy.is_satisfied_by(u))
|
|
|
|
.collect::<Vec<_>>();
|
2020-08-10 17:16:47 +02:00
|
|
|
|
|
|
|
assert_eq!(filtered.len(), 1);
|
2020-12-14 17:14:24 +01:00
|
|
|
assert_eq!(filtered[0].keychain, KeychainKind::Internal);
|
2020-08-10 17:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_default_tx_version_1() {
|
|
|
|
let version = Version::default();
|
|
|
|
assert_eq!(version.0, 1);
|
|
|
|
}
|
2020-08-07 15:35:14 +02:00
|
|
|
}
|