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