@aztec/sequencer-client 0.0.1-commit.f650c0a5c → 0.0.1-commit.f7ea82942
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 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +3 -0
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +8 -11
- package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
- package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_predictor.js +128 -0
- package/dest/global_variable_builder/fee_provider.d.ts +21 -0
- package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_provider.js +58 -0
- package/dest/global_variable_builder/global_builder.d.ts +4 -9
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +3 -41
- package/dest/global_variable_builder/index.d.ts +3 -1
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +2 -0
- package/dest/publisher/config.d.ts +1 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +2 -2
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +3 -4
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.d.ts +10 -28
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +24 -43
- package/dest/sequencer/chain_state_overrides.d.ts +25 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +39 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +24 -21
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +263 -120
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
- package/dest/sequencer/events.d.ts +6 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +7 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +27 -20
- package/dest/sequencer/sequencer.d.ts +5 -2
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +15 -11
- package/dest/sequencer/timetable.d.ts +11 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +42 -43
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +5 -0
- package/src/config.ts +8 -11
- package/src/global_variable_builder/README.md +44 -0
- package/src/global_variable_builder/fee_predictor.ts +172 -0
- package/src/global_variable_builder/fee_provider.ts +75 -0
- package/src/global_variable_builder/global_builder.ts +7 -54
- package/src/global_variable_builder/index.ts +2 -0
- package/src/publisher/config.ts +6 -4
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-publisher.ts +49 -83
- package/src/sequencer/README.md +9 -8
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +328 -162
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/events.ts +5 -0
- package/src/sequencer/metrics.ts +35 -25
- package/src/sequencer/sequencer.ts +22 -11
- package/src/sequencer/timetable.ts +50 -49
|
@@ -436,13 +436,12 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
|
436
436
|
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
437
437
|
}
|
|
438
438
|
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _initProto;
|
|
439
|
-
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
440
439
|
import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
|
|
441
440
|
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
442
441
|
import { flipSignature, generateRecoverableSignature, generateUnrecoverableSignature } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
443
|
-
import { Signature } from '@aztec/foundation/eth-signature';
|
|
444
442
|
import { filter } from '@aztec/foundation/iterator';
|
|
445
443
|
import { createLogger } from '@aztec/foundation/log';
|
|
444
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
446
445
|
import { sleep, sleepUntil } from '@aztec/foundation/sleep';
|
|
447
446
|
import { Timer } from '@aztec/foundation/timer';
|
|
448
447
|
import { isErrorClass, unfreeze } from '@aztec/foundation/types';
|
|
@@ -456,6 +455,7 @@ import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p';
|
|
|
456
455
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
457
456
|
import { Attributes, trackSpan } from '@aztec/telemetry-client';
|
|
458
457
|
import { DutyAlreadySignedError, SlashingProtectionError } from '@aztec/validator-ha-signer/errors';
|
|
458
|
+
import { buildPipelinedParentSimulationOverridesPlan, buildSubmissionSimulationOverridesPlan } from './chain_state_overrides.js';
|
|
459
459
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
460
460
|
import { SequencerInterruptedError } from './errors.js';
|
|
461
461
|
import { SequencerState } from './utils.js';
|
|
@@ -497,6 +497,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
497
497
|
epochCache;
|
|
498
498
|
dateProvider;
|
|
499
499
|
metrics;
|
|
500
|
+
checkpointMetrics;
|
|
500
501
|
eventEmitter;
|
|
501
502
|
setStateFn;
|
|
502
503
|
tracer;
|
|
@@ -547,9 +548,9 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
547
548
|
}
|
|
548
549
|
log;
|
|
549
550
|
/** Tracks the fire-and-forget L1 submission promise so it can be awaited during shutdown. */ pendingL1Submission;
|
|
550
|
-
/**
|
|
551
|
+
/** Pipelined parent chain state used while building and later submitting this checkpoint. */ pipelinedParentSimulationOverridesPlan;
|
|
551
552
|
constructor(slotNow, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
|
|
552
|
-
proposer, publisher, attestorAddress, invalidateCheckpoint, validatorClient, globalsBuilder, p2pClient, worldState, l1ToL2MessageSource, l2BlockSource, checkpointsBuilder, blockSink, l1Constants, config, timetable, slasherClient, epochCache, dateProvider, metrics, eventEmitter, setStateFn, tracer, bindings, proposedCheckpointData){
|
|
553
|
+
proposer, publisher, attestorAddress, invalidateCheckpoint, validatorClient, globalsBuilder, p2pClient, worldState, l1ToL2MessageSource, l2BlockSource, checkpointsBuilder, blockSink, l1Constants, config, timetable, slasherClient, epochCache, dateProvider, metrics, checkpointMetrics, eventEmitter, setStateFn, tracer, bindings, proposedCheckpointData){
|
|
553
554
|
this.slotNow = slotNow;
|
|
554
555
|
this.targetSlot = targetSlot;
|
|
555
556
|
this.targetEpoch = targetEpoch;
|
|
@@ -574,6 +575,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
574
575
|
this.epochCache = epochCache;
|
|
575
576
|
this.dateProvider = dateProvider;
|
|
576
577
|
this.metrics = metrics;
|
|
578
|
+
this.checkpointMetrics = checkpointMetrics;
|
|
577
579
|
this.eventEmitter = eventEmitter;
|
|
578
580
|
this.setStateFn = setStateFn;
|
|
579
581
|
this.tracer = tracer;
|
|
@@ -590,73 +592,102 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
590
592
|
}
|
|
591
593
|
/**
|
|
592
594
|
* Executes the checkpoint proposal job.
|
|
593
|
-
* Builds blocks,
|
|
594
|
-
*
|
|
595
|
+
* Builds blocks, assembles checkpoint, and broadcasts the proposal (blocking).
|
|
596
|
+
* Attestation collection, signing, and L1 submission are backgrounded so the
|
|
597
|
+
* work loop can return to IDLE immediately for consecutive slot proposals.
|
|
595
598
|
* Returns the built checkpoint if successful, undefined otherwise.
|
|
596
599
|
*/ async execute() {
|
|
597
600
|
// Enqueue governance and slashing votes (returns promises that will be awaited later)
|
|
598
601
|
// In fisherman mode, we simulate slashing but don't actually publish to L1
|
|
599
602
|
// These are constant for the whole slot, so we only enqueue them once
|
|
600
603
|
const votesPromises = new CheckpointVoter(this.targetSlot, this.publisher, this.attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log).enqueueVotes();
|
|
601
|
-
// Build
|
|
602
|
-
//
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
604
|
+
// Build blocks, assemble checkpoint, and broadcast proposal (BLOCKING).
|
|
605
|
+
// Returns after broadcast — attestation collection is deferred.
|
|
606
|
+
const broadcast = await this.proposeCheckpoint();
|
|
607
|
+
if (!broadcast) {
|
|
608
|
+
await Promise.all(votesPromises);
|
|
609
|
+
// Still submit votes even without a checkpoint
|
|
610
|
+
if (!this.config.fishermanMode) {
|
|
611
|
+
this.pendingL1Submission = this.publisher.sendRequestsAt(this.dateProvider.nowAsDate()).then(()=>{});
|
|
612
|
+
}
|
|
613
|
+
return undefined;
|
|
609
614
|
}
|
|
615
|
+
const { checkpoint } = broadcast;
|
|
616
|
+
this.metrics.recordCheckpointProposalSuccess();
|
|
610
617
|
// Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
|
|
611
618
|
if (this.config.fishermanMode) {
|
|
612
619
|
await this.handleCheckpointEndAsFisherman(checkpoint);
|
|
613
|
-
return;
|
|
620
|
+
return checkpoint;
|
|
614
621
|
}
|
|
615
|
-
//
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
+
// Background the attestation → signing → L1 pipeline so the work loop is unblocked
|
|
623
|
+
this.pendingL1Submission = this.waitForAttestationsAndEnqueueSubmissionAsync(broadcast, votesPromises);
|
|
624
|
+
// Return the built checkpoint immediately — the work loop is now unblocked
|
|
625
|
+
return checkpoint;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Background pipeline: collects attestations, signs them, enqueues the checkpoint, and submits to L1.
|
|
629
|
+
* Runs as a fire-and-forget task stored in `pendingL1Submission` so the work loop is unblocked.
|
|
630
|
+
*/ async waitForAttestationsAndEnqueueSubmissionAsync(broadcast, votesPromises) {
|
|
631
|
+
const { checkpoint } = broadcast;
|
|
632
|
+
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
633
|
+
try {
|
|
634
|
+
// Wait for all votes actions, enqueued at the beginning, to resolve
|
|
635
|
+
await Promise.all(votesPromises);
|
|
636
|
+
// Try to collect attestations from the committee
|
|
637
|
+
const signedAttestations = await this.getSignedCommitteeAttestations(broadcast);
|
|
638
|
+
// If pipelining, wait for the previous checkpoint to land on L1 before submitting,
|
|
639
|
+
// so we can check it matches the proposed checkpoint we used as parent, and has valid attestations.
|
|
640
|
+
if (signedAttestations && (!isPipelining || await this.waitForValidParentCheckpointOnL1())) {
|
|
641
|
+
await this.enqueueCheckpointForSubmission({
|
|
642
|
+
checkpoint,
|
|
643
|
+
...signedAttestations
|
|
644
|
+
});
|
|
622
645
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
646
|
+
// If we failed to collect attestations, at least check if we need to issue an invalidation
|
|
647
|
+
// Note that if we are not pipelining, we enqueued the invalidation at the beginning
|
|
648
|
+
if (!signedAttestations && isPipelining && await this.waitForSyncedL2SlotNumber(this.slotNow)) {
|
|
649
|
+
const validationStatus = await this.l2BlockSource.getPendingChainValidationStatus();
|
|
650
|
+
if (!validationStatus.valid) {
|
|
651
|
+
this.log.warn(`Checkpoint ${validationStatus.checkpoint.checkpointNumber} has invalid attestations, enqueuing invalidation in spite of attestation collection failure`, {
|
|
652
|
+
checkpoint: validationStatus.checkpoint,
|
|
653
|
+
reason: validationStatus.reason
|
|
654
|
+
});
|
|
655
|
+
await this.enqueueInvalidation(validationStatus);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Send whatever was enqueued: votes + (propose | invalidation | nothing).
|
|
659
|
+
// Compute the earliest time to submit: pipeline slot start when pipelining, now otherwise.
|
|
660
|
+
const submitAfter = isPipelining ? new Date(Number(getTimestampForSlot(this.targetSlot, this.l1Constants)) * 1000) : new Date(this.dateProvider.now());
|
|
661
|
+
const l1Response = await this.publisher.sendRequestsAt(submitAfter);
|
|
630
662
|
const proposedAction = l1Response?.successfulActions.find((a)=>a === 'propose');
|
|
631
663
|
if (proposedAction) {
|
|
632
664
|
this.eventEmitter.emit('checkpoint-published', {
|
|
633
665
|
checkpoint: this.checkpointNumber,
|
|
634
666
|
slot: this.targetSlot
|
|
635
667
|
});
|
|
636
|
-
const coinbase = checkpoint
|
|
668
|
+
const coinbase = checkpoint.header.coinbase;
|
|
637
669
|
await this.metrics.incFilledSlot(this.publisher.getSenderAddress().toString(), coinbase);
|
|
638
|
-
} else
|
|
670
|
+
} else {
|
|
639
671
|
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
640
672
|
...l1Response,
|
|
641
673
|
slot: this.targetSlot
|
|
642
674
|
});
|
|
643
|
-
if (
|
|
675
|
+
if (isPipelining) {
|
|
644
676
|
this.metrics.recordPipelineDiscard();
|
|
645
677
|
}
|
|
646
678
|
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
651
|
-
slot: this.targetSlot
|
|
652
|
-
});
|
|
653
|
-
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
654
|
-
this.metrics.recordPipelineDiscard();
|
|
655
|
-
}
|
|
679
|
+
} catch (err) {
|
|
680
|
+
if (err instanceof SequencerInterruptedError) {
|
|
681
|
+
return;
|
|
656
682
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
683
|
+
this.log.error(`Background attestation/L1 pipeline failed for slot ${this.targetSlot}`, err);
|
|
684
|
+
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
685
|
+
slot: this.targetSlot
|
|
686
|
+
});
|
|
687
|
+
if (isPipelining) {
|
|
688
|
+
this.metrics.recordPipelineDiscard();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
660
691
|
}
|
|
661
692
|
/** Enqueues the checkpoint for L1 submission. Called after pipeline sleep in execute(). */ async enqueueCheckpointForSubmission(result) {
|
|
662
693
|
const { checkpoint, attestations, attestationsSignature } = result;
|
|
@@ -672,12 +703,135 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
672
703
|
return;
|
|
673
704
|
}
|
|
674
705
|
}
|
|
706
|
+
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
707
|
+
const submissionSimulationOverridesPlan = buildSubmissionSimulationOverridesPlan({
|
|
708
|
+
pipelinedParentPlan: this.pipelinedParentSimulationOverridesPlan,
|
|
709
|
+
invalidateToPendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
710
|
+
lastArchiveRoot: checkpoint.header.lastArchiveRoot,
|
|
711
|
+
pipeliningEnabled: isPipelining
|
|
712
|
+
});
|
|
675
713
|
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
676
714
|
txTimeoutAt,
|
|
677
|
-
|
|
678
|
-
|
|
715
|
+
...submissionSimulationOverridesPlan ? {
|
|
716
|
+
simulationOverridesPlan: submissionSimulationOverridesPlan
|
|
717
|
+
} : {}
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Wait until the archiver syncs past the given L2 slot number.
|
|
722
|
+
* The deadline is the end of `this.targetSlot`, beyond which any pipelined work would miss its
|
|
723
|
+
* L1 submission window and is no longer useful.
|
|
724
|
+
*/ async waitForSyncedL2SlotNumber(waitForSlot) {
|
|
725
|
+
const targetSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
|
|
726
|
+
const targetSlotEndMs = (targetSlotStart + this.l1Constants.slotDuration) * 1000;
|
|
727
|
+
const syncDelayTolerance = this.l1Constants.ethereumSlotDuration * 2 * 1000;
|
|
728
|
+
const timeoutSeconds = Math.max(0.1, (targetSlotEndMs + syncDelayTolerance - this.dateProvider.now()) / 1000);
|
|
729
|
+
try {
|
|
730
|
+
return await retryUntil(async ()=>{
|
|
731
|
+
const syncedSlot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
732
|
+
return syncedSlot !== undefined && syncedSlot >= waitForSlot;
|
|
733
|
+
}, `archiver sync past slot ${waitForSlot}`, timeoutSeconds, 0.2);
|
|
734
|
+
} catch {
|
|
735
|
+
this.log.warn(`Archiver did not sync L1 past slot ${waitForSlot} before slot ${this.targetSlot} expired, discarding pipelined work`, {
|
|
736
|
+
checkpointNumber: this.checkpointNumber
|
|
737
|
+
});
|
|
738
|
+
this.emitPipelinedCheckpointDiscarded('archiver-sync-timeout');
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Waits for the parent checkpoint to land on L1 before submitting a pipelined checkpoint.
|
|
744
|
+
* Polls until the archiver has synced L1 past the parent's slot, then verifies:
|
|
745
|
+
* - If we built on a proposed parent: it must have landed on L1 with matching hash and valid attestations.
|
|
746
|
+
* - If we built without a proposed parent: no new checkpoint must have appeared for that slot.
|
|
747
|
+
* If the parent has invalid attestations, enqueues an invalidation. Returns whether to proceed with the proposal.
|
|
748
|
+
*/ async waitForValidParentCheckpointOnL1() {
|
|
749
|
+
const parentCheckpointNumber = CheckpointNumber(this.checkpointNumber - 1);
|
|
750
|
+
// Wait until archiver has synced L1 past the parent's slot (slotNow)
|
|
751
|
+
if (!await this.waitForSyncedL2SlotNumber(this.slotNow)) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
const tips = await this.l2BlockSource.getL2Tips();
|
|
755
|
+
const checkpointedNumber = tips.checkpointed.checkpoint.number;
|
|
756
|
+
// We built on top of a proposed checkpoint. Verify it landed on L1 as expected.
|
|
757
|
+
if (this.proposedCheckpointData) {
|
|
758
|
+
// After syncing from L1 we see the chain tip has invalid attestations. This means the parent checkpoint was posted
|
|
759
|
+
// with invalid attestations, or it built on top of something with invalid attestations and didnt invalidate them.
|
|
760
|
+
// Either way, we thought our parent would be valid, so we have to throw away our work. But at least we'll try and
|
|
761
|
+
// invalidate on L1 so we clean up the chain for the next proposer. And we'll slash them, but that's handled elsewhere.
|
|
762
|
+
const validationStatus = await this.l2BlockSource.getPendingChainValidationStatus();
|
|
763
|
+
if (!validationStatus.valid) {
|
|
764
|
+
this.log.warn(`Parent checkpoint ${parentCheckpointNumber} has invalid attestations, discarding pipelined work`, {
|
|
765
|
+
checkpointNumber: this.checkpointNumber,
|
|
766
|
+
reason: validationStatus.reason
|
|
767
|
+
});
|
|
768
|
+
this.emitPipelinedCheckpointDiscarded('parent-invalid-attestations');
|
|
769
|
+
await this.enqueueInvalidation(validationStatus);
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
// The pending chain is valid. But did the parent checkpoint land on L1 at all?
|
|
773
|
+
if (checkpointedNumber < parentCheckpointNumber) {
|
|
774
|
+
this.log.warn(`Parent checkpoint ${parentCheckpointNumber} did not land on L1, discarding pipelined work`, {
|
|
775
|
+
checkpointNumber: this.checkpointNumber,
|
|
776
|
+
checkpointedNumber
|
|
777
|
+
});
|
|
778
|
+
this.emitPipelinedCheckpointDiscarded('parent-not-on-l1');
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
// It landed. But is it the one we were expecting?
|
|
782
|
+
const expectedHash = this.proposedCheckpointData.header.hash().toString();
|
|
783
|
+
if (tips.checkpointed.checkpoint.hash !== expectedHash) {
|
|
784
|
+
this.log.warn(`Parent checkpoint ${parentCheckpointNumber} hash mismatch on L1, discarding pipelined work`, {
|
|
785
|
+
checkpointNumber: this.checkpointNumber,
|
|
786
|
+
expectedHash,
|
|
787
|
+
actualHash: tips.checkpointed.checkpoint.hash
|
|
788
|
+
});
|
|
789
|
+
this.emitPipelinedCheckpointDiscarded('parent-hash-mismatch');
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
return true;
|
|
793
|
+
} else {
|
|
794
|
+
// We didn't see a proposed checkpoint at build time, so we built on checkpointed parent from two slots ago.
|
|
795
|
+
// But if a new checkpoint for the previous slot appeared on L1 in the meantime, our checkpoint assumed the wrong parent,
|
|
796
|
+
// so we have to discard our work. This can happen if we're somehow cut off from p2p and fail to see the checkpoint
|
|
797
|
+
// proposal for the previous slot.
|
|
798
|
+
if (checkpointedNumber > parentCheckpointNumber) {
|
|
799
|
+
this.log.warn(`Unexpected checkpoint ${checkpointedNumber} landed on L1 after we built on top of parent ${parentCheckpointNumber}, discarding pipelined work`, {
|
|
800
|
+
checkpointNumber: this.checkpointNumber,
|
|
801
|
+
checkpointedNumber
|
|
802
|
+
});
|
|
803
|
+
this.emitPipelinedCheckpointDiscarded('unexpected-parent-appeared');
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
return true;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/** Emits the pipelined-checkpoint-discarded event and records the metric. */ emitPipelinedCheckpointDiscarded(reason) {
|
|
810
|
+
this.metrics.recordPipelineParentCheckpointMismatch(reason);
|
|
811
|
+
this.eventEmitter.emit('pipelined-checkpoint-discarded', {
|
|
812
|
+
slot: this.targetSlot,
|
|
813
|
+
checkpointNumber: this.checkpointNumber,
|
|
814
|
+
reason
|
|
679
815
|
});
|
|
680
816
|
}
|
|
817
|
+
/** Simulates and enqueues an invalidation request for the invalid parent checkpoint. */ async enqueueInvalidation(validationStatus) {
|
|
818
|
+
if (this.config.skipInvalidateBlockAsProposer) {
|
|
819
|
+
this.log.warn(`Skipping checkpoint invalidation as proposer due to test configuration`);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const invalidateRequest = await this.publisher.simulateInvalidateCheckpoint(validationStatus);
|
|
823
|
+
if (invalidateRequest) {
|
|
824
|
+
const submissionSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
|
|
825
|
+
const txTimeoutAt = new Date((submissionSlotStart + this.l1Constants.slotDuration) * 1000);
|
|
826
|
+
this.publisher.enqueueInvalidateCheckpoint(invalidateRequest, {
|
|
827
|
+
txTimeoutAt
|
|
828
|
+
});
|
|
829
|
+
} else {
|
|
830
|
+
this.log.info(`Invalidation simulation returned undefined, checkpoint may have been removed already`, {
|
|
831
|
+
checkpointNumber: this.checkpointNumber
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
681
835
|
async proposeCheckpoint() {
|
|
682
836
|
try {
|
|
683
837
|
const env = {
|
|
@@ -686,6 +840,15 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
686
840
|
hasError: false
|
|
687
841
|
};
|
|
688
842
|
try {
|
|
843
|
+
const now = this.dateProvider.now();
|
|
844
|
+
if (this.epochCache.isProposerPipeliningEnabled() && this.proposedCheckpointData) {
|
|
845
|
+
// Measure against the wall-clock slot whose build window we are currently using.
|
|
846
|
+
// In pipelining mode `targetSlot` is intentionally one slot ahead, which makes the
|
|
847
|
+
// target-slot boundary a full slot away from the actual build start time.
|
|
848
|
+
const slotBoundaryMs = Number(getTimestampForSlot(this.slotNow, this.l1Constants)) * 1000;
|
|
849
|
+
this.checkpointMetrics.recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(now - slotBoundaryMs);
|
|
850
|
+
}
|
|
851
|
+
this.checkpointMetrics.startCheckpointTiming(now);
|
|
689
852
|
// Get operator configured coinbase and fee recipient for this attestor
|
|
690
853
|
const coinbase = this.validatorClient.getCoinbaseForAttestor(this.attestorAddress);
|
|
691
854
|
const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(this.attestorAddress);
|
|
@@ -707,15 +870,13 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
707
870
|
// When pipelining, force the proposed checkpoint number and fee header to our parent so the
|
|
708
871
|
// fee computation sees the same chain tip that L1 will see once the previous pipelined checkpoint lands.
|
|
709
872
|
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
forceProposedFeeHeader: this.computedForceProposedFeeHeader
|
|
718
|
-
});
|
|
873
|
+
this.pipelinedParentSimulationOverridesPlan = isPipelining ? await buildPipelinedParentSimulationOverridesPlan({
|
|
874
|
+
checkpointNumber: this.checkpointNumber,
|
|
875
|
+
proposedCheckpointData: this.proposedCheckpointData,
|
|
876
|
+
rollup: this.publisher.rollupContract,
|
|
877
|
+
log: this.log
|
|
878
|
+
}) : undefined;
|
|
879
|
+
const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.targetSlot, this.pipelinedParentSimulationOverridesPlan);
|
|
719
880
|
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
720
881
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
721
882
|
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -793,8 +954,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
793
954
|
return undefined;
|
|
794
955
|
}
|
|
795
956
|
// Record checkpoint-level build metrics
|
|
796
|
-
this.
|
|
797
|
-
//
|
|
957
|
+
this.checkpointMetrics.recordCheckpointBuild(checkpointBuildTimer.ms(), blocksInCheckpoint.length, checkpoint.getStats().txCount, Number(checkpoint.header.totalManaUsed.toBigInt()));
|
|
958
|
+
// In fisherman mode, return the checkpoint without broadcasting or collecting attestations
|
|
798
959
|
if (this.config.fishermanMode) {
|
|
799
960
|
this.log.info(`Built checkpoint for slot ${this.targetSlot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
|
|
800
961
|
slot: this.targetSlot,
|
|
@@ -802,38 +963,23 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
802
963
|
blocksBuilt: blocksInCheckpoint.length
|
|
803
964
|
});
|
|
804
965
|
this.metrics.recordCheckpointSuccess();
|
|
966
|
+
// Return a broadcast result with a dummy proposal — fisherman mode skips attestation collection
|
|
805
967
|
return {
|
|
806
968
|
checkpoint,
|
|
807
|
-
|
|
808
|
-
|
|
969
|
+
proposal: undefined,
|
|
970
|
+
blockProposedAt: this.dateProvider.now()
|
|
809
971
|
};
|
|
810
972
|
}
|
|
811
973
|
// Create the checkpoint proposal and broadcast it
|
|
812
|
-
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier, blockPendingBroadcast, this.proposer, checkpointProposalOptions);
|
|
974
|
+
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, this.checkpointNumber, feeAssetPriceModifier, blockPendingBroadcast, this.proposer, checkpointProposalOptions);
|
|
813
975
|
const blockProposedAt = this.dateProvider.now();
|
|
814
976
|
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
815
|
-
this.
|
|
816
|
-
|
|
817
|
-
const blockAttestedAt = this.dateProvider.now();
|
|
818
|
-
this.metrics.recordCheckpointAttestationDelay(blockAttestedAt - blockProposedAt);
|
|
819
|
-
// Proposer must sign over the attestations before pushing them to L1
|
|
820
|
-
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
821
|
-
let attestationsSignature;
|
|
822
|
-
try {
|
|
823
|
-
attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.targetSlot, this.checkpointNumber);
|
|
824
|
-
} catch (err) {
|
|
825
|
-
// We shouldn't really get here since we yield to another HA node
|
|
826
|
-
// as soon as we see these errors when creating block or checkpoint proposals.
|
|
827
|
-
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
828
|
-
return undefined;
|
|
829
|
-
}
|
|
830
|
-
throw err;
|
|
831
|
-
}
|
|
832
|
-
// Return the result for the caller to enqueue after the pipeline sleep
|
|
977
|
+
this.checkpointMetrics.noteCheckpointBroadcast(this.dateProvider.now());
|
|
978
|
+
// Return immediately after broadcast — attestation collection happens in the background
|
|
833
979
|
return {
|
|
834
980
|
checkpoint,
|
|
835
|
-
|
|
836
|
-
|
|
981
|
+
proposal,
|
|
982
|
+
blockProposedAt
|
|
837
983
|
};
|
|
838
984
|
} catch (e) {
|
|
839
985
|
env.error = e;
|
|
@@ -907,6 +1053,10 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
907
1053
|
break;
|
|
908
1054
|
}
|
|
909
1055
|
const { block, usedTxs } = buildResult;
|
|
1056
|
+
this.checkpointMetrics.noteCheckpointBlockBuilt(this.dateProvider.now(), {
|
|
1057
|
+
isFirstBlock: blocksBuilt === 0,
|
|
1058
|
+
isLastBlock: timingInfo.isLastBlock
|
|
1059
|
+
});
|
|
910
1060
|
blocksInCheckpoint.push(block);
|
|
911
1061
|
usedTxs.forEach((tx)=>txHashesAlreadyIncluded.add(tx.txHash.toString()));
|
|
912
1062
|
// Sign the block proposal. This will throw if HA signing fails.
|
|
@@ -945,7 +1095,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
945
1095
|
this.log.info(`Skipping block proposal for block ${block.number} in fisherman mode`);
|
|
946
1096
|
return Promise.resolve(undefined);
|
|
947
1097
|
}
|
|
948
|
-
return this.validatorClient.createBlockProposal(block.header, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
1098
|
+
return this.validatorClient.createBlockProposal(block.header, this.checkpointNumber, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
949
1099
|
}
|
|
950
1100
|
/** Sleeps until it is time to produce the next block in the slot */ async waitUntilNextSubslot(nextSubslotStart) {
|
|
951
1101
|
this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.targetSlot);
|
|
@@ -1048,7 +1198,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1048
1198
|
slot: this.targetSlot,
|
|
1049
1199
|
buildSlot: this.slotNow
|
|
1050
1200
|
});
|
|
1051
|
-
this.metrics.recordBuiltBlock(blockBuildDuration, block.header.totalManaUsed.toNumberUnsafe());
|
|
1201
|
+
this.metrics.recordBuiltBlock(blockBuildDuration, block.header.totalManaUsed.toNumberUnsafe(), this.targetSlot);
|
|
1052
1202
|
return {
|
|
1053
1203
|
block,
|
|
1054
1204
|
usedTxs
|
|
@@ -1123,6 +1273,30 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1123
1273
|
minTxs
|
|
1124
1274
|
};
|
|
1125
1275
|
}
|
|
1276
|
+
async getSignedCommitteeAttestations(broadcast) {
|
|
1277
|
+
const { proposal, blockProposedAt } = broadcast;
|
|
1278
|
+
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.targetSlot);
|
|
1279
|
+
const attestations = await this.waitForAttestations(proposal);
|
|
1280
|
+
if (!attestations) {
|
|
1281
|
+
return undefined;
|
|
1282
|
+
}
|
|
1283
|
+
this.checkpointMetrics.recordCheckpointAttestationDelay(this.dateProvider.now() - blockProposedAt);
|
|
1284
|
+
// Proposer must sign over the attestations before pushing them to L1
|
|
1285
|
+
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
1286
|
+
try {
|
|
1287
|
+
const attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.targetSlot, this.checkpointNumber);
|
|
1288
|
+
return {
|
|
1289
|
+
attestations,
|
|
1290
|
+
attestationsSignature
|
|
1291
|
+
};
|
|
1292
|
+
} catch (err) {
|
|
1293
|
+
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
this.log.error(`Error signing attestations for checkpoint proposal at slot ${proposal.slotNumber}`, err);
|
|
1297
|
+
return undefined;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1126
1300
|
/**
|
|
1127
1301
|
* Waits for enough attestations to be collected via p2p.
|
|
1128
1302
|
* This is run after all blocks for the checkpoint have been built.
|
|
@@ -1146,16 +1320,16 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1146
1320
|
const numberOfRequiredAttestations = computeQuorum(committee.length);
|
|
1147
1321
|
if (this.config.skipCollectingAttestations) {
|
|
1148
1322
|
this.log.warn('Skipping attestation collection as per config (attesting with own keys only)');
|
|
1149
|
-
const attestations = await this.validatorClient?.collectOwnAttestations(proposal);
|
|
1323
|
+
const attestations = await this.validatorClient?.collectOwnAttestations(proposal, this.checkpointNumber);
|
|
1150
1324
|
return new CommitteeAttestationsAndSigners(orderAttestations(attestations ?? [], committee));
|
|
1151
1325
|
}
|
|
1152
|
-
const attestationTimeAllowed = this.config.enforceTimeTable ? this.timetable.
|
|
1326
|
+
const attestationTimeAllowed = this.config.enforceTimeTable ? this.timetable.getCheckpointAttestationDeadline() : this.l1Constants.slotDuration;
|
|
1153
1327
|
const attestationDeadline = new Date((this.getSlotStartBuildTimestamp() + attestationTimeAllowed) * 1000);
|
|
1154
1328
|
this.metrics.recordRequiredAttestations(numberOfRequiredAttestations, attestationTimeAllowed);
|
|
1155
1329
|
const collectAttestationsTimer = new Timer();
|
|
1156
1330
|
let collectedAttestationsCount = 0;
|
|
1157
1331
|
try {
|
|
1158
|
-
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
|
|
1332
|
+
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline, this.checkpointNumber);
|
|
1159
1333
|
collectedAttestationsCount = attestations.length;
|
|
1160
1334
|
// Trim attestations to minimum required to save L1 calldata gas
|
|
1161
1335
|
const localAddresses = this.validatorClient.getValidatorAddresses();
|
|
@@ -1173,8 +1347,11 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1173
1347
|
} catch (err) {
|
|
1174
1348
|
if (err && err instanceof AttestationTimeoutError) {
|
|
1175
1349
|
collectedAttestationsCount = err.collectedCount;
|
|
1350
|
+
this.log.error(`Timeout while waiting for attestations for checkpoint proposal at slot ${proposal.slotNumber} (collected ${collectedAttestationsCount}/${numberOfRequiredAttestations})`, err);
|
|
1351
|
+
} else {
|
|
1352
|
+
this.log.error(`Error collecting attestations for checkpoint proposal at slot ${proposal.slotNumber}`, err);
|
|
1176
1353
|
}
|
|
1177
|
-
|
|
1354
|
+
return undefined;
|
|
1178
1355
|
} finally{
|
|
1179
1356
|
this.metrics.recordCollectedAttestations(collectedAttestationsCount, collectAttestationsTimer.ms());
|
|
1180
1357
|
}
|
|
@@ -1305,40 +1482,6 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1305
1482
|
}
|
|
1306
1483
|
return false;
|
|
1307
1484
|
}
|
|
1308
|
-
/**
|
|
1309
|
-
* In times of congestion we need to simulate using the correct fee header override for the previous block
|
|
1310
|
-
* We calculate the correct fee header values.
|
|
1311
|
-
*
|
|
1312
|
-
* If we are in block 1, or the checkpoint we are querying does not exist, we return undefined. However
|
|
1313
|
-
* If we are pipelining - where this function is called, the grandparentCheckpointNumber should always exist
|
|
1314
|
-
* @param parentCheckpointNumber
|
|
1315
|
-
* @returns
|
|
1316
|
-
*/ async computeForceProposedFeeHeader(parentCheckpointNumber) {
|
|
1317
|
-
if (!this.proposedCheckpointData) {
|
|
1318
|
-
return undefined;
|
|
1319
|
-
}
|
|
1320
|
-
const rollup = this.publisher.rollupContract;
|
|
1321
|
-
const grandparentCheckpointNumber = CheckpointNumber(this.checkpointNumber - 2);
|
|
1322
|
-
try {
|
|
1323
|
-
const [grandparentCheckpoint, manaTarget] = await Promise.all([
|
|
1324
|
-
rollup.getCheckpoint(grandparentCheckpointNumber),
|
|
1325
|
-
rollup.getManaTarget()
|
|
1326
|
-
]);
|
|
1327
|
-
if (!grandparentCheckpoint || !grandparentCheckpoint.feeHeader) {
|
|
1328
|
-
this.log.error(`Grandparent checkpoint or its feeHeader is undefined for checkpointNumber=${grandparentCheckpointNumber.toString()}`);
|
|
1329
|
-
return undefined;
|
|
1330
|
-
} else {
|
|
1331
|
-
const parentFeeHeader = RollupContract.computeChildFeeHeader(grandparentCheckpoint.feeHeader, this.proposedCheckpointData.totalManaUsed, this.proposedCheckpointData.feeAssetPriceModifier, manaTarget);
|
|
1332
|
-
return {
|
|
1333
|
-
checkpointNumber: parentCheckpointNumber,
|
|
1334
|
-
feeHeader: parentFeeHeader
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
} catch (err) {
|
|
1338
|
-
this.log.error(`Failed to fetch grandparent checkpoint or mana target for checkpointNumber=${grandparentCheckpointNumber.toString()}: ${err}`);
|
|
1339
|
-
return undefined;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
1485
|
/** Waits until a specific time within the current slot */ async waitUntilTimeInSlot(targetSecondsIntoSlot) {
|
|
1343
1486
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
1344
1487
|
const targetTimestamp = slotStartTimestamp + targetSecondsIntoSlot;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
2
|
+
/**
|
|
3
|
+
* Per-job recording surface used by {@link CheckpointProposalJob}.
|
|
4
|
+
*
|
|
5
|
+
* Ownership split:
|
|
6
|
+
* - {@link CheckpointProposalJobMetrics} owns the OpenTelemetry meter and instruments and should live for the
|
|
7
|
+
* lifetime of the sequencer process.
|
|
8
|
+
* - A recorder owns only mutable timing state for one checkpoint proposal job so overlapping jobs cannot
|
|
9
|
+
* overwrite each other's timing markers.
|
|
10
|
+
*/
|
|
11
|
+
export interface CheckpointProposalJobMetricsRecorder {
|
|
12
|
+
recordCheckpointAttestationDelay(durationMs: number): void;
|
|
13
|
+
recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number): void;
|
|
14
|
+
recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(offsetMs: number): void;
|
|
15
|
+
startCheckpointTiming(nowMs: number): void;
|
|
16
|
+
noteCheckpointBlockBuilt(nowMs: number, opts: {
|
|
17
|
+
isFirstBlock: boolean;
|
|
18
|
+
isLastBlock: boolean;
|
|
19
|
+
}): void;
|
|
20
|
+
noteCheckpointBroadcast(nowMs: number): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Long-lived owner of checkpoint proposal job telemetry instruments.
|
|
24
|
+
*
|
|
25
|
+
* The sequencer should construct this once and reuse it across jobs. Each call to {@link createRecorder} returns a
|
|
26
|
+
* lightweight recorder with isolated mutable state for a single checkpoint proposal job.
|
|
27
|
+
*/
|
|
28
|
+
export declare class CheckpointProposalJobMetrics {
|
|
29
|
+
private readonly meter;
|
|
30
|
+
private readonly instruments;
|
|
31
|
+
constructor(client: TelemetryClient, name?: string);
|
|
32
|
+
createRecorder(): CheckpointProposalJobMetricsRecorder;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF9wcm9wb3NhbF9qb2JfbWV0cmljcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9jaGVja3BvaW50X3Byb3Bvc2FsX2pvYl9tZXRyaWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFNTCxLQUFLLGVBQWUsRUFDckIsTUFBTSx5QkFBeUIsQ0FBQztBQWFqQzs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sV0FBVyxvQ0FBb0M7SUFDbkQsZ0NBQWdDLENBQUMsVUFBVSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDM0QscUJBQXFCLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDeEcseURBQXlELENBQUMsUUFBUSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDbEYscUJBQXFCLENBQUMsS0FBSyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDM0Msd0JBQXdCLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7UUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDO1FBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQTtLQUFFLEdBQUcsSUFBSSxDQUFDO0lBQ3JHLHVCQUF1QixDQUFDLEtBQUssRUFBRSxNQUFNLEdBQUcsSUFBSSxDQUFDO0NBQzlDO0FBMEREOzs7OztHQUtHO0FBQ0gscUJBQWEsNEJBQTRCO0lBQ3ZDLE9BQU8sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFRO0lBQzlCLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFtQztJQUUvRCxZQUFZLE1BQU0sRUFBRSxlQUFlLEVBQUUsSUFBSSxTQUEwQixFQWtCbEU7SUFFTSxjQUFjLElBQUksb0NBQW9DLENBRTVEO0NBQ0YifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint_proposal_job_metrics.d.ts","sourceRoot":"","sources":["../../src/sequencer/checkpoint_proposal_job_metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAajC;;;;;;;;GAQG;AACH,MAAM,WAAW,oCAAoC;IACnD,gCAAgC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACxG,yDAAyD,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAClF,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACrG,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AA0DD;;;;;GAKG;AACH,qBAAa,4BAA4B;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAE/D,YAAY,MAAM,EAAE,eAAe,EAAE,IAAI,SAA0B,EAkBlE;IAEM,cAAc,IAAI,oCAAoC,CAE5D;CACF"}
|