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

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 (44) 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 +2 -0
  6. package/dest/publisher/config.d.ts +2 -4
  7. package/dest/publisher/config.d.ts.map +1 -1
  8. package/dest/publisher/config.js +7 -10
  9. package/dest/publisher/index.d.ts +1 -1
  10. package/dest/publisher/index.d.ts.map +1 -1
  11. package/dest/publisher/index.js +1 -1
  12. package/dest/publisher/sequencer-publisher-factory.d.ts +5 -1
  13. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  14. package/dest/publisher/sequencer-publisher-factory.js +8 -1
  15. package/dest/publisher/sequencer-publisher.d.ts +14 -20
  16. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher.js +53 -58
  18. package/dest/sequencer/errors.d.ts +11 -0
  19. package/dest/sequencer/errors.d.ts.map +1 -0
  20. package/dest/sequencer/errors.js +15 -0
  21. package/dest/sequencer/metrics.d.ts +5 -17
  22. package/dest/sequencer/metrics.d.ts.map +1 -1
  23. package/dest/sequencer/metrics.js +22 -88
  24. package/dest/sequencer/sequencer.d.ts +4 -3
  25. package/dest/sequencer/sequencer.d.ts.map +1 -1
  26. package/dest/sequencer/sequencer.js +27 -21
  27. package/dest/sequencer/timetable.d.ts +0 -6
  28. package/dest/sequencer/timetable.d.ts.map +1 -1
  29. package/dest/sequencer/timetable.js +2 -9
  30. package/dest/sequencer/utils.d.ts +10 -24
  31. package/dest/sequencer/utils.d.ts.map +1 -1
  32. package/dest/sequencer/utils.js +9 -24
  33. package/package.json +28 -28
  34. package/src/client/sequencer-client.ts +6 -2
  35. package/src/config.ts +3 -0
  36. package/src/publisher/config.ts +13 -11
  37. package/src/publisher/index.ts +1 -1
  38. package/src/publisher/sequencer-publisher-factory.ts +12 -2
  39. package/src/publisher/sequencer-publisher.ts +77 -77
  40. package/src/sequencer/errors.ts +21 -0
  41. package/src/sequencer/metrics.ts +24 -100
  42. package/src/sequencer/sequencer.ts +51 -46
  43. package/src/sequencer/timetable.ts +2 -13
  44. package/src/sequencer/utils.ts +10 -24
@@ -4,15 +4,17 @@ 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
10
  import { EthAddress } from '@aztec/foundation/eth-address';
11
+ import { Signature } from '@aztec/foundation/eth-signature';
11
12
  import { Fr } from '@aztec/foundation/fields';
12
13
  import { createLogger } from '@aztec/foundation/log';
13
14
  import { RunningPromise } from '@aztec/foundation/running-promise';
14
15
  import { Timer } from '@aztec/foundation/timer';
15
- import { getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers';
16
+ import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
17
+ import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
16
18
  import { Gas } from '@aztec/stdlib/gas';
17
19
  import { SequencerConfigSchema } from '@aztec/stdlib/interfaces/server';
18
20
  import { orderAttestations } from '@aztec/stdlib/p2p';
@@ -22,8 +24,9 @@ import { ContentCommitment, ProposedBlockHeader } from '@aztec/stdlib/tx';
22
24
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
23
25
  import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
24
26
  import EventEmitter from 'node:events';
27
+ import { SequencerInterruptedError, SequencerTooSlowError } from './errors.js';
25
28
  import { SequencerMetrics } from './metrics.js';
26
- import { SequencerTimetable, SequencerTooSlowError } from './timetable.js';
29
+ import { SequencerTimetable } from './timetable.js';
27
30
  import { SequencerState } from './utils.js';
28
31
  export { SequencerState };
29
32
  /**
@@ -72,10 +75,7 @@ export { SequencerState };
72
75
  publisher;
73
76
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, blockBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
74
77
  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');
78
+ this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
79
79
  // Initialize config
80
80
  this.updateConfig(this.config);
81
81
  }
@@ -140,7 +140,6 @@ export { SequencerState };
140
140
  /**
141
141
  * Starts the sequencer and moves to IDLE state.
142
142
  */ start() {
143
- this.metrics.start();
144
143
  this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
145
144
  this.setState(SequencerState.IDLE, undefined, {
146
145
  force: true
@@ -152,9 +151,10 @@ export { SequencerState };
152
151
  * Stops the sequencer from processing txs and moves to STOPPED state.
153
152
  */ async stop() {
154
153
  this.log.info(`Stopping sequencer`);
155
- this.metrics.stop();
154
+ this.setState(SequencerState.STOPPING, undefined, {
155
+ force: true
156
+ });
156
157
  this.publisher?.interrupt();
157
- await this.validatorClient?.stop();
158
158
  await this.runningPromise?.stop();
159
159
  this.setState(SequencerState.STOPPED, undefined, {
160
160
  force: true
@@ -259,7 +259,6 @@ export { SequencerState };
259
259
  this.publisher = publisher;
260
260
  const coinbase = this.validatorClient.getCoinbaseForAttestor(attestorAddress);
261
261
  const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(attestorAddress);
262
- this.metrics.setCoinbase(coinbase);
263
262
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
264
263
  const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
265
264
  const canProposeCheck = await publisher.canProposeAtNextEthBlock(chainTipArchive, proposerAddressInNextSlot, invalidateBlock);
@@ -317,6 +316,7 @@ export { SequencerState };
317
316
  publisher.enqueueInvalidateBlock(invalidateBlock);
318
317
  }
319
318
  this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
319
+ this.metrics.incOpenSlot(slot, proposerAddressInNextSlot.toString());
320
320
  this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
321
321
  proposer: proposerInNextSlot?.toString(),
322
322
  coinbase,
@@ -379,7 +379,7 @@ export { SequencerState };
379
379
  blockNumber: newBlockNumber,
380
380
  slot: Number(slot)
381
381
  });
382
- this.metrics.incFilledSlot(publisher.getSenderAddress().toString());
382
+ await this.metrics.incFilledSlot(publisher.getSenderAddress().toString(), coinbase);
383
383
  } else if (block) {
384
384
  this.emit('block-publish-failed', l1Response ?? {});
385
385
  }
@@ -407,6 +407,10 @@ export { SequencerState };
407
407
  }
408
408
  }
409
409
  setState(proposedState, slotNumber, opts = {}) {
410
+ if (this.state === SequencerState.STOPPING && proposedState !== SequencerState.STOPPED && !opts.force) {
411
+ this.log.warn(`Cannot set sequencer to ${proposedState} as it is stopping.`);
412
+ throw new SequencerInterruptedError();
413
+ }
410
414
  if (this.state === SequencerState.STOPPED && !opts.force) {
411
415
  this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
412
416
  return;
@@ -452,6 +456,7 @@ export { SequencerState };
452
456
  maxTransactions: this.maxTxsPerBlock,
453
457
  maxBlockSize: this.maxBlockSizeInBytes,
454
458
  maxBlockGas: this.maxBlockGas,
459
+ maxBlobFields: BLOBS_PER_BLOCK * FIELDS_PER_BLOB,
455
460
  deadline
456
461
  };
457
462
  }
@@ -470,7 +475,6 @@ export { SequencerState };
470
475
  const blockNumber = newGlobalVariables.blockNumber;
471
476
  const slot = proposalHeader.slotNumber.toBigInt();
472
477
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
473
- // this.metrics.recordNewBlock(blockNumber, validTxs.length);
474
478
  const workTimer = new Timer();
475
479
  this.setState(SequencerState.CREATING_BLOCK, slot);
476
480
  try {
@@ -515,7 +519,9 @@ export { SequencerState };
515
519
  blockNumber
516
520
  });
517
521
  }
518
- await this.enqueuePublishL2Block(block, attestations, txHashes, invalidateBlock, publisher);
522
+ const attestationsAndSigners = new CommitteeAttestationsAndSigners(attestations ?? []);
523
+ const attestationsAndSignersSignature = this.validatorClient ? await this.validatorClient.signAttestationsAndSigners(attestationsAndSigners, proposerAddress) : Signature.empty();
524
+ await this.enqueuePublishL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, invalidateBlock, publisher);
519
525
  this.metrics.recordBuiltBlock(blockBuildDuration, publicGas.l2Gas);
520
526
  return block;
521
527
  } catch (err) {
@@ -561,32 +567,32 @@ export { SequencerState };
561
567
  const attestationTimeAllowed = this.enforceTimeTable ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_BLOCK) : this.aztecSlotDuration;
562
568
  this.metrics.recordRequiredAttestations(numberOfRequiredAttestations, attestationTimeAllowed);
563
569
  const timer = new Timer();
564
- let collectedAttestionsCount = 0;
570
+ let collectedAttestationsCount = 0;
565
571
  try {
566
572
  const attestationDeadline = new Date(this.dateProvider.now() + attestationTimeAllowed * 1000);
567
573
  const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
568
- collectedAttestionsCount = attestations.length;
574
+ collectedAttestationsCount = attestations.length;
569
575
  // note: the smart contract requires that the signatures are provided in the order of the committee
570
576
  return orderAttestations(attestations, committee);
571
577
  } catch (err) {
572
578
  if (err && err instanceof AttestationTimeoutError) {
573
- collectedAttestionsCount = err.collectedCount;
579
+ collectedAttestationsCount = err.collectedCount;
574
580
  }
575
581
  throw err;
576
582
  } finally{
577
- this.metrics.recordCollectedAttestations(collectedAttestionsCount, timer.ms());
583
+ this.metrics.recordCollectedAttestations(collectedAttestationsCount, timer.ms());
578
584
  }
579
585
  }
580
586
  /**
581
587
  * Publishes the L2Block to the rollup contract.
582
588
  * @param block - The L2Block to be published.
583
- */ async enqueuePublishL2Block(block, attestations, txHashes, invalidateBlock, publisher) {
589
+ */ async enqueuePublishL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, invalidateBlock, publisher) {
584
590
  // Publishes new block to the network and awaits the tx to be mined
585
591
  this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
586
592
  // Time out tx at the end of the slot
587
593
  const slot = block.header.globalVariables.slotNumber.toNumber();
588
594
  const txTimeoutAt = new Date((this.getSlotStartBuildTimestamp(slot) + this.aztecSlotDuration) * 1000);
589
- const enqueued = await publisher.enqueueProposeL2Block(block, attestations, txHashes, {
595
+ const enqueued = await publisher.enqueueProposeL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, {
590
596
  txTimeoutAt,
591
597
  forcePendingBlockNumber: invalidateBlock?.forcePendingBlockNumber
592
598
  });
@@ -689,7 +695,7 @@ export { SequencerState };
689
695
  await publisher.sendRequests();
690
696
  }
691
697
  getSlotStartBuildTimestamp(slotNumber) {
692
- return Number(this.l1Constants.l1GenesisTime) + Number(slotNumber) * this.l1Constants.slotDuration - this.l1Constants.ethereumSlotDuration;
698
+ return getSlotStartBuildTimestamp(slotNumber, this.l1Constants);
693
699
  }
694
700
  getSecondsIntoSlot(slotNumber) {
695
701
  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.2",
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.2",
30
+ "@aztec/bb-prover": "2.1.0-rc.2",
31
+ "@aztec/blob-lib": "2.1.0-rc.2",
32
+ "@aztec/blob-sink": "2.1.0-rc.2",
33
+ "@aztec/constants": "2.1.0-rc.2",
34
+ "@aztec/epoch-cache": "2.1.0-rc.2",
35
+ "@aztec/ethereum": "2.1.0-rc.2",
36
+ "@aztec/foundation": "2.1.0-rc.2",
37
+ "@aztec/l1-artifacts": "2.1.0-rc.2",
38
+ "@aztec/merkle-tree": "2.1.0-rc.2",
39
+ "@aztec/node-keystore": "2.1.0-rc.2",
40
+ "@aztec/noir-acvm_js": "2.1.0-rc.2",
41
+ "@aztec/noir-contracts.js": "2.1.0-rc.2",
42
+ "@aztec/noir-protocol-circuits-types": "2.1.0-rc.2",
43
+ "@aztec/noir-types": "2.1.0-rc.2",
44
+ "@aztec/p2p": "2.1.0-rc.2",
45
+ "@aztec/protocol-contracts": "2.1.0-rc.2",
46
+ "@aztec/prover-client": "2.1.0-rc.2",
47
+ "@aztec/simulator": "2.1.0-rc.2",
48
+ "@aztec/slasher": "2.1.0-rc.2",
49
+ "@aztec/stdlib": "2.1.0-rc.2",
50
+ "@aztec/telemetry-client": "2.1.0-rc.2",
51
+ "@aztec/validator-client": "2.1.0-rc.2",
52
+ "@aztec/world-state": "2.1.0-rc.2",
52
53
  "lodash.chunk": "^4.2.0",
53
- "lodash.pick": "^4.4.0",
54
54
  "tslib": "^2.4.0",
55
55
  "viem": "2.23.7"
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.2",
59
+ "@aztec/kv-store": "2.1.0-rc.2",
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 &
@@ -149,6 +151,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
149
151
  export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
150
152
  ...validatorClientConfigMappings,
151
153
  ...sequencerConfigMappings,
154
+ ...keyStoreConfigMappings,
152
155
  ...l1ReaderConfigMappings,
153
156
  ...getTxSenderConfigMappings('SEQ'),
154
157
  ...getPublisherConfigMappings('SEQ'),
@@ -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 {