@aztec/sequencer-client 0.0.1-commit.9372f48 → 0.0.1-commit.967fc6998
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 +12 -7
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +15 -4
- package/dest/config.d.ts +3 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +17 -12
- package/dest/global_variable_builder/global_builder.d.ts +2 -4
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/publisher/config.d.ts +35 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +106 -42
- package/dest/publisher/index.d.ts +2 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/index.js +2 -0
- package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +13 -2
- package/dest/publisher/sequencer-publisher.d.ts +22 -7
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +258 -29
- package/dest/sequencer/checkpoint_proposal_job.d.ts +7 -1
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +67 -40
- package/dest/sequencer/metrics.d.ts +17 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +86 -15
- package/dest/sequencer/sequencer.d.ts +17 -7
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +29 -26
- package/dest/sequencer/timetable.d.ts +1 -4
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +2 -5
- package/dest/test/index.d.ts +3 -5
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +5 -3
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +6 -4
- package/dest/test/utils.d.ts +3 -3
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +5 -4
- package/package.json +28 -28
- package/src/client/sequencer-client.ts +25 -7
- package/src/config.ts +26 -19
- package/src/global_variable_builder/global_builder.ts +1 -1
- package/src/publisher/config.ts +121 -43
- package/src/publisher/index.ts +3 -0
- package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
- package/src/publisher/l1_tx_failed_store/index.ts +3 -0
- package/src/publisher/sequencer-publisher-factory.ts +23 -6
- package/src/publisher/sequencer-publisher.ts +241 -36
- package/src/sequencer/checkpoint_proposal_job.ts +91 -39
- package/src/sequencer/metrics.ts +92 -18
- package/src/sequencer/sequencer.ts +39 -31
- package/src/sequencer/timetable.ts +7 -6
- package/src/test/index.ts +2 -4
- package/src/test/mock_checkpoint_builder.ts +12 -1
- package/src/test/utils.ts +5 -2
|
@@ -4,6 +4,7 @@ import type { EpochCache } from '@aztec/epoch-cache';
|
|
|
4
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
5
5
|
import {
|
|
6
6
|
type EmpireSlashingProposerContract,
|
|
7
|
+
FeeAssetPriceOracle,
|
|
7
8
|
type GovernanceProposerContract,
|
|
8
9
|
type IEmpireBase,
|
|
9
10
|
MULTI_CALL_3_ADDRESS,
|
|
@@ -18,11 +19,11 @@ import {
|
|
|
18
19
|
type L1BlobInputs,
|
|
19
20
|
type L1TxConfig,
|
|
20
21
|
type L1TxRequest,
|
|
22
|
+
type L1TxUtils,
|
|
21
23
|
MAX_L1_TX_LIMIT,
|
|
22
24
|
type TransactionStats,
|
|
23
25
|
WEI_CONST,
|
|
24
26
|
} from '@aztec/ethereum/l1-tx-utils';
|
|
25
|
-
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
26
27
|
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
27
28
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
28
29
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
@@ -32,6 +33,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
32
33
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
33
34
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
34
35
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
36
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
35
37
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
36
38
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
37
39
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
@@ -43,9 +45,19 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
|
43
45
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
44
46
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
45
47
|
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
import {
|
|
49
|
+
type Hex,
|
|
50
|
+
type StateOverride,
|
|
51
|
+
type TransactionReceipt,
|
|
52
|
+
type TypedDataDefinition,
|
|
53
|
+
encodeFunctionData,
|
|
54
|
+
keccak256,
|
|
55
|
+
multicall3Abi,
|
|
56
|
+
toHex,
|
|
57
|
+
} from 'viem';
|
|
58
|
+
|
|
59
|
+
import type { SequencerPublisherConfig } from './config.js';
|
|
60
|
+
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
49
61
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
50
62
|
|
|
51
63
|
/** Arguments to the process method of the rollup contract */
|
|
@@ -60,6 +72,8 @@ type L1ProcessArgs = {
|
|
|
60
72
|
attestationsAndSigners: CommitteeAttestationsAndSigners;
|
|
61
73
|
/** Attestations and signers signature */
|
|
62
74
|
attestationsAndSignersSignature: Signature;
|
|
75
|
+
/** The fee asset price modifier in basis points (from oracle) */
|
|
76
|
+
feeAssetPriceModifier: bigint;
|
|
63
77
|
};
|
|
64
78
|
|
|
65
79
|
export const Actions = [
|
|
@@ -105,6 +119,7 @@ export class SequencerPublisher {
|
|
|
105
119
|
private interrupted = false;
|
|
106
120
|
private metrics: SequencerPublisherMetrics;
|
|
107
121
|
public epochCache: EpochCache;
|
|
122
|
+
private failedTxStore?: Promise<L1TxFailedStore | undefined>;
|
|
108
123
|
|
|
109
124
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
110
125
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
@@ -112,6 +127,7 @@ export class SequencerPublisher {
|
|
|
112
127
|
protected lastActions: Partial<Record<Action, SlotNumber>> = {};
|
|
113
128
|
|
|
114
129
|
private isPayloadEmptyCache: Map<string, boolean> = new Map<string, boolean>();
|
|
130
|
+
private payloadProposedCache: Set<string> = new Set<string>();
|
|
115
131
|
|
|
116
132
|
protected log: Logger;
|
|
117
133
|
protected ethereumSlotDuration: bigint;
|
|
@@ -123,13 +139,17 @@ export class SequencerPublisher {
|
|
|
123
139
|
|
|
124
140
|
/** L1 fee analyzer for fisherman mode */
|
|
125
141
|
private l1FeeAnalyzer?: L1FeeAnalyzer;
|
|
142
|
+
|
|
143
|
+
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */
|
|
144
|
+
private feeAssetPriceOracle: FeeAssetPriceOracle;
|
|
145
|
+
|
|
126
146
|
// A CALL to a cold address is 2700 gas
|
|
127
147
|
public static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
128
148
|
|
|
129
149
|
// Gas report for VotingWithSigTest shows a max gas of 100k, but we've seen it cost 700k+ in testnet
|
|
130
150
|
public static VOTE_GAS_GUESS: bigint = 800_000n;
|
|
131
151
|
|
|
132
|
-
public l1TxUtils:
|
|
152
|
+
public l1TxUtils: L1TxUtils;
|
|
133
153
|
public rollupContract: RollupContract;
|
|
134
154
|
public govProposerContract: GovernanceProposerContract;
|
|
135
155
|
public slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -140,11 +160,12 @@ export class SequencerPublisher {
|
|
|
140
160
|
protected requests: RequestWithExpiry[] = [];
|
|
141
161
|
|
|
142
162
|
constructor(
|
|
143
|
-
private config:
|
|
163
|
+
private config: Pick<SequencerPublisherConfig, 'fishermanMode' | 'l1TxFailedStore'> &
|
|
164
|
+
Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
|
|
144
165
|
deps: {
|
|
145
166
|
telemetry?: TelemetryClient;
|
|
146
167
|
blobClient: BlobClientInterface;
|
|
147
|
-
l1TxUtils:
|
|
168
|
+
l1TxUtils: L1TxUtils;
|
|
148
169
|
rollupContract: RollupContract;
|
|
149
170
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
150
171
|
governanceProposerContract: GovernanceProposerContract;
|
|
@@ -188,12 +209,52 @@ export class SequencerPublisher {
|
|
|
188
209
|
createLogger('sequencer:publisher:fee-analyzer'),
|
|
189
210
|
);
|
|
190
211
|
}
|
|
212
|
+
|
|
213
|
+
// Initialize fee asset price oracle
|
|
214
|
+
this.feeAssetPriceOracle = new FeeAssetPriceOracle(
|
|
215
|
+
this.l1TxUtils.client,
|
|
216
|
+
this.rollupContract,
|
|
217
|
+
createLogger('sequencer:publisher:price-oracle'),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
// Initialize failed L1 tx store (optional, for test networks)
|
|
221
|
+
this.failedTxStore = createL1TxFailedStore(config.l1TxFailedStore, this.log);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Backs up a failed L1 transaction to the configured store for debugging.
|
|
226
|
+
* Does nothing if no store is configured.
|
|
227
|
+
*/
|
|
228
|
+
private backupFailedTx(failedTx: Omit<FailedL1Tx, 'timestamp'>): void {
|
|
229
|
+
if (!this.failedTxStore) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const tx: FailedL1Tx = {
|
|
234
|
+
...failedTx,
|
|
235
|
+
timestamp: Date.now(),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Fire and forget - don't block on backup
|
|
239
|
+
void this.failedTxStore
|
|
240
|
+
.then(store => store?.saveFailedTx(tx))
|
|
241
|
+
.catch(err => {
|
|
242
|
+
this.log.warn(`Failed to backup failed L1 tx to store`, err);
|
|
243
|
+
});
|
|
191
244
|
}
|
|
192
245
|
|
|
193
246
|
public getRollupContract(): RollupContract {
|
|
194
247
|
return this.rollupContract;
|
|
195
248
|
}
|
|
196
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Gets the fee asset price modifier from the oracle.
|
|
252
|
+
* Returns 0n if the oracle query fails.
|
|
253
|
+
*/
|
|
254
|
+
public getFeeAssetPriceModifier(): Promise<bigint> {
|
|
255
|
+
return this.feeAssetPriceOracle.computePriceModifier();
|
|
256
|
+
}
|
|
257
|
+
|
|
197
258
|
public getSenderAddress() {
|
|
198
259
|
return this.l1TxUtils.getSenderAddress();
|
|
199
260
|
}
|
|
@@ -361,6 +422,21 @@ export class SequencerPublisher {
|
|
|
361
422
|
validRequests.sort((a, b) => compareActions(a.action, b.action));
|
|
362
423
|
|
|
363
424
|
try {
|
|
425
|
+
// Capture context for failed tx backup before sending
|
|
426
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
427
|
+
const multicallData = encodeFunctionData({
|
|
428
|
+
abi: multicall3Abi,
|
|
429
|
+
functionName: 'aggregate3',
|
|
430
|
+
args: [
|
|
431
|
+
validRequests.map(r => ({
|
|
432
|
+
target: r.request.to!,
|
|
433
|
+
callData: r.request.data!,
|
|
434
|
+
allowFailure: true,
|
|
435
|
+
})),
|
|
436
|
+
],
|
|
437
|
+
});
|
|
438
|
+
const blobDataHex = blobConfig?.blobs?.map(b => toHex(b)) as Hex[] | undefined;
|
|
439
|
+
|
|
364
440
|
this.log.debug('Forwarding transactions', {
|
|
365
441
|
validRequests: validRequests.map(request => request.action),
|
|
366
442
|
txConfig,
|
|
@@ -373,7 +449,12 @@ export class SequencerPublisher {
|
|
|
373
449
|
this.rollupContract.address,
|
|
374
450
|
this.log,
|
|
375
451
|
);
|
|
376
|
-
const
|
|
452
|
+
const txContext = { multicallData, blobData: blobDataHex, l1BlockNumber };
|
|
453
|
+
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(
|
|
454
|
+
validRequests,
|
|
455
|
+
result,
|
|
456
|
+
txContext,
|
|
457
|
+
);
|
|
377
458
|
return { result, expiredActions, sentActions: validActions, successfulActions, failedActions };
|
|
378
459
|
} catch (err) {
|
|
379
460
|
const viemError = formatViemError(err);
|
|
@@ -393,11 +474,25 @@ export class SequencerPublisher {
|
|
|
393
474
|
|
|
394
475
|
private callbackBundledTransactions(
|
|
395
476
|
requests: RequestWithExpiry[],
|
|
396
|
-
result
|
|
477
|
+
result: { receipt: TransactionReceipt; errorMsg?: string } | FormattedViemError | undefined,
|
|
478
|
+
txContext: { multicallData: Hex; blobData?: Hex[]; l1BlockNumber: bigint },
|
|
397
479
|
) {
|
|
398
480
|
const actionsListStr = requests.map(r => r.action).join(', ');
|
|
399
481
|
if (result instanceof FormattedViemError) {
|
|
400
482
|
this.log.error(`Failed to publish bundled transactions (${actionsListStr})`, result);
|
|
483
|
+
this.backupFailedTx({
|
|
484
|
+
id: keccak256(txContext.multicallData),
|
|
485
|
+
failureType: 'send-error',
|
|
486
|
+
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
487
|
+
blobData: txContext.blobData,
|
|
488
|
+
l1BlockNumber: txContext.l1BlockNumber.toString(),
|
|
489
|
+
error: { message: result.message, name: result.name },
|
|
490
|
+
context: {
|
|
491
|
+
actions: requests.map(r => r.action),
|
|
492
|
+
requests: requests.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
493
|
+
sender: this.getSenderAddress().toString(),
|
|
494
|
+
},
|
|
495
|
+
});
|
|
401
496
|
return { failedActions: requests.map(r => r.action) };
|
|
402
497
|
} else {
|
|
403
498
|
this.log.verbose(`Published bundled transactions (${actionsListStr})`, { result, requests });
|
|
@@ -410,6 +505,30 @@ export class SequencerPublisher {
|
|
|
410
505
|
failedActions.push(request.action);
|
|
411
506
|
}
|
|
412
507
|
}
|
|
508
|
+
// Single backup for the whole reverted tx
|
|
509
|
+
if (failedActions.length > 0 && result?.receipt?.status === 'reverted') {
|
|
510
|
+
this.backupFailedTx({
|
|
511
|
+
id: result.receipt.transactionHash,
|
|
512
|
+
failureType: 'revert',
|
|
513
|
+
request: { to: MULTI_CALL_3_ADDRESS, data: txContext.multicallData },
|
|
514
|
+
blobData: txContext.blobData,
|
|
515
|
+
l1BlockNumber: result.receipt.blockNumber.toString(),
|
|
516
|
+
receipt: {
|
|
517
|
+
transactionHash: result.receipt.transactionHash,
|
|
518
|
+
blockNumber: result.receipt.blockNumber.toString(),
|
|
519
|
+
gasUsed: (result.receipt.gasUsed ?? 0n).toString(),
|
|
520
|
+
status: 'reverted',
|
|
521
|
+
},
|
|
522
|
+
error: { message: result.errorMsg ?? 'Transaction reverted' },
|
|
523
|
+
context: {
|
|
524
|
+
actions: failedActions,
|
|
525
|
+
requests: requests
|
|
526
|
+
.filter(r => failedActions.includes(r.action))
|
|
527
|
+
.map(r => ({ action: r.action, to: r.request.to! as Hex, data: r.request.data! })),
|
|
528
|
+
sender: this.getSenderAddress().toString(),
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
}
|
|
413
532
|
return { successfulActions, failedActions };
|
|
414
533
|
}
|
|
415
534
|
}
|
|
@@ -521,6 +640,8 @@ export class SequencerPublisher {
|
|
|
521
640
|
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
522
641
|
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
523
642
|
|
|
643
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
644
|
+
|
|
524
645
|
try {
|
|
525
646
|
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
526
647
|
request,
|
|
@@ -572,6 +693,18 @@ export class SequencerPublisher {
|
|
|
572
693
|
|
|
573
694
|
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
574
695
|
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
696
|
+
this.backupFailedTx({
|
|
697
|
+
id: keccak256(request.data!),
|
|
698
|
+
failureType: 'simulation',
|
|
699
|
+
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
700
|
+
l1BlockNumber: l1BlockNumber.toString(),
|
|
701
|
+
error: { message: viemError.message, name: viemError.name },
|
|
702
|
+
context: {
|
|
703
|
+
actions: [`invalidate-${reason}`],
|
|
704
|
+
checkpointNumber,
|
|
705
|
+
sender: this.getSenderAddress().toString(),
|
|
706
|
+
},
|
|
707
|
+
});
|
|
575
708
|
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
576
709
|
}
|
|
577
710
|
}
|
|
@@ -617,24 +750,8 @@ export class SequencerPublisher {
|
|
|
617
750
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
618
751
|
): Promise<bigint> {
|
|
619
752
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
620
|
-
|
|
621
|
-
// TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
|
|
622
|
-
// If we have no attestations, we still need to provide the empty attestations
|
|
623
|
-
// so that the committee is recalculated correctly
|
|
624
|
-
// const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
625
|
-
// if (ignoreSignatures) {
|
|
626
|
-
// const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
627
|
-
// if (!committee) {
|
|
628
|
-
// this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
629
|
-
// throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
630
|
-
// }
|
|
631
|
-
// attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
632
|
-
// CommitteeAttestation.fromAddress(committeeMember),
|
|
633
|
-
// );
|
|
634
|
-
// }
|
|
635
|
-
|
|
636
753
|
const blobFields = checkpoint.toBlobFields();
|
|
637
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
754
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
638
755
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
639
756
|
|
|
640
757
|
const args = [
|
|
@@ -642,7 +759,7 @@ export class SequencerPublisher {
|
|
|
642
759
|
header: checkpoint.header.toViem(),
|
|
643
760
|
archive: toHex(checkpoint.archive.root.toBuffer()),
|
|
644
761
|
oracleInput: {
|
|
645
|
-
feeAssetPriceModifier:
|
|
762
|
+
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
646
763
|
},
|
|
647
764
|
},
|
|
648
765
|
attestationsAndSigners.getPackedAttestations(),
|
|
@@ -691,6 +808,32 @@ export class SequencerPublisher {
|
|
|
691
808
|
return false;
|
|
692
809
|
}
|
|
693
810
|
|
|
811
|
+
// Check if payload was already submitted to governance
|
|
812
|
+
const cacheKey = payload.toString();
|
|
813
|
+
if (!this.payloadProposedCache.has(cacheKey)) {
|
|
814
|
+
try {
|
|
815
|
+
const l1StartBlock = await this.rollupContract.getL1StartBlock();
|
|
816
|
+
const proposed = await retry(
|
|
817
|
+
() => base.hasPayloadBeenProposed(payload.toString(), l1StartBlock),
|
|
818
|
+
'Check if payload was proposed',
|
|
819
|
+
makeBackoff([0, 1, 2]),
|
|
820
|
+
this.log,
|
|
821
|
+
true,
|
|
822
|
+
);
|
|
823
|
+
if (proposed) {
|
|
824
|
+
this.payloadProposedCache.add(cacheKey);
|
|
825
|
+
}
|
|
826
|
+
} catch (err) {
|
|
827
|
+
this.log.warn(`Failed to check if payload ${payload} was proposed after retries, skipping signal`, err);
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (this.payloadProposedCache.has(cacheKey)) {
|
|
833
|
+
this.log.info(`Payload ${payload} was already proposed to governance, stopping signals`);
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
|
|
694
837
|
const cachedLastVote = this.lastActions[signalType];
|
|
695
838
|
this.lastActions[signalType] = slotNumber;
|
|
696
839
|
const action = signalType;
|
|
@@ -709,11 +852,26 @@ export class SequencerPublisher {
|
|
|
709
852
|
lastValidL2Slot: slotNumber,
|
|
710
853
|
});
|
|
711
854
|
|
|
855
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
856
|
+
|
|
712
857
|
try {
|
|
713
858
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
714
859
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
715
860
|
} catch (err) {
|
|
716
|
-
|
|
861
|
+
const viemError = formatViemError(err);
|
|
862
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError);
|
|
863
|
+
this.backupFailedTx({
|
|
864
|
+
id: keccak256(request.data!),
|
|
865
|
+
failureType: 'simulation',
|
|
866
|
+
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
867
|
+
l1BlockNumber: l1BlockNumber.toString(),
|
|
868
|
+
error: { message: viemError.message, name: viemError.name },
|
|
869
|
+
context: {
|
|
870
|
+
actions: [action],
|
|
871
|
+
slot: slotNumber,
|
|
872
|
+
sender: this.getSenderAddress().toString(),
|
|
873
|
+
},
|
|
874
|
+
});
|
|
717
875
|
// Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
|
|
718
876
|
}
|
|
719
877
|
|
|
@@ -918,14 +1076,15 @@ export class SequencerPublisher {
|
|
|
918
1076
|
const checkpointHeader = checkpoint.header;
|
|
919
1077
|
|
|
920
1078
|
const blobFields = checkpoint.toBlobFields();
|
|
921
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
1079
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
922
1080
|
|
|
923
|
-
const proposeTxArgs = {
|
|
1081
|
+
const proposeTxArgs: L1ProcessArgs = {
|
|
924
1082
|
header: checkpointHeader,
|
|
925
1083
|
archive: checkpoint.archive.root.toBuffer(),
|
|
926
1084
|
blobs,
|
|
927
1085
|
attestationsAndSigners,
|
|
928
1086
|
attestationsAndSignersSignature,
|
|
1087
|
+
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
929
1088
|
};
|
|
930
1089
|
|
|
931
1090
|
let ts: bigint;
|
|
@@ -1008,6 +1167,8 @@ export class SequencerPublisher {
|
|
|
1008
1167
|
|
|
1009
1168
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
1010
1169
|
|
|
1170
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1171
|
+
|
|
1011
1172
|
let gasUsed: bigint;
|
|
1012
1173
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1013
1174
|
try {
|
|
@@ -1017,6 +1178,19 @@ export class SequencerPublisher {
|
|
|
1017
1178
|
const viemError = formatViemError(err, simulateAbi);
|
|
1018
1179
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1019
1180
|
|
|
1181
|
+
this.backupFailedTx({
|
|
1182
|
+
id: keccak256(request.data!),
|
|
1183
|
+
failureType: 'simulation',
|
|
1184
|
+
request: { to: request.to!, data: request.data!, value: request.value?.toString() },
|
|
1185
|
+
l1BlockNumber: l1BlockNumber.toString(),
|
|
1186
|
+
error: { message: viemError.message, name: viemError.name },
|
|
1187
|
+
context: {
|
|
1188
|
+
actions: [action],
|
|
1189
|
+
slot: slotNumber,
|
|
1190
|
+
sender: this.getSenderAddress().toString(),
|
|
1191
|
+
},
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1020
1194
|
return false;
|
|
1021
1195
|
}
|
|
1022
1196
|
|
|
@@ -1100,9 +1274,27 @@ export class SequencerPublisher {
|
|
|
1100
1274
|
kzg,
|
|
1101
1275
|
},
|
|
1102
1276
|
)
|
|
1103
|
-
.catch(err => {
|
|
1104
|
-
const
|
|
1105
|
-
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
1277
|
+
.catch(async err => {
|
|
1278
|
+
const viemError = formatViemError(err);
|
|
1279
|
+
this.log.error(`Failed to validate blobs`, viemError.message, { metaMessages: viemError.metaMessages });
|
|
1280
|
+
const validateBlobsData = encodeFunctionData({
|
|
1281
|
+
abi: RollupAbi,
|
|
1282
|
+
functionName: 'validateBlobs',
|
|
1283
|
+
args: [blobInput],
|
|
1284
|
+
});
|
|
1285
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1286
|
+
this.backupFailedTx({
|
|
1287
|
+
id: keccak256(validateBlobsData),
|
|
1288
|
+
failureType: 'simulation',
|
|
1289
|
+
request: { to: this.rollupContract.address as Hex, data: validateBlobsData },
|
|
1290
|
+
blobData: encodedData.blobs.map(b => toHex(b.data)) as Hex[],
|
|
1291
|
+
l1BlockNumber: l1BlockNumber.toString(),
|
|
1292
|
+
error: { message: viemError.message, name: viemError.name },
|
|
1293
|
+
context: {
|
|
1294
|
+
actions: ['validate-blobs'],
|
|
1295
|
+
sender: this.getSenderAddress().toString(),
|
|
1296
|
+
},
|
|
1297
|
+
});
|
|
1106
1298
|
throw new Error('Failed to validate blobs');
|
|
1107
1299
|
});
|
|
1108
1300
|
}
|
|
@@ -1113,8 +1305,7 @@ export class SequencerPublisher {
|
|
|
1113
1305
|
header: encodedData.header.toViem(),
|
|
1114
1306
|
archive: toHex(encodedData.archive),
|
|
1115
1307
|
oracleInput: {
|
|
1116
|
-
|
|
1117
|
-
feeAssetPriceModifier: 0n,
|
|
1308
|
+
feeAssetPriceModifier: encodedData.feeAssetPriceModifier,
|
|
1118
1309
|
},
|
|
1119
1310
|
},
|
|
1120
1311
|
encodedData.attestationsAndSigners.getPackedAttestations(),
|
|
@@ -1140,7 +1331,7 @@ export class SequencerPublisher {
|
|
|
1140
1331
|
readonly header: ViemHeader;
|
|
1141
1332
|
readonly archive: `0x${string}`;
|
|
1142
1333
|
readonly oracleInput: {
|
|
1143
|
-
readonly feeAssetPriceModifier:
|
|
1334
|
+
readonly feeAssetPriceModifier: bigint;
|
|
1144
1335
|
};
|
|
1145
1336
|
},
|
|
1146
1337
|
ViemCommitteeAttestations,
|
|
@@ -1182,6 +1373,8 @@ export class SequencerPublisher {
|
|
|
1182
1373
|
});
|
|
1183
1374
|
}
|
|
1184
1375
|
|
|
1376
|
+
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1377
|
+
|
|
1185
1378
|
const simulationResult = await this.l1TxUtils
|
|
1186
1379
|
.simulate(
|
|
1187
1380
|
{
|
|
@@ -1215,6 +1408,18 @@ export class SequencerPublisher {
|
|
|
1215
1408
|
};
|
|
1216
1409
|
}
|
|
1217
1410
|
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1411
|
+
this.backupFailedTx({
|
|
1412
|
+
id: keccak256(rollupData),
|
|
1413
|
+
failureType: 'simulation',
|
|
1414
|
+
request: { to: this.rollupContract.address, data: rollupData },
|
|
1415
|
+
l1BlockNumber: l1BlockNumber.toString(),
|
|
1416
|
+
error: { message: viemError.message, name: viemError.name },
|
|
1417
|
+
context: {
|
|
1418
|
+
actions: ['propose'],
|
|
1419
|
+
slot: Number(args[0].header.slotNumber),
|
|
1420
|
+
sender: this.getSenderAddress().toString(),
|
|
1421
|
+
},
|
|
1422
|
+
});
|
|
1218
1423
|
throw err;
|
|
1219
1424
|
});
|
|
1220
1425
|
|