@aztec/sequencer-client 0.0.1-commit.6b113946b → 0.0.1-commit.6bd18f1aa

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.
Files changed (32) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +8 -13
  4. package/dest/global_variable_builder/global_builder.d.ts +13 -7
  5. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  6. package/dest/global_variable_builder/global_builder.js +22 -22
  7. package/dest/global_variable_builder/index.d.ts +2 -2
  8. package/dest/global_variable_builder/index.d.ts.map +1 -1
  9. package/dest/publisher/config.d.ts +13 -1
  10. package/dest/publisher/config.d.ts.map +1 -1
  11. package/dest/publisher/config.js +17 -2
  12. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
  13. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  14. package/dest/publisher/sequencer-publisher-factory.js +2 -2
  15. package/dest/publisher/sequencer-publisher.d.ts +10 -5
  16. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher.js +46 -33
  18. package/dest/sequencer/checkpoint_proposal_job.js +1 -1
  19. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  20. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  21. package/dest/sequencer/checkpoint_voter.js +2 -5
  22. package/dest/sequencer/sequencer.js +3 -3
  23. package/package.json +27 -27
  24. package/src/client/sequencer-client.ts +11 -17
  25. package/src/global_variable_builder/global_builder.ts +22 -24
  26. package/src/global_variable_builder/index.ts +1 -1
  27. package/src/publisher/config.ts +32 -0
  28. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  29. package/src/publisher/sequencer-publisher.ts +37 -39
  30. package/src/sequencer/checkpoint_proposal_job.ts +1 -1
  31. package/src/sequencer/checkpoint_voter.ts +1 -12
  32. package/src/sequencer/sequencer.ts +3 -3
@@ -4,6 +4,8 @@ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l
4
4
  import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
 
7
+ import { parseEther } from 'viem';
8
+
7
9
  /** Configuration of the transaction publisher. */
8
10
  export type TxSenderConfig = L1ReaderConfig & {
9
11
  /** The private key to be used by the publisher. */
@@ -50,13 +52,37 @@ export type PublisherConfig = L1TxUtilsConfig &
50
52
  publisherForwarderAddress?: EthAddress;
51
53
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
52
54
  l1TxFailedStore?: string;
55
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
56
+ publisherFundingThreshold?: bigint;
57
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
58
+ publisherFundingAmount?: bigint;
53
59
  };
54
60
 
61
+ /** Shared config mappings for publisher funding, used by both sequencer and prover publisher configs. */
62
+ const publisherFundingConfigMappings = {
63
+ publisherFundingThreshold: {
64
+ env: 'PUBLISHER_FUNDING_THRESHOLD' as const,
65
+ description:
66
+ 'Min ETH balance below which a publisher gets funded. Specified in ether (e.g. 0.1). Unset = funding disabled.',
67
+ parseEnv: (val: string) => parseEther(val),
68
+ },
69
+ publisherFundingAmount: {
70
+ env: 'PUBLISHER_FUNDING_AMOUNT' as const,
71
+ description:
72
+ 'Amount of ETH to send when funding a publisher. Specified in ether (e.g. 0.5). Unset = funding disabled.',
73
+ parseEnv: (val: string) => parseEther(val),
74
+ },
75
+ };
76
+
55
77
  export type ProverPublisherConfig = L1TxUtilsConfig &
56
78
  BlobClientConfig & {
57
79
  fishermanMode?: boolean;
58
80
  proverPublisherAllowInvalidStates?: boolean;
59
81
  proverPublisherForwarderAddress?: EthAddress;
82
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
83
+ publisherFundingThreshold?: bigint;
84
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
85
+ publisherFundingAmount?: bigint;
60
86
  };
61
87
 
62
88
  export type SequencerPublisherConfig = L1TxUtilsConfig &
@@ -66,6 +92,10 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
66
92
  sequencerPublisherForwarderAddress?: EthAddress;
67
93
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
68
94
  l1TxFailedStore?: string;
95
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
96
+ publisherFundingThreshold?: bigint;
97
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
98
+ publisherFundingAmount?: bigint;
69
99
  };
70
100
 
71
101
  export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
@@ -142,6 +172,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
142
172
  env: 'L1_TX_FAILED_STORE',
143
173
  description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
144
174
  },
175
+ ...publisherFundingConfigMappings,
145
176
  };
146
177
 
147
178
  export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
@@ -163,4 +194,5 @@ export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherCo
163
194
  description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
164
195
  parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
165
196
  },
197
+ ...publisherFundingConfigMappings,
166
198
  };
@@ -117,8 +117,8 @@ export class SequencerPublisherFactory {
117
117
  };
118
118
  }
119
119
 
120
- /** Interrupts all publishers managed by this factory. Used during sequencer shutdown. */
121
- public interruptAll(): void {
122
- this.deps.publisherManager.interrupt();
120
+ /** Stops all publishers managed by this factory. Used during sequencer shutdown. */
121
+ public async stopAll(): Promise<void> {
122
+ await this.deps.publisherManager.stop();
123
123
  }
124
124
  }
@@ -42,6 +42,7 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
42
42
  import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
43
43
  import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
44
44
  import type { Checkpoint } from '@aztec/stdlib/checkpoint';
45
+ import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
45
46
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
46
47
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
47
48
  import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
@@ -134,6 +135,7 @@ export class SequencerPublisher {
134
135
  protected log: Logger;
135
136
  protected ethereumSlotDuration: bigint;
136
137
  protected aztecSlotDuration: bigint;
138
+ private dateProvider: DateProvider;
137
139
 
138
140
  private blobClient: BlobClientInterface;
139
141
 
@@ -187,6 +189,7 @@ export class SequencerPublisher {
187
189
  this.log = deps.log ?? createLogger('sequencer:publisher');
188
190
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
189
191
  this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
192
+ this.dateProvider = deps.dateProvider;
190
193
  this.epochCache = deps.epochCache;
191
194
  this.lastActions = deps.lastActions;
192
195
 
@@ -612,9 +615,10 @@ export class SequencerPublisher {
612
615
 
613
616
  const pipelined = opts.pipelined ?? this.epochCache.isProposerPipeliningEnabled();
614
617
  const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
618
+ const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
615
619
 
616
620
  return this.rollupContract
617
- .canProposeAt(tipArchive.toBuffer(), msgSender.toString(), this.ethereumSlotDuration, slotOffset, {
621
+ .canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
618
622
  forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
619
623
  })
620
624
  .catch(err => {
@@ -652,7 +656,7 @@ export class SequencerPublisher {
652
656
  flags,
653
657
  ] as const;
654
658
 
655
- const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
659
+ const ts = this.getSimulationTimestamp(header.slotNumber);
656
660
  const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
657
661
  opts?.forcePendingCheckpointNumber,
658
662
  );
@@ -675,7 +679,7 @@ export class SequencerPublisher {
675
679
  data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
676
680
  from: MULTI_CALL_3_ADDRESS,
677
681
  },
678
- { time: ts + 1n },
682
+ { time: ts },
679
683
  stateOverrides,
680
684
  );
681
685
  this.log.debug(`Simulated validateHeader`);
@@ -816,10 +820,7 @@ export class SequencerPublisher {
816
820
  attestationsAndSigners: CommitteeAttestationsAndSigners,
817
821
  attestationsAndSignersSignature: Signature,
818
822
  options: { forcePendingCheckpointNumber?: CheckpointNumber },
819
- ): Promise<bigint> {
820
- // Anchor the simulation timestamp to the checkpoint's own slot start time
821
- // rather than the current L1 block timestamp, which may overshoot into the next slot if the build ran late.
822
- const ts = checkpoint.header.timestamp;
823
+ ): Promise<void> {
823
824
  const blobFields = checkpoint.toBlobFields();
824
825
  const blobs = await getBlobsPerL1Block(blobFields);
825
826
  const blobInput = getPrefixedEthBlobCommitments(blobs);
@@ -838,13 +839,11 @@ export class SequencerPublisher {
838
839
  blobInput,
839
840
  ] as const;
840
841
 
841
- await this.simulateProposeTx(args, ts, options);
842
- return ts;
842
+ await this.simulateProposeTx(args, options);
843
843
  }
844
844
 
845
845
  private async enqueueCastSignalHelper(
846
846
  slotNumber: SlotNumber,
847
- timestamp: bigint,
848
847
  signalType: GovernanceSignalAction,
849
848
  payload: EthAddress,
850
849
  base: IEmpireBase,
@@ -923,13 +922,17 @@ export class SequencerPublisher {
923
922
  });
924
923
 
925
924
  const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
925
+ const timestamp = this.getSimulationTimestamp(slotNumber);
926
926
 
927
927
  try {
928
928
  await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
929
929
  this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
930
930
  } catch (err) {
931
931
  const viemError = formatViemError(err);
932
- this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError);
932
+ this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
933
+ simulationTimestamp: timestamp,
934
+ l1BlockNumber,
935
+ });
933
936
  this.backupFailedTx({
934
937
  id: keccak256(request.data!),
935
938
  failureType: 'simulation',
@@ -992,19 +995,16 @@ export class SequencerPublisher {
992
995
  /**
993
996
  * Enqueues a governance castSignal transaction to cast a signal for a given slot number.
994
997
  * @param slotNumber - The slot number to cast a signal for.
995
- * @param timestamp - The timestamp of the slot to cast a signal for.
996
998
  * @returns True if the signal was successfully enqueued, false otherwise.
997
999
  */
998
1000
  public enqueueGovernanceCastSignal(
999
1001
  governancePayload: EthAddress,
1000
1002
  slotNumber: SlotNumber,
1001
- timestamp: bigint,
1002
1003
  signerAddress: EthAddress,
1003
1004
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
1004
1005
  ): Promise<boolean> {
1005
1006
  return this.enqueueCastSignalHelper(
1006
1007
  slotNumber,
1007
- timestamp,
1008
1008
  'governance-signal',
1009
1009
  governancePayload,
1010
1010
  this.govProposerContract,
@@ -1017,7 +1017,6 @@ export class SequencerPublisher {
1017
1017
  public async enqueueSlashingActions(
1018
1018
  actions: ProposerSlashAction[],
1019
1019
  slotNumber: SlotNumber,
1020
- timestamp: bigint,
1021
1020
  signerAddress: EthAddress,
1022
1021
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
1023
1022
  ): Promise<boolean> {
@@ -1038,7 +1037,6 @@ export class SequencerPublisher {
1038
1037
  });
1039
1038
  await this.enqueueCastSignalHelper(
1040
1039
  slotNumber,
1041
- timestamp,
1042
1040
  'empire-slashing-signal',
1043
1041
  action.payload,
1044
1042
  this.slashingProposerContract,
@@ -1057,7 +1055,6 @@ export class SequencerPublisher {
1057
1055
  (receipt: TransactionReceipt) =>
1058
1056
  !!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
1059
1057
  slotNumber,
1060
- timestamp,
1061
1058
  );
1062
1059
  break;
1063
1060
  }
@@ -1075,7 +1072,6 @@ export class SequencerPublisher {
1075
1072
  request,
1076
1073
  (receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
1077
1074
  slotNumber,
1078
- timestamp,
1079
1075
  );
1080
1076
  break;
1081
1077
  }
@@ -1099,7 +1095,6 @@ export class SequencerPublisher {
1099
1095
  request,
1100
1096
  (receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs),
1101
1097
  slotNumber,
1102
- timestamp,
1103
1098
  );
1104
1099
  break;
1105
1100
  }
@@ -1121,7 +1116,6 @@ export class SequencerPublisher {
1121
1116
  request,
1122
1117
  (receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs),
1123
1118
  slotNumber,
1124
- timestamp,
1125
1119
  );
1126
1120
  break;
1127
1121
  }
@@ -1157,15 +1151,13 @@ export class SequencerPublisher {
1157
1151
  feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
1158
1152
  };
1159
1153
 
1160
- let ts: bigint;
1161
-
1162
1154
  try {
1163
1155
  // @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
1164
1156
  // This means that we can avoid the simulation issues in later checks.
1165
1157
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
1166
1158
  // make time consistency checks break.
1167
1159
  // TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
1168
- ts = await this.validateCheckpointForSubmission(
1160
+ await this.validateCheckpointForSubmission(
1169
1161
  checkpoint,
1170
1162
  attestationsAndSigners,
1171
1163
  attestationsAndSignersSignature,
@@ -1181,7 +1173,7 @@ export class SequencerPublisher {
1181
1173
  }
1182
1174
 
1183
1175
  this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
1184
- await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
1176
+ await this.addProposeTx(checkpoint, proposeTxArgs, opts);
1185
1177
  }
1186
1178
 
1187
1179
  public enqueueInvalidateCheckpoint(
@@ -1224,8 +1216,8 @@ export class SequencerPublisher {
1224
1216
  request: L1TxRequest,
1225
1217
  checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
1226
1218
  slotNumber: SlotNumber,
1227
- timestamp: bigint,
1228
1219
  ) {
1220
+ const timestamp = this.getSimulationTimestamp(slotNumber);
1229
1221
  const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
1230
1222
  if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
1231
1223
  this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
@@ -1241,8 +1233,9 @@ export class SequencerPublisher {
1241
1233
 
1242
1234
  let gasUsed: bigint;
1243
1235
  const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
1236
+
1244
1237
  try {
1245
- ({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi)); // TODO(palla/slash): Check the timestamp logic
1238
+ ({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
1246
1239
  this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
1247
1240
  } catch (err) {
1248
1241
  const viemError = formatViemError(err, simulateAbi);
@@ -1311,7 +1304,6 @@ export class SequencerPublisher {
1311
1304
 
1312
1305
  private async prepareProposeTx(
1313
1306
  encodedData: L1ProcessArgs,
1314
- timestamp: bigint,
1315
1307
  options: { forcePendingCheckpointNumber?: CheckpointNumber },
1316
1308
  ) {
1317
1309
  const kzg = Blob.getViemKzgInstance();
@@ -1384,7 +1376,7 @@ export class SequencerPublisher {
1384
1376
  blobInput,
1385
1377
  ] as const;
1386
1378
 
1387
- const { rollupData, simulationResult } = await this.simulateProposeTx(args, timestamp, options);
1379
+ const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
1388
1380
 
1389
1381
  return { args, blobEvaluationGas, rollupData, simulationResult };
1390
1382
  }
@@ -1392,7 +1384,6 @@ export class SequencerPublisher {
1392
1384
  /**
1393
1385
  * Simulates the propose tx with eth_simulateV1
1394
1386
  * @param args - The propose tx args
1395
- * @param timestamp - The timestamp to simulate proposal at
1396
1387
  * @returns The simulation result
1397
1388
  */
1398
1389
  private async simulateProposeTx(
@@ -1409,7 +1400,6 @@ export class SequencerPublisher {
1409
1400
  ViemSignature,
1410
1401
  `0x${string}`,
1411
1402
  ],
1412
- timestamp: bigint,
1413
1403
  options: { forcePendingCheckpointNumber?: CheckpointNumber },
1414
1404
  ) {
1415
1405
  const rollupData = encodeFunctionData({
@@ -1444,6 +1434,7 @@ export class SequencerPublisher {
1444
1434
  }
1445
1435
 
1446
1436
  const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
1437
+ const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
1447
1438
 
1448
1439
  const simulationResult = await this.l1TxUtils
1449
1440
  .simulate(
@@ -1454,8 +1445,7 @@ export class SequencerPublisher {
1454
1445
  ...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
1455
1446
  },
1456
1447
  {
1457
- // @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
1458
- time: timestamp + 1n,
1448
+ time: simTs,
1459
1449
  // @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
1460
1450
  gasLimit: MAX_L1_TX_LIMIT * 2n,
1461
1451
  },
@@ -1477,7 +1467,7 @@ export class SequencerPublisher {
1477
1467
  logs: [],
1478
1468
  };
1479
1469
  }
1480
- this.log.error(`Failed to simulate propose tx`, viemError);
1470
+ this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
1481
1471
  this.backupFailedTx({
1482
1472
  id: keccak256(rollupData),
1483
1473
  failureType: 'simulation',
@@ -1500,16 +1490,11 @@ export class SequencerPublisher {
1500
1490
  checkpoint: Checkpoint,
1501
1491
  encodedData: L1ProcessArgs,
1502
1492
  opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
1503
- timestamp: bigint,
1504
1493
  ): Promise<void> {
1505
1494
  const slot = checkpoint.header.slotNumber;
1506
1495
  const timer = new Timer();
1507
1496
  const kzg = Blob.getViemKzgInstance();
1508
- const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
1509
- encodedData,
1510
- timestamp,
1511
- opts,
1512
- );
1497
+ const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
1513
1498
  const startBlock = await this.l1TxUtils.getBlockNumber();
1514
1499
  const gasLimit = this.l1TxUtils.bumpGasLimit(
1515
1500
  BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
@@ -1585,4 +1570,17 @@ export class SequencerPublisher {
1585
1570
  },
1586
1571
  });
1587
1572
  }
1573
+
1574
+ /** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
1575
+ * for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
1576
+ private getSimulationTimestamp(slot: SlotNumber): bigint {
1577
+ const l1Constants = this.epochCache.getL1Constants();
1578
+ return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
1579
+ }
1580
+
1581
+ /** Returns the timestamp of the next L1 slot boundary after now. */
1582
+ private getNextL1SlotTimestamp(): bigint {
1583
+ const l1Constants = this.epochCache.getL1Constants();
1584
+ return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
1585
+ }
1588
1586
  }
@@ -960,7 +960,7 @@ export class CheckpointProposalJob implements Traceable {
960
960
  * would never receive its own block without this explicit sync.
961
961
  */
962
962
  private async syncProposedBlockToArchiver(block: L2Block): Promise<void> {
963
- if (this.config.skipPushProposedBlocksToArchiver !== false) {
963
+ if (this.config.skipPushProposedBlocksToArchiver) {
964
964
  this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
965
965
  blockNumber: block.number,
966
966
  slot: block.header.globalVariables.slotNumber,
@@ -2,7 +2,6 @@ import type { SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import type { Logger } from '@aztec/foundation/log';
4
4
  import type { SlasherClientInterface } from '@aztec/slasher';
5
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
6
5
  import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
7
6
  import type { ValidatorClient } from '@aztec/validator-client';
8
7
  import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
@@ -18,7 +17,6 @@ import type { SequencerRollupConstants } from './types.js';
18
17
  * Handles governance and slashing voting for a given slot.
19
18
  */
20
19
  export class CheckpointVoter {
21
- private slotTimestamp: bigint;
22
20
  private governanceSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
23
21
  private slashingSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
24
22
 
@@ -33,8 +31,6 @@ export class CheckpointVoter {
33
31
  private readonly metrics: SequencerMetrics,
34
32
  private readonly log: Logger,
35
33
  ) {
36
- this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
37
-
38
34
  // Create separate signers with appropriate duty contexts for governance and slashing votes
39
35
  // These use HA protection to ensure only one node signs per slot/duty
40
36
  const governanceContext: SigningContext = { slot: this.slot, dutyType: DutyType.GOVERNANCE_VOTE };
@@ -77,7 +73,6 @@ export class CheckpointVoter {
77
73
  return await this.publisher.enqueueGovernanceCastSignal(
78
74
  governanceProposerPayload,
79
75
  this.slot,
80
- this.slotTimestamp,
81
76
  this.attestorAddress,
82
77
  this.governanceSigner,
83
78
  );
@@ -108,13 +103,7 @@ export class CheckpointVoter {
108
103
 
109
104
  this.metrics.recordSlashingAttempt(actions.length);
110
105
 
111
- return await this.publisher.enqueueSlashingActions(
112
- actions,
113
- this.slot,
114
- this.slotTimestamp,
115
- this.attestorAddress,
116
- this.slashingSigner,
117
- );
106
+ return await this.publisher.enqueueSlashingActions(actions, this.slot, this.attestorAddress, this.slashingSigner);
118
107
  } catch (err) {
119
108
  if (err instanceof DutyAlreadySignedError) {
120
109
  this.log.info(`Slashing vote already signed by another node`, {
@@ -147,7 +147,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
147
147
  public async stop(): Promise<void> {
148
148
  this.log.info(`Stopping sequencer`);
149
149
  this.setState(SequencerState.STOPPING, undefined, { force: true });
150
- this.publisherFactory.interruptAll();
150
+ await this.publisherFactory.stopAll();
151
151
  await this.runningPromise?.stop();
152
152
  this.setState(SequencerState.STOPPED, undefined, { force: true });
153
153
  this.log.info('Stopped sequencer');
@@ -501,8 +501,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
501
501
  */
502
502
  protected async checkSync(args: { ts: bigint; slot: SlotNumber }): Promise<SequencerSyncCheckResult | undefined> {
503
503
  // Check that the archiver has fully synced the L2 slot before the one we want to propose in.
504
- // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
505
- // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
504
+ // The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
505
+ // See getSyncedL2SlotNumber for how missed L1 blocks are handled.
506
506
  const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
507
507
  const { slot } = args;
508
508
  if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {