@aztec/sequencer-client 0.0.1-commit.35158ae7e → 0.0.1-commit.3750d92a7
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 -4
- 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 +3 -8
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +4 -39
- 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-factory.d.ts +1 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +0 -1
- package/dest/publisher/sequencer-publisher.d.ts +47 -44
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +112 -115
- 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 +21 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +200 -134
- 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/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/metrics.d.ts +5 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +12 -20
- package/dest/sequencer/sequencer.d.ts +17 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +73 -20
- package/dest/sequencer/timetable.d.ts +14 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +45 -36
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +5 -7
- 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 +8 -43
- 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-factory.ts +0 -3
- package/src/publisher/sequencer-publisher.ts +174 -163
- package/src/sequencer/README.md +83 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +242 -154
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/metrics.ts +15 -25
- package/src/sequencer/sequencer.ts +110 -22
- package/src/sequencer/timetable.ts +57 -45
|
@@ -3,16 +3,18 @@ import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/
|
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
5
5
|
import {
|
|
6
|
-
type EmpireSlashingProposerContract,
|
|
7
6
|
FeeAssetPriceOracle,
|
|
8
7
|
type GovernanceProposerContract,
|
|
9
8
|
type IEmpireBase,
|
|
10
9
|
MULTI_CALL_3_ADDRESS,
|
|
11
10
|
Multicall3,
|
|
12
11
|
RollupContract,
|
|
13
|
-
|
|
12
|
+
SimulationOverridesBuilder,
|
|
13
|
+
type SimulationOverridesPlan,
|
|
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';
|
|
@@ -36,21 +37,20 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
36
37
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
37
38
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
38
39
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
40
|
+
import { InterruptibleSleep } from '@aztec/foundation/sleep';
|
|
39
41
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
40
|
-
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
42
|
+
import { type DateProvider, Timer } from '@aztec/foundation/timer';
|
|
41
43
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
42
44
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
43
45
|
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
44
46
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
45
|
-
import { getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
46
|
-
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
47
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
47
48
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
48
49
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
49
50
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
50
51
|
|
|
51
52
|
import {
|
|
52
53
|
type Hex,
|
|
53
|
-
type StateOverride,
|
|
54
54
|
type TransactionReceipt,
|
|
55
55
|
type TypedDataDefinition,
|
|
56
56
|
encodeFunctionData,
|
|
@@ -63,6 +63,20 @@ import type { SequencerPublisherConfig } from './config.js';
|
|
|
63
63
|
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
64
64
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
65
65
|
|
|
66
|
+
/** Result of a sendRequests call, returned by both sendRequests() and sendRequestsAt(). */
|
|
67
|
+
export type SendRequestsResult = {
|
|
68
|
+
/** The L1 transaction receipt or error from the bundled multicall. */
|
|
69
|
+
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError;
|
|
70
|
+
/** Actions that expired (past their deadline) before the request was sent. */
|
|
71
|
+
expiredActions: Action[];
|
|
72
|
+
/** Actions that were included in the sent L1 transaction. */
|
|
73
|
+
sentActions: Action[];
|
|
74
|
+
/** Actions whose L1 simulation succeeded (subset of sentActions). */
|
|
75
|
+
successfulActions: Action[];
|
|
76
|
+
/** Actions whose L1 simulation failed (subset of sentActions). */
|
|
77
|
+
failedActions: Action[];
|
|
78
|
+
};
|
|
79
|
+
|
|
66
80
|
/** Arguments to the process method of the rollup contract */
|
|
67
81
|
type L1ProcessArgs = {
|
|
68
82
|
/** The L2 block header. */
|
|
@@ -84,16 +98,13 @@ export const Actions = [
|
|
|
84
98
|
'invalidate-by-insufficient-attestations',
|
|
85
99
|
'propose',
|
|
86
100
|
'governance-signal',
|
|
87
|
-
'empire-slashing-signal',
|
|
88
|
-
'create-empire-payload',
|
|
89
|
-
'execute-empire-payload',
|
|
90
101
|
'vote-offenses',
|
|
91
102
|
'execute-slash',
|
|
92
103
|
] as const;
|
|
93
104
|
|
|
94
105
|
export type Action = (typeof Actions)[number];
|
|
95
106
|
|
|
96
|
-
type GovernanceSignalAction = Extract<Action, 'governance-signal'
|
|
107
|
+
type GovernanceSignalAction = Extract<Action, 'governance-signal'>;
|
|
97
108
|
|
|
98
109
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
99
110
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
@@ -104,6 +115,13 @@ export type InvalidateCheckpointRequest = {
|
|
|
104
115
|
gasUsed: bigint;
|
|
105
116
|
checkpointNumber: CheckpointNumber;
|
|
106
117
|
forcePendingCheckpointNumber: CheckpointNumber;
|
|
118
|
+
/** Archive at the rollback target checkpoint (checkpoint N-1). */
|
|
119
|
+
lastArchive: Fr;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type EnqueueProposeCheckpointOpts = {
|
|
123
|
+
txTimeoutAt?: Date;
|
|
124
|
+
simulationOverridesPlan?: SimulationOverridesPlan;
|
|
107
125
|
};
|
|
108
126
|
|
|
109
127
|
interface RequestWithExpiry {
|
|
@@ -112,6 +130,8 @@ interface RequestWithExpiry {
|
|
|
112
130
|
lastValidL2Slot: SlotNumber;
|
|
113
131
|
gasConfig?: Pick<L1TxConfig, 'txTimeoutAt' | 'gasLimit'>;
|
|
114
132
|
blobConfig?: L1BlobInputs;
|
|
133
|
+
/** Optional pre-send validation. If it rejects, the request is discarded. */
|
|
134
|
+
preCheck?: () => Promise<void>;
|
|
115
135
|
checkSuccess: (
|
|
116
136
|
request: L1TxRequest,
|
|
117
137
|
result?: { receipt: TransactionReceipt; stats?: TransactionStats; errorMsg?: string },
|
|
@@ -135,7 +155,9 @@ export class SequencerPublisher {
|
|
|
135
155
|
protected log: Logger;
|
|
136
156
|
protected ethereumSlotDuration: bigint;
|
|
137
157
|
protected aztecSlotDuration: bigint;
|
|
138
|
-
|
|
158
|
+
|
|
159
|
+
/** Date provider for wall-clock time. */
|
|
160
|
+
private readonly dateProvider: DateProvider;
|
|
139
161
|
|
|
140
162
|
private blobClient: BlobClientInterface;
|
|
141
163
|
|
|
@@ -151,6 +173,9 @@ export class SequencerPublisher {
|
|
|
151
173
|
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */
|
|
152
174
|
private feeAssetPriceOracle: FeeAssetPriceOracle;
|
|
153
175
|
|
|
176
|
+
/** Interruptible sleep used by sendRequestsAt to wait until a target timestamp. */
|
|
177
|
+
private readonly interruptibleSleep = new InterruptibleSleep();
|
|
178
|
+
|
|
154
179
|
// A CALL to a cold address is 2700 gas
|
|
155
180
|
public static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
156
181
|
|
|
@@ -160,8 +185,7 @@ export class SequencerPublisher {
|
|
|
160
185
|
public l1TxUtils: L1TxUtils;
|
|
161
186
|
public rollupContract: RollupContract;
|
|
162
187
|
public govProposerContract: GovernanceProposerContract;
|
|
163
|
-
public slashingProposerContract:
|
|
164
|
-
public slashFactoryContract: SlashFactoryContract;
|
|
188
|
+
public slashingProposerContract: SlashingProposerContract | undefined;
|
|
165
189
|
|
|
166
190
|
public readonly tracer: Tracer;
|
|
167
191
|
|
|
@@ -175,9 +199,8 @@ export class SequencerPublisher {
|
|
|
175
199
|
blobClient: BlobClientInterface;
|
|
176
200
|
l1TxUtils: L1TxUtils;
|
|
177
201
|
rollupContract: RollupContract;
|
|
178
|
-
slashingProposerContract:
|
|
202
|
+
slashingProposerContract: SlashingProposerContract | undefined;
|
|
179
203
|
governanceProposerContract: GovernanceProposerContract;
|
|
180
|
-
slashFactoryContract: SlashFactoryContract;
|
|
181
204
|
epochCache: EpochCache;
|
|
182
205
|
dateProvider: DateProvider;
|
|
183
206
|
metrics: SequencerPublisherMetrics;
|
|
@@ -194,6 +217,7 @@ export class SequencerPublisher {
|
|
|
194
217
|
this.lastActions = deps.lastActions;
|
|
195
218
|
|
|
196
219
|
this.blobClient = deps.blobClient;
|
|
220
|
+
this.dateProvider = deps.dateProvider;
|
|
197
221
|
|
|
198
222
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
199
223
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
@@ -211,8 +235,6 @@ export class SequencerPublisher {
|
|
|
211
235
|
const newSlashingProposer = await this.rollupContract.getSlashingProposer();
|
|
212
236
|
this.slashingProposerContract = newSlashingProposer;
|
|
213
237
|
});
|
|
214
|
-
this.slashFactoryContract = deps.slashFactoryContract;
|
|
215
|
-
|
|
216
238
|
// Initialize L1 fee analyzer for fisherman mode
|
|
217
239
|
if (config.fishermanMode) {
|
|
218
240
|
this.l1FeeAnalyzer = new L1FeeAnalyzer(
|
|
@@ -369,9 +391,10 @@ export class SequencerPublisher {
|
|
|
369
391
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
370
392
|
*/
|
|
371
393
|
@trackSpan('SequencerPublisher.sendRequests')
|
|
372
|
-
public async sendRequests() {
|
|
394
|
+
public async sendRequests(): Promise<SendRequestsResult | undefined> {
|
|
373
395
|
const requestsToProcess = [...this.requests];
|
|
374
396
|
this.requests = [];
|
|
397
|
+
|
|
375
398
|
if (this.interrupted || requestsToProcess.length === 0) {
|
|
376
399
|
return undefined;
|
|
377
400
|
}
|
|
@@ -530,6 +553,45 @@ export class SequencerPublisher {
|
|
|
530
553
|
}
|
|
531
554
|
}
|
|
532
555
|
|
|
556
|
+
/*
|
|
557
|
+
* Schedules sending all enqueued requests at (or after) the given timestamp.
|
|
558
|
+
* Uses InterruptibleSleep so it can be cancelled via interrupt().
|
|
559
|
+
* Returns the promise for the L1 response (caller should NOT await this in the work loop).
|
|
560
|
+
*/
|
|
561
|
+
public async sendRequestsAt(submitAfter: Date): Promise<SendRequestsResult | undefined> {
|
|
562
|
+
const ms = submitAfter.getTime() - this.dateProvider.now();
|
|
563
|
+
if (ms > 0) {
|
|
564
|
+
this.log.debug(`Sleeping ${ms}ms before sending requests`, { submitAfter });
|
|
565
|
+
await this.interruptibleSleep.sleep(ms);
|
|
566
|
+
}
|
|
567
|
+
if (this.interrupted) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Re-validate enqueued requests after the sleep (state may have changed, e.g. prune or L1 reorg)
|
|
572
|
+
const validRequests: RequestWithExpiry[] = [];
|
|
573
|
+
for (const request of this.requests) {
|
|
574
|
+
if (!request.preCheck) {
|
|
575
|
+
validRequests.push(request);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
await request.preCheck();
|
|
581
|
+
validRequests.push(request);
|
|
582
|
+
} catch (err) {
|
|
583
|
+
this.log.warn(`Pre-send validation failed for ${request.action}, discarding request`, err);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
this.requests = validRequests;
|
|
588
|
+
if (this.requests.length === 0) {
|
|
589
|
+
return undefined;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return this.sendRequests();
|
|
593
|
+
}
|
|
594
|
+
|
|
533
595
|
private callbackBundledTransactions(
|
|
534
596
|
requests: RequestWithExpiry[],
|
|
535
597
|
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError | undefined,
|
|
@@ -605,22 +667,21 @@ export class SequencerPublisher {
|
|
|
605
667
|
* @param tipArchive - The archive to check
|
|
606
668
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
607
669
|
*/
|
|
608
|
-
public canProposeAt(
|
|
609
|
-
tipArchive: Fr,
|
|
610
|
-
msgSender: EthAddress,
|
|
611
|
-
opts: { forcePendingCheckpointNumber?: CheckpointNumber; pipelined?: boolean } = {},
|
|
612
|
-
) {
|
|
670
|
+
public async canProposeAt(tipArchive: Fr, msgSender: EthAddress, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
613
671
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
614
672
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
615
673
|
|
|
616
|
-
const pipelined =
|
|
674
|
+
const pipelined = this.epochCache.isProposerPipeliningEnabled();
|
|
617
675
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
618
676
|
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
619
677
|
|
|
620
678
|
return this.rollupContract
|
|
621
|
-
.canProposeAt(
|
|
622
|
-
|
|
623
|
-
|
|
679
|
+
.canProposeAt(
|
|
680
|
+
tipArchive.toBuffer(),
|
|
681
|
+
msgSender.toString(),
|
|
682
|
+
nextL1SlotTs,
|
|
683
|
+
await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan),
|
|
684
|
+
)
|
|
624
685
|
.catch(err => {
|
|
625
686
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
626
687
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find(e => err.message.includes(e))}`, {
|
|
@@ -642,7 +703,7 @@ export class SequencerPublisher {
|
|
|
642
703
|
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
643
704
|
public async validateBlockHeader(
|
|
644
705
|
header: CheckpointHeader,
|
|
645
|
-
|
|
706
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
646
707
|
): Promise<void> {
|
|
647
708
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
648
709
|
|
|
@@ -656,10 +717,8 @@ export class SequencerPublisher {
|
|
|
656
717
|
flags,
|
|
657
718
|
] as const;
|
|
658
719
|
|
|
659
|
-
const ts = this.
|
|
660
|
-
const stateOverrides = await this.rollupContract
|
|
661
|
-
opts?.forcePendingCheckpointNumber,
|
|
662
|
-
);
|
|
720
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
721
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
663
722
|
let balance = 0n;
|
|
664
723
|
if (this.config.fishermanMode) {
|
|
665
724
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -679,7 +738,7 @@ export class SequencerPublisher {
|
|
|
679
738
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
680
739
|
from: MULTI_CALL_3_ADDRESS,
|
|
681
740
|
},
|
|
682
|
-
{ time: ts
|
|
741
|
+
{ time: ts },
|
|
683
742
|
stateOverrides,
|
|
684
743
|
);
|
|
685
744
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -732,6 +791,7 @@ export class SequencerPublisher {
|
|
|
732
791
|
gasUsed,
|
|
733
792
|
checkpointNumber,
|
|
734
793
|
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
794
|
+
lastArchive: validationResult.checkpoint.lastArchive,
|
|
735
795
|
reason,
|
|
736
796
|
};
|
|
737
797
|
} catch (err) {
|
|
@@ -744,8 +804,8 @@ export class SequencerPublisher {
|
|
|
744
804
|
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
745
805
|
{ ...logData, request, error: viemError.message },
|
|
746
806
|
);
|
|
747
|
-
const
|
|
748
|
-
if (
|
|
807
|
+
const latestProposedCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
808
|
+
if (latestProposedCheckpointNumber < checkpointNumber) {
|
|
749
809
|
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
750
810
|
return undefined;
|
|
751
811
|
} else {
|
|
@@ -819,16 +879,8 @@ export class SequencerPublisher {
|
|
|
819
879
|
checkpoint: Checkpoint,
|
|
820
880
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
821
881
|
attestationsAndSignersSignature: Signature,
|
|
822
|
-
|
|
823
|
-
): Promise<
|
|
824
|
-
// When pipelining, the checkpoint targets the next slot so its timestamp is in the future.
|
|
825
|
-
// Without pipelining, the checkpoint targets the current slot so its timestamp is in the past
|
|
826
|
-
// by the time we simulate (~24s of build time), causing eth_simulateV1 to reject it.
|
|
827
|
-
// In that case, use the latest L1 block timestamp + one ethereum slot, which is just ahead
|
|
828
|
-
// of L1 and still within the same L2 slot.
|
|
829
|
-
const ts = this.epochCache.isProposerPipeliningEnabled()
|
|
830
|
-
? checkpoint.header.timestamp
|
|
831
|
-
: (await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration;
|
|
882
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
883
|
+
): Promise<void> {
|
|
832
884
|
const blobFields = checkpoint.toBlobFields();
|
|
833
885
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
834
886
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -847,13 +899,11 @@ export class SequencerPublisher {
|
|
|
847
899
|
blobInput,
|
|
848
900
|
] as const;
|
|
849
901
|
|
|
850
|
-
await this.simulateProposeTx(args,
|
|
851
|
-
return ts;
|
|
902
|
+
await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
852
903
|
}
|
|
853
904
|
|
|
854
905
|
private async enqueueCastSignalHelper(
|
|
855
906
|
slotNumber: SlotNumber,
|
|
856
|
-
timestamp: bigint,
|
|
857
907
|
signalType: GovernanceSignalAction,
|
|
858
908
|
payload: EthAddress,
|
|
859
909
|
base: IEmpireBase,
|
|
@@ -932,13 +982,17 @@ export class SequencerPublisher {
|
|
|
932
982
|
});
|
|
933
983
|
|
|
934
984
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
985
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
935
986
|
|
|
936
987
|
try {
|
|
937
988
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
938
989
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
939
990
|
} catch (err) {
|
|
940
991
|
const viemError = formatViemError(err);
|
|
941
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
992
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
993
|
+
simulationTimestamp: timestamp,
|
|
994
|
+
l1BlockNumber,
|
|
995
|
+
});
|
|
942
996
|
this.backupFailedTx({
|
|
943
997
|
id: keccak256(request.data!),
|
|
944
998
|
failureType: 'simulation',
|
|
@@ -1001,19 +1055,16 @@ export class SequencerPublisher {
|
|
|
1001
1055
|
/**
|
|
1002
1056
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
1003
1057
|
* @param slotNumber - The slot number to cast a signal for.
|
|
1004
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
1005
1058
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
1006
1059
|
*/
|
|
1007
1060
|
public enqueueGovernanceCastSignal(
|
|
1008
1061
|
governancePayload: EthAddress,
|
|
1009
1062
|
slotNumber: SlotNumber,
|
|
1010
|
-
timestamp: bigint,
|
|
1011
1063
|
signerAddress: EthAddress,
|
|
1012
1064
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1013
1065
|
): Promise<boolean> {
|
|
1014
1066
|
return this.enqueueCastSignalHelper(
|
|
1015
1067
|
slotNumber,
|
|
1016
|
-
timestamp,
|
|
1017
1068
|
'governance-signal',
|
|
1018
1069
|
governancePayload,
|
|
1019
1070
|
this.govProposerContract,
|
|
@@ -1026,7 +1077,6 @@ export class SequencerPublisher {
|
|
|
1026
1077
|
public async enqueueSlashingActions(
|
|
1027
1078
|
actions: ProposerSlashAction[],
|
|
1028
1079
|
slotNumber: SlotNumber,
|
|
1029
|
-
timestamp: bigint,
|
|
1030
1080
|
signerAddress: EthAddress,
|
|
1031
1081
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1032
1082
|
): Promise<boolean> {
|
|
@@ -1037,58 +1087,6 @@ export class SequencerPublisher {
|
|
|
1037
1087
|
|
|
1038
1088
|
for (const action of actions) {
|
|
1039
1089
|
switch (action.type) {
|
|
1040
|
-
case 'vote-empire-payload': {
|
|
1041
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1042
|
-
this.log.error('Cannot vote for empire payload on non-empire slashing contract');
|
|
1043
|
-
break;
|
|
1044
|
-
}
|
|
1045
|
-
this.log.debug(`Enqueuing slashing vote for payload ${action.payload} at slot ${slotNumber}`, {
|
|
1046
|
-
signerAddress,
|
|
1047
|
-
});
|
|
1048
|
-
await this.enqueueCastSignalHelper(
|
|
1049
|
-
slotNumber,
|
|
1050
|
-
timestamp,
|
|
1051
|
-
'empire-slashing-signal',
|
|
1052
|
-
action.payload,
|
|
1053
|
-
this.slashingProposerContract,
|
|
1054
|
-
signerAddress,
|
|
1055
|
-
signer,
|
|
1056
|
-
);
|
|
1057
|
-
break;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
case 'create-empire-payload': {
|
|
1061
|
-
this.log.debug(`Enqueuing slashing create payload at slot ${slotNumber}`, { slotNumber, signerAddress });
|
|
1062
|
-
const request = this.slashFactoryContract.buildCreatePayloadRequest(action.data);
|
|
1063
|
-
await this.simulateAndEnqueueRequest(
|
|
1064
|
-
'create-empire-payload',
|
|
1065
|
-
request,
|
|
1066
|
-
(receipt: TransactionReceipt) =>
|
|
1067
|
-
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1068
|
-
slotNumber,
|
|
1069
|
-
timestamp,
|
|
1070
|
-
);
|
|
1071
|
-
break;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
case 'execute-empire-payload': {
|
|
1075
|
-
this.log.debug(`Enqueuing slashing execute payload at slot ${slotNumber}`, { slotNumber, signerAddress });
|
|
1076
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1077
|
-
this.log.error('Cannot execute slashing payload on non-empire slashing contract');
|
|
1078
|
-
return false;
|
|
1079
|
-
}
|
|
1080
|
-
const empireSlashingProposer = this.slashingProposerContract as EmpireSlashingProposerContract;
|
|
1081
|
-
const request = empireSlashingProposer.buildExecuteRoundRequest(action.round);
|
|
1082
|
-
await this.simulateAndEnqueueRequest(
|
|
1083
|
-
'execute-empire-payload',
|
|
1084
|
-
request,
|
|
1085
|
-
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1086
|
-
slotNumber,
|
|
1087
|
-
timestamp,
|
|
1088
|
-
);
|
|
1089
|
-
break;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
1090
|
case 'vote-offenses': {
|
|
1093
1091
|
this.log.debug(`Enqueuing slashing vote for ${action.votes.length} votes at slot ${slotNumber}`, {
|
|
1094
1092
|
slotNumber,
|
|
@@ -1096,19 +1094,17 @@ export class SequencerPublisher {
|
|
|
1096
1094
|
votesCount: action.votes.length,
|
|
1097
1095
|
signerAddress,
|
|
1098
1096
|
});
|
|
1099
|
-
if (this.slashingProposerContract
|
|
1100
|
-
this.log.error('
|
|
1097
|
+
if (!this.slashingProposerContract) {
|
|
1098
|
+
this.log.error('No slashing proposer contract available');
|
|
1101
1099
|
return false;
|
|
1102
1100
|
}
|
|
1103
|
-
const tallySlashingProposer = this.slashingProposerContract as TallySlashingProposerContract;
|
|
1104
1101
|
const votes = bufferToHex(encodeSlashConsensusVotes(action.votes));
|
|
1105
|
-
const request = await
|
|
1102
|
+
const request = await this.slashingProposerContract.buildVoteRequestFromSigner(votes, slotNumber, signer);
|
|
1106
1103
|
await this.simulateAndEnqueueRequest(
|
|
1107
1104
|
'vote-offenses',
|
|
1108
1105
|
request,
|
|
1109
|
-
(receipt: TransactionReceipt) => !!
|
|
1106
|
+
(receipt: TransactionReceipt) => !!this.slashingProposerContract!.tryExtractVoteCastEvent(receipt.logs),
|
|
1110
1107
|
slotNumber,
|
|
1111
|
-
timestamp,
|
|
1112
1108
|
);
|
|
1113
1109
|
break;
|
|
1114
1110
|
}
|
|
@@ -1119,18 +1115,20 @@ export class SequencerPublisher {
|
|
|
1119
1115
|
round: action.round,
|
|
1120
1116
|
signerAddress,
|
|
1121
1117
|
});
|
|
1122
|
-
if (this.slashingProposerContract
|
|
1123
|
-
this.log.error('
|
|
1118
|
+
if (!this.slashingProposerContract) {
|
|
1119
|
+
this.log.error('No slashing proposer contract available');
|
|
1124
1120
|
return false;
|
|
1125
1121
|
}
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1122
|
+
const executeRequest = this.slashingProposerContract.buildExecuteRoundRequest(
|
|
1123
|
+
action.round,
|
|
1124
|
+
action.committees,
|
|
1125
|
+
);
|
|
1128
1126
|
await this.simulateAndEnqueueRequest(
|
|
1129
1127
|
'execute-slash',
|
|
1130
|
-
|
|
1131
|
-
(receipt: TransactionReceipt) =>
|
|
1128
|
+
executeRequest,
|
|
1129
|
+
(receipt: TransactionReceipt) =>
|
|
1130
|
+
!!this.slashingProposerContract!.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1132
1131
|
slotNumber,
|
|
1133
|
-
timestamp,
|
|
1134
1132
|
);
|
|
1135
1133
|
break;
|
|
1136
1134
|
}
|
|
@@ -1150,7 +1148,7 @@ export class SequencerPublisher {
|
|
|
1150
1148
|
checkpoint: Checkpoint,
|
|
1151
1149
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
1152
1150
|
attestationsAndSignersSignature: Signature,
|
|
1153
|
-
opts:
|
|
1151
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1154
1152
|
): Promise<void> {
|
|
1155
1153
|
const checkpointHeader = checkpoint.header;
|
|
1156
1154
|
|
|
@@ -1166,7 +1164,9 @@ export class SequencerPublisher {
|
|
|
1166
1164
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1167
1165
|
};
|
|
1168
1166
|
|
|
1169
|
-
|
|
1167
|
+
const simulationOverridesPlan = SimulationOverridesBuilder.from(opts.simulationOverridesPlan)
|
|
1168
|
+
.withoutBlobCheck()
|
|
1169
|
+
.build();
|
|
1170
1170
|
|
|
1171
1171
|
try {
|
|
1172
1172
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
@@ -1174,23 +1174,47 @@ export class SequencerPublisher {
|
|
|
1174
1174
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1175
1175
|
// make time consistency checks break.
|
|
1176
1176
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1177
|
-
|
|
1177
|
+
await this.validateCheckpointForSubmission(
|
|
1178
1178
|
checkpoint,
|
|
1179
1179
|
attestationsAndSigners,
|
|
1180
1180
|
attestationsAndSignersSignature,
|
|
1181
|
-
|
|
1181
|
+
simulationOverridesPlan,
|
|
1182
1182
|
);
|
|
1183
1183
|
} catch (err: any) {
|
|
1184
1184
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1185
1185
|
...checkpoint.getStats(),
|
|
1186
1186
|
slotNumber: checkpoint.header.slotNumber,
|
|
1187
|
-
|
|
1187
|
+
simulationOverridesPlan,
|
|
1188
1188
|
});
|
|
1189
1189
|
throw err;
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1192
|
+
// Build a pre-check callback that re-validates the checkpoint before L1 submission.
|
|
1193
|
+
// During pipelining this catches stale proposals due to prunes or L1 reorgs that occur during the pipeline sleep.
|
|
1194
|
+
let preCheck = undefined;
|
|
1195
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
1196
|
+
preCheck = async () => {
|
|
1197
|
+
this.log.debug(`Re-validating checkpoint ${checkpoint.number} before L1 submission`);
|
|
1198
|
+
await this.validateCheckpointForSubmission(
|
|
1199
|
+
checkpoint,
|
|
1200
|
+
attestationsAndSigners,
|
|
1201
|
+
attestationsAndSignersSignature,
|
|
1202
|
+
simulationOverridesPlan,
|
|
1203
|
+
);
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
|
|
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
|
+
);
|
|
1194
1218
|
}
|
|
1195
1219
|
|
|
1196
1220
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1233,8 +1257,8 @@ export class SequencerPublisher {
|
|
|
1233
1257
|
request: L1TxRequest,
|
|
1234
1258
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1235
1259
|
slotNumber: SlotNumber,
|
|
1236
|
-
timestamp: bigint,
|
|
1237
1260
|
) {
|
|
1261
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1238
1262
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1239
1263
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1240
1264
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1250,8 +1274,9 @@ export class SequencerPublisher {
|
|
|
1250
1274
|
|
|
1251
1275
|
let gasUsed: bigint;
|
|
1252
1276
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1277
|
+
|
|
1253
1278
|
try {
|
|
1254
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1279
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1255
1280
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1256
1281
|
} catch (err) {
|
|
1257
1282
|
const viemError = formatViemError(err, simulateAbi);
|
|
@@ -1309,6 +1334,7 @@ export class SequencerPublisher {
|
|
|
1309
1334
|
*/
|
|
1310
1335
|
public interrupt() {
|
|
1311
1336
|
this.interrupted = true;
|
|
1337
|
+
this.interruptibleSleep.interrupt();
|
|
1312
1338
|
this.l1TxUtils.interrupt();
|
|
1313
1339
|
}
|
|
1314
1340
|
|
|
@@ -1318,11 +1344,7 @@ export class SequencerPublisher {
|
|
|
1318
1344
|
this.l1TxUtils.restart();
|
|
1319
1345
|
}
|
|
1320
1346
|
|
|
1321
|
-
private async prepareProposeTx(
|
|
1322
|
-
encodedData: L1ProcessArgs,
|
|
1323
|
-
timestamp: bigint,
|
|
1324
|
-
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1325
|
-
) {
|
|
1347
|
+
private async prepareProposeTx(encodedData: L1ProcessArgs, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
1326
1348
|
const kzg = Blob.getViemKzgInstance();
|
|
1327
1349
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
1328
1350
|
this.log.debug('Validating blob input', { blobInput });
|
|
@@ -1393,7 +1415,7 @@ export class SequencerPublisher {
|
|
|
1393
1415
|
blobInput,
|
|
1394
1416
|
] as const;
|
|
1395
1417
|
|
|
1396
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1418
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
1397
1419
|
|
|
1398
1420
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1399
1421
|
}
|
|
@@ -1401,7 +1423,6 @@ export class SequencerPublisher {
|
|
|
1401
1423
|
/**
|
|
1402
1424
|
* Simulates the propose tx with eth_simulateV1
|
|
1403
1425
|
* @param args - The propose tx args
|
|
1404
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1405
1426
|
* @returns The simulation result
|
|
1406
1427
|
*/
|
|
1407
1428
|
private async simulateProposeTx(
|
|
@@ -1418,8 +1439,7 @@ export class SequencerPublisher {
|
|
|
1418
1439
|
ViemSignature,
|
|
1419
1440
|
`0x${string}`,
|
|
1420
1441
|
],
|
|
1421
|
-
|
|
1422
|
-
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1442
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
1423
1443
|
) {
|
|
1424
1444
|
const rollupData = encodeFunctionData({
|
|
1425
1445
|
abi: RollupAbi,
|
|
@@ -1427,23 +1447,7 @@ export class SequencerPublisher {
|
|
|
1427
1447
|
args,
|
|
1428
1448
|
});
|
|
1429
1449
|
|
|
1430
|
-
|
|
1431
|
-
const forcePendingCheckpointNumberStateDiff = (
|
|
1432
|
-
options.forcePendingCheckpointNumber !== undefined
|
|
1433
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1434
|
-
: []
|
|
1435
|
-
).flatMap(override => override.stateDiff ?? []);
|
|
1436
|
-
|
|
1437
|
-
const stateOverrides: StateOverride = [
|
|
1438
|
-
{
|
|
1439
|
-
address: this.rollupContract.address,
|
|
1440
|
-
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1441
|
-
stateDiff: [
|
|
1442
|
-
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1443
|
-
...forcePendingCheckpointNumberStateDiff,
|
|
1444
|
-
],
|
|
1445
|
-
},
|
|
1446
|
-
];
|
|
1450
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
1447
1451
|
// In fisherman mode, simulate as the proposer but with sufficient balance
|
|
1448
1452
|
if (this.proposerAddressForSimulation) {
|
|
1449
1453
|
stateOverrides.push({
|
|
@@ -1453,6 +1457,7 @@ export class SequencerPublisher {
|
|
|
1453
1457
|
}
|
|
1454
1458
|
|
|
1455
1459
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1460
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1456
1461
|
|
|
1457
1462
|
const simulationResult = await this.l1TxUtils
|
|
1458
1463
|
.simulate(
|
|
@@ -1463,8 +1468,7 @@ export class SequencerPublisher {
|
|
|
1463
1468
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1464
1469
|
},
|
|
1465
1470
|
{
|
|
1466
|
-
|
|
1467
|
-
time: timestamp + 1n,
|
|
1471
|
+
time: simTs,
|
|
1468
1472
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1469
1473
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1470
1474
|
},
|
|
@@ -1486,7 +1490,7 @@ export class SequencerPublisher {
|
|
|
1486
1490
|
logs: [],
|
|
1487
1491
|
};
|
|
1488
1492
|
}
|
|
1489
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1493
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1490
1494
|
this.backupFailedTx({
|
|
1491
1495
|
id: keccak256(rollupData),
|
|
1492
1496
|
failureType: 'simulation',
|
|
@@ -1508,16 +1512,15 @@ export class SequencerPublisher {
|
|
|
1508
1512
|
private async addProposeTx(
|
|
1509
1513
|
checkpoint: Checkpoint,
|
|
1510
1514
|
encodedData: L1ProcessArgs,
|
|
1511
|
-
opts:
|
|
1512
|
-
|
|
1515
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1516
|
+
preCheck?: () => Promise<void>,
|
|
1513
1517
|
): Promise<void> {
|
|
1514
1518
|
const slot = checkpoint.header.slotNumber;
|
|
1515
1519
|
const timer = new Timer();
|
|
1516
1520
|
const kzg = Blob.getViemKzgInstance();
|
|
1517
1521
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1518
1522
|
encodedData,
|
|
1519
|
-
|
|
1520
|
-
opts,
|
|
1523
|
+
opts.simulationOverridesPlan,
|
|
1521
1524
|
);
|
|
1522
1525
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1523
1526
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
@@ -1541,7 +1544,8 @@ export class SequencerPublisher {
|
|
|
1541
1544
|
data: rollupData,
|
|
1542
1545
|
},
|
|
1543
1546
|
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1544
|
-
gasConfig: {
|
|
1547
|
+
gasConfig: { txTimeoutAt: opts.txTimeoutAt, gasLimit },
|
|
1548
|
+
preCheck,
|
|
1545
1549
|
blobConfig: {
|
|
1546
1550
|
blobs: encodedData.blobs.map(b => b.data),
|
|
1547
1551
|
kzg,
|
|
@@ -1595,7 +1599,14 @@ export class SequencerPublisher {
|
|
|
1595
1599
|
});
|
|
1596
1600
|
}
|
|
1597
1601
|
|
|
1598
|
-
/** Returns the timestamp
|
|
1602
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1603
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
|
|
1604
|
+
private getSimulationTimestamp(slot: SlotNumber): bigint {
|
|
1605
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1606
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */
|
|
1599
1610
|
private getNextL1SlotTimestamp(): bigint {
|
|
1600
1611
|
const l1Constants = this.epochCache.getL1Constants();
|
|
1601
1612
|
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|