2021-03-03 13:22:05 -08:00
// Bitcoin Dev Kit
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
2020-08-31 11:26:36 +02:00
//
2021-03-03 13:22:05 -08:00
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
2020-08-31 11:26:36 +02:00
//
2021-03-03 13:22:05 -08:00
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
2020-08-31 11:26:36 +02:00
2020-09-04 16:29:25 +02:00
//! Wallet
//!
//! This module defines the [`Wallet`] structure.
2020-02-07 23:22:28 +01:00
use std ::cell ::RefCell ;
2020-08-08 12:06:40 +02:00
use std ::collections ::HashMap ;
2020-05-03 16:15:11 +02:00
use std ::collections ::{ BTreeMap , HashSet } ;
2021-05-17 16:31:18 -04:00
use std ::fmt ;
2020-08-13 16:51:27 +02:00
use std ::ops ::{ Deref , DerefMut } ;
2021-07-29 11:11:16 +02:00
use std ::str ::FromStr ;
2020-08-12 12:51:50 +02:00
use std ::sync ::Arc ;
2020-02-07 23:22:28 +01:00
2020-11-16 22:07:38 +01:00
use bitcoin ::secp256k1 ::Secp256k1 ;
2020-02-07 23:22:28 +01:00
use bitcoin ::consensus ::encode ::serialize ;
2022-04-28 15:39:31 +02:00
use bitcoin ::util ::{ psbt , taproot } ;
2022-04-26 15:11:22 +02:00
use bitcoin ::{
2022-04-29 12:59:09 +02:00
Address , EcdsaSighashType , Network , OutPoint , SchnorrSighashType , Script , Transaction , TxOut ,
Txid , Witness ,
2022-04-26 15:11:22 +02:00
} ;
2020-02-07 23:22:28 +01:00
2021-02-02 20:06:40 -05:00
use miniscript ::descriptor ::DescriptorTrait ;
2020-10-09 12:03:47 +02:00
use miniscript ::psbt ::PsbtInputSatisfier ;
2022-04-28 15:39:31 +02:00
use miniscript ::ToPublicKey ;
2020-02-07 23:22:28 +01:00
#[ allow(unused_imports) ]
use log ::{ debug , error , info , trace } ;
2020-08-15 23:21:13 +02:00
pub mod address_validator ;
2020-08-06 16:56:41 +02:00
pub mod coin_selection ;
2020-08-07 10:19:06 +02:00
pub mod export ;
2020-08-12 12:51:50 +02:00
pub mod signer ;
2020-08-06 11:12:15 +02:00
pub mod time ;
2020-08-06 13:09:39 +02:00
pub mod tx_builder ;
2020-08-31 10:49:44 +02:00
pub ( crate ) mod utils ;
2021-05-27 16:58:42 +02:00
#[ cfg(feature = " verify " ) ]
#[ cfg_attr(docsrs, doc(cfg(feature = " verify " ))) ]
pub mod verify ;
2020-08-31 10:49:44 +02:00
pub use utils ::IsDust ;
2020-02-07 23:22:28 +01:00
2022-07-11 16:37:41 +08:00
#[ allow(deprecated) ]
2020-08-15 23:21:13 +02:00
use address_validator ::AddressValidator ;
2021-01-01 13:35:05 +11:00
use coin_selection ::DefaultCoinSelectionAlgorithm ;
2022-04-26 16:54:10 +02:00
use signer ::{ SignOptions , SignerOrdering , SignersContainer , TransactionSigner } ;
2021-01-01 13:35:05 +11:00
use tx_builder ::{ BumpFee , CreateTx , FeePolicy , TxBuilder , TxParams } ;
2021-12-19 02:55:24 -05:00
use utils ::{ check_nlocktime , check_nsequence_rbf , After , Older , SecpCtx } ;
2020-08-06 11:12:15 +02:00
2022-01-27 16:52:53 +11:00
use crate ::blockchain ::{ GetHeight , NoopProgress , Progress , WalletSync } ;
2021-07-29 11:11:16 +02:00
use crate ::database ::memory ::MemoryDatabase ;
2022-05-05 16:42:56 -07:00
use crate ::database ::{ AnyDatabase , BatchDatabase , BatchOperations , DatabaseUtils , SyncTime } ;
2021-02-02 20:06:40 -05:00
use crate ::descriptor ::derived ::AsDerived ;
2021-04-02 16:39:18 +02:00
use crate ::descriptor ::policy ::BuildSatisfaction ;
2020-08-12 12:51:50 +02:00
use crate ::descriptor ::{
2021-02-15 11:33:47 -05:00
get_checksum , into_wallet_descriptor_checked , DerivedDescriptor , DerivedDescriptorMeta ,
DescriptorMeta , DescriptorScripts , ExtendedDescriptor , ExtractPolicy , IntoWalletDescriptor ,
Policy , XKeyUtils ,
2020-08-12 12:51:50 +02:00
} ;
2020-02-07 23:22:28 +01:00
use crate ::error ::Error ;
2021-03-30 16:33:07 +02:00
use crate ::psbt ::PsbtUtils ;
2021-05-06 15:55:58 +02:00
use crate ::signer ::SignerError ;
2021-07-29 11:11:16 +02:00
use crate ::testutils ;
2020-02-07 23:22:28 +01:00
use crate ::types ::* ;
2020-08-06 18:11:07 +02:00
const CACHE_ADDR_BATCH_SIZE : u32 = 100 ;
2022-05-25 18:56:50 +01:00
const COINBASE_MATURITY : u32 = 100 ;
2020-08-06 18:11:07 +02:00
2020-09-04 16:29:25 +02:00
/// A Bitcoin wallet
///
2022-01-26 15:17:48 +11:00
/// The `Wallet` struct acts as a way of coherently interfacing with output descriptors and related transactions.
/// Its main components are:
2020-09-04 16:29:25 +02:00
///
2022-01-26 15:17:48 +11:00
/// 1. output *descriptors* from which it can derive addresses.
/// 2. A [`Database`] where it tracks transactions and utxos related to the descriptors.
2022-04-26 16:54:10 +02:00
/// 3. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
2022-01-26 15:17:48 +11:00
///
/// [`Database`]: crate::database::Database
2022-04-26 16:54:10 +02:00
/// [`signer`]: crate::signer
2021-01-22 14:11:29 +11:00
#[ derive(Debug) ]
2022-01-26 15:17:48 +11:00
pub struct Wallet < D > {
2020-02-07 23:22:28 +01:00
descriptor : ExtendedDescriptor ,
change_descriptor : Option < ExtendedDescriptor > ,
2020-08-12 12:51:50 +02:00
2020-10-09 12:03:47 +02:00
signers : Arc < SignersContainer > ,
change_signers : Arc < SignersContainer > ,
2020-08-12 12:51:50 +02:00
2022-07-11 16:37:41 +08:00
#[ allow(deprecated) ]
2020-11-03 16:03:04 +11:00
address_validators : Vec < Arc < dyn AddressValidator > > ,
2020-08-15 23:21:13 +02:00
2020-02-07 23:22:28 +01:00
network : Network ,
2020-02-17 14:22:53 +01:00
database : RefCell < D > ,
2020-11-16 22:07:38 +01:00
secp : SecpCtx ,
2020-02-07 23:22:28 +01:00
}
2021-03-08 16:17:10 -08:00
/// The address index selection strategy to use to derived an address from the wallet's external
2021-03-10 15:58:58 -08:00
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
2021-03-08 16:17:10 -08:00
#[ derive(Debug) ]
pub enum AddressIndex {
/// Return a new address after incrementing the current descriptor index.
New ,
/// Return the address for the current descriptor index if it has not been used in a received
/// transaction. Otherwise return a new address as with [`AddressIndex::New`].
///
/// Use with caution, if the wallet has not yet detected an address has been used it could
/// return an already used address. This function is primarily meant for situations where the
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
/// web page.
LastUnused ,
2021-03-10 15:58:58 -08:00
/// Return the address for a specific descriptor index. Does not change the current descriptor
/// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
///
/// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address may have already been used.
Peek ( u32 ) ,
/// Return the address for a specific descriptor index and reset the current descriptor index
/// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value.
///
/// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
/// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a
/// value earlier than the [`crate::blockchain::Blockchain`] stop_gap (default is 20) then a
2021-04-08 12:16:58 -07:00
/// larger stop_gap should be used to monitor for all possibly used addresses.
2021-03-10 15:58:58 -08:00
Reset ( u32 ) ,
2021-03-08 16:17:10 -08:00
}
2021-05-17 16:31:18 -04:00
/// A derived address and the index it was found at
/// For convenience this automatically derefs to `Address`
#[ derive(Debug, PartialEq) ]
pub struct AddressInfo {
/// Child index of this address
pub index : u32 ,
/// Address
pub address : Address ,
2022-03-10 06:22:02 +05:30
/// Type of keychain
pub keychain : KeychainKind ,
2021-05-17 16:31:18 -04:00
}
impl Deref for AddressInfo {
type Target = Address ;
fn deref ( & self ) -> & Self ::Target {
& self . address
}
}
impl fmt ::Display for AddressInfo {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
write! ( f , " {} " , self . address )
}
}
2022-01-27 16:52:53 +11:00
#[ derive(Debug, Default) ]
/// Options to a [`sync`].
///
/// [`sync`]: Wallet::sync
pub struct SyncOptions {
/// The progress tracker which may be informed when progress is made.
pub progress : Option < Box < dyn Progress > > ,
}
2022-01-26 15:17:48 +11:00
impl < D > Wallet < D >
2020-12-23 13:48:17 +11:00
where
D : BatchDatabase ,
{
2022-02-23 09:52:18 +11:00
#[ deprecated = " Just use Wallet::new -- all wallets are offline now! " ]
/// Create a new "offline" wallet
pub fn new_offline < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
database : D ,
) -> Result < Self , Error > {
Self ::new ( descriptor , change_descriptor , network , database )
}
2022-01-26 15:17:48 +11:00
/// Create a wallet.
///
/// The only way this can fail is if the descriptors passed in do not match the checksums in `database`.
pub fn new < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
mut database : D ,
) -> Result < Self , Error > {
let secp = Secp256k1 ::new ( ) ;
let ( descriptor , keymap ) = into_wallet_descriptor_checked ( descriptor , & secp , network ) ? ;
database . check_descriptor_checksum (
KeychainKind ::External ,
get_checksum ( & descriptor . to_string ( ) ) ? . as_bytes ( ) ,
) ? ;
2022-05-12 17:28:41 +02:00
let signers = Arc ::new ( SignersContainer ::build ( keymap , & descriptor , & secp ) ) ;
2022-01-26 15:17:48 +11:00
let ( change_descriptor , change_signers ) = match change_descriptor {
Some ( desc ) = > {
let ( change_descriptor , change_keymap ) =
into_wallet_descriptor_checked ( desc , & secp , network ) ? ;
database . check_descriptor_checksum (
KeychainKind ::Internal ,
get_checksum ( & change_descriptor . to_string ( ) ) ? . as_bytes ( ) ,
) ? ;
2022-05-12 17:28:41 +02:00
let change_signers = Arc ::new ( SignersContainer ::build (
change_keymap ,
& change_descriptor ,
& secp ,
) ) ;
2022-01-26 15:17:48 +11:00
// if !parsed.same_structure(descriptor.as_ref()) {
// return Err(Error::DifferentDescriptorStructure);
// }
( Some ( change_descriptor ) , change_signers )
}
None = > ( None , Arc ::new ( SignersContainer ::new ( ) ) ) ,
} ;
Ok ( Wallet {
descriptor ,
change_descriptor ,
signers ,
change_signers ,
address_validators : Vec ::new ( ) ,
network ,
database : RefCell ::new ( database ) ,
secp ,
} )
}
/// Get the Bitcoin network the wallet is using.
pub fn network ( & self ) -> Network {
self . network
}
2022-03-08 20:02:47 -06:00
// Return a newly derived address for the specified `keychain`.
2021-08-19 19:57:35 +10:00
fn get_new_address ( & self , keychain : KeychainKind ) -> Result < AddressInfo , Error > {
let incremented_index = self . fetch_and_increment_index ( keychain ) ? ;
2021-03-07 21:57:19 -08:00
2021-05-17 16:31:18 -04:00
let address_result = self
2021-08-19 19:57:35 +10:00
. get_descriptor_for_keychain ( keychain )
2021-03-08 16:17:10 -08:00
. as_derived ( incremented_index , & self . secp )
2021-05-17 16:31:18 -04:00
. address ( self . network ) ;
address_result
2021-05-18 11:09:11 -04:00
. map ( | address | AddressInfo {
address ,
index : incremented_index ,
2022-03-10 06:22:02 +05:30
keychain ,
2021-05-18 11:09:11 -04:00
} )
2021-03-08 16:17:10 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
2022-01-13 15:31:52 +11:00
// Return the the last previously derived address for `keychain` if it has not been used in a
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
2021-08-19 19:57:35 +10:00
fn get_unused_address ( & self , keychain : KeychainKind ) -> Result < AddressInfo , Error > {
let current_index = self . fetch_index ( keychain ) ? ;
2021-03-08 16:17:10 -08:00
2021-08-19 19:57:35 +10:00
let derived_key = self
. get_descriptor_for_keychain ( keychain )
. as_derived ( current_index , & self . secp ) ;
2021-03-08 16:17:10 -08:00
let script_pubkey = derived_key . script_pubkey ( ) ;
2021-03-07 21:57:19 -08:00
let found_used = self
. list_transactions ( true ) ?
. iter ( )
. flat_map ( | tx_details | tx_details . transaction . as_ref ( ) )
. flat_map ( | tx | tx . output . iter ( ) )
2021-03-08 16:17:10 -08:00
. any ( | o | o . script_pubkey = = script_pubkey ) ;
2021-03-07 21:57:19 -08:00
if found_used {
2021-08-19 19:57:35 +10:00
self . get_new_address ( keychain )
2021-03-07 21:57:19 -08:00
} else {
2021-03-08 16:17:10 -08:00
derived_key
2021-03-07 21:57:19 -08:00
. address ( self . network )
2021-05-18 11:09:11 -04:00
. map ( | address | AddressInfo {
address ,
index : current_index ,
2022-03-10 06:22:02 +05:30
keychain ,
2021-05-18 11:09:11 -04:00
} )
2021-03-07 21:57:19 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
}
2022-01-13 15:31:52 +11:00
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
2021-08-19 19:57:35 +10:00
fn peek_address ( & self , index : u32 , keychain : KeychainKind ) -> Result < AddressInfo , Error > {
self . get_descriptor_for_keychain ( keychain )
2021-03-10 15:58:58 -08:00
. as_derived ( index , & self . secp )
. address ( self . network )
2022-03-10 06:22:02 +05:30
. map ( | address | AddressInfo {
index ,
address ,
keychain ,
} )
2021-03-10 15:58:58 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
2022-01-13 15:31:52 +11:00
// Return derived address for `keychain` at a specific index and reset current
2021-03-10 15:58:58 -08:00
// address index
2021-08-19 19:57:35 +10:00
fn reset_address ( & self , index : u32 , keychain : KeychainKind ) -> Result < AddressInfo , Error > {
self . set_index ( keychain , index ) ? ;
2021-03-10 15:58:58 -08:00
2021-08-19 19:57:35 +10:00
self . get_descriptor_for_keychain ( keychain )
2021-03-10 15:58:58 -08:00
. as_derived ( index , & self . secp )
. address ( self . network )
2022-03-10 06:22:02 +05:30
. map ( | address | AddressInfo {
index ,
address ,
keychain ,
} )
2021-03-10 15:58:58 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
2021-03-08 16:17:10 -08:00
/// Return a derived address using the external descriptor, see [`AddressIndex`] for
2021-03-10 15:58:58 -08:00
/// available address index selection strategies. If none of the keys in the descriptor are derivable
2021-03-08 16:17:10 -08:00
/// (ie. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
2021-05-17 16:31:18 -04:00
pub fn get_address ( & self , address_index : AddressIndex ) -> Result < AddressInfo , Error > {
2021-08-19 19:57:35 +10:00
self . _get_address ( address_index , KeychainKind ::External )
}
/// Return a derived address using the internal (change) descriptor.
///
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
///
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
/// in the descriptor are derivable (ie. does not end with /*) then the same address will always
/// be returned for any [`AddressIndex`].
pub fn get_internal_address ( & self , address_index : AddressIndex ) -> Result < AddressInfo , Error > {
self . _get_address ( address_index , KeychainKind ::Internal )
}
fn _get_address (
& self ,
address_index : AddressIndex ,
keychain : KeychainKind ,
) -> Result < AddressInfo , Error > {
2021-05-18 11:09:11 -04:00
match address_index {
2021-08-19 19:57:35 +10:00
AddressIndex ::New = > self . get_new_address ( keychain ) ,
AddressIndex ::LastUnused = > self . get_unused_address ( keychain ) ,
AddressIndex ::Peek ( index ) = > self . peek_address ( index , keychain ) ,
AddressIndex ::Reset ( index ) = > self . reset_address ( index , keychain ) ,
2021-05-18 11:09:11 -04:00
}
2021-03-08 16:17:10 -08:00
}
2022-02-02 08:27:53 -05:00
/// Ensures that there are at least `max_addresses` addresses cached in the database if the
/// descriptor is derivable, or 1 address if it is not.
/// Will return `Ok(true)` if there are new addresses generated (either external or internal),
/// and `Ok(false)` if all the required addresses are already cached. This function is useful to
/// explicitly cache addresses in a wallet to do things like check [`Wallet::is_mine`] on
/// transaction output scripts.
pub fn ensure_addresses_cached ( & self , max_addresses : u32 ) -> Result < bool , Error > {
let mut new_addresses_cached = false ;
let max_address = match self . descriptor . is_deriveable ( ) {
false = > 0 ,
true = > max_addresses ,
} ;
debug! ( " max_address {} " , max_address ) ;
if self
. database
. borrow ( )
. get_script_pubkey_from_path ( KeychainKind ::External , max_address . saturating_sub ( 1 ) ) ?
. is_none ( )
{
debug! ( " caching external addresses " ) ;
new_addresses_cached = true ;
self . cache_addresses ( KeychainKind ::External , 0 , max_address ) ? ;
2021-05-18 11:09:11 -04:00
}
2022-02-02 08:27:53 -05:00
if let Some ( change_descriptor ) = & self . change_descriptor {
let max_address = match change_descriptor . is_deriveable ( ) {
false = > 0 ,
true = > max_addresses ,
} ;
if self
. database
. borrow ( )
. get_script_pubkey_from_path ( KeychainKind ::Internal , max_address . saturating_sub ( 1 ) ) ?
. is_none ( )
{
debug! ( " caching internal addresses " ) ;
new_addresses_cached = true ;
self . cache_addresses ( KeychainKind ::Internal , 0 , max_address ) ? ;
}
}
Ok ( new_addresses_cached )
2021-03-08 16:17:10 -08:00
}
2020-09-04 16:29:25 +02:00
/// Return whether or not a `script` is part of this wallet (either internal or external)
2020-02-07 23:22:28 +01:00
pub fn is_mine ( & self , script : & Script ) -> Result < bool , Error > {
2020-05-03 16:15:11 +02:00
self . database . borrow ( ) . is_mine ( script )
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Return the list of unspent outputs of this wallet
///
2021-09-23 16:12:53 +05:30
/// Note that this method only operates on the internal database, which first needs to be
2020-09-04 16:29:25 +02:00
/// [`Wallet::sync`] manually.
2021-02-04 12:09:53 +11:00
pub fn list_unspent ( & self ) -> Result < Vec < LocalUtxo > , Error > {
2022-03-09 16:15:34 +01:00
Ok ( self
. database
. borrow ( )
. iter_utxos ( ) ?
. into_iter ( )
. filter ( | l | ! l . is_spent )
. collect ( ) )
2020-02-07 23:22:28 +01:00
}
2021-01-01 13:35:05 +11:00
/// Returns the `UTXO` owned by this wallet corresponding to `outpoint` if it exists in the
/// wallet's database.
2021-02-04 12:09:53 +11:00
pub fn get_utxo ( & self , outpoint : OutPoint ) -> Result < Option < LocalUtxo > , Error > {
2021-01-01 13:35:05 +11:00
self . database . borrow ( ) . get_utxo ( & outpoint )
}
2021-09-23 16:12:53 +05:30
/// Return a single transactions made and received by the wallet
///
/// Optionally fill the [`TransactionDetails::transaction`] field with the raw transaction if
/// `include_raw` is `true`.
///
/// Note that this method only operates on the internal database, which first needs to be
/// [`Wallet::sync`] manually.
pub fn get_tx (
& self ,
txid : & Txid ,
include_raw : bool ,
) -> Result < Option < TransactionDetails > , Error > {
self . database . borrow ( ) . get_tx ( txid , include_raw )
}
2020-09-04 16:29:25 +02:00
/// Return the list of transactions made and received by the wallet
///
/// Optionally fill the [`TransactionDetails::transaction`] field with the raw transaction if
/// `include_raw` is `true`.
///
/// Note that this methods only operate on the internal database, which first needs to be
/// [`Wallet::sync`] manually.
2020-02-07 23:22:28 +01:00
pub fn list_transactions ( & self , include_raw : bool ) -> Result < Vec < TransactionDetails > , Error > {
self . database . borrow ( ) . iter_txs ( include_raw )
}
2020-09-04 16:29:25 +02:00
/// Return the balance, meaning the sum of this wallet's unspent outputs' values
///
/// Note that this methods only operate on the internal database, which first needs to be
/// [`Wallet::sync`] manually.
2020-02-07 23:22:28 +01:00
pub fn get_balance ( & self ) -> Result < u64 , Error > {
Ok ( self
. list_unspent ( ) ?
. iter ( )
. fold ( 0 , | sum , i | sum + i . txout . value ) )
}
2020-09-04 16:29:25 +02:00
/// Add an external signer
///
/// See [the `signer` module](signer) for an example.
2020-08-15 23:21:13 +02:00
pub fn add_signer (
& mut self ,
2020-12-14 17:14:24 +01:00
keychain : KeychainKind ,
2020-08-17 12:10:51 +02:00
ordering : SignerOrdering ,
2022-04-26 16:54:10 +02:00
signer : Arc < dyn TransactionSigner > ,
2020-08-15 23:21:13 +02:00
) {
2020-12-14 17:14:24 +01:00
let signers = match keychain {
KeychainKind ::External = > Arc ::make_mut ( & mut self . signers ) ,
KeychainKind ::Internal = > Arc ::make_mut ( & mut self . change_signers ) ,
2020-08-15 23:21:13 +02:00
} ;
2021-01-25 15:04:56 -05:00
signers . add_external ( signer . id ( & self . secp ) , ordering , signer ) ;
2020-08-15 23:21:13 +02:00
}
2022-03-09 18:38:11 +01:00
/// Get the signers
///
/// ## Example
///
/// ```
/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::bitcoin::Network;
/// # use bdk::database::MemoryDatabase;
/// let wallet = Wallet::new("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet, MemoryDatabase::new())?;
/// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
/// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
/// println!("secret_key: {}", secret_key);
/// }
///
/// Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn get_signers ( & self , keychain : KeychainKind ) -> Arc < SignersContainer > {
match keychain {
KeychainKind ::External = > Arc ::clone ( & self . signers ) ,
KeychainKind ::Internal = > Arc ::clone ( & self . change_signers ) ,
}
}
2020-09-04 16:29:25 +02:00
/// Add an address validator
///
/// See [the `address_validator` module](address_validator) for an example.
2022-07-11 16:37:41 +08:00
#[ deprecated ]
#[ allow(deprecated) ]
2020-11-03 16:03:04 +11:00
pub fn add_address_validator ( & mut self , validator : Arc < dyn AddressValidator > ) {
2020-08-15 23:21:13 +02:00
self . address_validators . push ( validator ) ;
}
2022-03-09 18:38:11 +01:00
/// Get the address validators
2022-07-11 16:37:41 +08:00
///
/// See [the `address_validator` module](address_validator).
#[ deprecated ]
#[ allow(deprecated) ]
2022-03-09 18:38:11 +01:00
pub fn get_address_validators ( & self ) -> & [ Arc < dyn AddressValidator > ] {
& self . address_validators
}
2021-01-01 13:35:05 +11:00
/// Start building a transaction.
///
/// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
2020-09-04 16:29:25 +02:00
///
/// ## Example
///
2021-01-01 14:15:24 +11:00
/// ```
2020-09-04 16:29:25 +02:00
/// # use std::str::FromStr;
/// # use bitcoin::*;
2020-09-14 14:25:38 +02:00
/// # use bdk::*;
/// # use bdk::database::*;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2021-01-01 14:15:24 +11:00
/// # let wallet = doctest_wallet!();
2020-09-04 16:29:25 +02:00
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
2021-01-11 14:14:14 +11:00
/// let (psbt, details) = {
/// let mut builder = wallet.build_tx();
/// builder
/// .add_recipient(to_address.script_pubkey(), 50_000);
/// builder.finish()?
/// };
2021-01-01 13:35:05 +11:00
///
2020-09-04 16:29:25 +02:00
/// // sign and broadcast ...
2020-09-14 14:25:38 +02:00
/// # Ok::<(), bdk::Error>(())
2020-09-04 16:29:25 +02:00
/// ```
2021-01-01 13:35:05 +11:00
///
/// [`TxBuilder`]: crate::TxBuilder
2022-01-26 15:17:48 +11:00
pub fn build_tx ( & self ) -> TxBuilder < '_ , D , DefaultCoinSelectionAlgorithm , CreateTx > {
2021-01-01 13:35:05 +11:00
TxBuilder {
2021-06-08 13:57:55 +10:00
wallet : self ,
2021-01-11 14:14:14 +11:00
params : TxParams ::default ( ) ,
coin_selection : DefaultCoinSelectionAlgorithm ::default ( ) ,
2021-01-01 13:35:05 +11:00
phantom : core ::marker ::PhantomData ,
}
}
pub ( crate ) fn create_tx < Cs : coin_selection ::CoinSelectionAlgorithm < D > > (
2020-08-06 16:56:41 +02:00
& self ,
2021-01-01 13:35:05 +11:00
coin_selection : Cs ,
params : TxParams ,
2022-04-14 17:20:46 +02:00
) -> Result < ( psbt ::PartiallySignedTransaction , TransactionDetails ) , Error > {
2020-11-10 15:06:14 +01:00
let external_policy = self
2020-08-12 12:51:50 +02:00
. descriptor
2021-04-02 16:39:18 +02:00
. extract_policy ( & self . signers , BuildSatisfaction ::None , & self . secp ) ?
2020-08-12 12:51:50 +02:00
. unwrap ( ) ;
2020-11-10 15:06:14 +01:00
let internal_policy = self
. change_descriptor
. as_ref ( )
. map ( | desc | {
Ok ::< _ , Error > (
2021-04-02 16:39:18 +02:00
desc . extract_policy ( & self . change_signers , BuildSatisfaction ::None , & self . secp ) ?
2020-11-10 15:06:14 +01:00
. unwrap ( ) ,
)
} )
. transpose ( ) ? ;
// The policy allows spending external outputs, but it requires a policy path that hasn't been
// provided
2021-01-01 13:35:05 +11:00
if params . change_policy ! = tx_builder ::ChangeSpendPolicy ::OnlyChange
2020-11-10 15:06:14 +01:00
& & external_policy . requires_path ( )
2021-01-01 13:35:05 +11:00
& & params . external_policy_path . is_none ( )
2020-11-10 15:06:14 +01:00
{
2020-12-14 17:14:24 +01:00
return Err ( Error ::SpendingPolicyRequired ( KeychainKind ::External ) ) ;
2020-11-10 15:06:14 +01:00
} ;
// Same for the internal_policy path, if present
if let Some ( internal_policy ) = & internal_policy {
2021-01-01 13:35:05 +11:00
if params . change_policy ! = tx_builder ::ChangeSpendPolicy ::ChangeForbidden
2020-11-10 15:06:14 +01:00
& & internal_policy . requires_path ( )
2021-01-01 13:35:05 +11:00
& & params . internal_policy_path . is_none ( )
2020-11-10 15:06:14 +01:00
{
2020-12-14 17:14:24 +01:00
return Err ( Error ::SpendingPolicyRequired ( KeychainKind ::Internal ) ) ;
2020-11-10 15:06:14 +01:00
} ;
2020-02-07 23:22:28 +01:00
}
2020-11-10 15:06:14 +01:00
let external_requirements = external_policy . get_condition (
2021-01-01 13:35:05 +11:00
params
2020-11-10 15:06:14 +01:00
. external_policy_path
. as_ref ( )
. unwrap_or ( & BTreeMap ::new ( ) ) ,
) ? ;
let internal_requirements = internal_policy
. map ( | policy | {
Ok ::< _ , Error > (
policy . get_condition (
2021-01-01 13:35:05 +11:00
params
2020-11-10 15:06:14 +01:00
. internal_policy_path
. as_ref ( )
. unwrap_or ( & BTreeMap ::new ( ) ) ,
) ? ,
)
} )
. transpose ( ) ? ;
2021-05-06 14:29:51 +10:00
let requirements =
external_requirements . merge ( & internal_requirements . unwrap_or_default ( ) ) ? ;
2020-11-10 15:06:14 +01:00
debug! ( " Policy requirements: {:?} " , requirements ) ;
2020-02-07 23:22:28 +01:00
2021-01-01 13:35:05 +11:00
let version = match params . version {
2020-08-10 17:16:47 +02:00
Some ( tx_builder ::Version ( 0 ) ) = > {
return Err ( Error ::Generic ( " Invalid version `0` " . into ( ) ) )
}
Some ( tx_builder ::Version ( 1 ) ) if requirements . csv . is_some ( ) = > {
2020-08-07 16:30:19 +02:00
return Err ( Error ::Generic (
" TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV "
. into ( ) ,
) )
}
2020-08-10 17:16:47 +02:00
Some ( tx_builder ::Version ( x ) ) = > x ,
None if requirements . csv . is_some ( ) = > 2 ,
_ = > 1 ,
2020-08-07 16:30:19 +02:00
} ;
2022-03-30 16:29:31 +02:00
// We use a match here instead of a map_or_else as it's way more readable :)
let current_height = match params . current_height {
// If they didn't tell us the current height, we assume it's the latest sync height.
None = > self
. database ( )
. get_sync_time ( ) ?
. map ( | sync_time | sync_time . block_time . height ) ,
h = > h ,
} ;
2021-01-01 13:35:05 +11:00
let lock_time = match params . locktime {
2022-03-30 16:29:31 +02:00
// When no nLockTime is specified, we try to prevent fee sniping, if possible
None = > {
// Fee sniping can be partially prevented by setting the timelock
// to current_height. If we don't know the current_height,
// we default to 0.
let fee_sniping_height = current_height . unwrap_or ( 0 ) ;
// We choose the biggest between the required nlocktime and the fee sniping
// height
std ::cmp ::max ( requirements . timelock . unwrap_or ( 0 ) , fee_sniping_height )
}
2020-12-07 14:48:17 +01:00
// Specific nLockTime required and we have no constraints, so just set to that value
2020-08-07 16:30:19 +02:00
Some ( x ) if requirements . timelock . is_none ( ) = > x ,
2020-12-07 14:48:17 +01:00
// Specific nLockTime required and it's compatible with the constraints
Some ( x ) if check_nlocktime ( x , requirements . timelock . unwrap ( ) ) = > x ,
// Invalid nLockTime required
2020-08-07 16:30:19 +02:00
Some ( x ) = > return Err ( Error ::Generic ( format! ( " TxBuilder requested timelock of ` {} `, but at least ` {} ` is required to spend from this script " , x , requirements . timelock . unwrap ( ) ) ) )
} ;
2021-01-01 13:35:05 +11:00
let n_sequence = match ( params . rbf , requirements . csv ) {
2020-12-07 14:48:17 +01:00
// No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
( None , None ) if lock_time ! = 0 = > 0xFFFFFFFE ,
// No RBF, CSV or nLockTime, make the transaction final
( None , None ) = > 0xFFFFFFFF ,
// No RBF requested, use the value from CSV. Note that this value is by definition
// non-final, so even if a timelock is enabled this nSequence is fine, hence why we
// don't bother checking for it here. The same is true for all the other branches below
2020-08-07 16:30:19 +02:00
( None , Some ( csv ) ) = > csv ,
2020-12-07 14:48:17 +01:00
// RBF with a specific value but that value is too high
2021-03-30 16:33:07 +02:00
( Some ( tx_builder ::RbfValue ::Value ( rbf ) ) , _ ) if rbf > = 0xFFFFFFFE = > {
2020-12-07 14:48:17 +01:00
return Err ( Error ::Generic (
" Cannot enable RBF with a nSequence >= 0xFFFFFFFE " . into ( ) ,
) )
}
// RBF with a specific value requested, but the value is incompatible with CSV
2021-03-30 16:33:07 +02:00
( Some ( tx_builder ::RbfValue ::Value ( rbf ) ) , Some ( csv ) )
2020-12-07 14:48:17 +01:00
if ! check_nsequence_rbf ( rbf , csv ) = >
{
return Err ( Error ::Generic ( format! (
" Cannot enable RBF with nSequence `{}` given a required OP_CSV of `{}` " ,
rbf , csv
) ) )
}
// RBF enabled with the default value with CSV also enabled. CSV takes precedence
2021-03-30 16:33:07 +02:00
( Some ( tx_builder ::RbfValue ::Default ) , Some ( csv ) ) = > csv ,
2020-12-07 14:48:17 +01:00
// Valid RBF, either default or with a specific value. We ignore the `CSV` value
// because we've already checked it before
( Some ( rbf ) , _ ) = > rbf . get_value ( ) ,
2020-08-07 16:30:19 +02:00
} ;
2021-01-01 13:35:05 +11:00
let ( fee_rate , mut fee_amount ) = match params
. fee_policy
. as_ref ( )
. unwrap_or ( & FeePolicy ::FeeRate ( FeeRate ::default ( ) ) )
{
//FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
FeePolicy ::FeeAmount ( fee ) = > {
if let Some ( previous_fee ) = params . bumping_fee {
if * fee < previous_fee . absolute {
return Err ( Error ::FeeTooLow {
required : previous_fee . absolute ,
} ) ;
}
}
2021-07-16 15:14:20 +10:00
( FeeRate ::from_sat_per_vb ( 0.0 ) , * fee )
2021-01-01 13:35:05 +11:00
}
FeePolicy ::FeeRate ( rate ) = > {
if let Some ( previous_fee ) = params . bumping_fee {
let required_feerate = FeeRate ::from_sat_per_vb ( previous_fee . rate + 1.0 ) ;
if * rate < required_feerate {
return Err ( Error ::FeeRateTooLow {
required : required_feerate ,
} ) ;
}
}
2021-07-16 15:14:20 +10:00
( * rate , 0 )
2021-01-01 13:35:05 +11:00
}
} ;
2020-02-07 23:22:28 +01:00
let mut tx = Transaction {
2020-08-07 16:30:19 +02:00
version ,
lock_time ,
2020-02-07 23:22:28 +01:00
input : vec ! [ ] ,
output : vec ! [ ] ,
} ;
2021-01-01 13:35:05 +11:00
if params . manually_selected_only & & params . utxos . is_empty ( ) {
2020-10-28 10:37:47 +01:00
return Err ( Error ::NoUtxosSelected ) ;
2020-02-07 23:22:28 +01:00
}
// we keep it as a float while we accumulate it, and only round it at the end
let mut outgoing : u64 = 0 ;
let mut received : u64 = 0 ;
2022-04-14 17:20:46 +02:00
fee_amount + = fee_rate . fee_wu ( tx . weight ( ) ) ;
2020-02-07 23:22:28 +01:00
2021-06-16 12:43:32 +10:00
let recipients = params . recipients . iter ( ) . map ( | ( r , v ) | ( r , * v ) ) ;
for ( index , ( script_pubkey , value ) ) in recipients . enumerate ( ) {
2021-12-19 02:55:24 -05:00
if value . is_dust ( script_pubkey ) & & ! script_pubkey . is_provably_unspendable ( ) {
2021-06-16 12:43:32 +10:00
return Err ( Error ::OutputBelowDustLimit ( index ) ) ;
}
2020-02-07 23:22:28 +01:00
2020-09-04 15:45:11 +02:00
if self . is_mine ( script_pubkey ) ? {
2020-02-07 23:22:28 +01:00
received + = value ;
}
let new_out = TxOut {
2020-09-04 15:45:11 +02:00
script_pubkey : script_pubkey . clone ( ) ,
2020-02-07 23:22:28 +01:00
value ,
} ;
2021-07-16 15:14:20 +10:00
fee_amount + = fee_rate . fee_vb ( serialize ( & new_out ) . len ( ) ) ;
2020-02-07 23:22:28 +01:00
tx . output . push ( new_out ) ;
outgoing + = value ;
}
2021-01-01 13:35:05 +11:00
if params . change_policy ! = tx_builder ::ChangeSpendPolicy ::ChangeAllowed
2020-08-10 17:16:47 +02:00
& & self . change_descriptor . is_none ( )
{
return Err ( Error ::Generic (
" The `change_policy` can be set only if the wallet has a change_descriptor " . into ( ) ,
) ) ;
}
2020-10-26 14:23:46 -04:00
let ( required_utxos , optional_utxos ) = self . preselect_utxos (
2021-01-01 13:35:05 +11:00
params . change_policy ,
& params . unspendable ,
2021-01-22 14:04:06 +11:00
params . utxos . clone ( ) ,
2021-01-01 13:35:05 +11:00
params . drain_wallet ,
params . manually_selected_only ,
params . bumping_fee . is_some ( ) , // we mandate confirmed transactions if we're bumping the fee
2022-05-25 18:56:50 +01:00
current_height ,
2020-08-07 19:40:13 +02:00
) ? ;
2020-10-14 14:03:12 +11:00
2021-02-08 15:40:56 +11:00
let coin_selection = coin_selection . coin_select (
2020-10-14 15:21:22 +02:00
self . database . borrow ( ) . deref ( ) ,
2020-10-26 14:08:57 -04:00
required_utxos ,
2020-10-26 14:12:46 -04:00
optional_utxos ,
2020-02-07 23:22:28 +01:00
fee_rate ,
outgoing ,
2020-08-06 16:56:41 +02:00
fee_amount ,
2020-02-07 23:22:28 +01:00
) ? ;
2021-02-08 15:40:56 +11:00
let mut fee_amount = coin_selection . fee_amount ;
tx . input = coin_selection
. selected
2020-08-08 12:06:40 +02:00
. iter ( )
2020-10-30 14:09:59 +11:00
. map ( | u | bitcoin ::TxIn {
2021-02-08 15:40:56 +11:00
previous_output : u . outpoint ( ) ,
2020-10-30 14:09:59 +11:00
script_sig : Script ::default ( ) ,
sequence : n_sequence ,
2022-04-14 17:20:46 +02:00
witness : Witness ::new ( ) ,
2020-10-30 14:09:59 +11:00
} )
. collect ( ) ;
2020-02-07 23:22:28 +01:00
2021-06-16 12:43:32 +10:00
// prepare the drain output
let mut drain_output = {
let script_pubkey = match params . drain_to {
Some ( ref drain_recipient ) = > drain_recipient . clone ( ) ,
2021-08-19 19:57:35 +10:00
None = > self
. get_internal_address ( AddressIndex ::New ) ?
. address
. script_pubkey ( ) ,
2021-06-16 12:43:32 +10:00
} ;
2020-02-07 23:22:28 +01:00
2021-06-16 12:43:32 +10:00
TxOut {
script_pubkey ,
value : 0 ,
2020-02-07 23:22:28 +01:00
}
} ;
2020-10-28 10:37:47 +01:00
2021-07-16 15:14:20 +10:00
fee_amount + = fee_rate . fee_vb ( serialize ( & drain_output ) . len ( ) ) ;
2020-10-28 10:37:47 +01:00
2021-06-16 12:43:32 +10:00
let drain_val = ( coin_selection . selected_amount ( ) - outgoing ) . saturating_sub ( fee_amount ) ;
if tx . output . is_empty ( ) {
2022-06-06 21:45:13 +02:00
// Uh oh, our transaction has no outputs.
// We allow this when:
// - We have a drain_to address and the utxos we must spend (this happens,
// for example, when we RBF)
// - We have a drain_to address and drain_wallet set
// Otherwise, we don't know who we should send the funds to, and how much
// we should send!
if params . drain_to . is_some ( ) & & ( params . drain_wallet | | ! params . utxos . is_empty ( ) ) {
2021-12-19 02:55:24 -05:00
if drain_val . is_dust ( & drain_output . script_pubkey ) {
2021-06-16 12:43:32 +10:00
return Err ( Error ::InsufficientFunds {
2021-12-19 02:55:24 -05:00
needed : drain_output . script_pubkey . dust_value ( ) . as_sat ( ) ,
2021-06-16 12:43:32 +10:00
available : drain_val ,
} ) ;
2020-10-28 10:37:47 +01:00
}
2021-06-16 12:43:32 +10:00
} else {
return Err ( Error ::NoRecipients ) ;
2020-02-07 23:22:28 +01:00
}
}
2021-12-19 02:55:24 -05:00
if drain_val . is_dust ( & drain_output . script_pubkey ) {
2021-06-16 12:43:32 +10:00
fee_amount + = drain_val ;
} else {
drain_output . value = drain_val ;
if self . is_mine ( & drain_output . script_pubkey ) ? {
received + = drain_val ;
}
tx . output . push ( drain_output ) ;
}
2020-08-07 15:35:14 +02:00
// sort input/outputs according to the chosen algorithm
2021-01-01 13:35:05 +11:00
params . ordering . sort_tx ( & mut tx ) ;
2020-02-07 23:22:28 +01:00
let txid = tx . txid ( ) ;
2021-02-08 15:40:56 +11:00
let sent = coin_selection . local_selected_amount ( ) ;
let psbt = self . complete_transaction ( tx , coin_selection . selected , params ) ? ;
2020-02-07 23:22:28 +01:00
2020-08-13 16:51:27 +02:00
let transaction_details = TransactionDetails {
transaction : None ,
txid ,
2021-06-12 15:01:44 +02:00
confirmation_time : None ,
2020-08-13 16:51:27 +02:00
received ,
2021-02-08 15:40:56 +11:00
sent ,
2021-06-12 15:01:44 +02:00
fee : Some ( fee_amount ) ,
2020-08-13 16:51:27 +02:00
} ;
2020-08-08 12:06:40 +02:00
2020-08-13 16:51:27 +02:00
Ok ( ( psbt , transaction_details ) )
}
2020-02-07 23:22:28 +01:00
2021-01-01 13:35:05 +11:00
/// Bump the fee of a transaction previously created with this wallet.
2020-09-04 16:29:25 +02:00
///
2021-01-01 13:35:05 +11:00
/// Returns an error if the transaction is already confirmed or doesn't explicitly signal
2021-11-23 13:40:58 -05:00
/// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
2021-01-01 13:35:05 +11:00
/// pre-populated with the inputs and outputs of the original transaction.
2020-09-04 16:29:25 +02:00
///
/// ## Example
///
/// ```no_run
2021-01-01 14:15:24 +11:00
/// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
2020-09-04 16:29:25 +02:00
/// # use std::str::FromStr;
/// # use bitcoin::*;
2020-09-14 14:25:38 +02:00
/// # use bdk::*;
/// # use bdk::database::*;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2021-01-01 14:15:24 +11:00
/// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
2021-04-28 09:53:03 +10:00
/// let (mut psbt, _) = {
2021-01-11 14:14:14 +11:00
/// let mut builder = wallet.build_tx();
/// builder
/// .add_recipient(to_address.script_pubkey(), 50_000)
/// .enable_rbf();
/// builder.finish()?
/// };
2021-04-19 14:16:39 +02:00
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
2021-01-01 14:15:24 +11:00
/// let tx = psbt.extract_tx();
/// // broadcast tx but it's taking too long to confirm so we want to bump the fee
2021-04-28 09:53:03 +10:00
/// let (mut psbt, _) = {
2021-01-11 14:14:14 +11:00
/// let mut builder = wallet.build_fee_bump(tx.txid())?;
/// builder
/// .fee_rate(FeeRate::from_sat_per_vb(5.0));
/// builder.finish()?
/// };
2021-01-01 14:15:24 +11:00
///
2021-04-19 14:16:39 +02:00
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
2021-01-01 14:15:24 +11:00
/// let fee_bumped_tx = psbt.extract_tx();
/// // broadcast fee_bumped_tx to replace original
2020-09-14 14:25:38 +02:00
/// # Ok::<(), bdk::Error>(())
2020-09-04 16:29:25 +02:00
/// ```
2020-08-13 16:51:27 +02:00
// TODO: support for merging multiple transactions while bumping the fees
2021-01-01 13:35:05 +11:00
pub fn build_fee_bump (
2020-08-13 16:51:27 +02:00
& self ,
2021-01-01 13:35:05 +11:00
txid : Txid ,
2022-01-26 15:17:48 +11:00
) -> Result < TxBuilder < '_ , D , DefaultCoinSelectionAlgorithm , BumpFee > , Error > {
2020-08-13 16:51:27 +02:00
let mut details = match self . database . borrow ( ) . get_tx ( & txid , true ) ? {
None = > return Err ( Error ::TransactionNotFound ) ,
Some ( tx ) if tx . transaction . is_none ( ) = > return Err ( Error ::TransactionNotFound ) ,
2021-06-12 15:01:44 +02:00
Some ( tx ) if tx . confirmation_time . is_some ( ) = > return Err ( Error ::TransactionConfirmed ) ,
2020-08-13 16:51:27 +02:00
Some ( tx ) = > tx ,
} ;
let mut tx = details . transaction . take ( ) . unwrap ( ) ;
if ! tx . input . iter ( ) . any ( | txin | txin . sequence < = 0xFFFFFFFD ) {
return Err ( Error ::IrreplaceableTransaction ) ;
}
2020-02-07 23:22:28 +01:00
2022-04-14 17:20:46 +02:00
let feerate = FeeRate ::from_wu ( details . fee . ok_or ( Error ::FeeRateUnavailable ) ? , tx . weight ( ) ) ;
2020-08-13 16:51:27 +02:00
2020-10-16 14:27:50 +02:00
// remove the inputs from the tx and process them
let original_txin = tx . input . drain ( .. ) . collect ::< Vec < _ > > ( ) ;
2021-01-01 13:35:05 +11:00
let original_utxos = original_txin
2020-10-16 14:27:50 +02:00
. iter ( )
2021-01-22 14:04:06 +11:00
. map ( | txin | -> Result < _ , Error > {
2020-10-16 14:27:50 +02:00
let txout = self
. database
. borrow ( )
. get_previous_output ( & txin . previous_output ) ?
2021-03-30 16:33:07 +02:00
. ok_or ( Error ::UnknownUtxo ) ? ;
2020-08-13 16:51:27 +02:00
2020-12-14 17:14:24 +01:00
let ( weight , keychain ) = match self
2020-10-16 14:27:50 +02:00
. database
. borrow ( )
. get_path_from_script_pubkey ( & txout . script_pubkey ) ?
{
2020-12-14 17:14:24 +01:00
Some ( ( keychain , _ ) ) = > (
2021-01-01 13:35:05 +11:00
self . _get_descriptor_for_keychain ( keychain )
2020-12-04 10:37:58 +11:00
. 0
2021-02-02 20:06:40 -05:00
. max_satisfaction_weight ( )
2020-12-04 10:37:58 +11:00
. unwrap ( ) ,
2020-12-14 17:14:24 +01:00
keychain ,
2020-12-04 10:37:58 +11:00
) ,
2020-10-16 14:27:50 +02:00
None = > {
// estimate the weight based on the scriptsig/witness size present in the
// original transaction
let weight =
serialize ( & txin . script_sig ) . len ( ) * 4 + serialize ( & txin . witness ) . len ( ) ;
2020-12-14 17:14:24 +01:00
( weight , KeychainKind ::External )
2020-08-13 16:51:27 +02:00
}
2020-10-16 14:27:50 +02:00
} ;
2021-02-04 12:09:53 +11:00
let utxo = LocalUtxo {
2020-10-16 14:27:50 +02:00
outpoint : txin . previous_output ,
txout ,
2020-12-14 17:14:24 +01:00
keychain ,
2022-03-09 16:15:34 +01:00
is_spent : true ,
2020-10-16 14:27:50 +02:00
} ;
2021-02-08 15:40:56 +11:00
Ok ( WeightedUtxo {
satisfaction_weight : weight ,
utxo : Utxo ::Local ( utxo ) ,
} )
2020-10-22 12:07:51 +11:00
} )
2021-01-22 14:04:06 +11:00
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
2020-10-16 14:27:50 +02:00
2021-01-01 13:35:05 +11:00
if tx . output . len ( ) > 1 {
let mut change_index = None ;
for ( index , txout ) in tx . output . iter ( ) . enumerate ( ) {
let ( _ , change_type ) = self . _get_descriptor_for_keychain ( KeychainKind ::Internal ) ;
match self
. database
. borrow ( )
. get_path_from_script_pubkey ( & txout . script_pubkey ) ?
{
Some ( ( keychain , _ ) ) if keychain = = change_type = > change_index = Some ( index ) ,
_ = > { }
2020-10-22 13:41:26 +02:00
}
2020-10-16 14:27:50 +02:00
}
2020-10-28 10:37:47 +01:00
2021-01-01 13:35:05 +11:00
if let Some ( change_index ) = change_index {
tx . output . remove ( change_index ) ;
2020-10-28 10:37:47 +01:00
}
2020-08-13 16:51:27 +02:00
}
2021-01-01 13:35:05 +11:00
let params = TxParams {
// TODO: figure out what rbf option should be?
version : Some ( tx_builder ::Version ( tx . version ) ) ,
recipients : tx
. output
. into_iter ( )
. map ( | txout | ( txout . script_pubkey , txout . value ) )
. collect ( ) ,
utxos : original_utxos ,
bumping_fee : Some ( tx_builder ::PreviousFee {
2021-06-12 15:01:44 +02:00
absolute : details . fee . ok_or ( Error ::FeeRateUnavailable ) ? ,
2021-07-16 15:14:20 +10:00
rate : feerate . as_sat_vb ( ) ,
2021-01-01 13:35:05 +11:00
} ) ,
.. Default ::default ( )
} ;
2020-08-13 16:51:27 +02:00
2021-01-01 13:35:05 +11:00
Ok ( TxBuilder {
2021-06-08 13:57:55 +10:00
wallet : self ,
2021-01-11 14:14:14 +11:00
params ,
coin_selection : DefaultCoinSelectionAlgorithm ::default ( ) ,
2021-01-01 13:35:05 +11:00
phantom : core ::marker ::PhantomData ,
} )
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Sign a transaction with all the wallet's signers, in the order specified by every signer's
/// [`SignerOrdering`]
///
2021-04-19 14:16:39 +02:00
/// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
/// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
/// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
/// in this library will.
///
2020-09-04 16:29:25 +02:00
/// ## Example
///
2021-01-01 14:15:24 +11:00
/// ```
2020-09-04 16:29:25 +02:00
/// # use std::str::FromStr;
/// # use bitcoin::*;
2020-09-14 14:25:38 +02:00
/// # use bdk::*;
/// # use bdk::database::*;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2021-01-01 14:15:24 +11:00
/// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
2021-04-28 09:53:03 +10:00
/// let (mut psbt, _) = {
2021-01-11 14:14:14 +11:00
/// let mut builder = wallet.build_tx();
/// builder.add_recipient(to_address.script_pubkey(), 50_000);
/// builder.finish()?
/// };
2021-04-19 14:16:39 +02:00
/// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
2021-01-01 14:15:24 +11:00
/// assert!(finalized, "we should have signed all the inputs");
2020-09-14 14:25:38 +02:00
/// # Ok::<(), bdk::Error>(())
2022-04-14 17:20:46 +02:00
pub fn sign (
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
sign_options : SignOptions ,
) -> Result < bool , Error > {
2020-02-17 14:22:53 +01:00
// this helps us doing our job later
2021-04-28 09:53:03 +10:00
self . add_input_hd_keypaths ( psbt ) ? ;
2020-02-17 14:22:53 +01:00
2022-04-29 12:59:09 +02:00
// If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
2021-05-06 17:11:43 +02:00
// has the `non_witness_utxo`
2021-05-06 11:35:58 +02:00
if ! sign_options . trust_witness_utxo
2021-05-06 17:11:43 +02:00
& & psbt
. inputs
. iter ( )
. filter ( | i | i . final_script_witness . is_none ( ) & & i . final_script_sig . is_none ( ) )
2022-04-29 12:59:09 +02:00
. filter ( | i | i . tap_internal_key . is_none ( ) & & i . tap_merkle_root . is_none ( ) )
2021-05-06 17:11:43 +02:00
. any ( | i | i . non_witness_utxo . is_none ( ) )
2021-05-06 11:35:58 +02:00
{
return Err ( Error ::Signer ( signer ::SignerError ::MissingNonWitnessUtxo ) ) ;
2021-04-19 14:16:39 +02:00
}
2021-05-26 10:34:25 +02:00
// If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
2022-04-29 12:59:09 +02:00
// is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
2021-05-26 10:34:25 +02:00
if ! sign_options . allow_all_sighashes
2022-04-14 17:20:46 +02:00
& & ! psbt . inputs . iter ( ) . all ( | i | {
2022-04-29 12:59:09 +02:00
i . sighash_type . is_none ( )
| | i . sighash_type = = Some ( EcdsaSighashType ::All . into ( ) )
| | i . sighash_type = = Some ( SchnorrSighashType ::All . into ( ) )
| | i . sighash_type = = Some ( SchnorrSighashType ::Default . into ( ) )
2022-04-14 17:20:46 +02:00
} )
2021-05-26 10:34:25 +02:00
{
return Err ( Error ::Signer ( signer ::SignerError ::NonStandardSighash ) ) ;
}
2020-08-17 12:10:51 +02:00
for signer in self
. signers
. signers ( )
. iter ( )
. chain ( self . change_signers . signers ( ) . iter ( ) )
{
2022-04-26 16:54:10 +02:00
signer . sign_transaction ( psbt , & self . secp ) ? ;
2020-02-07 23:22:28 +01:00
}
// attempt to finalize
2022-06-04 12:42:52 +07:00
if sign_options . try_finalize {
self . finalize_psbt ( psbt , sign_options )
} else {
Ok ( false )
}
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Return the spending policies for the wallet's descriptor
2020-12-14 17:14:24 +01:00
pub fn policies ( & self , keychain : KeychainKind ) -> Result < Option < Policy > , Error > {
match ( keychain , self . change_descriptor . as_ref ( ) ) {
2021-04-02 16:39:18 +02:00
( KeychainKind ::External , _ ) = > Ok ( self . descriptor . extract_policy (
& self . signers ,
BuildSatisfaction ::None ,
& self . secp ,
) ? ) ,
2020-12-14 17:14:24 +01:00
( KeychainKind ::Internal , None ) = > Ok ( None ) ,
2021-04-02 16:39:18 +02:00
( KeychainKind ::Internal , Some ( desc ) ) = > Ok ( desc . extract_policy (
& self . change_signers ,
BuildSatisfaction ::None ,
& self . secp ,
) ? ) ,
2020-02-07 23:22:28 +01:00
}
}
2020-09-04 16:29:25 +02:00
/// Return the "public" version of the wallet's descriptor, meaning a new descriptor that has
/// the same structure but with every secret key removed
///
/// This can be used to build a watch-only version of a wallet
2020-05-10 17:42:02 +02:00
pub fn public_descriptor (
& self ,
2020-12-14 17:14:24 +01:00
keychain : KeychainKind ,
2020-05-10 17:42:02 +02:00
) -> Result < Option < ExtendedDescriptor > , Error > {
2020-12-14 17:14:24 +01:00
match ( keychain , self . change_descriptor . as_ref ( ) ) {
( KeychainKind ::External , _ ) = > Ok ( Some ( self . descriptor . clone ( ) ) ) ,
( KeychainKind ::Internal , None ) = > Ok ( None ) ,
( KeychainKind ::Internal , Some ( desc ) ) = > Ok ( Some ( desc . clone ( ) ) ) ,
2020-05-10 17:42:02 +02:00
}
}
2020-09-04 16:29:25 +02:00
/// Try to finalize a PSBT
2021-04-19 14:16:39 +02:00
///
/// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
2022-04-14 17:20:46 +02:00
pub fn finalize_psbt (
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
sign_options : SignOptions ,
) -> Result < bool , Error > {
let tx = & psbt . unsigned_tx ;
2020-11-17 17:53:06 +11:00
let mut finished = true ;
2020-05-17 18:01:52 +02:00
2020-11-17 17:53:06 +11:00
for ( n , input ) in tx . input . iter ( ) . enumerate ( ) {
2021-05-06 15:55:58 +02:00
let psbt_input = & psbt
. inputs
. get ( n )
. ok_or ( Error ::Signer ( SignerError ::InputIndexOutOfRange ) ) ? ;
2020-11-17 17:53:06 +11:00
if psbt_input . final_script_sig . is_some ( ) | | psbt_input . final_script_witness . is_some ( ) {
continue ;
}
2020-05-17 18:01:52 +02:00
// if the height is None in the database it means it's still unconfirmed, so consider
// that as a very high value
let create_height = self
. database
. borrow ( )
. get_tx ( & input . previous_output . txid , false ) ?
2021-06-12 15:01:44 +02:00
. map ( | tx | tx . confirmation_time . map ( | c | c . height ) . unwrap_or ( u32 ::MAX ) ) ;
2022-01-26 15:17:48 +11:00
let last_sync_height = self
. database ( )
. get_sync_time ( ) ?
. map ( | sync_time | sync_time . block_time . height ) ;
let current_height = sign_options . assume_height . or ( last_sync_height ) ;
2020-05-17 18:01:52 +02:00
debug! (
" Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?} " ,
n , input . previous_output , create_height , current_height
) ;
2020-08-12 12:51:50 +02:00
// - Try to derive the descriptor by looking at the txout. If it's in our database, we
2020-12-14 17:14:24 +01:00
// know exactly which `keychain` to use, and which derivation index it is
2020-08-12 12:51:50 +02:00
// - If that fails, try to derive it by looking at the psbt input: the complete logic
2021-02-02 20:06:40 -05:00
// is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
2020-08-12 12:51:50 +02:00
// `redeem_script` and `witness_script` to determine the right derivation
// - If that also fails, it will try it on the internal descriptor, if present
2020-11-17 17:53:06 +11:00
let desc = psbt
2020-08-12 12:51:50 +02:00
. get_utxo_for ( n )
. map ( | txout | self . get_descriptor_for_txout ( & txout ) )
. transpose ( ) ?
. flatten ( )
2020-11-17 17:53:06 +11:00
. or_else ( | | {
self . descriptor . derive_from_psbt_input (
psbt_input ,
psbt . get_utxo_for ( n ) ,
& self . secp ,
)
} )
. or_else ( | | {
self . change_descriptor . as_ref ( ) . and_then ( | desc | {
desc . derive_from_psbt_input ( psbt_input , psbt . get_utxo_for ( n ) , & self . secp )
} )
} ) ;
match desc {
Some ( desc ) = > {
let mut tmp_input = bitcoin ::TxIn ::default ( ) ;
match desc . satisfy (
& mut tmp_input ,
(
2021-06-08 13:57:55 +10:00
PsbtInputSatisfier ::new ( psbt , n ) ,
2020-11-17 17:53:06 +11:00
After ::new ( current_height , false ) ,
Older ::new ( current_height , create_height , false ) ,
) ,
) {
Ok ( _ ) = > {
let psbt_input = & mut psbt . inputs [ n ] ;
psbt_input . final_script_sig = Some ( tmp_input . script_sig ) ;
psbt_input . final_script_witness = Some ( tmp_input . witness ) ;
2022-05-29 10:53:37 +07:00
if sign_options . remove_partial_sigs {
psbt_input . partial_sigs . clear ( ) ;
}
2020-11-17 17:53:06 +11:00
}
Err ( e ) = > {
debug! ( " satisfy error {:?} for input {} " , e , n ) ;
finished = false
}
}
2020-05-17 18:01:52 +02:00
}
2020-11-17 17:53:06 +11:00
None = > finished = false ,
2020-05-17 18:01:52 +02:00
}
}
2021-04-28 09:53:03 +10:00
Ok ( finished )
2020-05-17 18:01:52 +02:00
}
2020-12-11 14:10:11 -08:00
/// Return the secp256k1 context used for all signing operations
2020-11-16 22:07:38 +01:00
pub fn secp_ctx ( & self ) -> & SecpCtx {
& self . secp
}
2021-11-23 13:40:58 -05:00
/// Returns the descriptor used to create addresses for a particular `keychain`.
2021-01-01 13:35:05 +11:00
pub fn get_descriptor_for_keychain ( & self , keychain : KeychainKind ) -> & ExtendedDescriptor {
let ( descriptor , _ ) = self . _get_descriptor_for_keychain ( keychain ) ;
descriptor
}
2020-02-07 23:22:28 +01:00
// Internals
2021-01-01 13:35:05 +11:00
fn _get_descriptor_for_keychain (
2020-08-12 12:51:50 +02:00
& self ,
2020-12-14 17:14:24 +01:00
keychain : KeychainKind ,
) -> ( & ExtendedDescriptor , KeychainKind ) {
match keychain {
KeychainKind ::Internal if self . change_descriptor . is_some ( ) = > (
2020-08-06 18:11:07 +02:00
self . change_descriptor . as_ref ( ) . unwrap ( ) ,
2020-12-14 17:14:24 +01:00
KeychainKind ::Internal ,
2020-08-06 18:11:07 +02:00
) ,
2020-12-14 17:14:24 +01:00
_ = > ( & self . descriptor , KeychainKind ::External ) ,
2020-10-07 14:18:50 -07:00
}
2020-02-07 23:22:28 +01:00
}
2021-02-02 20:06:40 -05:00
fn get_descriptor_for_txout (
& self ,
txout : & TxOut ,
) -> Result < Option < DerivedDescriptor < '_ > > , Error > {
2020-08-12 12:51:50 +02:00
Ok ( self
. database
. borrow ( )
. get_path_from_script_pubkey ( & txout . script_pubkey ) ?
2021-01-01 13:35:05 +11:00
. map ( | ( keychain , child ) | ( self . get_descriptor_for_keychain ( keychain ) , child ) )
2021-02-02 20:06:40 -05:00
. map ( | ( desc , child ) | desc . as_derived ( child , & self . secp ) ) )
2020-02-07 23:22:28 +01:00
}
2020-12-14 17:14:24 +01:00
fn fetch_and_increment_index ( & self , keychain : KeychainKind ) -> Result < u32 , Error > {
2021-01-01 13:35:05 +11:00
let ( descriptor , keychain ) = self . _get_descriptor_for_keychain ( keychain ) ;
2021-02-02 20:06:40 -05:00
let index = match descriptor . is_deriveable ( ) {
false = > 0 ,
true = > self . database . borrow_mut ( ) . increment_last_index ( keychain ) ? ,
2020-02-07 23:22:28 +01:00
} ;
2020-08-06 18:11:07 +02:00
if self
2020-02-07 23:22:28 +01:00
. database
2020-08-06 18:11:07 +02:00
. borrow ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( keychain , index ) ?
2020-08-06 18:11:07 +02:00
. is_none ( )
{
2020-12-14 17:14:24 +01:00
self . cache_addresses ( keychain , index , CACHE_ADDR_BATCH_SIZE ) ? ;
2020-08-06 18:11:07 +02:00
}
2020-02-07 23:22:28 +01:00
2021-02-02 20:06:40 -05:00
let derived_descriptor = descriptor . as_derived ( index , & self . secp ) ;
2022-04-27 16:29:02 +02:00
let hd_keypaths = derived_descriptor . get_hd_keypaths ( & self . secp ) ;
2021-02-02 20:06:40 -05:00
let script = derived_descriptor . script_pubkey ( ) ;
2020-11-16 22:07:38 +01:00
2020-08-15 23:21:13 +02:00
for validator in & self . address_validators {
2022-07-11 16:37:41 +08:00
#[ allow(deprecated) ]
2020-12-14 17:14:24 +01:00
validator . validate ( keychain , & hd_keypaths , & script ) ? ;
2020-08-15 23:21:13 +02:00
}
2020-08-06 18:11:07 +02:00
Ok ( index )
}
2021-03-07 21:54:30 -08:00
fn fetch_index ( & self , keychain : KeychainKind ) -> Result < u32 , Error > {
let ( descriptor , keychain ) = self . _get_descriptor_for_keychain ( keychain ) ;
let index = match descriptor . is_deriveable ( ) {
false = > Some ( 0 ) ,
true = > self . database . borrow_mut ( ) . get_last_index ( keychain ) ? ,
} ;
if let Some ( i ) = index {
Ok ( i )
} else {
self . fetch_and_increment_index ( keychain )
}
}
2021-03-10 15:58:58 -08:00
fn set_index ( & self , keychain : KeychainKind , index : u32 ) -> Result < ( ) , Error > {
self . database . borrow_mut ( ) . set_last_index ( keychain , index ) ? ;
Ok ( ( ) )
}
2020-08-06 18:11:07 +02:00
fn cache_addresses (
& self ,
2020-12-14 17:14:24 +01:00
keychain : KeychainKind ,
2020-08-06 18:11:07 +02:00
from : u32 ,
mut count : u32 ,
) -> Result < ( ) , Error > {
2021-01-01 13:35:05 +11:00
let ( descriptor , keychain ) = self . _get_descriptor_for_keychain ( keychain ) ;
2021-02-02 20:06:40 -05:00
if ! descriptor . is_deriveable ( ) {
2020-08-06 18:11:07 +02:00
if from > 0 {
return Ok ( ( ) ) ;
}
count = 1 ;
}
let mut address_batch = self . database . borrow ( ) . begin_batch ( ) ;
let start_time = time ::Instant ::new ( ) ;
for i in from .. ( from + count ) {
address_batch . set_script_pubkey (
2021-02-02 20:06:40 -05:00
& descriptor . as_derived ( i , & self . secp ) . script_pubkey ( ) ,
2020-12-14 17:14:24 +01:00
keychain ,
2020-08-06 18:11:07 +02:00
i ,
) ? ;
}
info! (
" Derivation of {} addresses from {} took {} ms " ,
count ,
from ,
start_time . elapsed ( ) . as_millis ( )
) ;
self . database . borrow_mut ( ) . commit_batch ( address_batch ) ? ;
Ok ( ( ) )
2020-02-07 23:22:28 +01:00
}
2021-02-04 12:09:53 +11:00
fn get_available_utxos ( & self ) -> Result < Vec < ( LocalUtxo , usize ) > , Error > {
2020-12-04 10:37:58 +11:00
Ok ( self
. list_unspent ( ) ?
. into_iter ( )
. map ( | utxo | {
2020-12-14 17:14:24 +01:00
let keychain = utxo . keychain ;
2020-12-04 10:37:58 +11:00
(
utxo ,
2020-12-14 17:14:24 +01:00
self . get_descriptor_for_keychain ( keychain )
2021-02-02 20:06:40 -05:00
. max_satisfaction_weight ( )
2020-12-04 10:37:58 +11:00
. unwrap ( ) ,
)
} )
. collect ( ) )
2020-10-22 12:07:51 +11:00
}
2020-10-21 15:53:00 +11:00
2020-10-22 12:07:51 +11:00
/// Given the options returns the list of utxos that must be used to form the
/// transaction and any further that may be used if needed.
#[ allow(clippy::type_complexity) ]
2022-05-25 18:56:50 +01:00
#[ allow(clippy::too_many_arguments) ]
2020-10-26 14:23:46 -04:00
fn preselect_utxos (
2020-10-22 12:07:51 +11:00
& self ,
change_policy : tx_builder ::ChangeSpendPolicy ,
unspendable : & HashSet < OutPoint > ,
2021-02-08 15:40:56 +11:00
manually_selected : Vec < WeightedUtxo > ,
2020-10-22 12:07:51 +11:00
must_use_all_available : bool ,
manual_only : bool ,
2020-10-23 12:13:05 +11:00
must_only_use_confirmed_tx : bool ,
2022-05-25 18:56:50 +01:00
current_height : Option < u32 > ,
2021-02-08 15:40:56 +11:00
) -> Result < ( Vec < WeightedUtxo > , Vec < WeightedUtxo > ) , Error > {
2020-10-22 12:07:51 +11:00
// must_spend <- manually selected utxos
// may_spend <- all other available utxos
let mut may_spend = self . get_available_utxos ( ) ? ;
2021-05-06 14:32:08 +10:00
2021-01-01 13:35:05 +11:00
may_spend . retain ( | may_spend | {
2021-05-06 14:32:08 +10:00
! manually_selected
2020-10-22 12:07:51 +11:00
. iter ( )
2021-05-06 14:32:08 +10:00
. any ( | manually_selected | manually_selected . utxo . outpoint ( ) = = may_spend . 0. outpoint )
2021-01-01 13:35:05 +11:00
} ) ;
let mut must_spend = manually_selected ;
2020-10-22 12:07:51 +11:00
// NOTE: we are intentionally ignoring `unspendable` here. i.e manual
// selection overrides unspendable.
if manual_only {
return Ok ( ( must_spend , vec! [ ] ) ) ;
2020-02-07 23:22:28 +01:00
}
2020-10-22 12:07:51 +11:00
2022-05-25 18:56:50 +01:00
let database = self . database . borrow ( ) ;
let satisfies_confirmed = may_spend
. iter ( )
. map ( | u | {
database
. get_tx ( & u . 0. outpoint . txid , true )
. map ( | tx | match tx {
// We don't have the tx in the db for some reason,
// so we can't know for sure if it's mature or not.
// We prefer not to spend it.
None = > false ,
Some ( tx ) = > {
// Whether the UTXO is mature and, if needed, confirmed
let mut spendable = true ;
if must_only_use_confirmed_tx & & tx . confirmation_time . is_none ( ) {
return false ;
}
if tx
. transaction
. expect ( " We specifically ask for the transaction above " )
. is_coin_base ( )
{
if let Some ( current_height ) = current_height {
match & tx . confirmation_time {
Some ( t ) = > {
// https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
spendable & = ( current_height . saturating_sub ( t . height ) )
> = COINBASE_MATURITY ;
}
None = > spendable = false ,
}
}
}
spendable
}
2020-10-23 12:13:05 +11:00
} )
2022-05-25 18:56:50 +01:00
} )
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
2020-10-23 12:13:05 +11:00
let mut i = 0 ;
2020-10-22 12:07:51 +11:00
may_spend . retain ( | u | {
2020-10-23 12:13:05 +11:00
let retain = change_policy . is_satisfied_by ( & u . 0 )
& & ! unspendable . contains ( & u . 0. outpoint )
& & satisfies_confirmed [ i ] ;
i + = 1 ;
retain
2020-10-22 12:07:51 +11:00
} ) ;
2021-02-08 15:40:56 +11:00
let mut may_spend = may_spend
. into_iter ( )
. map ( | ( local_utxo , satisfaction_weight ) | WeightedUtxo {
satisfaction_weight ,
utxo : Utxo ::Local ( local_utxo ) ,
} )
. collect ( ) ;
2020-10-22 12:07:51 +11:00
if must_use_all_available {
must_spend . append ( & mut may_spend ) ;
}
Ok ( ( must_spend , may_spend ) )
2020-02-07 23:22:28 +01:00
}
2021-01-01 13:35:05 +11:00
fn complete_transaction (
2020-08-13 16:51:27 +02:00
& self ,
tx : Transaction ,
2021-02-08 15:40:56 +11:00
selected : Vec < Utxo > ,
2021-01-01 13:35:05 +11:00
params : TxParams ,
2022-04-14 17:20:46 +02:00
) -> Result < psbt ::PartiallySignedTransaction , Error > {
let mut psbt = psbt ::PartiallySignedTransaction ::from_unsigned_tx ( tx ) ? ;
2020-11-30 15:13:33 +01:00
2021-01-01 13:35:05 +11:00
if params . add_global_xpubs {
2020-11-30 15:13:33 +01:00
let mut all_xpubs = self . descriptor . get_extended_keys ( ) ? ;
if let Some ( change_descriptor ) = & self . change_descriptor {
all_xpubs . extend ( change_descriptor . get_extended_keys ( ) ? ) ;
}
for xpub in all_xpubs {
let origin = match xpub . origin {
Some ( origin ) = > origin ,
None if xpub . xkey . depth = = 0 = > {
( xpub . root_fingerprint ( & self . secp ) , vec! [ ] . into ( ) )
}
_ = > return Err ( Error ::MissingKeyOrigin ( xpub . xkey . to_string ( ) ) ) ,
} ;
2022-04-14 17:20:46 +02:00
psbt . xpub . insert ( xpub . xkey , origin ) ;
2020-11-30 15:13:33 +01:00
}
}
2021-02-08 15:40:56 +11:00
let mut lookup_output = selected
2020-11-17 12:37:53 +11:00
. into_iter ( )
2021-02-08 15:40:56 +11:00
. map ( | utxo | ( utxo . outpoint ( ) , utxo ) )
2020-11-17 12:37:53 +11:00
. collect ::< HashMap < _ , _ > > ( ) ;
2020-08-13 16:51:27 +02:00
// add metadata for the inputs
2022-04-14 17:20:46 +02:00
for ( psbt_input , input ) in psbt . inputs . iter_mut ( ) . zip ( psbt . unsigned_tx . input . iter ( ) ) {
2021-02-08 15:40:56 +11:00
let utxo = match lookup_output . remove ( & input . previous_output ) {
2020-10-30 14:09:59 +11:00
Some ( utxo ) = > utxo ,
2020-08-13 16:51:27 +02:00
None = > continue ,
} ;
2021-02-08 15:40:56 +11:00
match utxo {
Utxo ::Local ( utxo ) = > {
2021-04-20 14:58:33 +02:00
* psbt_input =
match self . get_psbt_input ( utxo , params . sighash , params . only_witness_utxo ) {
Ok ( psbt_input ) = > psbt_input ,
Err ( e ) = > match e {
2022-04-14 17:20:46 +02:00
Error ::UnknownUtxo = > psbt ::Input {
2021-04-20 14:58:33 +02:00
sighash_type : params . sighash ,
2022-04-14 17:20:46 +02:00
.. psbt ::Input ::default ( )
2021-04-20 14:58:33 +02:00
} ,
_ = > return Err ( e ) ,
2021-03-15 21:50:51 -04:00
} ,
2021-04-20 14:58:33 +02:00
}
2020-08-13 16:51:27 +02:00
}
2021-02-08 15:40:56 +11:00
Utxo ::Foreign {
psbt_input : foreign_psbt_input ,
outpoint ,
} = > {
2022-04-28 15:39:31 +02:00
let is_taproot = foreign_psbt_input
. witness_utxo
. as_ref ( )
. map ( | txout | txout . script_pubkey . is_v1_p2tr ( ) )
. unwrap_or ( false ) ;
if ! is_taproot
& & ! params . only_witness_utxo
& & foreign_psbt_input . non_witness_utxo . is_none ( )
{
2021-02-08 15:40:56 +11:00
return Err ( Error ::Generic ( format! (
" Missing non_witness_utxo on foreign utxo {} " ,
outpoint
) ) ) ;
}
* psbt_input = * foreign_psbt_input ;
2020-08-13 16:51:27 +02:00
}
}
}
// probably redundant but it doesn't hurt...
self . add_input_hd_keypaths ( & mut psbt ) ? ;
// add metadata for the outputs
2022-04-14 17:20:46 +02:00
for ( psbt_output , tx_output ) in psbt . outputs . iter_mut ( ) . zip ( psbt . unsigned_tx . output . iter ( ) )
2020-08-13 16:51:27 +02:00
{
2020-12-14 17:14:24 +01:00
if let Some ( ( keychain , child ) ) = self
2020-08-13 16:51:27 +02:00
. database
. borrow ( )
. get_path_from_script_pubkey ( & tx_output . script_pubkey ) ?
{
2021-01-01 13:35:05 +11:00
let ( desc , _ ) = self . _get_descriptor_for_keychain ( keychain ) ;
2021-02-02 20:06:40 -05:00
let derived_descriptor = desc . as_derived ( child , & self . secp ) ;
2022-04-28 15:39:31 +02:00
if let miniscript ::Descriptor ::Tr ( tr ) = & derived_descriptor {
let tap_tree = if tr . taptree ( ) . is_some ( ) {
let mut builder = taproot ::TaprootBuilder ::new ( ) ;
for ( depth , ms ) in tr . iter_scripts ( ) {
let script = ms . encode ( ) ;
builder = builder . add_leaf ( depth , script ) . expect (
" Computing spend data on a valid Tree should always succeed " ,
) ;
}
Some (
psbt ::TapTree ::from_builder ( builder )
. expect ( " The tree should always be valid " ) ,
)
} else {
None
} ;
psbt_output . tap_tree = tap_tree ;
2022-04-27 16:29:02 +02:00
psbt_output
. tap_key_origins
. append ( & mut derived_descriptor . get_tap_key_origins ( & self . secp ) ) ;
2022-04-28 15:39:31 +02:00
psbt_output . tap_internal_key = Some ( tr . internal_key ( ) . to_x_only_pubkey ( ) ) ;
2022-04-27 16:29:02 +02:00
} else {
psbt_output
. bip32_derivation
. append ( & mut derived_descriptor . get_hd_keypaths ( & self . secp ) ) ;
}
2021-01-01 13:35:05 +11:00
if params . include_output_redeem_witness_script {
2021-02-02 20:06:40 -05:00
psbt_output . witness_script = derived_descriptor . psbt_witness_script ( ) ;
psbt_output . redeem_script = derived_descriptor . psbt_redeem_script ( ) ;
2020-11-16 16:25:16 -06:00
} ;
2020-08-13 16:51:27 +02:00
}
}
Ok ( psbt )
}
2021-03-15 21:50:51 -04:00
/// get the corresponding PSBT Input for a LocalUtxo
pub fn get_psbt_input (
& self ,
utxo : LocalUtxo ,
2022-04-14 17:20:46 +02:00
sighash_type : Option < psbt ::PsbtSighashType > ,
2021-04-20 14:58:33 +02:00
only_witness_utxo : bool ,
2022-04-14 17:20:46 +02:00
) -> Result < psbt ::Input , Error > {
2021-03-15 21:50:51 -04:00
// Try to find the prev_script in our db to figure out if this is internal or external,
// and the derivation index
2021-03-16 10:20:07 -04:00
let ( keychain , child ) = self
2021-03-15 21:50:51 -04:00
. database
. borrow ( )
. get_path_from_script_pubkey ( & utxo . txout . script_pubkey ) ?
2021-03-30 16:33:07 +02:00
. ok_or ( Error ::UnknownUtxo ) ? ;
2021-03-15 21:50:51 -04:00
2022-04-14 17:20:46 +02:00
let mut psbt_input = psbt ::Input {
2021-03-15 21:50:51 -04:00
sighash_type ,
2022-04-14 17:20:46 +02:00
.. psbt ::Input ::default ( )
2021-03-15 21:50:51 -04:00
} ;
let desc = self . get_descriptor_for_keychain ( keychain ) ;
let derived_descriptor = desc . as_derived ( child , & self . secp ) ;
2022-04-28 15:39:31 +02:00
if let miniscript ::Descriptor ::Tr ( tr ) = & derived_descriptor {
2022-04-27 16:29:02 +02:00
psbt_input . tap_key_origins = derived_descriptor . get_tap_key_origins ( & self . secp ) ;
2022-04-28 15:39:31 +02:00
psbt_input . tap_internal_key = Some ( tr . internal_key ( ) . to_x_only_pubkey ( ) ) ;
let spend_info = tr . spend_info ( ) ;
psbt_input . tap_merkle_root = spend_info . merkle_root ( ) ;
psbt_input . tap_scripts = spend_info
. as_script_map ( )
. keys ( )
. filter_map ( | script_ver | {
spend_info
. control_block ( script_ver )
. map ( | cb | ( cb , script_ver . clone ( ) ) )
} )
. collect ( ) ;
2022-04-27 16:29:02 +02:00
} else {
psbt_input . bip32_derivation = derived_descriptor . get_hd_keypaths ( & self . secp ) ;
}
2021-03-15 21:50:51 -04:00
psbt_input . redeem_script = derived_descriptor . psbt_redeem_script ( ) ;
psbt_input . witness_script = derived_descriptor . psbt_witness_script ( ) ;
let prev_output = utxo . outpoint ;
if let Some ( prev_tx ) = self . database . borrow ( ) . get_raw_tx ( & prev_output . txid ) ? {
2022-04-27 16:29:02 +02:00
if desc . is_witness ( ) | | desc . is_taproot ( ) {
2021-03-15 21:50:51 -04:00
psbt_input . witness_utxo = Some ( prev_tx . output [ prev_output . vout as usize ] . clone ( ) ) ;
}
2022-04-27 16:29:02 +02:00
if ! desc . is_taproot ( ) & & ( ! desc . is_witness ( ) | | ! only_witness_utxo ) {
2021-03-15 21:50:51 -04:00
psbt_input . non_witness_utxo = Some ( prev_tx ) ;
}
}
Ok ( psbt_input )
}
2022-04-14 17:20:46 +02:00
fn add_input_hd_keypaths (
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
) -> Result < ( ) , Error > {
2020-06-30 14:01:38 +02:00
let mut input_utxos = Vec ::with_capacity ( psbt . inputs . len ( ) ) ;
for n in 0 .. psbt . inputs . len ( ) {
input_utxos . push ( psbt . get_utxo_for ( n ) . clone ( ) ) ;
}
// try to add hd_keypaths if we've already seen the output
for ( psbt_input , out ) in psbt . inputs . iter_mut ( ) . zip ( input_utxos . iter ( ) ) {
if let Some ( out ) = out {
2020-12-14 17:14:24 +01:00
if let Some ( ( keychain , child ) ) = self
2020-06-30 14:01:38 +02:00
. database
. borrow ( )
2020-08-06 18:11:07 +02:00
. get_path_from_script_pubkey ( & out . script_pubkey ) ?
{
2020-12-14 17:14:24 +01:00
debug! ( " Found descriptor {:?}/{} " , keychain , child ) ;
2020-08-06 18:11:07 +02:00
2022-04-27 16:29:02 +02:00
// merge hd_keypaths or tap_key_origins
2021-01-01 13:35:05 +11:00
let desc = self . get_descriptor_for_keychain ( keychain ) ;
2022-04-27 16:29:02 +02:00
if desc . is_taproot ( ) {
let mut tap_key_origins = desc
. as_derived ( child , & self . secp )
. get_tap_key_origins ( & self . secp ) ;
psbt_input . tap_key_origins . append ( & mut tap_key_origins ) ;
} else {
let mut hd_keypaths = desc
. as_derived ( child , & self . secp )
. get_hd_keypaths ( & self . secp ) ;
psbt_input . bip32_derivation . append ( & mut hd_keypaths ) ;
}
2020-08-06 18:11:07 +02:00
}
2020-06-30 14:01:38 +02:00
}
}
Ok ( ( ) )
}
2021-10-23 15:00:09 +02:00
/// Return an immutable reference to the internal database
pub fn database ( & self ) -> impl std ::ops ::Deref < Target = D > + '_ {
self . database . borrow ( )
}
2020-02-07 23:22:28 +01:00
2020-09-04 16:29:25 +02:00
/// Sync the internal database with the blockchain
2020-07-20 15:51:57 +02:00
#[ maybe_async ]
2022-01-27 16:52:53 +11:00
pub fn sync < B : WalletSync + GetHeight > (
2020-08-25 16:07:26 +02:00
& self ,
2022-01-26 15:17:48 +11:00
blockchain : & B ,
2022-01-27 16:52:53 +11:00
sync_opts : SyncOptions ,
2020-08-25 16:07:26 +02:00
) -> Result < ( ) , Error > {
2020-08-06 18:11:07 +02:00
debug! ( " Begin sync... " ) ;
2020-02-07 23:22:28 +01:00
2022-03-07 10:44:41 +11:00
let SyncOptions { progress } = sync_opts ;
2022-01-27 16:52:53 +11:00
let progress = progress . unwrap_or_else ( | | Box ::new ( NoopProgress ) ) ;
2020-08-10 10:49:34 +02:00
2022-03-07 10:44:41 +11:00
let run_setup = self . ensure_addresses_cached ( CACHE_ADDR_BATCH_SIZE ) ? ;
2020-02-07 23:22:28 +01:00
2021-05-17 17:20:32 +02:00
debug! ( " run_setup: {} " , run_setup ) ;
2020-08-10 17:16:47 +02:00
// TODO: what if i generate an address first and cache some addresses?
// TODO: we should sync if generating an address triggers a new batch to be stored
2020-08-10 10:49:34 +02:00
if run_setup {
2022-01-26 15:17:48 +11:00
maybe_await! (
2022-01-27 16:52:53 +11:00
blockchain . wallet_setup ( self . database . borrow_mut ( ) . deref_mut ( ) , progress , )
2022-01-26 15:17:48 +11:00
) ? ;
2020-08-10 10:49:34 +02:00
} else {
2022-01-27 16:52:53 +11:00
maybe_await! ( blockchain . wallet_sync ( self . database . borrow_mut ( ) . deref_mut ( ) , progress , ) ) ? ;
2020-08-10 10:49:34 +02:00
}
2021-05-27 16:58:42 +02:00
2021-11-04 15:38:38 +00:00
let sync_time = SyncTime {
block_time : BlockTime {
2022-01-26 15:17:48 +11:00
height : maybe_await ! ( blockchain . get_height ( ) ) ? ,
2021-11-04 15:38:38 +00:00
timestamp : time ::get_timestamp ( ) ,
} ,
2021-10-23 15:25:49 +02:00
} ;
2021-11-04 15:38:38 +00:00
debug! ( " Saving `sync_time` = {:?} " , sync_time ) ;
self . database . borrow_mut ( ) . set_sync_time ( sync_time ) ? ;
2021-10-23 15:25:49 +02:00
2021-05-27 16:58:42 +02:00
Ok ( ( ) )
2020-02-07 23:22:28 +01:00
}
2022-03-09 18:38:11 +01:00
/// Return the checksum of the public descriptor associated to `keychain`
///
/// Internally calls [`Self::get_descriptor_for_keychain`] to fetch the right descriptor
pub fn descriptor_checksum ( & self , keychain : KeychainKind ) -> String {
self . get_descriptor_for_keychain ( keychain )
. to_string ( )
2022-05-03 12:41:22 +02:00
. split_once ( '#' )
2022-03-09 18:38:11 +01:00
. unwrap ( )
2022-05-03 12:41:22 +02:00
. 0
2022-03-09 18:38:11 +01:00
. to_string ( )
}
2020-02-07 23:22:28 +01:00
}
2020-08-06 18:11:07 +02:00
2022-04-15 22:12:34 +02:00
/// Deterministically generate a unique name given the descriptors defining the wallet
///
/// Compatible with [`wallet_name_from_descriptor`]
pub fn wallet_name_from_descriptor < T > (
descriptor : T ,
change_descriptor : Option < T > ,
network : Network ,
secp : & SecpCtx ,
) -> Result < String , Error >
where
T : IntoWalletDescriptor ,
{
//TODO check descriptors contains only public keys
let descriptor = descriptor
. into_wallet_descriptor ( secp , network ) ?
. 0
. to_string ( ) ;
let mut wallet_name = get_checksum ( & descriptor [ .. descriptor . find ( '#' ) . unwrap ( ) ] ) ? ;
if let Some ( change_descriptor ) = change_descriptor {
let change_descriptor = change_descriptor
. into_wallet_descriptor ( secp , network ) ?
. 0
. to_string ( ) ;
wallet_name . push_str (
get_checksum ( & change_descriptor [ .. change_descriptor . find ( '#' ) . unwrap ( ) ] ) ? . as_str ( ) ,
) ;
}
Ok ( wallet_name )
}
2021-07-29 11:11:16 +02:00
/// Return a fake wallet that appears to be funded for testing.
pub fn get_funded_wallet (
descriptor : & str ,
2022-05-05 16:42:56 -07:00
) -> ( Wallet < AnyDatabase > , ( String , Option < String > ) , bitcoin ::Txid ) {
2021-07-29 11:11:16 +02:00
let descriptors = testutils! ( @ descriptors ( descriptor ) ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new (
2021-07-29 11:11:16 +02:00
& descriptors . 0 ,
None ,
Network ::Regtest ,
2022-05-05 16:42:56 -07:00
AnyDatabase ::Memory ( MemoryDatabase ::new ( ) ) ,
2021-07-29 11:11:16 +02:00
)
. unwrap ( ) ;
let funding_address_kix = 0 ;
let tx_meta = testutils! {
@ tx ( ( @ external descriptors , funding_address_kix ) = > 50_000 ) ( @ confirmations 1 )
} ;
wallet
. database
. borrow_mut ( )
. set_script_pubkey (
& bitcoin ::Address ::from_str ( & tx_meta . output . get ( 0 ) . unwrap ( ) . to_address )
. unwrap ( )
. script_pubkey ( ) ,
KeychainKind ::External ,
funding_address_kix ,
)
. unwrap ( ) ;
wallet
. database
. borrow_mut ( )
. set_last_index ( KeychainKind ::External , funding_address_kix )
. unwrap ( ) ;
let txid = crate ::populate_test_db! ( wallet . database . borrow_mut ( ) , tx_meta , Some ( 100 ) ) ;
( wallet , descriptors , txid )
}
2020-08-06 18:11:07 +02:00
#[ cfg(test) ]
2021-05-06 14:41:30 +02:00
pub ( crate ) mod test {
2021-02-08 15:40:56 +11:00
use bitcoin ::{ util ::psbt , Network } ;
2020-08-10 17:16:47 +02:00
2020-08-06 18:11:07 +02:00
use crate ::database ::Database ;
2020-12-14 17:14:24 +01:00
use crate ::types ::KeychainKind ;
2020-08-06 18:11:07 +02:00
use super ::* ;
2021-05-26 10:34:25 +02:00
use crate ::signer ::{ SignOptions , SignerError } ;
2021-03-10 15:58:58 -08:00
use crate ::wallet ::AddressIndex ::{ LastUnused , New , Peek , Reset } ;
2020-08-06 18:11:07 +02:00
#[ test ]
fn test_cache_addresses_fixed ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new (
2020-08-06 18:11:07 +02:00
" wpkh(L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6) " ,
None ,
Network ::Testnet ,
db ,
)
. unwrap ( ) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
2020-08-06 18:11:07 +02:00
" tb1qj08ys4ct2hzzc2hcz6h2hgrvlmsjynaw43s835 "
) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
2020-08-06 18:11:07 +02:00
" tb1qj08ys4ct2hzzc2hcz6h2hgrvlmsjynaw43s835 "
) ;
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , 0 )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_some ( ) ) ;
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::Internal , 0 )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_none ( ) ) ;
}
#[ test ]
fn test_cache_addresses ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " , None , Network ::Testnet , db ) . unwrap ( ) ;
2020-08-06 18:11:07 +02:00
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
2020-08-06 18:11:07 +02:00
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
2020-08-06 18:11:07 +02:00
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , CACHE_ADDR_BATCH_SIZE - 1 )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_some ( ) ) ;
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , CACHE_ADDR_BATCH_SIZE )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_none ( ) ) ;
}
#[ test ]
fn test_cache_addresses_refill ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " , None , Network ::Testnet , db ) . unwrap ( ) ;
2020-08-06 18:11:07 +02:00
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
2020-08-06 18:11:07 +02:00
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , CACHE_ADDR_BATCH_SIZE - 1 )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_some ( ) ) ;
for _ in 0 .. CACHE_ADDR_BATCH_SIZE {
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) ;
2020-08-06 18:11:07 +02:00
}
assert! ( wallet
. database
. borrow_mut ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , CACHE_ADDR_BATCH_SIZE * 2 - 1 )
2020-08-06 18:11:07 +02:00
. unwrap ( )
. is_some ( ) ) ;
}
2020-08-10 17:16:47 +02:00
2020-08-15 23:21:13 +02:00
pub ( crate ) fn get_test_wpkh ( ) -> & 'static str {
2020-08-10 17:16:47 +02:00
" wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW) "
}
2020-08-15 23:21:13 +02:00
pub ( crate ) fn get_test_single_sig_csv ( ) -> & 'static str {
2020-08-10 17:16:47 +02:00
// and(pk(Alice),older(6))
" wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6))) "
}
2020-11-10 15:06:14 +01:00
pub ( crate ) fn get_test_a_or_b_plus_csv ( ) -> & 'static str {
// or(pk(Alice),and(pk(Bob),older(144)))
" wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),and_v(v:pk(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(144)))) "
}
2020-08-15 23:21:13 +02:00
pub ( crate ) fn get_test_single_sig_cltv ( ) -> & 'static str {
2020-08-10 17:16:47 +02:00
// and(pk(Alice),after(100000))
" wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000))) "
}
2022-04-27 16:29:02 +02:00
pub ( crate ) fn get_test_tr_single_sig ( ) -> & 'static str {
2022-05-18 19:55:21 +02:00
" tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG) "
2022-04-27 16:29:02 +02:00
}
2022-04-28 15:39:31 +02:00
pub ( crate ) fn get_test_tr_with_taptree ( ) -> & 'static str {
2022-04-29 12:59:09 +02:00
" tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)}) "
2022-04-28 15:39:31 +02:00
}
pub ( crate ) fn get_test_tr_repeated_key ( ) -> & 'static str {
" tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100)),and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(200))}) "
}
2022-05-18 19:55:21 +02:00
pub ( crate ) fn get_test_tr_single_sig_xprv ( ) -> & 'static str {
" tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*) "
}
pub ( crate ) fn get_test_tr_with_taptree_xprv ( ) -> & 'static str {
" tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)}) "
}
2020-08-13 16:51:27 +02:00
macro_rules ! assert_fee_rate {
( $tx :expr , $fees :expr , $fee_rate :expr $( , @ dust_change $( $dust_change :expr ) * ) * $( , @ add_signature $( $add_signature :expr ) * ) * ) = > ( {
let mut tx = $tx . clone ( ) ;
$(
$( $add_signature ) *
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
}
) *
#[ allow(unused_mut) ]
#[ allow(unused_assignments) ]
let mut dust_change = false ;
$(
$( $dust_change ) *
dust_change = true ;
) *
2022-04-14 17:20:46 +02:00
let tx_fee_rate = FeeRate ::from_wu ( $fees , tx . weight ( ) ) ;
2021-07-16 15:14:20 +10:00
let fee_rate = $fee_rate ;
2020-08-13 16:51:27 +02:00
if ! dust_change {
2021-07-16 15:14:20 +10:00
assert! ( ( tx_fee_rate - fee_rate ) . as_sat_vb ( ) . abs ( ) < 0.5 , " Expected fee rate of {:?}, the tx has {:?} " , fee_rate , tx_fee_rate ) ;
2020-08-13 16:51:27 +02:00
} else {
2021-07-16 15:14:20 +10:00
assert! ( tx_fee_rate > = fee_rate , " Expected fee rate of at least {:?}, the tx has {:?} " , fee_rate , tx_fee_rate ) ;
2020-08-13 16:51:27 +02:00
}
} ) ;
}
2022-04-27 16:29:02 +02:00
macro_rules ! from_str {
( $e :expr , $t :ty ) = > { {
use std ::str ::FromStr ;
< $t > ::from_str ( $e ) . unwrap ( )
} } ;
( $e :expr ) = > {
from_str! ( $e , _ )
} ;
}
2020-08-10 17:16:47 +02:00
#[ test ]
2020-10-28 10:37:47 +01:00
#[ should_panic(expected = " NoRecipients " ) ]
2020-08-31 10:49:44 +02:00
fn test_create_tx_empty_recipients ( ) {
2020-08-10 17:16:47 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:35:05 +11:00
wallet . build_tx ( ) . finish ( ) . unwrap ( ) ;
2020-10-28 10:37:47 +01:00
}
#[ test ]
#[ should_panic(expected = " NoUtxosSelected " ) ]
fn test_create_tx_manually_selected_empty_utxos ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. manually_selected_only ( ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(expected = " Invalid version `0` " ) ]
fn test_create_tx_version_0 ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. version ( 0 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(
expected = " TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV "
) ]
fn test_create_tx_version_1_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_csv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. version ( 1 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_custom_version ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. version ( 42 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . version , 42 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_default_locktime ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-03-30 16:29:31 +02:00
// Since we never synced the wallet we don't have a last_sync_height
// we could use to try to prevent fee sniping. We default to 0.
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . lock_time , 0 ) ;
2020-08-10 17:16:47 +02:00
}
2022-03-30 16:29:31 +02:00
#[ test ]
fn test_create_tx_fee_sniping_locktime_provided_height ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let sync_time = SyncTime {
block_time : BlockTime {
height : 24 ,
timestamp : 0 ,
} ,
} ;
wallet
. database
. borrow_mut ( )
. set_sync_time ( sync_time )
. unwrap ( ) ;
let current_height = 25 ;
2022-07-13 10:27:38 +02:00
builder . current_height ( current_height ) ;
2022-03-30 16:29:31 +02:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
// current_height will override the last sync height
assert_eq! ( psbt . unsigned_tx . lock_time , current_height ) ;
}
#[ test ]
fn test_create_tx_fee_sniping_locktime_last_sync ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let sync_time = SyncTime {
block_time : BlockTime {
height : 25 ,
timestamp : 0 ,
} ,
} ;
wallet
. database
. borrow_mut ( )
. set_sync_time ( sync_time . clone ( ) )
. unwrap ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
// If there's no current_height we're left with using the last sync height
assert_eq! ( psbt . unsigned_tx . lock_time , sync_time . block_time . height ) ;
}
2020-08-10 17:16:47 +02:00
#[ test ]
fn test_create_tx_default_locktime_cltv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_cltv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . lock_time , 100_000 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_custom_locktime ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2022-07-13 10:27:38 +02:00
. current_height ( 630_001 )
2021-01-11 14:14:14 +11:00
. nlocktime ( 630_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-03-30 16:29:31 +02:00
// When we explicitly specify a nlocktime
// we don't try any fee sniping prevention trick
// (we ignore the current_height)
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . lock_time , 630_000 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_custom_locktime_compatible_with_cltv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_cltv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. nlocktime ( 630_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . lock_time , 630_000 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(
expected = " TxBuilder requested timelock of `50000`, but at least `100000` is required to spend from this script "
) ]
fn test_create_tx_custom_locktime_incompatible_with_cltv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_cltv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. nlocktime ( 50000 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_no_rbf_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_csv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 6 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_with_default_rbf_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_csv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-12-07 14:48:17 +01:00
// When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
// It will be set to the OP_CSV value, in this case 6
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 6 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(
2020-12-07 14:48:17 +01:00
expected = " Cannot enable RBF with nSequence `3` given a required OP_CSV of `6` "
2020-08-10 17:16:47 +02:00
) ]
fn test_create_tx_with_custom_rbf_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_csv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf_with_sequence ( 3 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_no_rbf_cltv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_single_sig_cltv ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFE ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
2020-09-04 15:45:11 +02:00
#[ should_panic(expected = " Cannot enable RBF with a nSequence >= 0xFFFFFFFE " ) ]
2020-08-10 17:16:47 +02:00
fn test_create_tx_invalid_rbf_sequence ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf_with_sequence ( 0xFFFFFFFE ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_custom_rbf_sequence ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf_with_sequence ( 0xDEADBEEF ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 0xDEADBEEF ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_default_sequence ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFF ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(
expected = " The `change_policy` can be set only if the wallet has a change_descriptor "
) ]
fn test_create_tx_change_policy_no_internal ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. do_not_spend_change ( ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
2021-06-16 12:43:32 +10:00
fn test_create_tx_drain_wallet_and_drain_to ( ) {
2020-08-10 17:16:47 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 1 ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . output [ 0 ] . value ,
2021-06-12 15:01:44 +02:00
50_000 - details . fee . unwrap_or ( 0 )
2020-08-10 17:16:47 +02:00
) ;
}
2021-06-16 12:43:32 +10:00
#[ test ]
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt " ) . unwrap ( ) ;
let drain_addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , 20_000 )
. drain_to ( drain_addr . script_pubkey ( ) )
. drain_wallet ( ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2022-04-14 17:20:46 +02:00
let outputs = psbt . unsigned_tx . output ;
2021-06-16 12:43:32 +10:00
assert_eq! ( outputs . len ( ) , 2 ) ;
let main_output = outputs
. iter ( )
. find ( | x | x . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( ) ;
let drain_output = outputs
. iter ( )
. find ( | x | x . script_pubkey = = drain_addr . script_pubkey ( ) )
. unwrap ( ) ;
assert_eq! ( main_output . value , 20_000 , ) ;
assert_eq! ( drain_output . value , 30_000 - details . fee . unwrap_or ( 0 ) ) ;
}
2022-06-06 21:45:13 +02:00
#[ test ]
fn test_create_tx_drain_to_and_utxos ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let utxos : Vec < _ > = wallet
. get_available_utxos ( )
. unwrap ( )
. into_iter ( )
. map ( | ( u , _ ) | u . outpoint )
. collect ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. drain_to ( addr . script_pubkey ( ) )
. add_utxos ( & utxos )
. unwrap ( ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! (
psbt . unsigned_tx . output [ 0 ] . value ,
50_000 - details . fee . unwrap_or ( 0 )
) ;
}
#[ test ]
#[ should_panic(expected = " NoRecipients " ) ]
fn test_create_tx_drain_to_no_drain_wallet_no_utxos ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let drain_addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . drain_to ( drain_addr . script_pubkey ( ) ) ;
builder . finish ( ) . unwrap ( ) ;
}
2020-08-13 16:51:27 +02:00
#[ test ]
fn test_create_tx_default_fee_rate ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::default ( ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
fn test_create_tx_custom_fee_rate ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 5.0 ) ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 5.0 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
2020-10-20 18:10:59 +02:00
#[ test ]
fn test_create_tx_absolute_fee ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. fee_absolute ( 100 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 100 ) ;
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 1 ) ;
2020-10-22 09:11:58 +02:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . output [ 0 ] . value ,
2021-06-12 15:01:44 +02:00
50_000 - details . fee . unwrap_or ( 0 )
2020-10-22 09:11:58 +02:00
) ;
}
#[ test ]
fn test_create_tx_absolute_zero_fee ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. fee_absolute ( 0 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-22 09:11:58 +02:00
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 0 ) ;
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 1 ) ;
2020-10-22 09:11:58 +02:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . output [ 0 ] . value ,
2021-06-12 15:01:44 +02:00
50_000 - details . fee . unwrap_or ( 0 )
2020-10-22 09:11:58 +02:00
) ;
}
#[ test ]
#[ should_panic(expected = " InsufficientFunds " ) ]
fn test_create_tx_absolute_high_fee ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. fee_absolute ( 60_000 ) ;
let ( _psbt , _details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
}
2020-08-10 17:16:47 +02:00
#[ test ]
fn test_create_tx_add_change ( ) {
use super ::tx_builder ::TxOrdering ;
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. ordering ( TxOrdering ::Untouched ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 2 ) ;
assert_eq! ( psbt . unsigned_tx . output [ 0 ] . value , 25_000 ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . output [ 1 ] . value ,
2021-06-12 15:01:44 +02:00
25_000 - details . fee . unwrap_or ( 0 )
2020-08-10 17:16:47 +02:00
) ;
}
#[ test ]
fn test_create_tx_skip_change_dust ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 49_800 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! ( psbt . unsigned_tx . output [ 0 ] . value , 49_800 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 200 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
#[ should_panic(expected = " InsufficientFunds " ) ]
2021-06-16 12:43:32 +10:00
fn test_create_tx_drain_to_dust_amount ( ) {
2020-08-10 17:16:47 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
// very high fee rate, so that the only output would be below dust
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 453.0 ) ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_ordering_respected ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
. add_recipient ( addr . script_pubkey ( ) , 10_000 )
2021-03-30 16:33:07 +02:00
. ordering ( super ::tx_builder ::TxOrdering ::Bip69Lexicographic ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output . len ( ) , 3 ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . output [ 0 ] . value ,
2021-06-12 15:01:44 +02:00
10_000 - details . fee . unwrap_or ( 0 )
2020-08-10 17:16:47 +02:00
) ;
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . output [ 1 ] . value , 10_000 ) ;
assert_eq! ( psbt . unsigned_tx . output [ 2 ] . value , 30_000 ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_default_sighash ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 30_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2020-10-16 15:40:30 +02:00
assert_eq! ( psbt . inputs [ 0 ] . sighash_type , None ) ;
2020-08-10 17:16:47 +02:00
}
#[ test ]
fn test_create_tx_custom_sighash ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
2022-04-26 15:11:22 +02:00
. sighash ( bitcoin ::EcdsaSighashType ::Single . into ( ) ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
psbt . inputs [ 0 ] . sighash_type ,
2022-04-26 15:11:22 +02:00
Some ( bitcoin ::EcdsaSighashType ::Single . into ( ) )
2020-08-10 17:16:47 +02:00
) ;
}
#[ test ]
fn test_create_tx_input_hd_keypaths ( ) {
use bitcoin ::util ::bip32 ::{ DerivationPath , Fingerprint } ;
use std ::str ::FromStr ;
2020-09-21 15:44:07 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2021-02-02 20:06:40 -05:00
assert_eq! ( psbt . inputs [ 0 ] . bip32_derivation . len ( ) , 1 ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
2020-12-23 14:08:54 +11:00
psbt . inputs [ 0 ] . bip32_derivation . values ( ) . next ( ) . unwrap ( ) ,
2020-08-10 17:16:47 +02:00
& (
Fingerprint ::from_str ( " d34db33f " ) . unwrap ( ) ,
DerivationPath ::from_str ( " m/44'/0'/0'/0/0 " ) . unwrap ( )
)
) ;
}
#[ test ]
fn test_create_tx_output_hd_keypaths ( ) {
use bitcoin ::util ::bip32 ::{ DerivationPath , Fingerprint } ;
use std ::str ::FromStr ;
2020-09-21 15:44:07 +02:00
let ( wallet , descriptors , _ ) = get_funded_wallet ( " wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*) " ) ;
2020-08-10 17:16:47 +02:00
// cache some addresses
2021-03-08 16:17:10 -08:00
wallet . get_address ( New ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
let addr = testutils! ( @ external descriptors , 5 ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
2021-02-02 20:06:40 -05:00
assert_eq! ( psbt . outputs [ 0 ] . bip32_derivation . len ( ) , 1 ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
2020-12-23 14:08:54 +11:00
psbt . outputs [ 0 ] . bip32_derivation . values ( ) . next ( ) . unwrap ( ) ,
2020-08-10 17:16:47 +02:00
& (
Fingerprint ::from_str ( " d34db33f " ) . unwrap ( ) ,
DerivationPath ::from_str ( " m/44'/0'/0'/0/5 " ) . unwrap ( )
)
) ;
}
#[ test ]
fn test_create_tx_set_redeem_script_p2sh ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
let ( wallet , _ , _ ) =
get_funded_wallet ( " sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
psbt . inputs [ 0 ] . redeem_script ,
Some ( Script ::from (
Vec ::< u8 > ::from_hex (
" 21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac "
)
. unwrap ( )
) )
) ;
assert_eq! ( psbt . inputs [ 0 ] . witness_script , None ) ;
}
#[ test ]
fn test_create_tx_set_witness_script_p2wsh ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
let ( wallet , _ , _ ) =
get_funded_wallet ( " wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert_eq! ( psbt . inputs [ 0 ] . redeem_script , None ) ;
assert_eq! (
psbt . inputs [ 0 ] . witness_script ,
Some ( Script ::from (
Vec ::< u8 > ::from_hex (
" 21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac "
)
. unwrap ( )
) )
) ;
}
#[ test ]
fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
let ( wallet , _ , _ ) =
get_funded_wallet ( " sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
let script = Script ::from (
Vec ::< u8 > ::from_hex (
" 21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac " ,
)
. unwrap ( ) ,
) ;
assert_eq! ( psbt . inputs [ 0 ] . redeem_script , Some ( script . to_v0_p2wsh ( ) ) ) ;
assert_eq! ( psbt . inputs [ 0 ] . witness_script , Some ( script ) ) ;
}
#[ test ]
fn test_create_tx_non_witness_utxo ( ) {
let ( wallet , _ , _ ) =
get_funded_wallet ( " sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert! ( psbt . inputs [ 0 ] . non_witness_utxo . is_some ( ) ) ;
assert! ( psbt . inputs [ 0 ] . witness_utxo . is_none ( ) ) ;
}
#[ test ]
fn test_create_tx_only_witness_utxo ( ) {
let ( wallet , _ , _ ) =
get_funded_wallet ( " wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-04-20 14:58:33 +02:00
. only_witness_utxo ( )
2021-01-11 14:14:14 +11:00
. drain_wallet ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert! ( psbt . inputs [ 0 ] . non_witness_utxo . is_none ( ) ) ;
2020-11-17 12:30:58 +11:00
assert! ( psbt . inputs [ 0 ] . witness_utxo . is_some ( ) ) ;
}
#[ test ]
fn test_create_tx_shwpkh_has_witness_utxo ( ) {
let ( wallet , _ , _ ) =
get_funded_wallet ( " sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-11-17 12:30:58 +11:00
2020-08-10 17:16:47 +02:00
assert! ( psbt . inputs [ 0 ] . witness_utxo . is_some ( ) ) ;
}
#[ test ]
2021-04-20 14:58:33 +02:00
fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default ( ) {
2020-08-10 17:16:47 +02:00
let ( wallet , _ , _ ) =
get_funded_wallet ( " wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert! ( psbt . inputs [ 0 ] . non_witness_utxo . is_some ( ) ) ;
assert! ( psbt . inputs [ 0 ] . witness_utxo . is_some ( ) ) ;
}
2020-08-13 16:51:27 +02:00
2020-10-22 12:07:51 +11:00
#[ test ]
fn test_create_tx_add_utxo ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
let small_output_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-10-22 12:07:51 +11:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
. add_utxo ( OutPoint {
txid : small_output_txid ,
vout : 0 ,
} )
2020-10-22 12:07:51 +11:00
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-22 12:07:51 +11:00
assert_eq! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . input . len ( ) ,
2020-10-22 12:07:51 +11:00
2 ,
" should add an additional input since 25_000 < 30_000 "
) ;
assert_eq! ( details . sent , 75_000 , " total should be sum of both inputs " ) ;
}
#[ test ]
#[ should_panic(expected = " InsufficientFunds " ) ]
fn test_create_tx_manually_selected_insufficient ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
let small_output_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-10-22 12:07:51 +11:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
. add_utxo ( OutPoint {
txid : small_output_txid ,
vout : 0 ,
} )
. unwrap ( )
2021-01-11 14:14:14 +11:00
. manually_selected_only ( ) ;
builder . finish ( ) . unwrap ( ) ;
2020-10-22 12:07:51 +11:00
}
2020-11-10 15:06:14 +01:00
#[ test ]
#[ should_panic(expected = " SpendingPolicyRequired(External) " ) ]
fn test_create_tx_policy_path_required ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_a_or_b_plus_csv ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 30_000 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-11-10 15:06:14 +01:00
}
#[ test ]
fn test_create_tx_policy_path_no_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_a_or_b_plus_csv ( ) ) ;
2020-12-14 17:14:24 +01:00
let external_policy = wallet . policies ( KeychainKind ::External ) . unwrap ( ) . unwrap ( ) ;
2020-11-10 15:06:14 +01:00
let root_id = external_policy . id ;
// child #0 is just the key "A"
let path = vec! [ ( root_id , vec! [ 0 ] ) ] . into_iter ( ) . collect ( ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
2021-01-11 14:14:14 +11:00
. policy_path ( path , KeychainKind ::External ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-11-10 15:06:14 +01:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFF ) ;
2020-11-10 15:06:14 +01:00
}
#[ test ]
fn test_create_tx_policy_path_use_csv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_a_or_b_plus_csv ( ) ) ;
2020-12-14 17:14:24 +01:00
let external_policy = wallet . policies ( KeychainKind ::External ) . unwrap ( ) . unwrap ( ) ;
2020-11-10 15:06:14 +01:00
let root_id = external_policy . id ;
// child #1 is or(pk(B),older(144))
let path = vec! [ ( root_id , vec! [ 1 ] ) ] . into_iter ( ) . collect ( ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 30_000 )
2021-01-11 14:14:14 +11:00
. policy_path ( path , KeychainKind ::External ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-11-10 15:06:14 +01:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . unsigned_tx . input [ 0 ] . sequence , 144 ) ;
2020-11-10 15:06:14 +01:00
}
2020-12-01 11:10:14 +01:00
#[ test ]
fn test_create_tx_global_xpubs_with_origin ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
2022-04-14 17:20:46 +02:00
use bitcoin ::util ::bip32 ;
2020-12-01 11:10:14 +01:00
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. add_global_xpubs ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-12-01 11:10:14 +01:00
2022-04-14 17:20:46 +02:00
let key = bip32 ::ExtendedPubKey ::from_str ( " tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3 " ) . unwrap ( ) ;
let fingerprint = bip32 ::Fingerprint ::from_hex ( " 73756c7f " ) . unwrap ( ) ;
let path = bip32 ::DerivationPath ::from_str ( " m/48'/0'/0'/2' " ) . unwrap ( ) ;
2020-12-01 11:10:14 +01:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . xpub . len ( ) , 1 ) ;
assert_eq! ( psbt . xpub . get ( & key ) , Some ( & ( fingerprint , path ) ) ) ;
2020-12-01 11:10:14 +01:00
}
2021-02-08 15:40:56 +11:00
#[ test ]
fn test_add_foreign_utxo ( ) {
let ( wallet1 , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let ( wallet2 , _ , _ ) =
get_funded_wallet ( " wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm) " ) ;
2021-04-20 14:58:33 +02:00
2021-02-08 15:40:56 +11:00
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
let utxo = wallet2 . list_unspent ( ) . unwrap ( ) . remove ( 0 ) ;
let foreign_utxo_satisfaction = wallet2
. get_descriptor_for_keychain ( KeychainKind ::External )
. max_satisfaction_weight ( )
. unwrap ( ) ;
let psbt_input = psbt ::Input {
witness_utxo : Some ( utxo . txout . clone ( ) ) ,
.. Default ::default ( )
} ;
let mut builder = wallet1 . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , 60_000 )
2021-04-20 14:58:33 +02:00
. only_witness_utxo ( )
2021-02-08 15:40:56 +11:00
. add_foreign_utxo ( utxo . outpoint , psbt_input , foreign_utxo_satisfaction )
. unwrap ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2021-02-08 15:40:56 +11:00
assert_eq! (
details . sent - details . received ,
2021-06-12 15:01:44 +02:00
10_000 + details . fee . unwrap_or ( 0 ) ,
2021-02-08 15:40:56 +11:00
" we should have only net spent ~10_000 "
) ;
assert! (
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx
2021-02-08 15:40:56 +11:00
. input
. iter ( )
2021-04-08 12:19:40 -07:00
. any ( | input | input . previous_output = = utxo . outpoint ) ,
2021-02-08 15:40:56 +11:00
" foreign_utxo should be in there "
) ;
2021-04-20 14:58:33 +02:00
let finished = wallet1
. sign (
& mut psbt ,
SignOptions {
trust_witness_utxo : true ,
.. Default ::default ( )
} ,
)
. unwrap ( ) ;
2021-02-08 15:40:56 +11:00
assert! (
! finished ,
" only one of the inputs should have been signed so far "
) ;
2021-04-20 14:58:33 +02:00
let finished = wallet2
. sign (
& mut psbt ,
SignOptions {
trust_witness_utxo : true ,
.. Default ::default ( )
} ,
)
. unwrap ( ) ;
2021-02-08 15:40:56 +11:00
assert! ( finished , " all the inputs should have been signed now " ) ;
}
#[ test ]
#[ should_panic(expected = " Generic( \" Foreign utxo missing witness_utxo or non_witness_utxo \" ) " ) ]
fn test_add_foreign_utxo_invalid_psbt_input ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let mut builder = wallet . build_tx ( ) ;
let outpoint = wallet . list_unspent ( ) . unwrap ( ) [ 0 ] . outpoint ;
let foreign_utxo_satisfaction = wallet
. get_descriptor_for_keychain ( KeychainKind ::External )
. max_satisfaction_weight ( )
. unwrap ( ) ;
builder
. add_foreign_utxo ( outpoint , psbt ::Input ::default ( ) , foreign_utxo_satisfaction )
. unwrap ( ) ;
}
#[ test ]
fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input ( ) {
let ( wallet1 , _ , txid1 ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let ( wallet2 , _ , txid2 ) =
get_funded_wallet ( " wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm) " ) ;
let utxo2 = wallet2 . list_unspent ( ) . unwrap ( ) . remove ( 0 ) ;
let tx1 = wallet1
. database
. borrow ( )
. get_tx ( & txid1 , true )
. unwrap ( )
. unwrap ( )
. transaction
. unwrap ( ) ;
let tx2 = wallet2
. database
. borrow ( )
. get_tx ( & txid2 , true )
. unwrap ( )
. unwrap ( )
. transaction
. unwrap ( ) ;
let satisfaction_weight = wallet2
. get_descriptor_for_keychain ( KeychainKind ::External )
. max_satisfaction_weight ( )
. unwrap ( ) ;
let mut builder = wallet1 . build_tx ( ) ;
assert! (
builder
2021-02-12 10:35:20 +11:00
. add_foreign_utxo (
utxo2 . outpoint ,
psbt ::Input {
non_witness_utxo : Some ( tx1 ) ,
.. Default ::default ( )
} ,
satisfaction_weight
)
2021-02-08 15:40:56 +11:00
. is_err ( ) ,
" should fail when outpoint doesn't match psbt_input "
) ;
assert! (
builder
2021-02-12 10:35:20 +11:00
. add_foreign_utxo (
utxo2 . outpoint ,
psbt ::Input {
non_witness_utxo : Some ( tx2 ) ,
.. Default ::default ( )
} ,
satisfaction_weight
)
2021-02-08 15:40:56 +11:00
. is_ok ( ) ,
2021-02-12 10:20:11 +11:00
" shoulld be ok when outpoint does match psbt_input "
2021-02-08 15:40:56 +11:00
) ;
}
#[ test ]
2021-04-20 14:58:33 +02:00
fn test_add_foreign_utxo_only_witness_utxo ( ) {
2021-02-08 15:40:56 +11:00
let ( wallet1 , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let ( wallet2 , _ , txid2 ) =
get_funded_wallet ( " wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm) " ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
let utxo2 = wallet2 . list_unspent ( ) . unwrap ( ) . remove ( 0 ) ;
let satisfaction_weight = wallet2
. get_descriptor_for_keychain ( KeychainKind ::External )
. max_satisfaction_weight ( )
. unwrap ( ) ;
let mut builder = wallet1 . build_tx ( ) ;
2021-04-20 14:58:33 +02:00
builder . add_recipient ( addr . script_pubkey ( ) , 60_000 ) ;
2021-02-08 15:40:56 +11:00
{
let mut builder = builder . clone ( ) ;
let psbt_input = psbt ::Input {
witness_utxo : Some ( utxo2 . txout . clone ( ) ) ,
.. Default ::default ( )
} ;
builder
. add_foreign_utxo ( utxo2 . outpoint , psbt_input , satisfaction_weight )
. unwrap ( ) ;
assert! (
builder . finish ( ) . is_err ( ) ,
2021-02-12 10:35:20 +11:00
" psbt_input with witness_utxo should fail with only witness_utxo "
2021-02-08 15:40:56 +11:00
) ;
}
2021-04-20 14:58:33 +02:00
{
let mut builder = builder . clone ( ) ;
let psbt_input = psbt ::Input {
witness_utxo : Some ( utxo2 . txout . clone ( ) ) ,
.. Default ::default ( )
} ;
builder
. only_witness_utxo ( )
. add_foreign_utxo ( utxo2 . outpoint , psbt_input , satisfaction_weight )
. unwrap ( ) ;
assert! (
builder . finish ( ) . is_ok ( ) ,
" psbt_input with just witness_utxo should succeed when `only_witness_utxo` is enabled "
) ;
}
2021-02-08 15:40:56 +11:00
{
let mut builder = builder . clone ( ) ;
let tx2 = wallet2
. database
. borrow ( )
. get_tx ( & txid2 , true )
. unwrap ( )
. unwrap ( )
. transaction
. unwrap ( ) ;
let psbt_input = psbt ::Input {
non_witness_utxo : Some ( tx2 ) ,
.. Default ::default ( )
} ;
builder
. add_foreign_utxo ( utxo2 . outpoint , psbt_input , satisfaction_weight )
. unwrap ( ) ;
assert! (
builder . finish ( ) . is_ok ( ) ,
2021-04-20 14:58:33 +02:00
" psbt_input with non_witness_utxo should succeed by default "
2021-02-08 15:40:56 +11:00
) ;
}
}
2021-03-15 21:50:51 -04:00
#[ test ]
fn test_get_psbt_input ( ) {
// this should grab a known good utxo and set the input
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
for utxo in wallet . list_unspent ( ) . unwrap ( ) {
let psbt_input = wallet . get_psbt_input ( utxo , None , false ) . unwrap ( ) ;
assert! ( psbt_input . witness_utxo . is_some ( ) | | psbt_input . non_witness_utxo . is_some ( ) ) ;
}
}
2020-12-01 11:10:14 +01:00
#[ test ]
#[ should_panic(
expected = " MissingKeyOrigin( \" tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3 \" ) "
) ]
fn test_create_tx_global_xpubs_origin_missing ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. add_global_xpubs ( ) ;
builder . finish ( ) . unwrap ( ) ;
2020-12-01 11:10:14 +01:00
}
#[ test ]
fn test_create_tx_global_xpubs_master_without_origin ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
2022-04-14 17:20:46 +02:00
use bitcoin ::util ::bip32 ;
2020-12-01 11:10:14 +01:00
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. add_global_xpubs ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-12-01 11:10:14 +01:00
2022-04-14 17:20:46 +02:00
let key = bip32 ::ExtendedPubKey ::from_str ( " tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL " ) . unwrap ( ) ;
let fingerprint = bip32 ::Fingerprint ::from_hex ( " 997a323b " ) . unwrap ( ) ;
2020-12-01 11:10:14 +01:00
2022-04-14 17:20:46 +02:00
assert_eq! ( psbt . xpub . len ( ) , 1 ) ;
assert_eq! (
psbt . xpub . get ( & key ) ,
Some ( & ( fingerprint , bip32 ::DerivationPath ::default ( ) ) )
) ;
2020-12-01 11:10:14 +01:00
}
2020-08-13 16:51:27 +02:00
#[ test ]
#[ should_panic(expected = " IrreplaceableTransaction " ) ]
fn test_bump_fee_irreplaceable_tx ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , mut details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-08-13 16:51:27 +02:00
let tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the utxos, we know they can't be used anyways
details . transaction = Some ( tx ) ;
wallet . database . borrow_mut ( ) . set_tx ( & details ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
wallet . build_fee_bump ( txid ) . unwrap ( ) . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
#[ should_panic(expected = " TransactionConfirmed " ) ]
fn test_bump_fee_confirmed_tx ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , mut details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-08-13 16:51:27 +02:00
let tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the utxos, we know they can't be used anyways
details . transaction = Some ( tx ) ;
2021-11-03 16:05:30 +00:00
details . confirmation_time = Some ( BlockTime {
2021-06-12 15:01:44 +02:00
timestamp : 12345678 ,
height : 42 ,
} ) ;
2020-08-13 16:51:27 +02:00
wallet . database . borrow_mut ( ) . set_tx ( & details ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
wallet . build_fee_bump ( txid ) . unwrap ( ) . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
#[ should_panic(expected = " FeeRateTooLow " ) ]
fn test_bump_fee_low_fee_rate ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-08-13 16:51:27 +02:00
let tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the utxos, we know they can't be used anyways
details . transaction = Some ( tx ) ;
wallet . database . borrow_mut ( ) . set_tx ( & details ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 1.0 ) ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
}
2020-10-22 09:11:58 +02:00
#[ test ]
#[ should_panic(expected = " FeeTooLow " ) ]
2020-10-22 13:41:26 +02:00
fn test_bump_fee_low_abs ( ) {
2020-10-22 09:11:58 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-10-22 09:11:58 +02:00
let tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the utxos, we know they can't be used anyways
details . transaction = Some ( tx ) ;
wallet . database . borrow_mut ( ) . set_tx ( & details ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_absolute ( 10 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-10-22 09:11:58 +02:00
}
#[ test ]
#[ should_panic(expected = " FeeTooLow " ) ]
fn test_bump_fee_zero_abs ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-10-22 09:11:58 +02:00
let tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the utxos, we know they can't be used anyways
details . transaction = Some ( tx ) ;
wallet . database . borrow_mut ( ) . set_tx ( & details ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_absolute ( 0 ) ;
builder . finish ( ) . unwrap ( ) ;
2020-10-22 09:11:58 +02:00
}
2020-08-13 16:51:27 +02:00
#[ test ]
fn test_bump_fee_reduce_change ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
2021-04-12 15:38:35 +02:00
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 2.5 ) ) . enable_rbf ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent ) ;
assert_eq! (
2021-06-12 15:01:44 +02:00
details . received + details . fee . unwrap_or ( 0 ) ,
original_details . received + original_details . fee . unwrap_or ( 0 )
2020-08-13 16:51:27 +02:00
) ;
2021-06-12 15:01:44 +02:00
assert! ( details . fee . unwrap_or ( 0 ) > original_details . fee . unwrap_or ( 0 ) ) ;
2020-08-13 16:51:27 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
25_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 2.5 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
2020-10-20 18:10:59 +02:00
#[ test ]
fn test_bump_fee_absolute_reduce_change ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-10-20 18:10:59 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_absolute ( 200 ) ;
2021-04-12 15:38:35 +02:00
builder . enable_rbf ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
assert_eq! ( details . sent , original_details . sent ) ;
assert_eq! (
2021-06-12 15:01:44 +02:00
details . received + details . fee . unwrap_or ( 0 ) ,
original_details . received + original_details . fee . unwrap_or ( 0 )
2020-10-20 18:10:59 +02:00
) ;
assert! (
2021-06-12 15:01:44 +02:00
details . fee . unwrap_or ( 0 ) > original_details . fee . unwrap_or ( 0 ) ,
2020-10-20 18:10:59 +02:00
" {} > {} " ,
2021-06-12 15:01:44 +02:00
details . fee . unwrap_or ( 0 ) ,
original_details . fee . unwrap_or ( 0 )
2020-10-20 18:10:59 +02:00
) ;
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-10-20 18:10:59 +02:00
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
25_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 200 ) ;
2020-10-20 18:10:59 +02:00
}
2020-08-13 16:51:27 +02:00
#[ test ]
2020-10-28 10:37:47 +01:00
fn test_bump_fee_reduce_single_recipient ( ) {
2020-08-13 16:51:27 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
2021-01-01 13:35:05 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 2.5 ) )
2021-06-16 12:43:32 +10:00
. allow_shrinking ( addr . script_pubkey ( ) )
2021-01-29 12:10:22 +11:00
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent ) ;
2021-06-12 15:01:44 +02:00
assert! ( details . fee . unwrap_or ( 0 ) > original_details . fee . unwrap_or ( 0 ) ) ;
2020-08-13 16:51:27 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . output . len ( ) , 1 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( tx . output [ 0 ] . value + details . fee . unwrap_or ( 0 ) , details . sent ) ;
2020-08-13 16:51:27 +02:00
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 2.5 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
2020-10-20 18:10:59 +02:00
#[ test ]
2020-10-28 10:37:47 +01:00
fn test_bump_fee_absolute_reduce_single_recipient ( ) {
2020-10-20 18:10:59 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-10-20 18:10:59 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
2021-01-29 12:10:22 +11:00
builder
2021-06-16 12:43:32 +10:00
. allow_shrinking ( addr . script_pubkey ( ) )
2021-01-29 12:10:22 +11:00
. unwrap ( )
. fee_absolute ( 300 ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
assert_eq! ( details . sent , original_details . sent ) ;
2021-06-12 15:01:44 +02:00
assert! ( details . fee . unwrap_or ( 0 ) > original_details . fee . unwrap_or ( 0 ) ) ;
2020-10-20 18:10:59 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-10-20 18:10:59 +02:00
assert_eq! ( tx . output . len ( ) , 1 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( tx . output [ 0 ] . value + details . fee . unwrap_or ( 0 ) , details . sent ) ;
2020-10-20 18:10:59 +02:00
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 300 ) ;
2020-10-20 18:10:59 +02:00
}
2020-10-28 10:37:47 +01:00
#[ test ]
fn test_bump_fee_drain_wallet ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
// receive an extra tx so that our wallet has two utxos.
2021-01-01 13:22:33 +11:00
let incoming_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-10-28 10:37:47 +01:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let outpoint = OutPoint {
txid : incoming_txid ,
vout : 0 ,
} ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. add_utxo ( outpoint )
. unwrap ( )
. manually_selected_only ( )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-28 10:37:47 +01:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-10-28 10:37:47 +01:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
assert_eq! ( original_details . sent , 25_000 ) ;
// for the new feerate, it should be enough to reduce the output, but since we specify
// `drain_wallet` we expect to spend everything
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
2021-01-01 13:35:05 +11:00
. drain_wallet ( )
2021-06-16 12:43:32 +10:00
. allow_shrinking ( addr . script_pubkey ( ) )
2021-01-29 12:10:22 +11:00
. unwrap ( )
2021-01-11 14:14:14 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 5.0 ) ) ;
let ( _ , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-28 10:37:47 +01:00
assert_eq! ( details . sent , 75_000 ) ;
}
2020-08-13 16:51:27 +02:00
#[ test ]
#[ should_panic(expected = " InsufficientFunds " ) ]
2020-10-28 10:37:47 +01:00
fn test_bump_fee_remove_output_manually_selected_only ( ) {
2020-08-13 16:51:27 +02:00
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2020-10-28 10:37:47 +01:00
// receive an extra tx so that our wallet has two utxos. then we manually pick only one of
2021-01-22 14:04:06 +11:00
// them, and make sure that `bump_fee` doesn't try to add more. This fails because we've
// told the wallet it's not allowed to add more inputs AND it can't reduce the value of the
// existing output. In other words, bump_fee + manually_selected_only is always an error
2021-07-12 16:26:29 +10:00
// unless you've also set "allow_shrinking" OR there is a change output.
2021-01-01 13:22:33 +11:00
let incoming_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-08-13 16:51:27 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
2020-10-28 10:37:47 +01:00
let outpoint = OutPoint {
txid : incoming_txid ,
vout : 0 ,
} ;
2020-08-13 16:51:27 +02:00
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. add_utxo ( outpoint )
. unwrap ( )
. manually_selected_only ( )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
assert_eq! ( original_details . sent , 25_000 ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
2021-01-01 13:35:05 +11:00
. manually_selected_only ( )
2021-01-11 14:14:14 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 255.0 ) ) ;
builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
fn test_bump_fee_add_input ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-08-13 16:51:27 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 50.0 ) ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent + 25_000 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) + details . received , 30_000 ) ;
2020-08-13 16:51:27 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
45_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 50.0 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
2020-10-20 18:10:59 +02:00
#[ test ]
fn test_bump_fee_absolute_add_input ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-10-20 18:10:59 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-10-20 18:10:59 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_absolute ( 6_000 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
assert_eq! ( details . sent , original_details . sent + 25_000 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) + details . received , 30_000 ) ;
2020-10-20 18:10:59 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-10-20 18:10:59 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
45_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 6_000 ) ;
2020-10-20 18:10:59 +02:00
}
2020-08-13 16:51:27 +02:00
#[ test ]
fn test_bump_fee_no_change_add_input_and_change ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
let incoming_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-08-13 16:51:27 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
2021-07-12 16:26:29 +10:00
// initially make a tx without change by using `drain_to`
2020-08-13 16:51:27 +02:00
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2021-01-01 13:35:05 +11:00
. add_utxo ( OutPoint {
txid : incoming_txid ,
vout : 0 ,
} )
. unwrap ( )
. manually_selected_only ( )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2021-01-01 13:35:05 +11:00
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-06-16 12:43:32 +10:00
// now bump the fees without using `allow_shrinking`. the wallet should add an
2020-10-28 10:37:47 +01:00
// extra input and a change output, and leave the original output untouched
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 50.0 ) ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
2021-06-12 15:01:44 +02:00
let original_send_all_amount = original_details . sent - original_details . fee . unwrap_or ( 0 ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent + 50_000 ) ;
assert_eq! (
details . received ,
2021-06-12 15:01:44 +02:00
75_000 - original_send_all_amount - details . fee . unwrap_or ( 0 )
2020-08-13 16:51:27 +02:00
) ;
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
original_send_all_amount
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
2021-06-12 15:01:44 +02:00
75_000 - original_send_all_amount - details . fee . unwrap_or ( 0 )
2020-08-13 16:51:27 +02:00
) ;
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 50.0 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
fn test_bump_fee_add_input_change_dust ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-08-13 16:51:27 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
assert_eq! ( tx . input . len ( ) , 1 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
2021-12-19 02:55:24 -05:00
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 141.0 ) ) ;
2021-01-11 14:14:14 +11:00
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
2021-06-12 15:01:44 +02:00
assert_eq! (
original_details . received ,
5_000 - original_details . fee . unwrap_or ( 0 )
) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent + 25_000 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 30_000 ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . received , 0 ) ;
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 1 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
45_000
) ;
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 140.0 ) , @ dust_change , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
#[ test ]
fn test_bump_fee_force_add_input ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
let incoming_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-08-13 16:51:27 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-08-13 16:51:27 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
// the new fee_rate is low enough that just reducing the change would be fine, but we force
// the addition of an extra input with `add_utxo()`
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_utxo ( OutPoint {
txid : incoming_txid ,
vout : 0 ,
} )
. unwrap ( )
2021-01-11 14:14:14 +11:00
. fee_rate ( FeeRate ::from_sat_per_vb ( 5.0 ) ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-08-13 16:51:27 +02:00
assert_eq! ( details . sent , original_details . sent + 25_000 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) + details . received , 30_000 ) ;
2020-08-13 16:51:27 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-08-13 16:51:27 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
45_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_fee_rate! ( psbt . extract_tx ( ) , details . fee . unwrap_or ( 0 ) , FeeRate ::from_sat_per_vb ( 5.0 ) , @ add_signature ) ;
2020-08-13 16:51:27 +02:00
}
2020-08-30 20:34:51 +02:00
2020-10-20 18:10:59 +02:00
#[ test ]
fn test_bump_fee_absolute_force_add_input ( ) {
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
2021-01-01 13:22:33 +11:00
let incoming_txid = crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2020-10-20 18:10:59 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
// skip saving the new utxos, we know they can't be used anyways
for txin in & mut tx . input {
2022-04-14 17:20:46 +02:00
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
2020-10-20 18:10:59 +02:00
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
// the new fee_rate is low enough that just reducing the change would be fine, but we force
// the addition of an extra input with `add_utxo()`
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_utxo ( OutPoint {
txid : incoming_txid ,
vout : 0 ,
} )
. unwrap ( )
2021-01-11 14:14:14 +11:00
. fee_absolute ( 250 ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
2020-10-20 18:10:59 +02:00
assert_eq! ( details . sent , original_details . sent + 25_000 ) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) + details . received , 30_000 ) ;
2020-10-20 18:10:59 +02:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-10-20 18:10:59 +02:00
assert_eq! ( tx . input . len ( ) , 2 ) ;
assert_eq! ( tx . output . len ( ) , 2 ) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey = = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
45_000
) ;
assert_eq! (
tx . output
. iter ( )
. find ( | txout | txout . script_pubkey ! = addr . script_pubkey ( ) )
. unwrap ( )
. value ,
details . received
) ;
2021-06-12 15:01:44 +02:00
assert_eq! ( details . fee . unwrap_or ( 0 ) , 250 ) ;
2020-10-20 18:10:59 +02:00
}
2022-07-01 11:11:11 +02:00
#[ test ]
#[ should_panic(expected = " InsufficientFunds " ) ]
fn test_bump_fee_unconfirmed_inputs_only ( ) {
// We try to bump the fee, but:
// - We can't reduce the change, as we have no change
// - All our UTXOs are unconfirmed
// So, we fail with "InsufficientFunds", as per RBF rule 2:
// The replacement transaction may only include an unconfirmed input
// if that input was included in one of the original transactions.
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. drain_wallet ( )
. drain_to ( addr . script_pubkey ( ) )
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
// Now we receive one transaction with 0 confirmations. We won't be able to use that for
// fee bumping, as it's still unconfirmed!
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 0 ) ) ,
Some ( 100 ) ,
) ;
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder . fee_rate ( FeeRate ::from_sat_per_vb ( 25.0 ) ) ;
builder . finish ( ) . unwrap ( ) ;
}
#[ test ]
fn test_bump_fee_unconfirmed_input ( ) {
// We create a tx draining the wallet and spending one confirmed
// and one unconfirmed UTXO. We check that we can fee bump normally
// (BIP125 rule 2 only apply to newly added unconfirmed input, you can
// always fee bump with an unconfirmed input if it was included in the
// original transaction)
let ( wallet , descriptors , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
// We receive a tx with 0 confirmations, which will be used as an input
// in the drain tx.
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 0 ) ) ,
Some ( 100 ) ,
) ;
let mut builder = wallet . build_tx ( ) ;
builder
. drain_wallet ( )
. drain_to ( addr . script_pubkey ( ) )
. enable_rbf ( ) ;
let ( psbt , mut original_details ) = builder . finish ( ) . unwrap ( ) ;
let mut tx = psbt . extract_tx ( ) ;
let txid = tx . txid ( ) ;
for txin in & mut tx . input {
txin . witness . push ( [ 0x00 ; 108 ] ) ; // fake signature
wallet
. database
. borrow_mut ( )
. del_utxo ( & txin . previous_output )
. unwrap ( ) ;
}
original_details . transaction = Some ( tx ) ;
wallet
. database
. borrow_mut ( )
. set_tx ( & original_details )
. unwrap ( ) ;
let mut builder = wallet . build_fee_bump ( txid ) . unwrap ( ) ;
builder
. fee_rate ( FeeRate ::from_sat_per_vb ( 15.0 ) )
. allow_shrinking ( addr . script_pubkey ( ) )
. unwrap ( ) ;
builder . finish ( ) . unwrap ( ) ;
}
2020-08-30 20:34:51 +02:00
#[ test ]
fn test_sign_single_xprv ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-30 20:34:51 +02:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2020-08-30 20:34:51 +02:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2021-03-11 17:39:02 -05:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
#[ test ]
fn test_sign_single_xprv_with_master_fingerprint_and_path ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
2021-03-22 10:26:56 -07:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-03-11 17:39:02 -05:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2021-03-11 17:39:02 -05:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2021-03-11 17:39:02 -05:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2020-09-29 18:18:50 +02:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
#[ test ]
fn test_sign_single_xprv_bip44_path ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-09-29 18:18:50 +02:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2020-09-29 18:18:50 +02:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2020-08-30 20:34:51 +02:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
2020-09-16 17:31:43 +02:00
#[ test ]
fn test_sign_single_xprv_sh_wpkh ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-09-16 17:31:43 +02:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2020-09-16 17:31:43 +02:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2020-09-16 17:31:43 +02:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
2020-08-30 20:34:51 +02:00
#[ test ]
fn test_sign_single_wif ( ) {
let ( wallet , _ , _ ) =
get_funded_wallet ( " wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-04-28 09:53:03 +10:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-30 20:34:51 +02:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2020-08-30 20:34:51 +02:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2020-08-30 20:34:51 +02:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
#[ test ]
fn test_sign_single_xprv_no_hd_keypaths ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
2021-03-08 16:17:10 -08:00
let addr = wallet . get_address ( New ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
2021-06-16 12:43:32 +10:00
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
2021-01-11 14:14:14 +11:00
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-30 20:34:51 +02:00
2021-02-02 20:06:40 -05:00
psbt . inputs [ 0 ] . bip32_derivation . clear ( ) ;
assert_eq! ( psbt . inputs [ 0 ] . bip32_derivation . len ( ) , 0 ) ;
2020-08-30 20:34:51 +02:00
2021-04-19 14:16:39 +02:00
let finalized = wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ;
2021-05-06 14:22:30 +10:00
assert! ( finalized ) ;
2020-08-30 20:34:51 +02:00
2021-04-28 09:53:03 +10:00
let extracted = psbt . extract_tx ( ) ;
2020-08-30 20:34:51 +02:00
assert_eq! ( extracted . input [ 0 ] . witness . len ( ) , 2 ) ;
}
2020-11-16 16:25:16 -06:00
#[ test ]
fn test_include_output_redeem_witness_script ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu))) " ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. include_output_redeem_witness_script ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-11-16 16:25:16 -06:00
// p2sh-p2wsh transaction should contain both witness and redeem scripts
assert! ( psbt
. outputs
. iter ( )
. any ( | output | output . redeem_script . is_some ( ) & & output . witness_script . is_some ( ) ) ) ;
}
2020-11-17 17:53:06 +11:00
#[ test ]
fn test_signing_only_one_of_multiple_inputs ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
2021-01-11 14:14:14 +11:00
let mut builder = wallet . build_tx ( ) ;
builder
2021-01-01 13:35:05 +11:00
. add_recipient ( addr . script_pubkey ( ) , 45_000 )
2021-01-11 14:14:14 +11:00
. include_output_redeem_witness_script ( ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-11-17 17:53:06 +11:00
// add another input to the psbt that is at least passable.
2020-12-23 14:16:43 +11:00
let dud_input = bitcoin ::util ::psbt ::Input {
witness_utxo : Some ( TxOut {
value : 100_000 ,
script_pubkey : miniscript ::Descriptor ::< bitcoin ::PublicKey > ::from_str (
" wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357) " ,
)
. unwrap ( )
. script_pubkey ( ) ,
} ) ,
.. Default ::default ( )
} ;
2020-11-17 17:53:06 +11:00
psbt . inputs . push ( dud_input ) ;
2022-04-14 17:20:46 +02:00
psbt . unsigned_tx . input . push ( bitcoin ::TxIn ::default ( ) ) ;
2021-04-20 14:58:33 +02:00
let is_final = wallet
. sign (
& mut psbt ,
SignOptions {
trust_witness_utxo : true ,
.. Default ::default ( )
} ,
)
. unwrap ( ) ;
2020-11-17 17:53:06 +11:00
assert! (
! is_final ,
" shouldn't be final since we can't sign one of the inputs "
) ;
assert! (
psbt . inputs [ 0 ] . final_script_witness . is_some ( ) ,
" should finalized input it signed "
)
}
2021-03-07 21:57:19 -08:00
2022-05-29 10:53:37 +07:00
#[ test ]
fn test_remove_partial_sigs_after_finalize_sign_option ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
for remove_partial_sigs in & [ true , false ] {
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
let mut psbt = builder . finish ( ) . unwrap ( ) . 0 ;
assert! ( wallet
. sign (
& mut psbt ,
SignOptions {
remove_partial_sigs : * remove_partial_sigs ,
.. Default ::default ( )
} ,
)
. unwrap ( ) ) ;
psbt . inputs . iter ( ) . for_each ( | input | {
if * remove_partial_sigs {
assert! ( input . partial_sigs . is_empty ( ) )
} else {
assert! ( ! input . partial_sigs . is_empty ( ) )
}
} ) ;
}
}
2022-06-04 12:42:52 +07:00
#[ test ]
fn test_try_finalize_sign_option ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
for try_finalize in & [ true , false ] {
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
let mut psbt = builder . finish ( ) . unwrap ( ) . 0 ;
let finalized = wallet
. sign (
& mut psbt ,
SignOptions {
try_finalize : * try_finalize ,
.. Default ::default ( )
} ,
)
. unwrap ( ) ;
psbt . inputs . iter ( ) . for_each ( | input | {
if * try_finalize {
assert! ( finalized ) ;
assert! ( input . final_script_sig . is_some ( ) ) ;
assert! ( input . final_script_witness . is_some ( ) ) ;
} else {
assert! ( ! finalized ) ;
assert! ( input . final_script_sig . is_none ( ) ) ;
assert! ( input . final_script_witness . is_none ( ) ) ;
}
} ) ;
}
}
2021-03-07 21:57:19 -08:00
2021-05-26 10:34:25 +02:00
#[ test ]
fn test_sign_nonstandard_sighash ( ) {
2022-04-26 15:11:22 +02:00
let sighash = EcdsaSighashType ::NonePlusAnyoneCanPay ;
2021-05-26 10:34:25 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( " wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*) " ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
2021-06-16 12:43:32 +10:00
. drain_to ( addr . script_pubkey ( ) )
2022-04-14 17:20:46 +02:00
. sighash ( sighash . into ( ) )
2021-05-26 10:34:25 +02:00
. drain_wallet ( ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
let result = wallet . sign ( & mut psbt , Default ::default ( ) ) ;
assert! (
result . is_err ( ) ,
" Signing should have failed because the TX uses non-standard sighashes "
) ;
assert! (
matches! (
result . unwrap_err ( ) ,
Error ::Signer ( SignerError ::NonStandardSighash )
) ,
" Signing failed with the wrong error type "
) ;
// try again after opting-in
let result = wallet . sign (
& mut psbt ,
SignOptions {
allow_all_sighashes : true ,
.. Default ::default ( )
} ,
) ;
assert! ( result . is_ok ( ) , " Signing should have worked " ) ;
assert! (
result . unwrap ( ) ,
" Should finalize the input since we can produce signatures "
) ;
let extracted = psbt . extract_tx ( ) ;
assert_eq! (
2022-04-14 17:20:46 +02:00
* extracted . input [ 0 ] . witness . to_vec ( ) [ 0 ] . last ( ) . unwrap ( ) ,
sighash . to_u32 ( ) as u8 ,
2021-05-26 10:34:25 +02:00
" The signature should have been made with the right sighash "
) ;
}
2021-03-07 21:57:19 -08:00
#[ test ]
fn test_unused_address ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
2021-03-07 21:57:19 -08:00
None , Network ::Testnet , db ) . unwrap ( ) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( LastUnused ) . unwrap ( ) . to_string ( ) ,
2021-03-07 21:57:19 -08:00
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( LastUnused ) . unwrap ( ) . to_string ( ) ,
2021-03-07 21:57:19 -08:00
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
}
#[ test ]
fn test_next_unused_address ( ) {
let descriptor = " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ;
let descriptors = testutils! ( @ descriptors ( descriptor ) ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new (
2021-03-07 21:57:19 -08:00
& descriptors . 0 ,
None ,
Network ::Testnet ,
MemoryDatabase ::new ( ) ,
)
. unwrap ( ) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( LastUnused ) . unwrap ( ) . to_string ( ) ,
2021-03-07 21:57:19 -08:00
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
// use the above address
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
Some ( 100 ) ,
) ;
assert_eq! (
2021-03-08 16:17:10 -08:00
wallet . get_address ( LastUnused ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
}
#[ test ]
fn test_peek_address_at_index ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
2021-03-08 16:17:10 -08:00
None , Network ::Testnet , db ) . unwrap ( ) ;
assert_eq! (
wallet . get_address ( Peek ( 1 ) ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
assert_eq! (
wallet . get_address ( Peek ( 0 ) ) . unwrap ( ) . to_string ( ) ,
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
assert_eq! (
wallet . get_address ( Peek ( 2 ) ) . unwrap ( ) . to_string ( ) ,
" tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 "
) ;
// current new address is not affected
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
}
#[ test ]
fn test_peek_address_at_index_not_derivable ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1) " ,
2021-03-08 16:17:10 -08:00
None , Network ::Testnet , db ) . unwrap ( ) ;
assert_eq! (
wallet . get_address ( Peek ( 1 ) ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
assert_eq! (
wallet . get_address ( Peek ( 0 ) ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
assert_eq! (
wallet . get_address ( Peek ( 2 ) ) . unwrap ( ) . to_string ( ) ,
2021-03-07 21:57:19 -08:00
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
}
2021-03-10 15:58:58 -08:00
#[ test ]
fn test_reset_address_index ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
2021-03-10 15:58:58 -08:00
None , Network ::Testnet , db ) . unwrap ( ) ;
// new index 0
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a "
) ;
// new index 1
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
// new index 2
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 "
) ;
// reset index 1 again
assert_eq! (
wallet . get_address ( Reset ( 1 ) ) . unwrap ( ) . to_string ( ) ,
" tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 "
) ;
// new index 2 again
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) . to_string ( ) ,
" tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 "
) ;
}
2021-05-17 16:31:18 -04:00
#[ test ]
fn test_returns_index_and_address ( ) {
let db = MemoryDatabase ::new ( ) ;
2022-01-26 15:17:48 +11:00
let wallet = Wallet ::new ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
2021-05-17 16:31:18 -04:00
None , Network ::Testnet , db ) . unwrap ( ) ;
// new index 0
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 0 ,
address : Address ::from_str ( " tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a " ) . unwrap ( ) ,
2022-03-10 06:22:02 +05:30
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
// new index 1
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 1 ,
2022-03-10 06:22:02 +05:30
address : Address ::from_str ( " tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 " ) . unwrap ( ) ,
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
// peek index 25
assert_eq! (
wallet . get_address ( Peek ( 25 ) ) . unwrap ( ) ,
AddressInfo {
index : 25 ,
2022-03-10 06:22:02 +05:30
address : Address ::from_str ( " tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2 " ) . unwrap ( ) ,
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
// new index 2
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 2 ,
2022-03-10 06:22:02 +05:30
address : Address ::from_str ( " tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 " ) . unwrap ( ) ,
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
// reset index 1 again
assert_eq! (
wallet . get_address ( Reset ( 1 ) ) . unwrap ( ) ,
AddressInfo {
index : 1 ,
2022-03-10 06:22:02 +05:30
address : Address ::from_str ( " tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 " ) . unwrap ( ) ,
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
// new index 2 again
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 2 ,
2022-03-10 06:22:02 +05:30
address : Address ::from_str ( " tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 " ) . unwrap ( ) ,
keychain : KeychainKind ::External ,
2021-05-17 16:31:18 -04:00
}
) ;
}
2021-09-13 21:59:20 +05:30
#[ test ]
fn test_sending_to_bip350_bech32m_address ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let addr =
Address ::from_str ( " tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c " )
. unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 45_000 ) ;
builder . finish ( ) . unwrap ( ) ;
}
2021-08-19 19:57:35 +10:00
#[ test ]
fn test_get_address ( ) {
use crate ::descriptor ::template ::Bip84 ;
let key = bitcoin ::util ::bip32 ::ExtendedPrivKey ::from_str ( " tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy " ) . unwrap ( ) ;
2022-03-03 13:13:45 +11:00
let wallet = Wallet ::new (
2021-08-19 19:57:35 +10:00
Bip84 ( key , KeychainKind ::External ) ,
Some ( Bip84 ( key , KeychainKind ::Internal ) ) ,
Network ::Regtest ,
MemoryDatabase ::default ( ) ,
)
. unwrap ( ) ;
assert_eq! (
2022-03-10 06:22:02 +05:30
wallet . get_address ( AddressIndex ::New ) . unwrap ( ) ,
AddressInfo {
index : 0 ,
2022-04-15 20:08:30 +03:00
address : Address ::from_str ( " bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w " ) . unwrap ( ) ,
2022-03-10 06:22:02 +05:30
keychain : KeychainKind ::External ,
}
2021-08-19 19:57:35 +10:00
) ;
2022-03-10 06:22:02 +05:30
2021-08-19 19:57:35 +10:00
assert_eq! (
2022-03-10 06:22:02 +05:30
wallet . get_internal_address ( AddressIndex ::New ) . unwrap ( ) ,
AddressInfo {
index : 0 ,
2022-04-15 20:08:30 +03:00
address : Address ::from_str ( " bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79 " ) . unwrap ( ) ,
2022-03-10 06:22:02 +05:30
keychain : KeychainKind ::Internal ,
}
2021-08-19 19:57:35 +10:00
) ;
2022-03-03 13:13:45 +11:00
let wallet = Wallet ::new (
2021-08-19 19:57:35 +10:00
Bip84 ( key , KeychainKind ::External ) ,
None ,
Network ::Regtest ,
MemoryDatabase ::default ( ) ,
)
. unwrap ( ) ;
assert_eq! (
2022-03-10 06:22:02 +05:30
wallet . get_internal_address ( AddressIndex ::New ) . unwrap ( ) ,
AddressInfo {
index : 0 ,
2022-04-15 20:08:30 +03:00
address : Address ::from_str ( " bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w " ) . unwrap ( ) ,
2022-03-10 06:22:02 +05:30
keychain : KeychainKind ::Internal ,
} ,
2021-08-19 19:57:35 +10:00
" when there's no internal descriptor it should just use external "
) ;
}
2022-04-27 16:29:02 +02:00
2022-07-09 20:29:58 +08:00
#[ test ]
fn test_get_address_no_reuse_single_descriptor ( ) {
use crate ::descriptor ::template ::Bip84 ;
use std ::collections ::HashSet ;
let key = bitcoin ::util ::bip32 ::ExtendedPrivKey ::from_str ( " tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy " ) . unwrap ( ) ;
let wallet = Wallet ::new (
Bip84 ( key , KeychainKind ::External ) ,
None ,
Network ::Regtest ,
MemoryDatabase ::default ( ) ,
)
. unwrap ( ) ;
let mut used_set = HashSet ::new ( ) ;
( 0 .. 3 ) . for_each ( | _ | {
let external_addr = wallet . get_address ( AddressIndex ::New ) . unwrap ( ) . address ;
assert! ( used_set . insert ( external_addr ) ) ;
let internal_addr = wallet
. get_internal_address ( AddressIndex ::New )
. unwrap ( )
. address ;
assert! ( used_set . insert ( internal_addr ) ) ;
} ) ;
}
2022-04-27 16:29:02 +02:00
#[ test ]
fn test_taproot_psbt_populate_tap_key_origins ( ) {
2022-05-18 19:55:21 +02:00
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig_xprv ( ) ) ;
2022-04-27 16:29:02 +02:00
let addr = wallet . get_address ( AddressIndex ::New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
assert_eq! (
psbt . inputs [ 0 ]
. tap_key_origins
. clone ( )
. into_iter ( )
. collect ::< Vec < _ > > ( ) ,
vec! [ (
from_str! ( " b96d3a3dc76a4fc74e976511b23aecb78e0754c23c0ed7a6513e18cbbc7178e9 " ) ,
( vec! [ ] , ( from_str! ( " f6a5cb8b " ) , from_str! ( " m/0 " ) ) )
) ] ,
" Wrong input tap_key_origins "
) ;
assert_eq! (
psbt . outputs [ 0 ]
. tap_key_origins
. clone ( )
. into_iter ( )
. collect ::< Vec < _ > > ( ) ,
vec! [ (
from_str! ( " e9b03068cf4a2621d4f81e68f6c4216e6bd260fe6edf6acc55c8d8ae5aeff0a8 " ) ,
( vec! [ ] , ( from_str! ( " f6a5cb8b " ) , from_str! ( " m/1 " ) ) )
) ] ,
" Wrong output tap_key_origins "
) ;
}
2022-04-28 15:39:31 +02:00
#[ test ]
fn test_taproot_psbt_populate_tap_key_origins_repeated_key ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_repeated_key ( ) ) ;
let addr = wallet . get_address ( AddressIndex ::New ) . unwrap ( ) ;
2022-05-24 11:24:48 +02:00
let path = vec! [ ( " rn4nre9c " . to_string ( ) , vec! [ 0 ] ) ]
2022-04-28 15:39:31 +02:00
. into_iter ( )
. collect ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , 25_000 )
. policy_path ( path , KeychainKind ::External ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
assert_eq! (
psbt . inputs [ 0 ]
. tap_key_origins
. clone ( )
. into_iter ( )
. collect ::< Vec < _ > > ( ) ,
vec! [ (
from_str! ( " 2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3 " ) ,
(
vec! [
from_str! (
" 858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e "
) ,
from_str! (
" f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903 "
)
] ,
( Default ::default ( ) , Default ::default ( ) )
)
) ] ,
" Wrong input tap_key_origins "
) ;
assert_eq! (
psbt . outputs [ 0 ]
. tap_key_origins
. clone ( )
. into_iter ( )
. collect ::< Vec < _ > > ( ) ,
vec! [ (
from_str! ( " 2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3 " ) ,
(
vec! [
from_str! (
" 858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e "
) ,
from_str! (
" f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903 "
)
] ,
( Default ::default ( ) , Default ::default ( ) )
)
) ] ,
" Wrong output tap_key_origins "
) ;
}
#[ test ]
fn test_taproot_psbt_input_tap_tree ( ) {
2022-05-31 17:47:26 +02:00
use crate ::bitcoin ::psbt ::serialize ::Deserialize ;
use crate ::bitcoin ::psbt ::TapTree ;
2022-04-28 15:39:31 +02:00
use bitcoin ::hashes ::hex ::FromHex ;
use bitcoin ::util ::taproot ;
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_with_taptree ( ) ) ;
let addr = wallet . get_address ( AddressIndex ::Peek ( 0 ) ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
assert_eq! (
psbt . inputs [ 0 ] . tap_merkle_root ,
Some (
FromHex ::from_hex (
2022-04-29 12:59:09 +02:00
" 61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986 "
2022-04-28 15:39:31 +02:00
)
. unwrap ( )
) ,
) ;
assert_eq! (
psbt . inputs [ 0 ] . tap_scripts . clone ( ) . into_iter ( ) . collect ::< Vec < _ > > ( ) ,
vec! [
2022-04-29 12:59:09 +02:00
( taproot ::ControlBlock ::from_slice ( & Vec ::< u8 > ::from_hex ( " c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2 " ) . unwrap ( ) ) . unwrap ( ) , ( from_str! ( " 208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac " ) , taproot ::LeafVersion ::TapScript ) ) ,
( taproot ::ControlBlock ::from_slice ( & Vec ::< u8 > ::from_hex ( " c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834 " ) . unwrap ( ) ) . unwrap ( ) , ( from_str! ( " 2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac " ) , taproot ::LeafVersion ::TapScript ) ) ,
2022-04-28 15:39:31 +02:00
] ,
) ;
assert_eq! (
psbt . inputs [ 0 ] . tap_internal_key ,
Some ( from_str! (
" b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55 "
) )
) ;
// Since we are creating an output to the same address as the input, assert that the
// internal_key is the same
assert_eq! (
psbt . inputs [ 0 ] . tap_internal_key ,
psbt . outputs [ 0 ] . tap_internal_key
) ;
2022-05-31 17:47:26 +02:00
assert_eq! (
psbt . outputs [ 0 ] . tap_tree ,
Some ( TapTree ::deserialize ( & Vec ::< u8 > ::from_hex ( " 01c022208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac01c0222051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac " , ) . unwrap ( ) ) . unwrap ( ) )
) ;
2022-04-28 15:39:31 +02:00
}
2022-05-18 19:55:21 +02:00
#[ test ]
fn test_taproot_sign_missing_witness_utxo ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . drain_to ( addr . script_pubkey ( ) ) . drain_wallet ( ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
let witness_utxo = psbt . inputs [ 0 ] . witness_utxo . take ( ) ;
let result = wallet . sign (
& mut psbt ,
SignOptions {
allow_all_sighashes : true ,
.. Default ::default ( )
} ,
) ;
assert! (
result . is_err ( ) ,
" Signing should have failed because the witness_utxo is missing "
) ;
assert! (
matches! (
result . unwrap_err ( ) ,
Error ::Signer ( SignerError ::MissingWitnessUtxo )
) ,
" Signing failed with the wrong error type "
) ;
// restore the witness_utxo
psbt . inputs [ 0 ] . witness_utxo = witness_utxo ;
let result = wallet . sign (
& mut psbt ,
SignOptions {
allow_all_sighashes : true ,
.. Default ::default ( )
} ,
) ;
assert! ( result . is_ok ( ) , " Signing should have worked " ) ;
assert! (
result . unwrap ( ) ,
" Should finalize the input since we can produce signatures "
) ;
}
2022-04-28 15:39:31 +02:00
#[ test ]
fn test_taproot_foreign_utxo ( ) {
let ( wallet1 , _ , _ ) = get_funded_wallet ( get_test_wpkh ( ) ) ;
let ( wallet2 , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig ( ) ) ;
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
let utxo = wallet2 . list_unspent ( ) . unwrap ( ) . remove ( 0 ) ;
let psbt_input = wallet2 . get_psbt_input ( utxo . clone ( ) , None , false ) . unwrap ( ) ;
let foreign_utxo_satisfaction = wallet2
. get_descriptor_for_keychain ( KeychainKind ::External )
. max_satisfaction_weight ( )
. unwrap ( ) ;
assert! (
psbt_input . non_witness_utxo . is_none ( ) ,
" `non_witness_utxo` should never be populated for taproot "
) ;
let mut builder = wallet1 . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , 60_000 )
. add_foreign_utxo ( utxo . outpoint , psbt_input , foreign_utxo_satisfaction )
. unwrap ( ) ;
let ( psbt , details ) = builder . finish ( ) . unwrap ( ) ;
assert_eq! (
details . sent - details . received ,
10_000 + details . fee . unwrap_or ( 0 ) ,
" we should have only net spent ~10_000 "
) ;
assert! (
psbt . unsigned_tx
. input
. iter ( )
. any ( | input | input . previous_output = = utxo . outpoint ) ,
" foreign_utxo should be in there "
) ;
}
2022-04-29 12:59:09 +02:00
2022-05-18 19:55:21 +02:00
fn test_spend_from_wallet ( wallet : Wallet < AnyDatabase > ) {
2022-04-29 12:59:09 +02:00
let addr = wallet . get_address ( AddressIndex ::New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
assert! (
wallet . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ,
2022-05-18 19:55:21 +02:00
" Unable to finalize tx "
2022-04-29 12:59:09 +02:00
) ;
}
2022-05-18 19:55:21 +02:00
#[ test ]
fn test_taproot_key_spend ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig ( ) ) ;
test_spend_from_wallet ( wallet ) ;
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig_xprv ( ) ) ;
test_spend_from_wallet ( wallet ) ;
}
2022-04-29 12:59:09 +02:00
#[ test ]
fn test_taproot_script_spend ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_with_taptree ( ) ) ;
2022-05-18 19:55:21 +02:00
test_spend_from_wallet ( wallet ) ;
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_with_taptree_xprv ( ) ) ;
test_spend_from_wallet ( wallet ) ;
}
#[ test ]
fn test_taproot_sign_derive_index_from_psbt ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig_xprv ( ) ) ;
2022-04-29 12:59:09 +02:00
let addr = wallet . get_address ( AddressIndex ::New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder . add_recipient ( addr . script_pubkey ( ) , 25_000 ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2022-05-18 19:55:21 +02:00
// re-create the wallet with an empty db
let wallet_empty = Wallet ::new (
get_test_tr_single_sig_xprv ( ) ,
None ,
Network ::Regtest ,
AnyDatabase ::Memory ( MemoryDatabase ::new ( ) ) ,
)
. unwrap ( ) ;
// signing with an empty db means that we will only look at the psbt to infer the
// derivation index
2022-04-29 12:59:09 +02:00
assert! (
2022-05-18 19:55:21 +02:00
wallet_empty . sign ( & mut psbt , Default ::default ( ) ) . unwrap ( ) ,
" Unable to finalize tx "
) ;
}
#[ test ]
fn test_taproot_sign_explicit_sighash_all ( ) {
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. drain_to ( addr . script_pubkey ( ) )
. sighash ( SchnorrSighashType ::All . into ( ) )
. drain_wallet ( ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
let result = wallet . sign ( & mut psbt , Default ::default ( ) ) ;
assert! (
result . is_ok ( ) ,
" Signing should work because SIGHASH_ALL is safe "
)
}
#[ test ]
fn test_taproot_sign_non_default_sighash ( ) {
let sighash = SchnorrSighashType ::NonePlusAnyoneCanPay ;
let ( wallet , _ , _ ) = get_funded_wallet ( get_test_tr_single_sig ( ) ) ;
let addr = wallet . get_address ( New ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. drain_to ( addr . script_pubkey ( ) )
. sighash ( sighash . into ( ) )
. drain_wallet ( ) ;
let ( mut psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
let witness_utxo = psbt . inputs [ 0 ] . witness_utxo . take ( ) ;
let result = wallet . sign ( & mut psbt , Default ::default ( ) ) ;
assert! (
result . is_err ( ) ,
" Signing should have failed because the TX uses non-standard sighashes "
) ;
assert! (
matches! (
result . unwrap_err ( ) ,
Error ::Signer ( SignerError ::NonStandardSighash )
) ,
" Signing failed with the wrong error type "
) ;
// try again after opting-in
let result = wallet . sign (
& mut psbt ,
SignOptions {
allow_all_sighashes : true ,
.. Default ::default ( )
} ,
) ;
assert! (
result . is_err ( ) ,
" Signing should have failed because the witness_utxo is missing "
) ;
assert! (
matches! (
result . unwrap_err ( ) ,
Error ::Signer ( SignerError ::MissingWitnessUtxo )
) ,
" Signing failed with the wrong error type "
) ;
// restore the witness_utxo
psbt . inputs [ 0 ] . witness_utxo = witness_utxo ;
let result = wallet . sign (
& mut psbt ,
SignOptions {
allow_all_sighashes : true ,
.. Default ::default ( )
} ,
) ;
assert! ( result . is_ok ( ) , " Signing should have worked " ) ;
assert! (
result . unwrap ( ) ,
" Should finalize the input since we can produce signatures "
) ;
let extracted = psbt . extract_tx ( ) ;
assert_eq! (
* extracted . input [ 0 ] . witness . to_vec ( ) [ 0 ] . last ( ) . unwrap ( ) ,
sighash as u8 ,
" The signature should have been made with the right sighash "
2022-04-29 12:59:09 +02:00
) ;
}
2022-05-25 18:56:50 +01:00
#[ test ]
fn test_spend_coinbase ( ) {
let descriptors = testutils! ( @ descriptors ( get_test_wpkh ( ) ) ) ;
let wallet = Wallet ::new (
& descriptors . 0 ,
None ,
Network ::Regtest ,
AnyDatabase ::Memory ( MemoryDatabase ::new ( ) ) ,
)
. unwrap ( ) ;
let confirmation_time = 5 ;
crate ::populate_test_db! (
wallet . database . borrow_mut ( ) ,
2022-07-01 10:56:48 +02:00
testutils! ( @ tx ( ( @ external descriptors , 0 ) = > 25_000 ) ( @ confirmations 1 ) ) ,
2022-05-25 18:56:50 +01:00
Some ( confirmation_time ) ,
( @ coinbase true )
) ;
let not_yet_mature_time = confirmation_time + COINBASE_MATURITY - 1 ;
let maturity_time = confirmation_time + COINBASE_MATURITY ;
// The balance is nonzero, even if we can't spend anything
// FIXME: we should differentiate the balance between immature,
// trusted, untrusted_pending
// See https://github.com/bitcoindevkit/bdk/issues/238
let balance = wallet . get_balance ( ) . unwrap ( ) ;
assert! ( balance ! = 0 ) ;
// We try to create a transaction, only to notice that all
// our funds are unspendable
let addr = Address ::from_str ( " 2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX " ) . unwrap ( ) ;
let mut builder = wallet . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , balance / 2 )
2022-07-13 10:27:38 +02:00
. current_height ( confirmation_time ) ;
2022-05-25 18:56:50 +01:00
assert! ( matches! (
builder . finish ( ) . unwrap_err ( ) ,
Error ::InsufficientFunds {
needed : _ ,
available : 0
}
) ) ;
// Still unspendable...
let mut builder = wallet . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , balance / 2 )
2022-07-13 10:27:38 +02:00
. current_height ( not_yet_mature_time ) ;
2022-05-25 18:56:50 +01:00
assert! ( matches! (
builder . finish ( ) . unwrap_err ( ) ,
Error ::InsufficientFunds {
needed : _ ,
available : 0
}
) ) ;
// ...Now the coinbase is mature :)
let mut builder = wallet . build_tx ( ) ;
builder
. add_recipient ( addr . script_pubkey ( ) , balance / 2 )
2022-07-13 10:27:38 +02:00
. current_height ( maturity_time ) ;
2022-05-25 18:56:50 +01:00
builder . finish ( ) . unwrap ( ) ;
}
2020-08-06 18:11:07 +02:00
}