@aztec/end-to-end 0.0.1-commit.c7c42ec → 0.0.1-commit.f295ac2

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 (111) hide show
  1. package/dest/bench/client_flows/benchmark.d.ts +3 -2
  2. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  3. package/dest/bench/client_flows/benchmark.js +21 -1
  4. package/dest/bench/client_flows/client_flows_benchmark.d.ts +12 -13
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  6. package/dest/bench/client_flows/client_flows_benchmark.js +105 -135
  7. package/dest/bench/client_flows/data_extractor.js +3 -1
  8. package/dest/bench/utils.d.ts +5 -5
  9. package/dest/bench/utils.d.ts.map +1 -1
  10. package/dest/bench/utils.js +8 -5
  11. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +6 -7
  12. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  13. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +98 -113
  14. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -10
  15. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  16. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +89 -70
  17. package/dest/e2e_deploy_contract/deploy_test.d.ts +4 -3
  18. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  19. package/dest/e2e_deploy_contract/deploy_test.js +18 -13
  20. package/dest/e2e_epochs/epochs_test.js +2 -2
  21. package/dest/e2e_fees/bridging_race.notest.js +2 -4
  22. package/dest/e2e_fees/fees_test.d.ts +13 -13
  23. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  24. package/dest/e2e_fees/fees_test.js +123 -141
  25. package/dest/e2e_l1_publisher/write_json.d.ts +3 -3
  26. package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
  27. package/dest/e2e_l1_publisher/write_json.js +19 -15
  28. package/dest/e2e_nested_contract/nested_contract_test.d.ts +6 -9
  29. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  30. package/dest/e2e_nested_contract/nested_contract_test.js +32 -40
  31. package/dest/e2e_p2p/inactivity_slash_test.d.ts +3 -3
  32. package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
  33. package/dest/e2e_p2p/inactivity_slash_test.js +3 -3
  34. package/dest/e2e_p2p/p2p_network.d.ts +7 -6
  35. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  36. package/dest/e2e_p2p/p2p_network.js +107 -104
  37. package/dest/e2e_p2p/shared.d.ts +1 -1
  38. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  39. package/dest/e2e_p2p/shared.js +4 -4
  40. package/dest/e2e_token_contract/token_contract_test.d.ts +16 -9
  41. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  42. package/dest/e2e_token_contract/token_contract_test.js +90 -92
  43. package/dest/fixtures/e2e_prover_test.d.ts +8 -14
  44. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  45. package/dest/fixtures/e2e_prover_test.js +83 -95
  46. package/dest/fixtures/setup.d.ts +216 -0
  47. package/dest/fixtures/setup.d.ts.map +1 -0
  48. package/dest/fixtures/setup.js +684 -0
  49. package/dest/fixtures/utils.d.ts +5 -194
  50. package/dest/fixtures/utils.d.ts.map +1 -1
  51. package/dest/fixtures/utils.js +4 -619
  52. package/dest/quality_of_service/grafana_client.d.ts +41 -0
  53. package/dest/quality_of_service/grafana_client.d.ts.map +1 -0
  54. package/dest/quality_of_service/{alert_checker.js → grafana_client.js} +1 -1
  55. package/dest/quality_of_service/prometheus_client.d.ts +38 -0
  56. package/dest/quality_of_service/prometheus_client.d.ts.map +1 -0
  57. package/dest/quality_of_service/prometheus_client.js +67 -0
  58. package/dest/shared/cross_chain_test_harness.d.ts +14 -3
  59. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  60. package/dest/shared/cross_chain_test_harness.js +2 -2
  61. package/dest/shared/gas_portal_test_harness.d.ts +11 -1
  62. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  63. package/dest/shared/index.d.ts +2 -2
  64. package/dest/shared/index.d.ts.map +1 -1
  65. package/dest/shared/uniswap_l1_l2.d.ts +3 -28
  66. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  67. package/dest/shared/uniswap_l1_l2.js +39 -21
  68. package/dest/simulators/lending_simulator.d.ts +5 -1
  69. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  70. package/dest/spartan/setup_test_wallets.d.ts +4 -3
  71. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  72. package/dest/spartan/tx_metrics.d.ts +4 -1
  73. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  74. package/dest/spartan/tx_metrics.js +24 -1
  75. package/dest/spartan/utils.d.ts +12 -5
  76. package/dest/spartan/utils.d.ts.map +1 -1
  77. package/dest/spartan/utils.js +123 -73
  78. package/package.json +40 -39
  79. package/src/bench/client_flows/benchmark.ts +24 -2
  80. package/src/bench/client_flows/client_flows_benchmark.ts +143 -196
  81. package/src/bench/client_flows/data_extractor.ts +1 -1
  82. package/src/bench/utils.ts +11 -7
  83. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +107 -142
  84. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +127 -116
  85. package/src/e2e_deploy_contract/deploy_test.ts +21 -14
  86. package/src/e2e_epochs/epochs_test.ts +2 -2
  87. package/src/e2e_fees/bridging_race.notest.ts +2 -5
  88. package/src/e2e_fees/fees_test.ts +172 -216
  89. package/src/e2e_l1_publisher/write_json.ts +22 -17
  90. package/src/e2e_nested_contract/nested_contract_test.ts +35 -56
  91. package/src/e2e_p2p/inactivity_slash_test.ts +5 -5
  92. package/src/e2e_p2p/p2p_network.ts +166 -168
  93. package/src/e2e_p2p/shared.ts +6 -5
  94. package/src/e2e_token_contract/token_contract_test.ts +105 -118
  95. package/src/fixtures/e2e_prover_test.ts +107 -137
  96. package/src/fixtures/setup.ts +1010 -0
  97. package/src/fixtures/utils.ts +27 -907
  98. package/src/quality_of_service/{alert_checker.ts → grafana_client.ts} +1 -1
  99. package/src/quality_of_service/prometheus_client.ts +113 -0
  100. package/src/shared/cross_chain_test_harness.ts +3 -9
  101. package/src/shared/index.ts +1 -1
  102. package/src/shared/uniswap_l1_l2.ts +46 -58
  103. package/src/spartan/setup_test_wallets.ts +7 -1
  104. package/src/spartan/tx_metrics.ts +27 -4
  105. package/src/spartan/utils.ts +112 -24
  106. package/dest/fixtures/snapshot_manager.d.ts +0 -93
  107. package/dest/fixtures/snapshot_manager.d.ts.map +0 -1
  108. package/dest/fixtures/snapshot_manager.js +0 -493
  109. package/dest/quality_of_service/alert_checker.d.ts +0 -41
  110. package/dest/quality_of_service/alert_checker.d.ts.map +0 -1
  111. package/src/fixtures/snapshot_manager.ts +0 -651
@@ -25,7 +25,7 @@ const DEFAULT_CONFIG: AlertCheckerConfig = {
25
25
  grafanaCredentials: 'admin:admin',
26
26
  };
27
27
 
28
- export class AlertChecker {
28
+ export class GrafanaClient {
29
29
  private config: AlertCheckerConfig;
30
30
  private logger: Logger;
31
31
 
@@ -0,0 +1,113 @@
1
+ export type PromteheusClientOptions = {
2
+ server: URL;
3
+ };
4
+
5
+ export class PrometheusClient {
6
+ constructor(
7
+ private config: PromteheusClientOptions,
8
+ private httpClient: typeof fetch = fetch,
9
+ ) {}
10
+
11
+ public async querySingleValue(query: string, time = new Date()): Promise<number> {
12
+ const resp = await this.queryRaw(query, time);
13
+ if (resp.status === 'success') {
14
+ if (resp.data.resultType === 'vector') {
15
+ if (resp.data.result.length === 0) {
16
+ return 0;
17
+ }
18
+ const [_, value] = resp.data.result[0].value;
19
+ return parseFloat(value);
20
+ }
21
+ }
22
+
23
+ throw new TypeError('Unsupported response body', { cause: JSON.stringify(resp) });
24
+ }
25
+
26
+ public queryRaw(query: string, time = new Date()): Promise<PrometheusResponse> {
27
+ const searchParams = new URLSearchParams();
28
+ searchParams.set('query', query);
29
+ searchParams.set('time', String(Math.trunc(time.getTime() / 1000)));
30
+ searchParams.set('limit', '10');
31
+
32
+ return this.callPrometheus('query', searchParams);
33
+ }
34
+
35
+ public queryRangeRaw(
36
+ query: string,
37
+ step: PrometheusDuration,
38
+ start: Date,
39
+ end = new Date(),
40
+ ): Promise<PrometheusResponse> {
41
+ const searchParams = new URLSearchParams();
42
+ searchParams.set('query', query);
43
+ searchParams.set('step', step);
44
+ searchParams.set('start', String(Math.trunc(start.getTime() / 1000)));
45
+ searchParams.set('end', String(Math.trunc(end.getTime() / 1000)));
46
+ searchParams.set('limit', '10');
47
+
48
+ return this.callPrometheus('query_range', searchParams);
49
+ }
50
+
51
+ private async callPrometheus(api: string, searchParams: URLSearchParams): Promise<PrometheusResponse> {
52
+ const url = new URL('api/v1/' + api, this.config.server);
53
+ for (const [name, value] of searchParams) {
54
+ url.searchParams.append(name, value);
55
+ }
56
+
57
+ const resp = await this.httpClient(url, { method: 'GET' });
58
+ if (!resp.ok || resp.status !== 200) {
59
+ throw new Error('Invalid HTTP response from Prometheus', {
60
+ cause: {
61
+ url,
62
+ status: resp.status,
63
+ statusText: resp.statusText,
64
+ },
65
+ });
66
+ }
67
+
68
+ const body = await resp.json();
69
+ if ('status' in body && (body.status === 'error' || body.status === 'success')) {
70
+ return body;
71
+ }
72
+
73
+ throw new Error('Invalid response from Prometheus', {
74
+ cause: {
75
+ url,
76
+ body,
77
+ },
78
+ });
79
+ }
80
+ }
81
+
82
+ export type PrometheusDuration = `${number}s` | `${number}m` | `${number}h`;
83
+
84
+ export type PrometheusData =
85
+ | {
86
+ resultType: 'vector';
87
+ result: Array<{
88
+ metric: unknown;
89
+ value: [unixTimestamp: number, value: string];
90
+ }>;
91
+ }
92
+ | {
93
+ resultType: 'matrix';
94
+ result: Array<{
95
+ metric: unknown;
96
+ values: [unixTimestamp: number, value: string];
97
+ }>;
98
+ }
99
+ | {
100
+ resultType: 'scalar' | 'string';
101
+ result: unknown;
102
+ };
103
+
104
+ export type PrometheusResponse =
105
+ | {
106
+ status: 'error';
107
+ errorType: string;
108
+ error: string;
109
+ }
110
+ | {
111
+ status: 'success';
112
+ data: PrometheusData;
113
+ };
@@ -16,7 +16,7 @@ import type { Wallet } from '@aztec/aztec.js/wallet';
16
16
  import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
17
17
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
18
18
  import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
19
- import { BlockNumber } from '@aztec/foundation/branded-types';
19
+ import { EpochNumber } from '@aztec/foundation/branded-types';
20
20
  import { retryUntil } from '@aztec/foundation/retry';
21
21
  import type { FieldsOf } from '@aztec/foundation/types';
22
22
  import { TestERC20Abi, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts';
@@ -324,17 +324,11 @@ export class CrossChainTestHarness {
324
324
 
325
325
  withdrawFundsFromBridgeOnL1(
326
326
  amount: bigint,
327
- blockNumber: BlockNumber,
327
+ epochNumber: EpochNumber,
328
328
  messageIndex: bigint,
329
329
  siblingPath: SiblingPath<number>,
330
330
  ) {
331
- return this.l1TokenPortalManager.withdrawFunds(
332
- amount,
333
- this.ethAccount,
334
- BigInt(blockNumber),
335
- messageIndex,
336
- siblingPath,
337
- );
331
+ return this.l1TokenPortalManager.withdrawFunds(amount, this.ethAccount, epochNumber, messageIndex, siblingPath);
338
332
  }
339
333
 
340
334
  async transferToPrivateOnL2(shieldAmount: bigint) {
@@ -1 +1 @@
1
- export { uniswapL1L2TestSuite, type UniswapSetupContext } from './uniswap_l1_l2.js';
1
+ export { uniswapL1L2TestSuite } from './uniswap_l1_l2.js';
@@ -1,5 +1,6 @@
1
1
  import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses';
2
2
  import { computeAuthWitMessageHash } from '@aztec/aztec.js/authorization';
3
+ import { waitForProven } from '@aztec/aztec.js/contracts';
3
4
  import { generateClaimSecret } from '@aztec/aztec.js/ethereum';
4
5
  import { Fr } from '@aztec/aztec.js/fields';
5
6
  import type { Logger } from '@aztec/aztec.js/log';
@@ -10,6 +11,7 @@ import type { DeployAztecL1ContractsReturnType } from '@aztec/ethereum/deploy-az
10
11
  import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
11
12
  import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
12
13
  import { extractEvent } from '@aztec/ethereum/utils';
14
+ import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
13
15
  import { sha256ToField } from '@aztec/foundation/crypto/sha256';
14
16
  import { InboxAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts';
15
17
  import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap';
@@ -20,7 +22,7 @@ import type { TestWallet } from '@aztec/test-wallet/server';
20
22
  import { jest } from '@jest/globals';
21
23
  import { type GetContractReturnType, getContract, parseEther, toFunctionSelector } from 'viem';
22
24
 
23
- import { ensureAccountContractsPublished } from '../fixtures/utils.js';
25
+ import { type EndToEndContext, ensureAccountContractsPublished } from '../fixtures/utils.js';
24
26
  import { CrossChainTestHarness } from './cross_chain_test_harness.js';
25
27
 
26
28
  // PSA: This tests works on forked mainnet. There is a dump of the data in `dumpedState` such that we
@@ -32,28 +34,8 @@ import { CrossChainTestHarness } from './cross_chain_test_harness.js';
32
34
 
33
35
  const TIMEOUT = 360_000;
34
36
 
35
- /** Objects to be returned by the uniswap setup function */
36
- export type UniswapSetupContext = {
37
- /** Aztec Node instance */
38
- aztecNode: AztecNode;
39
- /** Logger instance named as the current test. */
40
- logger: Logger;
41
- /** The L1 wallet client, extended with public actions. */
42
- l1Client: ExtendedViemWalletClient;
43
- /** The wallet. */
44
- wallet: TestWallet;
45
- /** The owner address. */
46
- ownerAddress: AztecAddress;
47
- /** The sponsor wallet. */
48
- sponsorAddress: AztecAddress;
49
- /** */
50
- deployL1ContractsValues: DeployAztecL1ContractsReturnType;
51
- /** Cheat codes instance. */
52
- cheatCodes: CheatCodes;
53
- };
54
-
55
37
  export const uniswapL1L2TestSuite = (
56
- setup: () => Promise<UniswapSetupContext>,
38
+ setup: () => Promise<EndToEndContext>,
57
39
  cleanup: () => Promise<void>,
58
40
  expectedForkBlockNumber = 17514288,
59
41
  ) => {
@@ -90,8 +72,19 @@ export const uniswapL1L2TestSuite = (
90
72
  let cheatCodes: CheatCodes;
91
73
  let version: number;
92
74
  beforeAll(async () => {
93
- ({ aztecNode, logger, l1Client, wallet, ownerAddress, sponsorAddress, deployL1ContractsValues, cheatCodes } =
94
- await setup());
75
+ const t = await setup();
76
+ ({
77
+ aztecNode,
78
+ logger,
79
+ deployL1ContractsValues,
80
+ cheatCodes,
81
+ wallet,
82
+ accounts: [ownerAddress, sponsorAddress],
83
+ } = t);
84
+
85
+ l1Client = deployL1ContractsValues.l1Client;
86
+
87
+ t.watcher?.setIsMarkingAsProven(false);
95
88
 
96
89
  if (Number(await l1Client.getBlockNumber()) < expectedForkBlockNumber) {
97
90
  throw new Error('This test must be run on a fork of mainnet with the expected fork block');
@@ -259,24 +252,19 @@ export const uniswapL1L2TestSuite = (
259
252
  // ensure that uniswap contract didn't eat the funds.
260
253
  await wethCrossChainHarness.expectPublicBalanceOnL2(uniswapL2Contract.address, 0n);
261
254
 
262
- // Since the outbox is only consumable when the block is proven, we need to set the block to be proven
263
- await cheatCodes.rollup.markAsProven(await rollup.getCheckpointNumber());
255
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
256
+ const checkpointNumber = CheckpointNumber.fromBlockNumber(l2UniswapInteractionReceipt.blockNumber!);
257
+ const epoch = await rollup.getEpochNumberForCheckpoint(checkpointNumber);
258
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
259
+ await waitForProven(aztecNode, l2UniswapInteractionReceipt, { provenTimeout: 300 });
264
260
 
265
261
  // 5. Consume L2 to L1 message by calling uniswapPortal.swap_private()
266
262
  logger.info('Execute withdraw and swap on the uniswapPortal!');
267
263
  const daiL1BalanceOfPortalBeforeSwap = await daiCrossChainHarness.getL1BalanceOf(
268
264
  daiCrossChainHarness.tokenPortalAddress,
269
265
  );
270
- const swapResult = await computeL2ToL1MembershipWitness(
271
- aztecNode,
272
- l2UniswapInteractionReceipt.blockNumber!,
273
- swapPrivateLeaf,
274
- );
275
- const withdrawResult = await computeL2ToL1MembershipWitness(
276
- aztecNode,
277
- l2UniswapInteractionReceipt.blockNumber!,
278
- withdrawLeaf,
279
- );
266
+ const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPrivateLeaf);
267
+ const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf);
280
268
 
281
269
  const swapPrivateL2MessageIndex = swapResult!.leafIndex;
282
270
  const swapPrivateSiblingPath = swapResult!.siblingPath;
@@ -285,7 +273,7 @@ export const uniswapL1L2TestSuite = (
285
273
  const withdrawSiblingPath = withdrawResult!.siblingPath;
286
274
 
287
275
  const withdrawMessageMetadata = {
288
- _checkpointNumber: BigInt(l2UniswapInteractionReceipt.blockNumber!),
276
+ _epoch: BigInt(epoch),
289
277
  _leafIndex: BigInt(withdrawL2MessageIndex),
290
278
  _path: withdrawSiblingPath
291
279
  .toBufferArray()
@@ -293,7 +281,7 @@ export const uniswapL1L2TestSuite = (
293
281
  };
294
282
 
295
283
  const swapPrivateMessageMetadata = {
296
- _checkpointNumber: BigInt(l2UniswapInteractionReceipt.blockNumber!),
284
+ _epoch: BigInt(epoch),
297
285
  _leafIndex: BigInt(swapPrivateL2MessageIndex),
298
286
  _path: swapPrivateSiblingPath
299
287
  .toBufferArray()
@@ -504,7 +492,7 @@ export const uniswapL1L2TestSuite = (
504
492
  // );
505
493
 
506
494
  // const withdrawMessageMetadata = {
507
- // _checkpointNumber: BigInt(uniswapL2Interaction.blockNumber!),
495
+ // _epoch: epoch,
508
496
  // _leafIndex: BigInt(withdrawL2MessageIndex),
509
497
  // _path: withdrawSiblingPath
510
498
  // .toBufferArray()
@@ -512,7 +500,7 @@ export const uniswapL1L2TestSuite = (
512
500
  // };
513
501
 
514
502
  // const swapPrivateMessageMetadata = {
515
- // _checkpointNumber: BigInt(uniswapL2Interaction.blockNumber!),
503
+ // _epoch: epoch,
516
504
  // _leafIndex: BigInt(swapPrivateL2MessageIndex),
517
505
  // _path: swapPrivateSiblingPath
518
506
  // .toBufferArray()
@@ -856,12 +844,11 @@ export const uniswapL1L2TestSuite = (
856
844
  chainId: new Fr(l1Client.chain.id),
857
845
  });
858
846
 
859
- const swapResult = await computeL2ToL1MembershipWitness(aztecNode, withdrawReceipt.blockNumber!, swapPrivateLeaf);
860
- const withdrawResult = await computeL2ToL1MembershipWitness(
861
- aztecNode,
862
- withdrawReceipt.blockNumber!,
863
- withdrawLeaf,
847
+ const epoch = await rollup.getEpochNumberForCheckpoint(
848
+ CheckpointNumber.fromBlockNumber(withdrawReceipt.blockNumber!),
864
849
  );
850
+ const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPrivateLeaf);
851
+ const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf);
865
852
 
866
853
  const swapPrivateL2MessageIndex = swapResult!.leafIndex;
867
854
  const swapPrivateSiblingPath = swapResult!.siblingPath;
@@ -870,7 +857,7 @@ export const uniswapL1L2TestSuite = (
870
857
  const withdrawSiblingPath = withdrawResult!.siblingPath;
871
858
 
872
859
  const withdrawMessageMetadata = {
873
- _checkpointNumber: BigInt(withdrawReceipt.blockNumber!),
860
+ _epoch: BigInt(epoch),
874
861
  _leafIndex: BigInt(withdrawL2MessageIndex),
875
862
  _path: withdrawSiblingPath
876
863
  .toBufferArray()
@@ -878,7 +865,7 @@ export const uniswapL1L2TestSuite = (
878
865
  };
879
866
 
880
867
  const swapPrivateMessageMetadata = {
881
- _checkpointNumber: BigInt(withdrawReceipt.blockNumber!),
868
+ _epoch: BigInt(epoch),
882
869
  _leafIndex: BigInt(swapPrivateL2MessageIndex),
883
870
  _path: swapPrivateSiblingPath
884
871
  .toBufferArray()
@@ -888,8 +875,9 @@ export const uniswapL1L2TestSuite = (
888
875
  // ensure that user's funds were burnt
889
876
  await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge);
890
877
 
891
- // Since the outbox is only consumable when the block is proven, we need to set the block to be proven
892
- await cheatCodes.rollup.markAsProven(await rollup.getCheckpointNumber());
878
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
879
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
880
+ await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
893
881
 
894
882
  // On L1 call swap_public!
895
883
  logger.info('call swap_public on L1');
@@ -991,12 +979,11 @@ export const uniswapL1L2TestSuite = (
991
979
  chainId: new Fr(l1Client.chain.id),
992
980
  });
993
981
 
994
- const swapResult = await computeL2ToL1MembershipWitness(aztecNode, withdrawReceipt.blockNumber!, swapPublicLeaf);
995
- const withdrawResult = await computeL2ToL1MembershipWitness(
996
- aztecNode,
997
- withdrawReceipt.blockNumber!,
998
- withdrawLeaf,
982
+ const epoch = await rollup.getEpochNumberForCheckpoint(
983
+ CheckpointNumber.fromBlockNumber(withdrawReceipt.blockNumber!),
999
984
  );
985
+ const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPublicLeaf);
986
+ const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf);
1000
987
 
1001
988
  const swapPublicL2MessageIndex = swapResult!.leafIndex;
1002
989
  const swapPublicSiblingPath = swapResult!.siblingPath;
@@ -1005,7 +992,7 @@ export const uniswapL1L2TestSuite = (
1005
992
  const withdrawSiblingPath = withdrawResult!.siblingPath;
1006
993
 
1007
994
  const withdrawMessageMetadata = {
1008
- _checkpointNumber: BigInt(withdrawReceipt.blockNumber!),
995
+ _epoch: BigInt(epoch),
1009
996
  _leafIndex: BigInt(withdrawL2MessageIndex),
1010
997
  _path: withdrawSiblingPath
1011
998
  .toBufferArray()
@@ -1013,7 +1000,7 @@ export const uniswapL1L2TestSuite = (
1013
1000
  };
1014
1001
 
1015
1002
  const swapPublicMessageMetadata = {
1016
- _checkpointNumber: BigInt(withdrawReceipt.blockNumber!),
1003
+ _epoch: BigInt(epoch),
1017
1004
  _leafIndex: BigInt(swapPublicL2MessageIndex),
1018
1005
  _path: swapPublicSiblingPath
1019
1006
  .toBufferArray()
@@ -1023,8 +1010,9 @@ export const uniswapL1L2TestSuite = (
1023
1010
  // check weth balance of owner on L2 (we first bridged `wethAmountToBridge` into L2 and now withdrew it!)
1024
1011
  await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, 0n);
1025
1012
 
1026
- // Since the outbox is only consumable when the block is proven, we need to set the block to be proven
1027
- await cheatCodes.rollup.markAsProven(await rollup.getCheckpointNumber());
1013
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
1014
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
1015
+ await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
1028
1016
 
1029
1017
  // Call swap_private on L1
1030
1018
  logger.info('Execute withdraw and swap on the uniswapPortal!');
@@ -315,11 +315,17 @@ export async function performTransfers({
315
315
  }
316
316
  }
317
317
 
318
+ export type WalletWrapper = {
319
+ wallet: TestWallet;
320
+ aztecNode: AztecNode;
321
+ cleanup: () => Promise<void>;
322
+ };
323
+
318
324
  export async function createWalletAndAztecNodeClient(
319
325
  nodeUrl: string,
320
326
  proverEnabled: boolean,
321
327
  logger: Logger,
322
- ): Promise<{ wallet: TestWallet; aztecNode: AztecNode; cleanup: () => Promise<void> }> {
328
+ ): Promise<WalletWrapper> {
323
329
  const aztecNode = createAztecNodeClient(nodeUrl);
324
330
  const [bbConfig, acvmConfig] = await Promise.all([getBBConfig(logger), getACVMConfig(logger)]);
325
331
  const pxeConfig = {
@@ -1,5 +1,6 @@
1
1
  import type { AztecNode } from '@aztec/aztec.js/node';
2
- import type { L2Block } from '@aztec/stdlib/block';
2
+ import type { L2BlockNew } from '@aztec/stdlib/block';
3
+ import type { TopicType } from '@aztec/stdlib/p2p';
3
4
  import { Tx, type TxReceipt, TxStatus } from '@aztec/stdlib/tx';
4
5
 
5
6
  import { createHistogram } from 'perf_hooks';
@@ -19,7 +20,9 @@ export type TxInclusionData = {
19
20
  export class TxInclusionMetrics {
20
21
  private data = new Map<string, TxInclusionData>();
21
22
  private groups = new Set<string>();
22
- private blocks = new Map<number, Promise<L2Block>>();
23
+ private blocks = new Map<number, Promise<L2BlockNew | undefined>>();
24
+
25
+ private p2pGossipLatencyByTopic: Partial<Record<TopicType, { p50: number; p95: number }>> = {};
23
26
 
24
27
  constructor(private aztecNode: AztecNode) {}
25
28
 
@@ -48,10 +51,13 @@ export class TxInclusionMetrics {
48
51
  }
49
52
 
50
53
  if (!this.blocks.has(blockNumber)) {
51
- this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber) as Promise<L2Block>);
54
+ this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber));
52
55
  }
53
56
 
54
57
  const block = await this.blocks.get(blockNumber)!;
58
+ if (!block) {
59
+ return;
60
+ }
55
61
  const data = this.data.get(txHash.toString())!;
56
62
  data.blocknumber = blockNumber;
57
63
  data.minedAt = Number(block.header.globalVariables.timestamp);
@@ -71,7 +77,7 @@ export class TxInclusionMetrics {
71
77
  } {
72
78
  const histogram = createHistogram({});
73
79
  for (const tx of this.data.values()) {
74
- if (!tx.blocknumber || tx.group !== group) {
80
+ if (!tx.blocknumber || tx.group !== group || tx.minedAt === -1) {
75
81
  continue;
76
82
  }
77
83
 
@@ -101,6 +107,10 @@ export class TxInclusionMetrics {
101
107
  };
102
108
  }
103
109
 
110
+ public recordP2PGossipLatency(topicName: TopicType, p50: number, p95: number): void {
111
+ this.p2pGossipLatencyByTopic[topicName] = { p50, p95 };
112
+ }
113
+
104
114
  toGithubActionBenchmarkJSON(): Array<{ name: string; unit: string; value: number; range?: number; extra?: string }> {
105
115
  const data: Array<{ name: string; unit: string; value: number; range?: number; extra?: string }> = [];
106
116
  for (const group of this.groups) {
@@ -125,6 +135,19 @@ export class TxInclusionMetrics {
125
135
  );
126
136
  }
127
137
 
138
+ for (const [topic, { p50, p95 }] of Object.entries(this.p2pGossipLatencyByTopic)) {
139
+ data.push({
140
+ name: `p2p_gossip_latency/${topic}/p50`,
141
+ unit: 'ms',
142
+ value: p50,
143
+ });
144
+ data.push({
145
+ name: `p2p_gossip_latency/${topic}/p95`,
146
+ unit: 'ms',
147
+ value: p95,
148
+ });
149
+ }
150
+
128
151
  return data;
129
152
  }
130
153
  }
@@ -34,6 +34,7 @@ const testConfigSchema = z.object({
34
34
  AZTEC_SLOT_DURATION: z.coerce.number().optional().default(24),
35
35
  AZTEC_EPOCH_DURATION: z.coerce.number().optional().default(32),
36
36
  AZTEC_PROOF_SUBMISSION_WINDOW: z.coerce.number().optional().default(5),
37
+ AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: z.coerce.number().optional().default(2),
37
38
  });
38
39
 
39
40
  export type TestConfig = z.infer<typeof testConfigSchema>;
@@ -117,7 +118,7 @@ export async function startPortForward({
117
118
  );
118
119
 
119
120
  let isResolved = false;
120
- const connected = new Promise<number>(resolve => {
121
+ const connected = new Promise<number>((resolve, reject) => {
121
122
  process.stdout?.on('data', data => {
122
123
  const str = data.toString() as string;
123
124
  if (!isResolved && str.includes('Forwarding from')) {
@@ -125,7 +126,8 @@ export async function startPortForward({
125
126
  logger.debug(`Port forward for ${resource}: ${str}`);
126
127
  const port = str.search(/:\d+/);
127
128
  if (port === -1) {
128
- throw new Error('Port not found in port forward output');
129
+ reject(new Error('Port not found in port forward output'));
130
+ return;
129
131
  }
130
132
  const portNumber = parseInt(str.slice(port + 1));
131
133
  logger.verbose(`Port forwarded for ${resource} at ${portNumber}:${containerPort}`);
@@ -145,17 +147,26 @@ export async function startPortForward({
145
147
  process.on('close', () => {
146
148
  if (!isResolved) {
147
149
  isResolved = true;
148
- logger.warn(`Port forward for ${resource} closed before connection established`);
149
- resolve(0);
150
+ const msg = `Port forward for ${resource} closed before connection established`;
151
+ logger.warn(msg);
152
+ reject(new Error(msg));
150
153
  }
151
154
  });
152
155
  process.on('error', error => {
153
- logger.error(`Port forward for ${resource} error: ${error}`);
154
- resolve(0);
156
+ if (!isResolved) {
157
+ isResolved = true;
158
+ const msg = `Port forward for ${resource} error: ${error}`;
159
+ logger.error(msg);
160
+ reject(new Error(msg));
161
+ }
155
162
  });
156
163
  process.on('exit', code => {
157
- logger.verbose(`Port forward for ${resource} exited with code ${code}`);
158
- resolve(0);
164
+ if (!isResolved) {
165
+ isResolved = true;
166
+ const msg = `Port forward for ${resource} exited with code ${code}`;
167
+ logger.verbose(msg);
168
+ reject(new Error(msg));
169
+ }
159
170
  });
160
171
  });
161
172
 
@@ -197,6 +208,14 @@ export function getExternalIP(namespace: string, serviceName: string): Promise<s
197
208
  return promise;
198
209
  }
199
210
 
211
+ export function startPortForwardForPrometeheus(namespace: string) {
212
+ return startPortForward({
213
+ resource: `svc/${namespace}-prometheus-server`,
214
+ namespace,
215
+ containerPort: 80,
216
+ });
217
+ }
218
+
200
219
  export function startPortForwardForRPC(namespace: string, index = 0) {
201
220
  return startPortForward({
202
221
  resource: `pod/${namespace}-rpc-aztec-node-${index}`,
@@ -1089,24 +1108,93 @@ export async function getL1DeploymentAddresses(env: TestConfig): Promise<L1Contr
1089
1108
  /**
1090
1109
  * Rolls the Aztec pods in the given namespace.
1091
1110
  * @param namespace - The namespace to roll the Aztec pods in.
1092
- * @dev - IMPORTANT: This function DOES NOT delete the underlying PVCs.
1093
- * This means that the pods will be restarted with the same persistent storage.
1094
- * This is useful for testing, but you should be aware of the implications.
1111
+ * @param clearState - If true, also deletes the underlying PVCs to clear persistent storage.
1112
+ * This is required for rollup upgrades where the old state is incompatible with the new rollup.
1113
+ * Defaults to false, which preserves the existing storage.
1095
1114
  */
1096
- export async function rollAztecPods(namespace: string) {
1097
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=boot-node' });
1098
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-node' });
1099
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-broker' });
1100
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-agent' });
1101
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=validator' });
1102
- await deleteResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=pxe' });
1115
+ export async function rollAztecPods(namespace: string, clearState: boolean = false) {
1116
+ // Pod components use 'validator', but StatefulSets and PVCs use 'sequencer-node' for validators
1117
+ const podComponents = ['p2p-bootstrap', 'prover-node', 'prover-broker', 'prover-agent', 'sequencer-node', 'rpc'];
1118
+ const pvcComponents = ['p2p-bootstrap', 'prover-node', 'prover-broker', 'sequencer-node', 'rpc'];
1119
+ // StatefulSet components that need to be scaled down before PVC deletion
1120
+ // Note: validators use 'sequencer-node' as component label, not 'validator'
1121
+ const statefulSetComponents = ['p2p-bootstrap', 'prover-node', 'prover-broker', 'sequencer-node', 'rpc'];
1122
+
1123
+ if (clearState) {
1124
+ // To delete PVCs, we must first scale down StatefulSets so pods release the volumes
1125
+ // Otherwise PVC deletion will hang waiting for pods to terminate
1126
+
1127
+ // First, save original replica counts
1128
+ const originalReplicas: Map<string, number> = new Map();
1129
+ for (const component of statefulSetComponents) {
1130
+ try {
1131
+ const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o jsonpath='{.items[0].spec.replicas}'`;
1132
+ const { stdout } = await execAsync(getCmd);
1133
+ const replicas = parseInt(stdout.replace(/'/g, '').trim(), 10);
1134
+ if (!isNaN(replicas) && replicas > 0) {
1135
+ originalReplicas.set(component, replicas);
1136
+ }
1137
+ } catch {
1138
+ // Component might not exist, continue
1139
+ }
1140
+ }
1141
+
1142
+ // Scale down to 0
1143
+ for (const component of statefulSetComponents) {
1144
+ try {
1145
+ const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=0 --timeout=2m`;
1146
+ logger.info(`command: ${scaleCmd}`);
1147
+ await execAsync(scaleCmd);
1148
+ } catch (e) {
1149
+ // Component might not exist or might be a Deployment, continue
1150
+ logger.verbose(`Scale down ${component} skipped: ${e}`);
1151
+ }
1152
+ }
1153
+
1154
+ // Wait for pods to terminate
1155
+ await sleep(15 * 1000);
1156
+
1157
+ // Now delete PVCs (they should no longer be in use)
1158
+ for (const component of pvcComponents) {
1159
+ await deleteResourceByLabel({
1160
+ resource: 'persistentvolumeclaims',
1161
+ namespace: namespace,
1162
+ label: `app.kubernetes.io/component=${component}`,
1163
+ });
1164
+ }
1165
+
1166
+ // Scale StatefulSets back up to original replica counts
1167
+ for (const component of statefulSetComponents) {
1168
+ const replicas = originalReplicas.get(component) ?? 1;
1169
+ try {
1170
+ const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=${replicas} --timeout=2m`;
1171
+ logger.info(`command: ${scaleCmd}`);
1172
+ await execAsync(scaleCmd);
1173
+ } catch (e) {
1174
+ logger.verbose(`Scale up ${component} skipped: ${e}`);
1175
+ }
1176
+ }
1177
+ } else {
1178
+ // Just delete pods (no state clearing)
1179
+ for (const component of podComponents) {
1180
+ await deleteResourceByLabel({
1181
+ resource: 'pods',
1182
+ namespace: namespace,
1183
+ label: `app.kubernetes.io/component=${component}`,
1184
+ });
1185
+ }
1186
+ }
1187
+
1103
1188
  await sleep(10 * 1000);
1104
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=boot-node' });
1105
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-node' });
1106
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-broker' });
1107
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=prover-agent' });
1108
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=validator' });
1109
- await waitForResourceByLabel({ resource: 'pods', namespace: namespace, label: 'app=pxe' });
1189
+
1190
+ // Wait for pods to come back
1191
+ for (const component of podComponents) {
1192
+ await waitForResourceByLabel({
1193
+ resource: 'pods',
1194
+ namespace: namespace,
1195
+ label: `app.kubernetes.io/component=${component}`,
1196
+ });
1197
+ }
1110
1198
  }
1111
1199
 
1112
1200
  /**