Skip to content

Commit 94a2f57

Browse files
authored
test(e2e): stabilize invalidation slots in proposer invalidates multiple checkpoints (#23590)
Summary: - Scan for consecutive bad checkpoint slots whose prior pipelined target slot is not owned by either intended bad proposer. - Keep the malicious-config injection tied to the selected bad proposers and remove the now-unnecessary non-null assertion. - Add an inline comment documenting why the prior pipelined target slot matters. Why: The test applies malicious checkpoint config while sequencers are already running. With proposer pipelining, the previous target slot can snapshot that config before the intended bad slots are built. If that prior proposer is one of the intended bad proposers, the test may spend the malicious config on the wrong checkpoint and stop validating the intended two-checkpoint invalidation path. This mirrors the slot-selection issue fixed for the invalid proposal slashing test, but applies it to the consecutive checkpoint invalidation scenario. Testing: - yarn format end-to-end - yarn build - LOG_LEVEL="info; debug:sequencer,publisher,validator" yarn workspace @aztec/end-to-end test:e2e e2e_epochs/epochs_invalidate_block.parallel.test.ts -t "proposer invalidates multiple checkpoints"
1 parent 65dee1a commit 94a2f57

1 file changed

Lines changed: 34 additions & 5 deletions

File tree

yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -390,17 +390,46 @@ describe('e2e_epochs/epochs_invalidate_block', () => {
390390
const { l2SlotNumber: currentSlot } = await test.monitor.run();
391391
logger.warn(`First checkpoint mined, current slot is ${currentSlot}`);
392392

393-
// Pick the next two slots with a 2-slot gap to account for pipelining plus a margin
394-
const badSlot1 = SlotNumber.add(currentSlot, 3);
395-
const badSlot2 = SlotNumber.add(currentSlot, 4);
393+
// The bad config is applied while sequencers are already running; skip pairs where the prior pipelined
394+
// target slot could snapshot that config before the intended bad slots.
395+
let badSlot1: SlotNumber | undefined;
396+
let badSlot2: SlotNumber | undefined;
397+
let badProposers: EthAddress[] = [];
398+
const firstCandidateSlot = Number(currentSlot) + 3;
399+
const maxBadSlotSearchAttempts = 20;
400+
for (let attempt = 0; attempt < maxBadSlotSearchAttempts && badSlot1 === undefined; attempt++) {
401+
const candidateSlot1 = SlotNumber(firstCandidateSlot + attempt);
402+
const candidateSlot2 = SlotNumber.add(candidateSlot1, 1);
403+
const priorPipelinedTargetSlot = SlotNumber.add(candidateSlot1, -1);
404+
const [priorProposer, p1, p2] = await Promise.all([
405+
test.epochCache.getProposerAttesterAddressInSlot(priorPipelinedTargetSlot),
406+
test.epochCache.getProposerAttesterAddressInSlot(candidateSlot1),
407+
test.epochCache.getProposerAttesterAddressInSlot(candidateSlot2),
408+
]);
409+
410+
logger.warn(`Checking bad checkpoint slots ${candidateSlot1} and ${candidateSlot2}`, {
411+
priorPipelinedTargetSlot,
412+
priorProposer: priorProposer?.toString(),
413+
p1: p1?.toString(),
414+
p2: p2?.toString(),
415+
});
416+
417+
if (p1 && p2 && !priorProposer?.equals(p1) && !priorProposer?.equals(p2)) {
418+
badSlot1 = candidateSlot1;
419+
badSlot2 = candidateSlot2;
420+
badProposers = [p1, p2];
421+
}
422+
}
423+
if (badSlot1 === undefined || badSlot2 === undefined) {
424+
throw new Error(`Could not find bad checkpoint slots after ${maxBadSlotSearchAttempts} attempts`);
425+
}
396426
const badSlots = [badSlot1, badSlot2];
397-
const badProposers = await Promise.all(badSlots.map(s => test.epochCache.getProposerAttesterAddressInSlot(s)));
398427

399428
const badNodes = [];
400429
for (let badProposerIndex = 0; badProposerIndex < badProposers.length; badProposerIndex++) {
401430
const badProposer = badProposers[badProposerIndex];
402431
logger.warn(`Disabling invalidation checks and attestation gathering for proposer ${badProposer}`);
403-
const nodeIndex = nodes.findIndex(n => n.getSequencer()!.validatorAddresses!.some(a => a.equals(badProposer!)));
432+
const nodeIndex = nodes.findIndex(n => n.getSequencer()!.validatorAddresses!.some(a => a.equals(badProposer)));
404433
if (nodeIndex === -1) {
405434
throw new Error(`Could not find node for proposer ${badProposer}`);
406435
}

0 commit comments

Comments
 (0)