@aztec/sequencer-client 0.0.1-commit.ffe5b04ea → 0.0.1-commit.fff30aa
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 +5 -12
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +22 -72
- package/dest/config.d.ts +2 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +7 -1
- package/dest/global_variable_builder/global_builder.d.ts +13 -7
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +22 -21
- package/dest/global_variable_builder/index.d.ts +2 -2
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/publisher/config.d.ts +1 -5
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +1 -6
- package/dest/publisher/index.d.ts +1 -2
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +0 -14
- package/dest/publisher/sequencer-publisher.d.ts +15 -18
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +70 -290
- package/dest/sequencer/checkpoint_proposal_job.d.ts +3 -1
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +66 -63
- 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 +6 -3
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +17 -16
- package/dest/sequencer/types.d.ts +2 -5
- package/dest/sequencer/types.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +4 -4
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +2 -0
- package/package.json +28 -28
- package/src/client/sequencer-client.ts +34 -94
- package/src/config.ts +8 -1
- package/src/global_variable_builder/global_builder.ts +22 -23
- package/src/global_variable_builder/index.ts +1 -1
- package/src/publisher/config.ts +0 -9
- package/src/publisher/index.ts +0 -3
- package/src/publisher/sequencer-publisher-factory.ts +0 -15
- package/src/publisher/sequencer-publisher.ts +71 -276
- package/src/sequencer/checkpoint_proposal_job.ts +93 -77
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/sequencer.ts +20 -17
- package/src/sequencer/types.ts +2 -5
- package/src/test/mock_checkpoint_builder.ts +5 -3
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +0 -11
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/factory.js +0 -22
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +0 -59
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +0 -1
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +0 -15
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +0 -34
- package/dest/publisher/l1_tx_failed_store/index.d.ts +0 -4
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +0 -1
- package/dest/publisher/l1_tx_failed_store/index.js +0 -2
- package/src/publisher/l1_tx_failed_store/factory.ts +0 -32
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +0 -55
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +0 -46
- package/src/publisher/l1_tx_failed_store/index.ts +0 -3
|
@@ -379,8 +379,8 @@ import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from
|
|
|
379
379
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
380
380
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
381
381
|
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
382
|
+
import { trimmedBytesLength } from '@aztec/foundation/buffer';
|
|
382
383
|
import { pick } from '@aztec/foundation/collection';
|
|
383
|
-
import { TimeoutError } from '@aztec/foundation/error';
|
|
384
384
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
385
385
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
386
386
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -390,9 +390,9 @@ import { Timer } from '@aztec/foundation/timer';
|
|
|
390
390
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
391
391
|
import { encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
392
392
|
import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
393
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
393
394
|
import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
394
|
-
import { encodeFunctionData,
|
|
395
|
-
import { createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
395
|
+
import { encodeFunctionData, toHex } from 'viem';
|
|
396
396
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
397
397
|
export const Actions = [
|
|
398
398
|
'invalidate-by-invalid-attestation',
|
|
@@ -432,7 +432,6 @@ export class SequencerPublisher {
|
|
|
432
432
|
interrupted;
|
|
433
433
|
metrics;
|
|
434
434
|
epochCache;
|
|
435
|
-
failedTxStore;
|
|
436
435
|
governanceLog;
|
|
437
436
|
slashingLog;
|
|
438
437
|
lastActions;
|
|
@@ -440,9 +439,10 @@ export class SequencerPublisher {
|
|
|
440
439
|
payloadProposedCache;
|
|
441
440
|
log;
|
|
442
441
|
ethereumSlotDuration;
|
|
442
|
+
aztecSlotDuration;
|
|
443
|
+
dateProvider;
|
|
443
444
|
blobClient;
|
|
444
445
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */ proposerAddressForSimulation;
|
|
445
|
-
/** Optional callback to obtain a replacement publisher when the current one fails to send. */ getNextPublisher;
|
|
446
446
|
/** L1 fee analyzer for fisherman mode */ l1FeeAnalyzer;
|
|
447
447
|
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */ feeAssetPriceOracle;
|
|
448
448
|
// A CALL to a cold address is 2700 gas
|
|
@@ -467,6 +467,8 @@ export class SequencerPublisher {
|
|
|
467
467
|
this.requests = [];
|
|
468
468
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
469
469
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
470
|
+
this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
|
|
471
|
+
this.dateProvider = deps.dateProvider;
|
|
470
472
|
this.epochCache = deps.epochCache;
|
|
471
473
|
this.lastActions = deps.lastActions;
|
|
472
474
|
this.blobClient = deps.blobClient;
|
|
@@ -474,7 +476,6 @@ export class SequencerPublisher {
|
|
|
474
476
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
475
477
|
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
476
478
|
this.l1TxUtils = deps.l1TxUtils;
|
|
477
|
-
this.getNextPublisher = deps.getNextPublisher;
|
|
478
479
|
this.rollupContract = deps.rollupContract;
|
|
479
480
|
this.govProposerContract = deps.governanceProposerContract;
|
|
480
481
|
this.slashingProposerContract = deps.slashingProposerContract;
|
|
@@ -490,24 +491,6 @@ export class SequencerPublisher {
|
|
|
490
491
|
}
|
|
491
492
|
// Initialize fee asset price oracle
|
|
492
493
|
this.feeAssetPriceOracle = new FeeAssetPriceOracle(this.l1TxUtils.client, this.rollupContract, createLogger('sequencer:publisher:price-oracle'));
|
|
493
|
-
// Initialize failed L1 tx store (optional, for test networks)
|
|
494
|
-
this.failedTxStore = createL1TxFailedStore(config.l1TxFailedStore, this.log);
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Backs up a failed L1 transaction to the configured store for debugging.
|
|
498
|
-
* Does nothing if no store is configured.
|
|
499
|
-
*/ backupFailedTx(failedTx) {
|
|
500
|
-
if (!this.failedTxStore) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
const tx = {
|
|
504
|
-
...failedTx,
|
|
505
|
-
timestamp: Date.now()
|
|
506
|
-
};
|
|
507
|
-
// Fire and forget - don't block on backup
|
|
508
|
-
void this.failedTxStore.then((store)=>store?.saveFailedTx(tx)).catch((err)=>{
|
|
509
|
-
this.log.warn(`Failed to backup failed L1 tx to store`, err);
|
|
510
|
-
});
|
|
511
494
|
}
|
|
512
495
|
getRollupContract() {
|
|
513
496
|
return this.rollupContract;
|
|
@@ -625,8 +608,8 @@ export class SequencerPublisher {
|
|
|
625
608
|
// @note - we can only have one blob config per bundle
|
|
626
609
|
// find requests with gas and blob configs
|
|
627
610
|
// See https://github.com/AztecProtocol/aztec-packages/issues/11513
|
|
628
|
-
const gasConfigs =
|
|
629
|
-
const blobConfigs =
|
|
611
|
+
const gasConfigs = validRequests.filter((request)=>request.gasConfig).map((request)=>request.gasConfig);
|
|
612
|
+
const blobConfigs = validRequests.filter((request)=>request.blobConfig).map((request)=>request.blobConfig);
|
|
630
613
|
if (blobConfigs.length > 1) {
|
|
631
614
|
throw new Error('Multiple blob configs found');
|
|
632
615
|
}
|
|
@@ -653,34 +636,12 @@ export class SequencerPublisher {
|
|
|
653
636
|
// This ensures the committee gets precomputed correctly
|
|
654
637
|
validRequests.sort((a, b)=>compareActions(a.action, b.action));
|
|
655
638
|
try {
|
|
656
|
-
// Capture context for failed tx backup before sending
|
|
657
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
658
|
-
const multicallData = encodeFunctionData({
|
|
659
|
-
abi: multicall3Abi,
|
|
660
|
-
functionName: 'aggregate3',
|
|
661
|
-
args: [
|
|
662
|
-
validRequests.map((r)=>({
|
|
663
|
-
target: r.request.to,
|
|
664
|
-
callData: r.request.data,
|
|
665
|
-
allowFailure: true
|
|
666
|
-
}))
|
|
667
|
-
]
|
|
668
|
-
});
|
|
669
|
-
const blobDataHex = blobConfig?.blobs?.map((b)=>toHex(b));
|
|
670
|
-
const txContext = {
|
|
671
|
-
multicallData,
|
|
672
|
-
blobData: blobDataHex,
|
|
673
|
-
l1BlockNumber
|
|
674
|
-
};
|
|
675
639
|
this.log.debug('Forwarding transactions', {
|
|
676
640
|
validRequests: validRequests.map((request)=>request.action),
|
|
677
641
|
txConfig
|
|
678
642
|
});
|
|
679
|
-
const result = await
|
|
680
|
-
|
|
681
|
-
return undefined;
|
|
682
|
-
}
|
|
683
|
-
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result, txContext);
|
|
643
|
+
const result = await Multicall3.forward(validRequests.map((request)=>request.request), this.l1TxUtils, txConfig, blobConfig, this.rollupContract.address, this.log);
|
|
644
|
+
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result);
|
|
684
645
|
return {
|
|
685
646
|
result,
|
|
686
647
|
expiredActions,
|
|
@@ -700,74 +661,26 @@ export class SequencerPublisher {
|
|
|
700
661
|
}
|
|
701
662
|
}
|
|
702
663
|
}
|
|
703
|
-
|
|
704
|
-
* Forwards transactions via Multicall3, rotating to the next available publisher if a send
|
|
705
|
-
* failure occurs (i.e. the tx never reached the chain).
|
|
706
|
-
* On-chain reverts and simulation errors are returned as-is without rotation.
|
|
707
|
-
*/ async forwardWithPublisherRotation(validRequests, txConfig, blobConfig) {
|
|
708
|
-
const triedAddresses = [];
|
|
709
|
-
let currentPublisher = this.l1TxUtils;
|
|
710
|
-
while(true){
|
|
711
|
-
triedAddresses.push(currentPublisher.getSenderAddress());
|
|
712
|
-
try {
|
|
713
|
-
const result = await Multicall3.forward(validRequests.map((r)=>r.request), currentPublisher, txConfig, blobConfig, this.rollupContract.address, this.log);
|
|
714
|
-
this.l1TxUtils = currentPublisher;
|
|
715
|
-
return result;
|
|
716
|
-
} catch (err) {
|
|
717
|
-
if (err instanceof TimeoutError) {
|
|
718
|
-
throw err;
|
|
719
|
-
}
|
|
720
|
-
const viemError = formatViemError(err);
|
|
721
|
-
if (!this.getNextPublisher) {
|
|
722
|
-
this.log.error('Failed to publish bundled transactions', viemError);
|
|
723
|
-
return undefined;
|
|
724
|
-
}
|
|
725
|
-
this.log.warn(`Publisher ${currentPublisher.getSenderAddress()} failed to send, rotating to next publisher`, viemError);
|
|
726
|
-
const nextPublisher = await this.getNextPublisher([
|
|
727
|
-
...triedAddresses
|
|
728
|
-
]);
|
|
729
|
-
if (!nextPublisher) {
|
|
730
|
-
this.log.error('All available publishers exhausted, failed to publish bundled transactions');
|
|
731
|
-
return undefined;
|
|
732
|
-
}
|
|
733
|
-
currentPublisher = nextPublisher;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
callbackBundledTransactions(requests, result, txContext) {
|
|
664
|
+
callbackBundledTransactions(requests, result) {
|
|
738
665
|
const actionsListStr = requests.map((r)=>r.action).join(', ');
|
|
739
666
|
if (result instanceof FormattedViemError) {
|
|
740
667
|
this.log.error(`Failed to publish bundled transactions (${actionsListStr})`, result);
|
|
741
|
-
this.backupFailedTx({
|
|
742
|
-
id: keccak256(txContext.multicallData),
|
|
743
|
-
failureType: 'send-error',
|
|
744
|
-
request: {
|
|
745
|
-
to: MULTI_CALL_3_ADDRESS,
|
|
746
|
-
data: txContext.multicallData
|
|
747
|
-
},
|
|
748
|
-
blobData: txContext.blobData,
|
|
749
|
-
l1BlockNumber: txContext.l1BlockNumber.toString(),
|
|
750
|
-
error: {
|
|
751
|
-
message: result.message,
|
|
752
|
-
name: result.name
|
|
753
|
-
},
|
|
754
|
-
context: {
|
|
755
|
-
actions: requests.map((r)=>r.action),
|
|
756
|
-
requests: requests.map((r)=>({
|
|
757
|
-
action: r.action,
|
|
758
|
-
to: r.request.to,
|
|
759
|
-
data: r.request.data
|
|
760
|
-
})),
|
|
761
|
-
sender: this.getSenderAddress().toString()
|
|
762
|
-
}
|
|
763
|
-
});
|
|
764
668
|
return {
|
|
765
669
|
failedActions: requests.map((r)=>r.action)
|
|
766
670
|
};
|
|
767
671
|
} else {
|
|
768
672
|
this.log.verbose(`Published bundled transactions (${actionsListStr})`, {
|
|
769
673
|
result,
|
|
770
|
-
requests
|
|
674
|
+
requests: requests.map((r)=>({
|
|
675
|
+
...r,
|
|
676
|
+
// Avoid logging large blob data
|
|
677
|
+
blobConfig: r.blobConfig ? {
|
|
678
|
+
...r.blobConfig,
|
|
679
|
+
blobs: r.blobConfig.blobs.map((b)=>({
|
|
680
|
+
size: trimmedBytesLength(b)
|
|
681
|
+
}))
|
|
682
|
+
} : undefined
|
|
683
|
+
}))
|
|
771
684
|
});
|
|
772
685
|
const successfulActions = [];
|
|
773
686
|
const failedActions = [];
|
|
@@ -778,37 +691,6 @@ export class SequencerPublisher {
|
|
|
778
691
|
failedActions.push(request.action);
|
|
779
692
|
}
|
|
780
693
|
}
|
|
781
|
-
// Single backup for the whole reverted tx
|
|
782
|
-
if (failedActions.length > 0 && result?.receipt?.status === 'reverted') {
|
|
783
|
-
this.backupFailedTx({
|
|
784
|
-
id: result.receipt.transactionHash,
|
|
785
|
-
failureType: 'revert',
|
|
786
|
-
request: {
|
|
787
|
-
to: MULTI_CALL_3_ADDRESS,
|
|
788
|
-
data: txContext.multicallData
|
|
789
|
-
},
|
|
790
|
-
blobData: txContext.blobData,
|
|
791
|
-
l1BlockNumber: result.receipt.blockNumber.toString(),
|
|
792
|
-
receipt: {
|
|
793
|
-
transactionHash: result.receipt.transactionHash,
|
|
794
|
-
blockNumber: result.receipt.blockNumber.toString(),
|
|
795
|
-
gasUsed: (result.receipt.gasUsed ?? 0n).toString(),
|
|
796
|
-
status: 'reverted'
|
|
797
|
-
},
|
|
798
|
-
error: {
|
|
799
|
-
message: result.errorMsg ?? 'Transaction reverted'
|
|
800
|
-
},
|
|
801
|
-
context: {
|
|
802
|
-
actions: failedActions,
|
|
803
|
-
requests: requests.filter((r)=>failedActions.includes(r.action)).map((r)=>({
|
|
804
|
-
action: r.action,
|
|
805
|
-
to: r.request.to,
|
|
806
|
-
data: r.request.data
|
|
807
|
-
})),
|
|
808
|
-
sender: this.getSenderAddress().toString()
|
|
809
|
-
}
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
694
|
return {
|
|
813
695
|
successfulActions,
|
|
814
696
|
failedActions
|
|
@@ -816,17 +698,20 @@ export class SequencerPublisher {
|
|
|
816
698
|
}
|
|
817
699
|
}
|
|
818
700
|
/**
|
|
819
|
-
* @notice Will call `
|
|
701
|
+
* @notice Will call `canProposeAt` to make sure that it is possible to propose
|
|
820
702
|
* @param tipArchive - The archive to check
|
|
821
703
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
822
|
-
*/
|
|
704
|
+
*/ canProposeAt(tipArchive, msgSender, opts = {}) {
|
|
823
705
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
824
706
|
const ignoredErrors = [
|
|
825
707
|
'SlotAlreadyInChain',
|
|
826
708
|
'InvalidProposer',
|
|
827
709
|
'InvalidArchive'
|
|
828
710
|
];
|
|
829
|
-
|
|
711
|
+
const pipelined = opts.pipelined ?? false;
|
|
712
|
+
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
713
|
+
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
714
|
+
return this.rollupContract.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
830
715
|
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
831
716
|
}).catch((err)=>{
|
|
832
717
|
if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
|
|
@@ -858,7 +743,7 @@ export class SequencerPublisher {
|
|
|
858
743
|
header.blobsHash.toString(),
|
|
859
744
|
flags
|
|
860
745
|
];
|
|
861
|
-
const ts =
|
|
746
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
862
747
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(opts?.forcePendingCheckpointNumber);
|
|
863
748
|
let balance = 0n;
|
|
864
749
|
if (this.config.fishermanMode) {
|
|
@@ -881,7 +766,7 @@ export class SequencerPublisher {
|
|
|
881
766
|
}),
|
|
882
767
|
from: MULTI_CALL_3_ADDRESS
|
|
883
768
|
}, {
|
|
884
|
-
time: ts
|
|
769
|
+
time: ts
|
|
885
770
|
}, stateOverrides);
|
|
886
771
|
this.log.debug(`Simulated validateHeader`);
|
|
887
772
|
}
|
|
@@ -911,7 +796,6 @@ export class SequencerPublisher {
|
|
|
911
796
|
...logData,
|
|
912
797
|
request
|
|
913
798
|
});
|
|
914
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
915
799
|
try {
|
|
916
800
|
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, mergeAbis([
|
|
917
801
|
request.abi ?? [],
|
|
@@ -954,27 +838,6 @@ export class SequencerPublisher {
|
|
|
954
838
|
}
|
|
955
839
|
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
956
840
|
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
957
|
-
this.backupFailedTx({
|
|
958
|
-
id: keccak256(request.data),
|
|
959
|
-
failureType: 'simulation',
|
|
960
|
-
request: {
|
|
961
|
-
to: request.to,
|
|
962
|
-
data: request.data,
|
|
963
|
-
value: request.value?.toString()
|
|
964
|
-
},
|
|
965
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
966
|
-
error: {
|
|
967
|
-
message: viemError.message,
|
|
968
|
-
name: viemError.name
|
|
969
|
-
},
|
|
970
|
-
context: {
|
|
971
|
-
actions: [
|
|
972
|
-
`invalidate-${reason}`
|
|
973
|
-
],
|
|
974
|
-
checkpointNumber,
|
|
975
|
-
sender: this.getSenderAddress().toString()
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
841
|
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, {
|
|
979
842
|
cause: viemError
|
|
980
843
|
});
|
|
@@ -1001,7 +864,6 @@ export class SequencerPublisher {
|
|
|
1001
864
|
}
|
|
1002
865
|
}
|
|
1003
866
|
/** Simulates `propose` to make sure that the checkpoint is valid for submission */ async validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, options) {
|
|
1004
|
-
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
1005
867
|
const blobFields = checkpoint.toBlobFields();
|
|
1006
868
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
1007
869
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -1018,10 +880,9 @@ export class SequencerPublisher {
|
|
|
1018
880
|
attestationsAndSignersSignature.toViemSignature(),
|
|
1019
881
|
blobInput
|
|
1020
882
|
];
|
|
1021
|
-
await this.simulateProposeTx(args,
|
|
1022
|
-
return ts;
|
|
883
|
+
await this.simulateProposeTx(args, options);
|
|
1023
884
|
}
|
|
1024
|
-
async enqueueCastSignalHelper(slotNumber,
|
|
885
|
+
async enqueueCastSignalHelper(slotNumber, signalType, payload, base, signerAddress, signer) {
|
|
1025
886
|
if (this.lastActions[signalType] && this.lastActions[signalType] === slotNumber) {
|
|
1026
887
|
this.log.debug(`Skipping duplicate vote cast signal ${signalType} for slot ${slotNumber}`);
|
|
1027
888
|
return false;
|
|
@@ -1077,7 +938,7 @@ export class SequencerPublisher {
|
|
|
1077
938
|
signer: this.l1TxUtils.client.account?.address,
|
|
1078
939
|
lastValidL2Slot: slotNumber
|
|
1079
940
|
});
|
|
1080
|
-
const
|
|
941
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1081
942
|
try {
|
|
1082
943
|
await this.l1TxUtils.simulate(request, {
|
|
1083
944
|
time: timestamp
|
|
@@ -1090,27 +951,8 @@ export class SequencerPublisher {
|
|
|
1090
951
|
});
|
|
1091
952
|
} catch (err) {
|
|
1092
953
|
const viemError = formatViemError(err);
|
|
1093
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
1094
|
-
|
|
1095
|
-
id: keccak256(request.data),
|
|
1096
|
-
failureType: 'simulation',
|
|
1097
|
-
request: {
|
|
1098
|
-
to: request.to,
|
|
1099
|
-
data: request.data,
|
|
1100
|
-
value: request.value?.toString()
|
|
1101
|
-
},
|
|
1102
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1103
|
-
error: {
|
|
1104
|
-
message: viemError.message,
|
|
1105
|
-
name: viemError.name
|
|
1106
|
-
},
|
|
1107
|
-
context: {
|
|
1108
|
-
actions: [
|
|
1109
|
-
action
|
|
1110
|
-
],
|
|
1111
|
-
slot: slotNumber,
|
|
1112
|
-
sender: this.getSenderAddress().toString()
|
|
1113
|
-
}
|
|
954
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
955
|
+
simulationTimestamp: timestamp
|
|
1114
956
|
});
|
|
1115
957
|
// Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
|
|
1116
958
|
}
|
|
@@ -1155,12 +997,11 @@ export class SequencerPublisher {
|
|
|
1155
997
|
/**
|
|
1156
998
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
1157
999
|
* @param slotNumber - The slot number to cast a signal for.
|
|
1158
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
1159
1000
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
1160
|
-
*/ enqueueGovernanceCastSignal(governancePayload, slotNumber,
|
|
1161
|
-
return this.enqueueCastSignalHelper(slotNumber,
|
|
1001
|
+
*/ enqueueGovernanceCastSignal(governancePayload, slotNumber, signerAddress, signer) {
|
|
1002
|
+
return this.enqueueCastSignalHelper(slotNumber, 'governance-signal', governancePayload, this.govProposerContract, signerAddress, signer);
|
|
1162
1003
|
}
|
|
1163
|
-
/** Enqueues all slashing actions as returned by the slasher client. */ async enqueueSlashingActions(actions, slotNumber,
|
|
1004
|
+
/** Enqueues all slashing actions as returned by the slasher client. */ async enqueueSlashingActions(actions, slotNumber, signerAddress, signer) {
|
|
1164
1005
|
if (actions.length === 0) {
|
|
1165
1006
|
this.log.debug(`No slashing actions to enqueue for slot ${slotNumber}`);
|
|
1166
1007
|
return false;
|
|
@@ -1176,7 +1017,7 @@ export class SequencerPublisher {
|
|
|
1176
1017
|
this.log.debug(`Enqueuing slashing vote for payload ${action.payload} at slot ${slotNumber}`, {
|
|
1177
1018
|
signerAddress
|
|
1178
1019
|
});
|
|
1179
|
-
await this.enqueueCastSignalHelper(slotNumber,
|
|
1020
|
+
await this.enqueueCastSignalHelper(slotNumber, 'empire-slashing-signal', action.payload, this.slashingProposerContract, signerAddress, signer);
|
|
1180
1021
|
break;
|
|
1181
1022
|
}
|
|
1182
1023
|
case 'create-empire-payload':
|
|
@@ -1186,7 +1027,7 @@ export class SequencerPublisher {
|
|
|
1186
1027
|
signerAddress
|
|
1187
1028
|
});
|
|
1188
1029
|
const request = this.slashFactoryContract.buildCreatePayloadRequest(action.data);
|
|
1189
|
-
await this.simulateAndEnqueueRequest('create-empire-payload', request, (receipt)=>!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs), slotNumber
|
|
1030
|
+
await this.simulateAndEnqueueRequest('create-empire-payload', request, (receipt)=>!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs), slotNumber);
|
|
1190
1031
|
break;
|
|
1191
1032
|
}
|
|
1192
1033
|
case 'execute-empire-payload':
|
|
@@ -1201,7 +1042,7 @@ export class SequencerPublisher {
|
|
|
1201
1042
|
}
|
|
1202
1043
|
const empireSlashingProposer = this.slashingProposerContract;
|
|
1203
1044
|
const request = empireSlashingProposer.buildExecuteRoundRequest(action.round);
|
|
1204
|
-
await this.simulateAndEnqueueRequest('execute-empire-payload', request, (receipt)=>!!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs), slotNumber
|
|
1045
|
+
await this.simulateAndEnqueueRequest('execute-empire-payload', request, (receipt)=>!!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs), slotNumber);
|
|
1205
1046
|
break;
|
|
1206
1047
|
}
|
|
1207
1048
|
case 'vote-offenses':
|
|
@@ -1219,7 +1060,7 @@ export class SequencerPublisher {
|
|
|
1219
1060
|
const tallySlashingProposer = this.slashingProposerContract;
|
|
1220
1061
|
const votes = bufferToHex(encodeSlashConsensusVotes(action.votes));
|
|
1221
1062
|
const request = await tallySlashingProposer.buildVoteRequestFromSigner(votes, slotNumber, signer);
|
|
1222
|
-
await this.simulateAndEnqueueRequest('vote-offenses', request, (receipt)=>!!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs), slotNumber
|
|
1063
|
+
await this.simulateAndEnqueueRequest('vote-offenses', request, (receipt)=>!!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs), slotNumber);
|
|
1223
1064
|
break;
|
|
1224
1065
|
}
|
|
1225
1066
|
case 'execute-slash':
|
|
@@ -1235,7 +1076,7 @@ export class SequencerPublisher {
|
|
|
1235
1076
|
}
|
|
1236
1077
|
const tallySlashingProposer = this.slashingProposerContract;
|
|
1237
1078
|
const request = tallySlashingProposer.buildExecuteRoundRequest(action.round, action.committees);
|
|
1238
|
-
await this.simulateAndEnqueueRequest('execute-slash', request, (receipt)=>!!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs), slotNumber
|
|
1079
|
+
await this.simulateAndEnqueueRequest('execute-slash', request, (receipt)=>!!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs), slotNumber);
|
|
1239
1080
|
break;
|
|
1240
1081
|
}
|
|
1241
1082
|
default:
|
|
@@ -1259,14 +1100,13 @@ export class SequencerPublisher {
|
|
|
1259
1100
|
attestationsAndSignersSignature,
|
|
1260
1101
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier
|
|
1261
1102
|
};
|
|
1262
|
-
let ts;
|
|
1263
1103
|
try {
|
|
1264
1104
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1265
1105
|
// This means that we can avoid the simulation issues in later checks.
|
|
1266
1106
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1267
1107
|
// make time consistency checks break.
|
|
1268
1108
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1269
|
-
|
|
1109
|
+
await this.validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, opts);
|
|
1270
1110
|
} catch (err) {
|
|
1271
1111
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1272
1112
|
...checkpoint.getStats(),
|
|
@@ -1279,7 +1119,7 @@ export class SequencerPublisher {
|
|
|
1279
1119
|
...checkpoint.toCheckpointInfo(),
|
|
1280
1120
|
...opts
|
|
1281
1121
|
});
|
|
1282
|
-
await this.addProposeTx(checkpoint, proposeTxArgs, opts
|
|
1122
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts);
|
|
1283
1123
|
}
|
|
1284
1124
|
enqueueInvalidateCheckpoint(request, opts = {}) {
|
|
1285
1125
|
if (!request) {
|
|
@@ -1320,7 +1160,8 @@ export class SequencerPublisher {
|
|
|
1320
1160
|
}
|
|
1321
1161
|
});
|
|
1322
1162
|
}
|
|
1323
|
-
async simulateAndEnqueueRequest(action, request, checkSuccess, slotNumber
|
|
1163
|
+
async simulateAndEnqueueRequest(action, request, checkSuccess, slotNumber) {
|
|
1164
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1324
1165
|
const logData = {
|
|
1325
1166
|
slotNumber,
|
|
1326
1167
|
timestamp,
|
|
@@ -1333,7 +1174,6 @@ export class SequencerPublisher {
|
|
|
1333
1174
|
const cachedLastActionSlot = this.lastActions[action];
|
|
1334
1175
|
this.lastActions[action] = slotNumber;
|
|
1335
1176
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
1336
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1337
1177
|
let gasUsed;
|
|
1338
1178
|
const simulateAbi = mergeAbis([
|
|
1339
1179
|
request.abi ?? [],
|
|
@@ -1342,7 +1182,7 @@ export class SequencerPublisher {
|
|
|
1342
1182
|
try {
|
|
1343
1183
|
({ gasUsed } = await this.l1TxUtils.simulate(request, {
|
|
1344
1184
|
time: timestamp
|
|
1345
|
-
}, [], simulateAbi));
|
|
1185
|
+
}, [], simulateAbi));
|
|
1346
1186
|
this.log.verbose(`Simulation for ${action} succeeded`, {
|
|
1347
1187
|
...logData,
|
|
1348
1188
|
request,
|
|
@@ -1351,27 +1191,6 @@ export class SequencerPublisher {
|
|
|
1351
1191
|
} catch (err) {
|
|
1352
1192
|
const viemError = formatViemError(err, simulateAbi);
|
|
1353
1193
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1354
|
-
this.backupFailedTx({
|
|
1355
|
-
id: keccak256(request.data),
|
|
1356
|
-
failureType: 'simulation',
|
|
1357
|
-
request: {
|
|
1358
|
-
to: request.to,
|
|
1359
|
-
data: request.data,
|
|
1360
|
-
value: request.value?.toString()
|
|
1361
|
-
},
|
|
1362
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1363
|
-
error: {
|
|
1364
|
-
message: viemError.message,
|
|
1365
|
-
name: viemError.name
|
|
1366
|
-
},
|
|
1367
|
-
context: {
|
|
1368
|
-
actions: [
|
|
1369
|
-
action
|
|
1370
|
-
],
|
|
1371
|
-
slot: slotNumber,
|
|
1372
|
-
sender: this.getSenderAddress().toString()
|
|
1373
|
-
}
|
|
1374
|
-
});
|
|
1375
1194
|
return false;
|
|
1376
1195
|
}
|
|
1377
1196
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
@@ -1423,7 +1242,7 @@ export class SequencerPublisher {
|
|
|
1423
1242
|
this.interrupted = false;
|
|
1424
1243
|
this.l1TxUtils.restart();
|
|
1425
1244
|
}
|
|
1426
|
-
async prepareProposeTx(encodedData,
|
|
1245
|
+
async prepareProposeTx(encodedData, options) {
|
|
1427
1246
|
const kzg = Blob.getViemKzgInstance();
|
|
1428
1247
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
1429
1248
|
this.log.debug('Validating blob input', {
|
|
@@ -1450,38 +1269,10 @@ export class SequencerPublisher {
|
|
|
1450
1269
|
}, {}, {
|
|
1451
1270
|
blobs: encodedData.blobs.map((b)=>b.data),
|
|
1452
1271
|
kzg
|
|
1453
|
-
}).catch(
|
|
1454
|
-
const
|
|
1455
|
-
this.log.error(`Failed to validate blobs`,
|
|
1456
|
-
metaMessages
|
|
1457
|
-
});
|
|
1458
|
-
const validateBlobsData = encodeFunctionData({
|
|
1459
|
-
abi: RollupAbi,
|
|
1460
|
-
functionName: 'validateBlobs',
|
|
1461
|
-
args: [
|
|
1462
|
-
blobInput
|
|
1463
|
-
]
|
|
1464
|
-
});
|
|
1465
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1466
|
-
this.backupFailedTx({
|
|
1467
|
-
id: keccak256(validateBlobsData),
|
|
1468
|
-
failureType: 'simulation',
|
|
1469
|
-
request: {
|
|
1470
|
-
to: this.rollupContract.address,
|
|
1471
|
-
data: validateBlobsData
|
|
1472
|
-
},
|
|
1473
|
-
blobData: encodedData.blobs.map((b)=>toHex(b.data)),
|
|
1474
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1475
|
-
error: {
|
|
1476
|
-
message: viemError.message,
|
|
1477
|
-
name: viemError.name
|
|
1478
|
-
},
|
|
1479
|
-
context: {
|
|
1480
|
-
actions: [
|
|
1481
|
-
'validate-blobs'
|
|
1482
|
-
],
|
|
1483
|
-
sender: this.getSenderAddress().toString()
|
|
1484
|
-
}
|
|
1272
|
+
}).catch((err)=>{
|
|
1273
|
+
const { message, metaMessages } = formatViemError(err);
|
|
1274
|
+
this.log.error(`Failed to validate blobs`, message, {
|
|
1275
|
+
metaMessages
|
|
1485
1276
|
});
|
|
1486
1277
|
throw new Error('Failed to validate blobs');
|
|
1487
1278
|
});
|
|
@@ -1500,7 +1291,7 @@ export class SequencerPublisher {
|
|
|
1500
1291
|
encodedData.attestationsAndSignersSignature.toViemSignature(),
|
|
1501
1292
|
blobInput
|
|
1502
1293
|
];
|
|
1503
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1294
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
|
|
1504
1295
|
return {
|
|
1505
1296
|
args,
|
|
1506
1297
|
blobEvaluationGas,
|
|
@@ -1511,9 +1302,8 @@ export class SequencerPublisher {
|
|
|
1511
1302
|
/**
|
|
1512
1303
|
* Simulates the propose tx with eth_simulateV1
|
|
1513
1304
|
* @param args - The propose tx args
|
|
1514
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1515
1305
|
* @returns The simulation result
|
|
1516
|
-
*/ async simulateProposeTx(args,
|
|
1306
|
+
*/ async simulateProposeTx(args, options) {
|
|
1517
1307
|
const rollupData = encodeFunctionData({
|
|
1518
1308
|
abi: RollupAbi,
|
|
1519
1309
|
functionName: 'propose',
|
|
@@ -1541,7 +1331,7 @@ export class SequencerPublisher {
|
|
|
1541
1331
|
balance: 10n * WEI_CONST * WEI_CONST
|
|
1542
1332
|
});
|
|
1543
1333
|
}
|
|
1544
|
-
const
|
|
1334
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1545
1335
|
const simulationResult = await this.l1TxUtils.simulate({
|
|
1546
1336
|
to: this.rollupContract.address,
|
|
1547
1337
|
data: rollupData,
|
|
@@ -1550,8 +1340,7 @@ export class SequencerPublisher {
|
|
|
1550
1340
|
from: this.proposerAddressForSimulation.toString()
|
|
1551
1341
|
}
|
|
1552
1342
|
}, {
|
|
1553
|
-
|
|
1554
|
-
time: timestamp + 1n,
|
|
1343
|
+
time: simTs,
|
|
1555
1344
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1556
1345
|
gasLimit: MAX_L1_TX_LIMIT * 2n
|
|
1557
1346
|
}, stateOverrides, RollupAbi, {
|
|
@@ -1568,26 +1357,8 @@ export class SequencerPublisher {
|
|
|
1568
1357
|
logs: []
|
|
1569
1358
|
};
|
|
1570
1359
|
}
|
|
1571
|
-
this.log.error(`Failed to simulate propose tx`, viemError
|
|
1572
|
-
|
|
1573
|
-
id: keccak256(rollupData),
|
|
1574
|
-
failureType: 'simulation',
|
|
1575
|
-
request: {
|
|
1576
|
-
to: this.rollupContract.address,
|
|
1577
|
-
data: rollupData
|
|
1578
|
-
},
|
|
1579
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1580
|
-
error: {
|
|
1581
|
-
message: viemError.message,
|
|
1582
|
-
name: viemError.name
|
|
1583
|
-
},
|
|
1584
|
-
context: {
|
|
1585
|
-
actions: [
|
|
1586
|
-
'propose'
|
|
1587
|
-
],
|
|
1588
|
-
slot: Number(args[0].header.slotNumber),
|
|
1589
|
-
sender: this.getSenderAddress().toString()
|
|
1590
|
-
}
|
|
1360
|
+
this.log.error(`Failed to simulate propose tx`, viemError, {
|
|
1361
|
+
simulationTimestamp: simTs
|
|
1591
1362
|
});
|
|
1592
1363
|
throw err;
|
|
1593
1364
|
});
|
|
@@ -1596,11 +1367,11 @@ export class SequencerPublisher {
|
|
|
1596
1367
|
simulationResult
|
|
1597
1368
|
};
|
|
1598
1369
|
}
|
|
1599
|
-
async addProposeTx(checkpoint, encodedData, opts = {}
|
|
1370
|
+
async addProposeTx(checkpoint, encodedData, opts = {}) {
|
|
1600
1371
|
const slot = checkpoint.header.slotNumber;
|
|
1601
1372
|
const timer = new Timer();
|
|
1602
1373
|
const kzg = Blob.getViemKzgInstance();
|
|
1603
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData,
|
|
1374
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
|
|
1604
1375
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1605
1376
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(simulationResult.gasUsed) * 64 / 63)) + blobEvaluationGas + SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS);
|
|
1606
1377
|
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
@@ -1665,4 +1436,13 @@ export class SequencerPublisher {
|
|
|
1665
1436
|
}
|
|
1666
1437
|
});
|
|
1667
1438
|
}
|
|
1439
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1440
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */ getSimulationTimestamp(slot) {
|
|
1441
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1442
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1443
|
+
}
|
|
1444
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */ getNextL1SlotTimestamp() {
|
|
1445
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1446
|
+
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|
|
1447
|
+
}
|
|
1668
1448
|
}
|