@aztec/sequencer-client 0.0.1-commit.3f296a7d2 → 0.0.1-commit.42ee6df9b
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/global_variable_builder/global_builder.d.ts +3 -3
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +7 -4
- package/dest/publisher/sequencer-publisher.d.ts +46 -24
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +72 -40
- package/dest/sequencer/checkpoint_proposal_job.d.ts +28 -9
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +149 -74
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/sequencer.d.ts +14 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +60 -15
- package/package.json +27 -27
- package/src/global_variable_builder/global_builder.ts +15 -3
- package/src/publisher/sequencer-publisher.ts +113 -51
- package/src/sequencer/checkpoint_proposal_job.ts +181 -77
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/sequencer.ts +89 -19
|
@@ -454,6 +454,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
454
454
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
|
|
455
455
|
/** Last successful checkpoint proposed */ lastCheckpointProposed;
|
|
456
456
|
/** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
|
|
457
|
+
/** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */ lastCheckpointProposalJob;
|
|
457
458
|
/** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
|
|
458
459
|
/** Config for the sequencer */ config;
|
|
459
460
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
@@ -496,6 +497,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
496
497
|
});
|
|
497
498
|
await this.publisherFactory.stopAll();
|
|
498
499
|
await this.runningPromise?.stop();
|
|
500
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
499
501
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
500
502
|
force: true
|
|
501
503
|
});
|
|
@@ -546,6 +548,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
546
548
|
if (!checkpointProposalJob) {
|
|
547
549
|
return;
|
|
548
550
|
}
|
|
551
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
552
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
549
553
|
// Execute the checkpoint proposal job
|
|
550
554
|
const checkpoint = await checkpointProposalJob.execute();
|
|
551
555
|
// Update last checkpoint proposed (currently unused)
|
|
@@ -612,6 +616,13 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
612
616
|
}
|
|
613
617
|
// Next checkpoint follows from the last synced one
|
|
614
618
|
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
619
|
+
// Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
|
|
620
|
+
// confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
|
|
621
|
+
const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
|
|
622
|
+
if (checkpointNumber > confirmedCkpt + 2) {
|
|
623
|
+
this.log.warn(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
615
626
|
const logCtx = {
|
|
616
627
|
nowSeconds,
|
|
617
628
|
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
@@ -651,12 +662,35 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
651
662
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
652
663
|
}
|
|
653
664
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
654
|
-
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
665
|
+
let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
666
|
+
// Determine the correct archive and L1 state overrides for the canProposeAt check.
|
|
667
|
+
// The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
|
|
668
|
+
// When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
|
|
669
|
+
let archiveForCheck = syncedTo.archive;
|
|
670
|
+
const l1Overrides = {};
|
|
671
|
+
if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
|
|
672
|
+
// Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
|
|
673
|
+
// and the archive at that checkpoint so L1 simulation sees the correct chain tip.
|
|
674
|
+
const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
675
|
+
l1Overrides.forcePendingCheckpointNumber = parentCheckpointNumber;
|
|
676
|
+
l1Overrides.forceArchive = {
|
|
677
|
+
checkpointNumber: parentCheckpointNumber,
|
|
678
|
+
archive: syncedTo.archive
|
|
679
|
+
};
|
|
680
|
+
this.metrics.recordPipelineDepth(1);
|
|
681
|
+
this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
|
|
682
|
+
// Clear the invalidation - the proposed checkpoint should handle it.
|
|
683
|
+
invalidateCheckpoint = undefined;
|
|
684
|
+
} else if (invalidateCheckpoint) {
|
|
685
|
+
// After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
|
|
686
|
+
// exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
|
|
687
|
+
archiveForCheck = invalidateCheckpoint.lastArchive;
|
|
688
|
+
l1Overrides.forcePendingCheckpointNumber = invalidateCheckpoint.forcePendingCheckpointNumber;
|
|
689
|
+
this.metrics.recordPipelineDepth(0);
|
|
690
|
+
} else {
|
|
691
|
+
this.metrics.recordPipelineDepth(0);
|
|
692
|
+
}
|
|
693
|
+
const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, l1Overrides);
|
|
660
694
|
if (canProposeCheck === undefined) {
|
|
661
695
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
662
696
|
this.emit('proposer-rollup-check-failed', {
|
|
@@ -700,10 +734,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
700
734
|
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
701
735
|
});
|
|
702
736
|
// Create and return the checkpoint proposal job
|
|
703
|
-
return this.createCheckpointProposalJob(slot, targetSlot,
|
|
737
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
704
738
|
}
|
|
705
|
-
createCheckpointProposalJob(slot, targetSlot,
|
|
706
|
-
return new CheckpointProposalJob(slot, targetSlot,
|
|
739
|
+
createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
|
|
740
|
+
return new CheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, this.validatorClient, this.globalsBuilder, this.p2pClient, this.worldState, this.l1ToL2MessageSource, this.l2BlockSource, this.checkpointsBuilder, this.l2BlockSource, this.l1Constants, this.config, this.timetable, this.slasherClient, this.epochCache, this.dateProvider, this.metrics, this, this.setState.bind(this), this.tracer, this.log.getBindings(), proposedCheckpointData);
|
|
707
741
|
}
|
|
708
742
|
/**
|
|
709
743
|
* Returns the current sequencer state.
|
|
@@ -767,20 +801,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
767
801
|
number: syncSummary.latestBlockNumber,
|
|
768
802
|
hash: syncSummary.latestBlockHash
|
|
769
803
|
})),
|
|
770
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
804
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
805
|
+
proposed: t.proposed,
|
|
806
|
+
checkpointed: t.checkpointed,
|
|
807
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
808
|
+
})),
|
|
771
809
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
772
810
|
this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
|
|
773
|
-
this.l2BlockSource.getPendingChainValidationStatus()
|
|
811
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
812
|
+
this.l2BlockSource.getProposedCheckpointOnly()
|
|
774
813
|
]);
|
|
775
|
-
const [worldState,
|
|
814
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSource, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
776
815
|
// 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,
|
|
777
816
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
778
817
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
779
|
-
const result =
|
|
818
|
+
const result = l2Tips.proposed.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0 || worldState.hash === l2Tips.proposed.hash && p2p.hash === l2Tips.proposed.hash && l1ToL2MessageSource.hash === l2Tips.proposed.hash;
|
|
780
819
|
if (!result) {
|
|
781
820
|
this.log.debug(`Sequencer sync check failed`, {
|
|
782
821
|
worldState,
|
|
783
|
-
l2BlockSource,
|
|
822
|
+
l2BlockSource: l2Tips.proposed,
|
|
784
823
|
p2p,
|
|
785
824
|
l1ToL2MessageSource
|
|
786
825
|
});
|
|
@@ -792,8 +831,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
792
831
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
793
832
|
return {
|
|
794
833
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
834
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
795
835
|
blockNumber: BlockNumber.ZERO,
|
|
796
836
|
archive,
|
|
837
|
+
hasProposedCheckpoint: false,
|
|
797
838
|
syncedL2Slot,
|
|
798
839
|
pendingChainValidationStatus
|
|
799
840
|
};
|
|
@@ -804,11 +845,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
804
845
|
this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
|
|
805
846
|
return undefined;
|
|
806
847
|
}
|
|
848
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
807
849
|
return {
|
|
808
850
|
blockData,
|
|
809
851
|
blockNumber: blockData.header.getBlockNumber(),
|
|
810
852
|
checkpointNumber: blockData.checkpointNumber,
|
|
853
|
+
checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
|
|
811
854
|
archive: blockData.archive.root,
|
|
855
|
+
hasProposedCheckpoint,
|
|
856
|
+
proposedCheckpointData,
|
|
812
857
|
syncedL2Slot,
|
|
813
858
|
pendingChainValidationStatus
|
|
814
859
|
};
|
|
@@ -864,7 +909,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
864
909
|
proposer
|
|
865
910
|
];
|
|
866
911
|
}
|
|
867
|
-
this.log.
|
|
912
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
868
913
|
targetSlot,
|
|
869
914
|
proposer
|
|
870
915
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/sequencer-client",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.42ee6df9b",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -26,37 +26,37 @@
|
|
|
26
26
|
"test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@aztec/aztec.js": "0.0.1-commit.
|
|
30
|
-
"@aztec/bb-prover": "0.0.1-commit.
|
|
31
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
32
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
33
|
-
"@aztec/constants": "0.0.1-commit.
|
|
34
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
35
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
36
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
37
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
38
|
-
"@aztec/node-keystore": "0.0.1-commit.
|
|
39
|
-
"@aztec/noir-acvm_js": "0.0.1-commit.
|
|
40
|
-
"@aztec/noir-contracts.js": "0.0.1-commit.
|
|
41
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
42
|
-
"@aztec/noir-types": "0.0.1-commit.
|
|
43
|
-
"@aztec/p2p": "0.0.1-commit.
|
|
44
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
45
|
-
"@aztec/prover-client": "0.0.1-commit.
|
|
46
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
47
|
-
"@aztec/slasher": "0.0.1-commit.
|
|
48
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
49
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
50
|
-
"@aztec/validator-client": "0.0.1-commit.
|
|
51
|
-
"@aztec/validator-ha-signer": "0.0.1-commit.
|
|
52
|
-
"@aztec/world-state": "0.0.1-commit.
|
|
29
|
+
"@aztec/aztec.js": "0.0.1-commit.42ee6df9b",
|
|
30
|
+
"@aztec/bb-prover": "0.0.1-commit.42ee6df9b",
|
|
31
|
+
"@aztec/blob-client": "0.0.1-commit.42ee6df9b",
|
|
32
|
+
"@aztec/blob-lib": "0.0.1-commit.42ee6df9b",
|
|
33
|
+
"@aztec/constants": "0.0.1-commit.42ee6df9b",
|
|
34
|
+
"@aztec/epoch-cache": "0.0.1-commit.42ee6df9b",
|
|
35
|
+
"@aztec/ethereum": "0.0.1-commit.42ee6df9b",
|
|
36
|
+
"@aztec/foundation": "0.0.1-commit.42ee6df9b",
|
|
37
|
+
"@aztec/l1-artifacts": "0.0.1-commit.42ee6df9b",
|
|
38
|
+
"@aztec/node-keystore": "0.0.1-commit.42ee6df9b",
|
|
39
|
+
"@aztec/noir-acvm_js": "0.0.1-commit.42ee6df9b",
|
|
40
|
+
"@aztec/noir-contracts.js": "0.0.1-commit.42ee6df9b",
|
|
41
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.42ee6df9b",
|
|
42
|
+
"@aztec/noir-types": "0.0.1-commit.42ee6df9b",
|
|
43
|
+
"@aztec/p2p": "0.0.1-commit.42ee6df9b",
|
|
44
|
+
"@aztec/protocol-contracts": "0.0.1-commit.42ee6df9b",
|
|
45
|
+
"@aztec/prover-client": "0.0.1-commit.42ee6df9b",
|
|
46
|
+
"@aztec/simulator": "0.0.1-commit.42ee6df9b",
|
|
47
|
+
"@aztec/slasher": "0.0.1-commit.42ee6df9b",
|
|
48
|
+
"@aztec/stdlib": "0.0.1-commit.42ee6df9b",
|
|
49
|
+
"@aztec/telemetry-client": "0.0.1-commit.42ee6df9b",
|
|
50
|
+
"@aztec/validator-client": "0.0.1-commit.42ee6df9b",
|
|
51
|
+
"@aztec/validator-ha-signer": "0.0.1-commit.42ee6df9b",
|
|
52
|
+
"@aztec/world-state": "0.0.1-commit.42ee6df9b",
|
|
53
53
|
"lodash.chunk": "^4.2.0",
|
|
54
54
|
"tslib": "^2.4.0",
|
|
55
55
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@aztec/archiver": "0.0.1-commit.
|
|
59
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
58
|
+
"@aztec/archiver": "0.0.1-commit.42ee6df9b",
|
|
59
|
+
"@aztec/kv-store": "0.0.1-commit.42ee6df9b",
|
|
60
60
|
"@electric-sql/pglite": "^0.3.14",
|
|
61
61
|
"@jest/globals": "^30.0.0",
|
|
62
62
|
"@types/jest": "^30.0.0",
|
|
@@ -10,6 +10,7 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
10
10
|
import { type L1RollupConstants, getNextL1SlotTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
11
11
|
import { GasFees } from '@aztec/stdlib/gas';
|
|
12
12
|
import type {
|
|
13
|
+
BuildCheckpointGlobalVariablesOpts,
|
|
13
14
|
CheckpointGlobalVariables,
|
|
14
15
|
GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
15
16
|
} from '@aztec/stdlib/tx';
|
|
@@ -119,6 +120,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
119
120
|
coinbase: EthAddress,
|
|
120
121
|
feeRecipient: AztecAddress,
|
|
121
122
|
slotNumber: SlotNumber,
|
|
123
|
+
opts?: BuildCheckpointGlobalVariablesOpts,
|
|
122
124
|
): Promise<CheckpointGlobalVariables> {
|
|
123
125
|
const { chainId, version } = this;
|
|
124
126
|
|
|
@@ -127,9 +129,19 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
127
129
|
l1GenesisTime: this.l1GenesisTime,
|
|
128
130
|
});
|
|
129
131
|
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
const
|
|
132
|
+
// When pipelining, force the proposed checkpoint number and fee header to the parent so that
|
|
133
|
+
// the fee computation matches what L1 will see when the previous pipelined checkpoint has landed.
|
|
134
|
+
const pendingNumberOverride = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
135
|
+
opts?.forcePendingCheckpointNumber,
|
|
136
|
+
);
|
|
137
|
+
const feeHeaderOverride = opts?.forceProposedFeeHeader
|
|
138
|
+
? await this.rollupContract.makeFeeHeaderOverride(
|
|
139
|
+
opts.forceProposedFeeHeader.checkpointNumber,
|
|
140
|
+
opts.forceProposedFeeHeader.feeHeader,
|
|
141
|
+
)
|
|
142
|
+
: [];
|
|
143
|
+
const stateOverride = RollupContract.mergeStateOverrides(pendingNumberOverride, feeHeaderOverride);
|
|
144
|
+
const gasFees = new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true, stateOverride));
|
|
133
145
|
|
|
134
146
|
return { chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees };
|
|
135
147
|
}
|