@aztec/sequencer-client 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2

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 (85) hide show
  1. package/dest/client/index.js +0 -1
  2. package/dest/client/sequencer-client.js +52 -57
  3. package/dest/config.js +40 -42
  4. package/dest/global_variable_builder/global_builder.js +39 -26
  5. package/dest/global_variable_builder/index.js +0 -1
  6. package/dest/index.js +0 -1
  7. package/dest/publisher/config.js +49 -50
  8. package/dest/publisher/index.js +0 -1
  9. package/dest/publisher/sequencer-publisher-metrics.js +32 -23
  10. package/dest/publisher/sequencer-publisher.js +216 -191
  11. package/dest/sequencer/allowed.js +5 -12
  12. package/dest/sequencer/config.js +1 -2
  13. package/dest/sequencer/index.js +0 -1
  14. package/dest/sequencer/metrics.js +26 -18
  15. package/dest/sequencer/sequencer.js +648 -607
  16. package/dest/sequencer/timetable.js +33 -19
  17. package/dest/sequencer/utils.js +22 -32
  18. package/dest/slasher/factory.js +5 -4
  19. package/dest/slasher/index.js +0 -1
  20. package/dest/slasher/slasher_client.js +107 -110
  21. package/dest/test/index.js +4 -1
  22. package/dest/tx_validator/archive_cache.js +7 -7
  23. package/dest/tx_validator/gas_validator.js +76 -64
  24. package/dest/tx_validator/nullifier_cache.js +8 -8
  25. package/dest/tx_validator/phases_validator.js +27 -19
  26. package/dest/tx_validator/test_utils.js +1 -2
  27. package/dest/tx_validator/tx_validator_factory.js +5 -5
  28. package/package.json +23 -22
  29. package/src/config.ts +1 -1
  30. package/src/publisher/sequencer-publisher.ts +1 -1
  31. package/src/sequencer/sequencer.ts +29 -13
  32. package/dest/client/index.d.ts +0 -2
  33. package/dest/client/index.d.ts.map +0 -1
  34. package/dest/client/sequencer-client.d.ts +0 -68
  35. package/dest/client/sequencer-client.d.ts.map +0 -1
  36. package/dest/config.d.ts +0 -37
  37. package/dest/config.d.ts.map +0 -1
  38. package/dest/global_variable_builder/global_builder.d.ts +0 -29
  39. package/dest/global_variable_builder/global_builder.d.ts.map +0 -1
  40. package/dest/global_variable_builder/index.d.ts +0 -2
  41. package/dest/global_variable_builder/index.d.ts.map +0 -1
  42. package/dest/index.d.ts +0 -8
  43. package/dest/index.d.ts.map +0 -1
  44. package/dest/publisher/config.d.ts +0 -38
  45. package/dest/publisher/config.d.ts.map +0 -1
  46. package/dest/publisher/index.d.ts +0 -2
  47. package/dest/publisher/index.d.ts.map +0 -1
  48. package/dest/publisher/sequencer-publisher-metrics.d.ts +0 -24
  49. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +0 -1
  50. package/dest/publisher/sequencer-publisher.d.ts +0 -163
  51. package/dest/publisher/sequencer-publisher.d.ts.map +0 -1
  52. package/dest/sequencer/allowed.d.ts +0 -3
  53. package/dest/sequencer/allowed.d.ts.map +0 -1
  54. package/dest/sequencer/config.d.ts +0 -2
  55. package/dest/sequencer/config.d.ts.map +0 -1
  56. package/dest/sequencer/index.d.ts +0 -4
  57. package/dest/sequencer/index.d.ts.map +0 -1
  58. package/dest/sequencer/metrics.d.ts +0 -24
  59. package/dest/sequencer/metrics.d.ts.map +0 -1
  60. package/dest/sequencer/sequencer.d.ts +0 -172
  61. package/dest/sequencer/sequencer.d.ts.map +0 -1
  62. package/dest/sequencer/timetable.d.ts +0 -38
  63. package/dest/sequencer/timetable.d.ts.map +0 -1
  64. package/dest/sequencer/utils.d.ts +0 -47
  65. package/dest/sequencer/utils.d.ts.map +0 -1
  66. package/dest/slasher/factory.d.ts +0 -11
  67. package/dest/slasher/factory.d.ts.map +0 -1
  68. package/dest/slasher/index.d.ts +0 -3
  69. package/dest/slasher/index.d.ts.map +0 -1
  70. package/dest/slasher/slasher_client.d.ts +0 -127
  71. package/dest/slasher/slasher_client.d.ts.map +0 -1
  72. package/dest/test/index.d.ts +0 -17
  73. package/dest/test/index.d.ts.map +0 -1
  74. package/dest/tx_validator/archive_cache.d.ts +0 -14
  75. package/dest/tx_validator/archive_cache.d.ts.map +0 -1
  76. package/dest/tx_validator/gas_validator.d.ts +0 -12
  77. package/dest/tx_validator/gas_validator.d.ts.map +0 -1
  78. package/dest/tx_validator/nullifier_cache.d.ts +0 -16
  79. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  80. package/dest/tx_validator/phases_validator.d.ts +0 -11
  81. package/dest/tx_validator/phases_validator.d.ts.map +0 -1
  82. package/dest/tx_validator/test_utils.d.ts +0 -21
  83. package/dest/tx_validator/test_utils.d.ts.map +0 -1
  84. package/dest/tx_validator/tx_validator_factory.d.ts +0 -16
  85. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
@@ -1,6 +1,11 @@
1
- import { __esDecorate, __runInitializers } from "tslib";
2
- import { SequencerConfigSchema, Tx, } from '@aztec/circuit-types';
3
- import { AppendOnlyTreeSnapshot, BlockHeader, ContentCommitment, GENESIS_ARCHIVE_ROOT, Gas, StateReference, } from '@aztec/circuits.js';
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { SequencerConfigSchema, Tx } from '@aztec/circuit-types';
8
+ import { AppendOnlyTreeSnapshot, BlockHeader, ContentCommitment, GENESIS_ARCHIVE_ROOT, Gas, INITIAL_L2_BLOCK_NUM, StateReference } from '@aztec/circuits.js';
4
9
  import { AztecAddress } from '@aztec/foundation/aztec-address';
5
10
  import { omit } from '@aztec/foundation/collection';
6
11
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -25,622 +30,658 @@ export { SequencerState };
25
30
  * - Adds proof requests to the request pool (not for this milestone).
26
31
  * - Receives results to those proofs from the network (repeats as necessary) (not for this milestone).
27
32
  * - Publishes L1 tx(s) to the rollup contract via RollupPublisher.
28
- */
29
- let Sequencer = (() => {
30
- var _a;
31
- let _instanceExtraInitializers = [];
32
- let _work_decorators;
33
- let _buildBlockAndEnqueuePublish_decorators;
34
- let _collectAttestations_decorators;
35
- let _enqueuePublishL2Block_decorators;
36
- let _claimEpochProofRightIfAvailable_decorators;
37
- return _a = class Sequencer {
38
- constructor(publisher, validatorClient, // During migration the validator client can be inactive
39
- globalsBuilder, p2pClient, worldState, slasherClient, blockBuilderFactory, l2BlockSource, l1ToL2MessageSource, publicProcessorFactory, contractDataSource, l1Constants, dateProvider, config = {}, telemetry = getTelemetryClient(), log = createLogger('sequencer')) {
40
- this.publisher = (__runInitializers(this, _instanceExtraInitializers), publisher);
41
- this.validatorClient = validatorClient;
42
- this.globalsBuilder = globalsBuilder;
43
- this.p2pClient = p2pClient;
44
- this.worldState = worldState;
45
- this.slasherClient = slasherClient;
46
- this.blockBuilderFactory = blockBuilderFactory;
47
- this.l2BlockSource = l2BlockSource;
48
- this.l1ToL2MessageSource = l1ToL2MessageSource;
49
- this.publicProcessorFactory = publicProcessorFactory;
50
- this.contractDataSource = contractDataSource;
51
- this.l1Constants = l1Constants;
52
- this.dateProvider = dateProvider;
53
- this.config = config;
54
- this.log = log;
55
- this.pollingIntervalMs = 1000;
56
- this.maxTxsPerBlock = 32;
57
- this.minTxsPerBlock = 1;
58
- this.maxL1TxInclusionTimeIntoSlot = 0;
59
- // TODO: zero values should not be allowed for the following 2 values in PROD
60
- this._coinbase = EthAddress.ZERO;
61
- this._feeRecipient = AztecAddress.ZERO;
62
- this.state = SequencerState.STOPPED;
63
- this.allowedInSetup = [];
64
- this.maxBlockSizeInBytes = 1024 * 1024;
65
- this.maxBlockGas = new Gas(100e9, 100e9);
66
- this.isFlushing = false;
67
- this.enforceTimeTable = false;
68
- this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
69
- // Register the block builder with the validator client for re-execution
70
- this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
71
- // Register the slasher on the publisher to fetch slashing payloads
72
- this.publisher.registerSlashPayloadGetter(this.slasherClient.getSlashPayload.bind(this.slasherClient));
73
- }
74
- get tracer() {
75
- return this.metrics.tracer;
76
- }
77
- /**
78
- * Updates sequencer config.
79
- * @param config - New parameters.
80
- */
81
- async updateConfig(config) {
82
- this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup'));
83
- if (config.transactionPollingIntervalMS !== undefined) {
84
- this.pollingIntervalMs = config.transactionPollingIntervalMS;
85
- }
86
- if (config.maxTxsPerBlock !== undefined) {
87
- this.maxTxsPerBlock = config.maxTxsPerBlock;
88
- }
89
- if (config.minTxsPerBlock !== undefined) {
90
- this.minTxsPerBlock = config.minTxsPerBlock;
91
- }
92
- if (config.maxDABlockGas !== undefined) {
93
- this.maxBlockGas = new Gas(config.maxDABlockGas, this.maxBlockGas.l2Gas);
94
- }
95
- if (config.maxL2BlockGas !== undefined) {
96
- this.maxBlockGas = new Gas(this.maxBlockGas.daGas, config.maxL2BlockGas);
97
- }
98
- if (config.coinbase) {
99
- this._coinbase = config.coinbase;
100
- }
101
- if (config.feeRecipient) {
102
- this._feeRecipient = config.feeRecipient;
103
- }
104
- if (config.allowedInSetup) {
105
- this.allowedInSetup = config.allowedInSetup;
106
- }
107
- else {
108
- this.allowedInSetup = await getDefaultAllowedSetupFunctions();
109
- }
110
- if (config.maxBlockSizeInBytes !== undefined) {
111
- this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
112
- }
113
- if (config.governanceProposerPayload) {
114
- this.publisher.setGovernancePayload(config.governanceProposerPayload);
115
- }
116
- if (config.maxL1TxInclusionTimeIntoSlot !== undefined) {
117
- this.maxL1TxInclusionTimeIntoSlot = config.maxL1TxInclusionTimeIntoSlot;
118
- }
119
- if (config.enforceTimeTable !== undefined) {
120
- this.enforceTimeTable = config.enforceTimeTable;
121
- }
122
- this.setTimeTable();
123
- // TODO: Just read everything from the config object as needed instead of copying everything into local vars.
124
- this.config = config;
125
- }
126
- setTimeTable() {
127
- this.timetable = new SequencerTimetable(this.l1Constants.ethereumSlotDuration, this.aztecSlotDuration, this.maxL1TxInclusionTimeIntoSlot, this.enforceTimeTable, this.metrics, this.log);
128
- this.log.verbose(`Sequencer timetable updated`, { enforceTimeTable: this.enforceTimeTable });
129
- }
130
- /**
131
- * Starts the sequencer and moves to IDLE state.
132
- */
133
- async start() {
134
- await this.updateConfig(this.config);
135
- this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
136
- this.setState(SequencerState.IDLE, 0n, true /** force */);
137
- this.runningPromise.start();
138
- this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
139
- }
140
- /**
141
- * Stops the sequencer from processing txs and moves to STOPPED state.
142
- */
143
- async stop() {
144
- this.log.debug(`Stopping sequencer`);
145
- await this.validatorClient?.stop();
146
- await this.runningPromise?.stop();
147
- await this.slasherClient?.stop();
148
- this.publisher.interrupt();
149
- this.setState(SequencerState.STOPPED, 0n, true /** force */);
150
- this.log.info('Stopped sequencer');
151
- }
152
- /**
153
- * Starts a previously stopped sequencer.
154
- */
155
- restart() {
156
- this.log.info('Restarting sequencer');
157
- this.publisher.restart();
158
- this.runningPromise.start();
159
- this.setState(SequencerState.IDLE, 0n, true /** force */);
160
- }
161
- /**
162
- * Returns the current state of the sequencer.
163
- * @returns An object with a state entry with one of SequencerState.
164
- */
165
- status() {
166
- return { state: this.state };
167
- }
168
- /** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */
169
- flush() {
170
- this.isFlushing = true;
171
- }
172
- /**
173
- * @notice Performs most of the sequencer duties:
174
- * - Checks if we are up to date
175
- * - If we are and we are the sequencer, collect txs and build a block
176
- * - Collect attestations for the block
177
- * - Submit block
178
- * - If our block for some reason is not included, revert the state
179
- */
180
- async doRealWork() {
181
- this.setState(SequencerState.SYNCHRONIZING, 0n);
182
- // Update state when the previous block has been synced
183
- const prevBlockSynced = await this.isBlockSynced();
184
- // Do not go forward with new block if the previous one has not been mined and processed
185
- if (!prevBlockSynced) {
186
- return;
187
- }
188
- this.setState(SequencerState.PROPOSER_CHECK, 0n);
189
- const chainTip = await this.l2BlockSource.getBlock(-1);
190
- const newBlockNumber = (chainTip?.header.globalVariables.blockNumber.toNumber() ?? 0) + 1;
191
- // If we cannot find a tip archive, assume genesis.
192
- const chainTipArchive = chainTip?.archive.root ?? new Fr(GENESIS_ARCHIVE_ROOT);
193
- const slot = await this.slotForProposal(chainTipArchive.toBuffer(), BigInt(newBlockNumber));
194
- if (!slot) {
195
- this.log.debug(`Cannot propose block ${newBlockNumber}`);
196
- return;
197
- }
198
- this.log.info(`Can propose block ${newBlockNumber} at slot ${slot}`);
199
- const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(newBlockNumber), this._coinbase, this._feeRecipient, slot);
200
- const enqueueGovernanceVotePromise = this.publisher.enqueueCastVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.GOVERNANCE);
201
- const enqueueSlashingVotePromise = this.publisher.enqueueCastVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.SLASHING);
202
- // Start collecting proof quotes for the previous epoch if needed in the background
203
- const createProofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
204
- this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
205
- this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
206
- chainTipArchive,
33
+ */ export class Sequencer {
34
+ publisher;
35
+ validatorClient;
36
+ globalsBuilder;
37
+ p2pClient;
38
+ worldState;
39
+ slasherClient;
40
+ blockBuilderFactory;
41
+ l2BlockSource;
42
+ l1ToL2MessageSource;
43
+ publicProcessorFactory;
44
+ contractDataSource;
45
+ l1Constants;
46
+ dateProvider;
47
+ config;
48
+ log;
49
+ runningPromise;
50
+ pollingIntervalMs;
51
+ maxTxsPerBlock;
52
+ minTxsPerBlock;
53
+ maxL1TxInclusionTimeIntoSlot;
54
+ // TODO: zero values should not be allowed for the following 2 values in PROD
55
+ _coinbase;
56
+ _feeRecipient;
57
+ state;
58
+ allowedInSetup;
59
+ maxBlockSizeInBytes;
60
+ maxBlockGas;
61
+ metrics;
62
+ isFlushing;
63
+ /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
64
+ enforceTimeTable;
65
+ constructor(publisher, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, blockBuilderFactory, l2BlockSource, l1ToL2MessageSource, publicProcessorFactory, contractDataSource, l1Constants, dateProvider, config = {}, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
66
+ this.publisher = publisher;
67
+ this.validatorClient = validatorClient;
68
+ this.globalsBuilder = globalsBuilder;
69
+ this.p2pClient = p2pClient;
70
+ this.worldState = worldState;
71
+ this.slasherClient = slasherClient;
72
+ this.blockBuilderFactory = blockBuilderFactory;
73
+ this.l2BlockSource = l2BlockSource;
74
+ this.l1ToL2MessageSource = l1ToL2MessageSource;
75
+ this.publicProcessorFactory = publicProcessorFactory;
76
+ this.contractDataSource = contractDataSource;
77
+ this.l1Constants = l1Constants;
78
+ this.dateProvider = dateProvider;
79
+ this.config = config;
80
+ this.log = log;
81
+ this.pollingIntervalMs = 1000;
82
+ this.maxTxsPerBlock = 32;
83
+ this.minTxsPerBlock = 1;
84
+ this.maxL1TxInclusionTimeIntoSlot = 0;
85
+ this._coinbase = EthAddress.ZERO;
86
+ this._feeRecipient = AztecAddress.ZERO;
87
+ this.state = SequencerState.STOPPED;
88
+ this.allowedInSetup = [];
89
+ this.maxBlockSizeInBytes = 1024 * 1024;
90
+ this.maxBlockGas = new Gas(100e9, 100e9);
91
+ this.isFlushing = false;
92
+ this.enforceTimeTable = false;
93
+ this.metrics = new SequencerMetrics(telemetry, ()=>this.state, 'Sequencer');
94
+ // Register the block builder with the validator client for re-execution
95
+ this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
96
+ // Register the slasher on the publisher to fetch slashing payloads
97
+ this.publisher.registerSlashPayloadGetter(this.slasherClient.getSlashPayload.bind(this.slasherClient));
98
+ }
99
+ get tracer() {
100
+ return this.metrics.tracer;
101
+ }
102
+ /**
103
+ * Updates sequencer config.
104
+ * @param config - New parameters.
105
+ */ async updateConfig(config) {
106
+ this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup'));
107
+ if (config.transactionPollingIntervalMS !== undefined) {
108
+ this.pollingIntervalMs = config.transactionPollingIntervalMS;
109
+ }
110
+ if (config.maxTxsPerBlock !== undefined) {
111
+ this.maxTxsPerBlock = config.maxTxsPerBlock;
112
+ }
113
+ if (config.minTxsPerBlock !== undefined) {
114
+ this.minTxsPerBlock = config.minTxsPerBlock;
115
+ }
116
+ if (config.maxDABlockGas !== undefined) {
117
+ this.maxBlockGas = new Gas(config.maxDABlockGas, this.maxBlockGas.l2Gas);
118
+ }
119
+ if (config.maxL2BlockGas !== undefined) {
120
+ this.maxBlockGas = new Gas(this.maxBlockGas.daGas, config.maxL2BlockGas);
121
+ }
122
+ if (config.coinbase) {
123
+ this._coinbase = config.coinbase;
124
+ }
125
+ if (config.feeRecipient) {
126
+ this._feeRecipient = config.feeRecipient;
127
+ }
128
+ if (config.allowedInSetup) {
129
+ this.allowedInSetup = config.allowedInSetup;
130
+ } else {
131
+ this.allowedInSetup = await getDefaultAllowedSetupFunctions();
132
+ }
133
+ if (config.maxBlockSizeInBytes !== undefined) {
134
+ this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
135
+ }
136
+ if (config.governanceProposerPayload) {
137
+ this.publisher.setGovernancePayload(config.governanceProposerPayload);
138
+ }
139
+ if (config.maxL1TxInclusionTimeIntoSlot !== undefined) {
140
+ this.maxL1TxInclusionTimeIntoSlot = config.maxL1TxInclusionTimeIntoSlot;
141
+ }
142
+ if (config.enforceTimeTable !== undefined) {
143
+ this.enforceTimeTable = config.enforceTimeTable;
144
+ }
145
+ this.setTimeTable();
146
+ // TODO: Just read everything from the config object as needed instead of copying everything into local vars.
147
+ this.config = config;
148
+ }
149
+ setTimeTable() {
150
+ this.timetable = new SequencerTimetable(this.l1Constants.ethereumSlotDuration, this.aztecSlotDuration, this.maxL1TxInclusionTimeIntoSlot, this.enforceTimeTable, this.metrics, this.log);
151
+ this.log.verbose(`Sequencer timetable updated`, {
152
+ enforceTimeTable: this.enforceTimeTable
153
+ });
154
+ }
155
+ /**
156
+ * Starts the sequencer and moves to IDLE state.
157
+ */ async start() {
158
+ await this.updateConfig(this.config);
159
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
160
+ this.setState(SequencerState.IDLE, 0n, true);
161
+ this.runningPromise.start();
162
+ this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
163
+ }
164
+ /**
165
+ * Stops the sequencer from processing txs and moves to STOPPED state.
166
+ */ async stop() {
167
+ this.log.debug(`Stopping sequencer`);
168
+ await this.validatorClient?.stop();
169
+ await this.runningPromise?.stop();
170
+ await this.slasherClient?.stop();
171
+ this.publisher.interrupt();
172
+ this.setState(SequencerState.STOPPED, 0n, true);
173
+ this.log.info('Stopped sequencer');
174
+ }
175
+ /**
176
+ * Starts a previously stopped sequencer.
177
+ */ restart() {
178
+ this.log.info('Restarting sequencer');
179
+ this.publisher.restart();
180
+ this.runningPromise.start();
181
+ this.setState(SequencerState.IDLE, 0n, true);
182
+ }
183
+ /**
184
+ * Returns the current state of the sequencer.
185
+ * @returns An object with a state entry with one of SequencerState.
186
+ */ status() {
187
+ return {
188
+ state: this.state
189
+ };
190
+ }
191
+ /** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */ flush() {
192
+ this.isFlushing = true;
193
+ }
194
+ /**
195
+ * @notice Performs most of the sequencer duties:
196
+ * - Checks if we are up to date
197
+ * - If we are and we are the sequencer, collect txs and build a block
198
+ * - Collect attestations for the block
199
+ * - Submit block
200
+ * - If our block for some reason is not included, revert the state
201
+ */ async doRealWork() {
202
+ this.setState(SequencerState.SYNCHRONIZING, 0n);
203
+ // Update state when the previous block has been synced
204
+ const chainTip = await this.getChainTip();
205
+ // Do not go forward with new block if the previous one has not been mined and processed
206
+ if (!chainTip) {
207
+ return;
208
+ }
209
+ this.setState(SequencerState.PROPOSER_CHECK, 0n);
210
+ const newBlockNumber = chainTip.blockNumber + 1;
211
+ // If we cannot find a tip archive, assume genesis.
212
+ const chainTipArchive = chainTip.archive;
213
+ const slot = await this.slotForProposal(chainTipArchive.toBuffer(), BigInt(newBlockNumber));
214
+ if (!slot) {
215
+ this.log.debug(`Cannot propose block ${newBlockNumber}`);
216
+ return;
217
+ }
218
+ this.log.debug(`Can propose block ${newBlockNumber} at slot ${slot}`);
219
+ const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(newBlockNumber), this._coinbase, this._feeRecipient, slot);
220
+ const enqueueGovernanceVotePromise = this.publisher.enqueueCastVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.GOVERNANCE);
221
+ const enqueueSlashingVotePromise = this.publisher.enqueueCastVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.SLASHING);
222
+ // Start collecting proof quotes for the previous epoch if needed in the background
223
+ const createProofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
224
+ this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
225
+ this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
226
+ chainTipArchive,
227
+ blockNumber: newBlockNumber,
228
+ slot
229
+ });
230
+ // If I created a "partial" header here that should make our job much easier.
231
+ const proposalHeader = new BlockHeader(new AppendOnlyTreeSnapshot(chainTipArchive, 1), ContentCommitment.empty(), StateReference.empty(), newGlobalVariables, Fr.ZERO, Fr.ZERO);
232
+ let finishedFlushing = false;
233
+ const pendingTxCount = await this.p2pClient.getPendingTxCount();
234
+ if (pendingTxCount >= this.minTxsPerBlock || this.isFlushing) {
235
+ // We don't fetch exactly maxTxsPerBlock txs here because we may not need all of them if we hit a limit before,
236
+ // and also we may need to fetch more if we don't have enough valid txs.
237
+ const pendingTxs = this.p2pClient.iteratePendingTxs();
238
+ await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader).catch((err)=>{
239
+ this.log.error(`Error building/enqueuing block`, err, {
207
240
  blockNumber: newBlockNumber,
208
- slot,
209
- });
210
- // If I created a "partial" header here that should make our job much easier.
211
- const proposalHeader = new BlockHeader(new AppendOnlyTreeSnapshot(chainTipArchive, 1), ContentCommitment.empty(), StateReference.empty(), newGlobalVariables, Fr.ZERO, Fr.ZERO);
212
- let finishedFlushing = false;
213
- const pendingTxCount = await this.p2pClient.getPendingTxCount();
214
- if (pendingTxCount >= this.minTxsPerBlock || this.isFlushing) {
215
- // We don't fetch exactly maxTxsPerBlock txs here because we may not need all of them if we hit a limit before,
216
- // and also we may need to fetch more if we don't have enough valid txs.
217
- const pendingTxs = this.p2pClient.iteratePendingTxs();
218
- await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader).catch(err => {
219
- this.log.error(`Error building/enqueuing block`, err, { blockNumber: newBlockNumber, slot });
220
- });
221
- finishedFlushing = true;
222
- }
223
- else {
224
- this.log.debug(`Not enough txs to build block ${newBlockNumber} at slot ${slot}: got ${pendingTxCount} txs, need ${this.minTxsPerBlock}`);
225
- }
226
- await enqueueGovernanceVotePromise.catch(err => {
227
- this.log.error(`Error enqueuing governance vote`, err, { blockNumber: newBlockNumber, slot });
241
+ slot
228
242
  });
229
- await enqueueSlashingVotePromise.catch(err => {
230
- this.log.error(`Error enqueuing slashing vote`, err, { blockNumber: newBlockNumber, slot });
231
- });
232
- await createProofQuotePromise
233
- .then(quote => (quote ? this.publisher.enqueueClaimEpochProofRight(quote) : undefined))
234
- .catch(err => {
235
- this.log.error(`Error creating proof quote`, err, { blockNumber: newBlockNumber, slot });
236
- });
237
- await this.publisher.sendRequests();
238
- if (finishedFlushing) {
239
- this.isFlushing = false;
240
- }
241
- this.setState(SequencerState.IDLE, 0n);
243
+ });
244
+ finishedFlushing = true;
245
+ } else {
246
+ this.log.debug(`Not enough txs to build block ${newBlockNumber} at slot ${slot}: got ${pendingTxCount} txs, need ${this.minTxsPerBlock}`);
247
+ }
248
+ await enqueueGovernanceVotePromise.catch((err)=>{
249
+ this.log.error(`Error enqueuing governance vote`, err, {
250
+ blockNumber: newBlockNumber,
251
+ slot
252
+ });
253
+ });
254
+ await enqueueSlashingVotePromise.catch((err)=>{
255
+ this.log.error(`Error enqueuing slashing vote`, err, {
256
+ blockNumber: newBlockNumber,
257
+ slot
258
+ });
259
+ });
260
+ await createProofQuotePromise.then((quote)=>quote ? this.publisher.enqueueClaimEpochProofRight(quote) : undefined).catch((err)=>{
261
+ this.log.error(`Error creating proof quote`, err, {
262
+ blockNumber: newBlockNumber,
263
+ slot
264
+ });
265
+ });
266
+ await this.publisher.sendRequests();
267
+ if (finishedFlushing) {
268
+ this.isFlushing = false;
269
+ }
270
+ this.setState(SequencerState.IDLE, 0n);
271
+ }
272
+ async work() {
273
+ try {
274
+ await this.doRealWork();
275
+ } catch (err) {
276
+ if (err instanceof SequencerTooSlowError) {
277
+ this.log.warn(err.message);
278
+ } else {
279
+ // Re-throw other errors
280
+ throw err;
242
281
  }
243
- async work() {
244
- try {
245
- await this.doRealWork();
246
- }
247
- catch (err) {
248
- if (err instanceof SequencerTooSlowError) {
249
- this.log.warn(err.message);
250
- }
251
- else {
252
- // Re-throw other errors
253
- throw err;
254
- }
255
- }
256
- finally {
257
- this.setState(SequencerState.IDLE, 0n);
258
- }
259
- }
260
- getForwarderAddress() {
261
- return this.publisher.getForwarderAddress();
262
- }
263
- /**
264
- * Checks if we can propose at the next block and returns the slot number if we can.
265
- * @param tipArchive - The archive of the previous block.
266
- * @param proposalBlockNumber - The block number of the proposal.
267
- * @returns The slot number if we can propose at the next block, otherwise undefined.
268
- */
269
- async slotForProposal(tipArchive, proposalBlockNumber) {
270
- const result = await this.publisher.canProposeAtNextEthBlock(tipArchive);
271
- if (!result) {
272
- return undefined;
273
- }
274
- const [slot, blockNumber] = result;
275
- if (proposalBlockNumber !== blockNumber) {
276
- const msg = `Sequencer block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}.`;
277
- this.log.warn(msg);
278
- throw new Error(msg);
279
- }
280
- return slot;
281
- }
282
- /**
283
- * Sets the sequencer state and checks if we have enough time left in the slot to transition to the new state.
284
- * @param proposedState - The new state to transition to.
285
- * @param currentSlotNumber - The current slot number.
286
- * @param force - Whether to force the transition even if the sequencer is stopped.
287
- *
288
- * @dev If the `currentSlotNumber` doesn't matter (e.g. transitioning to IDLE), pass in `0n`;
289
- * it is only used to check if we have enough time left in the slot to transition to the new state.
290
- */
291
- setState(proposedState, currentSlotNumber, force = false) {
292
- if (this.state === SequencerState.STOPPED && force !== true) {
293
- this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
294
- return;
295
- }
296
- const secondsIntoSlot = this.getSecondsIntoSlot(currentSlotNumber);
297
- this.timetable.assertTimeLeft(proposedState, secondsIntoSlot);
298
- this.log.debug(`Transitioning from ${this.state} to ${proposedState}`);
299
- this.state = proposedState;
282
+ } finally{
283
+ this.setState(SequencerState.IDLE, 0n);
284
+ }
285
+ }
286
+ getForwarderAddress() {
287
+ return this.publisher.getForwarderAddress();
288
+ }
289
+ /**
290
+ * Checks if we can propose at the next block and returns the slot number if we can.
291
+ * @param tipArchive - The archive of the previous block.
292
+ * @param proposalBlockNumber - The block number of the proposal.
293
+ * @returns The slot number if we can propose at the next block, otherwise undefined.
294
+ */ async slotForProposal(tipArchive, proposalBlockNumber) {
295
+ const result = await this.publisher.canProposeAtNextEthBlock(tipArchive);
296
+ if (!result) {
297
+ return undefined;
298
+ }
299
+ const [slot, blockNumber] = result;
300
+ if (proposalBlockNumber !== blockNumber) {
301
+ const msg = `Sequencer block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}.`;
302
+ this.log.warn(msg);
303
+ throw new Error(msg);
304
+ }
305
+ return slot;
306
+ }
307
+ /**
308
+ * Sets the sequencer state and checks if we have enough time left in the slot to transition to the new state.
309
+ * @param proposedState - The new state to transition to.
310
+ * @param currentSlotNumber - The current slot number.
311
+ * @param force - Whether to force the transition even if the sequencer is stopped.
312
+ *
313
+ * @dev If the `currentSlotNumber` doesn't matter (e.g. transitioning to IDLE), pass in `0n`;
314
+ * it is only used to check if we have enough time left in the slot to transition to the new state.
315
+ */ setState(proposedState, currentSlotNumber, force = false) {
316
+ if (this.state === SequencerState.STOPPED && force !== true) {
317
+ this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
318
+ return;
319
+ }
320
+ const secondsIntoSlot = this.getSecondsIntoSlot(currentSlotNumber);
321
+ this.timetable.assertTimeLeft(proposedState, secondsIntoSlot);
322
+ this.log.debug(`Transitioning from ${this.state} to ${proposedState}`);
323
+ this.state = proposedState;
324
+ }
325
+ /**
326
+ * Build a block
327
+ *
328
+ * Shared between the sequencer and the validator for re-execution
329
+ *
330
+ * @param pendingTxs - The pending transactions to construct the block from
331
+ * @param newGlobalVariables - The global variables for the new block
332
+ * @param historicalHeader - The historical header of the parent
333
+ * @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
334
+ */ async buildBlock(pendingTxs, newGlobalVariables, opts = {}) {
335
+ const blockNumber = newGlobalVariables.blockNumber.toNumber();
336
+ const slot = newGlobalVariables.slotNumber.toBigInt();
337
+ this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
338
+ const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber));
339
+ const msgCount = l1ToL2Messages.length;
340
+ this.log.verbose(`Building block ${blockNumber} for slot ${slot}`, {
341
+ slot,
342
+ blockNumber,
343
+ msgCount,
344
+ validator: opts.validateOnly
345
+ });
346
+ // Sync to the previous block at least
347
+ await this.worldState.syncImmediate(blockNumber - 1);
348
+ this.log.debug(`Synced to previous block ${blockNumber - 1}`);
349
+ // NB: separating the dbs because both should update the state
350
+ const publicProcessorFork = await this.worldState.fork();
351
+ const orchestratorFork = await this.worldState.fork();
352
+ const previousBlockHeader = (await this.l2BlockSource.getBlock(blockNumber - 1))?.header ?? orchestratorFork.getInitialHeader();
353
+ try {
354
+ const processor = this.publicProcessorFactory.create(publicProcessorFork, newGlobalVariables, true);
355
+ const blockBuildingTimer = new Timer();
356
+ const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
357
+ await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages, previousBlockHeader);
358
+ // Deadline for processing depends on whether we're proposing a block
359
+ const secondsIntoSlot = this.getSecondsIntoSlot(slot);
360
+ const processingEndTimeWithinSlot = opts.validateOnly ? this.timetable.getValidatorReexecTimeEnd(secondsIntoSlot) : this.timetable.getBlockProposalExecTimeEnd(secondsIntoSlot);
361
+ // Deadline is only set if enforceTimeTable is enabled.
362
+ const deadline = this.enforceTimeTable ? new Date((this.getSlotStartTimestamp(slot) + processingEndTimeWithinSlot) * 1000) : undefined;
363
+ this.log.verbose(`Processing pending txs`, {
364
+ slot,
365
+ slotStart: new Date(this.getSlotStartTimestamp(slot) * 1000),
366
+ now: new Date(this.dateProvider.now()),
367
+ deadline
368
+ });
369
+ const validators = createValidatorsForBlockBuilding(publicProcessorFork, this.contractDataSource, newGlobalVariables, !!this.config.enforceFees, this.allowedInSetup);
370
+ // TODO(#11000): Public processor should just handle processing, one tx at a time. It should be responsibility
371
+ // of the sequencer to update world state and iterate over txs. We should refactor this along with unifying the
372
+ // publicProcessorFork and orchestratorFork, to avoid doing tree insertions twice when building the block.
373
+ const proposerLimits = {
374
+ maxTransactions: this.maxTxsPerBlock,
375
+ maxBlockSize: this.maxBlockSizeInBytes,
376
+ maxBlockGas: this.maxBlockGas
377
+ };
378
+ const limits = opts.validateOnly ? {
379
+ deadline
380
+ } : {
381
+ deadline,
382
+ ...proposerLimits
383
+ };
384
+ const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(()=>processor.process(pendingTxs, limits, validators));
385
+ if (!opts.validateOnly && failedTxs.length > 0) {
386
+ const failedTxData = failedTxs.map((fail)=>fail.tx);
387
+ const failedTxHashes = await Tx.getHashes(failedTxData);
388
+ this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
389
+ await this.p2pClient.deleteTxs(failedTxHashes);
300
390
  }
301
- /**
302
- * Build a block
303
- *
304
- * Shared between the sequencer and the validator for re-execution
305
- *
306
- * @param pendingTxs - The pending transactions to construct the block from
307
- * @param newGlobalVariables - The global variables for the new block
308
- * @param historicalHeader - The historical header of the parent
309
- * @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
310
- */
311
- async buildBlock(pendingTxs, newGlobalVariables, opts = {}) {
312
- const blockNumber = newGlobalVariables.blockNumber.toNumber();
313
- const slot = newGlobalVariables.slotNumber.toBigInt();
314
- this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
315
- const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber));
316
- const msgCount = l1ToL2Messages.length;
317
- this.log.verbose(`Building block ${blockNumber} for slot ${slot}`, {
391
+ if (!opts.validateOnly && // We check for minTxCount only if we are proposing a block, not if we are validating it
392
+ !this.isFlushing && // And we skip the check when flushing, since we want all pending txs to go out, no matter if too few
393
+ this.minTxsPerBlock !== undefined && processedTxs.length < this.minTxsPerBlock) {
394
+ this.log.warn(`Block ${blockNumber} has too few txs to be proposed (got ${processedTxs.length} but required ${this.minTxsPerBlock})`, {
318
395
  slot,
319
396
  blockNumber,
320
- msgCount,
321
- validator: opts.validateOnly,
397
+ processedTxCount: processedTxs.length
322
398
  });
323
- // Sync to the previous block at least
324
- await this.worldState.syncImmediate(blockNumber - 1);
325
- this.log.debug(`Synced to previous block ${blockNumber - 1}`);
326
- // NB: separating the dbs because both should update the state
327
- const publicProcessorFork = await this.worldState.fork();
328
- const orchestratorFork = await this.worldState.fork();
329
- const previousBlockHeader = (await this.l2BlockSource.getBlock(blockNumber - 1))?.header ?? orchestratorFork.getInitialHeader();
330
- try {
331
- const processor = this.publicProcessorFactory.create(publicProcessorFork, newGlobalVariables, true);
332
- const blockBuildingTimer = new Timer();
333
- const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
334
- await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages, previousBlockHeader);
335
- // Deadline for processing depends on whether we're proposing a block
336
- const secondsIntoSlot = this.getSecondsIntoSlot(slot);
337
- const processingEndTimeWithinSlot = opts.validateOnly
338
- ? this.timetable.getValidatorReexecTimeEnd(secondsIntoSlot)
339
- : this.timetable.getBlockProposalExecTimeEnd(secondsIntoSlot);
340
- // Deadline is only set if enforceTimeTable is enabled.
341
- const deadline = this.enforceTimeTable
342
- ? new Date((this.getSlotStartTimestamp(slot) + processingEndTimeWithinSlot) * 1000)
343
- : undefined;
344
- this.log.verbose(`Processing pending txs`, {
345
- slot,
346
- slotStart: new Date(this.getSlotStartTimestamp(slot) * 1000),
347
- now: new Date(this.dateProvider.now()),
348
- deadline,
349
- });
350
- const validators = createValidatorsForBlockBuilding(publicProcessorFork, this.contractDataSource, newGlobalVariables, !!this.config.enforceFees, this.allowedInSetup);
351
- // TODO(#11000): Public processor should just handle processing, one tx at a time. It should be responsibility
352
- // of the sequencer to update world state and iterate over txs. We should refactor this along with unifying the
353
- // publicProcessorFork and orchestratorFork, to avoid doing tree insertions twice when building the block.
354
- const proposerLimits = {
355
- maxTransactions: this.maxTxsPerBlock,
356
- maxBlockSize: this.maxBlockSizeInBytes,
357
- maxBlockGas: this.maxBlockGas,
358
- };
359
- const limits = opts.validateOnly ? { deadline } : { deadline, ...proposerLimits };
360
- const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() => processor.process(pendingTxs, limits, validators));
361
- if (!opts.validateOnly && failedTxs.length > 0) {
362
- const failedTxData = failedTxs.map(fail => fail.tx);
363
- const failedTxHashes = await Tx.getHashes(failedTxData);
364
- this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
365
- await this.p2pClient.deleteTxs(failedTxHashes);
366
- }
367
- if (!opts.validateOnly && // We check for minTxCount only if we are proposing a block, not if we are validating it
368
- !this.isFlushing && // And we skip the check when flushing, since we want all pending txs to go out, no matter if too few
369
- this.minTxsPerBlock !== undefined &&
370
- processedTxs.length < this.minTxsPerBlock) {
371
- this.log.warn(`Block ${blockNumber} has too few txs to be proposed (got ${processedTxs.length} but required ${this.minTxsPerBlock})`, { slot, blockNumber, processedTxCount: processedTxs.length });
372
- throw new Error(`Block has too few successful txs to be proposed`);
373
- }
374
- const start = process.hrtime.bigint();
375
- await blockBuilder.addTxs(processedTxs);
376
- const end = process.hrtime.bigint();
377
- const duration = Number(end - start) / 1000;
378
- this.metrics.recordBlockBuilderTreeInsertions(duration);
379
- // All real transactions have been added, set the block as full and pad if needed
380
- const block = await blockBuilder.setBlockCompleted();
381
- // How much public gas was processed
382
- const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
383
- return {
384
- block,
385
- publicGas,
386
- publicProcessorDuration,
387
- numMsgs: l1ToL2Messages.length,
388
- numTxs: processedTxs.length,
389
- numFailedTxs: failedTxs.length,
390
- blockBuildingTimer,
391
- };
392
- }
393
- finally {
394
- // We create a fresh processor each time to reset any cached state (eg storage writes)
395
- // We wait a bit to close the forks since the processor may still be working on a dangling tx
396
- // which was interrupted due to the processingDeadline being hit.
397
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
398
- setTimeout(async () => {
399
- try {
400
- await publicProcessorFork.close();
401
- await orchestratorFork.close();
402
- }
403
- catch (err) {
404
- // This can happen if the sequencer is stopped before we hit this timeout.
405
- this.log.warn(`Error closing forks for block processing`, err);
406
- }
407
- }, 5000);
408
- }
399
+ throw new Error(`Block has too few successful txs to be proposed`);
409
400
  }
410
- /**
411
- * @notice Build and propose a block to the chain
412
- *
413
- * @dev MUST throw instead of exiting early to ensure that world-state
414
- * is being rolled back if the block is dropped.
415
- *
416
- * @param pendingTxs - Iterable of pending transactions to construct the block from
417
- * @param proposalHeader - The partial header constructed for the proposal
418
- */
419
- async buildBlockAndEnqueuePublish(pendingTxs, proposalHeader) {
420
- await this.publisher.validateBlockForSubmission(proposalHeader);
421
- const newGlobalVariables = proposalHeader.globalVariables;
422
- const blockNumber = newGlobalVariables.blockNumber.toNumber();
423
- const slot = newGlobalVariables.slotNumber.toBigInt();
424
- // this.metrics.recordNewBlock(blockNumber, validTxs.length);
425
- const workTimer = new Timer();
426
- this.setState(SequencerState.CREATING_BLOCK, slot);
401
+ const start = process.hrtime.bigint();
402
+ await blockBuilder.addTxs(processedTxs);
403
+ const end = process.hrtime.bigint();
404
+ const duration = Number(end - start) / 1_000;
405
+ this.metrics.recordBlockBuilderTreeInsertions(duration);
406
+ // All real transactions have been added, set the block as full and pad if needed
407
+ const block = await blockBuilder.setBlockCompleted();
408
+ // How much public gas was processed
409
+ const publicGas = processedTxs.reduce((acc, tx)=>acc.add(tx.gasUsed.publicGas), Gas.empty());
410
+ return {
411
+ block,
412
+ publicGas,
413
+ publicProcessorDuration,
414
+ numMsgs: l1ToL2Messages.length,
415
+ numTxs: processedTxs.length,
416
+ numFailedTxs: failedTxs.length,
417
+ blockBuildingTimer
418
+ };
419
+ } finally{
420
+ // We create a fresh processor each time to reset any cached state (eg storage writes)
421
+ // We wait a bit to close the forks since the processor may still be working on a dangling tx
422
+ // which was interrupted due to the processingDeadline being hit.
423
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
424
+ setTimeout(async ()=>{
427
425
  try {
428
- const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables);
429
- const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
430
- this.metrics.recordBuiltBlock(workTimer.ms(), publicGas.l2Gas);
431
- // TODO(@PhilWindle) We should probably periodically check for things like another
432
- // block being published before ours instead of just waiting on our block
433
- await this.publisher.validateBlockForSubmission(block.header);
434
- const blockStats = {
435
- eventName: 'l2-block-built',
436
- creator: this.publisher.getSenderAddress().toString(),
437
- duration: workTimer.ms(),
438
- publicProcessDuration: publicProcessorDuration,
439
- rollupCircuitsDuration: blockBuildingTimer.ms(),
440
- ...block.getStats(),
441
- };
442
- const blockHash = await block.hash();
443
- const txHashes = block.body.txEffects.map(tx => tx.txHash);
444
- this.log.info(`Built block ${block.number} for slot ${slot} with ${numTxs} txs and ${numMsgs} messages. ${publicGas.l2Gas / workTimer.s()} mana/s`, {
445
- blockHash,
446
- globalVariables: block.header.globalVariables.toInspect(),
447
- txHashes,
448
- ...blockStats,
449
- });
450
- this.log.debug('Collecting attestations');
451
- const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
452
- const attestations = await this.collectAttestations(block, txHashes);
453
- if (attestations !== undefined) {
454
- this.log.verbose(`Collected ${attestations.length} attestations`, { blockHash, blockNumber });
455
- }
456
- stopCollectingAttestationsTimer();
457
- return this.enqueuePublishL2Block(block, attestations, txHashes);
458
- }
459
- catch (err) {
460
- this.metrics.recordFailedBlock();
461
- throw err;
462
- }
463
- }
464
- async collectAttestations(block, txHashes) {
465
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
466
- const committee = await this.publisher.getCurrentEpochCommittee();
467
- if (committee.length === 0) {
468
- this.log.verbose(`Attesting committee is empty`);
469
- return undefined;
470
- }
471
- else {
472
- this.log.debug(`Attesting committee length is ${committee.length}`);
473
- }
474
- if (!this.validatorClient) {
475
- const msg = 'Missing validator client: Cannot collect attestations';
476
- this.log.error(msg);
477
- throw new Error(msg);
478
- }
479
- const numberOfRequiredAttestations = Math.floor((committee.length * 2) / 3) + 1;
480
- const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
481
- this.setState(SequencerState.COLLECTING_ATTESTATIONS, slotNumber);
482
- this.log.debug('Creating block proposal for validators');
483
- const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
484
- if (!proposal) {
485
- const msg = `Failed to create block proposal`;
486
- throw new Error(msg);
487
- }
488
- this.log.debug('Broadcasting block proposal to validators');
489
- this.validatorClient.broadcastBlockProposal(proposal);
490
- const attestationTimeAllowed = this.enforceTimeTable
491
- ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_BLOCK)
492
- : this.aztecSlotDuration;
493
- const attestationDeadline = new Date(this.dateProvider.now() + attestationTimeAllowed * 1000);
494
- const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
495
- // note: the smart contract requires that the signatures are provided in the order of the committee
496
- return orderAttestations(attestations, committee);
497
- }
498
- async createProofClaimForPreviousEpoch(slotNumber) {
499
- try {
500
- // Find out which epoch we are currently in
501
- const epochToProve = await this.publisher.getClaimableEpoch();
502
- if (epochToProve === undefined) {
503
- this.log.trace(`No epoch to claim at slot ${slotNumber}`);
504
- return undefined;
505
- }
506
- // Get quotes for the epoch to be proven
507
- this.log.debug(`Collecting proof quotes for epoch ${epochToProve}`);
508
- const p2pQuotes = await this.p2pClient
509
- .getEpochProofQuotes(epochToProve)
510
- .then(quotes => quotes
511
- .filter(x => x.payload.validUntilSlot >= slotNumber)
512
- .filter(x => x.payload.epochToProve === epochToProve));
513
- this.log.verbose(`Retrieved ${p2pQuotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
514
- epochToProve,
515
- slotNumber,
516
- quotes: p2pQuotes.map(q => q.payload),
517
- });
518
- if (!p2pQuotes.length) {
519
- return undefined;
520
- }
521
- // ensure these quotes are still valid for the slot and have the contract validate them
522
- const validQuotes = await this.publisher.filterValidQuotes(p2pQuotes);
523
- if (!validQuotes.length) {
524
- this.log.warn(`Failed to find any valid proof quotes`);
525
- return undefined;
526
- }
527
- // pick the quote with the lowest fee
528
- const sortedQuotes = validQuotes.sort((a, b) => a.payload.basisPointFee - b.payload.basisPointFee);
529
- const quote = sortedQuotes[0];
530
- this.log.info(`Selected proof quote for proof claim`, { quote: quote.toInspect() });
531
- return quote;
532
- }
533
- catch (err) {
534
- this.log.error(`Failed to create proof claim for previous epoch`, err, { slotNumber });
535
- return undefined;
536
- }
537
- }
538
- /**
539
- * Publishes the L2Block to the rollup contract.
540
- * @param block - The L2Block to be published.
541
- */
542
- async enqueuePublishL2Block(block, attestations, txHashes) {
543
- // Publishes new block to the network and awaits the tx to be mined
544
- this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
545
- // Time out tx at the end of the slot
546
- const slot = block.header.globalVariables.slotNumber.toNumber();
547
- const txTimeoutAt = new Date((this.getSlotStartTimestamp(slot) + this.aztecSlotDuration) * 1000);
548
- const enqueued = await this.publisher.enqueueProposeL2Block(block, attestations, txHashes, {
549
- txTimeoutAt,
426
+ await publicProcessorFork.close();
427
+ await orchestratorFork.close();
428
+ } catch (err) {
429
+ // This can happen if the sequencer is stopped before we hit this timeout.
430
+ this.log.warn(`Error closing forks for block processing`, err);
431
+ }
432
+ }, 5000);
433
+ }
434
+ }
435
+ /**
436
+ * @notice Build and propose a block to the chain
437
+ *
438
+ * @dev MUST throw instead of exiting early to ensure that world-state
439
+ * is being rolled back if the block is dropped.
440
+ *
441
+ * @param pendingTxs - Iterable of pending transactions to construct the block from
442
+ * @param proposalHeader - The partial header constructed for the proposal
443
+ */ async buildBlockAndEnqueuePublish(pendingTxs, proposalHeader) {
444
+ await this.publisher.validateBlockForSubmission(proposalHeader);
445
+ const newGlobalVariables = proposalHeader.globalVariables;
446
+ const blockNumber = newGlobalVariables.blockNumber.toNumber();
447
+ const slot = newGlobalVariables.slotNumber.toBigInt();
448
+ // this.metrics.recordNewBlock(blockNumber, validTxs.length);
449
+ const workTimer = new Timer();
450
+ this.setState(SequencerState.CREATING_BLOCK, slot);
451
+ try {
452
+ const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables);
453
+ const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
454
+ this.metrics.recordBuiltBlock(workTimer.ms(), publicGas.l2Gas);
455
+ // TODO(@PhilWindle) We should probably periodically check for things like another
456
+ // block being published before ours instead of just waiting on our block
457
+ await this.publisher.validateBlockForSubmission(block.header);
458
+ const blockStats = {
459
+ eventName: 'l2-block-built',
460
+ creator: this.publisher.getSenderAddress().toString(),
461
+ duration: workTimer.ms(),
462
+ publicProcessDuration: publicProcessorDuration,
463
+ rollupCircuitsDuration: blockBuildingTimer.ms(),
464
+ ...block.getStats()
465
+ };
466
+ const blockHash = await block.hash();
467
+ const txHashes = block.body.txEffects.map((tx)=>tx.txHash);
468
+ this.log.info(`Built block ${block.number} for slot ${slot} with ${numTxs} txs and ${numMsgs} messages. ${publicGas.l2Gas / workTimer.s()} mana/s`, {
469
+ blockHash,
470
+ globalVariables: block.header.globalVariables.toInspect(),
471
+ txHashes,
472
+ ...blockStats
473
+ });
474
+ this.log.debug('Collecting attestations');
475
+ const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
476
+ const attestations = await this.collectAttestations(block, txHashes);
477
+ if (attestations !== undefined) {
478
+ this.log.verbose(`Collected ${attestations.length} attestations`, {
479
+ blockHash,
480
+ blockNumber
550
481
  });
551
- if (!enqueued) {
552
- throw new Error(`Failed to enqueue publish of block ${block.number}`);
553
- }
554
- }
555
- async claimEpochProofRightIfAvailable(slotNumber) {
556
- const proofQuote = await this.createProofClaimForPreviousEpoch(slotNumber);
557
- if (proofQuote === undefined) {
558
- return;
559
- }
560
- const epoch = proofQuote.payload.epochToProve;
561
- const ctx = { slotNumber, epoch, quote: proofQuote.toInspect() };
562
- this.log.verbose(`Claiming proof right for epoch ${epoch}`, ctx);
563
- const enqueued = this.publisher.enqueueClaimEpochProofRight(proofQuote);
564
- if (!enqueued) {
565
- throw new Error(`Failed to enqueue claim of proof right for epoch ${epoch}`);
566
- }
567
- this.log.info(`Enqueued claim of proof right for epoch ${epoch}`, ctx);
568
- return epoch;
569
- }
570
- /**
571
- * Returns whether all dependencies have caught up.
572
- * We don't check against the previous block submitted since it may have been reorg'd out.
573
- * @returns Boolean indicating if our dependencies are synced to the latest block.
574
- */
575
- async isBlockSynced() {
576
- const syncedBlocks = await Promise.all([
577
- this.worldState.status().then((s) => s.syncedToL2Block),
578
- this.l2BlockSource.getL2Tips().then(t => t.latest),
579
- this.p2pClient.getStatus().then(s => s.syncedToL2Block.number),
580
- this.l1ToL2MessageSource.getBlockNumber(),
581
- ]);
582
- const [worldState, l2BlockSource, p2p, l1ToL2MessageSource] = syncedBlocks;
583
- const result =
584
- // check that world state has caught up with archiver
585
- // note that the archiver reports undefined hash for the genesis block
586
- // because it doesn't have access to world state to compute it (facepalm)
587
- (l2BlockSource.hash === undefined || worldState.hash === l2BlockSource.hash) &&
588
- // and p2p client and message source are at least at the same block
589
- // this should change to hashes once p2p client handles reorgs
590
- // and once we stop pretending that the l1tol2message source is not
591
- // just the archiver under a different name
592
- p2p >= l2BlockSource.number &&
593
- l1ToL2MessageSource >= l2BlockSource.number;
594
- this.log.debug(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
595
- worldStateNumber: worldState.number,
596
- worldStateHash: worldState.hash,
597
- l2BlockSourceNumber: l2BlockSource.number,
598
- l2BlockSourceHash: l2BlockSource.hash,
599
- p2pNumber: p2p,
600
- l1ToL2MessageSourceNumber: l1ToL2MessageSource,
601
- });
602
- return result;
603
- }
604
- getSlotStartTimestamp(slotNumber) {
605
- return Number(this.l1Constants.l1GenesisTime) + Number(slotNumber) * this.l1Constants.slotDuration;
606
482
  }
607
- getSecondsIntoSlot(slotNumber) {
608
- const slotStartTimestamp = this.getSlotStartTimestamp(slotNumber);
609
- return Number((this.dateProvider.now() / 1000 - slotStartTimestamp).toFixed(3));
483
+ stopCollectingAttestationsTimer();
484
+ return this.enqueuePublishL2Block(block, attestations, txHashes);
485
+ } catch (err) {
486
+ this.metrics.recordFailedBlock();
487
+ throw err;
488
+ }
489
+ }
490
+ async collectAttestations(block, txHashes) {
491
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
492
+ const committee = await this.publisher.getCurrentEpochCommittee();
493
+ if (committee.length === 0) {
494
+ this.log.verbose(`Attesting committee is empty`);
495
+ return undefined;
496
+ } else {
497
+ this.log.debug(`Attesting committee length is ${committee.length}`);
498
+ }
499
+ if (!this.validatorClient) {
500
+ const msg = 'Missing validator client: Cannot collect attestations';
501
+ this.log.error(msg);
502
+ throw new Error(msg);
503
+ }
504
+ const numberOfRequiredAttestations = Math.floor(committee.length * 2 / 3) + 1;
505
+ const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
506
+ this.setState(SequencerState.COLLECTING_ATTESTATIONS, slotNumber);
507
+ this.log.debug('Creating block proposal for validators');
508
+ const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
509
+ if (!proposal) {
510
+ const msg = `Failed to create block proposal`;
511
+ throw new Error(msg);
512
+ }
513
+ this.log.debug('Broadcasting block proposal to validators');
514
+ this.validatorClient.broadcastBlockProposal(proposal);
515
+ const attestationTimeAllowed = this.enforceTimeTable ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_BLOCK) : this.aztecSlotDuration;
516
+ const attestationDeadline = new Date(this.dateProvider.now() + attestationTimeAllowed * 1000);
517
+ const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
518
+ // note: the smart contract requires that the signatures are provided in the order of the committee
519
+ return orderAttestations(attestations, committee);
520
+ }
521
+ async createProofClaimForPreviousEpoch(slotNumber) {
522
+ try {
523
+ // Find out which epoch we are currently in
524
+ const epochToProve = await this.publisher.getClaimableEpoch();
525
+ if (epochToProve === undefined) {
526
+ this.log.trace(`No epoch to claim at slot ${slotNumber}`);
527
+ return undefined;
610
528
  }
611
- get aztecSlotDuration() {
612
- return this.l1Constants.slotDuration;
529
+ // Get quotes for the epoch to be proven
530
+ this.log.debug(`Collecting proof quotes for epoch ${epochToProve}`);
531
+ const p2pQuotes = await this.p2pClient.getEpochProofQuotes(epochToProve).then((quotes)=>quotes.filter((x)=>x.payload.validUntilSlot >= slotNumber).filter((x)=>x.payload.epochToProve === epochToProve));
532
+ this.log.verbose(`Retrieved ${p2pQuotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
533
+ epochToProve,
534
+ slotNumber,
535
+ quotes: p2pQuotes.map((q)=>q.payload)
536
+ });
537
+ if (!p2pQuotes.length) {
538
+ return undefined;
613
539
  }
614
- get coinbase() {
615
- return this._coinbase;
540
+ // ensure these quotes are still valid for the slot and have the contract validate them
541
+ const validQuotes = await this.publisher.filterValidQuotes(p2pQuotes);
542
+ if (!validQuotes.length) {
543
+ this.log.warn(`Failed to find any valid proof quotes`);
544
+ return undefined;
616
545
  }
617
- get feeRecipient() {
618
- return this._feeRecipient;
546
+ // pick the quote with the lowest fee
547
+ const sortedQuotes = validQuotes.sort((a, b)=>a.payload.basisPointFee - b.payload.basisPointFee);
548
+ const quote = sortedQuotes[0];
549
+ this.log.info(`Selected proof quote for proof claim`, {
550
+ quote: quote.toInspect()
551
+ });
552
+ return quote;
553
+ } catch (err) {
554
+ this.log.error(`Failed to create proof claim for previous epoch`, err, {
555
+ slotNumber
556
+ });
557
+ return undefined;
558
+ }
559
+ }
560
+ /**
561
+ * Publishes the L2Block to the rollup contract.
562
+ * @param block - The L2Block to be published.
563
+ */ async enqueuePublishL2Block(block, attestations, txHashes) {
564
+ // Publishes new block to the network and awaits the tx to be mined
565
+ this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
566
+ // Time out tx at the end of the slot
567
+ const slot = block.header.globalVariables.slotNumber.toNumber();
568
+ const txTimeoutAt = new Date((this.getSlotStartTimestamp(slot) + this.aztecSlotDuration) * 1000);
569
+ const enqueued = await this.publisher.enqueueProposeL2Block(block, attestations, txHashes, {
570
+ txTimeoutAt
571
+ });
572
+ if (!enqueued) {
573
+ throw new Error(`Failed to enqueue publish of block ${block.number}`);
574
+ }
575
+ }
576
+ async claimEpochProofRightIfAvailable(slotNumber) {
577
+ const proofQuote = await this.createProofClaimForPreviousEpoch(slotNumber);
578
+ if (proofQuote === undefined) {
579
+ return;
580
+ }
581
+ const epoch = proofQuote.payload.epochToProve;
582
+ const ctx = {
583
+ slotNumber,
584
+ epoch,
585
+ quote: proofQuote.toInspect()
586
+ };
587
+ this.log.verbose(`Claiming proof right for epoch ${epoch}`, ctx);
588
+ const enqueued = this.publisher.enqueueClaimEpochProofRight(proofQuote);
589
+ if (!enqueued) {
590
+ throw new Error(`Failed to enqueue claim of proof right for epoch ${epoch}`);
591
+ }
592
+ this.log.info(`Enqueued claim of proof right for epoch ${epoch}`, ctx);
593
+ return epoch;
594
+ }
595
+ /**
596
+ * Returns whether all dependencies have caught up.
597
+ * We don't check against the previous block submitted since it may have been reorg'd out.
598
+ * @returns Boolean indicating if our dependencies are synced to the latest block.
599
+ */ async getChainTip() {
600
+ const syncedBlocks = await Promise.all([
601
+ this.worldState.status().then((s)=>s.syncedToL2Block),
602
+ this.l2BlockSource.getL2Tips().then((t)=>t.latest),
603
+ this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
604
+ this.l1ToL2MessageSource.getBlockNumber()
605
+ ]);
606
+ const [worldState, l2BlockSource, p2p, l1ToL2MessageSource] = syncedBlocks;
607
+ const result = // check that world state has caught up with archiver
608
+ // note that the archiver reports undefined hash for the genesis block
609
+ // because it doesn't have access to world state to compute it (facepalm)
610
+ (l2BlockSource.hash === undefined || worldState.hash === l2BlockSource.hash) && // and p2p client and message source are at least at the same block
611
+ // this should change to hashes once p2p client handles reorgs
612
+ // and once we stop pretending that the l1tol2message source is not
613
+ // just the archiver under a different name
614
+ (!l2BlockSource.hash || p2p.hash === l2BlockSource.hash) && l1ToL2MessageSource === l2BlockSource.number;
615
+ this.log.debug(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
616
+ worldStateNumber: worldState.number,
617
+ worldStateHash: worldState.hash,
618
+ l2BlockSourceNumber: l2BlockSource.number,
619
+ l2BlockSourceHash: l2BlockSource.hash,
620
+ p2pNumber: p2p.number,
621
+ p2pHash: p2p.hash,
622
+ l1ToL2MessageSourceNumber: l1ToL2MessageSource
623
+ });
624
+ if (!result) {
625
+ return undefined;
626
+ }
627
+ if (worldState.number >= INITIAL_L2_BLOCK_NUM) {
628
+ const block = await this.l2BlockSource.getBlock(worldState.number);
629
+ if (!block) {
630
+ // this shouldn't really happen because a moment ago we checked that all components were in synch
631
+ return undefined;
619
632
  }
620
- },
621
- (() => {
622
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
623
- _work_decorators = [trackSpan('Sequencer.work')];
624
- _buildBlockAndEnqueuePublish_decorators = [trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, proposalHeader) => ({
625
- [Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
626
- }))];
627
- _collectAttestations_decorators = [trackSpan('Sequencer.collectAttestations', (block, txHashes) => ({
628
- [Attributes.BLOCK_NUMBER]: block.number,
629
- [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
630
- [Attributes.BLOCK_TXS_COUNT]: txHashes.length,
631
- }))];
632
- _enqueuePublishL2Block_decorators = [trackSpan('Sequencer.enqueuePublishL2Block', block => ({
633
- [Attributes.BLOCK_NUMBER]: block.number,
634
- }))];
635
- _claimEpochProofRightIfAvailable_decorators = [trackSpan('Sequencer.claimEpochProofRightIfAvailable', slotNumber => ({ [Attributes.SLOT_NUMBER]: Number(slotNumber) }), epoch => ({ [Attributes.EPOCH_NUMBER]: Number(epoch) }))];
636
- __esDecorate(_a, null, _work_decorators, { kind: "method", name: "work", static: false, private: false, access: { has: obj => "work" in obj, get: obj => obj.work }, metadata: _metadata }, null, _instanceExtraInitializers);
637
- __esDecorate(_a, null, _buildBlockAndEnqueuePublish_decorators, { kind: "method", name: "buildBlockAndEnqueuePublish", static: false, private: false, access: { has: obj => "buildBlockAndEnqueuePublish" in obj, get: obj => obj.buildBlockAndEnqueuePublish }, metadata: _metadata }, null, _instanceExtraInitializers);
638
- __esDecorate(_a, null, _collectAttestations_decorators, { kind: "method", name: "collectAttestations", static: false, private: false, access: { has: obj => "collectAttestations" in obj, get: obj => obj.collectAttestations }, metadata: _metadata }, null, _instanceExtraInitializers);
639
- __esDecorate(_a, null, _enqueuePublishL2Block_decorators, { kind: "method", name: "enqueuePublishL2Block", static: false, private: false, access: { has: obj => "enqueuePublishL2Block" in obj, get: obj => obj.enqueuePublishL2Block }, metadata: _metadata }, null, _instanceExtraInitializers);
640
- __esDecorate(_a, null, _claimEpochProofRightIfAvailable_decorators, { kind: "method", name: "claimEpochProofRightIfAvailable", static: false, private: false, access: { has: obj => "claimEpochProofRightIfAvailable" in obj, get: obj => obj.claimEpochProofRightIfAvailable }, metadata: _metadata }, null, _instanceExtraInitializers);
641
- if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
642
- })(),
643
- _a;
644
- })();
645
- export { Sequencer };
646
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9zZXF1ZW5jZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFNTCxxQkFBcUIsRUFDckIsRUFBRSxHQUdILE1BQU0sc0JBQXNCLENBQUM7QUFHOUIsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixXQUFXLEVBQ1gsaUJBQWlCLEVBRWpCLG9CQUFvQixFQUNwQixHQUFHLEVBRUgsY0FBYyxHQUNmLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQy9ELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDbkUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzNELE9BQU8sRUFBcUIsS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBSTVFLE9BQU8sRUFBRSxVQUFVLEVBQXFDLGtCQUFrQixFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBSXZILE9BQU8sRUFBMkIsUUFBUSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFFeEYsT0FBTyxFQUFFLGdDQUFnQyxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDM0YsT0FBTyxFQUFFLCtCQUErQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRS9ELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNoRCxPQUFPLEVBQUUsa0JBQWtCLEVB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
633
+ return {
634
+ blockNumber: block.number,
635
+ archive: block.archive.root
636
+ };
637
+ } else {
638
+ return {
639
+ blockNumber: INITIAL_L2_BLOCK_NUM - 1,
640
+ archive: new Fr(GENESIS_ARCHIVE_ROOT)
641
+ };
642
+ }
643
+ }
644
+ getSlotStartTimestamp(slotNumber) {
645
+ return Number(this.l1Constants.l1GenesisTime) + Number(slotNumber) * this.l1Constants.slotDuration;
646
+ }
647
+ getSecondsIntoSlot(slotNumber) {
648
+ const slotStartTimestamp = this.getSlotStartTimestamp(slotNumber);
649
+ return Number((this.dateProvider.now() / 1000 - slotStartTimestamp).toFixed(3));
650
+ }
651
+ get aztecSlotDuration() {
652
+ return this.l1Constants.slotDuration;
653
+ }
654
+ get coinbase() {
655
+ return this._coinbase;
656
+ }
657
+ get feeRecipient() {
658
+ return this._feeRecipient;
659
+ }
660
+ }
661
+ _ts_decorate([
662
+ trackSpan('Sequencer.work')
663
+ ], Sequencer.prototype, "work", null);
664
+ _ts_decorate([
665
+ trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, proposalHeader)=>({
666
+ [Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber()
667
+ }))
668
+ ], Sequencer.prototype, "buildBlockAndEnqueuePublish", null);
669
+ _ts_decorate([
670
+ trackSpan('Sequencer.collectAttestations', (block, txHashes)=>({
671
+ [Attributes.BLOCK_NUMBER]: block.number,
672
+ [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
673
+ [Attributes.BLOCK_TXS_COUNT]: txHashes.length
674
+ }))
675
+ ], Sequencer.prototype, "collectAttestations", null);
676
+ _ts_decorate([
677
+ trackSpan('Sequencer.enqueuePublishL2Block', (block)=>({
678
+ [Attributes.BLOCK_NUMBER]: block.number
679
+ }))
680
+ ], Sequencer.prototype, "enqueuePublishL2Block", null);
681
+ _ts_decorate([
682
+ trackSpan('Sequencer.claimEpochProofRightIfAvailable', (slotNumber)=>({
683
+ [Attributes.SLOT_NUMBER]: Number(slotNumber)
684
+ }), (epoch)=>({
685
+ [Attributes.EPOCH_NUMBER]: Number(epoch)
686
+ }))
687
+ ], Sequencer.prototype, "claimEpochProofRightIfAvailable", null);