@aztec/sequencer-client 0.0.1-commit.358457c → 0.0.1-commit.381b1a9
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 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +85 -13
- package/dest/config.d.ts +23 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +23 -16
- 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.d.ts +2 -8
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +8 -217
- package/dest/sequencer/checkpoint_proposal_job.d.ts +2 -4
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +60 -44
- package/dest/sequencer/sequencer.d.ts +10 -7
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +12 -14
- package/dest/sequencer/timetable.d.ts +4 -3
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +6 -7
- package/dest/sequencer/types.d.ts +2 -2
- package/dest/sequencer/types.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +10 -8
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +41 -30
- package/package.json +28 -28
- package/src/client/sequencer-client.ts +111 -12
- package/src/config.ts +28 -19
- package/src/publisher/config.ts +0 -9
- package/src/publisher/index.ts +0 -3
- package/src/publisher/sequencer-publisher.ts +8 -178
- package/src/sequencer/checkpoint_proposal_job.ts +90 -55
- package/src/sequencer/sequencer.ts +13 -15
- package/src/sequencer/timetable.ts +7 -7
- package/src/sequencer/types.ts +1 -1
- package/src/test/mock_checkpoint_builder.ts +52 -47
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
+
import { MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT } from '@aztec/constants';
|
|
2
3
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
3
4
|
import { isAnvilTestChain } from '@aztec/ethereum/chain';
|
|
4
5
|
import { getPublicClient } from '@aztec/ethereum/client';
|
|
@@ -18,10 +19,15 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
|
18
19
|
import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
|
|
19
20
|
import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
|
|
20
21
|
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
DefaultSequencerConfig,
|
|
24
|
+
type SequencerClientConfig,
|
|
25
|
+
getPublisherConfigFromSequencerConfig,
|
|
26
|
+
} from '../config.js';
|
|
22
27
|
import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
|
|
23
28
|
import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
|
|
24
29
|
import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
|
|
30
|
+
import { SequencerTimetable } from '../sequencer/timetable.js';
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
33
|
* Encapsulates the full sequencer and publisher.
|
|
@@ -137,17 +143,14 @@ export class SequencerClient {
|
|
|
137
143
|
});
|
|
138
144
|
|
|
139
145
|
const ethereumSlotDuration = config.ethereumSlotDuration;
|
|
140
|
-
const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration };
|
|
141
146
|
|
|
142
|
-
const globalsBuilder = new GlobalVariableBuilder({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
sequencerManaLimit = rollupManaLimit;
|
|
150
|
-
}
|
|
147
|
+
const globalsBuilder = new GlobalVariableBuilder({
|
|
148
|
+
...config,
|
|
149
|
+
l1GenesisTime,
|
|
150
|
+
slotDuration: Number(slotDuration),
|
|
151
|
+
ethereumSlotDuration,
|
|
152
|
+
rollupVersion,
|
|
153
|
+
});
|
|
151
154
|
|
|
152
155
|
// When running in anvil, assume we can post a tx up until one second before the end of an L1 slot.
|
|
153
156
|
// Otherwise, we need the full L1 slot duration for publishing to ensure inclusion.
|
|
@@ -157,6 +160,15 @@ export class SequencerClient {
|
|
|
157
160
|
const l1PublishingTimeBasedOnChain = isAnvilTestChain(config.l1ChainId) ? 1 : ethereumSlotDuration;
|
|
158
161
|
const l1PublishingTime = config.l1PublishingTime ?? l1PublishingTimeBasedOnChain;
|
|
159
162
|
|
|
163
|
+
const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = computeBlockLimits(
|
|
164
|
+
config,
|
|
165
|
+
rollupManaLimit,
|
|
166
|
+
l1PublishingTime,
|
|
167
|
+
log,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration, rollupManaLimit };
|
|
171
|
+
|
|
160
172
|
const sequencer = new Sequencer(
|
|
161
173
|
publisherFactory,
|
|
162
174
|
validatorClient,
|
|
@@ -171,7 +183,7 @@ export class SequencerClient {
|
|
|
171
183
|
deps.dateProvider,
|
|
172
184
|
epochCache,
|
|
173
185
|
rollupContract,
|
|
174
|
-
{ ...config, l1PublishingTime, maxL2BlockGas
|
|
186
|
+
{ ...config, l1PublishingTime, maxL2BlockGas, maxDABlockGas, maxTxsPerBlock },
|
|
175
187
|
telemetryClient,
|
|
176
188
|
log,
|
|
177
189
|
);
|
|
@@ -234,3 +246,90 @@ export class SequencerClient {
|
|
|
234
246
|
return this.sequencer.maxL2BlockGas;
|
|
235
247
|
}
|
|
236
248
|
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Computes per-block L2 gas, DA gas, and TX count budgets based on the L1 rollup limits and the timetable.
|
|
252
|
+
* If the user explicitly set a limit, it is capped at the corresponding checkpoint limit.
|
|
253
|
+
* Otherwise, derives it as (checkpointLimit / maxBlocks) * multiplier, capped at the checkpoint limit.
|
|
254
|
+
*/
|
|
255
|
+
export function computeBlockLimits(
|
|
256
|
+
config: SequencerClientConfig,
|
|
257
|
+
rollupManaLimit: number,
|
|
258
|
+
l1PublishingTime: number,
|
|
259
|
+
log: ReturnType<typeof createLogger>,
|
|
260
|
+
): { maxL2BlockGas: number; maxDABlockGas: number; maxTxsPerBlock: number } {
|
|
261
|
+
const maxNumberOfBlocks = new SequencerTimetable({
|
|
262
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
263
|
+
aztecSlotDuration: config.aztecSlotDuration,
|
|
264
|
+
l1PublishingTime,
|
|
265
|
+
p2pPropagationTime: config.attestationPropagationTime,
|
|
266
|
+
blockDurationMs: config.blockDurationMs,
|
|
267
|
+
enforce: config.enforceTimeTable ?? DefaultSequencerConfig.enforceTimeTable,
|
|
268
|
+
}).maxNumberOfBlocks;
|
|
269
|
+
|
|
270
|
+
const multiplier = config.perBlockAllocationMultiplier ?? DefaultSequencerConfig.perBlockAllocationMultiplier;
|
|
271
|
+
|
|
272
|
+
// Compute maxL2BlockGas
|
|
273
|
+
let maxL2BlockGas: number;
|
|
274
|
+
if (config.maxL2BlockGas !== undefined) {
|
|
275
|
+
if (config.maxL2BlockGas > rollupManaLimit) {
|
|
276
|
+
log.warn(
|
|
277
|
+
`Provided MAX_L2_BLOCK_GAS ${config.maxL2BlockGas} exceeds L1 rollup mana limit ${rollupManaLimit} (capping)`,
|
|
278
|
+
);
|
|
279
|
+
maxL2BlockGas = rollupManaLimit;
|
|
280
|
+
} else {
|
|
281
|
+
maxL2BlockGas = config.maxL2BlockGas;
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
maxL2BlockGas = Math.min(rollupManaLimit, Math.ceil((rollupManaLimit / maxNumberOfBlocks) * multiplier));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Compute maxDABlockGas
|
|
288
|
+
const daCheckpointLimit = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;
|
|
289
|
+
let maxDABlockGas: number;
|
|
290
|
+
if (config.maxDABlockGas !== undefined) {
|
|
291
|
+
if (config.maxDABlockGas > daCheckpointLimit) {
|
|
292
|
+
log.warn(
|
|
293
|
+
`Provided MAX_DA_BLOCK_GAS ${config.maxDABlockGas} exceeds DA checkpoint limit ${daCheckpointLimit} (capping)`,
|
|
294
|
+
);
|
|
295
|
+
maxDABlockGas = daCheckpointLimit;
|
|
296
|
+
} else {
|
|
297
|
+
maxDABlockGas = config.maxDABlockGas;
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
maxDABlockGas = Math.min(daCheckpointLimit, Math.ceil((daCheckpointLimit / maxNumberOfBlocks) * multiplier));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Compute maxTxsPerBlock
|
|
304
|
+
const defaultMaxTxsPerBlock = 32;
|
|
305
|
+
let maxTxsPerBlock: number;
|
|
306
|
+
if (config.maxTxsPerBlock !== undefined) {
|
|
307
|
+
if (config.maxTxsPerCheckpoint !== undefined && config.maxTxsPerBlock > config.maxTxsPerCheckpoint) {
|
|
308
|
+
log.warn(
|
|
309
|
+
`Provided MAX_TX_PER_BLOCK ${config.maxTxsPerBlock} exceeds MAX_TX_PER_CHECKPOINT ${config.maxTxsPerCheckpoint} (capping)`,
|
|
310
|
+
);
|
|
311
|
+
maxTxsPerBlock = config.maxTxsPerCheckpoint;
|
|
312
|
+
} else {
|
|
313
|
+
maxTxsPerBlock = config.maxTxsPerBlock;
|
|
314
|
+
}
|
|
315
|
+
} else if (config.maxTxsPerCheckpoint !== undefined) {
|
|
316
|
+
maxTxsPerBlock = Math.min(
|
|
317
|
+
config.maxTxsPerCheckpoint,
|
|
318
|
+
Math.ceil((config.maxTxsPerCheckpoint / maxNumberOfBlocks) * multiplier),
|
|
319
|
+
);
|
|
320
|
+
} else {
|
|
321
|
+
maxTxsPerBlock = defaultMaxTxsPerBlock;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
log.info(`Computed block limits L2=${maxL2BlockGas} DA=${maxDABlockGas} maxTxs=${maxTxsPerBlock}`, {
|
|
325
|
+
maxL2BlockGas,
|
|
326
|
+
maxDABlockGas,
|
|
327
|
+
maxTxsPerBlock,
|
|
328
|
+
rollupManaLimit,
|
|
329
|
+
daCheckpointLimit,
|
|
330
|
+
maxNumberOfBlocks,
|
|
331
|
+
multiplier,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock };
|
|
335
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -35,15 +35,12 @@ export type { SequencerConfig };
|
|
|
35
35
|
* Default values for SequencerConfig.
|
|
36
36
|
* Centralized location for all sequencer configuration defaults.
|
|
37
37
|
*/
|
|
38
|
-
export const DefaultSequencerConfig
|
|
38
|
+
export const DefaultSequencerConfig = {
|
|
39
39
|
sequencerPollingIntervalMS: 500,
|
|
40
|
-
maxTxsPerBlock: 32,
|
|
41
40
|
minTxsPerBlock: 1,
|
|
42
41
|
buildCheckpointIfEmpty: false,
|
|
43
42
|
publishTxsWithProposals: false,
|
|
44
|
-
|
|
45
|
-
maxDABlockGas: 10e9,
|
|
46
|
-
maxBlockSizeInBytes: 1024 * 1024,
|
|
43
|
+
perBlockAllocationMultiplier: 1.2,
|
|
47
44
|
enforceTimeTable: true,
|
|
48
45
|
attestationPropagationTime: DEFAULT_P2P_PROPAGATION_TIME,
|
|
49
46
|
secondsBeforeInvalidatingBlockAsCommitteeMember: 144, // 12 L1 blocks
|
|
@@ -52,11 +49,13 @@ export const DefaultSequencerConfig: ResolvedSequencerConfig = {
|
|
|
52
49
|
skipInvalidateBlockAsProposer: false,
|
|
53
50
|
broadcastInvalidBlockProposal: false,
|
|
54
51
|
injectFakeAttestation: false,
|
|
52
|
+
injectHighSValueAttestation: false,
|
|
53
|
+
injectUnrecoverableSignatureAttestation: false,
|
|
55
54
|
fishermanMode: false,
|
|
56
55
|
shuffleAttestationOrdering: false,
|
|
57
56
|
skipPushProposedBlocksToArchiver: false,
|
|
58
57
|
skipPublishingCheckpointsPercent: 0,
|
|
59
|
-
};
|
|
58
|
+
} satisfies ResolvedSequencerConfig;
|
|
60
59
|
|
|
61
60
|
/**
|
|
62
61
|
* Configuration settings for the SequencerClient.
|
|
@@ -68,7 +67,7 @@ export type SequencerClientConfig = SequencerPublisherConfig &
|
|
|
68
67
|
SequencerConfig &
|
|
69
68
|
L1ReaderConfig &
|
|
70
69
|
ChainConfig &
|
|
71
|
-
Pick<P2PConfig, '
|
|
70
|
+
Pick<P2PConfig, 'txPublicSetupAllowListExtend'> &
|
|
72
71
|
Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration' | 'aztecEpochDuration'>;
|
|
73
72
|
|
|
74
73
|
export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
@@ -77,10 +76,10 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
77
76
|
description: 'The number of ms to wait between polling for checking to build on the next slot.',
|
|
78
77
|
...numberConfigHelper(DefaultSequencerConfig.sequencerPollingIntervalMS),
|
|
79
78
|
},
|
|
80
|
-
|
|
81
|
-
env: '
|
|
82
|
-
description: 'The maximum number of txs
|
|
83
|
-
|
|
79
|
+
maxTxsPerCheckpoint: {
|
|
80
|
+
env: 'SEQ_MAX_TX_PER_CHECKPOINT',
|
|
81
|
+
description: 'The maximum number of txs across all blocks in a checkpoint.',
|
|
82
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
84
83
|
},
|
|
85
84
|
minTxsPerBlock: {
|
|
86
85
|
env: 'SEQ_MIN_TX_PER_BLOCK',
|
|
@@ -99,12 +98,19 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
99
98
|
maxL2BlockGas: {
|
|
100
99
|
env: 'SEQ_MAX_L2_BLOCK_GAS',
|
|
101
100
|
description: 'The maximum L2 block gas.',
|
|
102
|
-
|
|
101
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
103
102
|
},
|
|
104
103
|
maxDABlockGas: {
|
|
105
104
|
env: 'SEQ_MAX_DA_BLOCK_GAS',
|
|
106
105
|
description: 'The maximum DA block gas.',
|
|
107
|
-
|
|
106
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
107
|
+
},
|
|
108
|
+
perBlockAllocationMultiplier: {
|
|
109
|
+
env: 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER',
|
|
110
|
+
description:
|
|
111
|
+
'Per-block gas budget multiplier for both L2 and DA gas. Budget per block is (checkpointLimit / maxBlocks) * multiplier.' +
|
|
112
|
+
' Values greater than one allow early blocks to use more than their even share, relying on checkpoint-level capping for later blocks.',
|
|
113
|
+
...numberConfigHelper(DefaultSequencerConfig.perBlockAllocationMultiplier),
|
|
108
114
|
},
|
|
109
115
|
coinbase: {
|
|
110
116
|
env: 'COINBASE',
|
|
@@ -124,11 +130,6 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
124
130
|
env: 'ACVM_BINARY_PATH',
|
|
125
131
|
description: 'The path to the ACVM binary',
|
|
126
132
|
},
|
|
127
|
-
maxBlockSizeInBytes: {
|
|
128
|
-
env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES',
|
|
129
|
-
description: 'Max block size',
|
|
130
|
-
...numberConfigHelper(DefaultSequencerConfig.maxBlockSizeInBytes),
|
|
131
|
-
},
|
|
132
133
|
enforceTimeTable: {
|
|
133
134
|
env: 'SEQ_ENFORCE_TIME_TABLE',
|
|
134
135
|
description: 'Whether to enforce the time table when building blocks',
|
|
@@ -186,6 +187,14 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
186
187
|
description: 'Inject a fake attestation (for testing only)',
|
|
187
188
|
...booleanConfigHelper(DefaultSequencerConfig.injectFakeAttestation),
|
|
188
189
|
},
|
|
190
|
+
injectHighSValueAttestation: {
|
|
191
|
+
description: 'Inject a malleable attestation with a high-s value (for testing only)',
|
|
192
|
+
...booleanConfigHelper(DefaultSequencerConfig.injectHighSValueAttestation),
|
|
193
|
+
},
|
|
194
|
+
injectUnrecoverableSignatureAttestation: {
|
|
195
|
+
description: 'Inject an attestation with an unrecoverable signature (for testing only)',
|
|
196
|
+
...booleanConfigHelper(DefaultSequencerConfig.injectUnrecoverableSignatureAttestation),
|
|
197
|
+
},
|
|
189
198
|
fishermanMode: {
|
|
190
199
|
env: 'FISHERMAN_MODE',
|
|
191
200
|
description:
|
|
@@ -214,7 +223,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
214
223
|
description: 'Percent probability (0 - 100) of sequencer skipping checkpoint publishing (testing only)',
|
|
215
224
|
...numberConfigHelper(DefaultSequencerConfig.skipPublishingCheckpointsPercent),
|
|
216
225
|
},
|
|
217
|
-
...pickConfigMappings(p2pConfigMappings, ['
|
|
226
|
+
...pickConfigMappings(p2pConfigMappings, ['txPublicSetupAllowListExtend']),
|
|
218
227
|
};
|
|
219
228
|
|
|
220
229
|
export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
|
package/src/publisher/config.ts
CHANGED
|
@@ -48,8 +48,6 @@ export type PublisherConfig = L1TxUtilsConfig &
|
|
|
48
48
|
fishermanMode?: boolean;
|
|
49
49
|
/** Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only) */
|
|
50
50
|
publisherForwarderAddress?: EthAddress;
|
|
51
|
-
/** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
|
|
52
|
-
l1TxFailedStore?: string;
|
|
53
51
|
};
|
|
54
52
|
|
|
55
53
|
export type ProverPublisherConfig = L1TxUtilsConfig &
|
|
@@ -64,8 +62,6 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
|
|
|
64
62
|
fishermanMode?: boolean;
|
|
65
63
|
sequencerPublisherAllowInvalidStates?: boolean;
|
|
66
64
|
sequencerPublisherForwarderAddress?: EthAddress;
|
|
67
|
-
/** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
|
|
68
|
-
l1TxFailedStore?: string;
|
|
69
65
|
};
|
|
70
66
|
|
|
71
67
|
export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
|
|
@@ -81,7 +77,6 @@ export function getPublisherConfigFromSequencerConfig(config: SequencerPublisher
|
|
|
81
77
|
...config,
|
|
82
78
|
publisherAllowInvalidStates: config.sequencerPublisherAllowInvalidStates,
|
|
83
79
|
publisherForwarderAddress: config.sequencerPublisherForwarderAddress,
|
|
84
|
-
l1TxFailedStore: config.l1TxFailedStore,
|
|
85
80
|
};
|
|
86
81
|
}
|
|
87
82
|
|
|
@@ -138,10 +133,6 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
|
|
|
138
133
|
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
139
134
|
parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
|
|
140
135
|
},
|
|
141
|
-
l1TxFailedStore: {
|
|
142
|
-
env: 'L1_TX_FAILED_STORE',
|
|
143
|
-
description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
|
|
144
|
-
},
|
|
145
136
|
};
|
|
146
137
|
|
|
147
138
|
export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
|
package/src/publisher/index.ts
CHANGED
|
@@ -3,6 +3,3 @@ export { SequencerPublisherFactory } from './sequencer-publisher-factory.js';
|
|
|
3
3
|
|
|
4
4
|
// Used for tests
|
|
5
5
|
export { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
6
|
-
|
|
7
|
-
// Failed L1 tx store (optional, for test networks)
|
|
8
|
-
export { type FailedL1Tx, type FailedL1TxUri, type L1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
@@ -45,19 +45,9 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
|
45
45
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
46
46
|
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
47
47
|
|
|
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';
|
|
48
|
+
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
58
49
|
|
|
59
50
|
import type { SequencerPublisherConfig } from './config.js';
|
|
60
|
-
import { type FailedL1Tx, type L1TxFailedStore, createL1TxFailedStore } from './l1_tx_failed_store/index.js';
|
|
61
51
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
62
52
|
|
|
63
53
|
/** Arguments to the process method of the rollup contract */
|
|
@@ -119,7 +109,6 @@ export class SequencerPublisher {
|
|
|
119
109
|
private interrupted = false;
|
|
120
110
|
private metrics: SequencerPublisherMetrics;
|
|
121
111
|
public epochCache: EpochCache;
|
|
122
|
-
private failedTxStore?: Promise<L1TxFailedStore | undefined>;
|
|
123
112
|
|
|
124
113
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
125
114
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
@@ -160,7 +149,7 @@ export class SequencerPublisher {
|
|
|
160
149
|
protected requests: RequestWithExpiry[] = [];
|
|
161
150
|
|
|
162
151
|
constructor(
|
|
163
|
-
private config: Pick<SequencerPublisherConfig, 'fishermanMode'
|
|
152
|
+
private config: Pick<SequencerPublisherConfig, 'fishermanMode'> &
|
|
164
153
|
Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
|
|
165
154
|
deps: {
|
|
166
155
|
telemetry?: TelemetryClient;
|
|
@@ -216,31 +205,6 @@ export class SequencerPublisher {
|
|
|
216
205
|
this.rollupContract,
|
|
217
206
|
createLogger('sequencer:publisher:price-oracle'),
|
|
218
207
|
);
|
|
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
|
-
});
|
|
244
208
|
}
|
|
245
209
|
|
|
246
210
|
public getRollupContract(): RollupContract {
|
|
@@ -422,21 +386,6 @@ export class SequencerPublisher {
|
|
|
422
386
|
validRequests.sort((a, b) => compareActions(a.action, b.action));
|
|
423
387
|
|
|
424
388
|
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
|
-
|
|
440
389
|
this.log.debug('Forwarding transactions', {
|
|
441
390
|
validRequests: validRequests.map(request => request.action),
|
|
442
391
|
txConfig,
|
|
@@ -449,12 +398,7 @@ export class SequencerPublisher {
|
|
|
449
398
|
this.rollupContract.address,
|
|
450
399
|
this.log,
|
|
451
400
|
);
|
|
452
|
-
const
|
|
453
|
-
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(
|
|
454
|
-
validRequests,
|
|
455
|
-
result,
|
|
456
|
-
txContext,
|
|
457
|
-
);
|
|
401
|
+
const { successfulActions = [], failedActions = [] } = this.callbackBundledTransactions(validRequests, result);
|
|
458
402
|
return { result, expiredActions, sentActions: validActions, successfulActions, failedActions };
|
|
459
403
|
} catch (err) {
|
|
460
404
|
const viemError = formatViemError(err);
|
|
@@ -474,25 +418,11 @@ export class SequencerPublisher {
|
|
|
474
418
|
|
|
475
419
|
private callbackBundledTransactions(
|
|
476
420
|
requests: RequestWithExpiry[],
|
|
477
|
-
result
|
|
478
|
-
txContext: { multicallData: Hex; blobData?: Hex[]; l1BlockNumber: bigint },
|
|
421
|
+
result?: { receipt: TransactionReceipt } | FormattedViemError,
|
|
479
422
|
) {
|
|
480
423
|
const actionsListStr = requests.map(r => r.action).join(', ');
|
|
481
424
|
if (result instanceof FormattedViemError) {
|
|
482
425
|
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
|
-
});
|
|
496
426
|
return { failedActions: requests.map(r => r.action) };
|
|
497
427
|
} else {
|
|
498
428
|
this.log.verbose(`Published bundled transactions (${actionsListStr})`, { result, requests });
|
|
@@ -505,30 +435,6 @@ export class SequencerPublisher {
|
|
|
505
435
|
failedActions.push(request.action);
|
|
506
436
|
}
|
|
507
437
|
}
|
|
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
|
-
}
|
|
532
438
|
return { successfulActions, failedActions };
|
|
533
439
|
}
|
|
534
440
|
}
|
|
@@ -640,8 +546,6 @@ export class SequencerPublisher {
|
|
|
640
546
|
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
641
547
|
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
642
548
|
|
|
643
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
644
|
-
|
|
645
549
|
try {
|
|
646
550
|
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
647
551
|
request,
|
|
@@ -693,18 +597,6 @@ export class SequencerPublisher {
|
|
|
693
597
|
|
|
694
598
|
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
695
599
|
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
|
-
});
|
|
708
600
|
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
709
601
|
}
|
|
710
602
|
}
|
|
@@ -852,26 +744,11 @@ export class SequencerPublisher {
|
|
|
852
744
|
lastValidL2Slot: slotNumber,
|
|
853
745
|
});
|
|
854
746
|
|
|
855
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
856
|
-
|
|
857
747
|
try {
|
|
858
748
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
859
749
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
860
750
|
} catch (err) {
|
|
861
|
-
|
|
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
|
-
});
|
|
751
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, err);
|
|
875
752
|
// Yes, we enqueue the request anyway, in case there was a bug with the simulation itself
|
|
876
753
|
}
|
|
877
754
|
|
|
@@ -1167,8 +1044,6 @@ export class SequencerPublisher {
|
|
|
1167
1044
|
|
|
1168
1045
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
1169
1046
|
|
|
1170
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1171
|
-
|
|
1172
1047
|
let gasUsed: bigint;
|
|
1173
1048
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1174
1049
|
try {
|
|
@@ -1178,19 +1053,6 @@ export class SequencerPublisher {
|
|
|
1178
1053
|
const viemError = formatViemError(err, simulateAbi);
|
|
1179
1054
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1180
1055
|
|
|
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
|
-
|
|
1194
1056
|
return false;
|
|
1195
1057
|
}
|
|
1196
1058
|
|
|
@@ -1274,27 +1136,9 @@ export class SequencerPublisher {
|
|
|
1274
1136
|
kzg,
|
|
1275
1137
|
},
|
|
1276
1138
|
)
|
|
1277
|
-
.catch(
|
|
1278
|
-
const
|
|
1279
|
-
this.log.error(`Failed to validate blobs`,
|
|
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
|
-
});
|
|
1139
|
+
.catch(err => {
|
|
1140
|
+
const { message, metaMessages } = formatViemError(err);
|
|
1141
|
+
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
1298
1142
|
throw new Error('Failed to validate blobs');
|
|
1299
1143
|
});
|
|
1300
1144
|
}
|
|
@@ -1373,8 +1217,6 @@ export class SequencerPublisher {
|
|
|
1373
1217
|
});
|
|
1374
1218
|
}
|
|
1375
1219
|
|
|
1376
|
-
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1377
|
-
|
|
1378
1220
|
const simulationResult = await this.l1TxUtils
|
|
1379
1221
|
.simulate(
|
|
1380
1222
|
{
|
|
@@ -1408,18 +1250,6 @@ export class SequencerPublisher {
|
|
|
1408
1250
|
};
|
|
1409
1251
|
}
|
|
1410
1252
|
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
|
-
});
|
|
1423
1253
|
throw err;
|
|
1424
1254
|
});
|
|
1425
1255
|
|