1use std::cell::RefCell;
17use std::collections::HashMap;
18use std::collections::{BTreeMap, HashSet};
19use std::fmt;
20use std::ops::Deref;
21use std::str::FromStr;
22use std::sync::Arc;
23
24use bitcoin::secp256k1::Secp256k1;
25
26use bitcoin::consensus::encode::serialize;
27use bitcoin::util::psbt;
28use bitcoin::{
29 Address, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence,
30 Transaction, TxOut, Txid, Witness,
31};
32
33use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
34
35#[allow(unused_imports)]
36use log::{debug, error, info, trace};
37
38pub mod coin_selection;
39pub mod export;
40pub mod signer;
41pub mod time;
42pub mod tx_builder;
43pub(crate) mod utils;
44#[cfg(feature = "verify")]
45#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
46pub mod verify;
47
48#[cfg(feature = "hardware-signer")]
49#[cfg_attr(docsrs, doc(cfg(feature = "hardware-signer")))]
50pub mod hardwaresigner;
51
52pub use utils::IsDust;
53
54use coin_selection::DefaultCoinSelectionAlgorithm;
55use signer::{SignOptions, SignerOrdering, SignersContainer, TransactionSigner};
56use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
57use utils::{check_nsequence_rbf, After, Older, SecpCtx};
58
59use crate::blockchain::{GetHeight, NoopProgress, Progress, WalletSync};
60use crate::database::memory::MemoryDatabase;
61use crate::database::{AnyDatabase, BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
62use crate::descriptor::checksum::calc_checksum_bytes_internal;
63use crate::descriptor::policy::BuildSatisfaction;
64use crate::descriptor::{
65 calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DescriptorMeta,
66 ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
67};
68use crate::error::{Error, MiniscriptPsbtError};
69use crate::psbt::PsbtUtils;
70use crate::signer::SignerError;
71use crate::testutils;
72use crate::types::*;
73use crate::wallet::coin_selection::Excess::{Change, NoChange};
74
75const CACHE_ADDR_BATCH_SIZE: u32 = 100;
76const COINBASE_MATURITY: u32 = 100;
77
78#[derive(Debug)]
90pub struct Wallet<D> {
91 descriptor: ExtendedDescriptor,
92 change_descriptor: Option<ExtendedDescriptor>,
93
94 signers: Arc<SignersContainer>,
95 change_signers: Arc<SignersContainer>,
96
97 network: Network,
98
99 database: RefCell<D>,
100
101 secp: SecpCtx,
102}
103
104#[derive(Debug)]
107pub enum AddressIndex {
108 New,
110 LastUnused,
118 Peek(u32),
124 Reset(u32),
133}
134
135#[derive(Debug, PartialEq, Eq)]
138pub struct AddressInfo {
139 pub index: u32,
141 pub address: Address,
143 pub keychain: KeychainKind,
145}
146
147impl Deref for AddressInfo {
148 type Target = Address;
149
150 fn deref(&self) -> &Self::Target {
151 &self.address
152 }
153}
154
155impl fmt::Display for AddressInfo {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "{}", self.address)
158 }
159}
160
161#[derive(Debug, Default)]
162pub struct SyncOptions {
166 pub progress: Option<Box<dyn Progress>>,
168}
169
170impl<D> Wallet<D>
171where
172 D: BatchDatabase,
173{
174 #[deprecated = "Just use Wallet::new -- all wallets are offline now!"]
175 pub fn new_offline<E: IntoWalletDescriptor>(
177 descriptor: E,
178 change_descriptor: Option<E>,
179 network: Network,
180 database: D,
181 ) -> Result<Self, Error> {
182 Self::new(descriptor, change_descriptor, network, database)
183 }
184
185 pub fn new<E: IntoWalletDescriptor>(
189 descriptor: E,
190 change_descriptor: Option<E>,
191 network: Network,
192 mut database: D,
193 ) -> Result<Self, Error> {
194 let secp = Secp256k1::new();
195
196 let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
197 Self::db_checksum(
198 &mut database,
199 &descriptor.to_string(),
200 KeychainKind::External,
201 )?;
202 let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp));
203 let (change_descriptor, change_signers) = match change_descriptor {
204 Some(desc) => {
205 let (change_descriptor, change_keymap) =
206 into_wallet_descriptor_checked(desc, &secp, network)?;
207 Self::db_checksum(
208 &mut database,
209 &change_descriptor.to_string(),
210 KeychainKind::Internal,
211 )?;
212
213 let change_signers = Arc::new(SignersContainer::build(
214 change_keymap,
215 &change_descriptor,
216 &secp,
217 ));
218
219 (Some(change_descriptor), change_signers)
220 }
221 None => (None, Arc::new(SignersContainer::new())),
222 };
223
224 Ok(Wallet {
225 descriptor,
226 change_descriptor,
227 signers,
228 change_signers,
229 network,
230 database: RefCell::new(database),
231 secp,
232 })
233 }
234
235 fn db_checksum(db: &mut D, desc: &str, kind: KeychainKind) -> Result<(), Error> {
239 let checksum = calc_checksum_bytes_internal(desc, true)?;
240 if db.check_descriptor_checksum(kind, checksum).is_ok() {
241 return Ok(());
242 }
243
244 let checksum_inception = calc_checksum_bytes_internal(desc, false)?;
245 db.check_descriptor_checksum(kind, checksum_inception)
246 }
247
248 pub fn network(&self) -> Network {
250 self.network
251 }
252
253 fn get_new_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
255 let incremented_index = self.fetch_and_increment_index(keychain)?;
256
257 let address_result = self
258 .get_descriptor_for_keychain(keychain)
259 .at_derivation_index(incremented_index)
260 .address(self.network);
261
262 address_result
263 .map(|address| AddressInfo {
264 address,
265 index: incremented_index,
266 keychain,
267 })
268 .map_err(|_| Error::ScriptDoesntHaveAddressForm)
269 }
270
271 fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
274 let current_index = self.fetch_index(keychain)?;
275
276 let derived_key = self
277 .get_descriptor_for_keychain(keychain)
278 .at_derivation_index(current_index);
279
280 let script_pubkey = derived_key.script_pubkey();
281
282 let found_used = self
283 .list_transactions(true)?
284 .iter()
285 .flat_map(|tx_details| tx_details.transaction.as_ref())
286 .flat_map(|tx| tx.output.iter())
287 .any(|o| o.script_pubkey == script_pubkey);
288
289 if found_used {
290 self.get_new_address(keychain)
291 } else {
292 derived_key
293 .address(self.network)
294 .map(|address| AddressInfo {
295 address,
296 index: current_index,
297 keychain,
298 })
299 .map_err(|_| Error::ScriptDoesntHaveAddressForm)
300 }
301 }
302
303 fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
305 self.get_descriptor_for_keychain(keychain)
306 .at_derivation_index(index)
307 .address(self.network)
308 .map(|address| AddressInfo {
309 index,
310 address,
311 keychain,
312 })
313 .map_err(|_| Error::ScriptDoesntHaveAddressForm)
314 }
315
316 fn reset_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
319 self.set_index(keychain, index)?;
320
321 self.get_descriptor_for_keychain(keychain)
322 .at_derivation_index(index)
323 .address(self.network)
324 .map(|address| AddressInfo {
325 index,
326 address,
327 keychain,
328 })
329 .map_err(|_| Error::ScriptDoesntHaveAddressForm)
330 }
331
332 pub fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
336 self._get_address(address_index, KeychainKind::External)
337 }
338
339 pub fn get_internal_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
347 self._get_address(address_index, KeychainKind::Internal)
348 }
349
350 fn _get_address(
351 &self,
352 address_index: AddressIndex,
353 keychain: KeychainKind,
354 ) -> Result<AddressInfo, Error> {
355 match address_index {
356 AddressIndex::New => self.get_new_address(keychain),
357 AddressIndex::LastUnused => self.get_unused_address(keychain),
358 AddressIndex::Peek(index) => self.peek_address(index, keychain),
359 AddressIndex::Reset(index) => self.reset_address(index, keychain),
360 }
361 }
362
363 pub fn ensure_addresses_cached(&self, max_addresses: u32) -> Result<bool, Error> {
370 let mut new_addresses_cached = false;
371 let max_address = match self.descriptor.has_wildcard() {
372 false => 0,
373 true => max_addresses,
374 };
375 debug!("max_address {}", max_address);
376 if self
377 .database
378 .borrow()
379 .get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))?
380 .is_none()
381 {
382 debug!("caching external addresses");
383 new_addresses_cached = true;
384 self.cache_addresses(KeychainKind::External, 0, max_address)?;
385 }
386
387 if let Some(change_descriptor) = &self.change_descriptor {
388 let max_address = match change_descriptor.has_wildcard() {
389 false => 0,
390 true => max_addresses,
391 };
392
393 if self
394 .database
395 .borrow()
396 .get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))?
397 .is_none()
398 {
399 debug!("caching internal addresses");
400 new_addresses_cached = true;
401 self.cache_addresses(KeychainKind::Internal, 0, max_address)?;
402 }
403 }
404 Ok(new_addresses_cached)
405 }
406
407 pub fn is_mine(&self, script: &Script) -> Result<bool, Error> {
409 self.database.borrow().is_mine(script)
410 }
411
412 pub fn list_unspent(&self) -> Result<Vec<LocalUtxo>, Error> {
417 Ok(self
418 .database
419 .borrow()
420 .iter_utxos()?
421 .into_iter()
422 .filter(|l| !l.is_spent)
423 .collect())
424 }
425
426 pub fn get_utxo(&self, outpoint: OutPoint) -> Result<Option<LocalUtxo>, Error> {
429 self.database.borrow().get_utxo(&outpoint)
430 }
431
432 pub fn get_tx(
440 &self,
441 txid: &Txid,
442 include_raw: bool,
443 ) -> Result<Option<TransactionDetails>, Error> {
444 self.database.borrow().get_tx(txid, include_raw)
445 }
446
447 pub fn list_transactions(&self, include_raw: bool) -> Result<Vec<TransactionDetails>, Error> {
466 self.database.borrow().iter_txs(include_raw)
467 }
468
469 pub fn get_balance(&self) -> Result<Balance, Error> {
475 let mut immature = 0;
476 let mut trusted_pending = 0;
477 let mut untrusted_pending = 0;
478 let mut confirmed = 0;
479 let utxos = self.list_unspent()?;
480 let database = self.database.borrow();
481 let last_sync_height = match database
482 .get_sync_time()?
483 .map(|sync_time| sync_time.block_time.height)
484 {
485 Some(height) => height,
486 None => return Ok(Balance::default()),
488 };
489 for u in utxos {
490 let tx = database
492 .get_tx(&u.outpoint.txid, true)?
493 .expect("Transaction not found in database");
494 if let Some(tx_conf_time) = &tx.confirmation_time {
495 if tx.transaction.expect("No transaction").is_coin_base()
496 && (last_sync_height - tx_conf_time.height) < COINBASE_MATURITY
497 {
498 immature += u.txout.value;
499 } else {
500 confirmed += u.txout.value;
501 }
502 } else if u.keychain == KeychainKind::Internal {
503 trusted_pending += u.txout.value;
504 } else {
505 untrusted_pending += u.txout.value;
506 }
507 }
508
509 Ok(Balance {
510 immature,
511 trusted_pending,
512 untrusted_pending,
513 confirmed,
514 })
515 }
516
517 pub fn add_signer(
521 &mut self,
522 keychain: KeychainKind,
523 ordering: SignerOrdering,
524 signer: Arc<dyn TransactionSigner>,
525 ) {
526 let signers = match keychain {
527 KeychainKind::External => Arc::make_mut(&mut self.signers),
528 KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
529 };
530
531 signers.add_external(signer.id(&self.secp), ordering, signer);
532 }
533
534 pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
551 match keychain {
552 KeychainKind::External => Arc::clone(&self.signers),
553 KeychainKind::Internal => Arc::clone(&self.change_signers),
554 }
555 }
556
557 pub fn build_tx(&self) -> TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, CreateTx> {
584 TxBuilder {
585 wallet: self,
586 params: TxParams::default(),
587 coin_selection: DefaultCoinSelectionAlgorithm::default(),
588 phantom: core::marker::PhantomData,
589 }
590 }
591
592 pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm<D>>(
593 &self,
594 coin_selection: Cs,
595 params: TxParams,
596 ) -> Result<(psbt::PartiallySignedTransaction, TransactionDetails), Error> {
597 let external_policy = self
598 .descriptor
599 .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
600 .unwrap();
601 let internal_policy = self
602 .change_descriptor
603 .as_ref()
604 .map(|desc| {
605 Ok::<_, Error>(
606 desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
607 .unwrap(),
608 )
609 })
610 .transpose()?;
611
612 if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
615 && external_policy.requires_path()
616 && params.external_policy_path.is_none()
617 {
618 return Err(Error::SpendingPolicyRequired(KeychainKind::External));
619 };
620 if let Some(internal_policy) = &internal_policy {
622 if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
623 && internal_policy.requires_path()
624 && params.internal_policy_path.is_none()
625 {
626 return Err(Error::SpendingPolicyRequired(KeychainKind::Internal));
627 };
628 }
629
630 let external_requirements = external_policy.get_condition(
631 params
632 .external_policy_path
633 .as_ref()
634 .unwrap_or(&BTreeMap::new()),
635 )?;
636 let internal_requirements = internal_policy
637 .map(|policy| {
638 Ok::<_, Error>(
639 policy.get_condition(
640 params
641 .internal_policy_path
642 .as_ref()
643 .unwrap_or(&BTreeMap::new()),
644 )?,
645 )
646 })
647 .transpose()?;
648
649 let requirements =
650 external_requirements.merge(&internal_requirements.unwrap_or_default())?;
651 debug!("Policy requirements: {:?}", requirements);
652
653 let version = match params.version {
654 Some(tx_builder::Version(0)) => {
655 return Err(Error::Generic("Invalid version `0`".into()))
656 }
657 Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
658 return Err(Error::Generic(
659 "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
660 .into(),
661 ))
662 }
663 Some(tx_builder::Version(x)) => x,
664 None if requirements.csv.is_some() => 2,
665 _ => 1,
666 };
667
668 let current_height = match params.current_height {
670 None => self.database().get_sync_time()?.map(|sync_time| {
672 LockTime::from_height(sync_time.block_time.height).expect("Invalid height")
673 }),
674 h => h,
675 };
676
677 let lock_time = match params.locktime {
678 None => {
680 let fee_sniping_height = current_height.unwrap_or(LockTime::ZERO);
684
685 match requirements.timelock {
688 None => fee_sniping_height,
690 Some(value @ LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height,
692 Some(value) => value,
695 }
696 }
697 Some(x) if requirements.timelock.is_none() => x,
699 Some(x) if requirements.timelock.unwrap().is_same_unit(x) && x >= requirements.timelock.unwrap() => x,
701 Some(x) => return Err(Error::Generic(format!("TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script", x, requirements.timelock.unwrap())))
703 };
704
705 let n_sequence = match (params.rbf, requirements.csv) {
706 (None, None) if lock_time != LockTime::ZERO => Sequence::ENABLE_LOCKTIME_NO_RBF,
708 (None, None) => Sequence::MAX,
710
711 (None, Some(csv)) => csv,
715
716 (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
718 return Err(Error::Generic(
719 "Cannot enable RBF with a nSequence >= 0xFFFFFFFE".into(),
720 ))
721 }
722 (Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
724 if !check_nsequence_rbf(rbf, csv) =>
725 {
726 return Err(Error::Generic(format!(
727 "Cannot enable RBF with nSequence `{:?}` given a required OP_CSV of `{:?}`",
728 rbf, csv
729 )))
730 }
731
732 (Some(tx_builder::RbfValue::Default), Some(csv)) => csv,
734 (Some(rbf), _) => rbf.get_value(),
737 };
738
739 let (fee_rate, mut fee_amount) = match params
740 .fee_policy
741 .as_ref()
742 .unwrap_or(&FeePolicy::FeeRate(FeeRate::default()))
743 {
744 FeePolicy::FeeAmount(fee) => {
746 if let Some(previous_fee) = params.bumping_fee {
747 if *fee < previous_fee.absolute {
748 return Err(Error::FeeTooLow {
749 required: previous_fee.absolute,
750 });
751 }
752 }
753 (FeeRate::from_sat_per_vb(0.0), *fee)
754 }
755 FeePolicy::FeeRate(rate) => {
756 if let Some(previous_fee) = params.bumping_fee {
757 let required_feerate = FeeRate::from_sat_per_vb(previous_fee.rate + 1.0);
758 if *rate < required_feerate {
759 return Err(Error::FeeRateTooLow {
760 required: required_feerate,
761 });
762 }
763 }
764 (*rate, 0)
765 }
766 };
767
768 let mut tx = Transaction {
769 version,
770 lock_time: lock_time.into(),
771 input: vec![],
772 output: vec![],
773 };
774
775 if params.manually_selected_only && params.utxos.is_empty() {
776 return Err(Error::NoUtxosSelected);
777 }
778
779 let mut outgoing: u64 = 0;
781 let mut received: u64 = 0;
782
783 let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
784
785 for (index, (script_pubkey, value)) in recipients.enumerate() {
786 if !params.allow_dust
787 && value.is_dust(script_pubkey)
788 && !script_pubkey.is_provably_unspendable()
789 {
790 return Err(Error::OutputBelowDustLimit(index));
791 }
792
793 if self.is_mine(script_pubkey)? {
794 received += value;
795 }
796
797 let new_out = TxOut {
798 script_pubkey: script_pubkey.clone(),
799 value,
800 };
801
802 tx.output.push(new_out);
803
804 outgoing += value;
805 }
806
807 fee_amount += fee_rate.fee_wu(tx.weight());
808
809 fee_amount += fee_rate.fee_wu(2);
819
820 if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
821 && self.change_descriptor.is_none()
822 {
823 return Err(Error::Generic(
824 "The `change_policy` can be set only if the wallet has a change_descriptor".into(),
825 ));
826 }
827
828 let (required_utxos, optional_utxos) = self.preselect_utxos(
829 params.change_policy,
830 ¶ms.unspendable,
831 params.utxos.clone(),
832 params.drain_wallet,
833 params.manually_selected_only,
834 params.bumping_fee.is_some(), current_height.map(LockTime::to_consensus_u32),
836 )?;
837
838 let drain_script = match params.drain_to {
840 Some(ref drain_recipient) => drain_recipient.clone(),
841 None => self
842 .get_internal_address(AddressIndex::New)?
843 .address
844 .script_pubkey(),
845 };
846
847 let coin_selection = coin_selection.coin_select(
848 self.database.borrow().deref(),
849 required_utxos,
850 optional_utxos,
851 fee_rate,
852 outgoing + fee_amount,
853 &drain_script,
854 )?;
855 fee_amount += coin_selection.fee_amount;
856 let excess = &coin_selection.excess;
857
858 tx.input = coin_selection
859 .selected
860 .iter()
861 .map(|u| bitcoin::TxIn {
862 previous_output: u.outpoint(),
863 script_sig: Script::default(),
864 sequence: n_sequence,
865 witness: Witness::new(),
866 })
867 .collect();
868
869 if tx.output.is_empty() {
870 if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
878 if let NoChange {
879 dust_threshold,
880 remaining_amount,
881 change_fee,
882 } = excess
883 {
884 return Err(Error::InsufficientFunds {
885 needed: *dust_threshold,
886 available: remaining_amount.saturating_sub(*change_fee),
887 });
888 }
889 } else {
890 return Err(Error::NoRecipients);
891 }
892 }
893
894 match excess {
895 NoChange {
896 remaining_amount, ..
897 } => fee_amount += remaining_amount,
898 Change { amount, fee } => {
899 if self.is_mine(&drain_script)? {
900 received += amount;
901 }
902 fee_amount += fee;
903
904 let drain_output = TxOut {
906 value: *amount,
907 script_pubkey: drain_script,
908 };
909
910 tx.output.push(drain_output);
914 }
915 };
916
917 params.ordering.sort_tx(&mut tx);
919
920 let txid = tx.txid();
921 let sent = coin_selection.local_selected_amount();
922 let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
923
924 let transaction_details = TransactionDetails {
925 transaction: None,
926 txid,
927 confirmation_time: None,
928 received,
929 sent,
930 fee: Some(fee_amount),
931 };
932
933 Ok((psbt, transaction_details))
934 }
935
936 pub fn build_fee_bump(
977 &self,
978 txid: Txid,
979 ) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
980 let mut details = match self.database.borrow().get_tx(&txid, true)? {
981 None => return Err(Error::TransactionNotFound),
982 Some(tx) if tx.transaction.is_none() => return Err(Error::TransactionNotFound),
983 Some(tx) if tx.confirmation_time.is_some() => return Err(Error::TransactionConfirmed),
984 Some(tx) => tx,
985 };
986 let mut tx = details.transaction.take().unwrap();
987 if !tx
988 .input
989 .iter()
990 .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
991 {
992 return Err(Error::IrreplaceableTransaction);
993 }
994
995 let feerate = FeeRate::from_wu(details.fee.ok_or(Error::FeeRateUnavailable)?, tx.weight());
996
997 let original_txin = tx.input.drain(..).collect::<Vec<_>>();
999 let original_utxos = original_txin
1000 .iter()
1001 .map(|txin| -> Result<_, Error> {
1002 let txout = self
1003 .database
1004 .borrow()
1005 .get_previous_output(&txin.previous_output)?
1006 .ok_or(Error::UnknownUtxo)?;
1007
1008 let (weight, keychain) = match self
1009 .database
1010 .borrow()
1011 .get_path_from_script_pubkey(&txout.script_pubkey)?
1012 {
1013 Some((keychain, _)) => (
1014 self._get_descriptor_for_keychain(keychain)
1015 .0
1016 .max_satisfaction_weight()
1017 .unwrap(),
1018 keychain,
1019 ),
1020 None => {
1021 let weight =
1024 serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len();
1025 (weight, KeychainKind::External)
1026 }
1027 };
1028
1029 let utxo = LocalUtxo {
1030 outpoint: txin.previous_output,
1031 txout,
1032 keychain,
1033 is_spent: true,
1034 };
1035
1036 Ok(WeightedUtxo {
1037 satisfaction_weight: weight,
1038 utxo: Utxo::Local(utxo),
1039 })
1040 })
1041 .collect::<Result<Vec<_>, _>>()?;
1042
1043 if tx.output.len() > 1 {
1044 let mut change_index = None;
1045 for (index, txout) in tx.output.iter().enumerate() {
1046 let (_, change_type) = self._get_descriptor_for_keychain(KeychainKind::Internal);
1047 match self
1048 .database
1049 .borrow()
1050 .get_path_from_script_pubkey(&txout.script_pubkey)?
1051 {
1052 Some((keychain, _)) if keychain == change_type => change_index = Some(index),
1053 _ => {}
1054 }
1055 }
1056
1057 if let Some(change_index) = change_index {
1058 tx.output.remove(change_index);
1059 }
1060 }
1061
1062 let params = TxParams {
1063 version: Some(tx_builder::Version(tx.version)),
1065 recipients: tx
1066 .output
1067 .into_iter()
1068 .map(|txout| (txout.script_pubkey, txout.value))
1069 .collect(),
1070 utxos: original_utxos,
1071 bumping_fee: Some(tx_builder::PreviousFee {
1072 absolute: details.fee.ok_or(Error::FeeRateUnavailable)?,
1073 rate: feerate.as_sat_per_vb(),
1074 }),
1075 ..Default::default()
1076 };
1077
1078 Ok(TxBuilder {
1079 wallet: self,
1080 params,
1081 coin_selection: DefaultCoinSelectionAlgorithm::default(),
1082 phantom: core::marker::PhantomData,
1083 })
1084 }
1085
1086 pub fn sign(
1113 &self,
1114 psbt: &mut psbt::PartiallySignedTransaction,
1115 sign_options: SignOptions,
1116 ) -> Result<bool, Error> {
1117 self.update_psbt_with_descriptor(psbt)?;
1120
1121 if !sign_options.trust_witness_utxo
1124 && psbt
1125 .inputs
1126 .iter()
1127 .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
1128 .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
1129 .any(|i| i.non_witness_utxo.is_none())
1130 {
1131 return Err(Error::Signer(signer::SignerError::MissingNonWitnessUtxo));
1132 }
1133
1134 if !sign_options.allow_all_sighashes
1137 && !psbt.inputs.iter().all(|i| {
1138 i.sighash_type.is_none()
1139 || i.sighash_type == Some(EcdsaSighashType::All.into())
1140 || i.sighash_type == Some(SchnorrSighashType::All.into())
1141 || i.sighash_type == Some(SchnorrSighashType::Default.into())
1142 })
1143 {
1144 return Err(Error::Signer(signer::SignerError::NonStandardSighash));
1145 }
1146
1147 for signer in self
1148 .signers
1149 .signers()
1150 .iter()
1151 .chain(self.change_signers.signers().iter())
1152 {
1153 signer.sign_transaction(psbt, &sign_options, &self.secp)?;
1154 }
1155
1156 if sign_options.try_finalize {
1158 self.finalize_psbt(psbt, sign_options)
1159 } else {
1160 Ok(false)
1161 }
1162 }
1163
1164 pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, Error> {
1166 match (keychain, self.change_descriptor.as_ref()) {
1167 (KeychainKind::External, _) => Ok(self.descriptor.extract_policy(
1168 &self.signers,
1169 BuildSatisfaction::None,
1170 &self.secp,
1171 )?),
1172 (KeychainKind::Internal, None) => Ok(None),
1173 (KeychainKind::Internal, Some(desc)) => Ok(desc.extract_policy(
1174 &self.change_signers,
1175 BuildSatisfaction::None,
1176 &self.secp,
1177 )?),
1178 }
1179 }
1180
1181 pub fn public_descriptor(
1186 &self,
1187 keychain: KeychainKind,
1188 ) -> Result<Option<ExtendedDescriptor>, Error> {
1189 match (keychain, self.change_descriptor.as_ref()) {
1190 (KeychainKind::External, _) => Ok(Some(self.descriptor.clone())),
1191 (KeychainKind::Internal, None) => Ok(None),
1192 (KeychainKind::Internal, Some(desc)) => Ok(Some(desc.clone())),
1193 }
1194 }
1195
1196 pub fn finalize_psbt(
1205 &self,
1206 psbt: &mut psbt::PartiallySignedTransaction,
1207 sign_options: SignOptions,
1208 ) -> Result<bool, Error> {
1209 let tx = &psbt.unsigned_tx;
1210 let mut finished = true;
1211
1212 for (n, input) in tx.input.iter().enumerate() {
1213 let psbt_input = &psbt
1214 .inputs
1215 .get(n)
1216 .ok_or(Error::Signer(SignerError::InputIndexOutOfRange))?;
1217 if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
1218 continue;
1219 }
1220 let create_height = self
1223 .database
1224 .borrow()
1225 .get_tx(&input.previous_output.txid, false)?
1226 .map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
1227 let last_sync_height = self
1228 .database()
1229 .get_sync_time()?
1230 .map(|sync_time| sync_time.block_time.height);
1231 let current_height = sign_options.assume_height.or(last_sync_height);
1232
1233 debug!(
1234 "Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
1235 n, input.previous_output, create_height, current_height
1236 );
1237
1238 let desc = psbt
1245 .get_utxo_for(n)
1246 .map(|txout| self.get_descriptor_for_txout(&txout))
1247 .transpose()?
1248 .flatten()
1249 .or_else(|| {
1250 self.descriptor.derive_from_psbt_input(
1251 psbt_input,
1252 psbt.get_utxo_for(n),
1253 &self.secp,
1254 )
1255 })
1256 .or_else(|| {
1257 self.change_descriptor.as_ref().and_then(|desc| {
1258 desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
1259 })
1260 });
1261
1262 match desc {
1263 Some(desc) => {
1264 let mut tmp_input = bitcoin::TxIn::default();
1265 match desc.satisfy(
1266 &mut tmp_input,
1267 (
1268 PsbtInputSatisfier::new(psbt, n),
1269 After::new(current_height, false),
1270 Older::new(current_height, create_height, false),
1271 ),
1272 ) {
1273 Ok(_) => {
1274 let psbt_input = &mut psbt.inputs[n];
1275 psbt_input.final_script_sig = Some(tmp_input.script_sig);
1276 psbt_input.final_script_witness = Some(tmp_input.witness);
1277 if sign_options.remove_partial_sigs {
1278 psbt_input.partial_sigs.clear();
1279 }
1280 }
1281 Err(e) => {
1282 debug!("satisfy error {:?} for input {}", e, n);
1283 finished = false
1284 }
1285 }
1286 }
1287 None => finished = false,
1288 }
1289 }
1290
1291 Ok(finished)
1292 }
1293
1294 pub fn secp_ctx(&self) -> &SecpCtx {
1296 &self.secp
1297 }
1298
1299 pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
1301 let (descriptor, _) = self._get_descriptor_for_keychain(keychain);
1302 descriptor
1303 }
1304
1305 fn _get_descriptor_for_keychain(
1308 &self,
1309 keychain: KeychainKind,
1310 ) -> (&ExtendedDescriptor, KeychainKind) {
1311 match keychain {
1312 KeychainKind::Internal if self.change_descriptor.is_some() => (
1313 self.change_descriptor.as_ref().unwrap(),
1314 KeychainKind::Internal,
1315 ),
1316 _ => (&self.descriptor, KeychainKind::External),
1317 }
1318 }
1319
1320 fn get_descriptor_for_txout(&self, txout: &TxOut) -> Result<Option<DerivedDescriptor>, Error> {
1321 Ok(self
1322 .database
1323 .borrow()
1324 .get_path_from_script_pubkey(&txout.script_pubkey)?
1325 .map(|(keychain, child)| (self.get_descriptor_for_keychain(keychain), child))
1326 .map(|(desc, child)| desc.at_derivation_index(child)))
1327 }
1328
1329 fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
1330 let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
1331 let index = match descriptor.has_wildcard() {
1332 false => 0,
1333 true => self.database.borrow_mut().increment_last_index(keychain)?,
1334 };
1335
1336 if self
1337 .database
1338 .borrow()
1339 .get_script_pubkey_from_path(keychain, index)?
1340 .is_none()
1341 {
1342 self.cache_addresses(keychain, index, CACHE_ADDR_BATCH_SIZE)?;
1343 }
1344
1345 Ok(index)
1346 }
1347
1348 fn fetch_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
1349 let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
1350 let index = match descriptor.has_wildcard() {
1351 false => Some(0),
1352 true => self.database.borrow_mut().get_last_index(keychain)?,
1353 };
1354
1355 if let Some(i) = index {
1356 Ok(i)
1357 } else {
1358 self.fetch_and_increment_index(keychain)
1359 }
1360 }
1361
1362 fn set_index(&self, keychain: KeychainKind, index: u32) -> Result<(), Error> {
1363 self.database.borrow_mut().set_last_index(keychain, index)?;
1364 Ok(())
1365 }
1366
1367 fn cache_addresses(
1368 &self,
1369 keychain: KeychainKind,
1370 from: u32,
1371 mut count: u32,
1372 ) -> Result<(), Error> {
1373 let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
1374 if !descriptor.has_wildcard() {
1375 if from > 0 {
1376 return Ok(());
1377 }
1378
1379 count = 1;
1380 }
1381
1382 let mut address_batch = self.database.borrow().begin_batch();
1383
1384 let start_time = time::Instant::new();
1385 for i in from..(from + count) {
1386 address_batch.set_script_pubkey(
1387 &descriptor.at_derivation_index(i).script_pubkey(),
1388 keychain,
1389 i,
1390 )?;
1391 }
1392
1393 info!(
1394 "Derivation of {} addresses from {} took {} ms",
1395 count,
1396 from,
1397 start_time.elapsed().as_millis()
1398 );
1399
1400 self.database.borrow_mut().commit_batch(address_batch)?;
1401
1402 Ok(())
1403 }
1404
1405 fn get_available_utxos(&self) -> Result<Vec<(LocalUtxo, usize)>, Error> {
1406 Ok(self
1407 .list_unspent()?
1408 .into_iter()
1409 .map(|utxo| {
1410 let keychain = utxo.keychain;
1411 (
1412 utxo,
1413 self.get_descriptor_for_keychain(keychain)
1414 .max_satisfaction_weight()
1415 .unwrap(),
1416 )
1417 })
1418 .collect())
1419 }
1420
1421 #[allow(clippy::type_complexity)]
1424 #[allow(clippy::too_many_arguments)]
1425 fn preselect_utxos(
1426 &self,
1427 change_policy: tx_builder::ChangeSpendPolicy,
1428 unspendable: &HashSet<OutPoint>,
1429 manually_selected: Vec<WeightedUtxo>,
1430 must_use_all_available: bool,
1431 manual_only: bool,
1432 must_only_use_confirmed_tx: bool,
1433 current_height: Option<u32>,
1434 ) -> Result<(Vec<WeightedUtxo>, Vec<WeightedUtxo>), Error> {
1435 let mut may_spend = self.get_available_utxos()?;
1438
1439 may_spend.retain(|may_spend| {
1440 !manually_selected
1441 .iter()
1442 .any(|manually_selected| manually_selected.utxo.outpoint() == may_spend.0.outpoint)
1443 });
1444 let mut must_spend = manually_selected;
1445
1446 if manual_only {
1449 return Ok((must_spend, vec![]));
1450 }
1451
1452 let database = self.database.borrow();
1453 let satisfies_confirmed = may_spend
1454 .iter()
1455 .map(|u| {
1456 database
1457 .get_tx(&u.0.outpoint.txid, true)
1458 .map(|tx| match tx {
1459 None => false,
1463 Some(tx) => {
1464 let mut spendable = true;
1466 if must_only_use_confirmed_tx && tx.confirmation_time.is_none() {
1467 return false;
1468 }
1469 if tx
1470 .transaction
1471 .expect("We specifically ask for the transaction above")
1472 .is_coin_base()
1473 {
1474 if let Some(current_height) = current_height {
1475 match &tx.confirmation_time {
1476 Some(t) => {
1477 spendable &= (current_height.saturating_sub(t.height))
1479 >= COINBASE_MATURITY;
1480 }
1481 None => spendable = false,
1482 }
1483 }
1484 }
1485 spendable
1486 }
1487 })
1488 })
1489 .collect::<Result<Vec<_>, _>>()?;
1490
1491 let mut i = 0;
1492 may_spend.retain(|u| {
1493 let retain = change_policy.is_satisfied_by(&u.0)
1494 && !unspendable.contains(&u.0.outpoint)
1495 && satisfies_confirmed[i];
1496 i += 1;
1497 retain
1498 });
1499
1500 let mut may_spend = may_spend
1501 .into_iter()
1502 .map(|(local_utxo, satisfaction_weight)| WeightedUtxo {
1503 satisfaction_weight,
1504 utxo: Utxo::Local(local_utxo),
1505 })
1506 .collect();
1507
1508 if must_use_all_available {
1509 must_spend.append(&mut may_spend);
1510 }
1511
1512 Ok((must_spend, may_spend))
1513 }
1514
1515 fn complete_transaction(
1516 &self,
1517 tx: Transaction,
1518 selected: Vec<Utxo>,
1519 params: TxParams,
1520 ) -> Result<psbt::PartiallySignedTransaction, Error> {
1521 let mut psbt = psbt::PartiallySignedTransaction::from_unsigned_tx(tx)?;
1522
1523 if params.add_global_xpubs {
1524 let mut all_xpubs = self.descriptor.get_extended_keys()?;
1525 if let Some(change_descriptor) = &self.change_descriptor {
1526 all_xpubs.extend(change_descriptor.get_extended_keys()?);
1527 }
1528
1529 for xpub in all_xpubs {
1530 let origin = match xpub.origin {
1531 Some(origin) => origin,
1532 None if xpub.xkey.depth == 0 => {
1533 (xpub.root_fingerprint(&self.secp), vec![].into())
1534 }
1535 _ => return Err(Error::MissingKeyOrigin(xpub.xkey.to_string())),
1536 };
1537
1538 psbt.xpub.insert(xpub.xkey, origin);
1539 }
1540 }
1541
1542 let mut lookup_output = selected
1543 .into_iter()
1544 .map(|utxo| (utxo.outpoint(), utxo))
1545 .collect::<HashMap<_, _>>();
1546
1547 for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
1549 let utxo = match lookup_output.remove(&input.previous_output) {
1550 Some(utxo) => utxo,
1551 None => continue,
1552 };
1553
1554 match utxo {
1555 Utxo::Local(utxo) => {
1556 *psbt_input =
1557 match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
1558 Ok(psbt_input) => psbt_input,
1559 Err(e) => match e {
1560 Error::UnknownUtxo => psbt::Input {
1561 sighash_type: params.sighash,
1562 ..psbt::Input::default()
1563 },
1564 _ => return Err(e),
1565 },
1566 }
1567 }
1568 Utxo::Foreign {
1569 psbt_input: foreign_psbt_input,
1570 outpoint,
1571 } => {
1572 let is_taproot = foreign_psbt_input
1573 .witness_utxo
1574 .as_ref()
1575 .map(|txout| txout.script_pubkey.is_v1_p2tr())
1576 .unwrap_or(false);
1577 if !is_taproot
1578 && !params.only_witness_utxo
1579 && foreign_psbt_input.non_witness_utxo.is_none()
1580 {
1581 return Err(Error::Generic(format!(
1582 "Missing non_witness_utxo on foreign utxo {}",
1583 outpoint
1584 )));
1585 }
1586 *psbt_input = *foreign_psbt_input;
1587 }
1588 }
1589 }
1590
1591 self.update_psbt_with_descriptor(&mut psbt)?;
1592
1593 Ok(psbt)
1594 }
1595
1596 pub fn cus_complete_transaction(
1597 &self,
1598 tx: Transaction,
1599 ) -> Result<psbt::PartiallySignedTransaction, Error> {
1600 let params = TxParams::default();
1601 let selected: Vec<Utxo> = self.list_unspent()
1603 .unwrap()
1604 .into_iter()
1605 .filter(|utxo| {
1606 tx.input
1607 .iter()
1608 .any(|tx_input| {
1609 tx_input.previous_output.txid == utxo.outpoint.txid &&
1610 tx_input.previous_output.vout == utxo.outpoint.vout
1611 })
1612 })
1613 .map(|utxo| {
1614 Utxo::Local(utxo)
1615 })
1616 .collect();
1617
1618
1619 let mut psbt = psbt::PartiallySignedTransaction::from_unsigned_tx(tx)?;
1620
1621 if params.add_global_xpubs {
1622 let mut all_xpubs = self.descriptor.get_extended_keys()?;
1623 if let Some(change_descriptor) = &self.change_descriptor {
1624 all_xpubs.extend(change_descriptor.get_extended_keys()?);
1625 }
1626
1627 for xpub in all_xpubs {
1628 let origin = match xpub.origin {
1629 Some(origin) => origin,
1630 None if xpub.xkey.depth == 0 => {
1631 (xpub.root_fingerprint(&self.secp), vec![].into())
1632 }
1633 _ => return Err(Error::MissingKeyOrigin(xpub.xkey.to_string())),
1634 };
1635
1636 psbt.xpub.insert(xpub.xkey, origin);
1637 }
1638 }
1639
1640 let mut lookup_output = selected
1641 .into_iter()
1642 .map(|utxo| (utxo.outpoint(), utxo))
1643 .collect::<HashMap<_, _>>();
1644
1645 for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
1647 let utxo = match lookup_output.remove(&input.previous_output) {
1648 Some(utxo) => utxo,
1649 None => continue,
1650 };
1651
1652 match utxo {
1653 Utxo::Local(utxo) => {
1654 *psbt_input =
1655 match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
1656 Ok(psbt_input) => psbt_input,
1657 Err(e) => match e {
1658 Error::UnknownUtxo => psbt::Input {
1659 sighash_type: params.sighash,
1660 ..psbt::Input::default()
1661 },
1662 _ => return Err(e),
1663 },
1664 }
1665 }
1666 Utxo::Foreign {
1667 psbt_input: foreign_psbt_input,
1668 outpoint,
1669 } => {
1670 let is_taproot = foreign_psbt_input
1671 .witness_utxo
1672 .as_ref()
1673 .map(|txout| txout.script_pubkey.is_v1_p2tr())
1674 .unwrap_or(false);
1675 if !is_taproot
1676 && !params.only_witness_utxo
1677 && foreign_psbt_input.non_witness_utxo.is_none()
1678 {
1679 return Err(Error::Generic(format!(
1680 "Missing non_witness_utxo on foreign utxo {}",
1681 outpoint
1682 )));
1683 }
1684 *psbt_input = *foreign_psbt_input;
1685 }
1686 }
1687 }
1688
1689 self.update_psbt_with_descriptor(&mut psbt)?;
1690
1691 Ok(psbt)
1692 }
1693
1694 pub fn get_psbt_input(
1696 &self,
1697 utxo: LocalUtxo,
1698 sighash_type: Option<psbt::PsbtSighashType>,
1699 only_witness_utxo: bool,
1700 ) -> Result<psbt::Input, Error> {
1701 let (keychain, child) = self
1704 .database
1705 .borrow()
1706 .get_path_from_script_pubkey(&utxo.txout.script_pubkey)?
1707 .ok_or(Error::UnknownUtxo)?;
1708
1709 let mut psbt_input = psbt::Input {
1710 sighash_type,
1711 ..psbt::Input::default()
1712 };
1713
1714 let desc = self.get_descriptor_for_keychain(keychain);
1715 let derived_descriptor = desc.at_derivation_index(child);
1716
1717 psbt_input
1718 .update_with_descriptor_unchecked(&derived_descriptor)
1719 .map_err(MiniscriptPsbtError::Conversion)?;
1720
1721 let prev_output = utxo.outpoint;
1722 if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
1723 if desc.is_witness() || desc.is_taproot() {
1724 psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
1725 }
1726 if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
1727 psbt_input.non_witness_utxo = Some(prev_tx);
1728 }
1729 }
1730 Ok(psbt_input)
1731 }
1732
1733 fn update_psbt_with_descriptor(
1734 &self,
1735 psbt: &mut psbt::PartiallySignedTransaction,
1736 ) -> Result<(), Error> {
1737 #[allow(clippy::needless_collect)]
1742 let utxos = (0..psbt.inputs.len())
1743 .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
1744 .chain(
1745 psbt.unsigned_tx
1746 .output
1747 .iter()
1748 .enumerate()
1749 .map(|(i, out)| (false, i, out.clone())),
1750 )
1751 .collect::<Vec<_>>();
1752
1753 for (is_input, index, out) in utxos.into_iter() {
1755 if let Some((keychain, child)) = self
1756 .database
1757 .borrow()
1758 .get_path_from_script_pubkey(&out.script_pubkey)?
1759 {
1760 debug!(
1761 "Found descriptor for input #{} {:?}/{}",
1762 index, keychain, child
1763 );
1764
1765 let desc = self.get_descriptor_for_keychain(keychain);
1766 let desc = desc.at_derivation_index(child);
1767
1768 if is_input {
1769 psbt.update_input_with_descriptor(index, &desc)
1770 .map_err(MiniscriptPsbtError::UtxoUpdate)?;
1771 } else {
1772 psbt.update_output_with_descriptor(index, &desc)
1773 .map_err(MiniscriptPsbtError::OutputUpdate)?;
1774 }
1775 }
1776 }
1777
1778 Ok(())
1779 }
1780
1781 pub fn database(&self) -> impl std::ops::Deref<Target = D> + '_ {
1783 self.database.borrow()
1784 }
1785
1786 #[maybe_async]
1788 pub fn sync<B: WalletSync + GetHeight>(
1789 &self,
1790 blockchain: &B,
1791 sync_opts: SyncOptions,
1792 ) -> Result<(), Error> {
1793 debug!("Begin sync...");
1794
1795 let mut progress_iter = sync_opts.progress.into_iter();
1798 let mut new_progress = || {
1799 progress_iter
1800 .next()
1801 .unwrap_or_else(|| Box::new(NoopProgress))
1802 };
1803
1804 let run_setup = self.ensure_addresses_cached(CACHE_ADDR_BATCH_SIZE)?;
1805 debug!("run_setup: {}", run_setup);
1806
1807 let has_wildcard = self.descriptor.has_wildcard()
1813 && (self.change_descriptor.is_none()
1814 || self.change_descriptor.as_ref().unwrap().has_wildcard());
1815
1816 let max_rounds = if has_wildcard { 100 } else { 1 };
1818
1819 for _ in 0..max_rounds {
1820 let sync_res = if run_setup {
1821 maybe_await!(blockchain.wallet_setup(&self.database, new_progress()))
1822 } else {
1823 maybe_await!(blockchain.wallet_sync(&self.database, new_progress()))
1824 };
1825
1826 let ensure_cache = sync_res.map_or_else(
1831 |e| match e {
1832 Error::MissingCachedScripts(inner) => {
1833 let extra =
1835 std::cmp::max(inner.missing_count as u32, CACHE_ADDR_BATCH_SIZE);
1836 let last = inner.last_count as u32;
1837 Ok(extra + last)
1838 }
1839 _ => Err(e),
1840 },
1841 |_| Ok(0_u32),
1842 )?;
1843
1844 if !self.ensure_addresses_cached(ensure_cache)? {
1846 break;
1847 }
1848 }
1849
1850 let sync_time = SyncTime {
1851 block_time: BlockTime {
1852 height: maybe_await!(blockchain.get_height())?,
1853 timestamp: time::get_timestamp(),
1854 },
1855 };
1856 debug!("Saving `sync_time` = {:?}", sync_time);
1857 self.database.borrow_mut().set_sync_time(sync_time)?;
1858
1859 Ok(())
1860 }
1861
1862 pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
1866 self.get_descriptor_for_keychain(keychain)
1867 .to_string()
1868 .split_once('#')
1869 .unwrap()
1870 .1
1871 .to_string()
1872 }
1873}
1874
1875pub fn wallet_name_from_descriptor<T>(
1879 descriptor: T,
1880 change_descriptor: Option<T>,
1881 network: Network,
1882 secp: &SecpCtx,
1883) -> Result<String, Error>
1884where
1885 T: IntoWalletDescriptor,
1886{
1887 let descriptor = descriptor
1889 .into_wallet_descriptor(secp, network)?
1890 .0
1891 .to_string();
1892 let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
1893 if let Some(change_descriptor) = change_descriptor {
1894 let change_descriptor = change_descriptor
1895 .into_wallet_descriptor(secp, network)?
1896 .0
1897 .to_string();
1898 wallet_name.push_str(
1899 calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
1900 );
1901 }
1902
1903 Ok(wallet_name)
1904}
1905
1906pub fn get_funded_wallet(
1908 descriptor: &str,
1909) -> (Wallet<AnyDatabase>, (String, Option<String>), bitcoin::Txid) {
1910 let descriptors = testutils!(@descriptors (descriptor));
1911 let wallet = Wallet::new(
1912 &descriptors.0,
1913 None,
1914 Network::Regtest,
1915 AnyDatabase::Memory(MemoryDatabase::new()),
1916 )
1917 .unwrap();
1918
1919 let funding_address_kix = 0;
1920
1921 let tx_meta = testutils! {
1922 @tx ( (@external descriptors, funding_address_kix) => 50_000 ) (@confirmations 1)
1923 };
1924
1925 wallet
1926 .database
1927 .borrow_mut()
1928 .set_script_pubkey(
1929 &bitcoin::Address::from_str(&tx_meta.output.get(0).unwrap().to_address)
1930 .unwrap()
1931 .script_pubkey(),
1932 KeychainKind::External,
1933 funding_address_kix,
1934 )
1935 .unwrap();
1936 wallet
1937 .database
1938 .borrow_mut()
1939 .set_last_index(KeychainKind::External, funding_address_kix)
1940 .unwrap();
1941
1942 let txid = crate::populate_test_db!(wallet.database.borrow_mut(), tx_meta, Some(100));
1943
1944 (wallet, descriptors, txid)
1945}
1946
1947#[cfg(test)]
1948pub(crate) mod test {
1949 use assert_matches::assert_matches;
1950 use bitcoin::{util::psbt, Network, PackedLockTime, Sequence};
1951
1952 use crate::database::Database;
1953 use crate::types::KeychainKind;
1954
1955 use super::*;
1956 use crate::signer::{SignOptions, SignerError};
1957 use crate::wallet::AddressIndex::{LastUnused, New, Peek, Reset};
1958
1959 const P2WPKH_FAKE_WITNESS_SIZE: usize = 106;
1966
1967 #[test]
1968 fn test_descriptor_checksum() {
1969 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
1970 let checksum = wallet.descriptor_checksum(KeychainKind::External);
1971 assert_eq!(checksum.len(), 8);
1972 assert_eq!(
1973 calc_checksum(&wallet.descriptor.to_string()).unwrap(),
1974 checksum
1975 );
1976 }
1977
1978 #[test]
1979 fn test_db_checksum() {
1980 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
1981 let desc = wallet.descriptor.to_string();
1982
1983 let checksum = calc_checksum_bytes_internal(&desc, true).unwrap();
1984 let checksum_inception = calc_checksum_bytes_internal(&desc, false).unwrap();
1985 let checksum_invalid = [b'q'; 8];
1986
1987 let mut db = MemoryDatabase::new();
1988 db.check_descriptor_checksum(KeychainKind::External, checksum)
1989 .expect("failed to save actual checksum");
1990 Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
1991 .expect("db that uses actual checksum should be supported");
1992
1993 let mut db = MemoryDatabase::new();
1994 db.check_descriptor_checksum(KeychainKind::External, checksum_inception)
1995 .expect("failed to save checksum inception");
1996 Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
1997 .expect("db that uses checksum inception should be supported");
1998
1999 let mut db = MemoryDatabase::new();
2000 db.check_descriptor_checksum(KeychainKind::External, checksum_invalid)
2001 .expect("failed to save invalid checksum");
2002 Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
2003 .expect_err("db that uses invalid checksum should fail");
2004 }
2005
2006 #[test]
2007 fn test_get_funded_wallet_balance() {
2008 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2009 assert_eq!(wallet.get_balance().unwrap().confirmed, 50000);
2010 }
2011
2012 #[test]
2013 fn test_cache_addresses_fixed() {
2014 let db = MemoryDatabase::new();
2015 let wallet = Wallet::new(
2016 "wpkh(L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6)",
2017 None,
2018 Network::Testnet,
2019 db,
2020 )
2021 .unwrap();
2022
2023 assert_eq!(
2024 wallet.get_address(New).unwrap().to_string(),
2025 "tb1qj08ys4ct2hzzc2hcz6h2hgrvlmsjynaw43s835"
2026 );
2027 assert_eq!(
2028 wallet.get_address(New).unwrap().to_string(),
2029 "tb1qj08ys4ct2hzzc2hcz6h2hgrvlmsjynaw43s835"
2030 );
2031
2032 assert!(wallet
2033 .database
2034 .borrow_mut()
2035 .get_script_pubkey_from_path(KeychainKind::External, 0)
2036 .unwrap()
2037 .is_some());
2038 assert!(wallet
2039 .database
2040 .borrow_mut()
2041 .get_script_pubkey_from_path(KeychainKind::Internal, 0)
2042 .unwrap()
2043 .is_none());
2044 }
2045
2046 #[test]
2047 fn test_cache_addresses() {
2048 let db = MemoryDatabase::new();
2049 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
2050
2051 assert_eq!(
2052 wallet.get_address(New).unwrap().to_string(),
2053 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2054 );
2055 assert_eq!(
2056 wallet.get_address(New).unwrap().to_string(),
2057 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
2058 );
2059
2060 assert!(wallet
2061 .database
2062 .borrow_mut()
2063 .get_script_pubkey_from_path(KeychainKind::External, CACHE_ADDR_BATCH_SIZE - 1)
2064 .unwrap()
2065 .is_some());
2066 assert!(wallet
2067 .database
2068 .borrow_mut()
2069 .get_script_pubkey_from_path(KeychainKind::External, CACHE_ADDR_BATCH_SIZE)
2070 .unwrap()
2071 .is_none());
2072 }
2073
2074 #[test]
2075 fn test_cache_addresses_refill() {
2076 let db = MemoryDatabase::new();
2077 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
2078
2079 assert_eq!(
2080 wallet.get_address(New).unwrap().to_string(),
2081 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2082 );
2083 assert!(wallet
2084 .database
2085 .borrow_mut()
2086 .get_script_pubkey_from_path(KeychainKind::External, CACHE_ADDR_BATCH_SIZE - 1)
2087 .unwrap()
2088 .is_some());
2089
2090 for _ in 0..CACHE_ADDR_BATCH_SIZE {
2091 wallet.get_address(New).unwrap();
2092 }
2093
2094 assert!(wallet
2095 .database
2096 .borrow_mut()
2097 .get_script_pubkey_from_path(KeychainKind::External, CACHE_ADDR_BATCH_SIZE * 2 - 1)
2098 .unwrap()
2099 .is_some());
2100 }
2101
2102 pub(crate) fn get_test_wpkh() -> &'static str {
2103 "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
2104 }
2105
2106 pub(crate) fn get_test_single_sig_csv() -> &'static str {
2107 "wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6)))"
2109 }
2110
2111 pub(crate) fn get_test_a_or_b_plus_csv() -> &'static str {
2112 "wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),and_v(v:pk(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(144))))"
2114 }
2115
2116 pub(crate) fn get_test_single_sig_cltv() -> &'static str {
2117 "wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000)))"
2119 }
2120
2121 pub(crate) fn get_test_tr_single_sig() -> &'static str {
2122 "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG)"
2123 }
2124
2125 pub(crate) fn get_test_tr_with_taptree() -> &'static str {
2126 "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
2127 }
2128
2129 pub(crate) fn get_test_tr_with_taptree_both_priv() -> &'static str {
2130 "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh),pk(cNaQCDwmmh4dS9LzCgVtyy1e1xjCJ21GUDHe9K98nzb689JvinGV)})"
2131 }
2132
2133 pub(crate) fn get_test_tr_repeated_key() -> &'static str {
2134 "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100)),and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(200))})"
2135 }
2136
2137 pub(crate) fn get_test_tr_single_sig_xprv() -> &'static str {
2138 "tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)"
2139 }
2140
2141 pub(crate) fn get_test_tr_with_taptree_xprv() -> &'static str {
2142 "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG,{pk(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
2143 }
2144
2145 pub(crate) fn get_test_tr_dup_keys() -> &'static str {
2146 "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG,{pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
2147 }
2148
2149 macro_rules! assert_fee_rate {
2150 ($psbt:expr, $fees:expr, $fee_rate:expr $( ,@dust_change $( $dust_change:expr )* )* $( ,@add_signature $( $add_signature:expr )* )* ) => ({
2151 let psbt = $psbt.clone();
2152 #[allow(unused_mut)]
2153 let mut tx = $psbt.clone().extract_tx();
2154 $(
2155 $( $add_signature )*
2156 for txin in &mut tx.input {
2157 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); }
2159 )*
2160
2161 #[allow(unused_mut)]
2162 #[allow(unused_assignments)]
2163 let mut dust_change = false;
2164 $(
2165 $( $dust_change )*
2166 dust_change = true;
2167 )*
2168
2169 let fee_amount = psbt
2170 .inputs
2171 .iter()
2172 .fold(0, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
2173 - psbt
2174 .unsigned_tx
2175 .output
2176 .iter()
2177 .fold(0, |acc, o| acc + o.value);
2178
2179 assert_eq!(fee_amount, $fees);
2180
2181 let tx_fee_rate = FeeRate::from_wu($fees, tx.weight());
2182 let fee_rate = $fee_rate;
2183
2184 if !dust_change {
2185 assert!(tx_fee_rate >= fee_rate && (tx_fee_rate - fee_rate).as_sat_per_vb().abs() < 0.5, "Expected fee rate of {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
2186 } else {
2187 assert!(tx_fee_rate >= fee_rate, "Expected fee rate of at least {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
2188 }
2189 });
2190 }
2191
2192 macro_rules! from_str {
2193 ($e:expr, $t:ty) => {{
2194 use std::str::FromStr;
2195 <$t>::from_str($e).unwrap()
2196 }};
2197
2198 ($e:expr) => {
2199 from_str!($e, _)
2200 };
2201 }
2202
2203 #[test]
2204 #[should_panic(expected = "NoRecipients")]
2205 fn test_create_tx_empty_recipients() {
2206 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2207 wallet.build_tx().finish().unwrap();
2208 }
2209
2210 #[test]
2211 #[should_panic(expected = "NoUtxosSelected")]
2212 fn test_create_tx_manually_selected_empty_utxos() {
2213 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2214 let addr = wallet.get_address(New).unwrap();
2215 let mut builder = wallet.build_tx();
2216 builder
2217 .add_recipient(addr.script_pubkey(), 25_000)
2218 .manually_selected_only();
2219 builder.finish().unwrap();
2220 }
2221
2222 #[test]
2223 #[should_panic(expected = "Invalid version `0`")]
2224 fn test_create_tx_version_0() {
2225 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2226 let addr = wallet.get_address(New).unwrap();
2227 let mut builder = wallet.build_tx();
2228 builder
2229 .add_recipient(addr.script_pubkey(), 25_000)
2230 .version(0);
2231 builder.finish().unwrap();
2232 }
2233
2234 #[test]
2235 #[should_panic(
2236 expected = "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
2237 )]
2238 fn test_create_tx_version_1_csv() {
2239 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
2240 let addr = wallet.get_address(New).unwrap();
2241 let mut builder = wallet.build_tx();
2242 builder
2243 .add_recipient(addr.script_pubkey(), 25_000)
2244 .version(1);
2245 builder.finish().unwrap();
2246 }
2247
2248 #[test]
2249 fn test_create_tx_custom_version() {
2250 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2251 let addr = wallet.get_address(New).unwrap();
2252 let mut builder = wallet.build_tx();
2253 builder
2254 .add_recipient(addr.script_pubkey(), 25_000)
2255 .version(42);
2256 let (psbt, _) = builder.finish().unwrap();
2257
2258 assert_eq!(psbt.unsigned_tx.version, 42);
2259 }
2260
2261 #[test]
2262 fn test_create_tx_default_locktime() {
2263 let descriptors = testutils!(@descriptors (get_test_wpkh()));
2264 let wallet = Wallet::new(
2265 &descriptors.0,
2266 None,
2267 Network::Regtest,
2268 AnyDatabase::Memory(MemoryDatabase::new()),
2269 )
2270 .unwrap();
2271
2272 let tx_meta = testutils! {
2273 @tx ( (@external descriptors, 0) => 50_000 )
2274 };
2275
2276 crate::populate_test_db!(wallet.database.borrow_mut(), tx_meta, None);
2278
2279 let addr = wallet.get_address(New).unwrap();
2280 let mut builder = wallet.build_tx();
2281 builder.add_recipient(addr.script_pubkey(), 25_000);
2282 let (psbt, _) = builder.finish().unwrap();
2283
2284 assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(0));
2287 }
2288
2289 #[test]
2290 fn test_create_tx_fee_sniping_locktime_provided_height() {
2291 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2292 let addr = wallet.get_address(New).unwrap();
2293 let mut builder = wallet.build_tx();
2294 builder.add_recipient(addr.script_pubkey(), 25_000);
2295 let sync_time = SyncTime {
2296 block_time: BlockTime {
2297 height: 24,
2298 timestamp: 0,
2299 },
2300 };
2301 wallet
2302 .database
2303 .borrow_mut()
2304 .set_sync_time(sync_time)
2305 .unwrap();
2306 let current_height = 25;
2307 builder.current_height(current_height);
2308 let (psbt, _) = builder.finish().unwrap();
2309
2310 assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(current_height));
2312 }
2313
2314 #[test]
2315 fn test_create_tx_fee_sniping_locktime_last_sync() {
2316 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2317 let addr = wallet.get_address(New).unwrap();
2318 let mut builder = wallet.build_tx();
2319 builder.add_recipient(addr.script_pubkey(), 25_000);
2320 let sync_time = SyncTime {
2321 block_time: BlockTime {
2322 height: 25,
2323 timestamp: 0,
2324 },
2325 };
2326 wallet
2327 .database
2328 .borrow_mut()
2329 .set_sync_time(sync_time.clone())
2330 .unwrap();
2331 let (psbt, _) = builder.finish().unwrap();
2332
2333 assert_eq!(
2335 psbt.unsigned_tx.lock_time,
2336 PackedLockTime(sync_time.block_time.height)
2337 );
2338 }
2339
2340 #[test]
2341 fn test_create_tx_default_locktime_cltv() {
2342 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
2343 let addr = wallet.get_address(New).unwrap();
2344 let mut builder = wallet.build_tx();
2345 builder.add_recipient(addr.script_pubkey(), 25_000);
2346 let (psbt, _) = builder.finish().unwrap();
2347
2348 assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(100_000));
2349 }
2350
2351 #[test]
2352 fn test_create_tx_custom_locktime() {
2353 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2354 let addr = wallet.get_address(New).unwrap();
2355 let mut builder = wallet.build_tx();
2356 builder
2357 .add_recipient(addr.script_pubkey(), 25_000)
2358 .current_height(630_001)
2359 .nlocktime(LockTime::from_height(630_000).unwrap());
2360 let (psbt, _) = builder.finish().unwrap();
2361
2362 assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000));
2366 }
2367
2368 #[test]
2369 fn test_create_tx_custom_locktime_compatible_with_cltv() {
2370 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
2371 let addr = wallet.get_address(New).unwrap();
2372 let mut builder = wallet.build_tx();
2373 builder
2374 .add_recipient(addr.script_pubkey(), 25_000)
2375 .nlocktime(LockTime::from_height(630_000).unwrap());
2376 let (psbt, _) = builder.finish().unwrap();
2377
2378 assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000));
2379 }
2380
2381 #[test]
2382 #[should_panic(
2383 expected = "TxBuilder requested timelock of `Blocks(Height(50000))`, but at least `Blocks(Height(100000))` is required to spend from this script"
2384 )]
2385 fn test_create_tx_custom_locktime_incompatible_with_cltv() {
2386 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
2387 let addr = wallet.get_address(New).unwrap();
2388 let mut builder = wallet.build_tx();
2389 builder
2390 .add_recipient(addr.script_pubkey(), 25_000)
2391 .nlocktime(LockTime::from_height(50000).unwrap());
2392 builder.finish().unwrap();
2393 }
2394
2395 #[test]
2396 fn test_create_tx_no_rbf_csv() {
2397 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
2398 let addr = wallet.get_address(New).unwrap();
2399 let mut builder = wallet.build_tx();
2400 builder.add_recipient(addr.script_pubkey(), 25_000);
2401 let (psbt, _) = builder.finish().unwrap();
2402
2403 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
2404 }
2405
2406 #[test]
2407 fn test_create_tx_with_default_rbf_csv() {
2408 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
2409 let addr = wallet.get_address(New).unwrap();
2410 let mut builder = wallet.build_tx();
2411 builder
2412 .add_recipient(addr.script_pubkey(), 25_000)
2413 .enable_rbf();
2414 let (psbt, _) = builder.finish().unwrap();
2415 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
2418 }
2419
2420 #[test]
2421 #[should_panic(
2422 expected = "Cannot enable RBF with nSequence `Sequence(3)` given a required OP_CSV of `Sequence(6)`"
2423 )]
2424 fn test_create_tx_with_custom_rbf_csv() {
2425 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
2426 let addr = wallet.get_address(New).unwrap();
2427 let mut builder = wallet.build_tx();
2428 builder
2429 .add_recipient(addr.script_pubkey(), 25_000)
2430 .enable_rbf_with_sequence(Sequence(3));
2431 builder.finish().unwrap();
2432 }
2433
2434 #[test]
2435 fn test_create_tx_no_rbf_cltv() {
2436 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
2437 let addr = wallet.get_address(New).unwrap();
2438 let mut builder = wallet.build_tx();
2439 builder.add_recipient(addr.script_pubkey(), 25_000);
2440 let (psbt, _) = builder.finish().unwrap();
2441
2442 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
2443 }
2444
2445 #[test]
2446 #[should_panic(expected = "Cannot enable RBF with a nSequence >= 0xFFFFFFFE")]
2447 fn test_create_tx_invalid_rbf_sequence() {
2448 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2449 let addr = wallet.get_address(New).unwrap();
2450 let mut builder = wallet.build_tx();
2451 builder
2452 .add_recipient(addr.script_pubkey(), 25_000)
2453 .enable_rbf_with_sequence(Sequence(0xFFFFFFFE));
2454 builder.finish().unwrap();
2455 }
2456
2457 #[test]
2458 fn test_create_tx_custom_rbf_sequence() {
2459 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2460 let addr = wallet.get_address(New).unwrap();
2461 let mut builder = wallet.build_tx();
2462 builder
2463 .add_recipient(addr.script_pubkey(), 25_000)
2464 .enable_rbf_with_sequence(Sequence(0xDEADBEEF));
2465 let (psbt, _) = builder.finish().unwrap();
2466
2467 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xDEADBEEF));
2468 }
2469
2470 #[test]
2471 fn test_create_tx_default_sequence() {
2472 let descriptors = testutils!(@descriptors (get_test_wpkh()));
2473 let wallet = Wallet::new(
2474 &descriptors.0,
2475 None,
2476 Network::Regtest,
2477 AnyDatabase::Memory(MemoryDatabase::new()),
2478 )
2479 .unwrap();
2480
2481 let tx_meta = testutils! {
2482 @tx ( (@external descriptors, 0) => 50_000 )
2483 };
2484
2485 crate::populate_test_db!(wallet.database.borrow_mut(), tx_meta, None);
2488
2489 let addr = wallet.get_address(New).unwrap();
2490 let mut builder = wallet.build_tx();
2491 builder.add_recipient(addr.script_pubkey(), 25_000);
2492 let (psbt, _) = builder.finish().unwrap();
2493
2494 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF));
2495 }
2496
2497 #[test]
2498 #[should_panic(
2499 expected = "The `change_policy` can be set only if the wallet has a change_descriptor"
2500 )]
2501 fn test_create_tx_change_policy_no_internal() {
2502 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2503 let addr = wallet.get_address(New).unwrap();
2504 let mut builder = wallet.build_tx();
2505 builder
2506 .add_recipient(addr.script_pubkey(), 25_000)
2507 .do_not_spend_change();
2508 builder.finish().unwrap();
2509 }
2510
2511 #[test]
2512 fn test_create_tx_drain_wallet_and_drain_to() {
2513 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2514 let addr = wallet.get_address(New).unwrap();
2515 let mut builder = wallet.build_tx();
2516 builder.drain_to(addr.script_pubkey()).drain_wallet();
2517 let (psbt, details) = builder.finish().unwrap();
2518
2519 assert_eq!(psbt.unsigned_tx.output.len(), 1);
2520 assert_eq!(
2521 psbt.unsigned_tx.output[0].value,
2522 50_000 - details.fee.unwrap_or(0)
2523 );
2524 }
2525
2526 #[test]
2527 fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
2528 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2529 let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
2530 let drain_addr = wallet.get_address(New).unwrap();
2531 let mut builder = wallet.build_tx();
2532 builder
2533 .add_recipient(addr.script_pubkey(), 20_000)
2534 .drain_to(drain_addr.script_pubkey())
2535 .drain_wallet();
2536 let (psbt, details) = builder.finish().unwrap();
2537 let outputs = psbt.unsigned_tx.output;
2538
2539 assert_eq!(outputs.len(), 2);
2540 let main_output = outputs
2541 .iter()
2542 .find(|x| x.script_pubkey == addr.script_pubkey())
2543 .unwrap();
2544 let drain_output = outputs
2545 .iter()
2546 .find(|x| x.script_pubkey == drain_addr.script_pubkey())
2547 .unwrap();
2548 assert_eq!(main_output.value, 20_000,);
2549 assert_eq!(drain_output.value, 30_000 - details.fee.unwrap_or(0));
2550 }
2551
2552 #[test]
2553 fn test_create_tx_drain_to_and_utxos() {
2554 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2555 let addr = wallet.get_address(New).unwrap();
2556 let utxos: Vec<_> = wallet
2557 .get_available_utxos()
2558 .unwrap()
2559 .into_iter()
2560 .map(|(u, _)| u.outpoint)
2561 .collect();
2562 let mut builder = wallet.build_tx();
2563 builder
2564 .drain_to(addr.script_pubkey())
2565 .add_utxos(&utxos)
2566 .unwrap();
2567 let (psbt, details) = builder.finish().unwrap();
2568
2569 assert_eq!(psbt.unsigned_tx.output.len(), 1);
2570 assert_eq!(
2571 psbt.unsigned_tx.output[0].value,
2572 50_000 - details.fee.unwrap_or(0)
2573 );
2574 }
2575
2576 #[test]
2577 #[should_panic(expected = "NoRecipients")]
2578 fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
2579 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2580 let drain_addr = wallet.get_address(New).unwrap();
2581 let mut builder = wallet.build_tx();
2582 builder.drain_to(drain_addr.script_pubkey());
2583 builder.finish().unwrap();
2584 }
2585
2586 #[test]
2587 fn test_create_tx_default_fee_rate() {
2588 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2589 let addr = wallet.get_address(New).unwrap();
2590 let mut builder = wallet.build_tx();
2591 builder.add_recipient(addr.script_pubkey(), 25_000);
2592 let (psbt, details) = builder.finish().unwrap();
2593
2594 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::default(), @add_signature);
2595 }
2596
2597 #[test]
2598 fn test_create_tx_custom_fee_rate() {
2599 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2600 let addr = wallet.get_address(New).unwrap();
2601 let mut builder = wallet.build_tx();
2602 builder
2603 .add_recipient(addr.script_pubkey(), 25_000)
2604 .fee_rate(FeeRate::from_sat_per_vb(5.0));
2605 let (psbt, details) = builder.finish().unwrap();
2606
2607 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
2608 }
2609
2610 #[test]
2611 fn test_create_tx_absolute_fee() {
2612 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2613 let addr = wallet.get_address(New).unwrap();
2614 let mut builder = wallet.build_tx();
2615 builder
2616 .drain_to(addr.script_pubkey())
2617 .drain_wallet()
2618 .fee_absolute(100);
2619 let (psbt, details) = builder.finish().unwrap();
2620
2621 assert_eq!(details.fee.unwrap_or(0), 100);
2622 assert_eq!(psbt.unsigned_tx.output.len(), 1);
2623 assert_eq!(
2624 psbt.unsigned_tx.output[0].value,
2625 50_000 - details.fee.unwrap_or(0)
2626 );
2627 }
2628
2629 #[test]
2630 fn test_create_tx_absolute_zero_fee() {
2631 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2632 let addr = wallet.get_address(New).unwrap();
2633 let mut builder = wallet.build_tx();
2634 builder
2635 .drain_to(addr.script_pubkey())
2636 .drain_wallet()
2637 .fee_absolute(0);
2638 let (psbt, details) = builder.finish().unwrap();
2639
2640 assert_eq!(details.fee.unwrap_or(0), 0);
2641 assert_eq!(psbt.unsigned_tx.output.len(), 1);
2642 assert_eq!(
2643 psbt.unsigned_tx.output[0].value,
2644 50_000 - details.fee.unwrap_or(0)
2645 );
2646 }
2647
2648 #[test]
2649 #[should_panic(expected = "InsufficientFunds")]
2650 fn test_create_tx_absolute_high_fee() {
2651 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2652 let addr = wallet.get_address(New).unwrap();
2653 let mut builder = wallet.build_tx();
2654 builder
2655 .drain_to(addr.script_pubkey())
2656 .drain_wallet()
2657 .fee_absolute(60_000);
2658 let (_psbt, _details) = builder.finish().unwrap();
2659 }
2660
2661 #[test]
2662 fn test_create_tx_add_change() {
2663 use super::tx_builder::TxOrdering;
2664
2665 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2666 let addr = wallet.get_address(New).unwrap();
2667 let mut builder = wallet.build_tx();
2668 builder
2669 .add_recipient(addr.script_pubkey(), 25_000)
2670 .ordering(TxOrdering::Untouched);
2671 let (psbt, details) = builder.finish().unwrap();
2672
2673 assert_eq!(psbt.unsigned_tx.output.len(), 2);
2674 assert_eq!(psbt.unsigned_tx.output[0].value, 25_000);
2675 assert_eq!(
2676 psbt.unsigned_tx.output[1].value,
2677 25_000 - details.fee.unwrap_or(0)
2678 );
2679 }
2680
2681 #[test]
2682 fn test_create_tx_skip_change_dust() {
2683 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2684 let addr = wallet.get_address(New).unwrap();
2685 let mut builder = wallet.build_tx();
2686 builder.add_recipient(addr.script_pubkey(), 49_800);
2687 let (psbt, details) = builder.finish().unwrap();
2688
2689 assert_eq!(psbt.unsigned_tx.output.len(), 1);
2690 assert_eq!(psbt.unsigned_tx.output[0].value, 49_800);
2691 assert_eq!(details.fee.unwrap_or(0), 200);
2692 }
2693
2694 #[test]
2695 #[should_panic(expected = "InsufficientFunds")]
2696 fn test_create_tx_drain_to_dust_amount() {
2697 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2698 let addr = wallet.get_address(New).unwrap();
2699 let mut builder = wallet.build_tx();
2701 builder
2702 .drain_to(addr.script_pubkey())
2703 .drain_wallet()
2704 .fee_rate(FeeRate::from_sat_per_vb(453.0));
2705 builder.finish().unwrap();
2706 }
2707
2708 #[test]
2709 fn test_create_tx_ordering_respected() {
2710 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2711 let addr = wallet.get_address(New).unwrap();
2712 let mut builder = wallet.build_tx();
2713 builder
2714 .add_recipient(addr.script_pubkey(), 30_000)
2715 .add_recipient(addr.script_pubkey(), 10_000)
2716 .ordering(super::tx_builder::TxOrdering::Bip69Lexicographic);
2717 let (psbt, details) = builder.finish().unwrap();
2718
2719 assert_eq!(psbt.unsigned_tx.output.len(), 3);
2720 assert_eq!(
2721 psbt.unsigned_tx.output[0].value,
2722 10_000 - details.fee.unwrap_or(0)
2723 );
2724 assert_eq!(psbt.unsigned_tx.output[1].value, 10_000);
2725 assert_eq!(psbt.unsigned_tx.output[2].value, 30_000);
2726 }
2727
2728 #[test]
2729 fn test_create_tx_default_sighash() {
2730 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2731 let addr = wallet.get_address(New).unwrap();
2732 let mut builder = wallet.build_tx();
2733 builder.add_recipient(addr.script_pubkey(), 30_000);
2734 let (psbt, _) = builder.finish().unwrap();
2735
2736 assert_eq!(psbt.inputs[0].sighash_type, None);
2737 }
2738
2739 #[test]
2740 fn test_create_tx_custom_sighash() {
2741 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
2742 let addr = wallet.get_address(New).unwrap();
2743 let mut builder = wallet.build_tx();
2744 builder
2745 .add_recipient(addr.script_pubkey(), 30_000)
2746 .sighash(bitcoin::EcdsaSighashType::Single.into());
2747 let (psbt, _) = builder.finish().unwrap();
2748
2749 assert_eq!(
2750 psbt.inputs[0].sighash_type,
2751 Some(bitcoin::EcdsaSighashType::Single.into())
2752 );
2753 }
2754
2755 #[test]
2756 fn test_create_tx_input_hd_keypaths() {
2757 use bitcoin::util::bip32::{DerivationPath, Fingerprint};
2758 use std::str::FromStr;
2759
2760 let (wallet, _, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
2761 let addr = wallet.get_address(New).unwrap();
2762 let mut builder = wallet.build_tx();
2763 builder.drain_to(addr.script_pubkey()).drain_wallet();
2764 let (psbt, _) = builder.finish().unwrap();
2765
2766 assert_eq!(psbt.inputs[0].bip32_derivation.len(), 1);
2767 assert_eq!(
2768 psbt.inputs[0].bip32_derivation.values().next().unwrap(),
2769 &(
2770 Fingerprint::from_str("d34db33f").unwrap(),
2771 DerivationPath::from_str("m/44'/0'/0'/0/0").unwrap()
2772 )
2773 );
2774 }
2775
2776 #[test]
2777 fn test_create_tx_output_hd_keypaths() {
2778 use bitcoin::util::bip32::{DerivationPath, Fingerprint};
2779 use std::str::FromStr;
2780
2781 let (wallet, descriptors, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
2782 wallet.get_address(New).unwrap();
2784
2785 let addr = testutils!(@external descriptors, 5);
2786 let mut builder = wallet.build_tx();
2787 builder.drain_to(addr.script_pubkey()).drain_wallet();
2788 let (psbt, _) = builder.finish().unwrap();
2789
2790 assert_eq!(psbt.outputs[0].bip32_derivation.len(), 1);
2791 assert_eq!(
2792 psbt.outputs[0].bip32_derivation.values().next().unwrap(),
2793 &(
2794 Fingerprint::from_str("d34db33f").unwrap(),
2795 DerivationPath::from_str("m/44'/0'/0'/0/5").unwrap()
2796 )
2797 );
2798 }
2799
2800 #[test]
2801 fn test_create_tx_set_redeem_script_p2sh() {
2802 use bitcoin::hashes::hex::FromHex;
2803
2804 let (wallet, _, _) =
2805 get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2806 let addr = wallet.get_address(New).unwrap();
2807 let mut builder = wallet.build_tx();
2808 builder.drain_to(addr.script_pubkey()).drain_wallet();
2809 let (psbt, _) = builder.finish().unwrap();
2810
2811 assert_eq!(
2812 psbt.inputs[0].redeem_script,
2813 Some(Script::from(
2814 Vec::<u8>::from_hex(
2815 "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
2816 )
2817 .unwrap()
2818 ))
2819 );
2820 assert_eq!(psbt.inputs[0].witness_script, None);
2821 }
2822
2823 #[test]
2824 fn test_create_tx_set_witness_script_p2wsh() {
2825 use bitcoin::hashes::hex::FromHex;
2826
2827 let (wallet, _, _) =
2828 get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2829 let addr = wallet.get_address(New).unwrap();
2830 let mut builder = wallet.build_tx();
2831 builder.drain_to(addr.script_pubkey()).drain_wallet();
2832 let (psbt, _) = builder.finish().unwrap();
2833
2834 assert_eq!(psbt.inputs[0].redeem_script, None);
2835 assert_eq!(
2836 psbt.inputs[0].witness_script,
2837 Some(Script::from(
2838 Vec::<u8>::from_hex(
2839 "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
2840 )
2841 .unwrap()
2842 ))
2843 );
2844 }
2845
2846 #[test]
2847 fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
2848 use bitcoin::hashes::hex::FromHex;
2849
2850 let (wallet, _, _) =
2851 get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
2852 let addr = wallet.get_address(New).unwrap();
2853 let mut builder = wallet.build_tx();
2854 builder.drain_to(addr.script_pubkey()).drain_wallet();
2855 let (psbt, _) = builder.finish().unwrap();
2856
2857 let script = Script::from(
2858 Vec::<u8>::from_hex(
2859 "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
2860 )
2861 .unwrap(),
2862 );
2863
2864 assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_v0_p2wsh()));
2865 assert_eq!(psbt.inputs[0].witness_script, Some(script));
2866 }
2867
2868 #[test]
2869 fn test_create_tx_non_witness_utxo() {
2870 let (wallet, _, _) =
2871 get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2872 let addr = wallet.get_address(New).unwrap();
2873 let mut builder = wallet.build_tx();
2874 builder.drain_to(addr.script_pubkey()).drain_wallet();
2875 let (psbt, _) = builder.finish().unwrap();
2876
2877 assert!(psbt.inputs[0].non_witness_utxo.is_some());
2878 assert!(psbt.inputs[0].witness_utxo.is_none());
2879 }
2880
2881 #[test]
2882 fn test_create_tx_only_witness_utxo() {
2883 let (wallet, _, _) =
2884 get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2885 let addr = wallet.get_address(New).unwrap();
2886 let mut builder = wallet.build_tx();
2887 builder
2888 .drain_to(addr.script_pubkey())
2889 .only_witness_utxo()
2890 .drain_wallet();
2891 let (psbt, _) = builder.finish().unwrap();
2892
2893 assert!(psbt.inputs[0].non_witness_utxo.is_none());
2894 assert!(psbt.inputs[0].witness_utxo.is_some());
2895 }
2896
2897 #[test]
2898 fn test_create_tx_shwpkh_has_witness_utxo() {
2899 let (wallet, _, _) =
2900 get_funded_wallet("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2901 let addr = wallet.get_address(New).unwrap();
2902 let mut builder = wallet.build_tx();
2903 builder.drain_to(addr.script_pubkey()).drain_wallet();
2904 let (psbt, _) = builder.finish().unwrap();
2905
2906 assert!(psbt.inputs[0].witness_utxo.is_some());
2907 }
2908
2909 #[test]
2910 fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() {
2911 let (wallet, _, _) =
2912 get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
2913 let addr = wallet.get_address(New).unwrap();
2914 let mut builder = wallet.build_tx();
2915 builder.drain_to(addr.script_pubkey()).drain_wallet();
2916 let (psbt, _) = builder.finish().unwrap();
2917
2918 assert!(psbt.inputs[0].non_witness_utxo.is_some());
2919 assert!(psbt.inputs[0].witness_utxo.is_some());
2920 }
2921
2922 #[test]
2923 fn test_create_tx_add_utxo() {
2924 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
2925 let small_output_txid = crate::populate_test_db!(
2926 wallet.database.borrow_mut(),
2927 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
2928 Some(100),
2929 );
2930
2931 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
2932 let mut builder = wallet.build_tx();
2933 builder
2934 .add_recipient(addr.script_pubkey(), 30_000)
2935 .add_utxo(OutPoint {
2936 txid: small_output_txid,
2937 vout: 0,
2938 })
2939 .unwrap();
2940 let (psbt, details) = builder.finish().unwrap();
2941
2942 assert_eq!(
2943 psbt.unsigned_tx.input.len(),
2944 2,
2945 "should add an additional input since 25_000 < 30_000"
2946 );
2947 assert_eq!(details.sent, 75_000, "total should be sum of both inputs");
2948 }
2949
2950 #[test]
2951 #[should_panic(expected = "InsufficientFunds")]
2952 fn test_create_tx_manually_selected_insufficient() {
2953 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
2954 let small_output_txid = crate::populate_test_db!(
2955 wallet.database.borrow_mut(),
2956 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
2957 Some(100),
2958 );
2959
2960 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
2961 let mut builder = wallet.build_tx();
2962 builder
2963 .add_recipient(addr.script_pubkey(), 30_000)
2964 .add_utxo(OutPoint {
2965 txid: small_output_txid,
2966 vout: 0,
2967 })
2968 .unwrap()
2969 .manually_selected_only();
2970 builder.finish().unwrap();
2971 }
2972
2973 #[test]
2974 #[should_panic(expected = "SpendingPolicyRequired(External)")]
2975 fn test_create_tx_policy_path_required() {
2976 let (wallet, _, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
2977
2978 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
2979 let mut builder = wallet.build_tx();
2980 builder.add_recipient(addr.script_pubkey(), 30_000);
2981 builder.finish().unwrap();
2982 }
2983
2984 #[test]
2985 fn test_create_tx_policy_path_no_csv() {
2986 let descriptors = testutils!(@descriptors (get_test_wpkh()));
2987 let wallet = Wallet::new(
2988 &descriptors.0,
2989 None,
2990 Network::Regtest,
2991 AnyDatabase::Memory(MemoryDatabase::new()),
2992 )
2993 .unwrap();
2994
2995 let tx_meta = testutils! {
2996 @tx ( (@external descriptors, 0) => 50_000 )
2997 };
2998
2999 crate::populate_test_db!(wallet.database.borrow_mut(), tx_meta, None);
3002
3003 let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
3004 let root_id = external_policy.id;
3005 let path = vec![(root_id, vec![0])].into_iter().collect();
3007
3008 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3009 let mut builder = wallet.build_tx();
3010 builder
3011 .add_recipient(addr.script_pubkey(), 30_000)
3012 .policy_path(path, KeychainKind::External);
3013 let (psbt, _) = builder.finish().unwrap();
3014
3015 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF));
3016 }
3017
3018 #[test]
3019 fn test_create_tx_policy_path_use_csv() {
3020 let (wallet, _, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
3021
3022 let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
3023 let root_id = external_policy.id;
3024 let path = vec![(root_id, vec![1])].into_iter().collect();
3026
3027 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3028 let mut builder = wallet.build_tx();
3029 builder
3030 .add_recipient(addr.script_pubkey(), 30_000)
3031 .policy_path(path, KeychainKind::External);
3032 let (psbt, _) = builder.finish().unwrap();
3033
3034 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
3035 }
3036
3037 #[test]
3038 fn test_create_tx_policy_path_ignored_subtree_with_csv() {
3039 let (wallet, _, _) = get_funded_wallet("wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),or_i(and_v(v:pkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(30)),and_v(v:pkh(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(90)))))");
3040
3041 let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
3042 let root_id = external_policy.id;
3043 let path = vec![(root_id, vec![0])].into_iter().collect();
3045
3046 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3047 let mut builder = wallet.build_tx();
3048 builder
3049 .add_recipient(addr.script_pubkey(), 30_000)
3050 .policy_path(path, KeychainKind::External);
3051 let (psbt, _) = builder.finish().unwrap();
3052
3053 assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
3054 }
3055
3056 #[test]
3057 fn test_create_tx_global_xpubs_with_origin() {
3058 use bitcoin::hashes::hex::FromHex;
3059 use bitcoin::util::bip32;
3060
3061 let (wallet, _, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
3062 let addr = wallet.get_address(New).unwrap();
3063 let mut builder = wallet.build_tx();
3064 builder
3065 .add_recipient(addr.script_pubkey(), 25_000)
3066 .add_global_xpubs();
3067 let (psbt, _) = builder.finish().unwrap();
3068
3069 let key = bip32::ExtendedPubKey::from_str("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
3070 let fingerprint = bip32::Fingerprint::from_hex("73756c7f").unwrap();
3071 let path = bip32::DerivationPath::from_str("m/48'/0'/0'/2'").unwrap();
3072
3073 assert_eq!(psbt.xpub.len(), 1);
3074 assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
3075 }
3076
3077 #[test]
3078 fn test_add_foreign_utxo() {
3079 let (wallet1, _, _) = get_funded_wallet(get_test_wpkh());
3080 let (wallet2, _, _) =
3081 get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
3082
3083 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3084 let utxo = wallet2.list_unspent().unwrap().remove(0);
3085 let foreign_utxo_satisfaction = wallet2
3086 .get_descriptor_for_keychain(KeychainKind::External)
3087 .max_satisfaction_weight()
3088 .unwrap();
3089
3090 let psbt_input = psbt::Input {
3091 witness_utxo: Some(utxo.txout.clone()),
3092 ..Default::default()
3093 };
3094
3095 let mut builder = wallet1.build_tx();
3096 builder
3097 .add_recipient(addr.script_pubkey(), 60_000)
3098 .only_witness_utxo()
3099 .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
3100 .unwrap();
3101 let (mut psbt, details) = builder.finish().unwrap();
3102
3103 assert_eq!(
3104 details.sent - details.received,
3105 10_000 + details.fee.unwrap_or(0),
3106 "we should have only net spent ~10_000"
3107 );
3108
3109 assert!(
3110 psbt.unsigned_tx
3111 .input
3112 .iter()
3113 .any(|input| input.previous_output == utxo.outpoint),
3114 "foreign_utxo should be in there"
3115 );
3116
3117 let finished = wallet1
3118 .sign(
3119 &mut psbt,
3120 SignOptions {
3121 trust_witness_utxo: true,
3122 ..Default::default()
3123 },
3124 )
3125 .unwrap();
3126
3127 assert!(
3128 !finished,
3129 "only one of the inputs should have been signed so far"
3130 );
3131
3132 let finished = wallet2
3133 .sign(
3134 &mut psbt,
3135 SignOptions {
3136 trust_witness_utxo: true,
3137 ..Default::default()
3138 },
3139 )
3140 .unwrap();
3141 assert!(finished, "all the inputs should have been signed now");
3142 }
3143
3144 #[test]
3145 #[should_panic(expected = "Generic(\"Foreign utxo missing witness_utxo or non_witness_utxo\")")]
3146 fn test_add_foreign_utxo_invalid_psbt_input() {
3147 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3148 let mut builder = wallet.build_tx();
3149 let outpoint = wallet.list_unspent().unwrap()[0].outpoint;
3150 let foreign_utxo_satisfaction = wallet
3151 .get_descriptor_for_keychain(KeychainKind::External)
3152 .max_satisfaction_weight()
3153 .unwrap();
3154 builder
3155 .add_foreign_utxo(outpoint, psbt::Input::default(), foreign_utxo_satisfaction)
3156 .unwrap();
3157 }
3158
3159 #[test]
3160 fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
3161 let (wallet1, _, txid1) = get_funded_wallet(get_test_wpkh());
3162 let (wallet2, _, txid2) =
3163 get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
3164
3165 let utxo2 = wallet2.list_unspent().unwrap().remove(0);
3166 let tx1 = wallet1
3167 .database
3168 .borrow()
3169 .get_tx(&txid1, true)
3170 .unwrap()
3171 .unwrap()
3172 .transaction
3173 .unwrap();
3174 let tx2 = wallet2
3175 .database
3176 .borrow()
3177 .get_tx(&txid2, true)
3178 .unwrap()
3179 .unwrap()
3180 .transaction
3181 .unwrap();
3182
3183 let satisfaction_weight = wallet2
3184 .get_descriptor_for_keychain(KeychainKind::External)
3185 .max_satisfaction_weight()
3186 .unwrap();
3187
3188 let mut builder = wallet1.build_tx();
3189 assert!(
3190 builder
3191 .add_foreign_utxo(
3192 utxo2.outpoint,
3193 psbt::Input {
3194 non_witness_utxo: Some(tx1),
3195 ..Default::default()
3196 },
3197 satisfaction_weight
3198 )
3199 .is_err(),
3200 "should fail when outpoint doesn't match psbt_input"
3201 );
3202 assert!(
3203 builder
3204 .add_foreign_utxo(
3205 utxo2.outpoint,
3206 psbt::Input {
3207 non_witness_utxo: Some(tx2),
3208 ..Default::default()
3209 },
3210 satisfaction_weight
3211 )
3212 .is_ok(),
3213 "shoulld be ok when outpoint does match psbt_input"
3214 );
3215 }
3216
3217 #[test]
3218 fn test_add_foreign_utxo_only_witness_utxo() {
3219 let (wallet1, _, _) = get_funded_wallet(get_test_wpkh());
3220 let (wallet2, _, txid2) =
3221 get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
3222 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3223 let utxo2 = wallet2.list_unspent().unwrap().remove(0);
3224
3225 let satisfaction_weight = wallet2
3226 .get_descriptor_for_keychain(KeychainKind::External)
3227 .max_satisfaction_weight()
3228 .unwrap();
3229
3230 let mut builder = wallet1.build_tx();
3231 builder.add_recipient(addr.script_pubkey(), 60_000);
3232
3233 {
3234 let mut builder = builder.clone();
3235 let psbt_input = psbt::Input {
3236 witness_utxo: Some(utxo2.txout.clone()),
3237 ..Default::default()
3238 };
3239 builder
3240 .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
3241 .unwrap();
3242 assert!(
3243 builder.finish().is_err(),
3244 "psbt_input with witness_utxo should fail with only witness_utxo"
3245 );
3246 }
3247
3248 {
3249 let mut builder = builder.clone();
3250 let psbt_input = psbt::Input {
3251 witness_utxo: Some(utxo2.txout.clone()),
3252 ..Default::default()
3253 };
3254 builder
3255 .only_witness_utxo()
3256 .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
3257 .unwrap();
3258 assert!(
3259 builder.finish().is_ok(),
3260 "psbt_input with just witness_utxo should succeed when `only_witness_utxo` is enabled"
3261 );
3262 }
3263
3264 {
3265 let mut builder = builder.clone();
3266 let tx2 = wallet2
3267 .database
3268 .borrow()
3269 .get_tx(&txid2, true)
3270 .unwrap()
3271 .unwrap()
3272 .transaction
3273 .unwrap();
3274 let psbt_input = psbt::Input {
3275 non_witness_utxo: Some(tx2),
3276 ..Default::default()
3277 };
3278 builder
3279 .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
3280 .unwrap();
3281 assert!(
3282 builder.finish().is_ok(),
3283 "psbt_input with non_witness_utxo should succeed by default"
3284 );
3285 }
3286 }
3287
3288 #[test]
3289 fn test_get_psbt_input() {
3290 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3292 for utxo in wallet.list_unspent().unwrap() {
3293 let psbt_input = wallet.get_psbt_input(utxo, None, false).unwrap();
3294 assert!(psbt_input.witness_utxo.is_some() || psbt_input.non_witness_utxo.is_some());
3295 }
3296 }
3297
3298 #[test]
3299 #[should_panic(
3300 expected = "MissingKeyOrigin(\"tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3\")"
3301 )]
3302 fn test_create_tx_global_xpubs_origin_missing() {
3303 let (wallet, _, _) = get_funded_wallet("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
3304 let addr = wallet.get_address(New).unwrap();
3305 let mut builder = wallet.build_tx();
3306 builder
3307 .add_recipient(addr.script_pubkey(), 25_000)
3308 .add_global_xpubs();
3309 builder.finish().unwrap();
3310 }
3311
3312 #[test]
3313 fn test_create_tx_global_xpubs_master_without_origin() {
3314 use bitcoin::hashes::hex::FromHex;
3315 use bitcoin::util::bip32;
3316
3317 let (wallet, _, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
3318 let addr = wallet.get_address(New).unwrap();
3319 let mut builder = wallet.build_tx();
3320 builder
3321 .add_recipient(addr.script_pubkey(), 25_000)
3322 .add_global_xpubs();
3323 let (psbt, _) = builder.finish().unwrap();
3324
3325 let key = bip32::ExtendedPubKey::from_str("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
3326 let fingerprint = bip32::Fingerprint::from_hex("997a323b").unwrap();
3327
3328 assert_eq!(psbt.xpub.len(), 1);
3329 assert_eq!(
3330 psbt.xpub.get(&key),
3331 Some(&(fingerprint, bip32::DerivationPath::default()))
3332 );
3333 }
3334
3335 #[test]
3336 #[should_panic(expected = "IrreplaceableTransaction")]
3337 fn test_bump_fee_irreplaceable_tx() {
3338 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3339 let addr = wallet.get_address(New).unwrap();
3340 let mut builder = wallet.build_tx();
3341 builder.add_recipient(addr.script_pubkey(), 25_000);
3342 let (psbt, mut details) = builder.finish().unwrap();
3343
3344 let tx = psbt.extract_tx();
3345 let txid = tx.txid();
3346 details.transaction = Some(tx);
3348 wallet.database.borrow_mut().set_tx(&details).unwrap();
3349
3350 wallet.build_fee_bump(txid).unwrap().finish().unwrap();
3351 }
3352
3353 #[test]
3354 #[should_panic(expected = "TransactionConfirmed")]
3355 fn test_bump_fee_confirmed_tx() {
3356 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3357 let addr = wallet.get_address(New).unwrap();
3358 let mut builder = wallet.build_tx();
3359 builder.add_recipient(addr.script_pubkey(), 25_000);
3360 let (psbt, mut details) = builder.finish().unwrap();
3361
3362 let tx = psbt.extract_tx();
3363 let txid = tx.txid();
3364 details.transaction = Some(tx);
3366 details.confirmation_time = Some(BlockTime {
3367 timestamp: 12345678,
3368 height: 42,
3369 });
3370 wallet.database.borrow_mut().set_tx(&details).unwrap();
3371
3372 wallet.build_fee_bump(txid).unwrap().finish().unwrap();
3373 }
3374
3375 #[test]
3376 #[should_panic(expected = "FeeRateTooLow")]
3377 fn test_bump_fee_low_fee_rate() {
3378 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3379 let addr = wallet.get_address(New).unwrap();
3380 let mut builder = wallet.build_tx();
3381 builder
3382 .add_recipient(addr.script_pubkey(), 25_000)
3383 .enable_rbf();
3384 let (psbt, mut details) = builder.finish().unwrap();
3385
3386 let tx = psbt.extract_tx();
3387 let txid = tx.txid();
3388 details.transaction = Some(tx);
3390 wallet.database.borrow_mut().set_tx(&details).unwrap();
3391
3392 let mut builder = wallet.build_fee_bump(txid).unwrap();
3393 builder.fee_rate(FeeRate::from_sat_per_vb(1.0));
3394 builder.finish().unwrap();
3395 }
3396
3397 #[test]
3398 #[should_panic(expected = "FeeTooLow")]
3399 fn test_bump_fee_low_abs() {
3400 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3401 let addr = wallet.get_address(New).unwrap();
3402 let mut builder = wallet.build_tx();
3403 builder
3404 .add_recipient(addr.script_pubkey(), 25_000)
3405 .enable_rbf();
3406 let (psbt, mut details) = builder.finish().unwrap();
3407
3408 let tx = psbt.extract_tx();
3409 let txid = tx.txid();
3410 details.transaction = Some(tx);
3412 wallet.database.borrow_mut().set_tx(&details).unwrap();
3413
3414 let mut builder = wallet.build_fee_bump(txid).unwrap();
3415 builder.fee_absolute(10);
3416 builder.finish().unwrap();
3417 }
3418
3419 #[test]
3420 #[should_panic(expected = "FeeTooLow")]
3421 fn test_bump_fee_zero_abs() {
3422 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3423 let addr = wallet.get_address(New).unwrap();
3424 let mut builder = wallet.build_tx();
3425 builder
3426 .add_recipient(addr.script_pubkey(), 25_000)
3427 .enable_rbf();
3428 let (psbt, mut details) = builder.finish().unwrap();
3429
3430 let tx = psbt.extract_tx();
3431 let txid = tx.txid();
3432 details.transaction = Some(tx);
3434 wallet.database.borrow_mut().set_tx(&details).unwrap();
3435
3436 let mut builder = wallet.build_fee_bump(txid).unwrap();
3437 builder.fee_absolute(0);
3438 builder.finish().unwrap();
3439 }
3440
3441 #[test]
3442 fn test_bump_fee_reduce_change() {
3443 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3444 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3445 let mut builder = wallet.build_tx();
3446 builder
3447 .add_recipient(addr.script_pubkey(), 25_000)
3448 .enable_rbf();
3449 let (psbt, mut original_details) = builder.finish().unwrap();
3450 let mut tx = psbt.extract_tx();
3451 let txid = tx.txid();
3452 for txin in &mut tx.input {
3454 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3456 .database
3457 .borrow_mut()
3458 .del_utxo(&txin.previous_output)
3459 .unwrap();
3460 }
3461 original_details.transaction = Some(tx);
3462 wallet
3463 .database
3464 .borrow_mut()
3465 .set_tx(&original_details)
3466 .unwrap();
3467
3468 let mut builder = wallet.build_fee_bump(txid).unwrap();
3469 builder.fee_rate(FeeRate::from_sat_per_vb(2.5)).enable_rbf();
3470 let (psbt, details) = builder.finish().unwrap();
3471
3472 assert_eq!(details.sent, original_details.sent);
3473 assert_eq!(
3474 details.received + details.fee.unwrap_or(0),
3475 original_details.received + original_details.fee.unwrap_or(0)
3476 );
3477 assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
3478
3479 let tx = &psbt.unsigned_tx;
3480 assert_eq!(tx.output.len(), 2);
3481 assert_eq!(
3482 tx.output
3483 .iter()
3484 .find(|txout| txout.script_pubkey == addr.script_pubkey())
3485 .unwrap()
3486 .value,
3487 25_000
3488 );
3489 assert_eq!(
3490 tx.output
3491 .iter()
3492 .find(|txout| txout.script_pubkey != addr.script_pubkey())
3493 .unwrap()
3494 .value,
3495 details.received
3496 );
3497
3498 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
3499 }
3500
3501 #[test]
3502 fn test_bump_fee_absolute_reduce_change() {
3503 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3504 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3505 let mut builder = wallet.build_tx();
3506 builder
3507 .add_recipient(addr.script_pubkey(), 25_000)
3508 .enable_rbf();
3509 let (psbt, mut original_details) = builder.finish().unwrap();
3510 let mut tx = psbt.extract_tx();
3511 let txid = tx.txid();
3512 for txin in &mut tx.input {
3514 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3516 .database
3517 .borrow_mut()
3518 .del_utxo(&txin.previous_output)
3519 .unwrap();
3520 }
3521 original_details.transaction = Some(tx);
3522 wallet
3523 .database
3524 .borrow_mut()
3525 .set_tx(&original_details)
3526 .unwrap();
3527
3528 let mut builder = wallet.build_fee_bump(txid).unwrap();
3529 builder.fee_absolute(200);
3530 builder.enable_rbf();
3531 let (psbt, details) = builder.finish().unwrap();
3532
3533 assert_eq!(details.sent, original_details.sent);
3534 assert_eq!(
3535 details.received + details.fee.unwrap_or(0),
3536 original_details.received + original_details.fee.unwrap_or(0)
3537 );
3538 assert!(
3539 details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0),
3540 "{} > {}",
3541 details.fee.unwrap_or(0),
3542 original_details.fee.unwrap_or(0)
3543 );
3544
3545 let tx = &psbt.unsigned_tx;
3546 assert_eq!(tx.output.len(), 2);
3547 assert_eq!(
3548 tx.output
3549 .iter()
3550 .find(|txout| txout.script_pubkey == addr.script_pubkey())
3551 .unwrap()
3552 .value,
3553 25_000
3554 );
3555 assert_eq!(
3556 tx.output
3557 .iter()
3558 .find(|txout| txout.script_pubkey != addr.script_pubkey())
3559 .unwrap()
3560 .value,
3561 details.received
3562 );
3563
3564 assert_eq!(details.fee.unwrap_or(0), 200);
3565 }
3566
3567 #[test]
3568 fn test_bump_fee_reduce_single_recipient() {
3569 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3570 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3571 let mut builder = wallet.build_tx();
3572 builder
3573 .drain_to(addr.script_pubkey())
3574 .drain_wallet()
3575 .enable_rbf();
3576 let (psbt, mut original_details) = builder.finish().unwrap();
3577 let mut tx = psbt.extract_tx();
3578 let txid = tx.txid();
3579 for txin in &mut tx.input {
3580 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3582 .database
3583 .borrow_mut()
3584 .del_utxo(&txin.previous_output)
3585 .unwrap();
3586 }
3587 original_details.transaction = Some(tx);
3588 wallet
3589 .database
3590 .borrow_mut()
3591 .set_tx(&original_details)
3592 .unwrap();
3593
3594 let mut builder = wallet.build_fee_bump(txid).unwrap();
3595 builder
3596 .fee_rate(FeeRate::from_sat_per_vb(2.5))
3597 .allow_shrinking(addr.script_pubkey())
3598 .unwrap();
3599 let (psbt, details) = builder.finish().unwrap();
3600
3601 assert_eq!(details.sent, original_details.sent);
3602 assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
3603
3604 let tx = &psbt.unsigned_tx;
3605 assert_eq!(tx.output.len(), 1);
3606 assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
3607
3608 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
3609 }
3610
3611 #[test]
3612 fn test_bump_fee_absolute_reduce_single_recipient() {
3613 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
3614 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3615 let mut builder = wallet.build_tx();
3616 builder
3617 .drain_to(addr.script_pubkey())
3618 .drain_wallet()
3619 .enable_rbf();
3620 let (psbt, mut original_details) = builder.finish().unwrap();
3621 let mut tx = psbt.extract_tx();
3622 let txid = tx.txid();
3623 for txin in &mut tx.input {
3624 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3626 .database
3627 .borrow_mut()
3628 .del_utxo(&txin.previous_output)
3629 .unwrap();
3630 }
3631 original_details.transaction = Some(tx);
3632 wallet
3633 .database
3634 .borrow_mut()
3635 .set_tx(&original_details)
3636 .unwrap();
3637
3638 let mut builder = wallet.build_fee_bump(txid).unwrap();
3639 builder
3640 .allow_shrinking(addr.script_pubkey())
3641 .unwrap()
3642 .fee_absolute(300);
3643 let (psbt, details) = builder.finish().unwrap();
3644
3645 assert_eq!(details.sent, original_details.sent);
3646 assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
3647
3648 let tx = &psbt.unsigned_tx;
3649 assert_eq!(tx.output.len(), 1);
3650 assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
3651
3652 assert_eq!(details.fee.unwrap_or(0), 300);
3653 }
3654
3655 #[test]
3656 fn test_bump_fee_drain_wallet() {
3657 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3658 let incoming_txid = crate::populate_test_db!(
3660 wallet.database.borrow_mut(),
3661 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3662 Some(100),
3663 );
3664 let outpoint = OutPoint {
3665 txid: incoming_txid,
3666 vout: 0,
3667 };
3668 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3669 let mut builder = wallet.build_tx();
3670 builder
3671 .drain_to(addr.script_pubkey())
3672 .add_utxo(outpoint)
3673 .unwrap()
3674 .manually_selected_only()
3675 .enable_rbf();
3676 let (psbt, mut original_details) = builder.finish().unwrap();
3677 let mut tx = psbt.extract_tx();
3678 let txid = tx.txid();
3679 for txin in &mut tx.input {
3680 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3682 .database
3683 .borrow_mut()
3684 .del_utxo(&txin.previous_output)
3685 .unwrap();
3686 }
3687 original_details.transaction = Some(tx);
3688 wallet
3689 .database
3690 .borrow_mut()
3691 .set_tx(&original_details)
3692 .unwrap();
3693 assert_eq!(original_details.sent, 25_000);
3694
3695 let mut builder = wallet.build_fee_bump(txid).unwrap();
3698 builder
3699 .drain_wallet()
3700 .allow_shrinking(addr.script_pubkey())
3701 .unwrap()
3702 .fee_rate(FeeRate::from_sat_per_vb(5.0));
3703 let (_, details) = builder.finish().unwrap();
3704 assert_eq!(details.sent, 75_000);
3705 }
3706
3707 #[test]
3708 #[should_panic(expected = "InsufficientFunds")]
3709 fn test_bump_fee_remove_output_manually_selected_only() {
3710 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3711 let incoming_txid = crate::populate_test_db!(
3717 wallet.database.borrow_mut(),
3718 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3719 Some(100),
3720 );
3721 let outpoint = OutPoint {
3722 txid: incoming_txid,
3723 vout: 0,
3724 };
3725 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3726 let mut builder = wallet.build_tx();
3727 builder
3728 .drain_to(addr.script_pubkey())
3729 .add_utxo(outpoint)
3730 .unwrap()
3731 .manually_selected_only()
3732 .enable_rbf();
3733 let (psbt, mut original_details) = builder.finish().unwrap();
3734 let mut tx = psbt.extract_tx();
3735 let txid = tx.txid();
3736 for txin in &mut tx.input {
3737 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3739 .database
3740 .borrow_mut()
3741 .del_utxo(&txin.previous_output)
3742 .unwrap();
3743 }
3744 original_details.transaction = Some(tx);
3745 wallet
3746 .database
3747 .borrow_mut()
3748 .set_tx(&original_details)
3749 .unwrap();
3750 assert_eq!(original_details.sent, 25_000);
3751
3752 let mut builder = wallet.build_fee_bump(txid).unwrap();
3753 builder
3754 .manually_selected_only()
3755 .fee_rate(FeeRate::from_sat_per_vb(255.0));
3756 builder.finish().unwrap();
3757 }
3758
3759 #[test]
3760 fn test_bump_fee_add_input() {
3761 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3762 crate::populate_test_db!(
3763 wallet.database.borrow_mut(),
3764 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3765 Some(100),
3766 );
3767
3768 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3769 let mut builder = wallet.build_tx();
3770 builder
3771 .add_recipient(addr.script_pubkey(), 45_000)
3772 .enable_rbf();
3773 let (psbt, mut original_details) = builder.finish().unwrap();
3774 let mut tx = psbt.extract_tx();
3775 let txid = tx.txid();
3776 for txin in &mut tx.input {
3778 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3780 .database
3781 .borrow_mut()
3782 .del_utxo(&txin.previous_output)
3783 .unwrap();
3784 }
3785 original_details.transaction = Some(tx);
3786 wallet
3787 .database
3788 .borrow_mut()
3789 .set_tx(&original_details)
3790 .unwrap();
3791
3792 let mut builder = wallet.build_fee_bump(txid).unwrap();
3793 builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
3794 let (psbt, details) = builder.finish().unwrap();
3795
3796 assert_eq!(details.sent, original_details.sent + 25_000);
3797 assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
3798
3799 let tx = &psbt.unsigned_tx;
3800 assert_eq!(tx.input.len(), 2);
3801 assert_eq!(tx.output.len(), 2);
3802 assert_eq!(
3803 tx.output
3804 .iter()
3805 .find(|txout| txout.script_pubkey == addr.script_pubkey())
3806 .unwrap()
3807 .value,
3808 45_000
3809 );
3810 assert_eq!(
3811 tx.output
3812 .iter()
3813 .find(|txout| txout.script_pubkey != addr.script_pubkey())
3814 .unwrap()
3815 .value,
3816 details.received
3817 );
3818
3819 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
3820 }
3821
3822 #[test]
3823 fn test_bump_fee_absolute_add_input() {
3824 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3825 crate::populate_test_db!(
3826 wallet.database.borrow_mut(),
3827 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3828 Some(100),
3829 );
3830
3831 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3832 let mut builder = wallet.build_tx();
3833 builder
3834 .add_recipient(addr.script_pubkey(), 45_000)
3835 .enable_rbf();
3836 let (psbt, mut original_details) = builder.finish().unwrap();
3837 let mut tx = psbt.extract_tx();
3838 let txid = tx.txid();
3839 for txin in &mut tx.input {
3841 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3843 .database
3844 .borrow_mut()
3845 .del_utxo(&txin.previous_output)
3846 .unwrap();
3847 }
3848 original_details.transaction = Some(tx);
3849 wallet
3850 .database
3851 .borrow_mut()
3852 .set_tx(&original_details)
3853 .unwrap();
3854
3855 let mut builder = wallet.build_fee_bump(txid).unwrap();
3856 builder.fee_absolute(6_000);
3857 let (psbt, details) = builder.finish().unwrap();
3858
3859 assert_eq!(details.sent, original_details.sent + 25_000);
3860 assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
3861
3862 let tx = &psbt.unsigned_tx;
3863 assert_eq!(tx.input.len(), 2);
3864 assert_eq!(tx.output.len(), 2);
3865 assert_eq!(
3866 tx.output
3867 .iter()
3868 .find(|txout| txout.script_pubkey == addr.script_pubkey())
3869 .unwrap()
3870 .value,
3871 45_000
3872 );
3873 assert_eq!(
3874 tx.output
3875 .iter()
3876 .find(|txout| txout.script_pubkey != addr.script_pubkey())
3877 .unwrap()
3878 .value,
3879 details.received
3880 );
3881
3882 assert_eq!(details.fee.unwrap_or(0), 6_000);
3883 }
3884
3885 #[test]
3886 fn test_bump_fee_no_change_add_input_and_change() {
3887 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3888 let incoming_txid = crate::populate_test_db!(
3889 wallet.database.borrow_mut(),
3890 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3891 Some(100),
3892 );
3893
3894 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3896 let mut builder = wallet.build_tx();
3897 builder
3898 .drain_to(addr.script_pubkey())
3899 .add_utxo(OutPoint {
3900 txid: incoming_txid,
3901 vout: 0,
3902 })
3903 .unwrap()
3904 .manually_selected_only()
3905 .enable_rbf();
3906 let (psbt, mut original_details) = builder.finish().unwrap();
3907
3908 let mut tx = psbt.extract_tx();
3909 let txid = tx.txid();
3910 for txin in &mut tx.input {
3912 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3914 .database
3915 .borrow_mut()
3916 .del_utxo(&txin.previous_output)
3917 .unwrap();
3918 }
3919 original_details.transaction = Some(tx);
3920 wallet
3921 .database
3922 .borrow_mut()
3923 .set_tx(&original_details)
3924 .unwrap();
3925
3926 let mut builder = wallet.build_fee_bump(txid).unwrap();
3929 builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
3930 let (psbt, details) = builder.finish().unwrap();
3931
3932 let original_send_all_amount = original_details.sent - original_details.fee.unwrap_or(0);
3933 assert_eq!(details.sent, original_details.sent + 50_000);
3934 assert_eq!(
3935 details.received,
3936 75_000 - original_send_all_amount - details.fee.unwrap_or(0)
3937 );
3938
3939 let tx = &psbt.unsigned_tx;
3940 assert_eq!(tx.input.len(), 2);
3941 assert_eq!(tx.output.len(), 2);
3942 assert_eq!(
3943 tx.output
3944 .iter()
3945 .find(|txout| txout.script_pubkey == addr.script_pubkey())
3946 .unwrap()
3947 .value,
3948 original_send_all_amount
3949 );
3950 assert_eq!(
3951 tx.output
3952 .iter()
3953 .find(|txout| txout.script_pubkey != addr.script_pubkey())
3954 .unwrap()
3955 .value,
3956 75_000 - original_send_all_amount - details.fee.unwrap_or(0)
3957 );
3958
3959 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
3960 }
3961
3962 #[test]
3963 fn test_bump_fee_add_input_change_dust() {
3964 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3965 crate::populate_test_db!(
3966 wallet.database.borrow_mut(),
3967 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3968 Some(100),
3969 );
3970
3971 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3972 let mut builder = wallet.build_tx();
3973 builder
3974 .add_recipient(addr.script_pubkey(), 45_000)
3975 .enable_rbf();
3976 let (psbt, mut original_details) = builder.finish().unwrap();
3977 let mut tx = psbt.extract_tx();
3978 assert_eq!(tx.input.len(), 1);
3979 assert_eq!(tx.output.len(), 2);
3980 let txid = tx.txid();
3981 for txin in &mut tx.input {
3983 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
3985 .database
3986 .borrow_mut()
3987 .del_utxo(&txin.previous_output)
3988 .unwrap();
3989 }
3990 let original_tx_weight = tx.weight();
3991 original_details.transaction = Some(tx);
3992 wallet
3993 .database
3994 .borrow_mut()
3995 .set_tx(&original_details)
3996 .unwrap();
3997
3998 let mut builder = wallet.build_fee_bump(txid).unwrap();
3999 let new_tx_weight = original_tx_weight + 160 + 112 - 124;
4009 let fee_abs = 50_000 + 25_000 - 45_000 - 10;
4012 builder.fee_rate(FeeRate::from_wu(fee_abs, new_tx_weight));
4013 let (psbt, details) = builder.finish().unwrap();
4014
4015 assert_eq!(
4016 original_details.received,
4017 5_000 - original_details.fee.unwrap_or(0)
4018 );
4019
4020 assert_eq!(details.sent, original_details.sent + 25_000);
4021 assert_eq!(details.fee.unwrap_or(0), 30_000);
4022 assert_eq!(details.received, 0);
4023
4024 let tx = &psbt.unsigned_tx;
4025 assert_eq!(tx.input.len(), 2);
4026 assert_eq!(tx.output.len(), 1);
4027 assert_eq!(
4028 tx.output
4029 .iter()
4030 .find(|txout| txout.script_pubkey == addr.script_pubkey())
4031 .unwrap()
4032 .value,
4033 45_000
4034 );
4035
4036 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature);
4037 }
4038
4039 #[test]
4040 fn test_bump_fee_force_add_input() {
4041 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
4042 let incoming_txid = crate::populate_test_db!(
4043 wallet.database.borrow_mut(),
4044 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
4045 Some(100),
4046 );
4047
4048 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4049 let mut builder = wallet.build_tx();
4050 builder
4051 .add_recipient(addr.script_pubkey(), 45_000)
4052 .enable_rbf();
4053 let (psbt, mut original_details) = builder.finish().unwrap();
4054 let mut tx = psbt.extract_tx();
4055 let txid = tx.txid();
4056 for txin in &mut tx.input {
4058 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
4060 .database
4061 .borrow_mut()
4062 .del_utxo(&txin.previous_output)
4063 .unwrap();
4064 }
4065 original_details.transaction = Some(tx);
4066 wallet
4067 .database
4068 .borrow_mut()
4069 .set_tx(&original_details)
4070 .unwrap();
4071
4072 let mut builder = wallet.build_fee_bump(txid).unwrap();
4075 builder
4076 .add_utxo(OutPoint {
4077 txid: incoming_txid,
4078 vout: 0,
4079 })
4080 .unwrap()
4081 .fee_rate(FeeRate::from_sat_per_vb(5.0));
4082 let (psbt, details) = builder.finish().unwrap();
4083
4084 assert_eq!(details.sent, original_details.sent + 25_000);
4085 assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
4086
4087 let tx = &psbt.unsigned_tx;
4088 assert_eq!(tx.input.len(), 2);
4089 assert_eq!(tx.output.len(), 2);
4090 assert_eq!(
4091 tx.output
4092 .iter()
4093 .find(|txout| txout.script_pubkey == addr.script_pubkey())
4094 .unwrap()
4095 .value,
4096 45_000
4097 );
4098 assert_eq!(
4099 tx.output
4100 .iter()
4101 .find(|txout| txout.script_pubkey != addr.script_pubkey())
4102 .unwrap()
4103 .value,
4104 details.received
4105 );
4106
4107 assert_fee_rate!(psbt, details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
4108 }
4109
4110 #[test]
4111 fn test_bump_fee_absolute_force_add_input() {
4112 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
4113 let incoming_txid = crate::populate_test_db!(
4114 wallet.database.borrow_mut(),
4115 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
4116 Some(100),
4117 );
4118
4119 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4120 let mut builder = wallet.build_tx();
4121 builder
4122 .add_recipient(addr.script_pubkey(), 45_000)
4123 .enable_rbf();
4124 let (psbt, mut original_details) = builder.finish().unwrap();
4125 let mut tx = psbt.extract_tx();
4126 let txid = tx.txid();
4127 for txin in &mut tx.input {
4129 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
4131 .database
4132 .borrow_mut()
4133 .del_utxo(&txin.previous_output)
4134 .unwrap();
4135 }
4136 original_details.transaction = Some(tx);
4137 wallet
4138 .database
4139 .borrow_mut()
4140 .set_tx(&original_details)
4141 .unwrap();
4142
4143 let mut builder = wallet.build_fee_bump(txid).unwrap();
4146 builder
4147 .add_utxo(OutPoint {
4148 txid: incoming_txid,
4149 vout: 0,
4150 })
4151 .unwrap()
4152 .fee_absolute(250);
4153 let (psbt, details) = builder.finish().unwrap();
4154
4155 assert_eq!(details.sent, original_details.sent + 25_000);
4156 assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
4157
4158 let tx = &psbt.unsigned_tx;
4159 assert_eq!(tx.input.len(), 2);
4160 assert_eq!(tx.output.len(), 2);
4161 assert_eq!(
4162 tx.output
4163 .iter()
4164 .find(|txout| txout.script_pubkey == addr.script_pubkey())
4165 .unwrap()
4166 .value,
4167 45_000
4168 );
4169 assert_eq!(
4170 tx.output
4171 .iter()
4172 .find(|txout| txout.script_pubkey != addr.script_pubkey())
4173 .unwrap()
4174 .value,
4175 details.received
4176 );
4177
4178 assert_eq!(details.fee.unwrap_or(0), 250);
4179 }
4180
4181 #[test]
4182 #[should_panic(expected = "InsufficientFunds")]
4183 fn test_bump_fee_unconfirmed_inputs_only() {
4184 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
4191 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4192 let mut builder = wallet.build_tx();
4193 builder
4194 .drain_wallet()
4195 .drain_to(addr.script_pubkey())
4196 .enable_rbf();
4197 let (psbt, mut original_details) = builder.finish().unwrap();
4198 crate::populate_test_db!(
4201 wallet.database.borrow_mut(),
4202 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 0)),
4203 Some(100),
4204 );
4205 let mut tx = psbt.extract_tx();
4206 let txid = tx.txid();
4207 for txin in &mut tx.input {
4208 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
4210 .database
4211 .borrow_mut()
4212 .del_utxo(&txin.previous_output)
4213 .unwrap();
4214 }
4215 original_details.transaction = Some(tx);
4216 wallet
4217 .database
4218 .borrow_mut()
4219 .set_tx(&original_details)
4220 .unwrap();
4221
4222 let mut builder = wallet.build_fee_bump(txid).unwrap();
4223 builder.fee_rate(FeeRate::from_sat_per_vb(25.0));
4224 builder.finish().unwrap();
4225 }
4226
4227 #[test]
4228 fn test_bump_fee_unconfirmed_input() {
4229 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
4235 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4236 crate::populate_test_db!(
4239 wallet.database.borrow_mut(),
4240 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 0)),
4241 Some(100),
4242 );
4243 let mut builder = wallet.build_tx();
4244 builder
4245 .drain_wallet()
4246 .drain_to(addr.script_pubkey())
4247 .enable_rbf();
4248 let (psbt, mut original_details) = builder.finish().unwrap();
4249 let mut tx = psbt.extract_tx();
4250 let txid = tx.txid();
4251 for txin in &mut tx.input {
4252 txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); wallet
4254 .database
4255 .borrow_mut()
4256 .del_utxo(&txin.previous_output)
4257 .unwrap();
4258 }
4259 original_details.transaction = Some(tx);
4260 wallet
4261 .database
4262 .borrow_mut()
4263 .set_tx(&original_details)
4264 .unwrap();
4265
4266 let mut builder = wallet.build_fee_bump(txid).unwrap();
4267 builder
4268 .fee_rate(FeeRate::from_sat_per_vb(15.0))
4269 .allow_shrinking(addr.script_pubkey())
4270 .unwrap();
4271 builder.finish().unwrap();
4272 }
4273
4274 #[test]
4275 fn test_fee_amount_negative_drain_val() {
4276 let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
4285 let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap();
4286 let fee_rate = FeeRate::from_sat_per_vb(2.01);
4287 let incoming_txid = crate::populate_test_db!(
4288 wallet.database.borrow_mut(),
4289 testutils! (@tx ( (@external descriptors, 0) => 8859 ) (@confirmations 1)),
4290 Some(100),
4291 );
4292
4293 let mut builder = wallet.build_tx();
4294 builder
4295 .add_recipient(send_to.script_pubkey(), 8630)
4296 .add_utxo(OutPoint::new(incoming_txid, 0))
4297 .unwrap()
4298 .enable_rbf()
4299 .fee_rate(fee_rate);
4300 let (psbt, details) = builder.finish().unwrap();
4301
4302 assert!(psbt.inputs.len() == 1);
4303 assert_fee_rate!(psbt, details.fee.unwrap_or(0), fee_rate, @add_signature);
4304 }
4305
4306 #[test]
4307 fn test_sign_single_xprv() {
4308 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4309 let addr = wallet.get_address(New).unwrap();
4310 let mut builder = wallet.build_tx();
4311 builder.drain_to(addr.script_pubkey()).drain_wallet();
4312 let (mut psbt, _) = builder.finish().unwrap();
4313
4314 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4315 assert!(finalized);
4316
4317 let extracted = psbt.extract_tx();
4318 assert_eq!(extracted.input[0].witness.len(), 2);
4319 }
4320
4321 #[test]
4322 fn test_sign_single_xprv_with_master_fingerprint_and_path() {
4323 let (wallet, _, _) = get_funded_wallet("wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4324 let addr = wallet.get_address(New).unwrap();
4325 let mut builder = wallet.build_tx();
4326 builder.drain_to(addr.script_pubkey()).drain_wallet();
4327 let (mut psbt, _) = builder.finish().unwrap();
4328
4329 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4330 assert!(finalized);
4331
4332 let extracted = psbt.extract_tx();
4333 assert_eq!(extracted.input[0].witness.len(), 2);
4334 }
4335
4336 #[test]
4337 fn test_sign_single_xprv_bip44_path() {
4338 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)");
4339 let addr = wallet.get_address(New).unwrap();
4340 let mut builder = wallet.build_tx();
4341 builder.drain_to(addr.script_pubkey()).drain_wallet();
4342 let (mut psbt, _) = builder.finish().unwrap();
4343
4344 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4345 assert!(finalized);
4346
4347 let extracted = psbt.extract_tx();
4348 assert_eq!(extracted.input[0].witness.len(), 2);
4349 }
4350
4351 #[test]
4352 fn test_sign_single_xprv_sh_wpkh() {
4353 let (wallet, _, _) = get_funded_wallet("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))");
4354 let addr = wallet.get_address(New).unwrap();
4355 let mut builder = wallet.build_tx();
4356 builder.drain_to(addr.script_pubkey()).drain_wallet();
4357 let (mut psbt, _) = builder.finish().unwrap();
4358
4359 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4360 assert!(finalized);
4361
4362 let extracted = psbt.extract_tx();
4363 assert_eq!(extracted.input[0].witness.len(), 2);
4364 }
4365
4366 #[test]
4367 fn test_sign_single_wif() {
4368 let (wallet, _, _) =
4369 get_funded_wallet("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)");
4370 let addr = wallet.get_address(New).unwrap();
4371 let mut builder = wallet.build_tx();
4372 builder.drain_to(addr.script_pubkey()).drain_wallet();
4373 let (mut psbt, _) = builder.finish().unwrap();
4374
4375 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4376 assert!(finalized);
4377
4378 let extracted = psbt.extract_tx();
4379 assert_eq!(extracted.input[0].witness.len(), 2);
4380 }
4381
4382 #[test]
4383 fn test_sign_single_xprv_no_hd_keypaths() {
4384 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4385 let addr = wallet.get_address(New).unwrap();
4386 let mut builder = wallet.build_tx();
4387 builder.drain_to(addr.script_pubkey()).drain_wallet();
4388 let (mut psbt, _) = builder.finish().unwrap();
4389
4390 psbt.inputs[0].bip32_derivation.clear();
4391 assert_eq!(psbt.inputs[0].bip32_derivation.len(), 0);
4392
4393 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4394 assert!(finalized);
4395
4396 let extracted = psbt.extract_tx();
4397 assert_eq!(extracted.input[0].witness.len(), 2);
4398 }
4399
4400 #[test]
4401 fn test_include_output_redeem_witness_script() {
4402 let (wallet, _, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))");
4403 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4404 let mut builder = wallet.build_tx();
4405 builder
4406 .add_recipient(addr.script_pubkey(), 45_000)
4407 .include_output_redeem_witness_script();
4408 let (psbt, _) = builder.finish().unwrap();
4409
4410 assert!(psbt
4412 .outputs
4413 .iter()
4414 .any(|output| output.redeem_script.is_some() && output.witness_script.is_some()));
4415 }
4416
4417 #[test]
4418 fn test_signing_only_one_of_multiple_inputs() {
4419 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
4420 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
4421 let mut builder = wallet.build_tx();
4422 builder
4423 .add_recipient(addr.script_pubkey(), 45_000)
4424 .include_output_redeem_witness_script();
4425 let (mut psbt, _) = builder.finish().unwrap();
4426
4427 let dud_input = bitcoin::util::psbt::Input {
4429 witness_utxo: Some(TxOut {
4430 value: 100_000,
4431 script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
4432 "wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
4433 )
4434 .unwrap()
4435 .script_pubkey(),
4436 }),
4437 ..Default::default()
4438 };
4439
4440 psbt.inputs.push(dud_input);
4441 psbt.unsigned_tx.input.push(bitcoin::TxIn::default());
4442 let is_final = wallet
4443 .sign(
4444 &mut psbt,
4445 SignOptions {
4446 trust_witness_utxo: true,
4447 ..Default::default()
4448 },
4449 )
4450 .unwrap();
4451 assert!(
4452 !is_final,
4453 "shouldn't be final since we can't sign one of the inputs"
4454 );
4455 assert!(
4456 psbt.inputs[0].final_script_witness.is_some(),
4457 "should finalized input it signed"
4458 )
4459 }
4460
4461 #[test]
4462 fn test_remove_partial_sigs_after_finalize_sign_option() {
4463 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4464
4465 for remove_partial_sigs in &[true, false] {
4466 let addr = wallet.get_address(New).unwrap();
4467 let mut builder = wallet.build_tx();
4468 builder.drain_to(addr.script_pubkey()).drain_wallet();
4469 let mut psbt = builder.finish().unwrap().0;
4470
4471 assert!(wallet
4472 .sign(
4473 &mut psbt,
4474 SignOptions {
4475 remove_partial_sigs: *remove_partial_sigs,
4476 ..Default::default()
4477 },
4478 )
4479 .unwrap());
4480
4481 psbt.inputs.iter().for_each(|input| {
4482 if *remove_partial_sigs {
4483 assert!(input.partial_sigs.is_empty())
4484 } else {
4485 assert!(!input.partial_sigs.is_empty())
4486 }
4487 });
4488 }
4489 }
4490
4491 #[test]
4492 fn test_try_finalize_sign_option() {
4493 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4494
4495 for try_finalize in &[true, false] {
4496 let addr = wallet.get_address(New).unwrap();
4497 let mut builder = wallet.build_tx();
4498 builder.drain_to(addr.script_pubkey()).drain_wallet();
4499 let mut psbt = builder.finish().unwrap().0;
4500
4501 let finalized = wallet
4502 .sign(
4503 &mut psbt,
4504 SignOptions {
4505 try_finalize: *try_finalize,
4506 ..Default::default()
4507 },
4508 )
4509 .unwrap();
4510
4511 psbt.inputs.iter().for_each(|input| {
4512 if *try_finalize {
4513 assert!(finalized);
4514 assert!(input.final_script_sig.is_some());
4515 assert!(input.final_script_witness.is_some());
4516 } else {
4517 assert!(!finalized);
4518 assert!(input.final_script_sig.is_none());
4519 assert!(input.final_script_witness.is_none());
4520 }
4521 });
4522 }
4523 }
4524
4525 #[test]
4526 fn test_sign_nonstandard_sighash() {
4527 let sighash = EcdsaSighashType::NonePlusAnyoneCanPay;
4528
4529 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
4530 let addr = wallet.get_address(New).unwrap();
4531 let mut builder = wallet.build_tx();
4532 builder
4533 .drain_to(addr.script_pubkey())
4534 .sighash(sighash.into())
4535 .drain_wallet();
4536 let (mut psbt, _) = builder.finish().unwrap();
4537
4538 let result = wallet.sign(&mut psbt, Default::default());
4539 assert!(
4540 result.is_err(),
4541 "Signing should have failed because the TX uses non-standard sighashes"
4542 );
4543 assert_matches!(
4544 result,
4545 Err(Error::Signer(SignerError::NonStandardSighash)),
4546 "Signing failed with the wrong error type"
4547 );
4548
4549 let result = wallet.sign(
4551 &mut psbt,
4552 SignOptions {
4553 allow_all_sighashes: true,
4554 ..Default::default()
4555 },
4556 );
4557 assert!(result.is_ok(), "Signing should have worked");
4558 assert!(
4559 result.unwrap(),
4560 "Should finalize the input since we can produce signatures"
4561 );
4562
4563 let extracted = psbt.extract_tx();
4564 assert_eq!(
4565 *extracted.input[0].witness.to_vec()[0].last().unwrap(),
4566 sighash.to_u32() as u8,
4567 "The signature should have been made with the right sighash"
4568 );
4569 }
4570
4571 #[test]
4572 fn test_unused_address() {
4573 let db = MemoryDatabase::new();
4574 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
4575 None, Network::Testnet, db).unwrap();
4576
4577 assert_eq!(
4578 wallet.get_address(LastUnused).unwrap().to_string(),
4579 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4580 );
4581 assert_eq!(
4582 wallet.get_address(LastUnused).unwrap().to_string(),
4583 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4584 );
4585 }
4586
4587 #[test]
4588 fn test_next_unused_address() {
4589 let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
4590 let descriptors = testutils!(@descriptors (descriptor));
4591 let wallet = Wallet::new(
4592 &descriptors.0,
4593 None,
4594 Network::Testnet,
4595 MemoryDatabase::new(),
4596 )
4597 .unwrap();
4598
4599 assert_eq!(
4600 wallet.get_address(LastUnused).unwrap().to_string(),
4601 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4602 );
4603
4604 crate::populate_test_db!(
4606 wallet.database.borrow_mut(),
4607 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
4608 Some(100),
4609 );
4610
4611 assert_eq!(
4612 wallet.get_address(LastUnused).unwrap().to_string(),
4613 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4614 );
4615 }
4616
4617 #[test]
4618 fn test_peek_address_at_index() {
4619 let db = MemoryDatabase::new();
4620 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
4621 None, Network::Testnet, db).unwrap();
4622
4623 assert_eq!(
4624 wallet.get_address(Peek(1)).unwrap().to_string(),
4625 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4626 );
4627
4628 assert_eq!(
4629 wallet.get_address(Peek(0)).unwrap().to_string(),
4630 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4631 );
4632
4633 assert_eq!(
4634 wallet.get_address(Peek(2)).unwrap().to_string(),
4635 "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
4636 );
4637
4638 assert_eq!(
4640 wallet.get_address(New).unwrap().to_string(),
4641 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4642 );
4643
4644 assert_eq!(
4645 wallet.get_address(New).unwrap().to_string(),
4646 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4647 );
4648 }
4649
4650 #[test]
4651 fn test_peek_address_at_index_not_derivable() {
4652 let db = MemoryDatabase::new();
4653 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
4654 None, Network::Testnet, db).unwrap();
4655
4656 assert_eq!(
4657 wallet.get_address(Peek(1)).unwrap().to_string(),
4658 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4659 );
4660
4661 assert_eq!(
4662 wallet.get_address(Peek(0)).unwrap().to_string(),
4663 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4664 );
4665
4666 assert_eq!(
4667 wallet.get_address(Peek(2)).unwrap().to_string(),
4668 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4669 );
4670 }
4671
4672 #[test]
4673 fn test_reset_address_index() {
4674 let db = MemoryDatabase::new();
4675 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
4676 None, Network::Testnet, db).unwrap();
4677
4678 assert_eq!(
4680 wallet.get_address(New).unwrap().to_string(),
4681 "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
4682 );
4683
4684 assert_eq!(
4686 wallet.get_address(New).unwrap().to_string(),
4687 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4688 );
4689
4690 assert_eq!(
4692 wallet.get_address(New).unwrap().to_string(),
4693 "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
4694 );
4695
4696 assert_eq!(
4698 wallet.get_address(Reset(1)).unwrap().to_string(),
4699 "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
4700 );
4701
4702 assert_eq!(
4704 wallet.get_address(New).unwrap().to_string(),
4705 "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
4706 );
4707 }
4708
4709 #[test]
4710 fn test_returns_index_and_address() {
4711 let db = MemoryDatabase::new();
4712 let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
4713 None, Network::Testnet, db).unwrap();
4714
4715 assert_eq!(
4717 wallet.get_address(New).unwrap(),
4718 AddressInfo {
4719 index: 0,
4720 address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a").unwrap(),
4721 keychain: KeychainKind::External,
4722 }
4723 );
4724
4725 assert_eq!(
4727 wallet.get_address(New).unwrap(),
4728 AddressInfo {
4729 index: 1,
4730 address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(),
4731 keychain: KeychainKind::External,
4732 }
4733 );
4734
4735 assert_eq!(
4737 wallet.get_address(Peek(25)).unwrap(),
4738 AddressInfo {
4739 index: 25,
4740 address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2").unwrap(),
4741 keychain: KeychainKind::External,
4742 }
4743 );
4744
4745 assert_eq!(
4747 wallet.get_address(New).unwrap(),
4748 AddressInfo {
4749 index: 2,
4750 address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(),
4751 keychain: KeychainKind::External,
4752 }
4753 );
4754
4755 assert_eq!(
4757 wallet.get_address(Reset(1)).unwrap(),
4758 AddressInfo {
4759 index: 1,
4760 address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(),
4761 keychain: KeychainKind::External,
4762 }
4763 );
4764
4765 assert_eq!(
4767 wallet.get_address(New).unwrap(),
4768 AddressInfo {
4769 index: 2,
4770 address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(),
4771 keychain: KeychainKind::External,
4772 }
4773 );
4774 }
4775
4776 #[test]
4777 fn test_sending_to_bip350_bech32m_address() {
4778 let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
4779 let addr =
4780 Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
4781 .unwrap();
4782 let mut builder = wallet.build_tx();
4783 builder.add_recipient(addr.script_pubkey(), 45_000);
4784 builder.finish().unwrap();
4785 }
4786
4787 #[test]
4788 fn test_get_address() {
4789 use crate::descriptor::template::Bip84;
4790 let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
4791 let wallet = Wallet::new(
4792 Bip84(key, KeychainKind::External),
4793 Some(Bip84(key, KeychainKind::Internal)),
4794 Network::Regtest,
4795 MemoryDatabase::default(),
4796 )
4797 .unwrap();
4798
4799 assert_eq!(
4800 wallet.get_address(AddressIndex::New).unwrap(),
4801 AddressInfo {
4802 index: 0,
4803 address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(),
4804 keychain: KeychainKind::External,
4805 }
4806 );
4807
4808 assert_eq!(
4809 wallet.get_internal_address(AddressIndex::New).unwrap(),
4810 AddressInfo {
4811 index: 0,
4812 address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79").unwrap(),
4813 keychain: KeychainKind::Internal,
4814 }
4815 );
4816
4817 let wallet = Wallet::new(
4818 Bip84(key, KeychainKind::External),
4819 None,
4820 Network::Regtest,
4821 MemoryDatabase::default(),
4822 )
4823 .unwrap();
4824
4825 assert_eq!(
4826 wallet.get_internal_address(AddressIndex::New).unwrap(),
4827 AddressInfo {
4828 index: 0,
4829 address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(),
4830 keychain: KeychainKind::Internal,
4831 },
4832 "when there's no internal descriptor it should just use external"
4833 );
4834 }
4835
4836 #[test]
4837 fn test_get_address_no_reuse_single_descriptor() {
4838 use crate::descriptor::template::Bip84;
4839 use std::collections::HashSet;
4840
4841 let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
4842 let wallet = Wallet::new(
4843 Bip84(key, KeychainKind::External),
4844 None,
4845 Network::Regtest,
4846 MemoryDatabase::default(),
4847 )
4848 .unwrap();
4849
4850 let mut used_set = HashSet::new();
4851
4852 (0..3).for_each(|_| {
4853 let external_addr = wallet.get_address(AddressIndex::New).unwrap().address;
4854 assert!(used_set.insert(external_addr));
4855
4856 let internal_addr = wallet
4857 .get_internal_address(AddressIndex::New)
4858 .unwrap()
4859 .address;
4860 assert!(used_set.insert(internal_addr));
4861 });
4862 }
4863
4864 #[test]
4865 fn test_taproot_psbt_populate_tap_key_origins() {
4866 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
4867 let addr = wallet.get_address(AddressIndex::New).unwrap();
4868
4869 let mut builder = wallet.build_tx();
4870 builder.add_recipient(addr.script_pubkey(), 25_000);
4871 let (psbt, _) = builder.finish().unwrap();
4872
4873 assert_eq!(
4874 psbt.inputs[0]
4875 .tap_key_origins
4876 .clone()
4877 .into_iter()
4878 .collect::<Vec<_>>(),
4879 vec![(
4880 from_str!("b96d3a3dc76a4fc74e976511b23aecb78e0754c23c0ed7a6513e18cbbc7178e9"),
4881 (vec![], (from_str!("f6a5cb8b"), from_str!("m/0")))
4882 )],
4883 "Wrong input tap_key_origins"
4884 );
4885 assert_eq!(
4886 psbt.outputs[0]
4887 .tap_key_origins
4888 .clone()
4889 .into_iter()
4890 .collect::<Vec<_>>(),
4891 vec![(
4892 from_str!("e9b03068cf4a2621d4f81e68f6c4216e6bd260fe6edf6acc55c8d8ae5aeff0a8"),
4893 (vec![], (from_str!("f6a5cb8b"), from_str!("m/1")))
4894 )],
4895 "Wrong output tap_key_origins"
4896 );
4897 }
4898
4899 #[test]
4900 fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
4901 let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key());
4902 let addr = wallet.get_address(AddressIndex::New).unwrap();
4903
4904 let path = vec![("e5mmg3xh".to_string(), vec![0])]
4905 .into_iter()
4906 .collect();
4907
4908 let mut builder = wallet.build_tx();
4909 builder
4910 .add_recipient(addr.script_pubkey(), 25_000)
4911 .policy_path(path, KeychainKind::External);
4912 let (psbt, _) = builder.finish().unwrap();
4913
4914 let mut input_key_origins = psbt.inputs[0]
4915 .tap_key_origins
4916 .clone()
4917 .into_iter()
4918 .collect::<Vec<_>>();
4919 input_key_origins.sort();
4920
4921 assert_eq!(
4922 input_key_origins,
4923 vec![
4924 (
4925 from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
4926 (
4927 vec![],
4928 (FromStr::from_str("871fd295").unwrap(), vec![].into())
4929 )
4930 ),
4931 (
4932 from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
4933 (
4934 vec![
4935 from_str!(
4936 "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
4937 ),
4938 from_str!(
4939 "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
4940 ),
4941 ],
4942 (FromStr::from_str("ece52657").unwrap(), vec![].into())
4943 )
4944 )
4945 ],
4946 "Wrong input tap_key_origins"
4947 );
4948
4949 let mut output_key_origins = psbt.outputs[0]
4950 .tap_key_origins
4951 .clone()
4952 .into_iter()
4953 .collect::<Vec<_>>();
4954 output_key_origins.sort();
4955
4956 assert_eq!(
4957 input_key_origins, output_key_origins,
4958 "Wrong output tap_key_origins"
4959 );
4960 }
4961
4962 #[test]
4963 fn test_taproot_psbt_input_tap_tree() {
4964 use crate::bitcoin::psbt::serialize::Deserialize;
4965 use crate::bitcoin::psbt::TapTree;
4966 use bitcoin::hashes::hex::FromHex;
4967 use bitcoin::util::taproot;
4968
4969 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree());
4970 let addr = wallet.get_address(AddressIndex::Peek(0)).unwrap();
4971
4972 let mut builder = wallet.build_tx();
4973 builder.drain_to(addr.script_pubkey()).drain_wallet();
4974 let (psbt, _) = builder.finish().unwrap();
4975
4976 assert_eq!(
4977 psbt.inputs[0].tap_merkle_root,
4978 Some(
4979 FromHex::from_hex(
4980 "61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
4981 )
4982 .unwrap()
4983 ),
4984 );
4985 assert_eq!(
4986 psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
4987 vec![
4988 (taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (from_str!("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac"), taproot::LeafVersion::TapScript)),
4989 (taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (from_str!("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac"), taproot::LeafVersion::TapScript)),
4990 ],
4991 );
4992 assert_eq!(
4993 psbt.inputs[0].tap_internal_key,
4994 Some(from_str!(
4995 "b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"
4996 ))
4997 );
4998
4999 assert_eq!(
5002 psbt.inputs[0].tap_internal_key,
5003 psbt.outputs[0].tap_internal_key
5004 );
5005
5006 assert_eq!(
5007 psbt.outputs[0].tap_tree,
5008 Some(TapTree::deserialize(&Vec::<u8>::from_hex("01c022208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac01c0222051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",).unwrap()).unwrap())
5009 );
5010 }
5011
5012 #[test]
5013 fn test_taproot_sign_missing_witness_utxo() {
5014 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig());
5015 let addr = wallet.get_address(New).unwrap();
5016 let mut builder = wallet.build_tx();
5017 builder.drain_to(addr.script_pubkey()).drain_wallet();
5018 let (mut psbt, _) = builder.finish().unwrap();
5019 let witness_utxo = psbt.inputs[0].witness_utxo.take();
5020
5021 let result = wallet.sign(
5022 &mut psbt,
5023 SignOptions {
5024 allow_all_sighashes: true,
5025 ..Default::default()
5026 },
5027 );
5028 assert_matches!(
5029 result,
5030 Err(Error::Signer(SignerError::MissingWitnessUtxo)),
5031 "Signing should have failed with the correct error because the witness_utxo is missing"
5032 );
5033
5034 psbt.inputs[0].witness_utxo = witness_utxo;
5036
5037 let result = wallet.sign(
5038 &mut psbt,
5039 SignOptions {
5040 allow_all_sighashes: true,
5041 ..Default::default()
5042 },
5043 );
5044
5045 assert_matches!(
5046 result,
5047 Ok(true),
5048 "Should finalize the input since we can produce signatures"
5049 );
5050 }
5051
5052 #[test]
5053 fn test_taproot_sign_using_non_witness_utxo() {
5054 let (wallet, _, prev_txid) = get_funded_wallet(get_test_tr_single_sig());
5055 let addr = wallet.get_address(New).unwrap();
5056 let mut builder = wallet.build_tx();
5057 builder.drain_to(addr.script_pubkey()).drain_wallet();
5058 let (mut psbt, _) = builder.finish().unwrap();
5059
5060 psbt.inputs[0].witness_utxo = None;
5061 psbt.inputs[0].non_witness_utxo = wallet.database().get_raw_tx(&prev_txid).unwrap();
5062 assert!(
5063 psbt.inputs[0].non_witness_utxo.is_some(),
5064 "Previous tx should be present in the database"
5065 );
5066
5067 let result = wallet.sign(&mut psbt, Default::default());
5068 assert!(result.is_ok(), "Signing should have worked");
5069 assert!(
5070 result.unwrap(),
5071 "Should finalize the input since we can produce signatures"
5072 );
5073 }
5074
5075 #[test]
5076 fn test_taproot_foreign_utxo() {
5077 let (wallet1, _, _) = get_funded_wallet(get_test_wpkh());
5078 let (wallet2, _, _) = get_funded_wallet(get_test_tr_single_sig());
5079
5080 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
5081 let utxo = wallet2.list_unspent().unwrap().remove(0);
5082 let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
5083 let foreign_utxo_satisfaction = wallet2
5084 .get_descriptor_for_keychain(KeychainKind::External)
5085 .max_satisfaction_weight()
5086 .unwrap();
5087
5088 assert!(
5089 psbt_input.non_witness_utxo.is_none(),
5090 "`non_witness_utxo` should never be populated for taproot"
5091 );
5092
5093 let mut builder = wallet1.build_tx();
5094 builder
5095 .add_recipient(addr.script_pubkey(), 60_000)
5096 .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
5097 .unwrap();
5098 let (psbt, details) = builder.finish().unwrap();
5099
5100 assert_eq!(
5101 details.sent - details.received,
5102 10_000 + details.fee.unwrap_or(0),
5103 "we should have only net spent ~10_000"
5104 );
5105
5106 assert!(
5107 psbt.unsigned_tx
5108 .input
5109 .iter()
5110 .any(|input| input.previous_output == utxo.outpoint),
5111 "foreign_utxo should be in there"
5112 );
5113 }
5114
5115 fn test_spend_from_wallet(wallet: Wallet<AnyDatabase>) {
5116 let addr = wallet.get_address(AddressIndex::New).unwrap();
5117
5118 let mut builder = wallet.build_tx();
5119 builder.add_recipient(addr.script_pubkey(), 25_000);
5120 let (mut psbt, _) = builder.finish().unwrap();
5121
5122 assert!(
5123 wallet.sign(&mut psbt, Default::default()).unwrap(),
5124 "Unable to finalize tx"
5125 );
5126 }
5127
5128 #[test]
5129 fn test_taproot_key_spend() {
5130 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig());
5131 test_spend_from_wallet(wallet);
5132
5133 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
5134 test_spend_from_wallet(wallet);
5135 }
5136
5137 #[test]
5138 fn test_taproot_no_key_spend() {
5139 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
5140 let addr = wallet.get_address(AddressIndex::New).unwrap();
5141
5142 let mut builder = wallet.build_tx();
5143 builder.add_recipient(addr.script_pubkey(), 25_000);
5144 let (mut psbt, _) = builder.finish().unwrap();
5145
5146 assert!(
5147 wallet
5148 .sign(
5149 &mut psbt,
5150 SignOptions {
5151 sign_with_tap_internal_key: false,
5152 ..Default::default()
5153 },
5154 )
5155 .unwrap(),
5156 "Unable to finalize tx"
5157 );
5158
5159 assert!(psbt.inputs.iter().all(|i| i.tap_key_sig.is_none()));
5160 }
5161
5162 #[test]
5163 fn test_taproot_script_spend() {
5164 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree());
5165 test_spend_from_wallet(wallet);
5166
5167 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_xprv());
5168 test_spend_from_wallet(wallet);
5169 }
5170
5171 #[test]
5172 fn test_taproot_script_spend_sign_all_leaves() {
5173 use crate::signer::TapLeavesOptions;
5174 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
5175 let addr = wallet.get_address(AddressIndex::New).unwrap();
5176
5177 let mut builder = wallet.build_tx();
5178 builder.add_recipient(addr.script_pubkey(), 25_000);
5179 let (mut psbt, _) = builder.finish().unwrap();
5180
5181 assert!(
5182 wallet
5183 .sign(
5184 &mut psbt,
5185 SignOptions {
5186 tap_leaves_options: TapLeavesOptions::All,
5187 ..Default::default()
5188 },
5189 )
5190 .unwrap(),
5191 "Unable to finalize tx"
5192 );
5193
5194 assert!(psbt
5195 .inputs
5196 .iter()
5197 .all(|i| i.tap_script_sigs.len() == i.tap_scripts.len()));
5198 }
5199
5200 #[test]
5201 fn test_taproot_script_spend_sign_include_some_leaves() {
5202 use crate::signer::TapLeavesOptions;
5203 use bitcoin::util::taproot::TapLeafHash;
5204
5205 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
5206 let addr = wallet.get_address(AddressIndex::New).unwrap();
5207
5208 let mut builder = wallet.build_tx();
5209 builder.add_recipient(addr.script_pubkey(), 25_000);
5210 let (mut psbt, _) = builder.finish().unwrap();
5211 let mut script_leaves: Vec<_> = psbt.inputs[0]
5212 .tap_scripts
5213 .clone()
5214 .values()
5215 .map(|(script, version)| TapLeafHash::from_script(script, *version))
5216 .collect();
5217 let included_script_leaves = vec![script_leaves.pop().unwrap()];
5218 let excluded_script_leaves = script_leaves;
5219
5220 assert!(
5221 wallet
5222 .sign(
5223 &mut psbt,
5224 SignOptions {
5225 tap_leaves_options: TapLeavesOptions::Include(
5226 included_script_leaves.clone()
5227 ),
5228 ..Default::default()
5229 },
5230 )
5231 .unwrap(),
5232 "Unable to finalize tx"
5233 );
5234
5235 assert!(psbt.inputs[0]
5236 .tap_script_sigs
5237 .iter()
5238 .all(|s| included_script_leaves.contains(&s.0 .1)
5239 && !excluded_script_leaves.contains(&s.0 .1)));
5240 }
5241
5242 #[test]
5243 fn test_taproot_script_spend_sign_exclude_some_leaves() {
5244 use crate::signer::TapLeavesOptions;
5245 use bitcoin::util::taproot::TapLeafHash;
5246
5247 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
5248 let addr = wallet.get_address(AddressIndex::New).unwrap();
5249
5250 let mut builder = wallet.build_tx();
5251 builder.add_recipient(addr.script_pubkey(), 25_000);
5252 let (mut psbt, _) = builder.finish().unwrap();
5253 let mut script_leaves: Vec<_> = psbt.inputs[0]
5254 .tap_scripts
5255 .clone()
5256 .values()
5257 .map(|(script, version)| TapLeafHash::from_script(script, *version))
5258 .collect();
5259 let included_script_leaves = vec![script_leaves.pop().unwrap()];
5260 let excluded_script_leaves = script_leaves;
5261
5262 assert!(
5263 wallet
5264 .sign(
5265 &mut psbt,
5266 SignOptions {
5267 tap_leaves_options: TapLeavesOptions::Exclude(
5268 excluded_script_leaves.clone()
5269 ),
5270 ..Default::default()
5271 },
5272 )
5273 .unwrap(),
5274 "Unable to finalize tx"
5275 );
5276
5277 assert!(psbt.inputs[0]
5278 .tap_script_sigs
5279 .iter()
5280 .all(|s| included_script_leaves.contains(&s.0 .1)
5281 && !excluded_script_leaves.contains(&s.0 .1)));
5282 }
5283
5284 #[test]
5285 fn test_taproot_script_spend_sign_no_leaves() {
5286 use crate::signer::TapLeavesOptions;
5287 let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
5288 let addr = wallet.get_address(AddressIndex::New).unwrap();
5289
5290 let mut builder = wallet.build_tx();
5291 builder.add_recipient(addr.script_pubkey(), 25_000);
5292 let (mut psbt, _) = builder.finish().unwrap();
5293
5294 wallet
5295 .sign(
5296 &mut psbt,
5297 SignOptions {
5298 tap_leaves_options: TapLeavesOptions::None,
5299 ..Default::default()
5300 },
5301 )
5302 .unwrap();
5303
5304 assert!(psbt.inputs.iter().all(|i| i.tap_script_sigs.is_empty()));
5305 }
5306
5307 #[test]
5308 fn test_taproot_sign_derive_index_from_psbt() {
5309 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
5310
5311 let addr = wallet.get_address(AddressIndex::New).unwrap();
5312
5313 let mut builder = wallet.build_tx();
5314 builder.add_recipient(addr.script_pubkey(), 25_000);
5315 let (mut psbt, _) = builder.finish().unwrap();
5316
5317 let wallet_empty = Wallet::new(
5319 get_test_tr_single_sig_xprv(),
5320 None,
5321 Network::Regtest,
5322 AnyDatabase::Memory(MemoryDatabase::new()),
5323 )
5324 .unwrap();
5325
5326 assert!(
5329 wallet_empty.sign(&mut psbt, Default::default()).unwrap(),
5330 "Unable to finalize tx"
5331 );
5332 }
5333
5334 #[test]
5335 fn test_taproot_sign_explicit_sighash_all() {
5336 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig());
5337 let addr = wallet.get_address(New).unwrap();
5338 let mut builder = wallet.build_tx();
5339 builder
5340 .drain_to(addr.script_pubkey())
5341 .sighash(SchnorrSighashType::All.into())
5342 .drain_wallet();
5343 let (mut psbt, _) = builder.finish().unwrap();
5344
5345 let result = wallet.sign(&mut psbt, Default::default());
5346 assert!(
5347 result.is_ok(),
5348 "Signing should work because SIGHASH_ALL is safe"
5349 )
5350 }
5351
5352 #[test]
5353 fn test_taproot_sign_non_default_sighash() {
5354 let sighash = SchnorrSighashType::NonePlusAnyoneCanPay;
5355
5356 let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig());
5357 let addr = wallet.get_address(New).unwrap();
5358 let mut builder = wallet.build_tx();
5359 builder
5360 .drain_to(addr.script_pubkey())
5361 .sighash(sighash.into())
5362 .drain_wallet();
5363 let (mut psbt, _) = builder.finish().unwrap();
5364
5365 let witness_utxo = psbt.inputs[0].witness_utxo.take();
5366
5367 let result = wallet.sign(&mut psbt, Default::default());
5368 assert!(
5369 result.is_err(),
5370 "Signing should have failed because the TX uses non-standard sighashes"
5371 );
5372 assert_matches!(
5373 result,
5374 Err(Error::Signer(SignerError::NonStandardSighash)),
5375 "Signing failed with the wrong error type"
5376 );
5377
5378 let result = wallet.sign(
5380 &mut psbt,
5381 SignOptions {
5382 allow_all_sighashes: true,
5383 ..Default::default()
5384 },
5385 );
5386 assert!(
5387 result.is_err(),
5388 "Signing should have failed because the witness_utxo is missing"
5389 );
5390 assert_matches!(
5391 result,
5392 Err(Error::Signer(SignerError::MissingWitnessUtxo)),
5393 "Signing failed with the wrong error type"
5394 );
5395
5396 psbt.inputs[0].witness_utxo = witness_utxo;
5398
5399 let result = wallet.sign(
5400 &mut psbt,
5401 SignOptions {
5402 allow_all_sighashes: true,
5403 ..Default::default()
5404 },
5405 );
5406
5407 assert!(result.is_ok(), "Signing should have worked");
5408 assert!(
5409 result.unwrap(),
5410 "Should finalize the input since we can produce signatures"
5411 );
5412
5413 let extracted = psbt.extract_tx();
5414 assert_eq!(
5415 *extracted.input[0].witness.to_vec()[0].last().unwrap(),
5416 sighash as u8,
5417 "The signature should have been made with the right sighash"
5418 );
5419 }
5420
5421 #[test]
5422 fn test_spend_coinbase() {
5423 let descriptors = testutils!(@descriptors (get_test_wpkh()));
5424 let wallet = Wallet::new(
5425 &descriptors.0,
5426 None,
5427 Network::Regtest,
5428 AnyDatabase::Memory(MemoryDatabase::new()),
5429 )
5430 .unwrap();
5431
5432 let confirmation_time = 5;
5433
5434 crate::populate_test_db!(
5435 wallet.database.borrow_mut(),
5436 testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
5437 Some(confirmation_time),
5438 (@coinbase true)
5439 );
5440 let sync_time = SyncTime {
5441 block_time: BlockTime {
5442 height: confirmation_time,
5443 timestamp: 0,
5444 },
5445 };
5446 wallet
5447 .database
5448 .borrow_mut()
5449 .set_sync_time(sync_time)
5450 .unwrap();
5451
5452 let not_yet_mature_time = confirmation_time + COINBASE_MATURITY - 1;
5453 let maturity_time = confirmation_time + COINBASE_MATURITY;
5454
5455 let balance = wallet.get_balance().unwrap();
5456 assert_eq!(
5457 balance,
5458 Balance {
5459 immature: 25_000,
5460 trusted_pending: 0,
5461 untrusted_pending: 0,
5462 confirmed: 0
5463 }
5464 );
5465
5466 let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
5469 let mut builder = wallet.build_tx();
5470 builder
5471 .add_recipient(addr.script_pubkey(), balance.immature / 2)
5472 .current_height(confirmation_time);
5473 assert_matches!(
5474 builder.finish(),
5475 Err(Error::InsufficientFunds {
5476 needed: _,
5477 available: 0
5478 })
5479 );
5480
5481 let mut builder = wallet.build_tx();
5483 builder
5484 .add_recipient(addr.script_pubkey(), balance.immature / 2)
5485 .current_height(not_yet_mature_time);
5486 assert_matches!(
5487 builder.finish(),
5488 Err(Error::InsufficientFunds {
5489 needed: _,
5490 available: 0
5491 })
5492 );
5493
5494 let sync_time = SyncTime {
5496 block_time: BlockTime {
5497 height: maturity_time,
5498 timestamp: 0,
5499 },
5500 };
5501 wallet
5502 .database
5503 .borrow_mut()
5504 .set_sync_time(sync_time)
5505 .unwrap();
5506
5507 let balance = wallet.get_balance().unwrap();
5508 assert_eq!(
5509 balance,
5510 Balance {
5511 immature: 0,
5512 trusted_pending: 0,
5513 untrusted_pending: 0,
5514 confirmed: 25_000
5515 }
5516 );
5517 let mut builder = wallet.build_tx();
5518 builder
5519 .add_recipient(addr.script_pubkey(), balance.confirmed / 2)
5520 .current_height(maturity_time);
5521 builder.finish().unwrap();
5522 }
5523
5524 #[test]
5525 fn test_allow_dust_limit() {
5526 let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
5527
5528 let addr = wallet.get_address(New).unwrap();
5529
5530 let mut builder = wallet.build_tx();
5531
5532 builder.add_recipient(addr.script_pubkey(), 0);
5533
5534 assert_matches!(builder.finish(), Err(Error::OutputBelowDustLimit(0)));
5535
5536 let mut builder = wallet.build_tx();
5537
5538 builder
5539 .allow_dust(true)
5540 .add_recipient(addr.script_pubkey(), 0);
5541
5542 assert!(builder.finish().is_ok());
5543 }
5544
5545 #[test]
5546 fn test_fee_rate_sign_no_grinding_high_r() {
5547 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
5551 let addr = wallet.get_address(New).unwrap();
5552 let fee_rate = FeeRate::from_sat_per_vb(1.0);
5553 let mut builder = wallet.build_tx();
5554 let mut data = vec![0];
5555 builder
5556 .drain_to(addr.script_pubkey())
5557 .drain_wallet()
5558 .fee_rate(fee_rate)
5559 .add_data(&data);
5560 let (mut psbt, details) = builder.finish().unwrap();
5561 let (op_return_vout, _) = psbt
5562 .unsigned_tx
5563 .output
5564 .iter()
5565 .enumerate()
5566 .find(|(_n, i)| i.script_pubkey.is_op_return())
5567 .unwrap();
5568
5569 let mut sig_len: usize = 0;
5570 while sig_len < 71 {
5572 data[0] += 1;
5575 psbt.unsigned_tx.output[op_return_vout].script_pubkey = Script::new_op_return(&data);
5576 psbt.inputs[0].partial_sigs.clear();
5578 wallet
5580 .sign(
5581 &mut psbt,
5582 SignOptions {
5583 remove_partial_sigs: false,
5584 try_finalize: false,
5585 allow_grinding: false,
5586 ..Default::default()
5587 },
5588 )
5589 .unwrap();
5590 let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
5592 sig_len = psbt.inputs[0].partial_sigs[key].sig.serialize_der().len();
5593 }
5594 wallet
5596 .sign(
5597 &mut psbt,
5598 SignOptions {
5599 remove_partial_sigs: false,
5600 allow_grinding: false,
5601 ..Default::default()
5602 },
5603 )
5604 .unwrap();
5605 assert_fee_rate!(psbt, details.fee.unwrap_or(0), fee_rate);
5607 }
5608
5609 #[test]
5610 fn test_fee_rate_sign_grinding_low_r() {
5611 let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
5616 let addr = wallet.get_address(New).unwrap();
5617 let fee_rate = FeeRate::from_sat_per_vb(1.0);
5618 let mut builder = wallet.build_tx();
5619 builder
5620 .drain_to(addr.script_pubkey())
5621 .drain_wallet()
5622 .fee_rate(fee_rate);
5623 let (mut psbt, details) = builder.finish().unwrap();
5624
5625 wallet
5626 .sign(
5627 &mut psbt,
5628 SignOptions {
5629 remove_partial_sigs: false,
5630 allow_grinding: true,
5631 ..Default::default()
5632 },
5633 )
5634 .unwrap();
5635
5636 let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
5637 let sig_len = psbt.inputs[0].partial_sigs[key].sig.serialize_der().len();
5638 assert_eq!(sig_len, 70);
5639 assert_fee_rate!(psbt, details.fee.unwrap_or(0), fee_rate);
5640 }
5641
5642 #[cfg(feature = "test-hardware-signer")]
5643 #[test]
5644 fn test_create_signer() {
5645 use crate::wallet::hardwaresigner::HWISigner;
5646 use hwi::types::HWIChain;
5647 use hwi::HWIClient;
5648
5649 let mut devices = HWIClient::enumerate().unwrap();
5650 if devices.is_empty() {
5651 panic!("No devices found!");
5652 }
5653 let device = devices.remove(0).unwrap();
5654 let client = HWIClient::get_client(&device, true, HWIChain::Regtest).unwrap();
5655 let descriptors = client.get_descriptors::<String>(None).unwrap();
5656 let custom_signer = HWISigner::from_device(&device, HWIChain::Regtest).unwrap();
5657
5658 let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]);
5659 wallet.add_signer(
5660 KeychainKind::External,
5661 SignerOrdering(200),
5662 Arc::new(custom_signer),
5663 );
5664
5665 let addr = wallet.get_address(LastUnused).unwrap();
5666 let mut builder = wallet.build_tx();
5667 builder.drain_to(addr.script_pubkey()).drain_wallet();
5668 let (mut psbt, _) = builder.finish().unwrap();
5669
5670 let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
5671 assert!(finalized);
5672 }
5673
5674 #[test]
5675 fn test_taproot_load_descriptor_duplicated_keys() {
5676 let (wallet, _, _) = get_funded_wallet(get_test_tr_dup_keys());
5681 let addr = wallet.get_address(New).unwrap();
5682
5683 assert_eq!(
5684 addr.to_string(),
5685 "bcrt1pvysh4nmh85ysrkpwtrr8q8gdadhgdejpy6f9v424a8v9htjxjhyqw9c5s5"
5686 );
5687 }
5688}