@aztec/sequencer-client 2.1.0-rc.2 → 2.1.0-rc.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,YAAY,CAAC;AAE/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAuB,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,KAAK,qBAAqB,EAAiC,MAAM,yBAAyB,CAAC;AAEpG,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,uBAAuB,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,eAAe,GACjD,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,eAAe,GACf,cAAc,GACd,WAAW,GACX,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,GACzC,IAAI,CAAC,iBAAiB,EAAE,sBAAsB,GAAG,mBAAmB,GAAG,oBAAoB,CAAC,CAAC;AAE/F,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,eAAe,CAuGvE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,kBAAkB,CAAC,qBAAqB,CASnF,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAExD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,YAAY,CAAC;AAE/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAuB,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,KAAK,qBAAqB,EAAiC,MAAM,yBAAyB,CAAC;AAEpG,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,uBAAuB,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,eAAe,GACjD,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,eAAe,GACf,cAAc,GACd,WAAW,GACX,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,GACzC,IAAI,CAAC,iBAAiB,EAAE,sBAAsB,GAAG,mBAAmB,GAAG,oBAAoB,CAAC,CAAC;AAE/F,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,eAAe,CA2GvE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,kBAAkB,CAAC,qBAAqB,CASnF,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAExD"}
package/dest/config.js CHANGED
@@ -106,6 +106,10 @@ export const sequencerConfigMappings = {
106
106
  description: 'Do not invalidate the previous block if invalid when we are the proposer (for testing only)',
107
107
  ...booleanConfigHelper(false)
108
108
  },
109
+ injectFakeAttestation: {
110
+ description: 'Inject a fake attestation (for testing only)',
111
+ ...booleanConfigHelper(false)
112
+ },
109
113
  ...pickConfigMappings(p2pConfigMappings, [
110
114
  'txPublicSetupAllowList'
111
115
  ])
@@ -5,10 +5,10 @@ import { EthAddress } from '@aztec/foundation/eth-address';
5
5
  import { Signature } from '@aztec/foundation/eth-signature';
6
6
  import { Fr } from '@aztec/foundation/fields';
7
7
  import { type DateProvider } from '@aztec/foundation/timer';
8
- import type { TypedEventEmitter } from '@aztec/foundation/types';
8
+ import { type TypedEventEmitter } from '@aztec/foundation/types';
9
9
  import type { P2P } from '@aztec/p2p';
10
10
  import type { SlasherClientInterface } from '@aztec/slasher';
11
- import { type CommitteeAttestation, CommitteeAttestationsAndSigners, type L2BlockSource, type ValidateBlockResult } from '@aztec/stdlib/block';
11
+ import { CommitteeAttestation, CommitteeAttestationsAndSigners, type L2BlockSource, type ValidateBlockResult } from '@aztec/stdlib/block';
12
12
  import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
13
13
  import { type IFullNodeBlockBuilder, type PublicProcessorLimits, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
14
14
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
@@ -89,6 +89,8 @@ export declare class Sequencer extends Sequencer_base {
89
89
  private metrics;
90
90
  private lastBlockPublished;
91
91
  private governanceProposerPayload;
92
+ /** The last slot for which we attempted to vote when sync failed, to prevent duplicate attempts. */
93
+ private lastSlotForVoteWhenSyncFailed;
92
94
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */
93
95
  protected timetable: SequencerTimetable;
94
96
  protected enforceTimeTable: boolean;
@@ -128,8 +130,10 @@ export declare class Sequencer extends Sequencer_base {
128
130
  * - Submit block
129
131
  * - If our block for some reason is not included, revert the state
130
132
  */
131
- protected doRealWork(): Promise<void>;
132
133
  protected work(): Promise<void>;
134
+ /** Tries building a block proposal, and if successful, enqueues it for publishing. */
135
+ private tryBuildBlockAndEnqueuePublish;
136
+ protected safeWork(): Promise<void>;
133
137
  /**
134
138
  * Sets the sequencer state and checks if we have enough time left in the slot to transition to the new state.
135
139
  * @param proposedState - The new state to transition to.
@@ -165,22 +169,47 @@ export declare class Sequencer extends Sequencer_base {
165
169
  /**
166
170
  * Returns whether all dependencies have caught up.
167
171
  * We don't check against the previous block submitted since it may have been reorg'd out.
168
- * @returns Boolean indicating if our dependencies are synced to the latest block.
169
172
  */
170
- protected getChainTip(): Promise<{
173
+ protected checkSync(args: {
174
+ ts: bigint;
175
+ slot: bigint;
176
+ }): Promise<{
171
177
  block?: L2Block;
172
178
  blockNumber: number;
173
179
  archive: Fr;
174
180
  l1Timestamp: bigint;
175
181
  pendingChainValidationStatus: ValidateBlockResult;
176
182
  } | undefined>;
183
+ /**
184
+ * Enqueues governance and slashing votes with the publisher. Does not block.
185
+ * @param publisher - The publisher to enqueue votes with
186
+ * @param attestorAddress - The attestor address to use for signing
187
+ * @param slot - The slot number
188
+ * @param timestamp - The timestamp for the votes
189
+ * @param context - Optional context for logging (e.g., block number)
190
+ * @returns A tuple of [governanceEnqueued, slashingEnqueued]
191
+ */
192
+ protected enqueueGovernanceAndSlashingVotes(publisher: SequencerPublisher, attestorAddress: EthAddress, slot: bigint, timestamp: bigint): [Promise<boolean> | undefined, Promise<boolean> | undefined];
193
+ /**
194
+ * Checks if we are the proposer for the next slot.
195
+ * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
196
+ */
197
+ protected checkCanPropose(slot: bigint): Promise<[boolean, EthAddress | undefined]>;
198
+ /**
199
+ * Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
200
+ * This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
201
+ */
202
+ protected tryVoteWhenSyncFails(args: {
203
+ slot: bigint;
204
+ ts: bigint;
205
+ }): Promise<void>;
177
206
  /**
178
207
  * Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
179
208
  * has been there without being invalidated and whether the sequencer is in the committee or not. We always
180
209
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
181
210
  * and if they fail, any sequencer will try as well.
182
211
  */
183
- protected considerInvalidatingBlock(syncedTo: NonNullable<Awaited<ReturnType<Sequencer['getChainTip']>>>, currentSlot: bigint, ourValidatorAddresses: EthAddress[], publisher: SequencerPublisher): Promise<void>;
212
+ protected considerInvalidatingBlock(syncedTo: NonNullable<Awaited<ReturnType<Sequencer['checkSync']>>>, currentSlot: bigint): Promise<void>;
184
213
  private getSlotStartBuildTimestamp;
185
214
  private getSecondsIntoSlot;
186
215
  get aztecSlotDuration(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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"}
@@ -7,12 +7,14 @@ function _ts_decorate(decorators, target, key, desc) {
7
7
  import { BLOBS_PER_BLOCK, FIELDS_PER_BLOB, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
8
8
  import { FormattedViemError, NoCommitteeError } from '@aztec/ethereum';
9
9
  import { omit, pick } from '@aztec/foundation/collection';
10
+ import { randomInt } from '@aztec/foundation/crypto';
10
11
  import { EthAddress } from '@aztec/foundation/eth-address';
11
12
  import { Signature } from '@aztec/foundation/eth-signature';
12
13
  import { Fr } from '@aztec/foundation/fields';
13
14
  import { createLogger } from '@aztec/foundation/log';
14
15
  import { RunningPromise } from '@aztec/foundation/running-promise';
15
16
  import { Timer } from '@aztec/foundation/timer';
17
+ import { unfreeze } from '@aztec/foundation/types';
16
18
  import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
17
19
  import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
18
20
  import { Gas } from '@aztec/stdlib/gas';
@@ -65,6 +67,7 @@ export { SequencerState };
65
67
  metrics;
66
68
  lastBlockPublished;
67
69
  governanceProposerPayload;
70
+ /** The last slot for which we attempted to vote when sync failed, to prevent duplicate attempts. */ lastSlotForVoteWhenSyncFailed;
68
71
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
69
72
  enforceTimeTable;
70
73
  // This shouldn't be here as this gets re-created each time we build/propose a block.
@@ -140,7 +143,7 @@ export { SequencerState };
140
143
  /**
141
144
  * Starts the sequencer and moves to IDLE state.
142
145
  */ start() {
143
- this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
146
+ this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.pollingIntervalMs);
144
147
  this.setState(SequencerState.IDLE, undefined, {
145
148
  force: true
146
149
  });
@@ -176,21 +179,28 @@ export { SequencerState };
176
179
  * - Collect attestations for the block
177
180
  * - Submit block
178
181
  * - If our block for some reason is not included, revert the state
179
- */ async doRealWork() {
182
+ */ async work() {
180
183
  this.setState(SequencerState.SYNCHRONIZING, undefined);
181
- // Check all components are synced to latest as seen by the archiver
182
- const syncedTo = await this.getChainTip();
183
- // Do not go forward with new block if the previous one has not been mined and processed
184
+ const { slot, ts, now } = this.epochCache.getEpochAndSlotInNextL1Slot();
185
+ // Check we have not already published a block for this slot (cheapest check)
186
+ if (this.lastBlockPublished && this.lastBlockPublished.header.getSlot() >= slot) {
187
+ this.log.debug(`Cannot propose block at next L2 slot ${slot} since that slot was taken by our own block ${this.lastBlockPublished.number}`);
188
+ return;
189
+ }
190
+ // Check all components are synced to latest as seen by the archiver (queries all subsystems)
191
+ const syncedTo = await this.checkSync({
192
+ ts,
193
+ slot
194
+ });
184
195
  if (!syncedTo) {
196
+ await this.tryVoteWhenSyncFails({
197
+ slot,
198
+ ts
199
+ });
185
200
  return;
186
201
  }
187
202
  const chainTipArchive = syncedTo.archive;
188
203
  const newBlockNumber = syncedTo.blockNumber + 1;
189
- const { slot, ts, now } = this.epochCache.getEpochAndSlotInNextL1Slot();
190
- this.setState(SequencerState.PROPOSER_CHECK, slot);
191
- // Check that the archiver and dependencies have synced to the previous L1 slot at least
192
- // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
193
- // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
194
204
  const syncLogData = {
195
205
  now,
196
206
  syncedToL1Ts: syncedTo.l1Timestamp,
@@ -201,67 +211,35 @@ export { SequencerState };
201
211
  newBlockNumber,
202
212
  isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
203
213
  };
204
- if (syncedTo.l1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration) < ts) {
205
- this.log.debug(`Cannot propose block ${newBlockNumber} at next L2 slot ${slot} due to pending sync from L1`, syncLogData);
206
- return;
207
- }
208
- // Check that the slot is not taken by a block already
214
+ // Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
209
215
  if (syncedTo.block && syncedTo.block.header.getSlot() >= slot) {
210
- this.log.debug(`Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`, {
216
+ this.log.warn(`Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`, {
211
217
  ...syncLogData,
212
218
  block: syncedTo.block.header.toInspect()
213
219
  });
214
220
  return;
215
221
  }
216
- // Or that we haven't published it ourselves
217
- if (this.lastBlockPublished && this.lastBlockPublished.header.getSlot() >= slot) {
218
- this.log.debug(`Cannot propose block at next L2 slot ${slot} since that slot was taken by our own block ${this.lastBlockPublished.number}`, {
219
- ...syncLogData,
220
- block: this.lastBlockPublished.header.toInspect()
221
- });
222
- return;
223
- }
224
222
  // Check that we are a proposer for the next slot
225
- let proposerInNextSlot;
226
- try {
227
- proposerInNextSlot = await this.epochCache.getProposerAttesterAddressInNextSlot();
228
- } catch (e) {
229
- if (e instanceof NoCommitteeError) {
230
- this.log.warn(`Cannot propose block ${newBlockNumber} at next L2 slot ${slot} since the committee does not exist on L1`);
231
- return;
232
- }
233
- }
234
- // If get proposer in next slot is undefined, then the committee is empty and anyone may propose.
235
- // If the committee is defined and not empty, but none of our validators are the proposer, then stop.
236
- const validatorAddresses = this.validatorClient.getValidatorAddresses();
237
- if (proposerInNextSlot !== undefined && !validatorAddresses.some((addr)=>addr.equals(proposerInNextSlot))) {
238
- this.log.debug(`Cannot propose block ${newBlockNumber} since we are not a proposer`, {
239
- us: validatorAddresses,
240
- proposer: proposerInNextSlot,
241
- ...syncLogData
242
- });
243
- // If the pending chain is invalid, we may need to invalidate the block if no one else is doing it.
244
- if (!syncedTo.pendingChainValidationStatus.valid) {
245
- // We pass i undefined here to get any available publisher.
246
- const { publisher } = await this.publisherFactory.create(undefined);
247
- await this.considerInvalidatingBlock(syncedTo, slot, validatorAddresses, publisher);
248
- }
223
+ this.setState(SequencerState.PROPOSER_CHECK, slot);
224
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
225
+ // If we are not a proposer, check if we should invalidate a invalid block, and bail
226
+ if (!canPropose) {
227
+ await this.considerInvalidatingBlock(syncedTo, slot);
249
228
  return;
250
229
  }
251
- // Check with the rollup if we can indeed propose at the next L2 slot. This check should not fail
252
- // if all the previous checks are good, but we do it just in case.
253
- const proposerAddressInNextSlot = proposerInNextSlot ?? EthAddress.ZERO;
254
230
  // We now need to get ourselves a publisher.
255
231
  // The returned attestor will be the one we provided if we provided one.
256
232
  // Otherwise it will be a valid attestor for the returned publisher.
257
- const { attestorAddress, publisher } = await this.publisherFactory.create(proposerInNextSlot);
233
+ const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
258
234
  this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
259
235
  this.publisher = publisher;
260
236
  const coinbase = this.validatorClient.getCoinbaseForAttestor(attestorAddress);
261
237
  const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(attestorAddress);
262
238
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
263
239
  const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
264
- const canProposeCheck = await publisher.canProposeAtNextEthBlock(chainTipArchive, proposerAddressInNextSlot, invalidateBlock);
240
+ // Check with the rollup if we can indeed propose at the next L2 slot. This check should not fail
241
+ // if all the previous checks are good, but we do it just in case.
242
+ const canProposeCheck = await publisher.canProposeAtNextEthBlock(chainTipArchive, proposer ?? EthAddress.ZERO, invalidateBlock);
265
243
  if (canProposeCheck === undefined) {
266
244
  this.log.warn(`Cannot propose block ${newBlockNumber} at slot ${slot} due to failed rollup contract check`, syncLogData);
267
245
  this.emit('proposer-rollup-check-failed', {
@@ -291,43 +269,46 @@ export { SequencerState };
291
269
  });
292
270
  return;
293
271
  }
294
- this.log.debug(`Can propose block ${newBlockNumber} at slot ${slot}` + (proposerInNextSlot ? ` as ${proposerInNextSlot}` : ''), {
295
- ...syncLogData,
296
- validatorAddresses
272
+ this.log.debug(`Can propose block ${newBlockNumber} at slot ${slot} as ${proposer}`, {
273
+ ...syncLogData
297
274
  });
298
275
  const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(newBlockNumber, coinbase, feeRecipient, slot);
299
- const { timestamp } = newGlobalVariables;
300
- const signerFn = (msg)=>this.validatorClient.signWithAddress(attestorAddress, msg).then((s)=>s.toString());
301
- const enqueueGovernanceSignalPromise = this.governanceProposerPayload && !this.governanceProposerPayload.isZero() ? publisher.enqueueGovernanceCastSignal(this.governanceProposerPayload, slot, timestamp, attestorAddress, signerFn).catch((err)=>{
302
- this.log.error(`Error enqueuing governance vote`, err, {
303
- blockNumber: newBlockNumber,
304
- slot
305
- });
306
- return false;
307
- }) : Promise.resolve(false);
308
- const enqueueSlashingActionsPromise = this.slasherClient ? this.slasherClient.getProposerActions(slot).then((actions)=>publisher.enqueueSlashingActions(actions, slot, timestamp, attestorAddress, signerFn)).catch((err)=>{
309
- this.log.error(`Error enqueuing slashing actions`, err, {
310
- blockNumber: newBlockNumber,
311
- slot
312
- });
313
- return false;
314
- }) : Promise.resolve(false);
276
+ // Enqueue governance and slashing votes (returns promises that will be awaited later)
277
+ const votesPromises = this.enqueueGovernanceAndSlashingVotes(publisher, attestorAddress, slot, newGlobalVariables.timestamp);
278
+ // Enqueues block invalidation
315
279
  if (invalidateBlock && !this.config.skipInvalidateBlockAsProposer) {
316
280
  publisher.enqueueInvalidateBlock(invalidateBlock);
317
281
  }
282
+ // Actual block building
318
283
  this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
319
- this.metrics.incOpenSlot(slot, proposerAddressInNextSlot.toString());
284
+ const block = await this.tryBuildBlockAndEnqueuePublish(slot, proposer, newBlockNumber, publisher, newGlobalVariables, chainTipArchive, invalidateBlock);
285
+ // Wait until the voting promises have resolved, so all requests are enqueued
286
+ await Promise.all(votesPromises);
287
+ // And send the tx to L1
288
+ const l1Response = await publisher.sendRequests();
289
+ const proposedBlock = l1Response?.successfulActions.find((a)=>a === 'propose');
290
+ if (proposedBlock) {
291
+ this.lastBlockPublished = block;
292
+ this.emit('block-published', {
293
+ blockNumber: newBlockNumber,
294
+ slot: Number(slot)
295
+ });
296
+ await this.metrics.incFilledSlot(publisher.getSenderAddress().toString(), coinbase);
297
+ } else if (block) {
298
+ this.emit('block-publish-failed', l1Response ?? {});
299
+ }
300
+ this.setState(SequencerState.IDLE, undefined);
301
+ }
302
+ /** Tries building a block proposal, and if successful, enqueues it for publishing. */ async tryBuildBlockAndEnqueuePublish(slot, proposer, newBlockNumber, publisher, newGlobalVariables, chainTipArchive, invalidateBlock) {
303
+ this.metrics.incOpenSlot(slot, (proposer ?? EthAddress.ZERO).toString());
320
304
  this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
321
- proposer: proposerInNextSlot?.toString(),
322
- coinbase,
305
+ proposer,
323
306
  publisher: publisher.getSenderAddress(),
324
- feeRecipient,
325
307
  globalVariables: newGlobalVariables.toInspect(),
326
308
  chainTipArchive,
327
309
  blockNumber: newBlockNumber,
328
310
  slot
329
311
  });
330
- // If I created a "partial" header here that should make our job much easier.
331
312
  const proposalHeader = ProposedBlockHeader.from({
332
313
  ...newGlobalVariables,
333
314
  timestamp: newGlobalVariables.timestamp,
@@ -342,7 +323,7 @@ export { SequencerState };
342
323
  // and also we may need to fetch more if we don't have enough valid txs.
343
324
  const pendingTxs = this.p2pClient.iteratePendingTxs();
344
325
  try {
345
- block = await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader, newGlobalVariables, proposerInNextSlot, invalidateBlock, publisher);
326
+ block = await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader, newGlobalVariables, proposer, invalidateBlock, publisher);
346
327
  } catch (err) {
347
328
  this.emit('block-build-failed', {
348
329
  reason: err.message
@@ -367,27 +348,11 @@ export { SequencerState };
367
348
  availableTxs: pendingTxCount
368
349
  });
369
350
  }
370
- await Promise.all([
371
- enqueueGovernanceSignalPromise,
372
- enqueueSlashingActionsPromise
373
- ]);
374
- const l1Response = await publisher.sendRequests();
375
- const proposedBlock = l1Response?.successfulActions.find((a)=>a === 'propose');
376
- if (proposedBlock) {
377
- this.lastBlockPublished = block;
378
- this.emit('block-published', {
379
- blockNumber: newBlockNumber,
380
- slot: Number(slot)
381
- });
382
- await this.metrics.incFilledSlot(publisher.getSenderAddress().toString(), coinbase);
383
- } else if (block) {
384
- this.emit('block-publish-failed', l1Response ?? {});
385
- }
386
- this.setState(SequencerState.IDLE, undefined);
351
+ return block;
387
352
  }
388
- async work() {
353
+ async safeWork() {
389
354
  try {
390
- await this.doRealWork();
355
+ await this.work();
391
356
  } catch (err) {
392
357
  if (err instanceof SequencerTooSlowError) {
393
358
  // Log as warn only if we had to abort halfway through the block proposal
@@ -573,7 +538,14 @@ export { SequencerState };
573
538
  const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
574
539
  collectedAttestationsCount = attestations.length;
575
540
  // note: the smart contract requires that the signatures are provided in the order of the committee
576
- return orderAttestations(attestations, committee);
541
+ const sorted = orderAttestations(attestations, committee);
542
+ if (this.config.injectFakeAttestation) {
543
+ const nonEmpty = sorted.filter((a)=>!a.signature.isEmpty());
544
+ const randomIndex = randomInt(nonEmpty.length);
545
+ this.log.warn(`Injecting fake attestation in block ${block.number}`);
546
+ unfreeze(nonEmpty[randomIndex]).signature = Signature.random();
547
+ }
548
+ return sorted;
577
549
  } catch (err) {
578
550
  if (err && err instanceof AttestationTimeoutError) {
579
551
  collectedAttestationsCount = err.collectedCount;
@@ -603,8 +575,7 @@ export { SequencerState };
603
575
  /**
604
576
  * Returns whether all dependencies have caught up.
605
577
  * We don't check against the previous block submitted since it may have been reorg'd out.
606
- * @returns Boolean indicating if our dependencies are synced to the latest block.
607
- */ async getChainTip() {
578
+ */ async checkSync(args) {
608
579
  const syncedBlocks = await Promise.all([
609
580
  this.worldState.status().then(({ syncSummary })=>({
610
581
  number: syncSummary.latestBlockNumber,
@@ -620,32 +591,30 @@ export { SequencerState };
620
591
  // The archiver reports 'undefined' hash for the genesis block
621
592
  // because it doesn't have access to world state to compute it (facepalm)
622
593
  const result = l2BlockSource.hash === undefined ? worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0 : worldState.hash === l2BlockSource.hash && p2p.hash === l2BlockSource.hash && l1ToL2MessageSource.hash === l2BlockSource.hash;
623
- const logData = {
624
- worldState,
625
- l2BlockSource,
626
- p2p,
627
- l1ToL2MessageSource
628
- };
629
- this.log.debug(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, logData);
630
594
  if (!result) {
595
+ this.log.debug(`Sequencer sync check failed`, {
596
+ worldState,
597
+ l2BlockSource,
598
+ p2p,
599
+ l1ToL2MessageSource
600
+ });
631
601
  return undefined;
632
602
  }
603
+ // Check that the archiver and dependencies have synced to the previous L1 slot at least
604
+ // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
605
+ // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
606
+ const { slot, ts } = args;
607
+ if (l1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration) < ts) {
608
+ this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
609
+ slot,
610
+ ts,
611
+ l1Timestamp
612
+ });
613
+ return undefined;
614
+ }
615
+ // Special case for genesis state
633
616
  const blockNumber = worldState.number;
634
- if (blockNumber >= INITIAL_L2_BLOCK_NUM) {
635
- const block = await this.l2BlockSource.getBlock(blockNumber);
636
- if (!block) {
637
- // this shouldn't really happen because a moment ago we checked that all components were in sync
638
- this.log.warn(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`, logData);
639
- return undefined;
640
- }
641
- return {
642
- block,
643
- blockNumber: block.number,
644
- archive: block.archive.root,
645
- l1Timestamp,
646
- pendingChainValidationStatus
647
- };
648
- } else {
617
+ if (blockNumber < INITIAL_L2_BLOCK_NUM) {
649
618
  const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
650
619
  return {
651
620
  blockNumber: INITIAL_L2_BLOCK_NUM - 1,
@@ -654,20 +623,170 @@ export { SequencerState };
654
623
  pendingChainValidationStatus
655
624
  };
656
625
  }
626
+ const block = await this.l2BlockSource.getBlock(blockNumber);
627
+ if (!block) {
628
+ // this shouldn't really happen because a moment ago we checked that all components were in sync
629
+ this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
630
+ return undefined;
631
+ }
632
+ return {
633
+ block,
634
+ blockNumber: block.number,
635
+ archive: block.archive.root,
636
+ l1Timestamp,
637
+ pendingChainValidationStatus
638
+ };
639
+ }
640
+ /**
641
+ * Enqueues governance and slashing votes with the publisher. Does not block.
642
+ * @param publisher - The publisher to enqueue votes with
643
+ * @param attestorAddress - The attestor address to use for signing
644
+ * @param slot - The slot number
645
+ * @param timestamp - The timestamp for the votes
646
+ * @param context - Optional context for logging (e.g., block number)
647
+ * @returns A tuple of [governanceEnqueued, slashingEnqueued]
648
+ */ enqueueGovernanceAndSlashingVotes(publisher, attestorAddress, slot, timestamp) {
649
+ try {
650
+ const signerFn = (msg)=>this.validatorClient.signWithAddress(attestorAddress, msg).then((s)=>s.toString());
651
+ const enqueueGovernancePromise = this.governanceProposerPayload && !this.governanceProposerPayload.isZero() ? publisher.enqueueGovernanceCastSignal(this.governanceProposerPayload, slot, timestamp, attestorAddress, signerFn).catch((err)=>{
652
+ this.log.error(`Error enqueuing governance vote`, err, {
653
+ slot
654
+ });
655
+ return false;
656
+ }) : undefined;
657
+ const enqueueSlashingPromise = this.slasherClient ? this.slasherClient.getProposerActions(slot).then((actions)=>publisher.enqueueSlashingActions(actions, slot, timestamp, attestorAddress, signerFn)).catch((err)=>{
658
+ this.log.error(`Error enqueuing slashing actions`, err, {
659
+ slot
660
+ });
661
+ return false;
662
+ }) : undefined;
663
+ return [
664
+ enqueueGovernancePromise,
665
+ enqueueSlashingPromise
666
+ ];
667
+ } catch (err) {
668
+ this.log.error(`Error enqueueing governance and slashing votes`, err);
669
+ return [
670
+ undefined,
671
+ undefined
672
+ ];
673
+ }
674
+ }
675
+ /**
676
+ * Checks if we are the proposer for the next slot.
677
+ * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
678
+ */ async checkCanPropose(slot) {
679
+ let proposer;
680
+ try {
681
+ proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
682
+ } catch (e) {
683
+ if (e instanceof NoCommitteeError) {
684
+ this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
685
+ return [
686
+ false,
687
+ undefined
688
+ ];
689
+ }
690
+ this.log.error(`Error getting proposer for slot ${slot}`, e);
691
+ return [
692
+ false,
693
+ undefined
694
+ ];
695
+ }
696
+ // If proposer is undefined, then the committee is empty and anyone may propose
697
+ if (proposer === undefined) {
698
+ return [
699
+ true,
700
+ undefined
701
+ ];
702
+ }
703
+ const validatorAddresses = this.validatorClient.getValidatorAddresses();
704
+ const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
705
+ if (!weAreProposer) {
706
+ this.log.debug(`Cannot propose at slot ${slot} since we are not a proposer`, {
707
+ validatorAddresses,
708
+ proposer
709
+ });
710
+ return [
711
+ false,
712
+ proposer
713
+ ];
714
+ }
715
+ return [
716
+ true,
717
+ proposer
718
+ ];
719
+ }
720
+ /**
721
+ * Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
722
+ * This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
723
+ */ async tryVoteWhenSyncFails(args) {
724
+ const { slot, ts } = args;
725
+ // Prevent duplicate attempts in the same slot
726
+ if (this.lastSlotForVoteWhenSyncFailed === slot) {
727
+ this.log.debug(`Already attempted to vote in slot ${slot} (skipping)`);
728
+ return;
729
+ }
730
+ // Check if we're past the max time for initializing a proposal
731
+ const secondsIntoSlot = this.getSecondsIntoSlot(slot);
732
+ const maxAllowedTime = this.timetable.getMaxAllowedTime(SequencerState.INITIALIZING_PROPOSAL);
733
+ // If we haven't exceeded the time limit for initializing a proposal, don't proceed with voting
734
+ // We use INITIALIZING_PROPOSAL time limit because if we're past that, we can't build a block anyway
735
+ if (maxAllowedTime === undefined || secondsIntoSlot <= maxAllowedTime) {
736
+ this.log.trace(`Not attempting to vote since there is still for block building`, {
737
+ secondsIntoSlot,
738
+ maxAllowedTime
739
+ });
740
+ return;
741
+ }
742
+ this.log.debug(`Sync for slot ${slot} failed, checking for voting opportunities`, {
743
+ secondsIntoSlot,
744
+ maxAllowedTime
745
+ });
746
+ // Check if we're a proposer or proposal is open
747
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
748
+ if (!canPropose) {
749
+ this.log.debug(`Cannot vote in slot ${slot} since we are not a proposer`, {
750
+ slot,
751
+ proposer
752
+ });
753
+ return;
754
+ }
755
+ // Mark this slot as attempted
756
+ this.lastSlotForVoteWhenSyncFailed = slot;
757
+ // Get a publisher for voting
758
+ const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
759
+ this.log.debug(`Attempting to vote despite sync failure at slot ${slot}`, {
760
+ attestorAddress,
761
+ slot
762
+ });
763
+ // Enqueue governance and slashing votes using the shared helper method
764
+ const votesPromises = this.enqueueGovernanceAndSlashingVotes(publisher, attestorAddress, slot, ts);
765
+ await Promise.all(votesPromises);
766
+ if (votesPromises.every((p)=>!p)) {
767
+ this.log.debug(`No votes to enqueue for slot ${slot}`);
768
+ return;
769
+ }
770
+ this.log.info(`Voting in slot ${slot} despite sync failure`, {
771
+ slot
772
+ });
773
+ await publisher.sendRequests();
657
774
  }
658
775
  /**
659
776
  * Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
660
777
  * has been there without being invalidated and whether the sequencer is in the committee or not. We always
661
778
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
662
779
  * and if they fail, any sequencer will try as well.
663
- */ async considerInvalidatingBlock(syncedTo, currentSlot, ourValidatorAddresses, publisher) {
780
+ */ async considerInvalidatingBlock(syncedTo, currentSlot) {
664
781
  const { pendingChainValidationStatus, l1Timestamp } = syncedTo;
665
782
  if (pendingChainValidationStatus.valid) {
666
783
  return;
667
784
  }
785
+ const { publisher } = await this.publisherFactory.create(undefined);
668
786
  const invalidBlockNumber = pendingChainValidationStatus.block.blockNumber;
669
787
  const invalidBlockTimestamp = pendingChainValidationStatus.block.timestamp;
670
788
  const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(invalidBlockTimestamp);
789
+ const ourValidatorAddresses = this.validatorClient.getValidatorAddresses();
671
790
  const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
672
791
  const logData = {
673
792
  invalidL1Timestamp: invalidBlockTimestamp,
@@ -713,7 +832,7 @@ export { SequencerState };
713
832
  }
714
833
  _ts_decorate([
715
834
  trackSpan('Sequencer.work')
716
- ], Sequencer.prototype, "work", null);
835
+ ], Sequencer.prototype, "safeWork", null);
717
836
  _ts_decorate([
718
837
  trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, _proposalHeader, newGlobalVariables)=>({
719
838
  [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber