@aztec/sequencer-client 0.0.1-commit.f504929 → 0.0.1-commit.f5d02921e

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 (58) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +46 -23
  4. package/dest/config.d.ts +25 -5
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +21 -12
  7. package/dest/global_variable_builder/global_builder.d.ts +15 -9
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +29 -25
  10. package/dest/global_variable_builder/index.d.ts +2 -2
  11. package/dest/global_variable_builder/index.d.ts.map +1 -1
  12. package/dest/publisher/config.d.ts +13 -1
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +17 -2
  15. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
  16. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.js +2 -2
  18. package/dest/publisher/sequencer-publisher.d.ts +52 -25
  19. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  20. package/dest/publisher/sequencer-publisher.js +98 -42
  21. package/dest/sequencer/checkpoint_proposal_job.d.ts +33 -8
  22. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  23. package/dest/sequencer/checkpoint_proposal_job.js +284 -158
  24. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  25. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  26. package/dest/sequencer/checkpoint_voter.js +2 -5
  27. package/dest/sequencer/events.d.ts +2 -1
  28. package/dest/sequencer/events.d.ts.map +1 -1
  29. package/dest/sequencer/metrics.d.ts +5 -1
  30. package/dest/sequencer/metrics.d.ts.map +1 -1
  31. package/dest/sequencer/metrics.js +11 -0
  32. package/dest/sequencer/sequencer.d.ts +23 -10
  33. package/dest/sequencer/sequencer.d.ts.map +1 -1
  34. package/dest/sequencer/sequencer.js +123 -68
  35. package/dest/sequencer/timetable.d.ts +4 -3
  36. package/dest/sequencer/timetable.d.ts.map +1 -1
  37. package/dest/sequencer/timetable.js +6 -7
  38. package/dest/sequencer/types.d.ts +2 -2
  39. package/dest/sequencer/types.d.ts.map +1 -1
  40. package/dest/test/mock_checkpoint_builder.d.ts +7 -9
  41. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  42. package/dest/test/mock_checkpoint_builder.js +39 -30
  43. package/package.json +27 -28
  44. package/src/client/sequencer-client.ts +56 -21
  45. package/src/config.ts +28 -14
  46. package/src/global_variable_builder/global_builder.ts +37 -26
  47. package/src/global_variable_builder/index.ts +1 -1
  48. package/src/publisher/config.ts +32 -0
  49. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  50. package/src/publisher/sequencer-publisher.ts +144 -54
  51. package/src/sequencer/checkpoint_proposal_job.ts +367 -175
  52. package/src/sequencer/checkpoint_voter.ts +1 -12
  53. package/src/sequencer/events.ts +1 -1
  54. package/src/sequencer/metrics.ts +14 -0
  55. package/src/sequencer/sequencer.ts +178 -79
  56. package/src/sequencer/timetable.ts +7 -7
  57. package/src/sequencer/types.ts +1 -1
  58. package/src/test/mock_checkpoint_builder.ts +51 -48
@@ -1,6 +1,8 @@
1
+ import { IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
1
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { unfreeze } from '@aztec/foundation/types';
4
+ import { L2Block } from '@aztec/stdlib/block';
2
5
  import { Checkpoint } from '@aztec/stdlib/checkpoint';
3
- import { Gas } from '@aztec/stdlib/gas';
4
6
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
5
7
  import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
6
8
  /**
@@ -63,8 +65,10 @@ import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
63
65
  let block;
64
66
  let usedTxs;
65
67
  if (this.blockProvider) {
66
- // Dynamic mode: get block from provider
67
- block = this.blockProvider();
68
+ // Dynamic mode: get block from provider, cloning to avoid shared references across multiple buildBlock calls
69
+ block = L2Block.fromBuffer(this.blockProvider().toBuffer());
70
+ block.header.globalVariables.blockNumber = blockNumber;
71
+ await block.header.recomputeHash();
68
72
  usedTxs = [];
69
73
  this.builtBlocks.push(block);
70
74
  } else {
@@ -87,61 +91,63 @@ import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
87
91
  }
88
92
  return {
89
93
  block,
90
- publicGas: Gas.empty(),
91
94
  publicProcessorDuration: 0,
92
95
  numTxs: block?.body?.txEffects?.length ?? usedTxs.length,
93
96
  usedTxs,
94
- failedTxs: [],
95
- usedTxBlobFields: block?.body?.txEffects?.reduce((sum, tx)=>sum + tx.getNumBlobFields(), 0) ?? 0
97
+ failedTxs: []
96
98
  };
97
99
  }
98
100
  completeCheckpoint() {
99
101
  this.completeCheckpointCalled = true;
100
102
  const allBlocks = this.blockProvider ? this.builtBlocks : this.blocks;
101
- const lastBlock = allBlocks[allBlocks.length - 1];
102
- // Create a CheckpointHeader from the last block's header for testing
103
- const checkpointHeader = this.createCheckpointHeader(lastBlock);
104
- return Promise.resolve(new Checkpoint(makeAppendOnlyTreeSnapshot(lastBlock.header.globalVariables.blockNumber + 1), checkpointHeader, allBlocks, this.checkpointNumber));
103
+ return this.buildCheckpoint(allBlocks);
105
104
  }
106
105
  getCheckpoint() {
107
106
  this.getCheckpointCalled = true;
108
107
  const builtBlocks = this.blockProvider ? this.builtBlocks : this.blocks.slice(0, this.blockIndex);
109
- const lastBlock = builtBlocks[builtBlocks.length - 1];
110
- if (!lastBlock) {
108
+ if (builtBlocks.length === 0) {
111
109
  throw new Error('No blocks built yet');
112
110
  }
113
- // Create a CheckpointHeader from the last block's header for testing
114
- const checkpointHeader = this.createCheckpointHeader(lastBlock);
115
- return Promise.resolve(new Checkpoint(makeAppendOnlyTreeSnapshot(lastBlock.header.globalVariables.blockNumber + 1), checkpointHeader, builtBlocks, this.checkpointNumber));
116
- }
117
- /**
118
- * Creates a CheckpointHeader from a block's header for testing.
119
- * This is a simplified version that creates a minimal CheckpointHeader.
120
- */ createCheckpointHeader(block) {
121
- const header = block.header;
122
- const gv = header.globalVariables;
123
- return CheckpointHeader.empty({
124
- lastArchiveRoot: header.lastArchive.root,
111
+ return this.buildCheckpoint(builtBlocks);
112
+ }
113
+ /** Builds a structurally valid Checkpoint from a list of blocks, fixing up indexes and archive chaining. */ async buildCheckpoint(blocks) {
114
+ // Fix up indexWithinCheckpoint and archive chaining so the checkpoint passes structural validation.
115
+ for(let i = 0; i < blocks.length; i++){
116
+ blocks[i].indexWithinCheckpoint = IndexWithinCheckpoint(i);
117
+ if (i > 0) {
118
+ unfreeze(blocks[i].header).lastArchive = blocks[i - 1].archive;
119
+ await blocks[i].header.recomputeHash();
120
+ }
121
+ }
122
+ const firstBlock = blocks[0];
123
+ const lastBlock = blocks[blocks.length - 1];
124
+ const gv = firstBlock.header.globalVariables;
125
+ const checkpointHeader = CheckpointHeader.empty({
126
+ lastArchiveRoot: firstBlock.header.lastArchive.root,
125
127
  blockHeadersHash: Fr.random(),
126
128
  slotNumber: gv.slotNumber,
127
129
  timestamp: gv.timestamp,
128
130
  coinbase: gv.coinbase,
129
131
  feeRecipient: gv.feeRecipient,
130
132
  gasFees: gv.gasFees,
131
- totalManaUsed: header.totalManaUsed
133
+ totalManaUsed: lastBlock.header.totalManaUsed
132
134
  });
135
+ return new Checkpoint(makeAppendOnlyTreeSnapshot(lastBlock.header.globalVariables.blockNumber + 1), checkpointHeader, blocks, this.checkpointNumber);
133
136
  }
134
- /** Reset for reuse in another test */ reset() {
135
- this.blocks = [];
137
+ /** Resets per-checkpoint state (built blocks, consumed txs) while preserving config (blockProvider, seeded blocks). */ resetCheckpointState() {
136
138
  this.builtBlocks = [];
137
- this.usedTxsPerBlock = [];
138
139
  this.blockIndex = 0;
139
- this.buildBlockCalls = [];
140
140
  this.consumedTxHashes.clear();
141
141
  this.completeCheckpointCalled = false;
142
142
  this.getCheckpointCalled = false;
143
+ }
144
+ /** Reset for reuse in another test */ reset() {
145
+ this.blocks = [];
146
+ this.usedTxsPerBlock = [];
147
+ this.buildBlockCalls = [];
143
148
  this.errorOnBuild = undefined;
144
149
  this.blockProvider = undefined;
150
+ this.resetCheckpointState();
145
151
  }
146
152
  }
147
153
  /**
@@ -175,7 +181,8 @@ import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
175
181
  l1GenesisTime: 0n,
176
182
  slotDuration: 24,
177
183
  l1ChainId: 1,
178
- rollupVersion: 1
184
+ rollupVersion: 1,
185
+ rollupManaLimit: 200_000_000
179
186
  };
180
187
  }
181
188
  updateConfig(config) {
@@ -192,6 +199,8 @@ import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
192
199
  if (!this.checkpointBuilder) {
193
200
  // Auto-create a builder if none was set
194
201
  this.checkpointBuilder = new MockCheckpointBuilder(constants, checkpointNumber);
202
+ } else {
203
+ this.checkpointBuilder.resetCheckpointState();
195
204
  }
196
205
  return Promise.resolve(this.checkpointBuilder);
197
206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/sequencer-client",
3
- "version": "0.0.1-commit.f504929",
3
+ "version": "0.0.1-commit.f5d02921e",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -26,38 +26,37 @@
26
26
  "test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
27
27
  },
28
28
  "dependencies": {
29
- "@aztec/aztec.js": "0.0.1-commit.f504929",
30
- "@aztec/bb-prover": "0.0.1-commit.f504929",
31
- "@aztec/blob-client": "0.0.1-commit.f504929",
32
- "@aztec/blob-lib": "0.0.1-commit.f504929",
33
- "@aztec/constants": "0.0.1-commit.f504929",
34
- "@aztec/epoch-cache": "0.0.1-commit.f504929",
35
- "@aztec/ethereum": "0.0.1-commit.f504929",
36
- "@aztec/foundation": "0.0.1-commit.f504929",
37
- "@aztec/l1-artifacts": "0.0.1-commit.f504929",
38
- "@aztec/merkle-tree": "0.0.1-commit.f504929",
39
- "@aztec/node-keystore": "0.0.1-commit.f504929",
40
- "@aztec/noir-acvm_js": "0.0.1-commit.f504929",
41
- "@aztec/noir-contracts.js": "0.0.1-commit.f504929",
42
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.f504929",
43
- "@aztec/noir-types": "0.0.1-commit.f504929",
44
- "@aztec/p2p": "0.0.1-commit.f504929",
45
- "@aztec/protocol-contracts": "0.0.1-commit.f504929",
46
- "@aztec/prover-client": "0.0.1-commit.f504929",
47
- "@aztec/simulator": "0.0.1-commit.f504929",
48
- "@aztec/slasher": "0.0.1-commit.f504929",
49
- "@aztec/stdlib": "0.0.1-commit.f504929",
50
- "@aztec/telemetry-client": "0.0.1-commit.f504929",
51
- "@aztec/validator-client": "0.0.1-commit.f504929",
52
- "@aztec/validator-ha-signer": "0.0.1-commit.f504929",
53
- "@aztec/world-state": "0.0.1-commit.f504929",
29
+ "@aztec/aztec.js": "0.0.1-commit.f5d02921e",
30
+ "@aztec/bb-prover": "0.0.1-commit.f5d02921e",
31
+ "@aztec/blob-client": "0.0.1-commit.f5d02921e",
32
+ "@aztec/blob-lib": "0.0.1-commit.f5d02921e",
33
+ "@aztec/constants": "0.0.1-commit.f5d02921e",
34
+ "@aztec/epoch-cache": "0.0.1-commit.f5d02921e",
35
+ "@aztec/ethereum": "0.0.1-commit.f5d02921e",
36
+ "@aztec/foundation": "0.0.1-commit.f5d02921e",
37
+ "@aztec/l1-artifacts": "0.0.1-commit.f5d02921e",
38
+ "@aztec/node-keystore": "0.0.1-commit.f5d02921e",
39
+ "@aztec/noir-acvm_js": "0.0.1-commit.f5d02921e",
40
+ "@aztec/noir-contracts.js": "0.0.1-commit.f5d02921e",
41
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.f5d02921e",
42
+ "@aztec/noir-types": "0.0.1-commit.f5d02921e",
43
+ "@aztec/p2p": "0.0.1-commit.f5d02921e",
44
+ "@aztec/protocol-contracts": "0.0.1-commit.f5d02921e",
45
+ "@aztec/prover-client": "0.0.1-commit.f5d02921e",
46
+ "@aztec/simulator": "0.0.1-commit.f5d02921e",
47
+ "@aztec/slasher": "0.0.1-commit.f5d02921e",
48
+ "@aztec/stdlib": "0.0.1-commit.f5d02921e",
49
+ "@aztec/telemetry-client": "0.0.1-commit.f5d02921e",
50
+ "@aztec/validator-client": "0.0.1-commit.f5d02921e",
51
+ "@aztec/validator-ha-signer": "0.0.1-commit.f5d02921e",
52
+ "@aztec/world-state": "0.0.1-commit.f5d02921e",
54
53
  "lodash.chunk": "^4.2.0",
55
54
  "tslib": "^2.4.0",
56
55
  "viem": "npm:@aztec/viem@2.38.2"
57
56
  },
58
57
  "devDependencies": {
59
- "@aztec/archiver": "0.0.1-commit.f504929",
60
- "@aztec/kv-store": "0.0.1-commit.f504929",
58
+ "@aztec/archiver": "0.0.1-commit.f5d02921e",
59
+ "@aztec/kv-store": "0.0.1-commit.f5d02921e",
61
60
  "@electric-sql/pglite": "^0.3.14",
62
61
  "@jest/globals": "^30.0.0",
63
62
  "@types/jest": "^30.0.0",
@@ -1,4 +1,5 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
+ import { MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT } from '@aztec/constants';
2
3
  import { EpochCache } from '@aztec/epoch-cache';
3
4
  import { isAnvilTestChain } from '@aztec/ethereum/chain';
4
5
  import { getPublicClient } from '@aztec/ethereum/client';
@@ -19,7 +20,7 @@ import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
19
20
  import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
20
21
 
21
22
  import { type SequencerClientConfig, getPublisherConfigFromSequencerConfig } from '../config.js';
22
- import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
23
+ import type { GlobalVariableBuilder } from '../global_variable_builder/index.js';
23
24
  import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
24
25
  import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
25
26
 
@@ -64,7 +65,9 @@ export class SequencerClient {
64
65
  dateProvider: DateProvider;
65
66
  epochCache?: EpochCache;
66
67
  l1TxUtils: L1TxUtils[];
68
+ funderL1TxUtils?: L1TxUtils;
67
69
  nodeKeyStore: KeystoreManager;
70
+ globalVariableBuilder: GlobalVariableBuilder;
68
71
  },
69
72
  ) {
70
73
  const {
@@ -86,16 +89,14 @@ export class SequencerClient {
86
89
  publicClient,
87
90
  l1TxUtils.map(x => x.getSenderAddress()),
88
91
  );
89
- const publisherManager = new PublisherManager(
90
- l1TxUtils,
91
- getPublisherConfigFromSequencerConfig(config),
92
- log.getBindings(),
93
- );
92
+ const publisherManager = new PublisherManager(l1TxUtils, getPublisherConfigFromSequencerConfig(config), {
93
+ bindings: log.getBindings(),
94
+ funder: deps.funderL1TxUtils,
95
+ });
94
96
  const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
95
- const [l1GenesisTime, slotDuration, rollupVersion, rollupManaLimit] = await Promise.all([
97
+ const [l1GenesisTime, slotDuration, rollupManaLimit] = await Promise.all([
96
98
  rollupContract.getL1GenesisTime(),
97
99
  rollupContract.getSlotDuration(),
98
- rollupContract.getVersion(),
99
100
  rollupContract.getManaLimit().then(Number),
100
101
  ] as const);
101
102
 
@@ -112,6 +113,7 @@ export class SequencerClient {
112
113
  l1ChainId: chainId,
113
114
  viemPollingIntervalMS: config.viemPollingIntervalMS,
114
115
  ethereumSlotDuration: config.ethereumSlotDuration,
116
+ enableProposerPipelining: config.enableProposerPipelining,
115
117
  },
116
118
  { dateProvider: deps.dateProvider },
117
119
  ));
@@ -137,17 +139,8 @@ export class SequencerClient {
137
139
  });
138
140
 
139
141
  const ethereumSlotDuration = config.ethereumSlotDuration;
140
- const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration };
141
-
142
- const globalsBuilder = new GlobalVariableBuilder({ ...config, ...l1Constants, rollupVersion });
143
142
 
144
- let sequencerManaLimit = config.maxL2BlockGas ?? rollupManaLimit;
145
- if (sequencerManaLimit > rollupManaLimit) {
146
- log.warn(
147
- `Provided maxL2BlockGas ${sequencerManaLimit} is greater than the max allowed by L1. Setting limit to ${rollupManaLimit}.`,
148
- );
149
- sequencerManaLimit = rollupManaLimit;
150
- }
143
+ const globalsBuilder = deps.globalVariableBuilder;
151
144
 
152
145
  // When running in anvil, assume we can post a tx up until one second before the end of an L1 slot.
153
146
  // Otherwise, we need the full L1 slot duration for publishing to ensure inclusion.
@@ -157,6 +150,10 @@ export class SequencerClient {
157
150
  const l1PublishingTimeBasedOnChain = isAnvilTestChain(config.l1ChainId) ? 1 : ethereumSlotDuration;
158
151
  const l1PublishingTime = config.l1PublishingTime ?? l1PublishingTimeBasedOnChain;
159
152
 
153
+ const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = capPerBlockLimits(config, rollupManaLimit, log);
154
+
155
+ const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration, rollupManaLimit };
156
+
160
157
  const sequencer = new Sequencer(
161
158
  publisherFactory,
162
159
  validatorClient,
@@ -171,7 +168,7 @@ export class SequencerClient {
171
168
  deps.dateProvider,
172
169
  epochCache,
173
170
  rollupContract,
174
- { ...config, l1PublishingTime, maxL2BlockGas: sequencerManaLimit },
171
+ { ...config, l1PublishingTime, maxL2BlockGas, maxDABlockGas, maxTxsPerBlock },
175
172
  telemetryClient,
176
173
  log,
177
174
  );
@@ -199,7 +196,7 @@ export class SequencerClient {
199
196
  await this.validatorClient?.start();
200
197
  this.sequencer.start();
201
198
  this.l1Metrics?.start();
202
- await this.publisherManager.loadState();
199
+ await this.publisherManager.start();
203
200
  }
204
201
 
205
202
  /**
@@ -208,7 +205,7 @@ export class SequencerClient {
208
205
  public async stop() {
209
206
  await this.sequencer.stop();
210
207
  await this.validatorClient?.stop();
211
- this.publisherManager.interrupt();
208
+ await this.publisherManager.stop();
212
209
  this.l1Metrics?.stop();
213
210
  }
214
211
 
@@ -234,3 +231,41 @@ export class SequencerClient {
234
231
  return this.sequencer.maxL2BlockGas;
235
232
  }
236
233
  }
234
+
235
+ /**
236
+ * Caps operator-provided per-block limits at checkpoint-level limits.
237
+ * Returns undefined for any limit the operator didn't set — the checkpoint builder handles redistribution.
238
+ */
239
+ function capPerBlockLimits(
240
+ config: SequencerClientConfig,
241
+ rollupManaLimit: number,
242
+ log: ReturnType<typeof createLogger>,
243
+ ): { maxL2BlockGas: number | undefined; maxDABlockGas: number | undefined; maxTxsPerBlock: number | undefined } {
244
+ let maxL2BlockGas = config.maxL2BlockGas;
245
+ if (maxL2BlockGas !== undefined && maxL2BlockGas > rollupManaLimit) {
246
+ log.warn(`Provided MAX_L2_BLOCK_GAS ${maxL2BlockGas} exceeds rollup mana limit ${rollupManaLimit} (capping)`);
247
+ maxL2BlockGas = rollupManaLimit;
248
+ }
249
+
250
+ let maxDABlockGas = config.maxDABlockGas;
251
+ if (maxDABlockGas !== undefined && maxDABlockGas > MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT) {
252
+ log.warn(
253
+ `Provided MAX_DA_BLOCK_GAS ${maxDABlockGas} exceeds DA checkpoint limit ${MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT} (capping)`,
254
+ );
255
+ maxDABlockGas = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;
256
+ }
257
+
258
+ let maxTxsPerBlock = config.maxTxsPerBlock;
259
+ if (
260
+ maxTxsPerBlock !== undefined &&
261
+ config.maxTxsPerCheckpoint !== undefined &&
262
+ maxTxsPerBlock > config.maxTxsPerCheckpoint
263
+ ) {
264
+ log.warn(
265
+ `Provided MAX_TX_PER_BLOCK ${maxTxsPerBlock} exceeds MAX_TX_PER_CHECKPOINT ${config.maxTxsPerCheckpoint} (capping)`,
266
+ );
267
+ maxTxsPerBlock = config.maxTxsPerCheckpoint;
268
+ }
269
+
270
+ return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock };
271
+ }
package/src/config.ts CHANGED
@@ -13,9 +13,10 @@ import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config';
13
13
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
14
14
  import {
15
15
  type ChainConfig,
16
- DEFAULT_MAX_TXS_PER_BLOCK,
16
+ type PipelineConfig,
17
17
  type SequencerConfig,
18
18
  chainConfigMappings,
19
+ pipelineConfigMappings,
19
20
  sharedSequencerConfigMappings,
20
21
  } from '@aztec/stdlib/config';
21
22
  import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
@@ -36,15 +37,13 @@ export type { SequencerConfig };
36
37
  * Default values for SequencerConfig.
37
38
  * Centralized location for all sequencer configuration defaults.
38
39
  */
39
- export const DefaultSequencerConfig: ResolvedSequencerConfig = {
40
+ export const DefaultSequencerConfig = {
40
41
  sequencerPollingIntervalMS: 500,
41
- maxTxsPerBlock: DEFAULT_MAX_TXS_PER_BLOCK,
42
42
  minTxsPerBlock: 1,
43
43
  buildCheckpointIfEmpty: false,
44
44
  publishTxsWithProposals: false,
45
- maxL2BlockGas: 10e9,
46
- maxDABlockGas: 10e9,
47
- maxBlockSizeInBytes: 1024 * 1024,
45
+ perBlockAllocationMultiplier: 1.2,
46
+ redistributeCheckpointBudget: true,
48
47
  enforceTimeTable: true,
49
48
  attestationPropagationTime: DEFAULT_P2P_PROPAGATION_TIME,
50
49
  secondsBeforeInvalidatingBlockAsCommitteeMember: 144, // 12 L1 blocks
@@ -59,7 +58,7 @@ export const DefaultSequencerConfig: ResolvedSequencerConfig = {
59
58
  shuffleAttestationOrdering: false,
60
59
  skipPushProposedBlocksToArchiver: false,
61
60
  skipPublishingCheckpointsPercent: 0,
62
- };
61
+ } satisfies ResolvedSequencerConfig;
63
62
 
64
63
  /**
65
64
  * Configuration settings for the SequencerClient.
@@ -71,6 +70,7 @@ export type SequencerClientConfig = SequencerPublisherConfig &
71
70
  SequencerConfig &
72
71
  L1ReaderConfig &
73
72
  ChainConfig &
73
+ PipelineConfig &
74
74
  Pick<P2PConfig, 'txPublicSetupAllowListExtend'> &
75
75
  Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration' | 'aztecEpochDuration'>;
76
76
 
@@ -80,6 +80,11 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
80
80
  description: 'The number of ms to wait between polling for checking to build on the next slot.',
81
81
  ...numberConfigHelper(DefaultSequencerConfig.sequencerPollingIntervalMS),
82
82
  },
83
+ maxTxsPerCheckpoint: {
84
+ env: 'SEQ_MAX_TX_PER_CHECKPOINT',
85
+ description: 'The maximum number of txs across all blocks in a checkpoint.',
86
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
87
+ },
83
88
  minTxsPerBlock: {
84
89
  env: 'SEQ_MIN_TX_PER_BLOCK',
85
90
  description: 'The minimum number of txs to include in a block.',
@@ -97,12 +102,25 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
97
102
  maxL2BlockGas: {
98
103
  env: 'SEQ_MAX_L2_BLOCK_GAS',
99
104
  description: 'The maximum L2 block gas.',
100
- ...numberConfigHelper(DefaultSequencerConfig.maxL2BlockGas),
105
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
101
106
  },
102
107
  maxDABlockGas: {
103
108
  env: 'SEQ_MAX_DA_BLOCK_GAS',
104
109
  description: 'The maximum DA block gas.',
105
- ...numberConfigHelper(DefaultSequencerConfig.maxDABlockGas),
110
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
111
+ },
112
+ perBlockAllocationMultiplier: {
113
+ env: 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER',
114
+ description:
115
+ 'Per-block gas budget multiplier for both L2 and DA gas. Budget per block is (checkpointLimit / maxBlocks) * multiplier.' +
116
+ ' Values greater than one allow early blocks to use more than their even share, relying on checkpoint-level capping for later blocks.',
117
+ ...numberConfigHelper(DefaultSequencerConfig.perBlockAllocationMultiplier),
118
+ },
119
+ redistributeCheckpointBudget: {
120
+ env: 'SEQ_REDISTRIBUTE_CHECKPOINT_BUDGET',
121
+ description:
122
+ 'Redistribute remaining checkpoint budget evenly across remaining blocks instead of allowing a single block to consume the entire remaining budget.',
123
+ ...booleanConfigHelper(DefaultSequencerConfig.redistributeCheckpointBudget),
106
124
  },
107
125
  coinbase: {
108
126
  env: 'COINBASE',
@@ -122,11 +140,6 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
122
140
  env: 'ACVM_BINARY_PATH',
123
141
  description: 'The path to the ACVM binary',
124
142
  },
125
- maxBlockSizeInBytes: {
126
- env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES',
127
- description: 'Max block size',
128
- ...numberConfigHelper(DefaultSequencerConfig.maxBlockSizeInBytes),
129
- },
130
143
  enforceTimeTable: {
131
144
  env: 'SEQ_ENFORCE_TIME_TABLE',
132
145
  description: 'Whether to enforce the time table when building blocks',
@@ -231,6 +244,7 @@ export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientCo
231
244
  ...sequencerTxSenderConfigMappings,
232
245
  ...sequencerPublisherConfigMappings,
233
246
  ...chainConfigMappings,
247
+ ...pipelineConfigMappings,
234
248
  ...pickConfigMappings(l1ContractsConfigMappings, ['ethereumSlotDuration', 'aztecSlotDuration', 'aztecEpochDuration']),
235
249
  };
236
250
 
@@ -1,22 +1,27 @@
1
- import { createEthereumChain } from '@aztec/ethereum/chain';
2
- import type { L1ContractsConfig } from '@aztec/ethereum/config';
3
1
  import { RollupContract } from '@aztec/ethereum/contracts';
4
- import type { L1ReaderConfig } from '@aztec/ethereum/l1-reader';
2
+ import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
5
3
  import type { ViemPublicClient } from '@aztec/ethereum/types';
6
4
  import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
7
5
  import { Fr } from '@aztec/foundation/curves/bn254';
8
6
  import type { EthAddress } from '@aztec/foundation/eth-address';
9
7
  import { createLogger } from '@aztec/foundation/log';
8
+ import type { DateProvider } from '@aztec/foundation/timer';
10
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
11
- import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
10
+ import { type L1RollupConstants, getNextL1SlotTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
12
11
  import { GasFees } from '@aztec/stdlib/gas';
13
12
  import type {
13
+ BuildCheckpointGlobalVariablesOpts,
14
14
  CheckpointGlobalVariables,
15
15
  GlobalVariableBuilder as GlobalVariableBuilderInterface,
16
16
  } from '@aztec/stdlib/tx';
17
17
  import { GlobalVariables } from '@aztec/stdlib/tx';
18
18
 
19
- import { createPublicClient, fallback, http } from 'viem';
19
+ /** Configuration for the GlobalVariableBuilder (excludes L1 client config). */
20
+ export type GlobalVariableBuilderConfig = {
21
+ l1Contracts: Pick<L1ContractAddresses, 'rollupAddress'>;
22
+ ethereumSlotDuration: number;
23
+ rollupVersion: bigint;
24
+ } & Pick<L1RollupConstants, 'slotDuration' | 'l1GenesisTime'>;
20
25
 
21
26
  /**
22
27
  * Simple global variables builder.
@@ -27,7 +32,6 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
27
32
  private currentL1BlockNumber: bigint | undefined = undefined;
28
33
 
29
34
  private readonly rollupContract: RollupContract;
30
- private readonly publicClient: ViemPublicClient;
31
35
  private readonly ethereumSlotDuration: number;
32
36
  private readonly aztecSlotDuration: number;
33
37
  private readonly l1GenesisTime: bigint;
@@ -36,28 +40,18 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
36
40
  private version: Fr;
37
41
 
38
42
  constructor(
39
- config: L1ReaderConfig &
40
- Pick<L1ContractsConfig, 'ethereumSlotDuration'> &
41
- Pick<L1RollupConstants, 'slotDuration' | 'l1GenesisTime'> & { rollupVersion: bigint },
43
+ private readonly dateProvider: DateProvider,
44
+ private readonly publicClient: ViemPublicClient,
45
+ config: GlobalVariableBuilderConfig,
42
46
  ) {
43
- const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config;
44
-
45
- const chain = createEthereumChain(l1RpcUrls, chainId);
46
-
47
47
  this.version = new Fr(config.rollupVersion);
48
- this.chainId = new Fr(chainId);
48
+ this.chainId = new Fr(this.publicClient.chain!.id);
49
49
 
50
50
  this.ethereumSlotDuration = config.ethereumSlotDuration;
51
51
  this.aztecSlotDuration = config.slotDuration;
52
52
  this.l1GenesisTime = config.l1GenesisTime;
53
53
 
54
- this.publicClient = createPublicClient({
55
- chain: chain.chainInfo,
56
- transport: fallback(chain.rpcUrls.map(url => http(url, { batch: false }))),
57
- pollingInterval: config.viemPollingIntervalMS,
58
- });
59
-
60
- this.rollupContract = new RollupContract(this.publicClient, l1Contracts.rollupAddress);
54
+ this.rollupContract = new RollupContract(this.publicClient, config.l1Contracts.rollupAddress);
61
55
  }
62
56
 
63
57
  /**
@@ -73,7 +67,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
73
67
  const earliestTimestamp = await this.rollupContract.getTimestampForSlot(
74
68
  SlotNumber.fromBigInt(BigInt(lastCheckpoint.slotNumber) + 1n),
75
69
  );
76
- const nextEthTimestamp = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
70
+ const nextEthTimestamp = getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), {
71
+ l1GenesisTime: this.l1GenesisTime,
72
+ ethereumSlotDuration: this.ethereumSlotDuration,
73
+ });
77
74
  const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp;
78
75
 
79
76
  return new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
@@ -108,7 +105,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
108
105
  const slot: SlotNumber =
109
106
  maybeSlot ??
110
107
  (await this.rollupContract.getSlotAt(
111
- BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration)),
108
+ getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), {
109
+ l1GenesisTime: this.l1GenesisTime,
110
+ ethereumSlotDuration: this.ethereumSlotDuration,
111
+ }),
112
112
  ));
113
113
 
114
114
  const checkpointGlobalVariables = await this.buildCheckpointGlobalVariables(coinbase, feeRecipient, slot);
@@ -120,6 +120,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
120
120
  coinbase: EthAddress,
121
121
  feeRecipient: AztecAddress,
122
122
  slotNumber: SlotNumber,
123
+ opts?: BuildCheckpointGlobalVariablesOpts,
123
124
  ): Promise<CheckpointGlobalVariables> {
124
125
  const { chainId, version } = this;
125
126
 
@@ -128,9 +129,19 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
128
129
  l1GenesisTime: this.l1GenesisTime,
129
130
  });
130
131
 
131
- // We can skip much of the logic in getCurrentMinFees since it we already check that we are not within a slot elsewhere.
132
- // TODO(palla/mbps): Can we use a cached value here?
133
- const gasFees = new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
132
+ // When pipelining, force the proposed checkpoint number and fee header to the parent so that
133
+ // the fee computation matches what L1 will see when the previous pipelined checkpoint has landed.
134
+ const pendingNumberOverride = await this.rollupContract.makePendingCheckpointNumberOverride(
135
+ opts?.forcePendingCheckpointNumber,
136
+ );
137
+ const feeHeaderOverride = opts?.forceProposedFeeHeader
138
+ ? await this.rollupContract.makeFeeHeaderOverride(
139
+ opts.forceProposedFeeHeader.checkpointNumber,
140
+ opts.forceProposedFeeHeader.feeHeader,
141
+ )
142
+ : [];
143
+ const stateOverride = RollupContract.mergeStateOverrides(pendingNumberOverride, feeHeaderOverride);
144
+ const gasFees = new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true, stateOverride));
134
145
 
135
146
  return { chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees };
136
147
  }
@@ -1 +1 @@
1
- export { GlobalVariableBuilder } from './global_builder.js';
1
+ export { GlobalVariableBuilder, type GlobalVariableBuilderConfig } from './global_builder.js';