@aztec/sequencer-client 0.0.1-commit.f650c0a5c → 0.0.1-commit.f7ea82942
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 +3 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +3 -0
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +8 -11
- package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
- package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_predictor.js +128 -0
- package/dest/global_variable_builder/fee_provider.d.ts +21 -0
- package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_provider.js +58 -0
- package/dest/global_variable_builder/global_builder.d.ts +4 -9
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +3 -41
- package/dest/global_variable_builder/index.d.ts +3 -1
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +2 -0
- package/dest/publisher/config.d.ts +1 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +2 -2
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +3 -4
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.d.ts +10 -28
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +24 -43
- package/dest/sequencer/chain_state_overrides.d.ts +25 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +39 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +24 -21
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +263 -120
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
- package/dest/sequencer/events.d.ts +6 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +7 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +27 -20
- package/dest/sequencer/sequencer.d.ts +5 -2
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +15 -11
- package/dest/sequencer/timetable.d.ts +11 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +42 -43
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +5 -0
- package/src/config.ts +8 -11
- package/src/global_variable_builder/README.md +44 -0
- package/src/global_variable_builder/fee_predictor.ts +172 -0
- package/src/global_variable_builder/fee_provider.ts +75 -0
- package/src/global_variable_builder/global_builder.ts +7 -54
- package/src/global_variable_builder/index.ts +2 -0
- package/src/publisher/config.ts +6 -4
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-publisher.ts +49 -83
- package/src/sequencer/README.md +9 -8
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +328 -162
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/events.ts +5 -0
- package/src/sequencer/metrics.ts +35 -25
- package/src/sequencer/sequencer.ts +22 -11
- package/src/sequencer/timetable.ts +50 -49
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
RollupContract,
|
|
3
|
+
type SimulationOverridesPlan,
|
|
4
|
+
buildSimulationOverridesStateOverride,
|
|
5
|
+
} from '@aztec/ethereum/contracts';
|
|
2
6
|
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
3
7
|
import type { ViemPublicClient } from '@aztec/ethereum/types';
|
|
4
8
|
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
5
9
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
10
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
8
11
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
9
12
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
13
|
import { type L1RollupConstants, getNextL1SlotTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
11
14
|
import { GasFees } from '@aztec/stdlib/gas';
|
|
12
15
|
import type {
|
|
13
|
-
BuildCheckpointGlobalVariablesOpts,
|
|
14
16
|
CheckpointGlobalVariables,
|
|
15
17
|
GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
16
18
|
} from '@aztec/stdlib/tx';
|
|
@@ -27,10 +29,6 @@ export type GlobalVariableBuilderConfig = {
|
|
|
27
29
|
* Simple global variables builder.
|
|
28
30
|
*/
|
|
29
31
|
export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
30
|
-
private log = createLogger('sequencer:global_variable_builder');
|
|
31
|
-
private currentMinFees: Promise<GasFees> = Promise.resolve(new GasFees(0, 0));
|
|
32
|
-
private currentL1BlockNumber: bigint | undefined = undefined;
|
|
33
|
-
|
|
34
32
|
private readonly rollupContract: RollupContract;
|
|
35
33
|
private readonly ethereumSlotDuration: number;
|
|
36
34
|
private readonly aztecSlotDuration: number;
|
|
@@ -54,40 +52,6 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
54
52
|
this.rollupContract = new RollupContract(this.publicClient, config.l1Contracts.rollupAddress);
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
/**
|
|
58
|
-
* Computes the "current" min fees, e.g., the price that you currently should pay to get include in the next block
|
|
59
|
-
* @returns Min fees for the next block
|
|
60
|
-
*/
|
|
61
|
-
private async computeCurrentMinFees(): Promise<GasFees> {
|
|
62
|
-
// Since this might be called in the middle of a slot where a block might have been published,
|
|
63
|
-
// we need to fetch the last block written, and estimate the earliest timestamp for the next block.
|
|
64
|
-
// The timestamp of that last block will act as a lower bound for the next block.
|
|
65
|
-
|
|
66
|
-
const lastCheckpoint = await this.rollupContract.getPendingCheckpoint();
|
|
67
|
-
const earliestTimestamp = await this.rollupContract.getTimestampForSlot(
|
|
68
|
-
SlotNumber.fromBigInt(BigInt(lastCheckpoint.slotNumber) + 1n),
|
|
69
|
-
);
|
|
70
|
-
const nextEthTimestamp = getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), {
|
|
71
|
-
l1GenesisTime: this.l1GenesisTime,
|
|
72
|
-
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
73
|
-
});
|
|
74
|
-
const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp;
|
|
75
|
-
|
|
76
|
-
return new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public async getCurrentMinFees(): Promise<GasFees> {
|
|
80
|
-
// Get the current block number
|
|
81
|
-
const blockNumber = await this.publicClient.getBlockNumber();
|
|
82
|
-
|
|
83
|
-
// If the L1 block number has changed then chain a new promise to get the current min fees
|
|
84
|
-
if (this.currentL1BlockNumber === undefined || blockNumber > this.currentL1BlockNumber) {
|
|
85
|
-
this.currentL1BlockNumber = blockNumber;
|
|
86
|
-
this.currentMinFees = this.currentMinFees.then(() => this.computeCurrentMinFees());
|
|
87
|
-
}
|
|
88
|
-
return this.currentMinFees;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
55
|
/**
|
|
92
56
|
* Simple builder of global variables.
|
|
93
57
|
* @param blockNumber - The block number to build global variables for.
|
|
@@ -120,7 +84,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
120
84
|
coinbase: EthAddress,
|
|
121
85
|
feeRecipient: AztecAddress,
|
|
122
86
|
slotNumber: SlotNumber,
|
|
123
|
-
|
|
87
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
124
88
|
): Promise<CheckpointGlobalVariables> {
|
|
125
89
|
const { chainId, version } = this;
|
|
126
90
|
|
|
@@ -129,18 +93,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
129
93
|
l1GenesisTime: this.l1GenesisTime,
|
|
130
94
|
});
|
|
131
95
|
|
|
132
|
-
|
|
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);
|
|
96
|
+
const stateOverride = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
144
97
|
const gasFees = new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true, stateOverride));
|
|
145
98
|
|
|
146
99
|
return { chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees };
|
package/src/publisher/config.ts
CHANGED
|
@@ -120,7 +120,8 @@ export const proverTxSenderConfigMappings: ConfigMappingsType<Omit<ProverTxSende
|
|
|
120
120
|
proverPublisherPrivateKeys: {
|
|
121
121
|
env: `PROVER_PUBLISHER_PRIVATE_KEYS`,
|
|
122
122
|
description: 'The private keys to be used by the prover publisher.',
|
|
123
|
-
parseEnv: (val: string) =>
|
|
123
|
+
parseEnv: (val: string) =>
|
|
124
|
+
val.split(',').map(key => new SecretValue(`0x${key.replace('0x', '')}` as `0x${string}`)),
|
|
124
125
|
defaultValue: [],
|
|
125
126
|
fallback: [`PROVER_PUBLISHER_PRIVATE_KEY`],
|
|
126
127
|
},
|
|
@@ -137,7 +138,8 @@ export const sequencerTxSenderConfigMappings: ConfigMappingsType<Omit<SequencerT
|
|
|
137
138
|
sequencerPublisherPrivateKeys: {
|
|
138
139
|
env: `SEQ_PUBLISHER_PRIVATE_KEYS`,
|
|
139
140
|
description: 'The private keys to be used by the sequencer publisher.',
|
|
140
|
-
parseEnv: (val: string) =>
|
|
141
|
+
parseEnv: (val: string) =>
|
|
142
|
+
val.split(',').map(key => new SecretValue(`0x${key.replace('0x', '')}` as `0x${string}`)),
|
|
141
143
|
defaultValue: [],
|
|
142
144
|
fallback: [`SEQ_PUBLISHER_PRIVATE_KEY`],
|
|
143
145
|
},
|
|
@@ -166,7 +168,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
|
|
|
166
168
|
sequencerPublisherForwarderAddress: {
|
|
167
169
|
env: `SEQ_PUBLISHER_FORWARDER_ADDRESS`,
|
|
168
170
|
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
169
|
-
parseEnv: (val: string) =>
|
|
171
|
+
parseEnv: (val: string) => EthAddress.fromString(val),
|
|
170
172
|
},
|
|
171
173
|
l1TxFailedStore: {
|
|
172
174
|
env: 'L1_TX_FAILED_STORE',
|
|
@@ -192,7 +194,7 @@ export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherCo
|
|
|
192
194
|
proverPublisherForwarderAddress: {
|
|
193
195
|
env: `PROVER_PUBLISHER_FORWARDER_ADDRESS`,
|
|
194
196
|
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
195
|
-
parseEnv: (val: string) =>
|
|
197
|
+
parseEnv: (val: string) => EthAddress.fromString(val),
|
|
196
198
|
},
|
|
197
199
|
...publisherFundingConfigMappings,
|
|
198
200
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { Branded } from '@aztec/foundation/branded-types';
|
|
2
|
+
|
|
1
3
|
import type { Hex } from 'viem';
|
|
2
4
|
|
|
3
5
|
/** URI pointing to a stored failed L1 transaction. */
|
|
4
|
-
export type FailedL1TxUri = string
|
|
6
|
+
export type FailedL1TxUri = Branded<string, 'FailedL1TxUri'>;
|
|
5
7
|
|
|
6
8
|
/** A failed L1 transaction captured for debugging and replay. */
|
|
7
9
|
export type FailedL1Tx = {
|
|
@@ -4,15 +4,17 @@ import type { EpochCache } from '@aztec/epoch-cache';
|
|
|
4
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
5
5
|
import {
|
|
6
6
|
FeeAssetPriceOracle,
|
|
7
|
-
type FeeHeader,
|
|
8
7
|
type GovernanceProposerContract,
|
|
9
8
|
type IEmpireBase,
|
|
10
9
|
MULTI_CALL_3_ADDRESS,
|
|
11
10
|
Multicall3,
|
|
12
11
|
RollupContract,
|
|
12
|
+
SimulationOverridesBuilder,
|
|
13
|
+
type SimulationOverridesPlan,
|
|
13
14
|
type SlashingProposerContract,
|
|
14
15
|
type ViemCommitteeAttestations,
|
|
15
16
|
type ViemHeader,
|
|
17
|
+
buildSimulationOverridesStateOverride,
|
|
16
18
|
} from '@aztec/ethereum/contracts';
|
|
17
19
|
import { type L1FeeAnalysisResult, L1FeeAnalyzer } from '@aztec/ethereum/l1-fee-analysis';
|
|
18
20
|
import {
|
|
@@ -26,7 +28,6 @@ import {
|
|
|
26
28
|
} from '@aztec/ethereum/l1-tx-utils';
|
|
27
29
|
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
28
30
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
29
|
-
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
30
31
|
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
31
32
|
import { trimmedBytesLength } from '@aztec/foundation/buffer';
|
|
32
33
|
import { pick } from '@aztec/foundation/collection';
|
|
@@ -50,7 +51,6 @@ import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from
|
|
|
50
51
|
|
|
51
52
|
import {
|
|
52
53
|
type Hex,
|
|
53
|
-
type StateOverride,
|
|
54
54
|
type TransactionReceipt,
|
|
55
55
|
type TypedDataDefinition,
|
|
56
56
|
encodeFunctionData,
|
|
@@ -119,6 +119,11 @@ export type InvalidateCheckpointRequest = {
|
|
|
119
119
|
lastArchive: Fr;
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
+
type EnqueueProposeCheckpointOpts = {
|
|
123
|
+
txTimeoutAt?: Date;
|
|
124
|
+
simulationOverridesPlan?: SimulationOverridesPlan;
|
|
125
|
+
};
|
|
126
|
+
|
|
122
127
|
interface RequestWithExpiry {
|
|
123
128
|
action: Action;
|
|
124
129
|
request: L1TxRequest;
|
|
@@ -662,27 +667,21 @@ export class SequencerPublisher {
|
|
|
662
667
|
* @param tipArchive - The archive to check
|
|
663
668
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
664
669
|
*/
|
|
665
|
-
public canProposeAt(
|
|
666
|
-
tipArchive: Fr,
|
|
667
|
-
msgSender: EthAddress,
|
|
668
|
-
opts: {
|
|
669
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
670
|
-
forceArchive?: { checkpointNumber: CheckpointNumber; archive: Fr };
|
|
671
|
-
pipelined?: boolean;
|
|
672
|
-
} = {},
|
|
673
|
-
) {
|
|
670
|
+
public async canProposeAt(tipArchive: Fr, msgSender: EthAddress, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
674
671
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
675
672
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
676
673
|
|
|
677
|
-
const pipelined =
|
|
674
|
+
const pipelined = this.epochCache.isProposerPipeliningEnabled();
|
|
678
675
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
679
676
|
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
680
677
|
|
|
681
678
|
return this.rollupContract
|
|
682
|
-
.canProposeAt(
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
679
|
+
.canProposeAt(
|
|
680
|
+
tipArchive.toBuffer(),
|
|
681
|
+
msgSender.toString(),
|
|
682
|
+
nextL1SlotTs,
|
|
683
|
+
await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan),
|
|
684
|
+
)
|
|
686
685
|
.catch(err => {
|
|
687
686
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
688
687
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find(e => err.message.includes(e))}`, {
|
|
@@ -704,7 +703,7 @@ export class SequencerPublisher {
|
|
|
704
703
|
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
705
704
|
public async validateBlockHeader(
|
|
706
705
|
header: CheckpointHeader,
|
|
707
|
-
|
|
706
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
708
707
|
): Promise<void> {
|
|
709
708
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
710
709
|
|
|
@@ -719,9 +718,7 @@ export class SequencerPublisher {
|
|
|
719
718
|
] as const;
|
|
720
719
|
|
|
721
720
|
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
722
|
-
const stateOverrides = await this.rollupContract
|
|
723
|
-
opts?.forcePendingCheckpointNumber,
|
|
724
|
-
);
|
|
721
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
725
722
|
let balance = 0n;
|
|
726
723
|
if (this.config.fishermanMode) {
|
|
727
724
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -882,10 +879,7 @@ export class SequencerPublisher {
|
|
|
882
879
|
checkpoint: Checkpoint,
|
|
883
880
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
884
881
|
attestationsAndSignersSignature: Signature,
|
|
885
|
-
|
|
886
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
887
|
-
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
888
|
-
},
|
|
882
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
889
883
|
): Promise<void> {
|
|
890
884
|
const blobFields = checkpoint.toBlobFields();
|
|
891
885
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
@@ -905,7 +899,7 @@ export class SequencerPublisher {
|
|
|
905
899
|
blobInput,
|
|
906
900
|
] as const;
|
|
907
901
|
|
|
908
|
-
await this.simulateProposeTx(args,
|
|
902
|
+
await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
909
903
|
}
|
|
910
904
|
|
|
911
905
|
private async enqueueCastSignalHelper(
|
|
@@ -1154,11 +1148,7 @@ export class SequencerPublisher {
|
|
|
1154
1148
|
checkpoint: Checkpoint,
|
|
1155
1149
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
1156
1150
|
attestationsAndSignersSignature: Signature,
|
|
1157
|
-
opts: {
|
|
1158
|
-
txTimeoutAt?: Date;
|
|
1159
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1160
|
-
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1161
|
-
} = {},
|
|
1151
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1162
1152
|
): Promise<void> {
|
|
1163
1153
|
const checkpointHeader = checkpoint.header;
|
|
1164
1154
|
|
|
@@ -1174,6 +1164,10 @@ export class SequencerPublisher {
|
|
|
1174
1164
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1175
1165
|
};
|
|
1176
1166
|
|
|
1167
|
+
const simulationOverridesPlan = SimulationOverridesBuilder.from(opts.simulationOverridesPlan)
|
|
1168
|
+
.withoutBlobCheck()
|
|
1169
|
+
.build();
|
|
1170
|
+
|
|
1177
1171
|
try {
|
|
1178
1172
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1179
1173
|
// This means that we can avoid the simulation issues in later checks.
|
|
@@ -1184,13 +1178,13 @@ export class SequencerPublisher {
|
|
|
1184
1178
|
checkpoint,
|
|
1185
1179
|
attestationsAndSigners,
|
|
1186
1180
|
attestationsAndSignersSignature,
|
|
1187
|
-
|
|
1181
|
+
simulationOverridesPlan,
|
|
1188
1182
|
);
|
|
1189
1183
|
} catch (err: any) {
|
|
1190
1184
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1191
1185
|
...checkpoint.getStats(),
|
|
1192
1186
|
slotNumber: checkpoint.header.slotNumber,
|
|
1193
|
-
|
|
1187
|
+
simulationOverridesPlan,
|
|
1194
1188
|
});
|
|
1195
1189
|
throw err;
|
|
1196
1190
|
}
|
|
@@ -1205,16 +1199,22 @@ export class SequencerPublisher {
|
|
|
1205
1199
|
checkpoint,
|
|
1206
1200
|
attestationsAndSigners,
|
|
1207
1201
|
attestationsAndSignersSignature,
|
|
1208
|
-
|
|
1209
|
-
// Forcing pending checkpoint number is included its required if an invalidation request is included
|
|
1210
|
-
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
1211
|
-
},
|
|
1202
|
+
simulationOverridesPlan,
|
|
1212
1203
|
);
|
|
1213
1204
|
};
|
|
1214
1205
|
}
|
|
1215
1206
|
|
|
1216
|
-
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1217
|
-
|
|
1207
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1208
|
+
...checkpoint.toCheckpointInfo(),
|
|
1209
|
+
txTimeoutAt: opts.txTimeoutAt,
|
|
1210
|
+
simulationOverridesPlan,
|
|
1211
|
+
});
|
|
1212
|
+
await this.addProposeTx(
|
|
1213
|
+
checkpoint,
|
|
1214
|
+
proposeTxArgs,
|
|
1215
|
+
{ txTimeoutAt: opts.txTimeoutAt, simulationOverridesPlan },
|
|
1216
|
+
preCheck,
|
|
1217
|
+
);
|
|
1218
1218
|
}
|
|
1219
1219
|
|
|
1220
1220
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1344,10 +1344,7 @@ export class SequencerPublisher {
|
|
|
1344
1344
|
this.l1TxUtils.restart();
|
|
1345
1345
|
}
|
|
1346
1346
|
|
|
1347
|
-
private async prepareProposeTx(
|
|
1348
|
-
encodedData: L1ProcessArgs,
|
|
1349
|
-
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1350
|
-
) {
|
|
1347
|
+
private async prepareProposeTx(encodedData: L1ProcessArgs, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
1351
1348
|
const kzg = Blob.getViemKzgInstance();
|
|
1352
1349
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
1353
1350
|
this.log.debug('Validating blob input', { blobInput });
|
|
@@ -1418,7 +1415,7 @@ export class SequencerPublisher {
|
|
|
1418
1415
|
blobInput,
|
|
1419
1416
|
] as const;
|
|
1420
1417
|
|
|
1421
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1418
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
1422
1419
|
|
|
1423
1420
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1424
1421
|
}
|
|
@@ -1442,10 +1439,7 @@ export class SequencerPublisher {
|
|
|
1442
1439
|
ViemSignature,
|
|
1443
1440
|
`0x${string}`,
|
|
1444
1441
|
],
|
|
1445
|
-
|
|
1446
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1447
|
-
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1448
|
-
},
|
|
1442
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
1449
1443
|
) {
|
|
1450
1444
|
const rollupData = encodeFunctionData({
|
|
1451
1445
|
abi: RollupAbi,
|
|
@@ -1453,34 +1447,7 @@ export class SequencerPublisher {
|
|
|
1453
1447
|
args,
|
|
1454
1448
|
});
|
|
1455
1449
|
|
|
1456
|
-
|
|
1457
|
-
const forcePendingCheckpointNumberStateDiff = (
|
|
1458
|
-
options.forcePendingCheckpointNumber !== undefined
|
|
1459
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1460
|
-
: []
|
|
1461
|
-
).flatMap(override => override.stateDiff ?? []);
|
|
1462
|
-
|
|
1463
|
-
// override the fee header for a specific checkpoint number if requested (used when pipelining)
|
|
1464
|
-
const forceProposedFeeHeaderStateDiff = (
|
|
1465
|
-
options.forceProposedFeeHeader !== undefined
|
|
1466
|
-
? await this.rollupContract.makeFeeHeaderOverride(
|
|
1467
|
-
options.forceProposedFeeHeader.checkpointNumber,
|
|
1468
|
-
options.forceProposedFeeHeader.feeHeader,
|
|
1469
|
-
)
|
|
1470
|
-
: []
|
|
1471
|
-
).flatMap(override => override.stateDiff ?? []);
|
|
1472
|
-
|
|
1473
|
-
const stateOverrides: StateOverride = [
|
|
1474
|
-
{
|
|
1475
|
-
address: this.rollupContract.address,
|
|
1476
|
-
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1477
|
-
stateDiff: [
|
|
1478
|
-
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1479
|
-
...forcePendingCheckpointNumberStateDiff,
|
|
1480
|
-
...forceProposedFeeHeaderStateDiff,
|
|
1481
|
-
],
|
|
1482
|
-
},
|
|
1483
|
-
];
|
|
1450
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
1484
1451
|
// In fisherman mode, simulate as the proposer but with sufficient balance
|
|
1485
1452
|
if (this.proposerAddressForSimulation) {
|
|
1486
1453
|
stateOverrides.push({
|
|
@@ -1545,17 +1512,16 @@ export class SequencerPublisher {
|
|
|
1545
1512
|
private async addProposeTx(
|
|
1546
1513
|
checkpoint: Checkpoint,
|
|
1547
1514
|
encodedData: L1ProcessArgs,
|
|
1548
|
-
opts: {
|
|
1549
|
-
txTimeoutAt?: Date;
|
|
1550
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1551
|
-
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1552
|
-
} = {},
|
|
1515
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1553
1516
|
preCheck?: () => Promise<void>,
|
|
1554
1517
|
): Promise<void> {
|
|
1555
1518
|
const slot = checkpoint.header.slotNumber;
|
|
1556
1519
|
const timer = new Timer();
|
|
1557
1520
|
const kzg = Blob.getViemKzgInstance();
|
|
1558
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1521
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1522
|
+
encodedData,
|
|
1523
|
+
opts.simulationOverridesPlan,
|
|
1524
|
+
);
|
|
1559
1525
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1560
1526
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
1561
1527
|
BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
|
|
@@ -1578,7 +1544,7 @@ export class SequencerPublisher {
|
|
|
1578
1544
|
data: rollupData,
|
|
1579
1545
|
},
|
|
1580
1546
|
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1581
|
-
gasConfig: {
|
|
1547
|
+
gasConfig: { txTimeoutAt: opts.txTimeoutAt, gasLimit },
|
|
1582
1548
|
preCheck,
|
|
1583
1549
|
blobConfig: {
|
|
1584
1550
|
blobs: encodedData.blobs.map(b => b.data),
|
package/src/sequencer/README.md
CHANGED
|
@@ -88,7 +88,8 @@ checkpointFinalizationTime = propagationTime
|
|
|
88
88
|
timeReservedAtEnd (normal mode) = blockDuration (last sub-slot for reexecution)
|
|
89
89
|
+ checkpointFinalizationTime
|
|
90
90
|
|
|
91
|
-
timeReservedAtEnd (pipelining) =
|
|
91
|
+
timeReservedAtEnd (pipelining) = assembleTime
|
|
92
|
+
+ propagationTime (proposal must reach validators before the slot flips)
|
|
92
93
|
|
|
93
94
|
timeAvailableForBlocks = slotDuration - initializationOffset - timeReservedAtEnd
|
|
94
95
|
|
|
@@ -109,9 +110,9 @@ This means:
|
|
|
109
110
|
|
|
110
111
|
**The same slot with proposer pipelining enabled:**
|
|
111
112
|
```
|
|
112
|
-
timeReservedAtEnd =
|
|
113
|
-
timeAvailableForBlocks = 72s - 2s -
|
|
114
|
-
numberOfBlocks = floor(
|
|
113
|
+
timeReservedAtEnd = 1s + 2s = 3s
|
|
114
|
+
timeAvailableForBlocks = 72s - 2s - 3s = 67s
|
|
115
|
+
numberOfBlocks = floor(67s / 8s) = 8 blocks
|
|
115
116
|
```
|
|
116
117
|
|
|
117
118
|
The extra two block opportunities come from not charging the current slot for checkpoint finalization and L1 publishing.
|
|
@@ -128,8 +129,8 @@ It helps to think in terms of two different slots:
|
|
|
128
129
|
So the work is split like this:
|
|
129
130
|
|
|
130
131
|
- **During slot N-1**: Initialization, block building, and last-block re-execution
|
|
131
|
-
- **Near the end of slot N-1**: The checkpoint proposal is broadcast
|
|
132
|
-
- **During slot N**:
|
|
132
|
+
- **Near the end of slot N-1**: The checkpoint proposal is broadcast so validators can start the last re-execution as slot `N` begins.
|
|
133
|
+
- **During slot N**: Validators finish re-executing, send attestations, the proposer collects them, and the checkpoint is submitted to L1 before slot `N` reaches its publish cutoff
|
|
133
134
|
|
|
134
135
|
In other words, pipelining does not mean "do everything for slot N earlier". It specifically moves **block production and block re-execution** earlier, while **checkpoint proposal, attestation gathering, and L1 submission** remain aligned with slot `N`.
|
|
135
136
|
|
|
@@ -142,7 +143,7 @@ Slot 11 (wall clock):
|
|
|
142
143
|
- Collect checkpoint 12 attestations
|
|
143
144
|
|
|
144
145
|
Slot 12 (target/submission slot):
|
|
145
|
-
- Collect
|
|
146
|
+
- Collect attestations for checkpoint 12 until slot 12 reaches its L1 publish cutoff
|
|
146
147
|
- Submit checkpoint 12 to L1
|
|
147
148
|
```
|
|
148
149
|
|
|
@@ -534,7 +535,7 @@ The sequencer transitions through these states during a slot:
|
|
|
534
535
|
| **WAITING_UNTIL_NEXT_BLOCK** | Until next sub-slot start | Sleep between blocks to maintain intervals |
|
|
535
536
|
| **ASSEMBLING_CHECKPOINT** | assembleTime (1s) | Assemble final checkpoint |
|
|
536
537
|
| **COLLECTING_ATTESTATIONS** | Until L1 publish deadline | Wait for validator signatures |
|
|
537
|
-
| **PUBLISHING_CHECKPOINT** | Until
|
|
538
|
+
| **PUBLISHING_CHECKPOINT** | Until L1 publish deadline | Submit to L1 |
|
|
538
539
|
|
|
539
540
|
## Complete Example: 72-Second Slot with 8-Second Sub-Slots
|
|
540
541
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { RollupContract, SimulationOverridesBuilder, type SimulationOverridesPlan } from '@aztec/ethereum/contracts';
|
|
2
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
5
|
+
import type { ProposedCheckpointData } from '@aztec/stdlib/checkpoint';
|
|
6
|
+
|
|
7
|
+
type PipelinedParentSimulationOverridesPlanInput = {
|
|
8
|
+
checkpointNumber: CheckpointNumber;
|
|
9
|
+
proposedCheckpointData?: ProposedCheckpointData;
|
|
10
|
+
rollup: RollupContract;
|
|
11
|
+
log: Logger;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type SubmissionSimulationOverridesPlanInput = {
|
|
15
|
+
pipelinedParentPlan?: SimulationOverridesPlan;
|
|
16
|
+
invalidateToPendingCheckpointNumber?: CheckpointNumber;
|
|
17
|
+
lastArchiveRoot: Fr;
|
|
18
|
+
pipeliningEnabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Builds the simulated parent checkpoint view used while constructing a pipelined proposal. */
|
|
22
|
+
export async function buildPipelinedParentSimulationOverridesPlan(
|
|
23
|
+
input: PipelinedParentSimulationOverridesPlanInput,
|
|
24
|
+
): Promise<SimulationOverridesPlan | undefined> {
|
|
25
|
+
const parentCheckpointNumber = CheckpointNumber(input.checkpointNumber - 1);
|
|
26
|
+
const builder = new SimulationOverridesBuilder().forPendingCheckpoint(parentCheckpointNumber);
|
|
27
|
+
|
|
28
|
+
const pendingFeeHeader = await computePipelinedParentFeeHeader(input);
|
|
29
|
+
if (pendingFeeHeader) {
|
|
30
|
+
builder.withPendingFeeHeader(pendingFeeHeader);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return builder.build();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Builds the simulated chain view used when validating and enqueueing checkpoint submission. */
|
|
37
|
+
export function buildSubmissionSimulationOverridesPlan(
|
|
38
|
+
input: SubmissionSimulationOverridesPlanInput,
|
|
39
|
+
): SimulationOverridesPlan | undefined {
|
|
40
|
+
const pendingCheckpointNumber =
|
|
41
|
+
input.invalidateToPendingCheckpointNumber ?? input.pipelinedParentPlan?.pendingCheckpointNumber;
|
|
42
|
+
|
|
43
|
+
const builder = SimulationOverridesBuilder.from(input.pipelinedParentPlan).forPendingCheckpoint(
|
|
44
|
+
pendingCheckpointNumber,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (input.pipeliningEnabled && pendingCheckpointNumber !== undefined) {
|
|
48
|
+
builder.withPendingArchive(input.lastArchiveRoot);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return builder.build();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Derives the pending parent fee header used during pipelined proposal simulation. */
|
|
55
|
+
export async function computePipelinedParentFeeHeader(input: PipelinedParentSimulationOverridesPlanInput) {
|
|
56
|
+
if (!input.proposedCheckpointData || input.checkpointNumber < 2) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const grandparentCheckpointNumber = CheckpointNumber(input.checkpointNumber - 2);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const [grandparentCheckpoint, manaTarget] = await Promise.all([
|
|
64
|
+
input.rollup.getCheckpoint(grandparentCheckpointNumber),
|
|
65
|
+
input.rollup.getManaTarget(),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
if (!grandparentCheckpoint?.feeHeader) {
|
|
69
|
+
input.log.error(
|
|
70
|
+
`Grandparent checkpoint or feeHeader missing for checkpoint ${grandparentCheckpointNumber.toString()}`,
|
|
71
|
+
);
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return RollupContract.computeChildFeeHeader(
|
|
76
|
+
grandparentCheckpoint.feeHeader,
|
|
77
|
+
input.proposedCheckpointData.totalManaUsed,
|
|
78
|
+
input.proposedCheckpointData.feeAssetPriceModifier,
|
|
79
|
+
manaTarget,
|
|
80
|
+
);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
input.log.error(
|
|
83
|
+
`Failed to derive pipelined parent fee header for checkpoint ${grandparentCheckpointNumber.toString()}: ${err}`,
|
|
84
|
+
);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|