@aztec/sequencer-client 0.0.1-commit.88e6f9396 → 0.0.1-commit.8c0b8ff

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 (70) hide show
  1. package/dest/client/sequencer-client.d.ts +5 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +6 -12
  4. package/dest/config.d.ts +3 -3
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +1 -2
  7. package/dest/global_variable_builder/global_builder.d.ts +13 -7
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +22 -22
  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 +1 -5
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +1 -6
  15. package/dest/publisher/index.d.ts +1 -2
  16. package/dest/publisher/index.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.d.ts +1 -1
  18. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  19. package/dest/publisher/sequencer-publisher-factory.js +0 -14
  20. package/dest/publisher/sequencer-publisher.d.ts +11 -16
  21. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  22. package/dest/publisher/sequencer-publisher.js +53 -289
  23. package/dest/sequencer/checkpoint_proposal_job.d.ts +4 -10
  24. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  25. package/dest/sequencer/checkpoint_proposal_job.js +75 -117
  26. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  27. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  28. package/dest/sequencer/checkpoint_voter.js +2 -5
  29. package/dest/sequencer/events.d.ts +1 -2
  30. package/dest/sequencer/events.d.ts.map +1 -1
  31. package/dest/sequencer/metrics.d.ts +1 -5
  32. package/dest/sequencer/metrics.d.ts.map +1 -1
  33. package/dest/sequencer/metrics.js +0 -11
  34. package/dest/sequencer/sequencer.d.ts +5 -4
  35. package/dest/sequencer/sequencer.d.ts.map +1 -1
  36. package/dest/sequencer/sequencer.js +50 -59
  37. package/dest/test/mock_checkpoint_builder.d.ts +1 -1
  38. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  39. package/dest/test/mock_checkpoint_builder.js +2 -0
  40. package/package.json +28 -27
  41. package/src/client/sequencer-client.ts +9 -11
  42. package/src/config.ts +0 -4
  43. package/src/global_variable_builder/global_builder.ts +22 -24
  44. package/src/global_variable_builder/index.ts +1 -1
  45. package/src/publisher/config.ts +0 -9
  46. package/src/publisher/index.ts +0 -3
  47. package/src/publisher/sequencer-publisher-factory.ts +0 -15
  48. package/src/publisher/sequencer-publisher.ts +51 -274
  49. package/src/sequencer/checkpoint_proposal_job.ts +66 -139
  50. package/src/sequencer/checkpoint_voter.ts +1 -12
  51. package/src/sequencer/events.ts +1 -1
  52. package/src/sequencer/metrics.ts +0 -14
  53. package/src/sequencer/sequencer.ts +58 -84
  54. package/src/test/mock_checkpoint_builder.ts +2 -0
  55. package/dest/publisher/l1_tx_failed_store/factory.d.ts +0 -11
  56. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +0 -1
  57. package/dest/publisher/l1_tx_failed_store/factory.js +0 -22
  58. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +0 -59
  59. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +0 -1
  60. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +0 -1
  61. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +0 -15
  62. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +0 -1
  63. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +0 -34
  64. package/dest/publisher/l1_tx_failed_store/index.d.ts +0 -4
  65. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +0 -1
  66. package/dest/publisher/l1_tx_failed_store/index.js +0 -2
  67. package/src/publisher/l1_tx_failed_store/factory.ts +0 -32
  68. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +0 -55
  69. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +0 -46
  70. package/src/publisher/l1_tx_failed_store/index.ts +0 -3
@@ -143,6 +143,11 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
143
143
  this.log.info('Started sequencer');
144
144
  }
145
145
 
146
+ /** Triggers an immediate run of the sequencer, bypassing the polling interval. */
147
+ public trigger() {
148
+ return this.runningPromise?.trigger();
149
+ }
150
+
146
151
  /** Stops the sequencer from building blocks and moves to STOPPED state. */
147
152
  public async stop(): Promise<void> {
148
153
  this.log.info(`Stopping sequencer`);
@@ -192,18 +197,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
192
197
  @trackSpan('Sequencer.work')
193
198
  protected async work() {
194
199
  this.setState(SequencerState.SYNCHRONIZING, undefined);
195
- const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
196
- const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
200
+ const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
197
201
 
198
202
  // Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
199
- const checkpointProposalJob = await this.prepareCheckpointProposal(
200
- slot,
201
- targetSlot,
202
- epoch,
203
- targetEpoch,
204
- ts,
205
- nowSeconds,
206
- );
203
+ const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
207
204
  if (!checkpointProposalJob) {
208
205
  return;
209
206
  }
@@ -216,13 +213,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
216
213
  this.lastCheckpointProposed = checkpoint;
217
214
  }
218
215
 
219
- // Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
216
+ // Log fee strategy comparison if on fisherman
220
217
  if (
221
218
  this.config.fishermanMode &&
222
- (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)
219
+ (this.lastEpochForStrategyComparison === undefined || epoch > this.lastEpochForStrategyComparison)
223
220
  ) {
224
- this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
225
- this.lastEpochForStrategyComparison = targetEpoch;
221
+ this.logStrategyComparison(epoch, checkpointProposalJob.getPublisher());
222
+ this.lastEpochForStrategyComparison = epoch;
226
223
  }
227
224
 
228
225
  return checkpoint;
@@ -235,48 +232,43 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
235
232
  */
236
233
  @trackSpan('Sequencer.prepareCheckpointProposal')
237
234
  private async prepareCheckpointProposal(
238
- slot: SlotNumber,
239
- targetSlot: SlotNumber,
240
235
  epoch: EpochNumber,
241
- targetEpoch: EpochNumber,
236
+ slot: SlotNumber,
242
237
  ts: bigint,
243
- nowSeconds: bigint,
238
+ now: bigint,
244
239
  ): Promise<CheckpointProposalJob | undefined> {
245
- // Check we have not already processed this target slot (cheapest check)
240
+ // Check we have not already processed this slot (cheapest check)
246
241
  // We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
247
242
  // running against actual time (eg when we use sandbox-style automining)
248
243
  if (
249
244
  this.lastSlotForCheckpointProposalJob &&
250
- this.lastSlotForCheckpointProposalJob >= targetSlot &&
245
+ this.lastSlotForCheckpointProposalJob >= slot &&
251
246
  this.config.enforceTimeTable
252
247
  ) {
253
- this.log.trace(`Target slot ${targetSlot} has already been processed`);
248
+ this.log.trace(`Slot ${slot} has already been processed`);
254
249
  return undefined;
255
250
  }
256
251
 
257
- // But if we have already proposed for this slot, then we definitely have to skip it, automining or not
258
- if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
259
- this.log.trace(
260
- `Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`,
261
- );
252
+ // But if we have already proposed for this slot, the we definitely have to skip it, automining or not
253
+ if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= slot) {
254
+ this.log.trace(`Slot ${slot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
262
255
  return undefined;
263
256
  }
264
257
 
265
258
  // Check all components are synced to latest as seen by the archiver (queries all subsystems)
266
259
  const syncedTo = await this.checkSync({ ts, slot });
267
260
  if (!syncedTo) {
268
- await this.tryVoteWhenSyncFails({ slot, targetSlot, ts });
261
+ await this.tryVoteWhenSyncFails({ slot, ts });
269
262
  return undefined;
270
263
  }
271
264
 
272
- // If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
265
+ // If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
273
266
  // Still perform governance/slashing voting (as proposer) once per slot.
274
- // When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
275
- const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
267
+ const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
276
268
 
277
269
  if (isEscapeHatchOpen) {
278
270
  this.setState(SequencerState.PROPOSER_CHECK, slot);
279
- const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
271
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
280
272
  if (canPropose) {
281
273
  await this.tryVoteWhenEscapeHatchOpen({ slot, proposer });
282
274
  } else {
@@ -293,18 +285,17 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
293
285
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
294
286
 
295
287
  const logCtx = {
296
- nowSeconds,
288
+ now,
297
289
  syncedToL2Slot: syncedTo.syncedL2Slot,
298
290
  slot,
299
- targetSlot,
300
291
  slotTs: ts,
301
292
  checkpointNumber,
302
293
  isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex'),
303
294
  };
304
295
 
305
- // Check that we are a proposer for the target slot.
296
+ // Check that we are a proposer for the next slot
306
297
  this.setState(SequencerState.PROPOSER_CHECK, slot);
307
- const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
298
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
308
299
 
309
300
  // If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
310
301
  if (!canPropose) {
@@ -312,10 +303,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
312
303
  return undefined;
313
304
  }
314
305
 
315
- // Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
316
- if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
306
+ // Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
307
+ if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= slot) {
317
308
  this.log.warn(
318
- `Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`,
309
+ `Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`,
319
310
  { ...logCtx, block: syncedTo.blockData.header.toInspect() },
320
311
  );
321
312
  this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
@@ -339,11 +330,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
339
330
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
340
331
  const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
341
332
 
342
- // Check with the rollup contract if we can indeed propose at the target slot. This check should not fail
333
+ // Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
343
334
  // if all the previous checks are good, but we do it just in case.
344
- const canProposeCheck = await publisher.canProposeAt(syncedTo.archive, proposer ?? EthAddress.ZERO, {
345
- ...invalidateCheckpoint,
346
- });
335
+ const canProposeCheck = await publisher.canProposeAt(
336
+ syncedTo.archive,
337
+ proposer ?? EthAddress.ZERO,
338
+ invalidateCheckpoint,
339
+ );
347
340
 
348
341
  if (canProposeCheck === undefined) {
349
342
  this.log.warn(
@@ -355,10 +348,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
355
348
  return undefined;
356
349
  }
357
350
 
358
- if (canProposeCheck.slot !== targetSlot) {
351
+ if (canProposeCheck.slot !== slot) {
359
352
  this.log.warn(
360
- `Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${targetSlot} but got ${canProposeCheck.slot}.`,
361
- { ...logCtx, rollup: canProposeCheck, expectedSlot: targetSlot },
353
+ `Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${slot} but got ${canProposeCheck.slot}.`,
354
+ { ...logCtx, rollup: canProposeCheck, expectedSlot: slot },
362
355
  );
363
356
  this.emit('proposer-rollup-check-failed', { reason: 'Slot mismatch', slot });
364
357
  this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
@@ -375,24 +368,14 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
375
368
  return undefined;
376
369
  }
377
370
 
378
- this.lastSlotForCheckpointProposalJob = targetSlot;
379
-
380
- await this.p2pClient.prepareForSlot(targetSlot);
381
- this.log.info(
382
- `Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`,
383
- {
384
- ...logCtx,
385
- proposer,
386
- pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled(),
387
- },
388
- );
371
+ this.lastSlotForCheckpointProposalJob = slot;
372
+ await this.p2pClient.prepareForSlot(slot);
373
+ this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, { ...logCtx, proposer });
389
374
 
390
375
  // Create and return the checkpoint proposal job
391
376
  return this.createCheckpointProposalJob(
392
- slot,
393
- targetSlot,
394
377
  epoch,
395
- targetEpoch,
378
+ slot,
396
379
  checkpointNumber,
397
380
  syncedTo.blockNumber,
398
381
  proposer,
@@ -403,10 +386,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
403
386
  }
404
387
 
405
388
  protected createCheckpointProposalJob(
406
- slot: SlotNumber,
407
- targetSlot: SlotNumber,
408
389
  epoch: EpochNumber,
409
- targetEpoch: EpochNumber,
390
+ slot: SlotNumber,
410
391
  checkpointNumber: CheckpointNumber,
411
392
  syncedToBlockNumber: BlockNumber,
412
393
  proposer: EthAddress | undefined,
@@ -415,10 +396,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
415
396
  invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
416
397
  ): CheckpointProposalJob {
417
398
  return new CheckpointProposalJob(
418
- slot,
419
- targetSlot,
420
399
  epoch,
421
- targetEpoch,
400
+ slot,
422
401
  checkpointNumber,
423
402
  syncedToBlockNumber,
424
403
  proposer,
@@ -501,8 +480,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
501
480
  */
502
481
  protected async checkSync(args: { ts: bigint; slot: SlotNumber }): Promise<SequencerSyncCheckResult | undefined> {
503
482
  // Check that the archiver has fully synced the L2 slot before the one we want to propose in.
504
- // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
505
- // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
483
+ // The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
484
+ // See getSyncedL2SlotNumber for how missed L1 blocks are handled.
506
485
  const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
507
486
  const { slot } = args;
508
487
  if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
@@ -574,20 +553,20 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
574
553
  * Checks if we are the proposer for the next slot.
575
554
  * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
576
555
  */
577
- protected async checkCanPropose(targetSlot: SlotNumber): Promise<[boolean, EthAddress | undefined]> {
556
+ protected async checkCanPropose(slot: SlotNumber): Promise<[boolean, EthAddress | undefined]> {
578
557
  let proposer: EthAddress | undefined;
579
558
 
580
559
  try {
581
- proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
560
+ proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
582
561
  } catch (e) {
583
562
  if (e instanceof NoCommitteeError) {
584
- if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
585
- this.lastSlotForNoCommitteeWarning = targetSlot;
586
- this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
563
+ if (this.lastSlotForNoCommitteeWarning !== slot) {
564
+ this.lastSlotForNoCommitteeWarning = slot;
565
+ this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
587
566
  }
588
567
  return [false, undefined];
589
568
  }
590
- this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
569
+ this.log.error(`Error getting proposer for slot ${slot}`, e);
591
570
  return [false, undefined];
592
571
  }
593
572
 
@@ -604,15 +583,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
604
583
  const weAreProposer = validatorAddresses.some(addr => addr.equals(proposer));
605
584
 
606
585
  if (!weAreProposer) {
607
- this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
608
- targetSlot,
609
- validatorAddresses,
610
- proposer,
611
- });
586
+ this.log.debug(`Cannot propose at slot ${slot} since we are not a proposer`, { validatorAddresses, proposer });
612
587
  return [false, proposer];
613
588
  }
614
589
 
615
- this.log.debug(`We are the proposer for target slot ${targetSlot}`, { targetSlot, proposer });
616
590
  return [true, proposer];
617
591
  }
618
592
 
@@ -621,8 +595,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
621
595
  * This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
622
596
  */
623
597
  @trackSpan('Seqeuencer.tryVoteWhenSyncFails', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
624
- protected async tryVoteWhenSyncFails(args: { slot: SlotNumber; targetSlot: SlotNumber; ts: bigint }): Promise<void> {
625
- const { slot, targetSlot } = args;
598
+ protected async tryVoteWhenSyncFails(args: { slot: SlotNumber; ts: bigint }): Promise<void> {
599
+ const { slot } = args;
626
600
 
627
601
  // Prevent duplicate attempts in the same slot
628
602
  if (this.lastSlotForFallbackVote === slot) {
@@ -650,7 +624,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
650
624
  });
651
625
 
652
626
  // Check if we're a proposer or proposal is open
653
- const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
627
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
654
628
  if (!canPropose) {
655
629
  this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, { slot, proposer });
656
630
  return;
@@ -667,9 +641,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
667
641
  slot,
668
642
  });
669
643
 
670
- // Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
644
+ // Enqueue governance and slashing votes
671
645
  const voter = new CheckpointVoter(
672
- targetSlot,
646
+ slot,
673
647
  publisher,
674
648
  attestorAddress,
675
649
  this.validatorClient,
@@ -304,6 +304,8 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
304
304
  if (!this.checkpointBuilder) {
305
305
  // Auto-create a builder if none was set
306
306
  this.checkpointBuilder = new MockCheckpointBuilder(constants, checkpointNumber);
307
+ } else {
308
+ this.checkpointBuilder.resetCheckpointState();
307
309
  }
308
310
 
309
311
  return Promise.resolve(this.checkpointBuilder);
@@ -1,11 +0,0 @@
1
- import { type Logger } from '@aztec/foundation/log';
2
- import type { L1TxFailedStore } from './failed_tx_store.js';
3
- /**
4
- * Creates an L1TxFailedStore from a config string.
5
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
6
- * @param config - Config string (e.g., 'gs://bucket/path', 's3://bucket/path', 'file:///path'). If undefined, returns undefined.
7
- * @param logger - Optional logger.
8
- * @returns The store instance, or undefined if config is not provided.
9
- */
10
- export declare function createL1TxFailedStore(config: string | undefined, logger?: Logger): Promise<L1TxFailedStore | undefined>;
11
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3B1Ymxpc2hlci9sMV90eF9mYWlsZWRfc3RvcmUvZmFjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQWdCLE1BQU0sdUJBQXVCLENBQUM7QUFHbEUsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFHNUQ7Ozs7OztHQU1HO0FBQ0gsd0JBQXNCLHFCQUFxQixDQUN6QyxNQUFNLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDMUIsTUFBTSxHQUFFLE1BQXFELEdBQzVELE9BQU8sQ0FBQyxlQUFlLEdBQUcsU0FBUyxDQUFDLENBZXRDIn0=
@@ -1 +0,0 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/publisher/l1_tx_failed_store/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAGlE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,GAAE,MAAqD,GAC5D,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAetC"}
@@ -1,22 +0,0 @@
1
- import { createLogger } from '@aztec/foundation/log';
2
- import { createFileStore } from '@aztec/stdlib/file-store';
3
- import { FileStoreL1TxFailedStore } from './file_store_failed_tx_store.js';
4
- /**
5
- * Creates an L1TxFailedStore from a config string.
6
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
7
- * @param config - Config string (e.g., 'gs://bucket/path', 's3://bucket/path', 'file:///path'). If undefined, returns undefined.
8
- * @param logger - Optional logger.
9
- * @returns The store instance, or undefined if config is not provided.
10
- */ export async function createL1TxFailedStore(config, logger = createLogger('sequencer:l1-tx-failed-store')) {
11
- if (!config) {
12
- return undefined;
13
- }
14
- const fileStore = await createFileStore(config, logger);
15
- if (!fileStore) {
16
- throw new Error(`Failed to create file store from config: '${config}'. ` + `Supported formats: 'gs://bucket/path', 's3://bucket/path', 'file:///path'.`);
17
- }
18
- logger.info(`Created L1 tx failed store`, {
19
- config
20
- });
21
- return new FileStoreL1TxFailedStore(fileStore, logger);
22
- }
@@ -1,59 +0,0 @@
1
- import type { Hex } from 'viem';
2
- /** URI pointing to a stored failed L1 transaction. */
3
- export type FailedL1TxUri = string & {
4
- __brand: 'FailedL1TxUri';
5
- };
6
- /** A failed L1 transaction captured for debugging and replay. */
7
- export type FailedL1Tx = {
8
- /** Tx hash (for reverts) or keccak256(request.data) (for simulation/send failures). */
9
- id: Hex;
10
- /** Unix timestamp (ms) when failure occurred. */
11
- timestamp: number;
12
- /** Whether the failure was during simulation or after sending. */
13
- failureType: 'simulation' | 'revert' | 'send-error';
14
- /** The actual L1 transaction for replay (multicall-encoded for bundled txs). */
15
- request: {
16
- to: Hex;
17
- data: Hex;
18
- value?: string;
19
- };
20
- /** Raw blob data as hex for replay. */
21
- blobData?: Hex[];
22
- /** L1 block number at time of failure (simulation target or receipt block). */
23
- l1BlockNumber: string;
24
- /** Receipt info (present only for on-chain reverts). */
25
- receipt?: {
26
- transactionHash: Hex;
27
- blockNumber: string;
28
- gasUsed: string;
29
- status: 'reverted';
30
- };
31
- /** Error information. */
32
- error: {
33
- message: string;
34
- /** Decoded error name (e.g., 'Rollup__InvalidProposer'). */
35
- name?: string;
36
- };
37
- /** Context metadata. */
38
- context: {
39
- /** Actions involved (e.g., ['propose', 'governance-signal']). */
40
- actions: string[];
41
- /** Individual request data for each action (metadata, not used for replay). */
42
- requests?: Array<{
43
- action: string;
44
- to: Hex;
45
- data: Hex;
46
- }>;
47
- checkpointNumber?: number;
48
- slot?: number;
49
- sender: Hex;
50
- };
51
- };
52
- /** Store for failed L1 transactions for debugging purposes. */
53
- export interface L1TxFailedStore {
54
- /** Saves a failed transaction and returns its URI. */
55
- saveFailedTx(tx: FailedL1Tx): Promise<FailedL1TxUri>;
56
- /** Retrieves a failed transaction by its URI. */
57
- getFailedTx(uri: FailedL1TxUri): Promise<FailedL1Tx>;
58
- }
59
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpbGVkX3R4X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHVibGlzaGVyL2wxX3R4X2ZhaWxlZF9zdG9yZS9mYWlsZWRfdHhfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRWhDLHNEQUFzRDtBQUN0RCxNQUFNLE1BQU0sYUFBYSxHQUFHLE1BQU0sR0FBRztJQUFFLE9BQU8sRUFBRSxlQUFlLENBQUE7Q0FBRSxDQUFDO0FBRWxFLGlFQUFpRTtBQUNqRSxNQUFNLE1BQU0sVUFBVSxHQUFHO0lBQ3ZCLHVGQUF1RjtJQUN2RixFQUFFLEVBQUUsR0FBRyxDQUFDO0lBQ1IsaURBQWlEO0lBQ2pELFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsa0VBQWtFO0lBQ2xFLFdBQVcsRUFBRSxZQUFZLEdBQUcsUUFBUSxHQUFHLFlBQVksQ0FBQztJQUNwRCxnRkFBZ0Y7SUFDaEYsT0FBTyxFQUFFO1FBQ1AsRUFBRSxFQUFFLEdBQUcsQ0FBQztRQUNSLElBQUksRUFBRSxHQUFHLENBQUM7UUFDVixLQUFLLENBQUMsRUFBRSxNQUFNLENBQUM7S0FDaEIsQ0FBQztJQUNGLHVDQUF1QztJQUN2QyxRQUFRLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQztJQUNqQiwrRUFBK0U7SUFDL0UsYUFBYSxFQUFFLE1BQU0sQ0FBQztJQUN0Qix3REFBd0Q7SUFDeEQsT0FBTyxDQUFDLEVBQUU7UUFDUixlQUFlLEVBQUUsR0FBRyxDQUFDO1FBQ3JCLFdBQVcsRUFBRSxNQUFNLENBQUM7UUFDcEIsT0FBTyxFQUFFLE1BQU0sQ0FBQztRQUNoQixNQUFNLEVBQUUsVUFBVSxDQUFDO0tBQ3BCLENBQUM7SUFDRix5QkFBeUI7SUFDekIsS0FBSyxFQUFFO1FBQ0wsT0FBTyxFQUFFLE1BQU0sQ0FBQztRQUNoQiw0REFBNEQ7UUFDNUQsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFDO0tBQ2YsQ0FBQztJQUNGLHdCQUF3QjtJQUN4QixPQUFPLEVBQUU7UUFDUCxpRUFBaUU7UUFDakUsT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ2xCLCtFQUErRTtRQUMvRSxRQUFRLENBQUMsRUFBRSxLQUFLLENBQUM7WUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDO1lBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQztZQUFDLElBQUksRUFBRSxHQUFHLENBQUE7U0FBRSxDQUFDLENBQUM7UUFDekQsZ0JBQWdCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDMUIsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ2QsTUFBTSxFQUFFLEdBQUcsQ0FBQztLQUNiLENBQUM7Q0FDSCxDQUFDO0FBRUYsK0RBQStEO0FBQy9ELE1BQU0sV0FBVyxlQUFlO0lBQzlCLHNEQUFzRDtJQUN0RCxZQUFZLENBQUMsRUFBRSxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDckQsaURBQWlEO0lBQ2pELFdBQVcsQ0FBQyxHQUFHLEVBQUUsYUFBYSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztDQUN0RCJ9
@@ -1 +0,0 @@
1
- {"version":3,"file":"failed_tx_store.d.ts","sourceRoot":"","sources":["../../../src/publisher/l1_tx_failed_store/failed_tx_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEhC,sDAAsD;AACtD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC;AAElE,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG;IACvB,uFAAuF;IACvF,EAAE,EAAE,GAAG,CAAC;IACR,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,WAAW,EAAE,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;IACpD,gFAAgF;IAChF,OAAO,EAAE;QACP,EAAE,EAAE,GAAG,CAAC;QACR,IAAI,EAAE,GAAG,CAAC;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,uCAAuC;IACvC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,OAAO,CAAC,EAAE;QACR,eAAe,EAAE,GAAG,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,UAAU,CAAC;KACpB,CAAC;IACF,yBAAyB;IACzB,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,4DAA4D;QAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,wBAAwB;IACxB,OAAO,EAAE;QACP,iEAAiE;QACjE,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,+EAA+E;QAC/E,QAAQ,CAAC,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,GAAG,CAAC;YAAC,IAAI,EAAE,GAAG,CAAA;SAAE,CAAC,CAAC;QACzD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,GAAG,CAAC;KACb,CAAC;CACH,CAAC;AAEF,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,YAAY,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACrD,iDAAiD;IACjD,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACtD"}
@@ -1 +0,0 @@
1
- /** Store for failed L1 transactions for debugging purposes. */ export { };
@@ -1,15 +0,0 @@
1
- import { type Logger } from '@aztec/foundation/log';
2
- import type { FileStore } from '@aztec/stdlib/file-store';
3
- import type { FailedL1Tx, FailedL1TxUri, L1TxFailedStore } from './failed_tx_store.js';
4
- /**
5
- * L1TxFailedStore implementation using the FileStore abstraction.
6
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
7
- */
8
- export declare class FileStoreL1TxFailedStore implements L1TxFailedStore {
9
- private readonly fileStore;
10
- private readonly log;
11
- constructor(fileStore: FileStore, logger?: Logger);
12
- saveFailedTx(tx: FailedL1Tx): Promise<FailedL1TxUri>;
13
- getFailedTx(uri: FailedL1TxUri): Promise<FailedL1Tx>;
14
- }
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZV9zdG9yZV9mYWlsZWRfdHhfc3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wdWJsaXNoZXIvbDFfdHhfZmFpbGVkX3N0b3JlL2ZpbGVfc3RvcmVfZmFpbGVkX3R4X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUUxRCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRXZGOzs7R0FHRztBQUNILHFCQUFhLHdCQUF5QixZQUFXLGVBQWU7SUFJNUQsT0FBTyxDQUFDLFFBQVEsQ0FBQyxTQUFTO0lBSDVCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFTO0lBRTdCLFlBQ21CLFNBQVMsRUFBRSxTQUFTLEVBQ3JDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFHaEI7SUFFWSxZQUFZLENBQUMsRUFBRSxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDLENBb0JoRTtJQUVZLFdBQVcsQ0FBQyxHQUFHLEVBQUUsYUFBYSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FHaEU7Q0FDRiJ9
@@ -1 +0,0 @@
1
- {"version":3,"file":"file_store_failed_tx_store.d.ts","sourceRoot":"","sources":["../../../src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvF;;;GAGG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAI5D,OAAO,CAAC,QAAQ,CAAC,SAAS;IAH5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,YACmB,SAAS,EAAE,SAAS,EACrC,MAAM,CAAC,EAAE,MAAM,EAGhB;IAEY,YAAY,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAoBhE;IAEY,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAGhE;CACF"}
@@ -1,34 +0,0 @@
1
- import { createLogger } from '@aztec/foundation/log';
2
- /**
3
- * L1TxFailedStore implementation using the FileStore abstraction.
4
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
5
- */ export class FileStoreL1TxFailedStore {
6
- fileStore;
7
- log;
8
- constructor(fileStore, logger){
9
- this.fileStore = fileStore;
10
- this.log = logger ?? createLogger('sequencer:l1-tx-failed-store');
11
- }
12
- async saveFailedTx(tx) {
13
- const prefix = tx.receipt ? 'tx' : 'data';
14
- const path = `${tx.failureType}/${prefix}-${tx.id}.json`;
15
- const json = JSON.stringify(tx, null, 2);
16
- const uri = await this.fileStore.save(path, Buffer.from(json), {
17
- metadata: {
18
- 'content-type': 'application/json',
19
- actions: tx.context.actions.join(','),
20
- 'failure-type': tx.failureType
21
- }
22
- });
23
- this.log.info(`Saved failed L1 tx to ${uri}`, {
24
- id: tx.id,
25
- failureType: tx.failureType,
26
- actions: tx.context.actions.join(',')
27
- });
28
- return uri;
29
- }
30
- async getFailedTx(uri) {
31
- const data = await this.fileStore.read(uri);
32
- return JSON.parse(data.toString());
33
- }
34
- }
@@ -1,4 +0,0 @@
1
- export { type FailedL1Tx, type FailedL1TxUri, type L1TxFailedStore } from './failed_tx_store.js';
2
- export { createL1TxFailedStore } from './factory.js';
3
- export { FileStoreL1TxFailedStore } from './file_store_failed_tx_store.js';
4
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wdWJsaXNoZXIvbDFfdHhfZmFpbGVkX3N0b3JlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLFVBQVUsRUFBRSxLQUFLLGFBQWEsRUFBRSxLQUFLLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pHLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQyJ9
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/publisher/l1_tx_failed_store/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -1,2 +0,0 @@
1
- export { createL1TxFailedStore } from './factory.js';
2
- export { FileStoreL1TxFailedStore } from './file_store_failed_tx_store.js';
@@ -1,32 +0,0 @@
1
- import { type Logger, createLogger } from '@aztec/foundation/log';
2
- import { createFileStore } from '@aztec/stdlib/file-store';
3
-
4
- import type { L1TxFailedStore } from './failed_tx_store.js';
5
- import { FileStoreL1TxFailedStore } from './file_store_failed_tx_store.js';
6
-
7
- /**
8
- * Creates an L1TxFailedStore from a config string.
9
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
10
- * @param config - Config string (e.g., 'gs://bucket/path', 's3://bucket/path', 'file:///path'). If undefined, returns undefined.
11
- * @param logger - Optional logger.
12
- * @returns The store instance, or undefined if config is not provided.
13
- */
14
- export async function createL1TxFailedStore(
15
- config: string | undefined,
16
- logger: Logger = createLogger('sequencer:l1-tx-failed-store'),
17
- ): Promise<L1TxFailedStore | undefined> {
18
- if (!config) {
19
- return undefined;
20
- }
21
-
22
- const fileStore = await createFileStore(config, logger);
23
- if (!fileStore) {
24
- throw new Error(
25
- `Failed to create file store from config: '${config}'. ` +
26
- `Supported formats: 'gs://bucket/path', 's3://bucket/path', 'file:///path'.`,
27
- );
28
- }
29
-
30
- logger.info(`Created L1 tx failed store`, { config });
31
- return new FileStoreL1TxFailedStore(fileStore, logger);
32
- }
@@ -1,55 +0,0 @@
1
- import type { Hex } from 'viem';
2
-
3
- /** URI pointing to a stored failed L1 transaction. */
4
- export type FailedL1TxUri = string & { __brand: 'FailedL1TxUri' };
5
-
6
- /** A failed L1 transaction captured for debugging and replay. */
7
- export type FailedL1Tx = {
8
- /** Tx hash (for reverts) or keccak256(request.data) (for simulation/send failures). */
9
- id: Hex;
10
- /** Unix timestamp (ms) when failure occurred. */
11
- timestamp: number;
12
- /** Whether the failure was during simulation or after sending. */
13
- failureType: 'simulation' | 'revert' | 'send-error';
14
- /** The actual L1 transaction for replay (multicall-encoded for bundled txs). */
15
- request: {
16
- to: Hex;
17
- data: Hex;
18
- value?: string; // bigint as string
19
- };
20
- /** Raw blob data as hex for replay. */
21
- blobData?: Hex[];
22
- /** L1 block number at time of failure (simulation target or receipt block). */
23
- l1BlockNumber: string; // bigint as string
24
- /** Receipt info (present only for on-chain reverts). */
25
- receipt?: {
26
- transactionHash: Hex;
27
- blockNumber: string; // bigint as string
28
- gasUsed: string; // bigint as string
29
- status: 'reverted';
30
- };
31
- /** Error information. */
32
- error: {
33
- message: string;
34
- /** Decoded error name (e.g., 'Rollup__InvalidProposer'). */
35
- name?: string;
36
- };
37
- /** Context metadata. */
38
- context: {
39
- /** Actions involved (e.g., ['propose', 'governance-signal']). */
40
- actions: string[];
41
- /** Individual request data for each action (metadata, not used for replay). */
42
- requests?: Array<{ action: string; to: Hex; data: Hex }>;
43
- checkpointNumber?: number;
44
- slot?: number;
45
- sender: Hex;
46
- };
47
- };
48
-
49
- /** Store for failed L1 transactions for debugging purposes. */
50
- export interface L1TxFailedStore {
51
- /** Saves a failed transaction and returns its URI. */
52
- saveFailedTx(tx: FailedL1Tx): Promise<FailedL1TxUri>;
53
- /** Retrieves a failed transaction by its URI. */
54
- getFailedTx(uri: FailedL1TxUri): Promise<FailedL1Tx>;
55
- }
@@ -1,46 +0,0 @@
1
- import { type Logger, createLogger } from '@aztec/foundation/log';
2
- import type { FileStore } from '@aztec/stdlib/file-store';
3
-
4
- import type { FailedL1Tx, FailedL1TxUri, L1TxFailedStore } from './failed_tx_store.js';
5
-
6
- /**
7
- * L1TxFailedStore implementation using the FileStore abstraction.
8
- * Supports any backend that FileStore supports (GCS, S3, R2, local filesystem).
9
- */
10
- export class FileStoreL1TxFailedStore implements L1TxFailedStore {
11
- private readonly log: Logger;
12
-
13
- constructor(
14
- private readonly fileStore: FileStore,
15
- logger?: Logger,
16
- ) {
17
- this.log = logger ?? createLogger('sequencer:l1-tx-failed-store');
18
- }
19
-
20
- public async saveFailedTx(tx: FailedL1Tx): Promise<FailedL1TxUri> {
21
- const prefix = tx.receipt ? 'tx' : 'data';
22
- const path = `${tx.failureType}/${prefix}-${tx.id}.json`;
23
- const json = JSON.stringify(tx, null, 2);
24
-
25
- const uri = await this.fileStore.save(path, Buffer.from(json), {
26
- metadata: {
27
- 'content-type': 'application/json',
28
- actions: tx.context.actions.join(','),
29
- 'failure-type': tx.failureType,
30
- },
31
- });
32
-
33
- this.log.info(`Saved failed L1 tx to ${uri}`, {
34
- id: tx.id,
35
- failureType: tx.failureType,
36
- actions: tx.context.actions.join(','),
37
- });
38
-
39
- return uri as FailedL1TxUri;
40
- }
41
-
42
- public async getFailedTx(uri: FailedL1TxUri): Promise<FailedL1Tx> {
43
- const data = await this.fileStore.read(uri);
44
- return JSON.parse(data.toString()) as FailedL1Tx;
45
- }
46
- }
@@ -1,3 +0,0 @@
1
- export { type FailedL1Tx, type FailedL1TxUri, type L1TxFailedStore } from './failed_tx_store.js';
2
- export { createL1TxFailedStore } from './factory.js';
3
- export { FileStoreL1TxFailedStore } from './file_store_failed_tx_store.js';