diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index e5a55462b97..5acbbd261f6 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -843,7 +843,7 @@ class TestSubject extends Sequencer { } public override doRealWork() { - this.setState(SequencerState.IDLE, 0, true /** force */); + this.setState(SequencerState.IDLE, 0n, true /** force */); return super.doRealWork(); } } diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 8c9750ff043..325a2dd2d44 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -189,7 +189,7 @@ export class Sequencer { public start() { this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs); this.runningPromise.start(); - this.setState(SequencerState.IDLE, 0, true /** force */); + this.setState(SequencerState.IDLE, 0n, true /** force */); this.log.info('Sequencer started'); return Promise.resolve(); } @@ -201,7 +201,7 @@ export class Sequencer { this.log.debug(`Stopping sequencer`); await this.runningPromise?.stop(); this.publisher.interrupt(); - this.setState(SequencerState.STOPPED, 0, true /** force */); + this.setState(SequencerState.STOPPED, 0n, true /** force */); this.log.info('Stopped sequencer'); } @@ -212,7 +212,7 @@ export class Sequencer { this.log.info('Restarting sequencer'); this.publisher.restart(); this.runningPromise!.start(); - this.setState(SequencerState.IDLE, 0, true /** force */); + this.setState(SequencerState.IDLE, 0n, true /** force */); } /** @@ -232,7 +232,7 @@ export class Sequencer { * - If our block for some reason is not included, revert the state */ protected async doRealWork() { - this.setState(SequencerState.SYNCHRONIZING, 0); + this.setState(SequencerState.SYNCHRONIZING, 0n); // Update state when the previous block has been synced const prevBlockSynced = await this.isBlockSynced(); // Do not go forward with new block if the previous one has not been mined and processed @@ -243,7 +243,7 @@ export class Sequencer { this.log.debug('Previous block has been mined and processed'); - this.setState(SequencerState.PROPOSER_CHECK, 0); + this.setState(SequencerState.PROPOSER_CHECK, 0n); const chainTip = await this.l2BlockSource.getBlock(-1); const historicalHeader = chainTip?.header; @@ -277,9 +277,8 @@ export class Sequencer { if (!this.shouldProposeBlock(historicalHeader, {})) { return; } - const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration, Number(slot)); - this.setState(SequencerState.WAITING_FOR_TXS, secondsIntoSlot); + this.setState(SequencerState.WAITING_FOR_TXS, slot); // Get txs to build the new block. const pendingTxs = this.p2pClient.getTxs('pending'); @@ -325,7 +324,7 @@ export class Sequencer { } catch (err) { this.log.error(`Error assembling block`, (err as any).stack); } - this.setState(SequencerState.IDLE, 0); + this.setState(SequencerState.IDLE, 0n); } protected async work() { @@ -339,7 +338,7 @@ export class Sequencer { throw err; } } finally { - this.setState(SequencerState.IDLE, 0); + this.setState(SequencerState.IDLE, 0n); } } @@ -398,13 +397,23 @@ export class Sequencer { return true; } - setState(proposedState: SequencerState, secondsIntoSlot: number, force: boolean = false) { + /** + * Sets the sequencer state and checks if we have enough time left in the slot to transition to the new state. + * @param proposedState - The new state to transition to. + * @param currentSlotNumber - The current slot number. + * @param force - Whether to force the transition even if the sequencer is stopped. + * + * @dev If the `currentSlotNumber` doesn't matter (e.g. transitioning to IDLE), pass in `0n`; + * it is only used to check if we have enough time left in the slot to transition to the new state. + */ + setState(proposedState: SequencerState, currentSlotNumber: bigint, force: boolean = false) { if (this.state === SequencerState.STOPPED && force !== true) { this.log.warn( `Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped. Set force=true to override.`, ); return; } + const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration, Number(currentSlotNumber)); if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) { throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot); } @@ -567,12 +576,7 @@ export class Sequencer { this.metrics.recordNewBlock(newGlobalVariables.blockNumber.toNumber(), validTxs.length); const workTimer = new Timer(); - const secondsIntoSlot = getSecondsIntoSlot( - this.l1GenesisTime, - this.aztecSlotDuration, - newGlobalVariables.slotNumber.toNumber(), - ); - this.setState(SequencerState.CREATING_BLOCK, secondsIntoSlot); + this.setState(SequencerState.CREATING_BLOCK, newGlobalVariables.slotNumber.toBigInt()); this.log.info( `Building blockNumber=${newGlobalVariables.blockNumber.toNumber()} txCount=${ validTxs.length @@ -688,23 +692,13 @@ export class Sequencer { this.log.info('Creating block proposal'); const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes); - let secondsIntoSlot = getSecondsIntoSlot( - this.l1GenesisTime, - this.aztecSlotDuration, - block.header.globalVariables.slotNumber.toNumber(), - ); + const slotNumber = block.header.globalVariables.slotNumber.toBigInt(); - this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS, secondsIntoSlot); + this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS, slotNumber); this.log.info('Broadcasting block proposal to validators'); this.validatorClient.broadcastBlockProposal(proposal); - secondsIntoSlot = getSecondsIntoSlot( - this.l1GenesisTime, - this.aztecSlotDuration, - block.header.globalVariables.slotNumber.toNumber(), - ); - - this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, secondsIntoSlot); + this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, slotNumber); const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations); this.log.info(`Collected attestations from validators, number of attestations: ${attestations.length}`); @@ -761,13 +755,8 @@ export class Sequencer { txHashes?: TxHash[], proofQuote?: EpochProofQuote, ) { - const secondsIntoSlot = getSecondsIntoSlot( - this.l1GenesisTime, - this.aztecSlotDuration, - block.header.globalVariables.slotNumber.toNumber(), - ); // Publishes new block to the network and awaits the tx to be mined - this.setState(SequencerState.PUBLISHING_BLOCK, secondsIntoSlot); + this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt()); const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote); if (!publishedL2Block) { diff --git a/yarn-project/sequencer-client/src/sequencer/utils.ts b/yarn-project/sequencer-client/src/sequencer/utils.ts index 4c16e8c8a9b..8bb4b440dc2 100644 --- a/yarn-project/sequencer-client/src/sequencer/utils.ts +++ b/yarn-project/sequencer-client/src/sequencer/utils.ts @@ -75,5 +75,5 @@ export function orderAttestations(attestations: BlockAttestation[], orderAddress export function getSecondsIntoSlot(l1GenesisTime: number, aztecSlotDuration: number, slotNumber: number): number { const slotStartTimestamp = l1GenesisTime + slotNumber * aztecSlotDuration; - return Date.now() / 1000 - slotStartTimestamp; + return Number((Date.now() / 1000 - slotStartTimestamp).toFixed(3)); }