@aztec/sequencer-client 0.0.1-commit.358457c → 0.0.1-commit.381b1a9

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 (55) hide show
  1. package/dest/client/sequencer-client.d.ts +12 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +85 -13
  4. package/dest/config.d.ts +23 -4
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +23 -16
  7. package/dest/publisher/config.d.ts +1 -5
  8. package/dest/publisher/config.d.ts.map +1 -1
  9. package/dest/publisher/config.js +1 -6
  10. package/dest/publisher/index.d.ts +1 -2
  11. package/dest/publisher/index.d.ts.map +1 -1
  12. package/dest/publisher/sequencer-publisher.d.ts +2 -8
  13. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  14. package/dest/publisher/sequencer-publisher.js +8 -217
  15. package/dest/sequencer/checkpoint_proposal_job.d.ts +2 -4
  16. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  17. package/dest/sequencer/checkpoint_proposal_job.js +60 -44
  18. package/dest/sequencer/sequencer.d.ts +10 -7
  19. package/dest/sequencer/sequencer.d.ts.map +1 -1
  20. package/dest/sequencer/sequencer.js +12 -14
  21. package/dest/sequencer/timetable.d.ts +4 -3
  22. package/dest/sequencer/timetable.d.ts.map +1 -1
  23. package/dest/sequencer/timetable.js +6 -7
  24. package/dest/sequencer/types.d.ts +2 -2
  25. package/dest/sequencer/types.d.ts.map +1 -1
  26. package/dest/test/mock_checkpoint_builder.d.ts +10 -8
  27. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  28. package/dest/test/mock_checkpoint_builder.js +41 -30
  29. package/package.json +28 -28
  30. package/src/client/sequencer-client.ts +111 -12
  31. package/src/config.ts +28 -19
  32. package/src/publisher/config.ts +0 -9
  33. package/src/publisher/index.ts +0 -3
  34. package/src/publisher/sequencer-publisher.ts +8 -178
  35. package/src/sequencer/checkpoint_proposal_job.ts +90 -55
  36. package/src/sequencer/sequencer.ts +13 -15
  37. package/src/sequencer/timetable.ts +7 -7
  38. package/src/sequencer/types.ts +1 -1
  39. package/src/test/mock_checkpoint_builder.ts +52 -47
  40. package/dest/publisher/l1_tx_failed_store/factory.d.ts +0 -11
  41. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +0 -1
  42. package/dest/publisher/l1_tx_failed_store/factory.js +0 -22
  43. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +0 -59
  44. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +0 -1
  45. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +0 -1
  46. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +0 -15
  47. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +0 -1
  48. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +0 -34
  49. package/dest/publisher/l1_tx_failed_store/index.d.ts +0 -4
  50. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +0 -1
  51. package/dest/publisher/l1_tx_failed_store/index.js +0 -2
  52. package/src/publisher/l1_tx_failed_store/factory.ts +0 -32
  53. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +0 -55
  54. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +0 -46
  55. package/src/publisher/l1_tx_failed_store/index.ts +0 -3
@@ -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';
@@ -18,10 +19,15 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
18
19
  import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
19
20
  import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
20
21
 
21
- import { type SequencerClientConfig, getPublisherConfigFromSequencerConfig } from '../config.js';
22
+ import {
23
+ DefaultSequencerConfig,
24
+ type SequencerClientConfig,
25
+ getPublisherConfigFromSequencerConfig,
26
+ } from '../config.js';
22
27
  import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
23
28
  import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
24
29
  import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
30
+ import { SequencerTimetable } from '../sequencer/timetable.js';
25
31
 
26
32
  /**
27
33
  * Encapsulates the full sequencer and publisher.
@@ -137,17 +143,14 @@ export class SequencerClient {
137
143
  });
138
144
 
139
145
  const ethereumSlotDuration = config.ethereumSlotDuration;
140
- const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration };
141
146
 
142
- const globalsBuilder = new GlobalVariableBuilder({ ...config, ...l1Constants, rollupVersion });
143
-
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
- }
147
+ const globalsBuilder = new GlobalVariableBuilder({
148
+ ...config,
149
+ l1GenesisTime,
150
+ slotDuration: Number(slotDuration),
151
+ ethereumSlotDuration,
152
+ rollupVersion,
153
+ });
151
154
 
152
155
  // When running in anvil, assume we can post a tx up until one second before the end of an L1 slot.
153
156
  // Otherwise, we need the full L1 slot duration for publishing to ensure inclusion.
@@ -157,6 +160,15 @@ export class SequencerClient {
157
160
  const l1PublishingTimeBasedOnChain = isAnvilTestChain(config.l1ChainId) ? 1 : ethereumSlotDuration;
158
161
  const l1PublishingTime = config.l1PublishingTime ?? l1PublishingTimeBasedOnChain;
159
162
 
163
+ const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = computeBlockLimits(
164
+ config,
165
+ rollupManaLimit,
166
+ l1PublishingTime,
167
+ log,
168
+ );
169
+
170
+ const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration, rollupManaLimit };
171
+
160
172
  const sequencer = new Sequencer(
161
173
  publisherFactory,
162
174
  validatorClient,
@@ -171,7 +183,7 @@ export class SequencerClient {
171
183
  deps.dateProvider,
172
184
  epochCache,
173
185
  rollupContract,
174
- { ...config, l1PublishingTime, maxL2BlockGas: sequencerManaLimit },
186
+ { ...config, l1PublishingTime, maxL2BlockGas, maxDABlockGas, maxTxsPerBlock },
175
187
  telemetryClient,
176
188
  log,
177
189
  );
@@ -234,3 +246,90 @@ export class SequencerClient {
234
246
  return this.sequencer.maxL2BlockGas;
235
247
  }
236
248
  }
249
+
250
+ /**
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.
254
+ */
255
+ export function computeBlockLimits(
256
+ config: SequencerClientConfig,
257
+ rollupManaLimit: number,
258
+ l1PublishingTime: number,
259
+ 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));
285
+ }
286
+
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));
301
+ }
302
+
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),
319
+ );
320
+ } else {
321
+ maxTxsPerBlock = defaultMaxTxsPerBlock;
322
+ }
323
+
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
+ return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock };
335
+ }
package/src/config.ts CHANGED
@@ -35,15 +35,12 @@ export type { SequencerConfig };
35
35
  * Default values for SequencerConfig.
36
36
  * Centralized location for all sequencer configuration defaults.
37
37
  */
38
- export const DefaultSequencerConfig: ResolvedSequencerConfig = {
38
+ export const DefaultSequencerConfig = {
39
39
  sequencerPollingIntervalMS: 500,
40
- maxTxsPerBlock: 32,
41
40
  minTxsPerBlock: 1,
42
41
  buildCheckpointIfEmpty: false,
43
42
  publishTxsWithProposals: false,
44
- maxL2BlockGas: 10e9,
45
- maxDABlockGas: 10e9,
46
- maxBlockSizeInBytes: 1024 * 1024,
43
+ perBlockAllocationMultiplier: 1.2,
47
44
  enforceTimeTable: true,
48
45
  attestationPropagationTime: DEFAULT_P2P_PROPAGATION_TIME,
49
46
  secondsBeforeInvalidatingBlockAsCommitteeMember: 144, // 12 L1 blocks
@@ -52,11 +49,13 @@ export const DefaultSequencerConfig: ResolvedSequencerConfig = {
52
49
  skipInvalidateBlockAsProposer: false,
53
50
  broadcastInvalidBlockProposal: false,
54
51
  injectFakeAttestation: false,
52
+ injectHighSValueAttestation: false,
53
+ injectUnrecoverableSignatureAttestation: false,
55
54
  fishermanMode: false,
56
55
  shuffleAttestationOrdering: false,
57
56
  skipPushProposedBlocksToArchiver: false,
58
57
  skipPublishingCheckpointsPercent: 0,
59
- };
58
+ } satisfies ResolvedSequencerConfig;
60
59
 
61
60
  /**
62
61
  * Configuration settings for the SequencerClient.
@@ -68,7 +67,7 @@ export type SequencerClientConfig = SequencerPublisherConfig &
68
67
  SequencerConfig &
69
68
  L1ReaderConfig &
70
69
  ChainConfig &
71
- Pick<P2PConfig, 'txPublicSetupAllowList'> &
70
+ Pick<P2PConfig, 'txPublicSetupAllowListExtend'> &
72
71
  Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration' | 'aztecEpochDuration'>;
73
72
 
74
73
  export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
@@ -77,10 +76,10 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
77
76
  description: 'The number of ms to wait between polling for checking to build on the next slot.',
78
77
  ...numberConfigHelper(DefaultSequencerConfig.sequencerPollingIntervalMS),
79
78
  },
80
- maxTxsPerBlock: {
81
- env: 'SEQ_MAX_TX_PER_BLOCK',
82
- description: 'The maximum number of txs to include in a block.',
83
- ...numberConfigHelper(DefaultSequencerConfig.maxTxsPerBlock),
79
+ maxTxsPerCheckpoint: {
80
+ env: 'SEQ_MAX_TX_PER_CHECKPOINT',
81
+ description: 'The maximum number of txs across all blocks in a checkpoint.',
82
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
84
83
  },
85
84
  minTxsPerBlock: {
86
85
  env: 'SEQ_MIN_TX_PER_BLOCK',
@@ -99,12 +98,19 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
99
98
  maxL2BlockGas: {
100
99
  env: 'SEQ_MAX_L2_BLOCK_GAS',
101
100
  description: 'The maximum L2 block gas.',
102
- ...numberConfigHelper(DefaultSequencerConfig.maxL2BlockGas),
101
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
103
102
  },
104
103
  maxDABlockGas: {
105
104
  env: 'SEQ_MAX_DA_BLOCK_GAS',
106
105
  description: 'The maximum DA block gas.',
107
- ...numberConfigHelper(DefaultSequencerConfig.maxDABlockGas),
106
+ parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
107
+ },
108
+ perBlockAllocationMultiplier: {
109
+ env: 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER',
110
+ description:
111
+ 'Per-block gas budget multiplier for both L2 and DA gas. Budget per block is (checkpointLimit / maxBlocks) * multiplier.' +
112
+ ' Values greater than one allow early blocks to use more than their even share, relying on checkpoint-level capping for later blocks.',
113
+ ...numberConfigHelper(DefaultSequencerConfig.perBlockAllocationMultiplier),
108
114
  },
109
115
  coinbase: {
110
116
  env: 'COINBASE',
@@ -124,11 +130,6 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
124
130
  env: 'ACVM_BINARY_PATH',
125
131
  description: 'The path to the ACVM binary',
126
132
  },
127
- maxBlockSizeInBytes: {
128
- env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES',
129
- description: 'Max block size',
130
- ...numberConfigHelper(DefaultSequencerConfig.maxBlockSizeInBytes),
131
- },
132
133
  enforceTimeTable: {
133
134
  env: 'SEQ_ENFORCE_TIME_TABLE',
134
135
  description: 'Whether to enforce the time table when building blocks',
@@ -186,6 +187,14 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
186
187
  description: 'Inject a fake attestation (for testing only)',
187
188
  ...booleanConfigHelper(DefaultSequencerConfig.injectFakeAttestation),
188
189
  },
190
+ injectHighSValueAttestation: {
191
+ description: 'Inject a malleable attestation with a high-s value (for testing only)',
192
+ ...booleanConfigHelper(DefaultSequencerConfig.injectHighSValueAttestation),
193
+ },
194
+ injectUnrecoverableSignatureAttestation: {
195
+ description: 'Inject an attestation with an unrecoverable signature (for testing only)',
196
+ ...booleanConfigHelper(DefaultSequencerConfig.injectUnrecoverableSignatureAttestation),
197
+ },
189
198
  fishermanMode: {
190
199
  env: 'FISHERMAN_MODE',
191
200
  description:
@@ -214,7 +223,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
214
223
  description: 'Percent probability (0 - 100) of sequencer skipping checkpoint publishing (testing only)',
215
224
  ...numberConfigHelper(DefaultSequencerConfig.skipPublishingCheckpointsPercent),
216
225
  },
217
- ...pickConfigMappings(p2pConfigMappings, ['txPublicSetupAllowList']),
226
+ ...pickConfigMappings(p2pConfigMappings, ['txPublicSetupAllowListExtend']),
218
227
  };
219
228
 
220
229
  export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
@@ -48,8 +48,6 @@ export type PublisherConfig = L1TxUtilsConfig &
48
48
  fishermanMode?: boolean;
49
49
  /** Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only) */
50
50
  publisherForwarderAddress?: EthAddress;
51
- /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
52
- l1TxFailedStore?: string;
53
51
  };
54
52
 
55
53
  export type ProverPublisherConfig = L1TxUtilsConfig &
@@ -64,8 +62,6 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
64
62
  fishermanMode?: boolean;
65
63
  sequencerPublisherAllowInvalidStates?: boolean;
66
64
  sequencerPublisherForwarderAddress?: EthAddress;
67
- /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
68
- l1TxFailedStore?: string;
69
65
  };
70
66
 
71
67
  export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
@@ -81,7 +77,6 @@ export function getPublisherConfigFromSequencerConfig(config: SequencerPublisher
81
77
  ...config,
82
78
  publisherAllowInvalidStates: config.sequencerPublisherAllowInvalidStates,
83
79
  publisherForwarderAddress: config.sequencerPublisherForwarderAddress,
84
- l1TxFailedStore: config.l1TxFailedStore,
85
80
  };
86
81
  }
87
82
 
@@ -138,10 +133,6 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
138
133
  description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
139
134
  parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
140
135
  },
141
- l1TxFailedStore: {
142
- env: 'L1_TX_FAILED_STORE',
143
- description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
144
- },
145
136
  };
146
137
 
147
138
  export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
@@ -3,6 +3,3 @@ export { SequencerPublisherFactory } from './sequencer-publisher-factory.js';
3
3
 
4
4
  // Used for tests
5
5
  export { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
6
-
7
- // Failed L1 tx store (optional, for test networks)
8
- export { type FailedL1Tx, type FailedL1TxUri, type L1TxFailedStore } from './l1_tx_failed_store/index.js';
@@ -45,19 +45,9 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
45
45
  import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
46
46
  import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
47
47
 
48
- import {
49
- type Hex,
50
- type StateOverride,
51
- type TransactionReceipt,
52
- type TypedDataDefinition,
53
- encodeFunctionData,
54
- keccak256,
55
- multicall3Abi,
56
- toHex,
57
- } from 'viem';
48
+ import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
58
49
 
59
50
  import type { SequencerPublisherConfig } from './config.js';
60
- import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
61
51
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
62
52
 
63
53
  /** Arguments to the process method of the rollup contract */
@@ -119,7 +109,6 @@ export class SequencerPublisher {
119
109
  private interrupted = false;
120
110
  private metrics: SequencerPublisherMetrics;
121
111
  public epochCache: EpochCache;
122
- private failedTxStore?: Promise<L1TxFailedStore | undefined>;
123
112
 
124
113
  protected governanceLog = createLogger('sequencer:publisher:governance');
125
114
  protected slashingLog = createLogger('sequencer:publisher:slashing');
@@ -160,7 +149,7 @@ export class SequencerPublisher {
160
149
  protected requests: RequestWithExpiry[] = [];
161
150
 
162
151
  constructor(
163
- private config: Pick<SequencerPublisherConfig, 'fishermanMode' | 'l1TxFailedStore'> &
152
+ private config: Pick<SequencerPublisherConfig, 'fishermanMode'> &
164
153
  Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
165
154
  deps: {
166
155
  telemetry?: TelemetryClient;
@@ -216,31 +205,6 @@ export class SequencerPublisher {
216
205
  this.rollupContract,
217
206
  createLogger('sequencer:publisher:price-oracle'),
218
207
  );
219
-
220
- // Initialize failed L1 tx store (optional, for test networks)
221
- this.failedTxStore = createL1TxFailedStore(config.l1TxFailedStore, this.log);
222
- }
223
-
224
- /**
225
- * Backs up a failed L1 transaction to the configured store for debugging.
226
- * Does nothing if no store is configured.
227
- */
228
- private backupFailedTx(failedTx: Omit<FailedL1Tx, 'timestamp'>): void {
229
- if (!this.failedTxStore) {
230
- return;
231
- }
232
-
233
- const tx: FailedL1Tx = {
234
- ...failedTx,
235
- timestamp: Date.now(),
236
- };
237
-
238
- // Fire and forget - don't block on backup
239
- void this.failedTxStore
240
- .then(store => store?.saveFailedTx(tx))
241
- .catch(err => {
242
- this.log.warn(`Failed to backup failed L1 tx to store`, err);
243
- });
244
208
  }
245
209
 
246
210
  public getRollupContract(): RollupContract {
@@ -422,21 +386,6 @@ export class SequencerPublisher {
422
386
  validRequests.sort((a, b) => compareActions(a.action, b.action));
423
387
 
424
388
  try {
425
- // Capture context for failed tx backup before sending
426
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
427
- const multicallData = encodeFunctionData({
428
- abi: multicall3Abi,
429
- functionName: 'aggregate3',
430
- args: [
431
- validRequests.map(r => ({
432
- target: r.request.to!,
433
- callData: r.request.data!,
434
- allowFailure: true,
435
- })),
436
- ],
437
- });
438
- const blobDataHex = blobConfig?.blobs?.map(b => toHex(b)) as Hex[] | undefined;
439
-
440
389
  this.log.debug('Forwarding transactions', {
441
390
  validRequests: validRequests.map(request => request.action),
442
391
  txConfig,
@@ -449,12 +398,7 @@ export class SequencerPublisher {
449
398
  this.rollupContract.address,
450
399
  this.log,
451
400
  );
452
- const txContext = { multicallData, blobData: blobDataHex, l1BlockNumber };
453
- const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(
454
- validRequests,
455
- result,
456
- txContext,
457
- );
401
+ const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result);
458
402
  return { result, expiredActions, sentActions: validActions, successfulActions, failedActions };
459
403
  } catch (err) {
460
404
  const viemError = formatViemError(err);
@@ -474,25 +418,11 @@ export class SequencerPublisher {
474
418
 
475
419
  private callbackBundledTransactions(
476
420
  requests: RequestWithExpiry[],
477
- result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError | undefined,
478
- txContext: { multicallData: Hex; blobData?: Hex[]; l1BlockNumber: bigint },
421
+ result?: { receipt: TransactionReceipt } | FormattedViemError,
479
422
  ) {
480
423
  const actionsListStr = requests.map(r => r.action).join(', ');
481
424
  if (result instanceof FormattedViemError) {
482
425
  this.log.error(`Failed to publish bundled transactions (${actionsListStr})`, result);
483
- this.backupFailedTx({
484
- id: keccak256(txContext.multicallData),
485
- failureType: 'send-error',
486
- request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
487
- blobData: txContext.blobData,
488
- l1BlockNumber: txContext.l1BlockNumber.toString(),
489
- error: { message: result.message, name: result.name },
490
- context: {
491
- actions: requests.map(r => r.action),
492
- requests: requests.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
493
- sender: this.getSenderAddress().toString(),
494
- },
495
- });
496
426
  return { failedActions: requests.map(r => r.action) };
497
427
  } else {
498
428
  this.log.verbose(`Published bundled transactions (${actionsListStr})`, { result, requests });
@@ -505,30 +435,6 @@ export class SequencerPublisher {
505
435
  failedActions.push(request.action);
506
436
  }
507
437
  }
508
- // Single backup for the whole reverted tx
509
- if (failedActions.length > 0 && result?.receipt?.status === 'reverted') {
510
- this.backupFailedTx({
511
- id: result.receipt.transactionHash,
512
- failureType: 'revert',
513
- request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
514
- blobData: txContext.blobData,
515
- l1BlockNumber: result.receipt.blockNumber.toString(),
516
- receipt: {
517
- transactionHash: result.receipt.transactionHash,
518
- blockNumber: result.receipt.blockNumber.toString(),
519
- gasUsed: (result.receipt.gasUsed ?? 0n).toString(),
520
- status: 'reverted',
521
- },
522
- error: { message: result.errorMsg ?? 'Transaction reverted' },
523
- context: {
524
- actions: failedActions,
525
- requests: requests
526
- .filter(r => failedActions.includes(r.action))
527
- .map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
528
- sender: this.getSenderAddress().toString(),
529
- },
530
- });
531
- }
532
438
  return { successfulActions, failedActions };
533
439
  }
534
440
  }
@@ -640,8 +546,6 @@ export class SequencerPublisher {
640
546
  const request = this.buildInvalidateCheckpointRequest(validationResult);
641
547
  this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
642
548
 
643
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
644
-
645
549
  try {
646
550
  const { gasUsed } = await this.l1TxUtils.simulate(
647
551
  request,
@@ -693,18 +597,6 @@ export class SequencerPublisher {
693
597
 
694
598
  // Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
695
599
  this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
696
- this.backupFailedTx({
697
- id: keccak256(request.data!),
698
- failureType: 'simulation',
699
- request: { to: request.to!, data: request.data!, value: request.value?.toString() },
700
- l1BlockNumber: l1BlockNumber.toString(),
701
- error: { message: viemError.message, name: viemError.name },
702
- context: {
703
- actions: [`invalidate-${reason}`],
704
- checkpointNumber,
705
- sender: this.getSenderAddress().toString(),
706
- },
707
- });
708
600
  throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
709
601
  }
710
602
  }
@@ -852,26 +744,11 @@ export class SequencerPublisher {
852
744
  lastValidL2Slot: slotNumber,
853
745
  });
854
746
 
855
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
856
-
857
747
  try {
858
748
  await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
859
749
  this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
860
750
  } catch (err) {
861
- const viemError = formatViemError(err);
862
- this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError);
863
- this.backupFailedTx({
864
- id: keccak256(request.data!),
865
- failureType: 'simulation',
866
- request: { to: request.to!, data: request.data!, value: request.value?.toString() },
867
- l1BlockNumber: l1BlockNumber.toString(),
868
- error: { message: viemError.message, name: viemError.name },
869
- context: {
870
- actions: [action],
871
- slot: slotNumber,
872
- sender: this.getSenderAddress().toString(),
873
- },
874
- });
751
+ this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, err);
875
752
  // Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
876
753
  }
877
754
 
@@ -1167,8 +1044,6 @@ export class SequencerPublisher {
1167
1044
 
1168
1045
  this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
1169
1046
 
1170
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
1171
-
1172
1047
  let gasUsed: bigint;
1173
1048
  const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
1174
1049
  try {
@@ -1178,19 +1053,6 @@ export class SequencerPublisher {
1178
1053
  const viemError = formatViemError(err, simulateAbi);
1179
1054
  this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
1180
1055
 
1181
- this.backupFailedTx({
1182
- id: keccak256(request.data!),
1183
- failureType: 'simulation',
1184
- request: { to: request.to!, data: request.data!, value: request.value?.toString() },
1185
- l1BlockNumber: l1BlockNumber.toString(),
1186
- error: { message: viemError.message, name: viemError.name },
1187
- context: {
1188
- actions: [action],
1189
- slot: slotNumber,
1190
- sender: this.getSenderAddress().toString(),
1191
- },
1192
- });
1193
-
1194
1056
  return false;
1195
1057
  }
1196
1058
 
@@ -1274,27 +1136,9 @@ export class SequencerPublisher {
1274
1136
  kzg,
1275
1137
  },
1276
1138
  )
1277
- .catch(async err => {
1278
- const viemError = formatViemError(err);
1279
- this.log.error(`Failed to validate blobs`, viemError.message, { metaMessages: viemError.metaMessages });
1280
- const validateBlobsData = encodeFunctionData({
1281
- abi: RollupAbi,
1282
- functionName: 'validateBlobs',
1283
- args: [blobInput],
1284
- });
1285
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
1286
- this.backupFailedTx({
1287
- id: keccak256(validateBlobsData),
1288
- failureType: 'simulation',
1289
- request: { to: this.rollupContract.address as Hex, data: validateBlobsData },
1290
- blobData: encodedData.blobs.map(b => toHex(b.data)) as Hex[],
1291
- l1BlockNumber: l1BlockNumber.toString(),
1292
- error: { message: viemError.message, name: viemError.name },
1293
- context: {
1294
- actions: ['validate-blobs'],
1295
- sender: this.getSenderAddress().toString(),
1296
- },
1297
- });
1139
+ .catch(err => {
1140
+ const { message, metaMessages } = formatViemError(err);
1141
+ this.log.error(`Failed to validate blobs`, message, { metaMessages });
1298
1142
  throw new Error('Failed to validate blobs');
1299
1143
  });
1300
1144
  }
@@ -1373,8 +1217,6 @@ export class SequencerPublisher {
1373
1217
  });
1374
1218
  }
1375
1219
 
1376
- const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
1377
-
1378
1220
  const simulationResult = await this.l1TxUtils
1379
1221
  .simulate(
1380
1222
  {
@@ -1408,18 +1250,6 @@ export class SequencerPublisher {
1408
1250
  };
1409
1251
  }
1410
1252
  this.log.error(`Failed to simulate propose tx`, viemError);
1411
- this.backupFailedTx({
1412
- id: keccak256(rollupData),
1413
- failureType: 'simulation',
1414
- request: { to: this.rollupContract.address, data: rollupData },
1415
- l1BlockNumber: l1BlockNumber.toString(),
1416
- error: { message: viemError.message, name: viemError.name },
1417
- context: {
1418
- actions: ['propose'],
1419
- slot: Number(args[0].header.slotNumber),
1420
- sender: this.getSenderAddress().toString(),
1421
- },
1422
- });
1423
1253
  throw err;
1424
1254
  });
1425
1255