@aztec/sequencer-client 0.0.1-commit.ef17749e1 → 0.0.1-commit.f1b29a41e

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 (53) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -12
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +27 -76
  4. package/dest/config.d.ts +4 -3
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +9 -2
  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 -6
  22. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  23. package/dest/sequencer/checkpoint_proposal_job.js +261 -141
  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 +19 -7
  33. package/dest/sequencer/sequencer.d.ts.map +1 -1
  34. package/dest/sequencer/sequencer.js +123 -68
  35. package/dest/sequencer/types.d.ts +2 -5
  36. package/dest/sequencer/types.d.ts.map +1 -1
  37. package/dest/test/mock_checkpoint_builder.d.ts +4 -4
  38. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  39. package/package.json +27 -28
  40. package/src/client/sequencer-client.ts +37 -101
  41. package/src/config.ts +12 -1
  42. package/src/global_variable_builder/global_builder.ts +37 -26
  43. package/src/global_variable_builder/index.ts +1 -1
  44. package/src/publisher/config.ts +32 -0
  45. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  46. package/src/publisher/sequencer-publisher.ts +144 -54
  47. package/src/sequencer/checkpoint_proposal_job.ts +340 -147
  48. package/src/sequencer/checkpoint_voter.ts +1 -12
  49. package/src/sequencer/events.ts +1 -1
  50. package/src/sequencer/metrics.ts +14 -0
  51. package/src/sequencer/sequencer.ts +178 -79
  52. package/src/sequencer/types.ts +2 -5
  53. package/src/test/mock_checkpoint_builder.ts +3 -3
@@ -19,15 +19,10 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
19
19
  import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
20
20
  import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
21
21
 
22
- import {
23
- DefaultSequencerConfig,
24
- type SequencerClientConfig,
25
- getPublisherConfigFromSequencerConfig,
26
- } from '../config.js';
27
- import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
22
+ import { type SequencerClientConfig, getPublisherConfigFromSequencerConfig } from '../config.js';
23
+ import type { GlobalVariableBuilder } from '../global_variable_builder/index.js';
28
24
  import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
29
25
  import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
30
- import { SequencerTimetable } from '../sequencer/timetable.js';
31
26
 
32
27
  /**
33
28
  * Encapsulates the full sequencer and publisher.
@@ -70,7 +65,9 @@ export class SequencerClient {
70
65
  dateProvider: DateProvider;
71
66
  epochCache?: EpochCache;
72
67
  l1TxUtils: L1TxUtils[];
68
+ funderL1TxUtils?: L1TxUtils;
73
69
  nodeKeyStore: KeystoreManager;
70
+ globalVariableBuilder: GlobalVariableBuilder;
74
71
  },
75
72
  ) {
76
73
  const {
@@ -92,16 +89,14 @@ export class SequencerClient {
92
89
  publicClient,
93
90
  l1TxUtils.map(x => x.getSenderAddress()),
94
91
  );
95
- const publisherManager = new PublisherManager(
96
- l1TxUtils,
97
- getPublisherConfigFromSequencerConfig(config),
98
- log.getBindings(),
99
- );
92
+ const publisherManager = new PublisherManager(l1TxUtils, getPublisherConfigFromSequencerConfig(config), {
93
+ bindings: log.getBindings(),
94
+ funder: deps.funderL1TxUtils,
95
+ });
100
96
  const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
101
- const [l1GenesisTime, slotDuration, rollupVersion, rollupManaLimit] = await Promise.all([
97
+ const [l1GenesisTime, slotDuration, rollupManaLimit] = await Promise.all([
102
98
  rollupContract.getL1GenesisTime(),
103
99
  rollupContract.getSlotDuration(),
104
- rollupContract.getVersion(),
105
100
  rollupContract.getManaLimit().then(Number),
106
101
  ] as const);
107
102
 
@@ -118,6 +113,7 @@ export class SequencerClient {
118
113
  l1ChainId: chainId,
119
114
  viemPollingIntervalMS: config.viemPollingIntervalMS,
120
115
  ethereumSlotDuration: config.ethereumSlotDuration,
116
+ enableProposerPipelining: config.enableProposerPipelining,
121
117
  },
122
118
  { dateProvider: deps.dateProvider },
123
119
  ));
@@ -144,13 +140,7 @@ export class SequencerClient {
144
140
 
145
141
  const ethereumSlotDuration = config.ethereumSlotDuration;
146
142
 
147
- const globalsBuilder = new GlobalVariableBuilder({
148
- ...config,
149
- l1GenesisTime,
150
- slotDuration: Number(slotDuration),
151
- ethereumSlotDuration,
152
- rollupVersion,
153
- });
143
+ const globalsBuilder = deps.globalVariableBuilder;
154
144
 
155
145
  // When running in anvil, assume we can post a tx up until one second before the end of an L1 slot.
156
146
  // Otherwise, we need the full L1 slot duration for publishing to ensure inclusion.
@@ -160,12 +150,7 @@ export class SequencerClient {
160
150
  const l1PublishingTimeBasedOnChain = isAnvilTestChain(config.l1ChainId) ? 1 : ethereumSlotDuration;
161
151
  const l1PublishingTime = config.l1PublishingTime ?? l1PublishingTimeBasedOnChain;
162
152
 
163
- const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = computeBlockLimits(
164
- config,
165
- rollupManaLimit,
166
- l1PublishingTime,
167
- log,
168
- );
153
+ const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = capPerBlockLimits(config, rollupManaLimit, log);
169
154
 
170
155
  const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration, rollupManaLimit };
171
156
 
@@ -211,7 +196,7 @@ export class SequencerClient {
211
196
  await this.validatorClient?.start();
212
197
  this.sequencer.start();
213
198
  this.l1Metrics?.start();
214
- await this.publisherManager.loadState();
199
+ await this.publisherManager.start();
215
200
  }
216
201
 
217
202
  /**
@@ -220,7 +205,7 @@ export class SequencerClient {
220
205
  public async stop() {
221
206
  await this.sequencer.stop();
222
207
  await this.validatorClient?.stop();
223
- this.publisherManager.interrupt();
208
+ await this.publisherManager.stop();
224
209
  this.l1Metrics?.stop();
225
210
  }
226
211
 
@@ -248,88 +233,39 @@ export class SequencerClient {
248
233
  }
249
234
 
250
235
  /**
251
- * Computes per-block L2 gas, DA gas, and TX count budgets based on the L1 rollup limits and the timetable.
252
- * If the user explicitly set a limit, it is capped at the corresponding checkpoint limit.
253
- * Otherwise, derives it as (checkpointLimit / maxBlocks) * multiplier, capped at the checkpoint limit.
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.
254
238
  */
255
- export function computeBlockLimits(
239
+ function capPerBlockLimits(
256
240
  config: SequencerClientConfig,
257
241
  rollupManaLimit: number,
258
- l1PublishingTime: number,
259
242
  log: ReturnType<typeof createLogger>,
260
- ): { maxL2BlockGas: number; maxDABlockGas: number; maxTxsPerBlock: number } {
261
- const maxNumberOfBlocks = new SequencerTimetable({
262
- ethereumSlotDuration: config.ethereumSlotDuration,
263
- aztecSlotDuration: config.aztecSlotDuration,
264
- l1PublishingTime,
265
- p2pPropagationTime: config.attestationPropagationTime,
266
- blockDurationMs: config.blockDurationMs,
267
- enforce: config.enforceTimeTable ?? DefaultSequencerConfig.enforceTimeTable,
268
- }).maxNumberOfBlocks;
269
-
270
- const multiplier = config.perBlockAllocationMultiplier ?? DefaultSequencerConfig.perBlockAllocationMultiplier;
271
-
272
- // Compute maxL2BlockGas
273
- let maxL2BlockGas: number;
274
- if (config.maxL2BlockGas !== undefined) {
275
- if (config.maxL2BlockGas > rollupManaLimit) {
276
- log.warn(
277
- `Provided MAX_L2_BLOCK_GAS ${config.maxL2BlockGas} exceeds L1 rollup mana limit ${rollupManaLimit} (capping)`,
278
- );
279
- maxL2BlockGas = rollupManaLimit;
280
- } else {
281
- maxL2BlockGas = config.maxL2BlockGas;
282
- }
283
- } else {
284
- maxL2BlockGas = Math.min(rollupManaLimit, Math.ceil((rollupManaLimit / maxNumberOfBlocks) * multiplier));
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;
285
248
  }
286
249
 
287
- // Compute maxDABlockGas
288
- const daCheckpointLimit = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;
289
- let maxDABlockGas: number;
290
- if (config.maxDABlockGas !== undefined) {
291
- if (config.maxDABlockGas > daCheckpointLimit) {
292
- log.warn(
293
- `Provided MAX_DA_BLOCK_GAS ${config.maxDABlockGas} exceeds DA checkpoint limit ${daCheckpointLimit} (capping)`,
294
- );
295
- maxDABlockGas = daCheckpointLimit;
296
- } else {
297
- maxDABlockGas = config.maxDABlockGas;
298
- }
299
- } else {
300
- maxDABlockGas = Math.min(daCheckpointLimit, Math.ceil((daCheckpointLimit / maxNumberOfBlocks) * multiplier));
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;
301
256
  }
302
257
 
303
- // Compute maxTxsPerBlock
304
- const defaultMaxTxsPerBlock = 32;
305
- let maxTxsPerBlock: number;
306
- if (config.maxTxsPerBlock !== undefined) {
307
- if (config.maxTxsPerCheckpoint !== undefined && config.maxTxsPerBlock > config.maxTxsPerCheckpoint) {
308
- log.warn(
309
- `Provided MAX_TX_PER_BLOCK ${config.maxTxsPerBlock} exceeds MAX_TX_PER_CHECKPOINT ${config.maxTxsPerCheckpoint} (capping)`,
310
- );
311
- maxTxsPerBlock = config.maxTxsPerCheckpoint;
312
- } else {
313
- maxTxsPerBlock = config.maxTxsPerBlock;
314
- }
315
- } else if (config.maxTxsPerCheckpoint !== undefined) {
316
- maxTxsPerBlock = Math.min(
317
- config.maxTxsPerCheckpoint,
318
- Math.ceil((config.maxTxsPerCheckpoint / maxNumberOfBlocks) * multiplier),
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)`,
319
266
  );
320
- } else {
321
- maxTxsPerBlock = defaultMaxTxsPerBlock;
267
+ maxTxsPerBlock = config.maxTxsPerCheckpoint;
322
268
  }
323
269
 
324
- log.info(`Computed block limits L2=${maxL2BlockGas} DA=${maxDABlockGas} maxTxs=${maxTxsPerBlock}`, {
325
- maxL2BlockGas,
326
- maxDABlockGas,
327
- maxTxsPerBlock,
328
- rollupManaLimit,
329
- daCheckpointLimit,
330
- maxNumberOfBlocks,
331
- multiplier,
332
- });
333
-
334
270
  return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock };
335
271
  }
package/src/config.ts CHANGED
@@ -13,8 +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
+ type PipelineConfig,
16
17
  type SequencerConfig,
17
18
  chainConfigMappings,
19
+ pipelineConfigMappings,
18
20
  sharedSequencerConfigMappings,
19
21
  } from '@aztec/stdlib/config';
20
22
  import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
@@ -40,7 +42,8 @@ export const DefaultSequencerConfig = {
40
42
  minTxsPerBlock: 1,
41
43
  buildCheckpointIfEmpty: false,
42
44
  publishTxsWithProposals: false,
43
- perBlockAllocationMultiplier: 2,
45
+ perBlockAllocationMultiplier: 1.2,
46
+ redistributeCheckpointBudget: true,
44
47
  enforceTimeTable: true,
45
48
  attestationPropagationTime: DEFAULT_P2P_PROPAGATION_TIME,
46
49
  secondsBeforeInvalidatingBlockAsCommitteeMember: 144, // 12 L1 blocks
@@ -67,6 +70,7 @@ export type SequencerClientConfig = SequencerPublisherConfig &
67
70
  SequencerConfig &
68
71
  L1ReaderConfig &
69
72
  ChainConfig &
73
+ PipelineConfig &
70
74
  Pick<P2PConfig, 'txPublicSetupAllowListExtend'> &
71
75
  Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration' | 'aztecEpochDuration'>;
72
76
 
@@ -112,6 +116,12 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
112
116
  ' Values greater than one allow early blocks to use more than their even share, relying on checkpoint-level capping for later blocks.',
113
117
  ...numberConfigHelper(DefaultSequencerConfig.perBlockAllocationMultiplier),
114
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),
124
+ },
115
125
  coinbase: {
116
126
  env: 'COINBASE',
117
127
  parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
@@ -234,6 +244,7 @@ export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientCo
234
244
  ...sequencerTxSenderConfigMappings,
235
245
  ...sequencerPublisherConfigMappings,
236
246
  ...chainConfigMappings,
247
+ ...pipelineConfigMappings,
237
248
  ...pickConfigMappings(l1ContractsConfigMappings, ['ethereumSlotDuration', 'aztecSlotDuration', 'aztecEpochDuration']),
238
249
  };
239
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';
@@ -4,6 +4,8 @@ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l
4
4
  import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
 
7
+ import { parseEther } from 'viem';
8
+
7
9
  /** Configuration of the transaction publisher. */
8
10
  export type TxSenderConfig = L1ReaderConfig & {
9
11
  /** The private key to be used by the publisher. */
@@ -50,13 +52,37 @@ export type PublisherConfig = L1TxUtilsConfig &
50
52
  publisherForwarderAddress?: EthAddress;
51
53
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
52
54
  l1TxFailedStore?: string;
55
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
56
+ publisherFundingThreshold?: bigint;
57
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
58
+ publisherFundingAmount?: bigint;
53
59
  };
54
60
 
61
+ /** Shared config mappings for publisher funding, used by both sequencer and prover publisher configs. */
62
+ const publisherFundingConfigMappings = {
63
+ publisherFundingThreshold: {
64
+ env: 'PUBLISHER_FUNDING_THRESHOLD' as const,
65
+ description:
66
+ 'Min ETH balance below which a publisher gets funded. Specified in ether (e.g. 0.1). Unset = funding disabled.',
67
+ parseEnv: (val: string) => parseEther(val),
68
+ },
69
+ publisherFundingAmount: {
70
+ env: 'PUBLISHER_FUNDING_AMOUNT' as const,
71
+ description:
72
+ 'Amount of ETH to send when funding a publisher. Specified in ether (e.g. 0.5). Unset = funding disabled.',
73
+ parseEnv: (val: string) => parseEther(val),
74
+ },
75
+ };
76
+
55
77
  export type ProverPublisherConfig = L1TxUtilsConfig &
56
78
  BlobClientConfig & {
57
79
  fishermanMode?: boolean;
58
80
  proverPublisherAllowInvalidStates?: boolean;
59
81
  proverPublisherForwarderAddress?: EthAddress;
82
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
83
+ publisherFundingThreshold?: bigint;
84
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
85
+ publisherFundingAmount?: bigint;
60
86
  };
61
87
 
62
88
  export type SequencerPublisherConfig = L1TxUtilsConfig &
@@ -66,6 +92,10 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
66
92
  sequencerPublisherForwarderAddress?: EthAddress;
67
93
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
68
94
  l1TxFailedStore?: string;
95
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
96
+ publisherFundingThreshold?: bigint;
97
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
98
+ publisherFundingAmount?: bigint;
69
99
  };
70
100
 
71
101
  export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
@@ -142,6 +172,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
142
172
  env: 'L1_TX_FAILED_STORE',
143
173
  description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
144
174
  },
175
+ ...publisherFundingConfigMappings,
145
176
  };
146
177
 
147
178
  export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
@@ -163,4 +194,5 @@ export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherCo
163
194
  description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
164
195
  parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
165
196
  },
197
+ ...publisherFundingConfigMappings,
166
198
  };
@@ -117,8 +117,8 @@ export class SequencerPublisherFactory {
117
117
  };
118
118
  }
119
119
 
120
- /** Interrupts all publishers managed by this factory. Used during sequencer shutdown. */
121
- public interruptAll(): void {
122
- this.deps.publisherManager.interrupt();
120
+ /** Stops all publishers managed by this factory. Used during sequencer shutdown. */
121
+ public async stopAll(): Promise<void> {
122
+ await this.deps.publisherManager.stop();
123
123
  }
124
124
  }