@aztec/sequencer-client 0.66.0 → 0.67.1-devnet

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 (51) hide show
  1. package/dest/client/sequencer-client.js +2 -1
  2. package/dest/config.d.ts +3 -3
  3. package/dest/config.d.ts.map +1 -1
  4. package/dest/config.js +5 -58
  5. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  6. package/dest/global_variable_builder/global_builder.js +3 -4
  7. package/dest/index.d.ts +0 -1
  8. package/dest/index.d.ts.map +1 -1
  9. package/dest/index.js +1 -2
  10. package/dest/publisher/index.d.ts +0 -1
  11. package/dest/publisher/index.d.ts.map +1 -1
  12. package/dest/publisher/index.js +1 -2
  13. package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -1
  14. package/dest/publisher/l1-publisher-metrics.js +2 -8
  15. package/dest/publisher/l1-publisher.d.ts +3 -2
  16. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  17. package/dest/publisher/l1-publisher.js +159 -50
  18. package/dest/publisher/utils.d.ts.map +1 -1
  19. package/dest/publisher/utils.js +2 -1
  20. package/dest/sequencer/allowed.d.ts +3 -0
  21. package/dest/sequencer/allowed.d.ts.map +1 -0
  22. package/dest/sequencer/allowed.js +34 -0
  23. package/dest/sequencer/config.d.ts +1 -1
  24. package/dest/sequencer/config.d.ts.map +1 -1
  25. package/dest/sequencer/metrics.d.ts.map +1 -1
  26. package/dest/sequencer/metrics.js +2 -8
  27. package/dest/sequencer/sequencer.d.ts +2 -6
  28. package/dest/sequencer/sequencer.d.ts.map +1 -1
  29. package/dest/sequencer/sequencer.js +112 -103
  30. package/dest/tx_validator/gas_validator.d.ts +3 -4
  31. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  32. package/dest/tx_validator/gas_validator.js +27 -10
  33. package/dest/tx_validator/phases_validator.js +3 -3
  34. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  35. package/dest/tx_validator/tx_validator_factory.js +6 -4
  36. package/package.json +27 -20
  37. package/src/client/sequencer-client.ts +1 -1
  38. package/src/config.ts +7 -62
  39. package/src/global_variable_builder/global_builder.ts +3 -3
  40. package/src/index.ts +0 -1
  41. package/src/publisher/index.ts +0 -1
  42. package/src/publisher/l1-publisher-metrics.ts +1 -7
  43. package/src/publisher/l1-publisher.ts +200 -73
  44. package/src/publisher/utils.ts +1 -0
  45. package/src/sequencer/allowed.ts +36 -0
  46. package/src/sequencer/config.ts +1 -1
  47. package/src/sequencer/metrics.ts +0 -7
  48. package/src/sequencer/sequencer.ts +136 -149
  49. package/src/tx_validator/gas_validator.ts +34 -8
  50. package/src/tx_validator/phases_validator.ts +2 -2
  51. package/src/tx_validator/tx_validator_factory.ts +11 -3
@@ -4,6 +4,7 @@ import {
4
4
  type L2Block,
5
5
  type L2BlockSource,
6
6
  type ProcessedTx,
7
+ SequencerConfigSchema,
7
8
  Tx,
8
9
  type TxHash,
9
10
  type TxValidator,
@@ -13,17 +14,19 @@ import type { AllowedElement, Signature, WorldStateSynchronizerStatus } from '@a
13
14
  import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
14
15
  import {
15
16
  AppendOnlyTreeSnapshot,
17
+ BlockHeader,
16
18
  ContentCommitment,
17
19
  GENESIS_ARCHIVE_ROOT,
18
20
  type GlobalVariables,
19
- Header,
20
21
  StateReference,
21
22
  } from '@aztec/circuits.js';
22
23
  import { AztecAddress } from '@aztec/foundation/aztec-address';
24
+ import { omit } from '@aztec/foundation/collection';
23
25
  import { EthAddress } from '@aztec/foundation/eth-address';
24
26
  import { Fr } from '@aztec/foundation/fields';
25
- import { createDebugLogger } from '@aztec/foundation/log';
27
+ import { createLogger } from '@aztec/foundation/log';
26
28
  import { RunningPromise } from '@aztec/foundation/running-promise';
29
+ import { pickFromSchema } from '@aztec/foundation/schemas';
27
30
  import { Timer, elapsed } from '@aztec/foundation/timer';
28
31
  import { type P2P } from '@aztec/p2p';
29
32
  import { type BlockBuilderFactory } from '@aztec/prover-client/block-builder';
@@ -31,12 +34,11 @@ import { type PublicProcessorFactory } from '@aztec/simulator';
31
34
  import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
32
35
  import { type ValidatorClient } from '@aztec/validator-client';
33
36
 
34
- import { inspect } from 'util';
35
-
36
37
  import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
37
38
  import { type L1Publisher } from '../publisher/l1-publisher.js';
38
39
  import { prettyLogViemErrorMsg } from '../publisher/utils.js';
39
40
  import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
41
+ import { getDefaultAllowedSetupFunctions } from './allowed.js';
40
42
  import { type SequencerConfig } from './config.js';
41
43
  import { SequencerMetrics } from './metrics.js';
42
44
  import { SequencerState, getSecondsIntoSlot, orderAttestations } from './utils.js';
@@ -75,13 +77,11 @@ export class Sequencer {
75
77
  private pollingIntervalMs: number = 1000;
76
78
  private maxTxsPerBlock = 32;
77
79
  private minTxsPerBLock = 1;
78
- private minSecondsBetweenBlocks = 0;
79
- private maxSecondsBetweenBlocks = 0;
80
80
  // TODO: zero values should not be allowed for the following 2 values in PROD
81
81
  private _coinbase = EthAddress.ZERO;
82
82
  private _feeRecipient = AztecAddress.ZERO;
83
83
  private state = SequencerState.STOPPED;
84
- private allowedInSetup: AllowedElement[] = [];
84
+ private allowedInSetup: AllowedElement[] = getDefaultAllowedSetupFunctions();
85
85
  private maxBlockSizeInBytes: number = 1024 * 1024;
86
86
  private metrics: SequencerMetrics;
87
87
  private isFlushing: boolean = false;
@@ -108,11 +108,10 @@ export class Sequencer {
108
108
  private aztecSlotDuration: number,
109
109
  telemetry: TelemetryClient,
110
110
  private config: SequencerConfig = {},
111
- private log = createDebugLogger('aztec:sequencer'),
111
+ private log = createLogger('sequencer'),
112
112
  ) {
113
113
  this.updateConfig(config);
114
114
  this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
115
- this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`);
116
115
 
117
116
  // Register the block builder with the validator client for re-execution
118
117
  this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
@@ -127,6 +126,8 @@ export class Sequencer {
127
126
  * @param config - New parameters.
128
127
  */
129
128
  public updateConfig(config: SequencerConfig) {
129
+ this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup'));
130
+
130
131
  if (config.transactionPollingIntervalMS !== undefined) {
131
132
  this.pollingIntervalMs = config.transactionPollingIntervalMS;
132
133
  }
@@ -136,12 +137,6 @@ export class Sequencer {
136
137
  if (config.minTxsPerBlock !== undefined) {
137
138
  this.minTxsPerBLock = config.minTxsPerBlock;
138
139
  }
139
- if (config.minSecondsBetweenBlocks !== undefined) {
140
- this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
141
- }
142
- if (config.maxSecondsBetweenBlocks !== undefined) {
143
- this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
144
- }
145
140
  if (config.coinbase) {
146
141
  this._coinbase = config.coinbase;
147
142
  }
@@ -187,10 +182,10 @@ export class Sequencer {
187
182
  * Starts the sequencer and moves to IDLE state.
188
183
  */
189
184
  public start() {
190
- this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
191
- this.runningPromise.start();
185
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
192
186
  this.setState(SequencerState.IDLE, 0n, true /** force */);
193
- this.log.info('Sequencer started');
187
+ this.runningPromise.start();
188
+ this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
194
189
  return Promise.resolve();
195
190
  }
196
191
 
@@ -199,6 +194,7 @@ export class Sequencer {
199
194
  */
200
195
  public async stop(): Promise<void> {
201
196
  this.log.debug(`Stopping sequencer`);
197
+ await this.validatorClient?.stop();
202
198
  await this.runningPromise?.stop();
203
199
  this.publisher.interrupt();
204
200
  this.setState(SequencerState.STOPPED, 0n, true /** force */);
@@ -237,12 +233,9 @@ export class Sequencer {
237
233
  const prevBlockSynced = await this.isBlockSynced();
238
234
  // Do not go forward with new block if the previous one has not been mined and processed
239
235
  if (!prevBlockSynced) {
240
- this.log.debug('Previous block has not been mined and processed yet');
241
236
  return;
242
237
  }
243
238
 
244
- this.log.debug('Previous block has been mined and processed');
245
-
246
239
  this.setState(SequencerState.PROPOSER_CHECK, 0n);
247
240
 
248
241
  const chainTip = await this.l2BlockSource.getBlock(-1);
@@ -278,18 +271,23 @@ export class Sequencer {
278
271
  return;
279
272
  }
280
273
 
274
+ this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
275
+ chainTipArchive: new Fr(chainTipArchive),
276
+ blockNumber: newBlockNumber,
277
+ slot,
278
+ });
279
+
281
280
  this.setState(SequencerState.WAITING_FOR_TXS, slot);
282
281
 
283
282
  // Get txs to build the new block.
284
- const pendingTxs = this.p2pClient.getTxs('pending');
283
+ const pendingTxs = await this.p2pClient.getPendingTxs();
285
284
 
286
285
  if (!this.shouldProposeBlock(historicalHeader, { pendingTxsCount: pendingTxs.length })) {
287
286
  return;
288
287
  }
289
- this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
290
288
 
291
289
  // If I created a "partial" header here that should make our job much easier.
292
- const proposalHeader = new Header(
290
+ const proposalHeader = new BlockHeader(
293
291
  new AppendOnlyTreeSnapshot(Fr.fromBuffer(chainTipArchive), 1),
294
292
  ContentCommitment.empty(),
295
293
  StateReference.empty(),
@@ -311,6 +309,10 @@ export class Sequencer {
311
309
  // may break if we start emitting lots of log data from public-land.
312
310
  const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
313
311
 
312
+ this.log.verbose(
313
+ `Collected ${validTxs.length} txs out of ${allValidTxs.length} valid txs out of ${pendingTxs.length} total pending txs for block ${newBlockNumber}`,
314
+ );
315
+
314
316
  // Bail if we don't have enough valid txs
315
317
  if (!this.shouldProposeBlock(historicalHeader, { validTxsCount: validTxs.length })) {
316
318
  return;
@@ -322,11 +324,12 @@ export class Sequencer {
322
324
  // be in for a world of pain.
323
325
  await this.buildBlockAndAttemptToPublish(validTxs, proposalHeader, historicalHeader);
324
326
  } catch (err) {
325
- this.log.error(`Error assembling block`, (err as any).stack);
327
+ this.log.error(`Error assembling block`, err, { blockNumber: newBlockNumber, slot });
326
328
  }
327
329
  this.setState(SequencerState.IDLE, 0n);
328
330
  }
329
331
 
332
+ @trackSpan('Sequencer.work')
330
333
  protected async work() {
331
334
  try {
332
335
  await this.doRealWork();
@@ -342,33 +345,20 @@ export class Sequencer {
342
345
  }
343
346
  }
344
347
 
345
- /** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
346
- private skipMinTxsPerBlockCheck(historicalHeader: Header | undefined): boolean {
347
- const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
348
- const currentTime = Math.floor(Date.now() / 1000);
349
- const elapsed = currentTime - lastBlockTime;
350
-
351
- return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
352
- }
353
-
354
348
  async mayProposeBlock(tipArchive: Buffer, proposalBlockNumber: bigint): Promise<bigint> {
355
349
  // This checks that we can propose, and gives us the slot that we are to propose for
356
350
  try {
357
351
  const [slot, blockNumber] = await this.publisher.canProposeAtNextEthBlock(tipArchive);
358
352
 
359
353
  if (proposalBlockNumber !== blockNumber) {
360
- const msg = `Block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}`;
361
- this.log.debug(msg);
354
+ const msg = `Sequencer block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}.`;
355
+ this.log.warn(msg);
362
356
  throw new Error(msg);
363
357
  }
364
-
365
- this.log.verbose(`Can propose block ${proposalBlockNumber} at slot ${slot}`, {
366
- publisherAddress: this.publisher.publisherAddress,
367
- });
368
358
  return slot;
369
359
  } catch (err) {
370
360
  const msg = prettyLogViemErrorMsg(err);
371
- this.log.verbose(
361
+ this.log.debug(
372
362
  `Rejected from being able to propose at next block with ${tipArchive.toString('hex')}: ${msg ? `${msg}` : ''}`,
373
363
  );
374
364
  throw err;
@@ -385,7 +375,6 @@ export class Sequencer {
385
375
  }
386
376
 
387
377
  const bufferSeconds = this.timeTable[proposedState] - secondsIntoSlot;
388
- this.metrics.recordStateTransitionBufferMs(bufferSeconds * 1000, proposedState);
389
378
 
390
379
  if (bufferSeconds < 0) {
391
380
  this.log.warn(
@@ -393,6 +382,9 @@ export class Sequencer {
393
382
  );
394
383
  return false;
395
384
  }
385
+
386
+ this.metrics.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), proposedState);
387
+
396
388
  this.log.debug(
397
389
  `Enough time to transition to ${proposedState}, max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
398
390
  );
@@ -410,19 +402,18 @@ export class Sequencer {
410
402
  */
411
403
  setState(proposedState: SequencerState, currentSlotNumber: bigint, force: boolean = false) {
412
404
  if (this.state === SequencerState.STOPPED && force !== true) {
413
- this.log.warn(
414
- `Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped. Set force=true to override.`,
415
- );
405
+ this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
416
406
  return;
417
407
  }
418
408
  const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration, Number(currentSlotNumber));
419
409
  if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) {
420
410
  throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot);
421
411
  }
412
+ this.log.debug(`Transitioning from ${this.state} to ${proposedState}`);
422
413
  this.state = proposedState;
423
414
  }
424
415
 
425
- shouldProposeBlock(historicalHeader: Header | undefined, args: ShouldProposeArgs): boolean {
416
+ shouldProposeBlock(historicalHeader: BlockHeader | undefined, args: ShouldProposeArgs): boolean {
426
417
  if (this.isFlushing) {
427
418
  this.log.verbose(`Flushing all pending txs in new block`);
428
419
  return true;
@@ -436,53 +427,29 @@ export class Sequencer {
436
427
  `Last block mined at ${lastBlockTime} current time is ${currentTime} (elapsed ${elapsedSinceLastBlock})`,
437
428
  );
438
429
 
439
- // If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
440
- // Do not go forward with new block if not enough time has passed since last block
441
- if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
442
- this.log.debug(
443
- `Not creating block because not enough time ${this.minSecondsBetweenBlocks} has passed since last block`,
430
+ // We need to have at least minTxsPerBLock txs.
431
+ if (args.pendingTxsCount != undefined && args.pendingTxsCount < this.minTxsPerBLock) {
432
+ this.log.verbose(
433
+ `Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
444
434
  );
445
435
  return false;
446
436
  }
447
437
 
448
- const skipCheck = this.skipMinTxsPerBlockCheck(historicalHeader);
449
-
450
- // If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
451
- if (args.pendingTxsCount != undefined) {
452
- if (args.pendingTxsCount < this.minTxsPerBLock) {
453
- if (skipCheck) {
454
- this.log.debug(
455
- `Creating block with only ${args.pendingTxsCount} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
456
- );
457
- } else {
458
- this.log.debug(
459
- `Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
460
- );
461
- return false;
462
- }
463
- }
464
- }
465
-
466
438
  // Bail if we don't have enough valid txs
467
- if (args.validTxsCount != undefined) {
468
- // Bail if we don't have enough valid txs
469
- if (!skipCheck && args.validTxsCount < this.minTxsPerBLock) {
470
- this.log.debug(
471
- `Not creating block because not enough valid txs loaded from the pool (got ${args.validTxsCount} min ${this.minTxsPerBLock})`,
472
- );
473
- return false;
474
- }
439
+ if (args.validTxsCount != undefined && args.validTxsCount < this.minTxsPerBLock) {
440
+ this.log.verbose(
441
+ `Not creating block because not enough valid txs loaded from the pool (got ${args.validTxsCount} min ${this.minTxsPerBLock})`,
442
+ );
443
+ return false;
475
444
  }
476
445
 
477
446
  // TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
478
447
  // less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
479
448
  // go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
480
449
  // we should bail.
481
- if (args.processedTxsCount != undefined) {
482
- if (args.processedTxsCount === 0 && !skipCheck && this.minTxsPerBLock > 0) {
483
- this.log.verbose('No txs processed correctly to build block. Exiting');
484
- return false;
485
- }
450
+ if (args.processedTxsCount === 0 && this.minTxsPerBLock > 0) {
451
+ this.log.verbose('No txs processed correctly to build block.');
452
+ return false;
486
453
  }
487
454
 
488
455
  return true;
@@ -501,13 +468,23 @@ export class Sequencer {
501
468
  private async buildBlock(
502
469
  validTxs: Tx[],
503
470
  newGlobalVariables: GlobalVariables,
504
- historicalHeader?: Header,
471
+ historicalHeader?: BlockHeader,
505
472
  interrupt?: (processedTxs: ProcessedTx[]) => Promise<void>,
506
473
  ) {
507
- this.log.debug('Requesting L1 to L2 messages from contract');
508
- const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(newGlobalVariables.blockNumber.toBigInt());
474
+ const blockNumber = newGlobalVariables.blockNumber.toBigInt();
475
+ const slot = newGlobalVariables.slotNumber.toBigInt();
476
+
477
+ this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
478
+ const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
479
+
509
480
  this.log.verbose(
510
- `Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newGlobalVariables.blockNumber.toNumber()}`,
481
+ `Building block ${blockNumber} with ${validTxs.length} txs and ${l1ToL2Messages.length} messages`,
482
+ {
483
+ msgCount: l1ToL2Messages.length,
484
+ txCount: validTxs.length,
485
+ slot,
486
+ blockNumber,
487
+ },
511
488
  );
512
489
 
513
490
  const numRealTxs = validTxs.length;
@@ -515,7 +492,7 @@ export class Sequencer {
515
492
 
516
493
  // Sync to the previous block at least
517
494
  await this.worldState.syncImmediate(newGlobalVariables.blockNumber.toNumber() - 1);
518
- this.log.verbose(`Synced to previous block ${newGlobalVariables.blockNumber.toNumber() - 1}`);
495
+ this.log.debug(`Synced to previous block ${newGlobalVariables.blockNumber.toNumber() - 1}`);
519
496
 
520
497
  // NB: separating the dbs because both should update the state
521
498
  const publicProcessorFork = await this.worldState.fork();
@@ -525,28 +502,30 @@ export class Sequencer {
525
502
  const processor = this.publicProcessorFactory.create(publicProcessorFork, historicalHeader, newGlobalVariables);
526
503
  const blockBuildingTimer = new Timer();
527
504
  const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
528
- await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
505
+ await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages);
529
506
 
530
507
  const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
531
- processor.process(
532
- validTxs,
533
- blockSize,
534
- blockBuilder,
535
- this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork),
536
- ),
508
+ processor.process(validTxs, blockSize, this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork)),
537
509
  );
538
510
  if (failedTxs.length > 0) {
539
511
  const failedTxData = failedTxs.map(fail => fail.tx);
540
- this.log.debug(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
512
+ this.log.verbose(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
541
513
  await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
542
514
  }
515
+ await blockBuilder.addTxs(processedTxs);
543
516
 
544
517
  await interrupt?.(processedTxs);
545
518
 
546
519
  // All real transactions have been added, set the block as full and complete the proving.
547
520
  const block = await blockBuilder.setBlockCompleted();
548
521
 
549
- return { block, publicProcessorDuration, numProcessedTxs: processedTxs.length, blockBuildingTimer };
522
+ return {
523
+ block,
524
+ publicProcessorDuration,
525
+ numMsgs: l1ToL2Messages.length,
526
+ numProcessedTxs: processedTxs.length,
527
+ blockBuildingTimer,
528
+ };
550
529
  } finally {
551
530
  // We create a fresh processor each time to reset any cached state (eg storage writes)
552
531
  await publicProcessorFork.close();
@@ -569,21 +548,18 @@ export class Sequencer {
569
548
  }))
570
549
  private async buildBlockAndAttemptToPublish(
571
550
  validTxs: Tx[],
572
- proposalHeader: Header,
573
- historicalHeader: Header | undefined,
551
+ proposalHeader: BlockHeader,
552
+ historicalHeader: BlockHeader | undefined,
574
553
  ): Promise<void> {
575
554
  await this.publisher.validateBlockForSubmission(proposalHeader);
576
555
 
577
556
  const newGlobalVariables = proposalHeader.globalVariables;
557
+ const blockNumber = newGlobalVariables.blockNumber.toNumber();
558
+ const slot = newGlobalVariables.slotNumber.toBigInt();
578
559
 
579
- this.metrics.recordNewBlock(newGlobalVariables.blockNumber.toNumber(), validTxs.length);
560
+ this.metrics.recordNewBlock(blockNumber, validTxs.length);
580
561
  const workTimer = new Timer();
581
- this.setState(SequencerState.CREATING_BLOCK, newGlobalVariables.slotNumber.toBigInt());
582
- this.log.info(
583
- `Building blockNumber=${newGlobalVariables.blockNumber.toNumber()} txCount=${
584
- validTxs.length
585
- } slotNumber=${newGlobalVariables.slotNumber.toNumber()}`,
586
- );
562
+ this.setState(SequencerState.CREATING_BLOCK, slot);
587
563
 
588
564
  /**
589
565
  * BuildBlock is shared between the sequencer and the validator for re-execution
@@ -606,12 +582,8 @@ export class Sequencer {
606
582
  };
607
583
 
608
584
  try {
609
- const { block, publicProcessorDuration, numProcessedTxs, blockBuildingTimer } = await this.buildBlock(
610
- validTxs,
611
- newGlobalVariables,
612
- historicalHeader,
613
- interrupt,
614
- );
585
+ const buildBlockRes = await this.buildBlock(validTxs, newGlobalVariables, historicalHeader, interrupt);
586
+ const { block, publicProcessorDuration, numProcessedTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
615
587
 
616
588
  // TODO(@PhilWindle) We should probably periodically check for things like another
617
589
  // block being published before ours instead of just waiting on our block
@@ -619,43 +591,55 @@ export class Sequencer {
619
591
  await this.publisher.validateBlockForSubmission(block.header);
620
592
 
621
593
  const workDuration = workTimer.ms();
622
- this.log.info(
623
- `Assembled block ${block.number} (txEffectsHash: ${block.header.contentCommitment.txsEffectsHash.toString(
624
- 'hex',
625
- )})`,
626
- {
627
- eventName: 'l2-block-built',
628
- creator: this.publisher.getSenderAddress().toString(),
629
- duration: workDuration,
630
- publicProcessDuration: publicProcessorDuration,
631
- rollupCircuitsDuration: blockBuildingTimer.ms(),
632
- ...block.getStats(),
633
- } satisfies L2BlockBuiltStats,
634
- );
594
+ const blockStats: L2BlockBuiltStats = {
595
+ eventName: 'l2-block-built',
596
+ creator: this.publisher.getSenderAddress().toString(),
597
+ duration: workDuration,
598
+ publicProcessDuration: publicProcessorDuration,
599
+ rollupCircuitsDuration: blockBuildingTimer.ms(),
600
+ ...block.getStats(),
601
+ };
602
+
603
+ const blockHash = block.hash();
604
+ const txHashes = validTxs.map(tx => tx.getTxHash());
605
+ this.log.info(`Built block ${block.number} with hash ${blockHash}`, {
606
+ blockHash,
607
+ globalVariables: block.header.globalVariables.toInspect(),
608
+ txHashes,
609
+ ...blockStats,
610
+ });
635
611
 
636
612
  if (this.isFlushing) {
637
- this.log.info(`Flushing completed`);
613
+ this.log.verbose(`Sequencer flushing completed`);
638
614
  }
639
615
 
640
- const txHashes = validTxs.map(tx => tx.getTxHash());
641
-
642
616
  this.isFlushing = false;
643
- this.log.verbose('Collecting attestations');
617
+ this.log.debug('Collecting attestations');
644
618
  const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
645
619
  const attestations = await this.collectAttestations(block, txHashes);
646
- this.log.verbose('Attestations collected');
620
+ if (attestations !== undefined) {
621
+ this.log.verbose(`Collected ${attestations.length} attestations`);
622
+ }
647
623
  stopCollectingAttestationsTimer();
648
- this.log.verbose('Collecting proof quotes');
649
624
 
625
+ this.log.debug('Collecting proof quotes');
650
626
  const proofQuote = await this.createProofClaimForPreviousEpoch(newGlobalVariables.slotNumber.toBigInt());
651
- this.log.info(proofQuote ? `Using proof quote ${inspect(proofQuote.payload)}` : 'No proof quote available');
652
627
 
653
628
  await this.publishL2Block(block, attestations, txHashes, proofQuote);
654
629
  this.metrics.recordPublishedBlock(workDuration);
655
630
  this.log.info(
656
- `Submitted rollup block ${block.number} with ${numProcessedTxs} transactions duration=${Math.ceil(
657
- workDuration,
658
- )}ms (Submitter: ${this.publisher.getSenderAddress()})`,
631
+ `Published rollup block ${
632
+ block.number
633
+ } with ${numProcessedTxs} transactions and ${numMsgs} messages in ${Math.ceil(workDuration)}ms`,
634
+ {
635
+ blockNumber: block.number,
636
+ blockHash: blockHash,
637
+ slot,
638
+ txCount: numProcessedTxs,
639
+ msgCount: numMsgs,
640
+ duration: Math.ceil(workDuration),
641
+ submitter: this.publisher.getSenderAddress().toString(),
642
+ },
659
643
  );
660
644
  } catch (err) {
661
645
  this.metrics.recordFailedBlock();
@@ -676,11 +660,12 @@ export class Sequencer {
676
660
  protected async collectAttestations(block: L2Block, txHashes: TxHash[]): Promise<Signature[] | undefined> {
677
661
  // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
678
662
  const committee = await this.publisher.getCurrentEpochCommittee();
679
- this.log.debug(`Attesting committee length ${committee.length}`);
680
663
 
681
664
  if (committee.length === 0) {
682
- this.log.verbose(`Attesting committee length is 0, skipping`);
665
+ this.log.verbose(`Attesting committee is empty`);
683
666
  return undefined;
667
+ } else {
668
+ this.log.debug(`Attesting committee length is ${committee.length}`);
684
669
  }
685
670
 
686
671
  if (!this.validatorClient) {
@@ -691,22 +676,21 @@ export class Sequencer {
691
676
 
692
677
  const numberOfRequiredAttestations = Math.floor((committee.length * 2) / 3) + 1;
693
678
 
694
- this.log.info('Creating block proposal');
679
+ this.log.debug('Creating block proposal');
695
680
  const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
696
681
  if (!proposal) {
697
- this.log.verbose(`Failed to create block proposal, skipping`);
682
+ this.log.warn(`Failed to create block proposal, skipping collecting attestations`);
698
683
  return undefined;
699
684
  }
700
685
 
701
686
  const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
702
687
 
703
688
  this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS, slotNumber);
704
- this.log.info('Broadcasting block proposal to validators');
689
+ this.log.debug('Broadcasting block proposal to validators');
705
690
  this.validatorClient.broadcastBlockProposal(proposal);
706
691
 
707
692
  this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, slotNumber);
708
693
  const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations);
709
- this.log.info(`Collected attestations from validators, number of attestations: ${attestations.length}`);
710
694
 
711
695
  // note: the smart contract requires that the signatures are provided in the order of the committee
712
696
  return orderAttestations(attestations, committee);
@@ -717,16 +701,17 @@ export class Sequencer {
717
701
  // Find out which epoch we are currently in
718
702
  const epochToProve = await this.publisher.getClaimableEpoch();
719
703
  if (epochToProve === undefined) {
720
- this.log.verbose(`No epoch to prove`);
704
+ this.log.debug(`No epoch to prove`);
721
705
  return undefined;
722
706
  }
723
707
 
724
708
  // Get quotes for the epoch to be proven
725
709
  const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
726
- this.log.info(`Retrieved ${quotes.length} quotes, slot: ${slotNumber}, epoch to prove: ${epochToProve}`);
727
- for (const quote of quotes) {
728
- this.log.verbose(inspect(quote.payload));
729
- }
710
+ this.log.verbose(`Retrieved ${quotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
711
+ epochToProve,
712
+ slotNumber,
713
+ quotes: quotes.map(q => q.payload),
714
+ });
730
715
  // ensure these quotes are still valid for the slot and have the contract validate them
731
716
  const validQuotesPromise = Promise.all(
732
717
  quotes.filter(x => x.payload.validUntilSlot >= slotNumber).map(x => this.publisher.validateProofQuote(x)),
@@ -741,9 +726,11 @@ export class Sequencer {
741
726
  const sortedQuotes = validQuotes.sort(
742
727
  (a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
743
728
  );
744
- return sortedQuotes[0];
729
+ const quote = sortedQuotes[0];
730
+ this.log.info(`Selected proof quote for proof claim`, quote.payload);
731
+ return quote;
745
732
  } catch (err) {
746
- this.log.error(`Failed to create proof claim for previous epoch: ${err}`);
733
+ this.log.error(`Failed to create proof claim for previous epoch`, err, { slotNumber });
747
734
  return undefined;
748
735
  }
749
736
  }
@@ -788,7 +775,7 @@ export class Sequencer {
788
775
  for (const tx of txs) {
789
776
  const txSize = tx.getSize() - tx.clientIvcProof.clientIvcProofBuffer.length;
790
777
  if (totalSize + txSize > maxSize) {
791
- this.log.warn(
778
+ this.log.debug(
792
779
  `Dropping tx ${tx.getTxHash()} with estimated size ${txSize} due to exceeding ${maxSize} block size limit (currently at ${totalSize})`,
793
780
  );
794
781
  continue;
@@ -825,7 +812,7 @@ export class Sequencer {
825
812
  p2p >= l2BlockSource.number &&
826
813
  l1ToL2MessageSource >= l2BlockSource.number;
827
814
 
828
- this.log.verbose(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
815
+ this.log.debug(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
829
816
  worldStateNumber: worldState.number,
830
817
  worldStateHash: worldState.hash,
831
818
  l2BlockSourceNumber: l2BlockSource.number,