@aztec/sequencer-client 0.72.1 → 0.73.0
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 +11 -6
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +41 -10
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -4
- package/dest/publisher/config.d.ts +5 -0
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +9 -2
- package/dest/publisher/index.d.ts +1 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +2 -2
- package/dest/publisher/{l1-publisher-metrics.d.ts → sequencer-publisher-metrics.d.ts} +6 -2
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -0
- package/dest/publisher/sequencer-publisher-metrics.js +111 -0
- package/dest/publisher/sequencer-publisher.d.ts +158 -0
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -0
- package/dest/publisher/sequencer-publisher.js +555 -0
- package/dest/sequencer/allowed.d.ts +1 -1
- package/dest/sequencer/allowed.d.ts.map +1 -1
- package/dest/sequencer/allowed.js +4 -4
- package/dest/sequencer/metrics.d.ts +1 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +3 -4
- package/dest/sequencer/sequencer.d.ts +17 -10
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +103 -109
- package/dest/sequencer/utils.d.ts +1 -1
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +3 -3
- package/dest/slasher/slasher_client.d.ts.map +1 -1
- package/dest/slasher/slasher_client.js +6 -3
- package/dest/test/index.d.ts +3 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -2
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +4 -3
- package/dest/tx_validator/test_utils.d.ts +4 -4
- package/dest/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/tx_validator/test_utils.js +3 -3
- package/package.json +21 -20
- package/src/client/sequencer-client.ts +60 -14
- package/src/config.ts +3 -3
- package/src/publisher/config.ts +13 -1
- package/src/publisher/index.ts +1 -1
- package/src/publisher/{l1-publisher-metrics.ts → sequencer-publisher-metrics.ts} +41 -2
- package/src/publisher/sequencer-publisher.ts +730 -0
- package/src/sequencer/allowed.ts +3 -3
- package/src/sequencer/metrics.ts +2 -3
- package/src/sequencer/sequencer.ts +138 -125
- package/src/sequencer/utils.ts +5 -2
- package/src/slasher/slasher_client.ts +7 -2
- package/src/test/index.ts +2 -4
- package/src/tx_validator/gas_validator.ts +5 -4
- package/src/tx_validator/test_utils.ts +5 -5
- package/dest/publisher/l1-publisher-metrics.d.ts.map +0 -1
- package/dest/publisher/l1-publisher-metrics.js +0 -85
- package/dest/publisher/l1-publisher.d.ts +0 -195
- package/dest/publisher/l1-publisher.d.ts.map +0 -1
- package/dest/publisher/l1-publisher.js +0 -930
- package/dest/test/test-l1-publisher.d.ts +0 -9
- package/dest/test/test-l1-publisher.d.ts.map +0 -1
- package/dest/test/test-l1-publisher.js +0 -11
- package/src/publisher/l1-publisher.ts +0 -1288
- package/src/test/test-l1-publisher.ts +0 -20
package/src/sequencer/allowed.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
|
6
6
|
|
|
7
7
|
let defaultAllowedSetupFunctions: AllowedElement[] | undefined = undefined;
|
|
8
8
|
|
|
9
|
-
export function getDefaultAllowedSetupFunctions(): AllowedElement[] {
|
|
9
|
+
export async function getDefaultAllowedSetupFunctions(): Promise<AllowedElement[]> {
|
|
10
10
|
if (defaultAllowedSetupFunctions === undefined) {
|
|
11
11
|
defaultAllowedSetupFunctions = [
|
|
12
12
|
// needed for authwit support
|
|
@@ -21,12 +21,12 @@ export function getDefaultAllowedSetupFunctions(): AllowedElement[] {
|
|
|
21
21
|
},
|
|
22
22
|
// needed for private transfers via FPC
|
|
23
23
|
{
|
|
24
|
-
classId: getContractClassFromArtifact(TokenContractArtifact).id,
|
|
24
|
+
classId: (await getContractClassFromArtifact(TokenContractArtifact)).id,
|
|
25
25
|
// We can't restrict the selector because public functions get routed via dispatch.
|
|
26
26
|
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'),
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
classId: getContractClassFromArtifact(FPCContract.artifact).id,
|
|
29
|
+
classId: (await getContractClassFromArtifact(FPCContract.artifact)).id,
|
|
30
30
|
// We can't restrict the selector because public functions get routed via dispatch.
|
|
31
31
|
// selector: FunctionSelector.fromSignature('prepare_fee((Field),Field,(Field),Field)'),
|
|
32
32
|
},
|
package/src/sequencer/metrics.ts
CHANGED
|
@@ -105,13 +105,12 @@ export class SequencerMetrics {
|
|
|
105
105
|
this.setCurrentBlock(0, 0);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
recordBuiltBlock(buildDurationMs: number, totalMana: number) {
|
|
109
109
|
this.blockCounter.add(1, {
|
|
110
|
-
[Attributes.STATUS]: '
|
|
110
|
+
[Attributes.STATUS]: 'built',
|
|
111
111
|
});
|
|
112
112
|
this.blockBuildDuration.record(Math.ceil(buildDurationMs));
|
|
113
113
|
this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
|
|
114
|
-
this.setCurrentBlock(0, 0);
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
recordFailedBlock() {
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
type GlobalVariables,
|
|
22
22
|
StateReference,
|
|
23
23
|
} from '@aztec/circuits.js';
|
|
24
|
-
import { prettyLogViemErrorMsg } from '@aztec/ethereum';
|
|
25
24
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
26
25
|
import { omit } from '@aztec/foundation/collection';
|
|
27
26
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -37,7 +36,7 @@ import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trac
|
|
|
37
36
|
import { type ValidatorClient } from '@aztec/validator-client';
|
|
38
37
|
|
|
39
38
|
import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
40
|
-
import { type
|
|
39
|
+
import { type SequencerPublisher, VoteType } from '../publisher/sequencer-publisher.js';
|
|
41
40
|
import { type SlasherClient } from '../slasher/slasher_client.js';
|
|
42
41
|
import { createValidatorsForBlockBuilding } from '../tx_validator/tx_validator_factory.js';
|
|
43
42
|
import { getDefaultAllowedSetupFunctions } from './allowed.js';
|
|
@@ -69,7 +68,7 @@ export class Sequencer {
|
|
|
69
68
|
private _coinbase = EthAddress.ZERO;
|
|
70
69
|
private _feeRecipient = AztecAddress.ZERO;
|
|
71
70
|
private state = SequencerState.STOPPED;
|
|
72
|
-
private allowedInSetup: AllowedElement[] =
|
|
71
|
+
private allowedInSetup: AllowedElement[] = [];
|
|
73
72
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
74
73
|
private maxBlockGas: Gas = new Gas(100e9, 100e9);
|
|
75
74
|
private metrics: SequencerMetrics;
|
|
@@ -81,7 +80,7 @@ export class Sequencer {
|
|
|
81
80
|
protected enforceTimeTable: boolean = false;
|
|
82
81
|
|
|
83
82
|
constructor(
|
|
84
|
-
protected publisher:
|
|
83
|
+
protected publisher: SequencerPublisher,
|
|
85
84
|
protected validatorClient: ValidatorClient | undefined, // During migration the validator client can be inactive
|
|
86
85
|
protected globalsBuilder: GlobalVariableBuilder,
|
|
87
86
|
protected p2pClient: P2P,
|
|
@@ -98,7 +97,6 @@ export class Sequencer {
|
|
|
98
97
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
99
98
|
protected log = createLogger('sequencer'),
|
|
100
99
|
) {
|
|
101
|
-
this.updateConfig(config);
|
|
102
100
|
this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
|
|
103
101
|
|
|
104
102
|
// Register the block builder with the validator client for re-execution
|
|
@@ -116,7 +114,7 @@ export class Sequencer {
|
|
|
116
114
|
* Updates sequencer config.
|
|
117
115
|
* @param config - New parameters.
|
|
118
116
|
*/
|
|
119
|
-
public updateConfig(config: SequencerConfig) {
|
|
117
|
+
public async updateConfig(config: SequencerConfig) {
|
|
120
118
|
this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup'));
|
|
121
119
|
|
|
122
120
|
if (config.transactionPollingIntervalMS !== undefined) {
|
|
@@ -142,6 +140,8 @@ export class Sequencer {
|
|
|
142
140
|
}
|
|
143
141
|
if (config.allowedInSetup) {
|
|
144
142
|
this.allowedInSetup = config.allowedInSetup;
|
|
143
|
+
} else {
|
|
144
|
+
this.allowedInSetup = await getDefaultAllowedSetupFunctions();
|
|
145
145
|
}
|
|
146
146
|
if (config.maxBlockSizeInBytes !== undefined) {
|
|
147
147
|
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
|
|
@@ -177,12 +177,12 @@ export class Sequencer {
|
|
|
177
177
|
/**
|
|
178
178
|
* Starts the sequencer and moves to IDLE state.
|
|
179
179
|
*/
|
|
180
|
-
public start() {
|
|
180
|
+
public async start() {
|
|
181
|
+
await this.updateConfig(this.config);
|
|
181
182
|
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
|
|
182
183
|
this.setState(SequencerState.IDLE, 0n, true /** force */);
|
|
183
184
|
this.runningPromise.start();
|
|
184
185
|
this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
|
|
185
|
-
return Promise.resolve();
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/**
|
|
@@ -216,6 +216,11 @@ export class Sequencer {
|
|
|
216
216
|
return { state: this.state };
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
/** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */
|
|
220
|
+
public flush() {
|
|
221
|
+
this.isFlushing = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
219
224
|
/**
|
|
220
225
|
* @notice Performs most of the sequencer duties:
|
|
221
226
|
* - Checks if we are up to date
|
|
@@ -242,14 +247,14 @@ export class Sequencer {
|
|
|
242
247
|
// If we cannot find a tip archive, assume genesis.
|
|
243
248
|
const chainTipArchive = chainTip?.archive.root ?? new Fr(GENESIS_ARCHIVE_ROOT);
|
|
244
249
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} catch (err) {
|
|
249
|
-
this.log.debug(`Cannot propose for block ${newBlockNumber}`);
|
|
250
|
+
const slot = await this.slotForProposal(chainTipArchive.toBuffer(), BigInt(newBlockNumber));
|
|
251
|
+
if (!slot) {
|
|
252
|
+
this.log.debug(`Cannot propose block ${newBlockNumber}`);
|
|
250
253
|
return;
|
|
251
254
|
}
|
|
252
255
|
|
|
256
|
+
this.log.info(`Can propose block ${newBlockNumber} at slot ${slot}`);
|
|
257
|
+
|
|
253
258
|
const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(
|
|
254
259
|
new Fr(newBlockNumber),
|
|
255
260
|
this._coinbase,
|
|
@@ -257,19 +262,19 @@ export class Sequencer {
|
|
|
257
262
|
slot,
|
|
258
263
|
);
|
|
259
264
|
|
|
260
|
-
|
|
261
|
-
|
|
265
|
+
const enqueueGovernanceVotePromise = this.publisher.enqueueCastVote(
|
|
266
|
+
slot,
|
|
267
|
+
newGlobalVariables.timestamp.toBigInt(),
|
|
268
|
+
VoteType.GOVERNANCE,
|
|
269
|
+
);
|
|
270
|
+
const enqueueSlashingVotePromise = this.publisher.enqueueCastVote(
|
|
271
|
+
slot,
|
|
272
|
+
newGlobalVariables.timestamp.toBigInt(),
|
|
273
|
+
VoteType.SLASHING,
|
|
274
|
+
);
|
|
262
275
|
|
|
263
|
-
//
|
|
264
|
-
const
|
|
265
|
-
if (pendingTxCount < this.minTxsPerBlock && !this.isFlushing) {
|
|
266
|
-
this.log.verbose(`Not enough txs to propose block. Got ${pendingTxCount} min ${this.minTxsPerBlock}.`, {
|
|
267
|
-
slot,
|
|
268
|
-
blockNumber: newBlockNumber,
|
|
269
|
-
});
|
|
270
|
-
await this.claimEpochProofRightIfAvailable(slot);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
276
|
+
// Start collecting proof quotes for the previous epoch if needed in the background
|
|
277
|
+
const createProofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
|
|
273
278
|
|
|
274
279
|
this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
|
|
275
280
|
this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
|
|
@@ -278,10 +283,6 @@ export class Sequencer {
|
|
|
278
283
|
slot,
|
|
279
284
|
});
|
|
280
285
|
|
|
281
|
-
// We don't fetch exactly maxTxsPerBlock txs here because we may not need all of them if we hit a limit before,
|
|
282
|
-
// and also we may need to fetch more if we don't have enough valid txs.
|
|
283
|
-
const pendingTxs = this.p2pClient.iteratePendingTxs();
|
|
284
|
-
|
|
285
286
|
// If I created a "partial" header here that should make our job much easier.
|
|
286
287
|
const proposalHeader = new BlockHeader(
|
|
287
288
|
new AppendOnlyTreeSnapshot(chainTipArchive, 1),
|
|
@@ -292,18 +293,41 @@ export class Sequencer {
|
|
|
292
293
|
Fr.ZERO,
|
|
293
294
|
);
|
|
294
295
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
} catch (err) {
|
|
302
|
-
this.log.error(`Error assembling block`, err, { blockNumber: newBlockNumber, slot });
|
|
296
|
+
let finishedFlushing = false;
|
|
297
|
+
const pendingTxCount = await this.p2pClient.getPendingTxCount();
|
|
298
|
+
if (pendingTxCount >= this.minTxsPerBlock || this.isFlushing) {
|
|
299
|
+
// We don't fetch exactly maxTxsPerBlock txs here because we may not need all of them if we hit a limit before,
|
|
300
|
+
// and also we may need to fetch more if we don't have enough valid txs.
|
|
301
|
+
const pendingTxs = this.p2pClient.iteratePendingTxs();
|
|
303
302
|
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader).catch(err => {
|
|
304
|
+
this.log.error(`Error building/enqueuing block`, err, { blockNumber: newBlockNumber, slot });
|
|
305
|
+
});
|
|
306
|
+
finishedFlushing = true;
|
|
307
|
+
} else {
|
|
308
|
+
this.log.debug(
|
|
309
|
+
`Not enough txs to build block ${newBlockNumber} at slot ${slot}: got ${pendingTxCount} txs, need ${this.minTxsPerBlock}`,
|
|
310
|
+
);
|
|
306
311
|
}
|
|
312
|
+
|
|
313
|
+
await enqueueGovernanceVotePromise.catch(err => {
|
|
314
|
+
this.log.error(`Error enqueuing governance vote`, err, { blockNumber: newBlockNumber, slot });
|
|
315
|
+
});
|
|
316
|
+
await enqueueSlashingVotePromise.catch(err => {
|
|
317
|
+
this.log.error(`Error enqueuing slashing vote`, err, { blockNumber: newBlockNumber, slot });
|
|
318
|
+
});
|
|
319
|
+
await createProofQuotePromise
|
|
320
|
+
.then(quote => (quote ? this.publisher.enqueueClaimEpochProofRight(quote) : undefined))
|
|
321
|
+
.catch(err => {
|
|
322
|
+
this.log.error(`Error creating proof quote`, err, { blockNumber: newBlockNumber, slot });
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
await this.publisher.sendRequests();
|
|
326
|
+
|
|
327
|
+
if (finishedFlushing) {
|
|
328
|
+
this.isFlushing = false;
|
|
329
|
+
}
|
|
330
|
+
|
|
307
331
|
this.setState(SequencerState.IDLE, 0n);
|
|
308
332
|
}
|
|
309
333
|
|
|
@@ -323,24 +347,31 @@ export class Sequencer {
|
|
|
323
347
|
}
|
|
324
348
|
}
|
|
325
349
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const [slot, blockNumber] = await this.publisher.canProposeAtNextEthBlock(tipArchive);
|
|
350
|
+
public getForwarderAddress() {
|
|
351
|
+
return this.publisher.getForwarderAddress();
|
|
352
|
+
}
|
|
330
353
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Checks if we can propose at the next block and returns the slot number if we can.
|
|
356
|
+
* @param tipArchive - The archive of the previous block.
|
|
357
|
+
* @param proposalBlockNumber - The block number of the proposal.
|
|
358
|
+
* @returns The slot number if we can propose at the next block, otherwise undefined.
|
|
359
|
+
*/
|
|
360
|
+
async slotForProposal(tipArchive: Buffer, proposalBlockNumber: bigint): Promise<bigint | undefined> {
|
|
361
|
+
const result = await this.publisher.canProposeAtNextEthBlock(tipArchive);
|
|
362
|
+
|
|
363
|
+
if (!result) {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const [slot, blockNumber] = result;
|
|
368
|
+
|
|
369
|
+
if (proposalBlockNumber !== blockNumber) {
|
|
370
|
+
const msg = `Sequencer block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}.`;
|
|
371
|
+
this.log.warn(msg);
|
|
372
|
+
throw new Error(msg);
|
|
343
373
|
}
|
|
374
|
+
return slot;
|
|
344
375
|
}
|
|
345
376
|
|
|
346
377
|
/**
|
|
@@ -374,13 +405,12 @@ export class Sequencer {
|
|
|
374
405
|
* @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
|
|
375
406
|
*/
|
|
376
407
|
protected async buildBlock(
|
|
377
|
-
pendingTxs: Iterable<Tx>,
|
|
408
|
+
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
378
409
|
newGlobalVariables: GlobalVariables,
|
|
379
410
|
opts: { validateOnly?: boolean } = {},
|
|
380
411
|
) {
|
|
381
412
|
const blockNumber = newGlobalVariables.blockNumber.toNumber();
|
|
382
413
|
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
383
|
-
|
|
384
414
|
this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
|
|
385
415
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber));
|
|
386
416
|
const msgCount = l1ToL2Messages.length;
|
|
@@ -450,8 +480,9 @@ export class Sequencer {
|
|
|
450
480
|
|
|
451
481
|
if (!opts.validateOnly && failedTxs.length > 0) {
|
|
452
482
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
453
|
-
|
|
454
|
-
|
|
483
|
+
const failedTxHashes = await Tx.getHashes(failedTxData);
|
|
484
|
+
this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
|
|
485
|
+
await this.p2pClient.deleteTxs(failedTxHashes);
|
|
455
486
|
}
|
|
456
487
|
|
|
457
488
|
if (
|
|
@@ -492,6 +523,7 @@ export class Sequencer {
|
|
|
492
523
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
493
524
|
// We wait a bit to close the forks since the processor may still be working on a dangling tx
|
|
494
525
|
// which was interrupted due to the processingDeadline being hit.
|
|
526
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
495
527
|
setTimeout(async () => {
|
|
496
528
|
try {
|
|
497
529
|
await publicProcessorFork.close();
|
|
@@ -513,10 +545,13 @@ export class Sequencer {
|
|
|
513
545
|
* @param pendingTxs - Iterable of pending transactions to construct the block from
|
|
514
546
|
* @param proposalHeader - The partial header constructed for the proposal
|
|
515
547
|
*/
|
|
516
|
-
@trackSpan('Sequencer.
|
|
548
|
+
@trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, proposalHeader) => ({
|
|
517
549
|
[Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
|
|
518
550
|
}))
|
|
519
|
-
private async
|
|
551
|
+
private async buildBlockAndEnqueuePublish(
|
|
552
|
+
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
553
|
+
proposalHeader: BlockHeader,
|
|
554
|
+
): Promise<void> {
|
|
520
555
|
await this.publisher.validateBlockForSubmission(proposalHeader);
|
|
521
556
|
|
|
522
557
|
const newGlobalVariables = proposalHeader.globalVariables;
|
|
@@ -527,41 +562,38 @@ export class Sequencer {
|
|
|
527
562
|
const workTimer = new Timer();
|
|
528
563
|
this.setState(SequencerState.CREATING_BLOCK, slot);
|
|
529
564
|
|
|
530
|
-
// Start collecting proof quotes for the previous epoch if needed in the background
|
|
531
|
-
const proofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
|
|
532
|
-
|
|
533
565
|
try {
|
|
534
566
|
const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables);
|
|
535
567
|
const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
|
|
568
|
+
this.metrics.recordBuiltBlock(workTimer.ms(), publicGas.l2Gas);
|
|
536
569
|
|
|
537
570
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
538
571
|
// block being published before ours instead of just waiting on our block
|
|
539
572
|
await this.publisher.validateBlockForSubmission(block.header);
|
|
540
573
|
|
|
541
|
-
const workDuration = workTimer.ms();
|
|
542
574
|
const blockStats: L2BlockBuiltStats = {
|
|
543
575
|
eventName: 'l2-block-built',
|
|
544
576
|
creator: this.publisher.getSenderAddress().toString(),
|
|
545
|
-
duration:
|
|
577
|
+
duration: workTimer.ms(),
|
|
546
578
|
publicProcessDuration: publicProcessorDuration,
|
|
547
579
|
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
548
580
|
...block.getStats(),
|
|
549
581
|
};
|
|
550
582
|
|
|
551
|
-
const blockHash = block.hash();
|
|
583
|
+
const blockHash = await block.hash();
|
|
552
584
|
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
553
|
-
this.log.info(
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
585
|
+
this.log.info(
|
|
586
|
+
`Built block ${block.number} for slot ${slot} with ${numTxs} txs and ${numMsgs} messages. ${
|
|
587
|
+
publicGas.l2Gas / workTimer.s()
|
|
588
|
+
} mana/s`,
|
|
589
|
+
{
|
|
590
|
+
blockHash,
|
|
591
|
+
globalVariables: block.header.globalVariables.toInspect(),
|
|
592
|
+
txHashes,
|
|
593
|
+
...blockStats,
|
|
594
|
+
},
|
|
595
|
+
);
|
|
563
596
|
|
|
564
|
-
this.isFlushing = false;
|
|
565
597
|
this.log.debug('Collecting attestations');
|
|
566
598
|
const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
|
|
567
599
|
const attestations = await this.collectAttestations(block, txHashes);
|
|
@@ -570,37 +602,13 @@ export class Sequencer {
|
|
|
570
602
|
}
|
|
571
603
|
stopCollectingAttestationsTimer();
|
|
572
604
|
|
|
573
|
-
|
|
574
|
-
const proofQuote = await proofQuotePromise;
|
|
575
|
-
|
|
576
|
-
await this.publishL2Block(block, attestations, txHashes, proofQuote);
|
|
577
|
-
this.metrics.recordPublishedBlock(workDuration, publicGas.l2Gas);
|
|
578
|
-
const duration = Math.ceil(workDuration);
|
|
579
|
-
const manaPerSecond = Math.ceil((publicGas.l2Gas * 1000) / duration);
|
|
580
|
-
this.log.info(
|
|
581
|
-
`Published block ${block.number} with ${numTxs} txs and ${numMsgs} messages in ${duration} ms at ${manaPerSecond} mana/s`,
|
|
582
|
-
{
|
|
583
|
-
publicGas,
|
|
584
|
-
blockNumber: block.number,
|
|
585
|
-
blockHash: blockHash,
|
|
586
|
-
slot,
|
|
587
|
-
txCount: txHashes.length,
|
|
588
|
-
msgCount: numMsgs,
|
|
589
|
-
duration,
|
|
590
|
-
submitter: this.publisher.getSenderAddress().toString(),
|
|
591
|
-
},
|
|
592
|
-
);
|
|
605
|
+
return this.enqueuePublishL2Block(block, attestations, txHashes);
|
|
593
606
|
} catch (err) {
|
|
594
607
|
this.metrics.recordFailedBlock();
|
|
595
608
|
throw err;
|
|
596
609
|
}
|
|
597
610
|
}
|
|
598
611
|
|
|
599
|
-
/** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */
|
|
600
|
-
public flush() {
|
|
601
|
-
this.isFlushing = true;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
612
|
@trackSpan('Sequencer.collectAttestations', (block, txHashes) => ({
|
|
605
613
|
[Attributes.BLOCK_NUMBER]: block.number,
|
|
606
614
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
@@ -655,28 +663,33 @@ export class Sequencer {
|
|
|
655
663
|
try {
|
|
656
664
|
// Find out which epoch we are currently in
|
|
657
665
|
const epochToProve = await this.publisher.getClaimableEpoch();
|
|
666
|
+
|
|
658
667
|
if (epochToProve === undefined) {
|
|
659
|
-
this.log.trace(`No epoch to
|
|
668
|
+
this.log.trace(`No epoch to claim at slot ${slotNumber}`);
|
|
660
669
|
return undefined;
|
|
661
670
|
}
|
|
662
671
|
|
|
663
672
|
// Get quotes for the epoch to be proven
|
|
664
673
|
this.log.debug(`Collecting proof quotes for epoch ${epochToProve}`);
|
|
665
|
-
const
|
|
666
|
-
|
|
674
|
+
const p2pQuotes = await this.p2pClient
|
|
675
|
+
.getEpochProofQuotes(epochToProve)
|
|
676
|
+
.then(quotes =>
|
|
677
|
+
quotes
|
|
678
|
+
.filter(x => x.payload.validUntilSlot >= slotNumber)
|
|
679
|
+
.filter(x => x.payload.epochToProve === epochToProve),
|
|
680
|
+
);
|
|
681
|
+
this.log.verbose(`Retrieved ${p2pQuotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
|
|
667
682
|
epochToProve,
|
|
668
683
|
slotNumber,
|
|
669
|
-
quotes:
|
|
684
|
+
quotes: p2pQuotes.map(q => q.payload),
|
|
670
685
|
});
|
|
686
|
+
if (!p2pQuotes.length) {
|
|
687
|
+
return undefined;
|
|
688
|
+
}
|
|
689
|
+
|
|
671
690
|
// ensure these quotes are still valid for the slot and have the contract validate them
|
|
672
|
-
const
|
|
673
|
-
quotes
|
|
674
|
-
.filter(x => x.payload.validUntilSlot >= slotNumber)
|
|
675
|
-
.filter(x => x.payload.epochToProve === epochToProve)
|
|
676
|
-
.map(x => this.publisher.validateProofQuote(x)),
|
|
677
|
-
);
|
|
691
|
+
const validQuotes = await this.publisher.filterValidQuotes(p2pQuotes);
|
|
678
692
|
|
|
679
|
-
const validQuotes = (await validQuotesPromise).filter((q): q is EpochProofQuote => !!q);
|
|
680
693
|
if (!validQuotes.length) {
|
|
681
694
|
this.log.warn(`Failed to find any valid proof quotes`);
|
|
682
695
|
return undefined;
|
|
@@ -698,15 +711,14 @@ export class Sequencer {
|
|
|
698
711
|
* Publishes the L2Block to the rollup contract.
|
|
699
712
|
* @param block - The L2Block to be published.
|
|
700
713
|
*/
|
|
701
|
-
@trackSpan('Sequencer.
|
|
714
|
+
@trackSpan('Sequencer.enqueuePublishL2Block', block => ({
|
|
702
715
|
[Attributes.BLOCK_NUMBER]: block.number,
|
|
703
716
|
}))
|
|
704
|
-
protected async
|
|
717
|
+
protected async enqueuePublishL2Block(
|
|
705
718
|
block: L2Block,
|
|
706
719
|
attestations?: Signature[],
|
|
707
720
|
txHashes?: TxHash[],
|
|
708
|
-
|
|
709
|
-
) {
|
|
721
|
+
): Promise<void> {
|
|
710
722
|
// Publishes new block to the network and awaits the tx to be mined
|
|
711
723
|
this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
|
|
712
724
|
|
|
@@ -714,11 +726,12 @@ export class Sequencer {
|
|
|
714
726
|
const slot = block.header.globalVariables.slotNumber.toNumber();
|
|
715
727
|
const txTimeoutAt = new Date((this.getSlotStartTimestamp(slot) + this.aztecSlotDuration) * 1000);
|
|
716
728
|
|
|
717
|
-
const
|
|
729
|
+
const enqueued = await this.publisher.enqueueProposeL2Block(block, attestations, txHashes, {
|
|
718
730
|
txTimeoutAt,
|
|
719
731
|
});
|
|
720
|
-
|
|
721
|
-
|
|
732
|
+
|
|
733
|
+
if (!enqueued) {
|
|
734
|
+
throw new Error(`Failed to enqueue publish of block ${block.number}`);
|
|
722
735
|
}
|
|
723
736
|
}
|
|
724
737
|
|
|
@@ -737,11 +750,11 @@ export class Sequencer {
|
|
|
737
750
|
const epoch = proofQuote.payload.epochToProve;
|
|
738
751
|
const ctx = { slotNumber, epoch, quote: proofQuote.toInspect() };
|
|
739
752
|
this.log.verbose(`Claiming proof right for epoch ${epoch}`, ctx);
|
|
740
|
-
const
|
|
741
|
-
if (!
|
|
742
|
-
throw new Error(`Failed to claim proof right for epoch ${epoch}`);
|
|
753
|
+
const enqueued = this.publisher.enqueueClaimEpochProofRight(proofQuote);
|
|
754
|
+
if (!enqueued) {
|
|
755
|
+
throw new Error(`Failed to enqueue claim of proof right for epoch ${epoch}`);
|
|
743
756
|
}
|
|
744
|
-
this.log.info(`
|
|
757
|
+
this.log.info(`Enqueued claim of proof right for epoch ${epoch}`, ctx);
|
|
745
758
|
return epoch;
|
|
746
759
|
}
|
|
747
760
|
|
package/src/sequencer/utils.ts
CHANGED
|
@@ -49,12 +49,15 @@ export function sequencerStateToNumber(state: SequencerState): number {
|
|
|
49
49
|
*
|
|
50
50
|
* @todo: perform this logic within the memory attestation store instead?
|
|
51
51
|
*/
|
|
52
|
-
export function orderAttestations(
|
|
52
|
+
export async function orderAttestations(
|
|
53
|
+
attestations: BlockAttestation[],
|
|
54
|
+
orderAddresses: EthAddress[],
|
|
55
|
+
): Promise<Signature[]> {
|
|
53
56
|
// Create a map of sender addresses to BlockAttestations
|
|
54
57
|
const attestationMap = new Map<string, BlockAttestation>();
|
|
55
58
|
|
|
56
59
|
for (const attestation of attestations) {
|
|
57
|
-
const sender = attestation.getSender();
|
|
60
|
+
const sender = await attestation.getSender();
|
|
58
61
|
if (sender) {
|
|
59
62
|
attestationMap.set(sender.toString(), attestation);
|
|
60
63
|
}
|
|
@@ -315,7 +315,10 @@ export class SlasherClient extends WithTracer {
|
|
|
315
315
|
const blockHash =
|
|
316
316
|
blockNumber == 0
|
|
317
317
|
? ''
|
|
318
|
-
: await this.l2BlockSource
|
|
318
|
+
: await this.l2BlockSource
|
|
319
|
+
.getBlockHeader(blockNumber)
|
|
320
|
+
.then(header => header?.hash())
|
|
321
|
+
.then(hash => hash?.toString());
|
|
319
322
|
return Promise.resolve({
|
|
320
323
|
state: this.currentState,
|
|
321
324
|
syncedToL2Block: { number: blockNumber, hash: blockHash },
|
|
@@ -333,7 +336,9 @@ export class SlasherClient extends WithTracer {
|
|
|
333
336
|
}
|
|
334
337
|
|
|
335
338
|
const lastBlockNum = blocks[blocks.length - 1].number;
|
|
336
|
-
await Promise.all(
|
|
339
|
+
await Promise.all(
|
|
340
|
+
blocks.map(async block => this.synchedBlockHashes.set(block.number, (await block.hash()).toString())),
|
|
341
|
+
);
|
|
337
342
|
await this.synchedLatestBlockNumber.set(lastBlockNum);
|
|
338
343
|
this.log.debug(`Synched to latest block ${lastBlockNum}`);
|
|
339
344
|
this.startServiceIfSynched();
|
package/src/test/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { type PublicProcessorFactory } from '@aztec/simulator/server';
|
|
2
2
|
|
|
3
3
|
import { SequencerClient } from '../client/sequencer-client.js';
|
|
4
|
-
import { type
|
|
4
|
+
import { type SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
5
5
|
import { Sequencer } from '../sequencer/sequencer.js';
|
|
6
6
|
import { type SequencerTimetable } from '../sequencer/timetable.js';
|
|
7
7
|
|
|
8
8
|
class TestSequencer_ extends Sequencer {
|
|
9
9
|
public override publicProcessorFactory!: PublicProcessorFactory;
|
|
10
10
|
public override timetable!: SequencerTimetable;
|
|
11
|
-
public override publisher!:
|
|
11
|
+
public override publisher!: SequencerPublisher;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export type TestSequencer = TestSequencer_;
|
|
@@ -18,5 +18,3 @@ class TestSequencerClient_ extends SequencerClient {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type TestSequencerClient = TestSequencerClient_;
|
|
21
|
-
|
|
22
|
-
export * from './test-l1-publisher.js';
|
|
@@ -74,20 +74,21 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
74
74
|
// Read current balance of the feePayer
|
|
75
75
|
const initialBalance = await this.#publicDataSource.storageRead(
|
|
76
76
|
this.#feeJuiceAddress,
|
|
77
|
-
computeFeePayerBalanceStorageSlot(feePayer),
|
|
77
|
+
await computeFeePayerBalanceStorageSlot(feePayer),
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
81
81
|
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
82
|
+
const increasePublicBalanceSelector = await FunctionSelector.fromSignature(
|
|
83
|
+
'_increase_public_balance((Field),(Field,Field))',
|
|
84
|
+
);
|
|
82
85
|
const claimFunctionCall = setupFns.find(
|
|
83
86
|
fn =>
|
|
84
87
|
fn.callContext.contractAddress.equals(this.#feeJuiceAddress) &&
|
|
85
88
|
fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
|
|
86
89
|
fn.args.length > 2 &&
|
|
87
90
|
// Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
88
|
-
fn.args[0].equals(
|
|
89
|
-
FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))').toField(),
|
|
90
|
-
) &&
|
|
91
|
+
fn.args[0].equals(increasePublicBalanceSelector.toField()) &&
|
|
91
92
|
fn.args[1].equals(feePayer.toField()) &&
|
|
92
93
|
!fn.callContext.isStaticCall,
|
|
93
94
|
);
|
|
@@ -6,7 +6,7 @@ export function patchNonRevertibleFn(
|
|
|
6
6
|
tx: Tx,
|
|
7
7
|
index: number,
|
|
8
8
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
9
|
-
): { address: AztecAddress; selector: FunctionSelector } {
|
|
9
|
+
): Promise<{ address: AztecAddress; selector: FunctionSelector }> {
|
|
10
10
|
return patchFn('nonRevertibleAccumulatedData', tx, index, overrides);
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -14,16 +14,16 @@ export function patchRevertibleFn(
|
|
|
14
14
|
tx: Tx,
|
|
15
15
|
index: number,
|
|
16
16
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
17
|
-
): { address: AztecAddress; selector: FunctionSelector } {
|
|
17
|
+
): Promise<{ address: AztecAddress; selector: FunctionSelector }> {
|
|
18
18
|
return patchFn('revertibleAccumulatedData', tx, index, overrides);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function patchFn(
|
|
21
|
+
async function patchFn(
|
|
22
22
|
where: 'revertibleAccumulatedData' | 'nonRevertibleAccumulatedData',
|
|
23
23
|
tx: Tx,
|
|
24
24
|
index: number,
|
|
25
25
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
26
|
-
): { address: AztecAddress; selector: FunctionSelector } {
|
|
26
|
+
): Promise<{ address: AztecAddress; selector: FunctionSelector }> {
|
|
27
27
|
const fn = tx.enqueuedPublicFunctionCalls.at(-1 * index - 1)!;
|
|
28
28
|
fn.callContext.contractAddress = overrides.address ?? fn.callContext.contractAddress;
|
|
29
29
|
fn.callContext.functionSelector = overrides.selector;
|
|
@@ -36,7 +36,7 @@ function patchFn(
|
|
|
36
36
|
request.msgSender = fn.callContext.msgSender;
|
|
37
37
|
request.functionSelector = fn.callContext.functionSelector;
|
|
38
38
|
request.isStaticCall = fn.callContext.isStaticCall;
|
|
39
|
-
request.argsHash = computeVarArgsHash(fn.args);
|
|
39
|
+
request.argsHash = await computeVarArgsHash(fn.args);
|
|
40
40
|
tx.data.forPublic![where].publicCallRequests[index] = request;
|
|
41
41
|
|
|
42
42
|
return {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"l1-publisher-metrics.d.ts","sourceRoot":"","sources":["../../src/publisher/l1-publisher-metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC3G,OAAO,EAIL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,SAAS,GAAG,sBAAsB,CAAC;AAE1E,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAY;IAE5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,iBAAiB,CAAY;gBAEzB,MAAM,EAAE,eAAe,EAAE,IAAI,SAAgB;IAkDzD,cAAc,CAAC,MAAM,EAAE,QAAQ;IAO/B,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;IAIhE,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;IAInE,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc;IAItE,OAAO,CAAC,QAAQ;CA6BjB"}
|