@aztec/sequencer-client 0.0.1-commit.2b2662070 → 0.0.1-commit.2c0ee1788
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/config.d.ts +2 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +12 -7
- package/dest/publisher/sequencer-publisher.d.ts +1 -1
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +2 -2
- package/dest/sequencer/checkpoint_proposal_job.d.ts +22 -3
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +361 -43
- package/dest/sequencer/events.d.ts +6 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +5 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +22 -0
- package/dest/sequencer/sequencer.d.ts +12 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +33 -8
- package/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +7 -6
- package/package.json +27 -27
- package/src/config.ts +11 -4
- package/src/publisher/sequencer-publisher.ts +2 -4
- package/src/sequencer/README.md +20 -21
- package/src/sequencer/checkpoint_proposal_job.ts +414 -44
- package/src/sequencer/events.ts +5 -0
- package/src/sequencer/metrics.ts +29 -0
- package/src/sequencer/sequencer.ts +37 -9
- package/src/test/utils.ts +28 -10
package/src/sequencer/events.ts
CHANGED
|
@@ -24,4 +24,9 @@ export type SequencerEvents = {
|
|
|
24
24
|
}) => void;
|
|
25
25
|
['checkpoint-published']: (args: { checkpoint: CheckpointNumber; slot: SlotNumber }) => void;
|
|
26
26
|
['checkpoint-error']: (args: { error: Error }) => void;
|
|
27
|
+
['pipelined-checkpoint-discarded']: (args: {
|
|
28
|
+
slot: SlotNumber;
|
|
29
|
+
checkpointNumber: CheckpointNumber;
|
|
30
|
+
reason: string;
|
|
31
|
+
}) => void;
|
|
27
32
|
};
|
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;
|
|
@@ -46,6 +47,7 @@ export class SequencerMetrics {
|
|
|
46
47
|
private slashingAttempts: UpDownCounter;
|
|
47
48
|
private pipelineDepth: Gauge;
|
|
48
49
|
private pipelineDiscards: UpDownCounter;
|
|
50
|
+
private pipelineParentCheckpointMismatches: UpDownCounter;
|
|
49
51
|
|
|
50
52
|
// Fisherman fee analysis metrics
|
|
51
53
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
@@ -89,6 +91,8 @@ export class SequencerMetrics {
|
|
|
89
91
|
|
|
90
92
|
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
91
93
|
|
|
94
|
+
this.stateDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_DURATION);
|
|
95
|
+
|
|
92
96
|
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
|
|
93
97
|
|
|
94
98
|
this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
|
|
@@ -141,6 +145,19 @@ export class SequencerMetrics {
|
|
|
141
145
|
|
|
142
146
|
this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
|
|
143
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
|
+
);
|
|
144
161
|
this.pipelineDepth.record(0);
|
|
145
162
|
|
|
146
163
|
// Fisherman fee analysis metrics
|
|
@@ -238,6 +255,12 @@ export class SequencerMetrics {
|
|
|
238
255
|
});
|
|
239
256
|
}
|
|
240
257
|
|
|
258
|
+
recordStateDuration(durationMs: number, state: SequencerState) {
|
|
259
|
+
this.stateDuration.record(Math.ceil(durationMs), {
|
|
260
|
+
[Attributes.SEQUENCER_STATE]: state,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
241
264
|
recordPipelineDepth(depth: number) {
|
|
242
265
|
this.pipelineDepth.record(depth);
|
|
243
266
|
}
|
|
@@ -246,6 +269,12 @@ export class SequencerMetrics {
|
|
|
246
269
|
this.pipelineDiscards.add(count);
|
|
247
270
|
}
|
|
248
271
|
|
|
272
|
+
recordPipelineParentCheckpointMismatch(reason: string) {
|
|
273
|
+
this.pipelineParentCheckpointMismatches.add(1, {
|
|
274
|
+
[Attributes.ERROR_TYPE]: reason,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
249
278
|
incOpenSlot(slot: SlotNumber, proposer: string) {
|
|
250
279
|
// sequencer went through the loop a second time. Noop
|
|
251
280
|
if (slot === this.lastSeenSlot) {
|
|
@@ -6,7 +6,7 @@ import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/f
|
|
|
6
6
|
import { merge, omit, pick } from '@aztec/foundation/collection';
|
|
7
7
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
8
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
9
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
9
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
10
10
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
11
11
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
12
12
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
@@ -14,6 +14,7 @@ import type { P2P } from '@aztec/p2p';
|
|
|
14
14
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
15
15
|
import type { BlockData, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
16
16
|
import type { Checkpoint, ProposedCheckpointData } from '@aztec/stdlib/checkpoint';
|
|
17
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
17
18
|
import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
18
19
|
import {
|
|
19
20
|
type ResolvedSequencerConfig,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
type WorldStateSynchronizer,
|
|
23
24
|
} from '@aztec/stdlib/interfaces/server';
|
|
24
25
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
26
|
+
import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p';
|
|
25
27
|
import { pickFromSchema } from '@aztec/stdlib/schemas';
|
|
26
28
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
27
29
|
import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
@@ -56,8 +58,11 @@ export { SequencerState };
|
|
|
56
58
|
export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<SequencerEvents>) {
|
|
57
59
|
private runningPromise?: RunningPromise;
|
|
58
60
|
private state = SequencerState.STOPPED;
|
|
61
|
+
private stateSlotNumber: SlotNumber | undefined;
|
|
62
|
+
private stateEnteredAtMs = performance.now();
|
|
59
63
|
private metrics: SequencerMetrics;
|
|
60
64
|
private checkpointProposalJobMetrics: CheckpointProposalJobMetrics;
|
|
65
|
+
private readonly stateLog: Logger;
|
|
61
66
|
|
|
62
67
|
/** The last slot for which we attempted to perform our voting duties with degraded block production */
|
|
63
68
|
private lastSlotForFallbackVote: SlotNumber | undefined;
|
|
@@ -82,6 +87,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
82
87
|
|
|
83
88
|
/** Config for the sequencer */
|
|
84
89
|
protected config: ResolvedSequencerConfig = DefaultSequencerConfig;
|
|
90
|
+
private readonly signatureContext: CoordinationSignatureContext;
|
|
85
91
|
|
|
86
92
|
constructor(
|
|
87
93
|
protected publisherFactory: SequencerPublisherFactory,
|
|
@@ -97,17 +103,22 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
97
103
|
protected dateProvider: DateProvider,
|
|
98
104
|
protected epochCache: EpochCache,
|
|
99
105
|
protected rollupContract: RollupContract,
|
|
100
|
-
config: SequencerConfig,
|
|
106
|
+
config: SequencerConfig & Pick<ChainConfig, 'l1ChainId' | 'l1Contracts'>,
|
|
101
107
|
protected telemetry: TelemetryClient = getTelemetryClient(),
|
|
102
108
|
protected log = createLogger('sequencer'),
|
|
103
109
|
) {
|
|
104
110
|
super();
|
|
111
|
+
this.stateLog = log.createChild('state');
|
|
105
112
|
|
|
106
113
|
// Add [FISHERMAN] prefix to logger if in fisherman mode
|
|
107
114
|
if (config.fishermanMode) {
|
|
108
115
|
this.log = log.createChild('[FISHERMAN]');
|
|
109
116
|
}
|
|
110
117
|
|
|
118
|
+
this.signatureContext = {
|
|
119
|
+
chainId: config.l1ChainId,
|
|
120
|
+
rollupAddress: config.l1Contracts.rollupAddress,
|
|
121
|
+
};
|
|
111
122
|
this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
|
|
112
123
|
this.checkpointProposalJobMetrics = new CheckpointProposalJobMetrics(telemetry);
|
|
113
124
|
this.updateConfig(config);
|
|
@@ -377,7 +388,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
377
388
|
// and the archive at that checkpoint so L1 simulation sees the correct chain tip.
|
|
378
389
|
const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
379
390
|
l1SimulationOverridesBuilder.forPendingCheckpoint(parentCheckpointNumber).withPendingArchive(syncedTo.archive);
|
|
380
|
-
this.metrics.recordPipelineDepth(
|
|
391
|
+
this.metrics.recordPipelineDepth(syncedTo.checkpointNumber - syncedTo.checkpointedCheckpointNumber);
|
|
381
392
|
|
|
382
393
|
this.log.verbose(
|
|
383
394
|
`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`,
|
|
@@ -489,6 +500,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
489
500
|
this.checkpointsBuilder,
|
|
490
501
|
this.l2BlockSource,
|
|
491
502
|
this.l1Constants,
|
|
503
|
+
this.signatureContext,
|
|
492
504
|
this.config,
|
|
493
505
|
this.timetable,
|
|
494
506
|
this.slasherClient,
|
|
@@ -536,19 +548,35 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
536
548
|
this.timetable.assertTimeLeft(proposedState, secondsIntoSlot);
|
|
537
549
|
}
|
|
538
550
|
|
|
551
|
+
const oldState = this.state;
|
|
552
|
+
const oldStateSlotNumber = this.stateSlotNumber;
|
|
553
|
+
const stateChanged = proposedState !== oldState;
|
|
554
|
+
const transitionAtMs = performance.now();
|
|
555
|
+
const stateDurationMs = transitionAtMs - this.stateEnteredAtMs;
|
|
556
|
+
|
|
539
557
|
const boringStates = [SequencerState.IDLE, SequencerState.SYNCHRONIZING];
|
|
540
558
|
const logLevel =
|
|
541
|
-
boringStates.includes(proposedState) && boringStates.includes(
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
559
|
+
boringStates.includes(proposedState) && boringStates.includes(oldState) ? ('trace' as const) : ('debug' as const);
|
|
560
|
+
this.stateLog[logLevel](`Transitioning from ${oldState} to ${proposedState}`, {
|
|
561
|
+
oldState,
|
|
562
|
+
newState: proposedState,
|
|
563
|
+
slotNumber,
|
|
564
|
+
stateSlotNumber: oldStateSlotNumber,
|
|
565
|
+
secondsIntoSlot,
|
|
566
|
+
...(stateChanged && { stateDurationMs: Math.ceil(stateDurationMs) }),
|
|
567
|
+
});
|
|
545
568
|
|
|
546
569
|
this.emit('state-changed', {
|
|
547
|
-
oldState
|
|
570
|
+
oldState,
|
|
548
571
|
newState: proposedState,
|
|
549
572
|
secondsIntoSlot,
|
|
550
573
|
slot: slotNumber,
|
|
551
574
|
});
|
|
575
|
+
if (stateChanged) {
|
|
576
|
+
this.metrics.recordStateDuration(stateDurationMs, oldState);
|
|
577
|
+
this.stateEnteredAtMs = transitionAtMs;
|
|
578
|
+
this.stateSlotNumber = slotNumber;
|
|
579
|
+
}
|
|
552
580
|
this.state = proposedState;
|
|
553
581
|
}
|
|
554
582
|
|
|
@@ -581,7 +609,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
581
609
|
this.p2pClient.getStatus().then(p2p => p2p.syncedToL2Block),
|
|
582
610
|
this.l1ToL2MessageSource.getL2Tips().then(t => ({ proposed: t.proposed, checkpointed: t.checkpointed })),
|
|
583
611
|
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
584
|
-
this.l2BlockSource.
|
|
612
|
+
this.l2BlockSource.getLastProposedCheckpoint(),
|
|
585
613
|
] as const);
|
|
586
614
|
|
|
587
615
|
const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] =
|
package/src/test/utils.ts
CHANGED
|
@@ -10,7 +10,11 @@ import { PublicDataWrite } from '@aztec/stdlib/avm';
|
|
|
10
10
|
import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
|
|
11
11
|
import { BlockProposal, CheckpointAttestation, CheckpointProposal, ConsensusPayload } from '@aztec/stdlib/p2p';
|
|
12
12
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
TEST_COORDINATION_SIGNATURE_CONTEXT,
|
|
15
|
+
makeAppendOnlyTreeSnapshot,
|
|
16
|
+
mockTxForRollup,
|
|
17
|
+
} from '@aztec/stdlib/testing';
|
|
14
18
|
import { BlockHeader, GlobalVariables, type Tx, makeProcessedTxFromPrivateOnlyTx } from '@aztec/stdlib/tx';
|
|
15
19
|
|
|
16
20
|
import type { MockProxy } from 'jest-mock-extended';
|
|
@@ -109,6 +113,7 @@ export function createBlockProposal(block: L2Block, signature: Signature): Block
|
|
|
109
113
|
block.archive.root,
|
|
110
114
|
txHashes,
|
|
111
115
|
signature,
|
|
116
|
+
TEST_COORDINATION_SIGNATURE_CONTEXT,
|
|
112
117
|
);
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -123,12 +128,19 @@ export function createCheckpointProposal(
|
|
|
123
128
|
): CheckpointProposal {
|
|
124
129
|
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
125
130
|
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
126
|
-
return new CheckpointProposal(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
return new CheckpointProposal(
|
|
132
|
+
checkpointHeader,
|
|
133
|
+
block.archive.root,
|
|
134
|
+
feeAssetPriceModifier,
|
|
135
|
+
checkpointSignature,
|
|
136
|
+
TEST_COORDINATION_SIGNATURE_CONTEXT,
|
|
137
|
+
{
|
|
138
|
+
blockHeader: block.header,
|
|
139
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
140
|
+
txHashes,
|
|
141
|
+
signature: blockSignature ?? checkpointSignature, // Use checkpoint signature as block signature if not provided
|
|
142
|
+
},
|
|
143
|
+
);
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
/**
|
|
@@ -143,10 +155,16 @@ export function createCheckpointAttestation(
|
|
|
143
155
|
feeAssetPriceModifier: bigint = 0n,
|
|
144
156
|
): CheckpointAttestation {
|
|
145
157
|
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
146
|
-
const payload = new ConsensusPayload(
|
|
158
|
+
const payload = new ConsensusPayload(
|
|
159
|
+
checkpointHeader,
|
|
160
|
+
block.archive.root,
|
|
161
|
+
feeAssetPriceModifier,
|
|
162
|
+
TEST_COORDINATION_SIGNATURE_CONTEXT,
|
|
163
|
+
);
|
|
147
164
|
const attestation = new CheckpointAttestation(payload, signature, signature);
|
|
148
|
-
//
|
|
149
|
-
(attestation as any).
|
|
165
|
+
// Bypass signature recovery for testing since we use random signatures
|
|
166
|
+
(attestation as any).getSender = () => sender;
|
|
167
|
+
(attestation as any).getProposer = () => sender;
|
|
150
168
|
return attestation;
|
|
151
169
|
}
|
|
152
170
|
|