@aztec/sequencer-client 0.62.0 → 0.63.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/block_builder/light.d.ts.map +1 -1
- package/dest/block_builder/light.js +3 -5
- package/dest/client/sequencer-client.d.ts +1 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +9 -4
- package/dest/config.d.ts +2 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +14 -10
- package/dest/global_variable_builder/global_builder.d.ts +3 -2
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +4 -3
- package/dest/publisher/l1-publisher.d.ts +6 -4
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +22 -18
- package/dest/sequencer/metrics.d.ts +3 -2
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +16 -2
- package/dest/sequencer/sequencer.d.ts +23 -38
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +104 -94
- package/dest/sequencer/utils.d.ts +52 -0
- package/dest/sequencer/utils.d.ts.map +1 -0
- package/dest/sequencer/utils.js +70 -0
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +5 -5
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +4 -4
- package/dest/tx_validator/test_utils.js +9 -7
- package/package.json +19 -19
- package/src/block_builder/light.ts +1 -4
- package/src/client/sequencer-client.ts +10 -2
- package/src/config.ts +24 -10
- package/src/global_variable_builder/global_builder.ts +7 -10
- package/src/publisher/l1-publisher.ts +29 -21
- package/src/sequencer/metrics.ts +18 -2
- package/src/sequencer/sequencer.ts +130 -100
- package/src/sequencer/utils.ts +78 -0
- package/src/tx_validator/gas_validator.ts +4 -4
- package/src/tx_validator/phases_validator.ts +3 -3
- package/src/tx_validator/test_utils.ts +9 -7
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type BlockAttestation,
|
|
3
2
|
type EpochProofQuote,
|
|
4
3
|
type L1ToL2MessageSource,
|
|
5
4
|
type L2Block,
|
|
@@ -10,11 +9,7 @@ import {
|
|
|
10
9
|
type TxValidator,
|
|
11
10
|
type WorldStateSynchronizer,
|
|
12
11
|
} from '@aztec/circuit-types';
|
|
13
|
-
import {
|
|
14
|
-
type AllowedElement,
|
|
15
|
-
BlockProofError,
|
|
16
|
-
type WorldStateSynchronizerStatus,
|
|
17
|
-
} from '@aztec/circuit-types/interfaces';
|
|
12
|
+
import type { AllowedElement, Signature, WorldStateSynchronizerStatus } from '@aztec/circuit-types/interfaces';
|
|
18
13
|
import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
|
|
19
14
|
import {
|
|
20
15
|
AppendOnlyTreeSnapshot,
|
|
@@ -25,7 +20,6 @@ import {
|
|
|
25
20
|
} from '@aztec/circuits.js';
|
|
26
21
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
27
22
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
28
|
-
import { Signature } from '@aztec/foundation/eth-signature';
|
|
29
23
|
import { Fr } from '@aztec/foundation/fields';
|
|
30
24
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
31
25
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
@@ -44,6 +38,7 @@ import { prettyLogViemErrorMsg } from '../publisher/utils.js';
|
|
|
44
38
|
import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
|
|
45
39
|
import { type SequencerConfig } from './config.js';
|
|
46
40
|
import { SequencerMetrics } from './metrics.js';
|
|
41
|
+
import { SequencerState, getSecondsIntoSlot, orderAttestations } from './utils.js';
|
|
47
42
|
|
|
48
43
|
export type ShouldProposeArgs = {
|
|
49
44
|
pendingTxsCount?: number;
|
|
@@ -51,6 +46,20 @@ export type ShouldProposeArgs = {
|
|
|
51
46
|
processedTxsCount?: number;
|
|
52
47
|
};
|
|
53
48
|
|
|
49
|
+
export class SequencerTooSlowError extends Error {
|
|
50
|
+
constructor(
|
|
51
|
+
public readonly currentState: SequencerState,
|
|
52
|
+
public readonly proposedState: SequencerState,
|
|
53
|
+
public readonly maxAllowedTime: number,
|
|
54
|
+
public readonly currentTime: number,
|
|
55
|
+
) {
|
|
56
|
+
super(
|
|
57
|
+
`Too far into slot to transition to ${proposedState}. max allowed: ${maxAllowedTime}s, time into slot: ${currentTime}s`,
|
|
58
|
+
);
|
|
59
|
+
this.name = 'SequencerTooSlowError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
/**
|
|
55
64
|
* Sequencer client
|
|
56
65
|
* - Wins a period of time to become the sequencer (depending on finalized protocol).
|
|
@@ -72,11 +81,17 @@ export class Sequencer {
|
|
|
72
81
|
private _feeRecipient = AztecAddress.ZERO;
|
|
73
82
|
private state = SequencerState.STOPPED;
|
|
74
83
|
private allowedInSetup: AllowedElement[] = [];
|
|
75
|
-
private allowedInTeardown: AllowedElement[] = [];
|
|
76
84
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
77
85
|
private metrics: SequencerMetrics;
|
|
78
86
|
private isFlushing: boolean = false;
|
|
79
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The maximum number of seconds that the sequencer can be into a slot to transition to a particular state.
|
|
90
|
+
* For example, in order to transition into WAITING_FOR_ATTESTATIONS, the sequencer can be at most 3 seconds into the slot.
|
|
91
|
+
*/
|
|
92
|
+
protected timeTable!: Record<SequencerState, number>;
|
|
93
|
+
protected enforceTimeTable: boolean = false;
|
|
94
|
+
|
|
80
95
|
constructor(
|
|
81
96
|
private publisher: L1Publisher,
|
|
82
97
|
private validatorClient: ValidatorClient | undefined, // During migration the validator client can be inactive
|
|
@@ -88,6 +103,8 @@ export class Sequencer {
|
|
|
88
103
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
89
104
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
90
105
|
private txValidatorFactory: TxValidatorFactory,
|
|
106
|
+
protected l1GenesisTime: number,
|
|
107
|
+
private aztecSlotDuration: number,
|
|
91
108
|
telemetry: TelemetryClient,
|
|
92
109
|
private config: SequencerConfig = {},
|
|
93
110
|
private log = createDebugLogger('aztec:sequencer'),
|
|
@@ -133,25 +150,42 @@ export class Sequencer {
|
|
|
133
150
|
if (config.maxBlockSizeInBytes !== undefined) {
|
|
134
151
|
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
|
|
135
152
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.allowedInTeardown = config.allowedInTeardown;
|
|
139
|
-
}
|
|
140
|
-
if (config.gerousiaPayload) {
|
|
141
|
-
this.publisher.setPayload(config.gerousiaPayload);
|
|
153
|
+
if (config.governanceProposerPayload) {
|
|
154
|
+
this.publisher.setPayload(config.governanceProposerPayload);
|
|
142
155
|
}
|
|
156
|
+
this.enforceTimeTable = config.enforceTimeTable === true;
|
|
157
|
+
|
|
158
|
+
this.setTimeTable();
|
|
143
159
|
|
|
144
160
|
// TODO: Just read everything from the config object as needed instead of copying everything into local vars.
|
|
145
161
|
this.config = config;
|
|
146
162
|
}
|
|
147
163
|
|
|
164
|
+
private setTimeTable() {
|
|
165
|
+
const newTimeTable: Record<SequencerState, number> = {
|
|
166
|
+
[SequencerState.STOPPED]: this.aztecSlotDuration,
|
|
167
|
+
[SequencerState.IDLE]: this.aztecSlotDuration,
|
|
168
|
+
[SequencerState.SYNCHRONIZING]: this.aztecSlotDuration,
|
|
169
|
+
[SequencerState.PROPOSER_CHECK]: this.aztecSlotDuration, // We always want to allow the full slot to check if we are the proposer
|
|
170
|
+
[SequencerState.WAITING_FOR_TXS]: 3,
|
|
171
|
+
[SequencerState.CREATING_BLOCK]: 5,
|
|
172
|
+
[SequencerState.PUBLISHING_BLOCK_TO_PEERS]: 5 + this.maxTxsPerBlock * 2, // if we take 5 seconds to create block, then 4 transactions at 2 seconds each
|
|
173
|
+
[SequencerState.WAITING_FOR_ATTESTATIONS]: 5 + this.maxTxsPerBlock * 2 + 3, // it shouldn't take 3 seconds to publish to peers
|
|
174
|
+
[SequencerState.PUBLISHING_BLOCK]: 5 + this.maxTxsPerBlock * 2 + 3 + 5, // wait 5 seconds for attestations
|
|
175
|
+
};
|
|
176
|
+
if (this.enforceTimeTable && newTimeTable[SequencerState.PUBLISHING_BLOCK] > this.aztecSlotDuration) {
|
|
177
|
+
throw new Error('Sequencer cannot publish block in less than a slot');
|
|
178
|
+
}
|
|
179
|
+
this.timeTable = newTimeTable;
|
|
180
|
+
}
|
|
181
|
+
|
|
148
182
|
/**
|
|
149
|
-
* Starts the sequencer and moves to IDLE state.
|
|
183
|
+
* Starts the sequencer and moves to IDLE state.
|
|
150
184
|
*/
|
|
151
185
|
public start() {
|
|
152
186
|
this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
|
|
153
187
|
this.runningPromise.start();
|
|
154
|
-
this.
|
|
188
|
+
this.setState(SequencerState.IDLE, true /** force */);
|
|
155
189
|
this.log.info('Sequencer started');
|
|
156
190
|
return Promise.resolve();
|
|
157
191
|
}
|
|
@@ -163,7 +197,7 @@ export class Sequencer {
|
|
|
163
197
|
this.log.debug(`Stopping sequencer`);
|
|
164
198
|
await this.runningPromise?.stop();
|
|
165
199
|
this.publisher.interrupt();
|
|
166
|
-
this.
|
|
200
|
+
this.setState(SequencerState.STOPPED, true /** force */);
|
|
167
201
|
this.log.info('Stopped sequencer');
|
|
168
202
|
}
|
|
169
203
|
|
|
@@ -174,7 +208,7 @@ export class Sequencer {
|
|
|
174
208
|
this.log.info('Restarting sequencer');
|
|
175
209
|
this.publisher.restart();
|
|
176
210
|
this.runningPromise!.start();
|
|
177
|
-
this.
|
|
211
|
+
this.setState(SequencerState.IDLE, true /** force */);
|
|
178
212
|
}
|
|
179
213
|
|
|
180
214
|
/**
|
|
@@ -193,7 +227,8 @@ export class Sequencer {
|
|
|
193
227
|
* - Submit block
|
|
194
228
|
* - If our block for some reason is not included, revert the state
|
|
195
229
|
*/
|
|
196
|
-
protected async
|
|
230
|
+
protected async doRealWork() {
|
|
231
|
+
this.setState(SequencerState.SYNCHRONIZING);
|
|
197
232
|
// Update state when the previous block has been synced
|
|
198
233
|
const prevBlockSynced = await this.isBlockSynced();
|
|
199
234
|
// Do not go forward with new block if the previous one has not been mined and processed
|
|
@@ -202,10 +237,9 @@ export class Sequencer {
|
|
|
202
237
|
return;
|
|
203
238
|
}
|
|
204
239
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
240
|
+
this.log.debug('Previous block has been mined and processed');
|
|
241
|
+
|
|
242
|
+
this.setState(SequencerState.PROPOSER_CHECK);
|
|
209
243
|
|
|
210
244
|
const chainTip = await this.l2BlockSource.getBlock(-1);
|
|
211
245
|
const historicalHeader = chainTip?.header;
|
|
@@ -240,7 +274,7 @@ export class Sequencer {
|
|
|
240
274
|
return;
|
|
241
275
|
}
|
|
242
276
|
|
|
243
|
-
this.
|
|
277
|
+
this.setState(SequencerState.WAITING_FOR_TXS);
|
|
244
278
|
|
|
245
279
|
// Get txs to build the new block.
|
|
246
280
|
const pendingTxs = this.p2pClient.getTxs('pending');
|
|
@@ -283,13 +317,24 @@ export class Sequencer {
|
|
|
283
317
|
// be in for a world of pain.
|
|
284
318
|
await this.buildBlockAndAttemptToPublish(validTxs, proposalHeader, historicalHeader);
|
|
285
319
|
} catch (err) {
|
|
286
|
-
if (BlockProofError.isBlockProofError(err)) {
|
|
287
|
-
const txHashes = err.txHashes.filter(h => !h.isZero());
|
|
288
|
-
this.log.warn(`Proving block failed, removing ${txHashes.length} txs from pool`);
|
|
289
|
-
await this.p2pClient.deleteTxs(txHashes);
|
|
290
|
-
}
|
|
291
320
|
this.log.error(`Error assembling block`, (err as any).stack);
|
|
292
321
|
}
|
|
322
|
+
this.setState(SequencerState.IDLE);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
protected async work() {
|
|
326
|
+
try {
|
|
327
|
+
await this.doRealWork();
|
|
328
|
+
} catch (err) {
|
|
329
|
+
if (err instanceof SequencerTooSlowError) {
|
|
330
|
+
this.log.warn(err.message);
|
|
331
|
+
} else {
|
|
332
|
+
// Re-throw other errors
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
} finally {
|
|
336
|
+
this.setState(SequencerState.IDLE);
|
|
337
|
+
}
|
|
293
338
|
}
|
|
294
339
|
|
|
295
340
|
/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
|
|
@@ -312,7 +357,7 @@ export class Sequencer {
|
|
|
312
357
|
throw new Error(msg);
|
|
313
358
|
}
|
|
314
359
|
|
|
315
|
-
this.log.
|
|
360
|
+
this.log.verbose(`Can propose block ${proposalBlockNumber} at slot ${slot}`);
|
|
316
361
|
return slot;
|
|
317
362
|
} catch (err) {
|
|
318
363
|
const msg = prettyLogViemErrorMsg(err);
|
|
@@ -323,6 +368,44 @@ export class Sequencer {
|
|
|
323
368
|
}
|
|
324
369
|
}
|
|
325
370
|
|
|
371
|
+
doIHaveEnoughTimeLeft(proposedState: SequencerState, secondsIntoSlot: number): boolean {
|
|
372
|
+
if (!this.enforceTimeTable) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (this.timeTable[proposedState] === this.aztecSlotDuration) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const bufferSeconds = this.timeTable[proposedState] - secondsIntoSlot;
|
|
381
|
+
this.metrics.recordStateTransitionBufferMs(bufferSeconds * 1000, proposedState);
|
|
382
|
+
|
|
383
|
+
if (bufferSeconds < 0) {
|
|
384
|
+
this.log.warn(
|
|
385
|
+
`Too far into slot to transition to ${proposedState}. max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
|
|
386
|
+
);
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
this.log.debug(
|
|
390
|
+
`Enough time to transition to ${proposedState}, max allowed: ${this.timeTable[proposedState]}s, time into slot: ${secondsIntoSlot}s`,
|
|
391
|
+
);
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
setState(proposedState: SequencerState, force: boolean = false) {
|
|
396
|
+
if (this.state === SequencerState.STOPPED && force !== true) {
|
|
397
|
+
this.log.warn(
|
|
398
|
+
`Cannot set sequencer from ${this.state} to ${proposedState} as it is stopped. Set force=true to override.`,
|
|
399
|
+
);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration);
|
|
403
|
+
if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) {
|
|
404
|
+
throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot);
|
|
405
|
+
}
|
|
406
|
+
this.state = proposedState;
|
|
407
|
+
}
|
|
408
|
+
|
|
326
409
|
shouldProposeBlock(historicalHeader: Header | undefined, args: ShouldProposeArgs): boolean {
|
|
327
410
|
if (this.isFlushing) {
|
|
328
411
|
this.log.verbose(`Flushing all pending txs in new block`);
|
|
@@ -413,7 +496,7 @@ export class Sequencer {
|
|
|
413
496
|
|
|
414
497
|
this.metrics.recordNewBlock(newGlobalVariables.blockNumber.toNumber(), validTxs.length);
|
|
415
498
|
const workTimer = new Timer();
|
|
416
|
-
this.
|
|
499
|
+
this.setState(SequencerState.CREATING_BLOCK);
|
|
417
500
|
this.log.info(
|
|
418
501
|
`Building blockNumber=${newGlobalVariables.blockNumber.toNumber()} txCount=${
|
|
419
502
|
validTxs.length
|
|
@@ -430,16 +513,23 @@ export class Sequencer {
|
|
|
430
513
|
const numRealTxs = validTxs.length;
|
|
431
514
|
const blockSize = Math.max(2, numRealTxs);
|
|
432
515
|
|
|
433
|
-
|
|
516
|
+
// NB: separating the dbs because both should update the state
|
|
517
|
+
const publicProcessorFork = await this.worldState.fork();
|
|
518
|
+
const orchestratorFork = await this.worldState.fork();
|
|
434
519
|
try {
|
|
435
520
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
436
|
-
const processor = this.publicProcessorFactory.create(
|
|
521
|
+
const processor = this.publicProcessorFactory.create(publicProcessorFork, historicalHeader, newGlobalVariables);
|
|
437
522
|
const blockBuildingTimer = new Timer();
|
|
438
|
-
const blockBuilder = this.blockBuilderFactory.create(
|
|
523
|
+
const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
|
|
439
524
|
await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
|
|
440
525
|
|
|
441
526
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
442
|
-
processor.process(
|
|
527
|
+
processor.process(
|
|
528
|
+
validTxs,
|
|
529
|
+
blockSize,
|
|
530
|
+
blockBuilder,
|
|
531
|
+
this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork),
|
|
532
|
+
),
|
|
443
533
|
);
|
|
444
534
|
if (failedTxs.length > 0) {
|
|
445
535
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
@@ -510,7 +600,8 @@ export class Sequencer {
|
|
|
510
600
|
throw err;
|
|
511
601
|
}
|
|
512
602
|
} finally {
|
|
513
|
-
await
|
|
603
|
+
await publicProcessorFork.close();
|
|
604
|
+
await orchestratorFork.close();
|
|
514
605
|
}
|
|
515
606
|
}
|
|
516
607
|
|
|
@@ -545,11 +636,11 @@ export class Sequencer {
|
|
|
545
636
|
this.log.verbose('Creating block proposal');
|
|
546
637
|
const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
|
|
547
638
|
|
|
548
|
-
this.
|
|
639
|
+
this.setState(SequencerState.PUBLISHING_BLOCK_TO_PEERS);
|
|
549
640
|
this.log.verbose('Broadcasting block proposal to validators');
|
|
550
641
|
this.validatorClient.broadcastBlockProposal(proposal);
|
|
551
642
|
|
|
552
|
-
this.
|
|
643
|
+
this.setState(SequencerState.WAITING_FOR_ATTESTATIONS);
|
|
553
644
|
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations);
|
|
554
645
|
this.log.verbose(`Collected attestations from validators, number of attestations: ${attestations.length}`);
|
|
555
646
|
|
|
@@ -607,7 +698,7 @@ export class Sequencer {
|
|
|
607
698
|
proofQuote?: EpochProofQuote,
|
|
608
699
|
) {
|
|
609
700
|
// Publishes new block to the network and awaits the tx to be mined
|
|
610
|
-
this.
|
|
701
|
+
this.setState(SequencerState.PUBLISHING_BLOCK);
|
|
611
702
|
|
|
612
703
|
const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote);
|
|
613
704
|
if (!publishedL2Block) {
|
|
@@ -693,64 +784,3 @@ export class Sequencer {
|
|
|
693
784
|
/**
|
|
694
785
|
* State of the sequencer.
|
|
695
786
|
*/
|
|
696
|
-
export enum SequencerState {
|
|
697
|
-
/**
|
|
698
|
-
* Will move to WAITING_FOR_TXS after a configured amount of time.
|
|
699
|
-
*/
|
|
700
|
-
IDLE,
|
|
701
|
-
/**
|
|
702
|
-
* Polling the P2P module for txs to include in a block. Will move to CREATING_BLOCK if there are valid txs to include, or back to IDLE otherwise.
|
|
703
|
-
*/
|
|
704
|
-
WAITING_FOR_TXS,
|
|
705
|
-
/**
|
|
706
|
-
* Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
|
|
707
|
-
*/
|
|
708
|
-
CREATING_BLOCK,
|
|
709
|
-
/**
|
|
710
|
-
* Publishing blocks to validator peers. Will move to WAITING_FOR_ATTESTATIONS.
|
|
711
|
-
*/
|
|
712
|
-
PUBLISHING_BLOCK_TO_PEERS,
|
|
713
|
-
/**
|
|
714
|
-
* The block has been published to peers, and we are waiting for attestations. Will move to PUBLISHING_CONTRACT_DATA.
|
|
715
|
-
*/
|
|
716
|
-
WAITING_FOR_ATTESTATIONS,
|
|
717
|
-
/**
|
|
718
|
-
* Sending the tx to L1 with encrypted logs and awaiting it to be mined. Will move back to PUBLISHING_BLOCK once finished.
|
|
719
|
-
*/
|
|
720
|
-
PUBLISHING_CONTRACT_DATA,
|
|
721
|
-
/**
|
|
722
|
-
* Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to IDLE.
|
|
723
|
-
*/
|
|
724
|
-
PUBLISHING_BLOCK,
|
|
725
|
-
/**
|
|
726
|
-
* Sequencer is stopped and not processing any txs from the pool.
|
|
727
|
-
*/
|
|
728
|
-
STOPPED,
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
/** Order Attestations
|
|
732
|
-
*
|
|
733
|
-
* Returns attestation signatures in the order of a series of provided ethereum addresses
|
|
734
|
-
* The rollup smart contract expects attestations to appear in the order of the committee
|
|
735
|
-
*
|
|
736
|
-
* @todo: perform this logic within the memory attestation store instead?
|
|
737
|
-
*/
|
|
738
|
-
function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Signature[] {
|
|
739
|
-
// Create a map of sender addresses to BlockAttestations
|
|
740
|
-
const attestationMap = new Map<string, BlockAttestation>();
|
|
741
|
-
|
|
742
|
-
for (const attestation of attestations) {
|
|
743
|
-
const sender = attestation.getSender();
|
|
744
|
-
if (sender) {
|
|
745
|
-
attestationMap.set(sender.toString(), attestation);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Create the ordered array based on the orderAddresses, else return an empty signature
|
|
750
|
-
const orderedAttestations = orderAddresses.map(address => {
|
|
751
|
-
const addressString = address.toString();
|
|
752
|
-
return attestationMap.get(addressString)?.signature || Signature.empty();
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
return orderedAttestations;
|
|
756
|
-
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { BlockAttestation, EthAddress } from '@aztec/circuit-types';
|
|
2
|
+
import { Signature } from '@aztec/foundation/eth-signature';
|
|
3
|
+
|
|
4
|
+
export enum SequencerState {
|
|
5
|
+
/**
|
|
6
|
+
* Sequencer is stopped and not processing any txs from the pool.
|
|
7
|
+
*/
|
|
8
|
+
STOPPED = 'STOPPED',
|
|
9
|
+
/**
|
|
10
|
+
* Sequencer is awaiting the next call to work().
|
|
11
|
+
*/
|
|
12
|
+
IDLE = 'IDLE',
|
|
13
|
+
/**
|
|
14
|
+
* Synchronizing with the L2 chain.
|
|
15
|
+
*/
|
|
16
|
+
SYNCHRONIZING = 'SYNCHRONIZING',
|
|
17
|
+
/**
|
|
18
|
+
* Checking if we are the proposer for the current slot.
|
|
19
|
+
*/
|
|
20
|
+
PROPOSER_CHECK = 'PROPOSER_CHECK',
|
|
21
|
+
/**
|
|
22
|
+
* Polling the P2P module for txs to include in a block. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise.
|
|
23
|
+
*/
|
|
24
|
+
WAITING_FOR_TXS = 'WAITING_FOR_TXS',
|
|
25
|
+
/**
|
|
26
|
+
* Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
|
|
27
|
+
*/
|
|
28
|
+
CREATING_BLOCK = 'CREATING_BLOCK',
|
|
29
|
+
/**
|
|
30
|
+
* Publishing blocks to validator peers. Will move to WAITING_FOR_ATTESTATIONS.
|
|
31
|
+
*/
|
|
32
|
+
PUBLISHING_BLOCK_TO_PEERS = 'PUBLISHING_BLOCK_TO_PEERS',
|
|
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',
|
|
37
|
+
/**
|
|
38
|
+
* Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to SYNCHRONIZING.
|
|
39
|
+
*/
|
|
40
|
+
PUBLISHING_BLOCK = 'PUBLISHING_BLOCK',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type SequencerStateCallback = () => SequencerState;
|
|
44
|
+
|
|
45
|
+
export function sequencerStateToNumber(state: SequencerState): number {
|
|
46
|
+
return Object.values(SequencerState).indexOf(state);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Order Attestations
|
|
50
|
+
*
|
|
51
|
+
* Returns attestation signatures in the order of a series of provided ethereum addresses
|
|
52
|
+
* The rollup smart contract expects attestations to appear in the order of the committee
|
|
53
|
+
*
|
|
54
|
+
* @todo: perform this logic within the memory attestation store instead?
|
|
55
|
+
*/
|
|
56
|
+
export function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Signature[] {
|
|
57
|
+
// Create a map of sender addresses to BlockAttestations
|
|
58
|
+
const attestationMap = new Map<string, BlockAttestation>();
|
|
59
|
+
|
|
60
|
+
for (const attestation of attestations) {
|
|
61
|
+
const sender = attestation.getSender();
|
|
62
|
+
if (sender) {
|
|
63
|
+
attestationMap.set(sender.toString(), attestation);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Create the ordered array based on the orderAddresses, else return an empty signature
|
|
68
|
+
const orderedAttestations = orderAddresses.map(address => {
|
|
69
|
+
const addressString = address.toString();
|
|
70
|
+
return attestationMap.get(addressString)?.signature || Signature.empty();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return orderedAttestations;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getSecondsIntoSlot(l1GenesisTime: number, aztecSlotDuration: number): number {
|
|
77
|
+
return (Date.now() / 1000 - l1GenesisTime) % aztecSlotDuration;
|
|
78
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
|
|
3
3
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
4
|
+
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
5
5
|
|
|
6
6
|
/** Provides a view into public contract state */
|
|
7
7
|
export interface PublicStateSource {
|
|
@@ -58,7 +58,7 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
61
|
-
const setupFns =
|
|
61
|
+
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
62
62
|
const claimFunctionCall = setupFns.find(
|
|
63
63
|
fn =>
|
|
64
64
|
fn.callContext.contractAddress.equals(this.#feeJuiceAddress) &&
|
|
@@ -66,7 +66,7 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
66
66
|
fn.args.length > 2 &&
|
|
67
67
|
// Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
68
68
|
fn.args[0].equals(FunctionSelector.fromSignature('_increase_public_balance((Field),Field)').toField()) &&
|
|
69
|
-
fn.args[1].equals(feePayer) &&
|
|
69
|
+
fn.args[1].equals(feePayer.toField()) &&
|
|
70
70
|
!fn.callContext.isStaticCall,
|
|
71
71
|
);
|
|
72
72
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AllowedElement,
|
|
3
3
|
type PublicExecutionRequest,
|
|
4
|
-
PublicKernelPhase,
|
|
5
4
|
Tx,
|
|
5
|
+
TxExecutionPhase,
|
|
6
6
|
type TxValidator,
|
|
7
7
|
} from '@aztec/circuit-types';
|
|
8
8
|
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
9
9
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
10
|
-
import { ContractsDataSourcePublicDB,
|
|
10
|
+
import { ContractsDataSourcePublicDB, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
11
11
|
|
|
12
12
|
export class PhasesTxValidator implements TxValidator<Tx> {
|
|
13
13
|
#log = createDebugLogger('aztec:sequencer:tx_validator:tx_phases');
|
|
@@ -45,7 +45,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
45
45
|
return true;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const setupFns =
|
|
48
|
+
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
49
49
|
for (const setupFn of setupFns) {
|
|
50
50
|
if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
|
|
51
51
|
this.#log.warn(
|
|
@@ -7,7 +7,7 @@ export function patchNonRevertibleFn(
|
|
|
7
7
|
index: number,
|
|
8
8
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
9
9
|
): { address: AztecAddress; selector: FunctionSelector } {
|
|
10
|
-
return patchFn('
|
|
10
|
+
return patchFn('nonRevertibleAccumulatedData', tx, index, overrides);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function patchRevertibleFn(
|
|
@@ -15,11 +15,11 @@ export function patchRevertibleFn(
|
|
|
15
15
|
index: number,
|
|
16
16
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
17
17
|
): { address: AztecAddress; selector: FunctionSelector } {
|
|
18
|
-
return patchFn('
|
|
18
|
+
return patchFn('revertibleAccumulatedData', tx, index, overrides);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function patchFn(
|
|
22
|
-
where: '
|
|
22
|
+
where: 'revertibleAccumulatedData' | 'nonRevertibleAccumulatedData',
|
|
23
23
|
tx: Tx,
|
|
24
24
|
index: number,
|
|
25
25
|
overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress },
|
|
@@ -31,11 +31,13 @@ function patchFn(
|
|
|
31
31
|
fn.callContext.msgSender = overrides.msgSender ?? fn.callContext.msgSender;
|
|
32
32
|
tx.enqueuedPublicFunctionCalls[index] = fn;
|
|
33
33
|
|
|
34
|
-
const request = tx.data.forPublic![where].
|
|
35
|
-
request.
|
|
36
|
-
request.
|
|
34
|
+
const request = tx.data.forPublic![where].publicCallRequests[index];
|
|
35
|
+
request.contractAddress = fn.callContext.contractAddress;
|
|
36
|
+
request.msgSender = fn.callContext.msgSender;
|
|
37
|
+
request.functionSelector = fn.callContext.functionSelector;
|
|
38
|
+
request.isStaticCall = fn.callContext.isStaticCall;
|
|
37
39
|
request.argsHash = computeVarArgsHash(fn.args);
|
|
38
|
-
tx.data.forPublic![where].
|
|
40
|
+
tx.data.forPublic![where].publicCallRequests[index] = request;
|
|
39
41
|
|
|
40
42
|
return {
|
|
41
43
|
address: fn.callContext.contractAddress,
|