@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
|
@@ -372,12 +372,11 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
|
372
372
|
}
|
|
373
373
|
var _dec, _dec1, _dec2, _initProto;
|
|
374
374
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
375
|
-
import { FeeAssetPriceOracle, MULTI_CALL_3_ADDRESS, Multicall3,
|
|
375
|
+
import { FeeAssetPriceOracle, MULTI_CALL_3_ADDRESS, Multicall3, SimulationOverridesBuilder, buildSimulationOverridesStateOverride } from '@aztec/ethereum/contracts';
|
|
376
376
|
import { L1FeeAnalyzer } from '@aztec/ethereum/l1-fee-analysis';
|
|
377
377
|
import { MAX_L1_TX_LIMIT, WEI_CONST } from '@aztec/ethereum/l1-tx-utils';
|
|
378
378
|
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
379
379
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
380
|
-
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
381
380
|
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
382
381
|
import { trimmedBytesLength } from '@aztec/foundation/buffer';
|
|
383
382
|
import { pick } from '@aztec/foundation/collection';
|
|
@@ -386,12 +385,13 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
386
385
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
387
386
|
import { createLogger } from '@aztec/foundation/log';
|
|
388
387
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
388
|
+
import { InterruptibleSleep } from '@aztec/foundation/sleep';
|
|
389
389
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
390
390
|
import { Timer } from '@aztec/foundation/timer';
|
|
391
391
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
392
392
|
import { encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
393
393
|
import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
394
|
-
import { getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
394
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
395
395
|
import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
396
396
|
import { encodeFunctionData, keccak256, multicall3Abi, toHex } from 'viem';
|
|
397
397
|
import { createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
@@ -401,9 +401,6 @@ export const Actions = [
|
|
|
401
401
|
'invalidate-by-insufficient-attestations',
|
|
402
402
|
'propose',
|
|
403
403
|
'governance-signal',
|
|
404
|
-
'empire-slashing-signal',
|
|
405
|
-
'create-empire-payload',
|
|
406
|
-
'execute-empire-payload',
|
|
407
404
|
'vote-offenses',
|
|
408
405
|
'execute-slash'
|
|
409
406
|
];
|
|
@@ -443,12 +440,13 @@ export class SequencerPublisher {
|
|
|
443
440
|
log;
|
|
444
441
|
ethereumSlotDuration;
|
|
445
442
|
aztecSlotDuration;
|
|
446
|
-
dateProvider;
|
|
443
|
+
/** Date provider for wall-clock time. */ dateProvider;
|
|
447
444
|
blobClient;
|
|
448
445
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */ proposerAddressForSimulation;
|
|
449
446
|
/** Optional callback to obtain a replacement publisher when the current one fails to send. */ getNextPublisher;
|
|
450
447
|
/** L1 fee analyzer for fisherman mode */ l1FeeAnalyzer;
|
|
451
448
|
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */ feeAssetPriceOracle;
|
|
449
|
+
/** Interruptible sleep used by sendRequestsAt to wait until a target timestamp. */ interruptibleSleep;
|
|
452
450
|
// A CALL to a cold address is 2700 gas
|
|
453
451
|
static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
454
452
|
// Gas report for VotingWithSigTest shows a max gas of 100k, but we've seen it cost 700k+ in testnet
|
|
@@ -457,7 +455,6 @@ export class SequencerPublisher {
|
|
|
457
455
|
rollupContract;
|
|
458
456
|
govProposerContract;
|
|
459
457
|
slashingProposerContract;
|
|
460
|
-
slashFactoryContract;
|
|
461
458
|
tracer;
|
|
462
459
|
requests;
|
|
463
460
|
constructor(config, deps){
|
|
@@ -468,6 +465,7 @@ export class SequencerPublisher {
|
|
|
468
465
|
this.lastActions = {};
|
|
469
466
|
this.isPayloadEmptyCache = new Map();
|
|
470
467
|
this.payloadProposedCache = new Set();
|
|
468
|
+
this.interruptibleSleep = new InterruptibleSleep();
|
|
471
469
|
this.requests = [];
|
|
472
470
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
473
471
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
@@ -476,6 +474,7 @@ export class SequencerPublisher {
|
|
|
476
474
|
this.epochCache = deps.epochCache;
|
|
477
475
|
this.lastActions = deps.lastActions;
|
|
478
476
|
this.blobClient = deps.blobClient;
|
|
477
|
+
this.dateProvider = deps.dateProvider;
|
|
479
478
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
480
479
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
481
480
|
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
@@ -489,7 +488,6 @@ export class SequencerPublisher {
|
|
|
489
488
|
const newSlashingProposer = await this.rollupContract.getSlashingProposer();
|
|
490
489
|
this.slashingProposerContract = newSlashingProposer;
|
|
491
490
|
});
|
|
492
|
-
this.slashFactoryContract = deps.slashFactoryContract;
|
|
493
491
|
// Initialize L1 fee analyzer for fisherman mode
|
|
494
492
|
if (config.fishermanMode) {
|
|
495
493
|
this.l1FeeAnalyzer = new L1FeeAnalyzer(this.l1TxUtils.client, deps.dateProvider, createLogger('sequencer:publisher:fee-analyzer'));
|
|
@@ -740,6 +738,41 @@ export class SequencerPublisher {
|
|
|
740
738
|
}
|
|
741
739
|
}
|
|
742
740
|
}
|
|
741
|
+
/*
|
|
742
|
+
* Schedules sending all enqueued requests at (or after) the given timestamp.
|
|
743
|
+
* Uses InterruptibleSleep so it can be cancelled via interrupt().
|
|
744
|
+
* Returns the promise for the L1 response (caller should NOT await this in the work loop).
|
|
745
|
+
*/ async sendRequestsAt(submitAfter) {
|
|
746
|
+
const ms = submitAfter.getTime() - this.dateProvider.now();
|
|
747
|
+
if (ms > 0) {
|
|
748
|
+
this.log.debug(`Sleeping ${ms}ms before sending requests`, {
|
|
749
|
+
submitAfter
|
|
750
|
+
});
|
|
751
|
+
await this.interruptibleSleep.sleep(ms);
|
|
752
|
+
}
|
|
753
|
+
if (this.interrupted) {
|
|
754
|
+
return undefined;
|
|
755
|
+
}
|
|
756
|
+
// Re-validate enqueued requests after the sleep (state may have changed, e.g. prune or L1 reorg)
|
|
757
|
+
const validRequests = [];
|
|
758
|
+
for (const request of this.requests){
|
|
759
|
+
if (!request.preCheck) {
|
|
760
|
+
validRequests.push(request);
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
await request.preCheck();
|
|
765
|
+
validRequests.push(request);
|
|
766
|
+
} catch (err) {
|
|
767
|
+
this.log.warn(`Pre-send validation failed for ${request.action}, discarding request`, err);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
this.requests = validRequests;
|
|
771
|
+
if (this.requests.length === 0) {
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
774
|
+
return this.sendRequests();
|
|
775
|
+
}
|
|
743
776
|
callbackBundledTransactions(requests, result, txContext) {
|
|
744
777
|
const actionsListStr = requests.map((r)=>r.action).join(', ');
|
|
745
778
|
if (result instanceof FormattedViemError) {
|
|
@@ -834,19 +867,17 @@ export class SequencerPublisher {
|
|
|
834
867
|
* @notice Will call `canProposeAt` to make sure that it is possible to propose
|
|
835
868
|
* @param tipArchive - The archive to check
|
|
836
869
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
837
|
-
*/ canProposeAt(tipArchive, msgSender,
|
|
870
|
+
*/ async canProposeAt(tipArchive, msgSender, simulationOverridesPlan) {
|
|
838
871
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
839
872
|
const ignoredErrors = [
|
|
840
873
|
'SlotAlreadyInChain',
|
|
841
874
|
'InvalidProposer',
|
|
842
875
|
'InvalidArchive'
|
|
843
876
|
];
|
|
844
|
-
const pipelined =
|
|
877
|
+
const pipelined = this.epochCache.isProposerPipeliningEnabled();
|
|
845
878
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
846
879
|
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
847
|
-
return this.rollupContract.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
848
|
-
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
849
|
-
}).catch((err)=>{
|
|
880
|
+
return this.rollupContract.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan)).catch((err)=>{
|
|
850
881
|
if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
|
|
851
882
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find((e)=>err.message.includes(e))}`, {
|
|
852
883
|
error: err.message
|
|
@@ -862,7 +893,7 @@ export class SequencerPublisher {
|
|
|
862
893
|
* @dev This is a convenience function that can be used by the sequencer to validate a "partial" header.
|
|
863
894
|
* It will throw if the block header is invalid.
|
|
864
895
|
* @param header - The block header to validate
|
|
865
|
-
*/ async validateBlockHeader(header,
|
|
896
|
+
*/ async validateBlockHeader(header, simulationOverridesPlan) {
|
|
866
897
|
const flags = {
|
|
867
898
|
ignoreDA: true,
|
|
868
899
|
ignoreSignatures: true
|
|
@@ -876,8 +907,8 @@ export class SequencerPublisher {
|
|
|
876
907
|
header.blobsHash.toString(),
|
|
877
908
|
flags
|
|
878
909
|
];
|
|
879
|
-
const ts = this.
|
|
880
|
-
const stateOverrides = await this.rollupContract
|
|
910
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
911
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
881
912
|
let balance = 0n;
|
|
882
913
|
if (this.config.fishermanMode) {
|
|
883
914
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -899,7 +930,7 @@ export class SequencerPublisher {
|
|
|
899
930
|
}),
|
|
900
931
|
from: MULTI_CALL_3_ADDRESS
|
|
901
932
|
}, {
|
|
902
|
-
time: ts
|
|
933
|
+
time: ts
|
|
903
934
|
}, stateOverrides);
|
|
904
935
|
this.log.debug(`Simulated validateHeader`);
|
|
905
936
|
}
|
|
@@ -945,6 +976,7 @@ export class SequencerPublisher {
|
|
|
945
976
|
gasUsed,
|
|
946
977
|
checkpointNumber,
|
|
947
978
|
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
979
|
+
lastArchive: validationResult.checkpoint.lastArchive,
|
|
948
980
|
reason
|
|
949
981
|
};
|
|
950
982
|
} catch (err) {
|
|
@@ -957,8 +989,8 @@ export class SequencerPublisher {
|
|
|
957
989
|
request,
|
|
958
990
|
error: viemError.message
|
|
959
991
|
});
|
|
960
|
-
const
|
|
961
|
-
if (
|
|
992
|
+
const latestProposedCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
993
|
+
if (latestProposedCheckpointNumber < checkpointNumber) {
|
|
962
994
|
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, {
|
|
963
995
|
...logData
|
|
964
996
|
});
|
|
@@ -1018,13 +1050,7 @@ export class SequencerPublisher {
|
|
|
1018
1050
|
throw new Error(`Unknown reason for invalidation`);
|
|
1019
1051
|
}
|
|
1020
1052
|
}
|
|
1021
|
-
/** Simulates `propose` to make sure that the checkpoint is valid for submission */ async validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature,
|
|
1022
|
-
// When pipelining, the checkpoint targets the next slot so its timestamp is in the future.
|
|
1023
|
-
// Without pipelining, the checkpoint targets the current slot so its timestamp is in the past
|
|
1024
|
-
// by the time we simulate (~24s of build time), causing eth_simulateV1 to reject it.
|
|
1025
|
-
// In that case, use the latest L1 block timestamp + one ethereum slot, which is just ahead
|
|
1026
|
-
// of L1 and still within the same L2 slot.
|
|
1027
|
-
const ts = this.epochCache.isProposerPipeliningEnabled() ? checkpoint.header.timestamp : (await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration;
|
|
1053
|
+
/** Simulates `propose` to make sure that the checkpoint is valid for submission */ async validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, simulationOverridesPlan) {
|
|
1028
1054
|
const blobFields = checkpoint.toBlobFields();
|
|
1029
1055
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
1030
1056
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -1041,10 +1067,9 @@ export class SequencerPublisher {
|
|
|
1041
1067
|
attestationsAndSignersSignature.toViemSignature(),
|
|
1042
1068
|
blobInput
|
|
1043
1069
|
];
|
|
1044
|
-
await this.simulateProposeTx(args,
|
|
1045
|
-
return ts;
|
|
1070
|
+
await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
1046
1071
|
}
|
|
1047
|
-
async enqueueCastSignalHelper(slotNumber,
|
|
1072
|
+
async enqueueCastSignalHelper(slotNumber, signalType, payload, base, signerAddress, signer) {
|
|
1048
1073
|
if (this.lastActions[signalType] && this.lastActions[signalType] === slotNumber) {
|
|
1049
1074
|
this.log.debug(`Skipping duplicate vote cast signal ${signalType} for slot ${slotNumber}`);
|
|
1050
1075
|
return false;
|
|
@@ -1101,6 +1126,7 @@ export class SequencerPublisher {
|
|
|
1101
1126
|
lastValidL2Slot: slotNumber
|
|
1102
1127
|
});
|
|
1103
1128
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1129
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1104
1130
|
try {
|
|
1105
1131
|
await this.l1TxUtils.simulate(request, {
|
|
1106
1132
|
time: timestamp
|
|
@@ -1113,7 +1139,10 @@ export class SequencerPublisher {
|
|
|
1113
1139
|
});
|
|
1114
1140
|
} catch (err) {
|
|
1115
1141
|
const viemError = formatViemError(err);
|
|
1116
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
1142
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
1143
|
+
simulationTimestamp: timestamp,
|
|
1144
|
+
l1BlockNumber
|
|
1145
|
+
});
|
|
1117
1146
|
this.backupFailedTx({
|
|
1118
1147
|
id: keccak256(request.data),
|
|
1119
1148
|
failureType: 'simulation',
|
|
@@ -1178,55 +1207,17 @@ export class SequencerPublisher {
|
|
|
1178
1207
|
/**
|
|
1179
1208
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
1180
1209
|
* @param slotNumber - The slot number to cast a signal for.
|
|
1181
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
1182
1210
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
1183
|
-
*/ enqueueGovernanceCastSignal(governancePayload, slotNumber,
|
|
1184
|
-
return this.enqueueCastSignalHelper(slotNumber,
|
|
1211
|
+
*/ enqueueGovernanceCastSignal(governancePayload, slotNumber, signerAddress, signer) {
|
|
1212
|
+
return this.enqueueCastSignalHelper(slotNumber, 'governance-signal', governancePayload, this.govProposerContract, signerAddress, signer);
|
|
1185
1213
|
}
|
|
1186
|
-
/** Enqueues all slashing actions as returned by the slasher client. */ async enqueueSlashingActions(actions, slotNumber,
|
|
1214
|
+
/** Enqueues all slashing actions as returned by the slasher client. */ async enqueueSlashingActions(actions, slotNumber, signerAddress, signer) {
|
|
1187
1215
|
if (actions.length === 0) {
|
|
1188
1216
|
this.log.debug(`No slashing actions to enqueue for slot ${slotNumber}`);
|
|
1189
1217
|
return false;
|
|
1190
1218
|
}
|
|
1191
1219
|
for (const action of actions){
|
|
1192
1220
|
switch(action.type){
|
|
1193
|
-
case 'vote-empire-payload':
|
|
1194
|
-
{
|
|
1195
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1196
|
-
this.log.error('Cannot vote for empire payload on non-empire slashing contract');
|
|
1197
|
-
break;
|
|
1198
|
-
}
|
|
1199
|
-
this.log.debug(`Enqueuing slashing vote for payload ${action.payload} at slot ${slotNumber}`, {
|
|
1200
|
-
signerAddress
|
|
1201
|
-
});
|
|
1202
|
-
await this.enqueueCastSignalHelper(slotNumber, timestamp, 'empire-slashing-signal', action.payload, this.slashingProposerContract, signerAddress, signer);
|
|
1203
|
-
break;
|
|
1204
|
-
}
|
|
1205
|
-
case 'create-empire-payload':
|
|
1206
|
-
{
|
|
1207
|
-
this.log.debug(`Enqueuing slashing create payload at slot ${slotNumber}`, {
|
|
1208
|
-
slotNumber,
|
|
1209
|
-
signerAddress
|
|
1210
|
-
});
|
|
1211
|
-
const request = this.slashFactoryContract.buildCreatePayloadRequest(action.data);
|
|
1212
|
-
await this.simulateAndEnqueueRequest('create-empire-payload', request, (receipt)=>!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs), slotNumber, timestamp);
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
case 'execute-empire-payload':
|
|
1216
|
-
{
|
|
1217
|
-
this.log.debug(`Enqueuing slashing execute payload at slot ${slotNumber}`, {
|
|
1218
|
-
slotNumber,
|
|
1219
|
-
signerAddress
|
|
1220
|
-
});
|
|
1221
|
-
if (this.slashingProposerContract?.type !== 'empire') {
|
|
1222
|
-
this.log.error('Cannot execute slashing payload on non-empire slashing contract');
|
|
1223
|
-
return false;
|
|
1224
|
-
}
|
|
1225
|
-
const empireSlashingProposer = this.slashingProposerContract;
|
|
1226
|
-
const request = empireSlashingProposer.buildExecuteRoundRequest(action.round);
|
|
1227
|
-
await this.simulateAndEnqueueRequest('execute-empire-payload', request, (receipt)=>!!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs), slotNumber, timestamp);
|
|
1228
|
-
break;
|
|
1229
|
-
}
|
|
1230
1221
|
case 'vote-offenses':
|
|
1231
1222
|
{
|
|
1232
1223
|
this.log.debug(`Enqueuing slashing vote for ${action.votes.length} votes at slot ${slotNumber}`, {
|
|
@@ -1235,14 +1226,13 @@ export class SequencerPublisher {
|
|
|
1235
1226
|
votesCount: action.votes.length,
|
|
1236
1227
|
signerAddress
|
|
1237
1228
|
});
|
|
1238
|
-
if (this.slashingProposerContract
|
|
1239
|
-
this.log.error('
|
|
1229
|
+
if (!this.slashingProposerContract) {
|
|
1230
|
+
this.log.error('No slashing proposer contract available');
|
|
1240
1231
|
return false;
|
|
1241
1232
|
}
|
|
1242
|
-
const tallySlashingProposer = this.slashingProposerContract;
|
|
1243
1233
|
const votes = bufferToHex(encodeSlashConsensusVotes(action.votes));
|
|
1244
|
-
const request = await
|
|
1245
|
-
await this.simulateAndEnqueueRequest('vote-offenses', request, (receipt)=>!!
|
|
1234
|
+
const request = await this.slashingProposerContract.buildVoteRequestFromSigner(votes, slotNumber, signer);
|
|
1235
|
+
await this.simulateAndEnqueueRequest('vote-offenses', request, (receipt)=>!!this.slashingProposerContract.tryExtractVoteCastEvent(receipt.logs), slotNumber);
|
|
1246
1236
|
break;
|
|
1247
1237
|
}
|
|
1248
1238
|
case 'execute-slash':
|
|
@@ -1252,13 +1242,12 @@ export class SequencerPublisher {
|
|
|
1252
1242
|
round: action.round,
|
|
1253
1243
|
signerAddress
|
|
1254
1244
|
});
|
|
1255
|
-
if (this.slashingProposerContract
|
|
1256
|
-
this.log.error('
|
|
1245
|
+
if (!this.slashingProposerContract) {
|
|
1246
|
+
this.log.error('No slashing proposer contract available');
|
|
1257
1247
|
return false;
|
|
1258
1248
|
}
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
await this.simulateAndEnqueueRequest('execute-slash', request, (receipt)=>!!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs), slotNumber, timestamp);
|
|
1249
|
+
const executeRequest = this.slashingProposerContract.buildExecuteRoundRequest(action.round, action.committees);
|
|
1250
|
+
await this.simulateAndEnqueueRequest('execute-slash', executeRequest, (receipt)=>!!this.slashingProposerContract.tryExtractRoundExecutedEvent(receipt.logs), slotNumber);
|
|
1262
1251
|
break;
|
|
1263
1252
|
}
|
|
1264
1253
|
default:
|
|
@@ -1282,27 +1271,40 @@ export class SequencerPublisher {
|
|
|
1282
1271
|
attestationsAndSignersSignature,
|
|
1283
1272
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier
|
|
1284
1273
|
};
|
|
1285
|
-
|
|
1274
|
+
const simulationOverridesPlan = SimulationOverridesBuilder.from(opts.simulationOverridesPlan).withoutBlobCheck().build();
|
|
1286
1275
|
try {
|
|
1287
1276
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1288
1277
|
// This means that we can avoid the simulation issues in later checks.
|
|
1289
1278
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1290
1279
|
// make time consistency checks break.
|
|
1291
1280
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1292
|
-
|
|
1281
|
+
await this.validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, simulationOverridesPlan);
|
|
1293
1282
|
} catch (err) {
|
|
1294
1283
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1295
1284
|
...checkpoint.getStats(),
|
|
1296
1285
|
slotNumber: checkpoint.header.slotNumber,
|
|
1297
|
-
|
|
1286
|
+
simulationOverridesPlan
|
|
1298
1287
|
});
|
|
1299
1288
|
throw err;
|
|
1300
1289
|
}
|
|
1290
|
+
// Build a pre-check callback that re-validates the checkpoint before L1 submission.
|
|
1291
|
+
// During pipelining this catches stale proposals due to prunes or L1 reorgs that occur during the pipeline sleep.
|
|
1292
|
+
let preCheck = undefined;
|
|
1293
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
1294
|
+
preCheck = async ()=>{
|
|
1295
|
+
this.log.debug(`Re-validating checkpoint ${checkpoint.number} before L1 submission`);
|
|
1296
|
+
await this.validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, simulationOverridesPlan);
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1301
1299
|
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1302
1300
|
...checkpoint.toCheckpointInfo(),
|
|
1303
|
-
|
|
1301
|
+
txTimeoutAt: opts.txTimeoutAt,
|
|
1302
|
+
simulationOverridesPlan
|
|
1304
1303
|
});
|
|
1305
|
-
await this.addProposeTx(checkpoint, proposeTxArgs,
|
|
1304
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, {
|
|
1305
|
+
txTimeoutAt: opts.txTimeoutAt,
|
|
1306
|
+
simulationOverridesPlan
|
|
1307
|
+
}, preCheck);
|
|
1306
1308
|
}
|
|
1307
1309
|
enqueueInvalidateCheckpoint(request, opts = {}) {
|
|
1308
1310
|
if (!request) {
|
|
@@ -1343,7 +1345,8 @@ export class SequencerPublisher {
|
|
|
1343
1345
|
}
|
|
1344
1346
|
});
|
|
1345
1347
|
}
|
|
1346
|
-
async simulateAndEnqueueRequest(action, request, checkSuccess, slotNumber
|
|
1348
|
+
async simulateAndEnqueueRequest(action, request, checkSuccess, slotNumber) {
|
|
1349
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1347
1350
|
const logData = {
|
|
1348
1351
|
slotNumber,
|
|
1349
1352
|
timestamp,
|
|
@@ -1365,7 +1368,7 @@ export class SequencerPublisher {
|
|
|
1365
1368
|
try {
|
|
1366
1369
|
({ gasUsed } = await this.l1TxUtils.simulate(request, {
|
|
1367
1370
|
time: timestamp
|
|
1368
|
-
}, [], simulateAbi));
|
|
1371
|
+
}, [], simulateAbi));
|
|
1369
1372
|
this.log.verbose(`Simulation for ${action} succeeded`, {
|
|
1370
1373
|
...logData,
|
|
1371
1374
|
request,
|
|
@@ -1440,13 +1443,14 @@ export class SequencerPublisher {
|
|
|
1440
1443
|
* A call to `restart` is required before you can continue publishing.
|
|
1441
1444
|
*/ interrupt() {
|
|
1442
1445
|
this.interrupted = true;
|
|
1446
|
+
this.interruptibleSleep.interrupt();
|
|
1443
1447
|
this.l1TxUtils.interrupt();
|
|
1444
1448
|
}
|
|
1445
1449
|
/** Restarts the publisher after calling `interrupt`. */ restart() {
|
|
1446
1450
|
this.interrupted = false;
|
|
1447
1451
|
this.l1TxUtils.restart();
|
|
1448
1452
|
}
|
|
1449
|
-
async prepareProposeTx(encodedData,
|
|
1453
|
+
async prepareProposeTx(encodedData, simulationOverridesPlan) {
|
|
1450
1454
|
const kzg = Blob.getViemKzgInstance();
|
|
1451
1455
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
1452
1456
|
this.log.debug('Validating blob input', {
|
|
@@ -1523,7 +1527,7 @@ export class SequencerPublisher {
|
|
|
1523
1527
|
encodedData.attestationsAndSignersSignature.toViemSignature(),
|
|
1524
1528
|
blobInput
|
|
1525
1529
|
];
|
|
1526
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1530
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, simulationOverridesPlan);
|
|
1527
1531
|
return {
|
|
1528
1532
|
args,
|
|
1529
1533
|
blobEvaluationGas,
|
|
@@ -1534,29 +1538,14 @@ export class SequencerPublisher {
|
|
|
1534
1538
|
/**
|
|
1535
1539
|
* Simulates the propose tx with eth_simulateV1
|
|
1536
1540
|
* @param args - The propose tx args
|
|
1537
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1538
1541
|
* @returns The simulation result
|
|
1539
|
-
*/ async simulateProposeTx(args,
|
|
1542
|
+
*/ async simulateProposeTx(args, simulationOverridesPlan) {
|
|
1540
1543
|
const rollupData = encodeFunctionData({
|
|
1541
1544
|
abi: RollupAbi,
|
|
1542
1545
|
functionName: 'propose',
|
|
1543
1546
|
args
|
|
1544
1547
|
});
|
|
1545
|
-
|
|
1546
|
-
const forcePendingCheckpointNumberStateDiff = (options.forcePendingCheckpointNumber !== undefined ? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber) : []).flatMap((override)=>override.stateDiff ?? []);
|
|
1547
|
-
const stateOverrides = [
|
|
1548
|
-
{
|
|
1549
|
-
address: this.rollupContract.address,
|
|
1550
|
-
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1551
|
-
stateDiff: [
|
|
1552
|
-
{
|
|
1553
|
-
slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true),
|
|
1554
|
-
value: toPaddedHex(0n, true)
|
|
1555
|
-
},
|
|
1556
|
-
...forcePendingCheckpointNumberStateDiff
|
|
1557
|
-
]
|
|
1558
|
-
}
|
|
1559
|
-
];
|
|
1548
|
+
const stateOverrides = await buildSimulationOverridesStateOverride(this.rollupContract, simulationOverridesPlan);
|
|
1560
1549
|
// In fisherman mode, simulate as the proposer but with sufficient balance
|
|
1561
1550
|
if (this.proposerAddressForSimulation) {
|
|
1562
1551
|
stateOverrides.push({
|
|
@@ -1565,6 +1554,7 @@ export class SequencerPublisher {
|
|
|
1565
1554
|
});
|
|
1566
1555
|
}
|
|
1567
1556
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1557
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1568
1558
|
const simulationResult = await this.l1TxUtils.simulate({
|
|
1569
1559
|
to: this.rollupContract.address,
|
|
1570
1560
|
data: rollupData,
|
|
@@ -1573,8 +1563,7 @@ export class SequencerPublisher {
|
|
|
1573
1563
|
from: this.proposerAddressForSimulation.toString()
|
|
1574
1564
|
}
|
|
1575
1565
|
}, {
|
|
1576
|
-
|
|
1577
|
-
time: timestamp + 1n,
|
|
1566
|
+
time: simTs,
|
|
1578
1567
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1579
1568
|
gasLimit: MAX_L1_TX_LIMIT * 2n
|
|
1580
1569
|
}, stateOverrides, RollupAbi, {
|
|
@@ -1591,7 +1580,9 @@ export class SequencerPublisher {
|
|
|
1591
1580
|
logs: []
|
|
1592
1581
|
};
|
|
1593
1582
|
}
|
|
1594
|
-
this.log.error(`Failed to simulate propose tx`, viemError
|
|
1583
|
+
this.log.error(`Failed to simulate propose tx`, viemError, {
|
|
1584
|
+
simulationTimestamp: simTs
|
|
1585
|
+
});
|
|
1595
1586
|
this.backupFailedTx({
|
|
1596
1587
|
id: keccak256(rollupData),
|
|
1597
1588
|
failureType: 'simulation',
|
|
@@ -1619,11 +1610,11 @@ export class SequencerPublisher {
|
|
|
1619
1610
|
simulationResult
|
|
1620
1611
|
};
|
|
1621
1612
|
}
|
|
1622
|
-
async addProposeTx(checkpoint, encodedData, opts = {},
|
|
1613
|
+
async addProposeTx(checkpoint, encodedData, opts = {}, preCheck) {
|
|
1623
1614
|
const slot = checkpoint.header.slotNumber;
|
|
1624
1615
|
const timer = new Timer();
|
|
1625
1616
|
const kzg = Blob.getViemKzgInstance();
|
|
1626
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData,
|
|
1617
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts.simulationOverridesPlan);
|
|
1627
1618
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1628
1619
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(simulationResult.gasUsed) * 64 / 63)) + blobEvaluationGas + SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS);
|
|
1629
1620
|
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
@@ -1639,9 +1630,10 @@ export class SequencerPublisher {
|
|
|
1639
1630
|
},
|
|
1640
1631
|
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1641
1632
|
gasConfig: {
|
|
1642
|
-
|
|
1633
|
+
txTimeoutAt: opts.txTimeoutAt,
|
|
1643
1634
|
gasLimit
|
|
1644
1635
|
},
|
|
1636
|
+
preCheck,
|
|
1645
1637
|
blobConfig: {
|
|
1646
1638
|
blobs: encodedData.blobs.map((b)=>b.data),
|
|
1647
1639
|
kzg
|
|
@@ -1688,7 +1680,12 @@ export class SequencerPublisher {
|
|
|
1688
1680
|
}
|
|
1689
1681
|
});
|
|
1690
1682
|
}
|
|
1691
|
-
/** Returns the timestamp
|
|
1683
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1684
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */ getSimulationTimestamp(slot) {
|
|
1685
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1686
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1687
|
+
}
|
|
1688
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */ getNextL1SlotTimestamp() {
|
|
1692
1689
|
const l1Constants = this.epochCache.getL1Constants();
|
|
1693
1690
|
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|
|
1694
1691
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RollupContract, type SimulationOverridesPlan } from '@aztec/ethereum/contracts';
|
|
2
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
5
|
+
import type { ProposedCheckpointData } from '@aztec/stdlib/checkpoint';
|
|
6
|
+
type PipelinedParentSimulationOverridesPlanInput = {
|
|
7
|
+
checkpointNumber: CheckpointNumber;
|
|
8
|
+
proposedCheckpointData?: ProposedCheckpointData;
|
|
9
|
+
rollup: RollupContract;
|
|
10
|
+
log: Logger;
|
|
11
|
+
};
|
|
12
|
+
type SubmissionSimulationOverridesPlanInput = {
|
|
13
|
+
pipelinedParentPlan?: SimulationOverridesPlan;
|
|
14
|
+
invalidateToPendingCheckpointNumber?: CheckpointNumber;
|
|
15
|
+
lastArchiveRoot: Fr;
|
|
16
|
+
pipeliningEnabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** Builds the simulated parent checkpoint view used while constructing a pipelined proposal. */
|
|
19
|
+
export declare function buildPipelinedParentSimulationOverridesPlan(input: PipelinedParentSimulationOverridesPlanInput): Promise<SimulationOverridesPlan | undefined>;
|
|
20
|
+
/** Builds the simulated chain view used when validating and enqueueing checkpoint submission. */
|
|
21
|
+
export declare function buildSubmissionSimulationOverridesPlan(input: SubmissionSimulationOverridesPlanInput): SimulationOverridesPlan | undefined;
|
|
22
|
+
/** Derives the pending parent fee header used during pipelined proposal simulation. */
|
|
23
|
+
export declare function computePipelinedParentFeeHeader(input: PipelinedParentSimulationOverridesPlanInput): Promise<import("@aztec/ethereum/contracts").FeeHeader | undefined>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhaW5fc3RhdGVfb3ZlcnJpZGVzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL2NoYWluX3N0YXRlX292ZXJyaWRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUE4QixLQUFLLHVCQUF1QixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDckgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxLQUFLLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDekQsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUV2RSxLQUFLLDJDQUEyQyxHQUFHO0lBQ2pELGdCQUFnQixFQUFFLGdCQUFnQixDQUFDO0lBQ25DLHNCQUFzQixDQUFDLEVBQUUsc0JBQXNCLENBQUM7SUFDaEQsTUFBTSxFQUFFLGNBQWMsQ0FBQztJQUN2QixHQUFHLEVBQUUsTUFBTSxDQUFDO0NBQ2IsQ0FBQztBQUVGLEtBQUssc0NBQXNDLEdBQUc7SUFDNUMsbUJBQW1CLENBQUMsRUFBRSx1QkFBdUIsQ0FBQztJQUM5QyxtQ0FBbUMsQ0FBQyxFQUFFLGdCQUFnQixDQUFDO0lBQ3ZELGVBQWUsRUFBRSxFQUFFLENBQUM7SUFDcEIsaUJBQWlCLEVBQUUsT0FBTyxDQUFDO0NBQzVCLENBQUM7QUFFRixnR0FBZ0c7QUFDaEcsd0JBQXNCLDJDQUEyQyxDQUMvRCxLQUFLLEVBQUUsMkNBQTJDLEdBQ2pELE9BQU8sQ0FBQyx1QkFBdUIsR0FBRyxTQUFTLENBQUMsQ0FVOUM7QUFFRCxpR0FBaUc7QUFDakcsd0JBQWdCLHNDQUFzQyxDQUNwRCxLQUFLLEVBQUUsc0NBQXNDLEdBQzVDLHVCQUF1QixHQUFHLFNBQVMsQ0FhckM7QUFFRCx1RkFBdUY7QUFDdkYsd0JBQXNCLCtCQUErQixDQUFDLEtBQUssRUFBRSwyQ0FBMkMsc0VBZ0N2RyJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain_state_overrides.d.ts","sourceRoot":"","sources":["../../src/sequencer/chain_state_overrides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA8B,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACrH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE,KAAK,2CAA2C,GAAG;IACjD,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,MAAM,EAAE,cAAc,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,sCAAsC,GAAG;IAC5C,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C,mCAAmC,CAAC,EAAE,gBAAgB,CAAC;IACvD,eAAe,EAAE,EAAE,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,gGAAgG;AAChG,wBAAsB,2CAA2C,CAC/D,KAAK,EAAE,2CAA2C,GACjD,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAU9C;AAED,iGAAiG;AACjG,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,sCAAsC,GAC5C,uBAAuB,GAAG,SAAS,CAarC;AAED,uFAAuF;AACvF,wBAAsB,+BAA+B,CAAC,KAAK,EAAE,2CAA2C,sEAgCvG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RollupContract, SimulationOverridesBuilder } from '@aztec/ethereum/contracts';
|
|
2
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
/** Builds the simulated parent checkpoint view used while constructing a pipelined proposal. */ export async function buildPipelinedParentSimulationOverridesPlan(input) {
|
|
4
|
+
const parentCheckpointNumber = CheckpointNumber(input.checkpointNumber - 1);
|
|
5
|
+
const builder = new SimulationOverridesBuilder().forPendingCheckpoint(parentCheckpointNumber);
|
|
6
|
+
const pendingFeeHeader = await computePipelinedParentFeeHeader(input);
|
|
7
|
+
if (pendingFeeHeader) {
|
|
8
|
+
builder.withPendingFeeHeader(pendingFeeHeader);
|
|
9
|
+
}
|
|
10
|
+
return builder.build();
|
|
11
|
+
}
|
|
12
|
+
/** Builds the simulated chain view used when validating and enqueueing checkpoint submission. */ export function buildSubmissionSimulationOverridesPlan(input) {
|
|
13
|
+
const pendingCheckpointNumber = input.invalidateToPendingCheckpointNumber ?? input.pipelinedParentPlan?.pendingCheckpointNumber;
|
|
14
|
+
const builder = SimulationOverridesBuilder.from(input.pipelinedParentPlan).forPendingCheckpoint(pendingCheckpointNumber);
|
|
15
|
+
if (input.pipeliningEnabled && pendingCheckpointNumber !== undefined) {
|
|
16
|
+
builder.withPendingArchive(input.lastArchiveRoot);
|
|
17
|
+
}
|
|
18
|
+
return builder.build();
|
|
19
|
+
}
|
|
20
|
+
/** Derives the pending parent fee header used during pipelined proposal simulation. */ export async function computePipelinedParentFeeHeader(input) {
|
|
21
|
+
if (!input.proposedCheckpointData || input.checkpointNumber < 2) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const grandparentCheckpointNumber = CheckpointNumber(input.checkpointNumber - 2);
|
|
25
|
+
try {
|
|
26
|
+
const [grandparentCheckpoint, manaTarget] = await Promise.all([
|
|
27
|
+
input.rollup.getCheckpoint(grandparentCheckpointNumber),
|
|
28
|
+
input.rollup.getManaTarget()
|
|
29
|
+
]);
|
|
30
|
+
if (!grandparentCheckpoint?.feeHeader) {
|
|
31
|
+
input.log.error(`Grandparent checkpoint or feeHeader missing for checkpoint ${grandparentCheckpointNumber.toString()}`);
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return RollupContract.computeChildFeeHeader(grandparentCheckpoint.feeHeader, input.proposedCheckpointData.totalManaUsed, input.proposedCheckpointData.feeAssetPriceModifier, manaTarget);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
input.log.error(`Failed to derive pipelined parent fee header for checkpoint ${grandparentCheckpointNumber.toString()}: ${err}`);
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|