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 } ;
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 ;
2020-11-30 15:13:33 +01:00
use bitcoin ::util ::base58 ;
2021-05-07 10:28:17 +10:00
use bitcoin ::util ::psbt ::raw ::Key as PsbtKey ;
2021-03-15 21:50:51 -04:00
use bitcoin ::util ::psbt ::Input ;
2021-05-07 10:25:45 +10:00
use bitcoin ::util ::psbt ::PartiallySignedTransaction as Psbt ;
2021-03-15 21:50:51 -04:00
use bitcoin ::{ Address , Network , OutPoint , Script , SigHashType , Transaction , TxOut , Txid } ;
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 ;
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
2020-08-15 23:21:13 +02:00
use address_validator ::AddressValidator ;
2021-01-01 13:35:05 +11:00
use coin_selection ::DefaultCoinSelectionAlgorithm ;
2021-04-19 14:16:39 +02:00
use signer ::{ SignOptions , Signer , SignerOrdering , SignersContainer } ;
2021-01-01 13:35:05 +11:00
use tx_builder ::{ BumpFee , CreateTx , FeePolicy , TxBuilder , TxParams } ;
2021-02-02 20:06:40 -05:00
use utils ::{ check_nlocktime , check_nsequence_rbf , After , Older , SecpCtx , DUST_LIMIT_SATOSHI } ;
2020-08-06 11:12:15 +02:00
2020-12-23 13:48:17 +11:00
use crate ::blockchain ::{ Blockchain , Progress } ;
2020-05-03 16:15:11 +02:00
use crate ::database ::{ BatchDatabase , BatchOperations , DatabaseUtils } ;
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 ;
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 ;
2020-09-04 16:29:25 +02:00
/// A Bitcoin wallet
///
2020-12-02 16:54:49 -08:00
/// A wallet takes descriptors, a [`database`](trait@crate::database::Database) and a
/// [`blockchain`](trait@crate::blockchain::Blockchain) and implements the basic functions that a Bitcoin wallets
2021-03-08 16:17:10 -08:00
/// needs to operate, like [generating addresses](Wallet::get_address), [returning the balance](Wallet::get_balance),
2021-01-01 13:35:05 +11:00
/// [creating transactions](Wallet::build_tx), etc.
2020-09-04 16:29:25 +02:00
///
/// A wallet can be either "online" if the [`blockchain`](crate::blockchain) type provided
2020-12-23 13:48:17 +11:00
/// implements [`Blockchain`], or "offline" if it is the unit type `()`. Offline wallets only expose
2020-09-04 16:29:25 +02:00
/// methods that don't need any interaction with the blockchain to work.
2021-01-22 14:11:29 +11:00
#[ derive(Debug) ]
2020-11-24 12:40:58 +11:00
pub struct Wallet < B , 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
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-05-06 17:17:14 +02:00
current_height : Option < u32 > ,
2020-12-23 13:48:17 +11:00
client : B ,
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
}
2020-12-23 13:48:17 +11:00
impl < D > Wallet < ( ) , D >
2020-02-07 23:22:28 +01:00
where
D : BatchDatabase ,
{
2020-09-04 16:29:25 +02:00
/// Create a new "offline" wallet
2021-02-12 22:34:43 -08:00
pub fn new_offline < E : IntoWalletDescriptor > (
2020-12-23 13:48:17 +11:00
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
database : D ,
) -> Result < Self , Error > {
Self ::_new ( descriptor , change_descriptor , network , database , ( ) , None )
}
}
impl < B , D > Wallet < B , D >
where
D : BatchDatabase ,
{
2021-02-12 22:34:43 -08:00
fn _new < E : IntoWalletDescriptor > (
2020-09-18 16:31:03 +02:00
descriptor : E ,
change_descriptor : Option < E > ,
2020-02-07 23:22:28 +01:00
network : Network ,
2020-02-15 21:27:51 +01:00
mut database : D ,
2020-12-23 13:48:17 +11:00
client : B ,
current_height : Option < u32 > ,
2020-02-15 21:27:51 +01:00
) -> Result < Self , Error > {
2021-02-02 20:06:40 -05:00
let secp = Secp256k1 ::new ( ) ;
2021-02-15 11:33:47 -05:00
let ( descriptor , keymap ) = into_wallet_descriptor_checked ( descriptor , & secp , network ) ? ;
2020-02-15 21:27:51 +01:00
database . check_descriptor_checksum (
2020-12-14 17:14:24 +01:00
KeychainKind ::External ,
2020-09-18 16:31:03 +02:00
get_checksum ( & descriptor . to_string ( ) ) ? . as_bytes ( ) ,
2020-02-15 21:27:51 +01:00
) ? ;
2020-08-12 12:51:50 +02:00
let signers = Arc ::new ( SignersContainer ::from ( keymap ) ) ;
let ( change_descriptor , change_signers ) = match change_descriptor {
2020-02-15 21:27:51 +01:00
Some ( desc ) = > {
2021-02-02 20:06:40 -05:00
let ( change_descriptor , change_keymap ) =
2021-02-15 11:33:47 -05:00
into_wallet_descriptor_checked ( desc , & secp , network ) ? ;
2020-02-15 21:27:51 +01:00
database . check_descriptor_checksum (
2020-12-14 17:14:24 +01:00
KeychainKind ::Internal ,
2020-09-18 16:31:03 +02:00
get_checksum ( & change_descriptor . to_string ( ) ) ? . as_bytes ( ) ,
2020-02-15 21:27:51 +01:00
) ? ;
2020-02-17 14:22:53 +01:00
2020-08-12 12:51:50 +02:00
let change_signers = Arc ::new ( SignersContainer ::from ( change_keymap ) ) ;
// if !parsed.same_structure(descriptor.as_ref()) {
// return Err(Error::DifferentDescriptorStructure);
// }
2020-02-17 14:22:53 +01:00
2020-08-12 12:51:50 +02:00
( Some ( change_descriptor ) , change_signers )
2020-02-15 21:27:51 +01:00
}
2020-08-12 12:51:50 +02:00
None = > ( None , Arc ::new ( SignersContainer ::new ( ) ) ) ,
2020-02-15 21:27:51 +01:00
} ;
Ok ( Wallet {
2020-02-07 23:22:28 +01:00
descriptor ,
change_descriptor ,
2020-08-12 12:51:50 +02:00
signers ,
change_signers ,
2020-08-15 23:21:13 +02:00
address_validators : Vec ::new ( ) ,
2020-02-07 23:22:28 +01:00
network ,
2020-12-23 13:48:17 +11:00
current_height ,
client ,
2020-02-07 23:22:28 +01:00
database : RefCell ::new ( database ) ,
2021-02-02 20:06:40 -05:00
secp ,
2020-02-15 21:27:51 +01:00
} )
2020-02-07 23:22:28 +01:00
}
2020-12-23 13:48:17 +11:00
}
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 ,
}
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 )
}
}
2020-12-23 13:48:17 +11:00
// offline actions, always available
impl < B , D > Wallet < B , D >
where
D : BatchDatabase ,
{
2021-03-08 16:17:10 -08:00
// Return a newly derived address using the external descriptor
2021-05-18 11:09:11 -04:00
fn get_new_address ( & self ) -> Result < AddressInfo , Error > {
2021-03-08 16:17:10 -08:00
let incremented_index = self . fetch_and_increment_index ( KeychainKind ::External ) ? ;
2021-03-07 21:57:19 -08:00
2021-05-17 16:31:18 -04:00
let address_result = self
. descriptor
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 ,
} )
2021-03-08 16:17:10 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
// Return the the last previously derived address if it has not been used in a received
// transaction. Otherwise return a new address using [`Wallet::get_new_address`].
2021-05-18 11:09:11 -04:00
fn get_unused_address ( & self ) -> Result < AddressInfo , Error > {
2021-03-08 16:17:10 -08:00
let current_index = self . fetch_index ( KeychainKind ::External ) ? ;
let derived_key = self . descriptor . as_derived ( current_index , & self . secp ) ;
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 {
self . get_new_address ( )
} 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 ,
} )
2021-03-07 21:57:19 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
}
2021-03-10 15:58:58 -08:00
// Return derived address for the external descriptor at a specific index
2021-05-18 11:09:11 -04:00
fn peek_address ( & self , index : u32 ) -> Result < AddressInfo , Error > {
2021-03-10 15:58:58 -08:00
self . descriptor
. as_derived ( index , & self . secp )
. address ( self . network )
2021-06-21 11:54:56 +02:00
. map ( | address | AddressInfo { index , address } )
2021-03-10 15:58:58 -08:00
. map_err ( | _ | Error ::ScriptDoesntHaveAddressForm )
}
// Return derived address for the external descriptor at a specific index and reset current
// address index
2021-05-18 11:09:11 -04:00
fn reset_address ( & self , index : u32 ) -> Result < AddressInfo , Error > {
2021-03-10 15:58:58 -08:00
self . set_index ( KeychainKind ::External , index ) ? ;
self . descriptor
. as_derived ( index , & self . secp )
. address ( self . network )
2021-06-21 11:54:56 +02:00
. map ( | address | AddressInfo { index , address } )
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-05-18 11:09:11 -04:00
match address_index {
2021-03-08 16:17:10 -08:00
AddressIndex ::New = > self . get_new_address ( ) ,
AddressIndex ::LastUnused = > self . get_unused_address ( ) ,
AddressIndex ::Peek ( index ) = > self . peek_address ( index ) ,
2021-03-10 15:58:58 -08:00
AddressIndex ::Reset ( index ) = > self . reset_address ( index ) ,
2021-05-18 11:09:11 -04:00
}
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
///
/// Note that this methods only operate on the internal database, which first needs to be
/// [`Wallet::sync`] manually.
2021-02-04 12:09:53 +11:00
pub fn list_unspent ( & self ) -> Result < Vec < LocalUtxo > , Error > {
2020-02-07 23:22:28 +01:00
self . database . borrow ( ) . iter_utxos ( )
}
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 )
}
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 ,
2020-11-03 16:03:44 +11:00
signer : Arc < dyn Signer > ,
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
}
2020-09-04 16:29:25 +02:00
/// Add an address validator
///
/// See [the `address_validator` module](address_validator) for an example.
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 ) ;
}
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
pub fn build_tx ( & self ) -> TxBuilder < '_ , B , D , DefaultCoinSelectionAlgorithm , CreateTx > {
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 ,
2021-05-07 10:25:45 +10:00
) -> Result < ( Psbt , 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
} ;
2021-01-01 13:35:05 +11:00
let lock_time = match params . locktime {
2020-12-07 14:48:17 +01:00
// No nLockTime, default to 0
2020-08-07 16:30:19 +02:00
None = > requirements . timelock . unwrap_or ( 0 ) ,
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 ,
} ) ;
}
}
( FeeRate ::from_sat_per_vb ( 0.0 ) , * fee as f32 )
}
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 ,
} ) ;
}
}
( * rate , 0.0 )
}
} ;
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 ;
2020-08-31 10:49:44 +02:00
let calc_fee_bytes = | wu | ( wu as f32 ) * fee_rate . as_sat_vb ( ) / 4.0 ;
2020-08-06 16:56:41 +02:00
fee_amount + = calc_fee_bytes ( tx . get_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 ( ) {
if value . is_dust ( ) {
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 ,
} ;
2020-08-06 16:56:41 +02:00
fee_amount + = calc_fee_bytes ( serialize ( & new_out ) . len ( ) * 4 ) ;
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
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 ,
witness : vec ! [ ] ,
} )
. 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 ( ) ,
None = > self . get_change_address ( ) ? ,
} ;
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-06-16 12:43:32 +10:00
fee_amount + = calc_fee_bytes ( serialize ( & drain_output ) . len ( ) * 4 ) ;
2020-10-28 10:37:47 +01:00
2021-06-16 12:43:32 +10:00
let mut fee_amount = fee_amount . ceil ( ) as u64 ;
let drain_val = ( coin_selection . selected_amount ( ) - outgoing ) . saturating_sub ( fee_amount ) ;
if tx . output . is_empty ( ) {
if params . drain_to . is_some ( ) {
if drain_val . is_dust ( ) {
return Err ( Error ::InsufficientFunds {
needed : DUST_LIMIT_SATOSHI ,
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-06-16 12:43:32 +10:00
if drain_val . is_dust ( ) {
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 ) ,
2021-05-27 16:58:42 +02:00
verified : true ,
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
/// *repalce by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
/// 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
// TODO: option to force addition of an extra output? seems bad for privacy to update the
// change
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 ,
) -> Result < TxBuilder < '_ , B , 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
2021-07-08 11:33:39 +10:00
let vbytes = tx . get_weight ( ) . vbytes ( ) ;
2021-06-12 15:01:44 +02:00
let feerate = details . fee . ok_or ( Error ::FeeRateUnavailable ) ? as f32 / vbytes ;
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 ,
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-01-01 13:35:05 +11:00
rate : feerate ,
} ) ,
.. 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>(())
2021-05-07 10:25:45 +10:00
pub fn sign ( & self , psbt : & mut Psbt , 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
2021-05-06 17:11:43 +02:00
// If we aren't allowed to use `witness_utxo`, ensure that every input but finalized one
// 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 ( ) )
. 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
// is using `SIGHASH_ALL`
if ! sign_options . allow_all_sighashes
& & ! psbt
. inputs
. iter ( )
. all ( | i | i . sighash_type . is_none ( ) | | i . sighash_type = = Some ( SigHashType ::All ) )
{
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 ( ) )
{
2020-08-17 23:50:50 +02:00
if signer . sign_whole_tx ( ) {
2021-04-28 09:53:03 +10:00
signer . sign ( psbt , None , & self . secp ) ? ;
2020-08-17 23:50:50 +02:00
} else {
for index in 0 .. psbt . inputs . len ( ) {
2021-04-28 09:53:03 +10:00
signer . sign ( psbt , Some ( index ) , & self . secp ) ? ;
2020-08-17 23:50:50 +02:00
}
2020-02-07 23:22:28 +01:00
}
}
// attempt to finalize
2021-04-19 14:16:39 +02:00
self . finalize_psbt ( psbt , sign_options )
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.
2021-05-07 10:25:45 +10:00
pub fn finalize_psbt ( & self , psbt : & mut Psbt , sign_options : SignOptions ) -> Result < bool , Error > {
2020-11-17 17:53:06 +11:00
let tx = & psbt . global . unsigned_tx ;
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 ) ) ;
2021-04-19 14:16:39 +02:00
let current_height = sign_options . assume_height . or ( self . current_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 ) ;
}
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-01-01 13:35:05 +11:00
/// Returns the descriptor used to create adddresses for a particular `keychain`.
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
}
fn get_change_address ( & self ) -> Result < Script , Error > {
2021-01-01 13:35:05 +11:00
let ( desc , keychain ) = self . _get_descriptor_for_keychain ( KeychainKind ::Internal ) ;
2020-12-14 17:14:24 +01:00
let index = self . fetch_and_increment_index ( keychain ) ? ;
2020-08-06 18:11:07 +02:00
2021-02-02 20:06:40 -05:00
Ok ( desc . as_derived ( index , & self . secp ) . script_pubkey ( ) )
2020-08-06 18:11:07 +02: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 ) ;
let hd_keypaths = derived_descriptor . get_hd_keypaths ( & self . secp ) ? ;
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 {
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) ]
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 ,
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
2020-10-23 12:13:05 +11:00
let satisfies_confirmed = match must_only_use_confirmed_tx {
true = > {
2021-06-12 15:01:44 +02:00
let database = self . database . borrow ( ) ;
2020-10-23 12:13:05 +11:00
may_spend
. iter ( )
. map ( | u | {
database
. get_tx ( & u . 0. outpoint . txid , true )
. map ( | tx | match tx {
None = > false ,
2021-06-12 15:01:44 +02:00
Some ( tx ) = > tx . confirmation_time . is_some ( ) ,
2020-10-23 12:13:05 +11:00
} )
} )
. collect ::< Result < Vec < _ > , _ > > ( ) ?
}
false = > vec! [ true ; may_spend . len ( ) ] ,
} ;
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 ,
2021-05-07 10:25:45 +10:00
) -> Result < Psbt , Error > {
2020-11-30 15:13:33 +01:00
use bitcoin ::util ::psbt ::serialize ::Serialize ;
2021-05-07 10:25:45 +10:00
let mut psbt = Psbt ::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 serialized_xpub = base58 ::from_check ( & xpub . xkey . to_string ( ) )
. expect ( " Internal serialization error " ) ;
2021-05-07 10:28:17 +10:00
let key = PsbtKey {
2020-11-30 15:13:33 +01:00
type_value : 0x01 ,
key : serialized_xpub ,
} ;
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 ( ) ) ) ,
} ;
psbt . global . unknown . insert ( key , origin . serialize ( ) ) ;
}
}
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
for ( psbt_input , input ) in psbt
. inputs
. iter_mut ( )
. zip ( psbt . global . 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 {
Error ::UnknownUtxo = > Input {
sighash_type : params . sighash ,
.. Input ::default ( )
} ,
_ = > 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 ,
} = > {
2021-04-20 14:58:33 +02:00
if ! 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
for ( psbt_output , tx_output ) in psbt
. outputs
. iter_mut ( )
. zip ( psbt . global . unsigned_tx . output . iter ( ) )
{
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 ) ;
psbt_output . bip32_derivation = 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 ,
sighash_type : Option < SigHashType > ,
2021-04-20 14:58:33 +02:00
only_witness_utxo : bool ,
2021-03-15 21:50:51 -04:00
) -> Result < Input , Error > {
// 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
let mut psbt_input = Input {
sighash_type ,
.. Input ::default ( )
} ;
let desc = self . get_descriptor_for_keychain ( keychain ) ;
let derived_descriptor = desc . as_derived ( child , & self . secp ) ;
psbt_input . bip32_derivation = derived_descriptor . get_hd_keypaths ( & self . secp ) ? ;
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 ) ? {
if desc . is_witness ( ) {
psbt_input . witness_utxo = Some ( prev_tx . output [ prev_output . vout as usize ] . clone ( ) ) ;
}
2021-04-20 14:58:33 +02:00
if ! 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 )
}
2021-05-07 10:25:45 +10:00
fn add_input_hd_keypaths ( & self , psbt : & mut Psbt ) -> 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
// merge hd_keypaths
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 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 ( ( ) )
}
2020-02-07 23:22:28 +01:00
}
2020-05-03 16:15:11 +02:00
impl < B , D > Wallet < B , D >
2020-02-07 23:22:28 +01:00
where
2020-09-09 18:17:49 +02:00
B : Blockchain ,
2020-02-07 23:22:28 +01:00
D : BatchDatabase ,
{
2020-09-04 16:29:25 +02:00
/// Create a new "online" wallet
2020-07-20 15:51:57 +02:00
#[ maybe_async ]
2021-02-12 22:34:43 -08:00
pub fn new < E : IntoWalletDescriptor > (
2020-09-18 16:31:03 +02:00
descriptor : E ,
change_descriptor : Option < E > ,
2020-02-07 23:22:28 +01:00
network : Network ,
2020-08-06 10:44:40 +02:00
database : D ,
client : B ,
2020-02-15 21:27:51 +01:00
) -> Result < Self , Error > {
2020-12-23 13:48:17 +11:00
let current_height = Some ( maybe_await! ( client . get_height ( ) ) ? as u32 ) ;
Self ::_new (
descriptor ,
change_descriptor ,
network ,
database ,
client ,
current_height ,
)
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 ]
2020-08-25 16:07:26 +02:00
pub fn sync < P : 'static + Progress > (
& self ,
progress_update : P ,
max_address_param : Option < u32 > ,
) -> Result < ( ) , Error > {
2020-08-06 18:11:07 +02:00
debug! ( " Begin sync... " ) ;
2020-02-07 23:22:28 +01:00
2020-08-10 10:49:34 +02:00
let mut run_setup = false ;
2021-02-02 20:06:40 -05:00
let max_address = match self . descriptor . is_deriveable ( ) {
false = > 0 ,
true = > max_address_param . unwrap_or ( CACHE_ADDR_BATCH_SIZE ) ,
2020-08-06 18:11:07 +02:00
} ;
2021-05-17 17:20:32 +02:00
debug! ( " max_address {} " , max_address ) ;
2020-08-06 18:11:07 +02:00
if self
2020-02-07 23:22:28 +01:00
. database
. borrow ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::External , max_address . saturating_sub ( 1 ) ) ?
2020-08-06 18:11:07 +02:00
. is_none ( )
{
2021-05-17 17:20:32 +02:00
debug! ( " caching external addresses " ) ;
2020-08-10 10:49:34 +02:00
run_setup = true ;
2020-12-14 17:14:24 +01:00
self . cache_addresses ( KeychainKind ::External , 0 , max_address ) ? ;
2020-08-06 18:11:07 +02:00
}
2020-02-07 23:22:28 +01:00
2020-08-06 18:11:07 +02:00
if let Some ( change_descriptor ) = & self . change_descriptor {
2021-02-02 20:06:40 -05:00
let max_address = match change_descriptor . is_deriveable ( ) {
false = > 0 ,
true = > max_address_param . unwrap_or ( CACHE_ADDR_BATCH_SIZE ) ,
2020-08-06 18:11:07 +02:00
} ;
2020-02-07 23:22:28 +01:00
2020-08-06 18:11:07 +02:00
if self
. database
. borrow ( )
2020-12-14 17:14:24 +01:00
. get_script_pubkey_from_path ( KeychainKind ::Internal , max_address . saturating_sub ( 1 ) ) ?
2020-08-06 18:11:07 +02:00
. is_none ( )
{
2021-05-17 17:20:32 +02:00
debug! ( " caching internal addresses " ) ;
2020-08-10 10:49:34 +02:00
run_setup = true ;
2020-12-14 17:14:24 +01:00
self . cache_addresses ( KeychainKind ::Internal , 0 , max_address ) ? ;
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 {
2021-07-15 12:04:03 -07:00
maybe_await! ( self
. client
. setup ( self . database . borrow_mut ( ) . deref_mut ( ) , progress_update , ) ) ? ;
2020-08-10 10:49:34 +02:00
} else {
2021-07-15 12:04:03 -07:00
maybe_await! ( self
. client
. sync ( self . database . borrow_mut ( ) . deref_mut ( ) , progress_update , ) ) ? ;
2020-08-10 10:49:34 +02:00
}
2021-05-27 16:58:42 +02:00
#[ cfg(feature = " verify " ) ]
{
debug! ( " Verifying transactions... " ) ;
for mut tx in self . database . borrow ( ) . iter_txs ( true ) ? {
if ! tx . verified {
verify ::verify_tx (
tx . transaction . as_ref ( ) . ok_or ( Error ::TransactionNotFound ) ? ,
self . database . borrow ( ) . deref ( ) ,
& self . client ,
) ? ;
tx . verified = true ;
self . database . borrow_mut ( ) . set_tx ( & tx ) ? ;
}
}
}
Ok ( ( ) )
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Return a reference to the internal blockchain client
2020-12-23 13:48:17 +11:00
pub fn client ( & self ) -> & B {
& self . client
2020-08-08 12:06:40 +02:00
}
2020-12-01 13:29:20 +11:00
/// Get the Bitcoin network the wallet is using.
pub fn network ( & self ) -> Network {
self . network
}
2020-09-04 16:29:25 +02:00
/// Broadcast a transaction to the network
2020-07-20 15:51:57 +02:00
#[ maybe_async ]
2020-07-15 18:49:24 +02:00
pub fn broadcast ( & self , tx : Transaction ) -> Result < Txid , Error > {
2020-12-23 13:48:17 +11:00
maybe_await! ( self . client . broadcast ( & tx ) ) ? ;
2020-02-07 23:22:28 +01:00
2020-05-17 18:01:52 +02:00
Ok ( tx . txid ( ) )
2020-02-07 23:22:28 +01:00
}
}
2020-08-06 18:11:07 +02:00
2021-07-08 11:33:39 +10:00
/// Trait implemented by types that can be used to measure weight units.
pub trait Vbytes {
/// Convert weight units to virtual bytes.
fn vbytes ( self ) -> f32 ;
}
impl Vbytes for usize {
fn vbytes ( self ) -> f32 {
self as f32 / 4.0
}
}
2020-08-06 18:11:07 +02:00
#[ cfg(test) ]
2021-05-06 14:41:30 +02:00
pub ( crate ) mod test {
2020-08-12 12:51:50 +02:00
use std ::str ::FromStr ;
2020-08-06 18:11:07 +02:00
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 ::memory ::MemoryDatabase ;
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-05-19 13:04:32 +10:00
use crate ::testutils ;
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 ( ) ;
2020-12-23 13:48:17 +11:00
let wallet = Wallet ::new_offline (
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 ( ) ;
2020-12-23 13:48:17 +11:00
let wallet = Wallet ::new_offline ( " 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 ( ) ;
2020-12-23 13:48:17 +11:00
let wallet = Wallet ::new_offline ( " 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))) "
}
2020-08-15 23:21:13 +02:00
pub ( crate ) fn get_funded_wallet (
2020-08-10 17:16:47 +02:00
descriptor : & str ,
) -> (
2020-12-23 13:48:17 +11:00
Wallet < ( ) , MemoryDatabase > ,
2020-08-12 12:51:50 +02:00
( String , Option < String > ) ,
2020-08-10 17:16:47 +02:00
bitcoin ::Txid ,
) {
let descriptors = testutils! ( @ descriptors ( descriptor ) ) ;
2020-12-23 13:48:17 +11:00
let wallet = Wallet ::new_offline (
2020-08-12 12:51:50 +02:00
& descriptors . 0 ,
2020-08-10 17:16:47 +02:00
None ,
Network ::Regtest ,
MemoryDatabase ::new ( ) ,
)
. unwrap ( ) ;
2021-03-15 21:50:51 -04:00
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 (
2021-04-08 12:16:58 -07:00
& bitcoin ::Address ::from_str ( & tx_meta . output . get ( 0 ) . unwrap ( ) . to_address )
2021-03-15 21:50:51 -04:00
. 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 ) ) ;
2020-08-10 17:16:47 +02:00
( wallet , descriptors , txid )
}
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // fake signature
}
) *
#[ allow(unused_mut) ]
#[ allow(unused_assignments) ]
let mut dust_change = false ;
$(
$( $dust_change ) *
dust_change = true ;
) *
2021-07-08 11:33:39 +10:00
let tx_fee_rate = $fees as f32 / ( tx . get_weight ( ) . vbytes ( ) ) ;
2020-08-13 16:51:27 +02:00
let fee_rate = $fee_rate . as_sat_vb ( ) ;
if ! dust_change {
2021-03-30 16:19:36 +02:00
assert! ( ( tx_fee_rate - fee_rate ) . 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-03-30 16:19:36 +02: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
}
} ) ;
}
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
assert_eq! ( psbt . global . unsigned_tx . version , 42 ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . lock_time , 0 ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . lock_time , 100_000 ) ;
}
#[ 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 )
2021-01-11 14:14:14 +11:00
. nlocktime ( 630_000 ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert_eq! ( psbt . global . unsigned_tx . lock_time , 630_000 ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . lock_time , 630_000 ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 6 ) ;
}
#[ 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
assert_eq! ( psbt . global . 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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFE ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 0xDEADBEEF ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFF ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! (
psbt . global . 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 ( ) ;
dbg! ( & psbt ) ;
let outputs = psbt . global . unsigned_tx . output ;
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 ) ) ;
}
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 ) ;
2020-10-22 09:11:58 +02:00
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! (
psbt . global . 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 ) ;
2020-10-22 09:11:58 +02:00
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! (
psbt . global . 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
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 2 ) ;
assert_eq! ( psbt . global . unsigned_tx . output [ 0 ] . value , 25_000 ) ;
assert_eq! (
psbt . global . 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
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 1 ) ;
assert_eq! ( psbt . global . 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
assert_eq! ( psbt . global . unsigned_tx . output . len ( ) , 3 ) ;
assert_eq! (
psbt . global . 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
) ;
assert_eq! ( psbt . global . unsigned_tx . output [ 1 ] . value , 10_000 ) ;
assert_eq! ( psbt . global . unsigned_tx . output [ 2 ] . value , 30_000 ) ;
}
#[ 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 )
2021-01-11 14:14:14 +11:00
. sighash ( bitcoin ::SigHashType ::Single ) ;
let ( psbt , _ ) = builder . finish ( ) . unwrap ( ) ;
2020-08-10 17:16:47 +02:00
assert_eq! (
psbt . inputs [ 0 ] . sighash_type ,
Some ( bitcoin ::SigHashType ::Single )
) ;
}
#[ 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! (
psbt . global . unsigned_tx . input . len ( ) ,
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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 0xFFFFFFFF ) ;
}
#[ 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
assert_eq! ( psbt . global . unsigned_tx . input [ 0 ] . sequence , 144 ) ;
}
2020-12-01 11:10:14 +01:00
#[ test ]
fn test_create_tx_global_xpubs_with_origin ( ) {
use bitcoin ::hashes ::hex ::FromHex ;
use bitcoin ::util ::base58 ;
use bitcoin ::util ::psbt ::raw ::Key ;
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
let type_value = 0x01 ;
let key = base58 ::from_check ( " tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3 " ) . unwrap ( ) ;
let psbt_key = Key { type_value , key } ;
// This key has an explicit origin, so it will be encoded here
let value_bytes = Vec ::< u8 > ::from_hex ( " 73756c7f30000080000000800000008002000080 " ) . unwrap ( ) ;
assert_eq! ( psbt . global . unknown . len ( ) , 1 ) ;
assert_eq! ( psbt . global . unknown . get ( & psbt_key ) , Some ( & value_bytes ) ) ;
}
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! (
psbt . global
. unsigned_tx
. 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 ;
use bitcoin ::util ::base58 ;
use bitcoin ::util ::psbt ::raw ::Key ;
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
let type_value = 0x01 ;
let key = base58 ::from_check ( " tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL " ) . unwrap ( ) ;
let psbt_key = Key { type_value , key } ;
// This key doesn't have an explicit origin, but it's a master key (depth = 0). So we encode
// its fingerprint directly and an empty path
let value_bytes = Vec ::< u8 > ::from_hex ( " 997a323b " ) . unwrap ( ) ;
assert_eq! ( psbt . global . unknown . len ( ) , 1 ) ;
assert_eq! ( psbt . global . unknown . get ( & psbt_key ) , Some ( & value_bytes ) ) ;
}
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-06-12 15:01:44 +02:00
details . confirmation_time = Some ( ConfirmationTime {
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
) ;
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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
) ;
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
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 ( 140.0 ) ) ;
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 ) ;
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
// 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
let tx = & psbt . global . unsigned_tx ;
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 {
txin . witness . push ( [ 0x00 ; 108 ] . to_vec ( ) ) ; // 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 ( ) ;
// 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
let tx = & psbt . global . unsigned_tx ;
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
}
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 ) ;
psbt . global . 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
2021-05-26 10:34:25 +02:00
#[ test ]
fn test_sign_nonstandard_sighash ( ) {
let sighash = SigHashType ::NonePlusAnyoneCanPay ;
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 ( ) )
2021-05-26 10:34:25 +02:00
. sighash ( sighash )
. 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! (
* extracted . input [ 0 ] . witness [ 0 ] . last ( ) . unwrap ( ) ,
sighash . as_u32 ( ) as u8 ,
" 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 ( ) ;
2021-04-08 12:16:58 -07:00
let wallet = Wallet ::new_offline ( " 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 ) ) ;
let wallet = Wallet ::new_offline (
& 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 ( ) ;
let wallet = Wallet ::new_offline ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
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 ( ) ;
let wallet = Wallet ::new_offline ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1) " ,
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 ( ) ;
let wallet = Wallet ::new_offline ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
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 ( ) ;
let wallet = Wallet ::new_offline ( " wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*) " ,
None , Network ::Testnet , db ) . unwrap ( ) ;
// new index 0
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 0 ,
address : Address ::from_str ( " tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a " ) . unwrap ( ) ,
}
) ;
// new index 1
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 1 ,
address : Address ::from_str ( " tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 " ) . unwrap ( )
}
) ;
// peek index 25
assert_eq! (
wallet . get_address ( Peek ( 25 ) ) . unwrap ( ) ,
AddressInfo {
index : 25 ,
address : Address ::from_str ( " tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2 " ) . unwrap ( )
}
) ;
// new index 2
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 2 ,
address : Address ::from_str ( " tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 " ) . unwrap ( )
}
) ;
// reset index 1 again
assert_eq! (
wallet . get_address ( Reset ( 1 ) ) . unwrap ( ) ,
AddressInfo {
index : 1 ,
address : Address ::from_str ( " tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7 " ) . unwrap ( )
}
) ;
// new index 2 again
assert_eq! (
wallet . get_address ( New ) . unwrap ( ) ,
AddressInfo {
index : 2 ,
address : Address ::from_str ( " tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2 " ) . unwrap ( )
}
) ;
}
2020-08-06 18:11:07 +02:00
}