@aztec/sequencer-client 0.0.1-commit.9ef841308 → 0.0.1-commit.a89ec08
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.
- package/dest/client/sequencer-client.d.ts +3 -2
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +7 -8
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +1 -2
- package/dest/publisher/config.d.ts +1 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +3 -23
- package/dest/publisher/index.d.ts +1 -2
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +2 -16
- package/dest/publisher/sequencer-publisher.d.ts +2 -12
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +11 -260
- package/dest/sequencer/checkpoint_proposal_job.d.ts +4 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +75 -117
- package/dest/sequencer/events.d.ts +1 -2
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +1 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +0 -11
- package/dest/sequencer/sequencer.d.ts +5 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +49 -58
- package/dest/test/mock_checkpoint_builder.d.ts +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +2 -0
- package/package.json +28 -27
- package/src/client/sequencer-client.ts +12 -8
- package/src/config.ts +0 -4
- package/src/publisher/config.ts +0 -41
- package/src/publisher/index.ts +0 -3
- package/src/publisher/sequencer-publisher-factory.ts +3 -18
- package/src/publisher/sequencer-publisher.ts +16 -237
- package/src/sequencer/checkpoint_proposal_job.ts +66 -139
- package/src/sequencer/events.ts +1 -1
- package/src/sequencer/metrics.ts +0 -14
- package/src/sequencer/sequencer.ts +57 -83
- package/src/test/mock_checkpoint_builder.ts +2 -0
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +0 -11
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/factory.js +0 -22
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +0 -59
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +0 -1
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +0 -15
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +0 -34
- package/dest/publisher/l1_tx_failed_store/index.d.ts +0 -4
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/index.js +0 -2
- package/src/publisher/l1_tx_failed_store/factory.ts +0 -32
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +0 -55
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +0 -46
- package/src/publisher/l1_tx_failed_store/index.ts +0 -3
|
@@ -143,11 +143,16 @@ 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`);
|
|
149
154
|
this.setState(SequencerState.STOPPING, undefined, { force: true });
|
|
150
|
-
|
|
155
|
+
this.publisherFactory.interruptAll();
|
|
151
156
|
await this.runningPromise?.stop();
|
|
152
157
|
this.setState(SequencerState.STOPPED, undefined, { force: true });
|
|
153
158
|
this.log.info('Stopped 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,
|
|
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
|
|
216
|
+
// Log fee strategy comparison if on fisherman
|
|
220
217
|
if (
|
|
221
218
|
this.config.fishermanMode &&
|
|
222
|
-
(this.lastEpochForStrategyComparison === undefined ||
|
|
219
|
+
(this.lastEpochForStrategyComparison === undefined || epoch > this.lastEpochForStrategyComparison)
|
|
223
220
|
) {
|
|
224
|
-
this.logStrategyComparison(
|
|
225
|
-
this.lastEpochForStrategyComparison =
|
|
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
|
-
|
|
236
|
+
slot: SlotNumber,
|
|
242
237
|
ts: bigint,
|
|
243
|
-
|
|
238
|
+
now: bigint,
|
|
244
239
|
): Promise<CheckpointProposalJob | undefined> {
|
|
245
|
-
// Check we have not already processed this
|
|
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 >=
|
|
245
|
+
this.lastSlotForCheckpointProposalJob >= slot &&
|
|
251
246
|
this.config.enforceTimeTable
|
|
252
247
|
) {
|
|
253
|
-
this.log.trace(`
|
|
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,
|
|
258
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
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,
|
|
261
|
+
await this.tryVoteWhenSyncFails({ slot, ts });
|
|
269
262
|
return undefined;
|
|
270
263
|
}
|
|
271
264
|
|
|
272
|
-
// If escape hatch is open for
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
316
|
-
if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >=
|
|
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
|
|
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
|
|
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(
|
|
345
|
-
|
|
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 !==
|
|
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 ${
|
|
361
|
-
{ ...logCtx, rollup: canProposeCheck, expectedSlot:
|
|
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 =
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
400
|
+
slot,
|
|
422
401
|
checkpointNumber,
|
|
423
402
|
syncedToBlockNumber,
|
|
424
403
|
proposer,
|
|
@@ -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(
|
|
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(
|
|
560
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
|
|
582
561
|
} catch (e) {
|
|
583
562
|
if (e instanceof NoCommitteeError) {
|
|
584
|
-
if (this.lastSlotForNoCommitteeWarning !==
|
|
585
|
-
this.lastSlotForNoCommitteeWarning =
|
|
586
|
-
this.log.warn(`Cannot propose at
|
|
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
|
|
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
|
|
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;
|
|
625
|
-
const { slot
|
|
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(
|
|
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
|
|
644
|
+
// Enqueue governance and slashing votes
|
|
671
645
|
const voter = new CheckpointVoter(
|
|
672
|
-
|
|
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,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
|
-
}
|