@aztec/sequencer-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.1142ef1
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 +5 -6
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +8 -1
- package/dest/global_variable_builder/global_builder.d.ts +4 -4
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +12 -12
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +2 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +15 -86
- package/dest/publisher/sequencer-publisher.d.ts +19 -18
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +443 -53
- package/dest/sequencer/block_builder.d.ts +1 -1
- package/dest/sequencer/block_builder.d.ts.map +1 -1
- package/dest/sequencer/block_builder.js +1 -1
- package/dest/sequencer/checkpoint_proposal_job.d.ts +11 -8
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +478 -29
- package/dest/sequencer/index.d.ts +1 -2
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +0 -1
- package/dest/sequencer/metrics.d.ts +3 -3
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +30 -121
- package/dest/sequencer/sequencer.d.ts +13 -11
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +425 -37
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +10 -2
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +23 -0
- package/dest/test/utils.d.ts +8 -4
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +21 -12
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +4 -5
- package/src/config.ts +7 -0
- package/src/global_variable_builder/global_builder.ts +12 -12
- package/src/index.ts +0 -3
- package/src/publisher/sequencer-publisher-factory.ts +1 -1
- package/src/publisher/sequencer-publisher-metrics.ts +14 -70
- package/src/publisher/sequencer-publisher.ts +87 -77
- package/src/sequencer/block_builder.ts +1 -2
- package/src/sequencer/checkpoint_proposal_job.ts +79 -40
- package/src/sequencer/index.ts +0 -1
- package/src/sequencer/metrics.ts +23 -131
- package/src/sequencer/sequencer.ts +46 -35
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +34 -2
- package/src/test/utils.ts +39 -19
- package/dest/sequencer/checkpoint_builder.d.ts +0 -63
- package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
- package/dest/sequencer/checkpoint_builder.js +0 -131
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- package/src/sequencer/checkpoint_builder.ts +0 -217
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -2,6 +2,7 @@ import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB } from '@aztec/constants';
|
|
|
2
2
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
3
|
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
4
4
|
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
5
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
7
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
7
8
|
import { filter } from '@aztec/foundation/iterator';
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
CommitteeAttestation,
|
|
16
17
|
CommitteeAttestationsAndSigners,
|
|
17
18
|
L2BlockNew,
|
|
19
|
+
type L2BlockSink,
|
|
18
20
|
MaliciousCommitteeAttestationsAndSigners,
|
|
19
21
|
} from '@aztec/stdlib/block';
|
|
20
22
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
@@ -25,18 +27,17 @@ import type {
|
|
|
25
27
|
ResolvedSequencerConfig,
|
|
26
28
|
WorldStateSynchronizer,
|
|
27
29
|
} from '@aztec/stdlib/interfaces/server';
|
|
28
|
-
import type
|
|
29
|
-
import type {
|
|
30
|
+
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
31
|
+
import type { BlockProposalOptions, CheckpointProposal, CheckpointProposalOptions } from '@aztec/stdlib/p2p';
|
|
30
32
|
import { orderAttestations } from '@aztec/stdlib/p2p';
|
|
31
|
-
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
32
33
|
import type { L2BlockBuiltStats } from '@aztec/stdlib/stats';
|
|
33
34
|
import { type FailedTx, Tx } from '@aztec/stdlib/tx';
|
|
34
35
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
35
|
-
import type
|
|
36
|
+
import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
37
|
+
import { CheckpointBuilder, type FullNodeCheckpointsBuilder, type ValidatorClient } from '@aztec/validator-client';
|
|
36
38
|
|
|
37
39
|
import type { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
38
|
-
import type {
|
|
39
|
-
import { CheckpointBuilder, type FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
|
|
40
|
+
import type { InvalidateCheckpointRequest, SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
40
41
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
41
42
|
import { SequencerInterruptedError } from './errors.js';
|
|
42
43
|
import type { SequencerEvents } from './events.js';
|
|
@@ -54,7 +55,7 @@ const TXS_POLLING_MS = 500;
|
|
|
54
55
|
* as well as enqueueing votes for slashing and governance proposals. This class is created from
|
|
55
56
|
* the Sequencer once the check for being the proposer for the slot has succeeded.
|
|
56
57
|
*/
|
|
57
|
-
export class CheckpointProposalJob {
|
|
58
|
+
export class CheckpointProposalJob implements Traceable {
|
|
58
59
|
constructor(
|
|
59
60
|
private readonly slot: SlotNumber,
|
|
60
61
|
private readonly checkpointNumber: CheckpointNumber,
|
|
@@ -63,13 +64,14 @@ export class CheckpointProposalJob {
|
|
|
63
64
|
private readonly proposer: EthAddress | undefined,
|
|
64
65
|
private readonly publisher: SequencerPublisher,
|
|
65
66
|
private readonly attestorAddress: EthAddress,
|
|
66
|
-
private readonly
|
|
67
|
+
private readonly invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
|
|
67
68
|
private readonly validatorClient: ValidatorClient,
|
|
68
69
|
private readonly globalsBuilder: GlobalVariableBuilder,
|
|
69
70
|
private readonly p2pClient: P2P,
|
|
70
71
|
private readonly worldState: WorldStateSynchronizer,
|
|
71
72
|
private readonly l1ToL2MessageSource: L1ToL2MessageSource,
|
|
72
73
|
private readonly checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
74
|
+
private readonly blockSink: L2BlockSink,
|
|
73
75
|
private readonly l1Constants: SequencerRollupConstants,
|
|
74
76
|
protected config: ResolvedSequencerConfig,
|
|
75
77
|
protected timetable: SequencerTimetable,
|
|
@@ -80,12 +82,14 @@ export class CheckpointProposalJob {
|
|
|
80
82
|
private readonly eventEmitter: TypedEventEmitter<SequencerEvents>,
|
|
81
83
|
private readonly setStateFn: (state: SequencerState, slot?: SlotNumber) => void,
|
|
82
84
|
protected readonly log: Logger,
|
|
85
|
+
public readonly tracer: Tracer,
|
|
83
86
|
) {}
|
|
84
87
|
|
|
85
88
|
/**
|
|
86
89
|
* Executes the checkpoint proposal job.
|
|
87
90
|
* Returns the published checkpoint if successful, undefined otherwise.
|
|
88
91
|
*/
|
|
92
|
+
@trackSpan('CheckpointProposalJob.execute')
|
|
89
93
|
public async execute(): Promise<Checkpoint | undefined> {
|
|
90
94
|
// Enqueue governance and slashing votes (returns promises that will be awaited later)
|
|
91
95
|
// In fisherman mode, we simulate slashing but don't actually publish to L1
|
|
@@ -108,6 +112,10 @@ export class CheckpointProposalJob {
|
|
|
108
112
|
// Wait until the voting promises have resolved, so all requests are enqueued (not sent)
|
|
109
113
|
await Promise.all(votesPromises);
|
|
110
114
|
|
|
115
|
+
if (checkpoint) {
|
|
116
|
+
this.metrics.recordBlockProposalSuccess();
|
|
117
|
+
}
|
|
118
|
+
|
|
111
119
|
// Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
|
|
112
120
|
if (this.config.fishermanMode) {
|
|
113
121
|
await this.handleCheckpointEndAsFisherman(checkpoint);
|
|
@@ -128,6 +136,13 @@ export class CheckpointProposalJob {
|
|
|
128
136
|
}
|
|
129
137
|
}
|
|
130
138
|
|
|
139
|
+
@trackSpan('CheckpointProposalJob.proposeCheckpoint', function () {
|
|
140
|
+
return {
|
|
141
|
+
// nullish operator needed for tests
|
|
142
|
+
[Attributes.COINBASE]: this.validatorClient.getCoinbaseForAttestor(this.attestorAddress)?.toString(),
|
|
143
|
+
[Attributes.SLOT_NUMBER]: this.slot,
|
|
144
|
+
};
|
|
145
|
+
})
|
|
131
146
|
private async proposeCheckpoint(): Promise<Checkpoint | undefined> {
|
|
132
147
|
try {
|
|
133
148
|
// Get operator configured coinbase and fee recipient for this attestor
|
|
@@ -138,9 +153,9 @@ export class CheckpointProposalJob {
|
|
|
138
153
|
this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.slot);
|
|
139
154
|
this.metrics.incOpenSlot(this.slot, this.proposer?.toString() ?? 'unknown');
|
|
140
155
|
|
|
141
|
-
// Enqueues
|
|
142
|
-
if (this.
|
|
143
|
-
this.publisher.
|
|
156
|
+
// Enqueues checkpoint invalidation (constant for the whole slot)
|
|
157
|
+
if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
|
|
158
|
+
this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
|
|
144
159
|
}
|
|
145
160
|
|
|
146
161
|
// Create checkpoint builder for the slot
|
|
@@ -150,8 +165,9 @@ export class CheckpointProposalJob {
|
|
|
150
165
|
this.slot,
|
|
151
166
|
);
|
|
152
167
|
|
|
153
|
-
// Collect L1 to L2 messages for the checkpoint
|
|
168
|
+
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
154
169
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
170
|
+
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
155
171
|
|
|
156
172
|
// Create a long-lived forked world state for the checkpoint builder
|
|
157
173
|
using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 });
|
|
@@ -170,10 +186,16 @@ export class CheckpointProposalJob {
|
|
|
170
186
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
171
187
|
};
|
|
172
188
|
|
|
189
|
+
const checkpointProposalOptions: CheckpointProposalOptions = {
|
|
190
|
+
publishFullTxs: !!this.config.publishTxsWithProposals,
|
|
191
|
+
broadcastInvalidCheckpointProposal: this.config.broadcastInvalidBlockProposal,
|
|
192
|
+
};
|
|
193
|
+
|
|
173
194
|
// Main loop: build blocks for the checkpoint
|
|
174
|
-
const { blocksInCheckpoint,
|
|
195
|
+
const { blocksInCheckpoint, blockPendingBroadcast } = await this.buildBlocksForCheckpoint(
|
|
175
196
|
checkpointBuilder,
|
|
176
197
|
checkpointGlobalVariables.timestamp,
|
|
198
|
+
inHash,
|
|
177
199
|
blockProposalOptions,
|
|
178
200
|
);
|
|
179
201
|
|
|
@@ -203,22 +225,30 @@ export class CheckpointProposalJob {
|
|
|
203
225
|
return checkpoint;
|
|
204
226
|
}
|
|
205
227
|
|
|
206
|
-
//
|
|
228
|
+
// Include the block pending broadcast in the checkpoint proposal if any
|
|
229
|
+
const lastBlock = blockPendingBroadcast && {
|
|
230
|
+
blockHeader: blockPendingBroadcast.block.header,
|
|
231
|
+
indexWithinCheckpoint: blockPendingBroadcast.block.indexWithinCheckpoint,
|
|
232
|
+
txs: blockPendingBroadcast.txs,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Create the checkpoint proposal and broadcast it
|
|
207
236
|
const proposal = await this.validatorClient.createCheckpointProposal(
|
|
208
237
|
checkpoint.header,
|
|
209
238
|
checkpoint.archive.root,
|
|
210
|
-
|
|
239
|
+
lastBlock,
|
|
211
240
|
this.proposer,
|
|
212
|
-
|
|
241
|
+
checkpointProposalOptions,
|
|
213
242
|
);
|
|
243
|
+
|
|
214
244
|
const blockProposedAt = this.dateProvider.now();
|
|
215
|
-
await this.p2pClient.
|
|
245
|
+
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
216
246
|
|
|
217
247
|
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.slot);
|
|
218
248
|
const attestations = await this.waitForAttestations(proposal);
|
|
219
249
|
const blockAttestedAt = this.dateProvider.now();
|
|
220
250
|
|
|
221
|
-
this.metrics.
|
|
251
|
+
this.metrics.recordCheckpointAttestationDelay(blockAttestedAt - blockProposedAt);
|
|
222
252
|
|
|
223
253
|
// Proposer must sign over the attestations before pushing them to L1
|
|
224
254
|
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
@@ -231,7 +261,7 @@ export class CheckpointProposalJob {
|
|
|
231
261
|
const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
|
|
232
262
|
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
233
263
|
txTimeoutAt,
|
|
234
|
-
|
|
264
|
+
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
235
265
|
});
|
|
236
266
|
|
|
237
267
|
return checkpoint;
|
|
@@ -244,20 +274,22 @@ export class CheckpointProposalJob {
|
|
|
244
274
|
/**
|
|
245
275
|
* Builds blocks for a checkpoint within the current slot.
|
|
246
276
|
*/
|
|
277
|
+
@trackSpan('CheckpointProposalJob.buildBlocksForCheckpoint')
|
|
247
278
|
private async buildBlocksForCheckpoint(
|
|
248
279
|
checkpointBuilder: CheckpointBuilder,
|
|
249
280
|
timestamp: bigint,
|
|
281
|
+
inHash: Fr,
|
|
250
282
|
blockProposalOptions: BlockProposalOptions,
|
|
251
283
|
): Promise<{
|
|
252
284
|
blocksInCheckpoint: L2BlockNew[];
|
|
253
|
-
|
|
285
|
+
blockPendingBroadcast: { block: L2BlockNew; txs: Tx[] } | undefined;
|
|
254
286
|
}> {
|
|
255
287
|
const blocksInCheckpoint: L2BlockNew[] = [];
|
|
256
288
|
const txHashesAlreadyIncluded = new Set<string>();
|
|
257
289
|
const initialBlockNumber = BlockNumber(this.syncedToBlockNumber + 1);
|
|
258
290
|
|
|
259
291
|
// Last block in the checkpoint will usually be flagged as pending broadcast, so we send it along with the checkpoint proposal
|
|
260
|
-
let
|
|
292
|
+
let blockPendingBroadcast: { block: L2BlockNew; txs: Tx[] } | undefined = undefined;
|
|
261
293
|
|
|
262
294
|
while (true) {
|
|
263
295
|
const blocksBuilt = blocksInCheckpoint.length;
|
|
@@ -327,17 +359,17 @@ export class CheckpointProposalJob {
|
|
|
327
359
|
blockNumber,
|
|
328
360
|
blocksBuilt,
|
|
329
361
|
});
|
|
330
|
-
|
|
362
|
+
blockPendingBroadcast = { block, txs: usedTxs };
|
|
331
363
|
break;
|
|
332
364
|
}
|
|
333
365
|
|
|
334
366
|
// For non-last blocks, broadcast the block proposal (unless we're in fisherman mode)
|
|
335
367
|
// If the block is the last one, we'll broadcast it along with the checkpoint at the end of the loop
|
|
336
368
|
if (!this.config.fishermanMode) {
|
|
337
|
-
// TODO(palla/mbps): Wire this to the new p2p API once available
|
|
338
369
|
const proposal = await this.validatorClient.createBlockProposal(
|
|
339
|
-
block.header
|
|
340
|
-
|
|
370
|
+
block.header,
|
|
371
|
+
block.indexWithinCheckpoint,
|
|
372
|
+
inHash,
|
|
341
373
|
block.archive.root,
|
|
342
374
|
usedTxs,
|
|
343
375
|
this.proposer,
|
|
@@ -355,13 +387,11 @@ export class CheckpointProposalJob {
|
|
|
355
387
|
blocksBuilt: blocksInCheckpoint.length,
|
|
356
388
|
});
|
|
357
389
|
|
|
358
|
-
return {
|
|
359
|
-
blocksInCheckpoint,
|
|
360
|
-
pendingBroadcast,
|
|
361
|
-
};
|
|
390
|
+
return { blocksInCheckpoint, blockPendingBroadcast };
|
|
362
391
|
}
|
|
363
392
|
|
|
364
393
|
/** Sleeps until it is time to produce the next block in the slot */
|
|
394
|
+
@trackSpan('CheckpointProposalJob.waitUntilNextSubslot')
|
|
365
395
|
private async waitUntilNextSubslot(nextSubslotStart: number) {
|
|
366
396
|
this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.slot);
|
|
367
397
|
this.log.verbose(`Waiting until time for the next block at ${nextSubslotStart}s into slot`, { slot: this.slot });
|
|
@@ -369,6 +399,7 @@ export class CheckpointProposalJob {
|
|
|
369
399
|
}
|
|
370
400
|
|
|
371
401
|
/** Builds a single block. Called from the main block building loop. */
|
|
402
|
+
@trackSpan('CheckpointProposalJob.buildSingleBlock')
|
|
372
403
|
private async buildSingleBlock(
|
|
373
404
|
checkpointBuilder: CheckpointBuilder,
|
|
374
405
|
opts: {
|
|
@@ -480,6 +511,7 @@ export class CheckpointProposalJob {
|
|
|
480
511
|
}
|
|
481
512
|
|
|
482
513
|
/** Waits until minTxs are available on the pool for building a block. */
|
|
514
|
+
@trackSpan('CheckpointProposalJob.waitForMinTxs')
|
|
483
515
|
private async waitForMinTxs(opts: {
|
|
484
516
|
forceCreate?: boolean;
|
|
485
517
|
blockNumber: BlockNumber;
|
|
@@ -520,7 +552,8 @@ export class CheckpointProposalJob {
|
|
|
520
552
|
* Waits for enough attestations to be collected via p2p.
|
|
521
553
|
* This is run after all blocks for the checkpoint have been built.
|
|
522
554
|
*/
|
|
523
|
-
|
|
555
|
+
@trackSpan('CheckpointProposalJob.waitForAttestations')
|
|
556
|
+
private async waitForAttestations(proposal: CheckpointProposal): Promise<CommitteeAttestationsAndSigners> {
|
|
524
557
|
if (this.config.fishermanMode) {
|
|
525
558
|
this.log.debug('Skipping attestation collection in fisherman mode');
|
|
526
559
|
return CommitteeAttestationsAndSigners.empty();
|
|
@@ -569,8 +602,7 @@ export class CheckpointProposalJob {
|
|
|
569
602
|
|
|
570
603
|
// Manipulate the attestations if we've been configured to do so
|
|
571
604
|
if (this.config.injectFakeAttestation || this.config.shuffleAttestationOrdering) {
|
|
572
|
-
|
|
573
|
-
return this.manipulateAttestations(checkpoint, epoch, seed, committee, sorted);
|
|
605
|
+
return this.manipulateAttestations(proposal.slotNumber, epoch, seed, committee, sorted);
|
|
574
606
|
}
|
|
575
607
|
|
|
576
608
|
return new CommitteeAttestationsAndSigners(sorted);
|
|
@@ -586,7 +618,7 @@ export class CheckpointProposalJob {
|
|
|
586
618
|
|
|
587
619
|
/** Breaks the attestations before publishing based on attack configs */
|
|
588
620
|
private manipulateAttestations(
|
|
589
|
-
|
|
621
|
+
slotNumber: SlotNumber,
|
|
590
622
|
epoch: EpochNumber,
|
|
591
623
|
seed: bigint,
|
|
592
624
|
committee: EthAddress[],
|
|
@@ -594,7 +626,6 @@ export class CheckpointProposalJob {
|
|
|
594
626
|
) {
|
|
595
627
|
// Compute the proposer index in the committee, since we dont want to tweak it.
|
|
596
628
|
// Otherwise, the L1 rollup contract will reject the block outright.
|
|
597
|
-
const { slotNumber } = checkpoint;
|
|
598
629
|
const proposerIndex = Number(
|
|
599
630
|
this.epochCache.computeProposerIndex(slotNumber, epoch, seed, BigInt(committee.length)),
|
|
600
631
|
);
|
|
@@ -643,16 +674,24 @@ export class CheckpointProposalJob {
|
|
|
643
674
|
}
|
|
644
675
|
|
|
645
676
|
/**
|
|
646
|
-
*
|
|
647
|
-
*
|
|
677
|
+
* Adds the proposed block to the archiver so it's available via P2P.
|
|
678
|
+
* Gossip doesn't echo messages back to the sender, so the proposer's archiver/world-state
|
|
679
|
+
* would never receive its own block without this explicit sync.
|
|
648
680
|
*/
|
|
649
681
|
private async syncProposedBlockToArchiver(block: L2BlockNew): Promise<void> {
|
|
650
|
-
|
|
682
|
+
// TODO(palla/mbps): Change default to false once block sync is stable.
|
|
683
|
+
if (this.config.skipPushProposedBlocksToArchiver !== false) {
|
|
684
|
+
this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
|
|
685
|
+
blockNumber: block.number,
|
|
686
|
+
slot: block.header.globalVariables.slotNumber,
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
this.log.debug(`Syncing proposed block ${block.number} to archiver`, {
|
|
651
691
|
blockNumber: block.number,
|
|
652
692
|
slot: block.header.globalVariables.slotNumber,
|
|
653
693
|
});
|
|
654
|
-
|
|
655
|
-
await Promise.resolve();
|
|
694
|
+
await this.blockSink.addBlock(block);
|
|
656
695
|
}
|
|
657
696
|
|
|
658
697
|
/** Runs fee analysis and logs checkpoint outcome as fisherman */
|
|
@@ -669,7 +708,6 @@ export class CheckpointProposalJob {
|
|
|
669
708
|
...checkpoint.getStats(),
|
|
670
709
|
feeAnalysisId: feeAnalysis?.id,
|
|
671
710
|
});
|
|
672
|
-
this.metrics.recordBlockProposalSuccess();
|
|
673
711
|
} else {
|
|
674
712
|
this.log.warn(`Validation block building FAILED for slot ${this.slot}`, {
|
|
675
713
|
slot: this.slot,
|
|
@@ -682,6 +720,7 @@ export class CheckpointProposalJob {
|
|
|
682
720
|
}
|
|
683
721
|
|
|
684
722
|
/** Waits until a specific time within the current slot */
|
|
723
|
+
@trackSpan('CheckpointProposalJob.waitUntilTimeInSlot')
|
|
685
724
|
protected async waitUntilTimeInSlot(targetSecondsIntoSlot: number): Promise<void> {
|
|
686
725
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
687
726
|
const targetTimestamp = slotStartTimestamp + targetSecondsIntoSlot;
|
package/src/sequencer/index.ts
CHANGED
package/src/sequencer/metrics.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
type TelemetryClient,
|
|
12
12
|
type Tracer,
|
|
13
13
|
type UpDownCounter,
|
|
14
|
-
ValueType,
|
|
15
14
|
} from '@aztec/telemetry-client';
|
|
16
15
|
|
|
17
16
|
import { type Hex, formatUnits } from 'viem';
|
|
@@ -44,7 +43,7 @@ export class SequencerMetrics {
|
|
|
44
43
|
private blockProposalPrecheckFailed: UpDownCounter;
|
|
45
44
|
private checkpointSuccess: UpDownCounter;
|
|
46
45
|
private slashingAttempts: UpDownCounter;
|
|
47
|
-
private
|
|
46
|
+
private checkpointAttestationDelay: Histogram;
|
|
48
47
|
|
|
49
48
|
// Fisherman fee analysis metrics
|
|
50
49
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
@@ -70,33 +69,13 @@ export class SequencerMetrics {
|
|
|
70
69
|
|
|
71
70
|
this.blockCounter = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
|
|
72
71
|
|
|
73
|
-
this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION
|
|
74
|
-
unit: 'ms',
|
|
75
|
-
description: 'Duration to build a block',
|
|
76
|
-
valueType: ValueType.INT,
|
|
77
|
-
});
|
|
72
|
+
this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
|
|
78
73
|
|
|
79
|
-
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND
|
|
80
|
-
unit: 'mana/s',
|
|
81
|
-
description: 'Mana per second when building a block',
|
|
82
|
-
valueType: ValueType.INT,
|
|
83
|
-
});
|
|
74
|
+
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
|
|
84
75
|
|
|
85
|
-
this.stateTransitionBufferDuration = this.meter.createHistogram(
|
|
86
|
-
Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION,
|
|
87
|
-
{
|
|
88
|
-
unit: 'ms',
|
|
89
|
-
description:
|
|
90
|
-
'The time difference between when the sequencer needed to transition to a new state and when it actually did.',
|
|
91
|
-
valueType: ValueType.INT,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
76
|
+
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
94
77
|
|
|
95
|
-
this.
|
|
96
|
-
unit: 'ms',
|
|
97
|
-
description: 'The time difference between block proposal and minimal attestation count reached,',
|
|
98
|
-
valueType: ValueType.INT,
|
|
99
|
-
});
|
|
78
|
+
this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
|
|
100
79
|
|
|
101
80
|
// Init gauges and counters
|
|
102
81
|
this.blockCounter.add(0, {
|
|
@@ -106,152 +85,65 @@ export class SequencerMetrics {
|
|
|
106
85
|
[Attributes.STATUS]: 'built',
|
|
107
86
|
});
|
|
108
87
|
|
|
109
|
-
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS
|
|
110
|
-
valueType: ValueType.DOUBLE,
|
|
111
|
-
description: 'The rewards earned',
|
|
112
|
-
});
|
|
88
|
+
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS);
|
|
113
89
|
|
|
114
|
-
this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT
|
|
115
|
-
valueType: ValueType.INT,
|
|
116
|
-
description: 'The number of slots this sequencer was selected for',
|
|
117
|
-
});
|
|
90
|
+
this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT);
|
|
118
91
|
|
|
119
92
|
/**
|
|
120
93
|
* NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
|
|
121
94
|
* Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
|
|
122
95
|
*/
|
|
123
|
-
this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT
|
|
124
|
-
valueType: ValueType.INT,
|
|
125
|
-
description: 'The number of slots this sequencer has filled',
|
|
126
|
-
});
|
|
96
|
+
this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT);
|
|
127
97
|
|
|
128
|
-
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION
|
|
129
|
-
description: 'The time spent collecting attestations from committee members',
|
|
130
|
-
unit: 'ms',
|
|
131
|
-
valueType: ValueType.INT,
|
|
132
|
-
});
|
|
98
|
+
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
|
|
133
99
|
|
|
134
|
-
this.allowanceToCollectAttestations = this.meter.createGauge(
|
|
135
|
-
Metrics.SEQUENCER_COLLECT_ATTESTATIONS_TIME_ALLOWANCE,
|
|
136
|
-
{
|
|
137
|
-
description: 'Maximum amount of time to collect attestations',
|
|
138
|
-
unit: 'ms',
|
|
139
|
-
valueType: ValueType.INT,
|
|
140
|
-
},
|
|
141
|
-
);
|
|
100
|
+
this.allowanceToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_TIME_ALLOWANCE);
|
|
142
101
|
|
|
143
|
-
this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT
|
|
144
|
-
valueType: ValueType.INT,
|
|
145
|
-
description: 'The minimum number of attestations required to publish a block',
|
|
146
|
-
});
|
|
102
|
+
this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT);
|
|
147
103
|
|
|
148
|
-
this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT
|
|
149
|
-
valueType: ValueType.INT,
|
|
150
|
-
description: 'The minimum number of attestations required to publish a block',
|
|
151
|
-
});
|
|
104
|
+
this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
|
|
152
105
|
|
|
153
|
-
this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT
|
|
154
|
-
valueType: ValueType.INT,
|
|
155
|
-
description: 'The number of times block proposal failed (including validation builds)',
|
|
156
|
-
});
|
|
106
|
+
this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT);
|
|
157
107
|
|
|
158
|
-
this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT
|
|
159
|
-
valueType: ValueType.INT,
|
|
160
|
-
description: 'The number of times block proposal succeeded (including validation builds)',
|
|
161
|
-
});
|
|
108
|
+
this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT);
|
|
162
109
|
|
|
163
|
-
this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT
|
|
164
|
-
valueType: ValueType.INT,
|
|
165
|
-
description: 'The number of times checkpoint publishing succeeded',
|
|
166
|
-
});
|
|
110
|
+
this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
|
|
167
111
|
|
|
168
112
|
this.blockProposalPrecheckFailed = this.meter.createUpDownCounter(
|
|
169
113
|
Metrics.SEQUENCER_BLOCK_PROPOSAL_PRECHECK_FAILED_COUNT,
|
|
170
|
-
{
|
|
171
|
-
valueType: ValueType.INT,
|
|
172
|
-
description: 'The number of times block proposal pre-build checks failed',
|
|
173
|
-
},
|
|
174
114
|
);
|
|
175
115
|
|
|
176
|
-
this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT
|
|
177
|
-
valueType: ValueType.INT,
|
|
178
|
-
description: 'The number of slashing action attempts',
|
|
179
|
-
});
|
|
116
|
+
this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
|
|
180
117
|
|
|
181
118
|
// Fisherman fee analysis metrics
|
|
182
|
-
this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED
|
|
183
|
-
valueType: ValueType.INT,
|
|
184
|
-
description: 'Whether the transaction would have been included in the block',
|
|
185
|
-
});
|
|
119
|
+
this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED);
|
|
186
120
|
|
|
187
|
-
this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK
|
|
188
|
-
unit: 'ms',
|
|
189
|
-
description: 'Time in ms between fee analysis and block being mined',
|
|
190
|
-
valueType: ValueType.INT,
|
|
191
|
-
});
|
|
121
|
+
this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
|
|
192
122
|
|
|
193
|
-
this.fishermanPendingBlobTxCount = this.meter.createHistogram(
|
|
194
|
-
Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_TX_COUNT,
|
|
195
|
-
{
|
|
196
|
-
description: 'Number of blob transactions seen in the pending block',
|
|
197
|
-
valueType: ValueType.INT,
|
|
198
|
-
},
|
|
199
|
-
);
|
|
123
|
+
this.fishermanPendingBlobTxCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_TX_COUNT);
|
|
200
124
|
|
|
201
125
|
this.fishermanIncludedBlobTxCount = this.meter.createHistogram(
|
|
202
126
|
Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_TX_COUNT,
|
|
203
|
-
{
|
|
204
|
-
description: 'Number of blob transactions that got included in the mined block',
|
|
205
|
-
valueType: ValueType.INT,
|
|
206
|
-
},
|
|
207
127
|
);
|
|
208
128
|
|
|
209
129
|
this.fishermanCalculatedPriorityFee = this.meter.createHistogram(
|
|
210
130
|
Metrics.FISHERMAN_FEE_ANALYSIS_CALCULATED_PRIORITY_FEE,
|
|
211
|
-
{
|
|
212
|
-
unit: 'gwei',
|
|
213
|
-
description: 'Priority fee calculated by each strategy',
|
|
214
|
-
valueType: ValueType.DOUBLE,
|
|
215
|
-
},
|
|
216
131
|
);
|
|
217
132
|
|
|
218
|
-
this.fishermanPriorityFeeDelta = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PRIORITY_FEE_DELTA
|
|
219
|
-
unit: 'gwei',
|
|
220
|
-
description: 'Difference between our priority fee and minimum included priority fee',
|
|
221
|
-
valueType: ValueType.DOUBLE,
|
|
222
|
-
});
|
|
133
|
+
this.fishermanPriorityFeeDelta = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PRIORITY_FEE_DELTA);
|
|
223
134
|
|
|
224
|
-
this.fishermanEstimatedCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_COST
|
|
225
|
-
unit: 'eth',
|
|
226
|
-
description: 'Estimated total cost in ETH for the transaction with this strategy',
|
|
227
|
-
valueType: ValueType.DOUBLE,
|
|
228
|
-
});
|
|
135
|
+
this.fishermanEstimatedCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_COST);
|
|
229
136
|
|
|
230
137
|
this.fishermanEstimatedOverpayment = this.meter.createHistogram(
|
|
231
138
|
Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_OVERPAYMENT,
|
|
232
|
-
{
|
|
233
|
-
unit: 'eth',
|
|
234
|
-
description: 'Estimated overpayment in ETH vs minimum required for inclusion',
|
|
235
|
-
valueType: ValueType.DOUBLE,
|
|
236
|
-
},
|
|
237
139
|
);
|
|
238
140
|
|
|
239
141
|
this.fishermanMinedBlobTxPriorityFee = this.meter.createHistogram(
|
|
240
142
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_PRIORITY_FEE,
|
|
241
|
-
{
|
|
242
|
-
unit: 'gwei',
|
|
243
|
-
description: 'Priority fee per gas for blob transactions in mined blocks',
|
|
244
|
-
valueType: ValueType.DOUBLE,
|
|
245
|
-
},
|
|
246
143
|
);
|
|
247
144
|
|
|
248
145
|
this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
|
|
249
146
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
|
|
250
|
-
{
|
|
251
|
-
unit: 'eth',
|
|
252
|
-
description: 'Total cost in ETH for blob transactions in mined blocks',
|
|
253
|
-
valueType: ValueType.DOUBLE,
|
|
254
|
-
},
|
|
255
147
|
);
|
|
256
148
|
}
|
|
257
149
|
|
|
@@ -264,8 +156,8 @@ export class SequencerMetrics {
|
|
|
264
156
|
this.timeToCollectAttestations.record(0);
|
|
265
157
|
}
|
|
266
158
|
|
|
267
|
-
public
|
|
268
|
-
this.
|
|
159
|
+
public recordCheckpointAttestationDelay(duration: number) {
|
|
160
|
+
this.checkpointAttestationDelay.record(duration);
|
|
269
161
|
}
|
|
270
162
|
|
|
271
163
|
public recordCollectedAttestations(count: number, durationMs: number) {
|