• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bitcoindevkit / bdk / 5834188079

pending completion
5834188079

Pull #1071

github

web-flow
Merge 68b42331c into 0ba6bbe11
Pull Request #1071: Update rust bitcoin (BDK 0.28)

563 of 563 new or added lines in 28 files covered. (100.0%)

14625 of 18342 relevant lines covered (79.74%)

9267.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.26
/src/testutils/blockchain_tests.rs
1
// Bitcoin Dev Kit
2
//
3
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
4
//
5
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
6
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
8
// You may not use this file except in accordance with one or both of these
9
// licenses.
10

11
use crate::testutils::TestIncomingTx;
12
use bitcoin::consensus::encode::{deserialize, serialize};
13
use bitcoin::hashes::sha256d;
14
use bitcoin::{absolute, Address, Amount, Script, ScriptBuf, Sequence, Transaction, Txid, Witness};
15
pub use bitcoincore_rpc::json::AddressType;
16
pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
17
use core::str::FromStr;
18
use electrsd::bitcoind::BitcoinD;
19
use electrsd::electrum_client::ElectrumApi as _;
20
use electrsd::{bitcoind, ElectrsD};
21
pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
22
#[allow(unused_imports)]
23
use log::{debug, error, info, log_enabled, trace, Level};
24
use std::collections::HashMap;
25
use std::env;
26
use std::ops::Deref;
27
use std::time::Duration;
28

29
pub struct TestClient {
30
    pub bitcoind: BitcoinD,
31
    pub electrsd: ElectrsD,
32
}
33

34
impl TestClient {
35
    pub fn new(bitcoind_exe: String, electrs_exe: String) -> Self {
138✔
36
        debug!("launching {} and {}", &bitcoind_exe, &electrs_exe);
138✔
37

38
        let mut conf = bitcoind::Conf::default();
138✔
39
        conf.view_stdout = log_enabled!(Level::Debug);
138✔
40
        let bitcoind = BitcoinD::with_conf(bitcoind_exe, &conf).unwrap();
138✔
41

138✔
42
        let mut conf = electrsd::Conf::default();
138✔
43
        conf.view_stderr = log_enabled!(Level::Debug);
138✔
44
        conf.http_enabled = cfg!(feature = "test-esplora");
138✔
45

138✔
46
        let electrsd = ElectrsD::with_conf(electrs_exe, &bitcoind, &conf).unwrap();
138✔
47

138✔
48
        let node_address = bitcoind
138✔
49
            .client
138✔
50
            .get_new_address(None, None)
138✔
51
            .unwrap()
138✔
52
            .assume_checked();
138✔
53
        bitcoind
138✔
54
            .client
138✔
55
            .generate_to_address(101, &node_address)
138✔
56
            .unwrap();
138✔
57

138✔
58
        let mut test_client = TestClient { bitcoind, electrsd };
138✔
59
        TestClient::wait_for_block(&mut test_client, 101);
138✔
60
        test_client
138✔
61
    }
138✔
62

63
    fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
918✔
64
        // wait for electrs to index the tx
918✔
65
        exponential_backoff_poll(|| {
1,379✔
66
            self.electrsd.trigger().unwrap();
920✔
67
            trace!("wait_for_tx {}", txid);
920✔
68

69
            self.electrsd
920✔
70
                .client
920✔
71
                .script_get_history(monitor_script)
920✔
72
                .unwrap()
920✔
73
                .iter()
920✔
74
                .position(|entry| entry.tx_hash == txid)
2,940✔
75
        });
1,379✔
76
    }
918✔
77

78
    fn wait_for_block(&mut self, min_height: usize) {
366✔
79
        self.electrsd.client.block_headers_subscribe().unwrap();
366✔
80

81
        loop {
366✔
82
            let header = exponential_backoff_poll(|| {
915✔
83
                self.electrsd.trigger().unwrap();
732✔
84
                self.electrsd.client.ping().unwrap();
732✔
85
                self.electrsd.client.block_headers_pop().unwrap()
732✔
86
            });
915✔
87
            if header.height >= min_height {
366✔
88
                break;
366✔
89
            }
×
90
        }
91
    }
366✔
92

93
    pub fn receive(&mut self, meta_tx: TestIncomingTx) -> Txid {
914✔
94
        assert!(
914✔
95
            !meta_tx.output.is_empty(),
914✔
96
            "can't create a transaction with no outputs"
914✔
97
        );
914✔
98

99
        let mut map = HashMap::new();
914✔
100

914✔
101
        let mut required_balance = 0;
914✔
102
        for out in &meta_tx.output {
1,852✔
103
            required_balance += out.value;
938✔
104
            map.insert(out.to_address.clone(), Amount::from_sat(out.value));
938✔
105
        }
938✔
106

107
        let input: Vec<_> = meta_tx
914✔
108
            .input
914✔
109
            .into_iter()
914✔
110
            .map(|x| x.into_raw_tx_input())
916✔
111
            .collect();
914✔
112

914✔
113
        if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) {
914✔
114
            panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap().assume_checked());
×
115
        }
914✔
116

914✔
117
        // FIXME: core can't create a tx with two outputs to the same address
914✔
118
        let tx = self
914✔
119
            .create_raw_transaction_hex(&input, &map, meta_tx.locktime, meta_tx.replaceable)
914✔
120
            .unwrap();
914✔
121
        let tx = self.fund_raw_transaction(tx, None, None).unwrap();
914✔
122
        let mut tx: Transaction = deserialize(&tx.hex).unwrap();
914✔
123

914✔
124
        if let Some(true) = meta_tx.replaceable {
914✔
125
            // for some reason core doesn't set this field right
126
            for input in &mut tx.input {
16✔
127
                input.sequence = Sequence(0xFFFFFFFD);
8✔
128
            }
8✔
129
        }
906✔
130

131
        let tx = self
914✔
132
            .sign_raw_transaction_with_wallet(&serialize(&tx), None, None)
914✔
133
            .unwrap();
914✔
134

914✔
135
        // broadcast through electrum so that it caches the tx immediately
914✔
136

914✔
137
        let txid = self
914✔
138
            .electrsd
914✔
139
            .client
914✔
140
            .transaction_broadcast(&deserialize(&tx.hex).unwrap())
914✔
141
            .unwrap();
914✔
142
        debug!("broadcasted to electrum {}", txid);
914✔
143

144
        if let Some(num) = meta_tx.min_confirmations {
914✔
145
            self.generate(num, None);
32✔
146
        }
882✔
147

148
        let monitor_script = Address::from_str(&meta_tx.output[0].to_address)
914✔
149
            .unwrap()
914✔
150
            .assume_checked()
914✔
151
            .script_pubkey();
914✔
152
        self.wait_for_tx(txid, &monitor_script);
914✔
153

914✔
154
        debug!("Sent tx: {}", txid);
914✔
155

156
        txid
914✔
157
    }
914✔
158

159
    pub fn bump_fee(&mut self, txid: &Txid) -> Txid {
4✔
160
        let tx = self.get_raw_transaction_info(txid, None).unwrap();
4✔
161
        assert!(
4✔
162
            tx.confirmations.is_none(),
4✔
163
            "Can't bump tx {} because it's already confirmed",
×
164
            txid
165
        );
166

167
        let bumped: serde_json::Value = self.call("bumpfee", &[txid.to_string().into()]).unwrap();
4✔
168
        let new_txid = Txid::from_str(&bumped["txid"].as_str().unwrap().to_string()).unwrap();
4✔
169
        let monitor_script = ScriptBuf::from_bytes(tx.vout[0].script_pub_key.hex.clone());
4✔
170
        self.wait_for_tx(new_txid, &monitor_script);
4✔
171

4✔
172
        debug!("Bumped {}, new txid {}", txid, new_txid);
4✔
173

174
        new_txid
4✔
175
    }
4✔
176

177
    pub fn generate_manually(&mut self, txs: Vec<Transaction>) -> String {
×
178
        use bitcoin::blockdata::block::{Block, Header, Version};
×
179
        use bitcoin::blockdata::script::Builder;
×
180
        use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut};
×
181
        use bitcoin::hash_types::{BlockHash, TxMerkleNode};
×
182
        use bitcoin::hashes::Hash;
×
183
        use bitcoin::pow::CompactTarget;
×
184

×
185
        let block_template: serde_json::Value = self
×
186
            .call("getblocktemplate", &[json!({"rules": ["segwit"]})])
×
187
            .unwrap();
×
188
        trace!("getblocktemplate: {:#?}", block_template);
×
189

190
        let header = Header {
×
191
            version: Version::from_consensus(block_template["version"].as_i64().unwrap() as i32),
×
192
            prev_blockhash: BlockHash::from_str(
×
193
                block_template["previousblockhash"].as_str().unwrap(),
×
194
            )
×
195
            .unwrap(),
×
196
            merkle_root: TxMerkleNode::all_zeros(),
×
197
            time: block_template["curtime"].as_u64().unwrap() as u32,
×
198
            bits: CompactTarget::from_consensus(
×
199
                u32::from_str_radix(block_template["bits"].as_str().unwrap(), 16).unwrap(),
×
200
            ),
×
201
            nonce: 0,
×
202
        };
×
203
        debug!("header: {:#?}", header);
×
204

205
        let height = block_template["height"].as_u64().unwrap() as i64;
×
206
        let witness_reserved_value = sha256d::Hash::all_zeros().as_byte_array().to_vec();
×
207
        // burn block subsidy and fees, not a big deal
×
208
        let mut coinbase_tx = Transaction {
×
209
            version: 1,
×
210
            lock_time: absolute::LockTime::ZERO,
×
211
            input: vec![TxIn {
×
212
                previous_output: OutPoint::null(),
×
213
                script_sig: Builder::new().push_int(height).into_script(),
×
214
                sequence: Sequence(0xFFFFFFFF),
×
215
                witness: Witness::from_slice(&vec![witness_reserved_value]),
×
216
            }],
×
217
            output: vec![],
×
218
        };
×
219

×
220
        let mut txdata = vec![coinbase_tx.clone()];
×
221
        txdata.extend_from_slice(&txs);
×
222

×
223
        let mut block = Block { header, txdata };
×
224

225
        if let Some(witness_root) = block.witness_root() {
×
226
            let witness_commitment = Block::compute_witness_commitment(
×
227
                &witness_root,
×
228
                &coinbase_tx.input[0]
×
229
                    .witness
×
230
                    .last()
×
231
                    .expect("Should contain the witness reserved value"),
×
232
            );
×
233

×
234
            // now update and replace the coinbase tx
×
235
            let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
×
236
            coinbase_witness_commitment_script.extend_from_slice(witness_commitment.as_ref());
×
237

×
238
            coinbase_tx.output.push(TxOut {
×
239
                value: 0,
×
240
                script_pubkey: coinbase_witness_commitment_script.into(),
×
241
            });
×
242
        }
×
243

244
        block.txdata[0] = coinbase_tx;
×
245

246
        // set merkle root
247
        if let Some(merkle_root) = block.compute_merkle_root() {
×
248
            block.header.merkle_root = merkle_root;
×
249
        }
×
250

251
        assert!(block.check_merkle_root());
×
252
        assert!(block.check_witness_commitment());
×
253

254
        // now do PoW :)
255
        let target = block.header.target();
×
256
        while block.header.validate_pow(target).is_err() {
×
257
            block.header.nonce = block.header.nonce.checked_add(1).unwrap(); // panic if we run out of nonces
×
258
        }
×
259

260
        let block_hex: String = bitcoin::consensus::encode::serialize_hex(&block);
×
261
        debug!("generated block hex: {}", block_hex);
×
262

263
        self.electrsd.client.block_headers_subscribe().unwrap();
×
264

×
265
        let submit_result: serde_json::Value =
×
266
            self.call("submitblock", &[block_hex.into()]).unwrap();
×
267
        debug!("submitblock: {:?}", submit_result);
×
268
        assert!(
×
269
            submit_result.is_null(),
×
270
            "submitblock error: {:?}",
×
271
            submit_result.as_str()
×
272
        );
273

274
        self.wait_for_block(height as usize);
×
275

×
276
        block.header.block_hash().to_string()
×
277
    }
×
278

279
    pub fn generate(&mut self, num_blocks: u64, address: Option<Address>) {
224✔
280
        let address =
224✔
281
            address.unwrap_or_else(|| self.get_new_address(None, None).unwrap().assume_checked());
318✔
282
        let hashes = self.generate_to_address(num_blocks, &address).unwrap();
224✔
283
        let best_hash = hashes.last().unwrap();
224✔
284
        let height = self.get_block_info(best_hash).unwrap().height;
224✔
285

224✔
286
        self.wait_for_block(height);
224✔
287

224✔
288
        debug!("Generated blocks to new height {}", height);
224✔
289
    }
224✔
290

291
    pub fn invalidate(&mut self, num_blocks: u64) {
4✔
292
        self.electrsd.client.block_headers_subscribe().unwrap();
4✔
293

4✔
294
        let best_hash = self.get_best_block_hash().unwrap();
4✔
295
        let initial_height = self.get_block_info(&best_hash).unwrap().height;
4✔
296

4✔
297
        let mut to_invalidate = best_hash;
4✔
298
        for i in 1..=num_blocks {
4✔
299
            trace!(
4✔
300
                "Invalidating block {}/{} ({})",
×
301
                i,
302
                num_blocks,
303
                to_invalidate
304
            );
305

306
            self.invalidate_block(&to_invalidate).unwrap();
4✔
307
            to_invalidate = self.get_best_block_hash().unwrap();
4✔
308
        }
309

310
        self.wait_for_block(initial_height - num_blocks as usize);
4✔
311

4✔
312
        debug!(
4✔
313
            "Invalidated {} blocks to new height of {}",
×
314
            num_blocks,
×
315
            initial_height - num_blocks as usize
×
316
        );
317
    }
4✔
318

319
    pub fn reorg(&mut self, num_blocks: u64) {
×
320
        self.invalidate(num_blocks);
×
321
        self.generate(num_blocks, None);
×
322
    }
×
323

324
    pub fn get_node_address(&self, address_type: Option<AddressType>) -> Address {
60✔
325
        Address::from_str(
60✔
326
            &self
60✔
327
                .get_new_address(None, address_type)
60✔
328
                .unwrap()
60✔
329
                .assume_checked()
60✔
330
                .to_string(),
60✔
331
        )
60✔
332
        .unwrap()
60✔
333
        .assume_checked()
60✔
334
    }
60✔
335
}
336

337
pub fn get_electrum_url() -> String {
×
338
    env::var("BDK_ELECTRUM_URL").unwrap_or_else(|_| "tcp://127.0.0.1:50001".to_string())
×
339
}
×
340

341
impl Deref for TestClient {
342
    type Target = RpcClient;
343

344
    fn deref(&self) -> &Self::Target {
4,384✔
345
        &self.bitcoind.client
4,384✔
346
    }
4,384✔
347
}
348

349
impl Default for TestClient {
350
    fn default() -> Self {
138✔
351
        let bitcoind_exe = env::var("BITCOIND_EXE")
138✔
352
            .ok()
138✔
353
            .or(bitcoind::downloaded_exe_path().ok())
138✔
354
            .expect(
138✔
355
                "you should provide env var BITCOIND_EXE or specifiy a bitcoind version feature",
138✔
356
            );
138✔
357
        let electrs_exe = env::var("ELECTRS_EXE")
138✔
358
            .ok()
138✔
359
            .or(electrsd::downloaded_exe_path())
138✔
360
            .expect(
138✔
361
                "you should provide env var ELECTRS_EXE or specifiy a electrsd version feature",
138✔
362
            );
138✔
363
        Self::new(bitcoind_exe, electrs_exe)
138✔
364
    }
138✔
365
}
366

367
fn exponential_backoff_poll<T, F>(mut poll: F) -> T
1,284✔
368
where
1,284✔
369
    F: FnMut() -> Option<T>,
1,284✔
370
{
1,284✔
371
    let mut delay = Duration::from_millis(64);
1,284✔
372
    loop {
373
        match poll() {
1,652✔
374
            Some(data) => break data,
1,284✔
375
            None if delay.as_millis() < 512 => delay = delay.mul_f32(2.0),
368✔
376
            None => {}
×
377
        }
378

379
        std::thread::sleep(delay);
368✔
380
    }
381
}
1,284✔
382

383
/// This macro runs blockchain tests against a `Blockchain` implementation. It requires access to a
384
/// Bitcoin core wallet via RPC. At the moment you have to dig into the code yourself and look at
385
/// the setup required to run the tests yourself.
386
#[macro_export]
387
macro_rules! bdk_blockchain_tests {
388
    (
389
     fn $_fn_name:ident ( $( $test_client:ident : &TestClient )? $(,)? ) -> $blockchain:ty $block:block) => {
390
        #[cfg(test)]
391
        mod bdk_blockchain_tests {
392
            use $crate::bitcoin::{Transaction, Network, blockdata::script::PushBytesBuf};
393
            use $crate::testutils::blockchain_tests::TestClient;
394
            use $crate::blockchain::Blockchain;
395
            use $crate::database::MemoryDatabase;
396
            use $crate::types::KeychainKind;
397
            use $crate::wallet::AddressIndex;
398
            use $crate::{Wallet, FeeRate, SyncOptions};
399
            use $crate::testutils;
400
            use std::convert::TryFrom;
401

402
            use super::*;
403

404
            #[allow(unused_variables)]
405
            fn get_blockchain(test_client: &TestClient) -> $blockchain {
128✔
406
                $( let $test_client = test_client; )?
128✔
407
                $block
128✔
408
            }
128✔
409

410
            fn get_wallet_from_descriptors(descriptors: &(String, Option<String>)) -> Wallet<MemoryDatabase> {
140✔
411
                Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new()).unwrap()
140✔
412
            }
140✔
413

414
            #[allow(dead_code)]
415
            enum WalletType {
416
                WpkhSingleSig,
417
                TaprootKeySpend,
418
                TaprootScriptSpend,
419
                TaprootScriptSpend2,
420
                TaprootScriptSpend3,
421
            }
422

423
            fn init_wallet(ty: WalletType) -> (Wallet<MemoryDatabase>, $blockchain, (String, Option<String>), TestClient) {
124✔
424
                let _ = env_logger::try_init();
124✔
425

426
                let descriptors = match ty {
124✔
427
                    WalletType::WpkhSingleSig => testutils! {
104✔
428
                        @descriptors ( "wpkh(Alice)" ) ( "wpkh(Alice)" ) ( @keys ( "Alice" => (@generate_xprv "/44'/0'/0'/0/*", "/44'/0'/0'/1/*") ) )
104✔
429
                    },
104✔
430
                    WalletType::TaprootKeySpend => testutils! {
8✔
431
                        @descriptors ( "tr(Alice)" ) ( "tr(Alice)" ) ( @keys ( "Alice" => (@generate_xprv "/44'/0'/0'/0/*", "/44'/0'/0'/1/*") ) )
8✔
432
                    },
8✔
433
                    WalletType::TaprootScriptSpend => testutils! {
4✔
434
                        @descriptors ( "tr(Key,and_v(v:pk(Script),older(6)))" ) ( "tr(Key,and_v(v:pk(Script),older(6)))" ) ( @keys ( "Key" => (@literal "30e14486f993d5a2d222770e97286c56cec5af115e1fb2e0065f476a0fcf8788"), "Script" => (@generate_xprv "/0/*", "/1/*") ) )
4✔
435
                    },
4✔
436
                    WalletType::TaprootScriptSpend2 => testutils! {
4✔
437
                        @descriptors ( "tr(Alice,pk(Bob))" ) ( "tr(Alice,pk(Bob))" ) ( @keys ( "Alice" => (@literal "30e14486f993d5a2d222770e97286c56cec5af115e1fb2e0065f476a0fcf8788"), "Bob" => (@generate_xprv "/0/*", "/1/*") ) )
4✔
438
                    },
4✔
439
                    WalletType::TaprootScriptSpend3 => testutils! {
4✔
440
                        @descriptors ( "tr(Alice,{pk(Bob),pk(Carol)})" ) ( "tr(Alice,{pk(Bob),pk(Carol)})" ) ( @keys ( "Alice" => (@literal "30e14486f993d5a2d222770e97286c56cec5af115e1fb2e0065f476a0fcf8788"), "Bob" => (@generate_xprv "/0/*", "/1/*"), "Carol" => (@generate_xprv "/0/*", "/1/*") ) )
4✔
441
                    },
4✔
442
                };
443

444
                let test_client = TestClient::default();
124✔
445
                let blockchain = get_blockchain(&test_client);
124✔
446
                let wallet = get_wallet_from_descriptors(&descriptors);
124✔
447

124✔
448
                // rpc need to call import_multi before receiving any tx, otherwise will not see tx in the mempool
124✔
449
                #[cfg(any(feature = "test-rpc", feature = "test-rpc-legacy"))]
124✔
450
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
124✔
451

124✔
452
                (wallet, blockchain, descriptors, test_client)
124✔
453
            }
124✔
454

455
            fn init_single_sig() -> (Wallet<MemoryDatabase>, $blockchain, (String, Option<String>), TestClient) {
104✔
456
                init_wallet(WalletType::WpkhSingleSig)
104✔
457
            }
104✔
458

459
            #[test]
460
            fn test_sync_simple() {
4✔
461
                use std::ops::Deref;
4✔
462
                use crate::database::Database;
4✔
463

4✔
464
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
465

4✔
466
                let tx = testutils! {
4✔
467
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
468
                };
4✔
469
                println!("{:?}", tx);
4✔
470
                let txid = test_client.receive(tx);
4✔
471

4✔
472
                // the RPC blockchain needs to call `sync()` during initialization to import the
4✔
473
                // addresses (see `init_single_sig()`), so we skip this assertion
4✔
474
                #[cfg(not(any(feature = "test-rpc", feature = "test-rpc-legacy")))]
4✔
475
                assert!(wallet.database().deref().get_sync_time().unwrap().is_none(), "initial sync_time not none");
4✔
476

4✔
477
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
478
                assert!(wallet.database().deref().get_sync_time().unwrap().is_some(), "sync_time hasn't been updated");
4✔
479

480
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
481
                assert_eq!(wallet.list_unspent().unwrap()[0].keychain, KeychainKind::External, "incorrect keychain kind");
4✔
482

483
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
484
                assert_eq!(list_tx_item.txid, txid, "incorrect txid");
4✔
485
                assert_eq!(list_tx_item.received, 50_000, "incorrect received");
4✔
486
                assert_eq!(list_tx_item.sent, 0, "incorrect sent");
4✔
487
                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time");
4✔
488
            }
4✔
489

490
            #[test]
491
            fn test_sync_stop_gap_20() {
4✔
492
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
493

4✔
494
                test_client.receive(testutils! {
4✔
495
                    @tx ( (@external descriptors, 5) => 50_000 )
4✔
496
                });
4✔
497
                test_client.receive(testutils! {
4✔
498
                    @tx ( (@external descriptors, 25) => 50_000 )
4✔
499
                });
4✔
500

4✔
501
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
502

4✔
503
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 100_000, "incorrect balance");
4✔
504
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
4✔
505
            }
4✔
506

507
            #[test]
508
            fn test_sync_before_and_after_receive() {
4✔
509
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
510

4✔
511
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
512
                assert_eq!(wallet.get_balance().unwrap().get_total(), 0);
4✔
513

514
                test_client.receive(testutils! {
4✔
515
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
516
                });
4✔
517

4✔
518
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
519

4✔
520
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
521

522
                test_client.receive(testutils! {
4✔
523
                    @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
4✔
524
                });
4✔
525

4✔
526
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
527

4✔
528
                assert_eq!(wallet.get_balance().unwrap().confirmed, 100_000, "incorrect balance");
4✔
529
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
4✔
530
            }
4✔
531

532
            #[test]
533
            fn test_sync_multiple_outputs_same_tx() {
4✔
534
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
535

4✔
536
                let txid = test_client.receive(testutils! {
4✔
537
                    @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
4✔
538
                });
4✔
539

4✔
540
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
541

4✔
542
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 105_000, "incorrect balance");
4✔
543
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
4✔
544
                assert_eq!(wallet.list_unspent().unwrap().len(), 3, "incorrect number of unspents");
4✔
545

546
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
547
                assert_eq!(list_tx_item.txid, txid, "incorrect txid");
4✔
548
                assert_eq!(list_tx_item.received, 105_000, "incorrect received");
4✔
549
                assert_eq!(list_tx_item.sent, 0, "incorrect sent");
4✔
550
                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time");
4✔
551
            }
4✔
552

553
            #[test]
554
            fn test_sync_receive_multi() {
4✔
555
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
556

4✔
557
                test_client.receive(testutils! {
4✔
558
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
559
                });
4✔
560
                test_client.receive(testutils! {
4✔
561
                    @tx ( (@external descriptors, 5) => 25_000 )
4✔
562
                });
4✔
563

4✔
564
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
565

4✔
566
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 75_000, "incorrect balance");
4✔
567
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
4✔
568
                assert_eq!(wallet.list_unspent().unwrap().len(), 2, "incorrect number of unspent");
4✔
569
            }
4✔
570

571
            #[test]
572
            fn test_sync_address_reuse() {
4✔
573
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
574

4✔
575
                test_client.receive(testutils! {
4✔
576
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
577
                });
4✔
578

4✔
579
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
580
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000);
4✔
581

582
                test_client.receive(testutils! {
4✔
583
                    @tx ( (@external descriptors, 0) => 25_000 )
4✔
584
                });
4✔
585

4✔
586
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
587
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 75_000, "incorrect balance");
4✔
588
            }
4✔
589

590
            #[test]
591
            fn test_sync_receive_rbf_replaced() {
4✔
592
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
593

4✔
594
                let txid = test_client.receive(testutils! {
4✔
595
                    @tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
4✔
596
                });
4✔
597

4✔
598
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
599

4✔
600
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
601
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
4✔
602
                assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect unspent");
4✔
603

604
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
605
                assert_eq!(list_tx_item.txid, txid, "incorrect txid");
4✔
606
                assert_eq!(list_tx_item.received, 50_000, "incorrect received");
4✔
607
                assert_eq!(list_tx_item.sent, 0, "incorrect sent");
4✔
608
                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time");
4✔
609

610
                let new_txid = test_client.bump_fee(&txid);
4✔
611

4✔
612
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
613

4✔
614
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance after bump");
4✔
615
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs after bump");
4✔
616
                assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect unspent after bump");
4✔
617

618
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
619
                assert_eq!(list_tx_item.txid, new_txid, "incorrect txid after bump");
4✔
620
                assert_eq!(list_tx_item.received, 50_000, "incorrect received after bump");
4✔
621
                assert_eq!(list_tx_item.sent, 0, "incorrect sent after bump");
4✔
622
                assert_eq!(list_tx_item.confirmation_time, None, "incorrect height after bump");
4✔
623
            }
4✔
624

625
            // FIXME: I would like this to be cfg_attr(not(feature = "test-esplora"), ignore) but it
626
            // doesn't work for some reason.
627
            #[cfg(not(feature = "esplora"))]
628
            #[test]
629
            fn test_sync_reorg_block() {
4✔
630
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
631

4✔
632
                let txid = test_client.receive(testutils! {
4✔
633
                    @tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
4✔
634
                });
4✔
635

4✔
636
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
637
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000, "incorrect balance");
4✔
638
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
4✔
639
                assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect number of unspents");
4✔
640

641
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
642
                assert_eq!(list_tx_item.txid, txid, "incorrect txid");
4✔
643
                assert!(list_tx_item.confirmation_time.is_some(), "incorrect confirmation_time");
4✔
644

645
                // Invalidate 1 block
646
                test_client.invalidate(1);
4✔
647

4✔
648
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
649

4✔
650
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance after invalidate");
4✔
651

652
                let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
4✔
653
                assert_eq!(list_tx_item.txid, txid, "incorrect txid after invalidate");
4✔
654
                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time after invalidate");
4✔
655
            }
4✔
656

657
            #[test]
658
            fn test_sync_after_send() {
4✔
659
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
660
                                println!("{}", descriptors.0);
4✔
661
                let node_addr = test_client.get_node_address(None);
4✔
662

4✔
663
                test_client.receive(testutils! {
4✔
664
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
665
                });
4✔
666

4✔
667
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
668
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
669

670
                let mut builder = wallet.build_tx();
4✔
671
                builder.add_recipient(node_addr.script_pubkey(), 25_000);
4✔
672
                let (mut psbt, details) = builder.finish().unwrap();
4✔
673
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
674
                assert!(finalized, "Cannot finalize transaction");
4✔
675
                let tx = psbt.extract_tx();
4✔
676
                println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
4✔
677
                blockchain.broadcast(&tx).unwrap();
4✔
678
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
679
                assert_eq!(wallet.get_balance().unwrap().trusted_pending, details.received, "incorrect balance after send");
4✔
680

681
                test_client.generate(1, Some(node_addr));
4✔
682
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
683

4✔
684
                assert_eq!(wallet.get_balance().unwrap().confirmed, details.received, "incorrect balance after send");
4✔
685

686
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
4✔
687
                assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect number of unspents");
4✔
688
            }
4✔
689

690
            // Syncing wallet should not result in wallet address index to decrement.
691
            // This is critical as we should always ensure to not reuse addresses.
692
            #[test]
693
            fn test_sync_address_index_should_not_decrement() {
4✔
694
                let (wallet, blockchain, _descriptors, mut test_client) = init_single_sig();
4✔
695

4✔
696
                const ADDRS_TO_FUND: u32 = 7;
4✔
697
                const ADDRS_TO_IGNORE: u32 = 11;
4✔
698

4✔
699
                let mut first_addr_index: u32 = 0;
4✔
700

4✔
701
                (0..ADDRS_TO_FUND + ADDRS_TO_IGNORE).for_each(|i| {
4✔
702
                    let new_addr = wallet.get_address(AddressIndex::New).unwrap();
703

704
                    if i == 0 {
705
                        first_addr_index = new_addr.index;
706
                    }
707
                    assert_eq!(new_addr.index, i+first_addr_index, "unexpected new address index (before sync)");
708

709
                    if i < ADDRS_TO_FUND {
710
                        test_client.receive(testutils! {
711
                            @tx ((@addr new_addr.address) => 50_000)
712
                        });
713
                    }
714
                });
4✔
715

4✔
716
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
717

4✔
718
                let new_addr = wallet.get_address(AddressIndex::New).unwrap();
4✔
719
                assert_eq!(new_addr.index, ADDRS_TO_FUND+ADDRS_TO_IGNORE+first_addr_index, "unexpected new address index (after sync)");
4✔
720
            }
4✔
721

722
            // Even if user does not explicitly grab new addresses, the address index should
723
            // increment after sync (if wallet has a balance).
724
            #[test]
725
            fn test_sync_address_index_should_increment() {
4✔
726
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
727

4✔
728
                const START_FUND: u32 = 4;
4✔
729
                const END_FUND: u32 = 20;
4✔
730

4✔
731
                // "secretly" fund wallet via given range
4✔
732
                (START_FUND..END_FUND).for_each(|addr_index| {
4✔
733
                    test_client.receive(testutils! {
734
                        @tx ((@external descriptors, addr_index) => 50_000)
735
                    });
736
                });
4✔
737

4✔
738
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
739

4✔
740
                let address = wallet.get_address(AddressIndex::New).unwrap();
4✔
741
                assert_eq!(address.index, END_FUND, "unexpected new address index (after sync)");
4✔
742
            }
4✔
743

744
            /// Send two conflicting transactions to the same address twice in a row.
745
            /// The coins should only be received once!
746
            #[test]
747
            fn test_sync_double_receive() {
4✔
748
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
749
                let receiver_wallet = get_wallet_from_descriptors(&("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(), None));
4✔
750
                // need to sync so rpc can start watching
4✔
751
                receiver_wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
752

4✔
753
                test_client.receive(testutils! {
4✔
754
                    @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
4✔
755
                });
4✔
756

4✔
757
                wallet.sync(&blockchain, SyncOptions::default()).expect("sync");
4✔
758
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 75_000, "incorrect balance");
4✔
759
                let target_addr = receiver_wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
4✔
760

761
                let tx1 = {
4✔
762
                    let mut builder = wallet.build_tx();
4✔
763
                    builder.add_recipient(target_addr.script_pubkey(), 49_000).enable_rbf();
4✔
764
                    let (mut psbt, _details) = builder.finish().expect("building first tx");
4✔
765
                    let finalized = wallet.sign(&mut psbt, Default::default()).expect("signing first tx");
4✔
766
                    assert!(finalized, "Cannot finalize transaction");
4✔
767
                    psbt.extract_tx()
4✔
768
                };
769

770
                let tx2 = {
4✔
771
                    let mut builder = wallet.build_tx();
4✔
772
                    builder.add_recipient(target_addr.script_pubkey(), 49_000).enable_rbf().fee_rate(FeeRate::from_sat_per_vb(5.0));
4✔
773
                    let (mut psbt, _details) = builder.finish().expect("building replacement tx");
4✔
774
                    let finalized = wallet.sign(&mut psbt, Default::default()).expect("signing replacement tx");
4✔
775
                    assert!(finalized, "Cannot finalize transaction");
4✔
776
                    psbt.extract_tx()
4✔
777
                };
4✔
778

4✔
779
                blockchain.broadcast(&tx1).expect("broadcasting first");
4✔
780
                blockchain.broadcast(&tx2).expect("broadcasting replacement");
4✔
781
                receiver_wallet.sync(&blockchain, SyncOptions::default()).expect("syncing receiver");
4✔
782
                assert_eq!(receiver_wallet.get_balance().expect("balance").untrusted_pending, 49_000, "should have received coins once and only once");
4✔
783
            }
4✔
784

785
            #[test]
786
            fn test_sync_many_sends_to_a_single_address() {
4✔
787
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
788

789
                for _ in 0..4 {
20✔
790
                    // split this up into multiple blocks so rpc doesn't get angry
791
                    for _ in 0..20 {
336✔
792
                        test_client.receive(testutils! {
320✔
793
                            @tx ( (@external descriptors, 0) => 1_000 )
320✔
794
                        });
320✔
795
                    }
320✔
796
                    test_client.generate(1, None);
16✔
797
                }
798

799
                // add some to the mempool as well.
800
                for _ in 0..20 {
84✔
801
                    test_client.receive(testutils! {
80✔
802
                        @tx ( (@external descriptors, 0) => 1_000 )
80✔
803
                    });
80✔
804
                }
80✔
805

806
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
807

4✔
808
                let balance = wallet.get_balance().unwrap();
4✔
809
                assert_eq!(balance.untrusted_pending + balance.get_spendable(), 100_000);
4✔
810
            }
4✔
811

812
            #[test]
813
            fn test_update_confirmation_time_after_generate() {
4✔
814
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
815
                                println!("{}", descriptors.0);
4✔
816
                let node_addr = test_client.get_node_address(None);
4✔
817

4✔
818
                let received_txid = test_client.receive(testutils! {
4✔
819
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
820
                });
4✔
821

4✔
822
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
823
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
824

825
                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
4✔
826
                let details = tx_map.get(&received_txid).unwrap();
4✔
827
                assert!(details.confirmation_time.is_none());
4✔
828

829
                test_client.generate(1, Some(node_addr));
4✔
830
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
831

4✔
832
                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
4✔
833
                let details = tx_map.get(&received_txid).unwrap();
4✔
834
                assert!(details.confirmation_time.is_some());
4✔
835

836
            }
4✔
837

838
            #[test]
839
            fn test_sync_outgoing_from_scratch() {
4✔
840
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
841
                                let node_addr = test_client.get_node_address(None);
4✔
842
                let received_txid = test_client.receive(testutils! {
4✔
843
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
844
                });
4✔
845

4✔
846
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
847
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
848

849
                let mut builder = wallet.build_tx();
4✔
850
                builder.add_recipient(node_addr.script_pubkey(), 25_000);
4✔
851
                let (mut psbt, details) = builder.finish().unwrap();
4✔
852

4✔
853
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
854
                assert!(finalized, "Cannot finalize transaction");
4✔
855
                let sent_tx = psbt.extract_tx();
4✔
856
                blockchain.broadcast(&sent_tx).unwrap();
4✔
857

4✔
858
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
859
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), details.received, "incorrect balance after receive");
4✔
860

861
                // empty wallet
862
                let wallet = get_wallet_from_descriptors(&descriptors);
4✔
863

4✔
864
                #[cfg(feature = "rpc")]  // rpc cannot see mempool tx before importmulti
4✔
865
                test_client.generate(1, Some(node_addr));
4✔
866

4✔
867
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
868
                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
4✔
869

4✔
870
                let received = tx_map.get(&received_txid).unwrap();
4✔
871
                assert_eq!(received.received, 50_000, "incorrect received from receiver");
4✔
872
                assert_eq!(received.sent, 0, "incorrect sent from receiver");
4✔
873

874
                let sent = tx_map.get(&sent_tx.txid()).unwrap();
4✔
875
                assert_eq!(sent.received, details.received, "incorrect received from sender");
4✔
876
                assert_eq!(sent.sent, details.sent, "incorrect sent from sender");
4✔
877
                assert_eq!(sent.fee.unwrap_or(0), details.fee.unwrap_or(0), "incorrect fees from sender");
4✔
878
            }
4✔
879

880
            #[test]
881
            fn test_sync_long_change_chain() {
4✔
882
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
883
                                let node_addr = test_client.get_node_address(None);
4✔
884

4✔
885
                test_client.receive(testutils! {
4✔
886
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
887
                });
4✔
888

4✔
889
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
890
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
891

892
                let mut total_sent = 0;
4✔
893
                for _ in 0..5 {
24✔
894
                    let mut builder = wallet.build_tx();
20✔
895
                    builder.add_recipient(node_addr.script_pubkey(), 5_000);
20✔
896
                    let (mut psbt, details) = builder.finish().unwrap();
20✔
897
                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
20✔
898
                    assert!(finalized, "Cannot finalize transaction");
20✔
899
                    blockchain.broadcast(&psbt.extract_tx()).unwrap();
20✔
900

20✔
901
                    wallet.sync(&blockchain, SyncOptions::default()).unwrap();
20✔
902

20✔
903
                    total_sent += 5_000 + details.fee.unwrap_or(0);
20✔
904
                }
905

906
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
907
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000 - total_sent, "incorrect balance after chain");
4✔
908

909
                // empty wallet
910

911
                let wallet = get_wallet_from_descriptors(&descriptors);
4✔
912

4✔
913
                #[cfg(feature = "rpc")]  // rpc cannot see mempool tx before importmulti
4✔
914
                test_client.generate(1, Some(node_addr));
4✔
915

4✔
916
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
917
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000 - total_sent, "incorrect balance empty wallet");
4✔
918

919
            }
4✔
920

921
            #[test]
922
            fn test_sync_bump_fee_basic() {
4✔
923
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
924
                                let node_addr = test_client.get_node_address(None);
4✔
925

4✔
926
                test_client.receive(testutils! {
4✔
927
                    @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
4✔
928
                });
4✔
929

4✔
930
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
931
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000, "incorrect balance");
4✔
932

933
                let mut builder = wallet.build_tx();
4✔
934
                builder.add_recipient(node_addr.script_pubkey().clone(), 5_000).enable_rbf();
4✔
935
                let (mut psbt, details) = builder.finish().unwrap();
4✔
936
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
937
                assert!(finalized, "Cannot finalize transaction");
4✔
938
                blockchain.broadcast(&psbt.extract_tx()).unwrap();
4✔
939
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
940
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000 - details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees");
4✔
941
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), details.received, "incorrect balance from received");
4✔
942

943
                let mut builder = wallet.build_fee_bump(details.txid).unwrap();
4✔
944
                builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
4✔
945
                let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx");
4✔
946
                let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
4✔
947
                assert!(finalized, "Cannot finalize transaction");
4✔
948
                blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
4✔
949
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
950
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000 - new_details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees after bump");
4✔
951
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), new_details.received, "incorrect balance from received after bump");
4✔
952

953
                assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees");
4✔
954
            }
4✔
955

956
            #[test]
957
            fn test_sync_bump_fee_remove_change() {
4✔
958
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
959
                                let node_addr = test_client.get_node_address(None);
4✔
960

4✔
961
                test_client.receive(testutils! {
4✔
962
                    @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
4✔
963
                });
4✔
964

4✔
965
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
966
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000, "incorrect balance");
4✔
967

968
                let mut builder = wallet.build_tx();
4✔
969
                builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
4✔
970
                let (mut psbt, details) = builder.finish().unwrap();
4✔
971
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
972
                assert!(finalized, "Cannot finalize transaction");
4✔
973
                blockchain.broadcast(&psbt.extract_tx()).unwrap();
4✔
974
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
975
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 1_000 - details.fee.unwrap_or(0), "incorrect balance after send");
4✔
976
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), details.received, "incorrect received after send");
4✔
977

978
                let mut builder = wallet.build_fee_bump(details.txid).unwrap();
4✔
979
                builder.fee_rate(FeeRate::from_sat_per_vb(5.1));
4✔
980
                let (mut new_psbt, new_details) = builder.finish().unwrap();
4✔
981
                let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
4✔
982
                assert!(finalized, "Cannot finalize transaction");
4✔
983
                blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
4✔
984
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
985
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 0, "incorrect balance after change removal");
4✔
986
                assert_eq!(new_details.received, 0, "incorrect received after change removal");
4✔
987

988
                assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees");
4✔
989
            }
4✔
990

991
            #[test]
992
            fn test_sync_bump_fee_add_input_simple() {
4✔
993
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
994
                                let node_addr = test_client.get_node_address(None);
4✔
995

4✔
996
                test_client.receive(testutils! {
4✔
997
                    @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
4✔
998
                });
4✔
999

4✔
1000
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1001
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 75_000, "incorrect balance");
4✔
1002

1003
                let mut builder = wallet.build_tx();
4✔
1004
                builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
4✔
1005
                let (mut psbt, details) = builder.finish().unwrap();
4✔
1006
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1007
                assert!(finalized, "Cannot finalize transaction");
4✔
1008
                blockchain.broadcast(&psbt.extract_tx()).unwrap();
4✔
1009
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1010
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
4✔
1011
                assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
4✔
1012

1013
                let mut builder = wallet.build_fee_bump(details.txid).unwrap();
4✔
1014
                builder.fee_rate(FeeRate::from_sat_per_vb(10.0));
4✔
1015
                let (mut new_psbt, new_details) = builder.finish().unwrap();
4✔
1016
                let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
4✔
1017
                assert!(finalized, "Cannot finalize transaction");
4✔
1018
                blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
4✔
1019
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1020
                assert_eq!(new_details.sent, 75_000, "incorrect sent");
4✔
1021
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), new_details.received, "incorrect balance after add input");
4✔
1022
            }
4✔
1023

1024
            #[test]
1025
            fn test_sync_bump_fee_add_input_no_change() {
4✔
1026
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
1027
                let node_addr = test_client.get_node_address(None);
4✔
1028

4✔
1029
                test_client.receive(testutils! {
4✔
1030
                    @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
4✔
1031
                });
4✔
1032

4✔
1033
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1034
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 75_000, "incorrect balance");
4✔
1035

1036
                let mut builder = wallet.build_tx();
4✔
1037
                builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
4✔
1038
                let (mut psbt, details) = builder.finish().unwrap();
4✔
1039
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1040
                assert!(finalized, "Cannot finalize transaction");
4✔
1041
                blockchain.broadcast(&psbt.extract_tx()).unwrap();
4✔
1042
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1043
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
4✔
1044
                assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
4✔
1045

1046
                let mut builder = wallet.build_fee_bump(details.txid).unwrap();
4✔
1047
                builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
4✔
1048
                let (mut new_psbt, new_details) = builder.finish().unwrap();
4✔
1049
                println!("{:#?}", new_details);
4✔
1050

4✔
1051
                let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
4✔
1052
                assert!(finalized, "Cannot finalize transaction");
4✔
1053
                blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
4✔
1054
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1055
                assert_eq!(new_details.sent, 75_000, "incorrect sent");
4✔
1056
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 0, "incorrect balance after add input");
4✔
1057
                assert_eq!(new_details.received, 0, "incorrect received after add input");
4✔
1058
            }
4✔
1059

1060

1061
            #[test]
1062
            fn test_add_data() {
4✔
1063
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
1064
                                let node_addr = test_client.get_node_address(None);
4✔
1065
                let _ = test_client.receive(testutils! {
4✔
1066
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
1067
                });
4✔
1068

4✔
1069
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1070
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
4✔
1071

1072
                let mut builder = wallet.build_tx();
4✔
1073
                let data = PushBytesBuf::try_from(vec![0 as u8]).unwrap();
4✔
1074
                builder.add_data(&data);
4✔
1075
                let (mut psbt, details) = builder.finish().unwrap();
4✔
1076

4✔
1077
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1078
                assert!(finalized, "Cannot finalize transaction");
4✔
1079
                let tx = psbt.extract_tx();
4✔
1080
                let serialized_tx = bitcoin::consensus::encode::serialize(&tx);
4✔
1081
                assert!(serialized_tx.windows(data.len()).any(|e| e==data.as_bytes()), "cannot find op_return data in transaction");
4✔
1082
                blockchain.broadcast(&tx).unwrap();
4✔
1083
                test_client.generate(1, Some(node_addr));
4✔
1084
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1085
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000 - details.fee.unwrap_or(0), "incorrect balance after send");
4✔
1086

1087
                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
4✔
1088
                let _ = tx_map.get(&tx.txid()).unwrap();
4✔
1089
            }
4✔
1090

1091
            #[test]
1092
            fn test_sync_receive_coinbase() {
4✔
1093
                let (wallet, blockchain, _, mut test_client) = init_single_sig();
4✔
1094

4✔
1095
                let wallet_addr = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
4✔
1096
                println!("wallet addr: {}", wallet_addr);
4✔
1097

4✔
1098
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1099
                assert_eq!(wallet.get_balance().unwrap().immature, 0, "incorrect balance");
4✔
1100

1101
                test_client.generate(1, Some(wallet_addr));
4✔
1102

4✔
1103
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1104

4✔
1105
                assert!(wallet.get_balance().unwrap().immature > 0, "incorrect balance after receiving coinbase");
4✔
1106

1107
                // make coinbase mature (100 blocks)
1108
                let node_addr = test_client.get_node_address(None);
4✔
1109
                test_client.generate(100, Some(node_addr));
4✔
1110
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1111

4✔
1112
                assert!(wallet.get_balance().unwrap().confirmed > 0, "incorrect balance after maturing coinbase");
4✔
1113

1114
            }
4✔
1115

1116
            #[test]
1117
            #[cfg(not(feature = "test-rpc-legacy"))]
1118
            fn test_send_to_bech32m_addr() {
4✔
1119
                use std::str::FromStr;
4✔
1120
                use serde;
4✔
1121
                use serde_json;
4✔
1122
                use serde::Serialize;
4✔
1123
                use bitcoincore_rpc::jsonrpc::serde_json::Value;
4✔
1124
                use bitcoincore_rpc::{Auth, Client, RpcApi};
4✔
1125

4✔
1126
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
1127

4✔
1128
                // TODO remove once rust-bitcoincore-rpc with PR 199 released
4✔
1129
                // https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/199
4✔
1130
                /// Import Descriptor Request
4✔
1131
                #[derive(Serialize, Clone, PartialEq, Eq, Debug)]
10✔
1132
                pub struct ImportDescriptorRequest {
4✔
1133
                    pub active: bool,
4✔
1134
                    #[serde(rename = "desc")]
4✔
1135
                    pub descriptor: String,
4✔
1136
                    pub range: [i64; 2],
4✔
1137
                    pub next_index: i64,
4✔
1138
                    pub timestamp: String,
4✔
1139
                    pub internal: bool,
4✔
1140
                }
4✔
1141

4✔
1142
                // TODO remove once rust-bitcoincore-rpc with PR 199 released
4✔
1143
                impl ImportDescriptorRequest {
4✔
1144
                    /// Create a new Import Descriptor request providing just the descriptor and internal flags
4✔
1145
                    pub fn new(descriptor: &str, internal: bool) -> Self {
8✔
1146
                        ImportDescriptorRequest {
8✔
1147
                            descriptor: descriptor.to_string(),
8✔
1148
                            internal,
8✔
1149
                            active: true,
8✔
1150
                            range: [0, 100],
8✔
1151
                            next_index: 0,
8✔
1152
                            timestamp: "now".to_string(),
8✔
1153
                        }
8✔
1154
                    }
8✔
1155
                }
4✔
1156

4✔
1157
                // 1. Create and add descriptors to a test bitcoind node taproot wallet
4✔
1158

4✔
1159
                // TODO replace once rust-bitcoincore-rpc with PR 174 released
4✔
1160
                // https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/174
4✔
1161
                let _createwallet_result: Value = test_client.bitcoind.client.call("createwallet", &["taproot_wallet".into(),false.into(),true.into(),serde_json::to_value("").unwrap(), false.into(), true.into()]).unwrap();
4✔
1162

4✔
1163
                // TODO replace once bitcoind released with support for rust-bitcoincore-rpc PR 174
4✔
1164
                let taproot_wallet_client = Client::new(&test_client.bitcoind.rpc_url_with_wallet("taproot_wallet"), Auth::CookieFile(test_client.bitcoind.params.cookie_file.clone())).unwrap();
4✔
1165

4✔
1166
                let wallet_descriptor = "tr(tprv8ZgxMBicQKsPdBtxmEMPnNq58KGusNAimQirKFHqX2yk2D8q1v6pNLiKYVAdzDHy2w3vF4chuGfMvNtzsbTTLVXBcdkCA1rje1JG6oksWv8/86h/1h/0h/0/*)#y283ssmn";
4✔
1167
                let change_descriptor = "tr(tprv8ZgxMBicQKsPdBtxmEMPnNq58KGusNAimQirKFHqX2yk2D8q1v6pNLiKYVAdzDHy2w3vF4chuGfMvNtzsbTTLVXBcdkCA1rje1JG6oksWv8/86h/1h/0h/1/*)#47zsd9tt";
4✔
1168

4✔
1169
                let tr_descriptors = vec![
4✔
1170
                            ImportDescriptorRequest::new(wallet_descriptor, false),
4✔
1171
                            ImportDescriptorRequest::new(change_descriptor, false),
4✔
1172
                        ];
4✔
1173

4✔
1174
                // TODO replace once rust-bitcoincore-rpc with PR 199 released
4✔
1175
                let _import_result: Value = taproot_wallet_client.call("importdescriptors", &[serde_json::to_value(tr_descriptors).unwrap()]).unwrap();
4✔
1176

4✔
1177
                // 2. Get a new bech32m address from test bitcoind node taproot wallet
4✔
1178

4✔
1179
                // TODO replace once rust-bitcoincore-rpc with PR 199 released
4✔
1180
                let node_addr: bitcoin::Address<bitcoin::address::NetworkUnchecked> = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).unwrap();
4✔
1181
                let node_addr = node_addr.assume_checked();
4✔
1182
                assert_eq!(node_addr, bitcoin::Address::from_str("bcrt1pj5y3f0fu4y7g98k4v63j9n0xvj3lmln0cpwhsjzknm6nt0hr0q7qnzwsy9").unwrap().assume_checked());
4✔
1183

1184
                // 3. Send 50_000 sats from test bitcoind node to test BDK wallet
1185

1186
                test_client.receive(testutils! {
4✔
1187
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
1188
                });
4✔
1189

4✔
1190
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1191
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "wallet has incorrect balance");
4✔
1192

1193
                // 4. Send 25_000 sats from test BDK wallet to test bitcoind node taproot wallet
1194

1195
                let mut builder = wallet.build_tx();
4✔
1196
                builder.add_recipient(node_addr.script_pubkey(), 25_000);
4✔
1197
                let (mut psbt, details) = builder.finish().unwrap();
4✔
1198
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1199
                assert!(finalized, "wallet cannot finalize transaction");
4✔
1200
                let tx = psbt.extract_tx();
4✔
1201
                blockchain.broadcast(&tx).unwrap();
4✔
1202
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1203
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), details.received, "wallet has incorrect balance after send");
4✔
1204
                assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "wallet has incorrect number of txs");
4✔
1205
                assert_eq!(wallet.list_unspent().unwrap().len(), 1, "wallet has incorrect number of unspents");
4✔
1206
                test_client.generate(1, None);
4✔
1207

4✔
1208
                // 5. Verify 25_000 sats are received by test bitcoind node taproot wallet
4✔
1209

4✔
1210
                let taproot_balance = taproot_wallet_client.get_balance(None, None).unwrap();
4✔
1211
                assert_eq!(taproot_balance.to_sat(), 25_000, "node has incorrect taproot wallet balance");
4✔
1212
            }
4✔
1213

1214
            #[test]
1215
            fn test_tx_chain() {
4✔
1216
                use bitcoincore_rpc::RpcApi;
4✔
1217
                use bitcoin::consensus::encode::deserialize;
4✔
1218
                use $crate::wallet::AddressIndex;
4✔
1219

4✔
1220
                // Here we want to test that we set correctly the send and receive
4✔
1221
                // fields in the transaction object. For doing so, we create two
4✔
1222
                // different txs, the second one spending from the first:
4✔
1223
                // 1.
4✔
1224
                // Core (#1) -> Core (#2)
4✔
1225
                //           -> Us   (#3)
4✔
1226
                // 2.
4✔
1227
                // Core (#2) -> Us   (#4)
4✔
1228

4✔
1229
                let (wallet, blockchain, _, mut test_client) = init_single_sig();
4✔
1230
                let bdk_address = wallet.get_address(AddressIndex::New).unwrap().address;
4✔
1231
                let core_address = test_client.get_new_address(None, None).unwrap().assume_checked();
4✔
1232
                let tx = testutils! {
4✔
1233
                    @tx ( (@addr bdk_address.clone()) => 50_000, (@addr core_address.clone()) => 40_000 )
4✔
1234
                };
4✔
1235

4✔
1236
                // Tx one: from Core #1 to Core #2 and Us #3.
4✔
1237
                let txid_1 = test_client.receive(tx);
4✔
1238
                let tx_1: Transaction = deserialize(&test_client.get_transaction(&txid_1, None).unwrap().hex).unwrap();
4✔
1239
                let vout_1 = tx_1.output.into_iter().position(|o| o.script_pubkey == core_address.script_pubkey()).unwrap() as u32;
4✔
1240
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1241
                let tx_1 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_1).unwrap();
4✔
1242
                assert_eq!(tx_1.received, 50_000);
4✔
1243
                assert_eq!(tx_1.sent, 0);
4✔
1244

1245
                // Tx two: from Core #2 to Us #4.
1246
                let tx = testutils! {
4✔
1247
                    @tx ( (@addr bdk_address) => 10_000 ) ( @inputs (txid_1,vout_1))
4✔
1248
                };
4✔
1249
                let txid_2 = test_client.receive(tx);
4✔
1250

4✔
1251
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1252
                let tx_2 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_2).unwrap();
4✔
1253
                assert_eq!(tx_2.received, 10_000);
4✔
1254
                assert_eq!(tx_2.sent, 0);
4✔
1255
            }
4✔
1256

1257
            #[test]
1258
            fn test_double_spend() {
4✔
1259
                // We create a tx and then we try to double spend it; BDK will always allow
4✔
1260
                // us to do so, as it never forgets about spent UTXOs
4✔
1261
                let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
4✔
1262
                let node_addr = test_client.get_node_address(None);
4✔
1263
                let _ = test_client.receive(testutils! {
4✔
1264
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
1265
                });
4✔
1266

4✔
1267
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1268
                let mut builder = wallet.build_tx();
4✔
1269
                builder.add_recipient(node_addr.script_pubkey(), 25_000);
4✔
1270
                let (mut psbt, _details) = builder.finish().unwrap();
4✔
1271
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1272
                assert!(finalized, "Cannot finalize transaction");
4✔
1273
                let initial_tx = psbt.extract_tx();
4✔
1274
                let _sent_txid = blockchain.broadcast(&initial_tx).unwrap();
4✔
1275
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1276
                for utxo in wallet.list_unspent().unwrap() {
4✔
1277
                    // Making sure the TXO we just spent is not returned by list_unspent
1278
                    assert!(utxo.outpoint != initial_tx.input[0].previous_output, "wallet displays spent txo in unspents");
4✔
1279
                }
1280
                // We can still create a transaction double spending `initial_tx`
1281
                let mut builder = wallet.build_tx();
4✔
1282
                builder
4✔
1283
                    .add_utxo(initial_tx.input[0].previous_output)
4✔
1284
                    .expect("Can't manually add an UTXO spent");
4✔
1285
                test_client.generate(1, Some(node_addr));
4✔
1286
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1287
                // Even after confirmation, we can still create a tx double spend it
4✔
1288
                let mut builder = wallet.build_tx();
4✔
1289
                builder
4✔
1290
                    .add_utxo(initial_tx.input[0].previous_output)
4✔
1291
                    .expect("Can't manually add an UTXO spent");
4✔
1292
                for utxo in wallet.list_unspent().unwrap() {
4✔
1293
                    // Making sure the TXO we just spent is not returned by list_unspent
1294
                    assert!(utxo.outpoint != initial_tx.input[0].previous_output, "wallet displays spent txo in unspents");
4✔
1295
                }
1296
            }
4✔
1297

1298
            #[test]
1299
            fn test_send_receive_pkh() {
4✔
1300
                let descriptors = ("pkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(), None);
4✔
1301
                let mut test_client = TestClient::default();
4✔
1302
                let blockchain = get_blockchain(&test_client);
4✔
1303

4✔
1304
                let wallet = get_wallet_from_descriptors(&descriptors);
4✔
1305
                #[cfg(any(feature = "test-rpc", feature = "test-rpc-legacy"))]
4✔
1306
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1307

4✔
1308
                let _ = test_client.receive(testutils! {
4✔
1309
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
1310
                });
4✔
1311

4✔
1312
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1313

4✔
1314
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000);
4✔
1315

1316
                let tx = {
4✔
1317
                    let mut builder = wallet.build_tx();
4✔
1318
                    builder.add_recipient(test_client.get_node_address(None).script_pubkey(), 25_000);
4✔
1319
                    let (mut psbt, _details) = builder.finish().unwrap();
4✔
1320
                    wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1321
                    psbt.extract_tx()
4✔
1322
                };
4✔
1323
                blockchain.broadcast(&tx).unwrap();
4✔
1324

4✔
1325
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1326
            }
4✔
1327

1328
            #[test]
1329
            #[cfg(not(feature = "test-rpc-legacy"))]
1330
            fn test_taproot_key_spend() {
4✔
1331
                let (wallet, blockchain, descriptors, mut test_client) = init_wallet(WalletType::TaprootKeySpend);
4✔
1332

4✔
1333
                let _ = test_client.receive(testutils! {
4✔
1334
                    @tx ( (@external descriptors, 0) => 50_000 )
4✔
1335
                });
4✔
1336
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1337
                assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000);
4✔
1338

1339
                let tx = {
4✔
1340
                    let mut builder = wallet.build_tx();
4✔
1341
                    builder.add_recipient(test_client.get_node_address(None).script_pubkey(), 25_000);
4✔
1342
                    let (mut psbt, _details) = builder.finish().unwrap();
4✔
1343
                    wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1344
                    psbt.extract_tx()
4✔
1345
                };
4✔
1346
                blockchain.broadcast(&tx).unwrap();
4✔
1347
            }
4✔
1348

1349
            #[test]
1350
            #[cfg(not(feature = "test-rpc-legacy"))]
1351
            fn test_taproot_script_spend() {
4✔
1352
                let (wallet, blockchain, descriptors, mut test_client) = init_wallet(WalletType::TaprootScriptSpend);
4✔
1353

4✔
1354
                let _ = test_client.receive(testutils! {
4✔
1355
                    @tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 6 )
4✔
1356
                });
4✔
1357
                wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4✔
1358
                assert_eq!(wallet.get_balance().unwrap().get_spendable(), 50_000);
4✔
1359

1360
                let ext_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
4✔
1361
                let int_policy = wallet.policies(KeychainKind::Internal).unwrap().unwrap();
4✔
1362

4✔
1363
                let ext_path = vec![(ext_policy.id.clone(), vec![1])].into_iter().collect();
4✔
1364
                let int_path = vec![(int_policy.id.clone(), vec![1])].into_iter().collect();
4✔
1365

4✔
1366
                let tx = {
4✔
1367
                    let mut builder = wallet.build_tx();
4✔
1368
                    builder.add_recipient(test_client.get_node_address(None).script_pubkey(), 25_000)
4✔
1369
                        .policy_path(ext_path, KeychainKind::External)
4✔
1370
                        .policy_path(int_path, KeychainKind::Internal);
4✔
1371
                    let (mut psbt, _details) = builder.finish().unwrap();
4✔
1372
                    wallet.sign(&mut psbt, Default::default()).unwrap();
4✔
1373
                    psbt.extract_tx()
4✔
1374
                };
4✔
1375
                blockchain.broadcast(&tx).unwrap();
4✔
1376
            }
4✔
1377

1378
            #[test]
1379
            #[cfg(not(feature = "test-rpc-legacy"))]
1380
            fn test_sign_taproot_core_keyspend_psbt() {
4✔
1381
                test_sign_taproot_core_psbt(WalletType::TaprootKeySpend);
4✔
1382
            }
4✔
1383

1384
            #[test]
1385
            #[cfg(not(feature = "test-rpc-legacy"))]
1386
            fn test_sign_taproot_core_scriptspend2_psbt() {
4✔
1387
                test_sign_taproot_core_psbt(WalletType::TaprootScriptSpend2);
4✔
1388
            }
4✔
1389

1390
            #[test]
1391
            #[cfg(not(feature = "test-rpc-legacy"))]
1392
            fn test_sign_taproot_core_scriptspend3_psbt() {
4✔
1393
                test_sign_taproot_core_psbt(WalletType::TaprootScriptSpend3);
4✔
1394
            }
4✔
1395

1396
            #[cfg(not(feature = "test-rpc-legacy"))]
1397
            fn test_sign_taproot_core_psbt(wallet_type: WalletType) {
12✔
1398
                use std::str::FromStr;
12✔
1399
                use serde_json;
12✔
1400
                use bitcoincore_rpc::jsonrpc::serde_json::Value;
12✔
1401
                use bitcoincore_rpc::{Auth, Client, RpcApi};
12✔
1402

12✔
1403
                let (wallet, _blockchain, _descriptors, test_client) = init_wallet(wallet_type);
12✔
1404

12✔
1405
                // TODO replace once rust-bitcoincore-rpc with PR 174 released
12✔
1406
                // https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/174
12✔
1407
                let _createwallet_result: Value = test_client.bitcoind.client.call("createwallet", &["taproot_wallet".into(), true.into(), true.into(), serde_json::to_value("").unwrap(), false.into(), true.into(), true.into(), false.into()]).expect("created wallet");
12✔
1408

12✔
1409
                let external_descriptor = wallet.get_descriptor_for_keychain(KeychainKind::External);
12✔
1410

12✔
1411
                // TODO replace once bitcoind released with support for rust-bitcoincore-rpc PR 174
12✔
1412
                let taproot_wallet_client = Client::new(&test_client.bitcoind.rpc_url_with_wallet("taproot_wallet"), Auth::CookieFile(test_client.bitcoind.params.cookie_file.clone())).unwrap();
12✔
1413

12✔
1414
                let descriptor_info = taproot_wallet_client.get_descriptor_info(external_descriptor.to_string().as_str()).expect("descriptor info");
12✔
1415

12✔
1416
                let import_descriptor_args = json!([{
12✔
1417
                    "desc": descriptor_info.descriptor,
12✔
1418
                    "active": true,
12✔
1419
                    "timestamp": "now",
12✔
1420
                    "label":"taproot key spend",
12✔
1421
                }]);
12✔
1422
                let _importdescriptors_result: Value = taproot_wallet_client.call("importdescriptors", &[import_descriptor_args]).expect("import wallet");
12✔
1423
                let generate_to_address: bitcoin::Address<bitcoin::address::NetworkUnchecked> = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).expect("new address");
12✔
1424
                let _generatetoaddress_result = taproot_wallet_client.generate_to_address(101, &generate_to_address.assume_checked()).expect("generated to address");
12✔
1425
                let send_to_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string();
12✔
1426
                let change_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string();
12✔
1427
                let send_addr_amounts = json!([{
12✔
1428
                    send_to_address: "0.4321"
12✔
1429
                }]);
12✔
1430
                let send_options = json!({
12✔
1431
                    "change_address": change_address,
12✔
1432
                    "psbt": true,
12✔
1433
                });
12✔
1434
                let send_result: Value = taproot_wallet_client.call("send", &[send_addr_amounts, Value::Null, "unset".into(), Value::Null, send_options]).expect("send psbt");
12✔
1435
                let core_psbt = send_result["psbt"].as_str().expect("core psbt str");
12✔
1436

12✔
1437
                use bitcoin::psbt::PartiallySignedTransaction;
12✔
1438

12✔
1439
                // Test parsing core created PSBT
12✔
1440
                let mut psbt = PartiallySignedTransaction::from_str(&core_psbt).expect("core taproot psbt");
12✔
1441

12✔
1442
                // Test signing core created PSBT
12✔
1443
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
12✔
1444
                assert_eq!(finalized, true);
12✔
1445

1446
                // Test with updated psbt
1447
                let update_result: Value = taproot_wallet_client.call("utxoupdatepsbt", &[core_psbt.into()]).expect("update psbt utxos");
12✔
1448
                let core_updated_psbt = update_result.as_str().expect("core updated psbt");
12✔
1449

12✔
1450
                // Test parsing core created and updated PSBT
12✔
1451
                let mut psbt = PartiallySignedTransaction::from_str(&core_updated_psbt).expect("core taproot psbt");
12✔
1452

12✔
1453
                // Test signing core created and updated PSBT
12✔
1454
                let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
12✔
1455
                assert_eq!(finalized, true);
12✔
1456
            }
12✔
1457

1458
            #[test]
1459
            fn test_get_block_hash() {
4✔
1460
                use bitcoincore_rpc::{ RpcApi };
4✔
1461
                use crate::blockchain::GetBlockHash;
4✔
1462

4✔
1463
                // create wallet with init_wallet
4✔
1464
                let (_, blockchain, _descriptors, mut test_client) = init_single_sig();
4✔
1465

4✔
1466
                let height = test_client.bitcoind.client.get_blockchain_info().unwrap().blocks as u64;
4✔
1467
                let best_hash = test_client.bitcoind.client.get_best_block_hash().unwrap();
4✔
1468

4✔
1469
                // use get_block_hash to get best block hash and compare with best_hash above
4✔
1470
                let block_hash = blockchain.get_block_hash(height).unwrap();
4✔
1471
                assert_eq!(best_hash, block_hash);
4✔
1472

1473
                // generate blocks to address
1474
                let node_addr = test_client.get_node_address(None);
4✔
1475
                test_client.generate(10, Some(node_addr));
4✔
1476

4✔
1477
                let height = test_client.bitcoind.client.get_blockchain_info().unwrap().blocks as u64;
4✔
1478
                let best_hash = test_client.bitcoind.client.get_best_block_hash().unwrap();
4✔
1479

4✔
1480
                let block_hash = blockchain.get_block_hash(height).unwrap();
4✔
1481
                assert_eq!(best_hash, block_hash);
4✔
1482

1483
                // try to get hash for block that has not yet been created.
1484
                assert!(blockchain.get_block_hash(height + 1).is_err());
4✔
1485
            }
4✔
1486
        }
1487
    };
1488

1489
    ( fn $fn_name:ident ($( $tt:tt )+) -> $blockchain:ty $block:block) => {
1490
        compile_error!(concat!("Invalid arguments `", stringify!($($tt)*), "` in the blockchain tests fn."));
1491
        compile_error!("Only the exact `&TestClient` type is supported, **without** any leading path items.");
1492
    };
1493
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc