Skip to content

Commit 4b563a6

Browse files
authored
test(e2e): migrate simple epoch tests to pipelining (#22973)
Enable pipelining on `epochs_first_slot` and `simple_block_building`
1 parent 5c006cf commit 4b563a6

5 files changed

Lines changed: 50 additions & 30 deletions

File tree

yarn-project/end-to-end/src/e2e_epochs/epochs_first_slot.test.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ jest.setTimeout(1000 * 60 * 10);
2929

3030
const NODE_COUNT = 8;
3131
const COMMITTEE_SIZE = 3;
32-
const TX_COUNT = 2;
33-
const EPOCH = EpochNumber(4);
32+
const TX_COUNT = 8;
3433

3534
// Spawns NODE_COUNT validator nodes, connected via a mocked gossip sub network, but sets
3635
// committee size to 3. Warps to immediately before the beginning of an epoch, and checks
@@ -54,6 +53,7 @@ describe('e2e_epochs/epochs_first_slot', () => {
5453
});
5554

5655
// Setup context with the given set of validators, no reorgs, mocked gossip sub network, and no anvil test watcher.
56+
// We expect 4 blocks per checkpoint with this config
5757
test = await EpochsTestContext.setup({
5858
numberOfAccounts: 0,
5959
initialValidators: validators,
@@ -62,6 +62,8 @@ describe('e2e_epochs/epochs_first_slot', () => {
6262
aztecProofSubmissionEpochs: 1024,
6363
aztecEpochDuration: 32,
6464
aztecSlotDurationInL1Slots: 3,
65+
ethereumSlotDuration: 12,
66+
blockDurationMs: 6000,
6567
startProverNode: false,
6668
aztecTargetCommitteeSize: COMMITTEE_SIZE,
6769
enforceTimeTable: true,
@@ -70,6 +72,8 @@ describe('e2e_epochs/epochs_first_slot', () => {
7072
attestationPropagationTime: 0.5,
7173
archiverPollingIntervalMS: 200,
7274
skipInitialSequencer: true,
75+
enableProposerPipelining: true,
76+
inboxLag: 2,
7377
});
7478

7579
({ context, logger } = test);
@@ -110,9 +114,18 @@ describe('e2e_epochs/epochs_first_slot', () => {
110114
const sequencers = nodes.map(node => node.getSequencer()!);
111115
const { failEvents } = test.watchSequencerEvents(sequencers, i => ({ validator: validators[i].attester }));
112116

113-
// Warp to before the first slot of an epoch, so that the sequencers are ready to build blocks.
114-
const [epochStart] = getTimestampRangeForEpoch(EPOCH, test.constants);
115-
await test.context.cheatCodes.eth.warp(Number(epochStart) - test.L1_BLOCK_TIME_IN_S, {
117+
// Jump to the beginning of two epochs from now
118+
const currentEpoch = (await test.monitor.run()).l2EpochNumber;
119+
const epoch = EpochNumber(currentEpoch + 2);
120+
121+
// Warp so that the next pipelined build cycle targets the first slot of the epoch. Under
122+
// proposer pipelining the build window starts one L2 slot earlier than the target slot
123+
// so we want wall-clock to enter `firstSlot - 1` (the last slot of the previous epoch) before
124+
// the next L1 block. Subtracting `L2_SLOT_DURATION + L1_BLOCK_TIME` puts us one L1 block before that
125+
// build slot starts, so the proposer for `firstSlot` gets the full build window available
126+
// before the epoch boundary is crossed on L1.
127+
const [epochStart] = getTimestampRangeForEpoch(epoch, test.constants);
128+
await test.context.cheatCodes.eth.warp(Number(epochStart) - test.L2_SLOT_DURATION_IN_S - test.L1_BLOCK_TIME_IN_S, {
116129
resetBlockInterval: true,
117130
});
118131

@@ -126,7 +139,7 @@ describe('e2e_epochs/epochs_first_slot', () => {
126139
logger.warn(`All txs have been mined`);
127140

128141
// Check that the first two slots of the epoch have a block
129-
const [firstSlot] = getSlotRangeForEpoch(EPOCH, test.constants);
142+
const [firstSlot] = getSlotRangeForEpoch(epoch, test.constants);
130143
const secondSlot = SlotNumber(firstSlot + 1);
131144
logger.warn(`Waiting until blocks are synced for slots ${firstSlot} and ${secondSlot}`);
132145
await retryUntil(
@@ -141,12 +154,6 @@ describe('e2e_epochs/epochs_first_slot', () => {
141154
1,
142155
);
143156

144-
// Expect no failures from sequencers during block building.
145-
// The following error is marked as a flake on the test ignore patterns,
146-
// so we can have this test run for a while before it breaks CI on a recoverable error.
147-
if (failEvents.length > 0) {
148-
logger.error(`Failed events from sequencers`, failEvents);
149-
}
150-
expect(failEvents).toEqual([]);
157+
test.assertNoFailuresFromSequencers(failEvents);
151158
});
152159
});

yarn-project/end-to-end/src/e2e_epochs/epochs_missed_l1_publish.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ describe('e2e_epochs/epochs_missed_l1_publish', () => {
374374
) {
375375
return false;
376376
}
377+
// Expected
378+
if (e.type === 'pipelined-checkpoint-discarded') {
379+
return false;
380+
}
377381
return true;
378382
});
379383
if (unexpectedFailEvents.length > 0) {

yarn-project/end-to-end/src/e2e_epochs/epochs_simple_block_building.test.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { EpochsTestContext } from './epochs_test.js';
2323
jest.setTimeout(1000 * 60 * 10);
2424

2525
const NODE_COUNT = 3;
26-
const TX_COUNT = 3;
26+
const TX_COUNT = 8;
2727

2828
// Sets up a lightweight RPC-only node without any account deployment, registers a test contract
2929
// locally, then spawns NODE_COUNT validator nodes connected via a mocked gossip sub network.
@@ -53,9 +53,13 @@ describe('e2e_epochs/epochs_simple_block_building', () => {
5353
mockGossipSubNetwork: true,
5454
disableAnvilTestWatcher: true,
5555
aztecProofSubmissionEpochs: 1024,
56+
aztecSlotDurationInL1Slots: 3,
57+
ethereumSlotDuration: 12,
58+
blockDurationMs: 6000,
5659
startProverNode: false,
5760
enforceTimeTable: true,
5861
skipInitialSequencer: true,
62+
enableProposerPipelining: true,
5963
inboxLag: 2,
6064
});
6165

@@ -100,12 +104,7 @@ describe('e2e_epochs/epochs_simple_block_building', () => {
100104
);
101105
logger.warn(`All txs have been mined`);
102106

103-
// Expect no failures from sequencers during block building.
104-
// The following error is marked as a flake on the test ignore patterns,
105-
// so we can have this test run for a while before it breaks CI on a recoverable error.
106-
if (failEvents.length > 0) {
107-
logger.error(`Failed events from sequencers`, failEvents);
108-
}
109-
expect(failEvents).toEqual([]);
107+
// Expect no failures from sequencers during block building
108+
test.assertNoFailuresFromSequencers(failEvents);
110109
});
111110
});

yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ export class EpochsTestContext {
497497
public watchSequencerEvents(
498498
sequencers: SequencerClient[],
499499
getMetadata: (i: number) => Record<string, any> = () => ({}),
500+
additionalFailEventKeys: (keyof SequencerEvents)[] = [],
500501
) {
501502
const stateChanges: TrackedSequencerEvent[] = [];
502503
const failEvents: TrackedSequencerEvent[] = [];
@@ -507,6 +508,10 @@ export class EpochsTestContext {
507508
'block-build-failed',
508509
'checkpoint-publish-failed',
509510
'proposer-rollup-check-failed',
511+
'checkpoint-error',
512+
'checkpoint-publish-failed',
513+
'pipelined-checkpoint-discarded',
514+
...additionalFailEventKeys,
510515
];
511516

512517
const makeEvent = (
@@ -545,4 +550,11 @@ export class EpochsTestContext {
545550

546551
return { failEvents, stateChanges };
547552
}
553+
554+
public assertNoFailuresFromSequencers(failEvents: TrackedSequencerEvent[]) {
555+
if (failEvents.length > 0) {
556+
this.logger.error(`Failed events from sequencers`, failEvents);
557+
}
558+
expect(failEvents).toEqual([]);
559+
}
548560
}

yarn-project/sequencer-client/src/sequencer/sequencer.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,15 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
181181
try {
182182
await this.work();
183183
} catch (err) {
184-
this.emit('checkpoint-error', { error: err as Error });
185-
if (err instanceof SequencerTooSlowError) {
186-
// Log as warn only if we had to abort halfway through the block proposal
187-
const logLvl = [SequencerState.INITIALIZING_CHECKPOINT, SequencerState.PROPOSER_CHECK].includes(
188-
err.proposedState,
189-
)
190-
? ('debug' as const)
191-
: ('warn' as const);
192-
this.log[logLvl](err.message, { now: this.dateProvider.nowInSeconds() });
184+
if (
185+
err instanceof SequencerTooSlowError &&
186+
[SequencerState.INITIALIZING_CHECKPOINT, SequencerState.PROPOSER_CHECK].includes(err.proposedState)
187+
) {
188+
// No need to alert if we just didn't get to start in time
189+
this.log.debug(err.message, { now: this.dateProvider.nowInSeconds() });
193190
} else {
194191
// Re-throw other errors
192+
this.emit('checkpoint-error', { error: err as Error });
195193
throw err;
196194
}
197195
} finally {

0 commit comments

Comments
 (0)