@aztec/sequencer-client 0.0.1-commit.3f296a7d2 → 0.0.1-commit.42ee6df9b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/global_variable_builder/global_builder.d.ts +3 -3
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +7 -4
- package/dest/publisher/sequencer-publisher.d.ts +46 -24
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +72 -40
- package/dest/sequencer/checkpoint_proposal_job.d.ts +28 -9
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +149 -74
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/sequencer.d.ts +14 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +60 -15
- package/package.json +27 -27
- package/src/global_variable_builder/global_builder.ts +15 -3
- package/src/publisher/sequencer-publisher.ts +113 -51
- package/src/sequencer/checkpoint_proposal_job.ts +181 -77
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/sequencer.ts +89 -19
|
@@ -5,6 +5,7 @@ import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
|
5
5
|
import {
|
|
6
6
|
type EmpireSlashingProposerContract,
|
|
7
7
|
FeeAssetPriceOracle,
|
|
8
|
+
type FeeHeader,
|
|
8
9
|
type GovernanceProposerContract,
|
|
9
10
|
type IEmpireBase,
|
|
10
11
|
MULTI_CALL_3_ADDRESS,
|
|
@@ -36,13 +37,14 @@ 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';
|
|
47
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
46
48
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
47
49
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
48
50
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
@@ -63,6 +65,20 @@ import type { SequencerPublisherConfig } from './config.js';
|
|
|
63
65
|
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
64
66
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
65
67
|
|
|
68
|
+
/** Result of a sendRequests call, returned by both sendRequests() and sendRequestsAt(). */
|
|
69
|
+
export type SendRequestsResult = {
|
|
70
|
+
/** The L1 transaction receipt or error from the bundled multicall. */
|
|
71
|
+
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError;
|
|
72
|
+
/** Actions that expired (past their deadline) before the request was sent. */
|
|
73
|
+
expiredActions: Action[];
|
|
74
|
+
/** Actions that were included in the sent L1 transaction. */
|
|
75
|
+
sentActions: Action[];
|
|
76
|
+
/** Actions whose L1 simulation succeeded (subset of sentActions). */
|
|
77
|
+
successfulActions: Action[];
|
|
78
|
+
/** Actions whose L1 simulation failed (subset of sentActions). */
|
|
79
|
+
failedActions: Action[];
|
|
80
|
+
};
|
|
81
|
+
|
|
66
82
|
/** Arguments to the process method of the rollup contract */
|
|
67
83
|
type L1ProcessArgs = {
|
|
68
84
|
/** The L2 block header. */
|
|
@@ -104,6 +120,8 @@ export type InvalidateCheckpointRequest = {
|
|
|
104
120
|
gasUsed: bigint;
|
|
105
121
|
checkpointNumber: CheckpointNumber;
|
|
106
122
|
forcePendingCheckpointNumber: CheckpointNumber;
|
|
123
|
+
/** Archive at the rollback target checkpoint (checkpoint N-1). */
|
|
124
|
+
lastArchive: Fr;
|
|
107
125
|
};
|
|
108
126
|
|
|
109
127
|
interface RequestWithExpiry {
|
|
@@ -135,7 +153,9 @@ export class SequencerPublisher {
|
|
|
135
153
|
protected log: Logger;
|
|
136
154
|
protected ethereumSlotDuration: bigint;
|
|
137
155
|
protected aztecSlotDuration: bigint;
|
|
138
|
-
|
|
156
|
+
|
|
157
|
+
/** Date provider for wall-clock time. */
|
|
158
|
+
private readonly dateProvider: DateProvider;
|
|
139
159
|
|
|
140
160
|
private blobClient: BlobClientInterface;
|
|
141
161
|
|
|
@@ -151,6 +171,9 @@ export class SequencerPublisher {
|
|
|
151
171
|
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */
|
|
152
172
|
private feeAssetPriceOracle: FeeAssetPriceOracle;
|
|
153
173
|
|
|
174
|
+
/** Interruptible sleep used by sendRequestsAt to wait until a target timestamp. */
|
|
175
|
+
private readonly interruptibleSleep = new InterruptibleSleep();
|
|
176
|
+
|
|
154
177
|
// A CALL to a cold address is 2700 gas
|
|
155
178
|
public static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
156
179
|
|
|
@@ -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');
|
|
@@ -369,9 +393,10 @@ export class SequencerPublisher {
|
|
|
369
393
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
370
394
|
*/
|
|
371
395
|
@trackSpan('SequencerPublisher.sendRequests')
|
|
372
|
-
public async sendRequests() {
|
|
396
|
+
public async sendRequests(): Promise<SendRequestsResult | undefined> {
|
|
373
397
|
const requestsToProcess = [...this.requests];
|
|
374
398
|
this.requests = [];
|
|
399
|
+
|
|
375
400
|
if (this.interrupted || requestsToProcess.length === 0) {
|
|
376
401
|
return undefined;
|
|
377
402
|
}
|
|
@@ -530,6 +555,23 @@ export class SequencerPublisher {
|
|
|
530
555
|
}
|
|
531
556
|
}
|
|
532
557
|
|
|
558
|
+
/*
|
|
559
|
+
* Schedules sending all enqueued requests at (or after) the given timestamp.
|
|
560
|
+
* Uses InterruptibleSleep so it can be cancelled via interrupt().
|
|
561
|
+
* Returns the promise for the L1 response (caller should NOT await this in the work loop).
|
|
562
|
+
*/
|
|
563
|
+
public async sendRequestsAt(submitAfter: Date): Promise<SendRequestsResult | undefined> {
|
|
564
|
+
const ms = submitAfter.getTime() - this.dateProvider.now();
|
|
565
|
+
if (ms > 0) {
|
|
566
|
+
this.log.debug(`Sleeping ${ms}ms before sending requests`, { submitAfter });
|
|
567
|
+
await this.interruptibleSleep.sleep(ms);
|
|
568
|
+
}
|
|
569
|
+
if (this.interrupted) {
|
|
570
|
+
return undefined;
|
|
571
|
+
}
|
|
572
|
+
return this.sendRequests();
|
|
573
|
+
}
|
|
574
|
+
|
|
533
575
|
private callbackBundledTransactions(
|
|
534
576
|
requests: RequestWithExpiry[],
|
|
535
577
|
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError | undefined,
|
|
@@ -608,7 +650,11 @@ export class SequencerPublisher {
|
|
|
608
650
|
public canProposeAt(
|
|
609
651
|
tipArchive: Fr,
|
|
610
652
|
msgSender: EthAddress,
|
|
611
|
-
opts: {
|
|
653
|
+
opts: {
|
|
654
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
655
|
+
forceArchive?: { checkpointNumber: CheckpointNumber; archive: Fr };
|
|
656
|
+
pipelined?: boolean;
|
|
657
|
+
} = {},
|
|
612
658
|
) {
|
|
613
659
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
614
660
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
@@ -620,6 +666,7 @@ export class SequencerPublisher {
|
|
|
620
666
|
return this.rollupContract
|
|
621
667
|
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
622
668
|
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
669
|
+
forceArchive: opts.forceArchive,
|
|
623
670
|
})
|
|
624
671
|
.catch(err => {
|
|
625
672
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
@@ -656,7 +703,7 @@ export class SequencerPublisher {
|
|
|
656
703
|
flags,
|
|
657
704
|
] as const;
|
|
658
705
|
|
|
659
|
-
const ts = this.
|
|
706
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
660
707
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
661
708
|
opts?.forcePendingCheckpointNumber,
|
|
662
709
|
);
|
|
@@ -679,7 +726,7 @@ export class SequencerPublisher {
|
|
|
679
726
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
680
727
|
from: MULTI_CALL_3_ADDRESS,
|
|
681
728
|
},
|
|
682
|
-
{ time: ts
|
|
729
|
+
{ time: ts },
|
|
683
730
|
stateOverrides,
|
|
684
731
|
);
|
|
685
732
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -732,6 +779,7 @@ export class SequencerPublisher {
|
|
|
732
779
|
gasUsed,
|
|
733
780
|
checkpointNumber,
|
|
734
781
|
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
782
|
+
lastArchive: validationResult.checkpoint.lastArchive,
|
|
735
783
|
reason,
|
|
736
784
|
};
|
|
737
785
|
} catch (err) {
|
|
@@ -744,8 +792,8 @@ export class SequencerPublisher {
|
|
|
744
792
|
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
745
793
|
{ ...logData, request, error: viemError.message },
|
|
746
794
|
);
|
|
747
|
-
const
|
|
748
|
-
if (
|
|
795
|
+
const latestProposedCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
796
|
+
if (latestProposedCheckpointNumber < checkpointNumber) {
|
|
749
797
|
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
750
798
|
return undefined;
|
|
751
799
|
} else {
|
|
@@ -819,11 +867,11 @@ export class SequencerPublisher {
|
|
|
819
867
|
checkpoint: Checkpoint,
|
|
820
868
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
821
869
|
attestationsAndSignersSignature: Signature,
|
|
822
|
-
options: {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
870
|
+
options: {
|
|
871
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
872
|
+
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
873
|
+
},
|
|
874
|
+
): Promise<void> {
|
|
827
875
|
const blobFields = checkpoint.toBlobFields();
|
|
828
876
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
829
877
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -842,13 +890,11 @@ export class SequencerPublisher {
|
|
|
842
890
|
blobInput,
|
|
843
891
|
] as const;
|
|
844
892
|
|
|
845
|
-
await this.simulateProposeTx(args,
|
|
846
|
-
return ts;
|
|
893
|
+
await this.simulateProposeTx(args, options);
|
|
847
894
|
}
|
|
848
895
|
|
|
849
896
|
private async enqueueCastSignalHelper(
|
|
850
897
|
slotNumber: SlotNumber,
|
|
851
|
-
timestamp: bigint,
|
|
852
898
|
signalType: GovernanceSignalAction,
|
|
853
899
|
payload: EthAddress,
|
|
854
900
|
base: IEmpireBase,
|
|
@@ -927,13 +973,17 @@ export class SequencerPublisher {
|
|
|
927
973
|
});
|
|
928
974
|
|
|
929
975
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
976
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
930
977
|
|
|
931
978
|
try {
|
|
932
979
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
933
980
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
934
981
|
} catch (err) {
|
|
935
982
|
const viemError = formatViemError(err);
|
|
936
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
983
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
984
|
+
simulationTimestamp: timestamp,
|
|
985
|
+
l1BlockNumber,
|
|
986
|
+
});
|
|
937
987
|
this.backupFailedTx({
|
|
938
988
|
id: keccak256(request.data!),
|
|
939
989
|
failureType: 'simulation',
|
|
@@ -996,19 +1046,16 @@ export class SequencerPublisher {
|
|
|
996
1046
|
/**
|
|
997
1047
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
998
1048
|
* @param slotNumber - The slot number to cast a signal for.
|
|
999
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
1000
1049
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
1001
1050
|
*/
|
|
1002
1051
|
public enqueueGovernanceCastSignal(
|
|
1003
1052
|
governancePayload: EthAddress,
|
|
1004
1053
|
slotNumber: SlotNumber,
|
|
1005
|
-
timestamp: bigint,
|
|
1006
1054
|
signerAddress: EthAddress,
|
|
1007
1055
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1008
1056
|
): Promise<boolean> {
|
|
1009
1057
|
return this.enqueueCastSignalHelper(
|
|
1010
1058
|
slotNumber,
|
|
1011
|
-
timestamp,
|
|
1012
1059
|
'governance-signal',
|
|
1013
1060
|
governancePayload,
|
|
1014
1061
|
this.govProposerContract,
|
|
@@ -1021,7 +1068,6 @@ export class SequencerPublisher {
|
|
|
1021
1068
|
public async enqueueSlashingActions(
|
|
1022
1069
|
actions: ProposerSlashAction[],
|
|
1023
1070
|
slotNumber: SlotNumber,
|
|
1024
|
-
timestamp: bigint,
|
|
1025
1071
|
signerAddress: EthAddress,
|
|
1026
1072
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1027
1073
|
): Promise<boolean> {
|
|
@@ -1042,7 +1088,6 @@ export class SequencerPublisher {
|
|
|
1042
1088
|
});
|
|
1043
1089
|
await this.enqueueCastSignalHelper(
|
|
1044
1090
|
slotNumber,
|
|
1045
|
-
timestamp,
|
|
1046
1091
|
'empire-slashing-signal',
|
|
1047
1092
|
action.payload,
|
|
1048
1093
|
this.slashingProposerContract,
|
|
@@ -1061,7 +1106,6 @@ export class SequencerPublisher {
|
|
|
1061
1106
|
(receipt: TransactionReceipt) =>
|
|
1062
1107
|
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1063
1108
|
slotNumber,
|
|
1064
|
-
timestamp,
|
|
1065
1109
|
);
|
|
1066
1110
|
break;
|
|
1067
1111
|
}
|
|
@@ -1079,7 +1123,6 @@ export class SequencerPublisher {
|
|
|
1079
1123
|
request,
|
|
1080
1124
|
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1081
1125
|
slotNumber,
|
|
1082
|
-
timestamp,
|
|
1083
1126
|
);
|
|
1084
1127
|
break;
|
|
1085
1128
|
}
|
|
@@ -1103,7 +1146,6 @@ export class SequencerPublisher {
|
|
|
1103
1146
|
request,
|
|
1104
1147
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs),
|
|
1105
1148
|
slotNumber,
|
|
1106
|
-
timestamp,
|
|
1107
1149
|
);
|
|
1108
1150
|
break;
|
|
1109
1151
|
}
|
|
@@ -1125,7 +1167,6 @@ export class SequencerPublisher {
|
|
|
1125
1167
|
request,
|
|
1126
1168
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1127
1169
|
slotNumber,
|
|
1128
|
-
timestamp,
|
|
1129
1170
|
);
|
|
1130
1171
|
break;
|
|
1131
1172
|
}
|
|
@@ -1145,7 +1186,11 @@ export class SequencerPublisher {
|
|
|
1145
1186
|
checkpoint: Checkpoint,
|
|
1146
1187
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
1147
1188
|
attestationsAndSignersSignature: Signature,
|
|
1148
|
-
opts: {
|
|
1189
|
+
opts: {
|
|
1190
|
+
txTimeoutAt?: Date;
|
|
1191
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1192
|
+
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1193
|
+
} = {},
|
|
1149
1194
|
): Promise<void> {
|
|
1150
1195
|
const checkpointHeader = checkpoint.header;
|
|
1151
1196
|
|
|
@@ -1161,15 +1206,13 @@ export class SequencerPublisher {
|
|
|
1161
1206
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1162
1207
|
};
|
|
1163
1208
|
|
|
1164
|
-
let ts: bigint;
|
|
1165
|
-
|
|
1166
1209
|
try {
|
|
1167
1210
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1168
1211
|
// This means that we can avoid the simulation issues in later checks.
|
|
1169
1212
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1170
1213
|
// make time consistency checks break.
|
|
1171
1214
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1172
|
-
|
|
1215
|
+
await this.validateCheckpointForSubmission(
|
|
1173
1216
|
checkpoint,
|
|
1174
1217
|
attestationsAndSigners,
|
|
1175
1218
|
attestationsAndSignersSignature,
|
|
@@ -1185,7 +1228,7 @@ export class SequencerPublisher {
|
|
|
1185
1228
|
}
|
|
1186
1229
|
|
|
1187
1230
|
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
1188
|
-
await this.addProposeTx(checkpoint, proposeTxArgs, opts
|
|
1231
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts);
|
|
1189
1232
|
}
|
|
1190
1233
|
|
|
1191
1234
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1228,8 +1271,8 @@ export class SequencerPublisher {
|
|
|
1228
1271
|
request: L1TxRequest,
|
|
1229
1272
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1230
1273
|
slotNumber: SlotNumber,
|
|
1231
|
-
timestamp: bigint,
|
|
1232
1274
|
) {
|
|
1275
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1233
1276
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1234
1277
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1235
1278
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1245,8 +1288,9 @@ export class SequencerPublisher {
|
|
|
1245
1288
|
|
|
1246
1289
|
let gasUsed: bigint;
|
|
1247
1290
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1291
|
+
|
|
1248
1292
|
try {
|
|
1249
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1293
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1250
1294
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1251
1295
|
} catch (err) {
|
|
1252
1296
|
const viemError = formatViemError(err, simulateAbi);
|
|
@@ -1304,6 +1348,7 @@ export class SequencerPublisher {
|
|
|
1304
1348
|
*/
|
|
1305
1349
|
public interrupt() {
|
|
1306
1350
|
this.interrupted = true;
|
|
1351
|
+
this.interruptibleSleep.interrupt();
|
|
1307
1352
|
this.l1TxUtils.interrupt();
|
|
1308
1353
|
}
|
|
1309
1354
|
|
|
@@ -1315,7 +1360,6 @@ export class SequencerPublisher {
|
|
|
1315
1360
|
|
|
1316
1361
|
private async prepareProposeTx(
|
|
1317
1362
|
encodedData: L1ProcessArgs,
|
|
1318
|
-
timestamp: bigint,
|
|
1319
1363
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1320
1364
|
) {
|
|
1321
1365
|
const kzg = Blob.getViemKzgInstance();
|
|
@@ -1388,7 +1432,7 @@ export class SequencerPublisher {
|
|
|
1388
1432
|
blobInput,
|
|
1389
1433
|
] as const;
|
|
1390
1434
|
|
|
1391
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1435
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
|
|
1392
1436
|
|
|
1393
1437
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1394
1438
|
}
|
|
@@ -1396,7 +1440,6 @@ export class SequencerPublisher {
|
|
|
1396
1440
|
/**
|
|
1397
1441
|
* Simulates the propose tx with eth_simulateV1
|
|
1398
1442
|
* @param args - The propose tx args
|
|
1399
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1400
1443
|
* @returns The simulation result
|
|
1401
1444
|
*/
|
|
1402
1445
|
private async simulateProposeTx(
|
|
@@ -1413,8 +1456,10 @@ export class SequencerPublisher {
|
|
|
1413
1456
|
ViemSignature,
|
|
1414
1457
|
`0x${string}`,
|
|
1415
1458
|
],
|
|
1416
|
-
|
|
1417
|
-
|
|
1459
|
+
options: {
|
|
1460
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1461
|
+
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1462
|
+
},
|
|
1418
1463
|
) {
|
|
1419
1464
|
const rollupData = encodeFunctionData({
|
|
1420
1465
|
abi: RollupAbi,
|
|
@@ -1422,13 +1467,23 @@ export class SequencerPublisher {
|
|
|
1422
1467
|
args,
|
|
1423
1468
|
});
|
|
1424
1469
|
|
|
1425
|
-
// override the
|
|
1470
|
+
// override the proposed checkpoint number if requested
|
|
1426
1471
|
const forcePendingCheckpointNumberStateDiff = (
|
|
1427
1472
|
options.forcePendingCheckpointNumber !== undefined
|
|
1428
1473
|
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1429
1474
|
: []
|
|
1430
1475
|
).flatMap(override => override.stateDiff ?? []);
|
|
1431
1476
|
|
|
1477
|
+
// override the fee header for a specific checkpoint number if requested (used when pipelining)
|
|
1478
|
+
const forceProposedFeeHeaderStateDiff = (
|
|
1479
|
+
options.forceProposedFeeHeader !== undefined
|
|
1480
|
+
? await this.rollupContract.makeFeeHeaderOverride(
|
|
1481
|
+
options.forceProposedFeeHeader.checkpointNumber,
|
|
1482
|
+
options.forceProposedFeeHeader.feeHeader,
|
|
1483
|
+
)
|
|
1484
|
+
: []
|
|
1485
|
+
).flatMap(override => override.stateDiff ?? []);
|
|
1486
|
+
|
|
1432
1487
|
const stateOverrides: StateOverride = [
|
|
1433
1488
|
{
|
|
1434
1489
|
address: this.rollupContract.address,
|
|
@@ -1436,6 +1491,7 @@ export class SequencerPublisher {
|
|
|
1436
1491
|
stateDiff: [
|
|
1437
1492
|
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1438
1493
|
...forcePendingCheckpointNumberStateDiff,
|
|
1494
|
+
...forceProposedFeeHeaderStateDiff,
|
|
1439
1495
|
],
|
|
1440
1496
|
},
|
|
1441
1497
|
];
|
|
@@ -1448,6 +1504,7 @@ export class SequencerPublisher {
|
|
|
1448
1504
|
}
|
|
1449
1505
|
|
|
1450
1506
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1507
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1451
1508
|
|
|
1452
1509
|
const simulationResult = await this.l1TxUtils
|
|
1453
1510
|
.simulate(
|
|
@@ -1458,8 +1515,7 @@ export class SequencerPublisher {
|
|
|
1458
1515
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1459
1516
|
},
|
|
1460
1517
|
{
|
|
1461
|
-
|
|
1462
|
-
time: timestamp + 1n,
|
|
1518
|
+
time: simTs,
|
|
1463
1519
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1464
1520
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1465
1521
|
},
|
|
@@ -1481,7 +1537,7 @@ export class SequencerPublisher {
|
|
|
1481
1537
|
logs: [],
|
|
1482
1538
|
};
|
|
1483
1539
|
}
|
|
1484
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1540
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1485
1541
|
this.backupFailedTx({
|
|
1486
1542
|
id: keccak256(rollupData),
|
|
1487
1543
|
failureType: 'simulation',
|
|
@@ -1503,17 +1559,16 @@ export class SequencerPublisher {
|
|
|
1503
1559
|
private async addProposeTx(
|
|
1504
1560
|
checkpoint: Checkpoint,
|
|
1505
1561
|
encodedData: L1ProcessArgs,
|
|
1506
|
-
opts: {
|
|
1507
|
-
|
|
1562
|
+
opts: {
|
|
1563
|
+
txTimeoutAt?: Date;
|
|
1564
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
1565
|
+
forceProposedFeeHeader?: { checkpointNumber: CheckpointNumber; feeHeader: FeeHeader };
|
|
1566
|
+
} = {},
|
|
1508
1567
|
): Promise<void> {
|
|
1509
1568
|
const slot = checkpoint.header.slotNumber;
|
|
1510
1569
|
const timer = new Timer();
|
|
1511
1570
|
const kzg = Blob.getViemKzgInstance();
|
|
1512
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1513
|
-
encodedData,
|
|
1514
|
-
timestamp,
|
|
1515
|
-
opts,
|
|
1516
|
-
);
|
|
1571
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
|
|
1517
1572
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1518
1573
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
1519
1574
|
BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
|
|
@@ -1590,7 +1645,14 @@ export class SequencerPublisher {
|
|
|
1590
1645
|
});
|
|
1591
1646
|
}
|
|
1592
1647
|
|
|
1593
|
-
/** Returns the timestamp
|
|
1648
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1649
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
|
|
1650
|
+
private getSimulationTimestamp(slot: SlotNumber): bigint {
|
|
1651
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1652
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */
|
|
1594
1656
|
private getNextL1SlotTimestamp(): bigint {
|
|
1595
1657
|
const l1Constants = this.epochCache.getL1Constants();
|
|
1596
1658
|
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|