@aztec/sequencer-client 0.65.2 → 0.67.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.map +1 -1
- package/dest/client/sequencer-client.js +3 -2
- package/dest/config.js +3 -3
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +3 -4
- package/dest/publisher/config.d.ts +4 -4
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +3 -2
- package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/l1-publisher-metrics.js +2 -8
- package/dest/publisher/l1-publisher.d.ts +7 -4
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +67 -50
- package/dest/publisher/utils.d.ts.map +1 -1
- package/dest/publisher/utils.js +2 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +2 -8
- package/dest/sequencer/sequencer.d.ts +13 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +124 -73
- package/dest/sequencer/utils.js +2 -2
- package/dest/tx_validator/gas_validator.js +3 -3
- package/dest/tx_validator/phases_validator.js +3 -3
- package/package.json +23 -19
- package/src/client/sequencer-client.ts +2 -2
- package/src/config.ts +2 -2
- package/src/global_variable_builder/global_builder.ts +3 -3
- package/src/publisher/config.ts +7 -4
- package/src/publisher/l1-publisher-metrics.ts +1 -7
- package/src/publisher/l1-publisher.ts +98 -64
- package/src/publisher/utils.ts +1 -0
- package/src/sequencer/metrics.ts +0 -7
- package/src/sequencer/sequencer.ts +152 -123
- package/src/sequencer/utils.ts +1 -1
- package/src/tx_validator/gas_validator.ts +2 -2
- package/src/tx_validator/phases_validator.ts +2 -2
- package/dest/block_builder/index.d.ts +0 -7
- package/dest/block_builder/index.d.ts.map +0 -1
- package/dest/block_builder/index.js +0 -3
- package/dest/block_builder/light.d.ts +0 -26
- package/dest/block_builder/light.d.ts.map +0 -1
- package/dest/block_builder/light.js +0 -58
- package/dest/block_builder/orchestrator.d.ts +0 -23
- package/dest/block_builder/orchestrator.d.ts.map +0 -1
- package/dest/block_builder/orchestrator.js +0 -33
- package/src/block_builder/index.ts +0 -7
- package/src/block_builder/light.ts +0 -92
- package/src/block_builder/orchestrator.ts +0 -43
package/src/sequencer/metrics.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
type Tracer,
|
|
8
8
|
type UpDownCounter,
|
|
9
9
|
ValueType,
|
|
10
|
-
millisecondBuckets,
|
|
11
10
|
} from '@aztec/telemetry-client';
|
|
12
11
|
|
|
13
12
|
import { type SequencerState, type SequencerStateCallback, sequencerStateToNumber } from './utils.js';
|
|
@@ -32,18 +31,12 @@ export class SequencerMetrics {
|
|
|
32
31
|
unit: 'ms',
|
|
33
32
|
description: 'Duration to build a block',
|
|
34
33
|
valueType: ValueType.INT,
|
|
35
|
-
advice: {
|
|
36
|
-
explicitBucketBoundaries: millisecondBuckets(2),
|
|
37
|
-
},
|
|
38
34
|
});
|
|
39
35
|
this.stateTransitionBufferDuration = meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION, {
|
|
40
36
|
unit: 'ms',
|
|
41
37
|
description:
|
|
42
38
|
'The time difference between when the sequencer needed to transition to a new state and when it actually did.',
|
|
43
39
|
valueType: ValueType.INT,
|
|
44
|
-
advice: {
|
|
45
|
-
explicitBucketBoundaries: millisecondBuckets(2),
|
|
46
|
-
},
|
|
47
40
|
});
|
|
48
41
|
|
|
49
42
|
const currentState = meter.createObservableGauge(Metrics.SEQUENCER_CURRENT_STATE, {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type L2Block,
|
|
5
5
|
type L2BlockSource,
|
|
6
6
|
type ProcessedTx,
|
|
7
|
+
SequencerConfigSchema,
|
|
7
8
|
Tx,
|
|
8
9
|
type TxHash,
|
|
9
10
|
type TxValidator,
|
|
@@ -13,26 +14,26 @@ import type { AllowedElement, Signature, WorldStateSynchronizerStatus } from '@a
|
|
|
13
14
|
import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
|
|
14
15
|
import {
|
|
15
16
|
AppendOnlyTreeSnapshot,
|
|
17
|
+
BlockHeader,
|
|
16
18
|
ContentCommitment,
|
|
17
19
|
GENESIS_ARCHIVE_ROOT,
|
|
18
20
|
type GlobalVariables,
|
|
19
|
-
Header,
|
|
20
21
|
StateReference,
|
|
21
22
|
} from '@aztec/circuits.js';
|
|
22
23
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
24
|
+
import { omit } from '@aztec/foundation/collection';
|
|
23
25
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
24
26
|
import { Fr } from '@aztec/foundation/fields';
|
|
25
|
-
import {
|
|
27
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
26
28
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
29
|
+
import { pickFromSchema } from '@aztec/foundation/schemas';
|
|
27
30
|
import { Timer, elapsed } from '@aztec/foundation/timer';
|
|
28
31
|
import { type P2P } from '@aztec/p2p';
|
|
32
|
+
import { type BlockBuilderFactory } from '@aztec/prover-client/block-builder';
|
|
29
33
|
import { type PublicProcessorFactory } from '@aztec/simulator';
|
|
30
34
|
import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
31
35
|
import { type ValidatorClient } from '@aztec/validator-client';
|
|
32
36
|
|
|
33
|
-
import { inspect } from 'util';
|
|
34
|
-
|
|
35
|
-
import { type BlockBuilderFactory } from '../block_builder/index.js';
|
|
36
37
|
import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
37
38
|
import { type L1Publisher } from '../publisher/l1-publisher.js';
|
|
38
39
|
import { prettyLogViemErrorMsg } from '../publisher/utils.js';
|
|
@@ -108,11 +109,10 @@ export class Sequencer {
|
|
|
108
109
|
private aztecSlotDuration: number,
|
|
109
110
|
telemetry: TelemetryClient,
|
|
110
111
|
private config: SequencerConfig = {},
|
|
111
|
-
private log =
|
|
112
|
+
private log = createLogger('sequencer'),
|
|
112
113
|
) {
|
|
113
114
|
this.updateConfig(config);
|
|
114
115
|
this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
|
|
115
|
-
this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`);
|
|
116
116
|
|
|
117
117
|
// Register the block builder with the validator client for re-execution
|
|
118
118
|
this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
|
|
@@ -127,6 +127,11 @@ export class Sequencer {
|
|
|
127
127
|
* @param config - New parameters.
|
|
128
128
|
*/
|
|
129
129
|
public updateConfig(config: SequencerConfig) {
|
|
130
|
+
this.log.info(
|
|
131
|
+
`Sequencer config set`,
|
|
132
|
+
omit(pickFromSchema(this.config, SequencerConfigSchema), 'allowedInSetup', 'allowedInTeardown'),
|
|
133
|
+
);
|
|
134
|
+
|
|
130
135
|
if (config.transactionPollingIntervalMS !== undefined) {
|
|
131
136
|
this.pollingIntervalMs = config.transactionPollingIntervalMS;
|
|
132
137
|
}
|
|
@@ -188,9 +193,9 @@ export class Sequencer {
|
|
|
188
193
|
*/
|
|
189
194
|
public start() {
|
|
190
195
|
this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
|
|
196
|
+
this.setState(SequencerState.IDLE, 0n, true /** force */);
|
|
191
197
|
this.runningPromise.start();
|
|
192
|
-
this.
|
|
193
|
-
this.log.info('Sequencer started');
|
|
198
|
+
this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
|
|
194
199
|
return Promise.resolve();
|
|
195
200
|
}
|
|
196
201
|
|
|
@@ -199,9 +204,10 @@ export class Sequencer {
|
|
|
199
204
|
*/
|
|
200
205
|
public async stop(): Promise<void> {
|
|
201
206
|
this.log.debug(`Stopping sequencer`);
|
|
207
|
+
await this.validatorClient?.stop();
|
|
202
208
|
await this.runningPromise?.stop();
|
|
203
209
|
this.publisher.interrupt();
|
|
204
|
-
this.setState(SequencerState.STOPPED,
|
|
210
|
+
this.setState(SequencerState.STOPPED, 0n, true /** force */);
|
|
205
211
|
this.log.info('Stopped sequencer');
|
|
206
212
|
}
|
|
207
213
|
|
|
@@ -212,7 +218,7 @@ export class Sequencer {
|
|
|
212
218
|
this.log.info('Restarting sequencer');
|
|
213
219
|
this.publisher.restart();
|
|
214
220
|
this.runningPromise!.start();
|
|
215
|
-
this.setState(SequencerState.IDLE,
|
|
221
|
+
this.setState(SequencerState.IDLE, 0n, true /** force */);
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
/**
|
|
@@ -232,18 +238,15 @@ export class Sequencer {
|
|
|
232
238
|
* - If our block for some reason is not included, revert the state
|
|
233
239
|
*/
|
|
234
240
|
protected async doRealWork() {
|
|
235
|
-
this.setState(SequencerState.SYNCHRONIZING,
|
|
241
|
+
this.setState(SequencerState.SYNCHRONIZING, 0n);
|
|
236
242
|
// Update state when the previous block has been synced
|
|
237
243
|
const prevBlockSynced = await this.isBlockSynced();
|
|
238
244
|
// Do not go forward with new block if the previous one has not been mined and processed
|
|
239
245
|
if (!prevBlockSynced) {
|
|
240
|
-
this.log.debug('Previous block has not been mined and processed yet');
|
|
241
246
|
return;
|
|
242
247
|
}
|
|
243
248
|
|
|
244
|
-
this.
|
|
245
|
-
|
|
246
|
-
this.setState(SequencerState.PROPOSER_CHECK, 0);
|
|
249
|
+
this.setState(SequencerState.PROPOSER_CHECK, 0n);
|
|
247
250
|
|
|
248
251
|
const chainTip = await this.l2BlockSource.getBlock(-1);
|
|
249
252
|
const historicalHeader = chainTip?.header;
|
|
@@ -277,20 +280,24 @@ export class Sequencer {
|
|
|
277
280
|
if (!this.shouldProposeBlock(historicalHeader, {})) {
|
|
278
281
|
return;
|
|
279
282
|
}
|
|
280
|
-
const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration, Number(slot));
|
|
281
283
|
|
|
282
|
-
this.
|
|
284
|
+
this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
|
|
285
|
+
chainTipArchive: new Fr(chainTipArchive),
|
|
286
|
+
blockNumber: newBlockNumber,
|
|
287
|
+
slot,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
this.setState(SequencerState.WAITING_FOR_TXS, slot);
|
|
283
291
|
|
|
284
292
|
// Get txs to build the new block.
|
|
285
|
-
const pendingTxs = this.p2pClient.
|
|
293
|
+
const pendingTxs = await this.p2pClient.getPendingTxs();
|
|
286
294
|
|
|
287
295
|
if (!this.shouldProposeBlock(historicalHeader, { pendingTxsCount: pendingTxs.length })) {
|
|
288
296
|
return;
|
|
289
297
|
}
|
|
290
|
-
this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
|
|
291
298
|
|
|
292
299
|
// If I created a "partial" header here that should make our job much easier.
|
|
293
|
-
const proposalHeader = new
|
|
300
|
+
const proposalHeader = new BlockHeader(
|
|
294
301
|
new AppendOnlyTreeSnapshot(Fr.fromBuffer(chainTipArchive), 1),
|
|
295
302
|
ContentCommitment.empty(),
|
|
296
303
|
StateReference.empty(),
|
|
@@ -312,6 +319,10 @@ export class Sequencer {
|
|
|
312
319
|
// may break if we start emitting lots of log data from public-land.
|
|
313
320
|
const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
|
|
314
321
|
|
|
322
|
+
this.log.verbose(
|
|
323
|
+
`Collected ${validTxs.length} txs out of ${allValidTxs.length} valid txs out of ${pendingTxs.length} total pending txs for block ${newBlockNumber}`,
|
|
324
|
+
);
|
|
325
|
+
|
|
315
326
|
// Bail if we don't have enough valid txs
|
|
316
327
|
if (!this.shouldProposeBlock(historicalHeader, { validTxsCount: validTxs.length })) {
|
|
317
328
|
return;
|
|
@@ -323,9 +334,9 @@ export class Sequencer {
|
|
|
323
334
|
// be in for a world of pain.
|
|
324
335
|
await this.buildBlockAndAttemptToPublish(validTxs, proposalHeader, historicalHeader);
|
|
325
336
|
} catch (err) {
|
|
326
|
-
this.log.error(`Error assembling block`,
|
|
337
|
+
this.log.error(`Error assembling block`, err, { blockNumber: newBlockNumber, slot });
|
|
327
338
|
}
|
|
328
|
-
this.setState(SequencerState.IDLE,
|
|
339
|
+
this.setState(SequencerState.IDLE, 0n);
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
protected async work() {
|
|
@@ -339,12 +350,12 @@ export class Sequencer {
|
|
|
339
350
|
throw err;
|
|
340
351
|
}
|
|
341
352
|
} finally {
|
|
342
|
-
this.setState(SequencerState.IDLE,
|
|
353
|
+
this.setState(SequencerState.IDLE, 0n);
|
|
343
354
|
}
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
|
|
347
|
-
private skipMinTxsPerBlockCheck(historicalHeader:
|
|
358
|
+
private skipMinTxsPerBlockCheck(historicalHeader: BlockHeader | undefined): boolean {
|
|
348
359
|
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
|
|
349
360
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
350
361
|
const elapsed = currentTime - lastBlockTime;
|
|
@@ -358,16 +369,14 @@ export class Sequencer {
|
|
|
358
369
|
const [slot, blockNumber] = await this.publisher.canProposeAtNextEthBlock(tipArchive);
|
|
359
370
|
|
|
360
371
|
if (proposalBlockNumber !== blockNumber) {
|
|
361
|
-
const msg = `
|
|
362
|
-
this.log.
|
|
372
|
+
const msg = `Sequencer block number mismatch. Expected ${proposalBlockNumber} but got ${blockNumber}.`;
|
|
373
|
+
this.log.warn(msg);
|
|
363
374
|
throw new Error(msg);
|
|
364
375
|
}
|
|
365
|
-
|
|
366
|
-
this.log.verbose(`Can propose block ${proposalBlockNumber} at slot ${slot}`);
|
|
367
376
|
return slot;
|
|
368
377
|
} catch (err) {
|
|
369
378
|
const msg = prettyLogViemErrorMsg(err);
|
|
370
|
-
this.log.
|
|
379
|
+
this.log.debug(
|
|
371
380
|
`Rejected from being able to propose at next block with ${tipArchive.toString('hex')}: ${msg ? `${msg}` : ''}`,
|
|
372
381
|
);
|
|
373
382
|
throw err;
|
|
@@ -384,7 +393,6 @@ export class Sequencer {
|
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
const bufferSeconds = this.timeTable[proposedState] - secondsIntoSlot;
|
|
387
|
-
this.metrics.recordStateTransitionBufferMs(bufferSeconds * 1000, proposedState);
|
|
388
396
|
|
|
389
397
|
if (bufferSeconds < 0) {
|
|
390
398
|
this.log.warn(
|
|
@@ -392,26 +400,38 @@ export class Sequencer {
|
|
|
392
400
|
);
|
|
393
401
|
return false;
|
|
394
402
|
}
|
|
403
|
+
|
|
404
|
+
this.metrics.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), proposedState);
|
|
405
|
+
|
|
395
406
|
this.log.debug(
|
|
396
407
|
`Enough time to transition to ${proposedState}, max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
|
|
397
408
|
);
|
|
398
409
|
return true;
|
|
399
410
|
}
|
|
400
411
|
|
|
401
|
-
|
|
412
|
+
/**
|
|
413
|
+
* Sets the sequencer state and checks if we have enough time left in the slot to transition to the new state.
|
|
414
|
+
* @param proposedState - The new state to transition to.
|
|
415
|
+
* @param currentSlotNumber - The current slot number.
|
|
416
|
+
* @param force - Whether to force the transition even if the sequencer is stopped.
|
|
417
|
+
*
|
|
418
|
+
* @dev If the `currentSlotNumber` doesn't matter (e.g. transitioning to IDLE), pass in `0n`;
|
|
419
|
+
* it is only used to check if we have enough time left in the slot to transition to the new state.
|
|
420
|
+
*/
|
|
421
|
+
setState(proposedState: SequencerState, currentSlotNumber: bigint, force: boolean = false) {
|
|
402
422
|
if (this.state === SequencerState.STOPPED && force !== true) {
|
|
403
|
-
this.log.warn(
|
|
404
|
-
`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped. Set force=true to override.`,
|
|
405
|
-
);
|
|
423
|
+
this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
|
|
406
424
|
return;
|
|
407
425
|
}
|
|
426
|
+
const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration, Number(currentSlotNumber));
|
|
408
427
|
if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) {
|
|
409
428
|
throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot);
|
|
410
429
|
}
|
|
430
|
+
this.log.debug(`Transitioning from ${this.state} to ${proposedState}`);
|
|
411
431
|
this.state = proposedState;
|
|
412
432
|
}
|
|
413
433
|
|
|
414
|
-
shouldProposeBlock(historicalHeader:
|
|
434
|
+
shouldProposeBlock(historicalHeader: BlockHeader | undefined, args: ShouldProposeArgs): boolean {
|
|
415
435
|
if (this.isFlushing) {
|
|
416
436
|
this.log.verbose(`Flushing all pending txs in new block`);
|
|
417
437
|
return true;
|
|
@@ -428,7 +448,7 @@ export class Sequencer {
|
|
|
428
448
|
// If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
|
|
429
449
|
// Do not go forward with new block if not enough time has passed since last block
|
|
430
450
|
if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
|
|
431
|
-
this.log.
|
|
451
|
+
this.log.verbose(
|
|
432
452
|
`Not creating block because not enough time ${this.minSecondsBetweenBlocks} has passed since last block`,
|
|
433
453
|
);
|
|
434
454
|
return false;
|
|
@@ -444,7 +464,7 @@ export class Sequencer {
|
|
|
444
464
|
`Creating block with only ${args.pendingTxsCount} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
|
|
445
465
|
);
|
|
446
466
|
} else {
|
|
447
|
-
this.log.
|
|
467
|
+
this.log.verbose(
|
|
448
468
|
`Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
|
|
449
469
|
);
|
|
450
470
|
return false;
|
|
@@ -456,7 +476,7 @@ export class Sequencer {
|
|
|
456
476
|
if (args.validTxsCount != undefined) {
|
|
457
477
|
// Bail if we don't have enough valid txs
|
|
458
478
|
if (!skipCheck && args.validTxsCount < this.minTxsPerBLock) {
|
|
459
|
-
this.log.
|
|
479
|
+
this.log.verbose(
|
|
460
480
|
`Not creating block because not enough valid txs loaded from the pool (got ${args.validTxsCount} min ${this.minTxsPerBLock})`,
|
|
461
481
|
);
|
|
462
482
|
return false;
|
|
@@ -469,7 +489,7 @@ export class Sequencer {
|
|
|
469
489
|
// we should bail.
|
|
470
490
|
if (args.processedTxsCount != undefined) {
|
|
471
491
|
if (args.processedTxsCount === 0 && !skipCheck && this.minTxsPerBLock > 0) {
|
|
472
|
-
this.log.verbose('No txs processed correctly to build block.
|
|
492
|
+
this.log.verbose('No txs processed correctly to build block.');
|
|
473
493
|
return false;
|
|
474
494
|
}
|
|
475
495
|
}
|
|
@@ -490,13 +510,23 @@ export class Sequencer {
|
|
|
490
510
|
private async buildBlock(
|
|
491
511
|
validTxs: Tx[],
|
|
492
512
|
newGlobalVariables: GlobalVariables,
|
|
493
|
-
historicalHeader?:
|
|
513
|
+
historicalHeader?: BlockHeader,
|
|
494
514
|
interrupt?: (processedTxs: ProcessedTx[]) => Promise<void>,
|
|
495
515
|
) {
|
|
496
|
-
|
|
497
|
-
const
|
|
516
|
+
const blockNumber = newGlobalVariables.blockNumber.toBigInt();
|
|
517
|
+
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
518
|
+
|
|
519
|
+
this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
|
|
520
|
+
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
521
|
+
|
|
498
522
|
this.log.verbose(
|
|
499
|
-
`
|
|
523
|
+
`Building block ${blockNumber} with ${validTxs.length} txs and ${l1ToL2Messages.length} messages`,
|
|
524
|
+
{
|
|
525
|
+
msgCount: l1ToL2Messages.length,
|
|
526
|
+
txCount: validTxs.length,
|
|
527
|
+
slot,
|
|
528
|
+
blockNumber,
|
|
529
|
+
},
|
|
500
530
|
);
|
|
501
531
|
|
|
502
532
|
const numRealTxs = validTxs.length;
|
|
@@ -504,7 +534,7 @@ export class Sequencer {
|
|
|
504
534
|
|
|
505
535
|
// Sync to the previous block at least
|
|
506
536
|
await this.worldState.syncImmediate(newGlobalVariables.blockNumber.toNumber() - 1);
|
|
507
|
-
this.log.
|
|
537
|
+
this.log.debug(`Synced to previous block ${newGlobalVariables.blockNumber.toNumber() - 1}`);
|
|
508
538
|
|
|
509
539
|
// NB: separating the dbs because both should update the state
|
|
510
540
|
const publicProcessorFork = await this.worldState.fork();
|
|
@@ -526,7 +556,7 @@ export class Sequencer {
|
|
|
526
556
|
);
|
|
527
557
|
if (failedTxs.length > 0) {
|
|
528
558
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
529
|
-
this.log.
|
|
559
|
+
this.log.verbose(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
|
|
530
560
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
531
561
|
}
|
|
532
562
|
|
|
@@ -535,7 +565,13 @@ export class Sequencer {
|
|
|
535
565
|
// All real transactions have been added, set the block as full and complete the proving.
|
|
536
566
|
const block = await blockBuilder.setBlockCompleted();
|
|
537
567
|
|
|
538
|
-
return {
|
|
568
|
+
return {
|
|
569
|
+
block,
|
|
570
|
+
publicProcessorDuration,
|
|
571
|
+
numMsgs: l1ToL2Messages.length,
|
|
572
|
+
numProcessedTxs: processedTxs.length,
|
|
573
|
+
blockBuildingTimer,
|
|
574
|
+
};
|
|
539
575
|
} finally {
|
|
540
576
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
541
577
|
await publicProcessorFork.close();
|
|
@@ -558,26 +594,18 @@ export class Sequencer {
|
|
|
558
594
|
}))
|
|
559
595
|
private async buildBlockAndAttemptToPublish(
|
|
560
596
|
validTxs: Tx[],
|
|
561
|
-
proposalHeader:
|
|
562
|
-
historicalHeader:
|
|
597
|
+
proposalHeader: BlockHeader,
|
|
598
|
+
historicalHeader: BlockHeader | undefined,
|
|
563
599
|
): Promise<void> {
|
|
564
600
|
await this.publisher.validateBlockForSubmission(proposalHeader);
|
|
565
601
|
|
|
566
602
|
const newGlobalVariables = proposalHeader.globalVariables;
|
|
603
|
+
const blockNumber = newGlobalVariables.blockNumber.toNumber();
|
|
604
|
+
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
567
605
|
|
|
568
|
-
this.metrics.recordNewBlock(
|
|
606
|
+
this.metrics.recordNewBlock(blockNumber, validTxs.length);
|
|
569
607
|
const workTimer = new Timer();
|
|
570
|
-
|
|
571
|
-
this.l1GenesisTime,
|
|
572
|
-
this.aztecSlotDuration,
|
|
573
|
-
newGlobalVariables.slotNumber.toNumber(),
|
|
574
|
-
);
|
|
575
|
-
this.setState(SequencerState.CREATING_BLOCK, secondsIntoSlot);
|
|
576
|
-
this.log.info(
|
|
577
|
-
`Building blockNumber=${newGlobalVariables.blockNumber.toNumber()} txCount=${
|
|
578
|
-
validTxs.length
|
|
579
|
-
} slotNumber=${newGlobalVariables.slotNumber.toNumber()}`,
|
|
580
|
-
);
|
|
608
|
+
this.setState(SequencerState.CREATING_BLOCK, slot);
|
|
581
609
|
|
|
582
610
|
/**
|
|
583
611
|
* BuildBlock is shared between the sequencer and the validator for re-execution
|
|
@@ -600,12 +628,8 @@ export class Sequencer {
|
|
|
600
628
|
};
|
|
601
629
|
|
|
602
630
|
try {
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
newGlobalVariables,
|
|
606
|
-
historicalHeader,
|
|
607
|
-
interrupt,
|
|
608
|
-
);
|
|
631
|
+
const buildBlockRes = await this.buildBlock(validTxs, newGlobalVariables, historicalHeader, interrupt);
|
|
632
|
+
const { block, publicProcessorDuration, numProcessedTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
|
|
609
633
|
|
|
610
634
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
611
635
|
// block being published before ours instead of just waiting on our block
|
|
@@ -613,43 +637,56 @@ export class Sequencer {
|
|
|
613
637
|
await this.publisher.validateBlockForSubmission(block.header);
|
|
614
638
|
|
|
615
639
|
const workDuration = workTimer.ms();
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
640
|
+
const blockStats: L2BlockBuiltStats = {
|
|
641
|
+
eventName: 'l2-block-built',
|
|
642
|
+
creator: this.publisher.getSenderAddress().toString(),
|
|
643
|
+
duration: workDuration,
|
|
644
|
+
publicProcessDuration: publicProcessorDuration,
|
|
645
|
+
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
646
|
+
...block.getStats(),
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const blockHash = block.hash();
|
|
650
|
+
const txHashes = validTxs.map(tx => tx.getTxHash());
|
|
651
|
+
this.log.info(`Built block ${block.number} with hash ${blockHash}`, {
|
|
652
|
+
txEffectsHash: block.header.contentCommitment.txsEffectsHash.toString('hex'),
|
|
653
|
+
blockHash,
|
|
654
|
+
globalVariables: block.header.globalVariables.toInspect(),
|
|
655
|
+
txHashes,
|
|
656
|
+
...blockStats,
|
|
657
|
+
});
|
|
629
658
|
|
|
630
659
|
if (this.isFlushing) {
|
|
631
|
-
this.log.
|
|
660
|
+
this.log.verbose(`Sequencer flushing completed`);
|
|
632
661
|
}
|
|
633
662
|
|
|
634
|
-
const txHashes = validTxs.map(tx => tx.getTxHash());
|
|
635
|
-
|
|
636
663
|
this.isFlushing = false;
|
|
637
|
-
this.log.
|
|
664
|
+
this.log.debug('Collecting attestations');
|
|
638
665
|
const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
|
|
639
666
|
const attestations = await this.collectAttestations(block, txHashes);
|
|
640
|
-
|
|
667
|
+
if (attestations !== undefined) {
|
|
668
|
+
this.log.verbose(`Collected ${attestations.length} attestations`);
|
|
669
|
+
}
|
|
641
670
|
stopCollectingAttestationsTimer();
|
|
642
|
-
this.log.verbose('Collecting proof quotes');
|
|
643
671
|
|
|
672
|
+
this.log.debug('Collecting proof quotes');
|
|
644
673
|
const proofQuote = await this.createProofClaimForPreviousEpoch(newGlobalVariables.slotNumber.toBigInt());
|
|
645
|
-
this.log.info(proofQuote ? `Using proof quote ${inspect(proofQuote.payload)}` : 'No proof quote available');
|
|
646
674
|
|
|
647
675
|
await this.publishL2Block(block, attestations, txHashes, proofQuote);
|
|
648
676
|
this.metrics.recordPublishedBlock(workDuration);
|
|
649
677
|
this.log.info(
|
|
650
|
-
`
|
|
651
|
-
|
|
652
|
-
|
|
678
|
+
`Published rollup block ${
|
|
679
|
+
block.number
|
|
680
|
+
} with ${numProcessedTxs} transactions and ${numMsgs} messages in ${Math.ceil(workDuration)}ms`,
|
|
681
|
+
{
|
|
682
|
+
blockNumber: block.number,
|
|
683
|
+
blockHash: blockHash,
|
|
684
|
+
slot,
|
|
685
|
+
txCount: numProcessedTxs,
|
|
686
|
+
msgCount: numMsgs,
|
|
687
|
+
duration: Math.ceil(workDuration),
|
|
688
|
+
submitter: this.publisher.getSenderAddress().toString(),
|
|
689
|
+
},
|
|
653
690
|
);
|
|
654
691
|
} catch (err) {
|
|
655
692
|
this.metrics.recordFailedBlock();
|
|
@@ -670,11 +707,12 @@ export class Sequencer {
|
|
|
670
707
|
protected async collectAttestations(block: L2Block, txHashes: TxHash[]): Promise<Signature[] | undefined> {
|
|
671
708
|
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
|
|
672
709
|
const committee = await this.publisher.getCurrentEpochCommittee();
|
|
673
|
-
this.log.debug(`Attesting committee length ${committee.length}`);
|
|
674
710
|
|
|
675
711
|
if (committee.length === 0) {
|
|
676
|
-
this.log.verbose(`Attesting committee
|
|
712
|
+
this.log.verbose(`Attesting committee is empty`);
|
|
677
713
|
return undefined;
|
|
714
|
+
} else {
|
|
715
|
+
this.log.debug(`Attesting committee length is ${committee.length}`);
|
|
678
716
|
}
|
|
679
717
|
|
|
680
718
|
if (!this.validatorClient) {
|
|
@@ -685,28 +723,21 @@ export class Sequencer {
|
|
|
685
723
|
|
|
686
724
|
const numberOfRequiredAttestations = Math.floor((committee.length * 2) / 3) + 1;
|
|
687
725
|
|
|
688
|
-
this.log.
|
|
726
|
+
this.log.debug('Creating block proposal');
|
|
689
727
|
const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
|
|
728
|
+
if (!proposal) {
|
|
729
|
+
this.log.warn(`Failed to create block proposal, skipping collecting attestations`);
|
|
730
|
+
return undefined;
|
|
731
|
+
}
|
|
690
732
|
|
|
691
|
-
|
|
692
|
-
this.l1GenesisTime,
|
|
693
|
-
this.aztecSlotDuration,
|
|
694
|
-
block.header.globalVariables.slotNumber.toNumber(),
|
|
695
|
-
);
|
|
733
|
+
const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
|
|
696
734
|
|
|
697
|
-
this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS,
|
|
698
|
-
this.log.
|
|
735
|
+
this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS, slotNumber);
|
|
736
|
+
this.log.debug('Broadcasting block proposal to validators');
|
|
699
737
|
this.validatorClient.broadcastBlockProposal(proposal);
|
|
700
738
|
|
|
701
|
-
|
|
702
|
-
this.l1GenesisTime,
|
|
703
|
-
this.aztecSlotDuration,
|
|
704
|
-
block.header.globalVariables.slotNumber.toNumber(),
|
|
705
|
-
);
|
|
706
|
-
|
|
707
|
-
this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, secondsIntoSlot);
|
|
739
|
+
this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, slotNumber);
|
|
708
740
|
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations);
|
|
709
|
-
this.log.info(`Collected attestations from validators, number of attestations: ${attestations.length}`);
|
|
710
741
|
|
|
711
742
|
// note: the smart contract requires that the signatures are provided in the order of the committee
|
|
712
743
|
return orderAttestations(attestations, committee);
|
|
@@ -717,16 +748,17 @@ export class Sequencer {
|
|
|
717
748
|
// Find out which epoch we are currently in
|
|
718
749
|
const epochToProve = await this.publisher.getClaimableEpoch();
|
|
719
750
|
if (epochToProve === undefined) {
|
|
720
|
-
this.log.
|
|
751
|
+
this.log.debug(`No epoch to prove`);
|
|
721
752
|
return undefined;
|
|
722
753
|
}
|
|
723
754
|
|
|
724
755
|
// Get quotes for the epoch to be proven
|
|
725
756
|
const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
|
|
726
|
-
this.log.
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
757
|
+
this.log.verbose(`Retrieved ${quotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
|
|
758
|
+
epochToProve,
|
|
759
|
+
slotNumber,
|
|
760
|
+
quotes: quotes.map(q => q.payload),
|
|
761
|
+
});
|
|
730
762
|
// ensure these quotes are still valid for the slot and have the contract validate them
|
|
731
763
|
const validQuotesPromise = Promise.all(
|
|
732
764
|
quotes.filter(x => x.payload.validUntilSlot >= slotNumber).map(x => this.publisher.validateProofQuote(x)),
|
|
@@ -741,9 +773,11 @@ export class Sequencer {
|
|
|
741
773
|
const sortedQuotes = validQuotes.sort(
|
|
742
774
|
(a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
|
|
743
775
|
);
|
|
744
|
-
|
|
776
|
+
const quote = sortedQuotes[0];
|
|
777
|
+
this.log.info(`Selected proof quote for proof claim`, quote.payload);
|
|
778
|
+
return quote;
|
|
745
779
|
} catch (err) {
|
|
746
|
-
this.log.error(`Failed to create proof claim for previous epoch
|
|
780
|
+
this.log.error(`Failed to create proof claim for previous epoch`, err, { slotNumber });
|
|
747
781
|
return undefined;
|
|
748
782
|
}
|
|
749
783
|
}
|
|
@@ -761,13 +795,8 @@ export class Sequencer {
|
|
|
761
795
|
txHashes?: TxHash[],
|
|
762
796
|
proofQuote?: EpochProofQuote,
|
|
763
797
|
) {
|
|
764
|
-
const secondsIntoSlot = getSecondsIntoSlot(
|
|
765
|
-
this.l1GenesisTime,
|
|
766
|
-
this.aztecSlotDuration,
|
|
767
|
-
block.header.globalVariables.slotNumber.toNumber(),
|
|
768
|
-
);
|
|
769
798
|
// Publishes new block to the network and awaits the tx to be mined
|
|
770
|
-
this.setState(SequencerState.PUBLISHING_BLOCK,
|
|
799
|
+
this.setState(SequencerState.PUBLISHING_BLOCK, block.header.globalVariables.slotNumber.toBigInt());
|
|
771
800
|
|
|
772
801
|
const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote);
|
|
773
802
|
if (!publishedL2Block) {
|
|
@@ -793,7 +822,7 @@ export class Sequencer {
|
|
|
793
822
|
for (const tx of txs) {
|
|
794
823
|
const txSize = tx.getSize() - tx.clientIvcProof.clientIvcProofBuffer.length;
|
|
795
824
|
if (totalSize + txSize > maxSize) {
|
|
796
|
-
this.log.
|
|
825
|
+
this.log.debug(
|
|
797
826
|
`Dropping tx ${tx.getTxHash()} with estimated size ${txSize} due to exceeding ${maxSize} block size limit (currently at ${totalSize})`,
|
|
798
827
|
);
|
|
799
828
|
continue;
|
|
@@ -830,7 +859,7 @@ export class Sequencer {
|
|
|
830
859
|
p2p >= l2BlockSource.number &&
|
|
831
860
|
l1ToL2MessageSource >= l2BlockSource.number;
|
|
832
861
|
|
|
833
|
-
this.log.
|
|
862
|
+
this.log.debug(`Sequencer sync check ${result ? 'succeeded' : 'failed'}`, {
|
|
834
863
|
worldStateNumber: worldState.number,
|
|
835
864
|
worldStateHash: worldState.hash,
|
|
836
865
|
l2BlockSourceNumber: l2BlockSource.number,
|
package/src/sequencer/utils.ts
CHANGED
|
@@ -75,5 +75,5 @@ export function orderAttestations(attestations: BlockAttestation[], orderAddress
|
|
|
75
75
|
|
|
76
76
|
export function getSecondsIntoSlot(l1GenesisTime: number, aztecSlotDuration: number, slotNumber: number): number {
|
|
77
77
|
const slotStartTimestamp = l1GenesisTime + slotNumber * aztecSlotDuration;
|
|
78
|
-
return Date.now() / 1000 - slotStartTimestamp;
|
|
78
|
+
return Number((Date.now() / 1000 - slotStartTimestamp).toFixed(3));
|
|
79
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
|
|
3
|
-
import {
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
5
5
|
|
|
6
6
|
/** Provides a view into public contract state */
|
|
@@ -9,7 +9,7 @@ export interface PublicStateSource {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export class GasTxValidator implements TxValidator<Tx> {
|
|
12
|
-
#log =
|
|
12
|
+
#log = createLogger('sequencer:tx_validator:tx_gas');
|
|
13
13
|
#publicDataSource: PublicStateSource;
|
|
14
14
|
#feeJuiceAddress: AztecAddress;
|
|
15
15
|
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
type TxValidator,
|
|
7
7
|
} from '@aztec/circuit-types';
|
|
8
8
|
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
9
|
-
import {
|
|
9
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
10
10
|
import { ContractsDataSourcePublicDB, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
11
11
|
|
|
12
12
|
export class PhasesTxValidator implements TxValidator<Tx> {
|
|
13
|
-
#log =
|
|
13
|
+
#log = createLogger('sequencer:tx_validator:tx_phases');
|
|
14
14
|
private contractDataSource: ContractsDataSourcePublicDB;
|
|
15
15
|
|
|
16
16
|
constructor(contracts: ContractDataSource, private setupAllowList: AllowedElement[]) {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type BlockBuilder, type MerkleTreeReadOperations } from '@aztec/circuit-types';
|
|
2
|
-
export * from './orchestrator.js';
|
|
3
|
-
export * from './light.js';
|
|
4
|
-
export interface BlockBuilderFactory {
|
|
5
|
-
create(db: MerkleTreeReadOperations): BlockBuilder;
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|