@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
|
@@ -28,9 +28,9 @@ import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from
|
|
|
28
28
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
29
29
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
30
30
|
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
31
|
+
import { trimmedBytesLength } from '@aztec/foundation/buffer';
|
|
31
32
|
import { pick } from '@aztec/foundation/collection';
|
|
32
33
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
33
|
-
import { TimeoutError } from '@aztec/foundation/error';
|
|
34
34
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
35
35
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
36
36
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -41,24 +41,15 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
|
41
41
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
42
42
|
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
43
43
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
44
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
44
45
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
45
46
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
46
47
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
47
48
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
48
49
|
|
|
49
|
-
import {
|
|
50
|
-
type Hex,
|
|
51
|
-
type StateOverride,
|
|
52
|
-
type TransactionReceipt,
|
|
53
|
-
type TypedDataDefinition,
|
|
54
|
-
encodeFunctionData,
|
|
55
|
-
keccak256,
|
|
56
|
-
multicall3Abi,
|
|
57
|
-
toHex,
|
|
58
|
-
} from 'viem';
|
|
50
|
+
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
59
51
|
|
|
60
52
|
import type { SequencerPublisherConfig } from './config.js';
|
|
61
|
-
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
62
53
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
63
54
|
|
|
64
55
|
/** Arguments to the process method of the rollup contract */
|
|
@@ -120,7 +111,6 @@ export class SequencerPublisher {
|
|
|
120
111
|
private interrupted = false;
|
|
121
112
|
private metrics: SequencerPublisherMetrics;
|
|
122
113
|
public epochCache: EpochCache;
|
|
123
|
-
private failedTxStore?: Promise<L1TxFailedStore | undefined>;
|
|
124
114
|
|
|
125
115
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
126
116
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
@@ -132,15 +122,14 @@ export class SequencerPublisher {
|
|
|
132
122
|
|
|
133
123
|
protected log: Logger;
|
|
134
124
|
protected ethereumSlotDuration: bigint;
|
|
125
|
+
protected aztecSlotDuration: bigint;
|
|
126
|
+
private dateProvider: DateProvider;
|
|
135
127
|
|
|
136
128
|
private blobClient: BlobClientInterface;
|
|
137
129
|
|
|
138
130
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
139
131
|
private proposerAddressForSimulation?: EthAddress;
|
|
140
132
|
|
|
141
|
-
/** Optional callback to obtain a replacement publisher when the current one fails to send. */
|
|
142
|
-
private getNextPublisher?: (excludeAddresses: EthAddress[]) => Promise<L1TxUtils | undefined>;
|
|
143
|
-
|
|
144
133
|
/** L1 fee analyzer for fisherman mode */
|
|
145
134
|
private l1FeeAnalyzer?: L1FeeAnalyzer;
|
|
146
135
|
|
|
@@ -164,8 +153,8 @@ export class SequencerPublisher {
|
|
|
164
153
|
protected requests: RequestWithExpiry[] = [];
|
|
165
154
|
|
|
166
155
|
constructor(
|
|
167
|
-
private config: Pick<SequencerPublisherConfig, 'fishermanMode'
|
|
168
|
-
Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
|
|
156
|
+
private config: Pick<SequencerPublisherConfig, 'fishermanMode'> &
|
|
157
|
+
Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration'> & { l1ChainId: number },
|
|
169
158
|
deps: {
|
|
170
159
|
telemetry?: TelemetryClient;
|
|
171
160
|
blobClient: BlobClientInterface;
|
|
@@ -179,11 +168,12 @@ export class SequencerPublisher {
|
|
|
179
168
|
metrics: SequencerPublisherMetrics;
|
|
180
169
|
lastActions: Partial<Record<Action, SlotNumber>>;
|
|
181
170
|
log?: Logger;
|
|
182
|
-
getNextPublisher?: (excludeAddresses: EthAddress[]) => Promise<L1TxUtils | undefined>;
|
|
183
171
|
},
|
|
184
172
|
) {
|
|
185
173
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
186
174
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
175
|
+
this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
|
|
176
|
+
this.dateProvider = deps.dateProvider;
|
|
187
177
|
this.epochCache = deps.epochCache;
|
|
188
178
|
this.lastActions = deps.lastActions;
|
|
189
179
|
|
|
@@ -193,7 +183,6 @@ export class SequencerPublisher {
|
|
|
193
183
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
194
184
|
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
195
185
|
this.l1TxUtils = deps.l1TxUtils;
|
|
196
|
-
this.getNextPublisher = deps.getNextPublisher;
|
|
197
186
|
|
|
198
187
|
this.rollupContract = deps.rollupContract;
|
|
199
188
|
|
|
@@ -222,31 +211,6 @@ export class SequencerPublisher {
|
|
|
222
211
|
this.rollupContract,
|
|
223
212
|
createLogger('sequencer:publisher:price-oracle'),
|
|
224
213
|
);
|
|
225
|
-
|
|
226
|
-
// Initialize failed L1 tx store (optional, for test networks)
|
|
227
|
-
this.failedTxStore = createL1TxFailedStore(config.l1TxFailedStore, this.log);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Backs up a failed L1 transaction to the configured store for debugging.
|
|
232
|
-
* Does nothing if no store is configured.
|
|
233
|
-
*/
|
|
234
|
-
private backupFailedTx(failedTx: Omit<FailedL1Tx, 'timestamp'>): void {
|
|
235
|
-
if (!this.failedTxStore) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const tx: FailedL1Tx = {
|
|
240
|
-
...failedTx,
|
|
241
|
-
timestamp: Date.now(),
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
// Fire and forget - don't block on backup
|
|
245
|
-
void this.failedTxStore
|
|
246
|
-
.then(store => store?.saveFailedTx(tx))
|
|
247
|
-
.catch(err => {
|
|
248
|
-
this.log.warn(`Failed to backup failed L1 tx to store`, err);
|
|
249
|
-
});
|
|
250
214
|
}
|
|
251
215
|
|
|
252
216
|
public getRollupContract(): RollupContract {
|
|
@@ -398,8 +362,8 @@ export class SequencerPublisher {
|
|
|
398
362
|
// @note - we can only have one blob config per bundle
|
|
399
363
|
// find requests with gas and blob configs
|
|
400
364
|
// See https://github.com/AztecProtocol/aztec-packages/issues/11513
|
|
401
|
-
const gasConfigs =
|
|
402
|
-
const blobConfigs =
|
|
365
|
+
const gasConfigs = validRequests.filter(request => request.gasConfig).map(request => request.gasConfig);
|
|
366
|
+
const blobConfigs = validRequests.filter(request => request.blobConfig).map(request => request.blobConfig);
|
|
403
367
|
|
|
404
368
|
if (blobConfigs.length > 1) {
|
|
405
369
|
throw new Error('Multiple blob configs found');
|
|
@@ -428,36 +392,19 @@ export class SequencerPublisher {
|
|
|
428
392
|
validRequests.sort((a, b) => compareActions(a.action, b.action));
|
|
429
393
|
|
|
430
394
|
try {
|
|
431
|
-
// Capture context for failed tx backup before sending
|
|
432
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
433
|
-
const multicallData = encodeFunctionData({
|
|
434
|
-
abi: multicall3Abi,
|
|
435
|
-
functionName: 'aggregate3',
|
|
436
|
-
args: [
|
|
437
|
-
validRequests.map(r => ({
|
|
438
|
-
target: r.request.to!,
|
|
439
|
-
callData: r.request.data!,
|
|
440
|
-
allowFailure: true,
|
|
441
|
-
})),
|
|
442
|
-
],
|
|
443
|
-
});
|
|
444
|
-
const blobDataHex = blobConfig?.blobs?.map(b => toHex(b)) as Hex[] | undefined;
|
|
445
|
-
|
|
446
|
-
const txContext = { multicallData, blobData: blobDataHex, l1BlockNumber };
|
|
447
|
-
|
|
448
395
|
this.log.debug('Forwarding transactions', {
|
|
449
396
|
validRequests: validRequests.map(request => request.action),
|
|
450
397
|
txConfig,
|
|
451
398
|
});
|
|
452
|
-
const result = await
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
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,
|
|
460
406
|
);
|
|
407
|
+
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result);
|
|
461
408
|
return { result, expiredActions, sentActions: validActions, successfulActions, failedActions };
|
|
462
409
|
} catch (err) {
|
|
463
410
|
const viemError = formatViemError(err);
|
|
@@ -475,79 +422,25 @@ export class SequencerPublisher {
|
|
|
475
422
|
}
|
|
476
423
|
}
|
|
477
424
|
|
|
478
|
-
/**
|
|
479
|
-
* Forwards transactions via Multicall3, rotating to the next available publisher if a send
|
|
480
|
-
* failure occurs (i.e. the tx never reached the chain).
|
|
481
|
-
* On-chain reverts and simulation errors are returned as-is without rotation.
|
|
482
|
-
*/
|
|
483
|
-
private async forwardWithPublisherRotation(
|
|
484
|
-
validRequests: RequestWithExpiry[],
|
|
485
|
-
txConfig: RequestWithExpiry['gasConfig'],
|
|
486
|
-
blobConfig: L1BlobInputs | undefined,
|
|
487
|
-
) {
|
|
488
|
-
const triedAddresses: EthAddress[] = [];
|
|
489
|
-
let currentPublisher = this.l1TxUtils;
|
|
490
|
-
|
|
491
|
-
while (true) {
|
|
492
|
-
triedAddresses.push(currentPublisher.getSenderAddress());
|
|
493
|
-
try {
|
|
494
|
-
const result = await Multicall3.forward(
|
|
495
|
-
validRequests.map(r => r.request),
|
|
496
|
-
currentPublisher,
|
|
497
|
-
txConfig,
|
|
498
|
-
blobConfig,
|
|
499
|
-
this.rollupContract.address,
|
|
500
|
-
this.log,
|
|
501
|
-
);
|
|
502
|
-
this.l1TxUtils = currentPublisher;
|
|
503
|
-
return result;
|
|
504
|
-
} catch (err) {
|
|
505
|
-
if (err instanceof TimeoutError) {
|
|
506
|
-
throw err;
|
|
507
|
-
}
|
|
508
|
-
const viemError = formatViemError(err);
|
|
509
|
-
if (!this.getNextPublisher) {
|
|
510
|
-
this.log.error('Failed to publish bundled transactions', viemError);
|
|
511
|
-
return undefined;
|
|
512
|
-
}
|
|
513
|
-
this.log.warn(
|
|
514
|
-
`Publisher ${currentPublisher.getSenderAddress()} failed to send, rotating to next publisher`,
|
|
515
|
-
viemError,
|
|
516
|
-
);
|
|
517
|
-
const nextPublisher = await this.getNextPublisher([...triedAddresses]);
|
|
518
|
-
if (!nextPublisher) {
|
|
519
|
-
this.log.error('All available publishers exhausted, failed to publish bundled transactions');
|
|
520
|
-
return undefined;
|
|
521
|
-
}
|
|
522
|
-
currentPublisher = nextPublisher;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
425
|
private callbackBundledTransactions(
|
|
528
426
|
requests: RequestWithExpiry[],
|
|
529
|
-
result
|
|
530
|
-
txContext: { multicallData: Hex; blobData?: Hex[]; l1BlockNumber: bigint },
|
|
427
|
+
result?: { receipt: TransactionReceipt } | FormattedViemError,
|
|
531
428
|
) {
|
|
532
429
|
const actionsListStr = requests.map(r => r.action).join(', ');
|
|
533
430
|
if (result instanceof FormattedViemError) {
|
|
534
431
|
this.log.error(`Failed to publish bundled transactions (${actionsListStr})`, result);
|
|
535
|
-
this.backupFailedTx({
|
|
536
|
-
id: keccak256(txContext.multicallData),
|
|
537
|
-
failureType: 'send-error',
|
|
538
|
-
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
539
|
-
blobData: txContext.blobData,
|
|
540
|
-
l1BlockNumber: txContext.l1BlockNumber.toString(),
|
|
541
|
-
error: { message: result.message, name: result.name },
|
|
542
|
-
context: {
|
|
543
|
-
actions: requests.map(r => r.action),
|
|
544
|
-
requests: requests.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
545
|
-
sender: this.getSenderAddress().toString(),
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
432
|
return { failedActions: requests.map(r => r.action) };
|
|
549
433
|
} else {
|
|
550
|
-
this.log.verbose(`Published bundled transactions (${actionsListStr})`, {
|
|
434
|
+
this.log.verbose(`Published bundled transactions (${actionsListStr})`, {
|
|
435
|
+
result,
|
|
436
|
+
requests: requests.map(r => ({
|
|
437
|
+
...r,
|
|
438
|
+
// Avoid logging large blob data
|
|
439
|
+
blobConfig: r.blobConfig
|
|
440
|
+
? { ...r.blobConfig, blobs: r.blobConfig.blobs.map(b => ({ size: trimmedBytesLength(b) })) }
|
|
441
|
+
: undefined,
|
|
442
|
+
})),
|
|
443
|
+
});
|
|
551
444
|
const successfulActions: Action[] = [];
|
|
552
445
|
const failedActions: Action[] = [];
|
|
553
446
|
for (const request of requests) {
|
|
@@ -557,49 +450,29 @@ export class SequencerPublisher {
|
|
|
557
450
|
failedActions.push(request.action);
|
|
558
451
|
}
|
|
559
452
|
}
|
|
560
|
-
// Single backup for the whole reverted tx
|
|
561
|
-
if (failedActions.length > 0 && result?.receipt?.status === 'reverted') {
|
|
562
|
-
this.backupFailedTx({
|
|
563
|
-
id: result.receipt.transactionHash,
|
|
564
|
-
failureType: 'revert',
|
|
565
|
-
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
566
|
-
blobData: txContext.blobData,
|
|
567
|
-
l1BlockNumber: result.receipt.blockNumber.toString(),
|
|
568
|
-
receipt: {
|
|
569
|
-
transactionHash: result.receipt.transactionHash,
|
|
570
|
-
blockNumber: result.receipt.blockNumber.toString(),
|
|
571
|
-
gasUsed: (result.receipt.gasUsed ?? 0n).toString(),
|
|
572
|
-
status: 'reverted',
|
|
573
|
-
},
|
|
574
|
-
error: { message: result.errorMsg ?? 'Transaction reverted' },
|
|
575
|
-
context: {
|
|
576
|
-
actions: failedActions,
|
|
577
|
-
requests: requests
|
|
578
|
-
.filter(r => failedActions.includes(r.action))
|
|
579
|
-
.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
580
|
-
sender: this.getSenderAddress().toString(),
|
|
581
|
-
},
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
453
|
return { successfulActions, failedActions };
|
|
585
454
|
}
|
|
586
455
|
}
|
|
587
456
|
|
|
588
457
|
/**
|
|
589
|
-
* @notice Will call `
|
|
458
|
+
* @notice Will call `canProposeAt` to make sure that it is possible to propose
|
|
590
459
|
* @param tipArchive - The archive to check
|
|
591
460
|
* @returns The slot and block number if it is possible to propose, undefined otherwise
|
|
592
461
|
*/
|
|
593
|
-
public
|
|
462
|
+
public canProposeAt(
|
|
594
463
|
tipArchive: Fr,
|
|
595
464
|
msgSender: EthAddress,
|
|
596
|
-
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
465
|
+
opts: { forcePendingCheckpointNumber?: CheckpointNumber; pipelined?: boolean } = {},
|
|
597
466
|
) {
|
|
598
467
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
599
468
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
600
469
|
|
|
470
|
+
const pipelined = opts.pipelined ?? false;
|
|
471
|
+
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
472
|
+
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
473
|
+
|
|
601
474
|
return this.rollupContract
|
|
602
|
-
.
|
|
475
|
+
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
603
476
|
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
604
477
|
})
|
|
605
478
|
.catch(err => {
|
|
@@ -636,7 +509,7 @@ export class SequencerPublisher {
|
|
|
636
509
|
flags,
|
|
637
510
|
] as const;
|
|
638
511
|
|
|
639
|
-
const ts =
|
|
512
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
640
513
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
641
514
|
opts?.forcePendingCheckpointNumber,
|
|
642
515
|
);
|
|
@@ -659,7 +532,7 @@ export class SequencerPublisher {
|
|
|
659
532
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
660
533
|
from: MULTI_CALL_3_ADDRESS,
|
|
661
534
|
},
|
|
662
|
-
{ time: ts
|
|
535
|
+
{ time: ts },
|
|
663
536
|
stateOverrides,
|
|
664
537
|
);
|
|
665
538
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -692,8 +565,6 @@ export class SequencerPublisher {
|
|
|
692
565
|
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
693
566
|
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
694
567
|
|
|
695
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
696
|
-
|
|
697
568
|
try {
|
|
698
569
|
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
699
570
|
request,
|
|
@@ -745,18 +616,6 @@ export class SequencerPublisher {
|
|
|
745
616
|
|
|
746
617
|
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
747
618
|
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
748
|
-
this.backupFailedTx({
|
|
749
|
-
id: keccak256(request.data!),
|
|
750
|
-
failureType: 'simulation',
|
|
751
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
752
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
753
|
-
error: { message: viemError.message, name: viemError.name },
|
|
754
|
-
context: {
|
|
755
|
-
actions: [`invalidate-${reason}`],
|
|
756
|
-
checkpointNumber,
|
|
757
|
-
sender: this.getSenderAddress().toString(),
|
|
758
|
-
},
|
|
759
|
-
});
|
|
760
619
|
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
761
620
|
}
|
|
762
621
|
}
|
|
@@ -800,8 +659,7 @@ export class SequencerPublisher {
|
|
|
800
659
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
801
660
|
attestationsAndSignersSignature: Signature,
|
|
802
661
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
803
|
-
): Promise<
|
|
804
|
-
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
662
|
+
): Promise<void> {
|
|
805
663
|
const blobFields = checkpoint.toBlobFields();
|
|
806
664
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
807
665
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -820,13 +678,11 @@ export class SequencerPublisher {
|
|
|
820
678
|
blobInput,
|
|
821
679
|
] as const;
|
|
822
680
|
|
|
823
|
-
await this.simulateProposeTx(args,
|
|
824
|
-
return ts;
|
|
681
|
+
await this.simulateProposeTx(args, options);
|
|
825
682
|
}
|
|
826
683
|
|
|
827
684
|
private async enqueueCastSignalHelper(
|
|
828
685
|
slotNumber: SlotNumber,
|
|
829
|
-
timestamp: bigint,
|
|
830
686
|
signalType: GovernanceSignalAction,
|
|
831
687
|
payload: EthAddress,
|
|
832
688
|
base: IEmpireBase,
|
|
@@ -904,25 +760,15 @@ export class SequencerPublisher {
|
|
|
904
760
|
lastValidL2Slot: slotNumber,
|
|
905
761
|
});
|
|
906
762
|
|
|
907
|
-
const
|
|
763
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
908
764
|
|
|
909
765
|
try {
|
|
910
766
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
911
767
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
912
768
|
} catch (err) {
|
|
913
769
|
const viemError = formatViemError(err);
|
|
914
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
915
|
-
|
|
916
|
-
id: keccak256(request.data!),
|
|
917
|
-
failureType: 'simulation',
|
|
918
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
919
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
920
|
-
error: { message: viemError.message, name: viemError.name },
|
|
921
|
-
context: {
|
|
922
|
-
actions: [action],
|
|
923
|
-
slot: slotNumber,
|
|
924
|
-
sender: this.getSenderAddress().toString(),
|
|
925
|
-
},
|
|
770
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
771
|
+
simulationTimestamp: timestamp,
|
|
926
772
|
});
|
|
927
773
|
// Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
|
|
928
774
|
}
|
|
@@ -974,19 +820,16 @@ export class SequencerPublisher {
|
|
|
974
820
|
/**
|
|
975
821
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
976
822
|
* @param slotNumber - The slot number to cast a signal for.
|
|
977
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
978
823
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
979
824
|
*/
|
|
980
825
|
public enqueueGovernanceCastSignal(
|
|
981
826
|
governancePayload: EthAddress,
|
|
982
827
|
slotNumber: SlotNumber,
|
|
983
|
-
timestamp: bigint,
|
|
984
828
|
signerAddress: EthAddress,
|
|
985
829
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
986
830
|
): Promise<boolean> {
|
|
987
831
|
return this.enqueueCastSignalHelper(
|
|
988
832
|
slotNumber,
|
|
989
|
-
timestamp,
|
|
990
833
|
'governance-signal',
|
|
991
834
|
governancePayload,
|
|
992
835
|
this.govProposerContract,
|
|
@@ -999,7 +842,6 @@ export class SequencerPublisher {
|
|
|
999
842
|
public async enqueueSlashingActions(
|
|
1000
843
|
actions: ProposerSlashAction[],
|
|
1001
844
|
slotNumber: SlotNumber,
|
|
1002
|
-
timestamp: bigint,
|
|
1003
845
|
signerAddress: EthAddress,
|
|
1004
846
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1005
847
|
): Promise<boolean> {
|
|
@@ -1020,7 +862,6 @@ export class SequencerPublisher {
|
|
|
1020
862
|
});
|
|
1021
863
|
await this.enqueueCastSignalHelper(
|
|
1022
864
|
slotNumber,
|
|
1023
|
-
timestamp,
|
|
1024
865
|
'empire-slashing-signal',
|
|
1025
866
|
action.payload,
|
|
1026
867
|
this.slashingProposerContract,
|
|
@@ -1039,7 +880,6 @@ export class SequencerPublisher {
|
|
|
1039
880
|
(receipt: TransactionReceipt) =>
|
|
1040
881
|
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1041
882
|
slotNumber,
|
|
1042
|
-
timestamp,
|
|
1043
883
|
);
|
|
1044
884
|
break;
|
|
1045
885
|
}
|
|
@@ -1057,7 +897,6 @@ export class SequencerPublisher {
|
|
|
1057
897
|
request,
|
|
1058
898
|
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1059
899
|
slotNumber,
|
|
1060
|
-
timestamp,
|
|
1061
900
|
);
|
|
1062
901
|
break;
|
|
1063
902
|
}
|
|
@@ -1081,7 +920,6 @@ export class SequencerPublisher {
|
|
|
1081
920
|
request,
|
|
1082
921
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs),
|
|
1083
922
|
slotNumber,
|
|
1084
|
-
timestamp,
|
|
1085
923
|
);
|
|
1086
924
|
break;
|
|
1087
925
|
}
|
|
@@ -1103,7 +941,6 @@ export class SequencerPublisher {
|
|
|
1103
941
|
request,
|
|
1104
942
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1105
943
|
slotNumber,
|
|
1106
|
-
timestamp,
|
|
1107
944
|
);
|
|
1108
945
|
break;
|
|
1109
946
|
}
|
|
@@ -1139,15 +976,13 @@ export class SequencerPublisher {
|
|
|
1139
976
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1140
977
|
};
|
|
1141
978
|
|
|
1142
|
-
let ts: bigint;
|
|
1143
|
-
|
|
1144
979
|
try {
|
|
1145
980
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1146
981
|
// This means that we can avoid the simulation issues in later checks.
|
|
1147
982
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1148
983
|
// make time consistency checks break.
|
|
1149
984
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1150
|
-
|
|
985
|
+
await this.validateCheckpointForSubmission(
|
|
1151
986
|
checkpoint,
|
|
1152
987
|
attestationsAndSigners,
|
|
1153
988
|
attestationsAndSignersSignature,
|
|
@@ -1163,7 +998,7 @@ export class SequencerPublisher {
|
|
|
1163
998
|
}
|
|
1164
999
|
|
|
1165
1000
|
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
1166
|
-
await this.addProposeTx(checkpoint, proposeTxArgs, opts
|
|
1001
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts);
|
|
1167
1002
|
}
|
|
1168
1003
|
|
|
1169
1004
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1206,8 +1041,8 @@ export class SequencerPublisher {
|
|
|
1206
1041
|
request: L1TxRequest,
|
|
1207
1042
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1208
1043
|
slotNumber: SlotNumber,
|
|
1209
|
-
timestamp: bigint,
|
|
1210
1044
|
) {
|
|
1045
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1211
1046
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1212
1047
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1213
1048
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1219,30 +1054,16 @@ export class SequencerPublisher {
|
|
|
1219
1054
|
|
|
1220
1055
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
1221
1056
|
|
|
1222
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1223
|
-
|
|
1224
1057
|
let gasUsed: bigint;
|
|
1225
1058
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1059
|
+
|
|
1226
1060
|
try {
|
|
1227
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1061
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1228
1062
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1229
1063
|
} catch (err) {
|
|
1230
1064
|
const viemError = formatViemError(err, simulateAbi);
|
|
1231
1065
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1232
1066
|
|
|
1233
|
-
this.backupFailedTx({
|
|
1234
|
-
id: keccak256(request.data!),
|
|
1235
|
-
failureType: 'simulation',
|
|
1236
|
-
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
1237
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1238
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1239
|
-
context: {
|
|
1240
|
-
actions: [action],
|
|
1241
|
-
slot: slotNumber,
|
|
1242
|
-
sender: this.getSenderAddress().toString(),
|
|
1243
|
-
},
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
1067
|
return false;
|
|
1247
1068
|
}
|
|
1248
1069
|
|
|
@@ -1293,7 +1114,6 @@ export class SequencerPublisher {
|
|
|
1293
1114
|
|
|
1294
1115
|
private async prepareProposeTx(
|
|
1295
1116
|
encodedData: L1ProcessArgs,
|
|
1296
|
-
timestamp: bigint,
|
|
1297
1117
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1298
1118
|
) {
|
|
1299
1119
|
const kzg = Blob.getViemKzgInstance();
|
|
@@ -1326,27 +1146,9 @@ export class SequencerPublisher {
|
|
|
1326
1146
|
kzg,
|
|
1327
1147
|
},
|
|
1328
1148
|
)
|
|
1329
|
-
.catch(
|
|
1330
|
-
const
|
|
1331
|
-
this.log.error(`Failed to validate blobs`,
|
|
1332
|
-
const validateBlobsData = encodeFunctionData({
|
|
1333
|
-
abi: RollupAbi,
|
|
1334
|
-
functionName: 'validateBlobs',
|
|
1335
|
-
args: [blobInput],
|
|
1336
|
-
});
|
|
1337
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1338
|
-
this.backupFailedTx({
|
|
1339
|
-
id: keccak256(validateBlobsData),
|
|
1340
|
-
failureType: 'simulation',
|
|
1341
|
-
request: { to: this.rollupContract.address as Hex, data: validateBlobsData },
|
|
1342
|
-
blobData: encodedData.blobs.map(b => toHex(b.data)) as Hex[],
|
|
1343
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1344
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1345
|
-
context: {
|
|
1346
|
-
actions: ['validate-blobs'],
|
|
1347
|
-
sender: this.getSenderAddress().toString(),
|
|
1348
|
-
},
|
|
1349
|
-
});
|
|
1149
|
+
.catch(err => {
|
|
1150
|
+
const { message, metaMessages } = formatViemError(err);
|
|
1151
|
+
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
1350
1152
|
throw new Error('Failed to validate blobs');
|
|
1351
1153
|
});
|
|
1352
1154
|
}
|
|
@@ -1366,7 +1168,7 @@ export class SequencerPublisher {
|
|
|
1366
1168
|
blobInput,
|
|
1367
1169
|
] as const;
|
|
1368
1170
|
|
|
1369
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1171
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
|
|
1370
1172
|
|
|
1371
1173
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1372
1174
|
}
|
|
@@ -1374,7 +1176,6 @@ export class SequencerPublisher {
|
|
|
1374
1176
|
/**
|
|
1375
1177
|
* Simulates the propose tx with eth_simulateV1
|
|
1376
1178
|
* @param args - The propose tx args
|
|
1377
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1378
1179
|
* @returns The simulation result
|
|
1379
1180
|
*/
|
|
1380
1181
|
private async simulateProposeTx(
|
|
@@ -1391,7 +1192,6 @@ export class SequencerPublisher {
|
|
|
1391
1192
|
ViemSignature,
|
|
1392
1193
|
`0x${string}`,
|
|
1393
1194
|
],
|
|
1394
|
-
timestamp: bigint,
|
|
1395
1195
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1396
1196
|
) {
|
|
1397
1197
|
const rollupData = encodeFunctionData({
|
|
@@ -1425,7 +1225,7 @@ export class SequencerPublisher {
|
|
|
1425
1225
|
});
|
|
1426
1226
|
}
|
|
1427
1227
|
|
|
1428
|
-
const
|
|
1228
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1429
1229
|
|
|
1430
1230
|
const simulationResult = await this.l1TxUtils
|
|
1431
1231
|
.simulate(
|
|
@@ -1436,8 +1236,7 @@ export class SequencerPublisher {
|
|
|
1436
1236
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1437
1237
|
},
|
|
1438
1238
|
{
|
|
1439
|
-
|
|
1440
|
-
time: timestamp + 1n,
|
|
1239
|
+
time: simTs,
|
|
1441
1240
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1442
1241
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1443
1242
|
},
|
|
@@ -1459,19 +1258,7 @@ export class SequencerPublisher {
|
|
|
1459
1258
|
logs: [],
|
|
1460
1259
|
};
|
|
1461
1260
|
}
|
|
1462
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1463
|
-
this.backupFailedTx({
|
|
1464
|
-
id: keccak256(rollupData),
|
|
1465
|
-
failureType: 'simulation',
|
|
1466
|
-
request: { to: this.rollupContract.address, data: rollupData },
|
|
1467
|
-
l1BlockNumber: l1BlockNumber.toString(),
|
|
1468
|
-
error: { message: viemError.message, name: viemError.name },
|
|
1469
|
-
context: {
|
|
1470
|
-
actions: ['propose'],
|
|
1471
|
-
slot: Number(args[0].header.slotNumber),
|
|
1472
|
-
sender: this.getSenderAddress().toString(),
|
|
1473
|
-
},
|
|
1474
|
-
});
|
|
1261
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1475
1262
|
throw err;
|
|
1476
1263
|
});
|
|
1477
1264
|
|
|
@@ -1482,16 +1269,11 @@ export class SequencerPublisher {
|
|
|
1482
1269
|
checkpoint: Checkpoint,
|
|
1483
1270
|
encodedData: L1ProcessArgs,
|
|
1484
1271
|
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1485
|
-
timestamp: bigint,
|
|
1486
1272
|
): Promise<void> {
|
|
1487
1273
|
const slot = checkpoint.header.slotNumber;
|
|
1488
1274
|
const timer = new Timer();
|
|
1489
1275
|
const kzg = Blob.getViemKzgInstance();
|
|
1490
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1491
|
-
encodedData,
|
|
1492
|
-
timestamp,
|
|
1493
|
-
opts,
|
|
1494
|
-
);
|
|
1276
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
|
|
1495
1277
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1496
1278
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
1497
1279
|
BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
|
|
@@ -1567,4 +1349,17 @@ export class SequencerPublisher {
|
|
|
1567
1349
|
},
|
|
1568
1350
|
});
|
|
1569
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
|
+
}
|
|
1570
1365
|
}
|