@aztec/sequencer-client 0.0.1-commit.1142ef1 → 0.0.1-commit.1bea0213

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.
Files changed (39) hide show
  1. package/dest/config.d.ts +1 -1
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +1 -3
  4. package/dest/global_variable_builder/global_builder.js +2 -2
  5. package/dest/index.d.ts +2 -2
  6. package/dest/index.d.ts.map +1 -1
  7. package/dest/index.js +1 -1
  8. package/dest/sequencer/checkpoint_proposal_job.d.ts +6 -4
  9. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  10. package/dest/sequencer/checkpoint_proposal_job.js +90 -14
  11. package/dest/sequencer/checkpoint_voter.d.ts +3 -2
  12. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  13. package/dest/sequencer/checkpoint_voter.js +34 -10
  14. package/dest/sequencer/index.d.ts +1 -2
  15. package/dest/sequencer/index.d.ts.map +1 -1
  16. package/dest/sequencer/index.js +0 -1
  17. package/dest/sequencer/sequencer.d.ts +17 -9
  18. package/dest/sequencer/sequencer.d.ts.map +1 -1
  19. package/dest/sequencer/sequencer.js +67 -11
  20. package/dest/test/mock_checkpoint_builder.d.ts +17 -13
  21. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  22. package/dest/test/mock_checkpoint_builder.js +28 -8
  23. package/dest/test/utils.d.ts +8 -8
  24. package/dest/test/utils.d.ts.map +1 -1
  25. package/dest/test/utils.js +7 -7
  26. package/package.json +30 -28
  27. package/src/config.ts +1 -3
  28. package/src/global_variable_builder/global_builder.ts +2 -2
  29. package/src/index.ts +1 -6
  30. package/src/sequencer/checkpoint_proposal_job.ts +127 -24
  31. package/src/sequencer/checkpoint_voter.ts +32 -7
  32. package/src/sequencer/index.ts +0 -1
  33. package/src/sequencer/sequencer.ts +81 -9
  34. package/src/test/mock_checkpoint_builder.ts +65 -33
  35. package/src/test/utils.ts +19 -12
  36. package/dest/sequencer/block_builder.d.ts +0 -26
  37. package/dest/sequencer/block_builder.d.ts.map +0 -1
  38. package/dest/sequencer/block_builder.js +0 -129
  39. package/src/sequencer/block_builder.ts +0 -216
@@ -12,7 +12,7 @@ import type { DateProvider } from '@aztec/foundation/timer';
12
12
  import type { TypedEventEmitter } from '@aztec/foundation/types';
13
13
  import type { P2P } from '@aztec/p2p';
14
14
  import type { SlasherClientInterface } from '@aztec/slasher';
15
- import type { L2BlockNew, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
15
+ import type { L2Block, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
16
16
  import type { Checkpoint } from '@aztec/stdlib/checkpoint';
17
17
  import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
18
18
  import {
@@ -57,8 +57,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
57
57
  private state = SequencerState.STOPPED;
58
58
  private metrics: SequencerMetrics;
59
59
 
60
- /** The last slot for which we attempted to vote when sync failed, to prevent duplicate attempts. */
61
- private lastSlotForVoteWhenSyncFailed: SlotNumber | undefined;
60
+ /** The last slot for which we attempted to perform our voting duties with degraded block production */
61
+ private lastSlotForFallbackVote: SlotNumber | undefined;
62
62
 
63
63
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */
64
64
  private lastSlotForCheckpointProposalJob: SlotNumber | undefined;
@@ -202,7 +202,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
202
202
  const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
203
203
 
204
204
  // Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
205
- const checkpointProposalJob = await this.prepareCheckpointProposal(slot, ts, now);
205
+ const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
206
206
  if (!checkpointProposalJob) {
207
207
  return;
208
208
  }
@@ -234,6 +234,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
234
234
  */
235
235
  @trackSpan('Sequencer.prepareCheckpointProposal')
236
236
  private async prepareCheckpointProposal(
237
+ epoch: EpochNumber,
237
238
  slot: SlotNumber,
238
239
  ts: bigint,
239
240
  now: bigint,
@@ -263,6 +264,25 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
263
264
  return undefined;
264
265
  }
265
266
 
267
+ // If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
268
+ // Still perform governance/slashing voting (as proposer) once per slot.
269
+ const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
270
+
271
+ if (isEscapeHatchOpen) {
272
+ this.setState(SequencerState.PROPOSER_CHECK, slot);
273
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
274
+ if (canPropose) {
275
+ await this.tryVoteWhenEscapeHatchOpen({ slot, proposer });
276
+ } else {
277
+ this.log.trace(`Escape hatch open but we are not proposer, skipping vote-only actions`, {
278
+ slot,
279
+ epoch,
280
+ proposer,
281
+ });
282
+ }
283
+ return undefined;
284
+ }
285
+
266
286
  // Next checkpoint follows from the last synced one
267
287
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
268
288
 
@@ -357,6 +377,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
357
377
 
358
378
  // Create and return the checkpoint proposal job
359
379
  return this.createCheckpointProposalJob(
380
+ epoch,
360
381
  slot,
361
382
  checkpointNumber,
362
383
  syncedTo.blockNumber,
@@ -368,6 +389,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
368
389
  }
369
390
 
370
391
  protected createCheckpointProposalJob(
392
+ epoch: EpochNumber,
371
393
  slot: SlotNumber,
372
394
  checkpointNumber: CheckpointNumber,
373
395
  syncedToBlockNumber: BlockNumber,
@@ -377,6 +399,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
377
399
  invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
378
400
  ): CheckpointProposalJob {
379
401
  return new CheckpointProposalJob(
402
+ epoch,
380
403
  slot,
381
404
  checkpointNumber,
382
405
  syncedToBlockNumber,
@@ -389,6 +412,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
389
412
  this.p2pClient,
390
413
  this.worldState,
391
414
  this.l1ToL2MessageSource,
415
+ this.l2BlockSource,
392
416
  this.checkpointsBuilder,
393
417
  this.l2BlockSource,
394
418
  this.l1Constants,
@@ -505,7 +529,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
505
529
  };
506
530
  }
507
531
 
508
- const block = await this.l2BlockSource.getL2BlockNew(blockNumber);
532
+ const block = await this.l2BlockSource.getL2Block(blockNumber);
509
533
  if (!block) {
510
534
  // this shouldn't really happen because a moment ago we checked that all components were in sync
511
535
  this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
@@ -569,7 +593,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
569
593
  const { slot } = args;
570
594
 
571
595
  // Prevent duplicate attempts in the same slot
572
- if (this.lastSlotForVoteWhenSyncFailed === slot) {
596
+ if (this.lastSlotForFallbackVote === slot) {
573
597
  this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
574
598
  return;
575
599
  }
@@ -601,7 +625,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
601
625
  }
602
626
 
603
627
  // Mark this slot as attempted
604
- this.lastSlotForVoteWhenSyncFailed = slot;
628
+ this.lastSlotForFallbackVote = slot;
605
629
 
606
630
  // Get a publisher for voting
607
631
  const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
@@ -636,7 +660,55 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
636
660
  }
637
661
 
638
662
  /**
639
- * Considers invalidating a checkpoint if the pending chain is invalid. Depends on how long the invalid checkpoint
663
+ * Tries to vote on slashing actions and governance proposals when escape hatch is open.
664
+ * This allows the sequencer to participate in voting without performing checkpoint proposal work.
665
+ */
666
+ @trackSpan('Sequencer.tryVoteWhenEscapeHatchOpen', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
667
+ protected async tryVoteWhenEscapeHatchOpen(args: {
668
+ slot: SlotNumber;
669
+ proposer: EthAddress | undefined;
670
+ }): Promise<void> {
671
+ const { slot, proposer } = args;
672
+
673
+ // Prevent duplicate attempts in the same slot
674
+ if (this.lastSlotForFallbackVote === slot) {
675
+ this.log.trace(`Already attempted to vote in slot ${slot} (escape hatch open, skipping)`);
676
+ return;
677
+ }
678
+
679
+ // Mark this slot as attempted
680
+ this.lastSlotForFallbackVote = slot;
681
+
682
+ const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
683
+
684
+ this.log.debug(`Escape hatch open for slot ${slot}, attempting vote-only actions`, { slot, attestorAddress });
685
+
686
+ const voter = new CheckpointVoter(
687
+ slot,
688
+ publisher,
689
+ attestorAddress,
690
+ this.validatorClient,
691
+ this.slasherClient,
692
+ this.l1Constants,
693
+ this.config,
694
+ this.metrics,
695
+ this.log,
696
+ );
697
+
698
+ const votesPromises = voter.enqueueVotes();
699
+ const votes = await Promise.all(votesPromises);
700
+
701
+ if (votes.every(p => !p)) {
702
+ this.log.debug(`No votes to enqueue for slot ${slot} (escape hatch open)`);
703
+ return;
704
+ }
705
+
706
+ this.log.info(`Voting in slot ${slot} (escape hatch open)`, { slot });
707
+ await publisher.sendRequests();
708
+ }
709
+
710
+ /**
711
+ * Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
640
712
  * has been there without being invalidated and whether the sequencer is in the committee or not. We always
641
713
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
642
714
  * and if they fail, any sequencer will try as well.
@@ -798,7 +870,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
798
870
  }
799
871
 
800
872
  type SequencerSyncCheckResult = {
801
- block?: L2BlockNew;
873
+ block?: L2Block;
802
874
  checkpointNumber: CheckpointNumber;
803
875
  blockNumber: BlockNumber;
804
876
  archive: Fr;
@@ -1,32 +1,33 @@
1
1
  import { type BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { Timer } from '@aztec/foundation/timer';
4
- import type { FunctionsOf } from '@aztec/foundation/types';
5
- import { L2BlockNew } from '@aztec/stdlib/block';
4
+ import { L2Block } from '@aztec/stdlib/block';
6
5
  import { Checkpoint } from '@aztec/stdlib/checkpoint';
7
6
  import { Gas } from '@aztec/stdlib/gas';
8
- import type { FullNodeBlockBuilderConfig, PublicProcessorLimits } from '@aztec/stdlib/interfaces/server';
7
+ import type {
8
+ FullNodeBlockBuilderConfig,
9
+ ICheckpointBlockBuilder,
10
+ ICheckpointsBuilder,
11
+ MerkleTreeWriteOperations,
12
+ PublicProcessorLimits,
13
+ } from '@aztec/stdlib/interfaces/server';
9
14
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
10
15
  import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
11
16
  import type { CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
12
- import type {
13
- BuildBlockInCheckpointResult,
14
- CheckpointBuilder,
15
- FullNodeCheckpointsBuilder,
16
- } from '@aztec/validator-client';
17
+ import type { BuildBlockInCheckpointResultWithTimer } from '@aztec/validator-client';
17
18
 
18
19
  /**
19
20
  * A fake CheckpointBuilder for testing that implements the same interface as the real one.
20
21
  * Can be seeded with blocks to return sequentially on each `buildBlock` call.
21
22
  */
22
- export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
23
- private blocks: L2BlockNew[] = [];
24
- private builtBlocks: L2BlockNew[] = [];
23
+ export class MockCheckpointBuilder implements ICheckpointBlockBuilder {
24
+ private blocks: L2Block[] = [];
25
+ private builtBlocks: L2Block[] = [];
25
26
  private usedTxsPerBlock: Tx[][] = [];
26
27
  private blockIndex = 0;
27
28
 
28
29
  /** Optional function to dynamically provide the block (alternative to seedBlocks) */
29
- private blockProvider: (() => L2BlockNew) | undefined = undefined;
30
+ private blockProvider: (() => L2Block) | undefined = undefined;
30
31
 
31
32
  /** Track calls for assertions */
32
33
  public buildBlockCalls: Array<{
@@ -34,6 +35,8 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
34
35
  timestamp: bigint;
35
36
  opts: PublicProcessorLimits;
36
37
  }> = [];
38
+ /** Track all consumed transaction hashes across buildBlock calls */
39
+ public consumedTxHashes: Set<string> = new Set();
37
40
  public completeCheckpointCalled = false;
38
41
  public getCheckpointCalled = false;
39
42
 
@@ -46,7 +49,7 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
46
49
  ) {}
47
50
 
48
51
  /** Seed the builder with blocks to return on successive buildBlock calls */
49
- seedBlocks(blocks: L2BlockNew[], usedTxsPerBlock?: Tx[][]): this {
52
+ seedBlocks(blocks: L2Block[], usedTxsPerBlock?: Tx[][]): this {
50
53
  this.blocks = blocks;
51
54
  this.usedTxsPerBlock = usedTxsPerBlock ?? blocks.map(() => []);
52
55
  this.blockIndex = 0;
@@ -58,7 +61,7 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
58
61
  * Set a function that provides blocks dynamically.
59
62
  * Useful for tests where the block is determined at call time (e.g., sequencer tests).
60
63
  */
61
- setBlockProvider(provider: () => L2BlockNew): this {
64
+ setBlockProvider(provider: () => L2Block): this {
62
65
  this.blockProvider = provider;
63
66
  this.blocks = [];
64
67
  return this;
@@ -68,19 +71,19 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
68
71
  return this.constants;
69
72
  }
70
73
 
71
- buildBlock(
72
- _pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
74
+ async buildBlock(
75
+ pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
73
76
  blockNumber: BlockNumber,
74
77
  timestamp: bigint,
75
78
  opts: PublicProcessorLimits,
76
- ): Promise<BuildBlockInCheckpointResult> {
79
+ ): Promise<BuildBlockInCheckpointResultWithTimer> {
77
80
  this.buildBlockCalls.push({ blockNumber, timestamp, opts });
78
81
 
79
82
  if (this.errorOnBuild) {
80
- return Promise.reject(this.errorOnBuild);
83
+ throw this.errorOnBuild;
81
84
  }
82
85
 
83
- let block: L2BlockNew;
86
+ let block: L2Block;
84
87
  let usedTxs: Tx[];
85
88
 
86
89
  if (this.blockProvider) {
@@ -96,7 +99,20 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
96
99
  this.builtBlocks.push(block);
97
100
  }
98
101
 
99
- return Promise.resolve({
102
+ // Check that no pending tx has already been consumed
103
+ for await (const tx of pendingTxs) {
104
+ const hash = tx.getTxHash().toString();
105
+ if (this.consumedTxHashes.has(hash)) {
106
+ throw new Error(`Transaction ${hash} was already consumed in a previous block`);
107
+ }
108
+ }
109
+
110
+ // Add used txs to consumed set
111
+ for (const tx of usedTxs) {
112
+ this.consumedTxHashes.add(tx.getTxHash().toString());
113
+ }
114
+
115
+ return {
100
116
  block,
101
117
  publicGas: Gas.empty(),
102
118
  publicProcessorDuration: 0,
@@ -104,7 +120,8 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
104
120
  blockBuildingTimer: new Timer(),
105
121
  usedTxs,
106
122
  failedTxs: [],
107
- });
123
+ usedTxBlobFields: block?.body?.txEffects?.reduce((sum, tx) => sum + tx.getNumBlobFields(), 0) ?? 0,
124
+ };
108
125
  }
109
126
 
110
127
  completeCheckpoint(): Promise<Checkpoint> {
@@ -146,7 +163,7 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
146
163
  * Creates a CheckpointHeader from a block's header for testing.
147
164
  * This is a simplified version that creates a minimal CheckpointHeader.
148
165
  */
149
- private createCheckpointHeader(block: L2BlockNew): CheckpointHeader {
166
+ private createCheckpointHeader(block: L2Block): CheckpointHeader {
150
167
  const header = block.header;
151
168
  const gv = header.globalVariables;
152
169
  return CheckpointHeader.empty({
@@ -168,6 +185,7 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
168
185
  this.usedTxsPerBlock = [];
169
186
  this.blockIndex = 0;
170
187
  this.buildBlockCalls = [];
188
+ this.consumedTxHashes.clear();
171
189
  this.completeCheckpointCalled = false;
172
190
  this.getCheckpointCalled = false;
173
191
  this.errorOnBuild = undefined;
@@ -180,7 +198,7 @@ export class MockCheckpointBuilder implements FunctionsOf<CheckpointBuilder> {
180
198
  * as FullNodeCheckpointsBuilder. Returns MockCheckpointBuilder instances.
181
199
  * Does NOT use jest mocks - this is a proper test double.
182
200
  */
183
- export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBuilder> {
201
+ export class MockCheckpointsBuilder implements ICheckpointsBuilder {
184
202
  private checkpointBuilder: MockCheckpointBuilder | undefined;
185
203
 
186
204
  /** Track calls for assertions */
@@ -188,12 +206,14 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
188
206
  checkpointNumber: CheckpointNumber;
189
207
  constants: CheckpointGlobalVariables;
190
208
  l1ToL2Messages: Fr[];
209
+ previousCheckpointOutHashes: Fr[];
191
210
  }> = [];
192
211
  public openCheckpointCalls: Array<{
193
212
  checkpointNumber: CheckpointNumber;
194
213
  constants: CheckpointGlobalVariables;
195
214
  l1ToL2Messages: Fr[];
196
- existingBlocks: L2BlockNew[];
215
+ previousCheckpointOutHashes: Fr[];
216
+ existingBlocks: L2Block[];
197
217
  }> = [];
198
218
  public updateConfigCalls: Array<Partial<FullNodeBlockBuilderConfig>> = [];
199
219
 
@@ -240,33 +260,45 @@ export class MockCheckpointsBuilder implements FunctionsOf<FullNodeCheckpointsBu
240
260
  checkpointNumber: CheckpointNumber,
241
261
  constants: CheckpointGlobalVariables,
242
262
  l1ToL2Messages: Fr[],
243
- _fork: unknown,
244
- ): Promise<CheckpointBuilder> {
245
- this.startCheckpointCalls.push({ checkpointNumber, constants, l1ToL2Messages });
263
+ previousCheckpointOutHashes: Fr[],
264
+ _fork: MerkleTreeWriteOperations,
265
+ ): Promise<ICheckpointBlockBuilder> {
266
+ this.startCheckpointCalls.push({ checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes });
246
267
 
247
268
  if (!this.checkpointBuilder) {
248
269
  // Auto-create a builder if none was set
249
270
  this.checkpointBuilder = new MockCheckpointBuilder(constants, checkpointNumber);
250
271
  }
251
272
 
252
- return Promise.resolve(this.checkpointBuilder as unknown as CheckpointBuilder);
273
+ return Promise.resolve(this.checkpointBuilder);
253
274
  }
254
275
 
255
276
  openCheckpoint(
256
277
  checkpointNumber: CheckpointNumber,
257
278
  constants: CheckpointGlobalVariables,
258
279
  l1ToL2Messages: Fr[],
259
- _fork: unknown,
260
- existingBlocks: L2BlockNew[] = [],
261
- ): Promise<CheckpointBuilder> {
262
- this.openCheckpointCalls.push({ checkpointNumber, constants, l1ToL2Messages, existingBlocks });
280
+ previousCheckpointOutHashes: Fr[],
281
+ _fork: MerkleTreeWriteOperations,
282
+ existingBlocks: L2Block[] = [],
283
+ ): Promise<ICheckpointBlockBuilder> {
284
+ this.openCheckpointCalls.push({
285
+ checkpointNumber,
286
+ constants,
287
+ l1ToL2Messages,
288
+ previousCheckpointOutHashes,
289
+ existingBlocks,
290
+ });
263
291
 
264
292
  if (!this.checkpointBuilder) {
265
293
  // Auto-create a builder if none was set
266
294
  this.checkpointBuilder = new MockCheckpointBuilder(constants, checkpointNumber);
267
295
  }
268
296
 
269
- return Promise.resolve(this.checkpointBuilder as unknown as CheckpointBuilder);
297
+ return Promise.resolve(this.checkpointBuilder);
298
+ }
299
+
300
+ getFork(_blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations> {
301
+ throw new Error('MockCheckpointsBuilder.getFork not implemented');
270
302
  }
271
303
 
272
304
  /** Reset for reuse in another test */
package/src/test/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Body } from '@aztec/aztec.js/block';
2
- import { CheckpointNumber } from '@aztec/foundation/branded-types';
2
+ import { CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
3
3
  import { times } from '@aztec/foundation/collection';
4
4
  import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
5
5
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -7,7 +7,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { Signature } from '@aztec/foundation/eth-signature';
8
8
  import type { P2P } from '@aztec/p2p';
9
9
  import { PublicDataWrite } from '@aztec/stdlib/avm';
10
- import { CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
10
+ import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
11
11
  import { BlockProposal, CheckpointAttestation, CheckpointProposal, ConsensusPayload } from '@aztec/stdlib/p2p';
12
12
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
13
13
  import { makeAppendOnlyTreeSnapshot, mockTxForRollup } from '@aztec/stdlib/testing';
@@ -30,9 +30,9 @@ export async function makeTx(seed?: number, chainId?: Fr): Promise<Tx> {
30
30
  }
31
31
 
32
32
  /**
33
- * Creates an L2BlockNew from transactions and global variables
33
+ * Creates an L2Block from transactions and global variables
34
34
  */
35
- export async function makeBlock(txs: Tx[], globalVariables: GlobalVariables): Promise<L2BlockNew> {
35
+ export async function makeBlock(txs: Tx[], globalVariables: GlobalVariables): Promise<L2Block> {
36
36
  const processedTxs = await Promise.all(
37
37
  txs.map(tx =>
38
38
  makeProcessedTxFromPrivateOnlyTx(tx, Fr.ZERO, new PublicDataWrite(Fr.random(), Fr.random()), globalVariables),
@@ -41,7 +41,13 @@ export async function makeBlock(txs: Tx[], globalVariables: GlobalVariables): Pr
41
41
  const body = new Body(processedTxs.map(tx => tx.txEffect));
42
42
  const header = BlockHeader.empty({ globalVariables });
43
43
  const archive = makeAppendOnlyTreeSnapshot(globalVariables.blockNumber + 1);
44
- return new L2BlockNew(archive, header, body, CheckpointNumber(globalVariables.blockNumber), 0);
44
+ return new L2Block(
45
+ archive,
46
+ header,
47
+ body,
48
+ CheckpointNumber.fromBlockNumber(globalVariables.blockNumber),
49
+ IndexWithinCheckpoint(0),
50
+ );
45
51
  }
46
52
 
47
53
  /**
@@ -70,16 +76,17 @@ export function createMockSignatures(signer: Secp256k1Signer): CommitteeAttestat
70
76
  }
71
77
 
72
78
  /**
73
- * Creates a CheckpointHeader from an L2BlockNew for testing purposes.
74
- * Uses mock values for blockHeadersHash, blobsHash and inHash since L2BlockNew doesn't have these fields.
79
+ * Creates a CheckpointHeader from an L2Block for testing purposes.
80
+ * Uses mock values for blockHeadersHash, blobsHash and inHash since L2Block doesn't have these fields.
75
81
  */
76
- function createCheckpointHeaderFromBlock(block: L2BlockNew): CheckpointHeader {
82
+ function createCheckpointHeaderFromBlock(block: L2Block): CheckpointHeader {
77
83
  const gv = block.header.globalVariables;
78
84
  return new CheckpointHeader(
79
85
  block.header.lastArchive.root,
80
86
  Fr.random(), // blockHeadersHash - mock value for testing
81
87
  Fr.random(), // blobsHash - mock value for testing
82
88
  Fr.random(), // inHash - mock value for testing
89
+ Fr.random(), // outHash - mock value for testing
83
90
  gv.slotNumber,
84
91
  gv.timestamp,
85
92
  gv.coinbase,
@@ -92,7 +99,7 @@ function createCheckpointHeaderFromBlock(block: L2BlockNew): CheckpointHeader {
92
99
  /**
93
100
  * Creates a block proposal from a block and signature
94
101
  */
95
- export function createBlockProposal(block: L2BlockNew, signature: Signature): BlockProposal {
102
+ export function createBlockProposal(block: L2Block, signature: Signature): BlockProposal {
96
103
  const txHashes = block.body.txEffects.map(tx => tx.txHash);
97
104
  return new BlockProposal(
98
105
  block.header,
@@ -108,7 +115,7 @@ export function createBlockProposal(block: L2BlockNew, signature: Signature): Bl
108
115
  * Creates a checkpoint proposal from a block and signature
109
116
  */
110
117
  export function createCheckpointProposal(
111
- block: L2BlockNew,
118
+ block: L2Block,
112
119
  checkpointSignature: Signature,
113
120
  blockSignature?: Signature,
114
121
  ): CheckpointProposal {
@@ -128,7 +135,7 @@ export function createCheckpointProposal(
128
135
  * In production, the sender is recovered from the signature.
129
136
  */
130
137
  export function createCheckpointAttestation(
131
- block: L2BlockNew,
138
+ block: L2Block,
132
139
  signature: Signature,
133
140
  sender: EthAddress,
134
141
  ): CheckpointAttestation {
@@ -149,7 +156,7 @@ export async function setupTxsAndBlock(
149
156
  globalVariables: GlobalVariables,
150
157
  txCount: number,
151
158
  chainId: Fr,
152
- ): Promise<{ txs: Tx[]; block: L2BlockNew }> {
159
+ ): Promise<{ txs: Tx[]; block: L2Block }> {
153
160
  const txs = await Promise.all(times(txCount, i => makeTx(i + 1, chainId)));
154
161
  const block = await makeBlock(txs, globalVariables);
155
162
  mockPendingTxs(p2p, txs);
@@ -1,26 +0,0 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
2
- import type { Fr } from '@aztec/foundation/curves/bn254';
3
- import { DateProvider } from '@aztec/foundation/timer';
4
- import { PublicProcessor } from '@aztec/simulator/server';
5
- import type { ContractDataSource } from '@aztec/stdlib/contract';
6
- import type { BuildBlockResult, FullNodeBlockBuilderConfig, IFullNodeBlockBuilder, MerkleTreeWriteOperations, PublicProcessorLimits, PublicProcessorValidator, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
7
- import { GlobalVariables, Tx } from '@aztec/stdlib/tx';
8
- import { type TelemetryClient } from '@aztec/telemetry-client';
9
- export declare class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
10
- private config;
11
- private worldState;
12
- private contractDataSource;
13
- private dateProvider;
14
- private telemetryClient;
15
- constructor(config: FullNodeBlockBuilderConfig, worldState: WorldStateSynchronizer, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient?: TelemetryClient);
16
- getConfig(): FullNodeBlockBuilderConfig;
17
- updateConfig(config: Partial<FullNodeBlockBuilderConfig>): void;
18
- makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations): Promise<{
19
- processor: PublicProcessor;
20
- validator: PublicProcessorValidator;
21
- }>;
22
- private syncToPreviousBlock;
23
- buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, l1ToL2Messages: Fr[], globalVariables: GlobalVariables, opts: PublicProcessorLimits, suppliedFork?: MerkleTreeWriteOperations): Promise<BuildBlockResult>;
24
- getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations>;
25
- }
26
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfYnVpbGRlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9ibG9ja19idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUU5RCxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUl6RCxPQUFPLEVBQUUsWUFBWSxFQUFrQixNQUFNLHlCQUF5QixDQUFDO0FBR3ZFLE9BQU8sRUFHTCxlQUFlLEVBRWhCLE1BQU0seUJBQXlCLENBQUM7QUFDakMsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUdqRSxPQUFPLEtBQUssRUFDVixnQkFBZ0IsRUFDaEIsMEJBQTBCLEVBQzFCLHFCQUFxQixFQUNyQix5QkFBeUIsRUFDekIscUJBQXFCLEVBQ3JCLHdCQUF3QixFQUN4QixzQkFBc0IsRUFDdkIsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6QyxPQUFPLEVBQUUsZUFBZSxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQTBFbkYscUJBQWEsb0JBQXFCLFlBQVcscUJBQXFCO0lBRTlELE9BQU8sQ0FBQyxNQUFNO0lBQ2QsT0FBTyxDQUFDLFVBQVU7SUFDbEIsT0FBTyxDQUFDLGtCQUFrQjtJQUMxQixPQUFPLENBQUMsWUFBWTtJQUNwQixPQUFPLENBQUMsZUFBZTtJQUx6QixZQUNVLE1BQU0sRUFBRSwwQkFBMEIsRUFDbEMsVUFBVSxFQUFFLHNCQUFzQixFQUNsQyxrQkFBa0IsRUFBRSxrQkFBa0IsRUFDdEMsWUFBWSxFQUFFLFlBQVksRUFDMUIsZUFBZSxHQUFFLGVBQXNDLEVBQzdEO0lBRUcsU0FBUyxJQUFJLDBCQUEwQixDQUU3QztJQUVNLFlBQVksQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLDBCQUEwQixDQUFDLFFBRTlEO0lBRVksb0JBQW9CLENBQUMsZUFBZSxFQUFFLGVBQWUsRUFBRSxJQUFJLEVBQUUseUJBQXlCOzs7T0FrQ2xHO1lBRWEsbUJBQW1CO0lBVTNCLFVBQVUsQ0FDZCxVQUFVLEVBQUUsUUFBUSxDQUFDLEVBQUUsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxFQUFFLENBQUMsRUFDNUMsY0FBYyxFQUFFLEVBQUUsRUFBRSxFQUNwQixlQUFlLEVBQUUsZUFBZSxFQUNoQyxJQUFJLEVBQUUscUJBQXFCLEVBQzNCLFlBQVksQ0FBQyxFQUFFLHlCQUF5QixHQUN2QyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FzQzNCO0lBRUQsT0FBTyxDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLHlCQUF5QixDQUFDLENBRXBFO0NBQ0YifQ==
@@ -1 +0,0 @@
1
- {"version":3,"file":"block_builder.d.ts","sourceRoot":"","sources":["../../src/sequencer/block_builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAIzD,OAAO,EAAE,YAAY,EAAkB,MAAM,yBAAyB,CAAC;AAGvE,OAAO,EAGL,eAAe,EAEhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,KAAK,EACV,gBAAgB,EAChB,0BAA0B,EAC1B,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AA0EnF,qBAAa,oBAAqB,YAAW,qBAAqB;IAE9D,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,eAAe;IALzB,YACU,MAAM,EAAE,0BAA0B,EAClC,UAAU,EAAE,sBAAsB,EAClC,kBAAkB,EAAE,kBAAkB,EACtC,YAAY,EAAE,YAAY,EAC1B,eAAe,GAAE,eAAsC,EAC7D;IAEG,SAAS,IAAI,0BAA0B,CAE7C;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,0BAA0B,CAAC,QAE9D;IAEY,oBAAoB,CAAC,eAAe,EAAE,eAAe,EAAE,IAAI,EAAE,yBAAyB;;;OAkClG;YAEa,mBAAmB;IAU3B,UAAU,CACd,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,EAC5C,cAAc,EAAE,EAAE,EAAE,EACpB,eAAe,EAAE,eAAe,EAChC,IAAI,EAAE,qBAAqB,EAC3B,YAAY,CAAC,EAAE,yBAAyB,GACvC,OAAO,CAAC,gBAAgB,CAAC,CAsC3B;IAED,OAAO,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAEpE;CACF"}
@@ -1,129 +0,0 @@
1
- import { MerkleTreeId } from '@aztec/aztec.js/trees';
2
- import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import { merge, pick } from '@aztec/foundation/collection';
4
- import { createLogger } from '@aztec/foundation/log';
5
- import { retryUntil } from '@aztec/foundation/retry';
6
- import { bufferToHex } from '@aztec/foundation/string';
7
- import { Timer, elapsed } from '@aztec/foundation/timer';
8
- import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
9
- import { LightweightBlockFactory } from '@aztec/prover-client/block-factory';
10
- import { GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor, createPublicTxSimulatorForBlockBuilding } from '@aztec/simulator/server';
11
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
12
- import { Gas } from '@aztec/stdlib/gas';
13
- import { getTelemetryClient } from '@aztec/telemetry-client';
14
- import { createValidatorForBlockBuilding } from '@aztec/validator-client';
15
- const log = createLogger('block-builder');
16
- /** Builds a block out of pending txs */ async function buildBlock(pendingTxs, l1ToL2Messages, newGlobalVariables, opts = {}, worldStateFork, processor, validator, l1Constants, dateProvider, telemetryClient = getTelemetryClient()) {
17
- const blockBuildingTimer = new Timer();
18
- const blockNumber = newGlobalVariables.blockNumber;
19
- const slot = newGlobalVariables.slotNumber;
20
- const msgCount = l1ToL2Messages.length;
21
- const stateReference = await worldStateFork.getStateReference();
22
- const archiveTree = await worldStateFork.getTreeInfo(MerkleTreeId.ARCHIVE);
23
- log.verbose(`Building block ${blockNumber} for slot ${slot}`, {
24
- slot,
25
- slotStart: new Date(Number(getTimestampForSlot(slot, l1Constants)) * 1000),
26
- now: new Date(dateProvider.now()),
27
- blockNumber,
28
- msgCount,
29
- initialStateReference: stateReference.toInspect(),
30
- initialArchiveRoot: bufferToHex(archiveTree.root),
31
- opts
32
- });
33
- const blockFactory = new LightweightBlockFactory(worldStateFork, telemetryClient);
34
- await blockFactory.startNewBlock(newGlobalVariables, l1ToL2Messages);
35
- const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(()=>processor.process(pendingTxs, opts, validator));
36
- // All real transactions have been added, set the block as full and pad if needed
37
- await blockFactory.addTxs(processedTxs);
38
- const block = await blockFactory.setBlockCompleted();
39
- // How much public gas was processed
40
- const publicGas = processedTxs.reduce((acc, tx)=>acc.add(tx.gasUsed.publicGas), Gas.empty());
41
- const res = {
42
- block,
43
- publicGas,
44
- publicProcessorDuration,
45
- numMsgs: l1ToL2Messages.length,
46
- numTxs: processedTxs.length,
47
- failedTxs: failedTxs,
48
- blockBuildingTimer,
49
- usedTxs
50
- };
51
- log.trace('Built block', res.block.header);
52
- return res;
53
- }
54
- const FullNodeBlockBuilderConfigKeys = [
55
- 'l1GenesisTime',
56
- 'slotDuration',
57
- 'l1ChainId',
58
- 'rollupVersion',
59
- 'txPublicSetupAllowList',
60
- 'fakeProcessingDelayPerTxMs',
61
- 'fakeThrowAfterProcessingTxCount'
62
- ];
63
- // TODO(palla/mbps): Try killing this in favor of the CheckpointsBuilder
64
- export class FullNodeBlockBuilder {
65
- config;
66
- worldState;
67
- contractDataSource;
68
- dateProvider;
69
- telemetryClient;
70
- constructor(config, worldState, contractDataSource, dateProvider, telemetryClient = getTelemetryClient()){
71
- this.config = config;
72
- this.worldState = worldState;
73
- this.contractDataSource = contractDataSource;
74
- this.dateProvider = dateProvider;
75
- this.telemetryClient = telemetryClient;
76
- }
77
- getConfig() {
78
- return pick(this.config, ...FullNodeBlockBuilderConfigKeys);
79
- }
80
- updateConfig(config) {
81
- this.config = merge(this.config, pick(config, ...FullNodeBlockBuilderConfigKeys));
82
- }
83
- async makeBlockBuilderDeps(globalVariables, fork) {
84
- const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? await getDefaultAllowedSetupFunctions();
85
- const contractsDB = new PublicContractsDB(this.contractDataSource);
86
- const guardedFork = new GuardedMerkleTreeOperations(fork);
87
- const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(guardedFork, contractsDB, globalVariables, this.telemetryClient);
88
- const processor = new PublicProcessor(globalVariables, guardedFork, contractsDB, publicTxSimulator, this.dateProvider, this.telemetryClient, undefined, this.config);
89
- const validator = createValidatorForBlockBuilding(fork, this.contractDataSource, globalVariables, txPublicSetupAllowList);
90
- return {
91
- processor,
92
- validator
93
- };
94
- }
95
- async syncToPreviousBlock(parentBlockNumber, timeout) {
96
- await retryUntil(()=>this.worldState.syncImmediate(parentBlockNumber, true).then((syncedTo)=>syncedTo >= parentBlockNumber), 'sync to previous block', timeout, 0.1);
97
- log.debug(`Synced to previous block ${parentBlockNumber}`);
98
- }
99
- async buildBlock(pendingTxs, l1ToL2Messages, globalVariables, opts, suppliedFork) {
100
- const parentBlockNumber = BlockNumber(globalVariables.blockNumber - 1);
101
- const syncTimeout = opts.deadline ? (opts.deadline.getTime() - this.dateProvider.now()) / 1000 : undefined;
102
- await this.syncToPreviousBlock(parentBlockNumber, syncTimeout);
103
- const fork = suppliedFork ?? await this.worldState.fork(parentBlockNumber);
104
- try {
105
- const { processor, validator } = await this.makeBlockBuilderDeps(globalVariables, fork);
106
- const res = await buildBlock(pendingTxs, l1ToL2Messages, globalVariables, opts, fork, processor, validator, this.config, this.dateProvider, this.telemetryClient);
107
- return res;
108
- } finally{
109
- // If the fork was supplied, we don't close it.
110
- // Otherwise, we wait a bit to close the fork we just created,
111
- // since the processor may still be working on a dangling tx
112
- // which was interrupted due to the processingDeadline being hit.
113
- if (!suppliedFork) {
114
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
115
- setTimeout(async ()=>{
116
- try {
117
- await fork.close();
118
- } catch (err) {
119
- // This can happen if the sequencer is stopped before we hit this timeout.
120
- log.warn(`Error closing forks for block processing`, err);
121
- }
122
- }, 5000);
123
- }
124
- }
125
- }
126
- getFork(blockNumber) {
127
- return this.worldState.fork(blockNumber);
128
- }
129
- }