@aztec/sequencer-client 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05
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 +6 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +11 -17
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +13 -12
- 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 +15 -14
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +16 -51
- package/dest/global_variable_builder/index.d.ts +4 -2
- 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 +13 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +19 -4
- 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 +3 -5
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +2 -3
- package/dest/publisher/sequencer-publisher.d.ts +48 -42
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +119 -111
- 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 +41 -11
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +525 -149
- 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/events.d.ts +6 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +9 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +34 -20
- package/dest/sequencer/sequencer.d.ts +27 -7
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +106 -28
- 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/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +7 -6
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +16 -24
- package/src/config.ts +13 -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 +26 -63
- package/src/global_variable_builder/index.ts +3 -1
- package/src/publisher/config.ts +38 -4
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-publisher-factory.ts +3 -6
- package/src/publisher/sequencer-publisher.ts +183 -159
- package/src/sequencer/README.md +82 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +616 -167
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +5 -0
- package/src/sequencer/metrics.ts +43 -24
- package/src/sequencer/sequencer.ts +148 -32
- package/src/sequencer/timetable.ts +57 -45
- package/src/test/utils.ts +28 -10
|
@@ -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,20 +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 {
|
|
47
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
46
48
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
47
49
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
48
50
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
49
51
|
|
|
50
52
|
import {
|
|
51
53
|
type Hex,
|
|
52
|
-
type StateOverride,
|
|
53
54
|
type TransactionReceipt,
|
|
54
55
|
type TypedDataDefinition,
|
|
55
56
|
encodeFunctionData,
|
|
@@ -62,6 +63,20 @@ import type { SequencerPublisherConfig } from './config.js';
|
|
|
62
63
|
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
63
64
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
64
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
|
+
|
|
65
80
|
/** Arguments to the process method of the rollup contract */
|
|
66
81
|
type L1ProcessArgs = {
|
|
67
82
|
/** The L2 block header. */
|
|
@@ -83,16 +98,13 @@ export const Actions = [
|
|
|
83
98
|
'invalidate-by-insufficient-attestations',
|
|
84
99
|
'propose',
|
|
85
100
|
'governance-signal',
|
|
86
|
-
'empire-slashing-signal',
|
|
87
|
-
'create-empire-payload',
|
|
88
|
-
'execute-empire-payload',
|
|
89
101
|
'vote-offenses',
|
|
90
102
|
'execute-slash',
|
|
91
103
|
] as const;
|
|
92
104
|
|
|
93
105
|
export type Action = (typeof Actions)[number];
|
|
94
106
|
|
|
95
|
-
type GovernanceSignalAction = Extract<Action, 'governance-signal'
|
|
107
|
+
type GovernanceSignalAction = Extract<Action, 'governance-signal'>;
|
|
96
108
|
|
|
97
109
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
98
110
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
@@ -103,6 +115,13 @@ export type InvalidateCheckpointRequest = {
|
|
|
103
115
|
gasUsed: bigint;
|
|
104
116
|
checkpointNumber: CheckpointNumber;
|
|
105
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;
|
|
106
125
|
};
|
|
107
126
|
|
|
108
127
|
interface RequestWithExpiry {
|
|
@@ -111,6 +130,8 @@ interface RequestWithExpiry {
|
|
|
111
130
|
lastValidL2Slot: SlotNumber;
|
|
112
131
|
gasConfig?: Pick<L1TxConfig, 'txTimeoutAt' | 'gasLimit'>;
|
|
113
132
|
blobConfig?: L1BlobInputs;
|
|
133
|
+
/** Optional pre-send validation. If it rejects, the request is discarded. */
|
|
134
|
+
preCheck?: () => Promise<void>;
|
|
114
135
|
checkSuccess: (
|
|
115
136
|
request: L1TxRequest,
|
|
116
137
|
result?: { receipt: TransactionReceipt; stats?: TransactionStats; errorMsg?: string },
|
|
@@ -135,6 +156,9 @@ export class SequencerPublisher {
|
|
|
135
156
|
protected ethereumSlotDuration: bigint;
|
|
136
157
|
protected aztecSlotDuration: bigint;
|
|
137
158
|
|
|
159
|
+
/** Date provider for wall-clock time. */
|
|
160
|
+
private readonly dateProvider: DateProvider;
|
|
161
|
+
|
|
138
162
|
private blobClient: BlobClientInterface;
|
|
139
163
|
|
|
140
164
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
@@ -149,6 +173,9 @@ export class SequencerPublisher {
|
|
|
149
173
|
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */
|
|
150
174
|
private feeAssetPriceOracle: FeeAssetPriceOracle;
|
|
151
175
|
|
|
176
|
+
/** Interruptible sleep used by sendRequestsAt to wait until a target timestamp. */
|
|
177
|
+
private readonly interruptibleSleep = new InterruptibleSleep();
|
|
178
|
+
|
|
152
179
|
// A CALL to a cold address is 2700 gas
|
|
153
180
|
public static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
154
181
|
|
|
@@ -158,8 +185,7 @@ export class SequencerPublisher {
|
|
|
158
185
|
public l1TxUtils: L1TxUtils;
|
|
159
186
|
public rollupContract: RollupContract;
|
|
160
187
|
public govProposerContract: GovernanceProposerContract;
|
|
161
|
-
public slashingProposerContract:
|
|
162
|
-
public slashFactoryContract: SlashFactoryContract;
|
|
188
|
+
public slashingProposerContract: SlashingProposerContract | undefined;
|
|
163
189
|
|
|
164
190
|
public readonly tracer: Tracer;
|
|
165
191
|
|
|
@@ -173,9 +199,8 @@ export class SequencerPublisher {
|
|
|
173
199
|
blobClient: BlobClientInterface;
|
|
174
200
|
l1TxUtils: L1TxUtils;
|
|
175
201
|
rollupContract: RollupContract;
|
|
176
|
-
slashingProposerContract:
|
|
202
|
+
slashingProposerContract: SlashingProposerContract | undefined;
|
|
177
203
|
governanceProposerContract: GovernanceProposerContract;
|
|
178
|
-
slashFactoryContract: SlashFactoryContract;
|
|
179
204
|
epochCache: EpochCache;
|
|
180
205
|
dateProvider: DateProvider;
|
|
181
206
|
metrics: SequencerPublisherMetrics;
|
|
@@ -187,10 +212,12 @@ export class SequencerPublisher {
|
|
|
187
212
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
188
213
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
189
214
|
this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
|
|
215
|
+
this.dateProvider = deps.dateProvider;
|
|
190
216
|
this.epochCache = deps.epochCache;
|
|
191
217
|
this.lastActions = deps.lastActions;
|
|
192
218
|
|
|
193
219
|
this.blobClient = deps.blobClient;
|
|
220
|
+
this.dateProvider = deps.dateProvider;
|
|
194
221
|
|
|
195
222
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
196
223
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
@@ -208,8 +235,6 @@ export class SequencerPublisher {
|
|
|
208
235
|
const newSlashingProposer = await this.rollupContract.getSlashingProposer();
|
|
209
236
|
this.slashingProposerContract = newSlashingProposer;
|
|
210
237
|
});
|
|
211
|
-
this.slashFactoryContract = deps.slashFactoryContract;
|
|
212
|
-
|
|
213
238
|
// Initialize L1 fee analyzer for fisherman mode
|
|
214
239
|
if (config.fishermanMode) {
|
|
215
240
|
this.l1FeeAnalyzer = new L1FeeAnalyzer(
|
|
@@ -366,9 +391,10 @@ export class SequencerPublisher {
|
|
|
366
391
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
367
392
|
*/
|
|
368
393
|
@trackSpan('SequencerPublisher.sendRequests')
|
|
369
|
-
public async sendRequests() {
|
|
394
|
+
public async sendRequests(): Promise<SendRequestsResult | undefined> {
|
|
370
395
|
const requestsToProcess = [...this.requests];
|
|
371
396
|
this.requests = [];
|
|
397
|
+
|
|
372
398
|
if (this.interrupted || requestsToProcess.length === 0) {
|
|
373
399
|
return undefined;
|
|
374
400
|
}
|
|
@@ -527,6 +553,45 @@ export class SequencerPublisher {
|
|
|
527
553
|
}
|
|
528
554
|
}
|
|
529
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
|
+
|
|
530
595
|
private callbackBundledTransactions(
|
|
531
596
|
requests: RequestWithExpiry[],
|
|
532
597
|
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError | undefined,
|
|
@@ -602,21 +667,21 @@ export class SequencerPublisher {
|
|
|
602
667
|
* @param tipArchive - The archive to check
|
|
603
668
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
604
669
|
*/
|
|
605
|
-
public canProposeAt(
|
|
606
|
-
tipArchive: Fr,
|
|
607
|
-
msgSender: EthAddress,
|
|
608
|
-
opts: { forcePendingCheckpointNumber?: CheckpointNumber; pipelined?: boolean } = {},
|
|
609
|
-
) {
|
|
670
|
+
public async canProposeAt(tipArchive: Fr, msgSender: EthAddress, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
610
671
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
611
672
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
612
673
|
|
|
613
|
-
const pipelined =
|
|
674
|
+
const pipelined = this.epochCache.isProposerPipeliningEnabled();
|
|
614
675
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
676
|
+
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
615
677
|
|
|
616
678
|
return this.rollupContract
|
|
617
|
-
.canProposeAt(
|
|
618
|
-
|
|
619
|
-
|
|
679
|
+
.canProposeAt(
|
|
680
|
+
tipArchive.toBuffer(),
|
|
681
|
+
msgSender.toString(),
|
|
682
|
+
nextL1SlotTs,
|
|
683
|
+
await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan),
|
|
684
|
+
)
|
|
620
685
|
.catch(err => {
|
|
621
686
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
622
687
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find(e => err.message.includes(e))}`, {
|
|
@@ -638,13 +703,13 @@ export class SequencerPublisher {
|
|
|
638
703
|
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
639
704
|
public async validateBlockHeader(
|
|
640
705
|
header: CheckpointHeader,
|
|
641
|
-
|
|
706
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
642
707
|
): Promise<void> {
|
|
643
708
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
644
709
|
|
|
645
710
|
const args = [
|
|
646
711
|
header.toViem(),
|
|
647
|
-
CommitteeAttestationsAndSigners.
|
|
712
|
+
CommitteeAttestationsAndSigners.packAttestations([]),
|
|
648
713
|
[], // no signers
|
|
649
714
|
Signature.empty().toViemSignature(),
|
|
650
715
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
@@ -652,10 +717,8 @@ export class SequencerPublisher {
|
|
|
652
717
|
flags,
|
|
653
718
|
] as const;
|
|
654
719
|
|
|
655
|
-
const ts =
|
|
656
|
-
const stateOverrides = await this.rollupContract
|
|
657
|
-
opts?.forcePendingCheckpointNumber,
|
|
658
|
-
);
|
|
720
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
721
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
659
722
|
let balance = 0n;
|
|
660
723
|
if (this.config.fishermanMode) {
|
|
661
724
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -675,7 +738,7 @@ export class SequencerPublisher {
|
|
|
675
738
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
676
739
|
from: MULTI_CALL_3_ADDRESS,
|
|
677
740
|
},
|
|
678
|
-
{ time: ts
|
|
741
|
+
{ time: ts },
|
|
679
742
|
stateOverrides,
|
|
680
743
|
);
|
|
681
744
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -728,6 +791,7 @@ export class SequencerPublisher {
|
|
|
728
791
|
gasUsed,
|
|
729
792
|
checkpointNumber,
|
|
730
793
|
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
794
|
+
lastArchive: validationResult.checkpoint.lastArchive,
|
|
731
795
|
reason,
|
|
732
796
|
};
|
|
733
797
|
} catch (err) {
|
|
@@ -740,8 +804,8 @@ export class SequencerPublisher {
|
|
|
740
804
|
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
741
805
|
{ ...logData, request, error: viemError.message },
|
|
742
806
|
);
|
|
743
|
-
const
|
|
744
|
-
if (
|
|
807
|
+
const latestProposedCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
808
|
+
if (latestProposedCheckpointNumber < checkpointNumber) {
|
|
745
809
|
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
746
810
|
return undefined;
|
|
747
811
|
} else {
|
|
@@ -786,9 +850,7 @@ export class SequencerPublisher {
|
|
|
786
850
|
const logData = { ...checkpoint, reason };
|
|
787
851
|
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
788
852
|
|
|
789
|
-
const attestationsAndSigners =
|
|
790
|
-
validationResult.attestations,
|
|
791
|
-
).getPackedAttestations();
|
|
853
|
+
const attestationsAndSigners = CommitteeAttestationsAndSigners.packAttestations(validationResult.attestations);
|
|
792
854
|
|
|
793
855
|
if (reason === 'invalid-attestation') {
|
|
794
856
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
@@ -815,11 +877,8 @@ export class SequencerPublisher {
|
|
|
815
877
|
checkpoint: Checkpoint,
|
|
816
878
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
817
879
|
attestationsAndSignersSignature: Signature,
|
|
818
|
-
|
|
819
|
-
): Promise<
|
|
820
|
-
// Anchor the simulation timestamp to the checkpoint's own slot start time
|
|
821
|
-
// rather than the current L1 block timestamp, which may overshoot into the next slot if the build ran late.
|
|
822
|
-
const ts = checkpoint.header.timestamp;
|
|
880
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
881
|
+
): Promise<void> {
|
|
823
882
|
const blobFields = checkpoint.toBlobFields();
|
|
824
883
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
825
884
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -838,13 +897,11 @@ export class SequencerPublisher {
|
|
|
838
897
|
blobInput,
|
|
839
898
|
] as const;
|
|
840
899
|
|
|
841
|
-
await this.simulateProposeTx(args,
|
|
842
|
-
return ts;
|
|
900
|
+
await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
843
901
|
}
|
|
844
902
|
|
|
845
903
|
private async enqueueCastSignalHelper(
|
|
846
904
|
slotNumber: SlotNumber,
|
|
847
|
-
timestamp: bigint,
|
|
848
905
|
signalType: GovernanceSignalAction,
|
|
849
906
|
payload: EthAddress,
|
|
850
907
|
base: IEmpireBase,
|
|
@@ -923,13 +980,17 @@ export class SequencerPublisher {
|
|
|
923
980
|
});
|
|
924
981
|
|
|
925
982
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
983
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
926
984
|
|
|
927
985
|
try {
|
|
928
986
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
929
987
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
930
988
|
} catch (err) {
|
|
931
989
|
const viemError = formatViemError(err);
|
|
932
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
990
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
991
|
+
simulationTimestamp: timestamp,
|
|
992
|
+
l1BlockNumber,
|
|
993
|
+
});
|
|
933
994
|
this.backupFailedTx({
|
|
934
995
|
id: keccak256(request.data!),
|
|
935
996
|
failureType: 'simulation',
|
|
@@ -992,19 +1053,16 @@ export class SequencerPublisher {
|
|
|
992
1053
|
/**
|
|
993
1054
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
994
1055
|
* @param slotNumber - The slot number to cast a signal for.
|
|
995
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
996
1056
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
997
1057
|
*/
|
|
998
1058
|
public enqueueGovernanceCastSignal(
|
|
999
1059
|
governancePayload: EthAddress,
|
|
1000
1060
|
slotNumber: SlotNumber,
|
|
1001
|
-
timestamp: bigint,
|
|
1002
1061
|
signerAddress: EthAddress,
|
|
1003
1062
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1004
1063
|
): Promise<boolean> {
|
|
1005
1064
|
return this.enqueueCastSignalHelper(
|
|
1006
1065
|
slotNumber,
|
|
1007
|
-
timestamp,
|
|
1008
1066
|
'governance-signal',
|
|
1009
1067
|
governancePayload,
|
|
1010
1068
|
this.govProposerContract,
|
|
@@ -1017,7 +1075,6 @@ export class SequencerPublisher {
|
|
|
1017
1075
|
public async enqueueSlashingActions(
|
|
1018
1076
|
actions: ProposerSlashAction[],
|
|
1019
1077
|
slotNumber: SlotNumber,
|
|
1020
|
-
timestamp: bigint,
|
|
1021
1078
|
signerAddress: EthAddress,
|
|
1022
1079
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1023
1080
|
): Promise<boolean> {
|
|
@@ -1028,58 +1085,6 @@ export class SequencerPublisher {
|
|
|
1028
1085
|
|
|
1029
1086
|
for (const action of actions) {
|
|
1030
1087
|
switch (action.type) {
|
|
1031
|
-
case 'vote-empire-payload': {
|
|
1032
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1033
|
-
this.log.error('Cannot vote for empire payload on non-empire slashing contract');
|
|
1034
|
-
break;
|
|
1035
|
-
}
|
|
1036
|
-
this.log.debug(`Enqueuing slashing vote for payload ${action.payload} at slot ${slotNumber}`, {
|
|
1037
|
-
signerAddress,
|
|
1038
|
-
});
|
|
1039
|
-
await this.enqueueCastSignalHelper(
|
|
1040
|
-
slotNumber,
|
|
1041
|
-
timestamp,
|
|
1042
|
-
'empire-slashing-signal',
|
|
1043
|
-
action.payload,
|
|
1044
|
-
this.slashingProposerContract,
|
|
1045
|
-
signerAddress,
|
|
1046
|
-
signer,
|
|
1047
|
-
);
|
|
1048
|
-
break;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
case 'create-empire-payload': {
|
|
1052
|
-
this.log.debug(`Enqueuing slashing create payload at slot ${slotNumber}`, { slotNumber, signerAddress });
|
|
1053
|
-
const request = this.slashFactoryContract.buildCreatePayloadRequest(action.data);
|
|
1054
|
-
await this.simulateAndEnqueueRequest(
|
|
1055
|
-
'create-empire-payload',
|
|
1056
|
-
request,
|
|
1057
|
-
(receipt: TransactionReceipt) =>
|
|
1058
|
-
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1059
|
-
slotNumber,
|
|
1060
|
-
timestamp,
|
|
1061
|
-
);
|
|
1062
|
-
break;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
case 'execute-empire-payload': {
|
|
1066
|
-
this.log.debug(`Enqueuing slashing execute payload at slot ${slotNumber}`, { slotNumber, signerAddress });
|
|
1067
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1068
|
-
this.log.error('Cannot execute slashing payload on non-empire slashing contract');
|
|
1069
|
-
return false;
|
|
1070
|
-
}
|
|
1071
|
-
const empireSlashingProposer = this.slashingProposerContract as EmpireSlashingProposerContract;
|
|
1072
|
-
const request = empireSlashingProposer.buildExecuteRoundRequest(action.round);
|
|
1073
|
-
await this.simulateAndEnqueueRequest(
|
|
1074
|
-
'execute-empire-payload',
|
|
1075
|
-
request,
|
|
1076
|
-
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1077
|
-
slotNumber,
|
|
1078
|
-
timestamp,
|
|
1079
|
-
);
|
|
1080
|
-
break;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
1088
|
case 'vote-offenses': {
|
|
1084
1089
|
this.log.debug(`Enqueuing slashing vote for ${action.votes.length} votes at slot ${slotNumber}`, {
|
|
1085
1090
|
slotNumber,
|
|
@@ -1087,19 +1092,17 @@ export class SequencerPublisher {
|
|
|
1087
1092
|
votesCount: action.votes.length,
|
|
1088
1093
|
signerAddress,
|
|
1089
1094
|
});
|
|
1090
|
-
if (this.slashingProposerContract
|
|
1091
|
-
this.log.error('
|
|
1095
|
+
if (!this.slashingProposerContract) {
|
|
1096
|
+
this.log.error('No slashing proposer contract available');
|
|
1092
1097
|
return false;
|
|
1093
1098
|
}
|
|
1094
|
-
const tallySlashingProposer = this.slashingProposerContract as TallySlashingProposerContract;
|
|
1095
1099
|
const votes = bufferToHex(encodeSlashConsensusVotes(action.votes));
|
|
1096
|
-
const request = await
|
|
1100
|
+
const request = await this.slashingProposerContract.buildVoteRequestFromSigner(votes, slotNumber, signer);
|
|
1097
1101
|
await this.simulateAndEnqueueRequest(
|
|
1098
1102
|
'vote-offenses',
|
|
1099
1103
|
request,
|
|
1100
|
-
(receipt: TransactionReceipt) => !!
|
|
1104
|
+
(receipt: TransactionReceipt) => !!this.slashingProposerContract!.tryExtractVoteCastEvent(receipt.logs),
|
|
1101
1105
|
slotNumber,
|
|
1102
|
-
timestamp,
|
|
1103
1106
|
);
|
|
1104
1107
|
break;
|
|
1105
1108
|
}
|
|
@@ -1110,18 +1113,20 @@ export class SequencerPublisher {
|
|
|
1110
1113
|
round: action.round,
|
|
1111
1114
|
signerAddress,
|
|
1112
1115
|
});
|
|
1113
|
-
if (this.slashingProposerContract
|
|
1114
|
-
this.log.error('
|
|
1116
|
+
if (!this.slashingProposerContract) {
|
|
1117
|
+
this.log.error('No slashing proposer contract available');
|
|
1115
1118
|
return false;
|
|
1116
1119
|
}
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1120
|
+
const executeRequest = this.slashingProposerContract.buildExecuteRoundRequest(
|
|
1121
|
+
action.round,
|
|
1122
|
+
action.committees,
|
|
1123
|
+
);
|
|
1119
1124
|
await this.simulateAndEnqueueRequest(
|
|
1120
1125
|
'execute-slash',
|
|
1121
|
-
|
|
1122
|
-
(receipt: TransactionReceipt) =>
|
|
1126
|
+
executeRequest,
|
|
1127
|
+
(receipt: TransactionReceipt) =>
|
|
1128
|
+
!!this.slashingProposerContract!.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1123
1129
|
slotNumber,
|
|
1124
|
-
timestamp,
|
|
1125
1130
|
);
|
|
1126
1131
|
break;
|
|
1127
1132
|
}
|
|
@@ -1141,7 +1146,7 @@ export class SequencerPublisher {
|
|
|
1141
1146
|
checkpoint: Checkpoint,
|
|
1142
1147
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
1143
1148
|
attestationsAndSignersSignature: Signature,
|
|
1144
|
-
opts:
|
|
1149
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1145
1150
|
): Promise<void> {
|
|
1146
1151
|
const checkpointHeader = checkpoint.header;
|
|
1147
1152
|
|
|
@@ -1157,7 +1162,9 @@ export class SequencerPublisher {
|
|
|
1157
1162
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1158
1163
|
};
|
|
1159
1164
|
|
|
1160
|
-
|
|
1165
|
+
const simulationOverridesPlan = SimulationOverridesBuilder.from(opts.simulationOverridesPlan)
|
|
1166
|
+
.withoutBlobCheck()
|
|
1167
|
+
.build();
|
|
1161
1168
|
|
|
1162
1169
|
try {
|
|
1163
1170
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
@@ -1165,23 +1172,47 @@ export class SequencerPublisher {
|
|
|
1165
1172
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1166
1173
|
// make time consistency checks break.
|
|
1167
1174
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1168
|
-
|
|
1175
|
+
await this.validateCheckpointForSubmission(
|
|
1169
1176
|
checkpoint,
|
|
1170
1177
|
attestationsAndSigners,
|
|
1171
1178
|
attestationsAndSignersSignature,
|
|
1172
|
-
|
|
1179
|
+
simulationOverridesPlan,
|
|
1173
1180
|
);
|
|
1174
1181
|
} catch (err: any) {
|
|
1175
1182
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1176
1183
|
...checkpoint.getStats(),
|
|
1177
1184
|
slotNumber: checkpoint.header.slotNumber,
|
|
1178
|
-
|
|
1185
|
+
simulationOverridesPlan,
|
|
1179
1186
|
});
|
|
1180
1187
|
throw err;
|
|
1181
1188
|
}
|
|
1182
1189
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1190
|
+
// Build a pre-check callback that re-validates the checkpoint before L1 submission.
|
|
1191
|
+
// During pipelining this catches stale proposals due to prunes or L1 reorgs that occur during the pipeline sleep.
|
|
1192
|
+
let preCheck = undefined;
|
|
1193
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
1194
|
+
preCheck = async () => {
|
|
1195
|
+
this.log.debug(`Re-validating checkpoint ${checkpoint.number} before L1 submission`);
|
|
1196
|
+
await this.validateCheckpointForSubmission(
|
|
1197
|
+
checkpoint,
|
|
1198
|
+
attestationsAndSigners,
|
|
1199
|
+
attestationsAndSignersSignature,
|
|
1200
|
+
simulationOverridesPlan,
|
|
1201
|
+
);
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1206
|
+
...checkpoint.toCheckpointInfo(),
|
|
1207
|
+
txTimeoutAt: opts.txTimeoutAt,
|
|
1208
|
+
simulationOverridesPlan,
|
|
1209
|
+
});
|
|
1210
|
+
await this.addProposeTx(
|
|
1211
|
+
checkpoint,
|
|
1212
|
+
proposeTxArgs,
|
|
1213
|
+
{ txTimeoutAt: opts.txTimeoutAt, simulationOverridesPlan },
|
|
1214
|
+
preCheck,
|
|
1215
|
+
);
|
|
1185
1216
|
}
|
|
1186
1217
|
|
|
1187
1218
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1224,8 +1255,8 @@ export class SequencerPublisher {
|
|
|
1224
1255
|
request: L1TxRequest,
|
|
1225
1256
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1226
1257
|
slotNumber: SlotNumber,
|
|
1227
|
-
timestamp: bigint,
|
|
1228
1258
|
) {
|
|
1259
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1229
1260
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1230
1261
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1231
1262
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1241,8 +1272,9 @@ export class SequencerPublisher {
|
|
|
1241
1272
|
|
|
1242
1273
|
let gasUsed: bigint;
|
|
1243
1274
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1275
|
+
|
|
1244
1276
|
try {
|
|
1245
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1277
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1246
1278
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1247
1279
|
} catch (err) {
|
|
1248
1280
|
const viemError = formatViemError(err, simulateAbi);
|
|
@@ -1300,6 +1332,7 @@ export class SequencerPublisher {
|
|
|
1300
1332
|
*/
|
|
1301
1333
|
public interrupt() {
|
|
1302
1334
|
this.interrupted = true;
|
|
1335
|
+
this.interruptibleSleep.interrupt();
|
|
1303
1336
|
this.l1TxUtils.interrupt();
|
|
1304
1337
|
}
|
|
1305
1338
|
|
|
@@ -1309,11 +1342,7 @@ export class SequencerPublisher {
|
|
|
1309
1342
|
this.l1TxUtils.restart();
|
|
1310
1343
|
}
|
|
1311
1344
|
|
|
1312
|
-
private async prepareProposeTx(
|
|
1313
|
-
encodedData: L1ProcessArgs,
|
|
1314
|
-
timestamp: bigint,
|
|
1315
|
-
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1316
|
-
) {
|
|
1345
|
+
private async prepareProposeTx(encodedData: L1ProcessArgs, simulationOverridesPlan?: SimulationOverridesPlan) {
|
|
1317
1346
|
const kzg = Blob.getViemKzgInstance();
|
|
1318
1347
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
1319
1348
|
this.log.debug('Validating blob input', { blobInput });
|
|
@@ -1384,7 +1413,7 @@ export class SequencerPublisher {
|
|
|
1384
1413
|
blobInput,
|
|
1385
1414
|
] as const;
|
|
1386
1415
|
|
|
1387
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1416
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
1388
1417
|
|
|
1389
1418
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1390
1419
|
}
|
|
@@ -1392,7 +1421,6 @@ export class SequencerPublisher {
|
|
|
1392
1421
|
/**
|
|
1393
1422
|
* Simulates the propose tx with eth_simulateV1
|
|
1394
1423
|
* @param args - The propose tx args
|
|
1395
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1396
1424
|
* @returns The simulation result
|
|
1397
1425
|
*/
|
|
1398
1426
|
private async simulateProposeTx(
|
|
@@ -1409,8 +1437,7 @@ export class SequencerPublisher {
|
|
|
1409
1437
|
ViemSignature,
|
|
1410
1438
|
`0x${string}`,
|
|
1411
1439
|
],
|
|
1412
|
-
|
|
1413
|
-
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1440
|
+
simulationOverridesPlan?: SimulationOverridesPlan,
|
|
1414
1441
|
) {
|
|
1415
1442
|
const rollupData = encodeFunctionData({
|
|
1416
1443
|
abi: RollupAbi,
|
|
@@ -1418,23 +1445,7 @@ export class SequencerPublisher {
|
|
|
1418
1445
|
args,
|
|
1419
1446
|
});
|
|
1420
1447
|
|
|
1421
|
-
|
|
1422
|
-
const forcePendingCheckpointNumberStateDiff = (
|
|
1423
|
-
options.forcePendingCheckpointNumber !== undefined
|
|
1424
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1425
|
-
: []
|
|
1426
|
-
).flatMap(override => override.stateDiff ?? []);
|
|
1427
|
-
|
|
1428
|
-
const stateOverrides: StateOverride = [
|
|
1429
|
-
{
|
|
1430
|
-
address: this.rollupContract.address,
|
|
1431
|
-
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1432
|
-
stateDiff: [
|
|
1433
|
-
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1434
|
-
...forcePendingCheckpointNumberStateDiff,
|
|
1435
|
-
],
|
|
1436
|
-
},
|
|
1437
|
-
];
|
|
1448
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
1438
1449
|
// In fisherman mode, simulate as the proposer but with sufficient balance
|
|
1439
1450
|
if (this.proposerAddressForSimulation) {
|
|
1440
1451
|
stateOverrides.push({
|
|
@@ -1444,6 +1455,7 @@ export class SequencerPublisher {
|
|
|
1444
1455
|
}
|
|
1445
1456
|
|
|
1446
1457
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1458
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1447
1459
|
|
|
1448
1460
|
const simulationResult = await this.l1TxUtils
|
|
1449
1461
|
.simulate(
|
|
@@ -1454,8 +1466,7 @@ export class SequencerPublisher {
|
|
|
1454
1466
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1455
1467
|
},
|
|
1456
1468
|
{
|
|
1457
|
-
|
|
1458
|
-
time: timestamp + 1n,
|
|
1469
|
+
time: simTs,
|
|
1459
1470
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1460
1471
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1461
1472
|
},
|
|
@@ -1477,7 +1488,7 @@ export class SequencerPublisher {
|
|
|
1477
1488
|
logs: [],
|
|
1478
1489
|
};
|
|
1479
1490
|
}
|
|
1480
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1491
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1481
1492
|
this.backupFailedTx({
|
|
1482
1493
|
id: keccak256(rollupData),
|
|
1483
1494
|
failureType: 'simulation',
|
|
@@ -1499,16 +1510,15 @@ export class SequencerPublisher {
|
|
|
1499
1510
|
private async addProposeTx(
|
|
1500
1511
|
checkpoint: Checkpoint,
|
|
1501
1512
|
encodedData: L1ProcessArgs,
|
|
1502
|
-
opts:
|
|
1503
|
-
|
|
1513
|
+
opts: EnqueueProposeCheckpointOpts = {},
|
|
1514
|
+
preCheck?: () => Promise<void>,
|
|
1504
1515
|
): Promise<void> {
|
|
1505
1516
|
const slot = checkpoint.header.slotNumber;
|
|
1506
1517
|
const timer = new Timer();
|
|
1507
1518
|
const kzg = Blob.getViemKzgInstance();
|
|
1508
1519
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1509
1520
|
encodedData,
|
|
1510
|
-
|
|
1511
|
-
opts,
|
|
1521
|
+
opts.simulationOverridesPlan,
|
|
1512
1522
|
);
|
|
1513
1523
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1514
1524
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
@@ -1532,7 +1542,8 @@ export class SequencerPublisher {
|
|
|
1532
1542
|
data: rollupData,
|
|
1533
1543
|
},
|
|
1534
1544
|
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1535
|
-
gasConfig: {
|
|
1545
|
+
gasConfig: { txTimeoutAt: opts.txTimeoutAt, gasLimit },
|
|
1546
|
+
preCheck,
|
|
1536
1547
|
blobConfig: {
|
|
1537
1548
|
blobs: encodedData.blobs.map(b => b.data),
|
|
1538
1549
|
kzg,
|
|
@@ -1585,4 +1596,17 @@ export class SequencerPublisher {
|
|
|
1585
1596
|
},
|
|
1586
1597
|
});
|
|
1587
1598
|
}
|
|
1599
|
+
|
|
1600
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1601
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
|
|
1602
|
+
private getSimulationTimestamp(slot: SlotNumber): bigint {
|
|
1603
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1604
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */
|
|
1608
|
+
private getNextL1SlotTimestamp(): bigint {
|
|
1609
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1610
|
+
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|
|
1611
|
+
}
|
|
1588
1612
|
}
|