Skip to content

Commit 3619a85

Browse files
committed
Move test bitcoind/electrsd out of OnceCell
`OnceCell` doesn't call `drop`, which makes the spawned `bitcoind`/`electrsd` instances linger around after our tests have finished. To fix this, we move them out of `OnceCell` and let every test that needs them spawn their own instances. This additional let us drop the `OnceCell` dev dependency. Additionally, we improve the test robustness by applying most of the changes from lightningdevkit/rust-lightning#2033.
1 parent 56c1375 commit 3619a85

File tree

2 files changed

+84
-74
lines changed

2 files changed

+84
-74
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ libc = "0.2"
6565
[dev-dependencies]
6666
electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] }
6767
electrum-client = "0.12.0"
68-
once_cell = "1.16.0"
6968
proptest = "1.0.0"
7069

7170
[profile.release]

src/test/functional_tests.rs

Lines changed: 84 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,93 +7,90 @@ use electrsd::bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
77
use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD};
88
use electrum_client::ElectrumApi;
99

10-
use once_cell::sync::OnceCell;
11-
1210
use std::env;
13-
use std::sync::Mutex;
1411
use std::time::Duration;
1512

16-
static BITCOIND: OnceCell<BitcoinD> = OnceCell::new();
17-
static ELECTRSD: OnceCell<ElectrsD> = OnceCell::new();
18-
static PREMINE: OnceCell<()> = OnceCell::new();
19-
static MINER_LOCK: Mutex<()> = Mutex::new(());
20-
21-
fn get_bitcoind() -> &'static BitcoinD {
22-
BITCOIND.get_or_init(|| {
23-
let bitcoind_exe =
24-
env::var("BITCOIND_EXE").ok().or_else(|| bitcoind::downloaded_exe_path().ok()).expect(
25-
"you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
26-
);
27-
let mut conf = bitcoind::Conf::default();
28-
conf.network = "regtest";
29-
BitcoinD::with_conf(bitcoind_exe, &conf).unwrap()
30-
})
31-
}
32-
33-
fn get_electrsd() -> &'static ElectrsD {
34-
ELECTRSD.get_or_init(|| {
35-
let bitcoind = get_bitcoind();
36-
let electrs_exe =
37-
env::var("ELECTRS_EXE").ok().or_else(electrsd::downloaded_exe_path).expect(
38-
"you need to provide env var ELECTRS_EXE or specify an electrsd version feature",
39-
);
40-
let mut conf = electrsd::Conf::default();
41-
conf.http_enabled = true;
42-
conf.network = "regtest";
43-
ElectrsD::with_conf(electrs_exe, &bitcoind, &conf).unwrap()
44-
})
13+
fn setup_bitcoind_and_electrsd() -> (BitcoinD, ElectrsD) {
14+
let bitcoind_exe =
15+
env::var("BITCOIND_EXE").ok().or_else(|| bitcoind::downloaded_exe_path().ok()).expect(
16+
"you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
17+
);
18+
let mut bitcoind_conf = bitcoind::Conf::default();
19+
bitcoind_conf.network = "regtest";
20+
let bitcoind = BitcoinD::with_conf(bitcoind_exe, &bitcoind_conf).unwrap();
21+
22+
let electrs_exe = env::var("ELECTRS_EXE")
23+
.ok()
24+
.or_else(electrsd::downloaded_exe_path)
25+
.expect("you need to provide env var ELECTRS_EXE or specify an electrsd version feature");
26+
let mut electrsd_conf = electrsd::Conf::default();
27+
electrsd_conf.http_enabled = true;
28+
electrsd_conf.network = "regtest";
29+
let electrsd = ElectrsD::with_conf(electrs_exe, &bitcoind, &electrsd_conf).unwrap();
30+
(bitcoind, electrsd)
4531
}
4632

47-
fn generate_blocks_and_wait(num: usize) {
48-
let _miner = MINER_LOCK.lock().unwrap();
49-
let cur_height = get_bitcoind().client.get_block_count().unwrap();
50-
let address =
51-
get_bitcoind().client.get_new_address(Some("test"), Some(AddressType::Legacy)).unwrap();
52-
let _block_hashes = get_bitcoind().client.generate_to_address(num as u64, &address).unwrap();
53-
wait_for_block(cur_height as usize + num);
33+
fn generate_blocks_and_wait(bitcoind: &BitcoinD, electrsd: &ElectrsD, num: usize) {
34+
let cur_height = bitcoind.client.get_block_count().expect("failed to get current block height");
35+
let address = bitcoind
36+
.client
37+
.get_new_address(Some("test"), Some(AddressType::Legacy))
38+
.expect("failed to get new address");
39+
// TODO: expect this Result once the WouldBlock issue is resolved upstream.
40+
let _block_hashes_res = bitcoind.client.generate_to_address(num as u64, &address);
41+
wait_for_block(electrsd, cur_height as usize + num);
5442
}
5543

56-
fn wait_for_block(min_height: usize) {
57-
let mut header = get_electrsd().client.block_headers_subscribe().unwrap();
44+
fn wait_for_block(electrsd: &ElectrsD, min_height: usize) {
45+
let mut header = match electrsd.client.block_headers_subscribe() {
46+
Ok(header) => header,
47+
Err(_) => {
48+
// While subscribing should succeed the first time around, we ran into some cases where
49+
// it didn't. Since we can't proceed without subscribing, we try again after a delay
50+
// and panic if it still fails.
51+
std::thread::sleep(Duration::from_secs(1));
52+
electrsd.client.block_headers_subscribe().expect("failed to subscribe to block headers")
53+
}
54+
};
5855
loop {
5956
if header.height >= min_height {
6057
break;
6158
}
6259
header = exponential_backoff_poll(|| {
63-
get_electrsd().trigger().unwrap();
64-
get_electrsd().client.ping().unwrap();
65-
get_electrsd().client.block_headers_pop().unwrap()
60+
electrsd.trigger().expect("failed to trigger electrsd");
61+
electrsd.client.ping().expect("failed to ping electrsd");
62+
electrsd.client.block_headers_pop().expect("failed to pop block header")
6663
});
6764
}
6865
}
6966

70-
fn wait_for_tx(txid: Txid) {
71-
let mut tx_res = get_electrsd().client.transaction_get(&txid);
67+
fn wait_for_tx(electrsd: &ElectrsD, txid: Txid) {
68+
let mut tx_res = electrsd.client.transaction_get(&txid);
7269
loop {
7370
if tx_res.is_ok() {
7471
break;
7572
}
7673
tx_res = exponential_backoff_poll(|| {
77-
get_electrsd().trigger().unwrap();
78-
get_electrsd().client.ping().unwrap();
79-
Some(get_electrsd().client.transaction_get(&txid))
74+
electrsd.trigger().unwrap();
75+
electrsd.client.ping().unwrap();
76+
Some(electrsd.client.transaction_get(&txid))
8077
});
8178
}
8279
}
8380

84-
fn wait_for_outpoint_spend(outpoint: OutPoint) {
85-
let tx = get_electrsd().client.transaction_get(&outpoint.txid).unwrap();
81+
fn wait_for_outpoint_spend(electrsd: &ElectrsD, outpoint: OutPoint) {
82+
let tx = electrsd.client.transaction_get(&outpoint.txid).unwrap();
8683
let txout_script = tx.output.get(outpoint.vout as usize).unwrap().clone().script_pubkey;
87-
let mut is_spent = !get_electrsd().client.script_get_history(&txout_script).unwrap().is_empty();
84+
let mut is_spent = !electrsd.client.script_get_history(&txout_script).unwrap().is_empty();
8885
loop {
8986
if is_spent {
9087
break;
9188
}
9289

9390
is_spent = exponential_backoff_poll(|| {
94-
get_electrsd().trigger().unwrap();
95-
get_electrsd().client.ping().unwrap();
96-
Some(!get_electrsd().client.script_get_history(&txout_script).unwrap().is_empty())
91+
electrsd.trigger().unwrap();
92+
electrsd.client.ping().unwrap();
93+
Some(!electrsd.client.script_get_history(&txout_script).unwrap().is_empty())
9794
});
9895
}
9996
}
@@ -119,26 +116,27 @@ where
119116
}
120117
}
121118

122-
fn premine_and_distribute_funds(addrs: Vec<Address>, amount: Amount) {
123-
PREMINE.get_or_init(|| {
124-
generate_blocks_and_wait(101);
125-
});
119+
fn premine_and_distribute_funds(
120+
bitcoind: &BitcoinD, electrsd: &ElectrsD, addrs: Vec<Address>, amount: Amount,
121+
) {
122+
generate_blocks_and_wait(bitcoind, electrsd, 101);
126123

127124
for addr in addrs {
128-
let txid = get_bitcoind()
125+
let txid = bitcoind
129126
.client
130127
.send_to_address(&addr, amount, None, None, None, None, None, None)
131128
.unwrap();
132-
wait_for_tx(txid);
129+
wait_for_tx(electrsd, txid);
133130
}
134131

135-
generate_blocks_and_wait(1);
132+
generate_blocks_and_wait(bitcoind, electrsd, 1);
136133
}
137134

138135
#[test]
139136
fn channel_full_cycle() {
137+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
140138
println!("== Node A ==");
141-
let esplora_url = get_electrsd().esplora_url.as_ref().unwrap();
139+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
142140
let config_a = random_config(esplora_url);
143141
let node_a = Builder::from_config(config_a).build();
144142
node_a.start().unwrap();
@@ -150,7 +148,12 @@ fn channel_full_cycle() {
150148
node_b.start().unwrap();
151149
let addr_b = node_b.new_funding_address().unwrap();
152150

153-
premine_and_distribute_funds(vec![addr_a, addr_b], Amount::from_sat(100000));
151+
premine_and_distribute_funds(
152+
&bitcoind,
153+
&electrsd,
154+
vec![addr_a, addr_b],
155+
Amount::from_sat(100000),
156+
);
154157
node_a.sync_wallets().unwrap();
155158
node_b.sync_wallets().unwrap();
156159
assert_eq!(node_a.on_chain_balance().unwrap().get_spendable(), 100000);
@@ -170,10 +173,10 @@ fn channel_full_cycle() {
170173
}
171174
};
172175

173-
wait_for_tx(funding_txo.txid);
176+
wait_for_tx(&electrsd, funding_txo.txid);
174177

175178
println!("\n .. generating blocks, syncing wallets .. ");
176-
generate_blocks_and_wait(6);
179+
generate_blocks_and_wait(&bitcoind, &electrsd, 6);
177180
node_a.sync_wallets().unwrap();
178181
node_b.sync_wallets().unwrap();
179182

@@ -276,9 +279,9 @@ fn channel_full_cycle() {
276279
expect_event!(node_a, ChannelClosed);
277280
expect_event!(node_b, ChannelClosed);
278281

279-
wait_for_outpoint_spend(funding_txo.into_bitcoin_outpoint());
282+
wait_for_outpoint_spend(&electrsd, funding_txo.into_bitcoin_outpoint());
280283

281-
generate_blocks_and_wait(1);
284+
generate_blocks_and_wait(&bitcoind, &electrsd, 1);
282285
node_a.sync_wallets().unwrap();
283286
node_b.sync_wallets().unwrap();
284287

@@ -293,8 +296,9 @@ fn channel_full_cycle() {
293296

294297
#[test]
295298
fn channel_open_fails_when_funds_insufficient() {
299+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
296300
println!("== Node A ==");
297-
let esplora_url = get_electrsd().esplora_url.as_ref().unwrap();
301+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
298302
let config_a = random_config(&esplora_url);
299303
let node_a = Builder::from_config(config_a).build();
300304
node_a.start().unwrap();
@@ -306,7 +310,12 @@ fn channel_open_fails_when_funds_insufficient() {
306310
node_b.start().unwrap();
307311
let addr_b = node_b.new_funding_address().unwrap();
308312

309-
premine_and_distribute_funds(vec![addr_a, addr_b], Amount::from_sat(100000));
313+
premine_and_distribute_funds(
314+
&bitcoind,
315+
&electrsd,
316+
vec![addr_a, addr_b],
317+
Amount::from_sat(100000),
318+
);
310319
node_a.sync_wallets().unwrap();
311320
node_b.sync_wallets().unwrap();
312321
assert_eq!(node_a.on_chain_balance().unwrap().get_spendable(), 100000);
@@ -322,7 +331,8 @@ fn channel_open_fails_when_funds_insufficient() {
322331

323332
#[test]
324333
fn connect_to_public_testnet_esplora() {
325-
let esplora_url = get_electrsd().esplora_url.as_ref().unwrap();
334+
let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd();
335+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
326336
let mut config = random_config(&esplora_url);
327337
config.esplora_server_url = "https://blockstream.info/testnet/api".to_string();
328338
config.network = bitcoin::Network::Testnet;
@@ -334,15 +344,16 @@ fn connect_to_public_testnet_esplora() {
334344

335345
#[test]
336346
fn start_stop_reinit() {
337-
let esplora_url = get_electrsd().esplora_url.as_ref().unwrap();
347+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
348+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
338349
let config = random_config(&esplora_url);
339350
let node = Builder::from_config(config.clone()).build();
340351
let expected_node_id = node.node_id();
341352

342353
let funding_address = node.new_funding_address().unwrap();
343354
let expected_amount = Amount::from_sat(100000);
344355

345-
premine_and_distribute_funds(vec![funding_address], expected_amount);
356+
premine_and_distribute_funds(&bitcoind, &electrsd, vec![funding_address], expected_amount);
346357
assert_eq!(node.on_chain_balance().unwrap().get_total(), 0);
347358

348359
node.start().unwrap();

0 commit comments

Comments
 (0)