@aztec/sequencer-client 0.0.1-commit.6230efd → 0.0.1-commit.64b6bbb
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/client/sequencer-client.js +1 -1
- package/dest/config.d.ts +1 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -9
- 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 +13 -13
- 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 +23 -86
- package/dest/publisher/sequencer-publisher.d.ts +17 -17
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +481 -67
- package/dest/sequencer/checkpoint_proposal_job.d.ts +40 -12
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +606 -57
- package/dest/sequencer/checkpoint_voter.d.ts +3 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +34 -10
- package/dest/sequencer/index.d.ts +1 -3
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +0 -2
- package/dest/sequencer/metrics.d.ts +4 -4
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +48 -129
- package/dest/sequencer/sequencer.d.ts +27 -15
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +492 -43
- package/dest/sequencer/timetable.d.ts +1 -4
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +1 -4
- 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 +23 -11
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +50 -9
- package/dest/test/utils.d.ts +13 -9
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +26 -17
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +4 -5
- package/src/config.ts +14 -11
- package/src/global_variable_builder/global_builder.ts +13 -13
- package/src/index.ts +1 -9
- package/src/publisher/sequencer-publisher-metrics.ts +17 -69
- package/src/publisher/sequencer-publisher.ts +118 -91
- package/src/sequencer/checkpoint_proposal_job.ts +258 -87
- package/src/sequencer/checkpoint_voter.ts +32 -7
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/metrics.ts +48 -138
- package/src/sequencer/sequencer.ts +133 -43
- package/src/sequencer/timetable.ts +6 -5
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +91 -29
- package/src/test/utils.ts +55 -28
- package/dest/sequencer/block_builder.d.ts +0 -26
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -129
- 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/block_builder.ts +0 -217
- 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
|
@@ -12,7 +12,7 @@ import type { DateProvider } from '@aztec/foundation/timer';
|
|
|
12
12
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
13
13
|
import type { P2P } from '@aztec/p2p';
|
|
14
14
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
15
|
-
import type {
|
|
15
|
+
import type { L2Block, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
16
16
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
18
18
|
import {
|
|
@@ -24,16 +24,15 @@ import {
|
|
|
24
24
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
25
25
|
import { pickFromSchema } from '@aztec/stdlib/schemas';
|
|
26
26
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
27
|
-
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
28
|
-
import type
|
|
27
|
+
import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
28
|
+
import { FullNodeCheckpointsBuilder, type ValidatorClient } from '@aztec/validator-client';
|
|
29
29
|
|
|
30
30
|
import EventEmitter from 'node:events';
|
|
31
31
|
|
|
32
32
|
import { DefaultSequencerConfig } from '../config.js';
|
|
33
33
|
import type { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
34
34
|
import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
|
|
35
|
-
import type {
|
|
36
|
-
import { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
|
|
35
|
+
import type { InvalidateCheckpointRequest, SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
37
36
|
import { CheckpointProposalJob } from './checkpoint_proposal_job.js';
|
|
38
37
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
39
38
|
import { SequencerInterruptedError, SequencerTooSlowError } from './errors.js';
|
|
@@ -58,8 +57,11 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
58
57
|
private state = SequencerState.STOPPED;
|
|
59
58
|
private metrics: SequencerMetrics;
|
|
60
59
|
|
|
61
|
-
/** The last slot for which we attempted to
|
|
62
|
-
private
|
|
60
|
+
/** The last slot for which we attempted to perform our voting duties with degraded block production */
|
|
61
|
+
private lastSlotForFallbackVote: SlotNumber | undefined;
|
|
62
|
+
|
|
63
|
+
/** The last slot for which we logged "no committee" warning, to avoid spam */
|
|
64
|
+
private lastSlotForNoCommitteeWarning: SlotNumber | undefined;
|
|
63
65
|
|
|
64
66
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */
|
|
65
67
|
private lastSlotForCheckpointProposalJob: SlotNumber | undefined;
|
|
@@ -91,7 +93,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
91
93
|
protected p2pClient: P2P,
|
|
92
94
|
protected worldState: WorldStateSynchronizer,
|
|
93
95
|
protected slasherClient: SlasherClientInterface | undefined,
|
|
94
|
-
protected l2BlockSource: L2BlockSource,
|
|
96
|
+
protected l2BlockSource: L2BlockSource & L2BlockSink,
|
|
95
97
|
protected l1ToL2MessageSource: L1ToL2MessageSource,
|
|
96
98
|
protected checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
97
99
|
protected l1Constants: SequencerRollupConstants,
|
|
@@ -160,7 +162,6 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
160
162
|
this.log.info('Stopped sequencer');
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
@trackSpan('Sequencer.work')
|
|
164
165
|
/** Main sequencer loop with a try/catch */
|
|
165
166
|
protected async safeWork() {
|
|
166
167
|
try {
|
|
@@ -198,12 +199,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
198
199
|
* - Collect attestations for the final block
|
|
199
200
|
* - Submit checkpoint
|
|
200
201
|
*/
|
|
202
|
+
@trackSpan('Sequencer.work')
|
|
201
203
|
protected async work() {
|
|
202
204
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
203
205
|
const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
204
206
|
|
|
205
207
|
// Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
|
|
206
|
-
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, ts, now);
|
|
208
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
|
|
207
209
|
if (!checkpointProposalJob) {
|
|
208
210
|
return;
|
|
209
211
|
}
|
|
@@ -233,7 +235,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
233
235
|
* This is the initial step in the main loop.
|
|
234
236
|
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
235
237
|
*/
|
|
238
|
+
@trackSpan('Sequencer.prepareCheckpointProposal')
|
|
236
239
|
private async prepareCheckpointProposal(
|
|
240
|
+
epoch: EpochNumber,
|
|
237
241
|
slot: SlotNumber,
|
|
238
242
|
ts: bigint,
|
|
239
243
|
now: bigint,
|
|
@@ -263,8 +267,27 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
263
267
|
return undefined;
|
|
264
268
|
}
|
|
265
269
|
|
|
266
|
-
//
|
|
267
|
-
|
|
270
|
+
// If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
|
|
271
|
+
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
272
|
+
const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
|
|
273
|
+
|
|
274
|
+
if (isEscapeHatchOpen) {
|
|
275
|
+
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
276
|
+
const [canPropose, proposer] = await this.checkCanPropose(slot);
|
|
277
|
+
if (canPropose) {
|
|
278
|
+
await this.tryVoteWhenEscapeHatchOpen({ slot, proposer });
|
|
279
|
+
} else {
|
|
280
|
+
this.log.trace(`Escape hatch open but we are not proposer, skipping vote-only actions`, {
|
|
281
|
+
slot,
|
|
282
|
+
epoch,
|
|
283
|
+
proposer,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Next checkpoint follows from the last synced one
|
|
290
|
+
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
268
291
|
|
|
269
292
|
const logCtx = {
|
|
270
293
|
now,
|
|
@@ -280,9 +303,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
280
303
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
281
304
|
const [canPropose, proposer] = await this.checkCanPropose(slot);
|
|
282
305
|
|
|
283
|
-
// If we are not a proposer check if we should invalidate
|
|
306
|
+
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
284
307
|
if (!canPropose) {
|
|
285
|
-
await this.
|
|
308
|
+
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
286
309
|
return undefined;
|
|
287
310
|
}
|
|
288
311
|
|
|
@@ -312,15 +335,14 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
312
335
|
}
|
|
313
336
|
|
|
314
337
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
315
|
-
|
|
316
|
-
const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
|
|
338
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
317
339
|
|
|
318
340
|
// Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
|
|
319
341
|
// if all the previous checks are good, but we do it just in case.
|
|
320
342
|
const canProposeCheck = await publisher.canProposeAtNextEthBlock(
|
|
321
343
|
syncedTo.archive,
|
|
322
344
|
proposer ?? EthAddress.ZERO,
|
|
323
|
-
|
|
345
|
+
invalidateCheckpoint,
|
|
324
346
|
);
|
|
325
347
|
|
|
326
348
|
if (canProposeCheck === undefined) {
|
|
@@ -354,43 +376,49 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
354
376
|
}
|
|
355
377
|
|
|
356
378
|
this.lastSlotForCheckpointProposalJob = slot;
|
|
379
|
+
await this.p2pClient.prepareForSlot(slot);
|
|
357
380
|
this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, { ...logCtx, proposer });
|
|
358
381
|
|
|
359
382
|
// Create and return the checkpoint proposal job
|
|
360
383
|
return this.createCheckpointProposalJob(
|
|
384
|
+
epoch,
|
|
361
385
|
slot,
|
|
362
386
|
checkpointNumber,
|
|
363
387
|
syncedTo.blockNumber,
|
|
364
388
|
proposer,
|
|
365
389
|
publisher,
|
|
366
390
|
attestorAddress,
|
|
367
|
-
|
|
391
|
+
invalidateCheckpoint,
|
|
368
392
|
);
|
|
369
393
|
}
|
|
370
394
|
|
|
371
395
|
protected createCheckpointProposalJob(
|
|
396
|
+
epoch: EpochNumber,
|
|
372
397
|
slot: SlotNumber,
|
|
373
398
|
checkpointNumber: CheckpointNumber,
|
|
374
399
|
syncedToBlockNumber: BlockNumber,
|
|
375
400
|
proposer: EthAddress | undefined,
|
|
376
401
|
publisher: SequencerPublisher,
|
|
377
402
|
attestorAddress: EthAddress,
|
|
378
|
-
|
|
403
|
+
invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
|
|
379
404
|
): CheckpointProposalJob {
|
|
380
405
|
return new CheckpointProposalJob(
|
|
406
|
+
epoch,
|
|
381
407
|
slot,
|
|
382
408
|
checkpointNumber,
|
|
383
409
|
syncedToBlockNumber,
|
|
384
410
|
proposer,
|
|
385
411
|
publisher,
|
|
386
412
|
attestorAddress,
|
|
387
|
-
|
|
413
|
+
invalidateCheckpoint,
|
|
388
414
|
this.validatorClient,
|
|
389
415
|
this.globalsBuilder,
|
|
390
416
|
this.p2pClient,
|
|
391
417
|
this.worldState,
|
|
392
418
|
this.l1ToL2MessageSource,
|
|
419
|
+
this.l2BlockSource,
|
|
393
420
|
this.checkpointsBuilder,
|
|
421
|
+
this.l2BlockSource,
|
|
394
422
|
this.l1Constants,
|
|
395
423
|
this.config,
|
|
396
424
|
this.timetable,
|
|
@@ -400,7 +428,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
400
428
|
this.metrics,
|
|
401
429
|
this,
|
|
402
430
|
this.setState.bind(this),
|
|
403
|
-
this.
|
|
431
|
+
this.tracer,
|
|
432
|
+
this.log.getBindings(),
|
|
404
433
|
);
|
|
405
434
|
}
|
|
406
435
|
|
|
@@ -469,9 +498,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
469
498
|
number: syncSummary.latestBlockNumber,
|
|
470
499
|
hash: syncSummary.latestBlockHash,
|
|
471
500
|
})),
|
|
472
|
-
this.l2BlockSource.getL2Tips().then(t => t.
|
|
501
|
+
this.l2BlockSource.getL2Tips().then(t => t.proposed),
|
|
473
502
|
this.p2pClient.getStatus().then(p2p => p2p.syncedToL2Block),
|
|
474
|
-
this.l1ToL2MessageSource.getL2Tips().then(t => t.
|
|
503
|
+
this.l1ToL2MessageSource.getL2Tips().then(t => t.proposed),
|
|
475
504
|
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
476
505
|
] as const);
|
|
477
506
|
|
|
@@ -479,6 +508,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
479
508
|
|
|
480
509
|
// Handle zero as a special case, since the block hash won't match across services if we're changing the prefilled data for the genesis block,
|
|
481
510
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
511
|
+
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
482
512
|
const result =
|
|
483
513
|
(l2BlockSource.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0) ||
|
|
484
514
|
(worldState.hash === l2BlockSource.hash &&
|
|
@@ -494,10 +524,16 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
494
524
|
const blockNumber = worldState.number;
|
|
495
525
|
if (blockNumber < INITIAL_L2_BLOCK_NUM) {
|
|
496
526
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
497
|
-
return {
|
|
527
|
+
return {
|
|
528
|
+
checkpointNumber: CheckpointNumber.ZERO,
|
|
529
|
+
blockNumber: BlockNumber.ZERO,
|
|
530
|
+
archive,
|
|
531
|
+
l1Timestamp,
|
|
532
|
+
pendingChainValidationStatus,
|
|
533
|
+
};
|
|
498
534
|
}
|
|
499
535
|
|
|
500
|
-
const block = await this.l2BlockSource.
|
|
536
|
+
const block = await this.l2BlockSource.getL2Block(blockNumber);
|
|
501
537
|
if (!block) {
|
|
502
538
|
// this shouldn't really happen because a moment ago we checked that all components were in sync
|
|
503
539
|
this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
|
|
@@ -507,6 +543,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
507
543
|
return {
|
|
508
544
|
block,
|
|
509
545
|
blockNumber: block.number,
|
|
546
|
+
checkpointNumber: block.checkpointNumber,
|
|
510
547
|
archive: block.archive.root,
|
|
511
548
|
l1Timestamp,
|
|
512
549
|
pendingChainValidationStatus,
|
|
@@ -524,7 +561,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
524
561
|
proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
|
|
525
562
|
} catch (e) {
|
|
526
563
|
if (e instanceof NoCommitteeError) {
|
|
527
|
-
this.
|
|
564
|
+
if (this.lastSlotForNoCommitteeWarning !== slot) {
|
|
565
|
+
this.lastSlotForNoCommitteeWarning = slot;
|
|
566
|
+
this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
|
|
567
|
+
}
|
|
528
568
|
return [false, undefined];
|
|
529
569
|
}
|
|
530
570
|
this.log.error(`Error getting proposer for slot ${slot}`, e);
|
|
@@ -555,11 +595,12 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
555
595
|
* Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
|
|
556
596
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
557
597
|
*/
|
|
598
|
+
@trackSpan('Seqeuencer.tryVoteWhenSyncFails', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
|
|
558
599
|
protected async tryVoteWhenSyncFails(args: { slot: SlotNumber; ts: bigint }): Promise<void> {
|
|
559
600
|
const { slot } = args;
|
|
560
601
|
|
|
561
602
|
// Prevent duplicate attempts in the same slot
|
|
562
|
-
if (this.
|
|
603
|
+
if (this.lastSlotForFallbackVote === slot) {
|
|
563
604
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
564
605
|
return;
|
|
565
606
|
}
|
|
@@ -591,7 +632,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
591
632
|
}
|
|
592
633
|
|
|
593
634
|
// Mark this slot as attempted
|
|
594
|
-
this.
|
|
635
|
+
this.lastSlotForFallbackVote = slot;
|
|
595
636
|
|
|
596
637
|
// Get a publisher for voting
|
|
597
638
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
|
|
@@ -625,13 +666,61 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
625
666
|
await publisher.sendRequests();
|
|
626
667
|
}
|
|
627
668
|
|
|
669
|
+
/**
|
|
670
|
+
* Tries to vote on slashing actions and governance proposals when escape hatch is open.
|
|
671
|
+
* This allows the sequencer to participate in voting without performing checkpoint proposal work.
|
|
672
|
+
*/
|
|
673
|
+
@trackSpan('Sequencer.tryVoteWhenEscapeHatchOpen', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
|
|
674
|
+
protected async tryVoteWhenEscapeHatchOpen(args: {
|
|
675
|
+
slot: SlotNumber;
|
|
676
|
+
proposer: EthAddress | undefined;
|
|
677
|
+
}): Promise<void> {
|
|
678
|
+
const { slot, proposer } = args;
|
|
679
|
+
|
|
680
|
+
// Prevent duplicate attempts in the same slot
|
|
681
|
+
if (this.lastSlotForFallbackVote === slot) {
|
|
682
|
+
this.log.trace(`Already attempted to vote in slot ${slot} (escape hatch open, skipping)`);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Mark this slot as attempted
|
|
687
|
+
this.lastSlotForFallbackVote = slot;
|
|
688
|
+
|
|
689
|
+
const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
|
|
690
|
+
|
|
691
|
+
this.log.debug(`Escape hatch open for slot ${slot}, attempting vote-only actions`, { slot, attestorAddress });
|
|
692
|
+
|
|
693
|
+
const voter = new CheckpointVoter(
|
|
694
|
+
slot,
|
|
695
|
+
publisher,
|
|
696
|
+
attestorAddress,
|
|
697
|
+
this.validatorClient,
|
|
698
|
+
this.slasherClient,
|
|
699
|
+
this.l1Constants,
|
|
700
|
+
this.config,
|
|
701
|
+
this.metrics,
|
|
702
|
+
this.log,
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const votesPromises = voter.enqueueVotes();
|
|
706
|
+
const votes = await Promise.all(votesPromises);
|
|
707
|
+
|
|
708
|
+
if (votes.every(p => !p)) {
|
|
709
|
+
this.log.debug(`No votes to enqueue for slot ${slot} (escape hatch open)`);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
this.log.info(`Voting in slot ${slot} (escape hatch open)`, { slot });
|
|
714
|
+
await publisher.sendRequests();
|
|
715
|
+
}
|
|
716
|
+
|
|
628
717
|
/**
|
|
629
718
|
* Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
|
|
630
719
|
* has been there without being invalidated and whether the sequencer is in the committee or not. We always
|
|
631
720
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
632
721
|
* and if they fail, any sequencer will try as well.
|
|
633
722
|
*/
|
|
634
|
-
protected async
|
|
723
|
+
protected async considerInvalidatingCheckpoint(
|
|
635
724
|
syncedTo: SequencerSyncCheckResult,
|
|
636
725
|
currentSlot: SlotNumber,
|
|
637
726
|
): Promise<void> {
|
|
@@ -640,18 +729,18 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
640
729
|
return;
|
|
641
730
|
}
|
|
642
731
|
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(
|
|
732
|
+
const invalidCheckpointNumber = pendingChainValidationStatus.checkpoint.checkpointNumber;
|
|
733
|
+
const invalidCheckpointTimestamp = pendingChainValidationStatus.checkpoint.timestamp;
|
|
734
|
+
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(invalidCheckpointTimestamp);
|
|
646
735
|
const ourValidatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
647
736
|
|
|
648
737
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } =
|
|
649
738
|
this.config;
|
|
650
739
|
|
|
651
740
|
const logData = {
|
|
652
|
-
invalidL1Timestamp:
|
|
741
|
+
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
653
742
|
l1Timestamp,
|
|
654
|
-
|
|
743
|
+
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
655
744
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
656
745
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
657
746
|
ourValidatorAddresses,
|
|
@@ -697,25 +786,25 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
697
786
|
|
|
698
787
|
const { publisher } = await this.publisherFactory.create(validatorToUse);
|
|
699
788
|
|
|
700
|
-
const
|
|
701
|
-
if (!
|
|
702
|
-
this.log.warn(`Failed to simulate invalidate
|
|
789
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(pendingChainValidationStatus);
|
|
790
|
+
if (!invalidateCheckpoint) {
|
|
791
|
+
this.log.warn(`Failed to simulate invalidate checkpoint`, logData);
|
|
703
792
|
return;
|
|
704
793
|
}
|
|
705
794
|
|
|
706
795
|
this.log.info(
|
|
707
796
|
invalidateAsCommitteeMember
|
|
708
|
-
? `Invalidating
|
|
709
|
-
: `Invalidating
|
|
797
|
+
? `Invalidating checkpoint ${invalidCheckpointNumber} as committee member`
|
|
798
|
+
: `Invalidating checkpoint ${invalidCheckpointNumber} as non-committee member`,
|
|
710
799
|
logData,
|
|
711
800
|
);
|
|
712
801
|
|
|
713
|
-
publisher.
|
|
802
|
+
publisher.enqueueInvalidateCheckpoint(invalidateCheckpoint);
|
|
714
803
|
|
|
715
804
|
if (!this.config.fishermanMode) {
|
|
716
805
|
await publisher.sendRequests();
|
|
717
806
|
} else {
|
|
718
|
-
this.log.info('Invalidating
|
|
807
|
+
this.log.info('Invalidating checkpoint in fisherman mode, clearing pending requests');
|
|
719
808
|
publisher.clearPendingRequests();
|
|
720
809
|
}
|
|
721
810
|
}
|
|
@@ -788,9 +877,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
788
877
|
}
|
|
789
878
|
|
|
790
879
|
type SequencerSyncCheckResult = {
|
|
791
|
-
block?:
|
|
880
|
+
block?: L2Block;
|
|
881
|
+
checkpointNumber: CheckpointNumber;
|
|
792
882
|
blockNumber: BlockNumber;
|
|
793
883
|
archive: Fr;
|
|
794
884
|
l1Timestamp: bigint;
|
|
795
|
-
pendingChainValidationStatus:
|
|
885
|
+
pendingChainValidationStatus: ValidateCheckpointResult;
|
|
796
886
|
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
|
+
import {
|
|
3
|
+
CHECKPOINT_ASSEMBLE_TIME,
|
|
4
|
+
CHECKPOINT_INITIALIZATION_TIME,
|
|
5
|
+
DEFAULT_P2P_PROPAGATION_TIME,
|
|
6
|
+
MIN_EXECUTION_TIME,
|
|
7
|
+
} from '@aztec/stdlib/timetable';
|
|
2
8
|
|
|
3
|
-
import { DEFAULT_ATTESTATION_PROPAGATION_TIME as DEFAULT_P2P_PROPAGATION_TIME } from '../config.js';
|
|
4
9
|
import { SequencerTooSlowError } from './errors.js';
|
|
5
10
|
import type { SequencerMetrics } from './metrics.js';
|
|
6
11
|
import { SequencerState } from './utils.js';
|
|
7
12
|
|
|
8
|
-
export const MIN_EXECUTION_TIME = 2;
|
|
9
|
-
export const CHECKPOINT_INITIALIZATION_TIME = 1;
|
|
10
|
-
export const CHECKPOINT_ASSEMBLE_TIME = 1;
|
|
11
|
-
|
|
12
13
|
export class SequencerTimetable {
|
|
13
14
|
/**
|
|
14
15
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
package/src/test/index.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
2
2
|
import type { PublisherManager } from '@aztec/ethereum/publisher-manager';
|
|
3
3
|
import type { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
4
|
-
import type { ValidatorClient } from '@aztec/validator-client';
|
|
4
|
+
import type { FullNodeCheckpointsBuilder, ValidatorClient } from '@aztec/validator-client';
|
|
5
5
|
|
|
6
6
|
import { SequencerClient } from '../client/sequencer-client.js';
|
|
7
7
|
import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
|
|
8
8
|
import type { SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
9
|
-
import type { FullNodeCheckpointsBuilder } from '../sequencer/checkpoint_builder.js';
|
|
10
9
|
import { Sequencer } from '../sequencer/sequencer.js';
|
|
11
10
|
import type { SequencerTimetable } from '../sequencer/timetable.js';
|
|
12
11
|
|