@aztec/sequencer-client 3.0.0-nightly.20251221 → 3.0.0-nightly.20251223

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 (79) hide show
  1. package/dest/client/sequencer-client.d.ts +9 -8
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +28 -24
  4. package/dest/config.d.ts +7 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +63 -26
  7. package/dest/global_variable_builder/global_builder.d.ts +16 -8
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +35 -26
  10. package/dest/index.d.ts +2 -2
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -1
  13. package/dest/publisher/config.d.ts +3 -3
  14. package/dest/publisher/config.d.ts.map +1 -1
  15. package/dest/publisher/config.js +2 -2
  16. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
  17. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  18. package/dest/publisher/sequencer-publisher-factory.js +1 -1
  19. package/dest/publisher/sequencer-publisher-metrics.d.ts +3 -3
  20. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  21. package/dest/publisher/sequencer-publisher.d.ts +11 -24
  22. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  23. package/dest/publisher/sequencer-publisher.js +50 -62
  24. package/dest/sequencer/block_builder.d.ts +1 -3
  25. package/dest/sequencer/block_builder.d.ts.map +1 -1
  26. package/dest/sequencer/block_builder.js +4 -2
  27. package/dest/sequencer/checkpoint_builder.d.ts +63 -0
  28. package/dest/sequencer/checkpoint_builder.d.ts.map +1 -0
  29. package/dest/sequencer/checkpoint_builder.js +131 -0
  30. package/dest/sequencer/checkpoint_proposal_job.d.ts +73 -0
  31. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
  32. package/dest/sequencer/checkpoint_proposal_job.js +638 -0
  33. package/dest/sequencer/checkpoint_voter.d.ts +34 -0
  34. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -0
  35. package/dest/sequencer/checkpoint_voter.js +85 -0
  36. package/dest/sequencer/events.d.ts +46 -0
  37. package/dest/sequencer/events.d.ts.map +1 -0
  38. package/dest/sequencer/events.js +1 -0
  39. package/dest/sequencer/index.d.ts +5 -1
  40. package/dest/sequencer/index.d.ts.map +1 -1
  41. package/dest/sequencer/index.js +4 -0
  42. package/dest/sequencer/metrics.d.ts +3 -1
  43. package/dest/sequencer/metrics.d.ts.map +1 -1
  44. package/dest/sequencer/metrics.js +9 -0
  45. package/dest/sequencer/sequencer.d.ts +87 -127
  46. package/dest/sequencer/sequencer.d.ts.map +1 -1
  47. package/dest/sequencer/sequencer.js +179 -596
  48. package/dest/sequencer/timetable.d.ts +33 -13
  49. package/dest/sequencer/timetable.d.ts.map +1 -1
  50. package/dest/sequencer/timetable.js +73 -39
  51. package/dest/sequencer/types.d.ts +3 -0
  52. package/dest/sequencer/types.d.ts.map +1 -0
  53. package/dest/sequencer/types.js +1 -0
  54. package/dest/sequencer/utils.d.ts +14 -8
  55. package/dest/sequencer/utils.d.ts.map +1 -1
  56. package/dest/sequencer/utils.js +7 -4
  57. package/dest/test/index.d.ts +3 -1
  58. package/dest/test/index.d.ts.map +1 -1
  59. package/package.json +27 -27
  60. package/src/client/sequencer-client.ts +24 -31
  61. package/src/config.ts +68 -25
  62. package/src/global_variable_builder/global_builder.ts +45 -39
  63. package/src/index.ts +2 -0
  64. package/src/publisher/config.ts +3 -3
  65. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  66. package/src/publisher/sequencer-publisher-metrics.ts +2 -2
  67. package/src/publisher/sequencer-publisher.ts +71 -74
  68. package/src/sequencer/block_builder.ts +4 -1
  69. package/src/sequencer/checkpoint_builder.ts +217 -0
  70. package/src/sequencer/checkpoint_proposal_job.ts +701 -0
  71. package/src/sequencer/checkpoint_voter.ts +105 -0
  72. package/src/sequencer/events.ts +27 -0
  73. package/src/sequencer/index.ts +4 -0
  74. package/src/sequencer/metrics.ts +11 -0
  75. package/src/sequencer/sequencer.ts +275 -804
  76. package/src/sequencer/timetable.ts +84 -49
  77. package/src/sequencer/types.ts +6 -0
  78. package/src/sequencer/utils.ts +18 -9
  79. package/src/test/index.ts +2 -0
@@ -1,6 +1,5 @@
1
- import { L2Block } from '@aztec/aztec.js/block';
1
+ import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
2
2
  import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
3
- import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
4
3
  import type { EpochCache } from '@aztec/epoch-cache';
5
4
  import type { L1ContractsConfig } from '@aztec/ethereum/config';
6
5
  import {
@@ -27,6 +26,7 @@ import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/eth
27
26
  import { sumBigint } from '@aztec/foundation/bigint';
28
27
  import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
29
28
  import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
29
+ import { pick } from '@aztec/foundation/collection';
30
30
  import type { Fr } from '@aztec/foundation/curves/bn254';
31
31
  import { EthAddress } from '@aztec/foundation/eth-address';
32
32
  import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
@@ -35,10 +35,11 @@ import { bufferToHex } from '@aztec/foundation/string';
35
35
  import { DateProvider, Timer } from '@aztec/foundation/timer';
36
36
  import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
37
37
  import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
38
- import { CommitteeAttestation, CommitteeAttestationsAndSigners, type ValidateBlockResult } from '@aztec/stdlib/block';
38
+ import { CommitteeAttestationsAndSigners, type ValidateBlockResult } from '@aztec/stdlib/block';
39
+ import type { Checkpoint } from '@aztec/stdlib/checkpoint';
39
40
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
40
41
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
41
- import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
42
+ import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
42
43
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
43
44
 
44
45
  import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
@@ -114,7 +115,7 @@ export class SequencerPublisher {
114
115
  protected log: Logger;
115
116
  protected ethereumSlotDuration: bigint;
116
117
 
117
- private blobSinkClient: BlobSinkClientInterface;
118
+ private blobClient: BlobClientInterface;
118
119
 
119
120
  /** Address to use for simulations in fisherman mode (actual proposer's address) */
120
121
  private proposerAddressForSimulation?: EthAddress;
@@ -144,7 +145,7 @@ export class SequencerPublisher {
144
145
  private config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
145
146
  deps: {
146
147
  telemetry?: TelemetryClient;
147
- blobSinkClient?: BlobSinkClientInterface;
148
+ blobClient?: BlobClientInterface;
148
149
  l1TxUtils: L1TxUtilsWithBlobs;
149
150
  rollupContract: RollupContract;
150
151
  slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
@@ -162,8 +163,8 @@ export class SequencerPublisher {
162
163
  this.epochCache = deps.epochCache;
163
164
  this.lastActions = deps.lastActions;
164
165
 
165
- this.blobSinkClient =
166
- deps.blobSinkClient ?? createBlobSinkClient(config, { logger: createLogger('sequencer:blob-sink:client') });
166
+ this.blobClient =
167
+ deps.blobClient ?? createBlobClient(config, { logger: createLogger('sequencer:blob-client:client') });
167
168
 
168
169
  const telemetry = deps.telemetry ?? getTelemetryClient();
169
170
  this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
@@ -241,7 +242,7 @@ export class SequencerPublisher {
241
242
  * @returns The analysis result (incomplete until block mines), or undefined if no requests
242
243
  */
243
244
  public async analyzeL1Fees(
244
- l2SlotNumber: bigint,
245
+ l2SlotNumber: SlotNumber,
245
246
  onComplete?: (analysis: L1FeeAnalysisResult) => void,
246
247
  ): Promise<L1FeeAnalysisResult | undefined> {
247
248
  if (!this.l1FeeAnalyzer) {
@@ -299,7 +300,7 @@ export class SequencerPublisher {
299
300
  public async sendRequests() {
300
301
  const requestsToProcess = [...this.requests];
301
302
  this.requests = [];
302
- if (this.interrupted) {
303
+ if (this.interrupted || requestsToProcess.length === 0) {
303
304
  return undefined;
304
305
  }
305
306
  const currentL2Slot = this.getCurrentL2Slot();
@@ -587,45 +588,38 @@ export class SequencerPublisher {
587
588
  }
588
589
  }
589
590
 
590
- /**
591
- * @notice Will simulate `propose` to make sure that the block is valid for submission
592
- *
593
- * @dev Throws if unable to propose
594
- *
595
- * @param block - The block to propose
596
- * @param attestationData - The block's attestation data
597
- *
598
- */
599
- public async validateBlockForSubmission(
600
- block: L2Block,
591
+ /** Simulates `propose` to make sure that the checkpoint is valid for submission */
592
+ public async validateCheckpointForSubmission(
593
+ checkpoint: Checkpoint,
601
594
  attestationsAndSigners: CommitteeAttestationsAndSigners,
602
595
  attestationsAndSignersSignature: Signature,
603
- options: { forcePendingBlockNumber?: BlockNumber },
596
+ options: { forcePendingBlockNumber?: BlockNumber }, // TODO(palla/mbps): Should this be forcePendingCheckpointNumber?
604
597
  ): Promise<bigint> {
605
598
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
606
599
 
600
+ // TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
607
601
  // If we have no attestations, we still need to provide the empty attestations
608
602
  // so that the committee is recalculated correctly
609
- const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
610
- if (ignoreSignatures) {
611
- const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
612
- if (!committee) {
613
- this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
614
- throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
615
- }
616
- attestationsAndSigners.attestations = committee.map(committeeMember =>
617
- CommitteeAttestation.fromAddress(committeeMember),
618
- );
619
- }
620
-
621
- const blobFields = block.getCheckpointBlobFields();
603
+ // const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
604
+ // if (ignoreSignatures) {
605
+ // const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
606
+ // if (!committee) {
607
+ // this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
608
+ // throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
609
+ // }
610
+ // attestationsAndSigners.attestations = committee.map(committeeMember =>
611
+ // CommitteeAttestation.fromAddress(committeeMember),
612
+ // );
613
+ // }
614
+
615
+ const blobFields = checkpoint.toBlobFields();
622
616
  const blobs = getBlobsPerL1Block(blobFields);
623
617
  const blobInput = getPrefixedEthBlobCommitments(blobs);
624
618
 
625
619
  const args = [
626
620
  {
627
- header: block.getCheckpointHeader().toViem(),
628
- archive: toHex(block.archive.root.toBuffer()),
621
+ header: checkpoint.header.toViem(),
622
+ archive: toHex(checkpoint.archive.root.toBuffer()),
629
623
  oracleInput: {
630
624
  feeAssetPriceModifier: 0n,
631
625
  },
@@ -718,14 +712,14 @@ export class SequencerPublisher {
718
712
  const logData = { ...result, slotNumber, round, payload: payload.toString() };
719
713
  if (!success) {
720
714
  this.log.error(
721
- `Signaling in [${action}] for ${payload} at slot ${slotNumber} in round ${round} failed`,
715
+ `Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} failed`,
722
716
  logData,
723
717
  );
724
718
  this.lastActions[signalType] = cachedLastVote;
725
719
  return false;
726
720
  } else {
727
721
  this.log.info(
728
- `Signaling in [${action}] for ${payload} at slot ${slotNumber} in round ${round} succeeded`,
722
+ `Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} succeeded`,
729
723
  logData,
730
724
  );
731
725
  return true;
@@ -893,27 +887,21 @@ export class SequencerPublisher {
893
887
  return true;
894
888
  }
895
889
 
896
- /**
897
- * Proposes a L2 block on L1.
898
- *
899
- * @param block - L2 block to propose.
900
- * @returns True if the tx has been enqueued, throws otherwise. See #9315
901
- */
902
- public async enqueueProposeL2Block(
903
- block: L2Block,
890
+ /** Simulates and enqueues a proposal for a checkpoint on L1 */
891
+ public async enqueueProposeCheckpoint(
892
+ checkpoint: Checkpoint,
904
893
  attestationsAndSigners: CommitteeAttestationsAndSigners,
905
894
  attestationsAndSignersSignature: Signature,
906
895
  opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
907
- ): Promise<boolean> {
908
- const checkpointHeader = block.getCheckpointHeader();
896
+ ): Promise<void> {
897
+ const checkpointHeader = checkpoint.header;
909
898
 
910
- const blobFields = block.getCheckpointBlobFields();
899
+ const blobFields = checkpoint.toBlobFields();
911
900
  const blobs = getBlobsPerL1Block(blobFields);
912
901
 
913
902
  const proposeTxArgs = {
914
903
  header: checkpointHeader,
915
- archive: block.archive.root.toBuffer(),
916
- body: block.body.toBuffer(),
904
+ archive: checkpoint.archive.root.toBuffer(),
917
905
  blobs,
918
906
  attestationsAndSigners,
919
907
  attestationsAndSignersSignature,
@@ -927,19 +915,23 @@ export class SequencerPublisher {
927
915
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
928
916
  // make time consistency checks break.
929
917
  // TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
930
- ts = await this.validateBlockForSubmission(block, attestationsAndSigners, attestationsAndSignersSignature, opts);
918
+ ts = await this.validateCheckpointForSubmission(
919
+ checkpoint,
920
+ attestationsAndSigners,
921
+ attestationsAndSignersSignature,
922
+ opts,
923
+ );
931
924
  } catch (err: any) {
932
- this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
933
- ...block.getStats(),
934
- slotNumber: block.header.globalVariables.slotNumber,
925
+ this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
926
+ ...checkpoint.getStats(),
927
+ slotNumber: checkpoint.header.slotNumber,
935
928
  forcePendingBlockNumber: opts.forcePendingBlockNumber,
936
929
  });
937
930
  throw err;
938
931
  }
939
932
 
940
- this.log.verbose(`Enqueuing block propose transaction`, { ...block.toBlockInfo(), ...opts });
941
- await this.addProposeTx(block, proposeTxArgs, opts, ts);
942
- return true;
933
+ this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
934
+ await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
943
935
  }
944
936
 
945
937
  public enqueueInvalidateBlock(request: InvalidateBlockRequest | undefined, opts: { txTimeoutAt?: Date } = {}) {
@@ -1204,11 +1196,12 @@ export class SequencerPublisher {
1204
1196
  }
1205
1197
 
1206
1198
  private async addProposeTx(
1207
- block: L2Block,
1199
+ checkpoint: Checkpoint,
1208
1200
  encodedData: L1ProcessArgs,
1209
1201
  opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
1210
1202
  timestamp: bigint,
1211
1203
  ): Promise<void> {
1204
+ const slot = checkpoint.header.slotNumber;
1212
1205
  const timer = new Timer();
1213
1206
  const kzg = Blob.getViemKzgInstance();
1214
1207
  const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
@@ -1223,11 +1216,11 @@ export class SequencerPublisher {
1223
1216
  SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS, // We issue the simulation against the rollup contract, so we need to account for the overhead of the multicall3
1224
1217
  );
1225
1218
 
1226
- // Send the blobs to the blob sink preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
1227
- // tx fails but it does get mined. We make sure that the blobs are sent to the blob sink regardless of the tx outcome.
1219
+ // Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
1220
+ // tx fails but it does get mined. We make sure that the blobs are sent to the blob client regardless of the tx outcome.
1228
1221
  void Promise.resolve().then(() =>
1229
- this.blobSinkClient.sendBlobsToBlobSink(encodedData.blobs).catch(_err => {
1230
- this.log.error('Failed to send blobs to blob sink');
1222
+ this.blobClient.sendBlobsToFilestore(encodedData.blobs).catch(_err => {
1223
+ this.log.error('Failed to send blobs to blob client');
1231
1224
  }),
1232
1225
  );
1233
1226
 
@@ -1237,7 +1230,7 @@ export class SequencerPublisher {
1237
1230
  to: this.rollupContract.address,
1238
1231
  data: rollupData,
1239
1232
  },
1240
- lastValidL2Slot: block.header.globalVariables.slotNumber,
1233
+ lastValidL2Slot: checkpoint.header.slotNumber,
1241
1234
  gasConfig: { ...opts, gasLimit },
1242
1235
  blobConfig: {
1243
1236
  blobs: encodedData.blobs.map(b => b.data),
@@ -1252,11 +1245,12 @@ export class SequencerPublisher {
1252
1245
  receipt &&
1253
1246
  receipt.status === 'success' &&
1254
1247
  tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointProposed');
1248
+
1255
1249
  if (success) {
1256
1250
  const endBlock = receipt.blockNumber;
1257
1251
  const inclusionBlocks = Number(endBlock - startBlock);
1258
1252
  const { calldataGas, calldataSize, sender } = stats!;
1259
- const publishStats: L1PublishBlockStats = {
1253
+ const publishStats: L1PublishCheckpointStats = {
1260
1254
  gasPrice: receipt.effectiveGasPrice,
1261
1255
  gasUsed: receipt.gasUsed,
1262
1256
  blobGasUsed: receipt.blobGasUsed ?? 0n,
@@ -1265,23 +1259,26 @@ export class SequencerPublisher {
1265
1259
  calldataGas,
1266
1260
  calldataSize,
1267
1261
  sender,
1268
- ...block.getStats(),
1262
+ ...checkpoint.getStats(),
1269
1263
  eventName: 'rollup-published-to-l1',
1270
1264
  blobCount: encodedData.blobs.length,
1271
1265
  inclusionBlocks,
1272
1266
  };
1273
- this.log.info(`Published L2 block to L1 rollup contract`, { ...stats, ...block.getStats(), ...receipt });
1267
+ this.log.info(`Published checkpoint ${checkpoint.number} at slot ${slot} to rollup contract`, {
1268
+ ...stats,
1269
+ ...checkpoint.getStats(),
1270
+ ...pick(receipt, 'transactionHash', 'blockHash'),
1271
+ });
1274
1272
  this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
1275
1273
 
1276
1274
  return true;
1277
1275
  } else {
1278
1276
  this.metrics.recordFailedTx('process');
1279
- this.log.error(`Rollup process tx failed: ${errorMsg ?? 'no error message'}`, undefined, {
1280
- ...block.getStats(),
1281
- receipt,
1282
- txHash: receipt.transactionHash,
1283
- slotNumber: block.header.globalVariables.slotNumber,
1284
- });
1277
+ this.log.error(
1278
+ `Publishing checkpoint at slot ${slot} failed with ${errorMsg ?? 'no error message'}`,
1279
+ undefined,
1280
+ { ...checkpoint.getStats(), ...receipt },
1281
+ );
1285
1282
  return false;
1286
1283
  }
1287
1284
  },
@@ -33,7 +33,8 @@ import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_fa
33
33
 
34
34
  const log = createLogger('block-builder');
35
35
 
36
- export async function buildBlock(
36
+ /** Builds a block out of pending txs */
37
+ async function buildBlock(
37
38
  pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
38
39
  l1ToL2Messages: Fr[],
39
40
  newGlobalVariables: GlobalVariables,
@@ -97,8 +98,10 @@ const FullNodeBlockBuilderConfigKeys = [
97
98
  'rollupVersion',
98
99
  'txPublicSetupAllowList',
99
100
  'fakeProcessingDelayPerTxMs',
101
+ 'fakeThrowAfterProcessingTxCount',
100
102
  ] as const;
101
103
 
104
+ // TODO(palla/mbps): Try killing this in favor of the CheckpointsBuilder
102
105
  export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
103
106
  constructor(
104
107
  private config: FullNodeBlockBuilderConfig,
@@ -0,0 +1,217 @@
1
+ import { MerkleTreeId } from '@aztec/aztec.js/trees';
2
+ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
+ import { merge, pick } from '@aztec/foundation/collection';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
+ import { createLogger } from '@aztec/foundation/log';
6
+ import { bufferToHex } from '@aztec/foundation/string';
7
+ import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
8
+ import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
9
+ import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
10
+ import {
11
+ GuardedMerkleTreeOperations,
12
+ PublicContractsDB,
13
+ PublicProcessor,
14
+ createPublicTxSimulatorForBlockBuilding,
15
+ } from '@aztec/simulator/server';
16
+ import { L2BlockNew } from '@aztec/stdlib/block';
17
+ import { Checkpoint } from '@aztec/stdlib/checkpoint';
18
+ import type { ContractDataSource } from '@aztec/stdlib/contract';
19
+ import { Gas } from '@aztec/stdlib/gas';
20
+ import {
21
+ type FullNodeBlockBuilderConfig,
22
+ FullNodeBlockBuilderConfigKeys,
23
+ type MerkleTreeWriteOperations,
24
+ type PublicProcessorLimits,
25
+ } from '@aztec/stdlib/interfaces/server';
26
+ import { type CheckpointGlobalVariables, type FailedTx, GlobalVariables, Tx } from '@aztec/stdlib/tx';
27
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
28
+
29
+ import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_factory.js';
30
+
31
+ const log = createLogger('checkpoint-builder');
32
+
33
+ export interface BuildBlockInCheckpointResult {
34
+ block: L2BlockNew;
35
+ publicGas: Gas;
36
+ publicProcessorDuration: number;
37
+ numTxs: number;
38
+ failedTxs: FailedTx[];
39
+ blockBuildingTimer: Timer;
40
+ usedTxs: Tx[];
41
+ }
42
+
43
+ /**
44
+ * Builder for a single checkpoint. Handles building blocks within the checkpoint
45
+ * and completing it.
46
+ */
47
+ export class CheckpointBuilder {
48
+ constructor(
49
+ private checkpointBuilder: LightweightCheckpointBuilder,
50
+ private fork: MerkleTreeWriteOperations,
51
+ private config: FullNodeBlockBuilderConfig,
52
+ private contractDataSource: ContractDataSource,
53
+ private dateProvider: DateProvider,
54
+ private telemetryClient: TelemetryClient,
55
+ ) {}
56
+
57
+ getConstantData(): CheckpointGlobalVariables {
58
+ return this.checkpointBuilder.constants;
59
+ }
60
+
61
+ /**
62
+ * Builds a single block within this checkpoint.
63
+ */
64
+ async buildBlock(
65
+ pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
66
+ blockNumber: BlockNumber,
67
+ timestamp: bigint,
68
+ opts: PublicProcessorLimits,
69
+ ): Promise<BuildBlockInCheckpointResult> {
70
+ const blockBuildingTimer = new Timer();
71
+ const slot = this.checkpointBuilder.constants.slotNumber;
72
+
73
+ log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, { slot, blockNumber, ...opts });
74
+
75
+ const constants = this.checkpointBuilder.constants;
76
+ const globalVariables = GlobalVariables.from({
77
+ chainId: constants.chainId,
78
+ version: constants.version,
79
+ blockNumber,
80
+ slotNumber: constants.slotNumber,
81
+ timestamp,
82
+ coinbase: constants.coinbase,
83
+ feeRecipient: constants.feeRecipient,
84
+ gasFees: constants.gasFees,
85
+ });
86
+ const { processor, validator } = await this.makeBlockBuilderDeps(globalVariables, this.fork);
87
+
88
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
89
+ processor.process(pendingTxs, opts, validator),
90
+ );
91
+
92
+ // Add block to checkpoint
93
+ const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs);
94
+
95
+ // How much public gas was processed
96
+ const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
97
+
98
+ const res = {
99
+ block,
100
+ publicGas,
101
+ publicProcessorDuration,
102
+ numTxs: processedTxs.length,
103
+ failedTxs,
104
+ blockBuildingTimer,
105
+ usedTxs,
106
+ };
107
+ log.debug('Built block within checkpoint', res.block.header);
108
+ return res;
109
+ }
110
+
111
+ /** Completes the checkpoint and returns it. */
112
+ async completeCheckpoint(): Promise<Checkpoint> {
113
+ const checkpoint = await this.checkpointBuilder.completeCheckpoint();
114
+
115
+ log.verbose(`Completed checkpoint ${checkpoint.number}`, {
116
+ checkpointNumber: checkpoint.number,
117
+ numBlocks: checkpoint.blocks.length,
118
+ archiveRoot: checkpoint.archive.root.toString(),
119
+ });
120
+
121
+ return checkpoint;
122
+ }
123
+
124
+ /** Gets the checkpoint currently in progress. */
125
+ getCheckpoint(): Promise<Checkpoint> {
126
+ return this.checkpointBuilder.clone().completeCheckpoint();
127
+ }
128
+
129
+ protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
130
+ const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
131
+ const contractsDB = new PublicContractsDB(this.contractDataSource);
132
+ const guardedFork = new GuardedMerkleTreeOperations(fork);
133
+
134
+ const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
135
+ guardedFork,
136
+ contractsDB,
137
+ globalVariables,
138
+ this.telemetryClient,
139
+ );
140
+
141
+ const processor = new PublicProcessor(
142
+ globalVariables,
143
+ guardedFork,
144
+ contractsDB,
145
+ publicTxSimulator,
146
+ this.dateProvider,
147
+ this.telemetryClient,
148
+ undefined,
149
+ this.config,
150
+ );
151
+
152
+ const validator = createValidatorForBlockBuilding(
153
+ fork,
154
+ this.contractDataSource,
155
+ globalVariables,
156
+ txPublicSetupAllowList,
157
+ );
158
+
159
+ return {
160
+ processor,
161
+ validator,
162
+ };
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Factory for creating checkpoint builders.
168
+ */
169
+ export class FullNodeCheckpointsBuilder {
170
+ constructor(
171
+ private config: FullNodeBlockBuilderConfig,
172
+ private contractDataSource: ContractDataSource,
173
+ private dateProvider: DateProvider,
174
+ private telemetryClient: TelemetryClient = getTelemetryClient(),
175
+ ) {}
176
+
177
+ public updateConfig(config: Partial<FullNodeBlockBuilderConfig>) {
178
+ this.config = merge(this.config, pick(config, ...FullNodeBlockBuilderConfigKeys));
179
+ }
180
+
181
+ /**
182
+ * Starts a new checkpoint and returns a CheckpointBuilder to build blocks within it.
183
+ */
184
+ async startCheckpoint(
185
+ checkpointNumber: CheckpointNumber,
186
+ constants: CheckpointGlobalVariables,
187
+ l1ToL2Messages: Fr[],
188
+ fork: MerkleTreeWriteOperations,
189
+ ): Promise<CheckpointBuilder> {
190
+ const stateReference = await fork.getStateReference();
191
+ const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
192
+
193
+ log.verbose(`Building checkpoint ${checkpointNumber}`, {
194
+ checkpointNumber,
195
+ msgCount: l1ToL2Messages.length,
196
+ initialStateReference: stateReference.toInspect(),
197
+ initialArchiveRoot: bufferToHex(archiveTree.root),
198
+ constants,
199
+ });
200
+
201
+ const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
202
+ checkpointNumber,
203
+ constants,
204
+ l1ToL2Messages,
205
+ fork,
206
+ );
207
+
208
+ return new CheckpointBuilder(
209
+ lightweightBuilder,
210
+ fork,
211
+ this.config,
212
+ this.contractDataSource,
213
+ this.dateProvider,
214
+ this.telemetryClient,
215
+ );
216
+ }
217
+ }