@aztec/sequencer-client 0.0.1-commit.7035c9bd6 → 0.0.1-commit.71324e566
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 +6 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +11 -17
- 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 +15 -14
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +16 -51
- package/dest/global_variable_builder/index.d.ts +4 -2
- 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 +13 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +19 -4
- 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-factory.d.ts +3 -5
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +2 -3
- package/dest/publisher/sequencer-publisher.d.ts +48 -42
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +117 -109
- 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 +21 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +200 -134
- 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/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/metrics.d.ts +5 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +12 -20
- package/dest/sequencer/sequencer.d.ts +17 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +76 -23
- package/dest/sequencer/timetable.d.ts +14 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +45 -36
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +16 -24
- 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 +26 -63
- package/src/global_variable_builder/index.ts +3 -1
- package/src/publisher/config.ts +38 -4
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-publisher-factory.ts +3 -6
- package/src/publisher/sequencer-publisher.ts +181 -155
- package/src/sequencer/README.md +83 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +242 -154
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/metrics.ts +15 -25
- package/src/sequencer/sequencer.ts +113 -25
- package/src/sequencer/timetable.ts +57 -45
|
@@ -446,7 +446,7 @@ import { Timer } from '@aztec/foundation/timer';
|
|
|
446
446
|
import { isErrorClass, unfreeze } from '@aztec/foundation/types';
|
|
447
447
|
import { CommitteeAttestationsAndSigners, MaliciousCommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
448
448
|
import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
449
|
-
import { getSlotStartBuildTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
449
|
+
import { computeQuorum, getSlotStartBuildTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
450
450
|
import { Gas } from '@aztec/stdlib/gas';
|
|
451
451
|
import { InsufficientValidTxsError } from '@aztec/stdlib/interfaces/server';
|
|
452
452
|
import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
@@ -454,6 +454,7 @@ import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p';
|
|
|
454
454
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
455
455
|
import { Attributes, trackSpan } from '@aztec/telemetry-client';
|
|
456
456
|
import { DutyAlreadySignedError, SlashingProtectionError } from '@aztec/validator-ha-signer/errors';
|
|
457
|
+
import { buildPipelinedParentSimulationOverridesPlan, buildSubmissionSimulationOverridesPlan } from './chain_state_overrides.js';
|
|
457
458
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
458
459
|
import { SequencerInterruptedError } from './errors.js';
|
|
459
460
|
import { SequencerState } from './utils.js';
|
|
@@ -473,7 +474,6 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
473
474
|
*/ export class CheckpointProposalJob {
|
|
474
475
|
slotNow;
|
|
475
476
|
targetSlot;
|
|
476
|
-
epochNow;
|
|
477
477
|
targetEpoch;
|
|
478
478
|
checkpointNumber;
|
|
479
479
|
syncedToBlockNumber;
|
|
@@ -496,9 +496,11 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
496
496
|
epochCache;
|
|
497
497
|
dateProvider;
|
|
498
498
|
metrics;
|
|
499
|
+
checkpointMetrics;
|
|
499
500
|
eventEmitter;
|
|
500
501
|
setStateFn;
|
|
501
502
|
tracer;
|
|
503
|
+
proposedCheckpointData;
|
|
502
504
|
static{
|
|
503
505
|
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
504
506
|
[
|
|
@@ -544,11 +546,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
544
546
|
], []));
|
|
545
547
|
}
|
|
546
548
|
log;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
+
/** Tracks the fire-and-forget L1 submission promise so it can be awaited during shutdown. */ pendingL1Submission;
|
|
550
|
+
/** Pipelined parent chain state used while building and later submitting this checkpoint. */ pipelinedParentSimulationOverridesPlan;
|
|
551
|
+
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, checkpointMetrics, eventEmitter, setStateFn, tracer, bindings, proposedCheckpointData){
|
|
549
553
|
this.slotNow = slotNow;
|
|
550
554
|
this.targetSlot = targetSlot;
|
|
551
|
-
this.epochNow = epochNow;
|
|
552
555
|
this.targetEpoch = targetEpoch;
|
|
553
556
|
this.checkpointNumber = checkpointNumber;
|
|
554
557
|
this.syncedToBlockNumber = syncedToBlockNumber;
|
|
@@ -571,77 +574,142 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
571
574
|
this.epochCache = epochCache;
|
|
572
575
|
this.dateProvider = dateProvider;
|
|
573
576
|
this.metrics = metrics;
|
|
577
|
+
this.checkpointMetrics = checkpointMetrics;
|
|
574
578
|
this.eventEmitter = eventEmitter;
|
|
575
579
|
this.setStateFn = setStateFn;
|
|
576
580
|
this.tracer = tracer;
|
|
581
|
+
this.proposedCheckpointData = proposedCheckpointData;
|
|
577
582
|
_initProto(this);
|
|
578
583
|
this.log = createLogger('sequencer:checkpoint-proposal', {
|
|
579
584
|
...bindings,
|
|
580
585
|
instanceId: `slot-${this.slotNow}`
|
|
581
586
|
});
|
|
582
587
|
}
|
|
583
|
-
/**
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
/** The wall-clock epoch. */ get epoch() {
|
|
587
|
-
return this.epochNow;
|
|
588
|
+
/** Awaits the pending L1 submission if one is in progress. Call during shutdown. */ async awaitPendingSubmission() {
|
|
589
|
+
this.log.info('Awaiting pending L1 payload submission');
|
|
590
|
+
await this.pendingL1Submission;
|
|
588
591
|
}
|
|
589
592
|
/**
|
|
590
593
|
* Executes the checkpoint proposal job.
|
|
591
|
-
*
|
|
594
|
+
* Builds blocks, assembles checkpoint, and broadcasts the proposal (blocking).
|
|
595
|
+
* Attestation collection, signing, and L1 submission are backgrounded so the
|
|
596
|
+
* work loop can return to IDLE immediately for consecutive slot proposals.
|
|
597
|
+
* Returns the built checkpoint if successful, undefined otherwise.
|
|
592
598
|
*/ async execute() {
|
|
593
599
|
// Enqueue governance and slashing votes (returns promises that will be awaited later)
|
|
594
600
|
// In fisherman mode, we simulate slashing but don't actually publish to L1
|
|
595
601
|
// These are constant for the whole slot, so we only enqueue them once
|
|
596
602
|
const votesPromises = new CheckpointVoter(this.targetSlot, this.publisher, this.attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log).enqueueVotes();
|
|
597
|
-
// Build
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
+
// Build blocks, assemble checkpoint, and broadcast proposal (BLOCKING).
|
|
604
|
+
// Returns after broadcast — attestation collection is deferred.
|
|
605
|
+
const broadcast = await this.proposeCheckpoint();
|
|
606
|
+
if (!broadcast) {
|
|
607
|
+
await Promise.all(votesPromises);
|
|
608
|
+
// Still submit votes even without a checkpoint
|
|
609
|
+
if (!this.config.fishermanMode) {
|
|
610
|
+
this.pendingL1Submission = this.publisher.sendRequestsAt(this.dateProvider.nowAsDate()).then(()=>{});
|
|
611
|
+
}
|
|
612
|
+
return undefined;
|
|
603
613
|
}
|
|
614
|
+
const { checkpoint } = broadcast;
|
|
615
|
+
this.metrics.recordCheckpointProposalSuccess();
|
|
604
616
|
// Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
|
|
605
617
|
if (this.config.fishermanMode) {
|
|
606
618
|
await this.handleCheckpointEndAsFisherman(checkpoint);
|
|
607
|
-
return;
|
|
619
|
+
return checkpoint;
|
|
608
620
|
}
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
621
|
+
// Background the attestation → signing → L1 pipeline so the work loop is unblocked
|
|
622
|
+
this.pendingL1Submission = this.waitForAttestationsAndEnqueueSubmissionAsync(broadcast, votesPromises);
|
|
623
|
+
// Return the built checkpoint immediately — the work loop is now unblocked
|
|
624
|
+
return checkpoint;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Background pipeline: collects attestations, signs them, enqueues the checkpoint, and submits to L1.
|
|
628
|
+
* Runs as a fire-and-forget task stored in `pendingL1Submission` so the work loop is unblocked.
|
|
629
|
+
*/ async waitForAttestationsAndEnqueueSubmissionAsync(broadcast, votesPromises) {
|
|
630
|
+
const { checkpoint, proposal, blockProposedAt } = broadcast;
|
|
631
|
+
try {
|
|
632
|
+
await Promise.all(votesPromises);
|
|
633
|
+
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.targetSlot);
|
|
634
|
+
const attestations = await this.waitForAttestations(proposal);
|
|
635
|
+
this.checkpointMetrics.recordCheckpointAttestationDelay(this.dateProvider.now() - blockProposedAt);
|
|
636
|
+
// Proposer must sign over the attestations before pushing them to L1
|
|
637
|
+
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
638
|
+
let attestationsSignature;
|
|
639
|
+
try {
|
|
640
|
+
attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.targetSlot, this.checkpointNumber);
|
|
641
|
+
} catch (err) {
|
|
642
|
+
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
throw err;
|
|
625
646
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
this.eventEmitter.emit('checkpoint-published', {
|
|
632
|
-
checkpoint: this.checkpointNumber,
|
|
633
|
-
slot: this.slot
|
|
647
|
+
// Enqueue the checkpoint for L1 submission
|
|
648
|
+
await this.enqueueCheckpointForSubmission({
|
|
649
|
+
checkpoint,
|
|
650
|
+
attestations,
|
|
651
|
+
attestationsSignature
|
|
634
652
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
653
|
+
// Compute the earliest time to submit: pipeline slot start when pipelining, now otherwise.
|
|
654
|
+
const submitAfter = this.epochCache.isProposerPipeliningEnabled() ? new Date(Number(getTimestampForSlot(this.targetSlot, this.l1Constants)) * 1000) : new Date(this.dateProvider.now());
|
|
655
|
+
const l1Response = await this.publisher.sendRequestsAt(submitAfter);
|
|
656
|
+
const proposedAction = l1Response?.successfulActions.find((a)=>a === 'propose');
|
|
657
|
+
if (proposedAction) {
|
|
658
|
+
this.eventEmitter.emit('checkpoint-published', {
|
|
659
|
+
checkpoint: this.checkpointNumber,
|
|
660
|
+
slot: this.targetSlot
|
|
661
|
+
});
|
|
662
|
+
const coinbase = checkpoint.header.coinbase;
|
|
663
|
+
await this.metrics.incFilledSlot(this.publisher.getSenderAddress().toString(), coinbase);
|
|
664
|
+
} else {
|
|
665
|
+
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
666
|
+
...l1Response,
|
|
667
|
+
slot: this.targetSlot
|
|
668
|
+
});
|
|
669
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
670
|
+
this.metrics.recordPipelineDiscard();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
} catch (err) {
|
|
674
|
+
if (err instanceof SequencerInterruptedError) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
this.log.error(`Background attestation/L1 pipeline failed for slot ${this.targetSlot}`, err);
|
|
639
678
|
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
640
|
-
|
|
641
|
-
slot: this.slot
|
|
679
|
+
slot: this.targetSlot
|
|
642
680
|
});
|
|
643
|
-
|
|
681
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
682
|
+
this.metrics.recordPipelineDiscard();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/** Enqueues the checkpoint for L1 submission. Called after pipeline sleep in execute(). */ async enqueueCheckpointForSubmission(result) {
|
|
687
|
+
const { checkpoint, attestations, attestationsSignature } = result;
|
|
688
|
+
this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.targetSlot);
|
|
689
|
+
const aztecSlotDuration = this.l1Constants.slotDuration;
|
|
690
|
+
const submissionSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
|
|
691
|
+
const txTimeoutAt = new Date((submissionSlotStart + aztecSlotDuration) * 1000);
|
|
692
|
+
// If we have been configured to potentially skip publishing checkpoint then roll the dice here
|
|
693
|
+
if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
|
|
694
|
+
const roll = Math.max(0, randomInt(100));
|
|
695
|
+
if (roll < this.config.skipPublishingCheckpointsPercent) {
|
|
696
|
+
this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${roll}`);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
644
699
|
}
|
|
700
|
+
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
701
|
+
const submissionSimulationOverridesPlan = buildSubmissionSimulationOverridesPlan({
|
|
702
|
+
pipelinedParentPlan: this.pipelinedParentSimulationOverridesPlan,
|
|
703
|
+
invalidateToPendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
704
|
+
lastArchiveRoot: checkpoint.header.lastArchiveRoot,
|
|
705
|
+
pipeliningEnabled: isPipelining
|
|
706
|
+
});
|
|
707
|
+
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
708
|
+
txTimeoutAt,
|
|
709
|
+
...submissionSimulationOverridesPlan ? {
|
|
710
|
+
simulationOverridesPlan: submissionSimulationOverridesPlan
|
|
711
|
+
} : {}
|
|
712
|
+
});
|
|
645
713
|
}
|
|
646
714
|
async proposeCheckpoint() {
|
|
647
715
|
try {
|
|
@@ -651,13 +719,22 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
651
719
|
hasError: false
|
|
652
720
|
};
|
|
653
721
|
try {
|
|
722
|
+
const now = this.dateProvider.now();
|
|
723
|
+
if (this.epochCache.isProposerPipeliningEnabled() && this.proposedCheckpointData) {
|
|
724
|
+
// Measure against the wall-clock slot whose build window we are currently using.
|
|
725
|
+
// In pipelining mode `targetSlot` is intentionally one slot ahead, which makes the
|
|
726
|
+
// target-slot boundary a full slot away from the actual build start time.
|
|
727
|
+
const slotBoundaryMs = Number(getTimestampForSlot(this.slotNow, this.l1Constants)) * 1000;
|
|
728
|
+
this.checkpointMetrics.recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(now - slotBoundaryMs);
|
|
729
|
+
}
|
|
730
|
+
this.checkpointMetrics.startCheckpointTiming(now);
|
|
654
731
|
// Get operator configured coinbase and fee recipient for this attestor
|
|
655
732
|
const coinbase = this.validatorClient.getCoinbaseForAttestor(this.attestorAddress);
|
|
656
733
|
const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(this.attestorAddress);
|
|
657
734
|
// Start the checkpoint
|
|
658
735
|
this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.targetSlot);
|
|
659
736
|
this.log.info(`Starting checkpoint proposal`, {
|
|
660
|
-
buildSlot: this.
|
|
737
|
+
buildSlot: this.slotNow,
|
|
661
738
|
submissionSlot: this.targetSlot,
|
|
662
739
|
pipelining: this.epochCache.isProposerPipeliningEnabled(),
|
|
663
740
|
proposer: this.proposer?.toString(),
|
|
@@ -668,8 +745,17 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
668
745
|
if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
|
|
669
746
|
this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
|
|
670
747
|
}
|
|
671
|
-
// Create checkpoint builder for the slot
|
|
672
|
-
|
|
748
|
+
// Create checkpoint builder for the slot.
|
|
749
|
+
// When pipelining, force the proposed checkpoint number and fee header to our parent so the
|
|
750
|
+
// fee computation sees the same chain tip that L1 will see once the previous pipelined checkpoint lands.
|
|
751
|
+
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
752
|
+
this.pipelinedParentSimulationOverridesPlan = isPipelining ? await buildPipelinedParentSimulationOverridesPlan({
|
|
753
|
+
checkpointNumber: this.checkpointNumber,
|
|
754
|
+
proposedCheckpointData: this.proposedCheckpointData,
|
|
755
|
+
rollup: this.publisher.rollupContract,
|
|
756
|
+
log: this.log
|
|
757
|
+
}) : undefined;
|
|
758
|
+
const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.targetSlot, this.pipelinedParentSimulationOverridesPlan);
|
|
673
759
|
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
674
760
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
675
761
|
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -741,14 +827,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
741
827
|
maxTxsPerCheckpoint: this.config.maxTxsPerCheckpoint
|
|
742
828
|
});
|
|
743
829
|
} catch (err) {
|
|
744
|
-
this.log.error(`Built an invalid checkpoint at slot ${this.
|
|
830
|
+
this.log.error(`Built an invalid checkpoint at slot ${this.slotNow} (skipping proposal)`, err, {
|
|
745
831
|
checkpoint: checkpoint.header.toInspect()
|
|
746
832
|
});
|
|
747
833
|
return undefined;
|
|
748
834
|
}
|
|
749
835
|
// Record checkpoint-level build metrics
|
|
750
|
-
this.
|
|
751
|
-
//
|
|
836
|
+
this.checkpointMetrics.recordCheckpointBuild(checkpointBuildTimer.ms(), blocksInCheckpoint.length, checkpoint.getStats().txCount, Number(checkpoint.header.totalManaUsed.toBigInt()));
|
|
837
|
+
// In fisherman mode, return the checkpoint without broadcasting or collecting attestations
|
|
752
838
|
if (this.config.fishermanMode) {
|
|
753
839
|
this.log.info(`Built checkpoint for slot ${this.targetSlot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
|
|
754
840
|
slot: this.targetSlot,
|
|
@@ -756,53 +842,24 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
756
842
|
blocksBuilt: blocksInCheckpoint.length
|
|
757
843
|
});
|
|
758
844
|
this.metrics.recordCheckpointSuccess();
|
|
759
|
-
|
|
845
|
+
// Return a broadcast result with a dummy proposal — fisherman mode skips attestation collection
|
|
846
|
+
return {
|
|
847
|
+
checkpoint,
|
|
848
|
+
proposal: undefined,
|
|
849
|
+
blockProposedAt: this.dateProvider.now()
|
|
850
|
+
};
|
|
760
851
|
}
|
|
761
|
-
// Include the block pending broadcast in the checkpoint proposal if any
|
|
762
|
-
const lastBlock = blockPendingBroadcast && {
|
|
763
|
-
blockHeader: blockPendingBroadcast.block.header,
|
|
764
|
-
indexWithinCheckpoint: blockPendingBroadcast.block.indexWithinCheckpoint,
|
|
765
|
-
txs: blockPendingBroadcast.txs
|
|
766
|
-
};
|
|
767
852
|
// Create the checkpoint proposal and broadcast it
|
|
768
|
-
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier,
|
|
853
|
+
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, this.checkpointNumber, feeAssetPriceModifier, blockPendingBroadcast, this.proposer, checkpointProposalOptions);
|
|
769
854
|
const blockProposedAt = this.dateProvider.now();
|
|
770
855
|
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
771
|
-
this.
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
try {
|
|
779
|
-
attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.targetSlot, this.checkpointNumber);
|
|
780
|
-
} catch (err) {
|
|
781
|
-
// We shouldn't really get here since we yield to another HA node
|
|
782
|
-
// as soon as we see these errors when creating block or checkpoint proposals.
|
|
783
|
-
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
784
|
-
return undefined;
|
|
785
|
-
}
|
|
786
|
-
throw err;
|
|
787
|
-
}
|
|
788
|
-
// Enqueue publishing the checkpoint to L1
|
|
789
|
-
this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.targetSlot);
|
|
790
|
-
const aztecSlotDuration = this.l1Constants.slotDuration;
|
|
791
|
-
const submissionSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
|
|
792
|
-
const txTimeoutAt = new Date((submissionSlotStart + aztecSlotDuration) * 1000);
|
|
793
|
-
// If we have been configured to potentially skip publishing checkpoint then roll the dice here
|
|
794
|
-
if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
|
|
795
|
-
const result = Math.max(0, randomInt(100));
|
|
796
|
-
if (result < this.config.skipPublishingCheckpointsPercent) {
|
|
797
|
-
this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${result}`);
|
|
798
|
-
return checkpoint;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
802
|
-
txTimeoutAt,
|
|
803
|
-
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber
|
|
804
|
-
});
|
|
805
|
-
return checkpoint;
|
|
856
|
+
this.checkpointMetrics.noteCheckpointBroadcast(this.dateProvider.now());
|
|
857
|
+
// Return immediately after broadcast — attestation collection happens in the background
|
|
858
|
+
return {
|
|
859
|
+
checkpoint,
|
|
860
|
+
proposal,
|
|
861
|
+
blockProposedAt
|
|
862
|
+
};
|
|
806
863
|
} catch (e) {
|
|
807
864
|
env.error = e;
|
|
808
865
|
env.hasError = true;
|
|
@@ -815,7 +872,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
815
872
|
// swallow this error. It's already been logged by a function deeper in the stack
|
|
816
873
|
return undefined;
|
|
817
874
|
}
|
|
818
|
-
this.log.error(`Error building checkpoint at slot ${this.
|
|
875
|
+
this.log.error(`Error building checkpoint at slot ${this.targetSlot}`, err);
|
|
819
876
|
return undefined;
|
|
820
877
|
}
|
|
821
878
|
}
|
|
@@ -852,17 +909,19 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
852
909
|
indexWithinCheckpoint,
|
|
853
910
|
txHashesAlreadyIncluded
|
|
854
911
|
});
|
|
855
|
-
//
|
|
856
|
-
if (
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
912
|
+
// If we failed to build the block due to insufficient txs, we try again if there is still time left in the slot
|
|
913
|
+
if ('failure' in buildResult) {
|
|
914
|
+
// If this was the last subslot, or we're running with a single block per slot, we're done
|
|
915
|
+
if (timingInfo.isLastBlock || timingInfo.deadline === undefined) {
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
// Otherwise, if there is still time for more blocks, we wait until the next subslot and try again
|
|
860
919
|
await this.waitUntilNextSubslot(timingInfo.deadline);
|
|
861
920
|
continue;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
921
|
+
}
|
|
922
|
+
// If there was an error building the block, we just exit the loop and give up the rest of the slot.
|
|
923
|
+
// We don't want to risk building more blocks if something went wrong.
|
|
924
|
+
if ('error' in buildResult) {
|
|
866
925
|
if (!(buildResult.error instanceof SequencerInterruptedError)) {
|
|
867
926
|
this.log.warn(`Halting block building for slot ${this.targetSlot}`, {
|
|
868
927
|
slot: this.targetSlot,
|
|
@@ -873,32 +932,29 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
873
932
|
break;
|
|
874
933
|
}
|
|
875
934
|
const { block, usedTxs } = buildResult;
|
|
935
|
+
this.checkpointMetrics.noteCheckpointBlockBuilt(this.dateProvider.now(), {
|
|
936
|
+
isFirstBlock: blocksBuilt === 0,
|
|
937
|
+
isLastBlock: timingInfo.isLastBlock
|
|
938
|
+
});
|
|
876
939
|
blocksInCheckpoint.push(block);
|
|
877
940
|
usedTxs.forEach((tx)=>txHashesAlreadyIncluded.add(tx.txHash.toString()));
|
|
878
|
-
//
|
|
879
|
-
|
|
941
|
+
// Sign the block proposal. This will throw if HA signing fails.
|
|
942
|
+
const proposal = await this.createBlockProposal(block, inHash, usedTxs, blockProposalOptions);
|
|
943
|
+
// Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal,
|
|
944
|
+
// so we avoid polluting our archive with a block that would fail.
|
|
945
|
+
// We wait for the sync to succeed, as this helps catch consistency errors, even if it means we lose some time for block-building.
|
|
946
|
+
// If this throws, we abort the entire checkpoint.
|
|
947
|
+
await this.syncProposedBlockToArchiver(block);
|
|
948
|
+
// If this is the last block, do not broadcast it, since it will be included in the checkpoint proposal.
|
|
880
949
|
if (timingInfo.isLastBlock) {
|
|
881
|
-
await this.syncProposedBlockToArchiver(block);
|
|
882
950
|
this.log.verbose(`Completed final block ${blockNumber} for slot ${this.targetSlot}`, {
|
|
883
951
|
slot: this.targetSlot,
|
|
884
952
|
blockNumber,
|
|
885
953
|
blocksBuilt
|
|
886
954
|
});
|
|
887
|
-
blockPendingBroadcast =
|
|
888
|
-
block,
|
|
889
|
-
txs: usedTxs
|
|
890
|
-
};
|
|
955
|
+
blockPendingBroadcast = proposal;
|
|
891
956
|
break;
|
|
892
957
|
}
|
|
893
|
-
// Broadcast the block proposal (unless we're in fisherman mode) unless the block is the last one,
|
|
894
|
-
// in which case we'll broadcast it along with the checkpoint at the end of the loop.
|
|
895
|
-
// Note that we only send the block to the archiver if we manage to create the proposal, so if there's
|
|
896
|
-
// a HA error we don't pollute our archiver with a block that won't make it to the chain.
|
|
897
|
-
const proposal = await this.createBlockProposal(block, inHash, usedTxs, blockProposalOptions);
|
|
898
|
-
// Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal.
|
|
899
|
-
// We wait for the sync to succeed, as this helps catch consistency errors, even if it means we lose some time for block-building.
|
|
900
|
-
// If this throws, we abort the entire checkpoint.
|
|
901
|
-
await this.syncProposedBlockToArchiver(block);
|
|
902
958
|
// Once we have a signed proposal and the archiver agreed with our proposed block, then we broadcast it.
|
|
903
959
|
proposal && await this.p2pClient.broadcastProposal(proposal);
|
|
904
960
|
// Wait until the next block's start time
|
|
@@ -918,7 +974,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
918
974
|
this.log.info(`Skipping block proposal for block ${block.number} in fisherman mode`);
|
|
919
975
|
return Promise.resolve(undefined);
|
|
920
976
|
}
|
|
921
|
-
return this.validatorClient.createBlockProposal(block.header, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
977
|
+
return this.validatorClient.createBlockProposal(block.header, this.checkpointNumber, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
922
978
|
}
|
|
923
979
|
/** Sleeps until it is time to produce the next block in the slot */ async waitUntilNextSubslot(nextSubslotStart) {
|
|
924
980
|
this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.targetSlot);
|
|
@@ -948,7 +1004,9 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
948
1004
|
slot: this.targetSlot
|
|
949
1005
|
});
|
|
950
1006
|
this.metrics.recordBlockProposalFailed('insufficient_txs');
|
|
951
|
-
return
|
|
1007
|
+
return {
|
|
1008
|
+
failure: 'insufficient-txs'
|
|
1009
|
+
};
|
|
952
1010
|
}
|
|
953
1011
|
// Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
|
|
954
1012
|
// just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
|
|
@@ -991,7 +1049,9 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
991
1049
|
slot: this.targetSlot
|
|
992
1050
|
});
|
|
993
1051
|
this.metrics.recordBlockProposalFailed('insufficient_valid_txs');
|
|
994
|
-
return
|
|
1052
|
+
return {
|
|
1053
|
+
failure: 'insufficient-valid-txs'
|
|
1054
|
+
};
|
|
995
1055
|
}
|
|
996
1056
|
// Block creation succeeded, emit stats and metrics
|
|
997
1057
|
const { block, publicProcessorDuration, usedTxs, blockBuildDuration, numTxs } = buildResult;
|
|
@@ -1010,12 +1070,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1010
1070
|
manaPerSec,
|
|
1011
1071
|
...blockStats
|
|
1012
1072
|
});
|
|
1073
|
+
// `slot` is the target/submission slot (may be one ahead when pipelining),
|
|
1074
|
+
// `buildSlot` is the wall-clock slot during which the block was actually built.
|
|
1013
1075
|
this.eventEmitter.emit('block-proposed', {
|
|
1014
1076
|
blockNumber: block.number,
|
|
1015
1077
|
slot: this.targetSlot,
|
|
1016
1078
|
buildSlot: this.slotNow
|
|
1017
1079
|
});
|
|
1018
|
-
this.metrics.recordBuiltBlock(blockBuildDuration, block.header.totalManaUsed.toNumberUnsafe());
|
|
1080
|
+
this.metrics.recordBuiltBlock(blockBuildDuration, block.header.totalManaUsed.toNumberUnsafe(), this.targetSlot);
|
|
1019
1081
|
return {
|
|
1020
1082
|
block,
|
|
1021
1083
|
usedTxs
|
|
@@ -1110,19 +1172,19 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1110
1172
|
committee
|
|
1111
1173
|
});
|
|
1112
1174
|
}
|
|
1113
|
-
const numberOfRequiredAttestations =
|
|
1175
|
+
const numberOfRequiredAttestations = computeQuorum(committee.length);
|
|
1114
1176
|
if (this.config.skipCollectingAttestations) {
|
|
1115
1177
|
this.log.warn('Skipping attestation collection as per config (attesting with own keys only)');
|
|
1116
|
-
const attestations = await this.validatorClient?.collectOwnAttestations(proposal);
|
|
1178
|
+
const attestations = await this.validatorClient?.collectOwnAttestations(proposal, this.checkpointNumber);
|
|
1117
1179
|
return new CommitteeAttestationsAndSigners(orderAttestations(attestations ?? [], committee));
|
|
1118
1180
|
}
|
|
1119
|
-
const attestationTimeAllowed = this.config.enforceTimeTable ? this.timetable.
|
|
1181
|
+
const attestationTimeAllowed = this.config.enforceTimeTable ? this.timetable.getCheckpointAttestationDeadline() : this.l1Constants.slotDuration;
|
|
1120
1182
|
const attestationDeadline = new Date((this.getSlotStartBuildTimestamp() + attestationTimeAllowed) * 1000);
|
|
1121
1183
|
this.metrics.recordRequiredAttestations(numberOfRequiredAttestations, attestationTimeAllowed);
|
|
1122
1184
|
const collectAttestationsTimer = new Timer();
|
|
1123
1185
|
let collectedAttestationsCount = 0;
|
|
1124
1186
|
try {
|
|
1125
|
-
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
|
|
1187
|
+
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline, this.checkpointNumber);
|
|
1126
1188
|
collectedAttestationsCount = attestations.length;
|
|
1127
1189
|
// Trim attestations to minimum required to save L1 calldata gas
|
|
1128
1190
|
const localAddresses = this.validatorClient.getValidatorAddresses();
|
|
@@ -1215,8 +1277,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1215
1277
|
* Adds the proposed block to the archiver so it's available via P2P.
|
|
1216
1278
|
* Gossip doesn't echo messages back to the sender, so the proposer's archiver/world-state
|
|
1217
1279
|
* would never receive its own block without this explicit sync.
|
|
1280
|
+
*
|
|
1281
|
+
* In fisherman mode we skip this push: the fisherman builds blocks locally for validation
|
|
1282
|
+
* and fee analysis only, and pushing them to the archiver causes spurious reorg cascades
|
|
1283
|
+
* whenever the real proposer's block arrives from L1.
|
|
1218
1284
|
*/ async syncProposedBlockToArchiver(block) {
|
|
1219
|
-
if (this.config.skipPushProposedBlocksToArchiver
|
|
1285
|
+
if (this.config.skipPushProposedBlocksToArchiver || this.config.fishermanMode) {
|
|
1220
1286
|
this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
|
|
1221
1287
|
blockNumber: block.number,
|
|
1222
1288
|
slot: block.header.globalVariables.slotNumber
|
|
@@ -1277,7 +1343,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1277
1343
|
await sleep(TXS_POLLING_MS);
|
|
1278
1344
|
}
|
|
1279
1345
|
getSlotStartBuildTimestamp() {
|
|
1280
|
-
return getSlotStartBuildTimestamp(this.
|
|
1346
|
+
return getSlotStartBuildTimestamp(this.slotNow, this.l1Constants);
|
|
1281
1347
|
}
|
|
1282
1348
|
getSecondsIntoSlot() {
|
|
1283
1349
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
@@ -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"}
|