Skip to content

Commit b6a22f6

Browse files
randomloginfebyeji
andcommitted
tests: wait for nodes to reach the chain tip after mining
This is a prerequisite PR for compact block filter node. Contrary to all other chain sources, CBF chain source in tests is different from the node who mines blocks. That means we need to ensure the tx from chain source has been broadcast. In the same fashion we ensure that after the block has mined we received it from the mining node. Tests assumed that a node observes a freshly mined block as soon as `sync_wallets` returns. Make `generate_blocks_and_wait` and `premine_and_distribute_funds` return the new chain height, add `wait_for_node_tip` to poll until a node reports at least that height (syncing on every try, for pull-based sources), and use it after mining in the tests. Other changes: - Let `exponential_backoff_poll` return `None` instead of panicking when it runs out of tries, and `expect` at the call sites. - Return a `Result` from the on-chain tx type assertions, so a failure is reported at the call site. - Wait for the sweep transaction to reach the mempool before mining it, and wait for the transaction before mining in `unified_send_receive_bip21_uri`, so the blocks are guaranteed to include it. - In `lsps2_client_trusts_lsp`, wait for the client's tip, which the assertions inspect, rather than the payer's. Co-authored-by: febyeji <yeji.han@sf.snu.ac.kr>
1 parent 6c2f077 commit b6a22f6

3 files changed

Lines changed: 183 additions & 69 deletions

File tree

‎tests/common/mod.rs‎

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ fn has_onchain_tx_type<F: Fn(&TransactionType) -> bool>(node: &TestNode, predica
546546

547547
fn assert_any_node_has_onchain_tx_type<F: Fn(&TransactionType) -> bool + Copy>(
548548
nodes: &[(&str, &TestNode)], tx_type_name: &str, predicate: F,
549-
) {
549+
) -> Result<(), String> {
550550
if nodes.iter().any(|(_, node)| has_onchain_tx_type(node, predicate)) {
551-
return;
551+
return Ok(());
552552
}
553553

554554
let observed: Vec<String> = nodes
@@ -560,14 +560,14 @@ fn assert_any_node_has_onchain_tx_type<F: Fn(&TransactionType) -> bool + Copy>(
560560
})
561561
})
562562
.collect();
563-
panic!("Expected on-chain payment with tx_type {}; observed {:?}", tx_type_name, observed);
563+
Err(format!("Expected on-chain payment with tx_type {}; observed {:?}", tx_type_name, observed))
564564
}
565565

566566
fn assert_all_nodes_have_onchain_tx_type<F: Fn(&TransactionType) -> bool + Copy>(
567567
nodes: &[(&str, &TestNode)], panic_msg: &str, tx_type_name: &str, predicate: F,
568-
) {
568+
) -> Result<(), String> {
569569
if nodes.iter().all(|(_, node)| has_onchain_tx_type(node, predicate)) {
570-
return;
570+
return Ok(());
571571
}
572572

573573
let observed: Vec<String> = nodes
@@ -579,10 +579,10 @@ fn assert_all_nodes_have_onchain_tx_type<F: Fn(&TransactionType) -> bool + Copy>
579579
})
580580
})
581581
.collect();
582-
panic!(
582+
return Err(format!(
583583
"Expected {}nodes to have on-chain payment with tx_type {}; observed {:?}",
584584
panic_msg, tx_type_name, observed
585-
);
585+
));
586586
}
587587

588588
async fn settle_force_close_balance<E: ElectrumApi>(
@@ -600,7 +600,12 @@ async fn settle_force_close_balance<E: ElectrumApi>(
600600
assert_eq!(actual_counterparty_node_id, counterparty_node_id);
601601
let cur_height = node.status().current_best_block.height;
602602
let blocks_to_go = confirmation_height - cur_height;
603-
generate_blocks_and_wait(bitcoind, electrsd, blocks_to_go as usize).await;
603+
let new_height =
604+
generate_blocks_and_wait(bitcoind, electrsd, blocks_to_go as usize).await;
605+
wait_for_node_tip(node, new_height).await.expect("node hasn't synced to the tip");
606+
wait_for_node_tip(peer_node, new_height)
607+
.await
608+
.expect("node hasn't synced to the tip");
604609
node.sync_wallets().unwrap();
605610
peer_node.sync_wallets().unwrap();
606611
},
@@ -615,7 +620,9 @@ async fn settle_force_close_balance<E: ElectrumApi>(
615620
if node.list_balances().lightning_balances.is_empty() {
616621
break;
617622
}
618-
generate_blocks_and_wait(bitcoind, electrsd, 1).await;
623+
let new_height = generate_blocks_and_wait(bitcoind, electrsd, 1).await;
624+
wait_for_node_tip(node, new_height).await.expect("node hasn't synced to the tip");
625+
wait_for_node_tip(peer_node, new_height).await.expect("node hasn't synced to the tip");
619626
node.sync_wallets().unwrap();
620627
peer_node.sync_wallets().unwrap();
621628
}
@@ -624,8 +631,14 @@ async fn settle_force_close_balance<E: ElectrumApi>(
624631
assert!(balances.lightning_balances.is_empty(), "Unexpected balance state: {:?}", balances);
625632
assert_eq!(balances.pending_balances_from_channel_closures.len(), 1);
626633
match balances.pending_balances_from_channel_closures[0] {
627-
PendingSweepBalance::BroadcastAwaitingConfirmation { .. } => {
628-
generate_blocks_and_wait(bitcoind, electrsd, 1).await;
634+
PendingSweepBalance::BroadcastAwaitingConfirmation { latest_spending_txid, .. } => {
635+
// The balance flips as soon as the sweeper generates the transaction, before it has
636+
// been relayed to bitcoind. Make sure it is in the mempool so the next block
637+
// includes it.
638+
wait_for_tx(electrsd, latest_spending_txid).await;
639+
let new_height = generate_blocks_and_wait(bitcoind, electrsd, 1).await;
640+
wait_for_node_tip(node, new_height).await.expect("node hasn't synced to the tip");
641+
wait_for_node_tip(peer_node, new_height).await.expect("node hasn't synced to the tip");
629642
node.sync_wallets().unwrap();
630643
peer_node.sync_wallets().unwrap();
631644

@@ -640,7 +653,9 @@ async fn settle_force_close_balance<E: ElectrumApi>(
640653
_ => panic!("Unexpected balance state!"),
641654
}
642655

643-
generate_blocks_and_wait(bitcoind, electrsd, 5).await;
656+
let new_height = generate_blocks_and_wait(bitcoind, electrsd, 5).await;
657+
wait_for_node_tip(node, new_height).await.expect("node hasn't synced to the tip");
658+
wait_for_node_tip(peer_node, new_height).await.expect("node hasn't synced to the tip");
644659
node.sync_wallets().unwrap();
645660
peer_node.sync_wallets().unwrap();
646661
}
@@ -906,7 +921,7 @@ fn start_node(node: TestNode) -> TestNode {
906921

907922
pub(crate) async fn generate_blocks_and_wait<E: ElectrumApi>(
908923
bitcoind: &BitcoindClient, electrs: &E, num: usize,
909-
) {
924+
) -> usize {
910925
let _ = bitcoind.create_wallet("ldk_node_test");
911926
let _ = bitcoind.load_wallet("ldk_node_test");
912927
print!("Generating {} blocks...", num);
@@ -915,9 +930,11 @@ pub(crate) async fn generate_blocks_and_wait<E: ElectrumApi>(
915930
let address = bitcoind.new_address().expect("failed to get new address");
916931
// TODO: expect this Result once the WouldBlock issue is resolved upstream.
917932
let _block_hashes_res = bitcoind.generate_to_address(num, &address);
918-
wait_for_block(bitcoind, electrs, cur_height as usize + num).await;
933+
let new_height = cur_height as usize + num;
934+
wait_for_block(bitcoind, electrs, new_height).await;
919935
print!(" Done!");
920936
println!("\n");
937+
return new_height;
921938
}
922939

923940
pub(crate) fn invalidate_blocks(bitcoind: &BitcoindClient, num_blocks: usize) {
@@ -946,14 +963,16 @@ pub(crate) async fn wait_for_block<E: ElectrumApi>(
946963
}
947964
bitcoind.get_block_hash(min_height as u64).ok()?.block_hash().ok()
948965
})
949-
.await;
966+
.await
967+
.expect("reached max tries");
950968
// A height-only wait can return the old header during a same-height reorg. Require the
951969
// replacement hash so callers cannot sync against the stale chain by mistake.
952970
exponential_backoff_poll(|| {
953971
let header = electrs.block_header(min_height).ok()?;
954972
(header.block_hash() == expected_block_hash).then_some(())
955973
})
956-
.await;
974+
.await
975+
.expect("reached max tries");
957976
}
958977

959978
pub(crate) async fn wait_for_tx<E: ElectrumApi>(electrs: &E, txid: Txid) {
@@ -965,7 +984,8 @@ pub(crate) async fn wait_for_tx<E: ElectrumApi>(electrs: &E, txid: Txid) {
965984
electrs.ping().unwrap();
966985
electrs.transaction_get(&txid).ok()
967986
})
968-
.await;
987+
.await
988+
.expect("reached max tries");
969989
}
970990

971991
pub(crate) async fn wait_for_outpoint_spend<E: ElectrumApi>(electrs: &E, outpoint: OutPoint) {
@@ -982,7 +1002,8 @@ pub(crate) async fn wait_for_outpoint_spend<E: ElectrumApi>(electrs: &E, outpoin
9821002
});
9831003
is_spent.then_some(())
9841004
})
985-
.await;
1005+
.await
1006+
.expect("reached max tries");
9861007
}
9871008

9881009
/// Polls the channel from `source_node` to `counterparty_node` until it reports `is_usable`
@@ -1016,34 +1037,46 @@ pub(crate) async fn wait_for_channel_ready_to_send(
10161037
);
10171038
}
10181039

1019-
pub(crate) async fn exponential_backoff_poll<T, F>(mut poll: F) -> T
1040+
pub(crate) async fn wait_for_node_tip(node: &Node, height: usize) -> Option<()> {
1041+
// Pull-based chain sources (Esplora, Electrum) only advance the tip when a sync runs, so
1042+
// trigger one on every try instead of relying on the slow background interval.
1043+
exponential_backoff_poll(|| {
1044+
node.sync_wallets().unwrap();
1045+
(node.status().current_best_block.height as usize >= height).then_some(())
1046+
})
1047+
.await
1048+
}
1049+
1050+
pub(crate) async fn exponential_backoff_poll<T, F>(mut poll: F) -> Option<T>
10201051
where
10211052
F: FnMut() -> Option<T>,
10221053
{
10231054
let mut delay = Duration::from_millis(64);
10241055
let mut tries = 0;
10251056
loop {
10261057
match poll() {
1027-
Some(data) => break data,
1058+
Some(data) => return Some(data),
10281059
None if delay.as_millis() < 512 => {
10291060
delay = delay.mul_f32(2.0);
10301061
},
10311062

10321063
None => {},
10331064
}
1034-
assert!(tries < 20, "Reached max tries.");
1065+
if tries >= 20 {
1066+
return None;
1067+
}
10351068
tries += 1;
10361069
tokio::time::sleep(delay).await;
10371070
}
10381071
}
10391072

10401073
pub(crate) async fn premine_and_distribute_funds<E: ElectrumApi>(
10411074
bitcoind: &BitcoindClient, electrs: &E, addrs: Vec<Address>, amount: Amount,
1042-
) {
1075+
) -> usize {
10431076
premine_blocks(bitcoind, electrs).await;
10441077

10451078
distribute_funds_unconfirmed(bitcoind, electrs, addrs, amount).await;
1046-
generate_blocks_and_wait(bitcoind, electrs, 1).await;
1079+
generate_blocks_and_wait(bitcoind, electrs, 1).await
10471080
}
10481081

10491082
pub(crate) async fn premine_blocks<E: ElectrumApi>(bitcoind: &BitcoindClient, electrs: &E) {
@@ -1259,13 +1292,15 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
12591292

12601293
let premine_amount_sat = if expect_anchor_channel { 2_125_000 } else { 2_100_000 };
12611294

1262-
premine_and_distribute_funds(
1295+
let new_height = premine_and_distribute_funds(
12631296
&bitcoind,
12641297
electrsd,
12651298
vec![addr_a, addr_b],
12661299
Amount::from_sat(premine_amount_sat),
12671300
)
12681301
.await;
1302+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1303+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
12691304
node_a.sync_wallets().unwrap();
12701305
node_b.sync_wallets().unwrap();
12711306
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
@@ -1341,7 +1376,9 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
13411376
wait_for_tx(electrsd, funding_txo_a.txid).await;
13421377

13431378
if !allow_0conf {
1344-
generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1379+
let new_height = generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1380+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1381+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
13451382
}
13461383

13471384
node_a.sync_wallets().unwrap();
@@ -1709,7 +1746,9 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
17091746
);
17101747

17111748
// Mine a block to give time for the HTLC to resolve
1712-
generate_blocks_and_wait(&bitcoind, electrsd, 1).await;
1749+
let new_height = generate_blocks_and_wait(&bitcoind, electrsd, 1).await;
1750+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1751+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
17131752

17141753
println!("\nB splices out to pay A");
17151754
let addr_a = node_a.onchain_payment().new_address().unwrap();
@@ -1721,7 +1760,9 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
17211760
let splice_out_txo = expect_splice_negotiated_event!(node_b, node_a.node_id());
17221761
wait_for_tx(electrsd, splice_out_txo.txid).await;
17231762

1724-
generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1763+
let new_height = generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1764+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1765+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
17251766
node_a.sync_wallets().unwrap();
17261767
node_b.sync_wallets().unwrap();
17271768

@@ -1743,7 +1784,9 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
17431784
let splice_in_txo = expect_splice_negotiated_event!(node_a, node_b.node_id());
17441785
wait_for_tx(electrsd, splice_in_txo.txid).await;
17451786

1746-
generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1787+
let new_height = generate_blocks_and_wait(&bitcoind, electrsd, 6).await;
1788+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1789+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
17471790
node_a.sync_wallets().unwrap();
17481791
node_b.sync_wallets().unwrap();
17491792

@@ -1819,7 +1862,9 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
18191862

18201863
wait_for_outpoint_spend(electrsd, splice_in_txo).await;
18211864

1822-
generate_blocks_and_wait(&bitcoind, electrsd, 1).await;
1865+
let new_height = generate_blocks_and_wait(&bitcoind, electrsd, 1).await;
1866+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1867+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
18231868
node_a.sync_wallets().unwrap();
18241869
node_b.sync_wallets().unwrap();
18251870

@@ -1864,7 +1909,10 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
18641909

18651910
assert_eq!(node_a_blocks_to_go, node_b_blocks_to_go);
18661911

1867-
generate_blocks_and_wait(&bitcoind, electrsd, node_a_blocks_to_go as usize).await;
1912+
let new_height =
1913+
generate_blocks_and_wait(&bitcoind, electrsd, node_a_blocks_to_go as usize).await;
1914+
wait_for_node_tip(&node_a, new_height).await.expect("node hasn't synced to the tip");
1915+
wait_for_node_tip(&node_b, new_height).await.expect("node hasn't synced to the tip");
18681916
node_a.sync_wallets().unwrap();
18691917
node_b.sync_wallets().unwrap();
18701918

@@ -1886,19 +1934,22 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
18861934
"no ",
18871935
"UnilateralClose",
18881936
|tx_type| !matches!(tx_type, TransactionType::UnilateralClose { .. }),
1889-
);
1937+
)
1938+
.unwrap();
18901939
assert_any_node_has_onchain_tx_type(
18911940
&[("node_a", &node_a), ("node_b", &node_b)],
18921941
"Sweep",
18931942
|tx_type| matches!(tx_type, TransactionType::Sweep { .. }),
1894-
);
1943+
)
1944+
.unwrap();
18951945
} else {
18961946
assert_all_nodes_have_onchain_tx_type(
18971947
&[("node_a", &node_a), ("node_b", &node_b)],
18981948
"all ",
18991949
"CooperativeClose",
19001950
|tx_type| matches!(tx_type, TransactionType::CooperativeClose { .. }),
1901-
);
1951+
)
1952+
.unwrap();
19021953
// Peer removed after cooperative close — no further reason to reconnect.
19031954
assert!(
19041955
!node_a.list_peers().iter().any(|p| p.node_id == node_b.node_id() && p.is_persisted),

0 commit comments

Comments
 (0)