@aztec/sequencer-client 0.0.1-commit.88e6f9396 → 0.0.1-commit.8c0b8ff
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 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +6 -12
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +1 -2
- 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 -22
- 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 +11 -16
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +53 -289
- package/dest/sequencer/checkpoint_proposal_job.d.ts +4 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +75 -117
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/events.d.ts +1 -2
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +1 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +0 -11
- package/dest/sequencer/sequencer.d.ts +5 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +50 -59
- package/dest/test/mock_checkpoint_builder.d.ts +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +2 -0
- package/package.json +28 -27
- package/src/client/sequencer-client.ts +9 -11
- package/src/config.ts +0 -4
- package/src/global_variable_builder/global_builder.ts +22 -24
- 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 +51 -274
- package/src/sequencer/checkpoint_proposal_job.ts +66 -139
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +1 -1
- package/src/sequencer/metrics.ts +0 -14
- package/src/sequencer/sequencer.ts +58 -84
- package/src/test/mock_checkpoint_builder.ts +2 -0
- 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
|
@@ -31,7 +31,6 @@ import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
|
31
31
|
import { trimmedBytesLength } from '@aztec/foundation/buffer';
|
|
32
32
|
import { pick } from '@aztec/foundation/collection';
|
|
33
33
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
34
|
-
import { TimeoutError } from '@aztec/foundation/error';
|
|
35
34
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
36
35
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
37
36
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -42,24 +41,15 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
|
42
41
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
43
42
|
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
44
43
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
44
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
45
45
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
46
46
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
47
47
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
48
48
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
49
49
|
|
|
50
|
-
import {
|
|
51
|
-
type Hex,
|
|
52
|
-
type StateOverride,
|
|
53
|
-
type TransactionReceipt,
|
|
54
|
-
type TypedDataDefinition,
|
|
55
|
-
encodeFunctionData,
|
|
56
|
-
keccak256,
|
|
57
|
-
multicall3Abi,
|
|
58
|
-
toHex,
|
|
59
|
-
} from 'viem';
|
|
50
|
+
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
60
51
|
|
|
61
52
|
import type { SequencerPublisherConfig } from './config.js';
|
|
62
|
-
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
63
53
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
64
54
|
|
|
65
55
|
/** Arguments to the process method of the rollup contract */
|
|
@@ -121,7 +111,6 @@ export class SequencerPublisher {
|
|
|
121
111
|
private interrupted = false;
|
|
122
112
|
private metrics: SequencerPublisherMetrics;
|
|
123
113
|
public epochCache: EpochCache;
|
|
124
|
-
private failedTxStore?: Promise<L1TxFailedStore | undefined>;
|
|
125
114
|
|
|
126
115
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
127
116
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
@@ -134,15 +123,13 @@ export class SequencerPublisher {
|
|
|
134
123
|
protected log: Logger;
|
|
135
124
|
protected ethereumSlotDuration: bigint;
|
|
136
125
|
protected aztecSlotDuration: bigint;
|
|
126
|
+
private dateProvider: DateProvider;
|
|
137
127
|
|
|
138
128
|
private blobClient: BlobClientInterface;
|
|
139
129
|
|
|
140
130
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
141
131
|
private proposerAddressForSimulation?: EthAddress;
|
|
142
132
|
|
|
143
|
-
/** Optional callback to obtain a replacement publisher when the current one fails to send. */
|
|
144
|
-
private getNextPublisher?: (excludeAddresses: EthAddress[]) => Promise<L1TxUtils | undefined>;
|
|
145
|
-
|
|
146
133
|
/** L1 fee analyzer for fisherman mode */
|
|
147
134
|
private l1FeeAnalyzer?: L1FeeAnalyzer;
|
|
148
135
|
|
|
@@ -166,7 +153,7 @@ export class SequencerPublisher {
|
|
|
166
153
|
protected requests: RequestWithExpiry[] = [];
|
|
167
154
|
|
|
168
155
|
constructor(
|
|
169
|
-
private config: Pick<SequencerPublisherConfig, 'fishermanMode'
|
|
156
|
+
private config: Pick<SequencerPublisherConfig, 'fishermanMode'> &
|
|
170
157
|
Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration'> & { l1ChainId: number },
|
|
171
158
|
deps: {
|
|
172
159
|
telemetry?: TelemetryClient;
|
|
@@ -181,12 +168,12 @@ export class SequencerPublisher {
|
|
|
181
168
|
metrics: SequencerPublisherMetrics;
|
|
182
169
|
lastActions: Partial<Record<Action, SlotNumber>>;
|
|
183
170
|
log?: Logger;
|
|
184
|
-
getNextPublisher?: (excludeAddresses: EthAddress[]) => Promise<L1TxUtils | undefined>;
|
|
185
171
|
},
|
|
186
172
|
) {
|
|
187
173
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
188
174
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
189
175
|
this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
|
|
176
|
+
this.dateProvider = deps.dateProvider;
|
|
190
177
|
this.epochCache = deps.epochCache;
|
|
191
178
|
this.lastActions = deps.lastActions;
|
|
192
179
|
|
|
@@ -196,7 +183,6 @@ export class SequencerPublisher {
|
|
|
196
183
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
197
184
|
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
198
185
|
this.l1TxUtils = deps.l1TxUtils;
|
|
199
|
-
this.getNextPublisher = deps.getNextPublisher;
|
|
200
186
|
|
|
201
187
|
this.rollupContract = deps.rollupContract;
|
|
202
188
|
|
|
@@ -225,31 +211,6 @@ export class SequencerPublisher {
|
|
|
225
211
|
this.rollupContract,
|
|
226
212
|
createLogger('sequencer:publisher:price-oracle'),
|
|
227
213
|
);
|
|
228
|
-
|
|
229
|
-
// Initialize failed L1 tx store (optional, for test networks)
|
|
230
|
-
this.failedTxStore = createL1TxFailedStore(config.l1TxFailedStore, this.log);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Backs up a failed L1 transaction to the configured store for debugging.
|
|
235
|
-
* Does nothing if no store is configured.
|
|
236
|
-
*/
|
|
237
|
-
private backupFailedTx(failedTx: Omit<FailedL1Tx, 'timestamp'>): void {
|
|
238
|
-
if (!this.failedTxStore) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const tx: FailedL1Tx = {
|
|
243
|
-
...failedTx,
|
|
244
|
-
timestamp: Date.now(),
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Fire and forget - don't block on backup
|
|
248
|
-
void this.failedTxStore
|
|
249
|
-
.then(store => store?.saveFailedTx(tx))
|
|
250
|
-
.catch(err => {
|
|
251
|
-
this.log.warn(`Failed to backup failed L1 tx to store`, err);
|
|
252
|
-
});
|
|
253
214
|
}
|
|
254
215
|
|
|
255
216
|
public getRollupContract(): RollupContract {
|
|
@@ -288,7 +249,7 @@ export class SequencerPublisher {
|
|
|
288
249
|
}
|
|
289
250
|
|
|
290
251
|
public getCurrentL2Slot(): SlotNumber {
|
|
291
|
-
return this.epochCache.
|
|
252
|
+
return this.epochCache.getEpochAndSlotNow().slot;
|
|
292
253
|
}
|
|
293
254
|
|
|
294
255
|
/**
|
|
@@ -431,36 +392,19 @@ export class SequencerPublisher {
|
|
|
431
392
|
validRequests.sort((a, b) => compareActions(a.action, b.action));
|
|
432
393
|
|
|
433
394
|
try {
|
|
434
|
-
// Capture context for failed tx backup before sending
|
|
435
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
436
|
-
const multicallData = encodeFunctionData({
|
|
437
|
-
abi: multicall3Abi,
|
|
438
|
-
functionName: 'aggregate3',
|
|
439
|
-
args: [
|
|
440
|
-
validRequests.map(r => ({
|
|
441
|
-
target: r.request.to!,
|
|
442
|
-
callData: r.request.data!,
|
|
443
|
-
allowFailure: true,
|
|
444
|
-
})),
|
|
445
|
-
],
|
|
446
|
-
});
|
|
447
|
-
const blobDataHex = blobConfig?.blobs?.map(b => toHex(b)) as Hex[] | undefined;
|
|
448
|
-
|
|
449
|
-
const txContext = { multicallData, blobData: blobDataHex, l1BlockNumber };
|
|
450
|
-
|
|
451
395
|
this.log.debug('Forwarding transactions', {
|
|
452
396
|
validRequests: validRequests.map(request => request.action),
|
|
453
397
|
txConfig,
|
|
454
398
|
});
|
|
455
|
-
const result = await
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
txContext,
|
|
399
|
+
const result = await Multicall3.forward(
|
|
400
|
+
validRequests.map(request => request.request),
|
|
401
|
+
this.l1TxUtils,
|
|
402
|
+
txConfig,
|
|
403
|
+
blobConfig,
|
|
404
|
+
this.rollupContract.address,
|
|
405
|
+
this.log,
|
|
463
406
|
);
|
|
407
|
+
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result);
|
|
464
408
|
return { result, expiredActions, sentActions: validActions, successfulActions, failedActions };
|
|
465
409
|
} catch (err) {
|
|
466
410
|
const viemError = formatViemError(err);
|
|
@@ -478,76 +422,13 @@ export class SequencerPublisher {
|
|
|
478
422
|
}
|
|
479
423
|
}
|
|
480
424
|
|
|
481
|
-
/**
|
|
482
|
-
* Forwards transactions via Multicall3, rotating to the next available publisher if a send
|
|
483
|
-
* failure occurs (i.e. the tx never reached the chain).
|
|
484
|
-
* On-chain reverts and simulation errors are returned as-is without rotation.
|
|
485
|
-
*/
|
|
486
|
-
private async forwardWithPublisherRotation(
|
|
487
|
-
validRequests: RequestWithExpiry[],
|
|
488
|
-
txConfig: RequestWithExpiry['gasConfig'],
|
|
489
|
-
blobConfig: L1BlobInputs | undefined,
|
|
490
|
-
) {
|
|
491
|
-
const triedAddresses: EthAddress[] = [];
|
|
492
|
-
let currentPublisher = this.l1TxUtils;
|
|
493
|
-
|
|
494
|
-
while (true) {
|
|
495
|
-
triedAddresses.push(currentPublisher.getSenderAddress());
|
|
496
|
-
try {
|
|
497
|
-
const result = await Multicall3.forward(
|
|
498
|
-
validRequests.map(r => r.request),
|
|
499
|
-
currentPublisher,
|
|
500
|
-
txConfig,
|
|
501
|
-
blobConfig,
|
|
502
|
-
this.rollupContract.address,
|
|
503
|
-
this.log,
|
|
504
|
-
);
|
|
505
|
-
this.l1TxUtils = currentPublisher;
|
|
506
|
-
return result;
|
|
507
|
-
} catch (err) {
|
|
508
|
-
if (err instanceof TimeoutError) {
|
|
509
|
-
throw err;
|
|
510
|
-
}
|
|
511
|
-
const viemError = formatViemError(err);
|
|
512
|
-
if (!this.getNextPublisher) {
|
|
513
|
-
this.log.error('Failed to publish bundled transactions', viemError);
|
|
514
|
-
return undefined;
|
|
515
|
-
}
|
|
516
|
-
this.log.warn(
|
|
517
|
-
`Publisher ${currentPublisher.getSenderAddress()} failed to send, rotating to next publisher`,
|
|
518
|
-
viemError,
|
|
519
|
-
);
|
|
520
|
-
const nextPublisher = await this.getNextPublisher([...triedAddresses]);
|
|
521
|
-
if (!nextPublisher) {
|
|
522
|
-
this.log.error('All available publishers exhausted, failed to publish bundled transactions');
|
|
523
|
-
return undefined;
|
|
524
|
-
}
|
|
525
|
-
currentPublisher = nextPublisher;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
425
|
private callbackBundledTransactions(
|
|
531
426
|
requests: RequestWithExpiry[],
|
|
532
|
-
result
|
|
533
|
-
txContext: { multicallData: Hex; blobData?: Hex[]; l1BlockNumber: bigint },
|
|
427
|
+
result?: { receipt: TransactionReceipt } | FormattedViemError,
|
|
534
428
|
) {
|
|
535
429
|
const actionsListStr = requests.map(r => r.action).join(', ');
|
|
536
430
|
if (result instanceof FormattedViemError) {
|
|
537
431
|
this.log.error(`Failed to publish bundled transactions (${actionsListStr})`, result);
|
|
538
|
-
this.backupFailedTx({
|
|
539
|
-
id: keccak256(txContext.multicallData),
|
|
540
|
-
failureType: 'send-error',
|
|
541
|
-
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
542
|
-
blobData: txContext.blobData,
|
|
543
|
-
l1BlockNumber: txContext.l1BlockNumber.toString(),
|
|
544
|
-
error: { message: result.message, name: result.name },
|
|
545
|
-
context: {
|
|
546
|
-
actions: requests.map(r => r.action),
|
|
547
|
-
requests: requests.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
548
|
-
sender: this.getSenderAddress().toString(),
|
|
549
|
-
},
|
|
550
|
-
});
|
|
551
432
|
return { failedActions: requests.map(r => r.action) };
|
|
552
433
|
} else {
|
|
553
434
|
this.log.verbose(`Published bundled transactions (${actionsListStr})`, {
|
|
@@ -569,30 +450,6 @@ export class SequencerPublisher {
|
|
|
569
450
|
failedActions.push(request.action);
|
|
570
451
|
}
|
|
571
452
|
}
|
|
572
|
-
// Single backup for the whole reverted tx
|
|
573
|
-
if (failedActions.length > 0 && result?.receipt?.status === 'reverted') {
|
|
574
|
-
this.backupFailedTx({
|
|
575
|
-
id: result.receipt.transactionHash,
|
|
576
|
-
failureType: 'revert',
|
|
577
|
-
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
578
|
-
blobData: txContext.blobData,
|
|
579
|
-
l1BlockNumber: result.receipt.blockNumber.toString(),
|
|
580
|
-
receipt: {
|
|
581
|
-
transactionHash: result.receipt.transactionHash,
|
|
582
|
-
blockNumber: result.receipt.blockNumber.toString(),
|
|
583
|
-
gasUsed: (result.receipt.gasUsed ?? 0n).toString(),
|
|
584
|
-
status: 'reverted',
|
|
585
|
-
},
|
|
586
|
-
error: { message: result.errorMsg ?? 'Transaction reverted' },
|
|
587
|
-
context: {
|
|
588
|
-
actions: failedActions,
|
|
589
|
-
requests: requests
|
|
590
|
-
.filter(r => failedActions.includes(r.action))
|
|
591
|
-
.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
592
|
-
sender: this.getSenderAddress().toString(),
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
453
|
return { successfulActions, failedActions };
|
|
597
454
|
}
|
|
598
455
|
}
|
|
@@ -610,11 +467,12 @@ export class SequencerPublisher {
|
|
|
610
467
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
611
468
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
612
469
|
|
|
613
|
-
const pipelined = opts.pipelined ??
|
|
470
|
+
const pipelined = opts.pipelined ?? false;
|
|
614
471
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
472
|
+
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
615
473
|
|
|
616
474
|
return this.rollupContract
|
|
617
|
-
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(),
|
|
475
|
+
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
618
476
|
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
619
477
|
})
|
|
620
478
|
.catch(err => {
|
|
@@ -628,7 +486,6 @@ export class SequencerPublisher {
|
|
|
628
486
|
return undefined;
|
|
629
487
|
});
|
|
630
488
|
}
|
|
631
|
-
|
|
632
489
|
/**
|
|
633
490
|
* @notice Will simulate `validateHeader` to make sure that the block header is valid
|
|
634
491
|
* @dev This is a convenience function that can be used by the sequencer to validate a "partial" header.
|
|
@@ -652,7 +509,7 @@ export class SequencerPublisher {
|
|
|
652
509
|
flags,
|
|
653
510
|
] as const;
|
|
654
511
|
|
|
655
|
-
const ts =
|
|
512
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
656
513
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
657
514
|
opts?.forcePendingCheckpointNumber,
|
|
658
515
|
);
|
|
@@ -675,7 +532,7 @@ export class SequencerPublisher {
|
|
|
675
532
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
676
533
|
from: MULTI_CALL_3_ADDRESS,
|
|
677
534
|
},
|
|
678
|
-
{ time: ts
|
|
535
|
+
{ time: ts },
|
|
679
536
|
stateOverrides,
|
|
680
537
|
);
|
|
681
538
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -708,8 +565,6 @@ export class SequencerPublisher {
|
|
|
708
565
|
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
709
566
|
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
710
567
|
|
|
711
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
712
|
-
|
|
713
568
|
try {
|
|
714
569
|
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
715
570
|
request,
|
|
@@ -761,18 +616,6 @@ export class SequencerPublisher {
|
|
|
761
616
|
|
|
762
617
|
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
763
618
|
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
764
|
-
this.backupFailedTx({
|
|
765
|
-
id: keccak256(request.data!),
|
|
766
|
-
failureType: 'simulation',
|
|
767
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
768
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
769
|
-
error: { message: viemError.message, name: viemError.name },
|
|
770
|
-
context: {
|
|
771
|
-
actions: [`invalidate-${reason}`],
|
|
772
|
-
checkpointNumber,
|
|
773
|
-
sender: this.getSenderAddress().toString(),
|
|
774
|
-
},
|
|
775
|
-
});
|
|
776
619
|
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
777
620
|
}
|
|
778
621
|
}
|
|
@@ -816,10 +659,7 @@ export class SequencerPublisher {
|
|
|
816
659
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
817
660
|
attestationsAndSignersSignature: Signature,
|
|
818
661
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
819
|
-
): Promise<
|
|
820
|
-
// Anchor the simulation timestamp to the checkpoint's own slot start time
|
|
821
|
-
// rather than the current L1 block timestamp, which may overshoot into the next slot if the build ran late.
|
|
822
|
-
const ts = checkpoint.header.timestamp;
|
|
662
|
+
): Promise<void> {
|
|
823
663
|
const blobFields = checkpoint.toBlobFields();
|
|
824
664
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
825
665
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -838,13 +678,11 @@ export class SequencerPublisher {
|
|
|
838
678
|
blobInput,
|
|
839
679
|
] as const;
|
|
840
680
|
|
|
841
|
-
await this.simulateProposeTx(args,
|
|
842
|
-
return ts;
|
|
681
|
+
await this.simulateProposeTx(args, options);
|
|
843
682
|
}
|
|
844
683
|
|
|
845
684
|
private async enqueueCastSignalHelper(
|
|
846
685
|
slotNumber: SlotNumber,
|
|
847
|
-
timestamp: bigint,
|
|
848
686
|
signalType: GovernanceSignalAction,
|
|
849
687
|
payload: EthAddress,
|
|
850
688
|
base: IEmpireBase,
|
|
@@ -922,25 +760,15 @@ export class SequencerPublisher {
|
|
|
922
760
|
lastValidL2Slot: slotNumber,
|
|
923
761
|
});
|
|
924
762
|
|
|
925
|
-
const
|
|
763
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
926
764
|
|
|
927
765
|
try {
|
|
928
766
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
929
767
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
930
768
|
} catch (err) {
|
|
931
769
|
const viemError = formatViemError(err);
|
|
932
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
933
|
-
|
|
934
|
-
id: keccak256(request.data!),
|
|
935
|
-
failureType: 'simulation',
|
|
936
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
937
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
938
|
-
error: { message: viemError.message, name: viemError.name },
|
|
939
|
-
context: {
|
|
940
|
-
actions: [action],
|
|
941
|
-
slot: slotNumber,
|
|
942
|
-
sender: this.getSenderAddress().toString(),
|
|
943
|
-
},
|
|
770
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
771
|
+
simulationTimestamp: timestamp,
|
|
944
772
|
});
|
|
945
773
|
// Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
|
|
946
774
|
}
|
|
@@ -992,19 +820,16 @@ export class SequencerPublisher {
|
|
|
992
820
|
/**
|
|
993
821
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
994
822
|
* @param slotNumber - The slot number to cast a signal for.
|
|
995
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
996
823
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
997
824
|
*/
|
|
998
825
|
public enqueueGovernanceCastSignal(
|
|
999
826
|
governancePayload: EthAddress,
|
|
1000
827
|
slotNumber: SlotNumber,
|
|
1001
|
-
timestamp: bigint,
|
|
1002
828
|
signerAddress: EthAddress,
|
|
1003
829
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1004
830
|
): Promise<boolean> {
|
|
1005
831
|
return this.enqueueCastSignalHelper(
|
|
1006
832
|
slotNumber,
|
|
1007
|
-
timestamp,
|
|
1008
833
|
'governance-signal',
|
|
1009
834
|
governancePayload,
|
|
1010
835
|
this.govProposerContract,
|
|
@@ -1017,7 +842,6 @@ export class SequencerPublisher {
|
|
|
1017
842
|
public async enqueueSlashingActions(
|
|
1018
843
|
actions: ProposerSlashAction[],
|
|
1019
844
|
slotNumber: SlotNumber,
|
|
1020
|
-
timestamp: bigint,
|
|
1021
845
|
signerAddress: EthAddress,
|
|
1022
846
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1023
847
|
): Promise<boolean> {
|
|
@@ -1038,7 +862,6 @@ export class SequencerPublisher {
|
|
|
1038
862
|
});
|
|
1039
863
|
await this.enqueueCastSignalHelper(
|
|
1040
864
|
slotNumber,
|
|
1041
|
-
timestamp,
|
|
1042
865
|
'empire-slashing-signal',
|
|
1043
866
|
action.payload,
|
|
1044
867
|
this.slashingProposerContract,
|
|
@@ -1057,7 +880,6 @@ export class SequencerPublisher {
|
|
|
1057
880
|
(receipt: TransactionReceipt) =>
|
|
1058
881
|
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1059
882
|
slotNumber,
|
|
1060
|
-
timestamp,
|
|
1061
883
|
);
|
|
1062
884
|
break;
|
|
1063
885
|
}
|
|
@@ -1075,7 +897,6 @@ export class SequencerPublisher {
|
|
|
1075
897
|
request,
|
|
1076
898
|
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1077
899
|
slotNumber,
|
|
1078
|
-
timestamp,
|
|
1079
900
|
);
|
|
1080
901
|
break;
|
|
1081
902
|
}
|
|
@@ -1099,7 +920,6 @@ export class SequencerPublisher {
|
|
|
1099
920
|
request,
|
|
1100
921
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs),
|
|
1101
922
|
slotNumber,
|
|
1102
|
-
timestamp,
|
|
1103
923
|
);
|
|
1104
924
|
break;
|
|
1105
925
|
}
|
|
@@ -1121,7 +941,6 @@ export class SequencerPublisher {
|
|
|
1121
941
|
request,
|
|
1122
942
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1123
943
|
slotNumber,
|
|
1124
|
-
timestamp,
|
|
1125
944
|
);
|
|
1126
945
|
break;
|
|
1127
946
|
}
|
|
@@ -1157,15 +976,13 @@ export class SequencerPublisher {
|
|
|
1157
976
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1158
977
|
};
|
|
1159
978
|
|
|
1160
|
-
let ts: bigint;
|
|
1161
|
-
|
|
1162
979
|
try {
|
|
1163
980
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1164
981
|
// This means that we can avoid the simulation issues in later checks.
|
|
1165
982
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1166
983
|
// make time consistency checks break.
|
|
1167
984
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1168
|
-
|
|
985
|
+
await this.validateCheckpointForSubmission(
|
|
1169
986
|
checkpoint,
|
|
1170
987
|
attestationsAndSigners,
|
|
1171
988
|
attestationsAndSignersSignature,
|
|
@@ -1181,7 +998,7 @@ export class SequencerPublisher {
|
|
|
1181
998
|
}
|
|
1182
999
|
|
|
1183
1000
|
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
1184
|
-
await this.addProposeTx(checkpoint, proposeTxArgs, opts
|
|
1001
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts);
|
|
1185
1002
|
}
|
|
1186
1003
|
|
|
1187
1004
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1224,8 +1041,8 @@ export class SequencerPublisher {
|
|
|
1224
1041
|
request: L1TxRequest,
|
|
1225
1042
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1226
1043
|
slotNumber: SlotNumber,
|
|
1227
|
-
timestamp: bigint,
|
|
1228
1044
|
) {
|
|
1045
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1229
1046
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1230
1047
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1231
1048
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1237,30 +1054,16 @@ export class SequencerPublisher {
|
|
|
1237
1054
|
|
|
1238
1055
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
1239
1056
|
|
|
1240
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1241
|
-
|
|
1242
1057
|
let gasUsed: bigint;
|
|
1243
1058
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1059
|
+
|
|
1244
1060
|
try {
|
|
1245
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1061
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1246
1062
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1247
1063
|
} catch (err) {
|
|
1248
1064
|
const viemError = formatViemError(err, simulateAbi);
|
|
1249
1065
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1250
1066
|
|
|
1251
|
-
this.backupFailedTx({
|
|
1252
|
-
id: keccak256(request.data!),
|
|
1253
|
-
failureType: 'simulation',
|
|
1254
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
1255
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1256
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1257
|
-
context: {
|
|
1258
|
-
actions: [action],
|
|
1259
|
-
slot: slotNumber,
|
|
1260
|
-
sender: this.getSenderAddress().toString(),
|
|
1261
|
-
},
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
1067
|
return false;
|
|
1265
1068
|
}
|
|
1266
1069
|
|
|
@@ -1311,7 +1114,6 @@ export class SequencerPublisher {
|
|
|
1311
1114
|
|
|
1312
1115
|
private async prepareProposeTx(
|
|
1313
1116
|
encodedData: L1ProcessArgs,
|
|
1314
|
-
timestamp: bigint,
|
|
1315
1117
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1316
1118
|
) {
|
|
1317
1119
|
const kzg = Blob.getViemKzgInstance();
|
|
@@ -1344,27 +1146,9 @@ export class SequencerPublisher {
|
|
|
1344
1146
|
kzg,
|
|
1345
1147
|
},
|
|
1346
1148
|
)
|
|
1347
|
-
.catch(
|
|
1348
|
-
const
|
|
1349
|
-
this.log.error(`Failed to validate blobs`,
|
|
1350
|
-
const validateBlobsData = encodeFunctionData({
|
|
1351
|
-
abi: RollupAbi,
|
|
1352
|
-
functionName: 'validateBlobs',
|
|
1353
|
-
args: [blobInput],
|
|
1354
|
-
});
|
|
1355
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1356
|
-
this.backupFailedTx({
|
|
1357
|
-
id: keccak256(validateBlobsData),
|
|
1358
|
-
failureType: 'simulation',
|
|
1359
|
-
request: { to: this.rollupContract.address as Hex, data: validateBlobsData },
|
|
1360
|
-
blobData: encodedData.blobs.map(b => toHex(b.data)) as Hex[],
|
|
1361
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1362
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1363
|
-
context: {
|
|
1364
|
-
actions: ['validate-blobs'],
|
|
1365
|
-
sender: this.getSenderAddress().toString(),
|
|
1366
|
-
},
|
|
1367
|
-
});
|
|
1149
|
+
.catch(err => {
|
|
1150
|
+
const { message, metaMessages } = formatViemError(err);
|
|
1151
|
+
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
1368
1152
|
throw new Error('Failed to validate blobs');
|
|
1369
1153
|
});
|
|
1370
1154
|
}
|
|
@@ -1384,7 +1168,7 @@ export class SequencerPublisher {
|
|
|
1384
1168
|
blobInput,
|
|
1385
1169
|
] as const;
|
|
1386
1170
|
|
|
1387
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1171
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
|
|
1388
1172
|
|
|
1389
1173
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1390
1174
|
}
|
|
@@ -1392,7 +1176,6 @@ export class SequencerPublisher {
|
|
|
1392
1176
|
/**
|
|
1393
1177
|
* Simulates the propose tx with eth_simulateV1
|
|
1394
1178
|
* @param args - The propose tx args
|
|
1395
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1396
1179
|
* @returns The simulation result
|
|
1397
1180
|
*/
|
|
1398
1181
|
private async simulateProposeTx(
|
|
@@ -1409,7 +1192,6 @@ export class SequencerPublisher {
|
|
|
1409
1192
|
ViemSignature,
|
|
1410
1193
|
`0x${string}`,
|
|
1411
1194
|
],
|
|
1412
|
-
timestamp: bigint,
|
|
1413
1195
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1414
1196
|
) {
|
|
1415
1197
|
const rollupData = encodeFunctionData({
|
|
@@ -1443,7 +1225,7 @@ export class SequencerPublisher {
|
|
|
1443
1225
|
});
|
|
1444
1226
|
}
|
|
1445
1227
|
|
|
1446
|
-
const
|
|
1228
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1447
1229
|
|
|
1448
1230
|
const simulationResult = await this.l1TxUtils
|
|
1449
1231
|
.simulate(
|
|
@@ -1454,8 +1236,7 @@ export class SequencerPublisher {
|
|
|
1454
1236
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1455
1237
|
},
|
|
1456
1238
|
{
|
|
1457
|
-
|
|
1458
|
-
time: timestamp + 1n,
|
|
1239
|
+
time: simTs,
|
|
1459
1240
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1460
1241
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1461
1242
|
},
|
|
@@ -1477,19 +1258,7 @@ export class SequencerPublisher {
|
|
|
1477
1258
|
logs: [],
|
|
1478
1259
|
};
|
|
1479
1260
|
}
|
|
1480
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1481
|
-
this.backupFailedTx({
|
|
1482
|
-
id: keccak256(rollupData),
|
|
1483
|
-
failureType: 'simulation',
|
|
1484
|
-
request: { to: this.rollupContract.address, data: rollupData },
|
|
1485
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1486
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1487
|
-
context: {
|
|
1488
|
-
actions: ['propose'],
|
|
1489
|
-
slot: Number(args[0].header.slotNumber),
|
|
1490
|
-
sender: this.getSenderAddress().toString(),
|
|
1491
|
-
},
|
|
1492
|
-
});
|
|
1261
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1493
1262
|
throw err;
|
|
1494
1263
|
});
|
|
1495
1264
|
|
|
@@ -1500,16 +1269,11 @@ export class SequencerPublisher {
|
|
|
1500
1269
|
checkpoint: Checkpoint,
|
|
1501
1270
|
encodedData: L1ProcessArgs,
|
|
1502
1271
|
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1503
|
-
timestamp: bigint,
|
|
1504
1272
|
): Promise<void> {
|
|
1505
1273
|
const slot = checkpoint.header.slotNumber;
|
|
1506
1274
|
const timer = new Timer();
|
|
1507
1275
|
const kzg = Blob.getViemKzgInstance();
|
|
1508
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1509
|
-
encodedData,
|
|
1510
|
-
timestamp,
|
|
1511
|
-
opts,
|
|
1512
|
-
);
|
|
1276
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
|
|
1513
1277
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1514
1278
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
1515
1279
|
BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
|
|
@@ -1585,4 +1349,17 @@ export class SequencerPublisher {
|
|
|
1585
1349
|
},
|
|
1586
1350
|
});
|
|
1587
1351
|
}
|
|
1352
|
+
|
|
1353
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1354
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
|
|
1355
|
+
private getSimulationTimestamp(slot: SlotNumber): bigint {
|
|
1356
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1357
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */
|
|
1361
|
+
private getNextL1SlotTimestamp(): bigint {
|
|
1362
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1363
|
+
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|
|
1364
|
+
}
|
|
1588
1365
|
}
|