Skip to content
Merged
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
36 changes: 29 additions & 7 deletions yarn-project/sequencer-client/src/sequencer/sequencer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,10 @@ describe('sequencer', () => {
expect(publisher.sendRequestsAt).toHaveBeenCalled();
});

it('should vote when sync fails even within the build time limit', async () => {
it('does not run fallback actions when sync fails before the build start deadline', async () => {
// A transient sync miss with time still left to build must not trigger fallback actions: the
// work loop should retry on a later tick once sync recovers. In particular it must not send a
// standalone prune, which would give up the slot prematurely.
const startDeadline = sequencer.getTimeTable().getBuildStartDeadline(SlotNumber(newSlotNumber));
dateProvider.setTime((startDeadline - 1) * 1000);

Expand All @@ -876,12 +879,11 @@ describe('sequencer', () => {

await sequencer.work();

expect(publisher.enqueueSlashingActions).toHaveBeenCalledWith(
mockSlashActions,
SlotNumber(newSlotNumber),
expect.any(EthAddress),
expect.any(Function),
);
expect(publisher.enqueueSlashingActions).not.toHaveBeenCalled();
expect(publisher.enqueuePruneIfPrunable).not.toHaveBeenCalled();
expect(publisher.sendRequestsAt).not.toHaveBeenCalled();
// The slot is left unmarked so a later work-loop tick can retry once sync recovers.
expect(sequencer.getLastSlotForCheckpointProposalJob()).toBeUndefined();
});

it('should not vote when sync fails but not a proposer', async () => {
Expand Down Expand Up @@ -980,6 +982,26 @@ describe('sequencer', () => {
expect(publisher.sendRequestsAt).not.toHaveBeenCalled();
});

it('does not enqueue a standalone prune before the build deadline even when the rollup is prunable', async () => {
// Standalone prune is reserved for when we can no longer build the slot (past the build start
// deadline). Before the deadline, a transient sync miss must retry rather than prune the pending
// chain, even if the rollup happens to be prunable at the target slot.
const startDeadline = sequencer.getTimeTable().getBuildStartDeadline(SlotNumber(newSlotNumber));
dateProvider.setTime((startDeadline - 1) * 1000);

// Set us as the proposer
validatorClient.getValidatorAddresses.mockReturnValue([signer.address]);
epochCache.getProposerAttesterAddressInSlot.mockResolvedValue(signer.address);

// The rollup is prunable, but we are still within the build window.
publisher.enqueuePruneIfPrunable.mockResolvedValue(true);

await sequencer.work();

expect(publisher.enqueuePruneIfPrunable).not.toHaveBeenCalled();
expect(publisher.sendRequestsAt).not.toHaveBeenCalled();
});

it('should enqueue prune alongside votes and send a single request', async () => {
const startDeadline = sequencer.getTimeTable().getBuildStartDeadline(SlotNumber(newSlotNumber));
dateProvider.setTime((startDeadline + 1) * 1000);
Expand Down
14 changes: 7 additions & 7 deletions yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,6 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
return undefined;
}

// We are the proposer and the escape hatch is closed: now run the full sync check before building.
const syncedTo = await this.checkSync({ ts, slot });
if (!syncedTo) {
await this.tryVoteAndPruneWhenCannotBuild({ slot, targetSlot });
return undefined;
}

// Explicit build-loop entry gate: if we are past the latest useful block-building start for the
// target slot, abandon building for this slot. The proposer prioritizes the ideal L1-publish path
// and does not plan around the late consensus-handoff path. This is the proposer build path's
Expand All @@ -480,6 +473,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
return undefined;
}

// We are the proposer, the escape hatch is closed, and we have time before the build start deadline.
// Now run the full sync check before building.
const syncedTo = await this.checkSync({ ts, slot });
if (!syncedTo) {
return undefined;
}

// Next checkpoint follows from the last synced one
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);

Expand Down
Loading