@aztec/end-to-end 0.0.1-commit.bf2612ae → 0.0.1-commit.c2595eba

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dest/e2e_epochs/epochs_test.d.ts +7 -1
  2. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  3. package/dest/e2e_epochs/epochs_test.js +28 -9
  4. package/dest/e2e_l1_publisher/write_json.d.ts +3 -2
  5. package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
  6. package/dest/e2e_l1_publisher/write_json.js +1 -7
  7. package/dest/e2e_p2p/shared.d.ts +1 -1
  8. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  9. package/dest/e2e_p2p/shared.js +2 -2
  10. package/dest/fixtures/e2e_prover_test.js +1 -1
  11. package/dest/fixtures/setup.d.ts +3 -3
  12. package/dest/fixtures/setup.d.ts.map +1 -1
  13. package/dest/fixtures/setup.js +20 -15
  14. package/dest/fixtures/setup_p2p_test.d.ts +4 -5
  15. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  16. package/dest/fixtures/setup_p2p_test.js +24 -19
  17. package/dest/spartan/tx_metrics.d.ts +35 -1
  18. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  19. package/dest/spartan/tx_metrics.js +150 -0
  20. package/dest/spartan/utils/index.d.ts +3 -3
  21. package/dest/spartan/utils/index.d.ts.map +1 -1
  22. package/dest/spartan/utils/index.js +2 -2
  23. package/dest/spartan/utils/k8s.d.ts +29 -1
  24. package/dest/spartan/utils/k8s.d.ts.map +1 -1
  25. package/dest/spartan/utils/k8s.js +118 -0
  26. package/dest/spartan/utils/nodes.d.ts +11 -1
  27. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  28. package/dest/spartan/utils/nodes.js +192 -27
  29. package/package.json +39 -39
  30. package/src/e2e_epochs/epochs_test.ts +31 -10
  31. package/src/e2e_l1_publisher/write_json.ts +1 -6
  32. package/src/e2e_p2p/shared.ts +10 -2
  33. package/src/fixtures/e2e_prover_test.ts +1 -1
  34. package/src/fixtures/setup.ts +13 -13
  35. package/src/fixtures/setup_p2p_test.ts +15 -20
  36. package/src/spartan/tx_metrics.ts +126 -0
  37. package/src/spartan/utils/index.ts +2 -0
  38. package/src/spartan/utils/k8s.ts +152 -0
  39. package/src/spartan/utils/nodes.ts +236 -24
@@ -1,11 +1,12 @@
1
1
  import { createLogger } from '@aztec/aztec.js/log';
2
- import { makeBackoff, retry } from '@aztec/foundation/retry';
2
+ import { createAztecNodeClient } from '@aztec/aztec.js/node';
3
+ import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
3
4
  import { sleep } from '@aztec/foundation/sleep';
4
5
  import { createAztecNodeAdminClient } from '@aztec/stdlib/interfaces/client';
5
6
  import { exec } from 'child_process';
6
7
  import { promisify } from 'util';
7
8
  import { execHelmCommand } from './helm.js';
8
- import { deleteResourceByLabel, getChartDir, startPortForward, waitForResourceByLabel } from './k8s.js';
9
+ import { deleteResourceByLabel, getChartDir, startPortForward, waitForResourceByLabel, waitForStatefulSetsReady } from './k8s.js';
9
10
  const execAsync = promisify(exec);
10
11
  const logger = createLogger('e2e:k8s-utils');
11
12
  export async function awaitCheckpointNumber(rollupCheatCodes, checkpointNumber, timeoutSeconds, log) {
@@ -23,6 +24,43 @@ export async function awaitCheckpointNumber(rollupCheatCodes, checkpointNumber,
23
24
  log.info(`Reached checkpoint ${tips.pending}`);
24
25
  }
25
26
  }
27
+ /**
28
+ * Waits until the proven block number increases.
29
+ *
30
+ * @param rpcUrl - URL of an Aztec RPC node to query
31
+ * @param log - Logger instance
32
+ * @param timeoutSeconds - Maximum time to wait
33
+ * @param pollIntervalSeconds - How often to check
34
+ */ export async function waitForProvenToAdvance(rpcUrl, log, timeoutSeconds = 300, pollIntervalSeconds = 12) {
35
+ const node = createAztecNodeClient(rpcUrl);
36
+ log.info('Waiting for proven block to advance (indicating epoch proof just submitted)...');
37
+ // Get current proven block number
38
+ let initialProvenBlock;
39
+ try {
40
+ const tips = await node.getL2Tips();
41
+ initialProvenBlock = Number(tips.proven.block.number);
42
+ log.info(`Current proven block: ${initialProvenBlock}. Waiting for it to increase...`);
43
+ } catch (err) {
44
+ log.warn(`Error getting initial tips: ${err}. Will poll until successful.`);
45
+ initialProvenBlock = 0;
46
+ }
47
+ await retryUntil(async ()=>{
48
+ try {
49
+ const tips = await node.getL2Tips();
50
+ const currentProvenBlock = Number(tips.proven.block.number);
51
+ const proposedBlock = Number(tips.proposed.number);
52
+ log.verbose(`Chain state: proposed=${proposedBlock}, proven=${currentProvenBlock} (waiting for > ${initialProvenBlock})`);
53
+ if (currentProvenBlock > initialProvenBlock) {
54
+ log.info(`Proven block advanced from ${initialProvenBlock} to ${currentProvenBlock}.`);
55
+ return true;
56
+ }
57
+ return false;
58
+ } catch (err) {
59
+ log.verbose(`Error checking tips: ${err}`);
60
+ return false;
61
+ }
62
+ }, 'proven block to advance', timeoutSeconds, pollIntervalSeconds);
63
+ }
26
64
  export async function getSequencers(namespace) {
27
65
  const selectors = [
28
66
  'app.kubernetes.io/name=validator',
@@ -63,7 +101,7 @@ export async function withSequencersAdmin(env, fn) {
63
101
  for (const sequencer of sequencers){
64
102
  // Wrap port-forward + fetch in a retry to handle flaky port-forwards
65
103
  const result = await retry(async ()=>{
66
- const { process, port } = await startPortForward({
104
+ const { process: process1, port } = await startPortForward({
67
105
  resource: `pod/${sequencer}`,
68
106
  namespace,
69
107
  containerPort: adminContainerPort
@@ -78,11 +116,11 @@ export async function withSequencersAdmin(env, fn) {
78
116
  const client = createAztecNodeAdminClient(url);
79
117
  return {
80
118
  result: await fn(client),
81
- process
119
+ process: process1
82
120
  };
83
121
  } catch (err) {
84
122
  // Kill the port-forward before retrying
85
- process.kill();
123
+ process1.kill();
86
124
  throw err;
87
125
  }
88
126
  }, 'connect to node admin', makeBackoff([
@@ -96,6 +134,58 @@ export async function withSequencersAdmin(env, fn) {
96
134
  }
97
135
  return results;
98
136
  }
137
+ async function getAztecImageForMigrations(namespace) {
138
+ const aztecDockerImage = process.env.AZTEC_DOCKER_IMAGE;
139
+ if (aztecDockerImage) {
140
+ return aztecDockerImage;
141
+ }
142
+ const { stdout } = await execAsync(`kubectl get pods -l app.kubernetes.io/name=validator -n ${namespace} -o jsonpath='{.items[0].spec.containers[?(@.name=="aztec")].image}' | cat`);
143
+ const image = stdout.trim().replace(/^'|'$/g, '');
144
+ if (!image) {
145
+ throw new Error(`Could not detect aztec image from validator pod in namespace ${namespace}`);
146
+ }
147
+ return image;
148
+ }
149
+ async function getHaDbConnectionUrl(namespace) {
150
+ const secretName = `${namespace}-validator-ha-db-postgres`;
151
+ const { stdout } = await execAsync(`kubectl get secret ${secretName} -n ${namespace} -o json`);
152
+ const secret = JSON.parse(stdout);
153
+ const data = secret?.data ?? {};
154
+ const decode = (value)=>value ? Buffer.from(value, 'base64').toString('utf8') : '';
155
+ const user = decode(data.POSTGRES_USER);
156
+ const password = decode(data.POSTGRES_PASSWORD);
157
+ const database = decode(data.POSTGRES_DB);
158
+ if (!user || !password || !database) {
159
+ throw new Error(`Missing HA DB credentials in secret ${secretName}`);
160
+ }
161
+ const host = `${namespace}-validator-ha-db-postgres.${namespace}.svc.cluster.local`;
162
+ return `postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:5432/${database}`;
163
+ }
164
+ export async function initHADb(namespace) {
165
+ const databaseUrl = await getHaDbConnectionUrl(namespace);
166
+ const image = await getAztecImageForMigrations(namespace);
167
+ const jobName = `${namespace}-validator-ha-db-migrate`;
168
+ await execAsync(`kubectl delete pod ${jobName} -n ${namespace} --ignore-not-found=true`).catch(()=>undefined);
169
+ const migrateCmd = [
170
+ `kubectl run ${jobName} -n ${namespace}`,
171
+ '--rm -i',
172
+ '--restart=Never',
173
+ `--image=${image}`,
174
+ `--env=DATABASE_URL=${databaseUrl}`,
175
+ '--command -- node --no-warnings /usr/src/yarn-project/aztec/dest/bin/index.js migrate-ha-db up'
176
+ ].join(' ');
177
+ const migrateCmdForLog = migrateCmd.replace(/--env=DATABASE_URL=\S+/, '--env=DATABASE_URL=<redacted>');
178
+ await retry(async ()=>{
179
+ logger.info(`command: ${migrateCmdForLog}`);
180
+ await execAsync(migrateCmd);
181
+ }, 'run HA DB migrations', makeBackoff([
182
+ 1,
183
+ 2,
184
+ 4,
185
+ 8,
186
+ 16
187
+ ]), logger, true);
188
+ }
99
189
  /**
100
190
  * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
101
191
  * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
@@ -201,14 +291,16 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
201
291
  'prover-broker',
202
292
  'prover-agent',
203
293
  'sequencer-node',
204
- 'rpc'
294
+ 'rpc',
295
+ 'validator-ha-db'
205
296
  ];
206
297
  const pvcComponents = [
207
298
  'p2p-bootstrap',
208
299
  'prover-node',
209
300
  'prover-broker',
210
301
  'sequencer-node',
211
- 'rpc'
302
+ 'rpc',
303
+ 'validator-ha-db'
212
304
  ];
213
305
  // StatefulSet components that need to be scaled down before PVC deletion
214
306
  // Note: validators use 'sequencer-node' as component label, not 'validator'
@@ -217,20 +309,27 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
217
309
  'prover-node',
218
310
  'prover-broker',
219
311
  'sequencer-node',
220
- 'rpc'
312
+ 'rpc',
313
+ 'validator-ha-db'
221
314
  ];
222
315
  if (clearState) {
223
316
  // To delete PVCs, we must first scale down StatefulSets so pods release the volumes
224
317
  // Otherwise PVC deletion will hang waiting for pods to terminate
225
- // First, save original replica counts
318
+ // Save original replica counts for all StatefulSets
226
319
  const originalReplicas = new Map();
227
320
  for (const component of statefulSetComponents){
228
321
  try {
229
- const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o jsonpath='{.items[0].spec.replicas}'`;
322
+ // Get all StatefulSets that match the component label
323
+ const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o json`;
230
324
  const { stdout } = await execAsync(getCmd);
231
- const replicas = parseInt(stdout.replace(/'/g, '').trim(), 10);
232
- if (!isNaN(replicas) && replicas > 0) {
233
- originalReplicas.set(component, replicas);
325
+ const result = JSON.parse(stdout);
326
+ for (const sts of result.items || []){
327
+ const name = sts.metadata.name;
328
+ const replicas = sts.spec.replicas ?? 1;
329
+ if (replicas > 0) {
330
+ originalReplicas.set(name, replicas);
331
+ logger.debug(`Saved replica count for StatefulSet ${name}: ${replicas}`);
332
+ }
234
333
  }
235
334
  } catch {
236
335
  // Component might not exist, continue
@@ -247,25 +346,79 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
247
346
  logger.verbose(`Scale down ${component} skipped: ${e}`);
248
347
  }
249
348
  }
250
- // Wait for pods to terminate
251
- await sleep(15 * 1000);
349
+ // Wait for all pods to fully terminate before deleting PVCs.
350
+ // terminationGracePeriodSeconds default is 30s.
351
+ logger.info('Waiting for pods to fully terminate before deleting PVCs...');
352
+ for (const component of statefulSetComponents){
353
+ try {
354
+ // Wait for all pods with this component label to be deleted
355
+ const waitCmd = `kubectl wait pods -l app.kubernetes.io/component=${component} --for=delete -n ${namespace} --timeout=2m`;
356
+ logger.info(`command: ${waitCmd}`);
357
+ await execAsync(waitCmd);
358
+ } catch (e) {
359
+ logger.verbose(`Wait for pod deletion ${component} skipped: ${e}`);
360
+ }
361
+ }
362
+ // Extra buffer to ensure PVC protection finalizers are cleared
363
+ await sleep(5 * 1000);
252
364
  // Now delete PVCs (they should no longer be in use)
253
365
  for (const component of pvcComponents){
254
- await deleteResourceByLabel({
255
- resource: 'persistentvolumeclaims',
256
- namespace: namespace,
257
- label: `app.kubernetes.io/component=${component}`
258
- });
366
+ try {
367
+ await deleteResourceByLabel({
368
+ resource: 'persistentvolumeclaims',
369
+ namespace: namespace,
370
+ label: `app.kubernetes.io/component=${component}`
371
+ });
372
+ } catch (e) {
373
+ logger.warn(`Failed to delete PVCs for ${component}: ${e}`);
374
+ }
259
375
  }
260
- // Scale StatefulSets back up to original replica counts
261
- for (const component of statefulSetComponents){
262
- const replicas = originalReplicas.get(component) ?? 1;
376
+ // Verify PVCs are deleted
377
+ for (const component of pvcComponents){
378
+ try {
379
+ const waitCmd = `kubectl wait pvc -l app.kubernetes.io/component=${component} --for=delete -n ${namespace} --timeout=2m`;
380
+ logger.info(`command: ${waitCmd}`);
381
+ await execAsync(waitCmd);
382
+ } catch (e) {
383
+ logger.verbose(`Wait for PVC deletion ${component} skipped: ${e}`);
384
+ }
385
+ }
386
+ const haDbStatefulSets = [
387
+ ...originalReplicas.entries()
388
+ ].filter(([name])=>name.includes('validator-ha-db'));
389
+ const otherStatefulSets = [
390
+ ...originalReplicas.entries()
391
+ ].filter(([name])=>!name.includes('validator-ha-db'));
392
+ // Bring up HA DB first so we can run migrations before validators start
393
+ for (const [stsName, replicas] of haDbStatefulSets){
263
394
  try {
264
- const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=${replicas} --timeout=2m`;
395
+ const scaleCmd = `kubectl scale statefulset ${stsName} -n ${namespace} --replicas=${replicas} --timeout=2m`;
265
396
  logger.info(`command: ${scaleCmd}`);
266
397
  await execAsync(scaleCmd);
267
398
  } catch (e) {
268
- logger.verbose(`Scale up ${component} skipped: ${e}`);
399
+ logger.verbose(`Scale up ${stsName} skipped: ${e}`);
400
+ }
401
+ }
402
+ if (haDbStatefulSets.length > 0) {
403
+ try {
404
+ await waitForStatefulSetsReady({
405
+ namespace,
406
+ label: 'app.kubernetes.io/component=validator-ha-db',
407
+ timeoutSeconds: 600
408
+ });
409
+ await initHADb(namespace);
410
+ } catch (e) {
411
+ logger.warn(`HA DB migration step skipped or failed: ${e}`);
412
+ }
413
+ }
414
+ // Scale remaining StatefulSets back up to original replica counts (by name, not label)
415
+ for (const [stsName, replicas] of otherStatefulSets){
416
+ try {
417
+ const scaleCmd = `kubectl scale statefulset ${stsName} -n ${namespace} --replicas=${replicas} --timeout=2m`;
418
+ logger.info(`command: ${scaleCmd}`);
419
+ await execAsync(scaleCmd);
420
+ } catch (e) {
421
+ logger.verbose(`Scale up ${stsName} skipped: ${e}`);
269
422
  }
270
423
  }
271
424
  } else {
@@ -279,8 +432,20 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
279
432
  }
280
433
  }
281
434
  await sleep(10 * 1000);
282
- // Wait for pods to come back
283
- for (const component of podComponents){
435
+ // Wait for StatefulSets to have all replicas ready.
436
+ for (const component of statefulSetComponents){
437
+ try {
438
+ await waitForStatefulSetsReady({
439
+ namespace,
440
+ label: `app.kubernetes.io/component=${component}`,
441
+ timeoutSeconds: 600
442
+ });
443
+ } catch (e) {
444
+ logger.warn(`StatefulSet component ${component} may not be fully ready: ${e}`);
445
+ }
446
+ }
447
+ const nonStatefulSetComponents = podComponents.filter((c)=>!statefulSetComponents.includes(c));
448
+ for (const component of nonStatefulSetComponents){
284
449
  await waitForResourceByLabel({
285
450
  resource: 'pods',
286
451
  namespace: namespace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/end-to-end",
3
- "version": "0.0.1-commit.bf2612ae",
3
+ "version": "0.0.1-commit.c2595eba",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "inherits": [
@@ -25,44 +25,44 @@
25
25
  "formatting": "run -T prettier --check ./src && run -T eslint ./src"
26
26
  },
27
27
  "dependencies": {
28
- "@aztec/accounts": "0.0.1-commit.bf2612ae",
29
- "@aztec/archiver": "0.0.1-commit.bf2612ae",
30
- "@aztec/aztec": "0.0.1-commit.bf2612ae",
31
- "@aztec/aztec-node": "0.0.1-commit.bf2612ae",
32
- "@aztec/aztec.js": "0.0.1-commit.bf2612ae",
33
- "@aztec/bb-prover": "0.0.1-commit.bf2612ae",
34
- "@aztec/bb.js": "0.0.1-commit.bf2612ae",
35
- "@aztec/blob-client": "0.0.1-commit.bf2612ae",
36
- "@aztec/blob-lib": "0.0.1-commit.bf2612ae",
37
- "@aztec/bot": "0.0.1-commit.bf2612ae",
38
- "@aztec/cli": "0.0.1-commit.bf2612ae",
39
- "@aztec/constants": "0.0.1-commit.bf2612ae",
40
- "@aztec/entrypoints": "0.0.1-commit.bf2612ae",
41
- "@aztec/epoch-cache": "0.0.1-commit.bf2612ae",
42
- "@aztec/ethereum": "0.0.1-commit.bf2612ae",
43
- "@aztec/foundation": "0.0.1-commit.bf2612ae",
44
- "@aztec/kv-store": "0.0.1-commit.bf2612ae",
45
- "@aztec/l1-artifacts": "0.0.1-commit.bf2612ae",
46
- "@aztec/merkle-tree": "0.0.1-commit.bf2612ae",
47
- "@aztec/node-keystore": "0.0.1-commit.bf2612ae",
48
- "@aztec/noir-contracts.js": "0.0.1-commit.bf2612ae",
49
- "@aztec/noir-noirc_abi": "0.0.1-commit.bf2612ae",
50
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.bf2612ae",
51
- "@aztec/noir-test-contracts.js": "0.0.1-commit.bf2612ae",
52
- "@aztec/p2p": "0.0.1-commit.bf2612ae",
53
- "@aztec/protocol-contracts": "0.0.1-commit.bf2612ae",
54
- "@aztec/prover-client": "0.0.1-commit.bf2612ae",
55
- "@aztec/prover-node": "0.0.1-commit.bf2612ae",
56
- "@aztec/pxe": "0.0.1-commit.bf2612ae",
57
- "@aztec/sequencer-client": "0.0.1-commit.bf2612ae",
58
- "@aztec/simulator": "0.0.1-commit.bf2612ae",
59
- "@aztec/slasher": "0.0.1-commit.bf2612ae",
60
- "@aztec/stdlib": "0.0.1-commit.bf2612ae",
61
- "@aztec/telemetry-client": "0.0.1-commit.bf2612ae",
62
- "@aztec/test-wallet": "0.0.1-commit.bf2612ae",
63
- "@aztec/validator-client": "0.0.1-commit.bf2612ae",
64
- "@aztec/validator-ha-signer": "0.0.1-commit.bf2612ae",
65
- "@aztec/world-state": "0.0.1-commit.bf2612ae",
28
+ "@aztec/accounts": "0.0.1-commit.c2595eba",
29
+ "@aztec/archiver": "0.0.1-commit.c2595eba",
30
+ "@aztec/aztec": "0.0.1-commit.c2595eba",
31
+ "@aztec/aztec-node": "0.0.1-commit.c2595eba",
32
+ "@aztec/aztec.js": "0.0.1-commit.c2595eba",
33
+ "@aztec/bb-prover": "0.0.1-commit.c2595eba",
34
+ "@aztec/bb.js": "0.0.1-commit.c2595eba",
35
+ "@aztec/blob-client": "0.0.1-commit.c2595eba",
36
+ "@aztec/blob-lib": "0.0.1-commit.c2595eba",
37
+ "@aztec/bot": "0.0.1-commit.c2595eba",
38
+ "@aztec/cli": "0.0.1-commit.c2595eba",
39
+ "@aztec/constants": "0.0.1-commit.c2595eba",
40
+ "@aztec/entrypoints": "0.0.1-commit.c2595eba",
41
+ "@aztec/epoch-cache": "0.0.1-commit.c2595eba",
42
+ "@aztec/ethereum": "0.0.1-commit.c2595eba",
43
+ "@aztec/foundation": "0.0.1-commit.c2595eba",
44
+ "@aztec/kv-store": "0.0.1-commit.c2595eba",
45
+ "@aztec/l1-artifacts": "0.0.1-commit.c2595eba",
46
+ "@aztec/merkle-tree": "0.0.1-commit.c2595eba",
47
+ "@aztec/node-keystore": "0.0.1-commit.c2595eba",
48
+ "@aztec/noir-contracts.js": "0.0.1-commit.c2595eba",
49
+ "@aztec/noir-noirc_abi": "0.0.1-commit.c2595eba",
50
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.c2595eba",
51
+ "@aztec/noir-test-contracts.js": "0.0.1-commit.c2595eba",
52
+ "@aztec/p2p": "0.0.1-commit.c2595eba",
53
+ "@aztec/protocol-contracts": "0.0.1-commit.c2595eba",
54
+ "@aztec/prover-client": "0.0.1-commit.c2595eba",
55
+ "@aztec/prover-node": "0.0.1-commit.c2595eba",
56
+ "@aztec/pxe": "0.0.1-commit.c2595eba",
57
+ "@aztec/sequencer-client": "0.0.1-commit.c2595eba",
58
+ "@aztec/simulator": "0.0.1-commit.c2595eba",
59
+ "@aztec/slasher": "0.0.1-commit.c2595eba",
60
+ "@aztec/stdlib": "0.0.1-commit.c2595eba",
61
+ "@aztec/telemetry-client": "0.0.1-commit.c2595eba",
62
+ "@aztec/test-wallet": "0.0.1-commit.c2595eba",
63
+ "@aztec/validator-client": "0.0.1-commit.c2595eba",
64
+ "@aztec/validator-ha-signer": "0.0.1-commit.c2595eba",
65
+ "@aztec/world-state": "0.0.1-commit.c2595eba",
66
66
  "@iarna/toml": "^2.2.5",
67
67
  "@jest/globals": "^30.0.0",
68
68
  "@noble/curves": "=1.0.0",
@@ -14,13 +14,15 @@ import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
14
14
  import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
15
15
  import { SecretValue } from '@aztec/foundation/config';
16
16
  import { randomBytes } from '@aztec/foundation/crypto/random';
17
- import { withLogNameSuffix } from '@aztec/foundation/log';
17
+ import { withLoggerBindings } from '@aztec/foundation/log/server';
18
18
  import { retryUntil } from '@aztec/foundation/retry';
19
19
  import { sleep } from '@aztec/foundation/sleep';
20
20
  import { SpamContract } from '@aztec/noir-test-contracts.js/Spam';
21
+ import { TestContract } from '@aztec/noir-test-contracts.js/Test';
21
22
  import { getMockPubSubP2PServiceFactory } from '@aztec/p2p/test-helpers';
22
23
  import { ProverNode, type ProverNodeConfig, ProverNodePublisher } from '@aztec/prover-node';
23
24
  import type { TestProverNode } from '@aztec/prover-node/test';
25
+ import type { PXEConfig } from '@aztec/pxe/config';
24
26
  import {
25
27
  type SequencerClient,
26
28
  type SequencerEvents,
@@ -49,7 +51,11 @@ export const WORLD_STATE_BLOCK_CHECK_INTERVAL = 50;
49
51
  export const ARCHIVER_POLL_INTERVAL = 50;
50
52
  export const DEFAULT_L1_BLOCK_TIME = process.env.CI ? 12 : 8;
51
53
 
52
- export type EpochsTestOpts = Partial<SetupOptions> & { numberOfAccounts?: number };
54
+ export type EpochsTestOpts = Partial<SetupOptions> & {
55
+ numberOfAccounts?: number;
56
+ pxeOpts?: Partial<PXEConfig>;
57
+ aztecSlotDurationInL1Slots?: number;
58
+ };
53
59
 
54
60
  export type TrackedSequencerEvent = {
55
61
  [K in keyof SequencerEvents]: Parameters<SequencerEvents[K]>[0] & {
@@ -94,7 +100,7 @@ export class EpochsTestContext {
94
100
  ? parseInt(process.env.L1_BLOCK_TIME)
95
101
  : DEFAULT_L1_BLOCK_TIME;
96
102
  const ethereumSlotDuration = opts.ethereumSlotDuration ?? envEthereumSlotDuration;
97
- const aztecSlotDuration = opts.aztecSlotDuration ?? ethereumSlotDuration * 2;
103
+ const aztecSlotDuration = opts.aztecSlotDuration ?? (opts.aztecSlotDurationInL1Slots ?? 2) * ethereumSlotDuration;
98
104
  const aztecEpochDuration = opts.aztecEpochDuration ?? 6;
99
105
  const aztecProofSubmissionEpochs = opts.aztecProofSubmissionEpochs ?? 1;
100
106
  const l1PublishingTime = opts.l1PublishingTime ?? 1;
@@ -147,8 +153,9 @@ export class EpochsTestContext {
147
153
  l1PublishingTime,
148
154
  ...opts,
149
155
  },
150
- // Use checkpointed chain tip for PXE to avoid issues with blocks being dropped due to pruned anchor blocks.
151
- { syncChainTip: 'checkpointed' },
156
+ // Use checkpointed chain tip for PXE by default to avoid issues with blocks being dropped due to pruned anchor blocks.
157
+ // Can be overridden via opts.pxeOpts.
158
+ { syncChainTip: 'checkpointed', ...opts.pxeOpts },
152
159
  );
153
160
 
154
161
  this.context = context;
@@ -204,14 +211,14 @@ export class EpochsTestContext {
204
211
  public async createProverNode(opts: { dontStart?: boolean } & Partial<ProverNodeConfig> = {}) {
205
212
  this.logger.warn('Creating and syncing a simulated prover node...');
206
213
  const proverNodePrivateKey = this.getNextPrivateKey();
207
- const suffix = (this.proverNodes.length + 1).toString();
208
- const proverNode = await withLogNameSuffix(suffix, () =>
214
+ const proverIndex = this.proverNodes.length + 1;
215
+ const proverNode = await withLoggerBindings({ actor: `prover-${proverIndex}` }, () =>
209
216
  createAndSyncProverNode(
210
217
  proverNodePrivateKey,
211
218
  { ...this.context.config },
212
219
  {
213
220
  dataDirectory: join(this.context.config.dataDirectory!, randomBytes(8).toString('hex')),
214
- proverId: EthAddress.fromNumber(parseInt(suffix, 10)),
221
+ proverId: EthAddress.fromNumber(proverIndex),
215
222
  dontStart: opts.dontStart,
216
223
  ...opts,
217
224
  },
@@ -240,12 +247,13 @@ export class EpochsTestContext {
240
247
  private async createNode(
241
248
  opts: Partial<AztecNodeConfig> & { txDelayerMaxInclusionTimeIntoSlot?: number; dontStartSequencer?: boolean } = {},
242
249
  ) {
243
- const suffix = (this.nodes.length + 1).toString();
250
+ const nodeIndex = this.nodes.length + 1;
251
+ const actorPrefix = opts.disableValidator ? 'node' : 'validator';
244
252
  const { mockGossipSubNetwork } = this.context;
245
253
  const resolvedConfig = { ...this.context.config, ...opts };
246
254
  const p2pEnabled = resolvedConfig.p2pEnabled || mockGossipSubNetwork !== undefined;
247
255
  const p2pIp = resolvedConfig.p2pIp ?? (p2pEnabled ? '127.0.0.1' : undefined);
248
- const node = await withLogNameSuffix(suffix, () =>
256
+ const node = await withLoggerBindings({ actor: `${actorPrefix}-${nodeIndex}` }, () =>
249
257
  AztecNodeService.createAndSync(
250
258
  {
251
259
  ...resolvedConfig,
@@ -375,6 +383,19 @@ export class EpochsTestContext {
375
383
  return SpamContract.at(instance.address, wallet);
376
384
  }
377
385
 
386
+ /** Registers the TestContract on the given wallet. */
387
+ public async registerTestContract(wallet: Wallet, salt = Fr.ZERO) {
388
+ const instance = await getContractInstanceFromInstantiationParams(TestContract.artifact, {
389
+ constructorArgs: [],
390
+ constructorArtifact: undefined,
391
+ salt,
392
+ publicKeys: undefined,
393
+ deployer: undefined,
394
+ });
395
+ await wallet.registerContract(instance, TestContract.artifact);
396
+ return TestContract.at(instance.address, wallet);
397
+ }
398
+
378
399
  /** Creates an L1 client using a fresh account with funds from anvil, with a tx delayer already set up. */
379
400
  public async createL1Client() {
380
401
  const { client, delayer } = withDelayer(
@@ -15,6 +15,7 @@ const AZTEC_GENERATE_TEST_DATA = !!process.env.AZTEC_GENERATE_TEST_DATA;
15
15
  */
16
16
  export async function writeJson(
17
17
  fileName: string,
18
+ checkpointHeader: CheckpointHeader,
18
19
  block: L2Block,
19
20
  l1ToL2Content: Fr[],
20
21
  blobs: Blob[],
@@ -33,12 +34,6 @@ export async function writeJson(
33
34
  return `0x${buffer.toString('hex').padStart(size, '0')}`;
34
35
  };
35
36
 
36
- // Create a checkpoint header for this block
37
- const checkpointHeader = CheckpointHeader.random({
38
- slotNumber: block.slot,
39
- timestamp: block.timestamp,
40
- });
41
-
42
37
  const jsonObject = {
43
38
  populate: {
44
39
  l1ToL2Content: l1ToL2Content.map(value => asHex(value)),
@@ -56,7 +56,11 @@ export const submitTransactions = async (
56
56
  ): Promise<TxHash[]> => {
57
57
  const rpcConfig = getRpcConfig();
58
58
  rpcConfig.proverEnabled = false;
59
- const wallet = await TestWallet.create(node, { ...getPXEConfig(), proverEnabled: false }, { useLogSuffix: true });
59
+ const wallet = await TestWallet.create(
60
+ node,
61
+ { ...getPXEConfig(), proverEnabled: false },
62
+ { loggerActorLabel: 'pxe-tx' },
63
+ );
60
64
  const fundedAccountManager = await wallet.createSchnorrAccount(fundedAccount.secret, fundedAccount.salt);
61
65
  return submitTxsTo(wallet, fundedAccountManager.address, numTxs, logger);
62
66
  };
@@ -70,7 +74,11 @@ export async function prepareTransactions(
70
74
  const rpcConfig = getRpcConfig();
71
75
  rpcConfig.proverEnabled = false;
72
76
 
73
- const wallet = await TestWallet.create(node, { ...getPXEConfig(), proverEnabled: false }, { useLogSuffix: true });
77
+ const wallet = await TestWallet.create(
78
+ node,
79
+ { ...getPXEConfig(), proverEnabled: false },
80
+ { loggerActorLabel: 'pxe-tx' },
81
+ );
74
82
  const fundedAccountManager = await wallet.createSchnorrAccount(fundedAccount.secret, fundedAccount.salt);
75
83
 
76
84
  const testContractInstance = await getContractInstanceFromInstantiationParams(TestContractArtifact, {
@@ -198,7 +198,7 @@ export class FullProverTest {
198
198
  this.aztecNode,
199
199
  { proverEnabled: this.realProofs },
200
200
  undefined,
201
- true,
201
+ 'pxe-proven',
202
202
  );
203
203
  this.logger.debug(`Contract address ${this.fakeProofsAsset.address}`);
204
204
  await provenWallet.registerContract(this.fakeProofsAssetInstance, TokenContract.artifact);
@@ -41,7 +41,7 @@ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
41
41
  import { SecretValue } from '@aztec/foundation/config';
42
42
  import { randomBytes } from '@aztec/foundation/crypto/random';
43
43
  import { tryRmDir } from '@aztec/foundation/fs';
44
- import { withLogNameSuffix } from '@aztec/foundation/log';
44
+ import { withLoggerBindings } from '@aztec/foundation/log/server';
45
45
  import { retryUntil } from '@aztec/foundation/retry';
46
46
  import { sleep } from '@aztec/foundation/sleep';
47
47
  import { DateProvider, TestDateProvider } from '@aztec/foundation/timer';
@@ -125,14 +125,14 @@ export async function setupSharedBlobStorage(config: { dataDirectory?: string }
125
125
  * @param aztecNode - An instance of Aztec Node.
126
126
  * @param opts - Partial configuration for the PXE.
127
127
  * @param logger - The logger to be used.
128
- * @param useLogSuffix - Whether to add a randomly generated suffix to the PXE debug logs.
128
+ * @param actor - Actor label to include in log output (e.g., 'pxe-test').
129
129
  * @returns A test wallet, logger and teardown function.
130
130
  */
131
131
  export async function setupPXEAndGetWallet(
132
132
  aztecNode: AztecNode,
133
133
  opts: Partial<PXEConfig> = {},
134
134
  logger = getLogger(),
135
- useLogSuffix = false,
135
+ actor?: string,
136
136
  ): Promise<{
137
137
  wallet: TestWallet;
138
138
  logger: Logger;
@@ -150,9 +150,7 @@ export async function setupPXEAndGetWallet(
150
150
 
151
151
  const teardown = configuredDataDirectory ? () => Promise.resolve() : () => tryRmDir(PXEConfig.dataDirectory!);
152
152
 
153
- const wallet = await TestWallet.create(aztecNode, PXEConfig, {
154
- useLogSuffix,
155
- });
153
+ const wallet = await TestWallet.create(aztecNode, PXEConfig, { loggerActorLabel: actor });
156
154
 
157
155
  return {
158
156
  wallet,
@@ -392,7 +390,7 @@ export async function setup(
392
390
  const res = await startAnvil({
393
391
  l1BlockTime: opts.ethereumSlotDuration,
394
392
  accounts: opts.anvilAccounts,
395
- port: opts.anvilPort,
393
+ port: opts.anvilPort ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : undefined),
396
394
  });
397
395
  anvil = res.anvil;
398
396
  config.l1RpcUrls = [res.rpcUrl];
@@ -574,10 +572,12 @@ export async function setup(
574
572
  }
575
573
  }
576
574
 
577
- const aztecNodeService = await AztecNodeService.createAndSync(
578
- config,
579
- { dateProvider, telemetry: telemetryClient, p2pClientDeps, logger: createLogger('node:MAIN-aztec-node') },
580
- { prefilledPublicData },
575
+ const aztecNodeService = await withLoggerBindings({ actor: 'node-0' }, () =>
576
+ AztecNodeService.createAndSync(
577
+ config,
578
+ { dateProvider, telemetry: telemetryClient, p2pClientDeps },
579
+ { prefilledPublicData },
580
+ ),
581
581
  );
582
582
  const sequencerClient = aztecNodeService.getSequencer();
583
583
 
@@ -611,7 +611,7 @@ export async function setup(
611
611
  pxeConfig.dataDirectory = path.join(directoryToCleanup, randomBytes(8).toString('hex'));
612
612
  // For tests we only want proving enabled if specifically requested
613
613
  pxeConfig.proverEnabled = !!pxeOpts.proverEnabled;
614
- const wallet = await TestWallet.create(aztecNodeService, pxeConfig);
614
+ const wallet = await TestWallet.create(aztecNodeService, pxeConfig, { loggerActorLabel: 'pxe-0' });
615
615
 
616
616
  if (opts.walletMinFeePadding !== undefined) {
617
617
  wallet.setMinFeePadding(opts.walletMinFeePadding);
@@ -797,7 +797,7 @@ export function createAndSyncProverNode(
797
797
  prefilledPublicData: PublicDataTreeLeaf[] = [],
798
798
  proverNodeDeps: ProverNodeDeps = {},
799
799
  ) {
800
- return withLogNameSuffix('prover-node', async () => {
800
+ return withLoggerBindings({ actor: 'prover-0' }, async () => {
801
801
  const aztecNodeTxProvider = aztecNode && {
802
802
  getTxByHash: aztecNode.getTxByHash.bind(aztecNode),
803
803
  getTxsByHash: aztecNode.getTxsByHash.bind(aztecNode),