Coverage Report

Created: 2024-10-10 10:38

/home/darosior/projects/bdk/crates/wallet/tests/wallet.rs
Line
Count
Source (jump to first uncovered line)
1
extern crate alloc;
2
3
use std::path::Path;
4
use std::str::FromStr;
5
6
use anyhow::Context;
7
use assert_matches::assert_matches;
8
use bdk_chain::{tx_graph, COINBASE_MATURITY};
9
use bdk_chain::{BlockId, ConfirmationTime};
10
use bdk_wallet::coin_selection::{self, LargestFirstCoinSelection};
11
use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
12
use bdk_wallet::error::CreateTxError;
13
use bdk_wallet::psbt::PsbtUtils;
14
use bdk_wallet::signer::{SignOptions, SignerError};
15
use bdk_wallet::tx_builder::AddForeignUtxoError;
16
use bdk_wallet::{AddressInfo, Balance, ChangeSet, Wallet, WalletPersister};
17
use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
18
use bitcoin::constants::ChainHash;
19
use bitcoin::hashes::Hash;
20
use bitcoin::key::Secp256k1;
21
use bitcoin::psbt;
22
use bitcoin::script::PushBytesBuf;
23
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
24
use bitcoin::taproot::TapNodeHash;
25
use bitcoin::{
26
    absolute, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, ScriptBuf,
27
    Sequence, Transaction, TxIn, TxOut, Txid, Weight,
28
};
29
use rand::rngs::StdRng;
30
use rand::SeedableRng;
31
32
mod common;
33
use common::*;
34
35
10
fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint {
36
10
    let addr = wallet.next_unused_address(KeychainKind::External).address;
37
10
    receive_output_to_address(wallet, addr, value, height)
38
10
}
39
40
10
fn receive_output_to_address(
41
10
    wallet: &mut Wallet,
42
10
    addr: Address,
43
10
    value: u64,
44
10
    height: ConfirmationTime,
45
10
) -> OutPoint {
46
10
    let tx = Transaction {
47
10
        version: transaction::Version::ONE,
48
10
        lock_time: absolute::LockTime::ZERO,
49
10
        input: vec![],
50
10
        output: vec![TxOut {
51
10
            script_pubkey: addr.script_pubkey(),
52
10
            value: Amount::from_sat(value),
53
10
        }],
54
10
    };
55
10
56
10
    let txid = tx.compute_txid();
57
10
    wallet.insert_tx(tx);
58
10
59
10
    match height {
60
6
        ConfirmationTime::Confirmed { .. } => {
61
6
            insert_anchor_from_conf(wallet, txid, height);
62
6
        }
63
4
        ConfirmationTime::Unconfirmed { last_seen } => {
64
4
            insert_seen_at(wallet, txid, last_seen);
65
4
        }
66
    }
67
68
10
    OutPoint { txid, vout: 0 }
69
10
}
70
71
7
fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
72
7
    let latest_cp = wallet.latest_checkpoint();
73
7
    let height = latest_cp.height();
74
7
    let anchor = if height == 0 {
75
1
        ConfirmationTime::Unconfirmed { last_seen: 0 }
76
    } else {
77
6
        ConfirmationTime::Confirmed { height, time: 0 }
78
    };
79
7
    receive_output(wallet, value, anchor)
80
7
}
81
82
24
fn insert_seen_at(wallet: &mut Wallet, txid: Txid, seen_at: u64) {
83
24
    use bdk_wallet::Update;
84
24
    wallet
85
24
        .apply_update(Update {
86
24
            tx_update: tx_graph::TxUpdate {
87
24
                seen_ats: [(txid, seen_at)].into_iter().collect(),
88
24
                ..Default::default()
89
24
            },
90
24
            ..Default::default()
91
24
        })
92
24
        .unwrap();
93
24
}
94
95
// The satisfaction size of a P2WPKH is 112 WU =
96
// 1 (elements in witness) + 1 (OP_PUSH) + 33 (pk) + 1 (OP_PUSH) + 72 (signature + sighash) + 1*4 (script len)
97
// On the witness itself, we have to push once for the pk (33WU) and once for signature + sighash (72WU), for
98
// a total of 105 WU.
99
// Here, we push just once for simplicity, so we have to add an extra byte for the missing
100
// OP_PUSH.
101
const P2WPKH_FAKE_WITNESS_SIZE: usize = 106;
102
103
const DB_MAGIC: &[u8] = &[0x21, 0x24, 0x48];
104
105
#[test]
106
1
fn wallet_is_persisted() -> anyhow::Result<()> {
107
2
    fn run<Db, CreateDb, OpenDb>(
108
2
        filename: &str,
109
2
        create_db: CreateDb,
110
2
        open_db: OpenDb,
111
2
    ) -> anyhow::Result<()>
112
2
    where
113
2
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
114
2
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
115
2
        Db: WalletPersister,
116
2
        Db::Error: std::error::Error + Send + Sync + 'static,
117
2
    {
118
2
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
119
2
        let file_path = temp_dir.path().join(filename);
120
2
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
121
1
122
1
        // create new wallet
123
2
        let wallet_spk_index = {
124
2
            let mut db = create_db(&file_path)
?0
;
125
2
            let mut wallet = Wallet::create(external_desc, internal_desc)
126
2
                .network(Network::Testnet)
127
2
                .create_wallet(&mut db)
?0
;
128
2
            wallet.reveal_next_address(KeychainKind::External);
129
2
130
2
            // persist new wallet changes
131
2
            assert!(wallet.persist(&mut db)
?0
,
"must write"0
);
132
2
            wallet.spk_index().clone()
133
1
        };
134
1
135
1
        // recover wallet
136
1
        {
137
2
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
138
2
            let _ = Wallet::load()
139
2
                .check_network(Network::Testnet)
140
2
                .load_wallet(&mut db)
?0
141
2
                .expect("wallet must exist");
142
1
        }
143
1
        {
144
2
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
145
2
            let wallet = Wallet::load()
146
2
                .descriptor(KeychainKind::External, Some(external_desc))
147
2
                .descriptor(KeychainKind::Internal, Some(internal_desc))
148
2
                .check_network(Network::Testnet)
149
2
                .load_wallet(&mut db)
?0
150
2
                .expect("wallet must exist");
151
2
152
2
            assert_eq!(wallet.network(), Network::Testnet);
153
2
            assert_eq!(
154
2
                wallet.spk_index().keychains().collect::<Vec<_>>(),
155
2
                wallet_spk_index.keychains().collect::<Vec<_>>()
156
2
            );
157
2
            assert_eq!(
158
2
                wallet.spk_index().last_revealed_indices(),
159
2
                wallet_spk_index.last_revealed_indices()
160
2
            );
161
2
            let secp = Secp256k1::new();
162
2
            assert_eq!(
163
2
                *wallet.public_descriptor(KeychainKind::External),
164
2
                external_desc
165
2
                    .into_wallet_descriptor(&secp, wallet.network())
166
2
                    .unwrap()
167
2
                    .0
168
2
            );
169
1
        }
170
1
171
2
        Ok(())
172
2
    }
wallet::wallet_is_persisted::run::<bdk_file_store::store::Store<bdk_wallet::wallet::changeset::ChangeSet>, wallet::wallet_is_persisted::{closure#0}, wallet::wallet_is_persisted::{closure#1}>
Line
Count
Source
107
1
    fn run<Db, CreateDb, OpenDb>(
108
1
        filename: &str,
109
1
        create_db: CreateDb,
110
1
        open_db: OpenDb,
111
1
    ) -> anyhow::Result<()>
112
1
    where
113
1
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
114
1
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
115
1
        Db: WalletPersister,
116
1
        Db::Error: std::error::Error + Send + Sync + 'static,
117
1
    {
118
1
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
119
1
        let file_path = temp_dir.path().join(filename);
120
1
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
121
122
        // create new wallet
123
1
        let wallet_spk_index = {
124
1
            let mut db = create_db(&file_path)
?0
;
125
1
            let mut wallet = Wallet::create(external_desc, internal_desc)
126
1
                .network(Network::Testnet)
127
1
                .create_wallet(&mut db)
?0
;
128
1
            wallet.reveal_next_address(KeychainKind::External);
129
1
130
1
            // persist new wallet changes
131
1
            assert!(wallet.persist(&mut db)
?0
,
"must write"0
);
132
1
            wallet.spk_index().clone()
133
        };
134
135
        // recover wallet
136
        {
137
1
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
138
1
            let _ = Wallet::load()
139
1
                .check_network(Network::Testnet)
140
1
                .load_wallet(&mut db)
?0
141
1
                .expect("wallet must exist");
142
        }
143
        {
144
1
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
145
1
            let wallet = Wallet::load()
146
1
                .descriptor(KeychainKind::External, Some(external_desc))
147
1
                .descriptor(KeychainKind::Internal, Some(internal_desc))
148
1
                .check_network(Network::Testnet)
149
1
                .load_wallet(&mut db)
?0
150
1
                .expect("wallet must exist");
151
1
152
1
            assert_eq!(wallet.network(), Network::Testnet);
153
1
            assert_eq!(
154
1
                wallet.spk_index().keychains().collect::<Vec<_>>(),
155
1
                wallet_spk_index.keychains().collect::<Vec<_>>()
156
1
            );
157
1
            assert_eq!(
158
1
                wallet.spk_index().last_revealed_indices(),
159
1
                wallet_spk_index.last_revealed_indices()
160
1
            );
161
1
            let secp = Secp256k1::new();
162
1
            assert_eq!(
163
1
                *wallet.public_descriptor(KeychainKind::External),
164
1
                external_desc
165
1
                    .into_wallet_descriptor(&secp, wallet.network())
166
1
                    .unwrap()
167
1
                    .0
168
1
            );
169
        }
170
171
1
        Ok(())
172
1
    }
wallet::wallet_is_persisted::run::<rusqlite::Connection, wallet::wallet_is_persisted::{closure#2}, wallet::wallet_is_persisted::{closure#3}>
Line
Count
Source
107
1
    fn run<Db, CreateDb, OpenDb>(
108
1
        filename: &str,
109
1
        create_db: CreateDb,
110
1
        open_db: OpenDb,
111
1
    ) -> anyhow::Result<()>
112
1
    where
113
1
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
114
1
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
115
1
        Db: WalletPersister,
116
1
        Db::Error: std::error::Error + Send + Sync + 'static,
117
1
    {
118
1
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
119
1
        let file_path = temp_dir.path().join(filename);
120
1
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
121
122
        // create new wallet
123
1
        let wallet_spk_index = {
124
1
            let mut db = create_db(&file_path)
?0
;
125
1
            let mut wallet = Wallet::create(external_desc, internal_desc)
126
1
                .network(Network::Testnet)
127
1
                .create_wallet(&mut db)
?0
;
128
1
            wallet.reveal_next_address(KeychainKind::External);
129
1
130
1
            // persist new wallet changes
131
1
            assert!(wallet.persist(&mut db)
?0
,
"must write"0
);
132
1
            wallet.spk_index().clone()
133
        };
134
135
        // recover wallet
136
        {
137
1
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
138
1
            let _ = Wallet::load()
139
1
                .check_network(Network::Testnet)
140
1
                .load_wallet(&mut db)
?0
141
1
                .expect("wallet must exist");
142
        }
143
        {
144
1
            let mut db = open_db(&file_path).context("failed to recover db")
?0
;
145
1
            let wallet = Wallet::load()
146
1
                .descriptor(KeychainKind::External, Some(external_desc))
147
1
                .descriptor(KeychainKind::Internal, Some(internal_desc))
148
1
                .check_network(Network::Testnet)
149
1
                .load_wallet(&mut db)
?0
150
1
                .expect("wallet must exist");
151
1
152
1
            assert_eq!(wallet.network(), Network::Testnet);
153
1
            assert_eq!(
154
1
                wallet.spk_index().keychains().collect::<Vec<_>>(),
155
1
                wallet_spk_index.keychains().collect::<Vec<_>>()
156
1
            );
157
1
            assert_eq!(
158
1
                wallet.spk_index().last_revealed_indices(),
159
1
                wallet_spk_index.last_revealed_indices()
160
1
            );
161
1
            let secp = Secp256k1::new();
162
1
            assert_eq!(
163
1
                *wallet.public_descriptor(KeychainKind::External),
164
1
                external_desc
165
1
                    .into_wallet_descriptor(&secp, wallet.network())
166
1
                    .unwrap()
167
1
                    .0
168
1
            );
169
        }
170
171
1
        Ok(())
172
1
    }
173
1
174
1
    run(
175
1
        "store.db",
176
1
        |path| Ok(bdk_file_store::Store::create_new(DB_MAGIC, path)
?0
),
177
2
        |path| Ok(bdk_file_store::Store::open(DB_MAGIC, path)
?0
),
178
1
    )
?0
;
179
1
    run::<bdk_chain::rusqlite::Connection, _, _>(
180
1
        "store.sqlite",
181
1
        |path| Ok(bdk_chain::rusqlite::Connection::open(path)
?0
),
182
2
        |path| Ok(bdk_chain::rusqlite::Connection::open(path)
?0
),
183
1
    )
?0
;
184
185
1
    Ok(())
186
1
}
187
188
#[test]
189
1
fn wallet_load_checks() -> anyhow::Result<()> {
190
2
    fn run<Db, CreateDb, OpenDb>(
191
2
        filename: &str,
192
2
        create_db: CreateDb,
193
2
        open_db: OpenDb,
194
2
    ) -> anyhow::Result<()>
195
2
    where
196
2
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
197
2
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
198
2
        Db: WalletPersister + std::fmt::Debug,
199
2
        Db::Error: std::error::Error + Send + Sync + 'static,
200
2
    {
201
2
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
202
2
        let file_path = temp_dir.path().join(filename);
203
2
        let network = Network::Testnet;
204
2
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
205
2
206
2
        // create new wallet
207
2
        let _ = Wallet::create(external_desc, internal_desc)
208
2
            .network(network)
209
2
            .create_wallet(&mut create_db(&file_path)
?0
)
?0
;
210
1
211
1
        
assert_matches!0
(
212
2
            Wallet::load()
213
2
                .check_network(Network::Regtest)
214
2
                .load_wallet(&mut open_db(&file_path)
?0
),
215
1
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
216
1
                LoadMismatch::Network {
217
1
                    loaded: Network::Testnet,
218
1
                    expected: Network::Regtest,
219
1
                }
220
1
            ))),
221
1
            "unexpected network check result: Regtest (check) is not Testnet (loaded)",
222
1
        );
223
2
        let mainnet_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes());
224
1
        
assert_matches!0
(
225
2
            Wallet::load().check_genesis_hash(mainnet_hash).load_wallet(&mut open_db(&file_path)
?0
),
226
1
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Genesis { .. }))),
227
1
            "unexpected genesis hash check result: mainnet hash (check) is not testnet hash (loaded)",
228
1
        );
229
1
        
assert_matches!0
(
230
2
            Wallet::load()
231
2
                .descriptor(KeychainKind::External, Some(internal_desc))
232
2
                .load_wallet(&mut open_db(&file_path)
?0
),
233
1
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
234
1
                LoadMismatch::Descriptor { .. }
235
1
            ))),
236
1
            "unexpected descriptors check result",
237
1
        );
238
1
        
assert_matches!0
(
239
2
            Wallet::load()
240
2
                .descriptor(KeychainKind::External, Option::<&str>::None)
241
2
                .load_wallet(&mut open_db(&file_path)
?0
),
242
1
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
243
1
                LoadMismatch::Descriptor { .. }
244
1
            ))),
245
1
            "unexpected descriptors check result",
246
1
        );
247
1
248
2
        Ok(())
249
2
    }
wallet::wallet_load_checks::run::<bdk_file_store::store::Store<bdk_wallet::wallet::changeset::ChangeSet>, wallet::wallet_load_checks::{closure#0}, wallet::wallet_load_checks::{closure#1}>
Line
Count
Source
190
1
    fn run<Db, CreateDb, OpenDb>(
191
1
        filename: &str,
192
1
        create_db: CreateDb,
193
1
        open_db: OpenDb,
194
1
    ) -> anyhow::Result<()>
195
1
    where
196
1
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
197
1
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
198
1
        Db: WalletPersister + std::fmt::Debug,
199
1
        Db::Error: std::error::Error + Send + Sync + 'static,
200
1
    {
201
1
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
202
1
        let file_path = temp_dir.path().join(filename);
203
1
        let network = Network::Testnet;
204
1
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
205
1
206
1
        // create new wallet
207
1
        let _ = Wallet::create(external_desc, internal_desc)
208
1
            .network(network)
209
1
            .create_wallet(&mut create_db(&file_path)
?0
)
?0
;
210
211
0
        assert_matches!(
212
1
            Wallet::load()
213
1
                .check_network(Network::Regtest)
214
1
                .load_wallet(&mut open_db(&file_path)
?0
),
215
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
216
                LoadMismatch::Network {
217
                    loaded: Network::Testnet,
218
                    expected: Network::Regtest,
219
                }
220
            ))),
221
            "unexpected network check result: Regtest (check) is not Testnet (loaded)",
222
        );
223
1
        let mainnet_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes());
224
0
        assert_matches!(
225
1
            Wallet::load().check_genesis_hash(mainnet_hash).load_wallet(&mut open_db(&file_path)
?0
),
226
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Genesis { .. }))),
227
            "unexpected genesis hash check result: mainnet hash (check) is not testnet hash (loaded)",
228
        );
229
0
        assert_matches!(
230
1
            Wallet::load()
231
1
                .descriptor(KeychainKind::External, Some(internal_desc))
232
1
                .load_wallet(&mut open_db(&file_path)
?0
),
233
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
234
                LoadMismatch::Descriptor { .. }
235
            ))),
236
            "unexpected descriptors check result",
237
        );
238
0
        assert_matches!(
239
1
            Wallet::load()
240
1
                .descriptor(KeychainKind::External, Option::<&str>::None)
241
1
                .load_wallet(&mut open_db(&file_path)
?0
),
242
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
243
                LoadMismatch::Descriptor { .. }
244
            ))),
245
            "unexpected descriptors check result",
246
        );
247
248
1
        Ok(())
249
1
    }
wallet::wallet_load_checks::run::<rusqlite::Connection, wallet::wallet_load_checks::{closure#2}, wallet::wallet_load_checks::{closure#3}>
Line
Count
Source
190
1
    fn run<Db, CreateDb, OpenDb>(
191
1
        filename: &str,
192
1
        create_db: CreateDb,
193
1
        open_db: OpenDb,
194
1
    ) -> anyhow::Result<()>
195
1
    where
196
1
        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
197
1
        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
198
1
        Db: WalletPersister + std::fmt::Debug,
199
1
        Db::Error: std::error::Error + Send + Sync + 'static,
200
1
    {
201
1
        let temp_dir = tempfile::tempdir().expect("must create tempdir");
202
1
        let file_path = temp_dir.path().join(filename);
203
1
        let network = Network::Testnet;
204
1
        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
205
1
206
1
        // create new wallet
207
1
        let _ = Wallet::create(external_desc, internal_desc)
208
1
            .network(network)
209
1
            .create_wallet(&mut create_db(&file_path)
?0
)
?0
;
210
211
0
        assert_matches!(
212
1
            Wallet::load()
213
1
                .check_network(Network::Regtest)
214
1
                .load_wallet(&mut open_db(&file_path)
?0
),
215
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
216
                LoadMismatch::Network {
217
                    loaded: Network::Testnet,
218
                    expected: Network::Regtest,
219
                }
220
            ))),
221
            "unexpected network check result: Regtest (check) is not Testnet (loaded)",
222
        );
223
1
        let mainnet_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes());
224
0
        assert_matches!(
225
1
            Wallet::load().check_genesis_hash(mainnet_hash).load_wallet(&mut open_db(&file_path)
?0
),
226
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Genesis { .. }))),
227
            "unexpected genesis hash check result: mainnet hash (check) is not testnet hash (loaded)",
228
        );
229
0
        assert_matches!(
230
1
            Wallet::load()
231
1
                .descriptor(KeychainKind::External, Some(internal_desc))
232
1
                .load_wallet(&mut open_db(&file_path)
?0
),
233
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
234
                LoadMismatch::Descriptor { .. }
235
            ))),
236
            "unexpected descriptors check result",
237
        );
238
0
        assert_matches!(
239
1
            Wallet::load()
240
1
                .descriptor(KeychainKind::External, Option::<&str>::None)
241
1
                .load_wallet(&mut open_db(&file_path)
?0
),
242
            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
243
                LoadMismatch::Descriptor { .. }
244
            ))),
245
            "unexpected descriptors check result",
246
        );
247
248
1
        Ok(())
249
1
    }
250
1
251
1
    run(
252
1
        "store.db",
253
1
        |path| {
254
1
            Ok(bdk_file_store::Store::<ChangeSet>::create_new(
255
1
                DB_MAGIC, path,
256
1
            )
?0
)
257
1
        },
258
4
        |path| Ok(bdk_file_store::Store::<ChangeSet>::open(DB_MAGIC, path)
?0
),
259
1
    )
?0
;
260
1
    run(
261
1
        "store.sqlite",
262
1
        |path| Ok(bdk_chain::rusqlite::Connection::open(path)
?0
),
263
4
        |path| Ok(bdk_chain::rusqlite::Connection::open(path)
?0
),
264
1
    )
?0
;
265
266
1
    Ok(())
267
1
}
268
269
#[test]
270
1
fn single_descriptor_wallet_persist_and_recover() {
271
1
    use bdk_chain::miniscript::Descriptor;
272
1
    use bdk_chain::miniscript::DescriptorPublicKey;
273
1
    use bdk_chain::rusqlite;
274
1
    let temp_dir = tempfile::tempdir().unwrap();
275
1
    let db_path = temp_dir.path().join("wallet.db");
276
1
    let mut db = rusqlite::Connection::open(db_path).unwrap();
277
1
278
1
    let desc = get_test_tr_single_sig_xprv();
279
1
    let mut wallet = Wallet::create_single(desc)
280
1
        .network(Network::Testnet)
281
1
        .create_wallet(&mut db)
282
1
        .unwrap();
283
1
    let _ = wallet.reveal_addresses_to(KeychainKind::External, 2);
284
1
    assert!(wallet.persist(&mut db).unwrap());
285
286
    // should recover persisted wallet
287
1
    let secp = wallet.secp_ctx();
288
1
    let (_, keymap) = <Descriptor<DescriptorPublicKey>>::parse_descriptor(secp, desc).unwrap();
289
1
    assert!(!keymap.is_empty());
290
1
    let wallet = Wallet::load()
291
1
        .descriptor(KeychainKind::External, Some(desc))
292
1
        .extract_keys()
293
1
        .load_wallet(&mut db)
294
1
        .unwrap()
295
1
        .expect("must have loaded changeset");
296
1
    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(2));
297
    // should have private key
298
1
    assert_eq!(
299
1
        wallet.get_signers(KeychainKind::External).as_key_map(secp),
300
1
        keymap,
301
1
    );
302
303
    // should error on wrong internal params
304
1
    let desc = get_test_wpkh();
305
1
    let (exp_desc, _) = <Descriptor<DescriptorPublicKey>>::parse_descriptor(secp, desc).unwrap();
306
1
    let err = Wallet::load()
307
1
        .descriptor(KeychainKind::Internal, Some(desc))
308
1
        .extract_keys()
309
1
        .load_wallet(&mut db);
310
1
    assert_matches!(
311
1
        err,
312
1
        Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Descriptor { keychain, loaded, expected })))
313
1
        if keychain == KeychainKind::Internal && loaded.is_none() && expected == Some(exp_desc),
314
1
        "single descriptor wallet should refuse change descriptor param"
315
1
    );
316
1
}
317
318
#[test]
319
1
fn test_error_external_and_internal_are_the_same() {
320
1
    // identical descriptors should fail to create wallet
321
1
    let desc = get_test_wpkh();
322
1
    let err = Wallet::create(desc, desc)
323
1
        .network(Network::Testnet)
324
1
        .create_wallet_no_persist();
325
1
    assert!(
326
1
        
matches!0
(&err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
327
0
        "expected same descriptors error, got {:?}",
328
        err,
329
    );
330
331
    // public + private of same descriptor should fail to create wallet
332
1
    let desc = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
333
1
    let change_desc = "wpkh([3c31d632/84'/1'/0']tpubDCYwFkks2cg78N7eoYbBatsFEGje8vW8arSKW4rLwD1AU1s9KJMDRHE32JkvYERuiFjArrsH7qpWSpJATed5ShZbG9KsskA5Rmi6NSYgYN2/0/*)";
334
1
    let err = Wallet::create(desc, change_desc)
335
1
        .network(Network::Testnet)
336
1
        .create_wallet_no_persist();
337
1
    assert!(
338
1
        
matches!0
(err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
339
0
        "expected same descriptors error, got {:?}",
340
        err,
341
    );
342
1
}
343
344
#[test]
345
1
fn test_descriptor_checksum() {
346
1
    let (wallet, _) = get_funded_wallet_wpkh();
347
1
    let checksum = wallet.descriptor_checksum(KeychainKind::External);
348
1
    assert_eq!(checksum.len(), 8);
349
350
1
    let raw_descriptor = wallet
351
1
        .keychains()
352
1
        .next()
353
1
        .unwrap()
354
1
        .1
355
1
        .to_string()
356
1
        .split_once('#')
357
1
        .unwrap()
358
1
        .0
359
1
        .to_string();
360
1
    assert_eq!(calc_checksum(&raw_descriptor).unwrap(), checksum);
361
1
}
362
363
#[test]
364
1
fn test_get_funded_wallet_balance() {
365
1
    let (wallet, _) = get_funded_wallet_wpkh();
366
1
367
1
    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
368
1
    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
369
1
    // sats are the transaction fee.
370
1
    assert_eq!(wallet.balance().confirmed, Amount::from_sat(50_000));
371
1
}
372
373
#[test]
374
1
fn test_get_funded_wallet_sent_and_received() {
375
1
    let (wallet, txid) = get_funded_wallet_wpkh();
376
1
377
1
    let mut tx_amounts: Vec<(Txid, (Amount, Amount))> = wallet
378
1
        .transactions()
379
2
        .map(|ct| (ct.tx_node.txid, wallet.sent_and_received(&ct.tx_node)))
380
1
        .collect();
381
1
    tx_amounts.sort_by(|a1, a2| a1.0.cmp(&a2.0));
382
1
383
1
    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
384
1
    let (sent, received) = wallet.sent_and_received(&tx);
385
1
386
1
    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
387
1
    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
388
1
    // sats are the transaction fee.
389
1
    assert_eq!(sent.to_sat(), 76_000);
390
1
    assert_eq!(received.to_sat(), 50_000);
391
1
}
392
393
#[test]
394
1
fn test_get_funded_wallet_tx_fees() {
395
1
    let (wallet, txid) = get_funded_wallet_wpkh();
396
1
397
1
    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
398
1
    let tx_fee = wallet.calculate_fee(&tx).expect("transaction fee");
399
1
400
1
    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
401
1
    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
402
1
    // sats are the transaction fee.
403
1
    assert_eq!(tx_fee, Amount::from_sat(1000))
404
1
}
405
406
#[test]
407
1
fn test_get_funded_wallet_tx_fee_rate() {
408
1
    let (wallet, txid) = get_funded_wallet_wpkh();
409
1
410
1
    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
411
1
    let tx_fee_rate = wallet
412
1
        .calculate_fee_rate(&tx)
413
1
        .expect("transaction fee rate");
414
1
415
1
    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
416
1
    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
417
1
    // sats are the transaction fee.
418
1
419
1
    // tx weight = 452 wu, as vbytes = (452 + 3) / 4 = 113
420
1
    // fee_rate (sats per kwu) = fee / weight = 1000sat / 0.452kwu = 2212
421
1
    // fee_rate (sats per vbyte ceil) = fee / vsize = 1000sat / 113vb = 9
422
1
    assert_eq!(tx_fee_rate.to_sat_per_kwu(), 2212);
423
1
    assert_eq!(tx_fee_rate.to_sat_per_vb_ceil(), 9);
424
1
}
425
426
#[test]
427
1
fn test_list_output() {
428
1
    let (wallet, txid) = get_funded_wallet_wpkh();
429
1
    let txos = wallet
430
1
        .list_output()
431
2
        .map(|op| (op.outpoint, op))
432
1
        .collect::<std::collections::BTreeMap<_, _>>();
433
1
    assert_eq!(txos.len(), 2);
434
3
    for (
op, txo2
) in txos {
435
2
        if op.txid == txid {
436
1
            assert_eq!(txo.txout.value.to_sat(), 50_000);
437
1
            assert!(!txo.is_spent);
438
        } else {
439
1
            assert_eq!(txo.txout.value.to_sat(), 76_000);
440
1
            assert!(txo.is_spent);
441
        }
442
    }
443
1
}
444
445
macro_rules! assert_fee_rate {
446
    ($psbt:expr, $fees:expr, $fee_rate:expr $( ,@dust_change $( $dust_change:expr )* )* $( ,@add_signature $( $add_signature:expr )* )* ) => ({
447
        let psbt = $psbt.clone();
448
        #[allow(unused_mut)]
449
        let mut tx = $psbt.clone().extract_tx().expect("failed to extract tx");
450
        $(
451
            $( $add_signature )*
452
                for txin in &mut tx.input {
453
                    txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
454
                }
455
        )*
456
457
            #[allow(unused_mut)]
458
        #[allow(unused_assignments)]
459
        let mut dust_change = false;
460
        $(
461
            $( $dust_change )*
462
                dust_change = true;
463
        )*
464
465
            let fee_amount = psbt
466
            .inputs
467
            .iter()
468
15
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_create_tx_default_fee_rate::{closure#0}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_create_tx_custom_fee_rate::{closure#0}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_reduce_change::{closure#2}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_reduce_single_recipient::{closure#0}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_add_input::{closure#2}
Line
Count
Source
468
2
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_no_change_add_input_and_change::{closure#2}
Line
Count
Source
468
2
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_add_input_change_dust::{closure#1}
Line
Count
Source
468
2
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_bump_fee_force_add_input::{closure#2}
Line
Count
Source
468
2
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_fee_amount_negative_drain_val::{closure#0}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_fee_rate_sign_no_grinding_high_r::{closure#1}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
wallet::test_fee_rate_sign_grinding_low_r::{closure#0}
Line
Count
Source
468
1
            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
469
            - psbt
470
            .unsigned_tx
471
            .output
472
            .iter()
473
18
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_create_tx_default_fee_rate::{closure#1}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_create_tx_custom_fee_rate::{closure#1}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_reduce_change::{closure#3}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_reduce_single_recipient::{closure#1}
Line
Count
Source
473
1
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_add_input::{closure#3}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_no_change_add_input_and_change::{closure#3}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_add_input_change_dust::{closure#2}
Line
Count
Source
473
1
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_bump_fee_force_add_input::{closure#3}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_fee_amount_negative_drain_val::{closure#1}
Line
Count
Source
473
1
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_fee_rate_sign_no_grinding_high_r::{closure#2}
Line
Count
Source
473
2
            .fold(Amount::ZERO, |acc, o| acc + o.value);
wallet::test_fee_rate_sign_grinding_low_r::{closure#1}
Line
Count
Source
473
1
            .fold(Amount::ZERO, |acc, o| acc + o.value);
474
475
        assert_eq!(fee_amount, $fees);
476
477
        let tx_fee_rate = (fee_amount / tx.weight())
478
            .to_sat_per_kwu();
479
        let fee_rate = $fee_rate.to_sat_per_kwu();
480
        let half_default = FeeRate::BROADCAST_MIN.checked_div(2)
481
            .unwrap()
482
            .to_sat_per_kwu();
483
484
        if !dust_change {
485
            assert!(tx_fee_rate >= fee_rate && tx_fee_rate - fee_rate < half_default, "Expected fee rate of {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
486
        } else {
487
            assert!(tx_fee_rate >= fee_rate, "Expected fee rate of at least {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
488
        }
489
    });
490
}
491
492
macro_rules! from_str {
493
    ($e:expr, $t:ty) => {{
494
        use core::str::FromStr;
495
        <$t>::from_str($e).unwrap()
496
    }};
497
498
    ($e:expr) => {
499
        from_str!($e, _)
500
    };
501
}
502
503
#[test]
504
#[should_panic(expected = "NoRecipients")]
505
1
fn test_create_tx_empty_recipients() {
506
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
507
1
    wallet.build_tx().finish().unwrap();
508
1
}
509
510
#[test]
511
#[should_panic(expected = "NoUtxosSelected")]
512
1
fn test_create_tx_manually_selected_empty_utxos() {
513
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
514
1
    let addr = wallet.next_unused_address(KeychainKind::External);
515
1
    let mut builder = wallet.build_tx();
516
1
    builder
517
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
518
1
        .manually_selected_only();
519
1
    builder.finish().unwrap();
520
1
}
521
522
#[test]
523
1
fn test_create_tx_version_0() {
524
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
525
1
    let addr = wallet.next_unused_address(KeychainKind::External);
526
1
    let mut builder = wallet.build_tx();
527
1
    builder
528
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
529
1
        .version(0);
530
1
    assert!(
matches!0
(builder.finish(), Err(CreateTxError::Version0)));
531
1
}
532
533
#[test]
534
1
fn test_create_tx_version_1_csv() {
535
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
536
1
    let addr = wallet.next_unused_address(KeychainKind::External);
537
1
    let mut builder = wallet.build_tx();
538
1
    builder
539
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
540
1
        .version(1);
541
1
    assert!(
matches!0
(builder.finish(), Err(CreateTxError::Version1Csv)));
542
1
}
543
544
#[test]
545
1
fn test_create_tx_custom_version() {
546
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
547
1
    let addr = wallet.next_unused_address(KeychainKind::External);
548
1
    let mut builder = wallet.build_tx();
549
1
    builder
550
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
551
1
        .version(42);
552
1
    let psbt = builder.finish().unwrap();
553
1
554
1
    assert_eq!(psbt.unsigned_tx.version.0, 42);
555
1
}
556
557
#[test]
558
1
fn test_create_tx_default_locktime_is_last_sync_height() {
559
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
560
1
561
1
    let addr = wallet.next_unused_address(KeychainKind::External);
562
1
    let mut builder = wallet.build_tx();
563
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
564
1
    let psbt = builder.finish().unwrap();
565
1
566
1
    // Since we never synced the wallet we don't have a last_sync_height
567
1
    // we could use to try to prevent fee sniping. We default to 0.
568
1
    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 2_000);
569
1
}
570
571
#[test]
572
1
fn test_create_tx_fee_sniping_locktime_last_sync() {
573
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
574
1
    let addr = wallet.next_unused_address(KeychainKind::External);
575
1
    let mut builder = wallet.build_tx();
576
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
577
1
578
1
    let psbt = builder.finish().unwrap();
579
1
580
1
    // If there's no current_height we're left with using the last sync height
581
1
    assert_eq!(
582
1
        psbt.unsigned_tx.lock_time.to_consensus_u32(),
583
1
        wallet.latest_checkpoint().height()
584
1
    );
585
1
}
586
587
#[test]
588
1
fn test_create_tx_default_locktime_cltv() {
589
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
590
1
    let addr = wallet.next_unused_address(KeychainKind::External);
591
1
    let mut builder = wallet.build_tx();
592
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
593
1
    let psbt = builder.finish().unwrap();
594
1
595
1
    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 100_000);
596
1
}
597
598
#[test]
599
1
fn test_create_tx_custom_locktime() {
600
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
601
1
    let addr = wallet.next_unused_address(KeychainKind::External);
602
1
    let mut builder = wallet.build_tx();
603
1
    builder
604
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
605
1
        .current_height(630_001)
606
1
        .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
607
1
    let psbt = builder.finish().unwrap();
608
1
609
1
    // When we explicitly specify a nlocktime
610
1
    // we don't try any fee sniping prevention trick
611
1
    // (we ignore the current_height)
612
1
    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
613
1
}
614
615
#[test]
616
1
fn test_create_tx_custom_locktime_compatible_with_cltv() {
617
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
618
1
    let addr = wallet.next_unused_address(KeychainKind::External);
619
1
    let mut builder = wallet.build_tx();
620
1
    builder
621
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
622
1
        .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
623
1
    let psbt = builder.finish().unwrap();
624
1
625
1
    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
626
1
}
627
628
#[test]
629
1
fn test_create_tx_custom_locktime_incompatible_with_cltv() {
630
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
631
1
    let addr = wallet.next_unused_address(KeychainKind::External);
632
1
    let mut builder = wallet.build_tx();
633
1
    builder
634
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
635
1
        .nlocktime(absolute::LockTime::from_height(50000).unwrap());
636
1
    assert!(matches!(builder.finish(),
637
1
        Err(CreateTxError::LockTime { requested, required })
638
1
        if requested.to_consensus_u32() == 50_000 && required.to_consensus_u32() == 100_000));
639
1
}
640
641
#[test]
642
1
fn test_create_tx_no_rbf_csv() {
643
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
644
1
    let addr = wallet.next_unused_address(KeychainKind::External);
645
1
    let mut builder = wallet.build_tx();
646
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
647
1
    let psbt = builder.finish().unwrap();
648
1
649
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
650
1
}
651
652
#[test]
653
1
fn test_create_tx_with_default_rbf_csv() {
654
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
655
1
    let addr = wallet.next_unused_address(KeychainKind::External);
656
1
    let mut builder = wallet.build_tx();
657
1
    builder
658
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
659
1
        .enable_rbf();
660
1
    let psbt = builder.finish().unwrap();
661
1
    // When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
662
1
    // It will be set to the OP_CSV value, in this case 6
663
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
664
1
}
665
666
#[test]
667
1
fn test_create_tx_with_custom_rbf_csv() {
668
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
669
1
    let addr = wallet.next_unused_address(KeychainKind::External);
670
1
    let mut builder = wallet.build_tx();
671
1
    builder
672
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
673
1
        .enable_rbf_with_sequence(Sequence(3));
674
1
    assert!(matches!(builder.finish(),
675
1
        Err(CreateTxError::RbfSequenceCsv { rbf, csv })
676
1
        if rbf.to_consensus_u32() == 3 && csv.to_consensus_u32() == 6));
677
1
}
678
679
#[test]
680
1
fn test_create_tx_no_rbf_cltv() {
681
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
682
1
    let addr = wallet.next_unused_address(KeychainKind::External);
683
1
    let mut builder = wallet.build_tx();
684
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
685
1
    let psbt = builder.finish().unwrap();
686
1
687
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
688
1
}
689
690
#[test]
691
1
fn test_create_tx_invalid_rbf_sequence() {
692
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
693
1
    let addr = wallet.next_unused_address(KeychainKind::External);
694
1
    let mut builder = wallet.build_tx();
695
1
    builder
696
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
697
1
        .enable_rbf_with_sequence(Sequence(0xFFFFFFFE));
698
1
    assert!(
matches!0
(builder.finish(), Err(CreateTxError::RbfSequence)));
699
1
}
700
701
#[test]
702
1
fn test_create_tx_custom_rbf_sequence() {
703
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
704
1
    let addr = wallet.next_unused_address(KeychainKind::External);
705
1
    let mut builder = wallet.build_tx();
706
1
    builder
707
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
708
1
        .enable_rbf_with_sequence(Sequence(0xDEADBEEF));
709
1
    let psbt = builder.finish().unwrap();
710
1
711
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xDEADBEEF));
712
1
}
713
714
#[test]
715
1
fn test_create_tx_change_policy() {
716
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
717
1
    let addr = wallet.next_unused_address(KeychainKind::External);
718
1
    let mut builder = wallet.build_tx();
719
1
    builder
720
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
721
1
        .do_not_spend_change();
722
1
    assert!(builder.finish().is_ok());
723
724
    // wallet has no change, so setting `only_spend_change`
725
    // should cause tx building to fail
726
1
    let mut builder = wallet.build_tx();
727
1
    builder
728
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
729
1
        .only_spend_change();
730
1
    assert!(
matches!0
(
731
1
        builder.finish(),
732
        Err(CreateTxError::CoinSelection(
733
            coin_selection::Error::InsufficientFunds { .. }
734
        )),
735
    ));
736
1
}
737
738
#[test]
739
1
fn test_create_tx_default_sequence() {
740
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
741
1
    let addr = wallet.next_unused_address(KeychainKind::External);
742
1
    let mut builder = wallet.build_tx();
743
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
744
1
    let psbt = builder.finish().unwrap();
745
1
746
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
747
1
}
748
749
macro_rules! check_fee {
750
    ($wallet:expr, $psbt: expr) => {{
751
        let tx = $psbt.clone().extract_tx().expect("failed to extract tx");
752
        let tx_fee = $wallet.calculate_fee(&tx).ok();
753
        assert_eq!(tx_fee, $psbt.fee_amount());
754
        tx_fee
755
    }};
756
}
757
758
#[test]
759
1
fn test_create_tx_drain_wallet_and_drain_to() {
760
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
761
1
    let addr = wallet.next_unused_address(KeychainKind::External);
762
1
    let mut builder = wallet.build_tx();
763
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
764
1
    let psbt = builder.finish().unwrap();
765
1
    let fee = check_fee!(wallet, psbt);
766
767
1
    assert_eq!(psbt.unsigned_tx.output.len(), 1);
768
1
    assert_eq!(
769
1
        psbt.unsigned_tx.output[0].value,
770
1
        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
771
1
    );
772
1
}
773
774
#[test]
775
1
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
776
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
777
1
    let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
778
1
        .unwrap()
779
1
        .assume_checked();
780
1
    let drain_addr = wallet.next_unused_address(KeychainKind::External);
781
1
    let mut builder = wallet.build_tx();
782
1
    builder
783
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(20_000))
784
1
        .drain_to(drain_addr.script_pubkey())
785
1
        .drain_wallet();
786
1
    let psbt = builder.finish().unwrap();
787
1
    let fee = check_fee!(wallet, psbt);
788
1
    let outputs = psbt.unsigned_tx.output;
789
1
790
1
    assert_eq!(outputs.len(), 2);
791
1
    let main_output = outputs
792
1
        .iter()
793
2
        .find(|x| x.script_pubkey == addr.script_pubkey())
794
1
        .unwrap();
795
1
    let drain_output = outputs
796
1
        .iter()
797
1
        .find(|x| x.script_pubkey == drain_addr.script_pubkey())
798
1
        .unwrap();
799
1
    assert_eq!(main_output.value, Amount::from_sat(20_000));
800
1
    assert_eq!(
801
1
        drain_output.value,
802
1
        Amount::from_sat(30_000) - fee.unwrap_or(Amount::ZERO)
803
1
    );
804
1
}
805
806
#[test]
807
1
fn test_create_tx_drain_to_and_utxos() {
808
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
809
1
    let addr = wallet.next_unused_address(KeychainKind::External);
810
1
    let utxos: Vec<_> = wallet.list_unspent().map(|u| u.outpoint).collect();
811
1
    let mut builder = wallet.build_tx();
812
1
    builder
813
1
        .drain_to(addr.script_pubkey())
814
1
        .add_utxos(&utxos)
815
1
        .unwrap();
816
1
    let psbt = builder.finish().unwrap();
817
1
    let fee = check_fee!(wallet, psbt);
818
819
1
    assert_eq!(psbt.unsigned_tx.output.len(), 1);
820
1
    assert_eq!(
821
1
        psbt.unsigned_tx.output[0].value,
822
1
        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
823
1
    );
824
1
}
825
826
#[test]
827
#[should_panic(expected = "NoRecipients")]
828
1
fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
829
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
830
1
    let drain_addr = wallet.next_unused_address(KeychainKind::External);
831
1
    let mut builder = wallet.build_tx();
832
1
    builder.drain_to(drain_addr.script_pubkey());
833
1
    builder.finish().unwrap();
834
1
}
835
836
#[test]
837
1
fn test_create_tx_default_fee_rate() {
838
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
839
1
    let addr = wallet.next_unused_address(KeychainKind::External);
840
1
    let mut builder = wallet.build_tx();
841
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
842
1
    let psbt = builder.finish().unwrap();
843
1
    let fee = check_fee!(wallet, psbt);
844
845
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::BROADCAST_MIN, @add_signature);
846
1
}
847
848
#[test]
849
1
fn test_create_tx_custom_fee_rate() {
850
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
851
1
    let addr = wallet.next_unused_address(KeychainKind::External);
852
1
    let mut builder = wallet.build_tx();
853
1
    builder
854
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
855
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
856
1
    let psbt = builder.finish().unwrap();
857
1
    let fee = check_fee!(wallet, psbt);
858
859
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
860
1
}
861
862
#[test]
863
1
fn test_create_tx_absolute_fee() {
864
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
865
1
    let addr = wallet.next_unused_address(KeychainKind::External);
866
1
    let mut builder = wallet.build_tx();
867
1
    builder
868
1
        .drain_to(addr.script_pubkey())
869
1
        .drain_wallet()
870
1
        .fee_absolute(Amount::from_sat(100));
871
1
    let psbt = builder.finish().unwrap();
872
1
    let fee = check_fee!(wallet, psbt);
873
874
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(100));
875
1
    assert_eq!(psbt.unsigned_tx.output.len(), 1);
876
1
    assert_eq!(
877
1
        psbt.unsigned_tx.output[0].value,
878
1
        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
879
1
    );
880
1
}
881
882
#[test]
883
1
fn test_create_tx_absolute_zero_fee() {
884
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
885
1
    let addr = wallet.next_unused_address(KeychainKind::External);
886
1
    let mut builder = wallet.build_tx();
887
1
    builder
888
1
        .drain_to(addr.script_pubkey())
889
1
        .drain_wallet()
890
1
        .fee_absolute(Amount::ZERO);
891
1
    let psbt = builder.finish().unwrap();
892
1
    let fee = check_fee!(wallet, psbt);
893
894
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::ZERO);
895
1
    assert_eq!(psbt.unsigned_tx.output.len(), 1);
896
1
    assert_eq!(
897
1
        psbt.unsigned_tx.output[0].value,
898
1
        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
899
1
    );
900
1
}
901
902
#[test]
903
#[should_panic(expected = "InsufficientFunds")]
904
1
fn test_create_tx_absolute_high_fee() {
905
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
906
1
    let addr = wallet.next_unused_address(KeychainKind::External);
907
1
    let mut builder = wallet.build_tx();
908
1
    builder
909
1
        .drain_to(addr.script_pubkey())
910
1
        .drain_wallet()
911
1
        .fee_absolute(Amount::from_sat(60_000));
912
1
    let _ = builder.finish().unwrap();
913
1
}
914
915
#[test]
916
1
fn test_create_tx_add_change() {
917
1
    use bdk_wallet::tx_builder::TxOrdering;
918
1
    let seed = [0; 32];
919
1
    let mut rng: StdRng = SeedableRng::from_seed(seed);
920
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
921
1
    let addr = wallet.next_unused_address(KeychainKind::External);
922
1
    let mut builder = wallet.build_tx();
923
1
    builder
924
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
925
1
        .ordering(TxOrdering::Shuffle);
926
1
    let psbt = builder.finish_with_aux_rand(&mut rng).unwrap();
927
1
    let fee = check_fee!(wallet, psbt);
928
929
1
    assert_eq!(psbt.unsigned_tx.output.len(), 2);
930
1
    assert_eq!(psbt.unsigned_tx.output[0].value, Amount::from_sat(25_000));
931
1
    assert_eq!(
932
1
        psbt.unsigned_tx.output[1].value,
933
1
        Amount::from_sat(25_000) - fee.unwrap_or(Amount::ZERO)
934
1
    );
935
1
}
936
937
#[test]
938
1
fn test_create_tx_skip_change_dust() {
939
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
940
1
    let addr = wallet.next_unused_address(KeychainKind::External);
941
1
    let mut builder = wallet.build_tx();
942
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(49_800));
943
1
    let psbt = builder.finish().unwrap();
944
1
    let fee = check_fee!(wallet, psbt);
945
946
1
    assert_eq!(psbt.unsigned_tx.output.len(), 1);
947
1
    assert_eq!(psbt.unsigned_tx.output[0].value.to_sat(), 49_800);
948
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
949
1
}
950
951
#[test]
952
#[should_panic(expected = "InsufficientFunds")]
953
1
fn test_create_tx_drain_to_dust_amount() {
954
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
955
1
    let addr = wallet.next_unused_address(KeychainKind::External);
956
1
    // very high fee rate, so that the only output would be below dust
957
1
    let mut builder = wallet.build_tx();
958
1
    builder
959
1
        .drain_to(addr.script_pubkey())
960
1
        .drain_wallet()
961
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(454));
962
1
    builder.finish().unwrap();
963
1
}
964
965
#[test]
966
1
fn test_create_tx_ordering_respected() {
967
1
    use alloc::sync::Arc;
968
1
969
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
970
1
    let addr = wallet.next_unused_address(KeychainKind::External);
971
1
972
1
    let bip69_txin_cmp = |tx_a: &TxIn, tx_b: &TxIn| {
973
0
        let project_outpoint = |t: &TxIn| (t.previous_output.txid, t.previous_output.vout);
974
0
        project_outpoint(tx_a).cmp(&project_outpoint(tx_b))
975
0
    };
976
977
3
    let 
bip69_txout_cmp1
= |tx_a: &TxOut, tx_b: &TxOut| {
978
6
        let project_utxo = |t: &TxOut| (t.value, t.script_pubkey.clone());
979
3
        project_utxo(tx_a).cmp(&project_utxo(tx_b))
980
3
    };
981
982
1
    let custom_bip69_ordering = bdk_wallet::tx_builder::TxOrdering::Custom {
983
1
        input_sort: Arc::new(bip69_txin_cmp),
984
1
        output_sort: Arc::new(bip69_txout_cmp),
985
1
    };
986
1
987
1
    let mut builder = wallet.build_tx();
988
1
    builder
989
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
990
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(10_000))
991
1
        .ordering(custom_bip69_ordering);
992
1
993
1
    let psbt = builder.finish().unwrap();
994
1
    let fee = check_fee!(wallet, psbt);
995
996
1
    assert_eq!(psbt.unsigned_tx.output.len(), 3);
997
1
    assert_eq!(
998
1
        psbt.unsigned_tx.output[0].value,
999
1
        Amount::from_sat(10_000) - fee.unwrap_or(Amount::ZERO)
1000
1
    );
1001
1
    assert_eq!(psbt.unsigned_tx.output[1].value, Amount::from_sat(10_000));
1002
1
    assert_eq!(psbt.unsigned_tx.output[2].value, Amount::from_sat(30_000));
1003
1
}
1004
1005
#[test]
1006
1
fn test_create_tx_default_sighash() {
1007
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1008
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1009
1
    let mut builder = wallet.build_tx();
1010
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(30_000));
1011
1
    let psbt = builder.finish().unwrap();
1012
1
1013
1
    assert_eq!(psbt.inputs[0].sighash_type, None);
1014
1
}
1015
1016
#[test]
1017
1
fn test_create_tx_custom_sighash() {
1018
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1019
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1020
1
    let mut builder = wallet.build_tx();
1021
1
    builder
1022
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1023
1
        .sighash(EcdsaSighashType::Single.into());
1024
1
    let psbt = builder.finish().unwrap();
1025
1
1026
1
    assert_eq!(
1027
1
        psbt.inputs[0].sighash_type,
1028
1
        Some(EcdsaSighashType::Single.into())
1029
1
    );
1030
1
}
1031
1032
#[test]
1033
1
fn test_create_tx_input_hd_keypaths() {
1034
1
    use bitcoin::bip32::{DerivationPath, Fingerprint};
1035
1
    use core::str::FromStr;
1036
1
1037
1
    let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
1038
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1039
1
    let mut builder = wallet.build_tx();
1040
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1041
1
    let psbt = builder.finish().unwrap();
1042
1
1043
1
    assert_eq!(psbt.inputs[0].bip32_derivation.len(), 1);
1044
1
    assert_eq!(
1045
1
        psbt.inputs[0].bip32_derivation.values().next().unwrap(),
1046
1
        &(
1047
1
            Fingerprint::from_str("d34db33f").unwrap(),
1048
1
            DerivationPath::from_str("m/44'/0'/0'/0/0").unwrap()
1049
1
        )
1050
1
    );
1051
1
}
1052
1053
#[test]
1054
1
fn test_create_tx_output_hd_keypaths() {
1055
1
    use bitcoin::bip32::{DerivationPath, Fingerprint};
1056
1
    use core::str::FromStr;
1057
1
1058
1
    let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
1059
1
1060
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1061
1
    let mut builder = wallet.build_tx();
1062
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1063
1
    let psbt = builder.finish().unwrap();
1064
1
1065
1
    assert_eq!(psbt.outputs[0].bip32_derivation.len(), 1);
1066
1
    let expected_derivation_path = format!("m/44'/0'/0'/0/{}", addr.index);
1067
1
    assert_eq!(
1068
1
        psbt.outputs[0].bip32_derivation.values().next().unwrap(),
1069
1
        &(
1070
1
            Fingerprint::from_str("d34db33f").unwrap(),
1071
1
            DerivationPath::from_str(&expected_derivation_path).unwrap()
1072
1
        )
1073
1
    );
1074
1
}
1075
1076
#[test]
1077
1
fn test_create_tx_set_redeem_script_p2sh() {
1078
1
    use bitcoin::hex::FromHex;
1079
1
1080
1
    let (mut wallet, _) =
1081
1
        get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1082
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1083
1
    let mut builder = wallet.build_tx();
1084
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1085
1
    let psbt = builder.finish().unwrap();
1086
1
1087
1
    assert_eq!(
1088
1
        psbt.inputs[0].redeem_script,
1089
1
        Some(ScriptBuf::from(
1090
1
            Vec::<u8>::from_hex(
1091
1
                "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
1092
1
            )
1093
1
            .unwrap()
1094
1
        ))
1095
1
    );
1096
1
    assert_eq!(psbt.inputs[0].witness_script, None);
1097
1
}
1098
1099
#[test]
1100
1
fn test_create_tx_set_witness_script_p2wsh() {
1101
1
    use bitcoin::hex::FromHex;
1102
1
1103
1
    let (mut wallet, _) =
1104
1
        get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1105
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1106
1
    let mut builder = wallet.build_tx();
1107
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1108
1
    let psbt = builder.finish().unwrap();
1109
1
1110
1
    assert_eq!(psbt.inputs[0].redeem_script, None);
1111
1
    assert_eq!(
1112
1
        psbt.inputs[0].witness_script,
1113
1
        Some(ScriptBuf::from(
1114
1
            Vec::<u8>::from_hex(
1115
1
                "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
1116
1
            )
1117
1
            .unwrap()
1118
1
        ))
1119
1
    );
1120
1
}
1121
1122
#[test]
1123
1
fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
1124
1
    let (mut wallet, _) =
1125
1
        get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
1126
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1127
1
    let mut builder = wallet.build_tx();
1128
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1129
1
    let psbt = builder.finish().unwrap();
1130
1
1131
1
    let script = ScriptBuf::from_hex(
1132
1
        "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
1133
1
    )
1134
1
    .unwrap();
1135
1
1136
1
    assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_p2wsh()));
1137
1
    assert_eq!(psbt.inputs[0].witness_script, Some(script));
1138
1
}
1139
1140
#[test]
1141
1
fn test_create_tx_non_witness_utxo() {
1142
1
    let (mut wallet, _) =
1143
1
        get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1144
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1145
1
    let mut builder = wallet.build_tx();
1146
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1147
1
    let psbt = builder.finish().unwrap();
1148
1
1149
1
    assert!(psbt.inputs[0].non_witness_utxo.is_some());
1150
1
    assert!(psbt.inputs[0].witness_utxo.is_none());
1151
1
}
1152
1153
#[test]
1154
1
fn test_create_tx_only_witness_utxo() {
1155
1
    let (mut wallet, _) =
1156
1
        get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1157
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1158
1
    let mut builder = wallet.build_tx();
1159
1
    builder
1160
1
        .drain_to(addr.script_pubkey())
1161
1
        .only_witness_utxo()
1162
1
        .drain_wallet();
1163
1
    let psbt = builder.finish().unwrap();
1164
1
1165
1
    assert!(psbt.inputs[0].non_witness_utxo.is_none());
1166
1
    assert!(psbt.inputs[0].witness_utxo.is_some());
1167
1
}
1168
1169
#[test]
1170
1
fn test_create_tx_shwpkh_has_witness_utxo() {
1171
1
    let (mut wallet, _) =
1172
1
        get_funded_wallet("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1173
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1174
1
    let mut builder = wallet.build_tx();
1175
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1176
1
    let psbt = builder.finish().unwrap();
1177
1
1178
1
    assert!(psbt.inputs[0].witness_utxo.is_some());
1179
1
}
1180
1181
#[test]
1182
1
fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() {
1183
1
    let (mut wallet, _) =
1184
1
        get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
1185
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1186
1
    let mut builder = wallet.build_tx();
1187
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
1188
1
    let psbt = builder.finish().unwrap();
1189
1
1190
1
    assert!(psbt.inputs[0].non_witness_utxo.is_some());
1191
1
    assert!(psbt.inputs[0].witness_utxo.is_some());
1192
1
}
1193
1194
#[test]
1195
1
fn test_create_tx_add_utxo() {
1196
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1197
1
    let small_output_tx = Transaction {
1198
1
        input: vec![],
1199
1
        output: vec![TxOut {
1200
1
            script_pubkey: wallet
1201
1
                .next_unused_address(KeychainKind::External)
1202
1
                .script_pubkey(),
1203
1
            value: Amount::from_sat(25_000),
1204
1
        }],
1205
1
        version: transaction::Version::non_standard(0),
1206
1
        lock_time: absolute::LockTime::ZERO,
1207
1
    };
1208
1
    let txid = small_output_tx.compute_txid();
1209
1
    wallet.insert_tx(small_output_tx);
1210
1
    insert_anchor_from_conf(
1211
1
        &mut wallet,
1212
1
        txid,
1213
1
        ConfirmationTime::Confirmed {
1214
1
            height: 2000,
1215
1
            time: 200,
1216
1
        },
1217
1
    );
1218
1
1219
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1220
1
        .unwrap()
1221
1
        .assume_checked();
1222
1
    let mut builder = wallet.build_tx();
1223
1
    builder
1224
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1225
1
        .add_utxo(OutPoint { txid, vout: 0 })
1226
1
        .unwrap();
1227
1
    let psbt = builder.finish().unwrap();
1228
1
    let sent_received =
1229
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1230
1
1231
1
    assert_eq!(
1232
1
        psbt.unsigned_tx.input.len(),
1233
        2,
1234
0
        "should add an additional input since 25_000 < 30_000"
1235
    );
1236
1
    assert_eq!(
1237
1
        sent_received.0,
1238
1
        Amount::from_sat(75_000),
1239
0
        "total should be sum of both inputs"
1240
    );
1241
1
}
1242
1243
#[test]
1244
#[should_panic(expected = "InsufficientFunds")]
1245
1
fn test_create_tx_manually_selected_insufficient() {
1246
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1247
1
    let small_output_tx = Transaction {
1248
1
        input: vec![],
1249
1
        output: vec![TxOut {
1250
1
            script_pubkey: wallet
1251
1
                .next_unused_address(KeychainKind::External)
1252
1
                .script_pubkey(),
1253
1
            value: Amount::from_sat(25_000),
1254
1
        }],
1255
1
        version: transaction::Version::non_standard(0),
1256
1
        lock_time: absolute::LockTime::ZERO,
1257
1
    };
1258
1
    let txid = small_output_tx.compute_txid();
1259
1
    wallet.insert_tx(small_output_tx.clone());
1260
1
    insert_anchor_from_conf(
1261
1
        &mut wallet,
1262
1
        txid,
1263
1
        ConfirmationTime::Confirmed {
1264
1
            height: 2000,
1265
1
            time: 200,
1266
1
        },
1267
1
    );
1268
1
1269
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1270
1
        .unwrap()
1271
1
        .assume_checked();
1272
1
    let mut builder = wallet.build_tx();
1273
1
    builder
1274
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1275
1
        .add_utxo(OutPoint { txid, vout: 0 })
1276
1
        .unwrap()
1277
1
        .manually_selected_only();
1278
1
    builder.finish().unwrap();
1279
1
}
1280
1281
#[test]
1282
#[should_panic(expected = "SpendingPolicyRequired(External)")]
1283
1
fn test_create_tx_policy_path_required() {
1284
1
    let (mut wallet, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
1285
1
1286
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1287
1
        .unwrap()
1288
1
        .assume_checked();
1289
1
    let mut builder = wallet.build_tx();
1290
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000));
1291
1
    builder.finish().unwrap();
1292
1
}
1293
1294
#[test]
1295
1
fn test_create_tx_policy_path_no_csv() {
1296
1
    let (descriptor, change_descriptor) = get_test_wpkh_with_change_desc();
1297
1
    let mut wallet = Wallet::create(descriptor, change_descriptor)
1298
1
        .network(Network::Regtest)
1299
1
        .create_wallet_no_persist()
1300
1
        .expect("wallet");
1301
1
1302
1
    let tx = Transaction {
1303
1
        version: transaction::Version::non_standard(0),
1304
1
        lock_time: absolute::LockTime::ZERO,
1305
1
        input: vec![],
1306
1
        output: vec![TxOut {
1307
1
            script_pubkey: wallet
1308
1
                .next_unused_address(KeychainKind::External)
1309
1
                .script_pubkey(),
1310
1
            value: Amount::from_sat(50_000),
1311
1
        }],
1312
1
    };
1313
1
    let txid = tx.compute_txid();
1314
1
    wallet.insert_tx(tx);
1315
1
    insert_seen_at(&mut wallet, txid, 0);
1316
1
1317
1
    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
1318
1
    let root_id = external_policy.id;
1319
1
    // child #0 is just the key "A"
1320
1
    let path = vec![(root_id, vec![0])].into_iter().collect();
1321
1
1322
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1323
1
        .unwrap()
1324
1
        .assume_checked();
1325
1
    let mut builder = wallet.build_tx();
1326
1
    builder
1327
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1328
1
        .policy_path(path, KeychainKind::External);
1329
1
    let psbt = builder.finish().unwrap();
1330
1
1331
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF));
1332
1
}
1333
1334
#[test]
1335
1
fn test_create_tx_policy_path_use_csv() {
1336
1
    let (mut wallet, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
1337
1
1338
1
    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
1339
1
    let root_id = external_policy.id;
1340
1
    // child #1 is or(pk(B),older(144))
1341
1
    let path = vec![(root_id, vec![1])].into_iter().collect();
1342
1
1343
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1344
1
        .unwrap()
1345
1
        .assume_checked();
1346
1
    let mut builder = wallet.build_tx();
1347
1
    builder
1348
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1349
1
        .policy_path(path, KeychainKind::External);
1350
1
    let psbt = builder.finish().unwrap();
1351
1
1352
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
1353
1
}
1354
1355
#[test]
1356
1
fn test_create_tx_policy_path_ignored_subtree_with_csv() {
1357
1
    let (mut 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)))))");
1358
1
1359
1
    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
1360
1
    let root_id = external_policy.id;
1361
1
    // child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
1362
1
    let path = vec![(root_id, vec![0])].into_iter().collect();
1363
1
1364
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1365
1
        .unwrap()
1366
1
        .assume_checked();
1367
1
    let mut builder = wallet.build_tx();
1368
1
    builder
1369
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
1370
1
        .policy_path(path, KeychainKind::External);
1371
1
    let psbt = builder.finish().unwrap();
1372
1
1373
1
    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
1374
1
}
1375
1376
#[test]
1377
1
fn test_create_tx_global_xpubs_with_origin() {
1378
1
    use bitcoin::bip32;
1379
1
    let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
1380
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1381
1
    let mut builder = wallet.build_tx();
1382
1
    builder
1383
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1384
1
        .add_global_xpubs();
1385
1
    let psbt = builder.finish().unwrap();
1386
1
1387
1
    let key = bip32::Xpub::from_str("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
1388
1
    let fingerprint = bip32::Fingerprint::from_hex("73756c7f").unwrap();
1389
1
    let path = bip32::DerivationPath::from_str("m/48'/0'/0'/2'").unwrap();
1390
1
1391
1
    assert_eq!(psbt.xpub.len(), 2);
1392
1
    assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
1393
1
}
1394
1395
#[test]
1396
1
fn test_add_foreign_utxo() {
1397
1
    let (mut wallet1, _) = get_funded_wallet_wpkh();
1398
1
    let (wallet2, _) =
1399
1
        get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
1400
1
1401
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1402
1
        .unwrap()
1403
1
        .assume_checked();
1404
1
    let utxo = wallet2.list_unspent().next().expect("must take!");
1405
1
    let foreign_utxo_satisfaction = wallet2
1406
1
        .public_descriptor(KeychainKind::External)
1407
1
        .max_weight_to_satisfy()
1408
1
        .unwrap();
1409
1
1410
1
    let psbt_input = psbt::Input {
1411
1
        witness_utxo: Some(utxo.txout.clone()),
1412
1
        ..Default::default()
1413
1
    };
1414
1
1415
1
    let mut builder = wallet1.build_tx();
1416
1
    builder
1417
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
1418
1
        .only_witness_utxo()
1419
1
        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
1420
1
        .unwrap();
1421
1
    let mut psbt = builder.finish().unwrap();
1422
1
    wallet1.insert_txout(utxo.outpoint, utxo.txout);
1423
1
    let fee = check_fee!(wallet1, psbt);
1424
1
    let sent_received =
1425
1
        wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1426
1
1427
1
    assert_eq!(
1428
1
        (sent_received.0 - sent_received.1),
1429
1
        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
1430
0
        "we should have only net spent ~10_000"
1431
    );
1432
1433
1
    assert!(
1434
1
        psbt.unsigned_tx
1435
1
            .input
1436
1
            .iter()
1437
2
            .any(|input| input.previous_output == utxo.outpoint
)1
,
1438
0
        "foreign_utxo should be in there"
1439
    );
1440
1441
1
    let finished = wallet1
1442
1
        .sign(
1443
1
            &mut psbt,
1444
1
            SignOptions {
1445
1
                trust_witness_utxo: true,
1446
1
                ..Default::default()
1447
1
            },
1448
1
        )
1449
1
        .unwrap();
1450
1
1451
1
    assert!(
1452
1
        !finished,
1453
0
        "only one of the inputs should have been signed so far"
1454
    );
1455
1456
1
    let finished = wallet2
1457
1
        .sign(
1458
1
            &mut psbt,
1459
1
            SignOptions {
1460
1
                trust_witness_utxo: true,
1461
1
                ..Default::default()
1462
1
            },
1463
1
        )
1464
1
        .unwrap();
1465
1
    assert!(finished, 
"all the inputs should have been signed now"0
);
1466
1
}
1467
1468
#[test]
1469
#[should_panic(
1470
    expected = "MissingTxOut([OutPoint { txid: 21d7fb1bceda00ab4069fc52d06baa13470803e9050edd16f5736e5d8c4925fd, vout: 0 }])"
1471
)]
1472
1
fn test_calculate_fee_with_missing_foreign_utxo() {
1473
1
    let (mut wallet1, _) = get_funded_wallet_wpkh();
1474
1
    let (wallet2, _) =
1475
1
        get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
1476
1
1477
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1478
1
        .unwrap()
1479
1
        .assume_checked();
1480
1
    let utxo = wallet2.list_unspent().next().expect("must take!");
1481
1
    let foreign_utxo_satisfaction = wallet2
1482
1
        .public_descriptor(KeychainKind::External)
1483
1
        .max_weight_to_satisfy()
1484
1
        .unwrap();
1485
1
1486
1
    let psbt_input = psbt::Input {
1487
1
        witness_utxo: Some(utxo.txout.clone()),
1488
1
        ..Default::default()
1489
1
    };
1490
1
1491
1
    let mut builder = wallet1.build_tx();
1492
1
    builder
1493
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
1494
1
        .only_witness_utxo()
1495
1
        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
1496
1
        .unwrap();
1497
1
    let psbt = builder.finish().unwrap();
1498
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1499
1
    wallet1.calculate_fee(&tx).unwrap();
1500
1
}
1501
1502
#[test]
1503
1
fn test_add_foreign_utxo_invalid_psbt_input() {
1504
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1505
1
    let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
1506
1
    let foreign_utxo_satisfaction = wallet
1507
1
        .public_descriptor(KeychainKind::External)
1508
1
        .max_weight_to_satisfy()
1509
1
        .unwrap();
1510
1
1511
1
    let mut builder = wallet.build_tx();
1512
1
    let result =
1513
1
        builder.add_foreign_utxo(outpoint, psbt::Input::default(), foreign_utxo_satisfaction);
1514
1
    assert!(
matches!0
(result, Err(AddForeignUtxoError::MissingUtxo)));
1515
1
}
1516
1517
#[test]
1518
1
fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
1519
1
    let (mut wallet1, txid1) = get_funded_wallet_wpkh();
1520
1
    let (wallet2, txid2) =
1521
1
        get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
1522
1
1523
1
    let utxo2 = wallet2.list_unspent().next().unwrap();
1524
1
    let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.tx.clone();
1525
1
    let tx2 = wallet2.get_tx(txid2).unwrap().tx_node.tx.clone();
1526
1
1527
1
    let satisfaction_weight = wallet2
1528
1
        .public_descriptor(KeychainKind::External)
1529
1
        .max_weight_to_satisfy()
1530
1
        .unwrap();
1531
1
1532
1
    let mut builder = wallet1.build_tx();
1533
1
    assert!(
1534
1
        builder
1535
1
            .add_foreign_utxo(
1536
1
                utxo2.outpoint,
1537
1
                psbt::Input {
1538
1
                    non_witness_utxo: Some(tx1.as_ref().clone()),
1539
1
                    ..Default::default()
1540
1
                },
1541
1
                satisfaction_weight
1542
1
            )
1543
1
            .is_err(),
1544
0
        "should fail when outpoint doesn't match psbt_input"
1545
    );
1546
1
    assert!(
1547
1
        builder
1548
1
            .add_foreign_utxo(
1549
1
                utxo2.outpoint,
1550
1
                psbt::Input {
1551
1
                    non_witness_utxo: Some(tx2.as_ref().clone()),
1552
1
                    ..Default::default()
1553
1
                },
1554
1
                satisfaction_weight
1555
1
            )
1556
1
            .is_ok(),
1557
0
        "should be ok when outpoint does match psbt_input"
1558
    );
1559
1
}
1560
1561
#[test]
1562
1
fn test_add_foreign_utxo_only_witness_utxo() {
1563
1
    let (mut wallet1, _) = get_funded_wallet_wpkh();
1564
1
    let (wallet2, txid2) =
1565
1
        get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
1566
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1567
1
        .unwrap()
1568
1
        .assume_checked();
1569
1
    let utxo2 = wallet2.list_unspent().next().unwrap();
1570
1
1571
1
    let satisfaction_weight = wallet2
1572
1
        .public_descriptor(KeychainKind::External)
1573
1
        .max_weight_to_satisfy()
1574
1
        .unwrap();
1575
1
1576
1
    let mut builder = wallet1.build_tx();
1577
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000));
1578
1
1579
1
    {
1580
1
        let mut builder = builder.clone();
1581
1
        let psbt_input = psbt::Input {
1582
1
            witness_utxo: Some(utxo2.txout.clone()),
1583
1
            ..Default::default()
1584
1
        };
1585
1
        builder
1586
1
            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
1587
1
            .unwrap();
1588
1
        assert!(
1589
1
            builder.finish().is_err(),
1590
0
            "psbt_input with witness_utxo should fail with only witness_utxo"
1591
        );
1592
    }
1593
1594
    {
1595
1
        let mut builder = builder.clone();
1596
1
        let psbt_input = psbt::Input {
1597
1
            witness_utxo: Some(utxo2.txout.clone()),
1598
1
            ..Default::default()
1599
1
        };
1600
1
        builder
1601
1
            .only_witness_utxo()
1602
1
            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
1603
1
            .unwrap();
1604
1
        assert!(
1605
1
            builder.finish().is_ok(),
1606
0
            "psbt_input with just witness_utxo should succeed when `only_witness_utxo` is enabled"
1607
        );
1608
    }
1609
1610
    {
1611
1
        let mut builder = builder.clone();
1612
1
        let tx2 = wallet2.get_tx(txid2).unwrap().tx_node.tx;
1613
1
        let psbt_input = psbt::Input {
1614
1
            non_witness_utxo: Some(tx2.as_ref().clone()),
1615
1
            ..Default::default()
1616
1
        };
1617
1
        builder
1618
1
            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
1619
1
            .unwrap();
1620
1
        assert!(
1621
1
            builder.finish().is_ok(),
1622
0
            "psbt_input with non_witness_utxo should succeed by default"
1623
        );
1624
    }
1625
1
}
1626
1627
#[test]
1628
1
fn test_get_psbt_input() {
1629
1
    // this should grab a known good utxo and set the input
1630
1
    let (wallet, _) = get_funded_wallet_wpkh();
1631
1
    for utxo in wallet.list_unspent() {
1632
1
        let psbt_input = wallet.get_psbt_input(utxo, None, false).unwrap();
1633
1
        assert!(psbt_input.witness_utxo.is_some() || 
psbt_input.non_witness_utxo.is_some()0
);
1634
    }
1635
1
}
1636
1637
#[test]
1638
#[should_panic(
1639
    expected = "MissingKeyOrigin(\"tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3\")"
1640
)]
1641
1
fn test_create_tx_global_xpubs_origin_missing() {
1642
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
1643
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1644
1
    let mut builder = wallet.build_tx();
1645
1
    builder
1646
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1647
1
        .add_global_xpubs();
1648
1
    builder.finish().unwrap();
1649
1
}
1650
1651
#[test]
1652
1
fn test_create_tx_global_xpubs_master_without_origin() {
1653
1
    use bitcoin::bip32;
1654
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
1655
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1656
1
    let mut builder = wallet.build_tx();
1657
1
    builder
1658
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1659
1
        .add_global_xpubs();
1660
1
    let psbt = builder.finish().unwrap();
1661
1
1662
1
    let key = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
1663
1
    let fingerprint = bip32::Fingerprint::from_hex("997a323b").unwrap();
1664
1
1665
1
    assert_eq!(psbt.xpub.len(), 2);
1666
1
    assert_eq!(
1667
1
        psbt.xpub.get(&key),
1668
1
        Some(&(fingerprint, bip32::DerivationPath::default()))
1669
1
    );
1670
1
}
1671
1672
#[test]
1673
#[should_panic(expected = "IrreplaceableTransaction")]
1674
1
fn test_bump_fee_irreplaceable_tx() {
1675
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1676
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1677
1
    let mut builder = wallet.build_tx();
1678
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
1679
1
    let psbt = builder.finish().unwrap();
1680
1
1681
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1682
1
    let txid = tx.compute_txid();
1683
1
    wallet.insert_tx(tx);
1684
1
    insert_seen_at(&mut wallet, txid, 0);
1685
1
    wallet.build_fee_bump(txid).unwrap().finish().unwrap();
1686
1
}
1687
1688
#[test]
1689
#[should_panic(expected = "TransactionConfirmed")]
1690
1
fn test_bump_fee_confirmed_tx() {
1691
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1692
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1693
1
    let mut builder = wallet.build_tx();
1694
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
1695
1
    let psbt = builder.finish().unwrap();
1696
1
1697
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1698
1
    let txid = tx.compute_txid();
1699
1
1700
1
    wallet.insert_tx(tx);
1701
1
    insert_anchor_from_conf(
1702
1
        &mut wallet,
1703
1
        txid,
1704
1
        ConfirmationTime::Confirmed {
1705
1
            height: 42,
1706
1
            time: 42_000,
1707
1
        },
1708
1
    );
1709
1
1710
1
    wallet.build_fee_bump(txid).unwrap().finish().unwrap();
1711
1
}
1712
1713
#[test]
1714
1
fn test_bump_fee_low_fee_rate() {
1715
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1716
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1717
1
    let mut builder = wallet.build_tx();
1718
1
    builder
1719
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1720
1
        .enable_rbf();
1721
1
    let psbt = builder.finish().unwrap();
1722
1
    let feerate = psbt.fee_rate().unwrap();
1723
1
1724
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1725
1
    let txid = tx.compute_txid();
1726
1
1727
1
    wallet.insert_tx(tx);
1728
1
    insert_seen_at(&mut wallet, txid, 0);
1729
1
1730
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1731
1
    builder.fee_rate(FeeRate::BROADCAST_MIN);
1732
1
    let res = builder.finish();
1733
0
    assert_matches!(
1734
1
        res,
1735
        Err(CreateTxError::FeeRateTooLow { .. }),
1736
        "expected FeeRateTooLow error"
1737
    );
1738
1739
1
    let required = feerate.to_sat_per_kwu() + 250; // +1 sat/vb
1740
1
    let sat_vb = required as f64 / 250.0;
1741
1
    let expect = format!("Fee rate too low: required {} sat/vb", sat_vb);
1742
1
    assert_eq!(res.unwrap_err().to_string(), expect);
1743
1
}
1744
1745
#[test]
1746
#[should_panic(expected = "FeeTooLow")]
1747
1
fn test_bump_fee_low_abs() {
1748
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1749
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1750
1
    let mut builder = wallet.build_tx();
1751
1
    builder
1752
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1753
1
        .enable_rbf();
1754
1
    let psbt = builder.finish().unwrap();
1755
1
1756
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1757
1
    let txid = tx.compute_txid();
1758
1
1759
1
    wallet.insert_tx(tx);
1760
1
    insert_seen_at(&mut wallet, txid, 0);
1761
1
1762
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1763
1
    builder.fee_absolute(Amount::from_sat(10));
1764
1
    builder.finish().unwrap();
1765
1
}
1766
1767
#[test]
1768
#[should_panic(expected = "FeeTooLow")]
1769
1
fn test_bump_fee_zero_abs() {
1770
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1771
1
    let addr = wallet.next_unused_address(KeychainKind::External);
1772
1
    let mut builder = wallet.build_tx();
1773
1
    builder
1774
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1775
1
        .enable_rbf();
1776
1
    let psbt = builder.finish().unwrap();
1777
1
1778
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1779
1
    let txid = tx.compute_txid();
1780
1
    wallet.insert_tx(tx);
1781
1
    insert_seen_at(&mut wallet, txid, 0);
1782
1
1783
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1784
1
    builder.fee_absolute(Amount::ZERO);
1785
1
    builder.finish().unwrap();
1786
1
}
1787
1788
#[test]
1789
1
fn test_bump_fee_reduce_change() {
1790
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1791
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1792
1
        .unwrap()
1793
1
        .assume_checked();
1794
1
    let mut builder = wallet.build_tx();
1795
1
    builder
1796
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
1797
1
        .enable_rbf();
1798
1
    let psbt = builder.finish().unwrap();
1799
1
    let original_sent_received =
1800
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1801
1
    let original_fee = check_fee!(wallet, psbt);
1802
1803
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1804
1
    let txid = tx.compute_txid();
1805
1
    wallet.insert_tx(tx);
1806
1
    insert_seen_at(&mut wallet, txid, 0);
1807
1
1808
1
    let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb
1809
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1810
1
    builder.fee_rate(feerate).enable_rbf();
1811
1
    let psbt = builder.finish().unwrap();
1812
1
    let sent_received =
1813
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1814
1
    let fee = check_fee!(wallet, psbt);
1815
1816
1
    assert_eq!(sent_received.0, original_sent_received.0);
1817
1
    assert_eq!(
1818
1
        sent_received.1 + fee.unwrap_or(Amount::ZERO),
1819
1
        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
1820
1
    );
1821
1
    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
1822
1823
1
    let tx = &psbt.unsigned_tx;
1824
1
    assert_eq!(tx.output.len(), 2);
1825
1
    assert_eq!(
1826
1
        tx.output
1827
1
            .iter()
1828
2
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
1829
1
            .unwrap()
1830
1
            .value,
1831
1
        Amount::from_sat(25_000)
1832
1
    );
1833
1
    assert_eq!(
1834
1
        tx.output
1835
1
            .iter()
1836
1
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
1837
1
            .unwrap()
1838
1
            .value,
1839
1
        sent_received.1
1840
1
    );
1841
1842
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
1843
1844
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1845
1
    builder.fee_absolute(Amount::from_sat(200));
1846
1
    builder.enable_rbf();
1847
1
    let psbt = builder.finish().unwrap();
1848
1
    let sent_received =
1849
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1850
1
    let fee = check_fee!(wallet, psbt);
1851
1852
1
    assert_eq!(sent_received.0, original_sent_received.0);
1853
1
    assert_eq!(
1854
1
        sent_received.1 + fee.unwrap_or(Amount::ZERO),
1855
1
        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
1856
1
    );
1857
1
    assert!(
1858
1
        fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO),
1859
0
        "{} > {}",
1860
0
        fee.unwrap_or(Amount::ZERO),
1861
0
        original_fee.unwrap_or(Amount::ZERO)
1862
    );
1863
1864
1
    let tx = &psbt.unsigned_tx;
1865
1
    assert_eq!(tx.output.len(), 2);
1866
1
    assert_eq!(
1867
1
        tx.output
1868
1
            .iter()
1869
1
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
1870
1
            .unwrap()
1871
1
            .value,
1872
1
        Amount::from_sat(25_000)
1873
1
    );
1874
1
    assert_eq!(
1875
1
        tx.output
1876
1
            .iter()
1877
2
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
1878
1
            .unwrap()
1879
1
            .value,
1880
1
        sent_received.1
1881
1
    );
1882
1883
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
1884
1
}
1885
1886
#[test]
1887
1
fn test_bump_fee_reduce_single_recipient() {
1888
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1889
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1890
1
        .unwrap()
1891
1
        .assume_checked();
1892
1
    let mut builder = wallet.build_tx();
1893
1
    builder
1894
1
        .drain_to(addr.script_pubkey())
1895
1
        .drain_wallet()
1896
1
        .enable_rbf();
1897
1
    let psbt = builder.finish().unwrap();
1898
1
    let tx = psbt.clone().extract_tx().expect("failed to extract tx");
1899
1
    let original_sent_received = wallet.sent_and_received(&tx);
1900
1
    let original_fee = check_fee!(wallet, psbt);
1901
1
    let txid = tx.compute_txid();
1902
1
    wallet.insert_tx(tx);
1903
1
    insert_seen_at(&mut wallet, txid, 0);
1904
1
1905
1
    let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb
1906
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1907
1
    builder
1908
1
        .fee_rate(feerate)
1909
1
        // remove original tx drain_to address and amount
1910
1
        .set_recipients(Vec::new())
1911
1
        // set back original drain_to address
1912
1
        .drain_to(addr.script_pubkey())
1913
1
        // drain wallet output amount will be re-calculated with new fee rate
1914
1
        .drain_wallet();
1915
1
    let psbt = builder.finish().unwrap();
1916
1
    let sent_received =
1917
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
1918
1
    let fee = check_fee!(wallet, psbt);
1919
1920
1
    assert_eq!(sent_received.0, original_sent_received.0);
1921
1
    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
1922
1923
1
    let tx = &psbt.unsigned_tx;
1924
1
    assert_eq!(tx.output.len(), 1);
1925
1
    assert_eq!(
1926
1
        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
1927
1
        sent_received.0
1928
1
    );
1929
1930
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
1931
1
}
1932
1933
#[test]
1934
1
fn test_bump_fee_absolute_reduce_single_recipient() {
1935
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1936
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1937
1
        .unwrap()
1938
1
        .assume_checked();
1939
1
    let mut builder = wallet.build_tx();
1940
1
    builder
1941
1
        .drain_to(addr.script_pubkey())
1942
1
        .drain_wallet()
1943
1
        .enable_rbf();
1944
1
    let psbt = builder.finish().unwrap();
1945
1
    let original_fee = check_fee!(wallet, psbt);
1946
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
1947
1
    let original_sent_received = wallet.sent_and_received(&tx);
1948
1
    let txid = tx.compute_txid();
1949
1
    wallet.insert_tx(tx);
1950
1
    insert_seen_at(&mut wallet, txid, 0);
1951
1
1952
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
1953
1
    builder
1954
1
        .fee_absolute(Amount::from_sat(300))
1955
1
        // remove original tx drain_to address and amount
1956
1
        .set_recipients(Vec::new())
1957
1
        // set back original drain_to address
1958
1
        .drain_to(addr.script_pubkey())
1959
1
        // drain wallet output amount will be re-calculated with new fee rate
1960
1
        .drain_wallet();
1961
1
    let psbt = builder.finish().unwrap();
1962
1
    let tx = &psbt.unsigned_tx;
1963
1
    let sent_received = wallet.sent_and_received(tx);
1964
1
    let fee = check_fee!(wallet, psbt);
1965
1966
1
    assert_eq!(sent_received.0, original_sent_received.0);
1967
1
    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
1968
1969
1
    assert_eq!(tx.output.len(), 1);
1970
1
    assert_eq!(
1971
1
        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
1972
1
        sent_received.0
1973
1
    );
1974
1975
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(300));
1976
1
}
1977
1978
#[test]
1979
1
fn test_bump_fee_drain_wallet() {
1980
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
1981
1
    // receive an extra tx so that our wallet has two utxos.
1982
1
    let tx = Transaction {
1983
1
        version: transaction::Version::ONE,
1984
1
        lock_time: absolute::LockTime::ZERO,
1985
1
        input: vec![],
1986
1
        output: vec![TxOut {
1987
1
            script_pubkey: wallet
1988
1
                .next_unused_address(KeychainKind::External)
1989
1
                .script_pubkey(),
1990
1
            value: Amount::from_sat(25_000),
1991
1
        }],
1992
1
    };
1993
1
    let txid = tx.compute_txid();
1994
1
    let tip = wallet.latest_checkpoint().height();
1995
1
    wallet.insert_tx(tx.clone());
1996
1
    insert_anchor_from_conf(
1997
1
        &mut wallet,
1998
1
        txid,
1999
1
        ConfirmationTime::Confirmed {
2000
1
            height: tip,
2001
1
            time: 42_000,
2002
1
        },
2003
1
    );
2004
1
2005
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2006
1
        .unwrap()
2007
1
        .assume_checked();
2008
1
2009
1
    let mut builder = wallet.build_tx();
2010
1
    builder
2011
1
        .drain_to(addr.script_pubkey())
2012
1
        .add_utxo(OutPoint {
2013
1
            txid: tx.compute_txid(),
2014
1
            vout: 0,
2015
1
        })
2016
1
        .unwrap()
2017
1
        .manually_selected_only()
2018
1
        .enable_rbf();
2019
1
    let psbt = builder.finish().unwrap();
2020
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
2021
1
    let original_sent_received = wallet.sent_and_received(&tx);
2022
1
2023
1
    let txid = tx.compute_txid();
2024
1
    wallet.insert_tx(tx);
2025
1
    insert_seen_at(&mut wallet, txid, 0);
2026
1
    assert_eq!(original_sent_received.0, Amount::from_sat(25_000));
2027
2028
    // for the new feerate, it should be enough to reduce the output, but since we specify
2029
    // `drain_wallet` we expect to spend everything
2030
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2031
1
    builder
2032
1
        .drain_wallet()
2033
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
2034
1
    let psbt = builder.finish().unwrap();
2035
1
    let sent_received = wallet.sent_and_received(&psbt.extract_tx().expect("failed to extract tx"));
2036
1
2037
1
    assert_eq!(sent_received.0, Amount::from_sat(75_000));
2038
1
}
2039
2040
#[test]
2041
#[should_panic(expected = "InsufficientFunds")]
2042
1
fn test_bump_fee_remove_output_manually_selected_only() {
2043
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2044
1
    // receive an extra tx so that our wallet has two utxos. then we manually pick only one of
2045
1
    // them, and make sure that `bump_fee` doesn't try to add more. This fails because we've
2046
1
    // told the wallet it's not allowed to add more inputs AND it can't reduce the value of the
2047
1
    // existing output. In other words, bump_fee + manually_selected_only is always an error
2048
1
    // unless there is a change output.
2049
1
    let init_tx = Transaction {
2050
1
        version: transaction::Version::ONE,
2051
1
        lock_time: absolute::LockTime::ZERO,
2052
1
        input: vec![],
2053
1
        output: vec![TxOut {
2054
1
            script_pubkey: wallet
2055
1
                .next_unused_address(KeychainKind::External)
2056
1
                .script_pubkey(),
2057
1
            value: Amount::from_sat(25_000),
2058
1
        }],
2059
1
    };
2060
1
    let position: ConfirmationTime = wallet
2061
1
        .transactions()
2062
1
        .last()
2063
1
        .unwrap()
2064
1
        .chain_position
2065
1
        .cloned()
2066
1
        .into();
2067
1
2068
1
    wallet.insert_tx(init_tx.clone());
2069
1
    insert_anchor_from_conf(&mut wallet, init_tx.compute_txid(), position);
2070
1
2071
1
    let outpoint = OutPoint {
2072
1
        txid: init_tx.compute_txid(),
2073
1
        vout: 0,
2074
1
    };
2075
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2076
1
        .unwrap()
2077
1
        .assume_checked();
2078
1
    let mut builder = wallet.build_tx();
2079
1
    builder
2080
1
        .drain_to(addr.script_pubkey())
2081
1
        .add_utxo(outpoint)
2082
1
        .unwrap()
2083
1
        .manually_selected_only()
2084
1
        .enable_rbf();
2085
1
    let psbt = builder.finish().unwrap();
2086
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
2087
1
    let original_sent_received = wallet.sent_and_received(&tx);
2088
1
    let txid = tx.compute_txid();
2089
1
    wallet.insert_tx(tx);
2090
1
    insert_seen_at(&mut wallet, txid, 0);
2091
1
    assert_eq!(original_sent_received.0, Amount::from_sat(25_000));
2092
2093
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2094
1
    builder
2095
1
        .manually_selected_only()
2096
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(255));
2097
1
    builder.finish().unwrap();
2098
1
}
2099
2100
#[test]
2101
1
fn test_bump_fee_add_input() {
2102
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2103
1
    let init_tx = Transaction {
2104
1
        version: transaction::Version::ONE,
2105
1
        lock_time: absolute::LockTime::ZERO,
2106
1
        input: vec![],
2107
1
        output: vec![TxOut {
2108
1
            script_pubkey: wallet
2109
1
                .next_unused_address(KeychainKind::External)
2110
1
                .script_pubkey(),
2111
1
            value: Amount::from_sat(25_000),
2112
1
        }],
2113
1
    };
2114
1
    let txid = init_tx.compute_txid();
2115
1
    let pos: ConfirmationTime = wallet
2116
1
        .transactions()
2117
1
        .last()
2118
1
        .unwrap()
2119
1
        .chain_position
2120
1
        .cloned()
2121
1
        .into();
2122
1
    wallet.insert_tx(init_tx);
2123
1
    insert_anchor_from_conf(&mut wallet, txid, pos);
2124
1
2125
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2126
1
        .unwrap()
2127
1
        .assume_checked();
2128
1
    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2129
1
    builder
2130
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2131
1
        .enable_rbf();
2132
1
    let psbt = builder.finish().unwrap();
2133
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
2134
1
    let original_details = wallet.sent_and_received(&tx);
2135
1
    let txid = tx.compute_txid();
2136
1
    wallet.insert_tx(tx);
2137
1
    insert_seen_at(&mut wallet, txid, 0);
2138
1
2139
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2140
1
    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
2141
1
    let psbt = builder.finish().unwrap();
2142
1
    let sent_received =
2143
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2144
1
    let fee = check_fee!(wallet, psbt);
2145
1
    assert_eq!(
2146
1
        sent_received.0,
2147
1
        original_details.0 + Amount::from_sat(25_000)
2148
1
    );
2149
1
    assert_eq!(
2150
1
        fee.unwrap_or(Amount::ZERO) + sent_received.1,
2151
1
        Amount::from_sat(30_000)
2152
1
    );
2153
2154
1
    let tx = &psbt.unsigned_tx;
2155
1
    assert_eq!(tx.input.len(), 2);
2156
1
    assert_eq!(tx.output.len(), 2);
2157
1
    assert_eq!(
2158
1
        tx.output
2159
1
            .iter()
2160
2
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2161
1
            .unwrap()
2162
1
            .value,
2163
1
        Amount::from_sat(45_000)
2164
1
    );
2165
1
    assert_eq!(
2166
1
        tx.output
2167
1
            .iter()
2168
1
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
2169
1
            .unwrap()
2170
1
            .value,
2171
1
        sent_received.1
2172
1
    );
2173
2174
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
2175
1
}
2176
2177
#[test]
2178
1
fn test_bump_fee_absolute_add_input() {
2179
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2180
1
    receive_output_in_latest_block(&mut wallet, 25_000);
2181
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2182
1
        .unwrap()
2183
1
        .assume_checked();
2184
1
    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2185
1
    builder
2186
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2187
1
        .enable_rbf();
2188
1
    let psbt = builder.finish().unwrap();
2189
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
2190
1
    let original_sent_received = wallet.sent_and_received(&tx);
2191
1
    let txid = tx.compute_txid();
2192
1
    wallet.insert_tx(tx);
2193
1
    insert_seen_at(&mut wallet, txid, 0);
2194
1
2195
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2196
1
    builder.fee_absolute(Amount::from_sat(6_000));
2197
1
    let psbt = builder.finish().unwrap();
2198
1
    let sent_received =
2199
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2200
1
    let fee = check_fee!(wallet, psbt);
2201
2202
1
    assert_eq!(
2203
1
        sent_received.0,
2204
1
        original_sent_received.0 + Amount::from_sat(25_000)
2205
1
    );
2206
1
    assert_eq!(
2207
1
        fee.unwrap_or(Amount::ZERO) + sent_received.1,
2208
1
        Amount::from_sat(30_000)
2209
1
    );
2210
2211
1
    let tx = &psbt.unsigned_tx;
2212
1
    assert_eq!(tx.input.len(), 2);
2213
1
    assert_eq!(tx.output.len(), 2);
2214
1
    assert_eq!(
2215
1
        tx.output
2216
1
            .iter()
2217
1
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2218
1
            .unwrap()
2219
1
            .value,
2220
1
        Amount::from_sat(45_000)
2221
1
    );
2222
1
    assert_eq!(
2223
1
        tx.output
2224
1
            .iter()
2225
2
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
2226
1
            .unwrap()
2227
1
            .value,
2228
1
        sent_received.1
2229
1
    );
2230
2231
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(6_000));
2232
1
}
2233
2234
#[test]
2235
1
fn test_bump_fee_no_change_add_input_and_change() {
2236
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2237
1
    let op = receive_output_in_latest_block(&mut wallet, 25_000);
2238
1
2239
1
    // initially make a tx without change by using `drain_to`
2240
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2241
1
        .unwrap()
2242
1
        .assume_checked();
2243
1
    let mut builder = wallet.build_tx();
2244
1
    builder
2245
1
        .drain_to(addr.script_pubkey())
2246
1
        .add_utxo(op)
2247
1
        .unwrap()
2248
1
        .manually_selected_only()
2249
1
        .enable_rbf();
2250
1
    let psbt = builder.finish().unwrap();
2251
1
    let original_sent_received =
2252
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2253
1
    let original_fee = check_fee!(wallet, psbt);
2254
2255
1
    let tx = psbt.extract_tx().expect("failed to extract tx");
2256
1
    let txid = tx.compute_txid();
2257
1
    wallet.insert_tx(tx);
2258
1
    insert_seen_at(&mut wallet, txid, 0);
2259
1
2260
1
    // Now bump the fees, the wallet should add an extra input and a change output, and leave
2261
1
    // the original output untouched.
2262
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2263
1
    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
2264
1
    let psbt = builder.finish().unwrap();
2265
1
    let sent_received =
2266
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2267
1
    let fee = check_fee!(wallet, psbt);
2268
2269
1
    let original_send_all_amount = original_sent_received.0 - original_fee.unwrap_or(Amount::ZERO);
2270
1
    assert_eq!(
2271
1
        sent_received.0,
2272
1
        original_sent_received.0 + Amount::from_sat(50_000)
2273
1
    );
2274
1
    assert_eq!(
2275
1
        sent_received.1,
2276
1
        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
2277
1
    );
2278
2279
1
    let tx = &psbt.unsigned_tx;
2280
1
    assert_eq!(tx.input.len(), 2);
2281
1
    assert_eq!(tx.output.len(), 2);
2282
1
    assert_eq!(
2283
1
        tx.output
2284
1
            .iter()
2285
2
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2286
1
            .unwrap()
2287
1
            .value,
2288
1
        original_send_all_amount
2289
1
    );
2290
1
    assert_eq!(
2291
1
        tx.output
2292
1
            .iter()
2293
1
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
2294
1
            .unwrap()
2295
1
            .value,
2296
1
        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
2297
1
    );
2298
2299
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
2300
1
}
2301
2302
#[test]
2303
1
fn test_bump_fee_add_input_change_dust() {
2304
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2305
1
    receive_output_in_latest_block(&mut wallet, 25_000);
2306
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2307
1
        .unwrap()
2308
1
        .assume_checked();
2309
1
    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2310
1
    builder
2311
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2312
1
        .enable_rbf();
2313
1
    let psbt = builder.finish().unwrap();
2314
1
    let original_sent_received =
2315
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2316
1
    let original_fee = check_fee!(wallet, psbt);
2317
2318
1
    let mut tx = psbt.extract_tx().expect("failed to extract tx");
2319
2
    for 
txin1
in &mut tx.input {
2320
1
        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // to get realistic weight
2321
1
    }
2322
1
    let original_tx_weight = tx.weight();
2323
1
    assert_eq!(tx.input.len(), 1);
2324
1
    assert_eq!(tx.output.len(), 2);
2325
1
    let txid = tx.compute_txid();
2326
1
    wallet.insert_tx(tx);
2327
1
    insert_seen_at(&mut wallet, txid, 0);
2328
1
2329
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2330
1
    // We set a fee high enough that during rbf we are forced to add
2331
1
    // a new input and also that we have to remove the change
2332
1
    // that we had previously
2333
1
2334
1
    // We calculate the new weight as:
2335
1
    //   original weight
2336
1
    // + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4
2337
1
    // + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4
2338
1
    // - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4
2339
1
    let new_tx_weight =
2340
1
        original_tx_weight + Weight::from_wu(160) + Weight::from_wu(112) - Weight::from_wu(124);
2341
1
    // two inputs (50k, 25k) and one output (45k) - epsilon
2342
1
    // We use epsilon here to avoid asking for a slightly too high feerate
2343
1
    let fee_abs = 50_000 + 25_000 - 45_000 - 10;
2344
1
    builder.fee_rate(Amount::from_sat(fee_abs) / new_tx_weight);
2345
1
    let psbt = builder.finish().unwrap();
2346
1
    let sent_received =
2347
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2348
1
    let fee = check_fee!(wallet, psbt);
2349
2350
1
    assert_eq!(
2351
1
        original_sent_received.1,
2352
1
        Amount::from_sat(5_000) - original_fee.unwrap_or(Amount::ZERO)
2353
1
    );
2354
2355
1
    assert_eq!(
2356
1
        sent_received.0,
2357
1
        original_sent_received.0 + Amount::from_sat(25_000)
2358
1
    );
2359
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(30_000));
2360
1
    assert_eq!(sent_received.1, Amount::ZERO);
2361
2362
1
    let tx = &psbt.unsigned_tx;
2363
1
    assert_eq!(tx.input.len(), 2);
2364
1
    assert_eq!(tx.output.len(), 1);
2365
1
    assert_eq!(
2366
1
        tx.output
2367
1
            .iter()
2368
1
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2369
1
            .unwrap()
2370
1
            .value,
2371
1
        Amount::from_sat(45_000)
2372
1
    );
2373
2374
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(140), @dust_change, @add_signature);
2375
1
}
2376
2377
#[test]
2378
1
fn test_bump_fee_force_add_input() {
2379
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2380
1
    let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
2381
1
2382
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2383
1
        .unwrap()
2384
1
        .assume_checked();
2385
1
    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2386
1
    builder
2387
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2388
1
        .enable_rbf();
2389
1
    let psbt = builder.finish().unwrap();
2390
1
    let mut tx = psbt.extract_tx().expect("failed to extract tx");
2391
1
    let original_sent_received = wallet.sent_and_received(&tx);
2392
1
    let txid = tx.compute_txid();
2393
2
    for 
txin1
in &mut tx.input {
2394
1
        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
2395
1
    }
2396
1
    wallet.insert_tx(tx.clone());
2397
1
    insert_seen_at(&mut wallet, txid, 0);
2398
1
    // the new fee_rate is low enough that just reducing the change would be fine, but we force
2399
1
    // the addition of an extra input with `add_utxo()`
2400
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2401
1
    builder
2402
1
        .add_utxo(incoming_op)
2403
1
        .unwrap()
2404
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
2405
1
    let psbt = builder.finish().unwrap();
2406
1
    let sent_received =
2407
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2408
1
    let fee = check_fee!(wallet, psbt);
2409
2410
1
    assert_eq!(
2411
1
        sent_received.0,
2412
1
        original_sent_received.0 + Amount::from_sat(25_000)
2413
1
    );
2414
1
    assert_eq!(
2415
1
        fee.unwrap_or(Amount::ZERO) + sent_received.1,
2416
1
        Amount::from_sat(30_000)
2417
1
    );
2418
2419
1
    let tx = &psbt.unsigned_tx;
2420
1
    assert_eq!(tx.input.len(), 2);
2421
1
    assert_eq!(tx.output.len(), 2);
2422
1
    assert_eq!(
2423
1
        tx.output
2424
1
            .iter()
2425
2
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2426
1
            .unwrap()
2427
1
            .value,
2428
1
        Amount::from_sat(45_000)
2429
1
    );
2430
1
    assert_eq!(
2431
1
        tx.output
2432
1
            .iter()
2433
1
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
2434
1
            .unwrap()
2435
1
            .value,
2436
1
        sent_received.1
2437
1
    );
2438
2439
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
2440
1
}
2441
2442
#[test]
2443
1
fn test_bump_fee_absolute_force_add_input() {
2444
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2445
1
    let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
2446
1
2447
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2448
1
        .unwrap()
2449
1
        .assume_checked();
2450
1
    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2451
1
    builder
2452
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2453
1
        .enable_rbf();
2454
1
    let psbt = builder.finish().unwrap();
2455
1
    let mut tx = psbt.extract_tx().expect("failed to extract tx");
2456
1
    let original_sent_received = wallet.sent_and_received(&tx);
2457
1
    let txid = tx.compute_txid();
2458
    // skip saving the new utxos, we know they can't be used anyways
2459
2
    for 
txin1
in &mut tx.input {
2460
1
        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
2461
1
    }
2462
1
    wallet.insert_tx(tx.clone());
2463
1
    insert_seen_at(&mut wallet, txid, 0);
2464
1
2465
1
    // the new fee_rate is low enough that just reducing the change would be fine, but we force
2466
1
    // the addition of an extra input with `add_utxo()`
2467
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2468
1
    builder
2469
1
        .add_utxo(incoming_op)
2470
1
        .unwrap()
2471
1
        .fee_absolute(Amount::from_sat(250));
2472
1
    let psbt = builder.finish().unwrap();
2473
1
    let sent_received =
2474
1
        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
2475
1
    let fee = check_fee!(wallet, psbt);
2476
2477
1
    assert_eq!(
2478
1
        sent_received.0,
2479
1
        original_sent_received.0 + Amount::from_sat(25_000)
2480
1
    );
2481
1
    assert_eq!(
2482
1
        fee.unwrap_or(Amount::ZERO) + sent_received.1,
2483
1
        Amount::from_sat(30_000)
2484
1
    );
2485
2486
1
    let tx = &psbt.unsigned_tx;
2487
1
    assert_eq!(tx.input.len(), 2);
2488
1
    assert_eq!(tx.output.len(), 2);
2489
1
    assert_eq!(
2490
1
        tx.output
2491
1
            .iter()
2492
2
            .find(|txout| txout.script_pubkey == addr.script_pubkey())
2493
1
            .unwrap()
2494
1
            .value,
2495
1
        Amount::from_sat(45_000)
2496
1
    );
2497
1
    assert_eq!(
2498
1
        tx.output
2499
1
            .iter()
2500
1
            .find(|txout| txout.script_pubkey != addr.script_pubkey())
2501
1
            .unwrap()
2502
1
            .value,
2503
1
        sent_received.1
2504
1
    );
2505
2506
1
    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(250));
2507
1
}
2508
2509
#[test]
2510
#[should_panic(expected = "InsufficientFunds")]
2511
1
fn test_bump_fee_unconfirmed_inputs_only() {
2512
1
    // We try to bump the fee, but:
2513
1
    // - We can't reduce the change, as we have no change
2514
1
    // - All our UTXOs are unconfirmed
2515
1
    // So, we fail with "InsufficientFunds", as per RBF rule 2:
2516
1
    // The replacement transaction may only include an unconfirmed input
2517
1
    // if that input was included in one of the original transactions.
2518
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2519
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2520
1
        .unwrap()
2521
1
        .assume_checked();
2522
1
    let mut builder = wallet.build_tx();
2523
1
    builder
2524
1
        .drain_wallet()
2525
1
        .drain_to(addr.script_pubkey())
2526
1
        .enable_rbf();
2527
1
    let psbt = builder.finish().unwrap();
2528
1
    // Now we receive one transaction with 0 confirmations. We won't be able to use that for
2529
1
    // fee bumping, as it's still unconfirmed!
2530
1
    receive_output(
2531
1
        &mut wallet,
2532
1
        25_000,
2533
1
        ConfirmationTime::Unconfirmed { last_seen: 0 },
2534
1
    );
2535
1
    let mut tx = psbt.extract_tx().expect("failed to extract tx");
2536
1
    let txid = tx.compute_txid();
2537
2
    for 
txin1
in &mut tx.input {
2538
1
        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
2539
1
    }
2540
1
    wallet.insert_tx(tx);
2541
1
    insert_seen_at(&mut wallet, txid, 0);
2542
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2543
1
    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(25));
2544
1
    builder.finish().unwrap();
2545
1
}
2546
2547
#[test]
2548
1
fn test_bump_fee_unconfirmed_input() {
2549
1
    // We create a tx draining the wallet and spending one confirmed
2550
1
    // and one unconfirmed UTXO. We check that we can fee bump normally
2551
1
    // (BIP125 rule 2 only apply to newly added unconfirmed input, you can
2552
1
    // always fee bump with an unconfirmed input if it was included in the
2553
1
    // original transaction)
2554
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2555
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2556
1
        .unwrap()
2557
1
        .assume_checked();
2558
1
    // We receive a tx with 0 confirmations, which will be used as an input
2559
1
    // in the drain tx.
2560
1
    receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
2561
1
    let mut builder = wallet.build_tx();
2562
1
    builder
2563
1
        .drain_wallet()
2564
1
        .drain_to(addr.script_pubkey())
2565
1
        .enable_rbf();
2566
1
    let psbt = builder.finish().unwrap();
2567
1
    let mut tx = psbt.extract_tx().expect("failed to extract tx");
2568
1
    let txid = tx.compute_txid();
2569
3
    for 
txin2
in &mut tx.input {
2570
2
        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
2571
2
    }
2572
1
    wallet.insert_tx(tx);
2573
1
    insert_seen_at(&mut wallet, txid, 0);
2574
1
2575
1
    let mut builder = wallet.build_fee_bump(txid).unwrap();
2576
1
    builder
2577
1
        .fee_rate(FeeRate::from_sat_per_vb_unchecked(15))
2578
1
        // remove original tx drain_to address and amount
2579
1
        .set_recipients(Vec::new())
2580
1
        // set back original drain_to address
2581
1
        .drain_to(addr.script_pubkey())
2582
1
        // drain wallet output amount will be re-calculated with new fee rate
2583
1
        .drain_wallet();
2584
1
    builder.finish().unwrap();
2585
1
}
2586
2587
#[test]
2588
1
fn test_fee_amount_negative_drain_val() {
2589
1
    // While building the transaction, bdk would calculate the drain_value
2590
1
    // as
2591
1
    // current_delta - fee_amount - drain_fee
2592
1
    // using saturating_sub, meaning that if the result would end up negative,
2593
1
    // it'll remain to zero instead.
2594
1
    // This caused a bug in master where we would calculate the wrong fee
2595
1
    // for a transaction.
2596
1
    // See https://github.com/bitcoindevkit/bdk/issues/660
2597
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2598
1
    let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
2599
1
        .unwrap()
2600
1
        .assume_checked();
2601
1
    let fee_rate = FeeRate::from_sat_per_kwu(500);
2602
1
    let incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
2603
1
2604
1
    let mut builder = wallet.build_tx();
2605
1
    builder
2606
1
        .add_recipient(send_to.script_pubkey(), Amount::from_sat(8630))
2607
1
        .add_utxo(incoming_op)
2608
1
        .unwrap()
2609
1
        .enable_rbf()
2610
1
        .fee_rate(fee_rate);
2611
1
    let psbt = builder.finish().unwrap();
2612
1
    let fee = check_fee!(wallet, psbt);
2613
2614
1
    assert_eq!(psbt.inputs.len(), 1);
2615
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate, @add_signature);
2616
1
}
2617
2618
#[test]
2619
1
fn test_sign_single_xprv() {
2620
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
2621
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2622
1
    let mut builder = wallet.build_tx();
2623
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2624
1
    let mut psbt = builder.finish().unwrap();
2625
1
2626
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2627
1
    assert!(finalized);
2628
2629
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2630
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2631
1
}
2632
2633
#[test]
2634
1
fn test_sign_single_xprv_with_master_fingerprint_and_path() {
2635
1
    let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
2636
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2637
1
    let mut builder = wallet.build_tx();
2638
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2639
1
    let mut psbt = builder.finish().unwrap();
2640
1
2641
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2642
1
    assert!(finalized);
2643
2644
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2645
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2646
1
}
2647
2648
#[test]
2649
1
fn test_sign_single_xprv_bip44_path() {
2650
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)");
2651
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2652
1
    let mut builder = wallet.build_tx();
2653
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2654
1
    let mut psbt = builder.finish().unwrap();
2655
1
2656
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2657
1
    assert!(finalized);
2658
2659
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2660
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2661
1
}
2662
2663
#[test]
2664
1
fn test_sign_single_xprv_sh_wpkh() {
2665
1
    let (mut wallet, _) = get_funded_wallet("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))");
2666
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2667
1
    let mut builder = wallet.build_tx();
2668
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2669
1
    let mut psbt = builder.finish().unwrap();
2670
1
2671
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2672
1
    assert!(finalized);
2673
2674
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2675
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2676
1
}
2677
2678
#[test]
2679
1
fn test_sign_single_wif() {
2680
1
    let (mut wallet, _) =
2681
1
        get_funded_wallet("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)");
2682
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2683
1
    let mut builder = wallet.build_tx();
2684
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2685
1
    let mut psbt = builder.finish().unwrap();
2686
1
2687
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2688
1
    assert!(finalized);
2689
2690
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2691
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2692
1
}
2693
2694
#[test]
2695
1
fn test_sign_single_xprv_no_hd_keypaths() {
2696
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
2697
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2698
1
    let mut builder = wallet.build_tx();
2699
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
2700
1
    let mut psbt = builder.finish().unwrap();
2701
1
2702
1
    psbt.inputs[0].bip32_derivation.clear();
2703
1
    assert_eq!(psbt.inputs[0].bip32_derivation.len(), 0);
2704
2705
1
    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
2706
1
    assert!(finalized);
2707
2708
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2709
1
    assert_eq!(extracted.input[0].witness.len(), 2);
2710
1
}
2711
2712
#[test]
2713
1
fn test_include_output_redeem_witness_script() {
2714
1
    let desc = get_test_wpkh();
2715
1
    let change_desc = "sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))";
2716
1
    let (mut wallet, _) = get_funded_wallet_with_change(desc, change_desc);
2717
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2718
1
        .unwrap()
2719
1
        .assume_checked();
2720
1
    let mut builder = wallet.build_tx();
2721
1
    builder
2722
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2723
1
        .include_output_redeem_witness_script();
2724
1
    let psbt = builder.finish().unwrap();
2725
1
2726
1
    // p2sh-p2wsh transaction should contain both witness and redeem scripts
2727
1
    assert!(psbt
2728
1
        .outputs
2729
1
        .iter()
2730
1
        .any(|output| output.redeem_script.is_some() && output.witness_script.is_some()));
2731
1
}
2732
2733
#[test]
2734
1
fn test_signing_only_one_of_multiple_inputs() {
2735
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
2736
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2737
1
        .unwrap()
2738
1
        .assume_checked();
2739
1
    let mut builder = wallet.build_tx();
2740
1
    builder
2741
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
2742
1
        .include_output_redeem_witness_script();
2743
1
    let mut psbt = builder.finish().unwrap();
2744
1
2745
1
    // add another input to the psbt that is at least passable.
2746
1
    let dud_input = bitcoin::psbt::Input {
2747
1
        witness_utxo: Some(TxOut {
2748
1
            value: Amount::from_sat(100_000),
2749
1
            script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
2750
1
                "wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
2751
1
            )
2752
1
            .unwrap()
2753
1
            .script_pubkey(),
2754
1
        }),
2755
1
        ..Default::default()
2756
1
    };
2757
1
2758
1
    psbt.inputs.push(dud_input);
2759
1
    psbt.unsigned_tx.input.push(bitcoin::TxIn::default());
2760
1
    let is_final = wallet
2761
1
        .sign(
2762
1
            &mut psbt,
2763
1
            SignOptions {
2764
1
                trust_witness_utxo: true,
2765
1
                ..Default::default()
2766
1
            },
2767
1
        )
2768
1
        .unwrap();
2769
1
    assert!(
2770
1
        !is_final,
2771
0
        "shouldn't be final since we can't sign one of the inputs"
2772
    );
2773
1
    assert!(
2774
1
        psbt.inputs[0].final_script_witness.is_some(),
2775
0
        "should finalized input it signed"
2776
    )
2777
1
}
2778
2779
#[test]
2780
1
fn test_try_finalize_sign_option() {
2781
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
2782
2783
3
    for 
try_finalize2
in &[true, false] {
2784
2
        let addr = wallet.next_unused_address(KeychainKind::External);
2785
2
        let mut builder = wallet.build_tx();
2786
2
        builder.drain_to(addr.script_pubkey()).drain_wallet();
2787
2
        let mut psbt = builder.finish().unwrap();
2788
2
2789
2
        let finalized = wallet
2790
2
            .sign(
2791
2
                &mut psbt,
2792
2
                SignOptions {
2793
2
                    try_finalize: *try_finalize,
2794
2
                    ..Default::default()
2795
2
                },
2796
2
            )
2797
2
            .unwrap();
2798
2
2799
2
        psbt.inputs.iter().for_each(|input| {
2800
2
            if *try_finalize {
2801
1
                assert!(finalized);
2802
1
                assert!(input.final_script_sig.is_none());
2803
1
                assert!(input.final_script_witness.is_some());
2804
            } else {
2805
1
                assert!(!finalized);
2806
1
                assert!(input.final_script_sig.is_none());
2807
1
                assert!(input.final_script_witness.is_none());
2808
            }
2809
2
        });
2810
2
    }
2811
1
}
2812
2813
#[test]
2814
1
fn test_taproot_try_finalize_sign_option() {
2815
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
2816
2817
3
    for 
try_finalize2
in &[true, false] {
2818
2
        let addr = wallet.next_unused_address(KeychainKind::External);
2819
2
        let mut builder = wallet.build_tx();
2820
2
        builder.drain_to(addr.script_pubkey()).drain_wallet();
2821
2
        let mut psbt = builder.finish().unwrap();
2822
2
2823
2
        let finalized = wallet
2824
2
            .sign(
2825
2
                &mut psbt,
2826
2
                SignOptions {
2827
2
                    try_finalize: *try_finalize,
2828
2
                    ..Default::default()
2829
2
                },
2830
2
            )
2831
2
            .unwrap();
2832
2
2833
2
        psbt.inputs.iter().for_each(|input| {
2834
2
            if *try_finalize {
2835
1
                assert!(finalized);
2836
1
                assert!(input.final_script_sig.is_none());
2837
1
                assert!(input.final_script_witness.is_some());
2838
1
                assert!(input.tap_key_sig.is_none());
2839
1
                assert!(input.tap_script_sigs.is_empty());
2840
1
                assert!(input.tap_scripts.is_empty());
2841
1
                assert!(input.tap_key_origins.is_empty());
2842
1
                assert!(input.tap_internal_key.is_none());
2843
1
                assert!(input.tap_merkle_root.is_none());
2844
            } else {
2845
1
                assert!(!finalized);
2846
1
                assert!(input.final_script_sig.is_none());
2847
1
                assert!(input.final_script_witness.is_none());
2848
            }
2849
2
        });
2850
2
        psbt.outputs.iter().for_each(|output| {
2851
2
            if *try_finalize {
2852
1
                assert!(finalized);
2853
1
                assert!(output.tap_key_origins.is_empty());
2854
            } else {
2855
1
                assert!(!finalized);
2856
1
                assert!(!output.tap_key_origins.is_empty());
2857
            }
2858
2
        });
2859
2
    }
2860
1
}
2861
2862
#[test]
2863
1
fn test_sign_nonstandard_sighash() {
2864
1
    let sighash = EcdsaSighashType::NonePlusAnyoneCanPay;
2865
1
2866
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
2867
1
    let addr = wallet.next_unused_address(KeychainKind::External);
2868
1
    let mut builder = wallet.build_tx();
2869
1
    builder
2870
1
        .drain_to(addr.script_pubkey())
2871
1
        .sighash(sighash.into())
2872
1
        .drain_wallet();
2873
1
    let mut psbt = builder.finish().unwrap();
2874
1
2875
1
    let result = wallet.sign(&mut psbt, Default::default());
2876
1
    assert!(
2877
1
        result.is_err(),
2878
0
        "Signing should have failed because the TX uses non-standard sighashes"
2879
    );
2880
0
    assert_matches!(
2881
1
        result,
2882
        Err(SignerError::NonStandardSighash),
2883
        "Signing failed with the wrong error type"
2884
    );
2885
2886
    // try again after opting-in
2887
1
    let result = wallet.sign(
2888
1
        &mut psbt,
2889
1
        SignOptions {
2890
1
            allow_all_sighashes: true,
2891
1
            ..Default::default()
2892
1
        },
2893
1
    );
2894
1
    assert!(result.is_ok(), 
"Signing should have worked"0
);
2895
1
    assert!(
2896
1
        result.unwrap(),
2897
0
        "Should finalize the input since we can produce signatures"
2898
    );
2899
2900
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
2901
1
    assert_eq!(
2902
1
        *extracted.input[0].witness.to_vec()[0].last().unwrap(),
2903
1
        sighash.to_u32() as u8,
2904
0
        "The signature should have been made with the right sighash"
2905
    );
2906
1
}
2907
2908
#[test]
2909
1
fn test_unused_address() {
2910
1
    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
2911
1
    let change_descriptor = get_test_wpkh();
2912
1
    let mut wallet = Wallet::create(descriptor, change_descriptor)
2913
1
        .network(Network::Testnet)
2914
1
        .create_wallet_no_persist()
2915
1
        .expect("wallet");
2916
1
2917
1
    // `list_unused_addresses` should be empty if we haven't revealed any
2918
1
    assert!(wallet
2919
1
        .list_unused_addresses(KeychainKind::External)
2920
1
        .next()
2921
1
        .is_none());
2922
2923
1
    assert_eq!(
2924
1
        wallet
2925
1
            .next_unused_address(KeychainKind::External)
2926
1
            .to_string(),
2927
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2928
1
    );
2929
1
    assert_eq!(
2930
1
        wallet
2931
1
            .list_unused_addresses(KeychainKind::External)
2932
1
            .next()
2933
1
            .unwrap()
2934
1
            .to_string(),
2935
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2936
1
    );
2937
1
}
2938
2939
#[test]
2940
1
fn test_next_unused_address() {
2941
1
    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
2942
1
    let change_descriptor = get_test_wpkh();
2943
1
    let mut wallet = Wallet::create(descriptor, change_descriptor)
2944
1
        .network(Network::Testnet)
2945
1
        .create_wallet_no_persist()
2946
1
        .expect("wallet");
2947
1
    assert_eq!(wallet.derivation_index(KeychainKind::External), None);
2948
2949
1
    assert_eq!(
2950
1
        wallet
2951
1
            .next_unused_address(KeychainKind::External)
2952
1
            .to_string(),
2953
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2954
1
    );
2955
1
    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0));
2956
    // calling next_unused again gives same address
2957
1
    assert_eq!(
2958
1
        wallet
2959
1
            .next_unused_address(KeychainKind::External)
2960
1
            .to_string(),
2961
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
2962
1
    );
2963
1
    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0));
2964
2965
    // test mark used / unused
2966
1
    assert!(wallet.mark_used(KeychainKind::External, 0));
2967
1
    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
2968
1
    assert_eq!(next_unused_addr.index, 1);
2969
2970
1
    assert!(wallet.unmark_used(KeychainKind::External, 0));
2971
1
    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
2972
1
    assert_eq!(next_unused_addr.index, 0);
2973
2974
    // use the above address
2975
1
    receive_output_in_latest_block(&mut wallet, 25_000);
2976
1
2977
1
    assert_eq!(
2978
1
        wallet
2979
1
            .next_unused_address(KeychainKind::External)
2980
1
            .to_string(),
2981
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
2982
1
    );
2983
1
    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(1));
2984
2985
    // trying to mark index 0 unused should return false
2986
1
    assert!(!wallet.unmark_used(KeychainKind::External, 0));
2987
1
}
2988
2989
#[test]
2990
1
fn test_peek_address_at_index() {
2991
1
    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
2992
1
    let change_descriptor = get_test_wpkh();
2993
1
    let mut wallet = Wallet::create(descriptor, change_descriptor)
2994
1
        .network(Network::Testnet)
2995
1
        .create_wallet_no_persist()
2996
1
        .expect("wallet");
2997
1
2998
1
    assert_eq!(
2999
1
        wallet.peek_address(KeychainKind::External, 1).to_string(),
3000
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3001
1
    );
3002
3003
1
    assert_eq!(
3004
1
        wallet.peek_address(KeychainKind::External, 0).to_string(),
3005
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
3006
1
    );
3007
3008
1
    assert_eq!(
3009
1
        wallet.peek_address(KeychainKind::External, 2).to_string(),
3010
1
        "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
3011
1
    );
3012
3013
    // current new address is not affected
3014
1
    assert_eq!(
3015
1
        wallet
3016
1
            .reveal_next_address(KeychainKind::External)
3017
1
            .to_string(),
3018
1
        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
3019
1
    );
3020
3021
1
    assert_eq!(
3022
1
        wallet
3023
1
            .reveal_next_address(KeychainKind::External)
3024
1
            .to_string(),
3025
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3026
1
    );
3027
1
}
3028
3029
#[test]
3030
1
fn test_peek_address_at_index_not_derivable() {
3031
1
    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)";
3032
1
    let wallet = Wallet::create(descriptor, get_test_wpkh())
3033
1
        .network(Network::Testnet)
3034
1
        .create_wallet_no_persist()
3035
1
        .unwrap();
3036
1
3037
1
    assert_eq!(
3038
1
        wallet.peek_address(KeychainKind::External, 1).to_string(),
3039
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3040
1
    );
3041
3042
1
    assert_eq!(
3043
1
        wallet.peek_address(KeychainKind::External, 0).to_string(),
3044
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3045
1
    );
3046
3047
1
    assert_eq!(
3048
1
        wallet.peek_address(KeychainKind::External, 2).to_string(),
3049
1
        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3050
1
    );
3051
1
}
3052
3053
#[test]
3054
1
fn test_returns_index_and_address() {
3055
1
    let descriptor =
3056
1
        "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3057
1
    let mut wallet = Wallet::create(descriptor, get_test_wpkh())
3058
1
        .network(Network::Testnet)
3059
1
        .create_wallet_no_persist()
3060
1
        .unwrap();
3061
1
3062
1
    // new index 0
3063
1
    assert_eq!(
3064
1
        wallet.reveal_next_address(KeychainKind::External),
3065
1
        AddressInfo {
3066
1
            index: 0,
3067
1
            address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
3068
1
                .unwrap()
3069
1
                .assume_checked(),
3070
1
            keychain: KeychainKind::External,
3071
1
        }
3072
1
    );
3073
3074
    // new index 1
3075
1
    assert_eq!(
3076
1
        wallet.reveal_next_address(KeychainKind::External),
3077
1
        AddressInfo {
3078
1
            index: 1,
3079
1
            address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
3080
1
                .unwrap()
3081
1
                .assume_checked(),
3082
1
            keychain: KeychainKind::External,
3083
1
        }
3084
1
    );
3085
3086
    // peek index 25
3087
1
    assert_eq!(
3088
1
        wallet.peek_address(KeychainKind::External, 25),
3089
1
        AddressInfo {
3090
1
            index: 25,
3091
1
            address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2")
3092
1
                .unwrap()
3093
1
                .assume_checked(),
3094
1
            keychain: KeychainKind::External,
3095
1
        }
3096
1
    );
3097
3098
    // new index 2
3099
1
    assert_eq!(
3100
1
        wallet.reveal_next_address(KeychainKind::External),
3101
1
        AddressInfo {
3102
1
            index: 2,
3103
1
            address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
3104
1
                .unwrap()
3105
1
                .assume_checked(),
3106
1
            keychain: KeychainKind::External,
3107
1
        }
3108
1
    );
3109
1
}
3110
3111
#[test]
3112
1
fn test_sending_to_bip350_bech32m_address() {
3113
1
    let (mut wallet, _) = get_funded_wallet_wpkh();
3114
1
    let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
3115
1
        .unwrap()
3116
1
        .assume_checked();
3117
1
    let mut builder = wallet.build_tx();
3118
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
3119
1
    builder.finish().unwrap();
3120
1
}
3121
3122
#[test]
3123
1
fn test_get_address() {
3124
1
    use bdk_wallet::descriptor::template::Bip84;
3125
1
    let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
3126
1
    let wallet = Wallet::create(
3127
1
        Bip84(key, KeychainKind::External),
3128
1
        Bip84(key, KeychainKind::Internal),
3129
1
    )
3130
1
    .network(Network::Regtest)
3131
1
    .create_wallet_no_persist()
3132
1
    .unwrap();
3133
1
3134
1
    assert_eq!(
3135
1
        wallet.peek_address(KeychainKind::External, 0),
3136
1
        AddressInfo {
3137
1
            index: 0,
3138
1
            address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
3139
1
                .unwrap()
3140
1
                .assume_checked(),
3141
1
            keychain: KeychainKind::External,
3142
1
        }
3143
1
    );
3144
3145
1
    assert_eq!(
3146
1
        wallet.peek_address(KeychainKind::Internal, 0),
3147
1
        AddressInfo {
3148
1
            index: 0,
3149
1
            address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79")
3150
1
                .unwrap()
3151
1
                .assume_checked(),
3152
1
            keychain: KeychainKind::Internal,
3153
1
        }
3154
1
    );
3155
1
}
3156
3157
#[test]
3158
1
fn test_reveal_addresses() {
3159
1
    let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
3160
1
    let mut wallet = Wallet::create(desc, change_desc)
3161
1
        .network(Network::Signet)
3162
1
        .create_wallet_no_persist()
3163
1
        .unwrap();
3164
1
    let keychain = KeychainKind::External;
3165
1
3166
1
    let last_revealed_addr = wallet.reveal_addresses_to(keychain, 9).last().unwrap();
3167
1
    assert_eq!(wallet.derivation_index(keychain), Some(9));
3168
3169
1
    let unused_addrs = wallet.list_unused_addresses(keychain).collect::<Vec<_>>();
3170
1
    assert_eq!(unused_addrs.len(), 10);
3171
1
    assert_eq!(unused_addrs.last().unwrap(), &last_revealed_addr);
3172
3173
    // revealing to an already revealed index returns nothing
3174
1
    let mut already_revealed = wallet.reveal_addresses_to(keychain, 9);
3175
1
    assert!(already_revealed.next().is_none());
3176
1
}
3177
3178
#[test]
3179
1
fn test_get_address_no_reuse() {
3180
1
    use bdk_wallet::descriptor::template::Bip84;
3181
1
    use std::collections::HashSet;
3182
1
3183
1
    let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
3184
1
    let mut wallet = Wallet::create(
3185
1
        Bip84(key, KeychainKind::External),
3186
1
        Bip84(key, KeychainKind::Internal),
3187
1
    )
3188
1
    .network(Network::Regtest)
3189
1
    .create_wallet_no_persist()
3190
1
    .unwrap();
3191
1
3192
1
    let mut used_set = HashSet::new();
3193
1
3194
3
    (0..3).for_each(|_| {
3195
3
        let external_addr = wallet.reveal_next_address(KeychainKind::External).address;
3196
3
        assert!(used_set.insert(external_addr));
3197
3198
3
        let internal_addr = wallet.reveal_next_address(KeychainKind::Internal).address;
3199
3
        assert!(used_set.insert(internal_addr));
3200
3
    });
3201
1
}
3202
3203
#[test]
3204
1
fn test_taproot_psbt_populate_tap_key_origins() {
3205
1
    let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
3206
1
    let (mut wallet, _) = get_funded_wallet_with_change(desc, change_desc);
3207
1
    let addr = wallet.reveal_next_address(KeychainKind::External);
3208
1
3209
1
    let mut builder = wallet.build_tx();
3210
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
3211
1
    let psbt = builder.finish().unwrap();
3212
1
3213
1
    assert_eq!(
3214
1
        psbt.inputs[0]
3215
1
            .tap_key_origins
3216
1
            .clone()
3217
1
            .into_iter()
3218
1
            .collect::<Vec<_>>(),
3219
1
        vec![(
3220
1
            from_str!("0841db1dbaf949dbbda893e01a18f2cca9179cf8ea2d8e667857690502b06483"),
3221
1
            (vec![], (from_str!("f6a5cb8b"), from_str!("m/0/0")))
3222
1
        )],
3223
0
        "Wrong input tap_key_origins"
3224
    );
3225
1
    assert_eq!(
3226
1
        psbt.outputs[0]
3227
1
            .tap_key_origins
3228
1
            .clone()
3229
1
            .into_iter()
3230
1
            .collect::<Vec<_>>(),
3231
1
        vec![(
3232
1
            from_str!("9187c1e80002d19ddde9c5c7f5394e9a063cee8695867b58815af0562695ca21"),
3233
1
            (vec![], (from_str!("f6a5cb8b"), from_str!("m/0/1")))
3234
1
        )],
3235
0
        "Wrong output tap_key_origins"
3236
    );
3237
1
}
3238
3239
#[test]
3240
1
fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
3241
1
    let (mut wallet, _) =
3242
1
        get_funded_wallet_with_change(get_test_tr_repeated_key(), get_test_tr_single_sig());
3243
1
    let addr = wallet.reveal_next_address(KeychainKind::External);
3244
1
3245
1
    let path = vec![("rn4nre9c".to_string(), vec![0])]
3246
1
        .into_iter()
3247
1
        .collect();
3248
1
3249
1
    let mut builder = wallet.build_tx();
3250
1
    builder
3251
1
        .drain_to(addr.script_pubkey())
3252
1
        .drain_wallet()
3253
1
        .policy_path(path, KeychainKind::External);
3254
1
    let psbt = builder.finish().unwrap();
3255
1
3256
1
    let mut input_key_origins = psbt.inputs[0]
3257
1
        .tap_key_origins
3258
1
        .clone()
3259
1
        .into_iter()
3260
1
        .collect::<Vec<_>>();
3261
1
    input_key_origins.sort();
3262
1
3263
1
    assert_eq!(
3264
1
        input_key_origins,
3265
1
        vec![
3266
1
            (
3267
1
                from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
3268
1
                (
3269
1
                    vec![
3270
1
                        from_str!(
3271
1
                            "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
3272
1
                        ),
3273
1
                        from_str!(
3274
1
                            "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
3275
1
                        ),
3276
1
                    ],
3277
1
                    (FromStr::from_str("ece52657").unwrap(), vec![].into())
3278
1
                )
3279
1
            ),
3280
1
            (
3281
1
                from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
3282
1
                (
3283
1
                    vec![],
3284
1
                    (FromStr::from_str("871fd295").unwrap(), vec![].into())
3285
1
                )
3286
1
            )
3287
1
        ],
3288
0
        "Wrong input tap_key_origins"
3289
    );
3290
3291
1
    let mut output_key_origins = psbt.outputs[0]
3292
1
        .tap_key_origins
3293
1
        .clone()
3294
1
        .into_iter()
3295
1
        .collect::<Vec<_>>();
3296
1
    output_key_origins.sort();
3297
1
3298
1
    assert_eq!(
3299
        input_key_origins, output_key_origins,
3300
0
        "Wrong output tap_key_origins"
3301
    );
3302
1
}
3303
3304
#[test]
3305
1
fn test_taproot_psbt_input_tap_tree() {
3306
1
    use bitcoin::hex::FromHex;
3307
1
    use bitcoin::taproot;
3308
1
3309
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
3310
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3311
1
3312
1
    let mut builder = wallet.build_tx();
3313
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
3314
1
    let psbt = builder.finish().unwrap();
3315
1
3316
1
    assert_eq!(
3317
1
        psbt.inputs[0].tap_merkle_root,
3318
1
        Some(
3319
1
            TapNodeHash::from_str(
3320
1
                "61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
3321
1
            )
3322
1
            .unwrap()
3323
1
        ),
3324
1
    );
3325
1
    assert_eq!(
3326
1
        psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
3327
1
        vec![
3328
1
            (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (ScriptBuf::from_hex("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac").unwrap(), taproot::LeafVersion::TapScript)),
3329
1
            (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (ScriptBuf::from_hex("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac").unwrap(), taproot::LeafVersion::TapScript)),
3330
1
        ],
3331
1
    );
3332
1
    assert_eq!(
3333
1
        psbt.inputs[0].tap_internal_key,
3334
1
        Some(from_str!(
3335
1
            "b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"
3336
1
        ))
3337
1
    );
3338
3339
    // Since we are creating an output to the same address as the input, assert that the
3340
    // internal_key is the same
3341
1
    assert_eq!(
3342
1
        psbt.inputs[0].tap_internal_key,
3343
1
        psbt.outputs[0].tap_internal_key
3344
1
    );
3345
3346
1
    let tap_tree: bitcoin::taproot::TapTree = serde_json::from_str(r#"[1,{"Script":["2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",192]},1,{"Script":["208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac",192]}]"#).unwrap();
3347
1
    assert_eq!(psbt.outputs[0].tap_tree, Some(tap_tree));
3348
1
}
3349
3350
#[test]
3351
1
fn test_taproot_sign_missing_witness_utxo() {
3352
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
3353
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3354
1
    let mut builder = wallet.build_tx();
3355
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
3356
1
    let mut psbt = builder.finish().unwrap();
3357
1
    let witness_utxo = psbt.inputs[0].witness_utxo.take();
3358
1
3359
1
    let result = wallet.sign(
3360
1
        &mut psbt,
3361
1
        SignOptions {
3362
1
            allow_all_sighashes: true,
3363
1
            ..Default::default()
3364
1
        },
3365
1
    );
3366
0
    assert_matches!(
3367
1
        result,
3368
        Err(SignerError::MissingWitnessUtxo),
3369
        "Signing should have failed with the correct error because the witness_utxo is missing"
3370
    );
3371
3372
    // restore the witness_utxo
3373
1
    psbt.inputs[0].witness_utxo = witness_utxo;
3374
1
3375
1
    let result = wallet.sign(
3376
1
        &mut psbt,
3377
1
        SignOptions {
3378
1
            allow_all_sighashes: true,
3379
1
            ..Default::default()
3380
1
        },
3381
1
    );
3382
3383
1
    assert_matches!(
3384
1
        result,
3385
        Ok(true),
3386
        "Should finalize the input since we can produce signatures"
3387
    );
3388
1
}
3389
3390
#[test]
3391
1
fn test_taproot_sign_using_non_witness_utxo() {
3392
1
    let (mut wallet, prev_txid) = get_funded_wallet(get_test_tr_single_sig());
3393
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3394
1
    let mut builder = wallet.build_tx();
3395
1
    builder.drain_to(addr.script_pubkey()).drain_wallet();
3396
1
    let mut psbt = builder.finish().unwrap();
3397
1
3398
1
    psbt.inputs[0].witness_utxo = None;
3399
1
    psbt.inputs[0].non_witness_utxo =
3400
1
        Some(wallet.get_tx(prev_txid).unwrap().tx_node.as_ref().clone());
3401
1
    assert!(
3402
1
        psbt.inputs[0].non_witness_utxo.is_some(),
3403
0
        "Previous tx should be present in the database"
3404
    );
3405
3406
1
    let result = wallet.sign(&mut psbt, Default::default());
3407
1
    assert!(result.is_ok(), 
"Signing should have worked"0
);
3408
1
    assert!(
3409
1
        result.unwrap(),
3410
0
        "Should finalize the input since we can produce signatures"
3411
    );
3412
1
}
3413
3414
#[test]
3415
1
fn test_taproot_foreign_utxo() {
3416
1
    let (mut wallet1, _) = get_funded_wallet_wpkh();
3417
1
    let (wallet2, _) = get_funded_wallet(get_test_tr_single_sig());
3418
1
3419
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
3420
1
        .unwrap()
3421
1
        .assume_checked();
3422
1
    let utxo = wallet2.list_unspent().next().unwrap();
3423
1
    let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
3424
1
    let foreign_utxo_satisfaction = wallet2
3425
1
        .public_descriptor(KeychainKind::External)
3426
1
        .max_weight_to_satisfy()
3427
1
        .unwrap();
3428
1
3429
1
    assert!(
3430
1
        psbt_input.non_witness_utxo.is_none(),
3431
0
        "`non_witness_utxo` should never be populated for taproot"
3432
    );
3433
3434
1
    let mut builder = wallet1.build_tx();
3435
1
    builder
3436
1
        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
3437
1
        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
3438
1
        .unwrap();
3439
1
    let psbt = builder.finish().unwrap();
3440
1
    let sent_received =
3441
1
        wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
3442
1
    wallet1.insert_txout(utxo.outpoint, utxo.txout);
3443
1
    let fee = check_fee!(wallet1, psbt);
3444
3445
1
    assert_eq!(
3446
1
        sent_received.0 - sent_received.1,
3447
1
        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
3448
0
        "we should have only net spent ~10_000"
3449
    );
3450
3451
1
    assert!(
3452
1
        psbt.unsigned_tx
3453
1
            .input
3454
1
            .iter()
3455
1
            .any(|input| input.previous_output == utxo.outpoint),
3456
0
        "foreign_utxo should be in there"
3457
    );
3458
1
}
3459
3460
2
fn test_spend_from_wallet(mut wallet: Wallet) {
3461
2
    let addr = wallet.next_unused_address(KeychainKind::External);
3462
2
3463
2
    let mut builder = wallet.build_tx();
3464
2
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3465
2
    let mut psbt = builder.finish().unwrap();
3466
2
3467
2
    assert!(
3468
2
        wallet.sign(&mut psbt, Default::default()).unwrap(),
3469
0
        "Unable to finalize tx"
3470
    );
3471
2
}
3472
3473
//     #[test]
3474
//     fn test_taproot_key_spend() {
3475
//         let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
3476
//         test_spend_from_wallet(wallet);
3477
3478
//         let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
3479
//         test_spend_from_wallet(wallet);
3480
//     }
3481
3482
#[test]
3483
1
fn test_taproot_no_key_spend() {
3484
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
3485
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3486
1
3487
1
    let mut builder = wallet.build_tx();
3488
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3489
1
    let mut psbt = builder.finish().unwrap();
3490
1
3491
1
    assert!(
3492
1
        wallet
3493
1
            .sign(
3494
1
                &mut psbt,
3495
1
                SignOptions {
3496
1
                    sign_with_tap_internal_key: false,
3497
1
                    ..Default::default()
3498
1
                },
3499
1
            )
3500
1
            .unwrap(),
3501
0
        "Unable to finalize tx"
3502
    );
3503
3504
1
    assert!(psbt.inputs.iter().all(|i| i.tap_key_sig.is_none()));
3505
1
}
3506
3507
#[test]
3508
1
fn test_taproot_script_spend() {
3509
1
    let (wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
3510
1
    test_spend_from_wallet(wallet);
3511
1
3512
1
    let (wallet, _) = get_funded_wallet(get_test_tr_with_taptree_xprv());
3513
1
    test_spend_from_wallet(wallet);
3514
1
}
3515
3516
#[test]
3517
1
fn test_taproot_script_spend_sign_all_leaves() {
3518
1
    use bdk_wallet::signer::TapLeavesOptions;
3519
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
3520
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3521
1
3522
1
    let mut builder = wallet.build_tx();
3523
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3524
1
    let mut psbt = builder.finish().unwrap();
3525
1
3526
1
    assert!(
3527
1
        wallet
3528
1
            .sign(
3529
1
                &mut psbt,
3530
1
                SignOptions {
3531
1
                    tap_leaves_options: TapLeavesOptions::All,
3532
1
                    ..Default::default()
3533
1
                },
3534
1
            )
3535
1
            .unwrap(),
3536
0
        "Unable to finalize tx"
3537
    );
3538
3539
1
    assert!(psbt
3540
1
        .inputs
3541
1
        .iter()
3542
1
        .all(|i| i.tap_script_sigs.len() == i.tap_scripts.len()));
3543
1
}
3544
3545
#[test]
3546
1
fn test_taproot_script_spend_sign_include_some_leaves() {
3547
1
    use bdk_wallet::signer::TapLeavesOptions;
3548
1
    use bitcoin::taproot::TapLeafHash;
3549
1
3550
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
3551
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3552
1
3553
1
    let mut builder = wallet.build_tx();
3554
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3555
1
    let mut psbt = builder.finish().unwrap();
3556
1
    let mut script_leaves: Vec<_> = psbt.inputs[0]
3557
1
        .tap_scripts
3558
1
        .clone()
3559
1
        .values()
3560
2
        .map(|(script, version)| TapLeafHash::from_script(script, *version))
3561
1
        .collect();
3562
1
    let included_script_leaves = vec![script_leaves.pop().unwrap()];
3563
1
    let excluded_script_leaves = script_leaves;
3564
1
3565
1
    assert!(
3566
1
        wallet
3567
1
            .sign(
3568
1
                &mut psbt,
3569
1
                SignOptions {
3570
1
                    tap_leaves_options: TapLeavesOptions::Include(included_script_leaves.clone()),
3571
1
                    ..Default::default()
3572
1
                },
3573
1
            )
3574
1
            .unwrap(),
3575
0
        "Unable to finalize tx"
3576
    );
3577
3578
1
    assert!(psbt.inputs[0]
3579
1
        .tap_script_sigs
3580
1
        .iter()
3581
1
        .all(|s| 
included_script_leaves.contains(&s.0 .1)0
3582
1
            && 
!excluded_script_leaves.contains(&s.0 .10
)0
));
3583
1
}
3584
3585
#[test]
3586
1
fn test_taproot_script_spend_sign_exclude_some_leaves() {
3587
1
    use bdk_wallet::signer::TapLeavesOptions;
3588
1
    use bitcoin::taproot::TapLeafHash;
3589
1
3590
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
3591
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3592
1
3593
1
    let mut builder = wallet.build_tx();
3594
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3595
1
    let mut psbt = builder.finish().unwrap();
3596
1
    let mut script_leaves: Vec<_> = psbt.inputs[0]
3597
1
        .tap_scripts
3598
1
        .clone()
3599
1
        .values()
3600
2
        .map(|(script, version)| TapLeafHash::from_script(script, *version))
3601
1
        .collect();
3602
1
    let included_script_leaves = [script_leaves.pop().unwrap()];
3603
1
    let excluded_script_leaves = script_leaves;
3604
1
3605
1
    assert!(
3606
1
        wallet
3607
1
            .sign(
3608
1
                &mut psbt,
3609
1
                SignOptions {
3610
1
                    tap_leaves_options: TapLeavesOptions::Exclude(excluded_script_leaves.clone()),
3611
1
                    ..Default::default()
3612
1
                },
3613
1
            )
3614
1
            .unwrap(),
3615
0
        "Unable to finalize tx"
3616
    );
3617
3618
1
    assert!(psbt.inputs[0]
3619
1
        .tap_script_sigs
3620
1
        .iter()
3621
1
        .all(|s| 
included_script_leaves.contains(&s.0 .1)0
3622
1
            && 
!excluded_script_leaves.contains(&s.0 .10
)0
));
3623
1
}
3624
3625
#[test]
3626
1
fn test_taproot_script_spend_sign_no_leaves() {
3627
1
    use bdk_wallet::signer::TapLeavesOptions;
3628
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
3629
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3630
1
3631
1
    let mut builder = wallet.build_tx();
3632
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3633
1
    let mut psbt = builder.finish().unwrap();
3634
1
3635
1
    wallet
3636
1
        .sign(
3637
1
            &mut psbt,
3638
1
            SignOptions {
3639
1
                tap_leaves_options: TapLeavesOptions::None,
3640
1
                ..Default::default()
3641
1
            },
3642
1
        )
3643
1
        .unwrap();
3644
1
3645
1
    assert!(psbt.inputs.iter().all(|i| i.tap_script_sigs.is_empty()));
3646
1
}
3647
3648
#[test]
3649
1
fn test_taproot_sign_derive_index_from_psbt() {
3650
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
3651
1
3652
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3653
1
3654
1
    let mut builder = wallet.build_tx();
3655
1
    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
3656
1
    let mut psbt = builder.finish().unwrap();
3657
1
3658
1
    // re-create the wallet with an empty db
3659
1
    let wallet_empty = Wallet::create(get_test_tr_single_sig_xprv(), get_test_tr_single_sig())
3660
1
        .network(Network::Regtest)
3661
1
        .create_wallet_no_persist()
3662
1
        .unwrap();
3663
1
3664
1
    // signing with an empty db means that we will only look at the psbt to infer the
3665
1
    // derivation index
3666
1
    assert!(
3667
1
        wallet_empty.sign(&mut psbt, Default::default()).unwrap(),
3668
0
        "Unable to finalize tx"
3669
    );
3670
1
}
3671
3672
#[test]
3673
1
fn test_taproot_sign_explicit_sighash_all() {
3674
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
3675
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3676
1
    let mut builder = wallet.build_tx();
3677
1
    builder
3678
1
        .drain_to(addr.script_pubkey())
3679
1
        .sighash(TapSighashType::All.into())
3680
1
        .drain_wallet();
3681
1
    let mut psbt = builder.finish().unwrap();
3682
1
3683
1
    let result = wallet.sign(&mut psbt, Default::default());
3684
1
    assert!(
3685
1
        result.is_ok(),
3686
0
        "Signing should work because SIGHASH_ALL is safe"
3687
    )
3688
1
}
3689
3690
#[test]
3691
1
fn test_taproot_sign_non_default_sighash() {
3692
1
    let sighash = TapSighashType::NonePlusAnyoneCanPay;
3693
1
3694
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
3695
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3696
1
    let mut builder = wallet.build_tx();
3697
1
    builder
3698
1
        .drain_to(addr.script_pubkey())
3699
1
        .sighash(sighash.into())
3700
1
        .drain_wallet();
3701
1
    let mut psbt = builder.finish().unwrap();
3702
1
3703
1
    let witness_utxo = psbt.inputs[0].witness_utxo.take();
3704
1
3705
1
    let result = wallet.sign(&mut psbt, Default::default());
3706
1
    assert!(
3707
1
        result.is_err(),
3708
0
        "Signing should have failed because the TX uses non-standard sighashes"
3709
    );
3710
0
    assert_matches!(
3711
1
        result,
3712
        Err(SignerError::NonStandardSighash),
3713
        "Signing failed with the wrong error type"
3714
    );
3715
3716
    // try again after opting-in
3717
1
    let result = wallet.sign(
3718
1
        &mut psbt,
3719
1
        SignOptions {
3720
1
            allow_all_sighashes: true,
3721
1
            ..Default::default()
3722
1
        },
3723
1
    );
3724
1
    assert!(
3725
1
        result.is_err(),
3726
0
        "Signing should have failed because the witness_utxo is missing"
3727
    );
3728
0
    assert_matches!(
3729
1
        result,
3730
        Err(SignerError::MissingWitnessUtxo),
3731
        "Signing failed with the wrong error type"
3732
    );
3733
3734
    // restore the witness_utxo
3735
1
    psbt.inputs[0].witness_utxo = witness_utxo;
3736
1
3737
1
    let result = wallet.sign(
3738
1
        &mut psbt,
3739
1
        SignOptions {
3740
1
            allow_all_sighashes: true,
3741
1
            ..Default::default()
3742
1
        },
3743
1
    );
3744
1
3745
1
    assert!(result.is_ok(), 
"Signing should have worked"0
);
3746
1
    assert!(
3747
1
        result.unwrap(),
3748
0
        "Should finalize the input since we can produce signatures"
3749
    );
3750
3751
1
    let extracted = psbt.extract_tx().expect("failed to extract tx");
3752
1
    assert_eq!(
3753
1
        *extracted.input[0].witness.to_vec()[0].last().unwrap(),
3754
1
        sighash as u8,
3755
0
        "The signature should have been made with the right sighash"
3756
    );
3757
1
}
3758
3759
#[test]
3760
1
fn test_spend_coinbase() {
3761
1
    let (desc, change_desc) = get_test_wpkh_with_change_desc();
3762
1
    let mut wallet = Wallet::create(desc, change_desc)
3763
1
        .network(Network::Regtest)
3764
1
        .create_wallet_no_persist()
3765
1
        .unwrap();
3766
1
3767
1
    let confirmation_height = 5;
3768
1
    wallet
3769
1
        .insert_checkpoint(BlockId {
3770
1
            height: confirmation_height,
3771
1
            hash: BlockHash::all_zeros(),
3772
1
        })
3773
1
        .unwrap();
3774
1
    let coinbase_tx = Transaction {
3775
1
        version: transaction::Version::ONE,
3776
1
        lock_time: absolute::LockTime::ZERO,
3777
1
        input: vec![TxIn {
3778
1
            previous_output: OutPoint::null(),
3779
1
            ..Default::default()
3780
1
        }],
3781
1
        output: vec![TxOut {
3782
1
            script_pubkey: wallet
3783
1
                .next_unused_address(KeychainKind::External)
3784
1
                .script_pubkey(),
3785
1
            value: Amount::from_sat(25_000),
3786
1
        }],
3787
1
    };
3788
1
    let txid = coinbase_tx.compute_txid();
3789
1
    wallet.insert_tx(coinbase_tx);
3790
1
    insert_anchor_from_conf(
3791
1
        &mut wallet,
3792
1
        txid,
3793
1
        ConfirmationTime::Confirmed {
3794
1
            height: confirmation_height,
3795
1
            time: 30_000,
3796
1
        },
3797
1
    );
3798
1
3799
1
    let not_yet_mature_time = confirmation_height + COINBASE_MATURITY - 1;
3800
1
    let maturity_time = confirmation_height + COINBASE_MATURITY;
3801
1
3802
1
    let balance = wallet.balance();
3803
1
    assert_eq!(
3804
1
        balance,
3805
1
        Balance {
3806
1
            immature: Amount::from_sat(25_000),
3807
1
            trusted_pending: Amount::ZERO,
3808
1
            untrusted_pending: Amount::ZERO,
3809
1
            confirmed: Amount::ZERO
3810
1
        }
3811
1
    );
3812
3813
    // We try to create a transaction, only to notice that all
3814
    // our funds are unspendable
3815
1
    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
3816
1
        .unwrap()
3817
1
        .assume_checked();
3818
1
    let mut builder = wallet.build_tx();
3819
1
    builder
3820
1
        .add_recipient(addr.script_pubkey(), balance.immature / 2)
3821
1
        .current_height(confirmation_height);
3822
1
    assert!(
matches!0
(
3823
1
        builder.finish(),
3824
        Err(CreateTxError::CoinSelection(
3825
            coin_selection::Error::InsufficientFunds {
3826
                needed: _,
3827
                available: 0
3828
            }
3829
        ))
3830
    ));
3831
3832
    // Still unspendable...
3833
1
    let mut builder = wallet.build_tx();
3834
1
    builder
3835
1
        .add_recipient(addr.script_pubkey(), balance.immature / 2)
3836
1
        .current_height(not_yet_mature_time);
3837
0
    assert_matches!(
3838
1
        builder.finish(),
3839
        Err(CreateTxError::CoinSelection(
3840
            coin_selection::Error::InsufficientFunds {
3841
                needed: _,
3842
                available: 0
3843
            }
3844
        ))
3845
    );
3846
3847
1
    wallet
3848
1
        .insert_checkpoint(BlockId {
3849
1
            height: maturity_time,
3850
1
            hash: BlockHash::all_zeros(),
3851
1
        })
3852
1
        .unwrap();
3853
1
    let balance = wallet.balance();
3854
1
    assert_eq!(
3855
1
        balance,
3856
1
        Balance {
3857
1
            immature: Amount::ZERO,
3858
1
            trusted_pending: Amount::ZERO,
3859
1
            untrusted_pending: Amount::ZERO,
3860
1
            confirmed: Amount::from_sat(25_000)
3861
1
        }
3862
1
    );
3863
1
    let mut builder = wallet.build_tx();
3864
1
    builder
3865
1
        .add_recipient(addr.script_pubkey(), balance.confirmed / 2)
3866
1
        .current_height(maturity_time);
3867
1
    builder.finish().unwrap();
3868
1
}
3869
3870
#[test]
3871
1
fn test_allow_dust_limit() {
3872
1
    let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
3873
1
3874
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3875
1
3876
1
    let mut builder = wallet.build_tx();
3877
1
3878
1
    builder.add_recipient(addr.script_pubkey(), Amount::ZERO);
3879
3880
0
    assert_matches!(
3881
1
        builder.finish(),
3882
        Err(CreateTxError::OutputBelowDustLimit(0))
3883
    );
3884
3885
1
    let mut builder = wallet.build_tx();
3886
1
3887
1
    builder
3888
1
        .allow_dust(true)
3889
1
        .add_recipient(addr.script_pubkey(), Amount::ZERO);
3890
1
3891
1
    assert!(builder.finish().is_ok());
3892
1
}
3893
3894
#[test]
3895
1
fn test_fee_rate_sign_no_grinding_high_r() {
3896
1
    // Our goal is to obtain a transaction with a signature with high-R (71 bytes
3897
1
    // instead of 70). We then check that our fee rate and fee calculation is
3898
1
    // alright.
3899
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
3900
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3901
1
    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
3902
1
    let mut builder = wallet.build_tx();
3903
1
    let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
3904
1
    builder
3905
1
        .drain_to(addr.script_pubkey())
3906
1
        .drain_wallet()
3907
1
        .fee_rate(fee_rate)
3908
1
        .add_data(&data);
3909
1
    let mut psbt = builder.finish().unwrap();
3910
1
    let fee = check_fee!(wallet, psbt);
3911
1
    let (op_return_vout, _) = psbt
3912
1
        .unsigned_tx
3913
1
        .output
3914
1
        .iter()
3915
1
        .enumerate()
3916
1
        .find(|(_n, i)| i.script_pubkey.is_op_return())
3917
1
        .unwrap();
3918
1
3919
1
    let mut sig_len: usize = 0;
3920
    // We try to sign many different times until we find a longer signature (71 bytes)
3921
3
    while sig_len < 71 {
3922
2
        // Changing the OP_RETURN data will make the signature change (but not the fee, until
3923
2
        // data[0] is small enough)
3924
2
        data.as_mut_bytes()[0] += 1;
3925
2
        psbt.unsigned_tx.output[op_return_vout].script_pubkey = ScriptBuf::new_op_return(&data);
3926
2
        // Clearing the previous signature
3927
2
        psbt.inputs[0].partial_sigs.clear();
3928
2
        // Signing
3929
2
        wallet
3930
2
            .sign(
3931
2
                &mut psbt,
3932
2
                SignOptions {
3933
2
                    try_finalize: false,
3934
2
                    allow_grinding: false,
3935
2
                    ..Default::default()
3936
2
                },
3937
2
            )
3938
2
            .unwrap();
3939
2
        // We only have one key in the partial_sigs map, this is a trick to retrieve it
3940
2
        let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
3941
2
        sig_len = psbt.inputs[0].partial_sigs[key]
3942
2
            .signature
3943
2
            .serialize_der()
3944
2
            .len();
3945
2
    }
3946
    // Actually finalizing the transaction...
3947
1
    wallet
3948
1
        .sign(
3949
1
            &mut psbt,
3950
1
            SignOptions {
3951
1
                allow_grinding: false,
3952
1
                ..Default::default()
3953
1
            },
3954
1
        )
3955
1
        .unwrap();
3956
    // ...and checking that everything is fine
3957
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
3958
1
}
3959
3960
#[test]
3961
1
fn test_fee_rate_sign_grinding_low_r() {
3962
1
    // Our goal is to obtain a transaction with a signature with low-R (70 bytes)
3963
1
    // by setting the `allow_grinding` signing option as true.
3964
1
    // We then check that our fee rate and fee calculation is alright and that our
3965
1
    // signature is 70 bytes.
3966
1
    let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
3967
1
    let addr = wallet.next_unused_address(KeychainKind::External);
3968
1
    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
3969
1
    let mut builder = wallet.build_tx();
3970
1
    builder
3971
1
        .drain_to(addr.script_pubkey())
3972
1
        .drain_wallet()
3973
1
        .fee_rate(fee_rate);
3974
1
    let mut psbt = builder.finish().unwrap();
3975
1
    let fee = check_fee!(wallet, psbt);
3976
3977
1
    wallet
3978
1
        .sign(
3979
1
            &mut psbt,
3980
1
            SignOptions {
3981
1
                try_finalize: false,
3982
1
                allow_grinding: true,
3983
1
                ..Default::default()
3984
1
            },
3985
1
        )
3986
1
        .unwrap();
3987
1
3988
1
    let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
3989
1
    let sig_len = psbt.inputs[0].partial_sigs[key]
3990
1
        .signature
3991
1
        .serialize_der()
3992
1
        .len();
3993
1
    assert_eq!(sig_len, 70);
3994
    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
3995
1
}
3996
3997
#[test]
3998
1
fn test_taproot_load_descriptor_duplicated_keys() {
3999
1
    // Added after issue https://github.com/bitcoindevkit/bdk/issues/760
4000
1
    //
4001
1
    // Having the same key in multiple taproot leaves is safe and should be accepted by BDK
4002
1
4003
1
    let (wallet, _) = get_funded_wallet(get_test_tr_dup_keys());
4004
1
    let addr = wallet.peek_address(KeychainKind::External, 0);
4005
1
4006
1
    assert_eq!(
4007
1
        addr.to_string(),
4008
1
        "bcrt1pvysh4nmh85ysrkpwtrr8q8gdadhgdejpy6f9v424a8v9htjxjhyqw9c5s5"
4009
1
    );
4010
1
}
4011
4012
/// In dev mode this test panics, but in release mode, or if the `debug_panic` in `TxOutIndex::replenish_inner_index`
4013
/// is commented out, there is no panic and the balance is calculated correctly. See issue [#1483]
4014
/// and PR [#1486] for discussion on mixing non-wildcard and wildcard descriptors.
4015
///
4016
/// [#1483]: https://github.com/bitcoindevkit/bdk/issues/1483
4017
/// [#1486]: https://github.com/bitcoindevkit/bdk/pull/1486
4018
#[test]
4019
#[cfg(debug_assertions)]
4020
#[should_panic(
4021
    expected = "replenish lookahead: must not have existing spk: keychain=Internal, lookahead=25, next_store_index=0, next_reveal_index=0"
4022
)]
4023
1
fn test_keychains_with_overlapping_spks() {
4024
1
    // this can happen if a non-wildcard descriptor keychain derives an spk that a
4025
1
    // wildcard descriptor keychain in the same wallet also derives.
4026
1
4027
1
    // index 1 spk overlaps with non-wildcard change descriptor
4028
1
    let wildcard_keychain = "wpkh(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)";
4029
1
    let non_wildcard_keychain = "wpkh(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/1)";
4030
1
4031
1
    let (mut wallet, _) = get_funded_wallet_with_change(wildcard_keychain, non_wildcard_keychain);
4032
1
    assert_eq!(wallet.balance().confirmed, Amount::from_sat(50000));
4033
4034
0
    let addr = wallet
4035
0
        .reveal_addresses_to(KeychainKind::External, 1)
4036
0
        .last()
4037
0
        .unwrap()
4038
0
        .address;
4039
0
    let _outpoint = receive_output_to_address(
4040
0
        &mut wallet,
4041
0
        addr,
4042
0
        8000,
4043
0
        ConfirmationTime::Confirmed {
4044
0
            height: 2000,
4045
0
            time: 0,
4046
0
        },
4047
0
    );
4048
0
    assert_eq!(wallet.balance().confirmed, Amount::from_sat(58000));
4049
0
}
4050
4051
#[test]
4052
/// The wallet should re-use previously allocated change addresses when the tx using them is cancelled
4053
1
fn test_tx_cancellation() {
4054
1
    macro_rules! new_tx {
4055
1
        ($wallet:expr) => {{
4056
1
            let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
4057
1
                .unwrap()
4058
1
                .assume_checked();
4059
1
            let mut builder = $wallet.build_tx();
4060
1
            builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000));
4061
1
4062
1
            let psbt = builder.finish().unwrap();
4063
1
4064
1
            psbt
4065
1
        }};
4066
1
    }
4067
1
4068
1
    let (mut wallet, _) =
4069
1
        get_funded_wallet_with_change(get_test_wpkh(), get_test_tr_single_sig_xprv());
4070
1
4071
1
    let psbt1 = new_tx!(wallet);
4072
1
    let change_derivation_1 = psbt1
4073
1
        .unsigned_tx
4074
1
        .output
4075
1
        .iter()
4076
1
        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
4077
1
        .unwrap();
4078
1
    assert_eq!(change_derivation_1, (KeychainKind::Internal, 0));
4079
4080
1
    let psbt2 = new_tx!(wallet);
4081
1
4082
1
    let change_derivation_2 = psbt2
4083
1
        .unsigned_tx
4084
1
        .output
4085
1
        .iter()
4086
1
        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
4087
1
        .unwrap();
4088
1
    assert_eq!(change_derivation_2, (KeychainKind::Internal, 1));
4089
4090
1
    wallet.cancel_tx(&psbt1.extract_tx().expect("failed to extract tx"));
4091
1
4092
1
    let psbt3 = new_tx!(wallet);
4093
1
    let change_derivation_3 = psbt3
4094
1
        .unsigned_tx
4095
1
        .output
4096
1
        .iter()
4097
2
        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
4098
1
        .unwrap();
4099
1
    assert_eq!(change_derivation_3, (KeychainKind::Internal, 0));
4100
4101
1
    let psbt3 = new_tx!(wallet);
4102
1
    let change_derivation_3 = psbt3
4103
1
        .unsigned_tx
4104
1
        .output
4105
1
        .iter()
4106
2
        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
4107
1
        .unwrap();
4108
1
    assert_eq!(change_derivation_3, (KeychainKind::Internal, 2));
4109
4110
1
    wallet.cancel_tx(&psbt3.extract_tx().expect("failed to extract tx"));
4111
1
4112
1
    let psbt3 = new_tx!(wallet);
4113
1
    let change_derivation_4 = psbt3
4114
1
        .unsigned_tx
4115
1
        .output
4116
1
        .iter()
4117
1
        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
4118
1
        .unwrap();
4119
1
    assert_eq!(change_derivation_4, (KeychainKind::Internal, 2));
4120
1
}
4121
4122
#[test]
4123
1
fn test_thread_safety() {
4124
1
    fn thread_safe<T: Send + Sync>() {}
4125
1
    thread_safe::<Wallet>(); // compiles only if true
4126
1
}
4127
4128
#[test]
4129
1
fn test_insert_tx_balance_and_utxos() {
4130
1
    // creating many txs has no effect on the wallet's available utxos
4131
1
    let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
4132
1
    let addr = Address::from_str("bcrt1qc6fweuf4xjvz4x3gx3t9e0fh4hvqyu2qw4wvxm")
4133
1
        .unwrap()
4134
1
        .assume_checked();
4135
1
4136
1
    let unspent: Vec<_> = wallet.list_unspent().collect();
4137
1
    assert!(!unspent.is_empty());
4138
4139
1
    let balance = wallet.balance().total();
4140
1
    let fee = Amount::from_sat(143);
4141
1
    let amt = balance - fee;
4142
4143
4
    for _ in 0..3 {
4144
3
        let mut builder = wallet.build_tx();
4145
3
        builder.add_recipient(addr.script_pubkey(), amt);
4146
3
        let mut psbt = builder.finish().unwrap();
4147
3
        assert!(wallet.sign(&mut psbt, SignOptions::default()).unwrap());
4148
3
        let tx = psbt.extract_tx().unwrap();
4149
3
        let _ = wallet.insert_tx(tx);
4150
    }
4151
1
    assert_eq!(wallet.list_unspent().collect::<Vec<_>>(), unspent);
4152
1
    assert_eq!(wallet.balance().confirmed, balance);
4153
4154
    // manually setting a tx last_seen will consume the wallet's available utxos
4155
1
    let addr = Address::from_str("bcrt1qfjg5lv3dvc9az8patec8fjddrs4aqtauadnagr")
4156
1
        .unwrap()
4157
1
        .assume_checked();
4158
1
    let mut builder = wallet.build_tx();
4159
1
    builder.add_recipient(addr.script_pubkey(), amt);
4160
1
    let mut psbt = builder.finish().unwrap();
4161
1
    assert!(wallet.sign(&mut psbt, SignOptions::default()).unwrap());
4162
1
    let tx = psbt.extract_tx().unwrap();
4163
1
    let txid = tx.compute_txid();
4164
1
    let _ = wallet.insert_tx(tx);
4165
1
    insert_seen_at(&mut wallet, txid, 2);
4166
1
    assert!(wallet.list_unspent().next().is_none());
4167
1
    assert_eq!(wallet.balance().total().to_sat(), 0);
4168
1
}
4169
4170
#[test]
4171
1
fn single_descriptor_wallet_can_create_tx_and_receive_change() {
4172
1
    // create single descriptor wallet and fund it
4173
1
    let mut wallet = Wallet::create_single(get_test_tr_single_sig_xprv())
4174
1
        .network(Network::Testnet)
4175
1
        .create_wallet_no_persist()
4176
1
        .unwrap();
4177
1
    assert_eq!(wallet.keychains().count(), 1);
4178
1
    let amt = Amount::from_sat(5_000);
4179
1
    receive_output(
4180
1
        &mut wallet,
4181
1
        2 * amt.to_sat(),
4182
1
        ConfirmationTime::Unconfirmed { last_seen: 2 },
4183
1
    );
4184
1
    // create spend tx that produces a change output
4185
1
    let addr = Address::from_str("bcrt1qc6fweuf4xjvz4x3gx3t9e0fh4hvqyu2qw4wvxm")
4186
1
        .unwrap()
4187
1
        .assume_checked();
4188
1
    let mut builder = wallet.build_tx();
4189
1
    builder.add_recipient(addr.script_pubkey(), amt);
4190
1
    let mut psbt = builder.finish().unwrap();
4191
1
    assert!(wallet.sign(&mut psbt, SignOptions::default()).unwrap());
4192
1
    let tx = psbt.extract_tx().unwrap();
4193
1
    let txid = tx.compute_txid();
4194
1
    wallet.insert_tx(tx);
4195
1
    insert_seen_at(&mut wallet, txid, 4);
4196
1
    let unspent: Vec<_> = wallet.list_unspent().collect();
4197
1
    assert_eq!(unspent.len(), 1);
4198
1
    let utxo = unspent.first().unwrap();
4199
1
    assert!(utxo.txout.value < amt);
4200
1
    assert_eq!(
4201
        utxo.keychain,
4202
        KeychainKind::External,
4203
0
        "tx change should go to external keychain"
4204
    );
4205
1
}