@aztec/sequencer-client 2.0.3 → 2.1.0-rc.10

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 (48) hide show
  1. package/dest/client/sequencer-client.d.ts.map +1 -1
  2. package/dest/client/sequencer-client.js +7 -4
  3. package/dest/config.d.ts +2 -1
  4. package/dest/config.d.ts.map +1 -1
  5. package/dest/config.js +6 -0
  6. package/dest/global_variable_builder/global_builder.js +1 -1
  7. package/dest/publisher/config.d.ts +2 -4
  8. package/dest/publisher/config.d.ts.map +1 -1
  9. package/dest/publisher/config.js +7 -10
  10. package/dest/publisher/index.d.ts +1 -1
  11. package/dest/publisher/index.d.ts.map +1 -1
  12. package/dest/publisher/index.js +1 -1
  13. package/dest/publisher/sequencer-publisher-factory.d.ts +5 -1
  14. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  15. package/dest/publisher/sequencer-publisher-factory.js +8 -1
  16. package/dest/publisher/sequencer-publisher-metrics.js +1 -1
  17. package/dest/publisher/sequencer-publisher.d.ts +17 -23
  18. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  19. package/dest/publisher/sequencer-publisher.js +54 -59
  20. package/dest/sequencer/errors.d.ts +11 -0
  21. package/dest/sequencer/errors.d.ts.map +1 -0
  22. package/dest/sequencer/errors.js +15 -0
  23. package/dest/sequencer/metrics.d.ts +5 -17
  24. package/dest/sequencer/metrics.d.ts.map +1 -1
  25. package/dest/sequencer/metrics.js +23 -89
  26. package/dest/sequencer/sequencer.d.ts +5 -4
  27. package/dest/sequencer/sequencer.d.ts.map +1 -1
  28. package/dest/sequencer/sequencer.js +37 -22
  29. package/dest/sequencer/timetable.d.ts +0 -6
  30. package/dest/sequencer/timetable.d.ts.map +1 -1
  31. package/dest/sequencer/timetable.js +2 -9
  32. package/dest/sequencer/utils.d.ts +10 -24
  33. package/dest/sequencer/utils.d.ts.map +1 -1
  34. package/dest/sequencer/utils.js +9 -24
  35. package/package.json +30 -30
  36. package/src/client/sequencer-client.ts +6 -2
  37. package/src/config.ts +7 -0
  38. package/src/global_variable_builder/global_builder.ts +1 -1
  39. package/src/publisher/config.ts +13 -11
  40. package/src/publisher/index.ts +1 -1
  41. package/src/publisher/sequencer-publisher-factory.ts +12 -2
  42. package/src/publisher/sequencer-publisher-metrics.ts +1 -1
  43. package/src/publisher/sequencer-publisher.ts +78 -78
  44. package/src/sequencer/errors.ts +21 -0
  45. package/src/sequencer/metrics.ts +24 -100
  46. package/src/sequencer/sequencer.ts +62 -49
  47. package/src/sequencer/timetable.ts +2 -13
  48. package/src/sequencer/utils.ts +10 -24
@@ -4,15 +4,19 @@ function _ts_decorate(decorators, target, key, desc) {
4
4
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  }
7
- import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
7
+ import { BLOBS_PER_BLOCK, FIELDS_PER_BLOB, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
8
8
  import { FormattedViemError, NoCommitteeError } from '@aztec/ethereum';
9
9
  import { omit, pick } from '@aztec/foundation/collection';
10
+ import { randomInt } from '@aztec/foundation/crypto';
10
11
  import { EthAddress } from '@aztec/foundation/eth-address';
12
+ import { Signature } from '@aztec/foundation/eth-signature';
11
13
  import { Fr } from '@aztec/foundation/fields';
12
14
  import { createLogger } from '@aztec/foundation/log';
13
15
  import { RunningPromise } from '@aztec/foundation/running-promise';
14
16
  import { Timer } from '@aztec/foundation/timer';
15
- import { getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers';
17
+ import { unfreeze } from '@aztec/foundation/types';
18
+ import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
19
+ import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
16
20
  import { Gas } from '@aztec/stdlib/gas';
17
21
  import { SequencerConfigSchema } from '@aztec/stdlib/interfaces/server';
18
22
  import { orderAttestations } from '@aztec/stdlib/p2p';
@@ -22,8 +26,9 @@ import { ContentCommitment, ProposedBlockHeader } from '@aztec/stdlib/tx';
22
26
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
23
27
  import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
24
28
  import EventEmitter from 'node:events';
29
+ import { SequencerInterruptedError, SequencerTooSlowError } from './errors.js';
25
30
  import { SequencerMetrics } from './metrics.js';
26
- import { SequencerTimetable, SequencerTooSlowError } from './timetable.js';
31
+ import { SequencerTimetable } from './timetable.js';
27
32
  import { SequencerState } from './utils.js';
28
33
  export { SequencerState };
29
34
  /**
@@ -72,10 +77,7 @@ export { SequencerState };
72
77
  publisher;
73
78
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, blockBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
74
79
  super(), this.publisherFactory = publisherFactory, this.validatorClient = validatorClient, this.globalsBuilder = globalsBuilder, this.p2pClient = p2pClient, this.worldState = worldState, this.slasherClient = slasherClient, this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.blockBuilder = blockBuilder, this.l1Constants = l1Constants, this.dateProvider = dateProvider, this.epochCache = epochCache, this.rollupContract = rollupContract, this.config = config, this.telemetry = telemetry, this.log = log, this.pollingIntervalMs = 1000, this.maxTxsPerBlock = 32, this.minTxsPerBlock = 1, this.maxL1TxInclusionTimeIntoSlot = 0, this.state = SequencerState.STOPPED, this.maxBlockSizeInBytes = 1024 * 1024, this.maxBlockGas = new Gas(100e9, 100e9), this.enforceTimeTable = false;
75
- // Set an initial coinbase for metrics purposes, but this will potentially change with each block.
76
- const validatorAddresses = this.validatorClient?.getValidatorAddresses() ?? [];
77
- const coinbase = validatorAddresses.length === 0 ? EthAddress.ZERO : this.validatorClient?.getCoinbaseForAttestor(validatorAddresses[0]) ?? EthAddress.ZERO;
78
- this.metrics = new SequencerMetrics(telemetry, ()=>this.state, coinbase, this.rollupContract, 'Sequencer');
80
+ this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
79
81
  // Initialize config
80
82
  this.updateConfig(this.config);
81
83
  }
@@ -140,7 +142,6 @@ export { SequencerState };
140
142
  /**
141
143
  * Starts the sequencer and moves to IDLE state.
142
144
  */ start() {
143
- this.metrics.start();
144
145
  this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
145
146
  this.setState(SequencerState.IDLE, undefined, {
146
147
  force: true
@@ -152,9 +153,10 @@ export { SequencerState };
152
153
  * Stops the sequencer from processing txs and moves to STOPPED state.
153
154
  */ async stop() {
154
155
  this.log.info(`Stopping sequencer`);
155
- this.metrics.stop();
156
+ this.setState(SequencerState.STOPPING, undefined, {
157
+ force: true
158
+ });
156
159
  this.publisher?.interrupt();
157
- await this.validatorClient?.stop();
158
160
  await this.runningPromise?.stop();
159
161
  this.setState(SequencerState.STOPPED, undefined, {
160
162
  force: true
@@ -259,7 +261,6 @@ export { SequencerState };
259
261
  this.publisher = publisher;
260
262
  const coinbase = this.validatorClient.getCoinbaseForAttestor(attestorAddress);
261
263
  const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(attestorAddress);
262
- this.metrics.setCoinbase(coinbase);
263
264
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
264
265
  const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
265
266
  const canProposeCheck = await publisher.canProposeAtNextEthBlock(chainTipArchive, proposerAddressInNextSlot, invalidateBlock);
@@ -317,6 +318,7 @@ export { SequencerState };
317
318
  publisher.enqueueInvalidateBlock(invalidateBlock);
318
319
  }
319
320
  this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
321
+ this.metrics.incOpenSlot(slot, proposerAddressInNextSlot.toString());
320
322
  this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
321
323
  proposer: proposerInNextSlot?.toString(),
322
324
  coinbase,
@@ -379,7 +381,7 @@ export { SequencerState };
379
381
  blockNumber: newBlockNumber,
380
382
  slot: Number(slot)
381
383
  });
382
- this.metrics.incFilledSlot(publisher.getSenderAddress().toString());
384
+ await this.metrics.incFilledSlot(publisher.getSenderAddress().toString(), coinbase);
383
385
  } else if (block) {
384
386
  this.emit('block-publish-failed', l1Response ?? {});
385
387
  }
@@ -407,6 +409,10 @@ export { SequencerState };
407
409
  }
408
410
  }
409
411
  setState(proposedState, slotNumber, opts = {}) {
412
+ if (this.state === SequencerState.STOPPING && proposedState !== SequencerState.STOPPED && !opts.force) {
413
+ this.log.warn(`Cannot set sequencer to ${proposedState} as it is stopping.`);
414
+ throw new SequencerInterruptedError();
415
+ }
410
416
  if (this.state === SequencerState.STOPPED && !opts.force) {
411
417
  this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
412
418
  return;
@@ -452,6 +458,7 @@ export { SequencerState };
452
458
  maxTransactions: this.maxTxsPerBlock,
453
459
  maxBlockSize: this.maxBlockSizeInBytes,
454
460
  maxBlockGas: this.maxBlockGas,
461
+ maxBlobFields: BLOBS_PER_BLOCK * FIELDS_PER_BLOB,
455
462
  deadline
456
463
  };
457
464
  }
@@ -470,7 +477,6 @@ export { SequencerState };
470
477
  const blockNumber = newGlobalVariables.blockNumber;
471
478
  const slot = proposalHeader.slotNumber.toBigInt();
472
479
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
473
- // this.metrics.recordNewBlock(blockNumber, validTxs.length);
474
480
  const workTimer = new Timer();
475
481
  this.setState(SequencerState.CREATING_BLOCK, slot);
476
482
  try {
@@ -515,7 +521,9 @@ export { SequencerState };
515
521
  blockNumber
516
522
  });
517
523
  }
518
- await this.enqueuePublishL2Block(block, attestations, txHashes, invalidateBlock, publisher);
524
+ const attestationsAndSigners = new CommitteeAttestationsAndSigners(attestations ?? []);
525
+ const attestationsAndSignersSignature = this.validatorClient ? await this.validatorClient.signAttestationsAndSigners(attestationsAndSigners, proposerAddress) : Signature.empty();
526
+ await this.enqueuePublishL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, invalidateBlock, publisher);
519
527
  this.metrics.recordBuiltBlock(blockBuildDuration, publicGas.l2Gas);
520
528
  return block;
521
529
  } catch (err) {
@@ -561,32 +569,39 @@ export { SequencerState };
561
569
  const attestationTimeAllowed = this.enforceTimeTable ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_BLOCK) : this.aztecSlotDuration;
562
570
  this.metrics.recordRequiredAttestations(numberOfRequiredAttestations, attestationTimeAllowed);
563
571
  const timer = new Timer();
564
- let collectedAttestionsCount = 0;
572
+ let collectedAttestationsCount = 0;
565
573
  try {
566
574
  const attestationDeadline = new Date(this.dateProvider.now() + attestationTimeAllowed * 1000);
567
575
  const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
568
- collectedAttestionsCount = attestations.length;
576
+ collectedAttestationsCount = attestations.length;
569
577
  // note: the smart contract requires that the signatures are provided in the order of the committee
570
- return orderAttestations(attestations, committee);
578
+ const sorted = orderAttestations(attestations, committee);
579
+ if (this.config.injectFakeAttestation) {
580
+ const nonEmpty = sorted.filter((a)=>!a.signature.isEmpty());
581
+ const randomIndex = randomInt(nonEmpty.length);
582
+ this.log.warn(`Injecting fake attestation in block ${block.number}`);
583
+ unfreeze(nonEmpty[randomIndex]).signature = Signature.random();
584
+ }
585
+ return sorted;
571
586
  } catch (err) {
572
587
  if (err && err instanceof AttestationTimeoutError) {
573
- collectedAttestionsCount = err.collectedCount;
588
+ collectedAttestationsCount = err.collectedCount;
574
589
  }
575
590
  throw err;
576
591
  } finally{
577
- this.metrics.recordCollectedAttestations(collectedAttestionsCount, timer.ms());
592
+ this.metrics.recordCollectedAttestations(collectedAttestationsCount, timer.ms());
578
593
  }
579
594
  }
580
595
  /**
581
596
  * Publishes the L2Block to the rollup contract.
582
597
  * @param block - The L2Block to be published.
583
- */ async enqueuePublishL2Block(block, attestations, txHashes, invalidateBlock, publisher) {
598
+ */ async enqueuePublishL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, invalidateBlock, publisher) {
584
599
  // Publishes new block to the network and awaits the tx to be mined
585
600
  this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
586
601
  // Time out tx at the end of the slot
587
602
  const slot = block.header.globalVariables.slotNumber.toNumber();
588
603
  const txTimeoutAt = new Date((this.getSlotStartBuildTimestamp(slot) + this.aztecSlotDuration) * 1000);
589
- const enqueued = await publisher.enqueueProposeL2Block(block, attestations, txHashes, {
604
+ const enqueued = await publisher.enqueueProposeL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, {
590
605
  txTimeoutAt,
591
606
  forcePendingBlockNumber: invalidateBlock?.forcePendingBlockNumber
592
607
  });
@@ -689,7 +704,7 @@ export { SequencerState };
689
704
  await publisher.sendRequests();
690
705
  }
691
706
  getSlotStartBuildTimestamp(slotNumber) {
692
- return Number(this.l1Constants.l1GenesisTime) + Number(slotNumber) * this.l1Constants.slotDuration - this.l1Constants.ethereumSlotDuration;
707
+ return getSlotStartBuildTimestamp(slotNumber, this.l1Constants);
693
708
  }
694
709
  getSecondsIntoSlot(slotNumber) {
695
710
  const slotStartTimestamp = this.getSlotStartBuildTimestamp(slotNumber);
@@ -45,10 +45,4 @@ export declare class SequencerTimetable {
45
45
  getMaxAllowedTime(state: SequencerState): number | undefined;
46
46
  assertTimeLeft(newState: SequencerState, secondsIntoSlot: number): void;
47
47
  }
48
- export declare class SequencerTooSlowError extends Error {
49
- readonly proposedState: SequencerState;
50
- readonly maxAllowedTime: number;
51
- readonly currentTime: number;
52
- constructor(proposedState: SequencerState, maxAllowedTime: number, currentTime: number);
53
- }
54
48
  //# sourceMappingURL=timetable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,qBAAa,kBAAkB;IA+C3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IA/CtB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,gBAAgB,SAAC;IAEjC,sHAAsH;IACtH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,mGAAmG;IACnG,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD,mIAAmI;IACnI,SAAgB,mBAAmB,EAAE,MAAM,CAAyB;IAEpE,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,2IAA2I;IAC3I,SAAgB,4BAA4B,EAAE,MAAM,CAAC;IAErD,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;gBAG/B,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,4BAA4B,EAAE,MAAM,CAAC;QACrC,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,EAAE,gBAAgB,YAAA,EAC1B,GAAG,mCAAsC;IA+C5D,OAAO,KAAK,yCAAyC,GAEpD;IAEM,2BAA2B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IAenE,OAAO,KAAK,0BAA0B,GAErC;IAEM,yBAAyB,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM;IAU3D,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS;IAsB5D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM;CAkBxE;AAED,qBAAa,qBAAsB,SAAQ,KAAK;aAE5B,aAAa,EAAE,cAAc;aAC7B,cAAc,EAAE,MAAM;aACtB,WAAW,EAAE,MAAM;gBAFnB,aAAa,EAAE,cAAc,EAC7B,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM;CAOtC"}
1
+ {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,qBAAa,kBAAkB;IA+C3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IA/CtB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,gBAAgB,SAAC;IAEjC,sHAAsH;IACtH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,mGAAmG;IACnG,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD,mIAAmI;IACnI,SAAgB,mBAAmB,EAAE,MAAM,CAAyB;IAEpE,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,2IAA2I;IAC3I,SAAgB,4BAA4B,EAAE,MAAM,CAAC;IAErD,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;gBAG/B,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,4BAA4B,EAAE,MAAM,CAAC;QACrC,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,EAAE,gBAAgB,YAAA,EAC1B,GAAG,mCAAsC;IA+C5D,OAAO,KAAK,yCAAyC,GAEpD;IAEM,2BAA2B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IAenE,OAAO,KAAK,0BAA0B,GAErC;IAEM,yBAAyB,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM;IAU3D,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS;IAuB5D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM;CAkBxE"}
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from '@aztec/aztec.js';
2
2
  import { DEFAULT_ATTESTATION_PROPAGATION_TIME } from '../config.js';
3
+ import { SequencerTooSlowError } from './errors.js';
3
4
  import { SequencerState } from './utils.js';
4
5
  const MIN_EXECUTION_TIME = 1;
5
6
  const BLOCK_PREPARE_TIME = 1;
@@ -95,6 +96,7 @@ export class SequencerTimetable {
95
96
  getMaxAllowedTime(state) {
96
97
  switch(state){
97
98
  case SequencerState.STOPPED:
99
+ case SequencerState.STOPPING:
98
100
  case SequencerState.IDLE:
99
101
  case SequencerState.SYNCHRONIZING:
100
102
  return; // We don't really care about times for this states
@@ -133,12 +135,3 @@ export class SequencerTimetable {
133
135
  });
134
136
  }
135
137
  }
136
- export class SequencerTooSlowError extends Error {
137
- proposedState;
138
- maxAllowedTime;
139
- currentTime;
140
- constructor(proposedState, maxAllowedTime, currentTime){
141
- super(`Too far into slot for ${proposedState} (time into slot ${currentTime}s greater than ${maxAllowedTime}s allowance)`), this.proposedState = proposedState, this.maxAllowedTime = maxAllowedTime, this.currentTime = currentTime;
142
- this.name = 'SequencerTooSlowError';
143
- }
144
- }
@@ -1,35 +1,21 @@
1
1
  export declare enum SequencerState {
2
- /**
3
- * Sequencer is stopped and not processing any txs from the pool.
4
- */
2
+ /** Sequencer is stopped and not processing any txs from the pool. */
5
3
  STOPPED = "STOPPED",
6
- /**
7
- * Sequencer is awaiting the next call to work().
8
- */
4
+ /** Sequencer is being stopped. Will move to STOPPED shortly. */
5
+ STOPPING = "STOPPING",
6
+ /** Sequencer is awaiting the next call to work(). */
9
7
  IDLE = "IDLE",
10
- /**
11
- * Synchronizing with the L2 chain.
12
- */
8
+ /** Synchronizing with the L2 chain. */
13
9
  SYNCHRONIZING = "SYNCHRONIZING",
14
- /**
15
- * Checking if we are the proposer for the current slot.
16
- */
10
+ /** Checking if we are the proposer for the current slot. */
17
11
  PROPOSER_CHECK = "PROPOSER_CHECK",
18
- /**
19
- * Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise.
20
- */
12
+ /** Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise. */
21
13
  INITIALIZING_PROPOSAL = "INITIALIZING_PROPOSAL",
22
- /**
23
- * Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
24
- */
14
+ /** Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA. */
25
15
  CREATING_BLOCK = "CREATING_BLOCK",
26
- /**
27
- * Collecting attestations from its peers. Will move to PUBLISHING_BLOCK.
28
- */
16
+ /** Collecting attestations from its peers. Will move to PUBLISHING_BLOCK. */
29
17
  COLLECTING_ATTESTATIONS = "COLLECTING_ATTESTATIONS",
30
- /**
31
- * Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING.
32
- */
18
+ /** Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING. */
33
19
  PUBLISHING_BLOCK = "PUBLISHING_BLOCK"
34
20
  }
35
21
  export type SequencerStateWithSlot = SequencerState.INITIALIZING_PROPOSAL | SequencerState.CREATING_BLOCK | SequencerState.COLLECTING_ATTESTATIONS | SequencerState.PUBLISHING_BLOCK | SequencerState.PROPOSER_CHECK;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/sequencer/utils.ts"],"names":[],"mappings":"AAAA,oBAAY,cAAc;IACxB;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,aAAa,kBAAkB;IAC/B;;OAEG;IACH,cAAc,mBAAmB;IACjC;;OAEG;IACH,qBAAqB,0BAA0B;IAC/C;;OAEG;IACH,cAAc,mBAAmB;IACjC;;OAEG;IACH,uBAAuB,4BAA4B;IACnD;;OAEG;IACH,gBAAgB,qBAAqB;CACtC;AAED,MAAM,MAAM,sBAAsB,GAC9B,cAAc,CAAC,qBAAqB,GACpC,cAAc,CAAC,cAAc,GAC7B,cAAc,CAAC,uBAAuB,GACtC,cAAc,CAAC,gBAAgB,GAC/B,cAAc,CAAC,cAAc,CAAC;AAElC,MAAM,MAAM,sBAAsB,GAAG,MAAM,cAAc,CAAC;AAE1D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEpE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/sequencer/utils.ts"],"names":[],"mappings":"AAAA,oBAAY,cAAc;IACxB,qEAAqE;IACrE,OAAO,YAAY;IACnB,gEAAgE;IAChE,QAAQ,aAAa;IACrB,qDAAqD;IACrD,IAAI,SAAS;IACb,uCAAuC;IACvC,aAAa,kBAAkB;IAC/B,4DAA4D;IAC5D,cAAc,mBAAmB;IACjC,0IAA0I;IAC1I,qBAAqB,0BAA0B;IAC/C,6IAA6I;IAC7I,cAAc,mBAAmB;IACjC,6EAA6E;IAC7E,uBAAuB,4BAA4B;IACnD,2GAA2G;IAC3G,gBAAgB,qBAAqB;CACtC;AAED,MAAM,MAAM,sBAAsB,GAC9B,cAAc,CAAC,qBAAqB,GACpC,cAAc,CAAC,cAAc,GAC7B,cAAc,CAAC,uBAAuB,GACtC,cAAc,CAAC,gBAAgB,GAC/B,cAAc,CAAC,cAAc,CAAC;AAElC,MAAM,MAAM,sBAAsB,GAAG,MAAM,cAAc,CAAC;AAE1D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEpE"}
@@ -1,28 +1,13 @@
1
1
  export var SequencerState = /*#__PURE__*/ function(SequencerState) {
2
- /**
3
- * Sequencer is stopped and not processing any txs from the pool.
4
- */ SequencerState["STOPPED"] = "STOPPED";
5
- /**
6
- * Sequencer is awaiting the next call to work().
7
- */ SequencerState["IDLE"] = "IDLE";
8
- /**
9
- * Synchronizing with the L2 chain.
10
- */ SequencerState["SYNCHRONIZING"] = "SYNCHRONIZING";
11
- /**
12
- * Checking if we are the proposer for the current slot.
13
- */ SequencerState["PROPOSER_CHECK"] = "PROPOSER_CHECK";
14
- /**
15
- * Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise.
16
- */ SequencerState["INITIALIZING_PROPOSAL"] = "INITIALIZING_PROPOSAL";
17
- /**
18
- * Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
19
- */ SequencerState["CREATING_BLOCK"] = "CREATING_BLOCK";
20
- /**
21
- * Collecting attestations from its peers. Will move to PUBLISHING_BLOCK.
22
- */ SequencerState["COLLECTING_ATTESTATIONS"] = "COLLECTING_ATTESTATIONS";
23
- /**
24
- * Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING.
25
- */ SequencerState["PUBLISHING_BLOCK"] = "PUBLISHING_BLOCK";
2
+ /** Sequencer is stopped and not processing any txs from the pool. */ SequencerState["STOPPED"] = "STOPPED";
3
+ /** Sequencer is being stopped. Will move to STOPPED shortly. */ SequencerState["STOPPING"] = "STOPPING";
4
+ /** Sequencer is awaiting the next call to work(). */ SequencerState["IDLE"] = "IDLE";
5
+ /** Synchronizing with the L2 chain. */ SequencerState["SYNCHRONIZING"] = "SYNCHRONIZING";
6
+ /** Checking if we are the proposer for the current slot. */ SequencerState["PROPOSER_CHECK"] = "PROPOSER_CHECK";
7
+ /** Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise. */ SequencerState["INITIALIZING_PROPOSAL"] = "INITIALIZING_PROPOSAL";
8
+ /** Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA. */ SequencerState["CREATING_BLOCK"] = "CREATING_BLOCK";
9
+ /** Collecting attestations from its peers. Will move to PUBLISHING_BLOCK. */ SequencerState["COLLECTING_ATTESTATIONS"] = "COLLECTING_ATTESTATIONS";
10
+ /** Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING. */ SequencerState["PUBLISHING_BLOCK"] = "PUBLISHING_BLOCK";
26
11
  return SequencerState;
27
12
  }({});
28
13
  export function sequencerStateToNumber(state) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/sequencer-client",
3
- "version": "2.0.3",
3
+ "version": "2.1.0-rc.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -26,37 +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": "2.0.3",
30
- "@aztec/bb-prover": "2.0.3",
31
- "@aztec/blob-lib": "2.0.3",
32
- "@aztec/blob-sink": "2.0.3",
33
- "@aztec/constants": "2.0.3",
34
- "@aztec/epoch-cache": "2.0.3",
35
- "@aztec/ethereum": "2.0.3",
36
- "@aztec/foundation": "2.0.3",
37
- "@aztec/l1-artifacts": "2.0.3",
38
- "@aztec/merkle-tree": "2.0.3",
39
- "@aztec/noir-acvm_js": "2.0.3",
40
- "@aztec/noir-contracts.js": "2.0.3",
41
- "@aztec/noir-protocol-circuits-types": "2.0.3",
42
- "@aztec/noir-types": "2.0.3",
43
- "@aztec/p2p": "2.0.3",
44
- "@aztec/protocol-contracts": "2.0.3",
45
- "@aztec/prover-client": "2.0.3",
46
- "@aztec/simulator": "2.0.3",
47
- "@aztec/slasher": "2.0.3",
48
- "@aztec/stdlib": "2.0.3",
49
- "@aztec/telemetry-client": "2.0.3",
50
- "@aztec/validator-client": "2.0.3",
51
- "@aztec/world-state": "2.0.3",
29
+ "@aztec/aztec.js": "2.1.0-rc.10",
30
+ "@aztec/bb-prover": "2.1.0-rc.10",
31
+ "@aztec/blob-lib": "2.1.0-rc.10",
32
+ "@aztec/blob-sink": "2.1.0-rc.10",
33
+ "@aztec/constants": "2.1.0-rc.10",
34
+ "@aztec/epoch-cache": "2.1.0-rc.10",
35
+ "@aztec/ethereum": "2.1.0-rc.10",
36
+ "@aztec/foundation": "2.1.0-rc.10",
37
+ "@aztec/l1-artifacts": "2.1.0-rc.10",
38
+ "@aztec/merkle-tree": "2.1.0-rc.10",
39
+ "@aztec/node-keystore": "2.1.0-rc.10",
40
+ "@aztec/noir-acvm_js": "2.1.0-rc.10",
41
+ "@aztec/noir-contracts.js": "2.1.0-rc.10",
42
+ "@aztec/noir-protocol-circuits-types": "2.1.0-rc.10",
43
+ "@aztec/noir-types": "2.1.0-rc.10",
44
+ "@aztec/p2p": "2.1.0-rc.10",
45
+ "@aztec/protocol-contracts": "2.1.0-rc.10",
46
+ "@aztec/prover-client": "2.1.0-rc.10",
47
+ "@aztec/simulator": "2.1.0-rc.10",
48
+ "@aztec/slasher": "2.1.0-rc.10",
49
+ "@aztec/stdlib": "2.1.0-rc.10",
50
+ "@aztec/telemetry-client": "2.1.0-rc.10",
51
+ "@aztec/validator-client": "2.1.0-rc.10",
52
+ "@aztec/world-state": "2.1.0-rc.10",
53
+ "@spalladino/viem": "2.38.2-eip7594.0",
52
54
  "lodash.chunk": "^4.2.0",
53
- "lodash.pick": "^4.4.0",
54
- "tslib": "^2.4.0",
55
- "viem": "2.23.7"
55
+ "tslib": "^2.4.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@aztec/archiver": "2.0.3",
59
- "@aztec/kv-store": "2.0.3",
58
+ "@aztec/archiver": "2.1.0-rc.10",
59
+ "@aztec/kv-store": "2.1.0-rc.10",
60
60
  "@jest/globals": "^30.0.0",
61
61
  "@types/jest": "^30.0.0",
62
62
  "@types/lodash.chunk": "^4.2.7",
@@ -64,7 +64,7 @@
64
64
  "@types/node": "^22.15.17",
65
65
  "concurrently": "^7.6.0",
66
66
  "eslint": "^9.26.0",
67
- "express": "^4.21.1",
67
+ "express": "^4.21.2",
68
68
  "jest": "^30.0.0",
69
69
  "jest-mock-extended": "^4.0.0",
70
70
  "prettier": "^3.5.3",
@@ -84,7 +84,7 @@ export class SequencerClient {
84
84
  telemetry: telemetryClient,
85
85
  } = deps;
86
86
  const { l1RpcUrls: rpcUrls, l1ChainId: chainId } = config;
87
- const log = createLogger('sequencer-client');
87
+ const log = createLogger('sequencer');
88
88
  const publicClient = getPublicClient(config);
89
89
  const l1TxUtils = deps.l1TxUtils;
90
90
  const l1Metrics = new L1Metrics(
@@ -92,7 +92,7 @@ export class SequencerClient {
92
92
  publicClient,
93
93
  l1TxUtils.map(x => x.getSenderAddress()),
94
94
  );
95
- const publisherManager = new PublisherManager(l1TxUtils);
95
+ const publisherManager = new PublisherManager(l1TxUtils, config);
96
96
  const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
97
97
  const [l1GenesisTime, slotDuration] = await Promise.all([
98
98
  rollupContract.getL1GenesisTime(),
@@ -133,6 +133,7 @@ export class SequencerClient {
133
133
  dateProvider: deps.dateProvider,
134
134
  publisherManager,
135
135
  nodeKeyStore: NodeKeystoreAdapter.fromKeyStoreManager(deps.nodeKeyStore),
136
+ logger: log,
136
137
  });
137
138
  const globalsBuilder = new GlobalVariableBuilder(config);
138
139
 
@@ -178,6 +179,7 @@ export class SequencerClient {
178
179
  rollupContract,
179
180
  { ...config, maxL1TxInclusionTimeIntoSlot, maxL2BlockGas: sequencerManaLimit },
180
181
  telemetryClient,
182
+ log,
181
183
  );
182
184
 
183
185
  await sequencer.init();
@@ -200,6 +202,7 @@ export class SequencerClient {
200
202
  await this.validatorClient?.start();
201
203
  this.sequencer.start();
202
204
  this.l1Metrics?.start();
205
+ await this.publisherManager.loadState();
203
206
  }
204
207
 
205
208
  /**
@@ -207,6 +210,7 @@ export class SequencerClient {
207
210
  */
208
211
  public async stop() {
209
212
  await this.sequencer.stop();
213
+ await this.validatorClient?.stop();
210
214
  this.publisherManager.interrupt();
211
215
  this.l1Metrics?.stop();
212
216
  }
package/src/config.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  pickConfigMappings,
13
13
  } from '@aztec/foundation/config';
14
14
  import { EthAddress } from '@aztec/foundation/eth-address';
15
+ import { type KeyStoreConfig, keyStoreConfigMappings } from '@aztec/node-keystore';
15
16
  import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p';
16
17
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
17
18
  import { type ChainConfig, type SequencerConfig, chainConfigMappings } from '@aztec/stdlib/config';
@@ -33,6 +34,7 @@ export const DEFAULT_ATTESTATION_PROPAGATION_TIME = 2;
33
34
  * Configuration settings for the SequencerClient.
34
35
  */
35
36
  export type SequencerClientConfig = PublisherConfig &
37
+ KeyStoreConfig &
36
38
  ValidatorClientConfig &
37
39
  TxSenderConfig &
38
40
  SequencerConfig &
@@ -143,12 +145,17 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
143
145
  description: 'Do not invalidate the previous block if invalid when we are the proposer (for testing only)',
144
146
  ...booleanConfigHelper(false),
145
147
  },
148
+ injectFakeAttestation: {
149
+ description: 'Inject a fake attestation (for testing only)',
150
+ ...booleanConfigHelper(false),
151
+ },
146
152
  ...pickConfigMappings(p2pConfigMappings, ['txPublicSetupAllowList']),
147
153
  };
148
154
 
149
155
  export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
150
156
  ...validatorClientConfigMappings,
151
157
  ...sequencerConfigMappings,
158
+ ...keyStoreConfigMappings,
152
159
  ...l1ReaderConfigMappings,
153
160
  ...getTxSenderConfigMappings('SEQ'),
154
161
  ...getPublisherConfigMappings('SEQ'),
@@ -13,7 +13,7 @@ import { GasFees } from '@aztec/stdlib/gas';
13
13
  import type { GlobalVariableBuilder as GlobalVariableBuilderInterface } from '@aztec/stdlib/tx';
14
14
  import { GlobalVariables } from '@aztec/stdlib/tx';
15
15
 
16
- import { createPublicClient, fallback, http } from 'viem';
16
+ import { createPublicClient, fallback, http } from '@spalladino/viem';
17
17
 
18
18
  /**
19
19
  * Simple global variables builder.
@@ -5,7 +5,12 @@ import {
5
5
  l1ReaderConfigMappings,
6
6
  l1TxUtilsConfigMappings,
7
7
  } from '@aztec/ethereum';
8
- import { type ConfigMappingsType, SecretValue, getConfigFromMappings } from '@aztec/foundation/config';
8
+ import {
9
+ type ConfigMappingsType,
10
+ SecretValue,
11
+ booleanConfigHelper,
12
+ getConfigFromMappings,
13
+ } from '@aztec/foundation/config';
9
14
  import { EthAddress } from '@aztec/foundation/eth-address';
10
15
 
11
16
  /**
@@ -28,10 +33,8 @@ export type TxSenderConfig = L1ReaderConfig & {
28
33
  */
29
34
  export type PublisherConfig = L1TxUtilsConfig &
30
35
  BlobSinkConfig & {
31
- /**
32
- * The interval to wait between publish retries.
33
- */
34
- l1PublishRetryIntervalMS: number;
36
+ /** True to use publishers in invalid states (timed out, cancelled, etc) if no other is available */
37
+ publisherAllowInvalidStates?: boolean;
35
38
  };
36
39
 
37
40
  export const getTxSenderConfigMappings: (
@@ -43,7 +46,7 @@ export const getTxSenderConfigMappings: (
43
46
  description: 'The private keys to be used by the publisher.',
44
47
  parseEnv: (val: string) => val.split(',').map(key => new SecretValue(`0x${key.replace('0x', '')}`)),
45
48
  defaultValue: [],
46
- fallback: scope === 'PROVER' ? ['PROVER_PUBLISHER_PRIVATE_KEY'] : ['SEQ_PUBLISHER_PRIVATE_KEY'],
49
+ fallback: [scope === 'PROVER' ? `PROVER_PUBLISHER_PRIVATE_KEY` : `SEQ_PUBLISHER_PRIVATE_KEY`],
47
50
  },
48
51
  publisherAddresses: {
49
52
  env: scope === 'PROVER' ? `PROVER_PUBLISHER_ADDRESSES` : `SEQ_PUBLISHER_ADDRESSES`,
@@ -60,11 +63,10 @@ export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSender
60
63
  export const getPublisherConfigMappings: (
61
64
  scope: 'PROVER' | 'SEQ',
62
65
  ) => ConfigMappingsType<PublisherConfig & L1TxUtilsConfig> = scope => ({
63
- l1PublishRetryIntervalMS: {
64
- env: scope === `PROVER` ? `PROVER_PUBLISH_RETRY_INTERVAL_MS` : `SEQ_PUBLISH_RETRY_INTERVAL_MS`,
65
- parseEnv: (val: string) => +val,
66
- defaultValue: 1000,
67
- description: 'The interval to wait between publish retries.',
66
+ publisherAllowInvalidStates: {
67
+ description: 'True to use publishers in invalid states (timed out, cancelled, etc) if no other is available',
68
+ env: scope === `PROVER` ? `PROVER_PUBLISHER_ALLOW_INVALID_STATES` : `SEQ_PUBLISHER_ALLOW_INVALID_STATES`,
69
+ ...booleanConfigHelper(true),
68
70
  },
69
71
  ...l1TxUtilsConfigMappings,
70
72
  ...blobSinkConfigMapping,
@@ -1,4 +1,4 @@
1
- export { SequencerPublisher, SignalType } from './sequencer-publisher.js';
1
+ export { SequencerPublisher } from './sequencer-publisher.js';
2
2
  export { SequencerPublisherFactory } from './sequencer-publisher-factory.js';
3
3
 
4
4
  // Used for tests
@@ -1,4 +1,4 @@
1
- import { EthAddress } from '@aztec/aztec.js';
1
+ import { EthAddress, type Logger, createLogger } from '@aztec/aztec.js';
2
2
  import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
3
3
  import type { EpochCache } from '@aztec/epoch-cache';
4
4
  import type { GovernanceProposerContract, PublisherFilter, PublisherManager, RollupContract } from '@aztec/ethereum';
@@ -10,7 +10,7 @@ import { NodeKeystoreAdapter } from '@aztec/validator-client';
10
10
 
11
11
  import type { SequencerClientConfig } from '../config.js';
12
12
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
13
- import { SequencerPublisher } from './sequencer-publisher.js';
13
+ import { type Action, SequencerPublisher } from './sequencer-publisher.js';
14
14
 
15
15
  export type AttestorPublisherPair = {
16
16
  attestorAddress: EthAddress;
@@ -19,6 +19,12 @@ export type AttestorPublisherPair = {
19
19
 
20
20
  export class SequencerPublisherFactory {
21
21
  private publisherMetrics: SequencerPublisherMetrics;
22
+
23
+ /** Stores the last slot in which every action was carried out by a publisher */
24
+ private lastActions: Partial<Record<Action, bigint>> = {};
25
+
26
+ private logger: Logger;
27
+
22
28
  constructor(
23
29
  private sequencerConfig: SequencerClientConfig,
24
30
  private deps: {
@@ -31,9 +37,11 @@ export class SequencerPublisherFactory {
31
37
  governanceProposerContract: GovernanceProposerContract;
32
38
  slashFactoryContract: SlashFactoryContract;
33
39
  nodeKeyStore: NodeKeystoreAdapter;
40
+ logger?: Logger;
34
41
  },
35
42
  ) {
36
43
  this.publisherMetrics = new SequencerPublisherMetrics(deps.telemetry, 'SequencerPublisher');
44
+ this.logger = deps.logger ?? createLogger('sequencer');
37
45
  }
38
46
  /**
39
47
  * Creates a new SequencerPublisher instance.
@@ -69,6 +77,8 @@ export class SequencerPublisherFactory {
69
77
  slashFactoryContract: this.deps.slashFactoryContract,
70
78
  dateProvider: this.deps.dateProvider,
71
79
  metrics: this.publisherMetrics,
80
+ lastActions: this.lastActions,
81
+ log: this.logger.createChild('publisher'),
72
82
  });
73
83
 
74
84
  return {
@@ -10,7 +10,7 @@ import {
10
10
  ValueType,
11
11
  } from '@aztec/telemetry-client';
12
12
 
13
- import { formatEther } from 'viem/utils';
13
+ import { formatEther } from '@spalladino/viem/utils';
14
14
 
15
15
  export type L1TxType = 'process';
16
16