@aztec/sequencer-client 0.46.7 → 0.47.1

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 (45) hide show
  1. package/dest/config.d.ts.map +1 -1
  2. package/dest/config.js +10 -26
  3. package/dest/index.d.ts +2 -0
  4. package/dest/index.d.ts.map +1 -1
  5. package/dest/index.js +3 -1
  6. package/dest/publisher/config.d.ts +2 -1
  7. package/dest/publisher/config.d.ts.map +1 -1
  8. package/dest/publisher/config.js +14 -2
  9. package/dest/publisher/index.d.ts +1 -1
  10. package/dest/publisher/index.d.ts.map +1 -1
  11. package/dest/publisher/index.js +2 -1
  12. package/dest/publisher/l1-publisher.d.ts +3 -3
  13. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  14. package/dest/publisher/l1-publisher.js +4 -4
  15. package/dest/publisher/viem-tx-sender.d.ts +1 -1
  16. package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
  17. package/dest/publisher/viem-tx-sender.js +4 -4
  18. package/dest/sequencer/sequencer.d.ts +5 -0
  19. package/dest/sequencer/sequencer.d.ts.map +1 -1
  20. package/dest/sequencer/sequencer.js +66 -19
  21. package/dest/tx_validator/data_validator.d.ts +6 -0
  22. package/dest/tx_validator/data_validator.d.ts.map +1 -0
  23. package/dest/tx_validator/data_validator.js +47 -0
  24. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  25. package/dest/tx_validator/gas_validator.js +2 -2
  26. package/dest/tx_validator/phases_validator.d.ts +2 -3
  27. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  28. package/dest/tx_validator/phases_validator.js +4 -4
  29. package/dest/tx_validator/test_utils.d.ts.map +1 -1
  30. package/dest/tx_validator/test_utils.js +10 -4
  31. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  32. package/dest/tx_validator/tx_validator_factory.js +3 -2
  33. package/package.json +15 -15
  34. package/src/config.ts +11 -39
  35. package/src/index.ts +2 -0
  36. package/src/publisher/config.ts +20 -2
  37. package/src/publisher/index.ts +1 -1
  38. package/src/publisher/l1-publisher.ts +5 -5
  39. package/src/publisher/viem-tx-sender.ts +3 -3
  40. package/src/sequencer/sequencer.ts +77 -17
  41. package/src/tx_validator/data_validator.ts +61 -0
  42. package/src/tx_validator/gas_validator.ts +3 -1
  43. package/src/tx_validator/phases_validator.ts +13 -5
  44. package/src/tx_validator/test_utils.ts +10 -3
  45. package/src/tx_validator/tx_validator_factory.ts +2 -0
package/src/config.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  import { type AllowedElement } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, FunctionSelector, getContractClassFromArtifact } from '@aztec/circuits.js';
3
- import { type L1ContractAddresses, NULL_KEY } from '@aztec/ethereum';
3
+ import { getL1ContractAddressesFromEnv } from '@aztec/ethereum';
4
4
  import { EthAddress } from '@aztec/foundation/eth-address';
5
5
  import { FPCContract } from '@aztec/noir-contracts.js/FPC';
6
6
  import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token';
7
7
  import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry';
8
8
  import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
9
9
 
10
- import { type Hex } from 'viem';
11
-
12
10
  import { type GlobalReaderConfig } from './global_variable_builder/index.js';
13
- import { type PublisherConfig, type TxSenderConfig } from './publisher/config.js';
11
+ import { type PublisherConfig, type TxSenderConfig, getTxSenderConfigFromEnv } from './publisher/config.js';
14
12
  import { type SequencerConfig } from './sequencer/config.js';
15
13
 
16
14
  /** Chain configuration. */
@@ -35,25 +33,17 @@ export type SequencerClientConfig = PublisherConfig &
35
33
  */
36
34
  export function getConfigEnvVars(): SequencerClientConfig {
37
35
  const {
38
- SEQ_PUBLISHER_PRIVATE_KEY,
39
- ETHEREUM_HOST,
40
- L1_CHAIN_ID,
41
36
  VERSION,
42
- SEQ_REQUIRED_CONFIRMATIONS,
43
37
  SEQ_PUBLISH_RETRY_INTERVAL_MS,
44
38
  SEQ_TX_POLLING_INTERVAL_MS,
45
39
  SEQ_MAX_TX_PER_BLOCK,
46
40
  SEQ_MIN_TX_PER_BLOCK,
41
+ SEQ_MAX_SECONDS_BETWEEN_BLOCKS,
42
+ SEQ_MIN_SECONDS_BETWEEN_BLOCKS,
47
43
  SEQ_ALLOWED_SETUP_FN,
48
44
  SEQ_ALLOWED_TEARDOWN_FN,
49
45
  SEQ_MAX_BLOCK_SIZE_IN_BYTES,
50
- AVAILABILITY_ORACLE_CONTRACT_ADDRESS,
51
- ROLLUP_CONTRACT_ADDRESS,
52
- REGISTRY_CONTRACT_ADDRESS,
53
- INBOX_CONTRACT_ADDRESS,
54
- OUTBOX_CONTRACT_ADDRESS,
55
- GAS_TOKEN_CONTRACT_ADDRESS,
56
- GAS_PORTAL_CONTRACT_ADDRESS,
46
+ SEQ_SKIP_SUBMIT_PROOFS,
57
47
  COINBASE,
58
48
  FEE_RECIPIENT,
59
49
  ACVM_WORKING_DIRECTORY,
@@ -61,37 +51,18 @@ export function getConfigEnvVars(): SequencerClientConfig {
61
51
  ENFORCE_FEES = '',
62
52
  } = process.env;
63
53
 
64
- const publisherPrivateKey: Hex = SEQ_PUBLISHER_PRIVATE_KEY
65
- ? `0x${SEQ_PUBLISHER_PRIVATE_KEY.replace('0x', '')}`
66
- : NULL_KEY;
67
- // Populate the relevant addresses for use by the sequencer
68
- const addresses: L1ContractAddresses = {
69
- availabilityOracleAddress: AVAILABILITY_ORACLE_CONTRACT_ADDRESS
70
- ? EthAddress.fromString(AVAILABILITY_ORACLE_CONTRACT_ADDRESS)
71
- : EthAddress.ZERO,
72
- rollupAddress: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO,
73
- registryAddress: REGISTRY_CONTRACT_ADDRESS ? EthAddress.fromString(REGISTRY_CONTRACT_ADDRESS) : EthAddress.ZERO,
74
- inboxAddress: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
75
- outboxAddress: OUTBOX_CONTRACT_ADDRESS ? EthAddress.fromString(OUTBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
76
- gasTokenAddress: GAS_TOKEN_CONTRACT_ADDRESS ? EthAddress.fromString(GAS_TOKEN_CONTRACT_ADDRESS) : EthAddress.ZERO,
77
- gasPortalAddress: GAS_PORTAL_CONTRACT_ADDRESS
78
- ? EthAddress.fromString(GAS_PORTAL_CONTRACT_ADDRESS)
79
- : EthAddress.ZERO,
80
- };
81
-
82
54
  return {
83
55
  enforceFees: ['1', 'true'].includes(ENFORCE_FEES),
84
- rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '',
85
- l1ChainId: L1_CHAIN_ID ? +L1_CHAIN_ID : 31337, // 31337 is the default chain id for anvil
86
56
  version: VERSION ? +VERSION : 1, // 1 is our default version
87
- requiredConfirmations: SEQ_REQUIRED_CONFIRMATIONS ? +SEQ_REQUIRED_CONFIRMATIONS : 1,
88
- l1BlockPublishRetryIntervalMS: SEQ_PUBLISH_RETRY_INTERVAL_MS ? +SEQ_PUBLISH_RETRY_INTERVAL_MS : 1_000,
57
+ l1PublishRetryIntervalMS: SEQ_PUBLISH_RETRY_INTERVAL_MS ? +SEQ_PUBLISH_RETRY_INTERVAL_MS : 1_000,
89
58
  transactionPollingIntervalMS: SEQ_TX_POLLING_INTERVAL_MS ? +SEQ_TX_POLLING_INTERVAL_MS : 1_000,
90
59
  maxBlockSizeInBytes: SEQ_MAX_BLOCK_SIZE_IN_BYTES ? +SEQ_MAX_BLOCK_SIZE_IN_BYTES : undefined,
91
- l1Contracts: addresses,
92
- publisherPrivateKey,
60
+ l1Contracts: getL1ContractAddressesFromEnv(),
93
61
  maxTxsPerBlock: SEQ_MAX_TX_PER_BLOCK ? +SEQ_MAX_TX_PER_BLOCK : 32,
94
62
  minTxsPerBlock: SEQ_MIN_TX_PER_BLOCK ? +SEQ_MIN_TX_PER_BLOCK : 1,
63
+ maxSecondsBetweenBlocks: SEQ_MAX_SECONDS_BETWEEN_BLOCKS ? +SEQ_MAX_SECONDS_BETWEEN_BLOCKS : 0,
64
+ minSecondsBetweenBlocks: SEQ_MIN_SECONDS_BETWEEN_BLOCKS ? +SEQ_MIN_SECONDS_BETWEEN_BLOCKS : 0,
65
+ sequencerSkipSubmitProofs: ['1', 'true'].includes(SEQ_SKIP_SUBMIT_PROOFS ?? ''),
95
66
  // TODO: undefined should not be allowed for the following 2 values in PROD
96
67
  coinbase: COINBASE ? EthAddress.fromString(COINBASE) : undefined,
97
68
  feeRecipient: FEE_RECIPIENT ? AztecAddress.fromString(FEE_RECIPIENT) : undefined,
@@ -103,6 +74,7 @@ export function getConfigEnvVars(): SequencerClientConfig {
103
74
  allowedInTeardown: SEQ_ALLOWED_TEARDOWN_FN
104
75
  ? parseSequencerAllowList(SEQ_ALLOWED_TEARDOWN_FN)
105
76
  : getDefaultAllowedTeardownFunctions(),
77
+ ...getTxSenderConfigFromEnv('SEQ'),
106
78
  };
107
79
  }
108
80
 
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@ export * from './client/index.js';
2
2
  export * from './config.js';
3
3
  export * from './publisher/index.js';
4
4
  export * from './sequencer/index.js';
5
+ export * from './tx_validator/aggregate_tx_validator.js';
6
+ export * from './tx_validator/data_validator.js';
5
7
 
6
8
  // Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
7
9
  export * from './global_variable_builder/index.js';
@@ -1,4 +1,6 @@
1
- import { type L1ContractAddresses } from '@aztec/ethereum';
1
+ import { type L1ContractAddresses, NULL_KEY } from '@aztec/ethereum';
2
+
3
+ import { type Hex } from 'viem';
2
4
 
3
5
  /**
4
6
  * The configuration of the rollup transaction publisher.
@@ -37,5 +39,21 @@ export interface PublisherConfig {
37
39
  /**
38
40
  * The interval to wait between publish retries.
39
41
  */
40
- l1BlockPublishRetryIntervalMS: number;
42
+ l1PublishRetryIntervalMS: number;
43
+ }
44
+
45
+ export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSenderConfig, 'l1Contracts'> {
46
+ const { ETHEREUM_HOST, L1_CHAIN_ID } = process.env;
47
+
48
+ const PUBLISHER_PRIVATE_KEY = process.env[`${scope}_PUBLISHER_PRIVATE_KEY`];
49
+ const REQUIRED_CONFIRMATIONS = process.env[`${scope}_REQUIRED_CONFIRMATIONS`];
50
+
51
+ const publisherPrivateKey: Hex = PUBLISHER_PRIVATE_KEY ? `0x${PUBLISHER_PRIVATE_KEY.replace('0x', '')}` : NULL_KEY;
52
+
53
+ return {
54
+ rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '',
55
+ requiredConfirmations: REQUIRED_CONFIRMATIONS ? +REQUIRED_CONFIRMATIONS : 1,
56
+ publisherPrivateKey,
57
+ l1ChainId: L1_CHAIN_ID ? +L1_CHAIN_ID : 31337,
58
+ };
41
59
  }
@@ -3,7 +3,7 @@ import { L1Publisher } from './l1-publisher.js';
3
3
  import { ViemTxSender } from './viem-tx-sender.js';
4
4
 
5
5
  export { L1Publisher } from './l1-publisher.js';
6
- export { PublisherConfig } from './config.js';
6
+ export { PublisherConfig, TxSenderConfig, getTxSenderConfigFromEnv } from './config.js';
7
7
 
8
8
  /**
9
9
  * Returns a new instance of the L1Publisher.
@@ -46,8 +46,8 @@ export interface L1PublisherTxSender {
46
46
  /** Returns the EOA used for sending txs to L1. */
47
47
  getSenderAddress(): Promise<EthAddress>;
48
48
 
49
- /** Returns the address elected for submitting a given block number or zero if anyone can submit. */
50
- getSubmitterAddressForBlock(blockNumber: number): Promise<EthAddress>;
49
+ /** Returns the address of the current proposer or zero if anyone can submit. */
50
+ getSubmitterAddressForBlock(): Promise<EthAddress>;
51
51
 
52
52
  /**
53
53
  * Publishes tx effects to Availability Oracle.
@@ -134,11 +134,11 @@ export class L1Publisher implements L2BlockReceiver {
134
134
  private log = createDebugLogger('aztec:sequencer:publisher');
135
135
 
136
136
  constructor(private txSender: L1PublisherTxSender, config?: PublisherConfig) {
137
- this.sleepTimeMs = config?.l1BlockPublishRetryIntervalMS ?? 60_000;
137
+ this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
138
138
  }
139
139
 
140
- public async isItMyTurnToSubmit(blockNumber: number): Promise<boolean> {
141
- const submitter = await this.txSender.getSubmitterAddressForBlock(blockNumber);
140
+ public async isItMyTurnToSubmit(): Promise<boolean> {
141
+ const submitter = await this.txSender.getSubmitterAddressForBlock();
142
142
  const sender = await this.txSender.getSenderAddress();
143
143
  return submitter.isZero() || submitter.equals(sender);
144
144
  }
@@ -77,12 +77,12 @@ export class ViemTxSender implements L1PublisherTxSender {
77
77
  return Promise.resolve(EthAddress.fromString(this.account.address));
78
78
  }
79
79
 
80
- async getSubmitterAddressForBlock(blockNumber: number): Promise<EthAddress> {
80
+ async getSubmitterAddressForBlock(): Promise<EthAddress> {
81
81
  try {
82
- const submitter = await this.rollupContract.read.whoseTurnIsIt([BigInt(blockNumber)]);
82
+ const submitter = await this.rollupContract.read.getCurrentProposer();
83
83
  return EthAddress.fromString(submitter);
84
84
  } catch (err) {
85
- this.log.warn(`Failed to get submitter for block ${blockNumber}: ${err}`);
85
+ this.log.warn(`Failed to get submitter: ${err}`);
86
86
  return EthAddress.ZERO;
87
87
  }
88
88
  }
@@ -42,6 +42,8 @@ export class Sequencer {
42
42
  private pollingIntervalMs: number = 1000;
43
43
  private maxTxsPerBlock = 32;
44
44
  private minTxsPerBLock = 1;
45
+ private minSecondsBetweenBlocks = 0;
46
+ private maxSecondsBetweenBlocks = 0;
45
47
  // TODO: zero values should not be allowed for the following 2 values in PROD
46
48
  private _coinbase = EthAddress.ZERO;
47
49
  private _feeRecipient = AztecAddress.ZERO;
@@ -50,6 +52,7 @@ export class Sequencer {
50
52
  private allowedInSetup: AllowedElement[] = [];
51
53
  private allowedInTeardown: AllowedElement[] = [];
52
54
  private maxBlockSizeInBytes: number = 1024 * 1024;
55
+ private skipSubmitProofs: boolean = false;
53
56
 
54
57
  public readonly tracer: Tracer;
55
58
 
@@ -77,15 +80,21 @@ export class Sequencer {
77
80
  * @param config - New parameters.
78
81
  */
79
82
  public updateConfig(config: SequencerConfig) {
80
- if (config.transactionPollingIntervalMS) {
83
+ if (config.transactionPollingIntervalMS !== undefined) {
81
84
  this.pollingIntervalMs = config.transactionPollingIntervalMS;
82
85
  }
83
- if (config.maxTxsPerBlock) {
86
+ if (config.maxTxsPerBlock !== undefined) {
84
87
  this.maxTxsPerBlock = config.maxTxsPerBlock;
85
88
  }
86
- if (config.minTxsPerBlock) {
89
+ if (config.minTxsPerBlock !== undefined) {
87
90
  this.minTxsPerBLock = config.minTxsPerBlock;
88
91
  }
92
+ if (config.minSecondsBetweenBlocks !== undefined) {
93
+ this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
94
+ }
95
+ if (config.maxSecondsBetweenBlocks !== undefined) {
96
+ this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
97
+ }
89
98
  if (config.coinbase) {
90
99
  this._coinbase = config.coinbase;
91
100
  }
@@ -95,13 +104,17 @@ export class Sequencer {
95
104
  if (config.allowedInSetup) {
96
105
  this.allowedInSetup = config.allowedInSetup;
97
106
  }
98
- if (config.maxBlockSizeInBytes) {
107
+ if (config.maxBlockSizeInBytes !== undefined) {
99
108
  this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
100
109
  }
101
110
  // TODO(#5917) remove this. it is no longer needed since we don't need to whitelist functions in teardown
102
111
  if (config.allowedInTeardown) {
103
112
  this.allowedInTeardown = config.allowedInTeardown;
104
113
  }
114
+ // TODO(palla/prover) This flag should not be needed: the sequencer should be initialized with a blockprover
115
+ // that does not return proofs at all (just simulates circuits), and use that to determine whether to submit
116
+ // proofs or not.
117
+ this.skipSubmitProofs = !!config.sequencerSkipSubmitProofs;
105
118
  }
106
119
 
107
120
  /**
@@ -174,17 +187,41 @@ export class Sequencer {
174
187
  : Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1;
175
188
 
176
189
  // Do not go forward with new block if not my turn
177
- if (!(await this.publisher.isItMyTurnToSubmit(newBlockNumber))) {
178
- this.log.verbose('Not my turn to submit block');
190
+ if (!(await this.publisher.isItMyTurnToSubmit())) {
191
+ this.log.debug('Not my turn to submit block');
192
+ return;
193
+ }
194
+
195
+ // Compute time elapsed since the previous block
196
+ const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
197
+ const currentTime = Math.floor(Date.now() / 1000);
198
+ const elapsedSinceLastBlock = currentTime - lastBlockTime;
199
+
200
+ // Do not go forward with new block if not enough time has passed since last block
201
+ if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
202
+ this.log.debug(
203
+ `Not creating block because not enough time has passed since last block (last block at ${lastBlockTime} current time ${currentTime})`,
204
+ );
179
205
  return;
180
206
  }
181
207
 
182
208
  this.state = SequencerState.WAITING_FOR_TXS;
183
209
 
184
- // Get txs to build the new block
185
- const pendingTxs = await this.p2pClient.getTxs();
210
+ // Get txs to build the new block.
211
+ const pendingTxs = this.p2pClient.getTxs('pending');
212
+
213
+ // If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
186
214
  if (pendingTxs.length < this.minTxsPerBLock) {
187
- return;
215
+ if (this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
216
+ this.log.debug(
217
+ `Creating block with only ${pendingTxs.length} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
218
+ );
219
+ } else {
220
+ this.log.debug(
221
+ `Not creating block because not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
222
+ );
223
+ return;
224
+ }
188
225
  }
189
226
  this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
190
227
 
@@ -207,11 +244,15 @@ export class Sequencer {
207
244
  // may break if we start emitting lots of log data from public-land.
208
245
  const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
209
246
 
210
- if (validTxs.length < this.minTxsPerBLock) {
247
+ // Bail if we don't have enough valid txs
248
+ if (!this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock) && validTxs.length < this.minTxsPerBLock) {
249
+ this.log.debug(
250
+ `Not creating block because not enough valid txs loaded from the pool (got ${validTxs.length} min ${this.minTxsPerBLock})`,
251
+ );
211
252
  return;
212
253
  }
213
254
 
214
- await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader);
255
+ await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader, elapsedSinceLastBlock);
215
256
  } catch (err) {
216
257
  if (BlockProofError.isBlockProofError(err)) {
217
258
  const txHashes = err.txHashes.filter(h => !h.isZero());
@@ -225,6 +266,11 @@ export class Sequencer {
225
266
  }
226
267
  }
227
268
 
269
+ /** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
270
+ private skipMinTxsPerBlockCheck(elapsed: number): boolean {
271
+ return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
272
+ }
273
+
228
274
  @trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({
229
275
  [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
230
276
  }))
@@ -232,6 +278,7 @@ export class Sequencer {
232
278
  validTxs: Tx[],
233
279
  newGlobalVariables: GlobalVariables,
234
280
  historicalHeader: Header | undefined,
281
+ elapsedSinceLastBlock: number,
235
282
  ): Promise<void> {
236
283
  const workTimer = new Timer();
237
284
  this.state = SequencerState.CREATING_BLOCK;
@@ -242,7 +289,8 @@ export class Sequencer {
242
289
  if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) {
243
290
  throw new Error('New block was emitted while building block');
244
291
  }
245
- if (!(await this.publisher.isItMyTurnToSubmit(newGlobalVariables.blockNumber.toNumber()))) {
292
+
293
+ if (!(await this.publisher.isItMyTurnToSubmit())) {
246
294
  throw new Error(`Not this sequencer turn to submit block`);
247
295
  }
248
296
  };
@@ -255,7 +303,7 @@ export class Sequencer {
255
303
  );
256
304
 
257
305
  // We create a fresh processor each time to reset any cached state (eg storage writes)
258
- const processor = await this.publicProcessorFactory.create(historicalHeader, newGlobalVariables);
306
+ const processor = this.publicProcessorFactory.create(historicalHeader, newGlobalVariables);
259
307
 
260
308
  const numRealTxs = validTxs.length;
261
309
  const blockSize = Math.max(2, numRealTxs);
@@ -272,7 +320,11 @@ export class Sequencer {
272
320
  await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
273
321
  }
274
322
 
275
- if (processedTxs.length === 0) {
323
+ // TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
324
+ // less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
325
+ // go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
326
+ // we should bail.
327
+ if (processedTxs.length === 0 && !this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
276
328
  this.log.verbose('No txs processed correctly to build block. Exiting');
277
329
  this.prover.cancelBlock();
278
330
  return;
@@ -309,10 +361,10 @@ export class Sequencer {
309
361
  await this.publishL2Block(block);
310
362
  this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`);
311
363
 
312
- // Submit the proof if we have configured this sequencer to run with a prover.
364
+ // Submit the proof if we have configured this sequencer to run with an actual prover.
313
365
  // This is temporary while we submit one proof per block, but will have to change once we
314
366
  // move onto proving batches of multiple blocks at a time.
315
- if (aggregationObject && proof) {
367
+ if (aggregationObject && proof && !this.skipSubmitProofs) {
316
368
  await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof);
317
369
  this.log.info(`Submitted proof for block ${block.number}`);
318
370
  }
@@ -378,7 +430,15 @@ export class Sequencer {
378
430
  this.l1ToL2MessageSource.getBlockNumber(),
379
431
  ]);
380
432
  const min = Math.min(...syncedBlocks);
381
- return min >= this.lastPublishedBlock;
433
+ const [worldState, p2p, l2BlockSource, l1ToL2MessageSource] = syncedBlocks;
434
+ const result = min >= this.lastPublishedBlock;
435
+ this.log.debug(`Sync check to last published block ${this.lastPublishedBlock} ${result ? 'succeeded' : 'failed'}`, {
436
+ worldState,
437
+ p2p,
438
+ l2BlockSource,
439
+ l1ToL2MessageSource,
440
+ });
441
+ return result;
382
442
  }
383
443
 
384
444
  get coinbase(): EthAddress {
@@ -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
+ }
@@ -60,7 +60,9 @@ export class GasTxValidator implements TxValidator<Tx> {
60
60
  fn =>
61
61
  fn.contractAddress.equals(this.#gasTokenAddress) &&
62
62
  fn.callContext.msgSender.equals(this.#gasTokenAddress) &&
63
- fn.functionSelector.equals(GasTokenArtifact.functions.find(f => f.name === '_increase_public_balance')!) &&
63
+ fn.callContext.functionSelector.equals(
64
+ GasTokenArtifact.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
  }
@@ -6,6 +6,7 @@ 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,6 +21,7 @@ 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),