@aztec/sequencer-client 5.0.0-private.20260318 → 5.0.0-rc.1
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/README.md +281 -21
- package/dest/client/sequencer-client.d.ts +8 -15
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +35 -96
- package/dest/config.d.ts +8 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +48 -26
- 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 +67 -0
- package/dest/global_variable_builder/global_builder.d.ts +14 -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 +15 -3
- 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-bundle-simulator.d.ts +96 -0
- package/dest/publisher/sequencer-bundle-simulator.d.ts.map +1 -0
- package/dest/publisher/sequencer-bundle-simulator.js +198 -0
- 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 +73 -65
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +317 -532
- package/dest/sequencer/automine/automine_factory.d.ts +56 -0
- package/dest/sequencer/automine/automine_factory.d.ts.map +1 -0
- package/dest/sequencer/automine/automine_factory.js +85 -0
- package/dest/sequencer/automine/automine_sequencer.d.ts +184 -0
- package/dest/sequencer/automine/automine_sequencer.d.ts.map +1 -0
- package/dest/sequencer/automine/automine_sequencer.js +677 -0
- package/dest/sequencer/automine/index.d.ts +3 -0
- package/dest/sequencer/automine/index.d.ts.map +1 -0
- package/dest/sequencer/automine/index.js +2 -0
- package/dest/sequencer/chain_state_overrides.d.ts +61 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +98 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +77 -17
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +768 -209
- 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/errors.d.ts +1 -8
- package/dest/sequencer/errors.d.ts.map +1 -1
- package/dest/sequencer/errors.js +0 -9
- package/dest/sequencer/events.d.ts +62 -4
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +13 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +45 -20
- package/dest/sequencer/sequencer.d.ts +115 -30
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +506 -174
- package/dest/sequencer/types.d.ts +2 -2
- package/dest/sequencer/types.d.ts.map +1 -1
- package/dest/test/index.d.ts +3 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +4 -8
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/utils.d.ts +15 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +23 -6
- package/package.json +28 -27
- package/src/client/sequencer-client.ts +53 -123
- package/src/config.ts +57 -23
- 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 +80 -0
- package/src/global_variable_builder/global_builder.ts +25 -63
- package/src/global_variable_builder/index.ts +3 -1
- package/src/publisher/config.ts +40 -6
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-bundle-simulator.ts +254 -0
- package/src/publisher/sequencer-publisher-factory.ts +3 -6
- package/src/publisher/sequencer-publisher.ts +376 -577
- package/src/sequencer/automine/README.md +60 -0
- package/src/sequencer/automine/automine_factory.ts +152 -0
- package/src/sequencer/automine/automine_sequencer.ts +783 -0
- package/src/sequencer/automine/index.ts +6 -0
- package/src/sequencer/chain_state_overrides.ts +169 -0
- package/src/sequencer/checkpoint_proposal_job.ts +917 -241
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/errors.ts +0 -15
- package/src/sequencer/events.ts +65 -4
- package/src/sequencer/metrics.ts +57 -24
- package/src/sequencer/sequencer.ts +604 -195
- package/src/sequencer/types.ts +1 -1
- package/src/test/index.ts +2 -2
- package/src/test/mock_checkpoint_builder.ts +3 -3
- package/src/test/utils.ts +59 -10
- package/dest/sequencer/timetable.d.ts +0 -88
- package/dest/sequencer/timetable.d.ts.map +0 -1
- package/dest/sequencer/timetable.js +0 -222
- package/src/sequencer/README.md +0 -531
- package/src/sequencer/timetable.ts +0 -283
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Attributes,
|
|
3
|
+
type Gauge,
|
|
4
|
+
type Histogram,
|
|
5
|
+
type Meter,
|
|
6
|
+
Metrics,
|
|
7
|
+
type TelemetryClient,
|
|
8
|
+
} from '@aztec/telemetry-client';
|
|
9
|
+
|
|
10
|
+
type CheckpointProposalJobInstruments = {
|
|
11
|
+
checkpointAttestationDelay: Histogram;
|
|
12
|
+
checkpointBuildDuration: Histogram;
|
|
13
|
+
checkpointStartToFirstBlockDuration: Histogram;
|
|
14
|
+
checkpointLastBlockToBroadcastDuration: Histogram;
|
|
15
|
+
pipelinedCheckpointBuildStartOffsetFromSlotBoundary: Histogram;
|
|
16
|
+
checkpointBlockCount: Gauge;
|
|
17
|
+
checkpointTxCount: Gauge;
|
|
18
|
+
checkpointTotalMana: Gauge;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Per-job recording surface used by {@link CheckpointProposalJob}.
|
|
23
|
+
*
|
|
24
|
+
* Ownership split:
|
|
25
|
+
* - {@link CheckpointProposalJobMetrics} owns the OpenTelemetry meter and instruments and should live for the
|
|
26
|
+
* lifetime of the sequencer process.
|
|
27
|
+
* - A recorder owns only mutable timing state for one checkpoint proposal job so overlapping jobs cannot
|
|
28
|
+
* overwrite each other's timing markers.
|
|
29
|
+
*/
|
|
30
|
+
export interface CheckpointProposalJobMetricsRecorder {
|
|
31
|
+
recordCheckpointAttestationDelay(durationMs: number): void;
|
|
32
|
+
recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number): void;
|
|
33
|
+
recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(offsetMs: number): void;
|
|
34
|
+
startCheckpointTiming(nowMs: number): void;
|
|
35
|
+
noteCheckpointBlockBuilt(nowMs: number, opts: { isFirstBlock: boolean; isLastBlock: boolean }): void;
|
|
36
|
+
noteCheckpointBroadcast(nowMs: number): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Concrete per-job recorder.
|
|
41
|
+
*
|
|
42
|
+
* This class should be short-lived: create one recorder per checkpoint proposal job and discard it when the job
|
|
43
|
+
* completes. It intentionally does not create instruments; it only holds the job-local timestamps needed to derive
|
|
44
|
+
* timing metrics safely.
|
|
45
|
+
*/
|
|
46
|
+
class CheckpointProposalJobMetricsRecorderImpl implements CheckpointProposalJobMetricsRecorder {
|
|
47
|
+
private checkpointStartedAt?: number;
|
|
48
|
+
private checkpointLastBlockBuiltAt?: number;
|
|
49
|
+
|
|
50
|
+
constructor(private readonly instruments: CheckpointProposalJobInstruments) {}
|
|
51
|
+
|
|
52
|
+
public recordCheckpointAttestationDelay(durationMs: number) {
|
|
53
|
+
this.instruments.checkpointAttestationDelay.record(Math.ceil(durationMs));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
|
|
57
|
+
this.instruments.checkpointBuildDuration.record(Math.ceil(durationMs));
|
|
58
|
+
this.instruments.checkpointBlockCount.record(blockCount);
|
|
59
|
+
this.instruments.checkpointTxCount.record(txCount);
|
|
60
|
+
this.instruments.checkpointTotalMana.record(totalMana);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(offsetMs: number) {
|
|
64
|
+
this.instruments.pipelinedCheckpointBuildStartOffsetFromSlotBoundary.record(Math.ceil(Math.abs(offsetMs)), {
|
|
65
|
+
[Attributes.SLOT_BOUNDARY_SIDE]: offsetMs < 0 ? 'before' : 'after',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public startCheckpointTiming(nowMs: number) {
|
|
70
|
+
this.checkpointStartedAt = nowMs;
|
|
71
|
+
this.checkpointLastBlockBuiltAt = undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public noteCheckpointBlockBuilt(nowMs: number, opts: { isFirstBlock: boolean; isLastBlock: boolean }) {
|
|
75
|
+
if (opts.isFirstBlock && this.checkpointStartedAt !== undefined) {
|
|
76
|
+
this.instruments.checkpointStartToFirstBlockDuration.record(Math.ceil(nowMs - this.checkpointStartedAt));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (opts.isLastBlock) {
|
|
80
|
+
this.checkpointLastBlockBuiltAt = nowMs;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public noteCheckpointBroadcast(nowMs: number) {
|
|
85
|
+
if (this.checkpointLastBlockBuiltAt !== undefined) {
|
|
86
|
+
this.instruments.checkpointLastBlockToBroadcastDuration.record(
|
|
87
|
+
Math.ceil(nowMs - this.checkpointLastBlockBuiltAt),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.checkpointLastBlockBuiltAt = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Long-lived owner of checkpoint proposal job telemetry instruments.
|
|
97
|
+
*
|
|
98
|
+
* The sequencer should construct this once and reuse it across jobs. Each call to {@link createRecorder} returns a
|
|
99
|
+
* lightweight recorder with isolated mutable state for a single checkpoint proposal job.
|
|
100
|
+
*/
|
|
101
|
+
export class CheckpointProposalJobMetrics {
|
|
102
|
+
private readonly meter: Meter;
|
|
103
|
+
private readonly instruments: CheckpointProposalJobInstruments;
|
|
104
|
+
|
|
105
|
+
constructor(client: TelemetryClient, name = 'CheckpointProposalJob') {
|
|
106
|
+
this.meter = client.getMeter(name);
|
|
107
|
+
this.instruments = {
|
|
108
|
+
checkpointAttestationDelay: this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY),
|
|
109
|
+
checkpointBuildDuration: this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION),
|
|
110
|
+
checkpointStartToFirstBlockDuration: this.meter.createHistogram(
|
|
111
|
+
Metrics.SEQUENCER_CHECKPOINT_START_TO_FIRST_BLOCK_DURATION,
|
|
112
|
+
),
|
|
113
|
+
checkpointLastBlockToBroadcastDuration: this.meter.createHistogram(
|
|
114
|
+
Metrics.SEQUENCER_CHECKPOINT_LAST_BLOCK_TO_BROADCAST_DURATION,
|
|
115
|
+
),
|
|
116
|
+
pipelinedCheckpointBuildStartOffsetFromSlotBoundary: this.meter.createHistogram(
|
|
117
|
+
Metrics.SEQUENCER_PIPELINED_CHECKPOINT_BUILD_START_OFFSET_FROM_SLOT_BOUNDARY,
|
|
118
|
+
),
|
|
119
|
+
checkpointBlockCount: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT),
|
|
120
|
+
checkpointTxCount: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT),
|
|
121
|
+
checkpointTotalMana: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public createRecorder(): CheckpointProposalJobMetricsRecorder {
|
|
126
|
+
return new CheckpointProposalJobMetricsRecorderImpl(this.instruments);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -2,7 +2,6 @@ import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
4
4
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
5
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
6
5
|
import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
6
|
import type { ValidatorClient } from '@aztec/validator-client';
|
|
8
7
|
import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
|
|
@@ -18,7 +17,6 @@ import type { SequencerRollupConstants } from './types.js';
|
|
|
18
17
|
* Handles governance and slashing voting for a given slot.
|
|
19
18
|
*/
|
|
20
19
|
export class CheckpointVoter {
|
|
21
|
-
private slotTimestamp: bigint;
|
|
22
20
|
private governanceSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
|
|
23
21
|
private slashingSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
|
|
24
22
|
|
|
@@ -33,8 +31,6 @@ export class CheckpointVoter {
|
|
|
33
31
|
private readonly metrics: SequencerMetrics,
|
|
34
32
|
private readonly log: Logger,
|
|
35
33
|
) {
|
|
36
|
-
this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
|
|
37
|
-
|
|
38
34
|
// Create separate signers with appropriate duty contexts for governance and slashing votes
|
|
39
35
|
// These use HA protection to ensure only one node signs per slot/duty
|
|
40
36
|
const governanceContext: SigningContext = { slot: this.slot, dutyType: DutyType.GOVERNANCE_VOTE };
|
|
@@ -77,7 +73,6 @@ export class CheckpointVoter {
|
|
|
77
73
|
return await this.publisher.enqueueGovernanceCastSignal(
|
|
78
74
|
governanceProposerPayload,
|
|
79
75
|
this.slot,
|
|
80
|
-
this.slotTimestamp,
|
|
81
76
|
this.attestorAddress,
|
|
82
77
|
this.governanceSigner,
|
|
83
78
|
);
|
|
@@ -108,13 +103,7 @@ export class CheckpointVoter {
|
|
|
108
103
|
|
|
109
104
|
this.metrics.recordSlashingAttempt(actions.length);
|
|
110
105
|
|
|
111
|
-
return await this.publisher.enqueueSlashingActions(
|
|
112
|
-
actions,
|
|
113
|
-
this.slot,
|
|
114
|
-
this.slotTimestamp,
|
|
115
|
-
this.attestorAddress,
|
|
116
|
-
this.slashingSigner,
|
|
117
|
-
);
|
|
106
|
+
return await this.publisher.enqueueSlashingActions(actions, this.slot, this.attestorAddress, this.slashingSigner);
|
|
118
107
|
} catch (err) {
|
|
119
108
|
if (err instanceof DutyAlreadySignedError) {
|
|
120
109
|
this.log.info(`Slashing vote already signed by another node`, {
|
package/src/sequencer/errors.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
import type { SequencerState } from './utils.js';
|
|
2
|
-
|
|
3
|
-
export class SequencerTooSlowError extends Error {
|
|
4
|
-
constructor(
|
|
5
|
-
public readonly proposedState: SequencerState,
|
|
6
|
-
public readonly maxAllowedTime: number,
|
|
7
|
-
public readonly currentTime: number,
|
|
8
|
-
) {
|
|
9
|
-
super(
|
|
10
|
-
`Too far into slot for ${proposedState} (time into slot ${currentTime}s greater than ${maxAllowedTime}s allowance)`,
|
|
11
|
-
);
|
|
12
|
-
this.name = 'SequencerTooSlowError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
1
|
export class SequencerInterruptedError extends Error {
|
|
17
2
|
constructor() {
|
|
18
3
|
super(`Sequencer was interrupted`);
|
package/src/sequencer/events.ts
CHANGED
|
@@ -1,20 +1,76 @@
|
|
|
1
|
-
import type { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
|
+
import type { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import type { BlockHash } from '@aztec/stdlib/block';
|
|
2
3
|
|
|
3
4
|
import type { Action } from '../publisher/sequencer-publisher.js';
|
|
4
5
|
import type { SequencerState } from './utils.js';
|
|
5
6
|
|
|
6
7
|
export type SequencerEvents = {
|
|
8
|
+
/**
|
|
9
|
+
* Emitted on every sequencer state transition (including no-op transitions to the same state). The
|
|
10
|
+
* timing fields are anchored to the build frame of the slot being proposed for, not to wall-clock
|
|
11
|
+
* slot boundaries, because the proposer builds for `targetSlot` during the previous (build) slot.
|
|
12
|
+
*
|
|
13
|
+
* - `oldState` / `newState` are the previous and new {@link SequencerState}.
|
|
14
|
+
* - `secondsIntoBuildFrame` is the wall-clock seconds elapsed since the build-frame start of
|
|
15
|
+
* `targetSlot` (`now − getBuildFrameStart(targetSlot)`). Undefined for lifecycle states with no
|
|
16
|
+
* associated slot (e.g. IDLE/STOPPING). It can be negative if the transition happens before the
|
|
17
|
+
* build frame opens.
|
|
18
|
+
* - `targetSlot` is the slot the checkpoint is being proposed for (the submission slot, one ahead of
|
|
19
|
+
* the wall-clock build slot under pipelining). Undefined for lifecycle states with no slot.
|
|
20
|
+
*/
|
|
7
21
|
['state-changed']: (args: {
|
|
8
22
|
oldState: SequencerState;
|
|
9
23
|
newState: SequencerState;
|
|
10
|
-
|
|
11
|
-
|
|
24
|
+
secondsIntoBuildFrame?: number;
|
|
25
|
+
targetSlot?: SlotNumber;
|
|
26
|
+
}) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Emitted by the sequencer once it has decided it is going to attempt to build a
|
|
29
|
+
* checkpoint for `targetSlot`, after computing the L1 simulation overrides used by
|
|
30
|
+
* `canProposeAt`. Fired BEFORE the L1 simulation is run, so consumers can observe the
|
|
31
|
+
* decision regardless of whether the propose ultimately lands.
|
|
32
|
+
*
|
|
33
|
+
* - `hadProposedParent` indicates whether the build saw a proposed (pipelined) parent
|
|
34
|
+
* checkpoint that hasn't landed on L1 yet.
|
|
35
|
+
* - `provenOverride` is the assumed proven checkpoint number pinned for the L1
|
|
36
|
+
* simulation. The plan always pins both chain tips to short-circuit `canPruneAtTime`,
|
|
37
|
+
* so this is populated whenever a simulation plan was built — the value either
|
|
38
|
+
* matches the on-chain proven snapshot (defensive pin) or the assumed-proven
|
|
39
|
+
* checkpoint when building optimistically across a pruning boundary.
|
|
40
|
+
* - `simulatedPending` is the pending checkpoint passed to L1 simulation. The plan
|
|
41
|
+
* always pins both chain tips to short-circuit `canPruneAtTime`, so this reflects
|
|
42
|
+
* either the pipelined/invalidated tip or the on-chain pending snapshot.
|
|
43
|
+
*/
|
|
44
|
+
['preparing-checkpoint']: (args: {
|
|
45
|
+
targetSlot: SlotNumber;
|
|
46
|
+
checkpointNumber: CheckpointNumber;
|
|
47
|
+
hadProposedParent: boolean;
|
|
48
|
+
provenOverride: CheckpointNumber | undefined;
|
|
49
|
+
simulatedPending: CheckpointNumber | undefined;
|
|
12
50
|
}) => void;
|
|
13
51
|
['proposer-rollup-check-failed']: (args: { reason: string; slot: SlotNumber }) => void;
|
|
14
52
|
['block-tx-count-check-failed']: (args: { minTxs: number; availableTxs: number; slot: SlotNumber }) => void;
|
|
15
53
|
['block-build-failed']: (args: { reason: string; slot: SlotNumber }) => void;
|
|
16
|
-
['block-proposed']: (args: {
|
|
54
|
+
['block-proposed']: (args: {
|
|
55
|
+
blockNumber: BlockNumber;
|
|
56
|
+
blockHash: BlockHash;
|
|
57
|
+
checkpointNumber: CheckpointNumber;
|
|
58
|
+
indexWithinCheckpoint: IndexWithinCheckpoint;
|
|
59
|
+
slot: SlotNumber;
|
|
60
|
+
buildSlot: SlotNumber;
|
|
61
|
+
}) => void;
|
|
17
62
|
['checkpoint-empty']: (args: { slot: SlotNumber }) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Emitted when the proposer's pre-broadcast `validateBlockHeader` simulation fails. This is a
|
|
65
|
+
* last-chance check before we gossip a checkpoint proposal: a failure here means the header
|
|
66
|
+
* would not be accepted by L1 (e.g. archive mismatch, stale chain tip, or some other state
|
|
67
|
+
* drift between when we built the checkpoint and when we are about to broadcast it).
|
|
68
|
+
*/
|
|
69
|
+
['header-validation-failed']: (args: {
|
|
70
|
+
slot: SlotNumber;
|
|
71
|
+
checkpointNumber: CheckpointNumber;
|
|
72
|
+
reason: string;
|
|
73
|
+
}) => void;
|
|
18
74
|
['checkpoint-publish-failed']: (args: {
|
|
19
75
|
slot: SlotNumber;
|
|
20
76
|
successfulActions?: Action[];
|
|
@@ -24,4 +80,9 @@ export type SequencerEvents = {
|
|
|
24
80
|
}) => void;
|
|
25
81
|
['checkpoint-published']: (args: { checkpoint: CheckpointNumber; slot: SlotNumber }) => void;
|
|
26
82
|
['checkpoint-error']: (args: { error: Error }) => void;
|
|
83
|
+
['pipelined-checkpoint-discarded']: (args: {
|
|
84
|
+
slot: SlotNumber;
|
|
85
|
+
checkpointNumber: CheckpointNumber;
|
|
86
|
+
reason: string;
|
|
87
|
+
}) => void;
|
|
27
88
|
};
|
package/src/sequencer/metrics.ts
CHANGED
|
@@ -26,6 +26,7 @@ export class SequencerMetrics {
|
|
|
26
26
|
private blockBuildDuration: Histogram;
|
|
27
27
|
private blockBuildManaPerSecond: Gauge;
|
|
28
28
|
private stateTransitionBufferDuration: Histogram;
|
|
29
|
+
private stateDuration: Histogram;
|
|
29
30
|
|
|
30
31
|
// these are gauges because for individual sequencers building a block is not something that happens often enough to warrant a histogram
|
|
31
32
|
private timeToCollectAttestations: Gauge;
|
|
@@ -44,11 +45,9 @@ export class SequencerMetrics {
|
|
|
44
45
|
private checkpointProposalFailed: UpDownCounter;
|
|
45
46
|
private checkpointSuccess: UpDownCounter;
|
|
46
47
|
private slashingAttempts: UpDownCounter;
|
|
47
|
-
private
|
|
48
|
-
private
|
|
49
|
-
private
|
|
50
|
-
private checkpointTxCount: Gauge;
|
|
51
|
-
private checkpointTotalMana: Gauge;
|
|
48
|
+
private pipelineDepth: Gauge;
|
|
49
|
+
private pipelineDiscards: UpDownCounter;
|
|
50
|
+
private pipelineParentCheckpointMismatches: UpDownCounter;
|
|
52
51
|
|
|
53
52
|
// Fisherman fee analysis metrics
|
|
54
53
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
@@ -66,6 +65,10 @@ export class SequencerMetrics {
|
|
|
66
65
|
private fishermanMinedBlobTxPriorityFee: Histogram;
|
|
67
66
|
private fishermanMinedBlobTxTotalCost: Histogram;
|
|
68
67
|
|
|
68
|
+
private blockInterBlockTime: Histogram;
|
|
69
|
+
private lastBlockBuiltTimestamp?: number;
|
|
70
|
+
private lastBlockBuiltSlot?: SlotNumber;
|
|
71
|
+
|
|
69
72
|
private lastSeenSlot?: SlotNumber;
|
|
70
73
|
|
|
71
74
|
constructor(
|
|
@@ -84,9 +87,11 @@ export class SequencerMetrics {
|
|
|
84
87
|
|
|
85
88
|
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
|
|
86
89
|
|
|
90
|
+
this.blockInterBlockTime = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_INTER_BLOCK_TIME);
|
|
91
|
+
|
|
87
92
|
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
88
93
|
|
|
89
|
-
this.
|
|
94
|
+
this.stateDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_DURATION);
|
|
90
95
|
|
|
91
96
|
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
|
|
92
97
|
|
|
@@ -136,13 +141,25 @@ export class SequencerMetrics {
|
|
|
136
141
|
Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
|
|
137
142
|
);
|
|
138
143
|
|
|
139
|
-
this.checkpointBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION);
|
|
140
|
-
this.checkpointBlockCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT);
|
|
141
|
-
this.checkpointTxCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT);
|
|
142
|
-
this.checkpointTotalMana = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA);
|
|
143
|
-
|
|
144
144
|
this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
|
|
145
145
|
|
|
146
|
+
this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
|
|
147
|
+
this.pipelineDiscards = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_PIPELINE_DISCARDS_COUNT);
|
|
148
|
+
this.pipelineParentCheckpointMismatches = createUpDownCounterWithDefault(
|
|
149
|
+
this.meter,
|
|
150
|
+
Metrics.SEQUENCER_PIPELINE_PARENT_CHECKPOINT_MISMATCH_COUNT,
|
|
151
|
+
{
|
|
152
|
+
[Attributes.ERROR_TYPE]: [
|
|
153
|
+
'archiver-sync-timeout',
|
|
154
|
+
'parent-not-on-l1',
|
|
155
|
+
'parent-hash-mismatch',
|
|
156
|
+
'parent-invalid-attestations',
|
|
157
|
+
'unexpected-parent-appeared',
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
this.pipelineDepth.record(0);
|
|
162
|
+
|
|
146
163
|
// Fisherman fee analysis metrics
|
|
147
164
|
this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
|
|
148
165
|
this.meter,
|
|
@@ -205,21 +222,25 @@ export class SequencerMetrics {
|
|
|
205
222
|
this.timeToCollectAttestations.record(0);
|
|
206
223
|
}
|
|
207
224
|
|
|
208
|
-
public recordCheckpointAttestationDelay(duration: number) {
|
|
209
|
-
this.checkpointAttestationDelay.record(duration);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
225
|
public recordCollectedAttestations(count: number, durationMs: number) {
|
|
213
226
|
this.collectedAttestions.record(count);
|
|
214
227
|
this.timeToCollectAttestations.record(Math.ceil(durationMs));
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
recordBuiltBlock(buildDurationMs: number, totalMana: number) {
|
|
230
|
+
recordBuiltBlock(buildDurationMs: number, totalMana: number, slot: SlotNumber) {
|
|
218
231
|
this.blockCounter.add(1, {
|
|
219
232
|
[Attributes.STATUS]: 'built',
|
|
220
233
|
});
|
|
221
234
|
this.blockBuildDuration.record(Math.ceil(buildDurationMs));
|
|
222
235
|
this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
|
|
236
|
+
|
|
237
|
+
// Only record inter-block time between blocks built within the same slot.
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
if (this.lastBlockBuiltTimestamp !== undefined && this.lastBlockBuiltSlot === slot) {
|
|
240
|
+
this.blockInterBlockTime.record(now - this.lastBlockBuiltTimestamp);
|
|
241
|
+
}
|
|
242
|
+
this.lastBlockBuiltTimestamp = now;
|
|
243
|
+
this.lastBlockBuiltSlot = slot;
|
|
223
244
|
}
|
|
224
245
|
|
|
225
246
|
recordFailedBlock() {
|
|
@@ -234,6 +255,26 @@ export class SequencerMetrics {
|
|
|
234
255
|
});
|
|
235
256
|
}
|
|
236
257
|
|
|
258
|
+
recordStateDuration(durationMs: number, state: SequencerState) {
|
|
259
|
+
this.stateDuration.record(Math.ceil(durationMs), {
|
|
260
|
+
[Attributes.SEQUENCER_STATE]: state,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
recordPipelineDepth(depth: number) {
|
|
265
|
+
this.pipelineDepth.record(depth);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
recordPipelineDiscard(count = 1) {
|
|
269
|
+
this.pipelineDiscards.add(count);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
recordPipelineParentCheckpointMismatch(reason: string) {
|
|
273
|
+
this.pipelineParentCheckpointMismatches.add(1, {
|
|
274
|
+
[Attributes.ERROR_TYPE]: reason,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
237
278
|
incOpenSlot(slot: SlotNumber, proposer: string) {
|
|
238
279
|
// sequencer went through the loop a second time. Noop
|
|
239
280
|
if (slot === this.lastSeenSlot) {
|
|
@@ -292,14 +333,6 @@ export class SequencerMetrics {
|
|
|
292
333
|
});
|
|
293
334
|
}
|
|
294
335
|
|
|
295
|
-
/** Records aggregate metrics for a completed checkpoint build. */
|
|
296
|
-
recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
|
|
297
|
-
this.checkpointBuildDuration.record(Math.ceil(durationMs));
|
|
298
|
-
this.checkpointBlockCount.record(blockCount);
|
|
299
|
-
this.checkpointTxCount.record(txCount);
|
|
300
|
-
this.checkpointTotalMana.record(totalMana);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
336
|
recordSlashingAttempt(actionCount: number) {
|
|
304
337
|
this.slashingAttempts.add(actionCount);
|
|
305
338
|
}
|