|
19 | 19 | //! # use bdk::*;
|
20 | 20 | //! # use bdk::wallet::ChangeSet;
|
21 | 21 | //! # use bdk::wallet::error::CreateTxError;
|
22 |
| -//! # use bdk::wallet::tx_builder::CreateTx; |
23 | 22 | //! # use bdk_chain::PersistBackend;
|
24 | 23 | //! # use anyhow::Error;
|
25 | 24 | //! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
|
|
43 | 42 | use alloc::{boxed::Box, rc::Rc, string::String, vec::Vec};
|
44 | 43 | use core::cell::RefCell;
|
45 | 44 | use core::fmt;
|
46 |
| -use core::marker::PhantomData; |
47 | 45 |
|
48 | 46 | use bdk_chain::PersistBackend;
|
49 | 47 | use bitcoin::psbt::{self, PartiallySignedTransaction as Psbt};
|
50 | 48 | use bitcoin::script::PushBytes;
|
51 | 49 | use bitcoin::{absolute, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
|
52 | 50 |
|
53 |
| -use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm}; |
| 51 | +use super::coin_selection::CoinSelectionAlgorithm; |
54 | 52 | use super::{ChangeSet, CreateTxError, Wallet};
|
55 | 53 | use crate::collections::{BTreeMap, HashSet};
|
56 | 54 | use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
|
57 | 55 |
|
58 |
| -/// Context in which the [`TxBuilder`] is valid |
59 |
| -pub trait TxBuilderContext: core::fmt::Debug + Default + Clone {} |
60 |
| - |
61 |
| -/// Marker type to indicate the [`TxBuilder`] is being used to create a new transaction (as opposed |
62 |
| -/// to bumping the fee of an existing one). |
63 |
| -#[derive(Debug, Default, Clone)] |
64 |
| -pub struct CreateTx; |
65 |
| -impl TxBuilderContext for CreateTx {} |
66 |
| - |
67 |
| -/// Marker type to indicate the [`TxBuilder`] is being used to bump the fee of an existing transaction. |
68 |
| -#[derive(Debug, Default, Clone)] |
69 |
| -pub struct BumpFee; |
70 |
| -impl TxBuilderContext for BumpFee {} |
71 |
| - |
72 | 56 | /// A transaction builder
|
73 | 57 | ///
|
74 | 58 | /// A `TxBuilder` is created by calling [`build_tx`] or [`build_fee_bump`] on a wallet. After
|
@@ -124,11 +108,10 @@ impl TxBuilderContext for BumpFee {}
|
124 | 108 | /// [`finish`]: Self::finish
|
125 | 109 | /// [`coin_selection`]: Self::coin_selection
|
126 | 110 | #[derive(Debug)]
|
127 |
| -pub struct TxBuilder<'a, D, Cs, Ctx> { |
| 111 | +pub struct TxBuilder<'a, D, Cs> { |
128 | 112 | pub(crate) wallet: Rc<RefCell<&'a mut Wallet<D>>>,
|
129 | 113 | pub(crate) params: TxParams,
|
130 | 114 | pub(crate) coin_selection: Cs,
|
131 |
| - pub(crate) phantom: PhantomData<Ctx>, |
132 | 115 | }
|
133 | 116 |
|
134 | 117 | /// The parameters for transaction creation sans coin selection algorithm.
|
@@ -176,19 +159,18 @@ impl Default for FeePolicy {
|
176 | 159 | }
|
177 | 160 | }
|
178 | 161 |
|
179 |
| -impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> { |
| 162 | +impl<'a, D, Cs: Clone> Clone for TxBuilder<'a, D, Cs> { |
180 | 163 | fn clone(&self) -> Self {
|
181 | 164 | TxBuilder {
|
182 | 165 | wallet: self.wallet.clone(),
|
183 | 166 | params: self.params.clone(),
|
184 | 167 | coin_selection: self.coin_selection.clone(),
|
185 |
| - phantom: PhantomData, |
186 | 168 | }
|
187 | 169 | }
|
188 | 170 | }
|
189 | 171 |
|
190 |
| -// methods supported by both contexts, for any CoinSelectionAlgorithm |
191 |
| -impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> { |
| 172 | +// methods supported for any CoinSelectionAlgorithm |
| 173 | +impl<'a, D, Cs> TxBuilder<'a, D, Cs> { |
192 | 174 | /// Set a custom fee rate.
|
193 | 175 | ///
|
194 | 176 | /// This method sets the mining fee paid by the transaction as a rate on its size.
|
@@ -556,16 +538,17 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
556 | 538 | ///
|
557 | 539 | /// Overrides the [`DefaultCoinSelectionAlgorithm`].
|
558 | 540 | ///
|
559 |
| - /// Note that this function consumes the builder and returns it so it is usually best to put this as the first call on the builder. |
| 541 | + /// Note that this function consumes the builder and returns it, so it is usually best to put this as the first call on the builder. |
| 542 | + /// |
| 543 | + /// [`DefaultCoinSelectionAlgorithm`]: super::coin_selection::DefaultCoinSelectionAlgorithm |
560 | 544 | pub fn coin_selection<P: CoinSelectionAlgorithm>(
|
561 | 545 | self,
|
562 | 546 | coin_selection: P,
|
563 |
| - ) -> TxBuilder<'a, D, P, Ctx> { |
| 547 | + ) -> TxBuilder<'a, D, P> { |
564 | 548 | TxBuilder {
|
565 | 549 | wallet: self.wallet,
|
566 | 550 | params: self.params,
|
567 | 551 | coin_selection,
|
568 |
| - phantom: PhantomData, |
569 | 552 | }
|
570 | 553 | }
|
571 | 554 |
|
@@ -613,9 +596,79 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
613 | 596 | self.params.allow_dust = allow_dust;
|
614 | 597 | self
|
615 | 598 | }
|
| 599 | + |
| 600 | + /// Replace the recipients already added with a new list |
| 601 | + pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self { |
| 602 | + self.params.recipients = recipients; |
| 603 | + self |
| 604 | + } |
| 605 | + |
| 606 | + /// Add a recipient to the internal list |
| 607 | + pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self { |
| 608 | + self.params.recipients.push((script_pubkey, amount)); |
| 609 | + self |
| 610 | + } |
| 611 | + |
| 612 | + /// Add data as an output, using OP_RETURN |
| 613 | + pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self { |
| 614 | + let script = ScriptBuf::new_op_return(data); |
| 615 | + self.add_recipient(script, 0u64); |
| 616 | + self |
| 617 | + } |
| 618 | + |
| 619 | + /// Sets the address to *drain* excess coins to. |
| 620 | + /// |
| 621 | + /// Usually, when there are excess coins they are sent to a change address generated by the |
| 622 | + /// wallet. This option replaces the usual change address with an arbitrary `script_pubkey` of |
| 623 | + /// your choosing. Just as with a change output, if the drain output is not needed (the excess |
| 624 | + /// coins are too small) it will not be included in the resulting transaction. The only |
| 625 | + /// difference is that it is valid to use `drain_to` without setting any ordinary recipients |
| 626 | + /// with [`add_recipient`] (but it is perfectly fine to add recipients as well). |
| 627 | + /// |
| 628 | + /// If you choose not to set any recipients, you should provide the utxos that the |
| 629 | + /// transaction should spend via [`add_utxos`]. |
| 630 | + /// |
| 631 | + /// # Example |
| 632 | + /// |
| 633 | + /// `drain_to` is very useful for draining all the coins in a wallet with [`drain_wallet`] to a |
| 634 | + /// single address. |
| 635 | + /// |
| 636 | + /// ``` |
| 637 | + /// # use std::str::FromStr; |
| 638 | + /// # use bitcoin::*; |
| 639 | + /// # use bdk::*; |
| 640 | + /// # use bdk::wallet::ChangeSet; |
| 641 | + /// # use bdk::wallet::error::CreateTxError; |
| 642 | + /// # use bdk_chain::PersistBackend; |
| 643 | + /// # use anyhow::Error; |
| 644 | + /// # let to_address = |
| 645 | + /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt") |
| 646 | + /// .unwrap() |
| 647 | + /// .assume_checked(); |
| 648 | + /// # let mut wallet = doctest_wallet!(); |
| 649 | + /// let mut tx_builder = wallet.build_tx(); |
| 650 | + /// |
| 651 | + /// tx_builder |
| 652 | + /// // Spend all outputs in this wallet. |
| 653 | + /// .drain_wallet() |
| 654 | + /// // Send the excess (which is all the coins minus the fee) to this address. |
| 655 | + /// .drain_to(to_address.script_pubkey()) |
| 656 | + /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) |
| 657 | + /// .enable_rbf(); |
| 658 | + /// let psbt = tx_builder.finish()?; |
| 659 | + /// # Ok::<(), anyhow::Error>(()) |
| 660 | + /// ``` |
| 661 | + /// |
| 662 | + /// [`add_recipient`]: Self::add_recipient |
| 663 | + /// [`add_utxos`]: Self::add_utxos |
| 664 | + /// [`drain_wallet`]: Self::drain_wallet |
| 665 | + pub fn drain_to(&mut self, script_pubkey: ScriptBuf) -> &mut Self { |
| 666 | + self.params.drain_to = Some(script_pubkey); |
| 667 | + self |
| 668 | + } |
616 | 669 | }
|
617 | 670 |
|
618 |
| -impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx> TxBuilder<'a, D, Cs, Ctx> { |
| 671 | +impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs> { |
619 | 672 | /// Finish building the transaction.
|
620 | 673 | ///
|
621 | 674 | /// Returns a new [`Psbt`] per [`BIP174`].
|
@@ -693,137 +746,6 @@ impl fmt::Display for AddForeignUtxoError {
|
693 | 746 | #[cfg(feature = "std")]
|
694 | 747 | impl std::error::Error for AddForeignUtxoError {}
|
695 | 748 |
|
696 |
| -#[derive(Debug)] |
697 |
| -/// Error returned from [`TxBuilder::allow_shrinking`] |
698 |
| -pub enum AllowShrinkingError { |
699 |
| - /// Script/PubKey was not in the original transaction |
700 |
| - MissingScriptPubKey(ScriptBuf), |
701 |
| -} |
702 |
| - |
703 |
| -impl fmt::Display for AllowShrinkingError { |
704 |
| - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
705 |
| - match self { |
706 |
| - Self::MissingScriptPubKey(script_buf) => write!( |
707 |
| - f, |
708 |
| - "Script/PubKey was not in the original transaction: {}", |
709 |
| - script_buf, |
710 |
| - ), |
711 |
| - } |
712 |
| - } |
713 |
| -} |
714 |
| - |
715 |
| -#[cfg(feature = "std")] |
716 |
| -impl std::error::Error for AllowShrinkingError {} |
717 |
| - |
718 |
| -impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> { |
719 |
| - /// Replace the recipients already added with a new list |
720 |
| - pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self { |
721 |
| - self.params.recipients = recipients; |
722 |
| - self |
723 |
| - } |
724 |
| - |
725 |
| - /// Add a recipient to the internal list |
726 |
| - pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self { |
727 |
| - self.params.recipients.push((script_pubkey, amount)); |
728 |
| - self |
729 |
| - } |
730 |
| - |
731 |
| - /// Add data as an output, using OP_RETURN |
732 |
| - pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self { |
733 |
| - let script = ScriptBuf::new_op_return(data); |
734 |
| - self.add_recipient(script, 0u64); |
735 |
| - self |
736 |
| - } |
737 |
| - |
738 |
| - /// Sets the address to *drain* excess coins to. |
739 |
| - /// |
740 |
| - /// Usually, when there are excess coins they are sent to a change address generated by the |
741 |
| - /// wallet. This option replaces the usual change address with an arbitrary `script_pubkey` of |
742 |
| - /// your choosing. Just as with a change output, if the drain output is not needed (the excess |
743 |
| - /// coins are too small) it will not be included in the resulting transaction. The only |
744 |
| - /// difference is that it is valid to use `drain_to` without setting any ordinary recipients |
745 |
| - /// with [`add_recipient`] (but it is perfectly fine to add recipients as well). |
746 |
| - /// |
747 |
| - /// If you choose not to set any recipients, you should either provide the utxos that the |
748 |
| - /// transaction should spend via [`add_utxos`], or set [`drain_wallet`] to spend all of them. |
749 |
| - /// |
750 |
| - /// When bumping the fees of a transaction made with this option, you probably want to |
751 |
| - /// use [`allow_shrinking`] to allow this output to be reduced to pay for the extra fees. |
752 |
| - /// |
753 |
| - /// # Example |
754 |
| - /// |
755 |
| - /// `drain_to` is very useful for draining all the coins in a wallet with [`drain_wallet`] to a |
756 |
| - /// single address. |
757 |
| - /// |
758 |
| - /// ``` |
759 |
| - /// # use std::str::FromStr; |
760 |
| - /// # use bitcoin::*; |
761 |
| - /// # use bdk::*; |
762 |
| - /// # use bdk::wallet::ChangeSet; |
763 |
| - /// # use bdk::wallet::error::CreateTxError; |
764 |
| - /// # use bdk::wallet::tx_builder::CreateTx; |
765 |
| - /// # use bdk_chain::PersistBackend; |
766 |
| - /// # use anyhow::Error; |
767 |
| - /// # let to_address = |
768 |
| - /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt") |
769 |
| - /// .unwrap() |
770 |
| - /// .assume_checked(); |
771 |
| - /// # let mut wallet = doctest_wallet!(); |
772 |
| - /// let mut tx_builder = wallet.build_tx(); |
773 |
| - /// |
774 |
| - /// tx_builder |
775 |
| - /// // Spend all outputs in this wallet. |
776 |
| - /// .drain_wallet() |
777 |
| - /// // Send the excess (which is all the coins minus the fee) to this address. |
778 |
| - /// .drain_to(to_address.script_pubkey()) |
779 |
| - /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) |
780 |
| - /// .enable_rbf(); |
781 |
| - /// let psbt = tx_builder.finish()?; |
782 |
| - /// # Ok::<(), anyhow::Error>(()) |
783 |
| - /// ``` |
784 |
| - /// |
785 |
| - /// [`allow_shrinking`]: Self::allow_shrinking |
786 |
| - /// [`add_recipient`]: Self::add_recipient |
787 |
| - /// [`add_utxos`]: Self::add_utxos |
788 |
| - /// [`drain_wallet`]: Self::drain_wallet |
789 |
| - pub fn drain_to(&mut self, script_pubkey: ScriptBuf) -> &mut Self { |
790 |
| - self.params.drain_to = Some(script_pubkey); |
791 |
| - self |
792 |
| - } |
793 |
| -} |
794 |
| - |
795 |
| -// methods supported only by bump_fee |
796 |
| -impl<'a, D> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> { |
797 |
| - /// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this |
798 |
| - /// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet |
799 |
| - /// will attempt to find a change output to shrink instead. |
800 |
| - /// |
801 |
| - /// **Note** that the output may shrink to below the dust limit and therefore be removed. If it is |
802 |
| - /// preserved then it is currently not guaranteed to be in the same position as it was |
803 |
| - /// originally. |
804 |
| - /// |
805 |
| - /// Returns an `Err` if `script_pubkey` can't be found among the recipients of the |
806 |
| - /// transaction we are bumping. |
807 |
| - pub fn allow_shrinking( |
808 |
| - &mut self, |
809 |
| - script_pubkey: ScriptBuf, |
810 |
| - ) -> Result<&mut Self, AllowShrinkingError> { |
811 |
| - match self |
812 |
| - .params |
813 |
| - .recipients |
814 |
| - .iter() |
815 |
| - .position(|(recipient_script, _)| *recipient_script == script_pubkey) |
816 |
| - { |
817 |
| - Some(position) => { |
818 |
| - self.params.recipients.remove(position); |
819 |
| - self.params.drain_to = Some(script_pubkey); |
820 |
| - Ok(self) |
821 |
| - } |
822 |
| - None => Err(AllowShrinkingError::MissingScriptPubKey(script_pubkey)), |
823 |
| - } |
824 |
| - } |
825 |
| -} |
826 |
| - |
827 | 749 | /// Ordering of the transaction's inputs and outputs
|
828 | 750 | #[derive(Default, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
829 | 751 | pub enum TxOrdering {
|
|
0 commit comments