@aztec/sequencer-client 0.0.1-commit.6c91f13 → 0.0.1-commit.96bb3f7
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 +4 -5
- 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-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 +17 -16
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +442 -49
- 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 +475 -28
- 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 +3 -4
- 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-metrics.ts +14 -70
- package/src/publisher/sequencer-publisher.ts +84 -73
- package/src/sequencer/block_builder.ts +1 -2
- package/src/sequencer/checkpoint_proposal_job.ts +75 -39
- 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
|
|
@@ -132,6 +136,13 @@ export class CheckpointProposalJob {
|
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
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
|
+
})
|
|
135
146
|
private async proposeCheckpoint(): Promise<Checkpoint | undefined> {
|
|
136
147
|
try {
|
|
137
148
|
// Get operator configured coinbase and fee recipient for this attestor
|
|
@@ -142,9 +153,9 @@ export class CheckpointProposalJob {
|
|
|
142
153
|
this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.slot);
|
|
143
154
|
this.metrics.incOpenSlot(this.slot, this.proposer?.toString() ?? 'unknown');
|
|
144
155
|
|
|
145
|
-
// Enqueues
|
|
146
|
-
if (this.
|
|
147
|
-
this.publisher.
|
|
156
|
+
// Enqueues checkpoint invalidation (constant for the whole slot)
|
|
157
|
+
if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
|
|
158
|
+
this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
|
|
148
159
|
}
|
|
149
160
|
|
|
150
161
|
// Create checkpoint builder for the slot
|
|
@@ -154,8 +165,9 @@ export class CheckpointProposalJob {
|
|
|
154
165
|
this.slot,
|
|
155
166
|
);
|
|
156
167
|
|
|
157
|
-
// Collect L1 to L2 messages for the checkpoint
|
|
168
|
+
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
158
169
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
170
|
+
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
159
171
|
|
|
160
172
|
// Create a long-lived forked world state for the checkpoint builder
|
|
161
173
|
using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 });
|
|
@@ -174,10 +186,16 @@ export class CheckpointProposalJob {
|
|
|
174
186
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
175
187
|
};
|
|
176
188
|
|
|
189
|
+
const checkpointProposalOptions: CheckpointProposalOptions = {
|
|
190
|
+
publishFullTxs: !!this.config.publishTxsWithProposals,
|
|
191
|
+
broadcastInvalidCheckpointProposal: this.config.broadcastInvalidBlockProposal,
|
|
192
|
+
};
|
|
193
|
+
|
|
177
194
|
// Main loop: build blocks for the checkpoint
|
|
178
|
-
const { blocksInCheckpoint,
|
|
195
|
+
const { blocksInCheckpoint, blockPendingBroadcast } = await this.buildBlocksForCheckpoint(
|
|
179
196
|
checkpointBuilder,
|
|
180
197
|
checkpointGlobalVariables.timestamp,
|
|
198
|
+
inHash,
|
|
181
199
|
blockProposalOptions,
|
|
182
200
|
);
|
|
183
201
|
|
|
@@ -207,22 +225,30 @@ export class CheckpointProposalJob {
|
|
|
207
225
|
return checkpoint;
|
|
208
226
|
}
|
|
209
227
|
|
|
210
|
-
//
|
|
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
|
|
211
236
|
const proposal = await this.validatorClient.createCheckpointProposal(
|
|
212
237
|
checkpoint.header,
|
|
213
238
|
checkpoint.archive.root,
|
|
214
|
-
|
|
239
|
+
lastBlock,
|
|
215
240
|
this.proposer,
|
|
216
|
-
|
|
241
|
+
checkpointProposalOptions,
|
|
217
242
|
);
|
|
243
|
+
|
|
218
244
|
const blockProposedAt = this.dateProvider.now();
|
|
219
|
-
await this.p2pClient.
|
|
245
|
+
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
220
246
|
|
|
221
247
|
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.slot);
|
|
222
248
|
const attestations = await this.waitForAttestations(proposal);
|
|
223
249
|
const blockAttestedAt = this.dateProvider.now();
|
|
224
250
|
|
|
225
|
-
this.metrics.
|
|
251
|
+
this.metrics.recordCheckpointAttestationDelay(blockAttestedAt - blockProposedAt);
|
|
226
252
|
|
|
227
253
|
// Proposer must sign over the attestations before pushing them to L1
|
|
228
254
|
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
@@ -235,7 +261,7 @@ export class CheckpointProposalJob {
|
|
|
235
261
|
const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
|
|
236
262
|
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
237
263
|
txTimeoutAt,
|
|
238
|
-
|
|
264
|
+
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
239
265
|
});
|
|
240
266
|
|
|
241
267
|
return checkpoint;
|
|
@@ -248,20 +274,22 @@ export class CheckpointProposalJob {
|
|
|
248
274
|
/**
|
|
249
275
|
* Builds blocks for a checkpoint within the current slot.
|
|
250
276
|
*/
|
|
277
|
+
@trackSpan('CheckpointProposalJob.buildBlocksForCheckpoint')
|
|
251
278
|
private async buildBlocksForCheckpoint(
|
|
252
279
|
checkpointBuilder: CheckpointBuilder,
|
|
253
280
|
timestamp: bigint,
|
|
281
|
+
inHash: Fr,
|
|
254
282
|
blockProposalOptions: BlockProposalOptions,
|
|
255
283
|
): Promise<{
|
|
256
284
|
blocksInCheckpoint: L2BlockNew[];
|
|
257
|
-
|
|
285
|
+
blockPendingBroadcast: { block: L2BlockNew; txs: Tx[] } | undefined;
|
|
258
286
|
}> {
|
|
259
287
|
const blocksInCheckpoint: L2BlockNew[] = [];
|
|
260
288
|
const txHashesAlreadyIncluded = new Set<string>();
|
|
261
289
|
const initialBlockNumber = BlockNumber(this.syncedToBlockNumber + 1);
|
|
262
290
|
|
|
263
291
|
// Last block in the checkpoint will usually be flagged as pending broadcast, so we send it along with the checkpoint proposal
|
|
264
|
-
let
|
|
292
|
+
let blockPendingBroadcast: { block: L2BlockNew; txs: Tx[] } | undefined = undefined;
|
|
265
293
|
|
|
266
294
|
while (true) {
|
|
267
295
|
const blocksBuilt = blocksInCheckpoint.length;
|
|
@@ -331,17 +359,17 @@ export class CheckpointProposalJob {
|
|
|
331
359
|
blockNumber,
|
|
332
360
|
blocksBuilt,
|
|
333
361
|
});
|
|
334
|
-
|
|
362
|
+
blockPendingBroadcast = { block, txs: usedTxs };
|
|
335
363
|
break;
|
|
336
364
|
}
|
|
337
365
|
|
|
338
366
|
// For non-last blocks, broadcast the block proposal (unless we're in fisherman mode)
|
|
339
367
|
// If the block is the last one, we'll broadcast it along with the checkpoint at the end of the loop
|
|
340
368
|
if (!this.config.fishermanMode) {
|
|
341
|
-
// TODO(palla/mbps): Wire this to the new p2p API once available
|
|
342
369
|
const proposal = await this.validatorClient.createBlockProposal(
|
|
343
|
-
block.header
|
|
344
|
-
|
|
370
|
+
block.header,
|
|
371
|
+
block.indexWithinCheckpoint,
|
|
372
|
+
inHash,
|
|
345
373
|
block.archive.root,
|
|
346
374
|
usedTxs,
|
|
347
375
|
this.proposer,
|
|
@@ -359,13 +387,11 @@ export class CheckpointProposalJob {
|
|
|
359
387
|
blocksBuilt: blocksInCheckpoint.length,
|
|
360
388
|
});
|
|
361
389
|
|
|
362
|
-
return {
|
|
363
|
-
blocksInCheckpoint,
|
|
364
|
-
pendingBroadcast,
|
|
365
|
-
};
|
|
390
|
+
return { blocksInCheckpoint, blockPendingBroadcast };
|
|
366
391
|
}
|
|
367
392
|
|
|
368
393
|
/** Sleeps until it is time to produce the next block in the slot */
|
|
394
|
+
@trackSpan('CheckpointProposalJob.waitUntilNextSubslot')
|
|
369
395
|
private async waitUntilNextSubslot(nextSubslotStart: number) {
|
|
370
396
|
this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.slot);
|
|
371
397
|
this.log.verbose(`Waiting until time for the next block at ${nextSubslotStart}s into slot`, { slot: this.slot });
|
|
@@ -373,6 +399,7 @@ export class CheckpointProposalJob {
|
|
|
373
399
|
}
|
|
374
400
|
|
|
375
401
|
/** Builds a single block. Called from the main block building loop. */
|
|
402
|
+
@trackSpan('CheckpointProposalJob.buildSingleBlock')
|
|
376
403
|
private async buildSingleBlock(
|
|
377
404
|
checkpointBuilder: CheckpointBuilder,
|
|
378
405
|
opts: {
|
|
@@ -484,6 +511,7 @@ export class CheckpointProposalJob {
|
|
|
484
511
|
}
|
|
485
512
|
|
|
486
513
|
/** Waits until minTxs are available on the pool for building a block. */
|
|
514
|
+
@trackSpan('CheckpointProposalJob.waitForMinTxs')
|
|
487
515
|
private async waitForMinTxs(opts: {
|
|
488
516
|
forceCreate?: boolean;
|
|
489
517
|
blockNumber: BlockNumber;
|
|
@@ -524,7 +552,8 @@ export class CheckpointProposalJob {
|
|
|
524
552
|
* Waits for enough attestations to be collected via p2p.
|
|
525
553
|
* This is run after all blocks for the checkpoint have been built.
|
|
526
554
|
*/
|
|
527
|
-
|
|
555
|
+
@trackSpan('CheckpointProposalJob.waitForAttestations')
|
|
556
|
+
private async waitForAttestations(proposal: CheckpointProposal): Promise<CommitteeAttestationsAndSigners> {
|
|
528
557
|
if (this.config.fishermanMode) {
|
|
529
558
|
this.log.debug('Skipping attestation collection in fisherman mode');
|
|
530
559
|
return CommitteeAttestationsAndSigners.empty();
|
|
@@ -573,8 +602,7 @@ export class CheckpointProposalJob {
|
|
|
573
602
|
|
|
574
603
|
// Manipulate the attestations if we've been configured to do so
|
|
575
604
|
if (this.config.injectFakeAttestation || this.config.shuffleAttestationOrdering) {
|
|
576
|
-
|
|
577
|
-
return this.manipulateAttestations(checkpoint, epoch, seed, committee, sorted);
|
|
605
|
+
return this.manipulateAttestations(proposal.slotNumber, epoch, seed, committee, sorted);
|
|
578
606
|
}
|
|
579
607
|
|
|
580
608
|
return new CommitteeAttestationsAndSigners(sorted);
|
|
@@ -590,7 +618,7 @@ export class CheckpointProposalJob {
|
|
|
590
618
|
|
|
591
619
|
/** Breaks the attestations before publishing based on attack configs */
|
|
592
620
|
private manipulateAttestations(
|
|
593
|
-
|
|
621
|
+
slotNumber: SlotNumber,
|
|
594
622
|
epoch: EpochNumber,
|
|
595
623
|
seed: bigint,
|
|
596
624
|
committee: EthAddress[],
|
|
@@ -598,7 +626,6 @@ export class CheckpointProposalJob {
|
|
|
598
626
|
) {
|
|
599
627
|
// Compute the proposer index in the committee, since we dont want to tweak it.
|
|
600
628
|
// Otherwise, the L1 rollup contract will reject the block outright.
|
|
601
|
-
const { slotNumber } = checkpoint;
|
|
602
629
|
const proposerIndex = Number(
|
|
603
630
|
this.epochCache.computeProposerIndex(slotNumber, epoch, seed, BigInt(committee.length)),
|
|
604
631
|
);
|
|
@@ -647,16 +674,24 @@ export class CheckpointProposalJob {
|
|
|
647
674
|
}
|
|
648
675
|
|
|
649
676
|
/**
|
|
650
|
-
*
|
|
651
|
-
*
|
|
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.
|
|
652
680
|
*/
|
|
653
681
|
private async syncProposedBlockToArchiver(block: L2BlockNew): Promise<void> {
|
|
654
|
-
|
|
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`, {
|
|
655
691
|
blockNumber: block.number,
|
|
656
692
|
slot: block.header.globalVariables.slotNumber,
|
|
657
693
|
});
|
|
658
|
-
|
|
659
|
-
await Promise.resolve();
|
|
694
|
+
await this.blockSink.addBlock(block);
|
|
660
695
|
}
|
|
661
696
|
|
|
662
697
|
/** Runs fee analysis and logs checkpoint outcome as fisherman */
|
|
@@ -685,6 +720,7 @@ export class CheckpointProposalJob {
|
|
|
685
720
|
}
|
|
686
721
|
|
|
687
722
|
/** Waits until a specific time within the current slot */
|
|
723
|
+
@trackSpan('CheckpointProposalJob.waitUntilTimeInSlot')
|
|
688
724
|
protected async waitUntilTimeInSlot(targetSecondsIntoSlot: number): Promise<void> {
|
|
689
725
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
690
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) {
|