@aztec/end-to-end 0.0.1-commit.4d79d1f2d → 0.0.1-commit.5358163d3

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 (119) hide show
  1. package/dest/bench/client_flows/client_flows_benchmark.d.ts +1 -1
  2. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  3. package/dest/bench/client_flows/client_flows_benchmark.js +1 -0
  4. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +2 -2
  5. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  6. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +3 -3
  7. package/dest/e2e_epochs/epochs_test.d.ts +9 -7
  8. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  9. package/dest/e2e_epochs/epochs_test.js +53 -34
  10. package/dest/e2e_fees/fees_test.js +1 -1
  11. package/dest/e2e_p2p/p2p_network.d.ts +3 -2
  12. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  13. package/dest/e2e_p2p/p2p_network.js +21 -3
  14. package/dest/e2e_p2p/reqresp/utils.d.ts +3 -3
  15. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  16. package/dest/e2e_p2p/reqresp/utils.js +32 -8
  17. package/dest/e2e_p2p/shared.d.ts +15 -1
  18. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  19. package/dest/e2e_p2p/shared.js +24 -1
  20. package/dest/e2e_token_contract/token_contract_test.d.ts +2 -2
  21. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  22. package/dest/e2e_token_contract/token_contract_test.js +3 -3
  23. package/dest/fixtures/authwit_proxy.d.ts +15 -0
  24. package/dest/fixtures/authwit_proxy.d.ts.map +1 -0
  25. package/dest/fixtures/authwit_proxy.js +30 -0
  26. package/dest/fixtures/e2e_prover_test.d.ts +3 -3
  27. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  28. package/dest/fixtures/e2e_prover_test.js +27 -34
  29. package/dest/fixtures/elu_monitor.d.ts +21 -0
  30. package/dest/fixtures/elu_monitor.d.ts.map +1 -0
  31. package/dest/fixtures/elu_monitor.js +102 -0
  32. package/dest/fixtures/fixtures.d.ts +5 -1
  33. package/dest/fixtures/fixtures.d.ts.map +1 -1
  34. package/dest/fixtures/fixtures.js +6 -0
  35. package/dest/fixtures/ha_setup.d.ts +1 -1
  36. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  37. package/dest/fixtures/ha_setup.js +3 -1
  38. package/dest/fixtures/setup.d.ts +25 -9
  39. package/dest/fixtures/setup.d.ts.map +1 -1
  40. package/dest/fixtures/setup.js +52 -78
  41. package/dest/fixtures/setup_p2p_test.d.ts +6 -3
  42. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  43. package/dest/fixtures/setup_p2p_test.js +12 -9
  44. package/dest/fixtures/token_utils.d.ts +2 -2
  45. package/dest/fixtures/token_utils.d.ts.map +1 -1
  46. package/dest/fixtures/token_utils.js +3 -2
  47. package/dest/shared/index.d.ts +2 -1
  48. package/dest/shared/index.d.ts.map +1 -1
  49. package/dest/shared/index.js +1 -0
  50. package/dest/shared/jest_setup.js +41 -1
  51. package/dest/shared/mock_state_view.d.ts +86 -0
  52. package/dest/shared/mock_state_view.d.ts.map +1 -0
  53. package/dest/shared/mock_state_view.js +186 -0
  54. package/dest/shared/submit-transactions.d.ts +1 -1
  55. package/dest/shared/submit-transactions.d.ts.map +1 -1
  56. package/dest/spartan/setup_test_wallets.d.ts +8 -1
  57. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  58. package/dest/spartan/setup_test_wallets.js +28 -0
  59. package/dest/spartan/tx_metrics.js +1 -1
  60. package/dest/spartan/utils/bot.d.ts +3 -2
  61. package/dest/spartan/utils/bot.d.ts.map +1 -1
  62. package/dest/spartan/utils/bot.js +2 -1
  63. package/dest/spartan/utils/config.d.ts +4 -1
  64. package/dest/spartan/utils/config.d.ts.map +1 -1
  65. package/dest/spartan/utils/config.js +2 -1
  66. package/dest/spartan/utils/index.d.ts +3 -2
  67. package/dest/spartan/utils/index.d.ts.map +1 -1
  68. package/dest/spartan/utils/index.js +3 -1
  69. package/dest/spartan/utils/k8s.d.ts +3 -1
  70. package/dest/spartan/utils/k8s.d.ts.map +1 -1
  71. package/dest/spartan/utils/k8s.js +6 -0
  72. package/dest/spartan/utils/nodes.d.ts +4 -5
  73. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  74. package/dest/spartan/utils/nodes.js +9 -9
  75. package/dest/test-wallet/test_wallet.d.ts +4 -3
  76. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  77. package/dest/test-wallet/test_wallet.js +3 -3
  78. package/dest/test-wallet/wallet_worker_script.d.ts +2 -0
  79. package/dest/test-wallet/wallet_worker_script.d.ts.map +1 -0
  80. package/dest/test-wallet/wallet_worker_script.js +48 -0
  81. package/dest/test-wallet/worker_wallet.d.ts +52 -0
  82. package/dest/test-wallet/worker_wallet.d.ts.map +1 -0
  83. package/dest/test-wallet/worker_wallet.js +151 -0
  84. package/dest/test-wallet/worker_wallet_schema.d.ts +274 -0
  85. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -0
  86. package/dest/test-wallet/worker_wallet_schema.js +10 -0
  87. package/package.json +43 -44
  88. package/src/bench/client_flows/client_flows_benchmark.ts +1 -0
  89. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +3 -3
  90. package/src/e2e_epochs/epochs_test.ts +66 -66
  91. package/src/e2e_fees/fees_test.ts +1 -1
  92. package/src/e2e_p2p/p2p_network.ts +27 -2
  93. package/src/e2e_p2p/reqresp/utils.ts +36 -8
  94. package/src/e2e_p2p/shared.ts +44 -1
  95. package/src/e2e_token_contract/token_contract_test.ts +3 -3
  96. package/src/fixtures/authwit_proxy.ts +50 -0
  97. package/src/fixtures/dumps/epoch_proof_result.json +1 -1
  98. package/src/fixtures/e2e_prover_test.ts +25 -35
  99. package/src/fixtures/elu_monitor.ts +126 -0
  100. package/src/fixtures/fixtures.ts +10 -0
  101. package/src/fixtures/ha_setup.ts +3 -1
  102. package/src/fixtures/setup.ts +72 -109
  103. package/src/fixtures/setup_p2p_test.ts +9 -17
  104. package/src/fixtures/token_utils.ts +2 -1
  105. package/src/shared/index.ts +1 -0
  106. package/src/shared/jest_setup.ts +51 -1
  107. package/src/shared/mock_state_view.ts +188 -0
  108. package/src/shared/submit-transactions.ts +4 -1
  109. package/src/spartan/setup_test_wallets.ts +55 -3
  110. package/src/spartan/tx_metrics.ts +1 -1
  111. package/src/spartan/utils/bot.ts +4 -1
  112. package/src/spartan/utils/config.ts +1 -0
  113. package/src/spartan/utils/index.ts +4 -0
  114. package/src/spartan/utils/k8s.ts +8 -0
  115. package/src/spartan/utils/nodes.ts +15 -10
  116. package/src/test-wallet/test_wallet.ts +5 -4
  117. package/src/test-wallet/wallet_worker_script.ts +60 -0
  118. package/src/test-wallet/worker_wallet.ts +213 -0
  119. package/src/test-wallet/worker_wallet_schema.ts +13 -0
@@ -1,3 +1,4 @@
1
+ import type { Archiver } from '@aztec/archiver';
1
2
  import { type AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
2
3
  import { getTimestampRangeForEpoch } from '@aztec/aztec.js/block';
3
4
  import { getContractInstanceFromInstantiationParams } from '@aztec/aztec.js/contracts';
@@ -9,7 +10,8 @@ import { EpochCache } from '@aztec/epoch-cache';
9
10
  import { createExtendedL1Client } from '@aztec/ethereum/client';
10
11
  import { DefaultL1ContractsConfig } from '@aztec/ethereum/config';
11
12
  import { RollupContract } from '@aztec/ethereum/contracts';
12
- import { ChainMonitor, DelayedTxUtils, type Delayer, waitUntilL1Timestamp, withDelayer } from '@aztec/ethereum/test';
13
+ import { Delayer, createDelayer, waitUntilL1Timestamp, wrapClientWithDelayer } from '@aztec/ethereum/l1-tx-utils';
14
+ import { ChainMonitor } from '@aztec/ethereum/test';
13
15
  import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
14
16
  import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
15
17
  import { SecretValue } from '@aztec/foundation/config';
@@ -20,16 +22,9 @@ import { sleep } from '@aztec/foundation/sleep';
20
22
  import { SpamContract } from '@aztec/noir-test-contracts.js/Spam';
21
23
  import { TestContract } from '@aztec/noir-test-contracts.js/Test';
22
24
  import { getMockPubSubP2PServiceFactory } from '@aztec/p2p/test-helpers';
23
- import { ProverNode, type ProverNodeConfig, ProverNodePublisher } from '@aztec/prover-node';
24
- import type { TestProverNode } from '@aztec/prover-node/test';
25
+ import type { ProverNodeConfig } from '@aztec/prover-node';
25
26
  import type { PXEConfig } from '@aztec/pxe/config';
26
- import {
27
- type SequencerClient,
28
- type SequencerEvents,
29
- type SequencerPublisher,
30
- SequencerState,
31
- } from '@aztec/sequencer-client';
32
- import type { TestSequencerClient } from '@aztec/sequencer-client/test';
27
+ import { type SequencerClient, type SequencerEvents, SequencerState } from '@aztec/sequencer-client';
33
28
  import { type BlockParameter, EthAddress } from '@aztec/stdlib/block';
34
29
  import { type L1RollupConstants, getProofSubmissionDeadlineTimestamp } from '@aztec/stdlib/epoch-helpers';
35
30
  import { tryStop } from '@aztec/stdlib/interfaces/server';
@@ -46,7 +41,7 @@ import {
46
41
  setup,
47
42
  } from '../fixtures/utils.js';
48
43
 
49
- export const WORLD_STATE_BLOCK_HISTORY = 2;
44
+ export const WORLD_STATE_CHECKPOINT_HISTORY = 2;
50
45
  export const WORLD_STATE_BLOCK_CHECK_INTERVAL = 50;
51
46
  export const ARCHIVER_POLL_INTERVAL = 50;
52
47
  export const DEFAULT_L1_BLOCK_TIME = process.env.CI ? 12 : 8;
@@ -81,7 +76,7 @@ export class EpochsTestContext {
81
76
  public proverDelayer!: Delayer;
82
77
  public sequencerDelayer!: Delayer;
83
78
 
84
- public proverNodes: ProverNode[] = [];
79
+ public proverNodes: AztecNodeService[] = [];
85
80
  public nodes: AztecNodeService[] = [];
86
81
 
87
82
  public epochDuration!: number;
@@ -147,7 +142,7 @@ export class EpochsTestContext {
147
142
  // using the prover's eth address if the proverId is used for something in the rollup contract
148
143
  // Use numeric EthAddress for deterministic prover id
149
144
  proverId: EthAddress.fromNumber(1),
150
- worldStateBlockHistory: WORLD_STATE_BLOCK_HISTORY,
145
+ worldStateCheckpointHistory: WORLD_STATE_CHECKPOINT_HISTORY,
151
146
  exitDelaySeconds: DefaultL1ContractsConfig.exitDelaySeconds,
152
147
  slasherFlavor: 'none',
153
148
  l1PublishingTime,
@@ -169,17 +164,8 @@ export class EpochsTestContext {
169
164
  // Loop that tracks L1 and L2 block numbers and logs whenever there's a new one.
170
165
  this.monitor = new ChainMonitor(this.rollup, context.dateProvider, this.logger).start();
171
166
 
172
- // This is hideous.
173
- // We ought to have a definite reference to the l1TxUtils that we're using in both places, provided by the test context.
174
- this.proverDelayer = context.proverNode
175
- ? (((context.proverNode as TestProverNode).publisher as ProverNodePublisher).l1TxUtils as DelayedTxUtils).delayer!
176
- : undefined!;
177
- this.sequencerDelayer = context.sequencer
178
- ? (
179
- ((context.sequencer as TestSequencerClient).sequencer.publisher as SequencerPublisher)
180
- .l1TxUtils as DelayedTxUtils
181
- ).delayer!
182
- : undefined!;
167
+ this.proverDelayer = context.proverDelayer!;
168
+ this.sequencerDelayer = context.sequencerDelayer!;
183
169
 
184
170
  if ((context.proverNode && !this.proverDelayer) || (context.sequencer && !this.sequencerDelayer)) {
185
171
  throw new Error(`Could not find prover or sequencer delayer`);
@@ -214,26 +200,29 @@ export class EpochsTestContext {
214
200
  const proverNodePrivateKey = this.getNextPrivateKey();
215
201
  const proverIndex = this.proverNodes.length + 1;
216
202
  const { mockGossipSubNetwork } = this.context;
217
- const proverNode = await withLoggerBindings({ actor: `prover-${proverIndex}` }, () =>
203
+ const { proverNode } = await withLoggerBindings({ actor: `prover-${proverIndex}` }, () =>
218
204
  createAndSyncProverNode(
219
205
  proverNodePrivateKey,
220
206
  {
221
207
  ...this.context.config,
222
208
  p2pEnabled: this.context.config.p2pEnabled || mockGossipSubNetwork !== undefined,
223
- },
224
- {
225
- dataDirectory: join(this.context.config.dataDirectory!, randomBytes(8).toString('hex')),
226
209
  proverId: EthAddress.fromNumber(proverIndex),
227
210
  dontStart: opts.dontStart,
228
211
  ...opts,
229
212
  },
230
- this.context.aztecNode,
231
- this.context.prefilledPublicData ?? [],
213
+ {
214
+ dataDirectory: join(this.context.config.dataDirectory!, randomBytes(8).toString('hex')),
215
+ },
232
216
  {
233
217
  dateProvider: this.context.dateProvider,
234
- p2pClientDeps: mockGossipSubNetwork
235
- ? { p2pServiceFactory: getMockPubSubP2PServiceFactory(mockGossipSubNetwork) }
236
- : undefined,
218
+ p2pClientDeps: {
219
+ p2pServiceFactory: mockGossipSubNetwork ? getMockPubSubP2PServiceFactory(mockGossipSubNetwork) : undefined,
220
+ rpcTxProviders: [this.context.aztecNode],
221
+ },
222
+ },
223
+ {
224
+ prefilledPublicData: this.context.prefilledPublicData ?? [],
225
+ dontStart: opts.dontStart,
237
226
  },
238
227
  ),
239
228
  );
@@ -248,15 +237,13 @@ export class EpochsTestContext {
248
237
 
249
238
  public createValidatorNode(
250
239
  privateKeys: `0x${string}`[],
251
- opts: Partial<AztecNodeConfig> & { txDelayerMaxInclusionTimeIntoSlot?: number; dontStartSequencer?: boolean } = {},
240
+ opts: Partial<AztecNodeConfig> & { dontStartSequencer?: boolean } = {},
252
241
  ) {
253
242
  this.logger.warn('Creating and syncing a validator node...');
254
243
  return this.createNode({ ...opts, disableValidator: false, validatorPrivateKeys: new SecretValue(privateKeys) });
255
244
  }
256
245
 
257
- private async createNode(
258
- opts: Partial<AztecNodeConfig> & { txDelayerMaxInclusionTimeIntoSlot?: number; dontStartSequencer?: boolean } = {},
259
- ) {
246
+ private async createNode(opts: Partial<AztecNodeConfig> & { dontStartSequencer?: boolean } = {}) {
260
247
  const nodeIndex = this.nodes.length + 1;
261
248
  const actorPrefix = opts.disableValidator ? 'node' : 'validator';
262
249
  const { mockGossipSubNetwork } = this.context;
@@ -285,26 +272,6 @@ export class EpochsTestContext {
285
272
  ),
286
273
  );
287
274
 
288
- // REFACTOR: We're getting too much into the internals of the sequencer here.
289
- // We should have a single method for constructing an aztec node that returns a TestAztecNodeService
290
- // which directly exposes the delayer and sets any test config.
291
- if (opts.txDelayerMaxInclusionTimeIntoSlot !== undefined) {
292
- this.logger.info(
293
- `Setting tx delayer max inclusion time into slot to ${opts.txDelayerMaxInclusionTimeIntoSlot} seconds`,
294
- );
295
- // Here we reach into the sequencer and hook in a tx delayer. The problem is that the sequencer's l1 utils only uses a public client, not a wallet.
296
- // The delayer needs a wallet (a client that can sign), so we have to create one here.
297
- const l1Client = createExtendedL1Client(
298
- resolvedConfig.l1RpcUrls!,
299
- resolvedConfig.publisherPrivateKeys![0]!.getValue(),
300
- );
301
- const sequencer = node.getSequencer() as TestSequencerClient;
302
- const publisher = sequencer.sequencer.publisher;
303
- const delayed = DelayedTxUtils.fromL1TxUtils(publisher.l1TxUtils, this.L1_BLOCK_TIME_IN_S, l1Client);
304
- delayed.delayer!.setMaxInclusionTimeIntoSlot(opts.txDelayerMaxInclusionTimeIntoSlot);
305
- publisher.l1TxUtils = delayed;
306
- }
307
-
308
275
  this.nodes.push(node);
309
276
  return node;
310
277
  }
@@ -356,7 +323,10 @@ export class EpochsTestContext {
356
323
  this.logger.info(`Waiting until last slot of submission window for epoch ${epochNumber} at ${date}`, {
357
324
  oneSlotBefore,
358
325
  });
359
- await waitUntilL1Timestamp(this.l1Client, oneSlotBefore);
326
+ // Use a timeout that accounts for the full proof submission window
327
+ const proofSubmissionWindowDuration =
328
+ this.constants.proofSubmissionEpochs * this.epochDuration * this.L2_SLOT_DURATION_IN_S;
329
+ await waitUntilL1Timestamp(this.l1Client, oneSlotBefore, undefined, proofSubmissionWindowDuration * 2);
360
330
  }
361
331
 
362
332
  /** Waits for the aztec node to sync to the target block number. */
@@ -408,15 +378,13 @@ export class EpochsTestContext {
408
378
 
409
379
  /** Creates an L1 client using a fresh account with funds from anvil, with a tx delayer already set up. */
410
380
  public async createL1Client() {
411
- const { client, delayer } = withDelayer(
412
- createExtendedL1Client(
413
- [...this.l1Client.chain.rpcUrls.default.http],
414
- privateKeyToAccount(this.getNextPrivateKey()),
415
- this.l1Client.chain,
416
- ),
417
- this.context.dateProvider,
418
- { ethereumSlotDuration: this.L1_BLOCK_TIME_IN_S },
381
+ const rawClient = createExtendedL1Client(
382
+ [...this.l1Client.chain.rpcUrls.default.http],
383
+ privateKeyToAccount(this.getNextPrivateKey()),
384
+ this.l1Client.chain,
419
385
  );
386
+ const delayer = createDelayer(this.context.dateProvider, { ethereumSlotDuration: this.L1_BLOCK_TIME_IN_S }, {});
387
+ const client = wrapClientWithDelayer(rawClient, delayer);
420
388
  expect(await client.getBalance({ address: client.account.address })).toBeGreaterThan(0n);
421
389
  return { client, delayer };
422
390
  }
@@ -433,6 +401,38 @@ export class EpochsTestContext {
433
401
  expect(result).toBe(expectedSuccess);
434
402
  }
435
403
 
404
+ /** Verifies at least one checkpoint has the target number of blocks (for MBPS validation). */
405
+ public async assertMultipleBlocksPerSlot(targetBlockCount: number) {
406
+ const archiver = (this.context.aztecNode as AztecNodeService).getBlockSource() as Archiver;
407
+ const checkpoints = await archiver.getCheckpoints(CheckpointNumber(1), 50);
408
+
409
+ this.logger.warn(`Retrieved ${checkpoints.length} checkpoints from archiver`, {
410
+ checkpoints: checkpoints.map(pc => pc.checkpoint.getStats()),
411
+ });
412
+
413
+ let expectedBlockNumber = checkpoints[0].checkpoint.blocks[0].number;
414
+ let targetFound = false;
415
+
416
+ for (const checkpoint of checkpoints) {
417
+ const blockCount = checkpoint.checkpoint.blocks.length;
418
+ targetFound = targetFound || blockCount >= targetBlockCount;
419
+
420
+ this.logger.verbose(`Checkpoint ${checkpoint.checkpoint.number} has ${blockCount} blocks`, {
421
+ checkpoint: checkpoint.checkpoint.getStats(),
422
+ });
423
+
424
+ for (let i = 0; i < blockCount; i++) {
425
+ const block = checkpoint.checkpoint.blocks[i];
426
+ expect(block.indexWithinCheckpoint).toBe(i);
427
+ expect(block.checkpointNumber).toBe(checkpoint.checkpoint.number);
428
+ expect(block.number).toBe(expectedBlockNumber);
429
+ expectedBlockNumber++;
430
+ }
431
+ }
432
+
433
+ expect(targetFound).toBe(true);
434
+ }
435
+
436
436
  public watchSequencerEvents(
437
437
  sequencers: SequencerClient[],
438
438
  getMetadata: (i: number) => Record<string, any> = () => ({}),
@@ -275,7 +275,7 @@ export class FeesTest {
275
275
 
276
276
  // @todo @lherskind As we deal with #13601
277
277
  // Right now the value is from `FeeLib.sol`
278
- const L1_GAS_PER_EPOCH_VERIFIED = 1000000n;
278
+ const L1_GAS_PER_EPOCH_VERIFIED = 3600000n;
279
279
 
280
280
  // We round up
281
281
  const mulDiv = (a: bigint, b: bigint, c: bigint) => (a * b) / c + ((a * b) % c > 0n ? 1n : 0n);
@@ -12,7 +12,7 @@ import {
12
12
  import type { Operator } from '@aztec/ethereum/deploy-aztec-l1-contracts';
13
13
  import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
14
14
  import { MultiAdderArtifact } from '@aztec/ethereum/l1-artifacts';
15
- import { createL1TxUtilsFromViemWallet } from '@aztec/ethereum/l1-tx-utils';
15
+ import { createL1TxUtils } from '@aztec/ethereum/l1-tx-utils';
16
16
  import { ChainMonitor } from '@aztec/ethereum/test';
17
17
  import type { ExtendedViemWalletClient, ViemClient } from '@aztec/ethereum/types';
18
18
  import { EpochNumber } from '@aztec/foundation/branded-types';
@@ -25,6 +25,7 @@ import type { BootstrapNode } from '@aztec/p2p/bootstrap';
25
25
  import { createBootstrapNodeFromPrivateKey, getBootstrapNodeEnr } from '@aztec/p2p/test-helpers';
26
26
  import { tryStop } from '@aztec/stdlib/interfaces/server';
27
27
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
28
+ import { TopicType } from '@aztec/stdlib/p2p';
28
29
  import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
29
30
  import { ZkPassportProofParams } from '@aztec/stdlib/zkpassport';
30
31
  import { getGenesisValues } from '@aztec/world-state/testing';
@@ -343,7 +344,7 @@ export class P2PNetworkTest {
343
344
  }
344
345
 
345
346
  private async _sendDummyTx(l1Client: ExtendedViemWalletClient) {
346
- const l1TxUtils = createL1TxUtilsFromViemWallet(l1Client);
347
+ const l1TxUtils = createL1TxUtils(l1Client);
347
348
  return await l1TxUtils.sendAndMonitorTransaction({
348
349
  to: l1Client.account!.address,
349
350
  value: 1n,
@@ -406,6 +407,7 @@ export class P2PNetworkTest {
406
407
  expectedNodeCount?: number,
407
408
  timeoutSeconds = 30,
408
409
  checkIntervalSeconds = 0.1,
410
+ topics: TopicType[] = [TopicType.tx],
409
411
  ) {
410
412
  const nodeCount = expectedNodeCount ?? nodes.length;
411
413
  const minPeerCount = nodeCount - 1;
@@ -431,6 +433,29 @@ export class P2PNetworkTest {
431
433
  );
432
434
 
433
435
  this.logger.warn('All nodes connected to P2P mesh');
436
+
437
+ // Wait for GossipSub mesh to form for all specified topics.
438
+ // We only require at least 1 mesh peer per node because GossipSub
439
+ // stops grafting once it reaches Dlo peers and won't fill the mesh to all available peers.
440
+ for (const topic of topics) {
441
+ this.logger.warn(`Waiting for GossipSub mesh to form for ${topic} topic...`);
442
+ await Promise.all(
443
+ nodes.map(async (node, index) => {
444
+ const p2p = node.getP2P();
445
+ await retryUntil(
446
+ async () => {
447
+ const meshPeers = await p2p.getGossipMeshPeerCount(topic);
448
+ this.logger.debug(`Node ${index} has ${meshPeers} gossip mesh peers for ${topic} topic`);
449
+ return meshPeers >= 1 ? true : undefined;
450
+ },
451
+ `Node ${index} to have gossip mesh peers for ${topic} topic`,
452
+ timeoutSeconds,
453
+ checkIntervalSeconds,
454
+ );
455
+ }),
456
+ );
457
+ this.logger.warn(`All nodes have gossip mesh peers for ${topic} topic`);
458
+ }
434
459
  }
435
460
 
436
461
  async teardown() {
@@ -3,24 +3,24 @@ import { createLogger } from '@aztec/aztec.js/log';
3
3
  import { waitForTx } from '@aztec/aztec.js/node';
4
4
  import { Tx } from '@aztec/aztec.js/tx';
5
5
  import { RollupContract } from '@aztec/ethereum/contracts';
6
- import { SlotNumber } from '@aztec/foundation/branded-types';
6
+ import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
7
7
  import { timesAsync } from '@aztec/foundation/collection';
8
8
  import { retryUntil } from '@aztec/foundation/retry';
9
9
 
10
- import { jest } from '@jest/globals';
10
+ import { expect, jest } from '@jest/globals';
11
11
  import fs from 'fs';
12
12
  import os from 'os';
13
13
  import path from 'path';
14
14
 
15
- import { shouldCollectMetrics } from '../../fixtures/fixtures.js';
15
+ import { getBootNodeUdpPort, shouldCollectMetrics } from '../../fixtures/fixtures.js';
16
16
  import { createNodes } from '../../fixtures/setup_p2p_test.js';
17
- import { P2PNetworkTest, SHORTENED_BLOCK_TIME_CONFIG_NO_PRUNES, WAIT_FOR_TX_TIMEOUT } from '../p2p_network.js';
17
+ import { P2PNetworkTest, WAIT_FOR_TX_TIMEOUT } from '../p2p_network.js';
18
18
  import { prepareTransactions } from '../shared.js';
19
19
 
20
20
  // Don't set this to a higher value than 9 because each node will use a different L1 publisher account and anvil seeds
21
21
  export const NUM_VALIDATORS = 6;
22
- export const NUM_TXS_PER_NODE = 2;
23
- export const BOOT_NODE_UDP_PORT = 4500;
22
+ export const NUM_TXS_PER_NODE = 4;
23
+ export const BOOT_NODE_UDP_PORT = getBootNodeUdpPort();
24
24
 
25
25
  export const createReqrespDataDir = () => fs.mkdtempSync(path.join(os.tmpdir(), 'reqresp-'));
26
26
 
@@ -38,8 +38,14 @@ export async function createReqrespTest(options: ReqrespOptions = {}): Promise<P
38
38
  // To collect metrics - run in aztec-packages `docker compose --profile metrics up`
39
39
  metricsPort: shouldCollectMetrics(),
40
40
  initialConfig: {
41
- ...SHORTENED_BLOCK_TIME_CONFIG_NO_PRUNES,
42
- aztecSlotDuration: 24,
41
+ ethereumSlotDuration: 8,
42
+ aztecSlotDuration: 36,
43
+ blockDurationMs: 6000,
44
+ l1PublishingTime: 8,
45
+ minTxsPerBlock: 1,
46
+ maxTxsPerBlock: 2,
47
+ enforceTimeTable: true,
48
+ aztecProofSubmissionEpochs: 1024, // effectively do not reorg
43
49
  ...(disableStatusHandshake ? { p2pDisableStatusHandshake: true } : {}),
44
50
  listenAddress: '127.0.0.1',
45
51
  aztecEpochDuration: 64, // stable committee
@@ -170,6 +176,28 @@ export async function runReqrespTxTest(params: {
170
176
 
171
177
  t.logger.info('All transactions mined');
172
178
 
179
+ // Assert that multiple blocks were built for at least one slot
180
+ t.logger.info('Verifying multiple blocks for at least one checkpoint');
181
+ const checkpoints = await nodes[0].getCheckpoints(CheckpointNumber(1), 50);
182
+ expect(checkpoints.length).toBeGreaterThan(0);
183
+
184
+ let mbpsFound = false;
185
+ let expectedBlockNumber = checkpoints[0].checkpoint.blocks[0].number;
186
+
187
+ for (const published of checkpoints) {
188
+ const blockCount = published.checkpoint.blocks.length;
189
+ mbpsFound = mbpsFound || blockCount >= 2;
190
+
191
+ for (let i = 0; i < blockCount; i++) {
192
+ const block = published.checkpoint.blocks[i];
193
+ expect(block.indexWithinCheckpoint).toBe(i);
194
+ expect(block.checkpointNumber).toBe(published.checkpoint.number);
195
+ expect(block.number).toBe(expectedBlockNumber);
196
+ expectedBlockNumber++;
197
+ }
198
+ }
199
+
200
+ expect(mbpsFound).toBe(true);
173
201
  return nodes;
174
202
  }
175
203
 
@@ -6,12 +6,13 @@ import { Fr } from '@aztec/aztec.js/fields';
6
6
  import type { Logger } from '@aztec/aztec.js/log';
7
7
  import { TxHash } from '@aztec/aztec.js/tx';
8
8
  import type { RollupCheatCodes } from '@aztec/aztec/testing';
9
+ import type { EpochCacheInterface } from '@aztec/epoch-cache';
9
10
  import type {
10
11
  EmpireSlashingProposerContract,
11
12
  RollupContract,
12
13
  TallySlashingProposerContract,
13
14
  } from '@aztec/ethereum/contracts';
14
- import { EpochNumber } from '@aztec/foundation/branded-types';
15
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
15
16
  import { timesAsync, unique } from '@aztec/foundation/collection';
16
17
  import { EthAddress } from '@aztec/foundation/eth-address';
17
18
  import { retryUntil } from '@aztec/foundation/retry';
@@ -150,6 +151,48 @@ export async function awaitCommitteeExists({
150
151
  return committee!.map(c => c.toString() as `0x${string}`);
151
152
  }
152
153
 
154
+ /**
155
+ * Advance epochs until we find one where the target proposer is selected for at least one slot.
156
+ * With N validators and M slots per epoch, a specific proposer may not be selected in any given epoch.
157
+ * For example, with 4 validators and 2 slots/epoch, there is about a 44% chance per epoch.
158
+ */
159
+ export async function awaitEpochWithProposer({
160
+ epochCache,
161
+ cheatCodes,
162
+ targetProposer,
163
+ logger,
164
+ maxAttempts = 20,
165
+ }: {
166
+ epochCache: EpochCacheInterface;
167
+ cheatCodes: RollupCheatCodes;
168
+ targetProposer: EthAddress;
169
+ logger: Logger;
170
+ maxAttempts?: number;
171
+ }): Promise<void> {
172
+ const { epochDuration } = await cheatCodes.getConfig();
173
+
174
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
175
+ const currentEpoch = await cheatCodes.getEpoch();
176
+ const startSlot = Number(currentEpoch) * Number(epochDuration);
177
+ const endSlot = startSlot + Number(epochDuration);
178
+
179
+ logger.info(`Checking epoch ${currentEpoch} (slots ${startSlot}-${endSlot - 1}) for proposer ${targetProposer}`);
180
+
181
+ for (let s = startSlot; s < endSlot; s++) {
182
+ const proposer = await epochCache.getProposerAttesterAddressInSlot(SlotNumber(s));
183
+ if (proposer && proposer.equals(targetProposer)) {
184
+ logger.warn(`Found target proposer ${targetProposer} in slot ${s} of epoch ${currentEpoch}`);
185
+ return;
186
+ }
187
+ }
188
+
189
+ logger.info(`Target proposer not found in epoch ${currentEpoch}, advancing to next epoch`);
190
+ await cheatCodes.advanceToNextEpoch();
191
+ }
192
+
193
+ throw new Error(`Target proposer ${targetProposer} not found in any slot after ${maxAttempts} epoch attempts`);
194
+ }
195
+
153
196
  export async function awaitOffenseDetected({
154
197
  logger,
155
198
  nodeAdmin,
@@ -26,7 +26,7 @@ export class TokenContractTest {
26
26
  node!: AztecNode;
27
27
 
28
28
  badAccount!: InvalidAccountContract;
29
- proxy!: GenericProxyContract;
29
+ authwitProxy!: GenericProxyContract;
30
30
  wallet!: TestWallet;
31
31
  adminAddress!: AztecAddress;
32
32
  account1Address!: AztecAddress;
@@ -98,8 +98,8 @@ export class TokenContractTest {
98
98
  // (so their notes are in scope), but msg_sender in the target must differ from the note owner
99
99
  // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender.
100
100
  this.logger.verbose(`Deploying generic proxy...`);
101
- this.proxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress });
102
- this.logger.verbose(`Deployed to ${this.proxy.address}.`);
101
+ this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress });
102
+ this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`);
103
103
 
104
104
  this.tokenSim = new TokenSimulator(this.asset, this.wallet, this.adminAddress, this.logger, [
105
105
  this.adminAddress,
@@ -0,0 +1,50 @@
1
+ import type {
2
+ ContractFunctionInteraction,
3
+ SendInteractionOptions,
4
+ SimulateInteractionOptions,
5
+ } from '@aztec/aztec.js/contracts';
6
+ import type { GenericProxyContract } from '@aztec/noir-test-contracts.js/GenericProxy';
7
+
8
+ /**
9
+ * Builds a proxy forwarding call from an action, selecting the right forward_private_N method.
10
+ */
11
+ async function buildProxyCall(proxy: GenericProxyContract, action: ContractFunctionInteraction) {
12
+ const call = await action.getFunctionCall();
13
+ const argCount = call.args.length;
14
+ if (argCount === 2) {
15
+ return proxy.methods.forward_private_2(call.to, call.selector, call.args);
16
+ } else if (argCount === 3) {
17
+ return proxy.methods.forward_private_3(call.to, call.selector, call.args);
18
+ } else if (argCount === 4) {
19
+ return proxy.methods.forward_private_4(call.to, call.selector, call.args);
20
+ }
21
+ throw new Error(`No forward_private_${argCount} method on proxy`);
22
+ }
23
+
24
+ /**
25
+ * Sends a contract call through the authwit proxy.
26
+ * The proxy becomes msg_sender in the target (triggering authwit validation),
27
+ * while the actual tx sender retains their keys in scope.
28
+ */
29
+ export async function sendThroughAuthwitProxy(
30
+ proxy: GenericProxyContract,
31
+ action: ContractFunctionInteraction,
32
+ options: SendInteractionOptions,
33
+ ) {
34
+ const proxyCall = await buildProxyCall(proxy, action);
35
+ return proxyCall.send(options);
36
+ }
37
+
38
+ /**
39
+ * Simulates a contract call through the authwit proxy.
40
+ * The proxy becomes msg_sender in the target (triggering authwit validation),
41
+ * while the actual tx sender retains their keys in scope.
42
+ */
43
+ export async function simulateThroughAuthwitProxy(
44
+ proxy: GenericProxyContract,
45
+ action: ContractFunctionInteraction,
46
+ options: SimulateInteractionOptions,
47
+ ) {
48
+ const proxyCall = await buildProxyCall(proxy, action);
49
+ return proxyCall.simulate(options);
50
+ }