Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/test-flake-tenure-extend.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed flake in tests::signer::v0::tenure_extend::non_blocking_minority_configured_to_favour_incoming_miner
51 changes: 40 additions & 11 deletions stacks-node/src/tests/signer/v0/tenure_extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3550,26 +3550,55 @@ fn non_blocking_minority_configured_to_favour_test(variant: NonBlockingMinorityV
"------------------------- Wait for Signers to Mark Incoming Miner as Invalid -------------------------"
);

// Drop stale stackerdb chunks BEFORE sleeping so the wait below can only be
// satisfied by state machine updates broadcast at or after the switch-back.
// The short-timeout signers switch back to miner 1 and broadcast DURING the
// sleep (their proposal timeout is shorter than the sleep by design), so
// clearing after the sleep would erase those broadcasts; with no new burn
// block or proposal to trigger re-broadcasts, the wait could then never
// reach the 70% threshold. Clearing here still drops the stale pre-switch
// chunks that would otherwise satisfy the wait immediately.
test_observer::clear();

// Sleep to let the short-timeout signers mark the incoming miner as invalid
std::thread::sleep(tenure_extend_wait_timeout.add(Duration::from_secs(1)));

// Verify the short-timeout signers have updated their state machine.
// In the latest protocol, the minority reports miner 1 as the active miner (extending).
// In v1 / FavourIncoming, signers still report miner 2 as the winning miner.
let (expected_pkh, stacks_tip_height) =
if matches!(variant, NonBlockingMinorityVariant::FavourPrevMiner) {
(miner_pkh_1.clone(), stacks_height_before - 1)
} else {
(miner_pkh_2.clone(), stacks_height_before)
};
// What we expect the short-timeout signers to broadcast after switching back
// to miner 1, and which signer set must reach 70% agreement for the wait to
// succeed. For FavourIncomingMiner the short-timeout signers are the
// majority, and we need their switched-back broadcasts to flip the *global*
// state to miner 1 BEFORE we unstall miner 2 - otherwise miner 2's
// BlockFound is evaluated against stale global state and gets accepted
// instead of rejected. The long-timeout minority signer never switches back
// within this window, so waiting against the full signer set (5) with the
// 70% threshold (>= 4) is exactly the threshold the GlobalStateEvaluator
// uses to flip the global state.
let all_signers_versions = miners.signer_test.signer_addresses_versions();
let (expected_pkh, stacks_tip_height, expected_signers) = match variant {
NonBlockingMinorityVariant::FavourPrevMiner => (
miner_pkh_1.clone(),
stacks_height_before - 1,
short_timeout_signers.as_slice(),
),
NonBlockingMinorityVariant::FavourPrevMinerV1 => (
miner_pkh_2.clone(),
stacks_height_before,
short_timeout_signers.as_slice(),
),
NonBlockingMinorityVariant::FavourIncomingMiner => (
miner_pkh_1.clone(),
stacks_height_before - 1,
all_signers_versions.as_slice(),
),
};
wait_for_state_machine_update(
30,
&get_chain_info(&conf_1).pox_consensus,
burn_height_before + 1,
Some((expected_pkh, stacks_tip_height)),
&short_timeout_signers,
expected_signers,
)
.expect("Short-timeout signers failed to update state machine");
.expect("Signers failed to reach expected state machine update after timeout");

if minority_favours_incoming {
// FavourIncomingMiner: miner 2 proposes first and gets rejected,
Expand Down
Loading