@aztec/sequencer-client 0.47.0 → 0.48.0

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 (78) hide show
  1. package/dest/client/sequencer-client.d.ts +2 -2
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +2 -2
  4. package/dest/config.d.ts +6 -2
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +96 -29
  7. package/dest/global_variable_builder/global_builder.d.ts +14 -8
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +10 -16
  10. package/dest/global_variable_builder/index.d.ts +2 -3
  11. package/dest/global_variable_builder/index.d.ts.map +1 -1
  12. package/dest/global_variable_builder/index.js +1 -1
  13. package/dest/global_variable_builder/viem-reader.d.ts +5 -4
  14. package/dest/global_variable_builder/viem-reader.d.ts.map +1 -1
  15. package/dest/global_variable_builder/viem-reader.js +11 -8
  16. package/dest/index.d.ts +2 -0
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +3 -1
  19. package/dest/publisher/config.d.ts +7 -15
  20. package/dest/publisher/config.d.ts.map +1 -1
  21. package/dest/publisher/config.js +38 -11
  22. package/dest/publisher/index.d.ts +3 -2
  23. package/dest/publisher/index.d.ts.map +1 -1
  24. package/dest/publisher/index.js +4 -4
  25. package/dest/publisher/l1-publisher-metrics.d.ts +17 -0
  26. package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -0
  27. package/dest/publisher/l1-publisher-metrics.js +75 -0
  28. package/dest/publisher/l1-publisher.d.ts +34 -6
  29. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  30. package/dest/publisher/l1-publisher.js +45 -37
  31. package/dest/publisher/viem-tx-sender.d.ts +9 -2
  32. package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
  33. package/dest/publisher/viem-tx-sender.js +86 -17
  34. package/dest/receiver.d.ts +2 -8
  35. package/dest/receiver.d.ts.map +1 -1
  36. package/dest/sequencer/metrics.d.ts +17 -0
  37. package/dest/sequencer/metrics.d.ts.map +1 -0
  38. package/dest/sequencer/metrics.js +56 -0
  39. package/dest/sequencer/sequencer.d.ts +11 -5
  40. package/dest/sequencer/sequencer.d.ts.map +1 -1
  41. package/dest/sequencer/sequencer.js +104 -34
  42. package/dest/tx_validator/data_validator.d.ts +6 -0
  43. package/dest/tx_validator/data_validator.d.ts.map +1 -0
  44. package/dest/tx_validator/data_validator.js +47 -0
  45. package/dest/tx_validator/gas_validator.d.ts +1 -1
  46. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  47. package/dest/tx_validator/gas_validator.js +12 -12
  48. package/dest/tx_validator/phases_validator.d.ts +2 -3
  49. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  50. package/dest/tx_validator/phases_validator.js +4 -4
  51. package/dest/tx_validator/test_utils.d.ts.map +1 -1
  52. package/dest/tx_validator/test_utils.js +10 -4
  53. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  54. package/dest/tx_validator/tx_validator_factory.js +4 -3
  55. package/package.json +15 -15
  56. package/src/client/sequencer-client.ts +3 -3
  57. package/src/config.ts +111 -50
  58. package/src/global_variable_builder/global_builder.ts +35 -25
  59. package/src/global_variable_builder/index.ts +3 -3
  60. package/src/global_variable_builder/viem-reader.ts +14 -11
  61. package/src/index.ts +2 -0
  62. package/src/publisher/config.ts +43 -31
  63. package/src/publisher/index.ts +5 -3
  64. package/src/publisher/l1-publisher-metrics.ts +108 -0
  65. package/src/publisher/l1-publisher.ts +79 -44
  66. package/src/publisher/viem-tx-sender.ts +90 -15
  67. package/src/receiver.ts +3 -8
  68. package/src/sequencer/metrics.ts +86 -0
  69. package/src/sequencer/sequencer.ts +144 -45
  70. package/src/tx_validator/data_validator.ts +61 -0
  71. package/src/tx_validator/gas_validator.ts +11 -9
  72. package/src/tx_validator/phases_validator.ts +13 -5
  73. package/src/tx_validator/test_utils.ts +10 -3
  74. package/src/tx_validator/tx_validator_factory.ts +4 -2
  75. package/dest/global_variable_builder/config.d.ts +0 -19
  76. package/dest/global_variable_builder/config.d.ts.map +0 -1
  77. package/dest/global_variable_builder/config.js +0 -2
  78. package/src/global_variable_builder/config.ts +0 -20
@@ -9,11 +9,11 @@ import {
9
9
  import {
10
10
  type AllowedElement,
11
11
  BlockProofError,
12
- type BlockProver,
13
12
  PROVING_STATUS,
13
+ type ProverClient,
14
14
  } from '@aztec/circuit-types/interfaces';
15
15
  import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
16
- import { AztecAddress, EthAddress, type GlobalVariables, type Header } from '@aztec/circuits.js';
16
+ import { AztecAddress, EthAddress, type GlobalVariables, type Header, IS_DEV_NET } from '@aztec/circuits.js';
17
17
  import { Fr } from '@aztec/foundation/fields';
18
18
  import { createDebugLogger } from '@aztec/foundation/log';
19
19
  import { RunningPromise } from '@aztec/foundation/running-promise';
@@ -24,9 +24,10 @@ import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec
24
24
  import { type WorldStateStatus, type WorldStateSynchronizer } from '@aztec/world-state';
25
25
 
26
26
  import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
27
- import { type L1Publisher } from '../publisher/l1-publisher.js';
27
+ import { type Attestation, type L1Publisher } from '../publisher/l1-publisher.js';
28
28
  import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
29
29
  import { type SequencerConfig } from './config.js';
30
+ import { SequencerMetrics } from './metrics.js';
30
31
 
31
32
  /**
32
33
  * Sequencer client
@@ -42,6 +43,8 @@ export class Sequencer {
42
43
  private pollingIntervalMs: number = 1000;
43
44
  private maxTxsPerBlock = 32;
44
45
  private minTxsPerBLock = 1;
46
+ private minSecondsBetweenBlocks = 0;
47
+ private maxSecondsBetweenBlocks = 0;
45
48
  // TODO: zero values should not be allowed for the following 2 values in PROD
46
49
  private _coinbase = EthAddress.ZERO;
47
50
  private _feeRecipient = AztecAddress.ZERO;
@@ -51,15 +54,14 @@ export class Sequencer {
51
54
  private allowedInTeardown: AllowedElement[] = [];
52
55
  private maxBlockSizeInBytes: number = 1024 * 1024;
53
56
  private skipSubmitProofs: boolean = false;
54
-
55
- public readonly tracer: Tracer;
57
+ private metrics: SequencerMetrics;
56
58
 
57
59
  constructor(
58
60
  private publisher: L1Publisher,
59
61
  private globalsBuilder: GlobalVariableBuilder,
60
62
  private p2pClient: P2P,
61
63
  private worldState: WorldStateSynchronizer,
62
- private prover: BlockProver,
64
+ private prover: ProverClient,
63
65
  private l2BlockSource: L2BlockSource,
64
66
  private l1ToL2MessageSource: L1ToL2MessageSource,
65
67
  private publicProcessorFactory: PublicProcessorFactory,
@@ -69,24 +71,34 @@ export class Sequencer {
69
71
  private log = createDebugLogger('aztec:sequencer'),
70
72
  ) {
71
73
  this.updateConfig(config);
72
- this.tracer = telemetry.getTracer('Sequencer');
74
+ this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
73
75
  this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`);
74
76
  }
75
77
 
78
+ get tracer(): Tracer {
79
+ return this.metrics.tracer;
80
+ }
81
+
76
82
  /**
77
83
  * Updates sequencer config.
78
84
  * @param config - New parameters.
79
85
  */
80
86
  public updateConfig(config: SequencerConfig) {
81
- if (config.transactionPollingIntervalMS) {
87
+ if (config.transactionPollingIntervalMS !== undefined) {
82
88
  this.pollingIntervalMs = config.transactionPollingIntervalMS;
83
89
  }
84
- if (config.maxTxsPerBlock) {
90
+ if (config.maxTxsPerBlock !== undefined) {
85
91
  this.maxTxsPerBlock = config.maxTxsPerBlock;
86
92
  }
87
- if (config.minTxsPerBlock) {
93
+ if (config.minTxsPerBlock !== undefined) {
88
94
  this.minTxsPerBLock = config.minTxsPerBlock;
89
95
  }
96
+ if (config.minSecondsBetweenBlocks !== undefined) {
97
+ this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
98
+ }
99
+ if (config.maxSecondsBetweenBlocks !== undefined) {
100
+ this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
101
+ }
90
102
  if (config.coinbase) {
91
103
  this._coinbase = config.coinbase;
92
104
  }
@@ -96,7 +108,7 @@ export class Sequencer {
96
108
  if (config.allowedInSetup) {
97
109
  this.allowedInSetup = config.allowedInSetup;
98
110
  }
99
- if (config.maxBlockSizeInBytes) {
111
+ if (config.maxBlockSizeInBytes !== undefined) {
100
112
  this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
101
113
  }
102
114
  // TODO(#5917) remove this. it is no longer needed since we don't need to whitelist functions in teardown
@@ -162,16 +174,17 @@ export class Sequencer {
162
174
  try {
163
175
  // Update state when the previous block has been synced
164
176
  const prevBlockSynced = await this.isBlockSynced();
165
- if (prevBlockSynced && this.state === SequencerState.PUBLISHING_BLOCK) {
166
- this.log.debug(`Block has been synced`);
167
- this.state = SequencerState.IDLE;
168
- }
169
-
170
177
  // Do not go forward with new block if the previous one has not been mined and processed
171
178
  if (!prevBlockSynced) {
179
+ this.log.debug('Previous block has not been mined and processed yet');
172
180
  return;
173
181
  }
174
182
 
183
+ if (prevBlockSynced && this.state === SequencerState.PUBLISHING_BLOCK) {
184
+ this.log.debug(`Block has been synced`);
185
+ this.state = SequencerState.IDLE;
186
+ }
187
+
175
188
  const historicalHeader = (await this.l2BlockSource.getBlock(-1))?.header;
176
189
  const newBlockNumber =
177
190
  (historicalHeader === undefined
@@ -179,21 +192,42 @@ export class Sequencer {
179
192
  : Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1;
180
193
 
181
194
  // Do not go forward with new block if not my turn
182
- if (!(await this.publisher.isItMyTurnToSubmit(newBlockNumber))) {
195
+ if (!(await this.publisher.isItMyTurnToSubmit())) {
183
196
  this.log.debug('Not my turn to submit block');
184
197
  return;
185
198
  }
186
199
 
187
- this.state = SequencerState.WAITING_FOR_TXS;
200
+ // Compute time elapsed since the previous block
201
+ const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
202
+ const currentTime = Math.floor(Date.now() / 1000);
203
+ const elapsedSinceLastBlock = currentTime - lastBlockTime;
188
204
 
189
- // Get txs to build the new block
190
- const pendingTxs = this.p2pClient.getTxs('pending');
191
- if (pendingTxs.length < this.minTxsPerBLock) {
205
+ // Do not go forward with new block if not enough time has passed since last block
206
+ if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
192
207
  this.log.debug(
193
- `Not creating block because there are not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
208
+ `Not creating block because not enough time has passed since last block (last block at ${lastBlockTime} current time ${currentTime})`,
194
209
  );
195
210
  return;
196
211
  }
212
+
213
+ this.state = SequencerState.WAITING_FOR_TXS;
214
+
215
+ // Get txs to build the new block.
216
+ const pendingTxs = this.p2pClient.getTxs('pending');
217
+
218
+ // If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
219
+ if (pendingTxs.length < this.minTxsPerBLock) {
220
+ if (this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
221
+ this.log.debug(
222
+ `Creating block with only ${pendingTxs.length} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
223
+ );
224
+ } else {
225
+ this.log.debug(
226
+ `Not creating block because not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
227
+ );
228
+ return;
229
+ }
230
+ }
197
231
  this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
198
232
 
199
233
  const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(
@@ -202,6 +236,8 @@ export class Sequencer {
202
236
  this._feeRecipient,
203
237
  );
204
238
 
239
+ // @todo @LHerskind Include some logic to consider slots
240
+
205
241
  // TODO: It should be responsibility of the P2P layer to validate txs before passing them on here
206
242
  const allValidTxs = await this.takeValidTxs(
207
243
  pendingTxs,
@@ -215,11 +251,15 @@ export class Sequencer {
215
251
  // may break if we start emitting lots of log data from public-land.
216
252
  const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
217
253
 
218
- if (validTxs.length < this.minTxsPerBLock) {
254
+ // Bail if we don't have enough valid txs
255
+ if (!this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock) && validTxs.length < this.minTxsPerBLock) {
256
+ this.log.debug(
257
+ `Not creating block because not enough valid txs loaded from the pool (got ${validTxs.length} min ${this.minTxsPerBLock})`,
258
+ );
219
259
  return;
220
260
  }
221
261
 
222
- await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader);
262
+ await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader, elapsedSinceLastBlock);
223
263
  } catch (err) {
224
264
  if (BlockProofError.isBlockProofError(err)) {
225
265
  const txHashes = err.txHashes.filter(h => !h.isZero());
@@ -227,12 +267,15 @@ export class Sequencer {
227
267
  await this.p2pClient.deleteTxs(txHashes);
228
268
  }
229
269
  this.log.error(`Rolling back world state DB due to error assembling block`, (err as any).stack);
230
- // Cancel any further proving on the block
231
- this.prover?.cancelBlock();
232
270
  await this.worldState.getLatest().rollback();
233
271
  }
234
272
  }
235
273
 
274
+ /** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
275
+ private skipMinTxsPerBlockCheck(elapsed: number): boolean {
276
+ return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
277
+ }
278
+
236
279
  @trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({
237
280
  [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
238
281
  }))
@@ -240,7 +283,9 @@ export class Sequencer {
240
283
  validTxs: Tx[],
241
284
  newGlobalVariables: GlobalVariables,
242
285
  historicalHeader: Header | undefined,
286
+ elapsedSinceLastBlock: number,
243
287
  ): Promise<void> {
288
+ this.metrics.recordNewBlock(newGlobalVariables.blockNumber.toNumber(), validTxs.length);
244
289
  const workTimer = new Timer();
245
290
  this.state = SequencerState.CREATING_BLOCK;
246
291
  this.log.info(`Building block ${newGlobalVariables.blockNumber.toNumber()} with ${validTxs.length} transactions`);
@@ -248,11 +293,15 @@ export class Sequencer {
248
293
  const assertBlockHeight = async () => {
249
294
  const currentBlockNumber = await this.l2BlockSource.getBlockNumber();
250
295
  if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) {
296
+ this.metrics.recordCancelledBlock();
251
297
  throw new Error('New block was emitted while building block');
252
298
  }
253
- if (!(await this.publisher.isItMyTurnToSubmit(newGlobalVariables.blockNumber.toNumber()))) {
299
+
300
+ if (!(await this.publisher.isItMyTurnToSubmit())) {
254
301
  throw new Error(`Not this sequencer turn to submit block`);
255
302
  }
303
+
304
+ // @todo @LHerskind Should take into account, block number, proposer and slot number
256
305
  };
257
306
 
258
307
  // Get l1 to l2 messages from the contract
@@ -269,10 +318,11 @@ export class Sequencer {
269
318
  const blockSize = Math.max(2, numRealTxs);
270
319
 
271
320
  const blockBuildingTimer = new Timer();
272
- const blockTicket = await this.prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
321
+ const prover = this.prover.createBlockProver(this.worldState.getLatest());
322
+ const blockTicket = await prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
273
323
 
274
324
  const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
275
- processor.process(validTxs, blockSize, this.prover, this.txValidatorFactory.validatorForProcessedTxs()),
325
+ processor.process(validTxs, blockSize, prover, this.txValidatorFactory.validatorForProcessedTxs()),
276
326
  );
277
327
  if (failedTxs.length > 0) {
278
328
  const failedTxData = failedTxs.map(fail => fail.tx);
@@ -280,16 +330,20 @@ export class Sequencer {
280
330
  await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
281
331
  }
282
332
 
283
- if (processedTxs.length === 0) {
333
+ // TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
334
+ // less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
335
+ // go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
336
+ // we should bail.
337
+ if (processedTxs.length === 0 && !this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
284
338
  this.log.verbose('No txs processed correctly to build block. Exiting');
285
- this.prover.cancelBlock();
339
+ prover.cancelBlock();
286
340
  return;
287
341
  }
288
342
 
289
343
  await assertBlockHeight();
290
344
 
291
345
  // All real transactions have been added, set the block as full and complete the proving.
292
- await this.prover.setBlockCompleted();
346
+ await prover.setBlockCompleted();
293
347
 
294
348
  // Here we are now waiting for the block to be proven.
295
349
  // TODO(@PhilWindle) We should probably periodically check for things like another
@@ -302,30 +356,75 @@ export class Sequencer {
302
356
  await assertBlockHeight();
303
357
 
304
358
  // Block is proven, now finalise and publish!
305
- const { block, aggregationObject, proof } = await this.prover.finaliseBlock();
359
+ const { block, aggregationObject, proof } = await prover.finaliseBlock();
306
360
 
307
361
  await assertBlockHeight();
308
362
 
309
- this.log.verbose(`Assembled block ${block.number}`, {
310
- eventName: 'l2-block-built',
311
- duration: workTimer.ms(),
312
- publicProcessDuration: publicProcessorDuration,
313
- rollupCircuitsDuration: blockBuildingTimer.ms(),
314
- ...block.getStats(),
315
- } satisfies L2BlockBuiltStats);
363
+ const workDuration = workTimer.ms();
364
+ this.log.verbose(
365
+ `Assembled block ${block.number} (txEffectsHash: ${block.header.contentCommitment.txsEffectsHash.toString(
366
+ 'hex',
367
+ )})`,
368
+ {
369
+ eventName: 'l2-block-built',
370
+ duration: workDuration,
371
+ publicProcessDuration: publicProcessorDuration,
372
+ rollupCircuitsDuration: blockBuildingTimer.ms(),
373
+ ...block.getStats(),
374
+ } satisfies L2BlockBuiltStats,
375
+ );
316
376
 
317
- await this.publishL2Block(block);
318
- this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`);
377
+ try {
378
+ const attestations = await this.collectAttestations(block);
379
+ await this.publishL2Block(block, attestations);
380
+ this.metrics.recordPublishedBlock(workDuration);
381
+ this.log.info(
382
+ `Submitted rollup block ${block.number} with ${
383
+ processedTxs.length
384
+ } transactions duration=${workDuration}ms (Submitter: ${await this.publisher.senderAddress()})`,
385
+ );
386
+ } catch (err) {
387
+ this.metrics.recordFailedBlock();
388
+ throw err;
389
+ }
319
390
 
320
391
  // Submit the proof if we have configured this sequencer to run with an actual prover.
321
392
  // This is temporary while we submit one proof per block, but will have to change once we
322
393
  // move onto proving batches of multiple blocks at a time.
323
394
  if (aggregationObject && proof && !this.skipSubmitProofs) {
324
- await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof);
395
+ await this.publisher.submitProof(
396
+ block.header,
397
+ block.archive.root,
398
+ prover.getProverId(),
399
+ aggregationObject,
400
+ proof,
401
+ );
325
402
  this.log.info(`Submitted proof for block ${block.number}`);
326
403
  }
327
404
  }
328
405
 
406
+ protected async collectAttestations(block: L2Block): Promise<Attestation[] | undefined> {
407
+ // @todo This should collect attestations properly and fix the ordering of them to make sense
408
+ // the current implementation is a PLACEHOLDER and should be nuked from orbit.
409
+ // It is assuming that there will only be ONE (1) validator, so only one attestation
410
+ // is needed.
411
+ // @note This is quite a sin, but I'm committing war crimes in this code already.
412
+ // _ ._ _ , _ ._
413
+ // (_ ' ( ` )_ .__)
414
+ // ( ( ( ) `) ) _)
415
+ // (__ (_ (_ . _) _) ,__)
416
+ // `~~`\ ' . /`~~`
417
+ // ; ;
418
+ // / \
419
+ // _____________/_ __ \_____________
420
+ if (IS_DEV_NET) {
421
+ return undefined;
422
+ }
423
+
424
+ const myAttestation = await this.publisher.attest(block.archive.root.toString());
425
+ return [myAttestation];
426
+ }
427
+
329
428
  /**
330
429
  * Publishes the L2Block to the rollup contract.
331
430
  * @param block - The L2Block to be published.
@@ -333,10 +432,10 @@ export class Sequencer {
333
432
  @trackSpan('Sequencer.publishL2Block', block => ({
334
433
  [Attributes.BLOCK_NUMBER]: block.number,
335
434
  }))
336
- protected async publishL2Block(block: L2Block) {
435
+ protected async publishL2Block(block: L2Block, attestations?: Attestation[]) {
337
436
  // Publishes new block to the network and awaits the tx to be mined
338
437
  this.state = SequencerState.PUBLISHING_BLOCK;
339
- const publishedL2Block = await this.publisher.processL2Block(block);
438
+ const publishedL2Block = await this.publisher.processL2Block(block, attestations);
340
439
  if (publishedL2Block) {
341
440
  this.lastPublishedBlock = block.number;
342
441
  } else {
@@ -0,0 +1,61 @@
1
+ import { Tx, type TxValidator } from '@aztec/circuit-types';
2
+ import { createDebugLogger } from '@aztec/foundation/log';
3
+
4
+ export class DataTxValidator implements TxValidator<Tx> {
5
+ #log = createDebugLogger('aztec:sequencer:tx_validator:tx_data');
6
+
7
+ validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
8
+ const validTxs: Tx[] = [];
9
+ const invalidTxs: Tx[] = [];
10
+ for (const tx of txs) {
11
+ if (!this.#hasCorrectExecutionRequests(tx)) {
12
+ invalidTxs.push(tx);
13
+ continue;
14
+ }
15
+
16
+ validTxs.push(tx);
17
+ }
18
+
19
+ return Promise.resolve([validTxs, invalidTxs]);
20
+ }
21
+
22
+ #hasCorrectExecutionRequests(tx: Tx): boolean {
23
+ const callRequests = [
24
+ ...tx.data.getRevertiblePublicCallRequests(),
25
+ ...tx.data.getNonRevertiblePublicCallRequests(),
26
+ ];
27
+ if (callRequests.length !== tx.enqueuedPublicFunctionCalls.length) {
28
+ this.#log.warn(
29
+ `Rejecting tx ${Tx.getHash(tx)} because of mismatch number of execution requests for public calls. Expected ${
30
+ callRequests.length
31
+ }. Got ${tx.enqueuedPublicFunctionCalls.length}.`,
32
+ );
33
+ return false;
34
+ }
35
+
36
+ const invalidExecutionRequestIndex = tx.enqueuedPublicFunctionCalls.findIndex(
37
+ (execRequest, i) => !execRequest.isForCallRequest(callRequests[i]),
38
+ );
39
+ if (invalidExecutionRequestIndex !== -1) {
40
+ this.#log.warn(
41
+ `Rejecting tx ${Tx.getHash(
42
+ tx,
43
+ )} because of incorrect execution requests for public call at index ${invalidExecutionRequestIndex}.`,
44
+ );
45
+ return false;
46
+ }
47
+
48
+ const teardownCallRequest = tx.data.getTeardownPublicCallRequest();
49
+ const isInvalidTeardownExecutionRequest =
50
+ (!teardownCallRequest && !tx.publicTeardownFunctionCall.isEmpty()) ||
51
+ (teardownCallRequest && !tx.publicTeardownFunctionCall.isForCallRequest(teardownCallRequest));
52
+ if (isInvalidTeardownExecutionRequest) {
53
+ this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} because of incorrect teardown execution requests.`);
54
+ return false;
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ // TODO: Check logs.
61
+ }
@@ -1,7 +1,7 @@
1
1
  import { PublicKernelType, type Tx, type TxValidator } from '@aztec/circuit-types';
2
2
  import { type AztecAddress, type Fr } from '@aztec/circuits.js';
3
3
  import { createDebugLogger } from '@aztec/foundation/log';
4
- import { GasTokenArtifact } from '@aztec/protocol-contracts/gas-token';
4
+ import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice';
5
5
  import { AbstractPhaseManager, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
6
6
 
7
7
  /** Provides a view into public contract state */
@@ -12,11 +12,11 @@ export interface PublicStateSource {
12
12
  export class GasTxValidator implements TxValidator<Tx> {
13
13
  #log = createDebugLogger('aztec:sequencer:tx_validator:tx_gas');
14
14
  #publicDataSource: PublicStateSource;
15
- #gasTokenAddress: AztecAddress;
15
+ #feeJuiceAddress: AztecAddress;
16
16
 
17
- constructor(publicDataSource: PublicStateSource, gasTokenAddress: AztecAddress, public enforceFees: boolean) {
17
+ constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, public enforceFees: boolean) {
18
18
  this.#publicDataSource = publicDataSource;
19
- this.#gasTokenAddress = gasTokenAddress;
19
+ this.#feeJuiceAddress = feeJuiceAddress;
20
20
  }
21
21
 
22
22
  async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
@@ -50,17 +50,19 @@ export class GasTxValidator implements TxValidator<Tx> {
50
50
 
51
51
  // Read current balance of the feePayer
52
52
  const initialBalance = await this.#publicDataSource.storageRead(
53
- this.#gasTokenAddress,
53
+ this.#feeJuiceAddress,
54
54
  computeFeePayerBalanceStorageSlot(feePayer),
55
55
  );
56
56
 
57
- // If there is a claim in this tx that increases the fee payer balance in gas token, add it to balance
57
+ // If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
58
58
  const { [PublicKernelType.SETUP]: setupFns } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx);
59
59
  const claimFunctionCall = setupFns.find(
60
60
  fn =>
61
- fn.contractAddress.equals(this.#gasTokenAddress) &&
62
- fn.callContext.msgSender.equals(this.#gasTokenAddress) &&
63
- fn.functionSelector.equals(GasTokenArtifact.functions.find(f => f.name === '_increase_public_balance')!) &&
61
+ fn.contractAddress.equals(this.#feeJuiceAddress) &&
62
+ fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
63
+ fn.callContext.functionSelector.equals(
64
+ FeeJuiceArtifact.functions.find(f => f.name === '_increase_public_balance')!,
65
+ ) &&
64
66
  fn.args[0].equals(feePayer) &&
65
67
  !fn.callContext.isStaticCall &&
66
68
  !fn.callContext.isDelegateCall,
@@ -1,5 +1,10 @@
1
- import { type AllowedElement, PublicKernelType, Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { type PublicCallRequest } from '@aztec/circuits.js';
1
+ import {
2
+ type AllowedElement,
3
+ type PublicExecutionRequest,
4
+ PublicKernelType,
5
+ Tx,
6
+ type TxValidator,
7
+ } from '@aztec/circuit-types';
3
8
  import { createDebugLogger } from '@aztec/foundation/log';
4
9
  import { AbstractPhaseManager, ContractsDataSourcePublicDB } from '@aztec/simulator';
5
10
  import { type ContractDataSource } from '@aztec/types/contracts';
@@ -47,7 +52,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
47
52
  this.#log.warn(
48
53
  `Rejecting tx ${Tx.getHash(tx)} because it calls setup function not on allow list: ${
49
54
  setupFn.contractAddress
50
- }:${setupFn.functionSelector}`,
55
+ }:${setupFn.callContext.functionSelector}`,
51
56
  );
52
57
 
53
58
  return false;
@@ -57,12 +62,15 @@ export class PhasesTxValidator implements TxValidator<Tx> {
57
62
  return true;
58
63
  }
59
64
 
60
- async isOnAllowList(publicCall: PublicCallRequest, allowList: AllowedElement[]): Promise<boolean> {
65
+ async isOnAllowList(publicCall: PublicExecutionRequest, allowList: AllowedElement[]): Promise<boolean> {
61
66
  if (publicCall.isEmpty()) {
62
67
  return true;
63
68
  }
64
69
 
65
- const { contractAddress, functionSelector } = publicCall;
70
+ const {
71
+ contractAddress,
72
+ callContext: { functionSelector },
73
+ } = publicCall;
66
74
 
67
75
  // do these checks first since they don't require the contract class
68
76
  for (const entry of allowList) {
@@ -1,5 +1,6 @@
1
1
  import { type Tx } from '@aztec/circuit-types';
2
2
  import { type AztecAddress, type Fr, type FunctionSelector } from '@aztec/circuits.js';
3
+ import { computeVarArgsHash } from '@aztec/circuits.js/hash';
3
4
 
4
5
  export function patchNonRevertibleFn(
5
6
  tx: Tx,
@@ -25,13 +26,19 @@ function patchFn(
25
26
  ): { address: AztecAddress; selector: FunctionSelector } {
26
27
  const fn = tx.enqueuedPublicFunctionCalls.at(-1 * index - 1)!;
27
28
  fn.contractAddress = overrides.address ?? fn.contractAddress;
28
- fn.functionSelector = overrides.selector;
29
+ fn.callContext.functionSelector = overrides.selector;
29
30
  fn.args = overrides.args ?? fn.args;
30
31
  fn.callContext.msgSender = overrides.msgSender ?? fn.callContext.msgSender;
31
- tx.data.forPublic![where].publicCallStack[index] = fn.toCallRequest();
32
+ tx.enqueuedPublicFunctionCalls[index] = fn;
33
+
34
+ const request = tx.data.forPublic![where].publicCallStack[index];
35
+ request.item.contractAddress = fn.contractAddress;
36
+ request.item.callContext = fn.callContext;
37
+ request.item.argsHash = computeVarArgsHash(fn.args);
38
+ tx.data.forPublic![where].publicCallStack[index] = request;
32
39
 
33
40
  return {
34
41
  address: fn.contractAddress,
35
- selector: fn.functionSelector,
42
+ selector: fn.callContext.functionSelector,
36
43
  };
37
44
  }
@@ -1,11 +1,12 @@
1
1
  import { type AllowedElement, type ProcessedTx, type Tx, type TxValidator } from '@aztec/circuit-types';
2
2
  import { type GlobalVariables } from '@aztec/circuits.js';
3
- import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
3
+ import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
4
4
  import { WorldStateDB, WorldStatePublicDB } from '@aztec/simulator';
5
5
  import { type ContractDataSource } from '@aztec/types/contracts';
6
6
  import { type MerkleTreeOperations } from '@aztec/world-state';
7
7
 
8
8
  import { AggregateTxValidator } from './aggregate_tx_validator.js';
9
+ import { DataTxValidator } from './data_validator.js';
9
10
  import { DoubleSpendTxValidator } from './double_spend_validator.js';
10
11
  import { GasTxValidator } from './gas_validator.js';
11
12
  import { MetadataTxValidator } from './metadata_validator.js';
@@ -20,10 +21,11 @@ export class TxValidatorFactory {
20
21
 
21
22
  validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
22
23
  return new AggregateTxValidator(
24
+ new DataTxValidator(),
23
25
  new MetadataTxValidator(globalVariables),
24
26
  new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb)),
25
27
  new PhasesTxValidator(this.contractDataSource, setupAllowList),
26
- new GasTxValidator(new WorldStatePublicDB(this.merkleTreeDb), GasTokenAddress, this.enforceFees),
28
+ new GasTxValidator(new WorldStatePublicDB(this.merkleTreeDb), FeeJuiceAddress, this.enforceFees),
27
29
  );
28
30
  }
29
31
 
@@ -1,19 +0,0 @@
1
- import { type L1ContractAddresses } from '@aztec/ethereum';
2
- /**
3
- * Configuration of the L1GlobalReader.
4
- */
5
- export interface GlobalReaderConfig {
6
- /**
7
- * The RPC Url of the ethereum host.
8
- */
9
- rpcUrl: string;
10
- /**
11
- * The chain ID of the ethereum host.
12
- */
13
- l1ChainId: number;
14
- /**
15
- * The deployed l1 contract addresses
16
- */
17
- l1Contracts: L1ContractAddresses;
18
- }
19
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/global_variable_builder/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,WAAW,EAAE,mBAAmB,CAAC;CAClC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2dsb2JhbF92YXJpYWJsZV9idWlsZGVyL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -1,20 +0,0 @@
1
- import { type L1ContractAddresses } from '@aztec/ethereum';
2
-
3
- /**
4
- * Configuration of the L1GlobalReader.
5
- */
6
- export interface GlobalReaderConfig {
7
- /**
8
- * The RPC Url of the ethereum host.
9
- */
10
- rpcUrl: string;
11
- /**
12
- * The chain ID of the ethereum host.
13
- */
14
- l1ChainId: number;
15
-
16
- /**
17
- * The deployed l1 contract addresses
18
- */
19
- l1Contracts: L1ContractAddresses;
20
- }