@aztec/sequencer-client 0.87.6 → 1.0.0-nightly.20250604

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 (50) hide show
  1. package/dest/client/sequencer-client.d.ts +5 -5
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +8 -8
  4. package/dest/global_variable_builder/global_builder.d.ts +4 -1
  5. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  6. package/dest/global_variable_builder/global_builder.js +14 -2
  7. package/dest/index.d.ts +1 -2
  8. package/dest/index.d.ts.map +1 -1
  9. package/dest/index.js +1 -2
  10. package/dest/publisher/config.js +1 -4
  11. package/dest/publisher/sequencer-publisher.d.ts +4 -4
  12. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  13. package/dest/publisher/sequencer-publisher.js +23 -14
  14. package/dest/sequencer/block_builder.d.ts +33 -0
  15. package/dest/sequencer/block_builder.d.ts.map +1 -0
  16. package/dest/sequencer/block_builder.js +109 -0
  17. package/dest/sequencer/index.d.ts +1 -0
  18. package/dest/sequencer/index.d.ts.map +1 -1
  19. package/dest/sequencer/index.js +1 -0
  20. package/dest/sequencer/sequencer.d.ts +17 -64
  21. package/dest/sequencer/sequencer.d.ts.map +1 -1
  22. package/dest/sequencer/sequencer.js +74 -173
  23. package/dest/sequencer/utils.d.ts +2 -2
  24. package/dest/sequencer/utils.d.ts.map +1 -1
  25. package/dest/sequencer/utils.js +6 -4
  26. package/dest/tx_validator/tx_validator_factory.d.ts +2 -6
  27. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  28. package/package.json +26 -25
  29. package/src/client/sequencer-client.ts +12 -20
  30. package/src/global_variable_builder/global_builder.ts +16 -2
  31. package/src/index.ts +1 -2
  32. package/src/publisher/config.ts +1 -1
  33. package/src/publisher/sequencer-publisher.ts +32 -21
  34. package/src/sequencer/block_builder.ts +192 -0
  35. package/src/sequencer/index.ts +1 -0
  36. package/src/sequencer/sequencer.ts +96 -221
  37. package/src/sequencer/utils.ts +14 -6
  38. package/src/tx_validator/tx_validator_factory.ts +2 -4
  39. package/dest/slasher/factory.d.ts +0 -7
  40. package/dest/slasher/factory.d.ts.map +0 -1
  41. package/dest/slasher/factory.js +0 -8
  42. package/dest/slasher/index.d.ts +0 -3
  43. package/dest/slasher/index.d.ts.map +0 -1
  44. package/dest/slasher/index.js +0 -2
  45. package/dest/slasher/slasher_client.d.ts +0 -75
  46. package/dest/slasher/slasher_client.d.ts.map +0 -1
  47. package/dest/slasher/slasher_client.js +0 -135
  48. package/src/slasher/factory.ts +0 -15
  49. package/src/slasher/index.ts +0 -2
  50. package/src/slasher/slasher_client.ts +0 -199
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/sequencer-client",
3
- "version": "0.87.6",
3
+ "version": "1.0.0-nightly.20250604",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -26,36 +26,37 @@
26
26
  "test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
27
27
  },
28
28
  "dependencies": {
29
- "@aztec/aztec.js": "0.87.6",
30
- "@aztec/bb-prover": "0.87.6",
31
- "@aztec/blob-lib": "0.87.6",
32
- "@aztec/blob-sink": "0.87.6",
33
- "@aztec/constants": "0.87.6",
34
- "@aztec/epoch-cache": "0.87.6",
35
- "@aztec/ethereum": "0.87.6",
36
- "@aztec/foundation": "0.87.6",
37
- "@aztec/l1-artifacts": "0.87.6",
38
- "@aztec/merkle-tree": "0.87.6",
39
- "@aztec/noir-acvm_js": "0.87.6",
40
- "@aztec/noir-contracts.js": "0.87.6",
41
- "@aztec/noir-protocol-circuits-types": "0.87.6",
42
- "@aztec/noir-types": "0.87.6",
43
- "@aztec/p2p": "0.87.6",
44
- "@aztec/protocol-contracts": "0.87.6",
45
- "@aztec/prover-client": "0.87.6",
46
- "@aztec/simulator": "0.87.6",
47
- "@aztec/stdlib": "0.87.6",
48
- "@aztec/telemetry-client": "0.87.6",
49
- "@aztec/validator-client": "0.87.6",
50
- "@aztec/world-state": "0.87.6",
29
+ "@aztec/aztec.js": "1.0.0-nightly.20250604",
30
+ "@aztec/bb-prover": "1.0.0-nightly.20250604",
31
+ "@aztec/blob-lib": "1.0.0-nightly.20250604",
32
+ "@aztec/blob-sink": "1.0.0-nightly.20250604",
33
+ "@aztec/constants": "1.0.0-nightly.20250604",
34
+ "@aztec/epoch-cache": "1.0.0-nightly.20250604",
35
+ "@aztec/ethereum": "1.0.0-nightly.20250604",
36
+ "@aztec/foundation": "1.0.0-nightly.20250604",
37
+ "@aztec/l1-artifacts": "1.0.0-nightly.20250604",
38
+ "@aztec/merkle-tree": "1.0.0-nightly.20250604",
39
+ "@aztec/noir-acvm_js": "1.0.0-nightly.20250604",
40
+ "@aztec/noir-contracts.js": "1.0.0-nightly.20250604",
41
+ "@aztec/noir-protocol-circuits-types": "1.0.0-nightly.20250604",
42
+ "@aztec/noir-types": "1.0.0-nightly.20250604",
43
+ "@aztec/p2p": "1.0.0-nightly.20250604",
44
+ "@aztec/protocol-contracts": "1.0.0-nightly.20250604",
45
+ "@aztec/prover-client": "1.0.0-nightly.20250604",
46
+ "@aztec/simulator": "1.0.0-nightly.20250604",
47
+ "@aztec/slasher": "1.0.0-nightly.20250604",
48
+ "@aztec/stdlib": "1.0.0-nightly.20250604",
49
+ "@aztec/telemetry-client": "1.0.0-nightly.20250604",
50
+ "@aztec/validator-client": "1.0.0-nightly.20250604",
51
+ "@aztec/world-state": "1.0.0-nightly.20250604",
51
52
  "lodash.chunk": "^4.2.0",
52
53
  "lodash.pick": "^4.4.0",
53
54
  "tslib": "^2.4.0",
54
55
  "viem": "2.23.7"
55
56
  },
56
57
  "devDependencies": {
57
- "@aztec/archiver": "0.87.6",
58
- "@aztec/kv-store": "0.87.6",
58
+ "@aztec/archiver": "1.0.0-nightly.20250604",
59
+ "@aztec/kv-store": "1.0.0-nightly.20250604",
59
60
  "@jest/globals": "^29.5.0",
60
61
  "@types/jest": "^29.5.0",
61
62
  "@types/lodash.chunk": "^4.2.7",
@@ -14,12 +14,10 @@ import { EthAddress } from '@aztec/foundation/eth-address';
14
14
  import { createLogger } from '@aztec/foundation/log';
15
15
  import type { DateProvider } from '@aztec/foundation/timer';
16
16
  import type { P2P } from '@aztec/p2p';
17
- import { LightweightBlockBuilderFactory } from '@aztec/prover-client/block-builder';
18
- import { PublicProcessorFactory } from '@aztec/simulator/server';
17
+ import type { SlasherClient } from '@aztec/slasher';
19
18
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
20
19
  import type { L2BlockSource } from '@aztec/stdlib/block';
21
- import type { ContractDataSource } from '@aztec/stdlib/contract';
22
- import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
20
+ import type { IFullNodeBlockBuilder, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
23
21
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
24
22
  import type { TelemetryClient } from '@aztec/telemetry-client';
25
23
  import type { ValidatorClient } from '@aztec/validator-client';
@@ -28,7 +26,6 @@ import type { SequencerClientConfig } from '../config.js';
28
26
  import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
29
27
  import { SequencerPublisher } from '../publisher/index.js';
30
28
  import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
31
- import type { SlasherClient } from '../slasher/index.js';
32
29
 
33
30
  /**
34
31
  * Encapsulates the full sequencer and publisher.
@@ -55,7 +52,7 @@ export class SequencerClient {
55
52
  p2pClient: P2P;
56
53
  worldStateSynchronizer: WorldStateSynchronizer;
57
54
  slasherClient: SlasherClient;
58
- contractDataSource: ContractDataSource;
55
+ blockBuilder: IFullNodeBlockBuilder;
59
56
  l2BlockSource: L2BlockSource;
60
57
  l1ToL2MessageSource: L1ToL2MessageSource;
61
58
  telemetry: TelemetryClient;
@@ -71,7 +68,7 @@ export class SequencerClient {
71
68
  p2pClient,
72
69
  worldStateSynchronizer,
73
70
  slasherClient,
74
- contractDataSource,
71
+ blockBuilder,
75
72
  l2BlockSource,
76
73
  l1ToL2MessageSource,
77
74
  telemetry: telemetryClient,
@@ -93,12 +90,7 @@ export class SequencerClient {
93
90
  config.customForwarderContractAddress.toString(),
94
91
  config.l1Contracts.rollupAddress.toString(),
95
92
  )
96
- : await ForwarderContract.create(
97
- l1Client.account.address,
98
- l1Client,
99
- log,
100
- config.l1Contracts.rollupAddress.toString(),
101
- );
93
+ : await ForwarderContract.create(l1Client, log, config.l1Contracts.rollupAddress.toString());
102
94
 
103
95
  const governanceProposerContract = new GovernanceProposerContract(
104
96
  l1Client,
@@ -136,8 +128,6 @@ export class SequencerClient {
136
128
  });
137
129
  const globalsBuilder = new GlobalVariableBuilder(config);
138
130
 
139
- const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, deps.dateProvider, telemetryClient);
140
-
141
131
  const ethereumSlotDuration = config.ethereumSlotDuration;
142
132
 
143
133
  const rollupManaLimit = Number(await rollupContract.getManaLimit());
@@ -171,11 +161,9 @@ export class SequencerClient {
171
161
  p2pClient,
172
162
  worldStateSynchronizer,
173
163
  slasherClient,
174
- new LightweightBlockBuilderFactory(telemetryClient),
175
164
  l2BlockSource,
176
165
  l1ToL2MessageSource,
177
- publicProcessorFactory,
178
- contractDataSource,
166
+ blockBuilder,
179
167
  l1Constants,
180
168
  deps.dateProvider,
181
169
  { ...config, maxL1TxInclusionTimeIntoSlot, maxL2BlockGas: sequencerManaLimit },
@@ -213,6 +201,10 @@ export class SequencerClient {
213
201
  this.sequencer.restart();
214
202
  }
215
203
 
204
+ public getSequencer(): Sequencer {
205
+ return this.sequencer;
206
+ }
207
+
216
208
  get coinbase(): EthAddress {
217
209
  return this.sequencer.coinbase;
218
210
  }
@@ -225,8 +217,8 @@ export class SequencerClient {
225
217
  return this.sequencer.getForwarderAddress();
226
218
  }
227
219
 
228
- get validatorAddress(): EthAddress | undefined {
229
- return this.sequencer.getValidatorAddress();
220
+ get validatorAddresses(): EthAddress[] | undefined {
221
+ return this.sequencer.getValidatorAddresses();
230
222
  }
231
223
 
232
224
  get maxL2BlockGas(): number | undefined {
@@ -20,6 +20,8 @@ import { createPublicClient, fallback, http } from 'viem';
20
20
  */
21
21
  export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
22
22
  private log = createLogger('sequencer:global_variable_builder');
23
+ private currentBaseFees: Promise<GasFees> = Promise.resolve(new GasFees(Fr.ZERO, Fr.ZERO));
24
+ private currentL1BlockNumber: bigint | undefined = undefined;
23
25
 
24
26
  private readonly rollupContract: RollupContract;
25
27
  private readonly publicClient: ViemPublicClient;
@@ -46,9 +48,9 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
46
48
 
47
49
  /**
48
50
  * Computes the "current" base fees, e.g., the price that you currently should pay to get include in the next block
49
- * @returns Base fees for the expected next block
51
+ * @returns Base fees for the next block
50
52
  */
51
- public async getCurrentBaseFees(): Promise<GasFees> {
53
+ private async computeCurrentBaseFees(): Promise<GasFees> {
52
54
  // Since this might be called in the middle of a slot where a block might have been published,
53
55
  // we need to fetch the last block written, and estimate the earliest timestamp for the next block.
54
56
  // The timestamp of that last block will act as a lower bound for the next block.
@@ -61,6 +63,18 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
61
63
  return new GasFees(Fr.ZERO, new Fr(await this.rollupContract.getManaBaseFeeAt(timestamp, true)));
62
64
  }
63
65
 
66
+ public async getCurrentBaseFees(): Promise<GasFees> {
67
+ // Get the current block number
68
+ const blockNumber = await this.publicClient.getBlockNumber();
69
+
70
+ // If the L1 block number has changed then chain a new promise to get the current base fees
71
+ if (this.currentL1BlockNumber === undefined || blockNumber > this.currentL1BlockNumber) {
72
+ this.currentL1BlockNumber = blockNumber;
73
+ this.currentBaseFees = this.currentBaseFees.then(() => this.computeCurrentBaseFees());
74
+ }
75
+ return this.currentBaseFees;
76
+ }
77
+
64
78
  public async getGlobalConstantVariables(): Promise<Pick<GlobalVariables, 'chainId' | 'version'>> {
65
79
  if (!this.chainId) {
66
80
  this.chainId = new Fr(this.publicClient.chain.id);
package/src/index.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  export * from './client/index.js';
2
2
  export * from './config.js';
3
3
  export * from './publisher/index.js';
4
+ export { FullNodeBlockBuilder as BlockBuilder, Sequencer, SequencerState } from './sequencer/index.js';
4
5
  export * from './tx_validator/tx_validator_factory.js';
5
- export * from './slasher/index.js';
6
- export { Sequencer, SequencerState } from './sequencer/index.js';
7
6
 
8
7
  // Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
9
8
  // ISSUE(#9832)
@@ -50,7 +50,7 @@ export const getTxSenderConfigMappings: (
50
50
  description: 'The private key to be used by the publisher.',
51
51
  parseEnv: (val: string) => (val ? `0x${val.replace('0x', '')}` : NULL_KEY),
52
52
  defaultValue: NULL_KEY,
53
- fallback: ['VALIDATOR_PRIVATE_KEY'],
53
+ // fallback: ['VALIDATOR_PRIVATE_KEY'],
54
54
  },
55
55
  });
56
56
 
@@ -20,10 +20,10 @@ import {
20
20
  import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
21
21
  import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
22
22
  import { EthAddress } from '@aztec/foundation/eth-address';
23
- import type { Signature } from '@aztec/foundation/eth-signature';
24
23
  import { createLogger } from '@aztec/foundation/log';
25
24
  import { Timer } from '@aztec/foundation/timer';
26
25
  import { ForwarderAbi, RollupAbi } from '@aztec/l1-artifacts';
26
+ import { CommitteeAttestation } from '@aztec/stdlib/block';
27
27
  import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/stdlib/p2p';
28
28
  import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
29
29
  import { type ProposedBlockHeader, TxHash } from '@aztec/stdlib/tx';
@@ -48,7 +48,7 @@ type L1ProcessArgs = {
48
48
  /** L2 block tx hashes */
49
49
  txHashes: TxHash[];
50
50
  /** Attestations */
51
- attestations?: Signature[];
51
+ attestations?: CommitteeAttestation[];
52
52
  };
53
53
 
54
54
  export enum VoteType {
@@ -74,7 +74,7 @@ interface RequestWithExpiry {
74
74
  export class SequencerPublisher {
75
75
  private interrupted = false;
76
76
  private metrics: SequencerPublisherMetrics;
77
- private epochCache: EpochCache;
77
+ public epochCache: EpochCache;
78
78
  private forwarderContract: ForwarderContract;
79
79
 
80
80
  protected governanceLog = createLogger('sequencer:publisher:governance');
@@ -264,7 +264,9 @@ export class SequencerPublisher {
264
264
  * @returns The slot and block number if it is possible to propose, undefined otherwise
265
265
  */
266
266
  public canProposeAtNextEthBlock(tipArchive: Buffer) {
267
+ // TODO: #14291 - should loop through multiple keys to check if any of them can propose
267
268
  const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
269
+
268
270
  return this.rollupContract
269
271
  .canProposeAtNextEthBlock(tipArchive, this.getForwarderAddress().toString(), this.ethereumSlotDuration)
270
272
  .catch(err => {
@@ -288,19 +290,29 @@ export class SequencerPublisher {
288
290
  */
289
291
  public async validateBlockForSubmission(
290
292
  header: ProposedBlockHeader,
291
- attestationData: { digest: Buffer; signatures: Signature[] } = {
293
+ attestationData: { digest: Buffer; attestations: CommitteeAttestation[] } = {
292
294
  digest: Buffer.alloc(32),
293
- signatures: [],
295
+ attestations: [],
294
296
  },
295
297
  ): Promise<bigint> {
296
298
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
297
299
 
298
- const formattedSignatures = attestationData.signatures.map(attest => attest.toViemSignature());
299
- const flags = { ignoreDA: true, ignoreSignatures: formattedSignatures.length == 0 };
300
+ // If we have no attestations, we still need to provide the empty attestations
301
+ // so that the committee is recalculated correctly
302
+ const ignoreSignatures = attestationData.attestations.length === 0;
303
+ if (ignoreSignatures) {
304
+ const committee = await this.epochCache.getCommittee(header.slotNumber.toBigInt());
305
+ attestationData.attestations = committee.committee.map(committeeMember =>
306
+ CommitteeAttestation.fromAddress(committeeMember),
307
+ );
308
+ }
309
+
310
+ const formattedAttestations = attestationData.attestations.map(attest => attest.toViem());
311
+ const flags = { ignoreDA: true, ignoreSignatures };
300
312
 
301
313
  const args = [
302
314
  toHex(header.toBuffer()),
303
- formattedSignatures,
315
+ formattedAttestations,
304
316
  toHex(attestationData.digest),
305
317
  ts,
306
318
  toHex(header.contentCommitment.blobsHash),
@@ -330,14 +342,8 @@ export class SequencerPublisher {
330
342
  return false;
331
343
  }
332
344
  const round = await base.computeRound(slotNumber);
333
- const [proposer, roundInfo] = await Promise.all([
334
- this.rollupContract.getProposerAt(timestamp),
335
- base.getRoundInfo(this.rollupContract.address, round),
336
- ]);
345
+ const roundInfo = await base.getRoundInfo(this.rollupContract.address, round);
337
346
 
338
- if (proposer.toLowerCase() !== this.getForwarderAddress().toString().toLowerCase()) {
339
- return false;
340
- }
341
347
  if (roundInfo.lastVote >= slotNumber) {
342
348
  return false;
343
349
  }
@@ -347,9 +353,15 @@ export class SequencerPublisher {
347
353
 
348
354
  const action = voteType === VoteType.GOVERNANCE ? 'governance-vote' : 'slashing-vote';
349
355
 
356
+ const request = await base.createVoteRequestWithSignature(payload.toString(), this.l1TxUtils.client);
357
+ this.log.debug(`Created ${action} request with signature`, {
358
+ request,
359
+ signer: this.l1TxUtils.client.account?.address,
360
+ });
361
+
350
362
  this.addRequest({
351
363
  action,
352
- request: base.createVoteRequest(payload.toString()),
364
+ request,
353
365
  lastValidL2Slot: slotNumber,
354
366
  onResult: (_request, result) => {
355
367
  if (!result || result.receipt.status !== 'success') {
@@ -376,6 +388,7 @@ export class SequencerPublisher {
376
388
  if (!slashPayload) {
377
389
  return undefined;
378
390
  }
391
+ this.log.info(`Slash payload: ${slashPayload}`);
379
392
  return { payload: slashPayload, base: this.slashingProposerContract };
380
393
  }
381
394
  throw new Error('Unreachable: Invalid vote type');
@@ -405,7 +418,7 @@ export class SequencerPublisher {
405
418
  */
406
419
  public async enqueueProposeL2Block(
407
420
  block: L2Block,
408
- attestations?: Signature[],
421
+ attestations?: CommitteeAttestation[],
409
422
  txHashes?: TxHash[],
410
423
  opts: { txTimeoutAt?: Date } = {},
411
424
  ): Promise<boolean> {
@@ -431,7 +444,7 @@ export class SequencerPublisher {
431
444
  // make time consistency checks break.
432
445
  const ts = await this.validateBlockForSubmission(proposedBlockHeader, {
433
446
  digest: digest.toBuffer(),
434
- signatures: attestations ?? [],
447
+ attestations: attestations ?? [],
435
448
  });
436
449
 
437
450
  this.log.debug(`Submitting propose transaction`);
@@ -486,9 +499,7 @@ export class SequencerPublisher {
486
499
  throw new Error('Failed to validate blobs');
487
500
  });
488
501
 
489
- const attestations = encodedData.attestations
490
- ? encodedData.attestations.map(attest => attest.toViemSignature())
491
- : [];
502
+ const attestations = encodedData.attestations ? encodedData.attestations.map(attest => attest.toViem()) : [];
492
503
  const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
493
504
  const args = [
494
505
  {
@@ -0,0 +1,192 @@
1
+ import { elapsed } from '@aztec/aztec.js';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { retryUntil } from '@aztec/foundation/retry';
4
+ import { DateProvider, Timer } from '@aztec/foundation/timer';
5
+ import { LightweightBlockFactory } from '@aztec/prover-client/block-factory';
6
+ import {
7
+ GuardedMerkleTreeOperations,
8
+ PublicContractsDB,
9
+ PublicProcessor,
10
+ TelemetryPublicTxSimulator,
11
+ } from '@aztec/simulator/server';
12
+ import type { ChainConfig } from '@aztec/stdlib/config';
13
+ import type { ContractDataSource } from '@aztec/stdlib/contract';
14
+ import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
15
+ import { Gas } from '@aztec/stdlib/gas';
16
+ import type {
17
+ BuildBlockOptions,
18
+ BuildBlockResult,
19
+ IFullNodeBlockBuilder,
20
+ MerkleTreeWriteOperations,
21
+ PublicProcessorValidator,
22
+ WorldStateSynchronizer,
23
+ } from '@aztec/stdlib/interfaces/server';
24
+ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
25
+ import { GlobalVariables, Tx } from '@aztec/stdlib/tx';
26
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
27
+
28
+ import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_factory.js';
29
+
30
+ const log = createLogger('sequencer:block-builder');
31
+
32
+ export async function buildBlock(
33
+ pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
34
+ newGlobalVariables: GlobalVariables,
35
+ opts: BuildBlockOptions = {},
36
+ l1ToL2MessageSource: L1ToL2MessageSource,
37
+ worldStateFork: MerkleTreeWriteOperations,
38
+ processor: PublicProcessor,
39
+ validator: PublicProcessorValidator,
40
+ l1Constants: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
41
+ dateProvider: DateProvider,
42
+ telemetryClient: TelemetryClient = getTelemetryClient(),
43
+ ): Promise<BuildBlockResult> {
44
+ const blockBuildingTimer = new Timer();
45
+ const blockNumber = newGlobalVariables.blockNumber.toNumber();
46
+ const slot = newGlobalVariables.slotNumber.toBigInt();
47
+ log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
48
+ const l1ToL2Messages = await l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber));
49
+ const msgCount = l1ToL2Messages.length;
50
+
51
+ log.verbose(`Building block ${blockNumber} for slot ${slot}`, {
52
+ slot,
53
+ slotStart: new Date(Number(getTimestampForSlot(slot, l1Constants)) * 1000),
54
+ now: new Date(dateProvider.now()),
55
+ blockNumber,
56
+ msgCount,
57
+ opts,
58
+ });
59
+
60
+ try {
61
+ const blockFactory = new LightweightBlockFactory(worldStateFork, telemetryClient);
62
+ await blockFactory.startNewBlock(newGlobalVariables, l1ToL2Messages);
63
+
64
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
65
+ processor.process(pendingTxs, opts, validator),
66
+ );
67
+
68
+ // All real transactions have been added, set the block as full and pad if needed
69
+ await blockFactory.addTxs(processedTxs);
70
+ const block = await blockFactory.setBlockCompleted();
71
+
72
+ // How much public gas was processed
73
+ const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
74
+
75
+ const res = {
76
+ block,
77
+ publicGas,
78
+ publicProcessorDuration,
79
+ numMsgs: l1ToL2Messages.length,
80
+ numTxs: processedTxs.length,
81
+ failedTxs: failedTxs,
82
+ blockBuildingTimer,
83
+ usedTxs,
84
+ };
85
+ log.trace('Built block', res.block.header);
86
+ return res;
87
+ } finally {
88
+ // We wait a bit to close the forks since the processor may still be working on a dangling tx
89
+ // which was interrupted due to the processingDeadline being hit.
90
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
91
+ setTimeout(async () => {
92
+ try {
93
+ await worldStateFork.close();
94
+ } catch (err) {
95
+ // This can happen if the sequencer is stopped before we hit this timeout.
96
+ log.warn(`Error closing forks for block processing`, err);
97
+ }
98
+ }, 5000);
99
+ }
100
+ }
101
+
102
+ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
103
+ constructor(
104
+ private config: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'> &
105
+ Pick<ChainConfig, 'l1ChainId' | 'rollupVersion'>,
106
+ private l1ToL2MessageSource: L1ToL2MessageSource,
107
+ private worldState: WorldStateSynchronizer,
108
+ private contractDataSource: ContractDataSource,
109
+ private dateProvider: DateProvider,
110
+ private telemetryClient: TelemetryClient = getTelemetryClient(),
111
+ ) {}
112
+
113
+ public getConfig() {
114
+ return {
115
+ l1GenesisTime: this.config.l1GenesisTime,
116
+ slotDuration: this.config.slotDuration,
117
+ l1ChainId: this.config.l1ChainId,
118
+ rollupVersion: this.config.rollupVersion,
119
+ };
120
+ }
121
+
122
+ public async makeBlockBuilderDeps(globalVariables: GlobalVariables, opts: BuildBlockOptions) {
123
+ const publicProcessorDBFork = await this.worldState.fork();
124
+ const contractsDB = new PublicContractsDB(this.contractDataSource);
125
+ const guardedFork = new GuardedMerkleTreeOperations(publicProcessorDBFork);
126
+
127
+ const publicTxSimulator = new TelemetryPublicTxSimulator(
128
+ guardedFork,
129
+ contractsDB,
130
+ globalVariables,
131
+ /*doMerkleOperations=*/ true,
132
+ /*skipFeeEnforcement=*/ true,
133
+ /*clientInitiatedSimulation=*/ false,
134
+ this.telemetryClient,
135
+ );
136
+
137
+ const processor = new PublicProcessor(
138
+ globalVariables,
139
+ guardedFork,
140
+ contractsDB,
141
+ publicTxSimulator,
142
+ this.dateProvider,
143
+ this.telemetryClient,
144
+ );
145
+ const validator = createValidatorForBlockBuilding(
146
+ publicProcessorDBFork,
147
+ this.contractDataSource,
148
+ globalVariables,
149
+ opts.txPublicSetupAllowList ?? [],
150
+ );
151
+
152
+ return {
153
+ publicProcessorDBFork,
154
+ processor,
155
+ validator,
156
+ };
157
+ }
158
+
159
+ private async syncToPreviousBlock(parentBlockNumber: number, timeout: number | undefined) {
160
+ await retryUntil(
161
+ () => this.worldState.syncImmediate(parentBlockNumber, true).then(syncedTo => syncedTo >= parentBlockNumber),
162
+ 'sync to previous block',
163
+ timeout,
164
+ 0.1,
165
+ );
166
+ log.debug(`Synced to previous block ${parentBlockNumber}`);
167
+ }
168
+
169
+ async buildBlock(
170
+ pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
171
+ globalVariables: GlobalVariables,
172
+ opts: BuildBlockOptions,
173
+ ): Promise<BuildBlockResult> {
174
+ const parentBlockNumber = globalVariables.blockNumber.toNumber() - 1;
175
+ const syncTimeout = opts.deadline ? (opts.deadline.getTime() - this.dateProvider.now()) / 1000 : undefined;
176
+ await this.syncToPreviousBlock(parentBlockNumber, syncTimeout);
177
+ const { publicProcessorDBFork, processor, validator } = await this.makeBlockBuilderDeps(globalVariables, opts);
178
+
179
+ return buildBlock(
180
+ pendingTxs,
181
+ globalVariables,
182
+ opts,
183
+ this.l1ToL2MessageSource,
184
+ publicProcessorDBFork,
185
+ processor,
186
+ validator,
187
+ this.config,
188
+ this.dateProvider,
189
+ this.telemetryClient,
190
+ );
191
+ }
192
+ }
@@ -1,2 +1,3 @@
1
+ export * from './block_builder.js';
1
2
  export * from './config.js';
2
3
  export * from './sequencer.js';