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
//!
2023-10-17 11:00:05 +02:00
//! This module defines the [`Wallet`].
2023-11-10 10:54:56 -05:00
use crate ::collections ::{ BTreeMap , HashMap } ;
2023-01-10 15:10:02 +11:00
use alloc ::{
boxed ::Box ,
string ::{ String , ToString } ,
sync ::Arc ,
vec ::Vec ,
} ;
2023-02-15 13:00:32 +11:00
pub use bdk_chain ::keychain ::Balance ;
2023-02-15 12:23:59 +11:00
use bdk_chain ::{
2023-08-07 17:43:17 +02:00
indexed_tx_graph ,
2023-08-21 15:18:16 +03:00
keychain ::{ self , KeychainTxOutIndex } ,
2023-10-11 13:42:21 +03:00
local_chain ::{
self , ApplyHeaderError , CannotConnectError , CheckPoint , CheckPointIter , LocalChain ,
} ,
2023-05-09 21:49:33 +08:00
tx_graph ::{ CanonicalTx , TxGraph } ,
2023-11-12 21:31:44 +08:00
Append , BlockId , ChainPosition , ConfirmationTime , ConfirmationTimeHeightAnchor , FullTxOut ,
2023-05-29 21:54:53 +08:00
IndexedTxGraph , Persist , PersistBackend ,
2023-02-15 12:23:59 +11:00
} ;
2023-10-27 14:14:25 +08:00
use bitcoin ::secp256k1 ::{ All , Secp256k1 } ;
2023-07-19 15:27:48 +02:00
use bitcoin ::sighash ::{ EcdsaSighashType , TapSighashType } ;
2022-04-26 15:11:22 +02:00
use bitcoin ::{
2023-10-11 13:42:21 +03:00
absolute , Address , Block , Network , OutPoint , Script , ScriptBuf , Sequence , Transaction , TxOut ,
Txid , Weight , Witness ,
2022-04-26 15:11:22 +02:00
} ;
2023-10-12 16:55:32 +08:00
use bitcoin ::{ consensus ::encode ::serialize , BlockHash } ;
use bitcoin ::{ constants ::genesis_block , psbt } ;
2023-02-21 12:38:16 +11:00
use core ::fmt ;
use core ::ops ::Deref ;
2023-11-16 10:22:37 -06:00
use descriptor ::error ::Error as DescriptorError ;
2022-10-25 11:15:43 +02:00
use miniscript ::psbt ::{ PsbtExt , PsbtInputExt , PsbtInputSatisfier } ;
2020-02-07 23:22:28 +01:00
2023-08-01 12:42:37 -05:00
use bdk_chain ::tx_graph ::CalculateFeeError ;
2020-02-07 23:22:28 +01:00
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 13:09:39 +02:00
pub mod tx_builder ;
2020-08-31 10:49:44 +02:00
pub ( crate ) mod utils ;
2023-11-16 10:22:37 -06:00
pub mod error ;
2020-08-31 10:49:44 +02:00
pub use utils ::IsDust ;
2020-02-07 23:22:28 +01:00
2022-11-03 15:59:38 +08:00
#[ allow(deprecated) ]
2021-01-01 13:35:05 +11:00
use coin_selection ::DefaultCoinSelectionAlgorithm ;
2022-04-26 16:54:10 +02:00
use signer ::{ SignOptions , SignerOrdering , SignersContainer , TransactionSigner } ;
2021-01-01 13:35:05 +11:00
use tx_builder ::{ BumpFee , CreateTx , FeePolicy , TxBuilder , TxParams } ;
2022-10-25 11:15:43 +02:00
use utils ::{ check_nsequence_rbf , After , Older , SecpCtx } ;
2020-08-06 11:12:15 +02:00
2021-04-02 16:39:18 +02:00
use crate ::descriptor ::policy ::BuildSatisfaction ;
2020-08-12 12:51:50 +02:00
use crate ::descriptor ::{
2023-11-16 10:22:37 -06:00
self , calc_checksum , into_wallet_descriptor_checked , DerivedDescriptor , DescriptorMeta ,
2023-02-15 12:23:59 +11:00
ExtendedDescriptor , ExtractPolicy , IntoWalletDescriptor , Policy , XKeyUtils ,
2020-08-12 12:51:50 +02:00
} ;
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 ::* ;
2022-06-13 10:49:31 -03:00
use crate ::wallet ::coin_selection ::Excess ::{ Change , NoChange } ;
2023-11-16 10:22:37 -06:00
use crate ::wallet ::error ::{ BuildFeeBumpError , CreateTxError , MiniscriptPsbtError } ;
2020-02-07 23:22:28 +01:00
2022-05-25 18:56:50 +01:00
const COINBASE_MATURITY : u32 = 100 ;
2020-08-06 18:11:07 +02:00
2020-09-04 16:29:25 +02:00
/// A Bitcoin wallet
///
2023-10-17 11:00:05 +02:00
/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
2022-01-26 15:17:48 +11:00
/// Its main components are:
2020-09-04 16:29:25 +02:00
///
2022-01-26 15:17:48 +11:00
/// 1. output *descriptors* from which it can derive addresses.
2023-02-21 12:38:16 +11:00
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
2022-01-26 15:17:48 +11:00
///
2022-04-26 16:54:10 +02:00
/// [`signer`]: crate::signer
2021-01-22 14:11:29 +11:00
#[ derive(Debug) ]
2023-02-15 12:23:59 +11:00
pub struct Wallet < D = ( ) > {
2020-10-09 12:03:47 +02:00
signers : Arc < SignersContainer > ,
change_signers : Arc < SignersContainer > ,
2023-05-09 21:49:33 +08:00
chain : LocalChain ,
2023-11-12 21:31:44 +08:00
indexed_graph : IndexedTxGraph < ConfirmationTimeHeightAnchor , KeychainTxOutIndex < KeychainKind > > ,
2023-05-29 21:54:53 +08:00
persist : Persist < D , ChangeSet > ,
2020-02-07 23:22:28 +01:00
network : Network ,
2020-11-16 22:07:38 +01:00
secp : SecpCtx ,
2020-02-07 23:22:28 +01:00
}
2023-09-06 09:47:45 +03:00
/// An update to [`Wallet`].
2023-08-21 12:13:58 +03:00
///
2023-08-25 12:49:29 +03:00
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
2023-09-06 09:47:45 +03:00
#[ derive(Debug, Clone, Default) ]
pub struct Update {
2023-08-21 12:13:58 +03:00
/// Contains the last active derivation indices per keychain (`K`), which is used to update the
/// [`KeychainTxOutIndex`].
2023-09-06 09:47:45 +03:00
pub last_active_indices : BTreeMap < KeychainKind , u32 > ,
2023-08-21 12:13:58 +03:00
2023-09-06 09:47:45 +03:00
/// Update for the wallet's internal [`TxGraph`].
2023-11-12 21:31:44 +08:00
pub graph : TxGraph < ConfirmationTimeHeightAnchor > ,
2023-08-21 12:13:58 +03:00
2023-09-06 09:47:45 +03:00
/// Update for the wallet's internal [`LocalChain`].
2023-08-21 12:13:58 +03:00
///
/// [`LocalChain`]: local_chain::LocalChain
2023-08-24 16:03:47 +03:00
pub chain : Option < local_chain ::Update > ,
2023-08-21 12:13:58 +03:00
}
2023-09-06 09:47:45 +03:00
/// The changes made to a wallet by applying an [`Update`].
#[ derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default) ]
pub struct ChangeSet {
2023-08-21 15:18:16 +03:00
/// Changes to the [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
pub chain : local_chain ::ChangeSet ,
2023-09-06 09:47:45 +03:00
/// Changes to [`IndexedTxGraph`].
2023-08-21 15:18:16 +03:00
///
/// [`IndexedTxGraph`]: bdk_chain::indexed_tx_graph::IndexedTxGraph
2023-11-12 21:31:44 +08:00
pub indexed_tx_graph : indexed_tx_graph ::ChangeSet <
ConfirmationTimeHeightAnchor ,
keychain ::ChangeSet < KeychainKind > ,
> ,
2023-10-26 06:20:37 +08:00
/// Stores the network type of the wallet.
pub network : Option < Network > ,
2023-08-21 15:18:16 +03:00
}
2023-09-06 09:47:45 +03:00
impl Append for ChangeSet {
2023-08-21 15:18:16 +03:00
fn append ( & mut self , other : Self ) {
Append ::append ( & mut self . chain , other . chain ) ;
2023-09-06 09:47:45 +03:00
Append ::append ( & mut self . indexed_tx_graph , other . indexed_tx_graph ) ;
2023-10-26 06:20:37 +08:00
if other . network . is_some ( ) {
2023-11-06 11:52:03 +08:00
debug_assert! (
self . network . is_none ( ) | | self . network = = other . network ,
" network type must be consistent "
) ;
2023-10-26 06:20:37 +08:00
self . network = other . network ;
}
2023-08-21 15:18:16 +03:00
}
fn is_empty ( & self ) -> bool {
2023-09-06 09:47:45 +03:00
self . chain . is_empty ( ) & & self . indexed_tx_graph . is_empty ( )
2023-08-21 15:18:16 +03:00
}
}
2023-09-06 09:47:45 +03:00
impl From < local_chain ::ChangeSet > for ChangeSet {
2023-08-21 15:18:16 +03:00
fn from ( chain : local_chain ::ChangeSet ) -> Self {
Self {
chain ,
.. Default ::default ( )
}
}
}
2023-11-12 21:31:44 +08:00
impl
From <
indexed_tx_graph ::ChangeSet <
ConfirmationTimeHeightAnchor ,
keychain ::ChangeSet < KeychainKind > ,
> ,
> for ChangeSet
2023-09-06 09:47:45 +03:00
{
fn from (
indexed_tx_graph : indexed_tx_graph ::ChangeSet <
2023-11-12 21:31:44 +08:00
ConfirmationTimeHeightAnchor ,
2023-09-06 09:47:45 +03:00
keychain ::ChangeSet < KeychainKind > ,
> ,
) -> Self {
2023-08-21 15:18:16 +03:00
Self {
2023-09-06 09:47:45 +03:00
indexed_tx_graph ,
2023-08-21 15:18:16 +03:00
.. Default ::default ( )
}
}
}
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 ) ,
2021-03-08 16:17:10 -08:00
}
2023-01-19 15:03:37 -05:00
/// A derived address and the index it was found at.
2021-05-17 16:31:18 -04:00
/// For convenience this automatically derefs to `Address`
2022-10-25 11:15:43 +02:00
#[ derive(Debug, PartialEq, Eq) ]
2021-05-17 16:31:18 -04:00
pub struct AddressInfo {
/// Child index of this address
pub index : u32 ,
/// Address
pub address : Address ,
2022-03-10 06:22:02 +05:30
/// Type of keychain
pub keychain : KeychainKind ,
2021-05-17 16:31:18 -04:00
}
impl Deref for AddressInfo {
type Target = Address ;
fn deref ( & self ) -> & Self ::Target {
& self . address
}
}
impl fmt ::Display for AddressInfo {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
write! ( f , " {} " , self . address )
}
}
2022-11-03 15:59:38 +08:00
impl Wallet {
2023-02-15 12:23:59 +11:00
/// Creates a wallet that does not persist data.
pub fn new_no_persist < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
2023-11-16 10:22:37 -06:00
) -> Result < Self , DescriptorError > {
2023-02-15 12:23:59 +11:00
Self ::new ( descriptor , change_descriptor , ( ) , network ) . map_err ( | e | match e {
2024-01-02 13:03:19 +08:00
NewError ::NonEmptyDatabase = > unreachable! ( " mock-database cannot have data " ) ,
2023-10-26 06:20:37 +08:00
NewError ::Descriptor ( e ) = > e ,
NewError ::Write ( _ ) = > unreachable! ( " mock-write must always succeed " ) ,
2023-02-15 12:23:59 +11:00
} )
}
2023-10-26 06:20:37 +08:00
/// Creates a wallet that does not persist data, with a custom genesis hash.
pub fn new_no_persist_with_genesis_hash < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
genesis_hash : BlockHash ,
) -> Result < Self , crate ::descriptor ::DescriptorError > {
Self ::new_with_genesis_hash ( descriptor , change_descriptor , ( ) , network , genesis_hash )
. map_err ( | e | match e {
2024-01-02 13:03:19 +08:00
NewError ::NonEmptyDatabase = > unreachable! ( " mock-database cannot have data " ) ,
2023-10-26 06:20:37 +08:00
NewError ::Descriptor ( e ) = > e ,
NewError ::Write ( _ ) = > unreachable! ( " mock-write must always succeed " ) ,
} )
}
2023-02-15 12:23:59 +11:00
}
2023-11-16 11:18:11 -06:00
impl < D > Wallet < D >
where
D : PersistBackend < ChangeSet , WriteError = core ::convert ::Infallible > ,
{
/// Infallibly return a derived address using the external descriptor, see [`AddressIndex`] for
/// available address index selection strategies. If none of the keys in the descriptor are derivable
/// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
2024-01-13 20:38:11 +08:00
///
/// # Panics
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
2023-11-16 11:18:11 -06:00
pub fn get_address ( & mut self , address_index : AddressIndex ) -> AddressInfo {
self . try_get_address ( address_index ) . unwrap ( )
}
/// Infallibly return a derived address using the internal (change) descriptor.
///
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
///
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
/// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
/// be returned for any [`AddressIndex`].
2024-01-13 20:38:11 +08:00
///
/// # Panics
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
2023-11-16 11:18:11 -06:00
pub fn get_internal_address ( & mut self , address_index : AddressIndex ) -> AddressInfo {
self . try_get_internal_address ( address_index ) . unwrap ( )
}
}
2023-10-27 14:14:25 +08:00
/// The error type when constructing a fresh [`Wallet`].
///
/// Methods [`new`] and [`new_with_genesis_hash`] may return this error.
///
/// [`new`]: Wallet::new
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
2023-10-12 16:55:32 +08:00
#[ derive(Debug) ]
2023-10-26 06:20:37 +08:00
pub enum NewError < W > {
2024-01-02 13:03:19 +08:00
/// Database already has data.
NonEmptyDatabase ,
2023-10-26 06:20:37 +08:00
/// There was problem with the passed-in descriptor(s).
2023-10-12 16:55:32 +08:00
Descriptor ( crate ::descriptor ::DescriptorError ) ,
2023-10-26 06:20:37 +08:00
/// We were unable to write the wallet's data to the persistence backend.
Write ( W ) ,
2023-10-12 16:55:32 +08:00
}
2023-10-26 06:20:37 +08:00
impl < W > fmt ::Display for NewError < W >
where
W : fmt ::Display ,
{
2023-10-12 16:55:32 +08:00
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
2024-01-02 13:03:19 +08:00
NewError ::NonEmptyDatabase = > write! (
f ,
" database already has data - use `load` or `new_or_load` methods instead "
) ,
2023-10-26 06:20:37 +08:00
NewError ::Descriptor ( e ) = > e . fmt ( f ) ,
NewError ::Write ( e ) = > e . fmt ( f ) ,
2023-10-12 16:55:32 +08:00
}
}
}
#[ cfg(feature = " std " ) ]
2023-10-26 06:20:37 +08:00
impl < W > std ::error ::Error for NewError < W > where W : core ::fmt ::Display + core ::fmt ::Debug { }
2023-10-12 16:55:32 +08:00
2023-10-27 14:14:25 +08:00
/// The error type when loading a [`Wallet`] from persistence.
///
/// Method [`load`] may return this error.
///
/// [`load`]: Wallet::load
2023-02-15 12:23:59 +11:00
#[ derive(Debug) ]
2023-10-26 06:20:37 +08:00
pub enum LoadError < L > {
/// There was a problem with the passed-in descriptor(s).
2023-02-15 12:23:59 +11:00
Descriptor ( crate ::descriptor ::DescriptorError ) ,
2023-10-26 06:20:37 +08:00
/// Loading data from the persistence backend failed.
Load ( L ) ,
2023-11-01 09:21:24 +08:00
/// Wallet not initialized, persistence backend is empty.
NotInitialized ,
2023-10-26 06:20:37 +08:00
/// Data loaded from persistence is missing network type.
MissingNetwork ,
/// Data loaded from persistence is missing genesis hash.
MissingGenesis ,
2023-02-15 12:23:59 +11:00
}
2023-10-26 06:20:37 +08:00
impl < L > fmt ::Display for LoadError < L >
2023-02-15 12:23:59 +11:00
where
2023-10-26 06:20:37 +08:00
L : fmt ::Display ,
2023-02-15 12:23:59 +11:00
{
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
2023-10-26 06:20:37 +08:00
LoadError ::Descriptor ( e ) = > e . fmt ( f ) ,
LoadError ::Load ( e ) = > e . fmt ( f ) ,
2023-11-01 09:21:24 +08:00
LoadError ::NotInitialized = > {
write! ( f , " wallet is not initialized, persistence backend is empty " )
}
2023-10-26 06:20:37 +08:00
LoadError ::MissingNetwork = > write! ( f , " loaded data is missing network type " ) ,
LoadError ::MissingGenesis = > write! ( f , " loaded data is missing genesis hash " ) ,
2023-02-15 12:23:59 +11:00
}
}
}
2023-10-12 16:55:32 +08:00
#[ cfg(feature = " std " ) ]
2023-10-26 06:20:37 +08:00
impl < L > std ::error ::Error for LoadError < L > where L : core ::fmt ::Display + core ::fmt ::Debug { }
2023-10-12 16:55:32 +08:00
2024-01-06 14:13:56 -03:00
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
2023-10-27 14:14:25 +08:00
///
/// Methods [`new_or_load`] and [`new_or_load_with_genesis_hash`] may return this error.
///
/// [`new_or_load`]: Wallet::new_or_load
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
#[ derive(Debug) ]
pub enum NewOrLoadError < W , L > {
/// There is a problem with the passed-in descriptor.
Descriptor ( crate ::descriptor ::DescriptorError ) ,
/// Writing to the persistence backend failed.
Write ( W ) ,
/// Loading from the persistence backend failed.
Load ( L ) ,
2023-11-01 09:21:24 +08:00
/// Wallet is not initialized, persistence backend is empty.
NotInitialized ,
2023-10-27 14:14:25 +08:00
/// The loaded genesis hash does not match what was provided.
LoadedGenesisDoesNotMatch {
/// The expected genesis block hash.
expected : BlockHash ,
/// The block hash loaded from persistence.
got : Option < BlockHash > ,
} ,
/// The loaded network type does not match what was provided.
LoadedNetworkDoesNotMatch {
/// The expected network type.
expected : Network ,
/// The network type loaded from persistence.
got : Option < Network > ,
} ,
}
impl < W , L > fmt ::Display for NewOrLoadError < W , L >
where
W : fmt ::Display ,
L : fmt ::Display ,
{
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
NewOrLoadError ::Descriptor ( e ) = > e . fmt ( f ) ,
NewOrLoadError ::Write ( e ) = > write! ( f , " failed to write to persistence: {} " , e ) ,
NewOrLoadError ::Load ( e ) = > write! ( f , " failed to load from persistence: {} " , e ) ,
2023-11-01 09:21:24 +08:00
NewOrLoadError ::NotInitialized = > {
write! ( f , " wallet is not initialized, persistence backend is empty " )
}
2023-10-27 14:14:25 +08:00
NewOrLoadError ::LoadedGenesisDoesNotMatch { expected , got } = > {
write! ( f , " loaded genesis hash is not {}, got {:?} " , expected , got )
}
NewOrLoadError ::LoadedNetworkDoesNotMatch { expected , got } = > {
write! ( f , " loaded network type is not {}, got {:?} " , expected , got )
}
}
}
}
#[ cfg(feature = " std " ) ]
impl < W , L > std ::error ::Error for NewOrLoadError < W , L >
where
W : core ::fmt ::Display + core ::fmt ::Debug ,
L : core ::fmt ::Display + core ::fmt ::Debug ,
{
}
2023-05-29 21:54:53 +08:00
/// An error that may occur when inserting a transaction into [`Wallet`].
2023-05-09 21:49:33 +08:00
#[ derive(Debug) ]
pub enum InsertTxError {
2023-05-29 21:54:53 +08:00
/// The error variant that occurs when the caller attempts to insert a transaction with a
/// confirmation height that is greater than the internal chain tip.
2023-05-09 21:49:33 +08:00
ConfirmationHeightCannotBeGreaterThanTip {
2023-05-29 21:54:53 +08:00
/// The internal chain's tip height.
2023-10-12 16:55:32 +08:00
tip_height : u32 ,
2023-05-29 21:54:53 +08:00
/// The introduced transaction's confirmation height.
2023-05-09 21:49:33 +08:00
tx_height : u32 ,
} ,
}
2023-10-11 13:42:21 +03:00
impl fmt ::Display for InsertTxError {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
InsertTxError ::ConfirmationHeightCannotBeGreaterThanTip {
tip_height ,
tx_height ,
} = > {
write! ( f , " cannot insert tx with confirmation height ({}) higher than internal tip height ({}) " , tx_height , tip_height )
}
}
}
}
#[ cfg(feature = " std " ) ]
impl std ::error ::Error for InsertTxError { }
/// An error that may occur when applying a block to [`Wallet`].
#[ derive(Debug) ]
pub enum ApplyBlockError {
/// Occurs when the update chain cannot connect with original chain.
CannotConnect ( CannotConnectError ) ,
/// Occurs when the `connected_to` hash does not match the hash derived from `block`.
UnexpectedConnectedToHash {
/// Block hash of `connected_to`.
connected_to_hash : BlockHash ,
/// Expected block hash of `connected_to`, as derived from `block`.
expected_hash : BlockHash ,
} ,
}
impl fmt ::Display for ApplyBlockError {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
ApplyBlockError ::CannotConnect ( err ) = > err . fmt ( f ) ,
ApplyBlockError ::UnexpectedConnectedToHash {
expected_hash : block_hash ,
connected_to_hash : checkpoint_hash ,
} = > write! (
f ,
" `connected_to` hash {} differs from the expected hash {} (which is derived from `block`) " ,
checkpoint_hash , block_hash
) ,
}
}
}
#[ cfg(feature = " std " ) ]
impl std ::error ::Error for ApplyBlockError { }
2023-02-15 12:23:59 +11:00
impl < D > Wallet < D > {
2023-10-27 14:14:25 +08:00
/// Initialize an empty [`Wallet`].
2022-01-26 15:17:48 +11:00
pub fn new < E : IntoWalletDescriptor > (
2023-10-12 16:55:32 +08:00
descriptor : E ,
change_descriptor : Option < E > ,
db : D ,
network : Network ,
2023-10-26 06:20:37 +08:00
) -> Result < Self , NewError < D ::WriteError > >
2023-10-12 16:55:32 +08:00
where
D : PersistBackend < ChangeSet > ,
{
2023-10-26 06:20:37 +08:00
let genesis_hash = genesis_block ( network ) . block_hash ( ) ;
Self ::new_with_genesis_hash ( descriptor , change_descriptor , db , network , genesis_hash )
2023-10-12 16:55:32 +08:00
}
2023-10-27 14:14:25 +08:00
/// Initialize an empty [`Wallet`] with a custom genesis hash.
2023-10-12 16:55:32 +08:00
///
2023-10-27 14:14:25 +08:00
/// This is like [`Wallet::new`] with an additional `genesis_hash` parameter. This is useful
/// for syncing from alternative networks.
2023-10-26 06:20:37 +08:00
pub fn new_with_genesis_hash < E : IntoWalletDescriptor > (
2022-01-26 15:17:48 +11:00
descriptor : E ,
change_descriptor : Option < E > ,
2024-01-02 13:03:19 +08:00
mut db : D ,
2022-01-26 15:17:48 +11:00
network : Network ,
2023-10-26 06:20:37 +08:00
genesis_hash : BlockHash ,
) -> Result < Self , NewError < D ::WriteError > >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2024-01-02 13:03:19 +08:00
if let Ok ( changeset ) = db . load_from_persistence ( ) {
if changeset . is_some ( ) {
return Err ( NewError ::NonEmptyDatabase ) ;
}
}
2022-01-26 15:17:48 +11:00
let secp = Secp256k1 ::new ( ) ;
2023-10-27 14:14:25 +08:00
let ( chain , chain_changeset ) = LocalChain ::from_genesis_hash ( genesis_hash ) ;
let mut index = KeychainTxOutIndex ::< KeychainKind > ::default ( ) ;
2022-01-26 15:17:48 +11:00
2023-10-27 14:14:25 +08:00
let ( signers , change_signers ) =
create_signers ( & mut index , & secp , descriptor , change_descriptor , network )
. map_err ( NewError ::Descriptor ) ? ;
let indexed_graph = IndexedTxGraph ::new ( index ) ;
2022-01-26 15:17:48 +11:00
2023-10-26 06:20:37 +08:00
let mut persist = Persist ::new ( db ) ;
persist . stage ( ChangeSet {
2023-10-27 14:14:25 +08:00
chain : chain_changeset ,
2023-10-26 06:20:37 +08:00
indexed_tx_graph : indexed_graph . initial_changeset ( ) ,
network : Some ( network ) ,
} ) ;
persist . commit ( ) . map_err ( NewError ::Write ) ? ;
2023-02-15 12:23:59 +11:00
2023-10-26 06:20:37 +08:00
Ok ( Wallet {
signers ,
change_signers ,
network ,
chain ,
indexed_graph ,
persist ,
secp ,
} )
}
2023-10-27 14:14:25 +08:00
/// Load [`Wallet`] from the given persistence backend.
2023-10-26 06:20:37 +08:00
pub fn load < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
mut db : D ,
) -> Result < Self , LoadError < D ::LoadError > >
2023-11-01 09:21:24 +08:00
where
D : PersistBackend < ChangeSet > ,
{
let changeset = db
. load_from_persistence ( )
. map_err ( LoadError ::Load ) ?
. ok_or ( LoadError ::NotInitialized ) ? ;
Self ::load_from_changeset ( descriptor , change_descriptor , db , changeset )
}
fn load_from_changeset < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
db : D ,
changeset : ChangeSet ,
) -> Result < Self , LoadError < D ::LoadError > >
2023-10-26 06:20:37 +08:00
where
D : PersistBackend < ChangeSet > ,
{
let secp = Secp256k1 ::new ( ) ;
let network = changeset . network . ok_or ( LoadError ::MissingNetwork ) ? ;
let chain =
LocalChain ::from_changeset ( changeset . chain ) . map_err ( | _ | LoadError ::MissingGenesis ) ? ;
let mut index = KeychainTxOutIndex ::< KeychainKind > ::default ( ) ;
2023-10-27 14:14:25 +08:00
let ( signers , change_signers ) =
create_signers ( & mut index , & secp , descriptor , change_descriptor , network )
. map_err ( LoadError ::Descriptor ) ? ;
2023-10-26 06:20:37 +08:00
2023-12-19 16:05:01 -05:00
let mut indexed_graph = IndexedTxGraph ::new ( index ) ;
indexed_graph . apply_changeset ( changeset . indexed_tx_graph ) ;
2023-05-09 21:49:33 +08:00
let persist = Persist ::new ( db ) ;
2023-02-15 12:23:59 +11:00
2022-01-26 15:17:48 +11:00
Ok ( Wallet {
signers ,
change_signers ,
2023-05-09 21:49:33 +08:00
chain ,
indexed_graph ,
2023-02-15 12:23:59 +11:00
persist ,
2023-10-26 06:20:37 +08:00
network ,
2022-01-26 15:17:48 +11:00
secp ,
} )
}
2023-10-27 14:14:25 +08:00
/// Either loads [`Wallet`] from persistence, or initializes it if it does not exist.
///
/// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
pub fn new_or_load < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
db : D ,
network : Network ,
) -> Result < Self , NewOrLoadError < D ::WriteError , D ::LoadError > >
where
D : PersistBackend < ChangeSet > ,
{
let genesis_hash = genesis_block ( network ) . block_hash ( ) ;
Self ::new_or_load_with_genesis_hash (
descriptor ,
change_descriptor ,
db ,
network ,
genesis_hash ,
)
}
/// Either loads [`Wallet`] from persistence, or initializes it if it does not exist (with a
/// custom genesis hash).
///
/// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
/// This is like [`Wallet::new_or_load`] with an additional `genesis_hash` parameter. This is
/// useful for syncing from alternative networks.
pub fn new_or_load_with_genesis_hash < E : IntoWalletDescriptor > (
descriptor : E ,
change_descriptor : Option < E > ,
mut db : D ,
network : Network ,
genesis_hash : BlockHash ,
) -> Result < Self , NewOrLoadError < D ::WriteError , D ::LoadError > >
where
D : PersistBackend < ChangeSet > ,
{
2023-11-01 09:21:24 +08:00
let changeset = db . load_from_persistence ( ) . map_err ( NewOrLoadError ::Load ) ? ;
match changeset {
Some ( changeset ) = > {
let wallet =
Self ::load_from_changeset ( descriptor , change_descriptor , db , changeset )
. map_err ( | e | match e {
LoadError ::Descriptor ( e ) = > NewOrLoadError ::Descriptor ( e ) ,
LoadError ::Load ( e ) = > NewOrLoadError ::Load ( e ) ,
LoadError ::NotInitialized = > NewOrLoadError ::NotInitialized ,
LoadError ::MissingNetwork = > {
NewOrLoadError ::LoadedNetworkDoesNotMatch {
expected : network ,
got : None ,
}
}
LoadError ::MissingGenesis = > {
NewOrLoadError ::LoadedGenesisDoesNotMatch {
expected : genesis_hash ,
got : None ,
}
}
} ) ? ;
if wallet . network ! = network {
return Err ( NewOrLoadError ::LoadedNetworkDoesNotMatch {
expected : network ,
got : Some ( wallet . network ) ,
} ) ;
}
if wallet . chain . genesis_hash ( ) ! = genesis_hash {
return Err ( NewOrLoadError ::LoadedGenesisDoesNotMatch {
expected : genesis_hash ,
got : Some ( wallet . chain . genesis_hash ( ) ) ,
} ) ;
}
Ok ( wallet )
}
None = > Self ::new_with_genesis_hash (
2023-10-27 14:14:25 +08:00
descriptor ,
change_descriptor ,
db ,
network ,
genesis_hash ,
)
. map_err ( | e | match e {
2024-01-02 13:03:19 +08:00
NewError ::NonEmptyDatabase = > {
unreachable! ( " database is already checked to have no data " )
}
2023-10-27 14:14:25 +08:00
NewError ::Descriptor ( e ) = > NewOrLoadError ::Descriptor ( e ) ,
NewError ::Write ( e ) = > NewOrLoadError ::Write ( e ) ,
2023-11-01 09:21:24 +08:00
} ) ,
2023-10-27 14:14:25 +08:00
}
}
2022-01-26 15:17:48 +11:00
/// Get the Bitcoin network the wallet is using.
pub fn network ( & self ) -> Network {
self . network
}
2022-11-03 15:59:38 +08:00
/// Iterator over all keychains in this wallet
2023-06-01 19:24:52 +02:00
pub fn keychains ( & self ) -> & BTreeMap < KeychainKind , ExtendedDescriptor > {
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . keychains ( )
2022-11-03 15:59:38 +08:00
}
2021-03-07 21:57:19 -08:00
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
2022-08-05 11:39:50 -04:00
/// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
2023-11-16 11:18:11 -06:00
///
/// A `PersistBackend<ChangeSet>::WriteError` will result if unable to persist the new address
/// to the `PersistBackend`.
2024-01-13 20:38:11 +08:00
///
/// # Panics
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
2023-11-16 11:18:11 -06:00
pub fn try_get_address (
& mut self ,
address_index : AddressIndex ,
) -> Result < AddressInfo , D ::WriteError >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2023-05-29 13:20:12 +08:00
self . _get_address ( KeychainKind ::External , address_index )
2021-08-19 19:57:35 +10:00
}
/// Return a derived address using the internal (change) descriptor.
///
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
///
2023-11-16 11:18:11 -06:00
/// A `PersistBackend<ChangeSet>::WriteError` will result if unable to persist the new address
/// to the `PersistBackend`.
///
2021-08-19 19:57:35 +10:00
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
2022-08-05 11:39:50 -04:00
/// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
2021-08-19 19:57:35 +10:00
/// be returned for any [`AddressIndex`].
2024-01-13 20:38:11 +08:00
///
/// # Panics
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
2023-11-16 11:18:11 -06:00
pub fn try_get_internal_address (
& mut self ,
address_index : AddressIndex ,
) -> Result < AddressInfo , D ::WriteError >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2023-05-29 13:20:12 +08:00
self . _get_address ( KeychainKind ::Internal , address_index )
2021-08-19 19:57:35 +10:00
}
2023-05-29 13:20:12 +08:00
/// Return a derived address using the specified `keychain` (external/internal).
///
/// If `keychain` is [`KeychainKind::External`], external addresses will be derived (used for
/// receiving funds).
///
/// If `keychain` is [`KeychainKind::Internal`], internal addresses will be derived (used for
/// creating change outputs). If the wallet does not have an internal keychain, it will use the
/// external keychain to derive change outputs.
///
/// See [`AddressIndex`] for available address index selection strategies. If none of the keys
/// in the descriptor are derivable (i.e. does not end with /*) then the same address will
/// always be returned for any [`AddressIndex`].
2024-01-13 20:38:11 +08:00
///
/// # Panics
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
2023-05-29 13:20:12 +08:00
fn _get_address (
& mut self ,
keychain : KeychainKind ,
address_index : AddressIndex ,
) -> Result < AddressInfo , D ::WriteError >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
let keychain = self . map_keychain ( keychain ) ;
2023-05-09 21:49:33 +08:00
let txout_index = & mut self . indexed_graph . index ;
2023-08-07 17:43:17 +02:00
let ( index , spk , changeset ) = match address_index {
2023-02-15 12:23:59 +11:00
AddressIndex ::New = > {
2023-08-07 17:43:17 +02:00
let ( ( index , spk ) , index_changeset ) = txout_index . reveal_next_spk ( & keychain ) ;
( index , spk . into ( ) , Some ( index_changeset ) )
2023-02-15 12:23:59 +11:00
}
AddressIndex ::LastUnused = > {
2023-08-07 17:43:17 +02:00
let ( ( index , spk ) , index_changeset ) = txout_index . next_unused_spk ( & keychain ) ;
( index , spk . into ( ) , Some ( index_changeset ) )
2023-05-29 13:20:12 +08:00
}
2024-01-13 20:38:11 +08:00
AddressIndex ::Peek ( mut peek_index ) = > {
let mut spk_iter = txout_index . unbounded_spk_iter ( & keychain ) ;
if ! spk_iter . descriptor ( ) . has_wildcard ( ) {
peek_index = 0 ;
}
let ( index , spk ) = spk_iter
. nth ( peek_index as usize )
. expect ( " derivation index is out of bounds " ) ;
2023-05-29 13:20:12 +08:00
( index , spk , None )
2023-02-15 12:23:59 +11:00
}
} ;
2023-05-29 13:20:12 +08:00
2023-08-07 17:43:17 +02:00
if let Some ( changeset ) = changeset {
2023-05-29 13:20:12 +08:00
self . persist
2023-08-07 17:43:17 +02:00
. stage ( ChangeSet ::from ( indexed_tx_graph ::ChangeSet ::from (
changeset ,
) ) ) ;
2023-05-29 13:20:12 +08:00
self . persist . commit ( ) ? ;
}
Ok ( AddressInfo {
2023-02-15 12:23:59 +11:00
index ,
address : Address ::from_script ( & spk , self . network )
. expect ( " descriptor must have address form " ) ,
keychain ,
2023-05-29 13:20:12 +08: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)
2022-11-03 15:59:38 +08:00
pub fn is_mine ( & self , script : & Script ) -> bool {
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . index_of_spk ( script ) . is_some ( )
2020-02-07 23:22:28 +01:00
}
2023-02-15 12:52:10 +11:00
/// Finds how the wallet derived the script pubkey `spk`.
///
/// Will only return `Some(_)` if the wallet has given out the spk.
pub fn derivation_of_spk ( & self , spk : & Script ) -> Option < ( KeychainKind , u32 ) > {
2024-01-13 20:04:49 +08:00
self . indexed_graph . index . index_of_spk ( spk )
2023-02-15 12:52:10 +11:00
}
2020-09-04 16:29:25 +02:00
/// Return the list of unspent outputs of this wallet
2023-11-01 12:33:57 +08:00
pub fn list_unspent ( & self ) -> impl Iterator < Item = LocalOutput > + '_ {
2023-05-09 21:49:33 +08:00
self . indexed_graph
. graph ( )
. filter_chain_unspents (
& self . chain ,
2023-10-12 16:55:32 +08:00
self . chain . tip ( ) . block_id ( ) ,
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . outpoints ( ) . iter ( ) . cloned ( ) ,
)
. map ( | ( ( k , i ) , full_txo ) | new_local_utxo ( k , i , full_txo ) )
2022-11-03 15:59:38 +08:00
}
2023-11-01 13:25:28 +08:00
/// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
///
/// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
pub fn list_output ( & self ) -> impl Iterator < Item = LocalOutput > + '_ {
self . indexed_graph
. graph ( )
. filter_chain_txouts (
& self . chain ,
self . chain . tip ( ) . block_id ( ) ,
self . indexed_graph . index . outpoints ( ) . iter ( ) . cloned ( ) ,
)
. map ( | ( ( k , i ) , full_txo ) | new_local_utxo ( k , i , full_txo ) )
2022-11-03 15:59:38 +08:00
}
2023-02-15 12:23:59 +11:00
/// Get all the checkpoints the wallet is currently storing indexed by height.
2023-07-19 17:42:52 +08:00
pub fn checkpoints ( & self ) -> CheckPointIter {
self . chain . iter_checkpoints ( )
2022-11-03 15:59:38 +08:00
}
/// Returns the latest checkpoint.
2023-10-12 16:55:32 +08:00
pub fn latest_checkpoint ( & self ) -> CheckPoint {
2023-05-09 21:49:33 +08:00
self . chain . tip ( )
2022-11-03 15:59:38 +08:00
}
2024-01-17 13:29:16 +08:00
/// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
2023-02-15 12:23:59 +11:00
///
2023-08-21 15:01:08 -05:00
/// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
2023-02-15 12:23:59 +11:00
/// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
2023-08-21 15:01:08 -05:00
/// electrum server) which will go through each address until it reaches a *stop gap*.
2023-02-15 12:23:59 +11:00
///
/// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
/// script pubkeys the wallet is storing internally).
2024-01-13 20:04:49 +08:00
pub fn all_unbounded_spk_iters (
2023-02-15 12:23:59 +11:00
& self ,
2023-07-19 15:27:48 +02:00
) -> BTreeMap < KeychainKind , impl Iterator < Item = ( u32 , ScriptBuf ) > + Clone > {
2024-01-13 20:04:49 +08:00
self . indexed_graph . index . all_unbounded_spk_iters ( )
2020-02-07 23:22:28 +01:00
}
2024-01-17 13:29:16 +08:00
/// Get an unbounded script pubkey iterator for the given `keychain`.
2023-02-15 12:23:59 +11:00
///
2024-01-13 20:04:49 +08:00
/// See [`all_unbounded_spk_iters`] for more documentation
2023-02-15 12:23:59 +11:00
///
2024-01-13 20:04:49 +08:00
/// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
pub fn unbounded_spk_iter (
2023-02-15 12:23:59 +11:00
& self ,
keychain : KeychainKind ,
2023-07-19 15:27:48 +02:00
) -> impl Iterator < Item = ( u32 , ScriptBuf ) > + Clone {
2024-01-13 20:04:49 +08:00
self . indexed_graph . index . unbounded_spk_iter ( & keychain )
2023-02-15 12:23:59 +11:00
}
/// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
2021-01-01 13:35:05 +11:00
/// wallet's database.
2023-11-01 12:33:57 +08:00
pub fn get_utxo ( & self , op : OutPoint ) -> Option < LocalOutput > {
2024-01-13 20:04:49 +08:00
let ( keychain , index , _ ) = self . indexed_graph . index . txout ( op ) ? ;
2023-05-09 21:49:33 +08:00
self . indexed_graph
. graph ( )
. filter_chain_unspents (
& self . chain ,
2023-10-12 16:55:32 +08:00
self . chain . tip ( ) . block_id ( ) ,
2024-01-13 20:04:49 +08:00
core ::iter ::once ( ( ( ) , op ) ) ,
2023-05-09 21:49:33 +08:00
)
2024-01-13 20:04:49 +08:00
. map ( | ( _ , full_txo ) | new_local_utxo ( keychain , index , full_txo ) )
2023-05-09 21:49:33 +08:00
. next ( )
2021-01-01 13:35:05 +11:00
}
2023-08-23 21:41:38 -05:00
/// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
///
2023-11-21 05:06:53 +08:00
/// This is used for providing a previous output's value so that we can use [`calculate_fee`]
/// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
/// not be returned in [`list_unspent`] or [`list_output`].
2023-08-06 23:05:41 -05:00
///
2023-11-21 05:06:53 +08:00
/// Any inserted `TxOut`s are not persisted until [`commit`] is called.
2023-08-06 23:05:41 -05:00
///
2023-11-21 05:06:53 +08:00
/// **WARNING:** This should only be used to add `TxOut`s that the wallet does not own. Only
/// insert `TxOut`s that you trust the values for!
2023-08-23 21:41:38 -05:00
///
/// [`calculate_fee`]: Self::calculate_fee
/// [`calculate_fee_rate`]: Self::calculate_fee_rate
2023-11-21 05:06:53 +08:00
/// [`list_unspent`]: Self::list_unspent
/// [`list_output`]: Self::list_output
2023-08-23 21:41:38 -05:00
/// [`commit`]: Self::commit
2023-08-06 23:05:41 -05:00
pub fn insert_txout ( & mut self , outpoint : OutPoint , txout : TxOut )
where
D : PersistBackend < ChangeSet > ,
{
2023-10-06 02:05:31 +08:00
let additions = self . indexed_graph . insert_txout ( outpoint , txout ) ;
2023-08-06 23:05:41 -05:00
self . persist . stage ( ChangeSet ::from ( additions ) ) ;
}
2023-07-26 19:46:40 -05:00
/// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
2021-09-23 16:12:53 +05:30
///
2023-08-23 21:41:38 -05:00
/// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
/// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
2023-08-06 23:05:41 -05:00
///
2023-07-26 19:46:40 -05:00
/// Note `tx` does not have to be in the graph for this to work.
2023-08-06 23:05:41 -05:00
///
2023-08-23 21:41:38 -05:00
/// # Examples
///
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
/// let fee = wallet.calculate_fee(tx).expect("fee");
/// ```
///
/// ```rust, no_run
/// # use bitcoin::psbt::PartiallySignedTransaction;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let mut psbt: PartiallySignedTransaction = todo!();
/// let tx = &psbt.clone().extract_tx();
/// let fee = wallet.calculate_fee(tx).expect("fee");
/// ```
2023-08-06 23:05:41 -05:00
/// [`insert_txout`]: Self::insert_txout
2023-07-26 19:46:40 -05:00
pub fn calculate_fee ( & self , tx : & Transaction ) -> Result < u64 , CalculateFeeError > {
2023-08-01 12:42:37 -05:00
self . indexed_graph . graph ( ) . calculate_fee ( tx )
2023-07-26 19:46:40 -05:00
}
2023-08-06 23:05:41 -05:00
/// Calculate the [`FeeRate`] for a given transaction.
///
2023-08-23 21:41:38 -05:00
/// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you must
/// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
2023-07-26 19:46:40 -05:00
///
/// Note `tx` does not have to be in the graph for this to work.
2023-08-06 23:05:41 -05:00
///
2023-08-23 21:41:38 -05:00
/// # Examples
///
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
/// ```
///
/// ```rust, no_run
/// # use bitcoin::psbt::PartiallySignedTransaction;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let mut psbt: PartiallySignedTransaction = todo!();
/// let tx = &psbt.clone().extract_tx();
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
/// ```
2023-08-06 23:05:41 -05:00
/// [`insert_txout`]: Self::insert_txout
2023-07-26 19:46:40 -05:00
pub fn calculate_fee_rate ( & self , tx : & Transaction ) -> Result < FeeRate , CalculateFeeError > {
self . calculate_fee ( tx ) . map ( | fee | {
let weight = tx . weight ( ) ;
FeeRate ::from_wu ( fee , weight )
} )
}
2024-01-17 13:00:27 -05:00
/// Compute the `tx`'s sent and received amounts (in satoshis).
2023-08-23 21:41:38 -05:00
///
2024-01-17 13:00:27 -05:00
/// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
/// that spend from previous txouts tracked by this wallet. Received is the summation
/// of this tx's outputs that send to script pubkeys tracked by this wallet.
2023-08-23 21:41:38 -05:00
///
/// # Examples
///
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
/// let (sent, received) = wallet.sent_and_received(tx);
/// ```
///
/// ```rust, no_run
/// # use bitcoin::psbt::PartiallySignedTransaction;
/// # use bdk::Wallet;
/// # let mut wallet: Wallet<()> = todo!();
/// # let mut psbt: PartiallySignedTransaction = todo!();
/// let tx = &psbt.clone().extract_tx();
/// let (sent, received) = wallet.sent_and_received(tx);
/// ```
2023-07-26 19:46:40 -05:00
pub fn sent_and_received ( & self , tx : & Transaction ) -> ( u64 , u64 ) {
self . indexed_graph . index . sent_and_received ( tx )
}
2023-08-06 23:05:41 -05:00
/// Get a single transaction from the wallet as a [`CanonicalTx`] (if the transaction exists).
///
/// `CanonicalTx` contains the full transaction alongside meta-data such as:
/// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
/// in the best chain.
/// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
/// confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
/// confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
/// the transaction was last seen in the mempool is provided.
///
/// ```rust, no_run
/// use bdk::{chain::ChainPosition, Wallet};
/// use bdk_chain::Anchor;
/// # let wallet: Wallet<()> = todo!();
/// # let my_txid: bitcoin::Txid = todo!();
///
/// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
///
/// // get reference to full transaction
/// println!("my tx: {:#?}", canonical_tx.tx_node.tx);
///
/// // list all transaction anchors
/// for anchor in canonical_tx.tx_node.anchors {
/// println!(
/// "tx is anchored by block of hash {}",
/// anchor.anchor_block().hash
/// );
/// }
///
/// // get confirmation status of transaction
/// match canonical_tx.chain_position {
/// ChainPosition::Confirmed(anchor) => println!(
/// "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
/// anchor.confirmation_height, anchor.anchor_block.height, anchor.anchor_block.hash,
/// ),
/// ChainPosition::Unconfirmed(last_seen) => println!(
/// "tx is last seen at {}, it is unconfirmed as it is not anchored in the best chain",
/// last_seen,
/// ),
/// }
/// ```
///
/// [`Anchor`]: bdk_chain::Anchor
2023-07-26 19:46:40 -05:00
pub fn get_tx (
& self ,
txid : Txid ,
2023-11-12 21:31:44 +08:00
) -> Option < CanonicalTx < '_ , Transaction , ConfirmationTimeHeightAnchor > > {
2023-05-09 21:49:33 +08:00
let graph = self . indexed_graph . graph ( ) ;
2023-07-26 19:46:40 -05:00
Some ( CanonicalTx {
2023-08-07 17:43:17 +02:00
chain_position : graph . get_chain_position (
2023-05-09 21:49:33 +08:00
& self . chain ,
2023-10-12 16:55:32 +08:00
self . chain . tip ( ) . block_id ( ) ,
2023-05-09 21:49:33 +08:00
txid ,
) ? ,
2023-08-07 17:43:17 +02:00
tx_node : graph . get_tx_node ( txid ) ? ,
2023-07-26 19:46:40 -05:00
} )
2021-09-23 16:12:53 +05:30
}
2023-02-15 12:23:59 +11:00
/// Add a new checkpoint to the wallet's internal view of the chain.
/// This stages but does not [`commit`] the change.
///
/// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
/// there).
///
/// [`commit`]: Self::commit
2022-11-03 15:59:38 +08:00
pub fn insert_checkpoint (
& mut self ,
block_id : BlockId ,
2023-10-12 16:55:32 +08:00
) -> Result < bool , local_chain ::AlterCheckPointError >
2023-05-09 21:49:33 +08:00
where
D : PersistBackend < ChangeSet > ,
{
let changeset = self . chain . insert_block ( block_id ) ? ;
let changed = ! changeset . is_empty ( ) ;
2023-05-11 22:56:26 +08:00
self . persist . stage ( changeset . into ( ) ) ;
2023-02-15 12:23:59 +11:00
Ok ( changed )
2022-11-03 15:59:38 +08:00
}
2023-05-28 15:57:46 +08:00
/// Add a transaction to the wallet's internal view of the chain. This stages but does not
/// [`commit`] the change.
2023-02-15 12:23:59 +11:00
///
/// Returns whether anything changed with the transaction insertion (e.g. `false` if the
/// transaction was already inserted at the same position).
///
2023-05-28 15:57:46 +08:00
/// A `tx` can be rejected if `position` has a height greater than the [`latest_checkpoint`].
/// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually
/// inserting new transactions.
///
/// **WARNING:** If `position` is confirmed, we anchor the `tx` to a the lowest checkpoint that
/// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our
/// local view of the best chain's history.
///
2023-02-15 12:23:59 +11:00
/// [`commit`]: Self::commit
/// [`latest_checkpoint`]: Self::latest_checkpoint
/// [`insert_checkpoint`]: Self::insert_checkpoint
2022-11-03 15:59:38 +08:00
pub fn insert_tx (
& mut self ,
tx : Transaction ,
position : ConfirmationTime ,
2023-05-09 21:49:33 +08:00
) -> Result < bool , InsertTxError >
where
D : PersistBackend < ChangeSet > ,
{
2023-05-11 22:56:26 +08:00
let ( anchor , last_seen ) = match position {
2023-05-09 21:49:33 +08:00
ConfirmationTime ::Confirmed { height , time } = > {
2023-05-28 15:57:46 +08:00
// anchor tx to checkpoint with lowest height that is >= position's height
let anchor = self
. chain
2023-08-01 18:27:24 +08:00
. blocks ( )
2023-05-28 15:57:46 +08:00
. range ( height .. )
. next ( )
. ok_or ( InsertTxError ::ConfirmationHeightCannotBeGreaterThanTip {
2023-10-12 16:55:32 +08:00
tip_height : self . chain . tip ( ) . height ( ) ,
2023-05-09 21:49:33 +08:00
tx_height : height ,
2023-05-28 15:57:46 +08:00
} )
2023-11-12 21:31:44 +08:00
. map ( | ( & anchor_height , & hash ) | ConfirmationTimeHeightAnchor {
2023-05-28 15:57:46 +08:00
anchor_block : BlockId {
height : anchor_height ,
2023-07-19 17:42:52 +08:00
hash ,
2023-05-28 15:57:46 +08:00
} ,
2023-05-11 22:56:26 +08:00
confirmation_height : height ,
confirmation_time : time ,
2023-05-28 15:57:46 +08:00
} ) ? ;
( Some ( anchor ) , None )
2023-05-09 21:49:33 +08:00
}
2023-05-11 22:56:26 +08:00
ConfirmationTime ::Unconfirmed { last_seen } = > ( None , Some ( last_seen ) ) ,
2023-05-09 21:49:33 +08:00
} ;
2023-10-06 11:07:00 +08:00
let mut changeset = ChangeSet ::default ( ) ;
let txid = tx . txid ( ) ;
changeset . append ( self . indexed_graph . insert_tx ( tx ) . into ( ) ) ;
if let Some ( anchor ) = anchor {
changeset . append ( self . indexed_graph . insert_anchor ( txid , anchor ) . into ( ) ) ;
}
if let Some ( last_seen ) = last_seen {
changeset . append ( self . indexed_graph . insert_seen_at ( txid , last_seen ) . into ( ) ) ;
}
2023-05-09 21:49:33 +08:00
let changed = ! changeset . is_empty ( ) ;
2023-05-11 22:56:26 +08:00
self . persist . stage ( changeset ) ;
2023-02-15 12:23:59 +11:00
Ok ( changed )
2022-11-03 15:59:38 +08:00
}
2023-05-17 11:48:35 +08:00
/// Iterate over the transactions in the wallet.
2023-02-14 13:54:07 +11:00
pub fn transactions (
2022-11-03 15:59:38 +08:00
& self ,
2023-11-12 21:31:44 +08:00
) -> impl Iterator < Item = CanonicalTx < '_ , Transaction , ConfirmationTimeHeightAnchor > > + '_ {
2023-10-12 16:55:32 +08:00
self . indexed_graph
. graph ( )
. list_chain_txs ( & self . chain , self . chain . tip ( ) . block_id ( ) )
2020-02-07 23:22:28 +01:00
}
2022-06-22 14:37:29 +05:30
/// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
/// values.
2022-11-03 15:59:38 +08:00
pub fn get_balance ( & self ) -> Balance {
2023-05-09 21:49:33 +08:00
self . indexed_graph . graph ( ) . balance (
& self . chain ,
2023-10-12 16:55:32 +08:00
self . chain . tip ( ) . block_id ( ) ,
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . outpoints ( ) . iter ( ) . cloned ( ) ,
| & ( k , _ ) , _ | k = = KeychainKind ::Internal ,
)
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Add an external signer
///
/// See [the `signer` module](signer) for an example.
2020-08-15 23:21:13 +02:00
pub fn add_signer (
& mut self ,
2020-12-14 17:14:24 +01:00
keychain : KeychainKind ,
2020-08-17 12:10:51 +02:00
ordering : SignerOrdering ,
2022-04-26 16:54:10 +02:00
signer : Arc < dyn TransactionSigner > ,
2020-08-15 23:21:13 +02:00
) {
2020-12-14 17:14:24 +01:00
let signers = match keychain {
KeychainKind ::External = > Arc ::make_mut ( & mut self . signers ) ,
KeychainKind ::Internal = > Arc ::make_mut ( & mut self . change_signers ) ,
2020-08-15 23:21:13 +02:00
} ;
2021-01-25 15:04:56 -05:00
signers . add_external ( signer . id ( & self . secp ) , ordering , signer ) ;
2020-08-15 23:21:13 +02:00
}
2022-03-09 18:38:11 +01:00
/// Get the signers
///
/// ## Example
///
/// ```
/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::bitcoin::Network;
2023-02-21 12:38:16 +11:00
/// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
2022-03-09 18:38:11 +01:00
/// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
/// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
/// println!("secret_key: {}", secret_key);
/// }
///
/// Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn get_signers ( & self , keychain : KeychainKind ) -> Arc < SignersContainer > {
match keychain {
KeychainKind ::External = > Arc ::clone ( & self . signers ) ,
KeychainKind ::Internal = > Arc ::clone ( & self . change_signers ) ,
}
}
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::*;
2023-11-16 10:22:37 -06:00
/// # use bdk::wallet::ChangeSet;
/// # use bdk::wallet::error::CreateTxError;
/// # use bdk_chain::PersistBackend;
/// # use anyhow::Error;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2023-02-21 12:38:16 +11:00
/// # let mut wallet = doctest_wallet!();
2023-07-19 15:27:48 +02:00
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
2023-07-26 19:46:40 -05:00
/// let 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-01-01 13:35:05 +11:00
///
2020-09-04 16:29:25 +02:00
/// // sign and broadcast ...
2023-11-16 10:22:37 -06:00
/// # Ok::<(), anyhow::Error>(())
2020-09-04 16:29:25 +02:00
/// ```
2021-01-01 13:35:05 +11:00
///
/// [`TxBuilder`]: crate::TxBuilder
2023-02-15 12:23:59 +11:00
pub fn build_tx ( & mut self ) -> TxBuilder < '_ , D , DefaultCoinSelectionAlgorithm , CreateTx > {
2021-01-01 13:35:05 +11:00
TxBuilder {
2023-01-10 15:10:02 +11:00
wallet : alloc ::rc ::Rc ::new ( core ::cell ::RefCell ::new ( 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 ,
}
}
2022-11-03 15:59:38 +08:00
pub ( crate ) fn create_tx < Cs : coin_selection ::CoinSelectionAlgorithm > (
& mut self ,
2021-01-01 13:35:05 +11:00
coin_selection : Cs ,
params : TxParams ,
2023-11-16 10:22:37 -06:00
) -> Result < psbt ::PartiallySignedTransaction , CreateTxError < D ::WriteError > >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2022-11-03 15:59:38 +08:00
let external_descriptor = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. index
2022-11-03 15:59:38 +08:00
. keychains ( )
. get ( & KeychainKind ::External )
. expect ( " must exist " ) ;
let internal_descriptor = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. index
2022-11-03 15:59:38 +08:00
. keychains ( )
. get ( & KeychainKind ::Internal ) ;
let external_policy = external_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 ( ) ;
2022-11-03 15:59:38 +08:00
let internal_policy = internal_descriptor
2020-11-10 15:06:14 +01:00
. as_ref ( )
. map ( | desc | {
2023-11-16 10:22:37 -06:00
Ok ::< _ , CreateTxError < D ::WriteError > > (
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
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::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
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::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 | {
2023-11-16 10:22:37 -06:00
Ok ::< _ , CreateTxError < D ::WriteError > > (
2020-11-10 15:06:14 +01:00
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-02-07 23:22:28 +01:00
2021-01-01 13:35:05 +11:00
let version = match params . version {
2023-11-16 10:22:37 -06:00
Some ( tx_builder ::Version ( 0 ) ) = > return Err ( CreateTxError ::Version0 ) ,
2020-08-10 17:16:47 +02:00
Some ( tx_builder ::Version ( 1 ) ) if requirements . csv . is_some ( ) = > {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::Version1Csv )
2020-08-07 16:30:19 +02:00
}
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
} ;
2023-10-12 16:55:32 +08:00
// We use a match here instead of a unwrap_or_else as it's way more readable :)
2022-03-30 16:29:31 +02:00
let current_height = match params . current_height {
// If they didn't tell us the current height, we assume it's the latest sync height.
2023-10-12 16:55:32 +08:00
None = > {
let tip_height = self . chain . tip ( ) . height ( ) ;
absolute ::LockTime ::from_height ( tip_height ) . expect ( " invalid height " )
}
Some ( h ) = > h ,
2022-03-30 16:29:31 +02:00
} ;
2021-01-01 13:35:05 +11:00
let lock_time = match params . locktime {
2022-03-30 16:29:31 +02:00
// When no nLockTime is specified, we try to prevent fee sniping, if possible
None = > {
// Fee sniping can be partially prevented by setting the timelock
// to current_height. If we don't know the current_height,
// we default to 0.
2023-10-12 16:55:32 +08:00
let fee_sniping_height = current_height ;
2022-10-25 11:15:43 +02:00
2022-03-30 16:29:31 +02:00
// We choose the biggest between the required nlocktime and the fee sniping
// height
2022-10-25 11:15:43 +02:00
match requirements . timelock {
// No requirement, just use the fee_sniping_height
None = > fee_sniping_height ,
// There's a block-based requirement, but the value is lower than the fee_sniping_height
2023-11-16 10:22:37 -06:00
Some ( value @ absolute ::LockTime ::Blocks ( _ ) ) if value < fee_sniping_height = > {
fee_sniping_height
}
2022-10-25 11:15:43 +02:00
// There's a time-based requirement or a block-based requirement greater
// than the fee_sniping_height use that value
Some ( value ) = > value ,
}
2022-03-30 16:29:31 +02:00
}
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
2023-11-16 10:22:37 -06:00
Some ( x )
if requirements . timelock . unwrap ( ) . is_same_unit ( x )
& & x > = requirements . timelock . unwrap ( ) = >
{
x
}
2020-12-07 14:48:17 +01:00
// Invalid nLockTime required
2023-11-16 10:22:37 -06:00
Some ( x ) = > {
return Err ( CreateTxError ::LockTime {
requested : x ,
required : requirements . timelock . unwrap ( ) ,
} )
}
2020-08-07 16:30:19 +02:00
} ;
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
2023-07-19 15:27:48 +02:00
( None , None ) if lock_time ! = absolute ::LockTime ::ZERO = > {
Sequence ::ENABLE_LOCKTIME_NO_RBF
}
2020-12-07 14:48:17 +01:00
// No RBF, CSV or nLockTime, make the transaction final
2022-10-25 11:15:43 +02:00
( None , None ) = > Sequence ::MAX ,
2020-12-07 14:48:17 +01:00
// 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
2022-10-25 11:15:43 +02:00
( Some ( tx_builder ::RbfValue ::Value ( rbf ) ) , _ ) if ! rbf . is_rbf ( ) = > {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::RbfSequence )
2020-12-07 14:48:17 +01:00
}
// 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 ) = >
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::RbfSequenceCsv { rbf , csv } )
2020-12-07 14:48:17 +01:00
}
// 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 {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::FeeTooLow {
2021-01-01 13:35:05 +11:00
required : previous_fee . absolute ,
} ) ;
}
}
2021-07-16 15:14:20 +10:00
( FeeRate ::from_sat_per_vb ( 0.0 ) , * fee )
2021-01-01 13:35:05 +11:00
}
FeePolicy ::FeeRate ( rate ) = > {
if let Some ( previous_fee ) = params . bumping_fee {
let required_feerate = FeeRate ::from_sat_per_vb ( previous_fee . rate + 1.0 ) ;
if * rate < required_feerate {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::FeeRateTooLow {
2021-01-01 13:35:05 +11:00
required : required_feerate ,
} ) ;
}
}
2021-07-16 15:14:20 +10:00
( * rate , 0 )
2021-01-01 13:35:05 +11:00
}
} ;
2020-02-07 23:22:28 +01:00
let mut tx = Transaction {
2020-08-07 16:30:19 +02:00
version ,
2023-07-19 15:27:48 +02:00
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 ( ) {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::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 ;
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 ( ) {
2022-07-29 15:30:43 -04:00
if ! params . allow_dust
& & value . is_dust ( script_pubkey )
& & ! script_pubkey . is_provably_unspendable ( )
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::OutputBelowDustLimit ( index ) ) ;
2021-06-16 12:43:32 +10:00
}
2020-02-07 23:22:28 +01:00
2022-11-03 15:59:38 +08: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 ,
} ;
tx . output . push ( new_out ) ;
outgoing + = value ;
}
2022-07-11 21:52:11 +02:00
fee_amount + = fee_rate . fee_wu ( tx . weight ( ) ) ;
2022-07-12 15:51:27 +02:00
// Segwit transactions' header is 2WU larger than legacy txs' header,
// as they contain a witness marker (1WU) and a witness flag (1WU) (see BIP144).
// At this point we really don't know if the resulting transaction will be segwit
// or legacy, so we just add this 2WU to the fee_amount - overshooting the fee amount
// is better than undershooting it.
// If we pass a fee_amount that is slightly higher than the final fee_amount, we
// end up with a transaction with a slightly higher fee rate than the requested one.
// If, instead, we undershoot, we may end up with a feerate lower than the requested one
// - we might come up with non broadcastable txs!
2023-07-19 15:27:48 +02:00
fee_amount + = fee_rate . fee_wu ( Weight ::from_wu ( 2 ) ) ;
2022-07-12 15:51:27 +02:00
2021-01-01 13:35:05 +11:00
if params . change_policy ! = tx_builder ::ChangeSpendPolicy ::ChangeAllowed
2022-11-03 15:59:38 +08:00
& & internal_descriptor . is_none ( )
2020-08-10 17:16:47 +02:00
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::ChangePolicyDescriptor ) ;
2020-08-10 17:16:47 +02:00
}
2023-11-10 10:54:56 -05:00
let ( required_utxos , optional_utxos ) =
self . preselect_utxos ( & params , Some ( current_height . to_consensus_u32 ( ) ) ) ;
2020-10-14 14:03:12 +11:00
2022-06-13 10:49:31 -03:00
// get drain script
let drain_script = match params . drain_to {
Some ( ref drain_recipient ) = > drain_recipient . clone ( ) ,
2023-02-15 12:23:59 +11:00
None = > {
let change_keychain = self . map_keychain ( KeychainKind ::Internal ) ;
2023-08-07 17:43:17 +02:00
let ( ( index , spk ) , index_changeset ) =
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . next_unused_spk ( & change_keychain ) ;
2023-07-19 15:27:48 +02:00
let spk = spk . into ( ) ;
2024-01-13 20:04:49 +08:00
self . indexed_graph . index . mark_used ( change_keychain , index ) ;
2023-05-13 23:28:03 +08:00
self . persist
2023-08-07 17:43:17 +02:00
. stage ( ChangeSet ::from ( indexed_tx_graph ::ChangeSet ::from (
index_changeset ,
) ) ) ;
2023-11-16 10:22:37 -06:00
self . persist . commit ( ) . map_err ( CreateTxError ::Persist ) ? ;
2023-02-15 12:23:59 +11:00
spk
}
2022-06-13 10:49:31 -03:00
} ;
2024-01-16 15:51:15 +08:00
let ( required_utxos , optional_utxos ) =
coin_selection ::filter_duplicates ( required_utxos , optional_utxos ) ;
2021-02-08 15:40:56 +11:00
let coin_selection = coin_selection . coin_select (
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 ,
2022-07-10 19:57:44 -03:00
outgoing + fee_amount ,
2022-06-13 10:49:31 -03:00
& drain_script ,
2020-02-07 23:22:28 +01:00
) ? ;
2022-07-10 19:57:44 -03:00
fee_amount + = coin_selection . fee_amount ;
2022-06-13 10:49:31 -03:00
let excess = & coin_selection . excess ;
2021-02-08 15:40:56 +11:00
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 ( ) ,
2023-07-19 15:27:48 +02:00
script_sig : ScriptBuf ::default ( ) ,
2020-10-30 14:09:59 +11:00
sequence : n_sequence ,
2022-04-14 17:20:46 +02:00
witness : Witness ::new ( ) ,
2020-10-30 14:09:59 +11:00
} )
. collect ( ) ;
2020-02-07 23:22:28 +01:00
2021-06-16 12:43:32 +10:00
if tx . output . is_empty ( ) {
2022-06-06 21:45:13 +02:00
// Uh oh, our transaction has no outputs.
// We allow this when:
// - We have a drain_to address and the utxos we must spend (this happens,
// for example, when we RBF)
// - We have a drain_to address and drain_wallet set
// Otherwise, we don't know who we should send the funds to, and how much
// we should send!
if params . drain_to . is_some ( ) & & ( params . drain_wallet | | ! params . utxos . is_empty ( ) ) {
2022-06-13 10:49:31 -03:00
if let NoChange {
dust_threshold ,
remaining_amount ,
change_fee ,
} = excess
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::InsufficientFunds {
2022-06-13 10:49:31 -03:00
needed : * dust_threshold ,
available : remaining_amount . saturating_sub ( * change_fee ) ,
2021-06-16 12:43:32 +10:00
} ) ;
2020-10-28 10:37:47 +01:00
}
2021-06-16 12:43:32 +10:00
} else {
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::NoRecipients ) ;
2020-02-07 23:22:28 +01:00
}
}
2022-06-13 10:49:31 -03:00
match excess {
NoChange {
remaining_amount , ..
} = > fee_amount + = remaining_amount ,
Change { amount , fee } = > {
2022-11-03 15:59:38 +08:00
if self . is_mine ( & drain_script ) {
2022-06-13 10:49:31 -03:00
received + = amount ;
}
fee_amount + = fee ;
// create drain output
let drain_output = TxOut {
value : * amount ,
script_pubkey : drain_script ,
} ;
2022-07-11 21:52:11 +02:00
// TODO: We should pay attention when adding a new output: this might increase
2023-10-20 17:37:28 -03:00
// the length of the "number of vouts" parameter by 2 bytes, potentially making
2022-07-11 21:52:11 +02:00
// our feerate too low
2022-06-13 10:49:31 -03:00
tx . output . push ( drain_output ) ;
2021-06-16 12:43:32 +10:00
}
2022-06-13 10:49:31 -03:00
} ;
2021-06-16 12:43:32 +10:00
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
2021-02-08 15:40:56 +11:00
let psbt = self . complete_transaction ( tx , coin_selection . selected , params ) ? ;
2023-07-26 19:46:40 -05:00
Ok ( psbt )
2020-08-13 16:51:27 +02:00
}
2020-02-07 23:22:28 +01:00
2021-01-01 13:35:05 +11:00
/// Bump the fee of a transaction previously created with this wallet.
2020-09-04 16:29:25 +02:00
///
2021-01-01 13:35:05 +11:00
/// Returns an error if the transaction is already confirmed or doesn't explicitly signal
2021-11-23 13:40:58 -05:00
/// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
2021-01-01 13:35:05 +11:00
/// pre-populated with the inputs and outputs of the original transaction.
2020-09-04 16:29:25 +02:00
///
/// ## Example
///
/// ```no_run
2021-01-01 14:15:24 +11:00
/// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
2020-09-04 16:29:25 +02:00
/// # use std::str::FromStr;
/// # use bitcoin::*;
2020-09-14 14:25:38 +02:00
/// # use bdk::*;
2023-11-16 10:22:37 -06:00
/// # use bdk::wallet::ChangeSet;
/// # use bdk::wallet::error::CreateTxError;
/// # use bdk_chain::PersistBackend;
/// # use anyhow::Error;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2023-02-21 12:38:16 +11:00
/// # let mut wallet = doctest_wallet!();
2023-07-19 15:27:48 +02:00
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
2023-07-26 19:46:40 -05: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
2023-07-26 19:46:40 -05:00
/// let mut psbt = {
2021-01-11 14:14:14 +11:00
/// let mut builder = wallet.build_fee_bump(tx.txid())?;
/// builder
2023-07-19 15:27:48 +02:00
/// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
2021-01-11 14:14:14 +11:00
/// 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
2023-11-16 10:22:37 -06:00
/// # Ok::<(), anyhow::Error>(())
2020-09-04 16:29:25 +02:00
/// ```
2020-08-13 16:51:27 +02:00
// TODO: support for merging multiple transactions while bumping the fees
2021-01-01 13:35:05 +11:00
pub fn build_fee_bump (
2022-11-03 15:59:38 +08:00
& mut self ,
2021-01-01 13:35:05 +11:00
txid : Txid ,
2023-11-16 10:22:37 -06:00
) -> Result < TxBuilder < '_ , D , DefaultCoinSelectionAlgorithm , BumpFee > , BuildFeeBumpError > {
2023-05-09 21:49:33 +08:00
let graph = self . indexed_graph . graph ( ) ;
let txout_index = & self . indexed_graph . index ;
2023-10-12 16:55:32 +08:00
let chain_tip = self . chain . tip ( ) . block_id ( ) ;
2023-05-09 21:49:33 +08:00
let mut tx = graph
. get_tx ( txid )
2023-11-16 10:22:37 -06:00
. ok_or ( BuildFeeBumpError ::TransactionNotFound ( txid ) ) ?
2023-05-09 21:49:33 +08:00
. clone ( ) ;
let pos = graph
. get_chain_position ( & self . chain , chain_tip , txid )
2023-11-16 10:22:37 -06:00
. ok_or ( BuildFeeBumpError ::TransactionNotFound ( txid ) ) ? ;
2023-05-24 11:37:26 +08:00
if let ChainPosition ::Confirmed ( _ ) = pos {
2023-11-16 10:22:37 -06:00
return Err ( BuildFeeBumpError ::TransactionConfirmed ( txid ) ) ;
2023-05-09 21:49:33 +08:00
}
2022-11-03 15:59:38 +08:00
2022-10-25 11:15:43 +02:00
if ! tx
. input
. iter ( )
. any ( | txin | txin . sequence . to_consensus_u32 ( ) < = 0xFFFFFFFD )
{
2023-11-16 10:22:37 -06:00
return Err ( BuildFeeBumpError ::IrreplaceableTransaction ( tx . txid ( ) ) ) ;
2020-08-13 16:51:27 +02:00
}
2020-02-07 23:22:28 +01:00
2023-08-01 12:42:37 -05:00
let fee = self
. calculate_fee ( & tx )
2023-11-16 10:22:37 -06:00
. map_err ( | _ | BuildFeeBumpError ::FeeRateUnavailable ) ? ;
2023-08-01 12:42:37 -05:00
let fee_rate = self
. calculate_fee_rate ( & tx )
2023-11-16 10:22:37 -06:00
. map_err ( | _ | BuildFeeBumpError ::FeeRateUnavailable ) ? ;
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 ( )
2023-11-16 10:22:37 -06:00
. map ( | txin | -> Result < _ , BuildFeeBumpError > {
2023-05-09 21:49:33 +08:00
let prev_tx = graph
. get_tx ( txin . previous_output . txid )
2023-11-16 10:22:37 -06:00
. ok_or ( BuildFeeBumpError ::UnknownUtxo ( txin . previous_output ) ) ? ;
2022-11-03 15:59:38 +08:00
let txout = & prev_tx . output [ txin . previous_output . vout as usize ] ;
2020-08-13 16:51:27 +02:00
2023-05-09 21:49:33 +08:00
let confirmation_time : ConfirmationTime = graph
. get_chain_position ( & self . chain , chain_tip , txin . previous_output . txid )
2023-11-16 10:22:37 -06:00
. ok_or ( BuildFeeBumpError ::UnknownUtxo ( txin . previous_output ) ) ?
2023-05-09 21:49:33 +08:00
. cloned ( )
. into ( ) ;
2022-11-03 15:59:38 +08:00
let weighted_utxo = match txout_index . index_of_spk ( & txout . script_pubkey ) {
2024-01-13 20:04:49 +08:00
Some ( ( keychain , derivation_index ) ) = > {
2023-07-19 15:27:48 +02:00
#[ allow(deprecated) ]
2022-11-03 15:59:38 +08:00
let satisfaction_weight = self
. get_descriptor_for_keychain ( keychain )
2021-02-02 20:06:40 -05:00
. max_satisfaction_weight ( )
2022-11-03 15:59:38 +08:00
. unwrap ( ) ;
WeightedUtxo {
2023-11-01 12:33:57 +08:00
utxo : Utxo ::Local ( LocalOutput {
2022-11-03 15:59:38 +08:00
outpoint : txin . previous_output ,
txout : txout . clone ( ) ,
keychain ,
is_spent : true ,
derivation_index ,
confirmation_time ,
} ) ,
satisfaction_weight ,
}
}
2020-10-16 14:27:50 +02:00
None = > {
2022-11-03 15:59:38 +08:00
let satisfaction_weight =
2020-10-16 14:27:50 +02:00
serialize ( & txin . script_sig ) . len ( ) * 4 + serialize ( & txin . witness ) . len ( ) ;
2022-11-03 15:59:38 +08:00
WeightedUtxo {
satisfaction_weight ,
utxo : Utxo ::Foreign {
outpoint : txin . previous_output ,
psbt_input : Box ::new ( psbt ::Input {
witness_utxo : Some ( txout . clone ( ) ) ,
non_witness_utxo : Some ( prev_tx . clone ( ) ) ,
.. Default ::default ( )
} ) ,
} ,
}
2020-08-13 16:51:27 +02:00
}
2020-10-16 14:27:50 +02:00
} ;
2022-11-03 15:59:38 +08:00
Ok ( weighted_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 ( ) {
2022-11-03 15:59:38 +08:00
let change_type = self . map_keychain ( KeychainKind ::Internal ) ;
match txout_index . index_of_spk ( & txout . script_pubkey ) {
2024-01-13 20:04:49 +08:00
Some ( ( keychain , _ ) ) if keychain = = change_type = > change_index = Some ( index ) ,
2021-01-01 13:35:05 +11:00
_ = > { }
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 {
2022-11-03 15:59:38 +08:00
absolute : fee ,
2023-08-01 12:42:37 -05:00
rate : fee_rate . as_sat_per_vb ( ) ,
2021-01-01 13:35:05 +11:00
} ) ,
.. Default ::default ( )
} ;
2020-08-13 16:51:27 +02:00
2021-01-01 13:35:05 +11:00
Ok ( TxBuilder {
2023-01-10 15:10:02 +11:00
wallet : alloc ::rc ::Rc ::new ( core ::cell ::RefCell ::new ( 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
2023-01-13 20:55:38 +09:00
/// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
2020-09-04 16:29:25 +02:00
///
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::*;
2023-11-16 10:22:37 -06:00
/// # use bdk::wallet::ChangeSet;
/// # use bdk::wallet::error::CreateTxError;
/// # use bdk_chain::PersistBackend;
2020-09-04 16:29:25 +02:00
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
2023-02-21 12:38:16 +11:00
/// # let mut wallet = doctest_wallet!();
2023-07-19 15:27:48 +02:00
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
2023-07-26 19:46:40 -05: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()?
/// };
2023-11-16 10:22:37 -06: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");
2023-11-16 10:22:37 -06:00
/// # Ok::<(),anyhow::Error>(())
2022-04-14 17:20:46 +02:00
pub fn sign (
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
sign_options : SignOptions ,
2023-11-16 10:22:37 -06:00
) -> Result < bool , SignerError > {
2022-10-25 11:15:43 +02:00
// This adds all the PSBT metadata for the inputs, which will help us later figure out how
// to derive our keys
2023-11-16 10:22:37 -06:00
self . update_psbt_with_descriptor ( psbt )
. map_err ( SignerError ::MiniscriptPsbt ) ? ;
2020-02-17 14:22:53 +01:00
2022-04-29 12:59:09 +02:00
// If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
2021-05-06 17:11:43 +02:00
// has the `non_witness_utxo`
2021-05-06 11:35:58 +02:00
if ! sign_options . trust_witness_utxo
2021-05-06 17:11:43 +02:00
& & psbt
. inputs
. iter ( )
. filter ( | i | i . final_script_witness . is_none ( ) & & i . final_script_sig . is_none ( ) )
2022-04-29 12:59:09 +02:00
. filter ( | i | i . tap_internal_key . is_none ( ) & & i . tap_merkle_root . is_none ( ) )
2021-05-06 17:11:43 +02:00
. any ( | i | i . non_witness_utxo . is_none ( ) )
2021-05-06 11:35:58 +02:00
{
2023-11-16 10:22:37 -06:00
return Err ( SignerError ::MissingNonWitnessUtxo ) ;
2021-04-19 14:16:39 +02:00
}
2021-05-26 10:34:25 +02:00
// If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
2022-04-29 12:59:09 +02:00
// is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
2021-05-26 10:34:25 +02:00
if ! sign_options . allow_all_sighashes
2022-04-14 17:20:46 +02:00
& & ! psbt . inputs . iter ( ) . all ( | i | {
2022-04-29 12:59:09 +02:00
i . sighash_type . is_none ( )
| | i . sighash_type = = Some ( EcdsaSighashType ::All . into ( ) )
2023-07-19 15:27:48 +02:00
| | i . sighash_type = = Some ( TapSighashType ::All . into ( ) )
| | i . sighash_type = = Some ( TapSighashType ::Default . into ( ) )
2022-04-14 17:20:46 +02:00
} )
2021-05-26 10:34:25 +02:00
{
2023-11-16 10:22:37 -06:00
return Err ( SignerError ::NonStandardSighash ) ;
2021-05-26 10:34:25 +02:00
}
2020-08-17 12:10:51 +02:00
for signer in self
. signers
. signers ( )
. iter ( )
. chain ( self . change_signers . signers ( ) . iter ( ) )
{
2022-06-22 18:08:23 +02:00
signer . sign_transaction ( psbt , & sign_options , & self . secp ) ? ;
2020-02-07 23:22:28 +01:00
}
// attempt to finalize
2022-06-04 12:42:52 +07:00
if sign_options . try_finalize {
self . finalize_psbt ( psbt , sign_options )
} else {
Ok ( false )
}
2020-02-07 23:22:28 +01:00
}
2020-09-04 16:29:25 +02:00
/// Return the spending policies for the wallet's descriptor
2023-11-16 10:22:37 -06:00
pub fn policies ( & self , keychain : KeychainKind ) -> Result < Option < Policy > , DescriptorError > {
2022-11-03 15:59:38 +08:00
let signers = match keychain {
KeychainKind ::External = > & self . signers ,
KeychainKind ::Internal = > & self . change_signers ,
} ;
match self . public_descriptor ( keychain ) {
Some ( desc ) = > Ok ( desc . extract_policy ( signers , BuildSatisfaction ::None , & self . secp ) ? ) ,
None = > Ok ( None ) ,
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
2022-11-03 15:59:38 +08:00
pub fn public_descriptor ( & self , keychain : KeychainKind ) -> Option < & ExtendedDescriptor > {
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . keychains ( ) . get ( & keychain )
2020-05-10 17:42:02 +02:00
}
2022-09-15 14:32:10 +02:00
/// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
/// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
/// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer)
/// for further information.
///
/// Returns `true` if the PSBT could be finalized, and `false` otherwise.
2021-04-19 14:16:39 +02:00
///
/// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
2022-04-14 17:20:46 +02:00
pub fn finalize_psbt (
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
sign_options : SignOptions ,
2023-11-16 10:22:37 -06:00
) -> Result < bool , SignerError > {
2023-10-12 16:55:32 +08:00
let chain_tip = self . chain . tip ( ) . block_id ( ) ;
2023-05-09 21:49:33 +08:00
2022-04-14 17:20:46 +02:00
let tx = & psbt . unsigned_tx ;
2020-11-17 17:53:06 +11:00
let mut finished = true ;
2020-05-17 18:01:52 +02:00
2020-11-17 17:53:06 +11:00
for ( n , input ) in tx . input . iter ( ) . enumerate ( ) {
2021-05-06 15:55:58 +02:00
let psbt_input = & psbt
. inputs
. get ( n )
2023-11-16 10:22:37 -06:00
. ok_or ( 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 ;
}
2022-11-03 15:59:38 +08:00
let confirmation_height = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. graph ( )
. get_chain_position ( & self . chain , chain_tip , input . previous_output . txid )
2023-08-07 17:43:17 +02:00
. map ( | chain_position | match chain_position {
2023-05-24 11:37:26 +08:00
ChainPosition ::Confirmed ( a ) = > a . confirmation_height ,
ChainPosition ::Unconfirmed ( _ ) = > u32 ::MAX ,
2022-11-03 15:59:38 +08:00
} ) ;
2023-05-09 21:49:33 +08:00
let current_height = sign_options
. assume_height
2023-10-12 16:55:32 +08:00
. unwrap_or_else ( | | self . chain . tip ( ) . height ( ) ) ;
2020-05-17 18:01:52 +02:00
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 )
2023-03-02 19:08:33 +01:00
. and_then ( | txout | self . get_descriptor_for_txout ( & txout ) )
2020-11-17 17:53:06 +11:00
. or_else ( | | {
2023-05-09 21:49:33 +08:00
self . indexed_graph
. index
2022-11-03 15:59:38 +08:00
. keychains ( )
. iter ( )
. find_map ( | ( _ , desc ) | {
desc . derive_from_psbt_input (
psbt_input ,
psbt . get_utxo_for ( n ) ,
& self . secp ,
)
} )
2020-11-17 17:53:06 +11:00
} ) ;
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 ) ,
2023-10-12 16:55:32 +08:00
After ::new ( Some ( current_height ) , false ) ,
Older ::new ( Some ( current_height ) , confirmation_height , false ) ,
2020-11-17 17:53:06 +11:00
) ,
) {
Ok ( _ ) = > {
let psbt_input = & mut psbt . inputs [ n ] ;
psbt_input . final_script_sig = Some ( tmp_input . script_sig ) ;
psbt_input . final_script_witness = Some ( tmp_input . witness ) ;
2022-05-29 10:53:37 +07:00
if sign_options . remove_partial_sigs {
psbt_input . partial_sigs . clear ( ) ;
}
2020-11-17 17:53:06 +11:00
}
2023-11-11 13:02:02 -06:00
Err ( _ ) = > finished = false ,
2020-11-17 17:53:06 +11:00
}
2020-05-17 18:01:52 +02:00
}
2020-11-17 17:53:06 +11:00
None = > finished = false ,
2020-05-17 18:01:52 +02:00
}
}
2021-04-28 09:53:03 +10:00
Ok ( finished )
2020-05-17 18:01:52 +02:00
}
2020-12-11 14:10:11 -08:00
/// Return the secp256k1 context used for all signing operations
2020-11-16 22:07:38 +01:00
pub fn secp_ctx ( & self ) -> & SecpCtx {
& self . secp
}
2021-11-23 13:40:58 -05:00
/// Returns the descriptor used to create addresses for a particular `keychain`.
2021-01-01 13:35:05 +11:00
pub fn get_descriptor_for_keychain ( & self , keychain : KeychainKind ) -> & ExtendedDescriptor {
2022-11-03 15:59:38 +08:00
self . public_descriptor ( self . map_keychain ( keychain ) )
. expect ( " we mapped it to external if it doesn't exist " )
2021-01-01 13:35:05 +11:00
}
2022-11-03 15:59:38 +08:00
/// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
/// Otherwise, it will return the index of the highest address it has derived.
pub fn derivation_index ( & self , keychain : KeychainKind ) -> Option < u32 > {
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . last_revealed_index ( & keychain )
2020-02-07 23:22:28 +01:00
}
2022-11-03 15:59:38 +08:00
/// The index of the next address that you would get if you were to ask the wallet for a new address
pub fn next_derivation_index ( & self , keychain : KeychainKind ) -> u32 {
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . next_index ( & keychain ) . 0
2020-02-07 23:22:28 +01:00
}
2023-02-15 12:52:10 +11:00
/// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
///
/// This frees up the change address used when creating the tx for use in future transactions.
// TODO: Make this free up reserved utxos when that's implemented
pub fn cancel_tx ( & mut self , tx : & Transaction ) {
2023-05-09 21:49:33 +08:00
let txout_index = & mut self . indexed_graph . index ;
2023-02-15 12:52:10 +11:00
for txout in & tx . output {
2024-01-13 20:04:49 +08:00
if let Some ( ( keychain , index ) ) = txout_index . index_of_spk ( & txout . script_pubkey ) {
2023-02-15 12:52:10 +11:00
// NOTE: unmark_used will **not** make something unused if it has actually been used
// by a tx in the tracker. It only removes the superficial marking.
2024-01-13 20:04:49 +08:00
txout_index . unmark_used ( keychain , index ) ;
2023-02-15 12:52:10 +11:00
}
}
}
2022-11-03 15:59:38 +08:00
fn map_keychain ( & self , keychain : KeychainKind ) -> KeychainKind {
if keychain = = KeychainKind ::Internal
& & self . public_descriptor ( KeychainKind ::Internal ) . is_none ( )
2020-08-06 18:11:07 +02:00
{
2023-02-21 12:38:16 +11:00
KeychainKind ::External
2021-03-07 21:54:30 -08:00
} else {
2022-11-03 15:59:38 +08:00
keychain
2021-03-07 21:54:30 -08:00
}
}
2022-11-03 15:59:38 +08:00
fn get_descriptor_for_txout ( & self , txout : & TxOut ) -> Option < DerivedDescriptor > {
2024-01-13 20:04:49 +08:00
let ( keychain , child ) = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. index
2022-11-03 15:59:38 +08:00
. index_of_spk ( & txout . script_pubkey ) ? ;
let descriptor = self . get_descriptor_for_keychain ( keychain ) ;
2023-07-19 15:27:48 +02:00
descriptor . at_derivation_index ( child ) . ok ( )
2020-02-07 23:22:28 +01:00
}
2023-11-01 12:33:57 +08:00
fn get_available_utxos ( & self ) -> Vec < ( LocalOutput , usize ) > {
2022-11-03 15:59:38 +08:00
self . list_unspent ( )
2020-12-04 10:37:58 +11:00
. map ( | utxo | {
2020-12-14 17:14:24 +01:00
let keychain = utxo . keychain ;
2023-07-19 15:27:48 +02:00
#[ allow(deprecated) ]
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 ( ) ,
)
} )
2022-11-03 15:59:38 +08:00
. 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.
2020-10-26 14:23:46 -04:00
fn preselect_utxos (
2020-10-22 12:07:51 +11:00
& self ,
2023-11-10 10:54:56 -05:00
params : & TxParams ,
2022-05-25 18:56:50 +01:00
current_height : Option < u32 > ,
2022-11-03 15:59:38 +08:00
) -> ( Vec < WeightedUtxo > , Vec < WeightedUtxo > ) {
2023-11-10 10:54:56 -05:00
let TxParams {
change_policy ,
unspendable ,
utxos ,
drain_wallet ,
manually_selected_only ,
bumping_fee ,
..
} = params ;
let manually_selected = utxos . clone ( ) ;
// we mandate confirmed transactions if we're bumping the fee
let must_only_use_confirmed_tx = bumping_fee . is_some ( ) ;
let must_use_all_available = * drain_wallet ;
2023-10-12 16:55:32 +08:00
let chain_tip = self . chain . tip ( ) . block_id ( ) ;
2020-10-22 12:07:51 +11:00
// must_spend <- manually selected utxos
// may_spend <- all other available utxos
2022-11-03 15:59:38 +08:00
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.
2023-11-10 10:54:56 -05:00
if * manually_selected_only {
2022-11-03 15:59:38 +08:00
return ( must_spend , vec! [ ] ) ;
2020-02-07 23:22:28 +01:00
}
2020-10-22 12:07:51 +11:00
2022-05-25 18:56:50 +01:00
let satisfies_confirmed = may_spend
. iter ( )
2023-05-09 21:49:33 +08:00
. map ( | u | -> bool {
2022-11-03 15:59:38 +08:00
let txid = u . 0. outpoint . txid ;
2023-05-09 21:49:33 +08:00
let tx = match self . indexed_graph . graph ( ) . get_tx ( txid ) {
Some ( tx ) = > tx ,
None = > return false ,
} ;
let confirmation_time : ConfirmationTime = match self
. indexed_graph
. graph ( )
. get_chain_position ( & self . chain , chain_tip , txid )
{
2023-08-07 17:43:17 +02:00
Some ( chain_position ) = > chain_position . cloned ( ) . into ( ) ,
2023-05-09 21:49:33 +08:00
None = > return false ,
} ;
// Whether the UTXO is mature and, if needed, confirmed
let mut spendable = true ;
if must_only_use_confirmed_tx & & ! confirmation_time . is_confirmed ( ) {
return false ;
}
if tx . is_coin_base ( ) {
debug_assert! (
confirmation_time . is_confirmed ( ) ,
" coinbase must always be confirmed "
) ;
if let Some ( current_height ) = current_height {
match confirmation_time {
ConfirmationTime ::Confirmed { height , .. } = > {
// https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
spendable & =
( current_height . saturating_sub ( height ) ) > = COINBASE_MATURITY ;
2022-05-25 18:56:50 +01:00
}
2023-05-11 22:56:26 +08:00
ConfirmationTime ::Unconfirmed { .. } = > spendable = false ,
2022-05-25 18:56:50 +01:00
}
2022-11-03 15:59:38 +08:00
}
}
2023-05-09 21:49:33 +08:00
spendable
2022-05-25 18:56:50 +01:00
} )
2022-11-03 15:59:38 +08:00
. collect ::< Vec < _ > > ( ) ;
2020-10-23 12:13:05 +11:00
let mut i = 0 ;
2020-10-22 12:07:51 +11:00
may_spend . retain ( | u | {
2020-10-23 12:13:05 +11:00
let retain = change_policy . is_satisfied_by ( & u . 0 )
& & ! unspendable . contains ( & u . 0. outpoint )
& & satisfies_confirmed [ i ] ;
i + = 1 ;
retain
2020-10-22 12:07:51 +11:00
} ) ;
2021-02-08 15:40:56 +11:00
let mut may_spend = may_spend
. into_iter ( )
. map ( | ( local_utxo , satisfaction_weight ) | WeightedUtxo {
satisfaction_weight ,
utxo : Utxo ::Local ( local_utxo ) ,
} )
. collect ( ) ;
2020-10-22 12:07:51 +11:00
if must_use_all_available {
must_spend . append ( & mut may_spend ) ;
}
2022-11-03 15:59:38 +08:00
( 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 ,
2023-11-16 10:22:37 -06:00
) -> Result < psbt ::PartiallySignedTransaction , CreateTxError < D ::WriteError > >
where
D : PersistBackend < ChangeSet > ,
{
2022-04-14 17:20:46 +02:00
let mut psbt = psbt ::PartiallySignedTransaction ::from_unsigned_tx ( tx ) ? ;
2020-11-30 15:13:33 +01:00
2021-01-01 13:35:05 +11:00
if params . add_global_xpubs {
2022-11-03 15:59:38 +08:00
let all_xpubs = self
2023-06-01 19:24:52 +02:00
. keychains ( )
2022-11-03 15:59:38 +08:00
. iter ( )
. flat_map ( | ( _ , desc ) | desc . get_extended_keys ( ) )
. collect ::< Vec < _ > > ( ) ;
2020-11-30 15:13:33 +01:00
for xpub in all_xpubs {
let origin = match xpub . origin {
Some ( origin ) = > origin ,
None if xpub . xkey . depth = = 0 = > {
( xpub . root_fingerprint ( & self . secp ) , vec! [ ] . into ( ) )
}
2023-11-16 10:22:37 -06:00
_ = > return Err ( CreateTxError ::MissingKeyOrigin ( xpub . xkey . to_string ( ) ) ) ,
2020-11-30 15:13:33 +01:00
} ;
2022-04-14 17:20:46 +02:00
psbt . xpub . insert ( xpub . xkey , origin ) ;
2020-11-30 15:13:33 +01:00
}
}
2021-02-08 15:40:56 +11:00
let mut lookup_output = selected
2020-11-17 12:37:53 +11:00
. into_iter ( )
2021-02-08 15:40:56 +11:00
. map ( | utxo | ( utxo . outpoint ( ) , utxo ) )
2020-11-17 12:37:53 +11:00
. collect ::< HashMap < _ , _ > > ( ) ;
2020-08-13 16:51:27 +02:00
// add metadata for the inputs
2022-04-14 17:20:46 +02:00
for ( psbt_input , input ) in psbt . inputs . iter_mut ( ) . zip ( psbt . unsigned_tx . input . iter ( ) ) {
2021-02-08 15:40:56 +11:00
let utxo = match lookup_output . remove ( & input . previous_output ) {
2020-10-30 14:09:59 +11:00
Some ( utxo ) = > utxo ,
2020-08-13 16:51:27 +02:00
None = > continue ,
} ;
2021-02-08 15:40:56 +11:00
match utxo {
Utxo ::Local ( utxo ) = > {
2021-04-20 14:58:33 +02:00
* psbt_input =
match self . get_psbt_input ( utxo , params . sighash , params . only_witness_utxo ) {
Ok ( psbt_input ) = > psbt_input ,
Err ( e ) = > match e {
2023-11-16 10:22:37 -06:00
CreateTxError ::UnknownUtxo = > psbt ::Input {
2021-04-20 14:58:33 +02:00
sighash_type : params . sighash ,
2022-04-14 17:20:46 +02:00
.. psbt ::Input ::default ( )
2021-04-20 14:58:33 +02:00
} ,
_ = > return Err ( e ) ,
2021-03-15 21:50:51 -04:00
} ,
2021-04-20 14:58:33 +02:00
}
2020-08-13 16:51:27 +02:00
}
2021-02-08 15:40:56 +11:00
Utxo ::Foreign {
psbt_input : foreign_psbt_input ,
outpoint ,
} = > {
2022-04-28 15:39:31 +02:00
let is_taproot = foreign_psbt_input
. witness_utxo
. as_ref ( )
. map ( | txout | txout . script_pubkey . is_v1_p2tr ( ) )
. unwrap_or ( false ) ;
if ! is_taproot
& & ! params . only_witness_utxo
& & foreign_psbt_input . non_witness_utxo . is_none ( )
{
2023-11-16 10:22:37 -06:00
return Err ( CreateTxError ::MissingNonWitnessUtxo ( outpoint ) ) ;
2021-02-08 15:40:56 +11:00
}
* psbt_input = * foreign_psbt_input ;
2020-08-13 16:51:27 +02:00
}
}
}
2022-10-25 11:15:43 +02:00
self . update_psbt_with_descriptor ( & mut psbt ) ? ;
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 ,
2023-11-01 12:33:57 +08:00
utxo : LocalOutput ,
2022-04-14 17:20:46 +02:00
sighash_type : Option < psbt ::PsbtSighashType > ,
2021-04-20 14:58:33 +02:00
only_witness_utxo : bool ,
2023-11-16 10:22:37 -06:00
) -> Result < psbt ::Input , CreateTxError < D ::WriteError > >
where
D : PersistBackend < ChangeSet > ,
{
2021-03-15 21:50:51 -04:00
// Try to find the prev_script in our db to figure out if this is internal or external,
// and the derivation index
2024-01-13 20:04:49 +08:00
let ( keychain , child ) = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. index
2022-11-03 15:59:38 +08:00
. index_of_spk ( & utxo . txout . script_pubkey )
2023-11-16 10:22:37 -06:00
. ok_or ( CreateTxError ::UnknownUtxo ) ? ;
2021-03-15 21:50:51 -04:00
2022-04-14 17:20:46 +02:00
let mut psbt_input = psbt ::Input {
2021-03-15 21:50:51 -04:00
sighash_type ,
2022-04-14 17:20:46 +02:00
.. psbt ::Input ::default ( )
2021-03-15 21:50:51 -04:00
} ;
let desc = self . get_descriptor_for_keychain ( keychain ) ;
2023-07-19 15:27:48 +02:00
let derived_descriptor = desc
. at_derivation_index ( child )
. expect ( " child can't be hardened " ) ;
2021-03-15 21:50:51 -04:00
2022-10-25 11:15:43 +02:00
psbt_input
. update_with_descriptor_unchecked ( & derived_descriptor )
. map_err ( MiniscriptPsbtError ::Conversion ) ? ;
2021-03-15 21:50:51 -04:00
let prev_output = utxo . outpoint ;
2023-05-09 21:49:33 +08:00
if let Some ( prev_tx ) = self . indexed_graph . graph ( ) . get_tx ( prev_output . txid ) {
2022-04-27 16:29:02 +02:00
if desc . is_witness ( ) | | desc . is_taproot ( ) {
2021-03-15 21:50:51 -04:00
psbt_input . witness_utxo = Some ( prev_tx . output [ prev_output . vout as usize ] . clone ( ) ) ;
}
2022-04-27 16:29:02 +02:00
if ! desc . is_taproot ( ) & & ( ! desc . is_witness ( ) | | ! only_witness_utxo ) {
2022-11-03 15:59:38 +08:00
psbt_input . non_witness_utxo = Some ( prev_tx . clone ( ) ) ;
2021-03-15 21:50:51 -04:00
}
}
Ok ( psbt_input )
}
2022-10-25 11:15:43 +02:00
fn update_psbt_with_descriptor (
2022-04-14 17:20:46 +02:00
& self ,
psbt : & mut psbt ::PartiallySignedTransaction ,
2023-11-16 10:22:37 -06:00
) -> Result < ( ) , MiniscriptPsbtError > {
2022-10-25 11:15:43 +02:00
// We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
// the input utxos and outputs
let utxos = ( 0 .. psbt . inputs . len ( ) )
. filter_map ( | i | psbt . get_utxo_for ( i ) . map ( | utxo | ( true , i , utxo ) ) )
. chain (
psbt . unsigned_tx
. output
. iter ( )
. enumerate ( )
. map ( | ( i , out ) | ( false , i , out . clone ( ) ) ) ,
)
. collect ::< Vec < _ > > ( ) ;
2020-06-30 14:01:38 +02:00
2022-10-25 11:15:43 +02:00
// Try to figure out the keychain and derivation for every input and output
for ( is_input , index , out ) in utxos . into_iter ( ) {
2024-01-13 20:04:49 +08:00
if let Some ( ( keychain , child ) ) =
2023-05-09 21:49:33 +08:00
self . indexed_graph . index . index_of_spk ( & out . script_pubkey )
2022-10-25 11:15:43 +02:00
{
let desc = self . get_descriptor_for_keychain ( keychain ) ;
2023-07-19 15:27:48 +02:00
let desc = desc
. at_derivation_index ( child )
. expect ( " child can't be hardened " ) ;
2022-10-25 11:15:43 +02:00
if is_input {
psbt . update_input_with_descriptor ( index , & desc )
. map_err ( MiniscriptPsbtError ::UtxoUpdate ) ? ;
} else {
psbt . update_output_with_descriptor ( index , & desc )
. map_err ( MiniscriptPsbtError ::OutputUpdate ) ? ;
2020-08-06 18:11:07 +02:00
}
2020-06-30 14:01:38 +02:00
}
}
Ok ( ( ) )
}
2021-10-23 15:00:09 +02:00
2022-03-09 18:38:11 +01:00
/// Return the checksum of the public descriptor associated to `keychain`
///
/// Internally calls [`Self::get_descriptor_for_keychain`] to fetch the right descriptor
pub fn descriptor_checksum ( & self , keychain : KeychainKind ) -> String {
self . get_descriptor_for_keychain ( keychain )
. to_string ( )
2022-05-03 12:41:22 +02:00
. split_once ( '#' )
2022-03-09 18:38:11 +01:00
. unwrap ( )
2022-09-27 21:56:25 +08:00
. 1
2022-03-09 18:38:11 +01:00
. to_string ( )
}
2023-02-15 12:23:59 +11:00
/// Applies an update to the wallet and stages the changes (but does not [`commit`] them).
2023-05-17 11:48:35 +08:00
///
2023-02-15 12:23:59 +11:00
/// Usually you create an `update` by interacting with some blockchain data source and inserting
/// transactions related to your wallet into it.
///
/// [`commit`]: Self::commit
2023-08-01 18:27:24 +08:00
pub fn apply_update ( & mut self , update : Update ) -> Result < ( ) , CannotConnectError >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2023-08-24 16:03:47 +03:00
let mut changeset = match update . chain {
Some ( chain_update ) = > ChangeSet ::from ( self . chain . apply_update ( chain_update ) ? ) ,
None = > ChangeSet ::default ( ) ,
} ;
2023-08-07 17:43:17 +02:00
let ( _ , index_changeset ) = self
2023-05-09 21:49:33 +08:00
. indexed_graph
. index
2023-07-22 19:42:12 +08:00
. reveal_to_target_multi ( & update . last_active_indices ) ;
2023-08-07 17:43:17 +02:00
changeset . append ( ChangeSet ::from ( indexed_tx_graph ::ChangeSet ::from (
index_changeset ,
) ) ) ;
2023-07-19 17:42:52 +08:00
changeset . append ( ChangeSet ::from (
self . indexed_graph . apply_update ( update . graph ) ,
) ) ;
2023-05-09 21:49:33 +08:00
2023-05-11 22:56:26 +08:00
self . persist . stage ( changeset ) ;
2023-08-01 18:27:24 +08:00
Ok ( ( ) )
2023-02-15 12:23:59 +11:00
}
2023-07-26 19:46:40 -05:00
/// Commits all currently [`staged`] changed to the persistence backend returning and error when
2023-05-17 11:48:35 +08:00
/// this fails.
///
/// This returns whether the `update` resulted in any changes.
2023-02-15 12:23:59 +11:00
///
/// [`staged`]: Self::staged
2023-05-09 21:49:33 +08:00
pub fn commit ( & mut self ) -> Result < bool , D ::WriteError >
2023-02-15 12:23:59 +11:00
where
2023-05-09 21:49:33 +08:00
D : PersistBackend < ChangeSet > ,
2023-02-15 12:23:59 +11:00
{
2023-05-09 21:49:33 +08:00
self . persist . commit ( ) . map ( | c | c . is_some ( ) )
2023-02-15 12:23:59 +11:00
}
2023-10-11 13:42:21 +03:00
/// Returns the changes that will be committed with the next call to [`commit`].
2023-02-15 12:23:59 +11:00
///
/// [`commit`]: Self::commit
2023-05-09 21:49:33 +08:00
pub fn staged ( & self ) -> & ChangeSet
where
D : PersistBackend < ChangeSet > ,
{
2023-02-15 12:23:59 +11:00
self . persist . staged ( )
}
2023-03-08 12:01:01 +13:00
2023-05-11 22:56:26 +08:00
/// Get a reference to the inner [`TxGraph`].
2023-11-12 21:31:44 +08:00
pub fn tx_graph ( & self ) -> & TxGraph < ConfirmationTimeHeightAnchor > {
2023-05-09 21:49:33 +08:00
self . indexed_graph . graph ( )
2023-03-08 12:01:01 +13:00
}
2023-05-11 22:56:26 +08:00
/// Get a reference to the inner [`KeychainTxOutIndex`].
pub fn spk_index ( & self ) -> & KeychainTxOutIndex < KeychainKind > {
2023-05-09 21:49:33 +08:00
& self . indexed_graph . index
2023-03-08 12:01:01 +13:00
}
2023-05-11 22:56:26 +08:00
/// Get a reference to the inner [`LocalChain`].
pub fn local_chain ( & self ) -> & LocalChain {
2023-05-09 21:49:33 +08:00
& self . chain
2023-03-08 12:01:01 +13:00
}
2023-10-11 13:42:21 +03:00
/// Introduces a `block` of `height` to the wallet, and tries to connect it to the
/// `prev_blockhash` of the block's header.
///
/// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
/// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
///
/// [`apply_block_connected_to`]: Self::apply_block_connected_to
2024-01-02 17:31:34 +08:00
pub fn apply_block ( & mut self , block : & Block , height : u32 ) -> Result < ( ) , CannotConnectError >
2023-10-11 13:42:21 +03:00
where
D : PersistBackend < ChangeSet > ,
{
let connected_to = match height . checked_sub ( 1 ) {
Some ( prev_height ) = > BlockId {
height : prev_height ,
hash : block . header . prev_blockhash ,
} ,
None = > BlockId {
height ,
hash : block . block_hash ( ) ,
} ,
} ;
self . apply_block_connected_to ( block , height , connected_to )
. map_err ( | err | match err {
ApplyHeaderError ::InconsistentBlocks = > {
unreachable! ( " connected_to is derived from the block so must be consistent " )
}
ApplyHeaderError ::CannotConnect ( err ) = > err ,
} )
}
/// Applies relevant transactions from `block` of `height` to the wallet, and connects the
/// block to the internal chain.
///
/// The `connected_to` parameter informs the wallet how this block connects to the internal
/// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
/// internal [`TxGraph`].
pub fn apply_block_connected_to (
& mut self ,
2024-01-02 17:31:34 +08:00
block : & Block ,
2023-10-11 13:42:21 +03:00
height : u32 ,
connected_to : BlockId ,
) -> Result < ( ) , ApplyHeaderError >
where
D : PersistBackend < ChangeSet > ,
{
let mut changeset = ChangeSet ::default ( ) ;
changeset . append (
self . chain
. apply_header_connected_to ( & block . header , height , connected_to ) ?
. into ( ) ,
) ;
changeset . append (
self . indexed_graph
. apply_block_relevant ( block , height )
. into ( ) ,
) ;
self . persist . stage ( changeset ) ;
Ok ( ( ) )
}
/// Apply relevant unconfirmed transactions to the wallet.
///
/// Transactions that are not relevant are filtered out.
///
/// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
/// when the transaction was last seen in the mempool. This is used for conflict resolution
/// when there is conflicting unconfirmed transactions. The transaction with the later
2024-02-04 06:13:40 -03:00
/// `last_seen` is prioritized.
2023-10-11 13:42:21 +03:00
pub fn apply_unconfirmed_txs < ' t > (
& mut self ,
unconfirmed_txs : impl IntoIterator < Item = ( & ' t Transaction , u64 ) > ,
) where
D : PersistBackend < ChangeSet > ,
{
let indexed_graph_changeset = self
. indexed_graph
. batch_insert_relevant_unconfirmed ( unconfirmed_txs ) ;
self . persist . stage ( ChangeSet ::from ( indexed_graph_changeset ) ) ;
}
2023-03-08 12:01:01 +13:00
}
2023-11-12 21:31:44 +08:00
impl < D > AsRef < bdk_chain ::tx_graph ::TxGraph < ConfirmationTimeHeightAnchor > > for Wallet < D > {
fn as_ref ( & self ) -> & bdk_chain ::tx_graph ::TxGraph < ConfirmationTimeHeightAnchor > {
2023-05-09 21:49:33 +08:00
self . indexed_graph . graph ( )
2023-03-08 12:01:01 +13:00
}
2020-02-07 23:22:28 +01:00
}
2020-08-06 18:11:07 +02:00
2022-04-15 22:12:34 +02:00
/// Deterministically generate a unique name given the descriptors defining the wallet
///
/// Compatible with [`wallet_name_from_descriptor`]
pub fn wallet_name_from_descriptor < T > (
descriptor : T ,
change_descriptor : Option < T > ,
network : Network ,
secp : & SecpCtx ,
2023-11-16 10:22:37 -06:00
) -> Result < String , DescriptorError >
2022-04-15 22:12:34 +02:00
where
T : IntoWalletDescriptor ,
{
//TODO check descriptors contains only public keys
let descriptor = descriptor
. into_wallet_descriptor ( secp , network ) ?
. 0
. to_string ( ) ;
2022-10-24 12:05:49 -05:00
let mut wallet_name = calc_checksum ( & descriptor [ .. descriptor . find ( '#' ) . unwrap ( ) ] ) ? ;
2022-04-15 22:12:34 +02:00
if let Some ( change_descriptor ) = change_descriptor {
let change_descriptor = change_descriptor
. into_wallet_descriptor ( secp , network ) ?
. 0
. to_string ( ) ;
wallet_name . push_str (
2022-10-24 12:05:49 -05:00
calc_checksum ( & change_descriptor [ .. change_descriptor . find ( '#' ) . unwrap ( ) ] ) ? . as_str ( ) ,
2022-04-15 22:12:34 +02:00
) ;
}
Ok ( wallet_name )
}
2023-02-21 12:38:16 +11:00
2023-05-09 21:49:33 +08:00
fn new_local_utxo (
keychain : KeychainKind ,
derivation_index : u32 ,
2023-11-12 21:31:44 +08:00
full_txo : FullTxOut < ConfirmationTimeHeightAnchor > ,
2023-11-01 12:33:57 +08:00
) -> LocalOutput {
LocalOutput {
2023-05-09 21:49:33 +08:00
outpoint : full_txo . outpoint ,
txout : full_txo . txout ,
is_spent : full_txo . spent_by . is_some ( ) ,
confirmation_time : full_txo . chain_position . into ( ) ,
keychain ,
derivation_index ,
}
}
2023-10-27 14:14:25 +08:00
fn create_signers < E : IntoWalletDescriptor > (
index : & mut KeychainTxOutIndex < KeychainKind > ,
secp : & Secp256k1 < All > ,
descriptor : E ,
change_descriptor : Option < E > ,
network : Network ,
) -> Result < ( Arc < SignersContainer > , Arc < SignersContainer > ) , crate ::descriptor ::error ::Error > {
let ( descriptor , keymap ) = into_wallet_descriptor_checked ( descriptor , secp , network ) ? ;
let signers = Arc ::new ( SignersContainer ::build ( keymap , & descriptor , secp ) ) ;
index . add_keychain ( KeychainKind ::External , descriptor ) ;
let change_signers = match change_descriptor {
Some ( descriptor ) = > {
let ( descriptor , keymap ) = into_wallet_descriptor_checked ( descriptor , secp , network ) ? ;
let signers = Arc ::new ( SignersContainer ::build ( keymap , & descriptor , secp ) ) ;
index . add_keychain ( KeychainKind ::Internal , descriptor ) ;
signers
}
None = > Arc ::new ( SignersContainer ::new ( ) ) ,
} ;
Ok ( ( signers , change_signers ) )
}
2023-02-21 12:38:16 +11:00
#[ macro_export ]
#[ doc(hidden) ]
/// Macro for getting a wallet for use in a doctest
macro_rules ! doctest_wallet {
( ) = > { {
2023-07-19 15:27:48 +02:00
use $crate ::bitcoin ::{ BlockHash , Transaction , absolute , TxOut , Network , hashes ::Hash } ;
2023-02-21 12:38:16 +11:00
use $crate ::chain ::{ ConfirmationTime , BlockId } ;
use $crate ::wallet ::{ AddressIndex , Wallet } ;
let descriptor = " tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*) " ;
let change_descriptor = " tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*) " ;
let mut wallet = Wallet ::new_no_persist (
descriptor ,
Some ( change_descriptor ) ,
Network ::Regtest ,
)
. unwrap ( ) ;
let address = wallet . get_address ( AddressIndex ::New ) . address ;
let tx = Transaction {
version : 1 ,
2023-07-19 15:27:48 +02:00
lock_time : absolute ::LockTime ::ZERO ,
2023-02-21 12:38:16 +11:00
input : vec ! [ ] ,
output : vec ! [ TxOut {
value : 500_000 ,
script_pubkey : address . script_pubkey ( ) ,
} ] ,
} ;
let _ = wallet . insert_checkpoint ( BlockId { height : 1_000 , hash : BlockHash ::all_zeros ( ) } ) ;
let _ = wallet . insert_tx ( tx . clone ( ) , ConfirmationTime ::Confirmed {
height : 500 ,
time : 50_000
2023-05-11 22:56:26 +08:00
} ) ;
2023-02-21 12:38:16 +11:00
wallet
} }
}