@aztec/sequencer-client 0.0.1-commit.1a99e26c → 0.0.1-commit.1bb068fb5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/config.d.ts +1 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +4 -8
- package/dest/publisher/sequencer-publisher.d.ts +8 -2
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +53 -23
- package/dest/sequencer/checkpoint_proposal_job.d.ts +29 -6
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +71 -47
- package/dest/sequencer/metrics.d.ts +4 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +25 -0
- package/dest/sequencer/sequencer.d.ts +3 -1
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +6 -1
- 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/mock_checkpoint_builder.d.ts +7 -5
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +6 -6
- package/dest/test/utils.d.ts +3 -3
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +5 -4
- package/package.json +28 -28
- package/src/config.ts +9 -11
- package/src/publisher/sequencer-publisher.ts +60 -22
- package/src/sequencer/checkpoint_proposal_job.ts +95 -63
- package/src/sequencer/metrics.ts +24 -0
- package/src/sequencer/sequencer.ts +8 -1
- package/src/sequencer/timetable.ts +6 -5
- package/src/test/mock_checkpoint_builder.ts +14 -5
- package/src/test/utils.ts +5 -2
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { NUM_CHECKPOINT_END_MARKER_FIELDS, getNumBlockEndBlobFields } from '@aztec/blob-lib/encoding';
|
|
2
2
|
import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB } from '@aztec/constants';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BlockNumber,
|
|
6
|
+
CheckpointNumber,
|
|
7
|
+
EpochNumber,
|
|
8
|
+
IndexWithinCheckpoint,
|
|
9
|
+
SlotNumber,
|
|
10
|
+
} from '@aztec/foundation/branded-types';
|
|
5
11
|
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
6
12
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
7
13
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -10,7 +16,7 @@ import { filter } from '@aztec/foundation/iterator';
|
|
|
10
16
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
11
17
|
import { sleep, sleepUntil } from '@aztec/foundation/sleep';
|
|
12
18
|
import { type DateProvider, Timer } from '@aztec/foundation/timer';
|
|
13
|
-
import { type TypedEventEmitter, unfreeze } from '@aztec/foundation/types';
|
|
19
|
+
import { type TypedEventEmitter, isErrorClass, unfreeze } from '@aztec/foundation/types';
|
|
14
20
|
import type { P2P } from '@aztec/p2p';
|
|
15
21
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
16
22
|
import {
|
|
@@ -24,10 +30,11 @@ import {
|
|
|
24
30
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
25
31
|
import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
26
32
|
import { Gas } from '@aztec/stdlib/gas';
|
|
27
|
-
import
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
import {
|
|
34
|
+
NoValidTxsError,
|
|
35
|
+
type PublicProcessorLimits,
|
|
36
|
+
type ResolvedSequencerConfig,
|
|
37
|
+
type WorldStateSynchronizer,
|
|
31
38
|
} from '@aztec/stdlib/interfaces/server';
|
|
32
39
|
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
33
40
|
import type { BlockProposalOptions, CheckpointProposal, CheckpointProposalOptions } from '@aztec/stdlib/p2p';
|
|
@@ -184,6 +191,9 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
184
191
|
);
|
|
185
192
|
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
186
193
|
|
|
194
|
+
// Get the fee asset price modifier from the oracle
|
|
195
|
+
const feeAssetPriceModifier = await this.publisher.getFeeAssetPriceModifier();
|
|
196
|
+
|
|
187
197
|
// Create a long-lived forked world state for the checkpoint builder
|
|
188
198
|
using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 });
|
|
189
199
|
|
|
@@ -191,6 +201,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
191
201
|
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(
|
|
192
202
|
this.checkpointNumber,
|
|
193
203
|
checkpointGlobalVariables,
|
|
204
|
+
feeAssetPriceModifier,
|
|
194
205
|
l1ToL2Messages,
|
|
195
206
|
previousCheckpointOutHashes,
|
|
196
207
|
fork,
|
|
@@ -225,19 +236,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
225
236
|
// These errors are expected in HA mode, so we yield and let another HA node handle the slot
|
|
226
237
|
// The only distinction between the 2 errors is SlashingProtectionError throws when the payload is different,
|
|
227
238
|
// which is normal for block building (may have picked different txs)
|
|
228
|
-
if (err
|
|
229
|
-
this.log.info(`Checkpoint proposal for slot ${this.slot} already signed by another HA node, yielding`, {
|
|
230
|
-
slot: this.slot,
|
|
231
|
-
signedByNode: err.signedByNode,
|
|
232
|
-
});
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
if (err instanceof SlashingProtectionError) {
|
|
236
|
-
this.log.info(`Checkpoint proposal for slot ${this.slot} blocked by slashing protection, yielding`, {
|
|
237
|
-
slot: this.slot,
|
|
238
|
-
existingMessageHash: err.existingMessageHash,
|
|
239
|
-
attemptedMessageHash: err.attemptedMessageHash,
|
|
240
|
-
});
|
|
239
|
+
if (this.handleHASigningError(err, 'Block proposal')) {
|
|
241
240
|
return undefined;
|
|
242
241
|
}
|
|
243
242
|
throw err;
|
|
@@ -280,6 +279,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
280
279
|
const proposal = await this.validatorClient.createCheckpointProposal(
|
|
281
280
|
checkpoint.header,
|
|
282
281
|
checkpoint.archive.root,
|
|
282
|
+
feeAssetPriceModifier,
|
|
283
283
|
lastBlock,
|
|
284
284
|
this.proposer,
|
|
285
285
|
checkpointProposalOptions,
|
|
@@ -306,20 +306,8 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
306
306
|
);
|
|
307
307
|
} catch (err) {
|
|
308
308
|
// We shouldn't really get here since we yield to another HA node
|
|
309
|
-
// as soon as we see these errors when creating block proposals.
|
|
310
|
-
if (err
|
|
311
|
-
this.log.info(`Attestations signature for slot ${this.slot} already signed by another HA node, yielding`, {
|
|
312
|
-
slot: this.slot,
|
|
313
|
-
signedByNode: err.signedByNode,
|
|
314
|
-
});
|
|
315
|
-
return undefined;
|
|
316
|
-
}
|
|
317
|
-
if (err instanceof SlashingProtectionError) {
|
|
318
|
-
this.log.info(`Attestations signature for slot ${this.slot} blocked by slashing protection, yielding`, {
|
|
319
|
-
slot: this.slot,
|
|
320
|
-
existingMessageHash: err.existingMessageHash,
|
|
321
|
-
attemptedMessageHash: err.attemptedMessageHash,
|
|
322
|
-
});
|
|
309
|
+
// as soon as we see these errors when creating block or checkpoint proposals.
|
|
310
|
+
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
323
311
|
return undefined;
|
|
324
312
|
}
|
|
325
313
|
throw err;
|
|
@@ -372,7 +360,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
372
360
|
|
|
373
361
|
while (true) {
|
|
374
362
|
const blocksBuilt = blocksInCheckpoint.length;
|
|
375
|
-
const indexWithinCheckpoint = blocksBuilt;
|
|
363
|
+
const indexWithinCheckpoint = IndexWithinCheckpoint(blocksBuilt);
|
|
376
364
|
const blockNumber = BlockNumber(initialBlockNumber + blocksBuilt);
|
|
377
365
|
|
|
378
366
|
const secondsIntoSlot = this.getSecondsIntoSlot();
|
|
@@ -402,6 +390,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
402
390
|
remainingBlobFields,
|
|
403
391
|
});
|
|
404
392
|
|
|
393
|
+
// TODO(palla/mbps): Review these conditions. We may want to keep trying in some scenarios.
|
|
405
394
|
if (!buildResult && timingInfo.isLastBlock) {
|
|
406
395
|
// If no block was produced due to not enough txs and this was the last subslot, exit
|
|
407
396
|
break;
|
|
@@ -488,13 +477,13 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
488
477
|
|
|
489
478
|
/** Builds a single block. Called from the main block building loop. */
|
|
490
479
|
@trackSpan('CheckpointProposalJob.buildSingleBlock')
|
|
491
|
-
|
|
480
|
+
protected async buildSingleBlock(
|
|
492
481
|
checkpointBuilder: CheckpointBuilder,
|
|
493
482
|
opts: {
|
|
494
483
|
forceCreate?: boolean;
|
|
495
484
|
blockTimestamp: bigint;
|
|
496
485
|
blockNumber: BlockNumber;
|
|
497
|
-
indexWithinCheckpoint:
|
|
486
|
+
indexWithinCheckpoint: IndexWithinCheckpoint;
|
|
498
487
|
buildDeadline: Date | undefined;
|
|
499
488
|
txHashesAlreadyIncluded: Set<string>;
|
|
500
489
|
remainingBlobFields: number;
|
|
@@ -532,7 +521,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
532
521
|
// Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
|
|
533
522
|
// just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
|
|
534
523
|
const pendingTxs = filter(
|
|
535
|
-
this.p2pClient.
|
|
524
|
+
this.p2pClient.iterateEligiblePendingTxs(),
|
|
536
525
|
tx => !txHashesAlreadyIncluded.has(tx.txHash.toString()),
|
|
537
526
|
);
|
|
538
527
|
|
|
@@ -555,45 +544,38 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
555
544
|
};
|
|
556
545
|
|
|
557
546
|
// Actually build the block by executing txs
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
usedTxs,
|
|
566
|
-
failedTxs,
|
|
567
|
-
usedTxBlobFields,
|
|
568
|
-
} = await checkpointBuilder.buildBlock(pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
|
|
569
|
-
const blockBuildDuration = workTimer.ms();
|
|
547
|
+
const buildResult = await this.buildSingleBlockWithCheckpointBuilder(
|
|
548
|
+
checkpointBuilder,
|
|
549
|
+
pendingTxs,
|
|
550
|
+
blockNumber,
|
|
551
|
+
blockTimestamp,
|
|
552
|
+
blockBuilderOptions,
|
|
553
|
+
);
|
|
570
554
|
|
|
571
555
|
// If any txs failed during execution, drop them from the mempool so we don't pick them up again
|
|
572
|
-
await this.dropFailedTxsFromP2P(failedTxs);
|
|
556
|
+
await this.dropFailedTxsFromP2P(buildResult.failedTxs);
|
|
573
557
|
|
|
574
558
|
// Check if we have created a block with enough txs. If there were invalid txs in the pool, or if execution took
|
|
575
559
|
// too long, then we may not get to minTxsPerBlock after executing public functions.
|
|
576
560
|
const minValidTxs = this.config.minValidTxsPerBlock ?? minTxs;
|
|
577
|
-
|
|
561
|
+
const numTxs = buildResult.status === 'no-valid-txs' ? 0 : buildResult.numTxs;
|
|
562
|
+
if (buildResult.status === 'no-valid-txs' || (!forceCreate && numTxs < minValidTxs)) {
|
|
578
563
|
this.log.warn(
|
|
579
|
-
`Block ${blockNumber} at index ${indexWithinCheckpoint} on slot ${this.slot} has too few valid txs to be proposed
|
|
580
|
-
{ slot: this.slot, blockNumber, numTxs, indexWithinCheckpoint },
|
|
564
|
+
`Block ${blockNumber} at index ${indexWithinCheckpoint} on slot ${this.slot} has too few valid txs to be proposed`,
|
|
565
|
+
{ slot: this.slot, blockNumber, numTxs, indexWithinCheckpoint, minValidTxs, buildResult: buildResult.status },
|
|
581
566
|
);
|
|
582
|
-
this.eventEmitter.emit('block-
|
|
583
|
-
minTxs: minValidTxs,
|
|
584
|
-
availableTxs: numTxs,
|
|
585
|
-
slot: this.slot,
|
|
586
|
-
});
|
|
567
|
+
this.eventEmitter.emit('block-build-failed', { reason: `Insufficient valid txs`, slot: this.slot });
|
|
587
568
|
this.metrics.recordBlockProposalFailed('insufficient_valid_txs');
|
|
588
569
|
return undefined;
|
|
589
570
|
}
|
|
590
571
|
|
|
591
572
|
// Block creation succeeded, emit stats and metrics
|
|
573
|
+
const { publicGas, block, publicProcessorDuration, usedTxs, usedTxBlobFields, blockBuildDuration } = buildResult;
|
|
574
|
+
|
|
592
575
|
const blockStats = {
|
|
593
576
|
eventName: 'l2-block-built',
|
|
594
577
|
duration: blockBuildDuration,
|
|
595
578
|
publicProcessDuration: publicProcessorDuration,
|
|
596
|
-
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
597
579
|
...block.getStats(),
|
|
598
580
|
} satisfies L2BlockBuiltStats;
|
|
599
581
|
|
|
@@ -619,17 +601,40 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
619
601
|
}
|
|
620
602
|
}
|
|
621
603
|
|
|
604
|
+
/** Uses the checkpoint builder to build a block, catching specific txs */
|
|
605
|
+
private async buildSingleBlockWithCheckpointBuilder(
|
|
606
|
+
checkpointBuilder: CheckpointBuilder,
|
|
607
|
+
pendingTxs: AsyncIterable<Tx>,
|
|
608
|
+
blockNumber: BlockNumber,
|
|
609
|
+
blockTimestamp: bigint,
|
|
610
|
+
blockBuilderOptions: PublicProcessorLimits,
|
|
611
|
+
) {
|
|
612
|
+
try {
|
|
613
|
+
const workTimer = new Timer();
|
|
614
|
+
const result = await checkpointBuilder.buildBlock(pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
|
|
615
|
+
const blockBuildDuration = workTimer.ms();
|
|
616
|
+
return { ...result, blockBuildDuration, status: 'success' as const };
|
|
617
|
+
} catch (err: unknown) {
|
|
618
|
+
if (isErrorClass(err, NoValidTxsError)) {
|
|
619
|
+
return { failedTxs: err.failedTxs, status: 'no-valid-txs' as const };
|
|
620
|
+
}
|
|
621
|
+
throw err;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
622
625
|
/** Waits until minTxs are available on the pool for building a block. */
|
|
623
626
|
@trackSpan('CheckpointProposalJob.waitForMinTxs')
|
|
624
627
|
private async waitForMinTxs(opts: {
|
|
625
628
|
forceCreate?: boolean;
|
|
626
629
|
blockNumber: BlockNumber;
|
|
627
|
-
indexWithinCheckpoint:
|
|
630
|
+
indexWithinCheckpoint: IndexWithinCheckpoint;
|
|
628
631
|
buildDeadline: Date | undefined;
|
|
629
632
|
}): Promise<{ canStartBuilding: boolean; availableTxs: number }> {
|
|
630
|
-
const minTxs = this.config.minTxsPerBlock;
|
|
631
633
|
const { indexWithinCheckpoint, blockNumber, buildDeadline, forceCreate } = opts;
|
|
632
634
|
|
|
635
|
+
// We only allow a block with 0 txs in the first block of the checkpoint
|
|
636
|
+
const minTxs = indexWithinCheckpoint > 0 && this.config.minTxsPerBlock === 0 ? 1 : this.config.minTxsPerBlock;
|
|
637
|
+
|
|
633
638
|
// Deadline is undefined if we are not enforcing the timetable, meaning we'll exit immediately when out of time
|
|
634
639
|
const startBuildingDeadline = buildDeadline
|
|
635
640
|
? new Date(buildDeadline.getTime() - this.timetable.minExecutionTime * 1000)
|
|
@@ -650,7 +655,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
650
655
|
`Waiting for enough txs to build block ${blockNumber} at index ${indexWithinCheckpoint} in slot ${this.slot} (have ${availableTxs} but need ${minTxs})`,
|
|
651
656
|
{ blockNumber, slot: this.slot, indexWithinCheckpoint },
|
|
652
657
|
);
|
|
653
|
-
await
|
|
658
|
+
await this.waitForTxsPollingInterval();
|
|
654
659
|
availableTxs = await this.p2pClient.getPendingTxCount();
|
|
655
660
|
}
|
|
656
661
|
|
|
@@ -779,7 +784,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
779
784
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
780
785
|
const failedTxHashes = failedTxData.map(tx => tx.getTxHash());
|
|
781
786
|
this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
|
|
782
|
-
await this.p2pClient.
|
|
787
|
+
await this.p2pClient.handleFailedExecution(failedTxHashes);
|
|
783
788
|
}
|
|
784
789
|
|
|
785
790
|
/**
|
|
@@ -827,6 +832,28 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
827
832
|
this.publisher.clearPendingRequests();
|
|
828
833
|
}
|
|
829
834
|
|
|
835
|
+
/**
|
|
836
|
+
* Helper to handle HA double-signing errors. Returns true if the error was handled (caller should yield).
|
|
837
|
+
*/
|
|
838
|
+
private handleHASigningError(err: any, errorContext: string): boolean {
|
|
839
|
+
if (err instanceof DutyAlreadySignedError) {
|
|
840
|
+
this.log.info(`${errorContext} for slot ${this.slot} already signed by another HA node, yielding`, {
|
|
841
|
+
slot: this.slot,
|
|
842
|
+
signedByNode: err.signedByNode,
|
|
843
|
+
});
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
if (err instanceof SlashingProtectionError) {
|
|
847
|
+
this.log.info(`${errorContext} for slot ${this.slot} blocked by slashing protection, yielding`, {
|
|
848
|
+
slot: this.slot,
|
|
849
|
+
existingMessageHash: err.existingMessageHash,
|
|
850
|
+
attemptedMessageHash: err.attemptedMessageHash,
|
|
851
|
+
});
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
|
|
830
857
|
/** Waits until a specific time within the current slot */
|
|
831
858
|
@trackSpan('CheckpointProposalJob.waitUntilTimeInSlot')
|
|
832
859
|
protected async waitUntilTimeInSlot(targetSecondsIntoSlot: number): Promise<void> {
|
|
@@ -835,6 +862,11 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
835
862
|
await sleepUntil(new Date(targetTimestamp * 1000), this.dateProvider.nowAsDate());
|
|
836
863
|
}
|
|
837
864
|
|
|
865
|
+
/** Waits the polling interval for transactions. Extracted for test overriding. */
|
|
866
|
+
protected async waitForTxsPollingInterval(): Promise<void> {
|
|
867
|
+
await sleep(TXS_POLLING_MS);
|
|
868
|
+
}
|
|
869
|
+
|
|
838
870
|
private getSlotStartBuildTimestamp(): number {
|
|
839
871
|
return getSlotStartBuildTimestamp(this.slot, this.l1Constants);
|
|
840
872
|
}
|
package/src/sequencer/metrics.ts
CHANGED
|
@@ -51,6 +51,9 @@ export class SequencerMetrics {
|
|
|
51
51
|
private fishermanTimeBeforeBlock: Histogram;
|
|
52
52
|
private fishermanPendingBlobTxCount: Histogram;
|
|
53
53
|
private fishermanIncludedBlobTxCount: Histogram;
|
|
54
|
+
private fishermanPendingBlobCount: Histogram;
|
|
55
|
+
private fishermanIncludedBlobCount: Histogram;
|
|
56
|
+
private fishermanBlockBlobsFull: UpDownCounter;
|
|
54
57
|
private fishermanCalculatedPriorityFee: Histogram;
|
|
55
58
|
private fishermanPriorityFeeDelta: Histogram;
|
|
56
59
|
private fishermanEstimatedCost: Histogram;
|
|
@@ -161,6 +164,18 @@ export class SequencerMetrics {
|
|
|
161
164
|
this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
|
|
162
165
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
|
|
163
166
|
);
|
|
167
|
+
|
|
168
|
+
this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
|
|
169
|
+
|
|
170
|
+
this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
|
|
171
|
+
|
|
172
|
+
this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(
|
|
173
|
+
this.meter,
|
|
174
|
+
Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL,
|
|
175
|
+
{
|
|
176
|
+
[Attributes.OK]: [true, false],
|
|
177
|
+
},
|
|
178
|
+
);
|
|
164
179
|
}
|
|
165
180
|
|
|
166
181
|
public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
|
|
@@ -281,10 +296,12 @@ export class SequencerMetrics {
|
|
|
281
296
|
|
|
282
297
|
// Record pending block snapshot data (once per strategy for comparison)
|
|
283
298
|
this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
|
|
299
|
+
this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
|
|
284
300
|
|
|
285
301
|
// Record mined block data if available
|
|
286
302
|
if (analysis.minedBlock) {
|
|
287
303
|
this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
|
|
304
|
+
this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
|
|
288
305
|
|
|
289
306
|
// Record actual fees from blob transactions in the mined block
|
|
290
307
|
for (const blobTx of analysis.minedBlock.includedBlobTxs) {
|
|
@@ -318,6 +335,13 @@ export class SequencerMetrics {
|
|
|
318
335
|
if (analysis.analysis) {
|
|
319
336
|
this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
|
|
320
337
|
|
|
338
|
+
// Record whether the block reached 100% blob capacity
|
|
339
|
+
if (analysis.analysis.blockBlobsFull) {
|
|
340
|
+
this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: true });
|
|
341
|
+
} else {
|
|
342
|
+
this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: false });
|
|
343
|
+
}
|
|
344
|
+
|
|
321
345
|
// Record strategy-specific inclusion result
|
|
322
346
|
if (strategyResult.wouldBeIncluded !== undefined) {
|
|
323
347
|
if (strategyResult.wouldBeIncluded) {
|
|
@@ -60,6 +60,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
60
60
|
/** The last slot for which we attempted to perform our voting duties with degraded block production */
|
|
61
61
|
private lastSlotForFallbackVote: SlotNumber | undefined;
|
|
62
62
|
|
|
63
|
+
/** The last slot for which we logged "no committee" warning, to avoid spam */
|
|
64
|
+
private lastSlotForNoCommitteeWarning: SlotNumber | undefined;
|
|
65
|
+
|
|
63
66
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */
|
|
64
67
|
private lastSlotForCheckpointProposalJob: SlotNumber | undefined;
|
|
65
68
|
|
|
@@ -373,6 +376,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
373
376
|
}
|
|
374
377
|
|
|
375
378
|
this.lastSlotForCheckpointProposalJob = slot;
|
|
379
|
+
await this.p2pClient.prepareForSlot(slot);
|
|
376
380
|
this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, { ...logCtx, proposer });
|
|
377
381
|
|
|
378
382
|
// Create and return the checkpoint proposal job
|
|
@@ -557,7 +561,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
557
561
|
proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
|
|
558
562
|
} catch (e) {
|
|
559
563
|
if (e instanceof NoCommitteeError) {
|
|
560
|
-
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
|
+
}
|
|
561
568
|
return [false, undefined];
|
|
562
569
|
}
|
|
563
570
|
this.log.error(`Error getting proposer for slot ${slot}`, e);
|
|
@@ -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,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
-
import { Timer } from '@aztec/foundation/timer';
|
|
4
3
|
import { L2Block } from '@aztec/stdlib/block';
|
|
5
4
|
import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
6
5
|
import { Gas } from '@aztec/stdlib/gas';
|
|
@@ -14,7 +13,7 @@ import type {
|
|
|
14
13
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
15
14
|
import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
|
|
16
15
|
import type { CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
17
|
-
import type {
|
|
16
|
+
import type { BuildBlockInCheckpointResult } from '@aztec/validator-client';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* A fake CheckpointBuilder for testing that implements the same interface as the real one.
|
|
@@ -76,7 +75,7 @@ export class MockCheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
76
75
|
blockNumber: BlockNumber,
|
|
77
76
|
timestamp: bigint,
|
|
78
77
|
opts: PublicProcessorLimits,
|
|
79
|
-
): Promise<
|
|
78
|
+
): Promise<BuildBlockInCheckpointResult> {
|
|
80
79
|
this.buildBlockCalls.push({ blockNumber, timestamp, opts });
|
|
81
80
|
|
|
82
81
|
if (this.errorOnBuild) {
|
|
@@ -117,7 +116,6 @@ export class MockCheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
117
116
|
publicGas: Gas.empty(),
|
|
118
117
|
publicProcessorDuration: 0,
|
|
119
118
|
numTxs: block?.body?.txEffects?.length ?? usedTxs.length,
|
|
120
|
-
blockBuildingTimer: new Timer(),
|
|
121
119
|
usedTxs,
|
|
122
120
|
failedTxs: [],
|
|
123
121
|
usedTxBlobFields: block?.body?.txEffects?.reduce((sum, tx) => sum + tx.getNumBlobFields(), 0) ?? 0,
|
|
@@ -207,6 +205,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
207
205
|
constants: CheckpointGlobalVariables;
|
|
208
206
|
l1ToL2Messages: Fr[];
|
|
209
207
|
previousCheckpointOutHashes: Fr[];
|
|
208
|
+
feeAssetPriceModifier: bigint;
|
|
210
209
|
}> = [];
|
|
211
210
|
public openCheckpointCalls: Array<{
|
|
212
211
|
checkpointNumber: CheckpointNumber;
|
|
@@ -214,6 +213,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
214
213
|
l1ToL2Messages: Fr[];
|
|
215
214
|
previousCheckpointOutHashes: Fr[];
|
|
216
215
|
existingBlocks: L2Block[];
|
|
216
|
+
feeAssetPriceModifier: bigint;
|
|
217
217
|
}> = [];
|
|
218
218
|
public updateConfigCalls: Array<Partial<FullNodeBlockBuilderConfig>> = [];
|
|
219
219
|
|
|
@@ -259,11 +259,18 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
259
259
|
startCheckpoint(
|
|
260
260
|
checkpointNumber: CheckpointNumber,
|
|
261
261
|
constants: CheckpointGlobalVariables,
|
|
262
|
+
feeAssetPriceModifier: bigint,
|
|
262
263
|
l1ToL2Messages: Fr[],
|
|
263
264
|
previousCheckpointOutHashes: Fr[],
|
|
264
265
|
_fork: MerkleTreeWriteOperations,
|
|
265
266
|
): Promise<ICheckpointBlockBuilder> {
|
|
266
|
-
this.startCheckpointCalls.push({
|
|
267
|
+
this.startCheckpointCalls.push({
|
|
268
|
+
checkpointNumber,
|
|
269
|
+
constants,
|
|
270
|
+
l1ToL2Messages,
|
|
271
|
+
previousCheckpointOutHashes,
|
|
272
|
+
feeAssetPriceModifier,
|
|
273
|
+
});
|
|
267
274
|
|
|
268
275
|
if (!this.checkpointBuilder) {
|
|
269
276
|
// Auto-create a builder if none was set
|
|
@@ -276,6 +283,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
276
283
|
openCheckpoint(
|
|
277
284
|
checkpointNumber: CheckpointNumber,
|
|
278
285
|
constants: CheckpointGlobalVariables,
|
|
286
|
+
feeAssetPriceModifier: bigint,
|
|
279
287
|
l1ToL2Messages: Fr[],
|
|
280
288
|
previousCheckpointOutHashes: Fr[],
|
|
281
289
|
_fork: MerkleTreeWriteOperations,
|
|
@@ -287,6 +295,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
287
295
|
l1ToL2Messages,
|
|
288
296
|
previousCheckpointOutHashes,
|
|
289
297
|
existingBlocks,
|
|
298
|
+
feeAssetPriceModifier,
|
|
290
299
|
});
|
|
291
300
|
|
|
292
301
|
if (!this.checkpointBuilder) {
|
package/src/test/utils.ts
CHANGED
|
@@ -56,6 +56,7 @@ export async function makeBlock(txs: Tx[], globalVariables: GlobalVariables): Pr
|
|
|
56
56
|
export function mockPendingTxs(p2p: MockProxy<P2P>, txs: Tx[]): void {
|
|
57
57
|
p2p.getPendingTxCount.mockResolvedValue(txs.length);
|
|
58
58
|
p2p.iteratePendingTxs.mockImplementation(() => mockTxIterator(Promise.resolve(txs)));
|
|
59
|
+
p2p.iterateEligiblePendingTxs.mockImplementation(() => mockTxIterator(Promise.resolve(txs)));
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/**
|
|
@@ -118,10 +119,11 @@ export function createCheckpointProposal(
|
|
|
118
119
|
block: L2Block,
|
|
119
120
|
checkpointSignature: Signature,
|
|
120
121
|
blockSignature?: Signature,
|
|
122
|
+
feeAssetPriceModifier: bigint = 0n,
|
|
121
123
|
): CheckpointProposal {
|
|
122
124
|
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
123
125
|
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
124
|
-
return new CheckpointProposal(checkpointHeader, block.archive.root, checkpointSignature, {
|
|
126
|
+
return new CheckpointProposal(checkpointHeader, block.archive.root, feeAssetPriceModifier, checkpointSignature, {
|
|
125
127
|
blockHeader: block.header,
|
|
126
128
|
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
127
129
|
txHashes,
|
|
@@ -138,9 +140,10 @@ export function createCheckpointAttestation(
|
|
|
138
140
|
block: L2Block,
|
|
139
141
|
signature: Signature,
|
|
140
142
|
sender: EthAddress,
|
|
143
|
+
feeAssetPriceModifier: bigint = 0n,
|
|
141
144
|
): CheckpointAttestation {
|
|
142
145
|
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
143
|
-
const payload = new ConsensusPayload(checkpointHeader, block.archive.root);
|
|
146
|
+
const payload = new ConsensusPayload(checkpointHeader, block.archive.root, feeAssetPriceModifier);
|
|
144
147
|
const attestation = new CheckpointAttestation(payload, signature, signature);
|
|
145
148
|
// Set sender directly for testing (bypasses signature recovery)
|
|
146
149
|
(attestation as any).sender = sender;
|