@aztec/sequencer-client 0.68.0 → 0.68.2
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 +3 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +17 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -1
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/sequencer/sequencer.d.ts +13 -7
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +111 -35
- package/dest/sequencer/utils.d.ts +2 -7
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +3 -11
- package/package.json +20 -20
- package/src/client/sequencer-client.ts +25 -5
- package/src/config.ts +5 -0
- package/src/index.ts +1 -0
- package/src/sequencer/sequencer.ts +134 -39
- package/src/sequencer/utils.ts +2 -11
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type L1ToL2MessageSource, type L2BlockSource, type WorldStateSynchronizer } from '@aztec/circuit-types';
|
|
2
2
|
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
3
|
+
import { isAnvilTestChain } from '@aztec/ethereum';
|
|
3
4
|
import { type EthAddress } from '@aztec/foundation/eth-address';
|
|
5
|
+
import { type DateProvider } from '@aztec/foundation/timer';
|
|
4
6
|
import { type P2P } from '@aztec/p2p';
|
|
5
7
|
import { LightweightBlockBuilderFactory } from '@aztec/prover-client/block-builder';
|
|
6
8
|
import { PublicProcessorFactory } from '@aztec/simulator';
|
|
@@ -17,7 +19,7 @@ import { TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
|
|
|
17
19
|
* Encapsulates the full sequencer and publisher.
|
|
18
20
|
*/
|
|
19
21
|
export class SequencerClient {
|
|
20
|
-
constructor(
|
|
22
|
+
constructor(protected sequencer: Sequencer) {}
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Initializes and starts a new instance.
|
|
@@ -43,6 +45,7 @@ export class SequencerClient {
|
|
|
43
45
|
l1ToL2MessageSource: L1ToL2MessageSource;
|
|
44
46
|
telemetry: TelemetryClient;
|
|
45
47
|
publisher?: L1Publisher;
|
|
48
|
+
dateProvider: DateProvider;
|
|
46
49
|
},
|
|
47
50
|
) {
|
|
48
51
|
const {
|
|
@@ -57,7 +60,7 @@ export class SequencerClient {
|
|
|
57
60
|
const publisher = deps.publisher ?? new L1Publisher(config, telemetryClient);
|
|
58
61
|
const globalsBuilder = new GlobalVariableBuilder(config);
|
|
59
62
|
|
|
60
|
-
const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, telemetryClient);
|
|
63
|
+
const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, deps.dateProvider, telemetryClient);
|
|
61
64
|
|
|
62
65
|
const rollup = publisher.getRollupContract();
|
|
63
66
|
const [l1GenesisTime, slotDuration] = await Promise.all([
|
|
@@ -65,6 +68,23 @@ export class SequencerClient {
|
|
|
65
68
|
rollup.read.SLOT_DURATION(),
|
|
66
69
|
] as const);
|
|
67
70
|
|
|
71
|
+
const ethereumSlotDuration = config.ethereumSlotDuration;
|
|
72
|
+
|
|
73
|
+
// When running in anvil, assume we can post a tx up until the very last second of an L1 slot.
|
|
74
|
+
// Otherwise, assume we must have broadcasted the tx before the slot started (we use a default
|
|
75
|
+
// maxL1TxInclusionTimeIntoSlot of zero) to get the tx into that L1 slot.
|
|
76
|
+
// In theory, the L1 slot has an initial 4s phase where the block is propagated, so we could
|
|
77
|
+
// make it with a propagation time into slot equal to 4s. However, we prefer being conservative.
|
|
78
|
+
// See https://www.blocknative.com/blog/anatomy-of-a-slot#7 for more info.
|
|
79
|
+
const maxL1TxInclusionTimeIntoSlot =
|
|
80
|
+
config.maxL1TxInclusionTimeIntoSlot ?? isAnvilTestChain(config.l1ChainId) ? ethereumSlotDuration : 0;
|
|
81
|
+
|
|
82
|
+
const l1Constants = {
|
|
83
|
+
l1GenesisTime,
|
|
84
|
+
slotDuration: Number(slotDuration),
|
|
85
|
+
ethereumSlotDuration,
|
|
86
|
+
};
|
|
87
|
+
|
|
68
88
|
const sequencer = new Sequencer(
|
|
69
89
|
publisher,
|
|
70
90
|
validatorClient,
|
|
@@ -76,10 +96,10 @@ export class SequencerClient {
|
|
|
76
96
|
l1ToL2MessageSource,
|
|
77
97
|
publicProcessorFactory,
|
|
78
98
|
new TxValidatorFactory(worldStateSynchronizer.getCommitted(), contractDataSource, !!config.enforceFees),
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
l1Constants,
|
|
100
|
+
deps.dateProvider,
|
|
81
101
|
telemetryClient,
|
|
82
|
-
config,
|
|
102
|
+
{ ...config, maxL1TxInclusionTimeIntoSlot },
|
|
83
103
|
);
|
|
84
104
|
await validatorClient?.start();
|
|
85
105
|
await sequencer.start();
|
package/src/config.ts
CHANGED
|
@@ -106,6 +106,11 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
106
106
|
parseEnv: (val: string) => EthAddress.fromString(val),
|
|
107
107
|
defaultValue: EthAddress.ZERO,
|
|
108
108
|
},
|
|
109
|
+
maxL1TxInclusionTimeIntoSlot: {
|
|
110
|
+
env: 'SEQ_MAX_L1_TX_INCLUSION_TIME_INTO_SLOT',
|
|
111
|
+
description: 'How many seconds into an L1 slot we can still send a tx and get it mined.',
|
|
112
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
113
|
+
},
|
|
109
114
|
};
|
|
110
115
|
|
|
111
116
|
export const chainConfigMappings: ConfigMappingsType<ChainConfig> = {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './client/index.js';
|
|
2
2
|
export * from './config.js';
|
|
3
3
|
export * from './publisher/index.js';
|
|
4
|
+
export { Sequencer, SequencerState } from './sequencer/index.js';
|
|
4
5
|
|
|
5
6
|
// Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
|
|
6
7
|
// ISSUE(#9832)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type EpochProofQuote,
|
|
3
|
+
type L1RollupConstants,
|
|
3
4
|
type L1ToL2MessageSource,
|
|
4
5
|
type L2Block,
|
|
5
6
|
type L2BlockSource,
|
|
@@ -27,7 +28,7 @@ import { Fr } from '@aztec/foundation/fields';
|
|
|
27
28
|
import { createLogger } from '@aztec/foundation/log';
|
|
28
29
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
29
30
|
import { pickFromSchema } from '@aztec/foundation/schemas';
|
|
30
|
-
import { Timer, elapsed } from '@aztec/foundation/timer';
|
|
31
|
+
import { type DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
|
|
31
32
|
import { type P2P } from '@aztec/p2p';
|
|
32
33
|
import { type BlockBuilderFactory } from '@aztec/prover-client/block-builder';
|
|
33
34
|
import { type PublicProcessorFactory } from '@aztec/simulator';
|
|
@@ -41,7 +42,9 @@ import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js
|
|
|
41
42
|
import { getDefaultAllowedSetupFunctions } from './allowed.js';
|
|
42
43
|
import { type SequencerConfig } from './config.js';
|
|
43
44
|
import { SequencerMetrics } from './metrics.js';
|
|
44
|
-
import { SequencerState,
|
|
45
|
+
import { SequencerState, orderAttestations } from './utils.js';
|
|
46
|
+
|
|
47
|
+
export { SequencerState };
|
|
45
48
|
|
|
46
49
|
export type ShouldProposeArgs = {
|
|
47
50
|
pendingTxsCount?: number;
|
|
@@ -63,6 +66,8 @@ export class SequencerTooSlowError extends Error {
|
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration'>;
|
|
70
|
+
|
|
66
71
|
/**
|
|
67
72
|
* Sequencer client
|
|
68
73
|
* - Wins a period of time to become the sequencer (depending on finalized protocol).
|
|
@@ -77,12 +82,14 @@ export class Sequencer {
|
|
|
77
82
|
private pollingIntervalMs: number = 1000;
|
|
78
83
|
private maxTxsPerBlock = 32;
|
|
79
84
|
private minTxsPerBLock = 1;
|
|
85
|
+
private maxL1TxInclusionTimeIntoSlot = 0;
|
|
80
86
|
// TODO: zero values should not be allowed for the following 2 values in PROD
|
|
81
87
|
private _coinbase = EthAddress.ZERO;
|
|
82
88
|
private _feeRecipient = AztecAddress.ZERO;
|
|
83
89
|
private state = SequencerState.STOPPED;
|
|
84
90
|
private allowedInSetup: AllowedElement[] = getDefaultAllowedSetupFunctions();
|
|
85
91
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
92
|
+
private processTxTime: number = 12;
|
|
86
93
|
private metrics: SequencerMetrics;
|
|
87
94
|
private isFlushing: boolean = false;
|
|
88
95
|
|
|
@@ -104,8 +111,8 @@ export class Sequencer {
|
|
|
104
111
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
105
112
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
106
113
|
private txValidatorFactory: TxValidatorFactory,
|
|
107
|
-
protected
|
|
108
|
-
private
|
|
114
|
+
protected l1Constants: SequencerRollupConstants,
|
|
115
|
+
private dateProvider: DateProvider,
|
|
109
116
|
telemetry: TelemetryClient,
|
|
110
117
|
private config: SequencerConfig = {},
|
|
111
118
|
private log = createLogger('sequencer'),
|
|
@@ -152,6 +159,9 @@ export class Sequencer {
|
|
|
152
159
|
if (config.governanceProposerPayload) {
|
|
153
160
|
this.publisher.setPayload(config.governanceProposerPayload);
|
|
154
161
|
}
|
|
162
|
+
if (config.maxL1TxInclusionTimeIntoSlot !== undefined) {
|
|
163
|
+
this.maxL1TxInclusionTimeIntoSlot = config.maxL1TxInclusionTimeIntoSlot;
|
|
164
|
+
}
|
|
155
165
|
this.enforceTimeTable = config.enforceTimeTable === true;
|
|
156
166
|
|
|
157
167
|
this.setTimeTable();
|
|
@@ -161,20 +171,59 @@ export class Sequencer {
|
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
private setTimeTable() {
|
|
174
|
+
// How late into the slot can we be to start working
|
|
175
|
+
const initialTime = 1;
|
|
176
|
+
|
|
177
|
+
// How long it takes to validate the txs collected and get ready to start building
|
|
178
|
+
const blockPrepareTime = 2;
|
|
179
|
+
|
|
180
|
+
// How long it takes to for attestations to travel across the p2p layer.
|
|
181
|
+
const attestationPropagationTime = 2;
|
|
182
|
+
|
|
183
|
+
// How long it takes to get a published block into L1. L1 builders typically accept txs up to 4 seconds into their slot,
|
|
184
|
+
// but we'll timeout sooner to give it more time to propagate (remember we also have blobs!). Still, when working in anvil,
|
|
185
|
+
// we can just post in the very last second of the L1 slot.
|
|
186
|
+
const l1PublishingTime = this.l1Constants.ethereumSlotDuration - this.maxL1TxInclusionTimeIntoSlot;
|
|
187
|
+
|
|
188
|
+
// How much time we spend validating and processing a block after building it
|
|
189
|
+
const blockValidationTime = 1;
|
|
190
|
+
|
|
191
|
+
// How much time we have left in the slot for actually processing txs and building the block.
|
|
192
|
+
const remainingTimeInSlot =
|
|
193
|
+
this.aztecSlotDuration -
|
|
194
|
+
initialTime -
|
|
195
|
+
blockPrepareTime -
|
|
196
|
+
l1PublishingTime -
|
|
197
|
+
2 * attestationPropagationTime -
|
|
198
|
+
blockValidationTime;
|
|
199
|
+
|
|
200
|
+
// Check that numbers make sense
|
|
201
|
+
if (this.enforceTimeTable && remainingTimeInSlot < 0) {
|
|
202
|
+
throw new Error(`Not enough time for block building in ${this.aztecSlotDuration}s slot`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// How much time we have for actually processing txs. Note that we need both the sequencer and the validators to execute txs.
|
|
206
|
+
const processTxsTime = remainingTimeInSlot / 2;
|
|
207
|
+
this.processTxTime = processTxsTime;
|
|
208
|
+
|
|
164
209
|
const newTimeTable: Record<SequencerState, number> = {
|
|
210
|
+
// No checks needed for any of these transitions
|
|
165
211
|
[SequencerState.STOPPED]: this.aztecSlotDuration,
|
|
166
212
|
[SequencerState.IDLE]: this.aztecSlotDuration,
|
|
167
213
|
[SequencerState.SYNCHRONIZING]: this.aztecSlotDuration,
|
|
168
|
-
|
|
169
|
-
[SequencerState.
|
|
170
|
-
|
|
171
|
-
[SequencerState.
|
|
172
|
-
|
|
173
|
-
[SequencerState.
|
|
214
|
+
// We always want to allow the full slot to check if we are the proposer
|
|
215
|
+
[SequencerState.PROPOSER_CHECK]: this.aztecSlotDuration,
|
|
216
|
+
// First transition towards building a block
|
|
217
|
+
[SequencerState.WAITING_FOR_TXS]: initialTime,
|
|
218
|
+
// We then validate the txs and prepare to start building the block
|
|
219
|
+
[SequencerState.CREATING_BLOCK]: initialTime + blockPrepareTime,
|
|
220
|
+
// We start collecting attestations after building the block
|
|
221
|
+
[SequencerState.COLLECTING_ATTESTATIONS]: initialTime + blockPrepareTime + processTxsTime + blockValidationTime,
|
|
222
|
+
// We publish the block after collecting attestations
|
|
223
|
+
[SequencerState.PUBLISHING_BLOCK]: this.aztecSlotDuration - l1PublishingTime,
|
|
174
224
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
225
|
+
|
|
226
|
+
this.log.verbose(`Sequencer time table updated with ${processTxsTime}s for processing txs`, newTimeTable);
|
|
178
227
|
this.timeTable = newTimeTable;
|
|
179
228
|
}
|
|
180
229
|
|
|
@@ -297,7 +346,8 @@ export class Sequencer {
|
|
|
297
346
|
Fr.ZERO,
|
|
298
347
|
);
|
|
299
348
|
|
|
300
|
-
// TODO: It should be responsibility of the P2P layer to validate txs before passing them on here
|
|
349
|
+
// TODO: It should be responsibility of the P2P layer to validate txs before passing them on here.
|
|
350
|
+
// TODO: We should validate only the number of txs we need to speed up this process.
|
|
301
351
|
const allValidTxs = await this.takeValidTxs(
|
|
302
352
|
pendingTxs,
|
|
303
353
|
this.txValidatorFactory.validatorForNewTxs(newGlobalVariables, this.allowedInSetup),
|
|
@@ -372,24 +422,21 @@ export class Sequencer {
|
|
|
372
422
|
return true;
|
|
373
423
|
}
|
|
374
424
|
|
|
375
|
-
|
|
425
|
+
const maxAllowedTime = this.timeTable[proposedState];
|
|
426
|
+
if (maxAllowedTime === this.aztecSlotDuration) {
|
|
376
427
|
return true;
|
|
377
428
|
}
|
|
378
429
|
|
|
379
|
-
const bufferSeconds =
|
|
430
|
+
const bufferSeconds = maxAllowedTime - secondsIntoSlot;
|
|
380
431
|
|
|
381
432
|
if (bufferSeconds < 0) {
|
|
382
|
-
this.log.warn(
|
|
383
|
-
`Too far into slot to transition to ${proposedState}. max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
|
|
384
|
-
);
|
|
433
|
+
this.log.warn(`Too far into slot to transition to ${proposedState}`, { maxAllowedTime, secondsIntoSlot });
|
|
385
434
|
return false;
|
|
386
435
|
}
|
|
387
436
|
|
|
388
437
|
this.metrics.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), proposedState);
|
|
389
438
|
|
|
390
|
-
this.log.
|
|
391
|
-
`Enough time to transition to ${proposedState}, max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
|
|
392
|
-
);
|
|
439
|
+
this.log.trace(`Enough time to transition to ${proposedState}`, { maxAllowedTime, secondsIntoSlot });
|
|
393
440
|
return true;
|
|
394
441
|
}
|
|
395
442
|
|
|
@@ -407,7 +454,7 @@ export class Sequencer {
|
|
|
407
454
|
this.log.warn(`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped.`);
|
|
408
455
|
return;
|
|
409
456
|
}
|
|
410
|
-
const secondsIntoSlot =
|
|
457
|
+
const secondsIntoSlot = this.getSecondsIntoSlot(currentSlotNumber);
|
|
411
458
|
if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) {
|
|
412
459
|
throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot);
|
|
413
460
|
}
|
|
@@ -466,12 +513,14 @@ export class Sequencer {
|
|
|
466
513
|
* @param newGlobalVariables - The global variables for the new block
|
|
467
514
|
* @param historicalHeader - The historical header of the parent
|
|
468
515
|
* @param interrupt - The interrupt callback, used to validate the block for submission and check if we should propose the block
|
|
516
|
+
* @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
|
|
469
517
|
*/
|
|
470
518
|
private async buildBlock(
|
|
471
519
|
validTxs: Tx[],
|
|
472
520
|
newGlobalVariables: GlobalVariables,
|
|
473
521
|
historicalHeader?: BlockHeader,
|
|
474
522
|
interrupt?: (processedTxs: ProcessedTx[]) => Promise<void>,
|
|
523
|
+
opts: { validateOnly?: boolean } = {},
|
|
475
524
|
) {
|
|
476
525
|
const blockNumber = newGlobalVariables.blockNumber.toBigInt();
|
|
477
526
|
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
@@ -511,15 +560,42 @@ export class Sequencer {
|
|
|
511
560
|
const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
|
|
512
561
|
await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages);
|
|
513
562
|
|
|
563
|
+
// We set the deadline for tx processing to the start of the CREATING_BLOCK phase, plus the expected time for tx processing.
|
|
564
|
+
// Deadline is only set if enforceTimeTable is enabled.
|
|
565
|
+
const processingEndTimeWithinSlot = this.timeTable[SequencerState.CREATING_BLOCK] + this.processTxTime;
|
|
566
|
+
const processingDeadline = this.enforceTimeTable
|
|
567
|
+
? new Date((this.getSlotStartTimestamp(slot) + processingEndTimeWithinSlot) * 1000)
|
|
568
|
+
: undefined;
|
|
569
|
+
this.log.verbose(`Processing ${validTxs.length} txs`, {
|
|
570
|
+
slot,
|
|
571
|
+
slotStart: new Date(this.getSlotStartTimestamp(slot) * 1000),
|
|
572
|
+
now: new Date(this.dateProvider.now()),
|
|
573
|
+
deadline: processingDeadline,
|
|
574
|
+
});
|
|
575
|
+
const processingTxValidator = this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork);
|
|
514
576
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
515
|
-
processor.process(validTxs, blockSize,
|
|
577
|
+
processor.process(validTxs, blockSize, processingTxValidator, processingDeadline),
|
|
516
578
|
);
|
|
579
|
+
|
|
517
580
|
if (failedTxs.length > 0) {
|
|
518
581
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
519
582
|
this.log.verbose(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
|
|
520
583
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
521
584
|
}
|
|
522
585
|
|
|
586
|
+
if (
|
|
587
|
+
!opts.validateOnly && // We check for minTxCount only if we are proposing a block, not if we are validating it
|
|
588
|
+
!this.isFlushing && // And we skip the check when flushing, since we want all pending txs to go out, no matter if too few
|
|
589
|
+
this.minTxsPerBLock !== undefined &&
|
|
590
|
+
processedTxs.length < this.minTxsPerBLock
|
|
591
|
+
) {
|
|
592
|
+
this.log.warn(
|
|
593
|
+
`Block ${blockNumber} has too few txs to be proposed (got ${processedTxs.length} but required ${this.minTxsPerBLock})`,
|
|
594
|
+
{ slot, blockNumber, processedTxCount: processedTxs.length },
|
|
595
|
+
);
|
|
596
|
+
throw new Error(`Block has too few successful txs to be proposed`);
|
|
597
|
+
}
|
|
598
|
+
|
|
523
599
|
const start = process.hrtime.bigint();
|
|
524
600
|
await blockBuilder.addTxs(processedTxs);
|
|
525
601
|
const end = process.hrtime.bigint();
|
|
@@ -528,7 +604,7 @@ export class Sequencer {
|
|
|
528
604
|
|
|
529
605
|
await interrupt?.(processedTxs);
|
|
530
606
|
|
|
531
|
-
// All real transactions have been added, set the block as full and
|
|
607
|
+
// All real transactions have been added, set the block as full and pad if needed
|
|
532
608
|
const block = await blockBuilder.setBlockCompleted();
|
|
533
609
|
|
|
534
610
|
return {
|
|
@@ -540,8 +616,16 @@ export class Sequencer {
|
|
|
540
616
|
};
|
|
541
617
|
} finally {
|
|
542
618
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
543
|
-
|
|
544
|
-
|
|
619
|
+
// We wait a bit to close the forks since the processor may still be working on a dangling tx
|
|
620
|
+
// which was interrupted due to the processingDeadline being hit.
|
|
621
|
+
setTimeout(async () => {
|
|
622
|
+
try {
|
|
623
|
+
await publicProcessorFork.close();
|
|
624
|
+
await orchestratorFork.close();
|
|
625
|
+
} catch (err) {
|
|
626
|
+
this.log.error(`Error closing forks`, err);
|
|
627
|
+
}
|
|
628
|
+
}, 5000);
|
|
545
629
|
}
|
|
546
630
|
}
|
|
547
631
|
|
|
@@ -593,6 +677,9 @@ export class Sequencer {
|
|
|
593
677
|
}
|
|
594
678
|
};
|
|
595
679
|
|
|
680
|
+
// Start collecting proof quotes for the previous epoch if needed in the background
|
|
681
|
+
const proofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
|
|
682
|
+
|
|
596
683
|
try {
|
|
597
684
|
const buildBlockRes = await this.buildBlock(validTxs, newGlobalVariables, historicalHeader, interrupt);
|
|
598
685
|
const { block, publicProcessorDuration, numProcessedTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
|
|
@@ -630,12 +717,12 @@ export class Sequencer {
|
|
|
630
717
|
const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
|
|
631
718
|
const attestations = await this.collectAttestations(block, txHashes);
|
|
632
719
|
if (attestations !== undefined) {
|
|
633
|
-
this.log.verbose(`Collected ${attestations.length} attestations
|
|
720
|
+
this.log.verbose(`Collected ${attestations.length} attestations`, { blockHash, blockNumber });
|
|
634
721
|
}
|
|
635
722
|
stopCollectingAttestationsTimer();
|
|
636
723
|
|
|
637
|
-
|
|
638
|
-
const proofQuote = await
|
|
724
|
+
// Get the proof quote for the previous epoch, if any
|
|
725
|
+
const proofQuote = await proofQuotePromise;
|
|
639
726
|
|
|
640
727
|
await this.publishL2Block(block, attestations, txHashes, proofQuote);
|
|
641
728
|
this.metrics.recordPublishedBlock(workDuration);
|
|
@@ -687,21 +774,19 @@ export class Sequencer {
|
|
|
687
774
|
}
|
|
688
775
|
|
|
689
776
|
const numberOfRequiredAttestations = Math.floor((committee.length * 2) / 3) + 1;
|
|
777
|
+
const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
|
|
778
|
+
this.setState(SequencerState.COLLECTING_ATTESTATIONS, slotNumber);
|
|
690
779
|
|
|
691
|
-
this.log.debug('Creating block proposal');
|
|
780
|
+
this.log.debug('Creating block proposal for validators');
|
|
692
781
|
const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
|
|
693
782
|
if (!proposal) {
|
|
694
783
|
this.log.warn(`Failed to create block proposal, skipping collecting attestations`);
|
|
695
784
|
return undefined;
|
|
696
785
|
}
|
|
697
786
|
|
|
698
|
-
const slotNumber = block.header.globalVariables.slotNumber.toBigInt();
|
|
699
|
-
|
|
700
|
-
this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS, slotNumber);
|
|
701
787
|
this.log.debug('Broadcasting block proposal to validators');
|
|
702
788
|
this.validatorClient.broadcastBlockProposal(proposal);
|
|
703
789
|
|
|
704
|
-
this.setState(SequencerState.WAITING_FOR_ATTESTATIONS, slotNumber);
|
|
705
790
|
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations);
|
|
706
791
|
|
|
707
792
|
// note: the smart contract requires that the signatures are provided in the order of the committee
|
|
@@ -718,6 +803,7 @@ export class Sequencer {
|
|
|
718
803
|
}
|
|
719
804
|
|
|
720
805
|
// Get quotes for the epoch to be proven
|
|
806
|
+
this.log.debug(`Collecting proof quotes for epoch ${epochToProve}`);
|
|
721
807
|
const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
|
|
722
808
|
this.log.verbose(`Retrieved ${quotes.length} quotes for slot ${slotNumber} epoch ${epochToProve}`, {
|
|
723
809
|
epochToProve,
|
|
@@ -861,6 +947,19 @@ export class Sequencer {
|
|
|
861
947
|
return result;
|
|
862
948
|
}
|
|
863
949
|
|
|
950
|
+
private getSlotStartTimestamp(slotNumber: number | bigint): number {
|
|
951
|
+
return Number(this.l1Constants.l1GenesisTime) + Number(slotNumber) * this.l1Constants.slotDuration;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
private getSecondsIntoSlot(slotNumber: number | bigint): number {
|
|
955
|
+
const slotStartTimestamp = this.getSlotStartTimestamp(slotNumber);
|
|
956
|
+
return Number((this.dateProvider.now() / 1000 - slotStartTimestamp).toFixed(3));
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
get aztecSlotDuration() {
|
|
960
|
+
return this.l1Constants.slotDuration;
|
|
961
|
+
}
|
|
962
|
+
|
|
864
963
|
get coinbase(): EthAddress {
|
|
865
964
|
return this._coinbase;
|
|
866
965
|
}
|
|
@@ -869,7 +968,3 @@ export class Sequencer {
|
|
|
869
968
|
return this._feeRecipient;
|
|
870
969
|
}
|
|
871
970
|
}
|
|
872
|
-
|
|
873
|
-
/**
|
|
874
|
-
* State of the sequencer.
|
|
875
|
-
*/
|
package/src/sequencer/utils.ts
CHANGED
|
@@ -27,13 +27,9 @@ export enum SequencerState {
|
|
|
27
27
|
*/
|
|
28
28
|
CREATING_BLOCK = 'CREATING_BLOCK',
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Collecting attestations from its peers. Will move to PUBLISHING_BLOCK.
|
|
31
31
|
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The block has been published to peers, and we are waiting for attestations. Will move to PUBLISHING_CONTRACT_DATA.
|
|
35
|
-
*/
|
|
36
|
-
WAITING_FOR_ATTESTATIONS = 'WAITING_FOR_ATTESTATIONS',
|
|
32
|
+
COLLECTING_ATTESTATIONS = 'COLLECTING_ATTESTATIONS',
|
|
37
33
|
/**
|
|
38
34
|
* Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING.
|
|
39
35
|
*/
|
|
@@ -72,8 +68,3 @@ export function orderAttestations(attestations: BlockAttestation[], orderAddress
|
|
|
72
68
|
|
|
73
69
|
return orderedAttestations;
|
|
74
70
|
}
|
|
75
|
-
|
|
76
|
-
export function getSecondsIntoSlot(l1GenesisTime: number, aztecSlotDuration: number, slotNumber: number): number {
|
|
77
|
-
const slotStartTimestamp = l1GenesisTime + slotNumber * aztecSlotDuration;
|
|
78
|
-
return Number((Date.now() / 1000 - slotStartTimestamp).toFixed(3));
|
|
79
|
-
}
|