@aztec/end-to-end 0.0.1-commit.2448fdb → 0.0.1-commit.2606882

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 (172) hide show
  1. package/dest/bench/client_flows/benchmark.d.ts +15 -1
  2. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  3. package/dest/bench/client_flows/benchmark.js +17 -0
  4. package/dest/bench/client_flows/client_flows_benchmark.d.ts +2 -2
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  6. package/dest/bench/client_flows/client_flows_benchmark.js +25 -35
  7. package/dest/bench/utils.d.ts +1 -1
  8. package/dest/bench/utils.d.ts.map +1 -1
  9. package/dest/bench/utils.js +2 -0
  10. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +3 -3
  11. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  12. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +13 -4
  13. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -4
  14. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  15. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +39 -7
  16. package/dest/e2e_deploy_contract/deploy_test.d.ts +3 -3
  17. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  18. package/dest/e2e_deploy_contract/deploy_test.js +2 -1
  19. package/dest/e2e_epochs/epochs_test.d.ts +23 -2
  20. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  21. package/dest/e2e_epochs/epochs_test.js +83 -12
  22. package/dest/e2e_fees/fees_test.d.ts +5 -2
  23. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  24. package/dest/e2e_fees/fees_test.js +31 -5
  25. package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
  26. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  27. package/dest/e2e_nested_contract/nested_contract_test.js +2 -1
  28. package/dest/e2e_p2p/inactivity_slash_test.js +3 -3
  29. package/dest/e2e_p2p/p2p_network.d.ts +11 -10
  30. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  31. package/dest/e2e_p2p/p2p_network.js +47 -28
  32. package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
  33. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  34. package/dest/e2e_p2p/reqresp/utils.js +42 -13
  35. package/dest/e2e_p2p/shared.d.ts +23 -26
  36. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  37. package/dest/e2e_p2p/shared.js +62 -80
  38. package/dest/e2e_token_contract/token_contract_test.d.ts +3 -3
  39. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  40. package/dest/e2e_token_contract/token_contract_test.js +4 -2
  41. package/dest/fixtures/e2e_prover_test.d.ts +6 -5
  42. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  43. package/dest/fixtures/e2e_prover_test.js +9 -13
  44. package/dest/fixtures/elu_monitor.d.ts +21 -0
  45. package/dest/fixtures/elu_monitor.d.ts.map +1 -0
  46. package/dest/fixtures/elu_monitor.js +102 -0
  47. package/dest/fixtures/fixtures.d.ts +70 -1
  48. package/dest/fixtures/fixtures.d.ts.map +1 -1
  49. package/dest/fixtures/fixtures.js +65 -0
  50. package/dest/fixtures/get_bb_config.d.ts +1 -1
  51. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  52. package/dest/fixtures/get_bb_config.js +5 -5
  53. package/dest/fixtures/ha_setup.d.ts +2 -2
  54. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  55. package/dest/fixtures/ha_setup.js +1 -1
  56. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  57. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  58. package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
  59. package/dest/fixtures/setup.d.ts +44 -10
  60. package/dest/fixtures/setup.d.ts.map +1 -1
  61. package/dest/fixtures/setup.js +145 -19
  62. package/dest/fixtures/setup_p2p_test.d.ts +10 -6
  63. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  64. package/dest/fixtures/setup_p2p_test.js +8 -8
  65. package/dest/fixtures/token_utils.d.ts +1 -1
  66. package/dest/fixtures/token_utils.d.ts.map +1 -1
  67. package/dest/fixtures/token_utils.js +2 -5
  68. package/dest/fixtures/utils.d.ts +2 -2
  69. package/dest/fixtures/utils.d.ts.map +1 -1
  70. package/dest/fixtures/utils.js +1 -1
  71. package/dest/install_legacy_contracts.d.cts +1 -1
  72. package/dest/install_legacy_contracts.d.cts.map +1 -1
  73. package/dest/legacy-jest-resolver.d.cts +1 -1
  74. package/dest/legacy-jest-resolver.d.cts.map +1 -1
  75. package/dest/shared/cross_chain_test_harness.d.ts +4 -2
  76. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  77. package/dest/shared/cross_chain_test_harness.js +9 -5
  78. package/dest/shared/gas_portal_test_harness.d.ts +8 -5
  79. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  80. package/dest/shared/gas_portal_test_harness.js +17 -8
  81. package/dest/shared/jest_setup.js +41 -1
  82. package/dest/shared/submit-transactions.d.ts +1 -1
  83. package/dest/shared/submit-transactions.d.ts.map +1 -1
  84. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  85. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  86. package/dest/shared/uniswap_l1_l2.js +50 -32
  87. package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
  88. package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
  89. package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
  90. package/dest/simulators/lending_simulator.d.ts +10 -3
  91. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  92. package/dest/simulators/lending_simulator.js +23 -11
  93. package/dest/simulators/token_simulator.js +1 -1
  94. package/dest/spartan/setup_test_wallets.d.ts +1 -1
  95. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  96. package/dest/spartan/setup_test_wallets.js +67 -43
  97. package/dest/spartan/tx_metrics.d.ts +18 -4
  98. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  99. package/dest/spartan/tx_metrics.js +74 -21
  100. package/dest/spartan/utils/config.d.ts +10 -30
  101. package/dest/spartan/utils/config.d.ts.map +1 -1
  102. package/dest/spartan/utils/config.js +3 -1
  103. package/dest/spartan/utils/index.d.ts +2 -1
  104. package/dest/spartan/utils/index.d.ts.map +1 -1
  105. package/dest/spartan/utils/index.js +2 -0
  106. package/dest/spartan/utils/nodes.d.ts +4 -5
  107. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  108. package/dest/spartan/utils/nodes.js +11 -11
  109. package/dest/spartan/utils/pod_logs.d.ts +25 -0
  110. package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
  111. package/dest/spartan/utils/pod_logs.js +74 -0
  112. package/dest/test-wallet/test_wallet.d.ts +3 -2
  113. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  114. package/dest/test-wallet/test_wallet.js +52 -28
  115. package/dest/test-wallet/utils.d.ts +3 -3
  116. package/dest/test-wallet/utils.d.ts.map +1 -1
  117. package/dest/test-wallet/utils.js +2 -7
  118. package/dest/test-wallet/wallet_worker_script.js +7 -2
  119. package/dest/test-wallet/worker_wallet.d.ts +2 -1
  120. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  121. package/dest/test-wallet/worker_wallet.js +6 -2
  122. package/dest/test-wallet/worker_wallet_schema.d.ts +110 -224
  123. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  124. package/dest/test-wallet/worker_wallet_schema.js +14 -2
  125. package/package.json +48 -46
  126. package/src/bench/client_flows/benchmark.ts +19 -0
  127. package/src/bench/client_flows/client_flows_benchmark.ts +62 -51
  128. package/src/bench/utils.ts +2 -1
  129. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +21 -4
  130. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +49 -11
  131. package/src/e2e_deploy_contract/deploy_test.ts +3 -2
  132. package/src/e2e_epochs/epochs_test.ts +85 -9
  133. package/src/e2e_fees/fees_test.ts +37 -4
  134. package/src/e2e_nested_contract/nested_contract_test.ts +3 -1
  135. package/src/e2e_p2p/inactivity_slash_test.ts +3 -3
  136. package/src/e2e_p2p/p2p_network.ts +58 -40
  137. package/src/e2e_p2p/reqresp/utils.ts +51 -12
  138. package/src/e2e_p2p/shared.ts +60 -113
  139. package/src/e2e_token_contract/token_contract_test.ts +12 -2
  140. package/src/fixtures/dumps/epoch_proof_result.json +1 -1
  141. package/src/fixtures/e2e_prover_test.ts +15 -18
  142. package/src/fixtures/elu_monitor.ts +126 -0
  143. package/src/fixtures/fixtures.ts +83 -0
  144. package/src/fixtures/get_bb_config.ts +7 -6
  145. package/src/fixtures/ha_setup.ts +4 -2
  146. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  147. package/src/fixtures/setup.ts +175 -24
  148. package/src/fixtures/setup_p2p_test.ts +13 -9
  149. package/src/fixtures/token_utils.ts +1 -4
  150. package/src/fixtures/utils.ts +2 -0
  151. package/src/guides/up_quick_start.sh +0 -2
  152. package/src/install_legacy_contracts.cjs +9 -5
  153. package/src/legacy-jest-resolver.cjs +15 -4
  154. package/src/shared/cross_chain_test_harness.ts +14 -4
  155. package/src/shared/gas_portal_test_harness.ts +20 -10
  156. package/src/shared/jest_setup.ts +51 -1
  157. package/src/shared/submit-transactions.ts +1 -4
  158. package/src/shared/uniswap_l1_l2.ts +84 -40
  159. package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
  160. package/src/simulators/lending_simulator.ts +27 -13
  161. package/src/simulators/token_simulator.ts +1 -1
  162. package/src/spartan/setup_test_wallets.ts +65 -40
  163. package/src/spartan/tx_metrics.ts +82 -24
  164. package/src/spartan/utils/config.ts +2 -0
  165. package/src/spartan/utils/index.ts +3 -0
  166. package/src/spartan/utils/nodes.ts +17 -12
  167. package/src/spartan/utils/pod_logs.ts +99 -0
  168. package/src/test-wallet/test_wallet.ts +49 -24
  169. package/src/test-wallet/utils.ts +3 -7
  170. package/src/test-wallet/wallet_worker_script.ts +5 -2
  171. package/src/test-wallet/worker_wallet.ts +7 -3
  172. package/src/test-wallet/worker_wallet_schema.ts +2 -2
@@ -1,6 +1,6 @@
1
1
  import type { AztecNode } from '@aztec/aztec.js/node';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
- import type { L2Block } from '@aztec/stdlib/block';
3
+ import type { BlockResponse } from '@aztec/stdlib/interfaces/client';
4
4
  import type { TopicType } from '@aztec/stdlib/p2p';
5
5
  import { Tx, type TxReceipt } from '@aztec/stdlib/tx';
6
6
 
@@ -134,9 +134,12 @@ export class ProvingMetrics {
134
134
 
135
135
  export type TxInclusionData = {
136
136
  txHash: string;
137
- sentAt: number;
138
- minedAt: number;
139
- attestedAt: number;
137
+ /** Wall-clock at client when the tx was submitted, in ms (Date.now()). */
138
+ sentAtMs: number;
139
+ /** Wall-clock at client when the block containing the tx first became visible, in ms (Date.now()). -1 if never observed. */
140
+ minedAtMs: number;
141
+ /** Reserved for future attestation-observed-at signal; -1 today. */
142
+ attestedAtMs: number;
140
143
  blocknumber: number;
141
144
  priorityFee: number;
142
145
  totalFee: number;
@@ -147,7 +150,7 @@ export type TxInclusionData = {
147
150
  export class TxInclusionMetrics {
148
151
  private data = new Map<string, TxInclusionData>();
149
152
  private groups = new Set<string>();
150
- private blocks = new Map<number, Promise<L2Block | undefined>>();
153
+ private blocks = new Map<number, Promise<BlockResponse<{ includeTransactions: true }> | undefined>>();
151
154
 
152
155
  private p2pGossipLatencyByTopic: Partial<Record<TopicType, { p50: number; p95: number }>> = {};
153
156
 
@@ -174,9 +177,9 @@ export class TxInclusionMetrics {
174
177
 
175
178
  this.data.set(txHash, {
176
179
  txHash,
177
- sentAt: Math.trunc(Date.now() / 1000),
178
- minedAt: -1,
179
- attestedAt: -1,
180
+ sentAtMs: Date.now(),
181
+ minedAtMs: -1,
182
+ attestedAtMs: -1,
180
183
  blocknumber: -1,
181
184
  priorityFee: Number(priorityFees.feePerDaGas + priorityFees.feePerL2Gas),
182
185
  totalFee: -1,
@@ -186,6 +189,28 @@ export class TxInclusionMetrics {
186
189
  this.groups.add(group);
187
190
  }
188
191
 
192
+ /**
193
+ * Stamp mined-at metadata for any tracked tx contained in this block, using
194
+ * `observedAtMs` (caller-supplied wall-clock at the moment they first saw the
195
+ * block). Idempotent: existing minedAtMs is preserved so the first observer
196
+ * wins (typically the block-watcher; recordMinedTx is a fallback).
197
+ */
198
+ observeBlockForMinedTxs(
199
+ blockNumber: number,
200
+ txHashes: ReadonlyArray<{ toString(): string }>,
201
+ observedAtMs: number,
202
+ ): void {
203
+ txHashes.forEach((txHash, position) => {
204
+ const data = this.data.get(txHash.toString());
205
+ if (!data || data.minedAtMs !== -1) {
206
+ return;
207
+ }
208
+ data.blocknumber = blockNumber;
209
+ data.minedAtMs = observedAtMs;
210
+ data.positionInBlock = position;
211
+ });
212
+ }
213
+
189
214
  async recordMinedTx(txReceipt: TxReceipt): Promise<void> {
190
215
  const { txHash, blockNumber } = txReceipt;
191
216
  if (!txReceipt.isMined() || !txReceipt.hasExecutionSucceeded() || !blockNumber) {
@@ -197,26 +222,43 @@ export class TxInclusionMetrics {
197
222
  return;
198
223
  }
199
224
 
200
- if (!this.blocks.has(blockNumber)) {
201
- this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber));
202
- }
203
-
204
- const block = await this.blocks.get(blockNumber)!;
205
- if (!block) {
206
- this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
207
- return;
208
- }
209
225
  const data = this.data.get(txHash.toString());
210
226
  if (!data) {
211
227
  const message = `Missing sent tx record for mined tx ${txHash.toString()}`;
212
228
  this.logger?.warn(message, { txHash: txHash.toString(), blockNumber });
213
229
  throw new Error(message);
214
230
  }
215
- data.blocknumber = blockNumber;
216
- data.minedAt = Number(block.header.globalVariables.timestamp);
217
- data.attestedAt = -1;
218
231
  data.totalFee = Number(txReceipt.transactionFee ?? 0n);
219
- data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
232
+
233
+ // Fallback path for txs the block-watcher missed (e.g. observed only after
234
+ // the watcher stopped). Stamp with the block's L2 slot timestamp; this is
235
+ // earlier than the true client-observed time by attestation+propagation
236
+ // lag, but it's the only deterministic timestamp available post-hoc.
237
+ if (data.minedAtMs === -1) {
238
+ if (!this.blocks.has(blockNumber)) {
239
+ this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber, { includeTransactions: true }));
240
+ }
241
+ const block = await this.blocks.get(blockNumber)!;
242
+ if (!block) {
243
+ this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
244
+ return;
245
+ }
246
+ data.blocknumber = blockNumber;
247
+ data.minedAtMs = Number(block.header.globalVariables.timestamp) * 1000;
248
+ data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
249
+ }
250
+ }
251
+
252
+ /** Per-tx inclusion records for a group. Used to serialise out for downstream tooling. */
253
+ getInclusionRecords(group?: string): TxInclusionData[] {
254
+ const out: TxInclusionData[] = [];
255
+ for (const tx of this.data.values()) {
256
+ if (group !== undefined && tx.group !== group) {
257
+ continue;
258
+ }
259
+ out.push({ ...tx });
260
+ }
261
+ return out;
220
262
  }
221
263
 
222
264
  public inclusionTimeInSeconds(group: string): {
@@ -229,12 +271,28 @@ export class TxInclusionMetrics {
229
271
  p99: number;
230
272
  } {
231
273
  const histogram = createHistogram({});
274
+ let nonPositive = 0;
232
275
  for (const tx of this.data.values()) {
233
- if (!tx.blocknumber || tx.group !== group || tx.minedAt === -1) {
276
+ if (!tx.blocknumber || tx.group !== group || tx.minedAtMs === -1) {
234
277
  continue;
235
278
  }
236
279
 
237
- histogram.record(tx.minedAt - tx.sentAt);
280
+ // Both timestamps are client wall-clock (ms). A negative delta should be
281
+ // impossible since the watcher stamps minedAtMs strictly after sentAtMs,
282
+ // but the fallback path (recordMinedTx via L2 slot timestamp) can stamp
283
+ // earlier than sentAtMs. perf_hooks.createHistogram rejects <=0; skip
284
+ // those instead of crashing.
285
+ const deltaMs = tx.minedAtMs - tx.sentAtMs;
286
+ if (deltaMs <= 0) {
287
+ nonPositive++;
288
+ continue;
289
+ }
290
+ // Histogram is recorded in seconds (rounded) to match the existing
291
+ // toGithubActionBenchmarkJSON output unit; per-tx records carry the raw ms.
292
+ histogram.record(Math.max(1, Math.round(deltaMs / 1000)));
293
+ }
294
+ if (nonPositive > 0) {
295
+ this.logger?.debug(`Dropped ${nonPositive} tx inclusion samples with non-positive delta`, { group });
238
296
  }
239
297
 
240
298
  if (histogram.count === 0) {
@@ -296,7 +354,7 @@ export class TxInclusionMetrics {
296
354
  value: stats.mean,
297
355
  },
298
356
  {
299
- name: `${group}/median_inclusion`,
357
+ name: `${group}/p50_inclusion`,
300
358
  unit: 's',
301
359
  value: stats.median,
302
360
  },
@@ -8,6 +8,7 @@ const logger = createLogger('e2e:k8s-utils');
8
8
  const testConfigSchema = z.object({
9
9
  NAMESPACE: z.string().default('scenario'),
10
10
  REAL_VERIFIER: schemas.Boolean.optional().default(true),
11
+ DEBUG_FORCE_TX_PROOF_VERIFICATION: schemas.Boolean.optional().default(true),
11
12
  CREATE_ETH_DEVNET: schemas.Boolean.optional().default(false),
12
13
  L1_RPC_URLS_JSON: z.string().optional(),
13
14
  L1_ACCOUNT_MNEMONIC: z.string().optional(),
@@ -16,6 +17,7 @@ const testConfigSchema = z.object({
16
17
  AZTEC_PROOF_SUBMISSION_WINDOW: z.coerce.number().optional().default(5),
17
18
  AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: z.coerce.number().optional().default(2),
18
19
  FUNDING_PRIVATE_KEY: z.string().optional(),
20
+ AZTEC_ADMIN_API_KEY: z.string().optional(),
19
21
  });
20
22
 
21
23
  export type TestConfig = z.infer<typeof testConfigSchema>;
@@ -66,3 +66,6 @@ export { getPublicViemClient, getL1DeploymentAddresses, getNodeClient } from './
66
66
 
67
67
  // Health checks
68
68
  export { ChainHealth, type ChainHealthSnapshot } from './health.js';
69
+
70
+ // Pod log extraction
71
+ export { type BlockBuiltLogEntry, fetchBlockBuiltLogs } from './pod_logs.js';
@@ -71,7 +71,7 @@ export async function waitForProvenToAdvance(
71
71
  // Get current proven block number
72
72
  let initialProvenBlock: number;
73
73
  try {
74
- const tips = await node.getL2Tips();
74
+ const tips = await node.getChainTips();
75
75
  initialProvenBlock = Number(tips.proven.block.number);
76
76
  log.info(`Current proven block: ${initialProvenBlock}. Waiting for it to increase...`);
77
77
  } catch (err) {
@@ -82,7 +82,7 @@ export async function waitForProvenToAdvance(
82
82
  await retryUntil(
83
83
  async () => {
84
84
  try {
85
- const tips = await node.getL2Tips();
85
+ const tips = await node.getChainTips();
86
86
  const currentProvenBlock = Number(tips.proven.block.number);
87
87
  const proposedBlock = Number(tips.proposed.number);
88
88
 
@@ -173,7 +173,7 @@ export async function withSequencersAdmin<T>(env: TestConfig, fn: (node: AztecNo
173
173
  if (statusRes.status !== 200) {
174
174
  throw new Error(`Admin endpoint returned status ${statusRes.status}`);
175
175
  }
176
- const client = createAztecNodeAdminClient(url);
176
+ const client = createAztecNodeAdminClient(url, {}, undefined, env.AZTEC_ADMIN_API_KEY);
177
177
  return { result: await fn(client), process };
178
178
  } catch (err) {
179
179
  // Kill the port-forward before retrying
@@ -255,21 +255,18 @@ export async function initHADb(namespace: string): Promise<void> {
255
255
  }
256
256
 
257
257
  /**
258
- * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
259
- * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
258
+ * Sets probabilistic transaction dropping on validators and waits for rollout.
259
+ * Use probability=0 to disable. Wired to env var P2P_DROP_TX_CHANCE via Helm values.
260
260
  */
261
261
  export async function setValidatorTxDrop({
262
262
  namespace,
263
- enabled,
264
263
  probability,
265
264
  logger: log,
266
265
  }: {
267
266
  namespace: string;
268
- enabled: boolean;
269
267
  probability: number;
270
268
  logger: Logger;
271
269
  }) {
272
- const drop = enabled ? 'true' : 'false';
273
270
  const prob = String(probability);
274
271
 
275
272
  const selectors = ['app.kubernetes.io/name=validator', 'app.kubernetes.io/component=validator', 'app=validator'];
@@ -284,7 +281,7 @@ export async function setValidatorTxDrop({
284
281
  if (names.length === 0) {
285
282
  continue;
286
283
  }
287
- const cmd = `kubectl set env statefulset -l ${selector} -n ${namespace} P2P_DROP_TX=${drop} P2P_DROP_TX_CHANCE=${prob}`;
284
+ const cmd = `kubectl set env statefulset -l ${selector} -n ${namespace} P2P_DROP_TX_CHANCE=${prob}`;
288
285
  log.info(`command: ${cmd}`);
289
286
  await execAsync(cmd);
290
287
  updated = true;
@@ -366,16 +363,24 @@ export async function enableValidatorDynamicBootNode(
366
363
  */
367
364
  export async function rollAztecPods(namespace: string, clearState: boolean = false) {
368
365
  // Pod components use 'validator', but StatefulSets and PVCs use 'sequencer-node' for validators
366
+ // RPC nodes have nodeType='rpc-node' in Helm values, so their component label is 'rpc-node' (not 'rpc')
369
367
  const podComponents = [
370
368
  'p2p-bootstrap',
371
369
  'prover-node',
372
370
  'prover-broker',
373
371
  'prover-agent',
374
372
  'sequencer-node',
375
- 'rpc',
373
+ 'rpc-node',
374
+ 'validator-ha-db',
375
+ ];
376
+ const pvcComponents = [
377
+ 'p2p-bootstrap',
378
+ 'prover-node',
379
+ 'prover-broker',
380
+ 'sequencer-node',
381
+ 'rpc-node',
376
382
  'validator-ha-db',
377
383
  ];
378
- const pvcComponents = ['p2p-bootstrap', 'prover-node', 'prover-broker', 'sequencer-node', 'rpc', 'validator-ha-db'];
379
384
  // StatefulSet components that need to be scaled down before PVC deletion
380
385
  // Note: validators use 'sequencer-node' as component label, not 'validator'
381
386
  const statefulSetComponents = [
@@ -383,7 +388,7 @@ export async function rollAztecPods(namespace: string, clearState: boolean = fal
383
388
  'prover-node',
384
389
  'prover-broker',
385
390
  'sequencer-node',
386
- 'rpc',
391
+ 'rpc-node',
387
392
  'validator-ha-db',
388
393
  ];
389
394
 
@@ -0,0 +1,99 @@
1
+ import type { Logger } from '@aztec/foundation/log';
2
+
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+
6
+ import { getSequencers } from './nodes.js';
7
+
8
+ const execAsync = promisify(exec);
9
+
10
+ /** Parsed l2-block-built stats from a sequencer pod log line. */
11
+ export type BlockBuiltLogEntry = {
12
+ blockNumber: number;
13
+ txCount: number;
14
+ duration: number;
15
+ publicProcessDuration: number;
16
+ manaPerSec: number;
17
+ privateLogCount: number;
18
+ publicLogCount: number;
19
+ contractClassLogCount: number;
20
+ contractClassLogSize: number;
21
+ };
22
+
23
+ const FIELDS: (keyof BlockBuiltLogEntry)[] = [
24
+ 'blockNumber',
25
+ 'txCount',
26
+ 'duration',
27
+ 'publicProcessDuration',
28
+ 'manaPerSec',
29
+ 'privateLogCount',
30
+ 'publicLogCount',
31
+ 'contractClassLogCount',
32
+ 'contractClassLogSize',
33
+ ];
34
+
35
+ /**
36
+ * Fetches l2-block-built log entries from sequencer pods for given block numbers.
37
+ * Queries all validator pods (only the proposer will have the log for a given block).
38
+ *
39
+ * @param namespace - Kubernetes namespace
40
+ * @param sinceTime - ISO 8601 timestamp to limit log search (e.g., from before block building was re-enabled)
41
+ * @param blockNumbers - Set of block numbers to filter for
42
+ * @param logger - Logger instance
43
+ * @returns Array of parsed BlockBuiltLogEntry, de-duplicated by blockNumber, sorted ascending
44
+ */
45
+ export async function fetchBlockBuiltLogs(
46
+ namespace: string,
47
+ sinceTime: string,
48
+ blockNumbers: Set<number>,
49
+ logger: Logger,
50
+ ): Promise<BlockBuiltLogEntry[]> {
51
+ const pods = await getSequencers(namespace);
52
+ const entriesByBlock = new Map<number, BlockBuiltLogEntry>();
53
+
54
+ // Subtract 60s from sinceTime to account for clock skew between test runner and k8s pods.
55
+ // Block number filtering ensures we only match the right blocks, so extra lines are harmless.
56
+ const sinceDate = new Date(new Date(sinceTime).getTime() - 60_000);
57
+ const sinceFlag = sinceDate.toISOString();
58
+
59
+ for (const pod of pods) {
60
+ try {
61
+ const cmd = `kubectl logs ${pod} -n ${namespace} -c aztec --since-time=${sinceFlag}`;
62
+ logger.info(`Fetching logs: ${cmd}`);
63
+ const { stdout } = await execAsync(cmd, { maxBuffer: 10 * 1024 * 1024 });
64
+
65
+ const lines = stdout.split('\n');
66
+ const matchingLines = lines.filter(l => l.includes('l2-block-built'));
67
+ logger.info(`Pod ${pod}: ${lines.length} log lines, ${matchingLines.length} contain l2-block-built`);
68
+
69
+ for (const line of matchingLines) {
70
+ try {
71
+ const parsed = JSON.parse(line);
72
+ if (parsed.eventName !== 'l2-block-built' || !blockNumbers.has(parsed.blockNumber)) {
73
+ continue;
74
+ }
75
+ if (entriesByBlock.has(parsed.blockNumber)) {
76
+ continue;
77
+ }
78
+ const entry: BlockBuiltLogEntry = {} as BlockBuiltLogEntry;
79
+ for (const field of FIELDS) {
80
+ entry[field] = parsed[field] ?? 0;
81
+ }
82
+ entriesByBlock.set(entry.blockNumber, entry);
83
+ logger.verbose(`Parsed l2-block-built log for block ${entry.blockNumber}`, entry);
84
+ } catch {
85
+ // Not valid JSON, skip
86
+ }
87
+ }
88
+ } catch (err) {
89
+ logger.warn(`Failed to fetch logs from pod ${pod}: ${err}`);
90
+ }
91
+ }
92
+
93
+ if (entriesByBlock.size < blockNumbers.size) {
94
+ const missing = [...blockNumbers].filter(bn => !entriesByBlock.has(bn));
95
+ logger.warn(`Missing l2-block-built logs for block(s): ${missing.join(', ')}`);
96
+ }
97
+
98
+ return [...entriesByBlock.values()].sort((a, b) => a.blockNumber - b.blockNumber);
99
+ }
@@ -25,7 +25,7 @@ import { type PXEConfig, getPXEConfig } from '@aztec/pxe/config';
25
25
  import { PXE, type PXECreationOptions, createPXE } from '@aztec/pxe/server';
26
26
  import { AuthWitness } from '@aztec/stdlib/auth-witness';
27
27
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
28
- import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
28
+ import { getContractClassFromArtifact } from '@aztec/stdlib/contract';
29
29
  import { deriveSigningKey } from '@aztec/stdlib/keys';
30
30
  import type { NoteDao } from '@aztec/stdlib/note';
31
31
  import {
@@ -40,6 +40,7 @@ import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
40
40
  import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
41
41
  import type { AccountType } from '@aztec/wallets/embedded';
42
42
 
43
+ import { DEFAULT_MIN_FEE_PADDING } from '../fixtures/fixtures.js';
43
44
  import { AztecNodeProxy, ProvenTx } from './utils.js';
44
45
 
45
46
  /**
@@ -63,6 +64,7 @@ export class TestWallet extends BaseWallet {
63
64
  private readonly nodeRef: AztecNodeProxy,
64
65
  ) {
65
66
  super(pxe, nodeRef);
67
+ this.minFeePadding = DEFAULT_MIN_FEE_PADDING;
66
68
  }
67
69
 
68
70
  static async create(
@@ -76,7 +78,9 @@ export class TestWallet extends BaseWallet {
76
78
  ...overridePXEConfig,
77
79
  });
78
80
  const pxe = await createPXE(nodeRef, pxeConfig, options);
79
- return new TestWallet(pxe, nodeRef);
81
+ const wallet = new TestWallet(pxe, nodeRef);
82
+ await wallet.initStubClasses();
83
+ return wallet;
80
84
  }
81
85
 
82
86
  /**
@@ -110,6 +114,27 @@ export class TestWallet extends BaseWallet {
110
114
  });
111
115
  }
112
116
 
117
+ // Stub class ids, populated on wallet startup
118
+ // to avoid redundant work per simulation
119
+ private stubClassIds = new Map<AccountType, Fr>();
120
+
121
+ /**
122
+ * Hashes and registers the stub class for every supported account type with PXE, populating
123
+ * stubClassIds. Called on wallet initialization.
124
+ */
125
+ private async initStubClasses(): Promise<void> {
126
+ const { id: schnorrClassId } = await getContractClassFromArtifact(StubSchnorrAccountContractArtifact);
127
+ await this.pxe.registerContractClass(StubSchnorrAccountContractArtifact);
128
+
129
+ // ecdsa stubs share the same class id
130
+ const { id: ecdsaClassId } = await getContractClassFromArtifact(StubEcdsaAccountContractArtifact);
131
+ await this.pxe.registerContractClass(StubEcdsaAccountContractArtifact);
132
+
133
+ this.stubClassIds.set('schnorr', schnorrClassId);
134
+ this.stubClassIds.set('ecdsasecp256k1', ecdsaClassId);
135
+ this.stubClassIds.set('ecdsasecp256r1', ecdsaClassId);
136
+ }
137
+
113
138
  /**
114
139
  * Builds contract overrides for all provided addresses by replacing their account contracts with stub implementations.
115
140
  */
@@ -130,17 +155,16 @@ export class TestWallet extends BaseWallet {
130
155
  );
131
156
  }
132
157
 
133
- const stubArtifact = this.getStubArtifactFor(address);
134
- const stubConstructorArgs =
135
- this.getTypeFor(address) === 'schnorr' ? [Fr.ZERO, Fr.ZERO] : [Buffer.alloc(32), Buffer.alloc(32)];
136
- const stubInstance = await getContractInstanceFromInstantiationParams(stubArtifact, {
137
- salt: Fr.random(),
138
- constructorArgs: stubConstructorArgs,
139
- });
158
+ const type = this.getTypeFor(address);
159
+ const stubClassId = this.stubClassIds.get(type);
160
+ if (!stubClassId) {
161
+ throw new Error(
162
+ `Stub class for account type '${type}' was not registered at wallet init. This is a bug — initStubClasses should cover every supported AccountType.`,
163
+ );
164
+ }
140
165
 
141
166
  contracts[address.toString()] = {
142
- instance: stubInstance,
143
- artifact: stubArtifact,
167
+ instance: { ...contractInstance, currentContractClassId: stubClassId },
144
168
  };
145
169
  }
146
170
 
@@ -153,12 +177,6 @@ export class TestWallet extends BaseWallet {
153
177
  return this.accounts.get(address.toString())?.type ?? 'schnorr';
154
178
  }
155
179
 
156
- private getStubArtifactFor(address: AztecAddress) {
157
- return this.getTypeFor(address) === 'schnorr'
158
- ? StubSchnorrAccountContractArtifact
159
- : StubEcdsaAccountContractArtifact;
160
- }
161
-
162
180
  private getStubAccountFor(address: AztecAddress, completeAddress: CompleteAddress) {
163
181
  return this.getTypeFor(address) === 'schnorr'
164
182
  ? createStubSchnorrAccount(completeAddress)
@@ -178,7 +196,7 @@ export class TestWallet extends BaseWallet {
178
196
  }
179
197
 
180
198
  setMinFeePadding(value?: number) {
181
- this.minFeePadding = value ?? 0.5;
199
+ this.minFeePadding = value ?? DEFAULT_MIN_FEE_PADDING;
182
200
  }
183
201
 
184
202
  protected getAccountFromAddress(address: AztecAddress): Promise<Account> {
@@ -203,7 +221,7 @@ export class TestWallet extends BaseWallet {
203
221
  const type = accountData?.type ?? 'schnorr';
204
222
  const contract = accountData?.contract ?? new SchnorrAccountContract(GrumpkinScalar.random());
205
223
 
206
- const accountManager = await AccountManager.create(this, secret, contract, salt);
224
+ const accountManager = await AccountManager.create(this, secret, contract, { salt });
207
225
 
208
226
  const instance = accountManager.getInstance();
209
227
  const artifact = await contract.getContractArtifact();
@@ -258,7 +276,7 @@ export class TestWallet extends BaseWallet {
258
276
  executionPayload: ExecutionPayload,
259
277
  opts: SimulateViaEntrypointOptions,
260
278
  ): Promise<TxSimulationResultWithAppOffset> {
261
- const { from, feeOptions, additionalScopes, skipTxValidation, skipFeeEnforcement } = opts;
279
+ const { from, feeOptions, additionalScopes, skipTxValidation, skipFeeEnforcement, sendMessagesAs } = opts;
262
280
  const scopes = this.scopesFrom(from, additionalScopes);
263
281
  const skipKernels = this.simulationMode !== 'full';
264
282
  const useOverride = this.simulationMode === 'kernelless-override';
@@ -269,11 +287,14 @@ export class TestWallet extends BaseWallet {
269
287
  : executionPayload;
270
288
  const chainInfo = await this.getChainInfo();
271
289
 
272
- let overrides: SimulationOverrides | undefined;
290
+ let overrides = opts.overrides;
273
291
  let txRequest: TxExecutionRequest;
274
292
  if (useOverride) {
275
293
  const accountOverrides = await this.buildAccountOverrides(scopes);
276
- overrides = new SimulationOverrides(accountOverrides);
294
+ overrides = new SimulationOverrides({
295
+ publicStorage: overrides?.publicStorage,
296
+ contracts: { ...overrides?.contracts, ...accountOverrides },
297
+ });
277
298
  }
278
299
 
279
300
  if (from === NO_FROM) {
@@ -308,6 +329,7 @@ export class TestWallet extends BaseWallet {
308
329
  skipTxValidation,
309
330
  overrides,
310
331
  scopes,
332
+ senderForTags: this.senderForTagsFrom(from, sendMessagesAs),
311
333
  });
312
334
  const appCallOffset = await this.computeAppCallOffset(from, feeOptions);
313
335
  return TxSimulationResultWithAppOffset.fromResultAndOffset(result, appCallOffset);
@@ -320,7 +342,10 @@ export class TestWallet extends BaseWallet {
320
342
  gasSettings: opts.fee?.gasSettings,
321
343
  });
322
344
  const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(exec, opts.from, fee);
323
- const txProvingResult = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes));
345
+ const txProvingResult = await this.pxe.proveTx(txRequest, {
346
+ scopes: this.scopesFrom(opts.from, opts.additionalScopes),
347
+ senderForTags: this.senderForTagsFrom(opts.from, opts.sendMessagesAs),
348
+ });
324
349
  return new ProvenTx(
325
350
  this.aztecNode,
326
351
  await txProvingResult.toTx(),
@@ -342,7 +367,7 @@ export class TestWallet extends BaseWallet {
342
367
  }
343
368
 
344
369
  sync(): Promise<void> {
345
- return this.pxe.debug.sync();
370
+ return this.pxe.sync();
346
371
  }
347
372
 
348
373
  stop(): Promise<void> {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ BatchCall,
2
3
  ContractFunctionInteraction,
3
4
  DeployMethod,
4
5
  type DeployOptions,
@@ -62,15 +63,10 @@ export class ProvenTx extends Tx {
62
63
 
63
64
  export async function proveInteraction(
64
65
  wallet: TestWallet,
65
- interaction: ContractFunctionInteraction | DeployMethod,
66
+ interaction: ContractFunctionInteraction | DeployMethod | BatchCall,
66
67
  options: SendInteractionOptions | DeployOptions,
67
68
  ) {
68
- let execPayload;
69
- if (interaction instanceof DeployMethod) {
70
- execPayload = await interaction.request(interaction.convertDeployOptionsToRequestOptions(options));
71
- } else {
72
- execPayload = await interaction.request(options);
73
- }
69
+ const execPayload = await interaction.request(options);
74
70
  return wallet.proveTx(execPayload, toSendOptions(options));
75
71
  }
76
72
 
@@ -1,9 +1,10 @@
1
1
  import { createAztecNodeClient } from '@aztec/aztec.js/node';
2
2
  import type { SendOptions } from '@aztec/aztec.js/wallet';
3
+ import { BackendType, BarretenbergSync } from '@aztec/bb.js';
3
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
4
5
  import { createLogger } from '@aztec/foundation/log';
5
6
  import type { ApiSchema, Fr } from '@aztec/foundation/schemas';
6
- import { parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
7
+ import { getSchemaParameters, parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
7
8
  import { NodeListener, TransportServer } from '@aztec/foundation/transport';
8
9
  import { ExecutionPayload, Tx } from '@aztec/stdlib/tx';
9
10
 
@@ -19,6 +20,8 @@ try {
19
20
 
20
21
  logger.info('Initializing worker wallet', { nodeUrl });
21
22
  const node = createAztecNodeClient(nodeUrl);
23
+ // Worker sync bb use is limited to crypto and proof serialization helpers.
24
+ await BarretenbergSync.initSingleton({ backend: BackendType.Wasm });
22
25
  const wallet = await TestWallet.create(node, pxeConfig);
23
26
  logger.info('Worker wallet initialized');
24
27
 
@@ -46,7 +49,7 @@ try {
46
49
  throw new Error(`Unknown method: ${msg.fn}`);
47
50
  }
48
51
  const jsonParams = JSON.parse(msg.args) as unknown[];
49
- const args: any[] = await parseWithOptionals(jsonParams, schema[msg.fn].parameters());
52
+ const args: any[] = await parseWithOptionals(jsonParams, getSchemaParameters(schema[msg.fn]));
50
53
  // we have to erase the fn type in order to be able to spread ...args
51
54
  const handler: ((...args: any[]) => Promise<any>) | undefined =
52
55
  msg.fn in customMethods ? customMethods[msg.fn as keyof typeof customMethods] : undefined;
@@ -22,7 +22,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
22
22
  import { jsonStringify } from '@aztec/foundation/json-rpc';
23
23
  import { createLogger } from '@aztec/foundation/log';
24
24
  import { promiseWithResolvers } from '@aztec/foundation/promise';
25
- import type { ApiSchema } from '@aztec/foundation/schemas';
25
+ import { type ApiSchema, getSchemaReturnType } from '@aztec/foundation/schemas';
26
26
  import { sleep } from '@aztec/foundation/sleep';
27
27
  import { NodeConnector, TransportClient } from '@aztec/foundation/transport';
28
28
  import type { PXEConfig } from '@aztec/pxe/config';
@@ -71,7 +71,7 @@ export class WorkerWallet implements Wallet {
71
71
  env: {
72
72
  ...parentEnv,
73
73
  ...(process.stderr.isTTY || process.env.FORCE_COLOR ? { FORCE_COLOR: '1' } : {}),
74
- LOG_LEVEL: process.env.WORKER_LOG_LEVEL ?? 'warn',
74
+ LOG_LEVEL: process.env.WORKER_LOG_LEVEL ?? 'info',
75
75
  },
76
76
  });
77
77
 
@@ -124,7 +124,7 @@ export class WorkerWallet implements Wallet {
124
124
  private async call(fn: string, ...args: any[]): Promise<any> {
125
125
  const resultJson = await this.callRaw(fn, ...args);
126
126
  const methodSchema = (WorkerWalletSchema as ApiSchema)[fn];
127
- return methodSchema.returnType().parseAsync(JSON.parse(resultJson));
127
+ return getSchemaReturnType(methodSchema).parseAsync(JSON.parse(resultJson));
128
128
  }
129
129
 
130
130
  getChainInfo(): Promise<ChainInfo> {
@@ -166,6 +166,10 @@ export class WorkerWallet implements Wallet {
166
166
  return this.call('registerContract', instance, artifact, secretKey);
167
167
  }
168
168
 
169
+ registerContractClass(artifact: ContractArtifact): Promise<void> {
170
+ return this.call('registerContractClass', artifact);
171
+ }
172
+
169
173
  simulateTx(exec: ExecutionPayload, opts: SimulateOptions): Promise<TxSimulationResultWithAppOffset> {
170
174
  return this.call('simulateTx', exec, opts);
171
175
  }
@@ -8,6 +8,6 @@ import { z } from 'zod';
8
8
  /** Schema for the WorkerWallet API — extends WalletSchema with proveTx and registerAccount. */
9
9
  export const WorkerWalletSchema = {
10
10
  ...WalletSchema,
11
- proveTx: z.function().args(ExecutionPayloadSchema, SendOptionsSchema).returns(Tx.schema),
12
- registerAccount: z.function().args(schemas.Fr, schemas.Fr).returns(AztecAddress.schema),
11
+ proveTx: z.function({ input: z.tuple([ExecutionPayloadSchema, SendOptionsSchema]), output: Tx.schema }),
12
+ registerAccount: z.function({ input: z.tuple([schemas.Fr, schemas.Fr]), output: AztecAddress.schema }),
13
13
  };