@aztec/sequencer-client 4.0.0-nightly.20260112 → 4.0.0-nightly.20260114
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/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/sequencer-publisher.d.ts +15 -15
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +44 -46
- package/dest/sequencer/block_builder.d.ts +2 -2
- package/dest/sequencer/block_builder.d.ts.map +1 -1
- package/dest/sequencer/block_builder.js +5 -5
- 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 +56 -30
- 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 +4 -4
- package/dest/sequencer/sequencer.d.ts +13 -12
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +30 -28
- 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 +13 -3
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +27 -2
- package/dest/test/utils.d.ts +8 -4
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +19 -9
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +3 -4
- package/src/config.ts +7 -0
- package/src/index.ts +0 -3
- package/src/publisher/sequencer-publisher.ts +75 -70
- package/src/sequencer/block_builder.ts +5 -3
- package/src/sequencer/checkpoint_proposal_job.ts +68 -38
- package/src/sequencer/index.ts +0 -1
- package/src/sequencer/metrics.ts +4 -4
- package/src/sequencer/sequencer.ts +47 -34
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +45 -3
- package/src/test/utils.ts +36 -9
- 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
package/src/sequencer/metrics.ts
CHANGED
|
@@ -43,7 +43,7 @@ export class SequencerMetrics {
|
|
|
43
43
|
private blockProposalPrecheckFailed: UpDownCounter;
|
|
44
44
|
private checkpointSuccess: UpDownCounter;
|
|
45
45
|
private slashingAttempts: UpDownCounter;
|
|
46
|
-
private
|
|
46
|
+
private checkpointAttestationDelay: Histogram;
|
|
47
47
|
|
|
48
48
|
// Fisherman fee analysis metrics
|
|
49
49
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
@@ -75,7 +75,7 @@ export class SequencerMetrics {
|
|
|
75
75
|
|
|
76
76
|
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
77
77
|
|
|
78
|
-
this.
|
|
78
|
+
this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
|
|
79
79
|
|
|
80
80
|
// Init gauges and counters
|
|
81
81
|
this.blockCounter.add(0, {
|
|
@@ -156,8 +156,8 @@ export class SequencerMetrics {
|
|
|
156
156
|
this.timeToCollectAttestations.record(0);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
public
|
|
160
|
-
this.
|
|
159
|
+
public recordCheckpointAttestationDelay(duration: number) {
|
|
160
|
+
this.checkpointAttestationDelay.record(duration);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
public recordCollectedAttestations(count: number, durationMs: number) {
|
|
@@ -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 { L2BlockNew, L2BlockSource,
|
|
15
|
+
import type { L2BlockNew, 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 {
|
|
@@ -25,15 +25,14 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
|
25
25
|
import { pickFromSchema } from '@aztec/stdlib/schemas';
|
|
26
26
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
27
27
|
import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
28
|
-
import type
|
|
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';
|
|
@@ -91,7 +90,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
91
90
|
protected p2pClient: P2P,
|
|
92
91
|
protected worldState: WorldStateSynchronizer,
|
|
93
92
|
protected slasherClient: SlasherClientInterface | undefined,
|
|
94
|
-
protected l2BlockSource: L2BlockSource,
|
|
93
|
+
protected l2BlockSource: L2BlockSource & L2BlockSink,
|
|
95
94
|
protected l1ToL2MessageSource: L1ToL2MessageSource,
|
|
96
95
|
protected checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
97
96
|
protected l1Constants: SequencerRollupConstants,
|
|
@@ -203,7 +202,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
203
202
|
const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
204
203
|
|
|
205
204
|
// 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);
|
|
205
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
|
|
207
206
|
if (!checkpointProposalJob) {
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
@@ -235,6 +234,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
235
234
|
*/
|
|
236
235
|
@trackSpan('Sequencer.prepareCheckpointProposal')
|
|
237
236
|
private async prepareCheckpointProposal(
|
|
237
|
+
epoch: EpochNumber,
|
|
238
238
|
slot: SlotNumber,
|
|
239
239
|
ts: bigint,
|
|
240
240
|
now: bigint,
|
|
@@ -264,8 +264,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
264
264
|
return undefined;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
//
|
|
268
|
-
const checkpointNumber = CheckpointNumber
|
|
267
|
+
// Next checkpoint follows from the last synced one
|
|
268
|
+
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
269
269
|
|
|
270
270
|
const logCtx = {
|
|
271
271
|
now,
|
|
@@ -281,9 +281,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
281
281
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
282
282
|
const [canPropose, proposer] = await this.checkCanPropose(slot);
|
|
283
283
|
|
|
284
|
-
// If we are not a proposer check if we should invalidate
|
|
284
|
+
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
285
285
|
if (!canPropose) {
|
|
286
|
-
await this.
|
|
286
|
+
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
287
287
|
return undefined;
|
|
288
288
|
}
|
|
289
289
|
|
|
@@ -313,15 +313,14 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
316
|
-
|
|
317
|
-
const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
|
|
316
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
318
317
|
|
|
319
318
|
// Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
|
|
320
319
|
// if all the previous checks are good, but we do it just in case.
|
|
321
320
|
const canProposeCheck = await publisher.canProposeAtNextEthBlock(
|
|
322
321
|
syncedTo.archive,
|
|
323
322
|
proposer ?? EthAddress.ZERO,
|
|
324
|
-
|
|
323
|
+
invalidateCheckpoint,
|
|
325
324
|
);
|
|
326
325
|
|
|
327
326
|
if (canProposeCheck === undefined) {
|
|
@@ -359,39 +358,44 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
359
358
|
|
|
360
359
|
// Create and return the checkpoint proposal job
|
|
361
360
|
return this.createCheckpointProposalJob(
|
|
361
|
+
epoch,
|
|
362
362
|
slot,
|
|
363
363
|
checkpointNumber,
|
|
364
364
|
syncedTo.blockNumber,
|
|
365
365
|
proposer,
|
|
366
366
|
publisher,
|
|
367
367
|
attestorAddress,
|
|
368
|
-
|
|
368
|
+
invalidateCheckpoint,
|
|
369
369
|
);
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
protected createCheckpointProposalJob(
|
|
373
|
+
epoch: EpochNumber,
|
|
373
374
|
slot: SlotNumber,
|
|
374
375
|
checkpointNumber: CheckpointNumber,
|
|
375
376
|
syncedToBlockNumber: BlockNumber,
|
|
376
377
|
proposer: EthAddress | undefined,
|
|
377
378
|
publisher: SequencerPublisher,
|
|
378
379
|
attestorAddress: EthAddress,
|
|
379
|
-
|
|
380
|
+
invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
|
|
380
381
|
): CheckpointProposalJob {
|
|
381
382
|
return new CheckpointProposalJob(
|
|
383
|
+
epoch,
|
|
382
384
|
slot,
|
|
383
385
|
checkpointNumber,
|
|
384
386
|
syncedToBlockNumber,
|
|
385
387
|
proposer,
|
|
386
388
|
publisher,
|
|
387
389
|
attestorAddress,
|
|
388
|
-
|
|
390
|
+
invalidateCheckpoint,
|
|
389
391
|
this.validatorClient,
|
|
390
392
|
this.globalsBuilder,
|
|
391
393
|
this.p2pClient,
|
|
392
394
|
this.worldState,
|
|
393
395
|
this.l1ToL2MessageSource,
|
|
396
|
+
this.l2BlockSource,
|
|
394
397
|
this.checkpointsBuilder,
|
|
398
|
+
this.l2BlockSource,
|
|
395
399
|
this.l1Constants,
|
|
396
400
|
this.config,
|
|
397
401
|
this.timetable,
|
|
@@ -471,9 +475,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
471
475
|
number: syncSummary.latestBlockNumber,
|
|
472
476
|
hash: syncSummary.latestBlockHash,
|
|
473
477
|
})),
|
|
474
|
-
this.l2BlockSource.getL2Tips().then(t => t.
|
|
478
|
+
this.l2BlockSource.getL2Tips().then(t => t.proposed),
|
|
475
479
|
this.p2pClient.getStatus().then(p2p => p2p.syncedToL2Block),
|
|
476
|
-
this.l1ToL2MessageSource.getL2Tips().then(t => t.
|
|
480
|
+
this.l1ToL2MessageSource.getL2Tips().then(t => t.proposed),
|
|
477
481
|
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
478
482
|
] as const);
|
|
479
483
|
|
|
@@ -481,6 +485,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
481
485
|
|
|
482
486
|
// 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,
|
|
483
487
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
488
|
+
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
484
489
|
const result =
|
|
485
490
|
(l2BlockSource.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0) ||
|
|
486
491
|
(worldState.hash === l2BlockSource.hash &&
|
|
@@ -496,7 +501,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
496
501
|
const blockNumber = worldState.number;
|
|
497
502
|
if (blockNumber < INITIAL_L2_BLOCK_NUM) {
|
|
498
503
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
499
|
-
return {
|
|
504
|
+
return {
|
|
505
|
+
checkpointNumber: CheckpointNumber.ZERO,
|
|
506
|
+
blockNumber: BlockNumber.ZERO,
|
|
507
|
+
archive,
|
|
508
|
+
l1Timestamp,
|
|
509
|
+
pendingChainValidationStatus,
|
|
510
|
+
};
|
|
500
511
|
}
|
|
501
512
|
|
|
502
513
|
const block = await this.l2BlockSource.getL2BlockNew(blockNumber);
|
|
@@ -509,6 +520,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
509
520
|
return {
|
|
510
521
|
block,
|
|
511
522
|
blockNumber: block.number,
|
|
523
|
+
checkpointNumber: block.checkpointNumber,
|
|
512
524
|
archive: block.archive.root,
|
|
513
525
|
l1Timestamp,
|
|
514
526
|
pendingChainValidationStatus,
|
|
@@ -629,12 +641,12 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
629
641
|
}
|
|
630
642
|
|
|
631
643
|
/**
|
|
632
|
-
* Considers invalidating a
|
|
644
|
+
* Considers invalidating a checkpoint if the pending chain is invalid. Depends on how long the invalid checkpoint
|
|
633
645
|
* has been there without being invalidated and whether the sequencer is in the committee or not. We always
|
|
634
646
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
635
647
|
* and if they fail, any sequencer will try as well.
|
|
636
648
|
*/
|
|
637
|
-
protected async
|
|
649
|
+
protected async considerInvalidatingCheckpoint(
|
|
638
650
|
syncedTo: SequencerSyncCheckResult,
|
|
639
651
|
currentSlot: SlotNumber,
|
|
640
652
|
): Promise<void> {
|
|
@@ -643,18 +655,18 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
643
655
|
return;
|
|
644
656
|
}
|
|
645
657
|
|
|
646
|
-
const
|
|
647
|
-
const
|
|
648
|
-
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(
|
|
658
|
+
const invalidCheckpointNumber = pendingChainValidationStatus.checkpoint.checkpointNumber;
|
|
659
|
+
const invalidCheckpointTimestamp = pendingChainValidationStatus.checkpoint.timestamp;
|
|
660
|
+
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(invalidCheckpointTimestamp);
|
|
649
661
|
const ourValidatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
650
662
|
|
|
651
663
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } =
|
|
652
664
|
this.config;
|
|
653
665
|
|
|
654
666
|
const logData = {
|
|
655
|
-
invalidL1Timestamp:
|
|
667
|
+
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
656
668
|
l1Timestamp,
|
|
657
|
-
|
|
669
|
+
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
658
670
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
659
671
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
660
672
|
ourValidatorAddresses,
|
|
@@ -700,25 +712,25 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
700
712
|
|
|
701
713
|
const { publisher } = await this.publisherFactory.create(validatorToUse);
|
|
702
714
|
|
|
703
|
-
const
|
|
704
|
-
if (!
|
|
705
|
-
this.log.warn(`Failed to simulate invalidate
|
|
715
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(pendingChainValidationStatus);
|
|
716
|
+
if (!invalidateCheckpoint) {
|
|
717
|
+
this.log.warn(`Failed to simulate invalidate checkpoint`, logData);
|
|
706
718
|
return;
|
|
707
719
|
}
|
|
708
720
|
|
|
709
721
|
this.log.info(
|
|
710
722
|
invalidateAsCommitteeMember
|
|
711
|
-
? `Invalidating
|
|
712
|
-
: `Invalidating
|
|
723
|
+
? `Invalidating checkpoint ${invalidCheckpointNumber} as committee member`
|
|
724
|
+
: `Invalidating checkpoint ${invalidCheckpointNumber} as non-committee member`,
|
|
713
725
|
logData,
|
|
714
726
|
);
|
|
715
727
|
|
|
716
|
-
publisher.
|
|
728
|
+
publisher.enqueueInvalidateCheckpoint(invalidateCheckpoint);
|
|
717
729
|
|
|
718
730
|
if (!this.config.fishermanMode) {
|
|
719
731
|
await publisher.sendRequests();
|
|
720
732
|
} else {
|
|
721
|
-
this.log.info('Invalidating
|
|
733
|
+
this.log.info('Invalidating checkpoint in fisherman mode, clearing pending requests');
|
|
722
734
|
publisher.clearPendingRequests();
|
|
723
735
|
}
|
|
724
736
|
}
|
|
@@ -792,8 +804,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
792
804
|
|
|
793
805
|
type SequencerSyncCheckResult = {
|
|
794
806
|
block?: L2BlockNew;
|
|
807
|
+
checkpointNumber: CheckpointNumber;
|
|
795
808
|
blockNumber: BlockNumber;
|
|
796
809
|
archive: Fr;
|
|
797
810
|
l1Timestamp: bigint;
|
|
798
|
-
pendingChainValidationStatus:
|
|
811
|
+
pendingChainValidationStatus: ValidateCheckpointResult;
|
|
799
812
|
};
|
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
|
|
|
@@ -9,12 +9,11 @@ import type { FullNodeBlockBuilderConfig, PublicProcessorLimits } from '@aztec/s
|
|
|
9
9
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
10
10
|
import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
|
|
11
11
|
import type { CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
12
|
-
|
|
13
12
|
import type {
|
|
14
13
|
BuildBlockInCheckpointResult,
|
|
15
14
|
CheckpointBuilder,
|
|
16
15
|
FullNodeCheckpointsBuilder,
|
|
17
|
-
} from '
|
|
16
|
+
} from '@aztec/validator-client';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* A fake CheckpointBuilder for testing that implements the same interface as the real one.
|
|
@@ -189,6 +188,14 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
|
|
|
189
188
|
checkpointNumber: CheckpointNumber;
|
|
190
189
|
constants: CheckpointGlobalVariables;
|
|
191
190
|
l1ToL2Messages: Fr[];
|
|
191
|
+
previousCheckpointOutHashes: Fr[];
|
|
192
|
+
}> = [];
|
|
193
|
+
public openCheckpointCalls: Array<{
|
|
194
|
+
checkpointNumber: CheckpointNumber;
|
|
195
|
+
constants: CheckpointGlobalVariables;
|
|
196
|
+
l1ToL2Messages: Fr[];
|
|
197
|
+
previousCheckpointOutHashes: Fr[];
|
|
198
|
+
existingBlocks: L2BlockNew[];
|
|
192
199
|
}> = [];
|
|
193
200
|
public updateConfigCalls: Array<Partial<FullNodeBlockBuilderConfig>> = [];
|
|
194
201
|
|
|
@@ -218,6 +225,15 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
|
|
|
218
225
|
return this.checkpointBuilder;
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
getConfig(): FullNodeBlockBuilderConfig {
|
|
229
|
+
return {
|
|
230
|
+
l1GenesisTime: 0n,
|
|
231
|
+
slotDuration: 24,
|
|
232
|
+
l1ChainId: 1,
|
|
233
|
+
rollupVersion: 1,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
221
237
|
updateConfig(config: Partial<FullNodeBlockBuilderConfig>): void {
|
|
222
238
|
this.updateConfigCalls.push(config);
|
|
223
239
|
}
|
|
@@ -226,9 +242,34 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
|
|
|
226
242
|
checkpointNumber: CheckpointNumber,
|
|
227
243
|
constants: CheckpointGlobalVariables,
|
|
228
244
|
l1ToL2Messages: Fr[],
|
|
245
|
+
previousCheckpointOutHashes: Fr[],
|
|
246
|
+
_fork: unknown,
|
|
247
|
+
): Promise<CheckpointBuilder> {
|
|
248
|
+
this.startCheckpointCalls.push({ checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes });
|
|
249
|
+
|
|
250
|
+
if (!this.checkpointBuilder) {
|
|
251
|
+
// Auto-create a builder if none was set
|
|
252
|
+
this.checkpointBuilder = new MockCheckpointBuilder(constants, checkpointNumber);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return Promise.resolve(this.checkpointBuilder as unknown as CheckpointBuilder);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
openCheckpoint(
|
|
259
|
+
checkpointNumber: CheckpointNumber,
|
|
260
|
+
constants: CheckpointGlobalVariables,
|
|
261
|
+
l1ToL2Messages: Fr[],
|
|
262
|
+
previousCheckpointOutHashes: Fr[],
|
|
229
263
|
_fork: unknown,
|
|
264
|
+
existingBlocks: L2BlockNew[] = [],
|
|
230
265
|
): Promise<CheckpointBuilder> {
|
|
231
|
-
this.
|
|
266
|
+
this.openCheckpointCalls.push({
|
|
267
|
+
checkpointNumber,
|
|
268
|
+
constants,
|
|
269
|
+
l1ToL2Messages,
|
|
270
|
+
previousCheckpointOutHashes,
|
|
271
|
+
existingBlocks,
|
|
272
|
+
});
|
|
232
273
|
|
|
233
274
|
if (!this.checkpointBuilder) {
|
|
234
275
|
// Auto-create a builder if none was set
|
|
@@ -242,6 +283,7 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
|
|
|
242
283
|
reset(): void {
|
|
243
284
|
this.checkpointBuilder = undefined;
|
|
244
285
|
this.startCheckpointCalls = [];
|
|
286
|
+
this.openCheckpointCalls = [];
|
|
245
287
|
this.updateConfigCalls = [];
|
|
246
288
|
}
|
|
247
289
|
}
|
package/src/test/utils.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Signature } from '@aztec/foundation/eth-signature';
|
|
|
8
8
|
import type { P2P } from '@aztec/p2p';
|
|
9
9
|
import { PublicDataWrite } from '@aztec/stdlib/avm';
|
|
10
10
|
import { CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
|
|
11
|
-
import {
|
|
11
|
+
import { BlockProposal, CheckpointAttestation, CheckpointProposal, ConsensusPayload } from '@aztec/stdlib/p2p';
|
|
12
12
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
13
13
|
import { makeAppendOnlyTreeSnapshot, mockTxForRollup } from '@aztec/stdlib/testing';
|
|
14
14
|
import { BlockHeader, GlobalVariables, type Tx, makeProcessedTxFromPrivateOnlyTx } from '@aztec/stdlib/tx';
|
|
@@ -80,6 +80,7 @@ function createCheckpointHeaderFromBlock(block: L2BlockNew): CheckpointHeader {
|
|
|
80
80
|
Fr.random(), // blockHeadersHash - mock value for testing
|
|
81
81
|
Fr.random(), // blobsHash - mock value for testing
|
|
82
82
|
Fr.random(), // inHash - mock value for testing
|
|
83
|
+
Fr.random(), // outHash - mock value for testing
|
|
83
84
|
gv.slotNumber,
|
|
84
85
|
gv.timestamp,
|
|
85
86
|
gv.coinbase,
|
|
@@ -93,23 +94,49 @@ function createCheckpointHeaderFromBlock(block: L2BlockNew): CheckpointHeader {
|
|
|
93
94
|
* Creates a block proposal from a block and signature
|
|
94
95
|
*/
|
|
95
96
|
export function createBlockProposal(block: L2BlockNew, signature: Signature): BlockProposal {
|
|
96
|
-
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
97
|
-
const consensusPayload = new ConsensusPayload(checkpointHeader, block.archive.root);
|
|
98
97
|
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
99
|
-
return new BlockProposal(
|
|
98
|
+
return new BlockProposal(
|
|
99
|
+
block.header,
|
|
100
|
+
block.indexWithinCheckpoint,
|
|
101
|
+
Fr.ZERO, // inHash - using zero for testing
|
|
102
|
+
block.archive.root,
|
|
103
|
+
txHashes,
|
|
104
|
+
signature,
|
|
105
|
+
);
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
/**
|
|
103
|
-
* Creates a
|
|
109
|
+
* Creates a checkpoint proposal from a block and signature
|
|
110
|
+
*/
|
|
111
|
+
export function createCheckpointProposal(
|
|
112
|
+
block: L2BlockNew,
|
|
113
|
+
checkpointSignature: Signature,
|
|
114
|
+
blockSignature?: Signature,
|
|
115
|
+
): CheckpointProposal {
|
|
116
|
+
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
117
|
+
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
118
|
+
return new CheckpointProposal(checkpointHeader, block.archive.root, checkpointSignature, {
|
|
119
|
+
blockHeader: block.header,
|
|
120
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
121
|
+
txHashes,
|
|
122
|
+
signature: blockSignature ?? checkpointSignature, // Use checkpoint signature as block signature if not provided
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a checkpoint attestation from a block and signature.
|
|
104
128
|
* Note: We manually set the sender since we use random signatures in tests.
|
|
105
129
|
* In production, the sender is recovered from the signature.
|
|
106
130
|
*/
|
|
107
|
-
export function
|
|
131
|
+
export function createCheckpointAttestation(
|
|
132
|
+
block: L2BlockNew,
|
|
133
|
+
signature: Signature,
|
|
134
|
+
sender: EthAddress,
|
|
135
|
+
): CheckpointAttestation {
|
|
108
136
|
const checkpointHeader = createCheckpointHeaderFromBlock(block);
|
|
109
|
-
const
|
|
110
|
-
const attestation = new
|
|
137
|
+
const payload = new ConsensusPayload(checkpointHeader, block.archive.root);
|
|
138
|
+
const attestation = new CheckpointAttestation(payload, signature, signature);
|
|
111
139
|
// Set sender directly for testing (bypasses signature recovery)
|
|
112
|
-
|
|
113
140
|
(attestation as any).sender = sender;
|
|
114
141
|
return attestation;
|
|
115
142
|
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
-
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
4
|
-
import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
|
|
5
|
-
import { PublicProcessor } from '@aztec/simulator/server';
|
|
6
|
-
import { L2BlockNew } from '@aztec/stdlib/block';
|
|
7
|
-
import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
8
|
-
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
9
|
-
import { Gas } from '@aztec/stdlib/gas';
|
|
10
|
-
import { type FullNodeBlockBuilderConfig, type MerkleTreeWriteOperations, type PublicProcessorLimits } from '@aztec/stdlib/interfaces/server';
|
|
11
|
-
import { type CheckpointGlobalVariables, type FailedTx, GlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
12
|
-
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
13
|
-
export interface BuildBlockInCheckpointResult {
|
|
14
|
-
block: L2BlockNew;
|
|
15
|
-
publicGas: Gas;
|
|
16
|
-
publicProcessorDuration: number;
|
|
17
|
-
numTxs: number;
|
|
18
|
-
failedTxs: FailedTx[];
|
|
19
|
-
blockBuildingTimer: Timer;
|
|
20
|
-
usedTxs: Tx[];
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Builder for a single checkpoint. Handles building blocks within the checkpoint
|
|
24
|
-
* and completing it.
|
|
25
|
-
*/
|
|
26
|
-
export declare class CheckpointBuilder {
|
|
27
|
-
private checkpointBuilder;
|
|
28
|
-
private fork;
|
|
29
|
-
private config;
|
|
30
|
-
private contractDataSource;
|
|
31
|
-
private dateProvider;
|
|
32
|
-
private telemetryClient;
|
|
33
|
-
constructor(checkpointBuilder: LightweightCheckpointBuilder, fork: MerkleTreeWriteOperations, config: FullNodeBlockBuilderConfig, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient: TelemetryClient);
|
|
34
|
-
getConstantData(): CheckpointGlobalVariables;
|
|
35
|
-
/**
|
|
36
|
-
* Builds a single block within this checkpoint.
|
|
37
|
-
*/
|
|
38
|
-
buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, blockNumber: BlockNumber, timestamp: bigint, opts: PublicProcessorLimits): Promise<BuildBlockInCheckpointResult>;
|
|
39
|
-
/** Completes the checkpoint and returns it. */
|
|
40
|
-
completeCheckpoint(): Promise<Checkpoint>;
|
|
41
|
-
/** Gets the checkpoint currently in progress. */
|
|
42
|
-
getCheckpoint(): Promise<Checkpoint>;
|
|
43
|
-
protected makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations): Promise<{
|
|
44
|
-
processor: PublicProcessor;
|
|
45
|
-
validator: import("@aztec/stdlib/interfaces/server").PublicProcessorValidator;
|
|
46
|
-
}>;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Factory for creating checkpoint builders.
|
|
50
|
-
*/
|
|
51
|
-
export declare class FullNodeCheckpointsBuilder {
|
|
52
|
-
private config;
|
|
53
|
-
private contractDataSource;
|
|
54
|
-
private dateProvider;
|
|
55
|
-
private telemetryClient;
|
|
56
|
-
constructor(config: FullNodeBlockBuilderConfig, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient?: TelemetryClient);
|
|
57
|
-
updateConfig(config: Partial<FullNodeBlockBuilderConfig>): void;
|
|
58
|
-
/**
|
|
59
|
-
* Starts a new checkpoint and returns a CheckpointBuilder to build blocks within it.
|
|
60
|
-
*/
|
|
61
|
-
startCheckpoint(checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, l1ToL2Messages: Fr[], fork: MerkleTreeWriteOperations): Promise<CheckpointBuilder>;
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF9idWlsZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL2NoZWNrcG9pbnRfYnVpbGRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFaEYsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBR3BELE9BQU8sRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkUsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDMUUsT0FBTyxFQUdMLGVBQWUsRUFFaEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3RELE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDakUsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3hDLE9BQU8sRUFDTCxLQUFLLDBCQUEwQixFQUUvQixLQUFLLHlCQUF5QixFQUM5QixLQUFLLHFCQUFxQixFQUMzQixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxLQUFLLHlCQUF5QixFQUFFLEtBQUssUUFBUSxFQUFFLGVBQWUsRUFBRSxFQUFFLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN0RyxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQXNCLE1BQU0seUJBQXlCLENBQUM7QUFNbkYsTUFBTSxXQUFXLDRCQUE0QjtJQUMzQyxLQUFLLEVBQUUsVUFBVSxDQUFDO0lBQ2xCLFNBQVMsRUFBRSxHQUFHLENBQUM7SUFDZix1QkFBdUIsRUFBRSxNQUFNLENBQUM7SUFDaEMsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLFNBQVMsRUFBRSxRQUFRLEVBQUUsQ0FBQztJQUN0QixrQkFBa0IsRUFBRSxLQUFLLENBQUM7SUFDMUIsT0FBTyxFQUFFLEVBQUUsRUFBRSxDQUFDO0NBQ2Y7QUFFRDs7O0dBR0c7QUFDSCxxQkFBYSxpQkFBaUI7SUFFMUIsT0FBTyxDQUFDLGlCQUFpQjtJQUN6QixPQUFPLENBQUMsSUFBSTtJQUNaLE9BQU8sQ0FBQyxNQUFNO0lBQ2QsT0FBTyxDQUFDLGtCQUFrQjtJQUMxQixPQUFPLENBQUMsWUFBWTtJQUNwQixPQUFPLENBQUMsZUFBZTtJQU56QixZQUNVLGlCQUFpQixFQUFFLDRCQUE0QixFQUMvQyxJQUFJLEVBQUUseUJBQXlCLEVBQy9CLE1BQU0sRUFBRSwwQkFBMEIsRUFDbEMsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFlBQVksRUFBRSxZQUFZLEVBQzFCLGVBQWUsRUFBRSxlQUFlLEVBQ3RDO0lBRUosZUFBZSxJQUFJLHlCQUF5QixDQUUzQztJQUVEOztPQUVHO0lBQ0csVUFBVSxDQUNkLFVBQVUsRUFBRSxRQUFRLENBQUMsRUFBRSxDQUFDLEdBQUcsYUFBYSxDQUFDLEVBQUUsQ0FBQyxFQUM1QyxXQUFXLEVBQUUsV0FBVyxFQUN4QixTQUFTLEVBQUUsTUFBTSxFQUNqQixJQUFJLEVBQUUscUJBQXFCLEdBQzFCLE9BQU8sQ0FBQyw0QkFBNEIsQ0FBQyxDQXdDdkM7SUFFRCwrQ0FBK0M7SUFDekMsa0JBQWtCLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQVU5QztJQUVELGlEQUFpRDtJQUNqRCxhQUFhLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUVuQztJQUVELFVBQWdCLG9CQUFvQixDQUFDLGVBQWUsRUFBRSxlQUFlLEVBQUUsSUFBSSxFQUFFLHlCQUF5Qjs7O09Ba0NyRztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxxQkFBYSwwQkFBMEI7SUFFbkMsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsa0JBQWtCO0lBQzFCLE9BQU8sQ0FBQyxZQUFZO0lBQ3BCLE9BQU8sQ0FBQyxlQUFlO0lBSnpCLFlBQ1UsTUFBTSxFQUFFLDBCQUEwQixFQUNsQyxrQkFBa0IsRUFBRSxrQkFBa0IsRUFDdEMsWUFBWSxFQUFFLFlBQVksRUFDMUIsZUFBZSxHQUFFLGVBQXNDLEVBQzdEO0lBRUcsWUFBWSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsMEJBQTBCLENBQUMsUUFFOUQ7SUFFRDs7T0FFRztJQUNHLGVBQWUsQ0FDbkIsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2xDLFNBQVMsRUFBRSx5QkFBeUIsRUFDcEMsY0FBYyxFQUFFLEVBQUUsRUFBRSxFQUNwQixJQUFJLEVBQUUseUJBQXlCLEdBQzlCLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQTJCNUI7Q0FDRiJ9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint_builder.d.ts","sourceRoot":"","sources":["../../src/sequencer/checkpoint_builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAGpD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAW,MAAM,yBAAyB,CAAC;AAEvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAGL,eAAe,EAEhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EACL,KAAK,0BAA0B,EAE/B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,yBAAyB,EAAE,KAAK,QAAQ,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAMnF,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,GAAG,CAAC;IACf,uBAAuB,EAAE,MAAM,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,kBAAkB,EAAE,KAAK,CAAC;IAC1B,OAAO,EAAE,EAAE,EAAE,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,eAAe;IANzB,YACU,iBAAiB,EAAE,4BAA4B,EAC/C,IAAI,EAAE,yBAAyB,EAC/B,MAAM,EAAE,0BAA0B,EAClC,kBAAkB,EAAE,kBAAkB,EACtC,YAAY,EAAE,YAAY,EAC1B,eAAe,EAAE,eAAe,EACtC;IAEJ,eAAe,IAAI,yBAAyB,CAE3C;IAED;;OAEG;IACG,UAAU,CACd,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,EAC5C,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,4BAA4B,CAAC,CAwCvC;IAED,+CAA+C;IACzC,kBAAkB,IAAI,OAAO,CAAC,UAAU,CAAC,CAU9C;IAED,iDAAiD;IACjD,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAEnC;IAED,UAAgB,oBAAoB,CAAC,eAAe,EAAE,eAAe,EAAE,IAAI,EAAE,yBAAyB;;;OAkCrG;CACF;AAED;;GAEG;AACH,qBAAa,0BAA0B;IAEnC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,eAAe;IAJzB,YACU,MAAM,EAAE,0BAA0B,EAClC,kBAAkB,EAAE,kBAAkB,EACtC,YAAY,EAAE,YAAY,EAC1B,eAAe,GAAE,eAAsC,EAC7D;IAEG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,0BAA0B,CAAC,QAE9D;IAED;;OAEG;IACG,eAAe,CACnB,gBAAgB,EAAE,gBAAgB,EAClC,SAAS,EAAE,yBAAyB,EACpC,cAAc,EAAE,EAAE,EAAE,EACpB,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CA2B5B;CACF"}
|