@aztec/sequencer-client 3.0.0-devnet.5 → 3.0.0-devnet.6-patch.1

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 (61) hide show
  1. package/dest/client/index.d.ts +1 -1
  2. package/dest/client/sequencer-client.d.ts +2 -2
  3. package/dest/client/sequencer-client.d.ts.map +1 -1
  4. package/dest/client/sequencer-client.js +6 -2
  5. package/dest/config.d.ts +3 -2
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +11 -1
  8. package/dest/global_variable_builder/global_builder.d.ts +5 -7
  9. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  10. package/dest/global_variable_builder/global_builder.js +12 -8
  11. package/dest/global_variable_builder/index.d.ts +1 -1
  12. package/dest/index.d.ts +1 -1
  13. package/dest/publisher/config.d.ts +7 -2
  14. package/dest/publisher/config.d.ts.map +1 -1
  15. package/dest/publisher/config.js +12 -1
  16. package/dest/publisher/index.d.ts +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -2
  18. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  19. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  20. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  21. package/dest/publisher/sequencer-publisher.d.ts +40 -31
  22. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  23. package/dest/publisher/sequencer-publisher.js +117 -62
  24. package/dest/sequencer/block_builder.d.ts +4 -3
  25. package/dest/sequencer/block_builder.d.ts.map +1 -1
  26. package/dest/sequencer/block_builder.js +5 -8
  27. package/dest/sequencer/config.d.ts +2 -2
  28. package/dest/sequencer/config.d.ts.map +1 -1
  29. package/dest/sequencer/errors.d.ts +1 -1
  30. package/dest/sequencer/errors.d.ts.map +1 -1
  31. package/dest/sequencer/index.d.ts +1 -1
  32. package/dest/sequencer/metrics.d.ts +12 -3
  33. package/dest/sequencer/metrics.d.ts.map +1 -1
  34. package/dest/sequencer/metrics.js +38 -0
  35. package/dest/sequencer/sequencer.d.ts +48 -27
  36. package/dest/sequencer/sequencer.d.ts.map +1 -1
  37. package/dest/sequencer/sequencer.js +418 -166
  38. package/dest/sequencer/timetable.d.ts +3 -1
  39. package/dest/sequencer/timetable.d.ts.map +1 -1
  40. package/dest/sequencer/utils.d.ts +1 -1
  41. package/dest/test/index.d.ts +2 -2
  42. package/dest/test/index.d.ts.map +1 -1
  43. package/dest/tx_validator/nullifier_cache.d.ts +1 -1
  44. package/dest/tx_validator/nullifier_cache.d.ts.map +1 -1
  45. package/dest/tx_validator/tx_validator_factory.d.ts +4 -3
  46. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  47. package/dest/tx_validator/tx_validator_factory.js +1 -1
  48. package/package.json +31 -30
  49. package/src/client/sequencer-client.ts +6 -9
  50. package/src/config.ts +12 -6
  51. package/src/global_variable_builder/global_builder.ts +19 -17
  52. package/src/publisher/config.ts +17 -6
  53. package/src/publisher/sequencer-publisher-factory.ts +4 -2
  54. package/src/publisher/sequencer-publisher.ts +165 -94
  55. package/src/sequencer/block_builder.ts +8 -12
  56. package/src/sequencer/config.ts +1 -1
  57. package/src/sequencer/metrics.ts +52 -3
  58. package/src/sequencer/sequencer.ts +480 -198
  59. package/src/sequencer/timetable.ts +7 -0
  60. package/src/test/index.ts +1 -1
  61. package/src/tx_validator/tx_validator_factory.ts +3 -2
@@ -2,32 +2,33 @@ import { L2Block } from '@aztec/aztec.js/block';
2
2
  import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
3
3
  import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
4
4
  import type { EpochCache } from '@aztec/epoch-cache';
5
+ import type { L1ContractsConfig } from '@aztec/ethereum/config';
5
6
  import {
6
7
  type EmpireSlashingProposerContract,
7
- FormattedViemError,
8
8
  type GovernanceProposerContract,
9
9
  type IEmpireBase,
10
- type L1BlobInputs,
11
- type L1ContractsConfig,
12
- type L1TxConfig,
13
- type L1TxRequest,
14
10
  MULTI_CALL_3_ADDRESS,
15
11
  Multicall3,
16
12
  RollupContract,
17
13
  type TallySlashingProposerContract,
18
- type TransactionStats,
19
14
  type ViemCommitteeAttestations,
20
15
  type ViemHeader,
21
- type ViemStateReference,
22
- formatViemError,
23
- tryExtractEvent,
24
- } from '@aztec/ethereum';
16
+ } from '@aztec/ethereum/contracts';
17
+ import {
18
+ type L1BlobInputs,
19
+ type L1TxConfig,
20
+ type L1TxRequest,
21
+ type TransactionStats,
22
+ WEI_CONST,
23
+ } from '@aztec/ethereum/l1-tx-utils';
25
24
  import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
25
+ import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/ethereum/utils';
26
26
  import { sumBigint } from '@aztec/foundation/bigint';
27
27
  import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
28
+ import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
29
+ import type { Fr } from '@aztec/foundation/curves/bn254';
28
30
  import { EthAddress } from '@aztec/foundation/eth-address';
29
31
  import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
30
- import type { Fr } from '@aztec/foundation/fields';
31
32
  import { type Logger, createLogger } from '@aztec/foundation/log';
32
33
  import { bufferToHex } from '@aztec/foundation/string';
33
34
  import { DateProvider, Timer } from '@aztec/foundation/timer';
@@ -37,10 +38,9 @@ import { CommitteeAttestation, CommitteeAttestationsAndSigners, type ValidateBlo
37
38
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
38
39
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
39
40
  import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
40
- import { StateReference } from '@aztec/stdlib/tx';
41
41
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
42
42
 
43
- import { type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
43
+ import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
44
44
 
45
45
  import type { PublisherConfig, TxSenderConfig } from './config.js';
46
46
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
@@ -51,8 +51,6 @@ type L1ProcessArgs = {
51
51
  header: CheckpointHeader;
52
52
  /** A root of the archive tree after the L2 block is applied. */
53
53
  archive: Buffer;
54
- /** State reference after the L2 block is applied. */
55
- stateReference: StateReference;
56
54
  /** L2 block blobs containing all tx effects. */
57
55
  blobs: Blob[];
58
56
  /** Attestations */
@@ -84,14 +82,14 @@ export type InvalidateBlockRequest = {
84
82
  request: L1TxRequest;
85
83
  reason: 'invalid-attestation' | 'insufficient-attestations';
86
84
  gasUsed: bigint;
87
- blockNumber: number;
88
- forcePendingBlockNumber: number;
85
+ blockNumber: BlockNumber;
86
+ forcePendingBlockNumber: BlockNumber;
89
87
  };
90
88
 
91
89
  interface RequestWithExpiry {
92
90
  action: Action;
93
91
  request: L1TxRequest;
94
- lastValidL2Slot: bigint;
92
+ lastValidL2Slot: SlotNumber;
95
93
  gasConfig?: Pick<L1TxConfig, 'txTimeoutAt' | 'gasLimit'>;
96
94
  blobConfig?: L1BlobInputs;
97
95
  checkSuccess: (
@@ -108,12 +106,15 @@ export class SequencerPublisher {
108
106
  protected governanceLog = createLogger('sequencer:publisher:governance');
109
107
  protected slashingLog = createLogger('sequencer:publisher:slashing');
110
108
 
111
- protected lastActions: Partial<Record<Action, bigint>> = {};
109
+ protected lastActions: Partial<Record<Action, SlotNumber>> = {};
112
110
 
113
111
  protected log: Logger;
114
112
  protected ethereumSlotDuration: bigint;
115
113
 
116
114
  private blobSinkClient: BlobSinkClientInterface;
115
+
116
+ /** Address to use for simulations in fisherman mode (actual proposer's address) */
117
+ private proposerAddressForSimulation?: EthAddress;
117
118
  // @note - with blobs, the below estimate seems too large.
118
119
  // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
119
120
  // Total used for emptier block from above test: 429k (of which 84k is 1x blob)
@@ -146,7 +147,7 @@ export class SequencerPublisher {
146
147
  epochCache: EpochCache;
147
148
  dateProvider: DateProvider;
148
149
  metrics: SequencerPublisherMetrics;
149
- lastActions: Partial<Record<Action, bigint>>;
150
+ lastActions: Partial<Record<Action, SlotNumber>>;
150
151
  log?: Logger;
151
152
  },
152
153
  ) {
@@ -183,14 +184,33 @@ export class SequencerPublisher {
183
184
  return this.l1TxUtils.getSenderAddress();
184
185
  }
185
186
 
187
+ /**
188
+ * Sets the proposer address to use for simulations in fisherman mode.
189
+ * @param proposerAddress - The actual proposer's address to use for balance lookups in simulations
190
+ */
191
+ public setProposerAddressForSimulation(proposerAddress: EthAddress | undefined) {
192
+ this.proposerAddressForSimulation = proposerAddress;
193
+ }
194
+
186
195
  public addRequest(request: RequestWithExpiry) {
187
196
  this.requests.push(request);
188
197
  }
189
198
 
190
- public getCurrentL2Slot(): bigint {
199
+ public getCurrentL2Slot(): SlotNumber {
191
200
  return this.epochCache.getEpochAndSlotNow().slot;
192
201
  }
193
202
 
203
+ /**
204
+ * Clears all pending requests without sending them.
205
+ */
206
+ public clearPendingRequests(): void {
207
+ const count = this.requests.length;
208
+ this.requests = [];
209
+ if (count > 0) {
210
+ this.log.debug(`Cleared ${count} pending request(s)`);
211
+ }
212
+ }
213
+
194
214
  /**
195
215
  * Sends all requests that are still valid.
196
216
  * @returns one of:
@@ -315,13 +335,18 @@ export class SequencerPublisher {
315
335
  public canProposeAtNextEthBlock(
316
336
  tipArchive: Fr,
317
337
  msgSender: EthAddress,
318
- opts: { forcePendingBlockNumber?: number } = {},
338
+ opts: { forcePendingBlockNumber?: BlockNumber } = {},
319
339
  ) {
320
340
  // TODO: #14291 - should loop through multiple keys to check if any of them can propose
321
341
  const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
322
342
 
323
343
  return this.rollupContract
324
- .canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), this.ethereumSlotDuration, opts)
344
+ .canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
345
+ forcePendingCheckpointNumber:
346
+ opts.forcePendingBlockNumber !== undefined
347
+ ? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
348
+ : undefined,
349
+ })
325
350
  .catch(err => {
326
351
  if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
327
352
  this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find(e => err.message.includes(e))}`, {
@@ -339,7 +364,10 @@ export class SequencerPublisher {
339
364
  * It will throw if the block header is invalid.
340
365
  * @param header - The block header to validate
341
366
  */
342
- public async validateBlockHeader(header: CheckpointHeader, opts?: { forcePendingBlockNumber: number | undefined }) {
367
+ public async validateBlockHeader(
368
+ header: CheckpointHeader,
369
+ opts?: { forcePendingBlockNumber: BlockNumber | undefined },
370
+ ) {
343
371
  const flags = { ignoreDA: true, ignoreSignatures: true };
344
372
 
345
373
  const args = [
@@ -353,10 +381,26 @@ export class SequencerPublisher {
353
381
  ] as const;
354
382
 
355
383
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
384
+ const optsForcePendingCheckpointNumber =
385
+ opts?.forcePendingBlockNumber !== undefined
386
+ ? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
387
+ : undefined;
388
+ const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
389
+ optsForcePendingCheckpointNumber,
390
+ );
391
+ let balance = 0n;
392
+ if (this.config.fishermanMode) {
393
+ // In fisherman mode, we can't know where the proposer is publishing from
394
+ // so we just add sufficient balance to the multicall3 address
395
+ balance = 10n * WEI_CONST * WEI_CONST; // 10 ETH
396
+ } else {
397
+ balance = await this.l1TxUtils.getSenderBalance();
398
+ }
399
+ stateOverrides.push({
400
+ address: MULTI_CALL_3_ADDRESS,
401
+ balance,
402
+ });
356
403
 
357
- // use sender balance to simulate
358
- const balance = await this.l1TxUtils.getSenderBalance();
359
- this.log.debug(`Simulating validateHeader with balance: ${balance}`);
360
404
  await this.l1TxUtils.simulate(
361
405
  {
362
406
  to: this.rollupContract.address,
@@ -364,10 +408,7 @@ export class SequencerPublisher {
364
408
  from: MULTI_CALL_3_ADDRESS,
365
409
  },
366
410
  { time: ts + 1n },
367
- [
368
- { address: MULTI_CALL_3_ADDRESS, balance },
369
- ...(await this.rollupContract.makePendingBlockNumberOverride(opts?.forcePendingBlockNumber)),
370
- ],
411
+ stateOverrides,
371
412
  );
372
413
  this.log.debug(`Simulated validateHeader`);
373
414
  }
@@ -387,7 +428,7 @@ export class SequencerPublisher {
387
428
  const blockNumber = block.blockNumber;
388
429
  const logData = { ...block, reason };
389
430
 
390
- const currentBlockNumber = await this.rollupContract.getBlockNumber();
431
+ const currentBlockNumber = await this.rollupContract.getCheckpointNumber();
391
432
  if (currentBlockNumber < validationResult.block.blockNumber) {
392
433
  this.log.verbose(
393
434
  `Skipping block ${blockNumber} invalidation since it has already been removed from the pending chain`,
@@ -403,7 +444,7 @@ export class SequencerPublisher {
403
444
  const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, ErrorsAbi);
404
445
  this.log.verbose(`Simulation for invalidate block ${blockNumber} succeeded`, { ...logData, request, gasUsed });
405
446
 
406
- return { request, gasUsed, blockNumber, forcePendingBlockNumber: blockNumber - 1, reason };
447
+ return { request, gasUsed, blockNumber, forcePendingBlockNumber: BlockNumber(blockNumber - 1), reason };
407
448
  } catch (err) {
408
449
  const viemError = formatViemError(err);
409
450
 
@@ -414,7 +455,7 @@ export class SequencerPublisher {
414
455
  `Simulation for invalidate block ${blockNumber} failed due to block not being in pending chain`,
415
456
  { ...logData, request, error: viemError.message },
416
457
  );
417
- const latestPendingBlockNumber = await this.rollupContract.getBlockNumber();
458
+ const latestPendingBlockNumber = await this.rollupContract.getCheckpointNumber();
418
459
  if (latestPendingBlockNumber < blockNumber) {
419
460
  this.log.verbose(`Block number ${blockNumber} has already been invalidated`, { ...logData });
420
461
  return undefined;
@@ -451,14 +492,14 @@ export class SequencerPublisher {
451
492
 
452
493
  if (reason === 'invalid-attestation') {
453
494
  return this.rollupContract.buildInvalidateBadAttestationRequest(
454
- block.blockNumber,
495
+ CheckpointNumber.fromBlockNumber(block.blockNumber),
455
496
  attestationsAndSigners,
456
497
  committee,
457
498
  validationResult.invalidIndex,
458
499
  );
459
500
  } else if (reason === 'insufficient-attestations') {
460
501
  return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
461
- block.blockNumber,
502
+ CheckpointNumber.fromBlockNumber(block.blockNumber),
462
503
  attestationsAndSigners,
463
504
  committee,
464
505
  );
@@ -481,7 +522,7 @@ export class SequencerPublisher {
481
522
  block: L2Block,
482
523
  attestationsAndSigners: CommitteeAttestationsAndSigners,
483
524
  attestationsAndSignersSignature: Signature,
484
- options: { forcePendingBlockNumber?: number },
525
+ options: { forcePendingBlockNumber?: BlockNumber },
485
526
  ): Promise<bigint> {
486
527
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
487
528
 
@@ -489,10 +530,10 @@ export class SequencerPublisher {
489
530
  // so that the committee is recalculated correctly
490
531
  const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
491
532
  if (ignoreSignatures) {
492
- const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber.toBigInt());
533
+ const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
493
534
  if (!committee) {
494
- this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
495
- throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
535
+ this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
536
+ throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
496
537
  }
497
538
  attestationsAndSigners.attestations = committee.map(committeeMember =>
498
539
  CommitteeAttestation.fromAddress(committeeMember),
@@ -507,7 +548,6 @@ export class SequencerPublisher {
507
548
  {
508
549
  header: block.getCheckpointHeader().toViem(),
509
550
  archive: toHex(block.archive.root.toBuffer()),
510
- stateReference: block.header.state.toViem(),
511
551
  oracleInput: {
512
552
  feeAssetPriceModifier: 0n,
513
553
  },
@@ -523,7 +563,7 @@ export class SequencerPublisher {
523
563
  }
524
564
 
525
565
  private async enqueueCastSignalHelper(
526
- slotNumber: bigint,
566
+ slotNumber: SlotNumber,
527
567
  timestamp: bigint,
528
568
  signalType: GovernanceSignalAction,
529
569
  payload: EthAddress,
@@ -616,7 +656,7 @@ export class SequencerPublisher {
616
656
  */
617
657
  public enqueueGovernanceCastSignal(
618
658
  governancePayload: EthAddress,
619
- slotNumber: bigint,
659
+ slotNumber: SlotNumber,
620
660
  timestamp: bigint,
621
661
  signerAddress: EthAddress,
622
662
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
@@ -635,7 +675,7 @@ export class SequencerPublisher {
635
675
  /** Enqueues all slashing actions as returned by the slasher client. */
636
676
  public async enqueueSlashingActions(
637
677
  actions: ProposerSlashAction[],
638
- slotNumber: bigint,
678
+ slotNumber: SlotNumber,
639
679
  timestamp: bigint,
640
680
  signerAddress: EthAddress,
641
681
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
@@ -765,7 +805,7 @@ export class SequencerPublisher {
765
805
  block: L2Block,
766
806
  attestationsAndSigners: CommitteeAttestationsAndSigners,
767
807
  attestationsAndSignersSignature: Signature,
768
- opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: number } = {},
808
+ opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
769
809
  ): Promise<boolean> {
770
810
  const checkpointHeader = block.getCheckpointHeader();
771
811
 
@@ -775,7 +815,6 @@ export class SequencerPublisher {
775
815
  const proposeTxArgs = {
776
816
  header: checkpointHeader,
777
817
  archive: block.archive.root.toBuffer(),
778
- stateReference: block.header.state,
779
818
  body: block.body.toBuffer(),
780
819
  blobs,
781
820
  attestationsAndSigners,
@@ -794,7 +833,7 @@ export class SequencerPublisher {
794
833
  } catch (err: any) {
795
834
  this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
796
835
  ...block.getStats(),
797
- slotNumber: block.header.globalVariables.slotNumber.toBigInt(),
836
+ slotNumber: block.header.globalVariables.slotNumber,
798
837
  forcePendingBlockNumber: opts.forcePendingBlockNumber,
799
838
  });
800
839
  throw err;
@@ -820,13 +859,13 @@ export class SequencerPublisher {
820
859
  action: `invalidate-by-${request.reason}`,
821
860
  request: request.request,
822
861
  gasConfig: { gasLimit, txTimeoutAt: opts.txTimeoutAt },
823
- lastValidL2Slot: this.getCurrentL2Slot() + 2n,
862
+ lastValidL2Slot: SlotNumber(this.getCurrentL2Slot() + 2),
824
863
  checkSuccess: (_req, result) => {
825
864
  const success =
826
865
  result &&
827
866
  result.receipt &&
828
867
  result.receipt.status === 'success' &&
829
- tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'BlockInvalidated');
868
+ tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
830
869
  if (!success) {
831
870
  this.log.warn(`Invalidate block ${request.blockNumber} failed`, { ...result, ...logData });
832
871
  } else {
@@ -841,7 +880,7 @@ export class SequencerPublisher {
841
880
  action: Action,
842
881
  request: L1TxRequest,
843
882
  checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
844
- slotNumber: bigint,
883
+ slotNumber: SlotNumber,
845
884
  timestamp: bigint,
846
885
  ) {
847
886
  const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
@@ -909,41 +948,50 @@ export class SequencerPublisher {
909
948
  private async prepareProposeTx(
910
949
  encodedData: L1ProcessArgs,
911
950
  timestamp: bigint,
912
- options: { forcePendingBlockNumber?: number },
951
+ options: { forcePendingBlockNumber?: BlockNumber },
913
952
  ) {
914
953
  const kzg = Blob.getViemKzgInstance();
915
954
  const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
916
955
  this.log.debug('Validating blob input', { blobInput });
917
- const blobEvaluationGas = await this.l1TxUtils
918
- .estimateGas(
919
- this.getSenderAddress().toString(),
920
- {
921
- to: this.rollupContract.address,
922
- data: encodeFunctionData({
923
- abi: RollupAbi,
924
- functionName: 'validateBlobs',
925
- args: [blobInput],
926
- }),
927
- },
928
- {},
929
- {
930
- blobs: encodedData.blobs.map(b => b.data),
931
- kzg,
932
- },
933
- )
934
- .catch(err => {
935
- const { message, metaMessages } = formatViemError(err);
936
- this.log.error(`Failed to validate blobs`, message, { metaMessages });
937
- throw new Error('Failed to validate blobs');
938
- });
939
956
 
957
+ // Get blob evaluation gas
958
+ let blobEvaluationGas: bigint;
959
+ if (this.config.fishermanMode) {
960
+ // In fisherman mode, we can't estimate blob gas because estimateGas doesn't support state overrides
961
+ // Use a fixed estimate.
962
+ blobEvaluationGas = BigInt(encodedData.blobs.length) * 21_000n;
963
+ this.log.debug(`Using fixed blob evaluation gas estimate in fisherman mode: ${blobEvaluationGas}`);
964
+ } else {
965
+ // Normal mode - use estimateGas with blob inputs
966
+ blobEvaluationGas = await this.l1TxUtils
967
+ .estimateGas(
968
+ this.getSenderAddress().toString(),
969
+ {
970
+ to: this.rollupContract.address,
971
+ data: encodeFunctionData({
972
+ abi: RollupAbi,
973
+ functionName: 'validateBlobs',
974
+ args: [blobInput],
975
+ }),
976
+ },
977
+ {},
978
+ {
979
+ blobs: encodedData.blobs.map(b => b.data),
980
+ kzg,
981
+ },
982
+ )
983
+ .catch(err => {
984
+ const { message, metaMessages } = formatViemError(err);
985
+ this.log.error(`Failed to validate blobs`, message, { metaMessages });
986
+ throw new Error('Failed to validate blobs');
987
+ });
988
+ }
940
989
  const signers = encodedData.attestationsAndSigners.getSigners().map(signer => signer.toString());
941
990
 
942
991
  const args = [
943
992
  {
944
993
  header: encodedData.header.toViem(),
945
994
  archive: toHex(encodedData.archive),
946
- stateReference: encodedData.stateReference.toViem(),
947
995
  oracleInput: {
948
996
  // We are currently not modifying these. See #9963
949
997
  feeAssetPriceModifier: 0n,
@@ -971,7 +1019,6 @@ export class SequencerPublisher {
971
1019
  {
972
1020
  readonly header: ViemHeader;
973
1021
  readonly archive: `0x${string}`;
974
- readonly stateReference: ViemStateReference;
975
1022
  readonly oracleInput: {
976
1023
  readonly feeAssetPriceModifier: 0n;
977
1024
  };
@@ -982,7 +1029,7 @@ export class SequencerPublisher {
982
1029
  `0x${string}`,
983
1030
  ],
984
1031
  timestamp: bigint,
985
- options: { forcePendingBlockNumber?: number },
1032
+ options: { forcePendingBlockNumber?: BlockNumber },
986
1033
  ) {
987
1034
  const rollupData = encodeFunctionData({
988
1035
  abi: RollupAbi,
@@ -990,19 +1037,42 @@ export class SequencerPublisher {
990
1037
  args,
991
1038
  });
992
1039
 
993
- // override the pending block number if requested
994
- const forcePendingBlockNumberStateDiff = (
1040
+ // override the pending checkpoint number if requested
1041
+ const optsForcePendingCheckpointNumber =
995
1042
  options.forcePendingBlockNumber !== undefined
996
- ? await this.rollupContract.makePendingBlockNumberOverride(options.forcePendingBlockNumber)
1043
+ ? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
1044
+ : undefined;
1045
+ const forcePendingCheckpointNumberStateDiff = (
1046
+ optsForcePendingCheckpointNumber !== undefined
1047
+ ? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber)
997
1048
  : []
998
1049
  ).flatMap(override => override.stateDiff ?? []);
999
1050
 
1051
+ const stateOverrides: StateOverride = [
1052
+ {
1053
+ address: this.rollupContract.address,
1054
+ // @note we override checkBlob to false since blobs are not part simulate()
1055
+ stateDiff: [
1056
+ { slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
1057
+ ...forcePendingCheckpointNumberStateDiff,
1058
+ ],
1059
+ },
1060
+ ];
1061
+ // In fisherman mode, simulate as the proposer but with sufficient balance
1062
+ if (this.proposerAddressForSimulation) {
1063
+ stateOverrides.push({
1064
+ address: this.proposerAddressForSimulation.toString(),
1065
+ balance: 10n * WEI_CONST * WEI_CONST, // 10 ETH
1066
+ });
1067
+ }
1068
+
1000
1069
  const simulationResult = await this.l1TxUtils
1001
1070
  .simulate(
1002
1071
  {
1003
1072
  to: this.rollupContract.address,
1004
1073
  data: rollupData,
1005
1074
  gas: SequencerPublisher.PROPOSE_GAS_GUESS,
1075
+ ...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
1006
1076
  },
1007
1077
  {
1008
1078
  // @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
@@ -1010,16 +1080,7 @@ export class SequencerPublisher {
1010
1080
  // @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
1011
1081
  gasLimit: SequencerPublisher.PROPOSE_GAS_GUESS * 2n,
1012
1082
  },
1013
- [
1014
- {
1015
- address: this.rollupContract.address,
1016
- // @note we override checkBlob to false since blobs are not part simulate()
1017
- stateDiff: [
1018
- { slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
1019
- ...forcePendingBlockNumberStateDiff,
1020
- ],
1021
- },
1022
- ],
1083
+ stateOverrides,
1023
1084
  RollupAbi,
1024
1085
  {
1025
1086
  // @note fallback gas estimate to use if the node doesn't support simulation API
@@ -1027,7 +1088,17 @@ export class SequencerPublisher {
1027
1088
  },
1028
1089
  )
1029
1090
  .catch(err => {
1030
- this.log.error(`Failed to simulate propose tx`, err);
1091
+ // In fisherman mode, we expect ValidatorSelection__MissingProposerSignature since fisherman doesn't have proposer signature
1092
+ const viemError = formatViemError(err);
1093
+ if (this.config.fishermanMode && viemError.message?.includes('ValidatorSelection__MissingProposerSignature')) {
1094
+ this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
1095
+ // Return a minimal simulation result with the fallback gas estimate
1096
+ return {
1097
+ gasUsed: SequencerPublisher.PROPOSE_GAS_GUESS,
1098
+ logs: [],
1099
+ };
1100
+ }
1101
+ this.log.error(`Failed to simulate propose tx`, viemError);
1031
1102
  throw err;
1032
1103
  });
1033
1104
 
@@ -1037,7 +1108,7 @@ export class SequencerPublisher {
1037
1108
  private async addProposeTx(
1038
1109
  block: L2Block,
1039
1110
  encodedData: L1ProcessArgs,
1040
- opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: number } = {},
1111
+ opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
1041
1112
  timestamp: bigint,
1042
1113
  ): Promise<void> {
1043
1114
  const timer = new Timer();
@@ -1066,7 +1137,7 @@ export class SequencerPublisher {
1066
1137
  to: this.rollupContract.address,
1067
1138
  data: rollupData,
1068
1139
  },
1069
- lastValidL2Slot: block.header.globalVariables.slotNumber.toBigInt(),
1140
+ lastValidL2Slot: block.header.globalVariables.slotNumber,
1070
1141
  gasConfig: { ...opts, gasLimit },
1071
1142
  blobConfig: {
1072
1143
  blobs: encodedData.blobs.map(b => b.data),
@@ -1080,7 +1151,7 @@ export class SequencerPublisher {
1080
1151
  const success =
1081
1152
  receipt &&
1082
1153
  receipt.status === 'success' &&
1083
- tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'L2BlockProposed');
1154
+ tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointProposed');
1084
1155
  if (success) {
1085
1156
  const endBlock = receipt.blockNumber;
1086
1157
  const inclusionBlocks = Number(endBlock - startBlock);
@@ -1109,7 +1180,7 @@ export class SequencerPublisher {
1109
1180
  ...block.getStats(),
1110
1181
  receipt,
1111
1182
  txHash: receipt.transactionHash,
1112
- slotNumber: block.header.globalVariables.slotNumber.toBigInt(),
1183
+ slotNumber: block.header.globalVariables.slotNumber,
1113
1184
  });
1114
1185
  return false;
1115
1186
  }
@@ -1,6 +1,7 @@
1
1
  import { MerkleTreeId } from '@aztec/aztec.js/trees';
2
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
3
  import { merge, pick } from '@aztec/foundation/collection';
3
- import type { Fr } from '@aztec/foundation/fields';
4
+ import type { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { createLogger } from '@aztec/foundation/log';
5
6
  import { retryUntil } from '@aztec/foundation/retry';
6
7
  import { bufferToHex } from '@aztec/foundation/string';
@@ -11,7 +12,7 @@ import {
11
12
  GuardedMerkleTreeOperations,
12
13
  PublicContractsDB,
13
14
  PublicProcessor,
14
- TelemetryPublicTxSimulator,
15
+ createPublicTxSimulatorForBlockBuilding,
15
16
  } from '@aztec/simulator/server';
16
17
  import type { ContractDataSource } from '@aztec/stdlib/contract';
17
18
  import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
@@ -46,7 +47,7 @@ export async function buildBlock(
46
47
  ): Promise<BuildBlockResult> {
47
48
  const blockBuildingTimer = new Timer();
48
49
  const blockNumber = newGlobalVariables.blockNumber;
49
- const slot = newGlobalVariables.slotNumber.toBigInt();
50
+ const slot = newGlobalVariables.slotNumber;
50
51
  const msgCount = l1ToL2Messages.length;
51
52
  const stateReference = await worldStateFork.getStateReference();
52
53
  const archiveTree = await worldStateFork.getTreeInfo(MerkleTreeId.ARCHIVE);
@@ -120,16 +121,11 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
120
121
  const contractsDB = new PublicContractsDB(this.contractDataSource);
121
122
  const guardedFork = new GuardedMerkleTreeOperations(fork);
122
123
 
123
- const publicTxSimulator = new TelemetryPublicTxSimulator(
124
+ const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
124
125
  guardedFork,
125
126
  contractsDB,
126
127
  globalVariables,
127
128
  this.telemetryClient,
128
- {
129
- doMerkleOperations: true,
130
- skipFeeEnforcement: true,
131
- clientInitiatedSimulation: false,
132
- },
133
129
  );
134
130
 
135
131
  const processor = new PublicProcessor(
@@ -156,7 +152,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
156
152
  };
157
153
  }
158
154
 
159
- private async syncToPreviousBlock(parentBlockNumber: number, timeout: number | undefined) {
155
+ private async syncToPreviousBlock(parentBlockNumber: BlockNumber, timeout: number | undefined) {
160
156
  await retryUntil(
161
157
  () => this.worldState.syncImmediate(parentBlockNumber, true).then(syncedTo => syncedTo >= parentBlockNumber),
162
158
  'sync to previous block',
@@ -173,7 +169,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
173
169
  opts: PublicProcessorLimits,
174
170
  suppliedFork?: MerkleTreeWriteOperations,
175
171
  ): Promise<BuildBlockResult> {
176
- const parentBlockNumber = globalVariables.blockNumber - 1;
172
+ const parentBlockNumber = BlockNumber(globalVariables.blockNumber - 1);
177
173
  const syncTimeout = opts.deadline ? (opts.deadline.getTime() - this.dateProvider.now()) / 1000 : undefined;
178
174
  await this.syncToPreviousBlock(parentBlockNumber, syncTimeout);
179
175
  const fork = suppliedFork ?? (await this.worldState.fork(parentBlockNumber));
@@ -212,7 +208,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
212
208
  }
213
209
  }
214
210
 
215
- getFork(blockNumber: number): Promise<MerkleTreeWriteOperations> {
211
+ getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations> {
216
212
  return this.worldState.fork(blockNumber);
217
213
  }
218
214
  }
@@ -1,4 +1,4 @@
1
- import type { GovernanceProposerContract, RollupContract } from '@aztec/ethereum';
1
+ import type { GovernanceProposerContract, RollupContract } from '@aztec/ethereum/contracts';
2
2
 
3
3
  export { type SequencerConfig } from '@aztec/stdlib/config';
4
4