Skip to content

Commit 4684a43

Browse files
committed
refactor(testenv)!: TestEnv::wait_until_electrum_sees_block
Add inputs `block_height` and `block_hash` so that it is more concrete what exactly we are waiting for. Introduce `TestEnv::wait_until_electrum_tip_syncs_with_bitcoind`.
1 parent 139d971 commit 4684a43

File tree

2 files changed

+59
-20
lines changed

2 files changed

+59
-20
lines changed

crates/electrum/tests/test_electrum.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
9292
None,
9393
)?;
9494
env.mine_blocks(1, None)?;
95-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
95+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
9696

9797
// use a full checkpoint linked list (since this is not what we are testing)
9898
let cp_tip = env.make_checkpoint_tip();
@@ -204,7 +204,7 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> {
204204
None,
205205
)?;
206206
env.mine_blocks(1, None)?;
207-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
207+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
208208

209209
// use a full checkpoint linked list (since this is not what we are testing)
210210
let cp_tip = env.make_checkpoint_tip();
@@ -248,7 +248,7 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> {
248248
None,
249249
)?;
250250
env.mine_blocks(1, None)?;
251-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
251+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
252252

253253
// A scan with gap limit 5 won't find the second transaction, but a scan with gap limit 6 will.
254254
// The last active indice won't be updated in the first case but will in the second one.
@@ -316,7 +316,7 @@ fn test_sync() -> anyhow::Result<()> {
316316

317317
// Mine some blocks.
318318
env.mine_blocks(101, Some(addr_to_mine))?;
319-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
319+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
320320

321321
// Broadcast transaction to mempool.
322322
let txid = env.send(&addr_to_track, SEND_AMOUNT)?;
@@ -341,7 +341,7 @@ fn test_sync() -> anyhow::Result<()> {
341341

342342
// Mine block to confirm transaction.
343343
env.mine_blocks(1, None)?;
344-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
344+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
345345

346346
let _ = sync_with_electrum(
347347
&client,
@@ -362,7 +362,7 @@ fn test_sync() -> anyhow::Result<()> {
362362

363363
// Perform reorg on block with confirmed transaction.
364364
env.reorg_empty_blocks(1)?;
365-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
365+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
366366

367367
let _ = sync_with_electrum(
368368
&client,
@@ -382,7 +382,7 @@ fn test_sync() -> anyhow::Result<()> {
382382

383383
// Mine block to confirm transaction again.
384384
env.mine_blocks(1, None)?;
385-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
385+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
386386

387387
let _ = sync_with_electrum(&client, [spk_to_track], &mut recv_chain, &mut recv_graph)?;
388388

@@ -425,7 +425,8 @@ fn test_sync() -> anyhow::Result<()> {
425425
Ok(())
426426
}
427427

428-
/// Ensure that confirmed txs that are reorged become unconfirmed.
428+
/// Ensure transactions can become unconfirmed during reorg.
429+
/// ~Ensure that confirmed txs that are reorged become unconfirmed.~
429430
///
430431
/// 1. Mine 101 blocks.
431432
/// 2. Mine 8 blocks with a confirmed tx in each.
@@ -469,7 +470,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
469470
}
470471

471472
// Sync up to tip.
472-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
473+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
473474
let update = sync_with_electrum(
474475
&client,
475476
[spk_to_track.clone()],
@@ -500,7 +501,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
500501
for depth in 1..=REORG_COUNT {
501502
env.reorg_empty_blocks(depth)?;
502503

503-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
504+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
504505
let update = sync_with_electrum(
505506
&client,
506507
[spk_to_track.clone()],
@@ -511,6 +512,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
511512
// Check that no new anchors are added during current reorg.
512513
assert!(initial_anchors.is_superset(&update.tx_update.anchors));
513514

515+
// TODO: Fails here.
514516
assert_eq!(
515517
get_balance(&recv_chain, &recv_graph)?,
516518
Balance {

crates/testenv/src/lib.rs

+47-10
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,45 @@ impl TestEnv {
186186
Ok((bt.height as usize, block.block_hash()))
187187
}
188188

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-
pub fn wait_until_electrum_sees_block(&self, timeout: Duration) -> anyhow::Result<()> {
192-
self.electrsd.client.block_headers_subscribe()?;
189+
/// Wait until Electrum is aware of a block of a given `block_height` (and optionally, matches
190+
/// the given `block_hash`).
191+
pub fn wait_until_electrum_sees_block(
192+
&self,
193+
block_height: usize,
194+
block_hash: Option<BlockHash>,
195+
timeout: Duration,
196+
) -> anyhow::Result<()> {
197+
self.electrsd.trigger()?;
198+
// NOTE: There is a reason why we use the subscribe endpoint specifically. You may think
199+
// just polling Electrs via Electrum for a block header at height `block_height` and
200+
// checking whether `block_hash` matches is enough. However, having the header polling call
201+
// up to date does not necessarily mean the spk histories are up to date. On the other hand,
202+
// getting a notification for a new block tip does mean that the confirmed spk histories
203+
// are up to date up to and including the new notified tip. This is all due to the internal
204+
// workings of Electrs.
205+
self.electrum_client().block_headers_subscribe()?;
206+
193207
let delay = Duration::from_millis(200);
194208
let start = std::time::Instant::now();
195209

196210
while start.elapsed() < timeout {
197-
self.electrsd.trigger()?;
198-
self.electrsd.client.ping()?;
199-
if self.electrsd.client.block_headers_pop()?.is_some() {
200-
return Ok(());
211+
self.electrum_client().ping()?;
212+
if let Some(header_notif) = self.electrum_client().block_headers_pop()? {
213+
if header_notif.height >= block_height {
214+
let header = if header_notif.height == block_height {
215+
header_notif.header
216+
} else {
217+
self.electrum_client().block_header(block_height)?
218+
};
219+
match block_hash {
220+
None => return Ok(()),
221+
Some(exp_hash) => {
222+
if exp_hash == header.block_hash() {
223+
return Ok(());
224+
}
225+
}
226+
}
227+
}
201228
}
202229

203230
std::thread::sleep(delay);
@@ -208,6 +235,16 @@ impl TestEnv {
208235
))
209236
}
210237

238+
/// Wait until Electrum is aware of bitcoind's chain tip.
239+
pub fn wait_until_electrum_tip_syncs_with_bitcoind(
240+
&self,
241+
timeout: Duration,
242+
) -> anyhow::Result<()> {
243+
let chain_height = self.rpc_client().get_block_count()?;
244+
let chain_hash = self.rpc_client().get_block_hash(chain_height)?;
245+
self.wait_until_electrum_sees_block(chain_height as _, Some(chain_hash), timeout)
246+
}
247+
211248
/// This method waits for Electrsd to see a transaction with given `txid`. `timeout` is the
212249
/// maximum [`Duration`] we want to wait for a response from Electrsd.
213250
pub fn wait_until_electrum_sees_txid(
@@ -321,15 +358,15 @@ mod test {
321358

322359
// Mine some blocks.
323360
env.mine_blocks(101, None)?;
324-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
361+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
325362
let height = env.bitcoind.client.get_block_count()?;
326363
let blocks = (0..=height)
327364
.map(|i| env.bitcoind.client.get_block_hash(i))
328365
.collect::<Result<Vec<_>, _>>()?;
329366

330367
// Perform reorg on six blocks.
331368
env.reorg(6)?;
332-
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
369+
env.wait_until_electrum_tip_syncs_with_bitcoind(Duration::from_secs(6))?;
333370
let reorged_height = env.bitcoind.client.get_block_count()?;
334371
let reorged_blocks = (0..=height)
335372
.map(|i| env.bitcoind.client.get_block_hash(i))

0 commit comments

Comments
 (0)