@aztec/sequencer-client 0.56.0 → 0.58.0

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 (54) hide show
  1. package/dest/block_builder/index.d.ts +2 -2
  2. package/dest/block_builder/index.d.ts.map +1 -1
  3. package/dest/block_builder/light.d.ts +8 -12
  4. package/dest/block_builder/light.d.ts.map +1 -1
  5. package/dest/block_builder/light.js +11 -15
  6. package/dest/block_builder/orchestrator.d.ts +8 -11
  7. package/dest/block_builder/orchestrator.d.ts.map +1 -1
  8. package/dest/block_builder/orchestrator.js +3 -10
  9. package/dest/client/sequencer-client.d.ts +2 -2
  10. package/dest/client/sequencer-client.d.ts.map +1 -1
  11. package/dest/client/sequencer-client.js +3 -4
  12. package/dest/config.d.ts.map +1 -1
  13. package/dest/config.js +11 -9
  14. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  15. package/dest/global_variable_builder/global_builder.js +2 -1
  16. package/dest/publisher/config.d.ts.map +1 -1
  17. package/dest/publisher/config.js +7 -2
  18. package/dest/publisher/index.d.ts +1 -1
  19. package/dest/publisher/index.d.ts.map +1 -1
  20. package/dest/publisher/index.js +1 -1
  21. package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -1
  22. package/dest/publisher/l1-publisher-metrics.js +2 -1
  23. package/dest/publisher/l1-publisher.d.ts +52 -36
  24. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  25. package/dest/publisher/l1-publisher.js +305 -117
  26. package/dest/publisher/utils.d.ts +1 -1
  27. package/dest/publisher/utils.js +1 -1
  28. package/dest/sequencer/sequencer.d.ts +6 -6
  29. package/dest/sequencer/sequencer.d.ts.map +1 -1
  30. package/dest/sequencer/sequencer.js +142 -88
  31. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  32. package/dest/tx_validator/gas_validator.js +7 -5
  33. package/dest/tx_validator/phases_validator.d.ts +1 -1
  34. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  35. package/dest/tx_validator/phases_validator.js +1 -1
  36. package/dest/tx_validator/tx_validator_factory.d.ts +9 -7
  37. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  38. package/dest/tx_validator/tx_validator_factory.js +20 -10
  39. package/package.json +22 -19
  40. package/src/block_builder/index.ts +2 -2
  41. package/src/block_builder/light.ts +16 -24
  42. package/src/block_builder/orchestrator.ts +10 -18
  43. package/src/client/sequencer-client.ts +3 -9
  44. package/src/config.ts +10 -8
  45. package/src/global_variable_builder/global_builder.ts +1 -0
  46. package/src/publisher/config.ts +6 -1
  47. package/src/publisher/index.ts +1 -1
  48. package/src/publisher/l1-publisher-metrics.ts +1 -0
  49. package/src/publisher/l1-publisher.ts +435 -144
  50. package/src/publisher/utils.ts +1 -1
  51. package/src/sequencer/sequencer.ts +173 -108
  52. package/src/tx_validator/gas_validator.ts +6 -7
  53. package/src/tx_validator/phases_validator.ts +1 -1
  54. package/src/tx_validator/tx_validator_factory.ts +39 -15
@@ -2,7 +2,7 @@ import { type Logger } from '@aztec/foundation/log';
2
2
 
3
3
  import { BaseError, ContractFunctionRevertedError } from 'viem';
4
4
 
5
- export function prettyLogVeimError(err: any, logger: Logger) {
5
+ export function prettyLogViemError(err: any, logger: Logger) {
6
6
  if (err instanceof BaseError) {
7
7
  const revertError = err.walk(err => err instanceof ContractFunctionRevertedError);
8
8
  if (revertError instanceof ContractFunctionRevertedError) {
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type BlockAttestation,
3
+ type EpochProofQuote,
3
4
  type L1ToL2MessageSource,
4
5
  type L2Block,
5
6
  type L2BlockSource,
@@ -7,10 +8,13 @@ import {
7
8
  Tx,
8
9
  type TxHash,
9
10
  type TxValidator,
10
- type WorldStateStatus,
11
11
  type WorldStateSynchronizer,
12
12
  } from '@aztec/circuit-types';
13
- import { type AllowedElement, BlockProofError, PROVING_STATUS } from '@aztec/circuit-types/interfaces';
13
+ import {
14
+ type AllowedElement,
15
+ BlockProofError,
16
+ type WorldStateSynchronizerStatus,
17
+ } from '@aztec/circuit-types/interfaces';
14
18
  import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
15
19
  import {
16
20
  AppendOnlyTreeSnapshot,
@@ -31,10 +35,12 @@ import { type PublicProcessorFactory } from '@aztec/simulator';
31
35
  import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
32
36
  import { type ValidatorClient } from '@aztec/validator-client';
33
37
 
38
+ import { inspect } from 'util';
39
+
34
40
  import { type BlockBuilderFactory } from '../block_builder/index.js';
35
41
  import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
36
42
  import { type L1Publisher } from '../publisher/l1-publisher.js';
37
- import { prettyLogVeimError } from '../publisher/utils.js';
43
+ import { prettyLogViemError } from '../publisher/utils.js';
38
44
  import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
39
45
  import { type SequencerConfig } from './config.js';
40
46
  import { SequencerMetrics } from './metrics.js';
@@ -64,7 +70,6 @@ export class Sequencer {
64
70
  // TODO: zero values should not be allowed for the following 2 values in PROD
65
71
  private _coinbase = EthAddress.ZERO;
66
72
  private _feeRecipient = AztecAddress.ZERO;
67
- private lastPublishedBlock = 0;
68
73
  private state = SequencerState.STOPPED;
69
74
  private allowedInSetup: AllowedElement[] = [];
70
75
  private allowedInTeardown: AllowedElement[] = [];
@@ -139,13 +144,12 @@ export class Sequencer {
139
144
  /**
140
145
  * Starts the sequencer and moves to IDLE state. Blocks until the initial sync is complete.
141
146
  */
142
- public async start() {
143
- await this.initialSync();
144
-
147
+ public start() {
145
148
  this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
146
149
  this.runningPromise.start();
147
150
  this.state = SequencerState.IDLE;
148
151
  this.log.info('Sequencer started');
152
+ return Promise.resolve();
149
153
  }
150
154
 
151
155
  /**
@@ -177,11 +181,6 @@ export class Sequencer {
177
181
  return { state: this.state };
178
182
  }
179
183
 
180
- protected async initialSync() {
181
- // TODO: Should we wait for world state to be ready, or is the caller expected to run await start?
182
- this.lastPublishedBlock = await this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block);
183
- }
184
-
185
184
  /**
186
185
  * @notice Performs most of the sequencer duties:
187
186
  * - Checks if we are up to date
@@ -276,15 +275,14 @@ export class Sequencer {
276
275
  // @note It is very important that the following function will FAIL and not just return early
277
276
  // if it have made any state changes. If not, we won't rollback the state, and you will
278
277
  // be in for a world of pain.
279
- await this.buildBlockAndPublish(validTxs, proposalHeader, historicalHeader);
278
+ await this.buildBlockAndAttemptToPublish(validTxs, proposalHeader, historicalHeader);
280
279
  } catch (err) {
281
280
  if (BlockProofError.isBlockProofError(err)) {
282
281
  const txHashes = err.txHashes.filter(h => !h.isZero());
283
282
  this.log.warn(`Proving block failed, removing ${txHashes.length} txs from pool`);
284
283
  await this.p2pClient.deleteTxs(txHashes);
285
284
  }
286
- this.log.error(`Rolling back world state DB due to error assembling block`, (err as any).stack);
287
- await this.worldState.getLatest().rollback();
285
+ this.log.error(`Error assembling block`, (err as any).stack);
288
286
  }
289
287
  }
290
288
 
@@ -311,7 +309,8 @@ export class Sequencer {
311
309
  this.log.debug(`Can propose block ${proposalBlockNumber} at slot ${slot}`);
312
310
  return slot;
313
311
  } catch (err) {
314
- prettyLogVeimError(err, this.log);
312
+ this.log.verbose(`Rejected from being able to propose at next block with ${tipArchive}`);
313
+ prettyLogViemError(err, this.log);
315
314
  throw err;
316
315
  }
317
316
  }
@@ -392,10 +391,10 @@ export class Sequencer {
392
391
  * @param proposalHeader - The partial header constructed for the proposal
393
392
  * @param historicalHeader - The historical header of the parent
394
393
  */
395
- @trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, proposalHeader, _historicalHeader) => ({
394
+ @trackSpan('Sequencer.buildBlockAndAttemptToPublish', (_validTxs, proposalHeader, _historicalHeader) => ({
396
395
  [Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
397
396
  }))
398
- private async buildBlockAndPublish(
397
+ private async buildBlockAndAttemptToPublish(
399
398
  validTxs: Tx[],
400
399
  proposalHeader: Header,
401
400
  historicalHeader: Header | undefined,
@@ -416,89 +415,90 @@ export class Sequencer {
416
415
  `Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newGlobalVariables.blockNumber.toNumber()}`,
417
416
  );
418
417
 
419
- // We create a fresh processor each time to reset any cached state (eg storage writes)
420
- const processor = this.publicProcessorFactory.create(historicalHeader, newGlobalVariables);
421
-
422
418
  const numRealTxs = validTxs.length;
423
419
  const blockSize = Math.max(2, numRealTxs);
424
420
 
425
- const blockBuildingTimer = new Timer();
426
- const blockBuilder = this.blockBuilderFactory.create(this.worldState.getLatest());
427
- const blockTicket = await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
428
-
429
- const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
430
- processor.process(validTxs, blockSize, blockBuilder, this.txValidatorFactory.validatorForProcessedTxs()),
431
- );
432
- if (failedTxs.length > 0) {
433
- const failedTxData = failedTxs.map(fail => fail.tx);
434
- this.log.debug(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
435
- await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
436
- }
437
-
438
- await this.publisher.validateBlockForSubmission(proposalHeader);
439
-
440
- if (
441
- !this.shouldProposeBlock(historicalHeader, {
442
- validTxsCount: validTxs.length,
443
- processedTxsCount: processedTxs.length,
444
- })
445
- ) {
446
- blockBuilder.cancel();
447
- throw new Error('Should not propose the block');
448
- }
421
+ const fork = await this.worldState.fork();
422
+ try {
423
+ // We create a fresh processor each time to reset any cached state (eg storage writes)
424
+ const processor = this.publicProcessorFactory.create(fork, historicalHeader, newGlobalVariables);
425
+ const blockBuildingTimer = new Timer();
426
+ const blockBuilder = this.blockBuilderFactory.create(fork);
427
+ await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
428
+
429
+ const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
430
+ processor.process(validTxs, blockSize, blockBuilder, this.txValidatorFactory.validatorForProcessedTxs(fork)),
431
+ );
432
+ if (failedTxs.length > 0) {
433
+ const failedTxData = failedTxs.map(fail => fail.tx);
434
+ this.log.debug(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
435
+ await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
436
+ }
449
437
 
450
- // All real transactions have been added, set the block as full and complete the proving.
451
- await blockBuilder.setBlockCompleted();
438
+ await this.publisher.validateBlockForSubmission(proposalHeader);
452
439
 
453
- // Here we are now waiting for the block to be proven (using simulated[fake] proofs).
454
- // TODO(@PhilWindle) We should probably periodically check for things like another
455
- // block being published before ours instead of just waiting on our block
456
- const result = await blockTicket.provingPromise;
457
- if (result.status === PROVING_STATUS.FAILURE) {
458
- throw new Error(`Block proving failed, reason: ${result.reason}`);
459
- }
440
+ if (
441
+ !this.shouldProposeBlock(historicalHeader, {
442
+ validTxsCount: validTxs.length,
443
+ processedTxsCount: processedTxs.length,
444
+ })
445
+ ) {
446
+ // TODO: Roll back changes to world state
447
+ throw new Error('Should not propose the block');
448
+ }
460
449
 
461
- // Block is ready, now finalise
462
- const { block } = await blockBuilder.finaliseBlock();
450
+ // All real transactions have been added, set the block as full and complete the proving.
451
+ const block = await blockBuilder.setBlockCompleted();
452
+
453
+ // TODO(@PhilWindle) We should probably periodically check for things like another
454
+ // block being published before ours instead of just waiting on our block
455
+
456
+ await this.publisher.validateBlockForSubmission(block.header);
457
+
458
+ const workDuration = workTimer.ms();
459
+ this.log.verbose(
460
+ `Assembled block ${block.number} (txEffectsHash: ${block.header.contentCommitment.txsEffectsHash.toString(
461
+ 'hex',
462
+ )})`,
463
+ {
464
+ eventName: 'l2-block-built',
465
+ creator: this.publisher.getSenderAddress().toString(),
466
+ duration: workDuration,
467
+ publicProcessDuration: publicProcessorDuration,
468
+ rollupCircuitsDuration: blockBuildingTimer.ms(),
469
+ ...block.getStats(),
470
+ } satisfies L2BlockBuiltStats,
471
+ );
463
472
 
464
- await this.publisher.validateBlockForSubmission(block.header);
473
+ if (this.isFlushing) {
474
+ this.log.verbose(`Flushing completed`);
475
+ }
465
476
 
466
- const workDuration = workTimer.ms();
467
- this.log.verbose(
468
- `Assembled block ${block.number} (txEffectsHash: ${block.header.contentCommitment.txsEffectsHash.toString(
469
- 'hex',
470
- )})`,
471
- {
472
- eventName: 'l2-block-built',
473
- duration: workDuration,
474
- publicProcessDuration: publicProcessorDuration,
475
- rollupCircuitsDuration: blockBuildingTimer.ms(),
476
- ...block.getStats(),
477
- } satisfies L2BlockBuiltStats,
478
- );
477
+ const txHashes = validTxs.map(tx => tx.getTxHash());
479
478
 
480
- if (this.isFlushing) {
481
- this.log.verbose(`Flushing completed`);
482
- }
479
+ this.isFlushing = false;
480
+ this.log.verbose('Collecting attestations');
481
+ const attestations = await this.collectAttestations(block, txHashes);
482
+ this.log.verbose('Attestations collected');
483
483
 
484
- const txHashes = validTxs.map(tx => tx.getTxHash());
484
+ this.log.verbose('Collecting proof quotes');
485
+ const proofQuote = await this.createProofClaimForPreviousEpoch(newGlobalVariables.slotNumber.toBigInt());
486
+ this.log.verbose(proofQuote ? `Using proof quote ${inspect(proofQuote.payload)}` : 'No proof quote available');
485
487
 
486
- this.isFlushing = false;
487
- this.log.verbose('Collecting attestations');
488
- const attestations = await this.collectAttestations(block, txHashes);
489
- this.log.verbose('Attestations collected');
490
-
491
- try {
492
- await this.publishL2Block(block, attestations, txHashes);
493
- this.metrics.recordPublishedBlock(workDuration);
494
- this.log.info(
495
- `Submitted rollup block ${block.number} with ${
496
- processedTxs.length
497
- } transactions duration=${workDuration}ms (Submitter: ${await this.publisher.getSenderAddress()})`,
498
- );
499
- } catch (err) {
500
- this.metrics.recordFailedBlock();
501
- throw err;
488
+ try {
489
+ await this.publishL2Block(block, attestations, txHashes, proofQuote);
490
+ this.metrics.recordPublishedBlock(workDuration);
491
+ this.log.info(
492
+ `Submitted rollup block ${block.number} with ${processedTxs.length} transactions duration=${Math.ceil(
493
+ workDuration,
494
+ )}ms (Submitter: ${this.publisher.getSenderAddress()})`,
495
+ );
496
+ } catch (err) {
497
+ this.metrics.recordFailedBlock();
498
+ throw err;
499
+ }
500
+ } finally {
501
+ await fork.close();
502
502
  }
503
503
  }
504
504
 
@@ -507,6 +507,11 @@ export class Sequencer {
507
507
  this.isFlushing = true;
508
508
  }
509
509
 
510
+ @trackSpan('Sequencer.collectAttestations', (block, txHashes) => ({
511
+ [Attributes.BLOCK_NUMBER]: block.number,
512
+ [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
513
+ [Attributes.BLOCK_TXS_COUNT]: txHashes.length,
514
+ }))
510
515
  protected async collectAttestations(block: L2Block, txHashes: TxHash[]): Promise<Signature[] | undefined> {
511
516
  // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
512
517
  const committee = await this.publisher.getCurrentEpochCommittee();
@@ -540,6 +545,50 @@ export class Sequencer {
540
545
  return orderAttestations(attestations, committee);
541
546
  }
542
547
 
548
+ protected async createProofClaimForPreviousEpoch(slotNumber: bigint): Promise<EpochProofQuote | undefined> {
549
+ try {
550
+ // Find out which epoch we are currently in
551
+ const epochForBlock = await this.publisher.getEpochForSlotNumber(slotNumber);
552
+ if (epochForBlock < 1n) {
553
+ // It's the 0th epoch, nothing to be proven yet
554
+ this.log.verbose(`First epoch has no claim`);
555
+ return undefined;
556
+ }
557
+ const epochToProve = epochForBlock - 1n;
558
+ // Find out the next epoch that can be claimed
559
+ const canClaim = await this.publisher.nextEpochToClaim();
560
+ if (canClaim != epochToProve) {
561
+ // It's not the one we are looking to claim
562
+ this.log.verbose(`Unable to claim previous epoch (${canClaim} != ${epochToProve})`);
563
+ return undefined;
564
+ }
565
+ // Get quotes for the epoch to be proven
566
+ const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
567
+ this.log.verbose(`Retrieved ${quotes.length} quotes, slot: ${slotNumber}, epoch to prove: ${epochToProve}`);
568
+ for (const quote of quotes) {
569
+ this.log.verbose(inspect(quote.payload));
570
+ }
571
+ // ensure these quotes are still valid for the slot and have the contract validate them
572
+ const validQuotesPromise = Promise.all(
573
+ quotes.filter(x => x.payload.validUntilSlot >= slotNumber).map(x => this.publisher.validateProofQuote(x)),
574
+ );
575
+
576
+ const validQuotes = (await validQuotesPromise).filter((q): q is EpochProofQuote => !!q);
577
+ if (!validQuotes.length) {
578
+ this.log.verbose(`Failed to find any valid proof quotes`);
579
+ return undefined;
580
+ }
581
+ // pick the quote with the lowest fee
582
+ const sortedQuotes = validQuotes.sort(
583
+ (a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
584
+ );
585
+ return sortedQuotes[0];
586
+ } catch (err) {
587
+ this.log.error(`Failed to create proof claim for previous epoch: ${err}`);
588
+ return undefined;
589
+ }
590
+ }
591
+
543
592
  /**
544
593
  * Publishes the L2Block to the rollup contract.
545
594
  * @param block - The L2Block to be published.
@@ -547,14 +596,17 @@ export class Sequencer {
547
596
  @trackSpan('Sequencer.publishL2Block', block => ({
548
597
  [Attributes.BLOCK_NUMBER]: block.number,
549
598
  }))
550
- protected async publishL2Block(block: L2Block, attestations?: Signature[], txHashes?: TxHash[]) {
599
+ protected async publishL2Block(
600
+ block: L2Block,
601
+ attestations?: Signature[],
602
+ txHashes?: TxHash[],
603
+ proofQuote?: EpochProofQuote,
604
+ ) {
551
605
  // Publishes new block to the network and awaits the tx to be mined
552
606
  this.state = SequencerState.PUBLISHING_BLOCK;
553
607
 
554
- const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes);
555
- if (publishedL2Block) {
556
- this.lastPublishedBlock = block.number;
557
- } else {
608
+ const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote);
609
+ if (!publishedL2Block) {
558
610
  throw new Error(`Failed to publish block ${block.number}`);
559
611
  }
560
612
  }
@@ -590,24 +642,37 @@ export class Sequencer {
590
642
  }
591
643
 
592
644
  /**
593
- * Returns whether the previous block sent has been mined, and all dependencies have caught up with it.
645
+ * Returns whether all dependencies have caught up.
646
+ * We don't check against the previous block submitted since it may have been reorg'd out.
594
647
  * @returns Boolean indicating if our dependencies are synced to the latest block.
595
648
  */
596
649
  protected async isBlockSynced() {
597
650
  const syncedBlocks = await Promise.all([
598
- this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block),
599
- this.p2pClient.getStatus().then(s => s.syncedToL2Block),
600
- this.l2BlockSource.getBlockNumber(),
651
+ this.worldState.status().then((s: WorldStateSynchronizerStatus) => s.syncedToL2Block),
652
+ this.l2BlockSource.getL2Tips().then(t => t.latest),
653
+ this.p2pClient.getStatus().then(s => s.syncedToL2Block.number),
601
654
  this.l1ToL2MessageSource.getBlockNumber(),
602
- ]);
603
- const min = Math.min(...syncedBlocks);
604
- const [worldState, p2p, l2BlockSource, l1ToL2MessageSource] = syncedBlocks;
605
- const result = min >= this.lastPublishedBlock;
606
- this.log.debug(`Sync check to last published block ${this.lastPublishedBlock} ${result ? 'succeeded' : 'failed'}`, {
607
- worldState,
608
- p2p,
609
- l2BlockSource,
610
- l1ToL2MessageSource,
655
+ ] as const);
656
+ const [worldState, l2BlockSource, p2p, l1ToL2MessageSource] = syncedBlocks;
657
+ const result =
658
+ // check that world state has caught up with archiver
659
+ // note that the archiver reports undefined hash for the genesis block
660
+ // because it doesn't have access to world state to compute it (facepalm)
661
+ (l2BlockSource.hash === undefined || worldState.hash === l2BlockSource.hash) &&
662
+ // and p2p client and message source are at least at the same block
663
+ // this should change to hashes once p2p client handles reorgs
664
+ // and once we stop pretending that the l1tol2message source is not
665
+ // just the archiver under a different name
666
+ p2p >= l2BlockSource.number &&
667
+ l1ToL2MessageSource >= l2BlockSource.number;
668
+
669
+ this.log.verbose(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
670
+ worldStateNumber: worldState.number,
671
+ worldStateHash: worldState.hash,
672
+ l2BlockSourceNumber: l2BlockSource.number,
673
+ l2BlockSourceHash: l2BlockSource.hash,
674
+ p2pNumber: p2p,
675
+ l1ToL2MessageSourceNumber: l1ToL2MessageSource,
611
676
  });
612
677
  return result;
613
678
  }
@@ -1,7 +1,6 @@
1
1
  import { PublicKernelPhase, type Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { type AztecAddress, type Fr } from '@aztec/circuits.js';
2
+ import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
3
3
  import { createDebugLogger } from '@aztec/foundation/log';
4
- import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice';
5
4
  import { EnqueuedCallsProcessor, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
6
5
 
7
6
  /** Provides a view into public contract state */
@@ -64,15 +63,15 @@ export class GasTxValidator implements TxValidator<Tx> {
64
63
  fn =>
65
64
  fn.contractAddress.equals(this.#feeJuiceAddress) &&
66
65
  fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
67
- fn.callContext.functionSelector.equals(
68
- FeeJuiceArtifact.functions.find(f => f.name === '_increase_public_balance')!,
69
- ) &&
70
- fn.args[0].equals(feePayer) &&
66
+ fn.args.length > 2 &&
67
+ // Public functions get routed through the dispatch function, whose first argument is the target function selector.
68
+ fn.args[0].equals(FunctionSelector.fromSignature('_increase_public_balance((Field),Field)').toField()) &&
69
+ fn.args[1].equals(feePayer) &&
71
70
  !fn.callContext.isStaticCall &&
72
71
  !fn.callContext.isDelegateCall,
73
72
  );
74
73
 
75
- const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[1]) : initialBalance;
74
+ const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance;
76
75
  if (balance.lt(feeLimit)) {
77
76
  this.#log.info(`Rejecting transaction due to not enough fee payer balance`, { feePayer, balance, feeLimit });
78
77
  return false;
@@ -5,9 +5,9 @@ import {
5
5
  Tx,
6
6
  type TxValidator,
7
7
  } from '@aztec/circuit-types';
8
+ import { type ContractDataSource } from '@aztec/circuits.js';
8
9
  import { createDebugLogger } from '@aztec/foundation/log';
9
10
  import { ContractsDataSourcePublicDB, EnqueuedCallsProcessor } from '@aztec/simulator';
10
- import { type ContractDataSource } from '@aztec/types/contracts';
11
11
 
12
12
  export class PhasesTxValidator implements TxValidator<Tx> {
13
13
  #log = createDebugLogger('aztec:sequencer:tx_validator:tx_phases');
@@ -1,33 +1,57 @@
1
- import { type AllowedElement, type ProcessedTx, type Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { type GlobalVariables } from '@aztec/circuits.js';
3
- import { AggregateTxValidator, DataTxValidator, DoubleSpendTxValidator, MetadataTxValidator } from '@aztec/p2p';
4
- import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
5
- import { WorldStateDB } from '@aztec/simulator';
6
- import { type ContractDataSource } from '@aztec/types/contracts';
7
- import { type MerkleTreeOperations } from '@aztec/world-state';
1
+ import {
2
+ type AllowedElement,
3
+ MerkleTreeId,
4
+ type MerkleTreeReadOperations,
5
+ type ProcessedTx,
6
+ type Tx,
7
+ type TxValidator,
8
+ } from '@aztec/circuit-types';
9
+ import { type ContractDataSource, type GlobalVariables } from '@aztec/circuits.js';
10
+ import {
11
+ AggregateTxValidator,
12
+ DataTxValidator,
13
+ DoubleSpendTxValidator,
14
+ MetadataTxValidator,
15
+ type NullifierSource,
16
+ } from '@aztec/p2p';
17
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
18
+ import { readPublicState } from '@aztec/simulator';
8
19
 
9
- import { GasTxValidator } from './gas_validator.js';
20
+ import { GasTxValidator, type PublicStateSource } from './gas_validator.js';
10
21
  import { PhasesTxValidator } from './phases_validator.js';
11
22
 
12
23
  export class TxValidatorFactory {
24
+ nullifierSource: NullifierSource;
25
+ publicStateSource: PublicStateSource;
13
26
  constructor(
14
- private merkleTreeDb: MerkleTreeOperations,
27
+ private committedDb: MerkleTreeReadOperations,
15
28
  private contractDataSource: ContractDataSource,
16
29
  private enforceFees: boolean,
17
- ) {}
30
+ ) {
31
+ this.nullifierSource = {
32
+ getNullifierIndex: nullifier => this.committedDb.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()),
33
+ };
34
+
35
+ this.publicStateSource = {
36
+ storageRead: (contractAddress, slot) => {
37
+ return readPublicState(this.committedDb, contractAddress, slot);
38
+ },
39
+ };
40
+ }
18
41
 
19
42
  validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
20
- const worldStateDB = new WorldStateDB(this.merkleTreeDb, this.contractDataSource);
21
43
  return new AggregateTxValidator(
22
44
  new DataTxValidator(),
23
45
  new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
24
- new DoubleSpendTxValidator(worldStateDB),
46
+ new DoubleSpendTxValidator(this.nullifierSource),
25
47
  new PhasesTxValidator(this.contractDataSource, setupAllowList),
26
- new GasTxValidator(worldStateDB, FeeJuiceAddress, this.enforceFees),
48
+ new GasTxValidator(this.publicStateSource, ProtocolContractAddress.FeeJuice, this.enforceFees),
27
49
  );
28
50
  }
29
51
 
30
- validatorForProcessedTxs(): TxValidator<ProcessedTx> {
31
- return new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb, this.contractDataSource));
52
+ validatorForProcessedTxs(fork: MerkleTreeReadOperations): TxValidator<ProcessedTx> {
53
+ return new DoubleSpendTxValidator({
54
+ getNullifierIndex: nullifier => fork.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()),
55
+ });
32
56
  }
33
57
  }