Skip to content

Commit 87b780e

Browse files
committed
Synchronize force-close sweep checks
The force-close loop mined separately for each node even though every node shares one chain. Mining for the first node advanced later nodes past the exact intermediate state the test required. Sweep publication and Electrum indexing are also asynchronous, so immediate assertions could observe either valid state. Advance the shared chain once and poll every claimable sweep through broadcast and confirmation. Preserve the force-close counterexample that fails when only the funding-depth fix is applied. Co-Authored-By: HAL 9000
1 parent 79a9794 commit 87b780e

2 files changed

Lines changed: 71 additions & 34 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
cc 06354c9b049db51c31557bf46d86a68bdd4049577cbd9190fb81c7824b18f0e6 # shrinks to reorg_depth = 6, force_close = false
2+
cc ffba5725835411b0948e834640dd37fc9a35696a301d7f2d8f2054f158bd2cae # shrinks to reorg_depth = 1, force_close = true

tests/reorg_test.rs

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,27 @@ use proptest::prelude::prop;
88
use proptest::proptest;
99

1010
use crate::common::{
11-
expect_event, generate_blocks_and_wait, invalidate_blocks, open_channel,
12-
premine_and_distribute_funds, random_chain_source, random_config, setup_bitcoind_and_electrsd,
13-
setup_node, wait_for_outpoint_spend,
11+
expect_event, exponential_backoff_poll, generate_blocks_and_wait, invalidate_blocks,
12+
open_channel, premine_and_distribute_funds, random_chain_source, random_config,
13+
setup_bitcoind_and_electrsd, setup_node, wait_for_outpoint_spend, wait_for_tx,
1414
};
1515

16+
async fn wait_for_pending_sweep_balance<F>(
17+
node: &ldk_node::Node, mut matches_balance: F,
18+
) -> PendingSweepBalance
19+
where
20+
F: FnMut(&PendingSweepBalance) -> bool,
21+
{
22+
exponential_backoff_poll(|| {
23+
node.sync_wallets().unwrap();
24+
node.list_balances()
25+
.pending_balances_from_channel_closures
26+
.into_iter()
27+
.find(|balance| matches_balance(balance))
28+
})
29+
.await
30+
}
31+
1632
proptest! {
1733
#![proptest_config(proptest::test_runner::Config::with_cases(5))]
1834
#[test]
@@ -146,40 +162,60 @@ proptest! {
146162
sync_wallets!();
147163

148164
if force_close {
149-
for node in &nodes {
150-
node.sync_wallets().unwrap();
151-
// If there is no more balance, there is nothing to process here.
152-
if node.list_balances().lightning_balances.len() < 1 {
153-
return;
154-
}
155-
match node.list_balances().lightning_balances[0] {
156-
LightningBalance::ClaimableAwaitingConfirmations {
157-
confirmation_height,
158-
..
159-
} => {
160-
let cur_height = node.status().current_best_block.height;
161-
let blocks_to_go = confirmation_height - cur_height;
162-
generate_blocks_and_wait(bitcoind, electrs, blocks_to_go as usize).await;
163-
node.sync_wallets().unwrap();
164-
},
165-
_ => panic!("Unexpected balance state for node_hub!"),
166-
}
165+
let claimable_nodes = nodes
166+
.iter()
167+
.filter_map(|node| {
168+
node.list_balances().lightning_balances.iter().find_map(|balance| {
169+
match balance {
170+
LightningBalance::ClaimableAwaitingConfirmations {
171+
confirmation_height,
172+
..
173+
} => Some((node, *confirmation_height)),
174+
_ => None,
175+
}
176+
})
177+
})
178+
.collect::<Vec<_>>();
179+
let confirmation_height = claimable_nodes
180+
.iter()
181+
.map(|(_, confirmation_height)| *confirmation_height)
182+
.max()
183+
.expect("Missing claimable force-close balance");
184+
let cur_height = nodes[0].status().current_best_block.height;
185+
let blocks_to_go = confirmation_height.saturating_sub(cur_height);
186+
if blocks_to_go > 0 {
187+
generate_blocks_and_wait(bitcoind, electrs, blocks_to_go as usize).await;
188+
sync_wallets!();
189+
}
167190

168-
assert!(node.list_balances().lightning_balances.len() < 2);
169-
assert!(node.list_balances().pending_balances_from_channel_closures.len() > 0);
170-
match node.list_balances().pending_balances_from_channel_closures[0] {
171-
PendingSweepBalance::BroadcastAwaitingConfirmation { .. } => {},
172-
_ => panic!("Unexpected balance state!"),
191+
// Mining for one node advances the shared chain for every node. Mature all
192+
// claimable outputs together, wait for every sweep to reach the mempool, then
193+
// confirm them and wait for `AwaitingThresholdConfirmations`.
194+
for (node, _) in &claimable_nodes {
195+
let pending_balance = wait_for_pending_sweep_balance(node, |balance| {
196+
matches!(
197+
balance,
198+
PendingSweepBalance::BroadcastAwaitingConfirmation { .. }
199+
| PendingSweepBalance::AwaitingThresholdConfirmations { .. }
200+
)
201+
})
202+
.await;
203+
if let PendingSweepBalance::BroadcastAwaitingConfirmation {
204+
latest_spending_txid,
205+
..
206+
} = pending_balance
207+
{
208+
wait_for_tx(electrs, latest_spending_txid).await;
173209
}
210+
}
174211

175-
generate_blocks_and_wait(&bitcoind, electrs, 1).await;
176-
node.sync_wallets().unwrap();
177-
assert!(node.list_balances().lightning_balances.len() < 2);
178-
assert!(node.list_balances().pending_balances_from_channel_closures.len() > 0);
179-
match node.list_balances().pending_balances_from_channel_closures[0] {
180-
PendingSweepBalance::AwaitingThresholdConfirmations { .. } => {},
181-
_ => panic!("Unexpected balance state!"),
182-
}
212+
generate_blocks_and_wait(bitcoind, electrs, 1).await;
213+
sync_wallets!();
214+
for (node, _) in &claimable_nodes {
215+
wait_for_pending_sweep_balance(node, |balance| {
216+
matches!(balance, PendingSweepBalance::AwaitingThresholdConfirmations { .. })
217+
})
218+
.await;
183219
}
184220
}
185221

0 commit comments

Comments
 (0)