@aztec/sequencer-client 0.69.0 → 0.69.1
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 +2 -0
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +3 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -1
- package/dest/index.d.ts +3 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +4 -2
- package/dest/publisher/config.d.ts +4 -0
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +6 -1
- package/dest/publisher/l1-publisher.d.ts +36 -11
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +154 -88
- package/dest/sequencer/index.d.ts +1 -0
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +2 -1
- package/dest/sequencer/sequencer.d.ts +9 -16
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +61 -130
- package/dest/sequencer/utils.d.ts +2 -2
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +3 -3
- package/dest/slasher/factory.d.ts +11 -0
- package/dest/slasher/factory.d.ts.map +1 -0
- package/dest/slasher/factory.js +10 -0
- package/dest/slasher/index.d.ts +3 -0
- package/dest/slasher/index.d.ts.map +1 -0
- package/dest/slasher/index.js +3 -0
- package/dest/slasher/slasher_client.d.ts +127 -0
- package/dest/slasher/slasher_client.d.ts.map +1 -0
- package/dest/slasher/slasher_client.js +305 -0
- package/dest/tx_validator/gas_validator.d.ts +2 -3
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +9 -22
- package/dest/tx_validator/nullifier_cache.d.ts +16 -0
- package/dest/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/tx_validator/nullifier_cache.js +24 -0
- package/dest/tx_validator/phases_validator.d.ts +2 -3
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +15 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +15 -14
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +38 -24
- package/package.json +21 -19
- package/src/client/sequencer-client.ts +5 -2
- package/src/config.ts +10 -0
- package/src/index.ts +3 -1
- package/src/publisher/config.ts +10 -0
- package/src/publisher/l1-publisher.ts +180 -97
- package/src/sequencer/index.ts +1 -0
- package/src/sequencer/sequencer.ts +70 -180
- package/src/sequencer/utils.ts +2 -2
- package/src/slasher/factory.ts +22 -0
- package/src/slasher/index.ts +2 -0
- package/src/slasher/slasher_client.ts +402 -0
- package/src/tx_validator/gas_validator.ts +11 -24
- package/src/tx_validator/nullifier_cache.ts +29 -0
- package/src/tx_validator/phases_validator.ts +22 -33
- package/src/tx_validator/tx_validator_factory.ts +82 -40
|
@@ -4,11 +4,9 @@ import {
|
|
|
4
4
|
type L1ToL2MessageSource,
|
|
5
5
|
type L2Block,
|
|
6
6
|
type L2BlockSource,
|
|
7
|
-
type ProcessedTx,
|
|
8
7
|
SequencerConfigSchema,
|
|
9
8
|
Tx,
|
|
10
9
|
type TxHash,
|
|
11
|
-
type TxValidator,
|
|
12
10
|
type WorldStateSynchronizer,
|
|
13
11
|
} from '@aztec/circuit-types';
|
|
14
12
|
import type { AllowedElement, Signature, WorldStateSynchronizerStatus } from '@aztec/circuit-types/interfaces';
|
|
@@ -17,7 +15,9 @@ import {
|
|
|
17
15
|
AppendOnlyTreeSnapshot,
|
|
18
16
|
BlockHeader,
|
|
19
17
|
ContentCommitment,
|
|
18
|
+
type ContractDataSource,
|
|
20
19
|
GENESIS_ARCHIVE_ROOT,
|
|
20
|
+
Gas,
|
|
21
21
|
type GlobalVariables,
|
|
22
22
|
StateReference,
|
|
23
23
|
} from '@aztec/circuits.js';
|
|
@@ -36,9 +36,10 @@ import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec
|
|
|
36
36
|
import { type ValidatorClient } from '@aztec/validator-client';
|
|
37
37
|
|
|
38
38
|
import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
39
|
-
import { type L1Publisher } from '../publisher/l1-publisher.js';
|
|
39
|
+
import { type L1Publisher, VoteType } from '../publisher/l1-publisher.js';
|
|
40
40
|
import { prettyLogViemErrorMsg } from '../publisher/utils.js';
|
|
41
|
-
import { type
|
|
41
|
+
import { type SlasherClient } from '../slasher/slasher_client.js';
|
|
42
|
+
import { createValidatorsForBlockBuilding } from '../tx_validator/tx_validator_factory.js';
|
|
42
43
|
import { getDefaultAllowedSetupFunctions } from './allowed.js';
|
|
43
44
|
import { type SequencerConfig } from './config.js';
|
|
44
45
|
import { SequencerMetrics } from './metrics.js';
|
|
@@ -46,12 +47,6 @@ import { SequencerState, orderAttestations } from './utils.js';
|
|
|
46
47
|
|
|
47
48
|
export { SequencerState };
|
|
48
49
|
|
|
49
|
-
export type ShouldProposeArgs = {
|
|
50
|
-
pendingTxsCount?: number;
|
|
51
|
-
validTxsCount?: number;
|
|
52
|
-
processedTxsCount?: number;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
50
|
export class SequencerTooSlowError extends Error {
|
|
56
51
|
constructor(
|
|
57
52
|
public readonly currentState: SequencerState,
|
|
@@ -89,6 +84,7 @@ export class Sequencer {
|
|
|
89
84
|
private state = SequencerState.STOPPED;
|
|
90
85
|
private allowedInSetup: AllowedElement[] = getDefaultAllowedSetupFunctions();
|
|
91
86
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
87
|
+
private maxBlockGas: Gas = new Gas(10e9, 10e9);
|
|
92
88
|
private processTxTime: number = 12;
|
|
93
89
|
private metrics: SequencerMetrics;
|
|
94
90
|
private isFlushing: boolean = false;
|
|
@@ -106,11 +102,12 @@ export class Sequencer {
|
|
|
106
102
|
private globalsBuilder: GlobalVariableBuilder,
|
|
107
103
|
private p2pClient: P2P,
|
|
108
104
|
private worldState: WorldStateSynchronizer,
|
|
105
|
+
private slasherClient: SlasherClient,
|
|
109
106
|
private blockBuilderFactory: BlockBuilderFactory,
|
|
110
107
|
private l2BlockSource: L2BlockSource,
|
|
111
108
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
112
109
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
113
|
-
private
|
|
110
|
+
private contractDataSource: ContractDataSource,
|
|
114
111
|
protected l1Constants: SequencerRollupConstants,
|
|
115
112
|
private dateProvider: DateProvider,
|
|
116
113
|
telemetry: TelemetryClient,
|
|
@@ -122,6 +119,9 @@ export class Sequencer {
|
|
|
122
119
|
|
|
123
120
|
// Register the block builder with the validator client for re-execution
|
|
124
121
|
this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
|
|
122
|
+
|
|
123
|
+
// Register the slasher on the publisher to fetch slashing payloads
|
|
124
|
+
this.publisher.registerSlashPayloadGetter(this.slasherClient.getSlashPayload.bind(this.slasherClient));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
get tracer(): Tracer {
|
|
@@ -144,6 +144,12 @@ export class Sequencer {
|
|
|
144
144
|
if (config.minTxsPerBlock !== undefined) {
|
|
145
145
|
this.minTxsPerBLock = config.minTxsPerBlock;
|
|
146
146
|
}
|
|
147
|
+
if (config.maxDABlockGas !== undefined) {
|
|
148
|
+
this.maxBlockGas = new Gas(config.maxDABlockGas, this.maxBlockGas.l2Gas);
|
|
149
|
+
}
|
|
150
|
+
if (config.maxL2BlockGas !== undefined) {
|
|
151
|
+
this.maxBlockGas = new Gas(this.maxBlockGas.daGas, config.maxL2BlockGas);
|
|
152
|
+
}
|
|
147
153
|
if (config.coinbase) {
|
|
148
154
|
this._coinbase = config.coinbase;
|
|
149
155
|
}
|
|
@@ -157,7 +163,7 @@ export class Sequencer {
|
|
|
157
163
|
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
|
|
158
164
|
}
|
|
159
165
|
if (config.governanceProposerPayload) {
|
|
160
|
-
this.publisher.
|
|
166
|
+
this.publisher.setGovernancePayload(config.governanceProposerPayload);
|
|
161
167
|
}
|
|
162
168
|
if (config.maxL1TxInclusionTimeIntoSlot !== undefined) {
|
|
163
169
|
this.maxL1TxInclusionTimeIntoSlot = config.maxL1TxInclusionTimeIntoSlot;
|
|
@@ -174,7 +180,7 @@ export class Sequencer {
|
|
|
174
180
|
// How late into the slot can we be to start working
|
|
175
181
|
const initialTime = 2;
|
|
176
182
|
|
|
177
|
-
// How long it takes to
|
|
183
|
+
// How long it takes to get ready to start building
|
|
178
184
|
const blockPrepareTime = 1;
|
|
179
185
|
|
|
180
186
|
// How long it takes to for attestations to travel across the p2p layer.
|
|
@@ -213,9 +219,9 @@ export class Sequencer {
|
|
|
213
219
|
[SequencerState.SYNCHRONIZING]: this.aztecSlotDuration,
|
|
214
220
|
// We always want to allow the full slot to check if we are the proposer
|
|
215
221
|
[SequencerState.PROPOSER_CHECK]: this.aztecSlotDuration,
|
|
216
|
-
//
|
|
217
|
-
[SequencerState.
|
|
218
|
-
//
|
|
222
|
+
// How late we can start initializing a new block proposal
|
|
223
|
+
[SequencerState.INITIALIZING_PROPOSAL]: initialTime,
|
|
224
|
+
// When we start building a block
|
|
219
225
|
[SequencerState.CREATING_BLOCK]: initialTime + blockPrepareTime,
|
|
220
226
|
// We start collecting attestations after building the block
|
|
221
227
|
[SequencerState.COLLECTING_ATTESTATIONS]: initialTime + blockPrepareTime + processTxsTime + blockValidationTime,
|
|
@@ -245,6 +251,7 @@ export class Sequencer {
|
|
|
245
251
|
this.log.debug(`Stopping sequencer`);
|
|
246
252
|
await this.validatorClient?.stop();
|
|
247
253
|
await this.runningPromise?.stop();
|
|
254
|
+
await this.slasherClient?.stop();
|
|
248
255
|
this.publisher.interrupt();
|
|
249
256
|
this.setState(SequencerState.STOPPED, 0n, true /** force */);
|
|
250
257
|
this.log.info('Stopped sequencer');
|
|
@@ -314,27 +321,30 @@ export class Sequencer {
|
|
|
314
321
|
slot,
|
|
315
322
|
);
|
|
316
323
|
|
|
317
|
-
void this.publisher.castVote(slot, newGlobalVariables.timestamp.toBigInt());
|
|
324
|
+
void this.publisher.castVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.GOVERNANCE);
|
|
325
|
+
void this.publisher.castVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.SLASHING);
|
|
318
326
|
|
|
319
|
-
|
|
327
|
+
// Check the pool has enough txs to build a block
|
|
328
|
+
const pendingTxCount = this.p2pClient.getPendingTxCount();
|
|
329
|
+
if (pendingTxCount < this.minTxsPerBLock && !this.isFlushing) {
|
|
330
|
+
this.log.verbose(`Not enough txs to propose block. Got ${pendingTxCount} min ${this.minTxsPerBLock}.`, {
|
|
331
|
+
slot,
|
|
332
|
+
blockNumber: newBlockNumber,
|
|
333
|
+
});
|
|
334
|
+
await this.claimEpochProofRightIfAvailable(slot);
|
|
320
335
|
return;
|
|
321
336
|
}
|
|
322
337
|
|
|
338
|
+
this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
|
|
323
339
|
this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
|
|
324
340
|
chainTipArchive: new Fr(chainTipArchive),
|
|
325
341
|
blockNumber: newBlockNumber,
|
|
326
342
|
slot,
|
|
327
343
|
});
|
|
328
344
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const pendingTxs = await this.p2pClient.getPendingTxs();
|
|
333
|
-
|
|
334
|
-
if (!this.shouldProposeBlock(historicalHeader, { pendingTxsCount: pendingTxs.length })) {
|
|
335
|
-
await this.claimEpochProofRightIfAvailable(slot);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
345
|
+
// We don't fetch exactly maxTxsPerBlock txs here because we may not need all of them if we hit a limit before,
|
|
346
|
+
// and also we may need to fetch more if we don't have enough valid txs.
|
|
347
|
+
const pendingTxs = this.p2pClient.iteratePendingTxs();
|
|
338
348
|
|
|
339
349
|
// If I created a "partial" header here that should make our job much easier.
|
|
340
350
|
const proposalHeader = new BlockHeader(
|
|
@@ -346,35 +356,12 @@ export class Sequencer {
|
|
|
346
356
|
Fr.ZERO,
|
|
347
357
|
);
|
|
348
358
|
|
|
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.
|
|
351
|
-
const allValidTxs = await this.takeValidTxs(
|
|
352
|
-
pendingTxs,
|
|
353
|
-
this.txValidatorFactory.validatorForNewTxs(newGlobalVariables, this.allowedInSetup),
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
// TODO: We are taking the size of the tx from private-land, but we should be doing this after running
|
|
357
|
-
// public functions. Only reason why we do it here now is because the public processor and orchestrator
|
|
358
|
-
// are set up such that they require knowing the total number of txs in advance. Still, main reason for
|
|
359
|
-
// exceeding max block size in bytes is contract class registration, which happens in private-land. This
|
|
360
|
-
// may break if we start emitting lots of log data from public-land.
|
|
361
|
-
const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
|
|
362
|
-
|
|
363
|
-
this.log.verbose(
|
|
364
|
-
`Collected ${validTxs.length} txs out of ${allValidTxs.length} valid txs out of ${pendingTxs.length} total pending txs for block ${newBlockNumber}`,
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
// Bail if we don't have enough valid txs
|
|
368
|
-
if (!this.shouldProposeBlock(historicalHeader, { validTxsCount: validTxs.length })) {
|
|
369
|
-
await this.claimEpochProofRightIfAvailable(slot);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
359
|
try {
|
|
360
|
+
// TODO(palla/txs) Is the note below still valid? We don't seem to be doing any rollback in there.
|
|
374
361
|
// @note It is very important that the following function will FAIL and not just return early
|
|
375
362
|
// if it have made any state changes. If not, we won't rollback the state, and you will
|
|
376
363
|
// be in for a world of pain.
|
|
377
|
-
await this.buildBlockAndAttemptToPublish(
|
|
364
|
+
await this.buildBlockAndAttemptToPublish(pendingTxs, proposalHeader, historicalHeader);
|
|
378
365
|
} catch (err) {
|
|
379
366
|
this.log.error(`Error assembling block`, err, { blockNumber: newBlockNumber, slot });
|
|
380
367
|
}
|
|
@@ -462,64 +449,20 @@ export class Sequencer {
|
|
|
462
449
|
this.state = proposedState;
|
|
463
450
|
}
|
|
464
451
|
|
|
465
|
-
shouldProposeBlock(historicalHeader: BlockHeader | undefined, args: ShouldProposeArgs): boolean {
|
|
466
|
-
if (this.isFlushing) {
|
|
467
|
-
this.log.verbose(`Flushing all pending txs in new block`);
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Compute time elapsed since the previous block
|
|
472
|
-
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
|
|
473
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
474
|
-
const elapsedSinceLastBlock = currentTime - lastBlockTime;
|
|
475
|
-
this.log.debug(
|
|
476
|
-
`Last block mined at ${lastBlockTime} current time is ${currentTime} (elapsed ${elapsedSinceLastBlock})`,
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
// We need to have at least minTxsPerBLock txs.
|
|
480
|
-
if (args.pendingTxsCount !== undefined && args.pendingTxsCount < this.minTxsPerBLock) {
|
|
481
|
-
this.log.verbose(
|
|
482
|
-
`Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
|
|
483
|
-
);
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Bail if we don't have enough valid txs
|
|
488
|
-
if (args.validTxsCount !== undefined && args.validTxsCount < this.minTxsPerBLock) {
|
|
489
|
-
this.log.verbose(
|
|
490
|
-
`Not creating block because not enough valid txs loaded from the pool (got ${args.validTxsCount} min ${this.minTxsPerBLock})`,
|
|
491
|
-
);
|
|
492
|
-
return false;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
|
|
496
|
-
// less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
|
|
497
|
-
// go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
|
|
498
|
-
// we should bail.
|
|
499
|
-
if (args.processedTxsCount === 0 && this.minTxsPerBLock > 0) {
|
|
500
|
-
this.log.verbose('No txs processed correctly to build block.');
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return true;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
452
|
/**
|
|
508
453
|
* Build a block
|
|
509
454
|
*
|
|
510
455
|
* Shared between the sequencer and the validator for re-execution
|
|
511
456
|
*
|
|
512
|
-
* @param
|
|
457
|
+
* @param pendingTxs - The pending transactions to construct the block from
|
|
513
458
|
* @param newGlobalVariables - The global variables for the new block
|
|
514
459
|
* @param historicalHeader - The historical header of the parent
|
|
515
|
-
* @param interrupt - The interrupt callback, used to validate the block for submission and check if we should propose the block
|
|
516
460
|
* @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
|
|
517
461
|
*/
|
|
518
462
|
private async buildBlock(
|
|
519
|
-
|
|
463
|
+
pendingTxs: Iterable<Tx>,
|
|
520
464
|
newGlobalVariables: GlobalVariables,
|
|
521
465
|
historicalHeader?: BlockHeader,
|
|
522
|
-
interrupt?: (processedTxs: ProcessedTx[]) => Promise<void>,
|
|
523
466
|
opts: { validateOnly?: boolean } = {},
|
|
524
467
|
) {
|
|
525
468
|
const blockNumber = newGlobalVariables.blockNumber.toBigInt();
|
|
@@ -527,19 +470,9 @@ export class Sequencer {
|
|
|
527
470
|
|
|
528
471
|
this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
|
|
529
472
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
473
|
+
const msgCount = l1ToL2Messages.length;
|
|
530
474
|
|
|
531
|
-
this.log.verbose(
|
|
532
|
-
`Building block ${blockNumber} with ${validTxs.length} txs and ${l1ToL2Messages.length} messages`,
|
|
533
|
-
{
|
|
534
|
-
msgCount: l1ToL2Messages.length,
|
|
535
|
-
txCount: validTxs.length,
|
|
536
|
-
slot,
|
|
537
|
-
blockNumber,
|
|
538
|
-
},
|
|
539
|
-
);
|
|
540
|
-
|
|
541
|
-
const numRealTxs = validTxs.length;
|
|
542
|
-
const blockSize = Math.max(2, numRealTxs);
|
|
475
|
+
this.log.verbose(`Building block ${blockNumber} for slot ${slot}`, { slot, blockNumber, msgCount });
|
|
543
476
|
|
|
544
477
|
// Sync to the previous block at least
|
|
545
478
|
await this.worldState.syncImmediate(newGlobalVariables.blockNumber.toNumber() - 1);
|
|
@@ -563,18 +496,30 @@ export class Sequencer {
|
|
|
563
496
|
// We set the deadline for tx processing to the start of the CREATING_BLOCK phase, plus the expected time for tx processing.
|
|
564
497
|
// Deadline is only set if enforceTimeTable is enabled.
|
|
565
498
|
const processingEndTimeWithinSlot = this.timeTable[SequencerState.CREATING_BLOCK] + this.processTxTime;
|
|
566
|
-
const
|
|
499
|
+
const deadline = this.enforceTimeTable
|
|
567
500
|
? new Date((this.getSlotStartTimestamp(slot) + processingEndTimeWithinSlot) * 1000)
|
|
568
501
|
: undefined;
|
|
569
|
-
this.log.verbose(`Processing
|
|
502
|
+
this.log.verbose(`Processing pending txs`, {
|
|
570
503
|
slot,
|
|
571
504
|
slotStart: new Date(this.getSlotStartTimestamp(slot) * 1000),
|
|
572
505
|
now: new Date(this.dateProvider.now()),
|
|
573
|
-
deadline
|
|
506
|
+
deadline,
|
|
574
507
|
});
|
|
575
|
-
|
|
508
|
+
|
|
509
|
+
const validators = createValidatorsForBlockBuilding(
|
|
510
|
+
publicProcessorFork,
|
|
511
|
+
this.contractDataSource,
|
|
512
|
+
newGlobalVariables,
|
|
513
|
+
!!this.config.enforceFees,
|
|
514
|
+
this.allowedInSetup,
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
// REFACTOR: Public processor should just handle processing, one tx at a time. It should be responsibility
|
|
518
|
+
// of the sequencer to update world state and iterate over txs. We should refactor this along with unifying the
|
|
519
|
+
// publicProcessorFork and orchestratorFork, to avoid doing tree insertions twice when building the block.
|
|
520
|
+
const limits = { deadline, maxTransactions: this.maxTxsPerBlock, maxBlockSize: this.maxBlockSizeInBytes };
|
|
576
521
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
577
|
-
processor.process(
|
|
522
|
+
processor.process(pendingTxs, limits, validators),
|
|
578
523
|
);
|
|
579
524
|
|
|
580
525
|
if (failedTxs.length > 0) {
|
|
@@ -602,8 +547,6 @@ export class Sequencer {
|
|
|
602
547
|
const duration = Number(end - start) / 1_000;
|
|
603
548
|
this.metrics.recordBlockBuilderTreeInsertions(duration);
|
|
604
549
|
|
|
605
|
-
await interrupt?.(processedTxs);
|
|
606
|
-
|
|
607
550
|
// All real transactions have been added, set the block as full and pad if needed
|
|
608
551
|
const block = await blockBuilder.setBlockCompleted();
|
|
609
552
|
|
|
@@ -611,7 +554,7 @@ export class Sequencer {
|
|
|
611
554
|
block,
|
|
612
555
|
publicProcessorDuration,
|
|
613
556
|
numMsgs: l1ToL2Messages.length,
|
|
614
|
-
|
|
557
|
+
numTxs: processedTxs.length,
|
|
615
558
|
blockBuildingTimer,
|
|
616
559
|
};
|
|
617
560
|
} finally {
|
|
@@ -635,7 +578,7 @@ export class Sequencer {
|
|
|
635
578
|
* @dev MUST throw instead of exiting early to ensure that world-state
|
|
636
579
|
* is being rolled back if the block is dropped.
|
|
637
580
|
*
|
|
638
|
-
* @param
|
|
581
|
+
* @param pendingTxs - Iterable of pending transactions to construct the block from
|
|
639
582
|
* @param proposalHeader - The partial header constructed for the proposal
|
|
640
583
|
* @param historicalHeader - The historical header of the parent
|
|
641
584
|
*/
|
|
@@ -643,7 +586,7 @@ export class Sequencer {
|
|
|
643
586
|
[Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
|
|
644
587
|
}))
|
|
645
588
|
private async buildBlockAndAttemptToPublish(
|
|
646
|
-
|
|
589
|
+
pendingTxs: Iterable<Tx>,
|
|
647
590
|
proposalHeader: BlockHeader,
|
|
648
591
|
historicalHeader: BlockHeader | undefined,
|
|
649
592
|
): Promise<void> {
|
|
@@ -653,40 +596,19 @@ export class Sequencer {
|
|
|
653
596
|
const blockNumber = newGlobalVariables.blockNumber.toNumber();
|
|
654
597
|
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
655
598
|
|
|
656
|
-
this.metrics.recordNewBlock(blockNumber, validTxs.length);
|
|
599
|
+
// this.metrics.recordNewBlock(blockNumber, validTxs.length);
|
|
657
600
|
const workTimer = new Timer();
|
|
658
601
|
this.setState(SequencerState.CREATING_BLOCK, slot);
|
|
659
602
|
|
|
660
|
-
/**
|
|
661
|
-
* BuildBlock is shared between the sequencer and the validator for re-execution
|
|
662
|
-
* We use the interrupt callback to validate the block for submission and check if we should propose the block
|
|
663
|
-
*
|
|
664
|
-
* If we fail, we throw an error in order to roll back
|
|
665
|
-
*/
|
|
666
|
-
const interrupt = async (processedTxs: ProcessedTx[]) => {
|
|
667
|
-
await this.publisher.validateBlockForSubmission(proposalHeader);
|
|
668
|
-
|
|
669
|
-
if (
|
|
670
|
-
!this.shouldProposeBlock(historicalHeader, {
|
|
671
|
-
validTxsCount: validTxs.length,
|
|
672
|
-
processedTxsCount: processedTxs.length,
|
|
673
|
-
})
|
|
674
|
-
) {
|
|
675
|
-
// TODO: Roll back changes to world state
|
|
676
|
-
throw new Error('Should not propose the block');
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
|
|
680
603
|
// Start collecting proof quotes for the previous epoch if needed in the background
|
|
681
604
|
const proofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
|
|
682
605
|
|
|
683
606
|
try {
|
|
684
|
-
const buildBlockRes = await this.buildBlock(
|
|
685
|
-
const { block, publicProcessorDuration,
|
|
607
|
+
const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables, historicalHeader);
|
|
608
|
+
const { block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
|
|
686
609
|
|
|
687
610
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
688
611
|
// block being published before ours instead of just waiting on our block
|
|
689
|
-
|
|
690
612
|
await this.publisher.validateBlockForSubmission(block.header);
|
|
691
613
|
|
|
692
614
|
const workDuration = workTimer.ms();
|
|
@@ -700,8 +622,8 @@ export class Sequencer {
|
|
|
700
622
|
};
|
|
701
623
|
|
|
702
624
|
const blockHash = block.hash();
|
|
703
|
-
const txHashes =
|
|
704
|
-
this.log.info(`Built block ${block.number}
|
|
625
|
+
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
626
|
+
this.log.info(`Built block ${block.number} for slot ${slot} with ${numTxs} txs`, {
|
|
705
627
|
blockHash,
|
|
706
628
|
globalVariables: block.header.globalVariables.toInspect(),
|
|
707
629
|
txHashes,
|
|
@@ -727,14 +649,12 @@ export class Sequencer {
|
|
|
727
649
|
await this.publishL2Block(block, attestations, txHashes, proofQuote);
|
|
728
650
|
this.metrics.recordPublishedBlock(workDuration);
|
|
729
651
|
this.log.info(
|
|
730
|
-
`Published
|
|
731
|
-
block.number
|
|
732
|
-
} with ${numProcessedTxs} transactions and ${numMsgs} messages in ${Math.ceil(workDuration)}ms`,
|
|
652
|
+
`Published block ${block.number} with ${numTxs} txs and ${numMsgs} messages in ${Math.ceil(workDuration)}ms`,
|
|
733
653
|
{
|
|
734
654
|
blockNumber: block.number,
|
|
735
655
|
blockHash: blockHash,
|
|
736
656
|
slot,
|
|
737
|
-
txCount:
|
|
657
|
+
txCount: txHashes.length,
|
|
738
658
|
msgCount: numMsgs,
|
|
739
659
|
duration: Math.ceil(workDuration),
|
|
740
660
|
submitter: this.publisher.getSenderAddress().toString(),
|
|
@@ -858,36 +778,6 @@ export class Sequencer {
|
|
|
858
778
|
}
|
|
859
779
|
}
|
|
860
780
|
|
|
861
|
-
protected async takeValidTxs<T extends Tx | ProcessedTx>(txs: T[], validator: TxValidator<T>): Promise<T[]> {
|
|
862
|
-
const [valid, invalid] = await validator.validateTxs(txs);
|
|
863
|
-
if (invalid.length > 0) {
|
|
864
|
-
this.log.debug(`Dropping invalid txs from the p2p pool ${Tx.getHashes(invalid).join(', ')}`);
|
|
865
|
-
await this.p2pClient.deleteTxs(Tx.getHashes(invalid));
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
return valid.slice(0, this.maxTxsPerBlock);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
protected takeTxsWithinMaxSize(txs: Tx[]): Tx[] {
|
|
872
|
-
const maxSize = this.maxBlockSizeInBytes;
|
|
873
|
-
let totalSize = 0;
|
|
874
|
-
|
|
875
|
-
const toReturn: Tx[] = [];
|
|
876
|
-
for (const tx of txs) {
|
|
877
|
-
const txSize = tx.getSize() - tx.clientIvcProof.clientIvcProofBuffer.length;
|
|
878
|
-
if (totalSize + txSize > maxSize) {
|
|
879
|
-
this.log.debug(
|
|
880
|
-
`Dropping tx ${tx.getTxHash()} with estimated size ${txSize} due to exceeding ${maxSize} block size limit (currently at ${totalSize})`,
|
|
881
|
-
);
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
toReturn.push(tx);
|
|
885
|
-
totalSize += txSize;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
return toReturn;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
781
|
@trackSpan(
|
|
892
782
|
'Sequencer.claimEpochProofRightIfAvailable',
|
|
893
783
|
slotNumber => ({ [Attributes.SLOT_NUMBER]: Number(slotNumber) }),
|
package/src/sequencer/utils.ts
CHANGED
|
@@ -19,9 +19,9 @@ export enum SequencerState {
|
|
|
19
19
|
*/
|
|
20
20
|
PROPOSER_CHECK = 'PROPOSER_CHECK',
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
INITIALIZING_PROPOSAL = 'INITIALIZING_PROPOSAL',
|
|
25
25
|
/**
|
|
26
26
|
* Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
|
|
27
27
|
*/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { L2BlockSource } from '@aztec/circuit-types';
|
|
2
|
+
import { type L1ContractsConfig, type L1ReaderConfig } from '@aztec/ethereum';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { type AztecKVStore } from '@aztec/kv-store';
|
|
5
|
+
import { type DataStoreConfig } from '@aztec/kv-store/config';
|
|
6
|
+
import { createStore } from '@aztec/kv-store/lmdb';
|
|
7
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
8
|
+
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
9
|
+
|
|
10
|
+
import { SlasherClient } from './slasher_client.js';
|
|
11
|
+
import { type SlasherConfig } from './slasher_client.js';
|
|
12
|
+
|
|
13
|
+
export const createSlasherClient = async (
|
|
14
|
+
_config: SlasherConfig & DataStoreConfig & L1ContractsConfig & L1ReaderConfig,
|
|
15
|
+
l2BlockSource: L2BlockSource,
|
|
16
|
+
telemetry: TelemetryClient = new NoopTelemetryClient(),
|
|
17
|
+
deps: { store?: AztecKVStore } = {},
|
|
18
|
+
) => {
|
|
19
|
+
const config = { ..._config };
|
|
20
|
+
const store = deps.store ?? (await createStore('slasher', config, createLogger('slasher:lmdb')));
|
|
21
|
+
return new SlasherClient(config, store, l2BlockSource, telemetry);
|
|
22
|
+
};
|