Coverage Report

Created: 2024-10-10 10:38

/home/darosior/projects/bdk/crates/testenv/src/lib.rs
Line
Count
Source (jump to first uncovered line)
1
use bdk_chain::{
2
    bitcoin::{
3
        address::NetworkChecked, block::Header, hash_types::TxMerkleNode, hashes::Hash,
4
        secp256k1::rand::random, transaction, Address, Amount, Block, BlockHash, CompactTarget,
5
        ScriptBuf, ScriptHash, Transaction, TxIn, TxOut, Txid,
6
    },
7
    local_chain::CheckPoint,
8
    BlockId,
9
};
10
use bitcoincore_rpc::{
11
    bitcoincore_rpc_json::{GetBlockTemplateModes, GetBlockTemplateRules},
12
    RpcApi,
13
};
14
use electrsd::bitcoind::anyhow::Context;
15
16
pub use electrsd;
17
pub use electrsd::bitcoind;
18
pub use electrsd::bitcoind::anyhow;
19
pub use electrsd::bitcoind::bitcoincore_rpc;
20
pub use electrsd::electrum_client;
21
use electrsd::electrum_client::ElectrumApi;
22
use std::time::Duration;
23
24
/// Struct for running a regtest environment with a single `bitcoind` node with an `electrs`
25
/// instance connected to it.
26
pub struct TestEnv {
27
    pub bitcoind: electrsd::bitcoind::BitcoinD,
28
    pub electrsd: electrsd::ElectrsD,
29
}
30
31
/// Configuration parameters.
32
#[derive(Debug)]
33
pub struct Config<'a> {
34
    /// [`bitcoind::Conf`]
35
    pub bitcoind: bitcoind::Conf<'a>,
36
    /// [`electrsd::Conf`]
37
    pub electrsd: electrsd::Conf<'a>,
38
}
39
40
impl<'a> Default for Config<'a> {
41
    /// Use the default configuration plus set `http_enabled = true` for [`electrsd::Conf`]
42
    /// which is required for testing `bdk_esplora`.
43
71
    fn default() -> Self {
44
71
        Self {
45
71
            bitcoind: bitcoind::Conf::default(),
46
71
            electrsd: {
47
71
                let mut conf = electrsd::Conf::default();
48
71
                conf.http_enabled = true;
49
71
                conf
50
71
            },
51
71
        }
52
71
    }
<bdk_testenv::Config as core::default::Default>::default
Line
Count
Source
43
69
    fn default() -> Self {
44
69
        Self {
45
69
            bitcoind: bitcoind::Conf::default(),
46
69
            electrsd: {
47
69
                let mut conf = electrsd::Conf::default();
48
69
                conf.http_enabled = true;
49
69
                conf
50
69
            },
51
69
        }
52
69
    }
<bdk_testenv::Config as core::default::Default>::default
Line
Count
Source
43
2
    fn default() -> Self {
44
2
        Self {
45
2
            bitcoind: bitcoind::Conf::default(),
46
2
            electrsd: {
47
2
                let mut conf = electrsd::Conf::default();
48
2
                conf.http_enabled = true;
49
2
                conf
50
2
            },
51
2
        }
52
2
    }
53
}
54
55
impl TestEnv {
56
    /// Construct a new [`TestEnv`] instance with the default configuration used by BDK.
57
71
    pub fn new() -> anyhow::Result<Self> {
58
71
        TestEnv::new_with_config(Config::default())
59
71
    }
<bdk_testenv::TestEnv>::new
Line
Count
Source
57
69
    pub fn new() -> anyhow::Result<Self> {
58
69
        TestEnv::new_with_config(Config::default())
59
69
    }
<bdk_testenv::TestEnv>::new
Line
Count
Source
57
2
    pub fn new() -> anyhow::Result<Self> {
58
2
        TestEnv::new_with_config(Config::default())
59
2
    }
60
61
    /// Construct a new [`TestEnv`] instance with the provided [`Config`].
62
71
    pub fn new_with_config(config: Config) -> anyhow::Result<Self> {
63
71
        let bitcoind_exe = match std::env::var("BITCOIND_EXE") {
64
0
            Ok(path) => path,
65
71
            Err(_) => bitcoind::downloaded_exe_path().context(
66
71
                "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
67
71
            )
?0
,
68
        };
69
71
        let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &config.bitcoind)
?0
;
70
71
71
        let electrs_exe = match std::env::var("ELECTRS_EXE") {
72
0
            Ok(path) => path,
73
71
            Err(_) => electrsd::downloaded_exe_path()
74
71
                .context("electrs version feature must be enabled")
?0
,
75
        };
76
71
        let electrsd = electrsd::ElectrsD::with_conf(electrs_exe, &bitcoind, &config.electrsd)
?0
;
77
78
71
        Ok(Self { bitcoind, electrsd })
79
71
    }
<bdk_testenv::TestEnv>::new_with_config
Line
Count
Source
62
69
    pub fn new_with_config(config: Config) -> anyhow::Result<Self> {
63
69
        let bitcoind_exe = match std::env::var("BITCOIND_EXE") {
64
0
            Ok(path) => path,
65
69
            Err(_) => bitcoind::downloaded_exe_path().context(
66
69
                "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
67
69
            )
?0
,
68
        };
69
69
        let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &config.bitcoind)
?0
;
70
71
69
        let electrs_exe = match std::env::var("ELECTRS_EXE") {
72
0
            Ok(path) => path,
73
69
            Err(_) => electrsd::downloaded_exe_path()
74
69
                .context("electrs version feature must be enabled")
?0
,
75
        };
76
69
        let electrsd = electrsd::ElectrsD::with_conf(electrs_exe, &bitcoind, &config.electrsd)
?0
;
77
78
69
        Ok(Self { bitcoind, electrsd })
79
69
    }
<bdk_testenv::TestEnv>::new_with_config
Line
Count
Source
62
2
    pub fn new_with_config(config: Config) -> anyhow::Result<Self> {
63
2
        let bitcoind_exe = match std::env::var("BITCOIND_EXE") {
64
0
            Ok(path) => path,
65
2
            Err(_) => bitcoind::downloaded_exe_path().context(
66
2
                "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
67
2
            )
?0
,
68
        };
69
2
        let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &config.bitcoind)
?0
;
70
71
2
        let electrs_exe = match std::env::var("ELECTRS_EXE") {
72
0
            Ok(path) => path,
73
2
            Err(_) => electrsd::downloaded_exe_path()
74
2
                .context("electrs version feature must be enabled")
?0
,
75
        };
76
2
        let electrsd = electrsd::ElectrsD::with_conf(electrs_exe, &bitcoind, &config.electrsd)
?0
;
77
78
2
        Ok(Self { bitcoind, electrsd })
79
2
    }
80
81
    /// Exposes the [`ElectrumApi`] calls from the Electrum client.
82
0
    pub fn electrum_client(&self) -> &impl ElectrumApi {
83
0
        &self.electrsd.client
84
0
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::electrum_client
Unexecuted instantiation: <bdk_testenv::TestEnv>::electrum_client
85
86
    /// Exposes the [`RpcApi`] calls from [`bitcoincore_rpc`].
87
201
    pub fn rpc_client(&self) -> &impl RpcApi {
88
201
        &self.bitcoind.client
89
201
    }
<bdk_testenv::TestEnv>::rpc_client
Line
Count
Source
87
201
    pub fn rpc_client(&self) -> &impl RpcApi {
88
201
        &self.bitcoind.client
89
201
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::rpc_client
90
91
    // Reset `electrsd` so that new blocks can be seen.
92
3
    pub fn reset_electrsd(mut self) -> anyhow::Result<Self> {
93
3
        let mut electrsd_conf = electrsd::Conf::default();
94
3
        electrsd_conf.http_enabled = true;
95
3
        let electrsd = match std::env::var_os("ELECTRS_EXE") {
96
0
            Some(env_electrs_exe) => {
97
0
                electrsd::ElectrsD::with_conf(env_electrs_exe, &self.bitcoind, &electrsd_conf)
98
            }
99
            None => {
100
3
                let electrs_exe = electrsd::downloaded_exe_path()
101
3
                    .expect("electrs version feature must be enabled");
102
3
                electrsd::ElectrsD::with_conf(electrs_exe, &self.bitcoind, &electrsd_conf)
103
            }
104
0
        }?;
105
3
        self.electrsd = electrsd;
106
3
        Ok(self)
107
3
    }
<bdk_testenv::TestEnv>::reset_electrsd
Line
Count
Source
92
3
    pub fn reset_electrsd(mut self) -> anyhow::Result<Self> {
93
3
        let mut electrsd_conf = electrsd::Conf::default();
94
3
        electrsd_conf.http_enabled = true;
95
3
        let electrsd = match std::env::var_os("ELECTRS_EXE") {
96
0
            Some(env_electrs_exe) => {
97
0
                electrsd::ElectrsD::with_conf(env_electrs_exe, &self.bitcoind, &electrsd_conf)
98
            }
99
            None => {
100
3
                let electrs_exe = electrsd::downloaded_exe_path()
101
3
                    .expect("electrs version feature must be enabled");
102
3
                electrsd::ElectrsD::with_conf(electrs_exe, &self.bitcoind, &electrsd_conf)
103
            }
104
0
        }?;
105
3
        self.electrsd = electrsd;
106
3
        Ok(self)
107
3
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::reset_electrsd
108
109
    /// Mine a number of blocks of a given size `count`, which may be specified to a given coinbase
110
    /// `address`.
111
190
    pub fn mine_blocks(
112
190
        &self,
113
190
        count: usize,
114
190
        address: Option<Address>,
115
190
    ) -> anyhow::Result<Vec<BlockHash>> {
116
190
        let coinbase_address = match address {
117
18
            Some(address) => address,
118
172
            None => self
119
172
                .bitcoind
120
172
                .client
121
172
                .get_new_address(None, None)
?0
122
172
                .assume_checked(),
123
        };
124
190
        let block_hashes = self
125
190
            .bitcoind
126
190
            .client
127
190
            .generate_to_address(count as _, &coinbase_address)
?0
;
128
190
        Ok(block_hashes)
129
190
    }
<bdk_testenv::TestEnv>::mine_blocks
Line
Count
Source
111
186
    pub fn mine_blocks(
112
186
        &self,
113
186
        count: usize,
114
186
        address: Option<Address>,
115
186
    ) -> anyhow::Result<Vec<BlockHash>> {
116
186
        let coinbase_address = match address {
117
18
            Some(address) => address,
118
168
            None => self
119
168
                .bitcoind
120
168
                .client
121
168
                .get_new_address(None, None)
?0
122
168
                .assume_checked(),
123
        };
124
186
        let block_hashes = self
125
186
            .bitcoind
126
186
            .client
127
186
            .generate_to_address(count as _, &coinbase_address)
?0
;
128
186
        Ok(block_hashes)
129
186
    }
<bdk_testenv::TestEnv>::mine_blocks
Line
Count
Source
111
4
    pub fn mine_blocks(
112
4
        &self,
113
4
        count: usize,
114
4
        address: Option<Address>,
115
4
    ) -> anyhow::Result<Vec<BlockHash>> {
116
4
        let coinbase_address = match address {
117
0
            Some(address) => address,
118
4
            None => self
119
4
                .bitcoind
120
4
                .client
121
4
                .get_new_address(None, None)
?0
122
4
                .assume_checked(),
123
        };
124
4
        let block_hashes = self
125
4
            .bitcoind
126
4
            .client
127
4
            .generate_to_address(count as _, &coinbase_address)
?0
;
128
4
        Ok(block_hashes)
129
4
    }
130
131
    /// Mine a block that is guaranteed to be empty even with transactions in the mempool.
132
1.00k
    pub fn mine_empty_block(&self) -> anyhow::Result<(usize, BlockHash)> {
133
1.00k
        let bt = self.bitcoind.client.get_block_template(
134
1.00k
            GetBlockTemplateModes::Template,
135
1.00k
            &[GetBlockTemplateRules::SegWit],
136
1.00k
            &[],
137
1.00k
        )
?0
;
138
139
1.00k
        let txdata = vec![Transaction {
140
1.00k
            version: transaction::Version::ONE,
141
1.00k
            lock_time: bdk_chain::bitcoin::absolute::LockTime::from_height(0)
?0
,
142
1.00k
            input: vec![TxIn {
143
1.00k
                previous_output: bdk_chain::bitcoin::OutPoint::default(),
144
1.00k
                script_sig: ScriptBuf::builder()
145
1.00k
                    .push_int(bt.height as _)
146
1.00k
                    // randomn number so that re-mining creates unique block
147
1.00k
                    .push_int(random())
148
1.00k
                    .into_script(),
149
1.00k
                sequence: bdk_chain::bitcoin::Sequence::default(),
150
1.00k
                witness: bdk_chain::bitcoin::Witness::new(),
151
1.00k
            }],
152
1.00k
            output: vec![TxOut {
153
1.00k
                value: Amount::ZERO,
154
1.00k
                script_pubkey: ScriptBuf::new_p2sh(&ScriptHash::all_zeros()),
155
1.00k
            }],
156
1.00k
        }];
157
1.00k
158
1.00k
        let bits: [u8; 4] = bt
159
1.00k
            .bits
160
1.00k
            .clone()
161
1.00k
            .try_into()
162
1.00k
            .expect("rpc provided us with invalid bits");
163
164
1.00k
        let mut block = Block {
165
            header: Header {
166
1.00k
                version: bdk_chain::bitcoin::block::Version::default(),
167
1.00k
                prev_blockhash: bt.previous_block_hash,
168
1.00k
                merkle_root: TxMerkleNode::all_zeros(),
169
1.00k
                time: Ord::max(bt.min_time, std::time::UNIX_EPOCH.elapsed()
?0
.as_secs()) as u32,
170
1.00k
                bits: CompactTarget::from_consensus(u32::from_be_bytes(bits)),
171
1.00k
                nonce: 0,
172
1.00k
            },
173
1.00k
            txdata,
174
1.00k
        };
175
1.00k
176
1.00k
        block.header.merkle_root = block.compute_merkle_root().expect("must compute");
177
178
2.03k
        for nonce in 0..=u32::MAX {
179
2.03k
            block.header.nonce = nonce;
180
2.03k
            if block.header.target().is_met_by(block.block_hash()) {
181
1.00k
                break;
182
1.03k
            }
183
        }
184
185
1.00k
        self.bitcoind.client.submit_block(&block)
?0
;
186
1.00k
        Ok((bt.height as usize, block.block_hash()))
187
1.00k
    }
<bdk_testenv::TestEnv>::mine_empty_block
Line
Count
Source
132
1.00k
    pub fn mine_empty_block(&self) -> anyhow::Result<(usize, BlockHash)> {
133
1.00k
        let bt = self.bitcoind.client.get_block_template(
134
1.00k
            GetBlockTemplateModes::Template,
135
1.00k
            &[GetBlockTemplateRules::SegWit],
136
1.00k
            &[],
137
1.00k
        )
?0
;
138
139
1.00k
        let txdata = vec![Transaction {
140
1.00k
            version: transaction::Version::ONE,
141
1.00k
            lock_time: bdk_chain::bitcoin::absolute::LockTime::from_height(0)
?0
,
142
1.00k
            input: vec![TxIn {
143
1.00k
                previous_output: bdk_chain::bitcoin::OutPoint::default(),
144
1.00k
                script_sig: ScriptBuf::builder()
145
1.00k
                    .push_int(bt.height as _)
146
1.00k
                    // randomn number so that re-mining creates unique block
147
1.00k
                    .push_int(random())
148
1.00k
                    .into_script(),
149
1.00k
                sequence: bdk_chain::bitcoin::Sequence::default(),
150
1.00k
                witness: bdk_chain::bitcoin::Witness::new(),
151
1.00k
            }],
152
1.00k
            output: vec![TxOut {
153
1.00k
                value: Amount::ZERO,
154
1.00k
                script_pubkey: ScriptBuf::new_p2sh(&ScriptHash::all_zeros()),
155
1.00k
            }],
156
1.00k
        }];
157
1.00k
158
1.00k
        let bits: [u8; 4] = bt
159
1.00k
            .bits
160
1.00k
            .clone()
161
1.00k
            .try_into()
162
1.00k
            .expect("rpc provided us with invalid bits");
163
164
1.00k
        let mut block = Block {
165
            header: Header {
166
1.00k
                version: bdk_chain::bitcoin::block::Version::default(),
167
1.00k
                prev_blockhash: bt.previous_block_hash,
168
1.00k
                merkle_root: TxMerkleNode::all_zeros(),
169
1.00k
                time: Ord::max(bt.min_time, std::time::UNIX_EPOCH.elapsed()
?0
.as_secs()) as u32,
170
1.00k
                bits: CompactTarget::from_consensus(u32::from_be_bytes(bits)),
171
1.00k
                nonce: 0,
172
1.00k
            },
173
1.00k
            txdata,
174
1.00k
        };
175
1.00k
176
1.00k
        block.header.merkle_root = block.compute_merkle_root().expect("must compute");
177
178
2.03k
        for nonce in 0..=u32::MAX {
179
2.03k
            block.header.nonce = nonce;
180
2.03k
            if block.header.target().is_met_by(block.block_hash()) {
181
1.00k
                break;
182
1.03k
            }
183
        }
184
185
1.00k
        self.bitcoind.client.submit_block(&block)
?0
;
186
1.00k
        Ok((bt.height as usize, block.block_hash()))
187
1.00k
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::mine_empty_block
188
189
    /// This method waits for the Electrum notification indicating that a new block has been mined.
190
    /// `timeout` is the maximum [`Duration`] we want to wait for a response from Electrsd.
191
52
    pub fn wait_until_electrum_sees_block(&self, timeout: Duration) -> anyhow::Result<()> {
192
52
        self.electrsd.client.block_headers_subscribe()
?0
;
193
52
        let delay = Duration::from_millis(200);
194
52
        let start = std::time::Instant::now();
195
196
104
        while start.elapsed() < timeout {
197
104
            self.electrsd.trigger()
?0
;
198
104
            self.electrsd.client.ping()
?0
;
199
104
            if self.electrsd.client.block_headers_pop()
?0
.is_some() {
200
52
                return Ok(());
201
52
            }
202
52
203
52
            std::thread::sleep(delay);
204
        }
205
206
0
        Err(anyhow::Error::msg(
207
0
            "Timed out waiting for Electrsd to get block header",
208
0
        ))
209
52
    }
<bdk_testenv::TestEnv>::wait_until_electrum_sees_block
Line
Count
Source
191
48
    pub fn wait_until_electrum_sees_block(&self, timeout: Duration) -> anyhow::Result<()> {
192
48
        self.electrsd.client.block_headers_subscribe()
?0
;
193
48
        let delay = Duration::from_millis(200);
194
48
        let start = std::time::Instant::now();
195
196
96
        while start.elapsed() < timeout {
197
96
            self.electrsd.trigger()
?0
;
198
96
            self.electrsd.client.ping()
?0
;
199
96
            if self.electrsd.client.block_headers_pop()
?0
.is_some() {
200
48
                return Ok(());
201
48
            }
202
48
203
48
            std::thread::sleep(delay);
204
        }
205
206
0
        Err(anyhow::Error::msg(
207
0
            "Timed out waiting for Electrsd to get block header",
208
0
        ))
209
48
    }
<bdk_testenv::TestEnv>::wait_until_electrum_sees_block
Line
Count
Source
191
4
    pub fn wait_until_electrum_sees_block(&self, timeout: Duration) -> anyhow::Result<()> {
192
4
        self.electrsd.client.block_headers_subscribe()
?0
;
193
4
        let delay = Duration::from_millis(200);
194
4
        let start = std::time::Instant::now();
195
196
8
        while start.elapsed() < timeout {
197
8
            self.electrsd.trigger()
?0
;
198
8
            self.electrsd.client.ping()
?0
;
199
8
            if self.electrsd.client.block_headers_pop()
?0
.is_some() {
200
4
                return Ok(());
201
4
            }
202
4
203
4
            std::thread::sleep(delay);
204
        }
205
206
0
        Err(anyhow::Error::msg(
207
0
            "Timed out waiting for Electrsd to get block header",
208
0
        ))
209
4
    }
210
211
    /// This method waits for Electrsd to see a transaction with given `txid`. `timeout` is the
212
    /// maximum [`Duration`] we want to wait for a response from Electrsd.
213
3
    pub fn wait_until_electrum_sees_txid(
214
3
        &self,
215
3
        txid: Txid,
216
3
        timeout: Duration,
217
3
    ) -> anyhow::Result<()> {
218
3
        let delay = Duration::from_millis(200);
219
3
        let start = std::time::Instant::now();
220
221
78
        while start.elapsed() < timeout {
222
78
            if self.electrsd.client.transaction_get(&txid).is_ok() {
223
3
                return Ok(());
224
75
            }
225
75
226
75
            std::thread::sleep(delay);
227
        }
228
229
0
        Err(anyhow::Error::msg(
230
0
            "Timed out waiting for Electrsd to get transaction",
231
0
        ))
232
3
    }
<bdk_testenv::TestEnv>::wait_until_electrum_sees_txid
Line
Count
Source
213
3
    pub fn wait_until_electrum_sees_txid(
214
3
        &self,
215
3
        txid: Txid,
216
3
        timeout: Duration,
217
3
    ) -> anyhow::Result<()> {
218
3
        let delay = Duration::from_millis(200);
219
3
        let start = std::time::Instant::now();
220
221
78
        while start.elapsed() < timeout {
222
78
            if self.electrsd.client.transaction_get(&txid).is_ok() {
223
3
                return Ok(());
224
75
            }
225
75
226
75
            std::thread::sleep(delay);
227
        }
228
229
0
        Err(anyhow::Error::msg(
230
0
            "Timed out waiting for Electrsd to get transaction",
231
0
        ))
232
3
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::wait_until_electrum_sees_txid
233
234
    /// Invalidate a number of blocks of a given size `count`.
235
122
    pub fn invalidate_blocks(&self, count: usize) -> anyhow::Result<()> {
236
122
        let mut hash = self.bitcoind.client.get_best_block_hash()
?0
;
237
122
        for _ in 0..count {
238
639
            let prev_hash = self
239
639
                .bitcoind
240
639
                .client
241
639
                .get_block_info(&hash)
?0
242
                .previousblockhash;
243
639
            self.bitcoind.client.invalidate_block(&hash)
?0
;
244
639
            match prev_hash {
245
639
                Some(prev_hash) => hash = prev_hash,
246
0
                None => break,
247
            }
248
        }
249
122
        Ok(())
250
122
    }
<bdk_testenv::TestEnv>::invalidate_blocks
Line
Count
Source
235
120
    pub fn invalidate_blocks(&self, count: usize) -> anyhow::Result<()> {
236
120
        let mut hash = self.bitcoind.client.get_best_block_hash()
?0
;
237
120
        for _ in 0..count {
238
627
            let prev_hash = self
239
627
                .bitcoind
240
627
                .client
241
627
                .get_block_info(&hash)
?0
242
                .previousblockhash;
243
627
            self.bitcoind.client.invalidate_block(&hash)
?0
;
244
627
            match prev_hash {
245
627
                Some(prev_hash) => hash = prev_hash,
246
0
                None => break,
247
            }
248
        }
249
120
        Ok(())
250
120
    }
<bdk_testenv::TestEnv>::invalidate_blocks
Line
Count
Source
235
2
    pub fn invalidate_blocks(&self, count: usize) -> anyhow::Result<()> {
236
2
        let mut hash = self.bitcoind.client.get_best_block_hash()
?0
;
237
2
        for _ in 0..count {
238
12
            let prev_hash = self
239
12
                .bitcoind
240
12
                .client
241
12
                .get_block_info(&hash)
?0
242
                .previousblockhash;
243
12
            self.bitcoind.client.invalidate_block(&hash)
?0
;
244
12
            match prev_hash {
245
12
                Some(prev_hash) => hash = prev_hash,
246
0
                None => break,
247
            }
248
        }
249
2
        Ok(())
250
2
    }
251
252
    /// Reorg a number of blocks of a given size `count`.
253
    /// Refer to [`TestEnv::mine_empty_block`] for more information.
254
5
    pub fn reorg(&self, count: usize) -> anyhow::Result<Vec<BlockHash>> {
255
5
        let start_height = self.bitcoind.client.get_block_count()
?0
;
256
5
        self.invalidate_blocks(count)
?0
;
257
258
5
        let res = self.mine_blocks(count, None);
259
5
        assert_eq!(
260
5
            self.bitcoind.client.get_block_count()
?0
,
261
            start_height,
262
0
            "reorg should not result in height change"
263
        );
264
5
        res
265
5
    }
<bdk_testenv::TestEnv>::reorg
Line
Count
Source
254
3
    pub fn reorg(&self, count: usize) -> anyhow::Result<Vec<BlockHash>> {
255
3
        let start_height = self.bitcoind.client.get_block_count()
?0
;
256
3
        self.invalidate_blocks(count)
?0
;
257
258
3
        let res = self.mine_blocks(count, None);
259
3
        assert_eq!(
260
3
            self.bitcoind.client.get_block_count()
?0
,
261
            start_height,
262
0
            "reorg should not result in height change"
263
        );
264
3
        res
265
3
    }
<bdk_testenv::TestEnv>::reorg
Line
Count
Source
254
2
    pub fn reorg(&self, count: usize) -> anyhow::Result<Vec<BlockHash>> {
255
2
        let start_height = self.bitcoind.client.get_block_count()
?0
;
256
2
        self.invalidate_blocks(count)
?0
;
257
258
2
        let res = self.mine_blocks(count, None);
259
2
        assert_eq!(
260
2
            self.bitcoind.client.get_block_count()
?0
,
261
            start_height,
262
0
            "reorg should not result in height change"
263
        );
264
2
        res
265
2
    }
266
267
    /// Reorg with a number of empty blocks of a given size `count`.
268
117
    pub fn reorg_empty_blocks(&self, count: usize) -> anyhow::Result<Vec<(usize, BlockHash)>> {
269
117
        let start_height = self.bitcoind.client.get_block_count()
?0
;
270
117
        self.invalidate_blocks(count)
?0
;
271
272
117
        let res = (0..count)
273
609
            .map(|_| self.mine_empty_block())
<bdk_testenv::TestEnv>::reorg_empty_blocks::{closure#0}
Line
Count
Source
273
609
            .map(|_| self.mine_empty_block())
Unexecuted instantiation: <bdk_testenv::TestEnv>::reorg_empty_blocks::{closure#0}
274
117
            .collect::<Result<Vec<_>, _>>()
?0
;
275
117
        assert_eq!(
276
117
            self.bitcoind.client.get_block_count()
?0
,
277
            start_height,
278
0
            "reorg should not result in height change"
279
        );
280
117
        Ok(res)
281
117
    }
<bdk_testenv::TestEnv>::reorg_empty_blocks
Line
Count
Source
268
117
    pub fn reorg_empty_blocks(&self, count: usize) -> anyhow::Result<Vec<(usize, BlockHash)>> {
269
117
        let start_height = self.bitcoind.client.get_block_count()
?0
;
270
117
        self.invalidate_blocks(count)
?0
;
271
272
117
        let res = (0..count)
273
117
            .map(|_| self.mine_empty_block())
274
117
            .collect::<Result<Vec<_>, _>>()
?0
;
275
117
        assert_eq!(
276
117
            self.bitcoind.client.get_block_count()
?0
,
277
            start_height,
278
0
            "reorg should not result in height change"
279
        );
280
117
        Ok(res)
281
117
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::reorg_empty_blocks
282
283
    /// Send a tx of a given `amount` to a given `address`.
284
159
    pub fn send(&self, address: &Address<NetworkChecked>, amount: Amount) -> anyhow::Result<Txid> {
285
159
        let txid = self
286
159
            .bitcoind
287
159
            .client
288
159
            .send_to_address(address, amount, None, None, None, None, None, None)
?0
;
289
159
        Ok(txid)
290
159
    }
<bdk_testenv::TestEnv>::send
Line
Count
Source
284
159
    pub fn send(&self, address: &Address<NetworkChecked>, amount: Amount) -> anyhow::Result<Txid> {
285
159
        let txid = self
286
159
            .bitcoind
287
159
            .client
288
159
            .send_to_address(address, amount, None, None, None, None, None, None)
?0
;
289
159
        Ok(txid)
290
159
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::send
291
292
    /// Create a checkpoint linked list of all the blocks in the chain.
293
54
    pub fn make_checkpoint_tip(&self) -> CheckPoint {
294
2.94k
        CheckPoint::from_block_ids((0_u32..).map_while(|height| {
295
2.94k
            self.bitcoind
296
2.94k
                .client
297
2.94k
                .get_block_hash(height as u64)
298
2.94k
                .ok()
299
2.94k
                .map(|hash| 
BlockId { height, hash }2.88k
)
<bdk_testenv::TestEnv>::make_checkpoint_tip::{closure#0}::{closure#0}
Line
Count
Source
299
2.88k
                .map(|hash| BlockId { height, hash })
Unexecuted instantiation: <bdk_testenv::TestEnv>::make_checkpoint_tip::{closure#0}::{closure#0}
300
2.94k
        }))
<bdk_testenv::TestEnv>::make_checkpoint_tip::{closure#0}
Line
Count
Source
294
2.94k
        CheckPoint::from_block_ids((0_u32..).map_while(|height| {
295
2.94k
            self.bitcoind
296
2.94k
                .client
297
2.94k
                .get_block_hash(height as u64)
298
2.94k
                .ok()
299
2.94k
                .map(|hash| BlockId { height, hash })
300
2.94k
        }))
Unexecuted instantiation: <bdk_testenv::TestEnv>::make_checkpoint_tip::{closure#0}
301
54
        .expect("must craft tip")
302
54
    }
<bdk_testenv::TestEnv>::make_checkpoint_tip
Line
Count
Source
293
54
    pub fn make_checkpoint_tip(&self) -> CheckPoint {
294
54
        CheckPoint::from_block_ids((0_u32..).map_while(|height| {
295
            self.bitcoind
296
                .client
297
                .get_block_hash(height as u64)
298
                .ok()
299
                .map(|hash| BlockId { height, hash })
300
54
        }))
301
54
        .expect("must craft tip")
302
54
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::make_checkpoint_tip
303
304
    /// Get the genesis hash of the blockchain.
305
18
    pub fn genesis_hash(&self) -> anyhow::Result<BlockHash> {
306
18
        let hash = self.bitcoind.client.get_block_hash(0)
?0
;
307
18
        Ok(hash)
308
18
    }
<bdk_testenv::TestEnv>::genesis_hash
Line
Count
Source
305
18
    pub fn genesis_hash(&self) -> anyhow::Result<BlockHash> {
306
18
        let hash = self.bitcoind.client.get_block_hash(0)
?0
;
307
18
        Ok(hash)
308
18
    }
Unexecuted instantiation: <bdk_testenv::TestEnv>::genesis_hash
309
}
310
311
#[cfg(test)]
312
mod test {
313
    use crate::TestEnv;
314
    use core::time::Duration;
315
    use electrsd::bitcoind::{anyhow::Result, bitcoincore_rpc::RpcApi};
316
317
    /// This checks that reorgs initiated by `bitcoind` is detected by our `electrsd` instance.
318
    #[test]
319
2
    fn test_reorg_is_detected_in_electrsd() -> Result<()> {
320
2
        let env = TestEnv::new()
?0
;
321
322
        // Mine some blocks.
323
2
        env.mine_blocks(101, None)
?0
;
324
2
        env.wait_until_electrum_sees_block(Duration::from_secs(6))
?0
;
325
2
        let height = env.bitcoind.client.get_block_count()
?0
;
326
2
        let blocks = (0..=height)
327
206
            .map(|i| env.bitcoind.client.get_block_hash(i))
328
2
            .collect::<Result<Vec<_>, _>>()
?0
;
329
330
        // Perform reorg on six blocks.
331
2
        env.reorg(6)
?0
;
332
2
        env.wait_until_electrum_sees_block(Duration::from_secs(6))
?0
;
333
2
        let reorged_height = env.bitcoind.client.get_block_count()
?0
;
334
2
        let reorged_blocks = (0..=height)
335
206
            .map(|i| env.bitcoind.client.get_block_hash(i))
336
2
            .collect::<Result<Vec<_>, _>>()
?0
;
337
338
2
        assert_eq!(height, reorged_height);
339
340
        // Block hashes should not be equal on the six reorged blocks.
341
206
        for (i, (block, reorged_block)) in 
blocks.iter().zip(reorged_blocks.iter()).enumerate()2
{
342
206
            match i <= height as usize - 6 {
343
194
                true => assert_eq!(block, reorged_block),
344
12
                false => assert_ne!(block, reorged_block),
345
            }
346
        }
347
348
2
        Ok(())
349
2
    }
350
}