@tokamak-private-dapps/private-state-cli 0.1.4 → 0.1.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6 - 2026-04-29
4
+
5
+ - Added `--groth16-cli-version` and `--tokamak-zk-evm-cli-version` install options with npm latest defaults.
6
+ - Installed selected proof backend package versions into managed runtime directories.
7
+ - Downloaded Groth16 CRS artifacts matching the selected Groth16 CLI version.
8
+ - Checked channel verifier compatible backend versions before local proof generation.
9
+ - Reported selected proof backend runtime versions from `--doctor`.
10
+
11
+ ## 0.1.5 - 2026-04-28
12
+
13
+ - Switched channel balance proof generation to invoke `tokamak-groth16 --prove` instead of importing Groth16 proof internals directly.
14
+ - Read proof artifacts from the fixed Groth16 runtime workspace manifest.
15
+
3
16
  ## 0.1.4 - 2026-04-28
4
17
 
5
18
  - Paced chunked log recovery queries at five requests per second to avoid RPC throughput bursts.
package/README.md CHANGED
@@ -15,6 +15,15 @@ artifacts:
15
15
  private-state-cli --install
16
16
  ```
17
17
 
18
+ By default, `--install` resolves the latest `@tokamak-zk-evm/cli` and `@tokamak-private-dapps/groth16` versions from
19
+ the npm registry. To pin exact proof backend versions for a channel, pass explicit versions:
20
+
21
+ ```bash
22
+ private-state-cli --install --tokamak-zk-evm-cli-version 2.0.8 --groth16-cli-version 0.1.1
23
+ ```
24
+
25
+ The Groth16 installer downloads the public Google Drive CRS archive with the same version as the selected Groth16 CLI.
26
+
18
27
  `--install` downloads public deployment artifacts from the configured artifact index. It does not read repository-local
19
28
  `deployment/` outputs by default. Repository development workflows that need local anvil artifacts can opt in explicitly:
20
29
 
@@ -52,7 +61,7 @@ A common private-state flow is:
52
61
  Use `private-state-cli --help` for the full command list and required options.
53
62
 
54
63
  `private-state-cli --doctor` reports the CLI package version, dependency versions recorded by the last
55
- `private-state-cli --install`, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
64
+ `private-state-cli --install`, selected proof backend runtime versions, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
56
65
  install mode, Docker mode, CUDA runtime metadata, live `nvidia-smi` and Docker GPU probe results, and Groth16
57
66
  runtime health. The doctor check fails when the Tokamak Docker `useGpus` metadata does not match the live GPU probes.
58
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokamak-private-dapps/private-state-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Command-line client for the Tokamak private-state DApp.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "Tokamak Network",
@@ -43,8 +43,8 @@
43
43
  "@ethereumjs/util": "^10.1.1",
44
44
  "@noble/curves": "^1.2.0",
45
45
  "@tokamak-private-dapps/common-library": "^0.1.0",
46
- "@tokamak-private-dapps/groth16": "^0.1.0",
47
- "@tokamak-zk-evm/cli": "^2.0.8",
46
+ "@tokamak-private-dapps/groth16": "^0.1.2",
47
+ "@tokamak-zk-evm/cli": "^2.0.12",
48
48
  "ethers": "^6.14.1",
49
49
  "tokamak-l2js": "^0.1.3"
50
50
  },
@@ -45,11 +45,9 @@ import {
45
45
  } from "@ethereumjs/util";
46
46
  import { deriveRpcUrl, resolveCliNetwork } from "@tokamak-private-dapps/common-library/network-config";
47
47
  import {
48
- buildTokamakCliInvocation,
49
48
  resolveTokamakBlockInputConfig,
50
- resolveTokamakCliPackageRoot,
51
- resolveTokamakCliResourceDir,
52
- resolveTokamakCliRuntimeRoot,
49
+ resolveTokamakCliEntryPath,
50
+ resolveTokamakCliPackageRoot as resolveBundledTokamakCliPackageRoot,
53
51
  } from "@tokamak-private-dapps/common-library/tokamak-runtime-paths";
54
52
  import {
55
53
  DEFAULT_PUBLIC_ARTIFACT_INDEX_FILE_ID,
@@ -66,8 +64,8 @@ import { toGroth16SolidityProof } from "@tokamak-private-dapps/common-library/gr
66
64
  import {
67
65
  PUBLIC_GROTH16_MPC_DRIVE_FOLDER_ID,
68
66
  downloadLatestPublicGroth16MpcArtifacts,
67
+ downloadPublicGroth16MpcArtifactsByVersion,
69
68
  } from "@tokamak-private-dapps/groth16/public-drive-crs";
70
- import { main as generateUpdateTreeProof } from "@tokamak-private-dapps/groth16/prover/updateTree/generateProof";
71
69
  import {
72
70
  CHANNEL_BOUND_L2_DERIVATION_MODE,
73
71
  deriveChannelIdFromName,
@@ -86,17 +84,20 @@ const require = createRequire(import.meta.url);
86
84
  const defaultCommandCwd = process.cwd();
87
85
  const privateStateCliPackageRoot = path.dirname(require.resolve("./package.json"));
88
86
  const workspaceRoot = path.resolve(os.homedir(), "tokamak-private-channels", "workspace");
89
- const tokamakCliInvocation = buildTokamakCliInvocation();
90
- const tokamakCliCommand = tokamakCliInvocation.command;
91
- const tokamakCliBaseArgs = tokamakCliInvocation.args;
92
87
  const flatDeploymentArtifactPathsByChainId = new Map();
93
88
  const DOCKER_CUDA_PROBE_IMAGE = "nvidia/cuda:12.2.0-base-ubuntu22.04";
94
89
  const DOCTOR_GPU_PROBE_TIMEOUT_MS = 120000;
90
+ const GROTH16_PACKAGE_NAME = "@tokamak-private-dapps/groth16";
91
+ const TOKAMAK_ZKEVM_CLI_PACKAGE_NAME = "@tokamak-zk-evm/cli";
95
92
 
96
93
  const abiCoder = AbiCoder.defaultAbiCoder();
97
94
  const erc20MetadataAbi = [
98
95
  "function decimals() view returns (uint8)",
99
96
  ];
97
+ const channelVerifierVersionAbi = [
98
+ "function grothVerifierCompatibleBackendVersion() view returns (string)",
99
+ "function tokamakVerifierCompatibleBackendVersion() view returns (string)",
100
+ ];
100
101
  const {
101
102
  aPubBlockLength: TOKAMAK_APUB_BLOCK_LENGTH,
102
103
  previousBlockHashCount: TOKAMAK_PREVIOUS_BLOCK_HASH_COUNT,
@@ -953,31 +954,36 @@ function clearWalletRecoveryArtifacts(walletDir) {
953
954
  }
954
955
 
955
956
  async function handleInstallZkEvm({ args }) {
956
- const installArgs = [...tokamakCliBaseArgs, "--install"];
957
- if (args.docker) {
958
- installArgs.push("--docker");
959
- }
960
- run(tokamakCliCommand, installArgs);
961
- const tokamakRuntimeRoot = resolveTokamakCliRuntimeRoot();
962
- const groth16Runtime = installGroth16RuntimeForPrivateState({
957
+ const selectedVersions = await resolvePrivateStateInstallRuntimeVersions(args);
958
+ const tokamakCliRuntime = await installTokamakCliRuntimeForPrivateState({
959
+ version: selectedVersions.tokamak,
960
+ docker: Boolean(args.docker),
961
+ });
962
+ const groth16Runtime = await installGroth16RuntimeForPrivateState({
963
+ version: selectedVersions.groth16,
963
964
  docker: Boolean(args.docker),
964
965
  });
965
966
  const localDeploymentBaseRoot = args.includeLocalArtifacts ? process.cwd() : null;
966
967
  const deploymentArtifacts = await installPrivateStateCliArtifacts({
967
968
  dappName: PRIVATE_STATE_DAPP_LABEL,
968
969
  localDeploymentBaseRoot,
970
+ groth16CrsVersion: selectedVersions.groth16,
969
971
  });
970
972
  const installManifest = writePrivateStateCliInstallManifest({
971
973
  dockerRequested: Boolean(args.docker),
972
974
  includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
973
975
  localDeploymentBaseRoot,
974
976
  deploymentArtifacts,
977
+ selectedVersions,
978
+ tokamakCliRuntime,
975
979
  groth16Runtime,
976
980
  });
977
981
  printJson({
978
982
  action: "install",
979
- tokamakCli: tokamakCliBaseArgs[0],
980
- runtimeRoot: tokamakRuntimeRoot,
983
+ selectedVersions,
984
+ tokamakCli: tokamakCliRuntime.entryPath,
985
+ runtimeRoot: tokamakCliRuntime.runtimeRoot,
986
+ tokamakCliRuntime,
981
987
  groth16Runtime,
982
988
  docker: Boolean(args.docker),
983
989
  includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
@@ -997,12 +1003,13 @@ async function handleInstallZkEvm({ args }) {
997
1003
 
998
1004
  async function handleUninstallZkEvm() {
999
1005
  let runtimeRoot = null;
1006
+ const invocation = buildTokamakCliInvocationForPackageRoot();
1000
1007
  try {
1001
- runtimeRoot = resolveTokamakCliRuntimeRoot();
1008
+ runtimeRoot = inspectTokamakCliRuntime({ packageRoot: invocation.packageRoot }).runtimeRoot;
1002
1009
  } catch {
1003
1010
  runtimeRoot = null;
1004
1011
  }
1005
- run(tokamakCliCommand, [...tokamakCliBaseArgs, "--uninstall"]);
1012
+ run(invocation.command, [...invocation.args, "--uninstall"], { cwd: invocation.packageRoot });
1006
1013
 
1007
1014
  printJson({
1008
1015
  action: "uninstall-zk-evm",
@@ -1310,10 +1317,16 @@ async function handleGrothVaultMove({ args, provider, direction }) {
1310
1317
  const contextResult = await loadPreferredWalletChannelContext({ walletContext, provider });
1311
1318
  const context = contextResult.context;
1312
1319
  const network = contextResult.network;
1320
+ const operationName = args.command === "withdraw-channel"
1321
+ ? "withdraw-channel"
1322
+ : direction === "deposit"
1323
+ ? "deposit-channel"
1324
+ : "withdraw";
1313
1325
  expect(
1314
1326
  ethers.toBigInt(walletContext.wallet.channelId) === ethers.toBigInt(context.workspace.channelId),
1315
1327
  "The provided wallet does not belong to the selected channel.",
1316
1328
  );
1329
+ await assertChannelProofBackendVersionCompatibility({ context, operationName });
1317
1330
 
1318
1331
  const { signer, l2Identity } = restoreWalletParticipant(walletContext, provider);
1319
1332
  const amountInput = requireArg(args.amount, "--amount");
@@ -1365,11 +1378,6 @@ async function handleGrothVaultMove({ args, provider, direction }) {
1365
1378
  nextValue = currentValue - amount;
1366
1379
  }
1367
1380
 
1368
- const operationName = args.command === "withdraw-channel"
1369
- ? "withdraw-channel"
1370
- : direction === "deposit"
1371
- ? "deposit-channel"
1372
- : "withdraw";
1373
1381
  const operationDir = createWalletOperationDir(
1374
1382
  walletContext.walletName,
1375
1383
  walletContext.wallet.network,
@@ -3058,6 +3066,7 @@ async function executeWalletTemplateSend({
3058
3066
  }) {
3059
3067
  await assertWorkspaceAlignedWithChain(context, signer.provider);
3060
3068
  assertWalletMatchesChannelContext(wallet, l2Identity, context);
3069
+ await assertChannelProofBackendVersionCompatibility({ context, operationName });
3061
3070
 
3062
3071
  const controllerAbi = readJson(
3063
3072
  requireLatestDappDeployArtifactPath(context.workspace.chainId, path.basename(templatePayload.abiFile)),
@@ -3429,6 +3438,111 @@ async function assertWorkspaceAlignedWithChain(context) {
3429
3438
  );
3430
3439
  }
3431
3440
 
3441
+ async function assertChannelProofBackendVersionCompatibility({ context, operationName }) {
3442
+ const channelVersions = await readChannelVerifierCompatibleBackendVersions(context);
3443
+ const localVersions = readLocalProofBackendPackageVersions();
3444
+ const checks = [
3445
+ {
3446
+ label: "Groth16",
3447
+ packageName: GROTH16_PACKAGE_NAME,
3448
+ channelVersion: channelVersions.groth16,
3449
+ localVersion: localVersions.groth16,
3450
+ },
3451
+ {
3452
+ label: "Tokamak zk-EVM",
3453
+ packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
3454
+ channelVersion: channelVersions.tokamak,
3455
+ localVersion: localVersions.tokamak,
3456
+ },
3457
+ ];
3458
+ const mismatches = checks.filter(({ channelVersion, localVersion }) => channelVersion !== localVersion);
3459
+ if (mismatches.length === 0) {
3460
+ return;
3461
+ }
3462
+
3463
+ throw new Error(
3464
+ [
3465
+ `Channel proof backend version mismatch before ${operationName} proof generation.`,
3466
+ `Channel: ${context.workspace.channelName ?? context.workspaceName ?? context.workspace.channelId}.`,
3467
+ ...mismatches.map(({ label, packageName, channelVersion, localVersion }) => (
3468
+ `${label} verifier expects ${packageName} ${channelVersion}, but the local installed package version is ${localVersion}.`
3469
+ )),
3470
+ "Install the matching CLI package versions before generating proofs for this channel.",
3471
+ ].join(" "),
3472
+ );
3473
+ }
3474
+
3475
+ async function readChannelVerifierCompatibleBackendVersions(context) {
3476
+ const channelManagerAddress = getAddress(context.workspace.channelManager);
3477
+ const channelManager = new Contract(
3478
+ channelManagerAddress,
3479
+ channelVerifierVersionAbi,
3480
+ context.channelManager.runner,
3481
+ );
3482
+ try {
3483
+ const [groth16, tokamak] = await Promise.all([
3484
+ channelManager.grothVerifierCompatibleBackendVersion(),
3485
+ channelManager.tokamakVerifierCompatibleBackendVersion(),
3486
+ ]);
3487
+ return {
3488
+ groth16: requireVersionString(groth16, "channel Groth16 verifier compatibleBackendVersion"),
3489
+ tokamak: requireVersionString(tokamak, "channel Tokamak verifier compatibleBackendVersion"),
3490
+ };
3491
+ } catch (error) {
3492
+ throw new Error(
3493
+ [
3494
+ `Unable to read verifier compatibleBackendVersion values from channel manager ${channelManagerAddress}.`,
3495
+ "The target channel must expose grothVerifierCompatibleBackendVersion() and tokamakVerifierCompatibleBackendVersion().",
3496
+ ].join(" "),
3497
+ { cause: error },
3498
+ );
3499
+ }
3500
+ }
3501
+
3502
+ function readLocalProofBackendPackageVersions() {
3503
+ return {
3504
+ groth16: requirePackageReportVersion(
3505
+ readPackageReport({
3506
+ name: GROTH16_PACKAGE_NAME,
3507
+ packageJsonPath: path.join(resolveActiveGroth16PackageRoot(), "package.json"),
3508
+ }),
3509
+ ),
3510
+ tokamak: requirePackageReportVersion(readTokamakCliPackageReport()),
3511
+ };
3512
+ }
3513
+
3514
+ function readTokamakCliPackageReport() {
3515
+ try {
3516
+ return readPackageReport({
3517
+ name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
3518
+ packageJsonPath: path.join(resolveActiveTokamakCliPackageRoot(), "package.json"),
3519
+ });
3520
+ } catch (error) {
3521
+ return {
3522
+ name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
3523
+ version: null,
3524
+ packageRoot: null,
3525
+ error: error.message,
3526
+ ok: false,
3527
+ };
3528
+ }
3529
+ }
3530
+
3531
+ function requirePackageReportVersion(report) {
3532
+ if (!report.version) {
3533
+ throw new Error(
3534
+ `Unable to determine local ${report.name} package version${report.error ? `: ${report.error}` : "."}`,
3535
+ );
3536
+ }
3537
+ return requireVersionString(report.version, `${report.name} package version`);
3538
+ }
3539
+
3540
+ function requireVersionString(value, label) {
3541
+ const normalized = String(value ?? "").trim();
3542
+ expect(normalized.length > 0, `${label} is missing.`);
3543
+ return normalized;
3544
+ }
3545
+
3432
3546
  async function buildGrothTransition({ operationDir, workspace, stateManager, vaultAddress, keyHex, nextValue }) {
3433
3547
  const vaultAddressObj = createAddressFromString(vaultAddress);
3434
3548
  const keyBigInt = ethers.toBigInt(keyHex);
@@ -3457,10 +3571,7 @@ async function buildGrothTransition({ operationDir, workspace, stateManager, vau
3457
3571
 
3458
3572
  const inputPath = path.join(operationDir, "input.json");
3459
3573
  writeJson(inputPath, input);
3460
- const proofManifest = await generateUpdateTreeProof([
3461
- "--input",
3462
- inputPath,
3463
- ]);
3574
+ const proofManifest = runGroth16UpdateTreeProof(inputPath);
3464
3575
 
3465
3576
  const proofJson = readJson(proofManifest.proofPath);
3466
3577
  const publicSignals = readJson(proofManifest.publicPath);
@@ -3507,6 +3618,21 @@ function runCaptured(command, args, { cwd = defaultCommandCwd, env = process.env
3507
3618
  };
3508
3619
  }
3509
3620
 
3621
+ function runGroth16UpdateTreeProof(inputPath) {
3622
+ const packageRoot = resolveActiveGroth16PackageRoot();
3623
+ const entryPath = resolveGroth16CliEntryPath(packageRoot);
3624
+ run(process.execPath, [entryPath, "--prove", inputPath], { cwd: packageRoot });
3625
+ const manifestPath = groth16ProofManifestPath();
3626
+ const manifest = readJson(manifestPath);
3627
+ expect(typeof manifest.proofPath === "string" && manifest.proofPath.length > 0, "Groth16 proof manifest is missing proofPath.");
3628
+ expect(typeof manifest.publicPath === "string" && manifest.publicPath.length > 0, "Groth16 proof manifest is missing publicPath.");
3629
+ return manifest;
3630
+ }
3631
+
3632
+ function groth16ProofManifestPath() {
3633
+ return path.join(os.homedir(), "tokamak-private-channels", "groth16", "proof", "proof-manifest.json");
3634
+ }
3635
+
3510
3636
  function runTokamakProofPipeline({ operationDir, bundlePath }) {
3511
3637
  runTokamakCliStage({
3512
3638
  operationDir,
@@ -3546,7 +3672,8 @@ function printLocalProofGenerationNotice(operationName) {
3546
3672
  }
3547
3673
 
3548
3674
  function runTokamakCliStage({ operationDir, stageName, args }) {
3549
- const result = runCaptured(tokamakCliCommand, [...tokamakCliBaseArgs, ...args]);
3675
+ const invocation = buildTokamakCliInvocationForPackageRoot();
3676
+ const result = runCaptured(invocation.command, [...invocation.args, ...args], { cwd: invocation.packageRoot });
3550
3677
  const logPath = writeTokamakCliStageLog(operationDir, stageName, result);
3551
3678
  if (result.status !== 0) {
3552
3679
  throw new Error(
@@ -3605,6 +3732,7 @@ function findTokamakConsoleError({ stdout, stderr }) {
3605
3732
  function copyTokamakOperationArtifacts(operationDir) {
3606
3733
  const resourceRoot = path.join(operationDir, "resource");
3607
3734
  fs.rmSync(resourceRoot, { recursive: true, force: true });
3735
+ const runtimeRoot = requireActiveTokamakCliRuntimeRoot();
3608
3736
 
3609
3737
  const requiredFiles = [
3610
3738
  ["preprocess", "output", "preprocess.json"],
@@ -3614,7 +3742,7 @@ function copyTokamakOperationArtifacts(operationDir) {
3614
3742
  ];
3615
3743
 
3616
3744
  for (const segments of requiredFiles) {
3617
- const sourcePath = resolveTokamakCliResourceDir(...segments);
3745
+ const sourcePath = resolveTokamakCliResourceDirForRuntimeRoot(runtimeRoot, ...segments);
3618
3746
  const targetPath = path.join(resourceRoot, ...segments);
3619
3747
  ensureDir(path.dirname(targetPath));
3620
3748
  fs.copyFileSync(sourcePath, targetPath);
@@ -4578,9 +4706,23 @@ function assertInstallZkEvmArgs(args) {
4578
4706
  assertAllowedCommandKeys(
4579
4707
  args,
4580
4708
  "--install",
4581
- new Set(["command", "positional", "install", "docker", "includeLocalArtifacts"]),
4582
- "optional --docker and --include-local-artifacts",
4709
+ new Set([
4710
+ "command",
4711
+ "positional",
4712
+ "install",
4713
+ "docker",
4714
+ "includeLocalArtifacts",
4715
+ "groth16CliVersion",
4716
+ "tokamakZkEvmCliVersion",
4717
+ ]),
4718
+ "optional --docker, --include-local-artifacts, --groth16-cli-version, and --tokamak-zk-evm-cli-version",
4583
4719
  );
4720
+ if (args.groth16CliVersion !== undefined) {
4721
+ requireSemverVersion(args.groth16CliVersion, "--groth16-cli-version");
4722
+ }
4723
+ if (args.tokamakZkEvmCliVersion !== undefined) {
4724
+ requireSemverVersion(args.tokamakZkEvmCliVersion, "--tokamak-zk-evm-cli-version");
4725
+ }
4584
4726
  }
4585
4727
 
4586
4728
  function assertUninstallZkEvmArgs(args) {
@@ -4790,8 +4932,9 @@ function persistCurrentState(context) {
4790
4932
  function printHelp() {
4791
4933
  console.log(`
4792
4934
  Commands:
4793
- --install [--docker] [--include-local-artifacts]
4935
+ --install [--docker] [--include-local-artifacts] [--groth16-cli-version <VERSION>] [--tokamak-zk-evm-cli-version <VERSION>]
4794
4936
  Install the Tokamak zk-EVM CLI runtime, Groth16 runtime, and private-state deployment artifacts
4937
+ Version options install exact CLI package versions; omitted versions resolve to npm registry latest
4795
4938
  Use --docker on Linux to forward Docker mode to the Tokamak zk-EVM and Groth16 runtimes
4796
4939
  Use --include-local-artifacts to also install local deployment/ artifacts from the current working directory
4797
4940
 
@@ -5054,6 +5197,14 @@ function expect(condition, message) {
5054
5197
  }
5055
5198
  }
5056
5199
 
5200
+ function requireSemverVersion(value, label) {
5201
+ const normalized = requireNonEmptyString(value, label);
5202
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?(?:\+[0-9A-Za-z.]+)?$/.test(normalized)) {
5203
+ throw new Error(`${label} must be an exact semantic version. Received: ${normalized}`);
5204
+ }
5205
+ return normalized;
5206
+ }
5207
+
5057
5208
  function resolveArtifactCacheBaseRoot(
5058
5209
  cacheBaseRoot = process.env.PRIVATE_STATE_ARTIFACT_CACHE_ROOT
5059
5210
  ?? process.env.TOKAMAK_PRIVATE_CHANNELS_ROOT
@@ -5066,6 +5217,10 @@ function privateStateCliArtifactRoot(cacheBaseRoot = resolveArtifactCacheBaseRoo
5066
5217
  return path.join(resolveArtifactCacheBaseRoot(cacheBaseRoot), "dapps", "private-state");
5067
5218
  }
5068
5219
 
5220
+ function privateStateCliRuntimeRoot(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
5221
+ return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "runtimes");
5222
+ }
5223
+
5069
5224
  function privateStateCliArtifactChainDir(cacheBaseRoot = resolveArtifactCacheBaseRoot(), chainId) {
5070
5225
  return path.join(privateStateCliArtifactRoot(cacheBaseRoot), `chain-id-${requireChainId(chainId)}`);
5071
5226
  }
@@ -5090,11 +5245,17 @@ function privateStateCliInstallManifestPath(cacheBaseRoot = resolveArtifactCache
5090
5245
  return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "install-manifest.json");
5091
5246
  }
5092
5247
 
5248
+ function readPrivateStateCliInstallManifest(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
5249
+ return readJsonIfExists(privateStateCliInstallManifestPath(cacheBaseRoot));
5250
+ }
5251
+
5093
5252
  function writePrivateStateCliInstallManifest({
5094
5253
  dockerRequested,
5095
5254
  includeLocalArtifacts,
5096
5255
  localDeploymentBaseRoot,
5097
5256
  deploymentArtifacts,
5257
+ selectedVersions,
5258
+ tokamakCliRuntime,
5098
5259
  groth16Runtime,
5099
5260
  }) {
5100
5261
  const manifestPath = privateStateCliInstallManifestPath(deploymentArtifacts.cacheBaseRoot);
@@ -5110,6 +5271,8 @@ function writePrivateStateCliInstallManifest({
5110
5271
  includeLocalArtifacts,
5111
5272
  localDeploymentBaseRoot,
5112
5273
  artifactCacheRoot: deploymentArtifacts.cacheBaseRoot,
5274
+ selectedVersions,
5275
+ tokamakCliRuntime,
5113
5276
  groth16Runtime,
5114
5277
  installedDeploymentArtifacts: deploymentArtifacts.installed.map((entry) => ({
5115
5278
  chainId: entry.chainId,
@@ -5138,6 +5301,11 @@ function buildDoctorReport() {
5138
5301
  const tokamakCli = inspectTokamakCliRuntime();
5139
5302
  const groth16Runtime = inspectGroth16Runtime();
5140
5303
  const gpuDockerReadiness = inspectGpuDockerReadiness(tokamakCli);
5304
+ const selectedRuntimeVersionCheck = buildSelectedRuntimeVersionCheck({
5305
+ installManifest,
5306
+ tokamakCli,
5307
+ groth16Runtime,
5308
+ });
5141
5309
  const checks = [
5142
5310
  {
5143
5311
  name: "dependency package versions",
@@ -5150,6 +5318,7 @@ function buildDoctorReport() {
5150
5318
  error: entry.error,
5151
5319
  })),
5152
5320
  },
5321
+ selectedRuntimeVersionCheck,
5153
5322
  {
5154
5323
  name: "tokamak zk-evm runtime",
5155
5324
  ok: tokamakCli.installed,
@@ -5203,6 +5372,9 @@ function buildDoctorReport() {
5203
5372
  installedAt: installManifest?.installedAt ?? null,
5204
5373
  dockerRequested: installManifest?.install?.dockerRequested ?? null,
5205
5374
  includeLocalArtifacts: installManifest?.install?.includeLocalArtifacts ?? null,
5375
+ selectedVersions: installManifest?.install?.selectedVersions ?? null,
5376
+ tokamakCliRuntime: installManifest?.install?.tokamakCliRuntime ?? null,
5377
+ groth16Runtime: installManifest?.install?.groth16Runtime ?? null,
5206
5378
  },
5207
5379
  dependencies: dependencyReports,
5208
5380
  tokamakCli,
@@ -5212,17 +5384,212 @@ function buildDoctorReport() {
5212
5384
  };
5213
5385
  }
5214
5386
 
5215
- function installGroth16RuntimeForPrivateState({ docker }) {
5216
- const packageRoot = resolveGroth16PackageRoot();
5387
+ function buildSelectedRuntimeVersionCheck({ installManifest, tokamakCli, groth16Runtime }) {
5388
+ const selectedVersions = installManifest?.install?.selectedVersions ?? null;
5389
+ const details = [
5390
+ {
5391
+ name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5392
+ selectedVersion: selectedVersions?.tokamak ?? null,
5393
+ installedVersion: tokamakCli.packageVersion ?? null,
5394
+ ok: !selectedVersions?.tokamak || selectedVersions.tokamak === tokamakCli.packageVersion,
5395
+ },
5396
+ {
5397
+ name: GROTH16_PACKAGE_NAME,
5398
+ selectedVersion: selectedVersions?.groth16 ?? null,
5399
+ installedVersion: groth16Runtime.packageVersion ?? null,
5400
+ crsVersion: groth16Runtime.crsVersion ?? null,
5401
+ ok: !selectedVersions?.groth16
5402
+ || (
5403
+ selectedVersions.groth16 === groth16Runtime.packageVersion
5404
+ && selectedVersions.groth16 === groth16Runtime.crsVersion
5405
+ ),
5406
+ },
5407
+ ];
5408
+ return {
5409
+ name: "selected proof backend runtime versions",
5410
+ ok: details.every((entry) => entry.ok),
5411
+ details,
5412
+ };
5413
+ }
5414
+
5415
+ async function resolvePrivateStateInstallRuntimeVersions(args) {
5416
+ const [groth16, tokamak] = await Promise.all([
5417
+ resolveRequestedNpmPackageVersion({
5418
+ packageName: GROTH16_PACKAGE_NAME,
5419
+ requestedVersion: args.groth16CliVersion,
5420
+ optionName: "--groth16-cli-version",
5421
+ }),
5422
+ resolveRequestedNpmPackageVersion({
5423
+ packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5424
+ requestedVersion: args.tokamakZkEvmCliVersion,
5425
+ optionName: "--tokamak-zk-evm-cli-version",
5426
+ }),
5427
+ ]);
5428
+ return { groth16, tokamak };
5429
+ }
5430
+
5431
+ async function resolveRequestedNpmPackageVersion({ packageName, requestedVersion, optionName }) {
5432
+ const metadata = await fetchNpmPackageMetadata(packageName);
5433
+ if (requestedVersion === undefined || requestedVersion === null) {
5434
+ return requireSemverVersion(metadata?.["dist-tags"]?.latest, `${packageName} npm latest version`);
5435
+ }
5436
+
5437
+ const normalizedVersion = requireSemverVersion(requestedVersion, optionName);
5438
+ if (!metadata.versions?.[normalizedVersion]) {
5439
+ throw new Error(`npm package ${packageName} does not contain version ${normalizedVersion}.`);
5440
+ }
5441
+ return normalizedVersion;
5442
+ }
5443
+
5444
+ async function fetchNpmPackageMetadata(packageName) {
5445
+ const normalizedPackageName = requireNonEmptyString(packageName, "packageName");
5446
+ const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(normalizedPackageName)}`;
5447
+ const response = await fetch(registryUrl, { redirect: "follow" });
5448
+ if (!response.ok) {
5449
+ throw new Error(`Failed to read npm package metadata for ${normalizedPackageName}: HTTP ${response.status}.`);
5450
+ }
5451
+ try {
5452
+ return await response.json();
5453
+ } catch (error) {
5454
+ throw new Error(`npm package metadata for ${normalizedPackageName} is not valid JSON: ${error.message}`);
5455
+ }
5456
+ }
5457
+
5458
+ async function installTokamakCliRuntimeForPrivateState({ version, docker }) {
5459
+ const packageInstall = installManagedNpmPackage({
5460
+ packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5461
+ version,
5462
+ });
5463
+ const invocation = buildTokamakCliInvocationForPackageRoot(packageInstall.packageRoot);
5464
+ const installArgs = [...invocation.args, "--install"];
5465
+ if (docker) {
5466
+ installArgs.push("--docker");
5467
+ }
5468
+ run(invocation.command, installArgs, { cwd: packageInstall.packageRoot });
5469
+ const doctor = runCaptured(invocation.command, [...invocation.args, "--doctor"], {
5470
+ cwd: packageInstall.packageRoot,
5471
+ });
5472
+ const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
5473
+ const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
5474
+ expect(
5475
+ doctor.status === 0 && runtimeRoot,
5476
+ [
5477
+ "Tokamak zk-EVM CLI install completed, but tokamak-cli --doctor did not report a healthy runtime.",
5478
+ doctorOutput.trim(),
5479
+ ].filter(Boolean).join(" "),
5480
+ );
5481
+ return {
5482
+ packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5483
+ packageVersion: version,
5484
+ packageRoot: packageInstall.packageRoot,
5485
+ entryPath: invocation.entryPath,
5486
+ installPrefix: packageInstall.installPrefix,
5487
+ runtimeRoot,
5488
+ dockerRequested: Boolean(docker),
5489
+ };
5490
+ }
5491
+
5492
+ async function installGroth16RuntimeForPrivateState({ version, docker }) {
5493
+ const packageInstall = installManagedNpmPackage({
5494
+ packageName: GROTH16_PACKAGE_NAME,
5495
+ version,
5496
+ });
5497
+ const packageRoot = packageInstall.packageRoot;
5217
5498
  const entryPath = resolveGroth16CliEntryPath(packageRoot);
5218
- const args = [entryPath, "--install"];
5499
+ const args = [entryPath, "--install", "--no-setup"];
5219
5500
  if (docker) {
5220
5501
  args.push("--docker");
5221
5502
  }
5222
5503
  run(process.execPath, args, { cwd: packageRoot });
5223
- const runtime = inspectGroth16Runtime();
5504
+ const crsInstall = await installGroth16CrsForPrivateStateVersion(version);
5505
+ const runtime = inspectGroth16Runtime({ packageRoot });
5224
5506
  expect(runtime.installed, "Groth16 runtime install completed, but tokamak-groth16 --doctor still reports an unhealthy runtime.");
5225
- return runtime;
5507
+ return {
5508
+ ...runtime,
5509
+ packageName: GROTH16_PACKAGE_NAME,
5510
+ packageVersion: version,
5511
+ packageRoot,
5512
+ entryPath,
5513
+ installPrefix: packageInstall.installPrefix,
5514
+ crsVersion: crsInstall.version,
5515
+ crs: crsInstall,
5516
+ dockerRequested: Boolean(docker),
5517
+ };
5518
+ }
5519
+
5520
+ async function installGroth16CrsForPrivateStateVersion(version) {
5521
+ const workspaceRoot = defaultGroth16WorkspaceRoot();
5522
+ const crsDir = path.join(workspaceRoot, "crs");
5523
+ const crsInstall = await downloadPublicGroth16MpcArtifactsByVersion({
5524
+ version,
5525
+ outputDir: crsDir,
5526
+ selectedFiles: [
5527
+ "circuit_final.zkey",
5528
+ "verification_key.json",
5529
+ "metadata.json",
5530
+ "zkey_provenance.json",
5531
+ ],
5532
+ });
5533
+ const manifestPath = path.join(workspaceRoot, "install-manifest.json");
5534
+ const manifest = readJsonIfExists(manifestPath) ?? {};
5535
+ writeJson(manifestPath, {
5536
+ ...manifest,
5537
+ workspaceRoot,
5538
+ crsSource: "public-drive-mpc",
5539
+ crs: crsInstall,
5540
+ });
5541
+ return crsInstall;
5542
+ }
5543
+
5544
+ function installManagedNpmPackage({ packageName, version, cacheBaseRoot = resolveArtifactCacheBaseRoot() }) {
5545
+ const normalizedPackageName = requireNonEmptyString(packageName, "packageName");
5546
+ const normalizedVersion = requireSemverVersion(version, `${normalizedPackageName} version`);
5547
+ const installPrefix = managedNpmPackageInstallPrefix({
5548
+ packageName: normalizedPackageName,
5549
+ version: normalizedVersion,
5550
+ cacheBaseRoot,
5551
+ });
5552
+ fs.mkdirSync(installPrefix, { recursive: true });
5553
+ run("npm", [
5554
+ "install",
5555
+ "--prefix",
5556
+ installPrefix,
5557
+ "--omit=dev",
5558
+ "--no-audit",
5559
+ "--fund=false",
5560
+ `${normalizedPackageName}@${normalizedVersion}`,
5561
+ ]);
5562
+ const packageRoot = path.join(installPrefix, "node_modules", ...normalizedPackageName.split("/"));
5563
+ const packageJsonPath = path.join(packageRoot, "package.json");
5564
+ const packageJson = readJson(packageJsonPath);
5565
+ expect(
5566
+ packageJson.name === normalizedPackageName && packageJson.version === normalizedVersion,
5567
+ `Installed package ${packageJsonPath} does not match ${normalizedPackageName}@${normalizedVersion}.`,
5568
+ );
5569
+ return {
5570
+ packageName: normalizedPackageName,
5571
+ version: normalizedVersion,
5572
+ installPrefix,
5573
+ packageRoot,
5574
+ };
5575
+ }
5576
+
5577
+ function managedNpmPackageInstallPrefix({ packageName, version, cacheBaseRoot = resolveArtifactCacheBaseRoot() }) {
5578
+ const safePackageName = requireNonEmptyString(packageName, "packageName")
5579
+ .replace(/^@/, "")
5580
+ .replace(/[^A-Za-z0-9._-]+/g, "__");
5581
+ return path.join(privateStateCliRuntimeRoot(cacheBaseRoot), "npm", safePackageName, requireSemverVersion(version, "version"));
5582
+ }
5583
+
5584
+ async function downloadGroth16CrsArtifactsForPrivateState({
5585
+ version,
5586
+ outputDir,
5587
+ selectedFiles,
5588
+ }) {
5589
+ if (version === undefined || version === null) {
5590
+ return downloadLatestPublicGroth16MpcArtifacts({ outputDir, selectedFiles });
5591
+ }
5592
+ return downloadPublicGroth16MpcArtifactsByVersion({ version, outputDir, selectedFiles });
5226
5593
  }
5227
5594
 
5228
5595
  function collectDependencyPackageReports(installManifest = null) {
@@ -5233,11 +5600,11 @@ function collectDependencyPackageReports(installManifest = null) {
5233
5600
  );
5234
5601
  const targets = [
5235
5602
  {
5236
- name: "@tokamak-zk-evm/cli",
5237
- packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
5603
+ name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5604
+ packageJsonPath: path.join(resolveBundledTokamakCliPackageRoot(), "package.json"),
5238
5605
  },
5239
5606
  {
5240
- name: "@tokamak-private-dapps/groth16",
5607
+ name: GROTH16_PACKAGE_NAME,
5241
5608
  resolveTarget: "@tokamak-private-dapps/groth16/public-drive-crs",
5242
5609
  },
5243
5610
  {
@@ -5301,21 +5668,42 @@ function resolveGroth16PackageRoot() {
5301
5668
  return path.dirname(findPackageJsonForName(path.dirname(publicDriveCrsPath), "@tokamak-private-dapps/groth16"));
5302
5669
  }
5303
5670
 
5671
+ function resolveActiveGroth16PackageRoot() {
5672
+ const manifestPackageRoot = readPrivateStateCliInstallManifest()?.install?.groth16Runtime?.packageRoot;
5673
+ if (manifestPackageRoot && fs.existsSync(path.join(manifestPackageRoot, "package.json"))) {
5674
+ return manifestPackageRoot;
5675
+ }
5676
+ return resolveGroth16PackageRoot();
5677
+ }
5678
+
5304
5679
  function resolveGroth16CliEntryPath(packageRoot = resolveGroth16PackageRoot()) {
5305
5680
  return path.join(packageRoot, "cli", "tokamak-groth16-cli.mjs");
5306
5681
  }
5307
5682
 
5308
- function inspectGroth16Runtime() {
5309
- const packageRoot = resolveGroth16PackageRoot();
5683
+ function defaultGroth16WorkspaceRoot() {
5684
+ return path.join(os.homedir(), "tokamak-private-channels", "groth16");
5685
+ }
5686
+
5687
+ function inspectGroth16Runtime({ packageRoot = resolveActiveGroth16PackageRoot() } = {}) {
5310
5688
  const entryPath = resolveGroth16CliEntryPath(packageRoot);
5311
5689
  const doctor = runCaptured(process.execPath, [entryPath, "--doctor", "--verbose"], { cwd: packageRoot });
5312
5690
  const stdout = stripAnsi(doctor.stdout).trim();
5313
5691
  const stderr = stripAnsi(doctor.stderr).trim();
5314
5692
  const report = parseJsonReport(stdout);
5693
+ const workspaceRoot = report?.workspaceRoot ?? defaultGroth16WorkspaceRoot();
5694
+ const workspaceManifest = readJsonIfExists(path.join(workspaceRoot, "install-manifest.json"));
5695
+ const packageReport = readPackageReport({
5696
+ name: GROTH16_PACKAGE_NAME,
5697
+ packageJsonPath: path.join(packageRoot, "package.json"),
5698
+ });
5315
5699
  return {
5316
5700
  installed: doctor.status === 0 && report?.ok === true,
5701
+ packageVersion: packageReport.version,
5317
5702
  packageRoot,
5703
+ entryPath,
5318
5704
  workspaceRoot: report?.workspaceRoot ?? null,
5705
+ crsVersion: workspaceManifest?.crs?.version ?? null,
5706
+ crs: workspaceManifest?.crs ?? null,
5319
5707
  checks: report?.checks ?? [],
5320
5708
  doctor: {
5321
5709
  status: doctor.status,
@@ -5325,9 +5713,41 @@ function inspectGroth16Runtime() {
5325
5713
  };
5326
5714
  }
5327
5715
 
5328
- function inspectTokamakCliRuntime() {
5329
- const doctor = runCaptured(tokamakCliCommand, [...tokamakCliBaseArgs, "--doctor"], {
5330
- cwd: resolveTokamakCliPackageRoot(),
5716
+ function resolveActiveTokamakCliPackageRoot() {
5717
+ const manifestPackageRoot = readPrivateStateCliInstallManifest()?.install?.tokamakCliRuntime?.packageRoot;
5718
+ if (manifestPackageRoot && fs.existsSync(path.join(manifestPackageRoot, "package.json"))) {
5719
+ return manifestPackageRoot;
5720
+ }
5721
+ return resolveBundledTokamakCliPackageRoot();
5722
+ }
5723
+
5724
+ function buildTokamakCliInvocationForPackageRoot(packageRoot = resolveActiveTokamakCliPackageRoot()) {
5725
+ const resolvedPackageRoot = path.resolve(packageRoot);
5726
+ const entryPath = resolvedPackageRoot === resolveBundledTokamakCliPackageRoot()
5727
+ ? resolveTokamakCliEntryPath()
5728
+ : path.join(resolvedPackageRoot, "dist", "cli.js");
5729
+ return {
5730
+ command: process.execPath,
5731
+ args: [entryPath],
5732
+ entryPath,
5733
+ packageRoot: resolvedPackageRoot,
5734
+ };
5735
+ }
5736
+
5737
+ function resolveTokamakCliResourceDirForRuntimeRoot(runtimeRoot, ...segments) {
5738
+ return path.join(runtimeRoot, "resource", ...segments);
5739
+ }
5740
+
5741
+ function requireActiveTokamakCliRuntimeRoot() {
5742
+ const runtime = inspectTokamakCliRuntime();
5743
+ expect(runtime.runtimeRoot, "Unable to resolve the installed Tokamak zk-EVM runtime root. Run --install first.");
5744
+ return runtime.runtimeRoot;
5745
+ }
5746
+
5747
+ function inspectTokamakCliRuntime({ packageRoot = resolveActiveTokamakCliPackageRoot() } = {}) {
5748
+ const invocation = buildTokamakCliInvocationForPackageRoot(packageRoot);
5749
+ const doctor = runCaptured(invocation.command, [...invocation.args, "--doctor"], {
5750
+ cwd: invocation.packageRoot,
5331
5751
  });
5332
5752
  const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
5333
5753
  const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
@@ -5338,12 +5758,13 @@ function inspectTokamakCliRuntime() {
5338
5758
 
5339
5759
  return {
5340
5760
  installed: doctor.status === 0 || installations.length > 0,
5341
- packageRoot: resolveTokamakCliPackageRoot(),
5761
+ packageRoot: invocation.packageRoot,
5762
+ entryPath: invocation.entryPath,
5342
5763
  cacheRoot,
5343
5764
  runtimeRoot,
5344
5765
  packageVersion: readPackageReport({
5345
- name: "@tokamak-zk-evm/cli",
5346
- packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
5766
+ name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
5767
+ packageJsonPath: path.join(invocation.packageRoot, "package.json"),
5347
5768
  }).version,
5348
5769
  dockerModeInstalled,
5349
5770
  cudaCompatible,
@@ -5484,6 +5905,7 @@ async function installPrivateStateCliArtifacts({
5484
5905
  cacheBaseRoot,
5485
5906
  localDeploymentBaseRoot,
5486
5907
  localChainIds = [31337],
5908
+ groth16CrsVersion,
5487
5909
  } = {}) {
5488
5910
  const normalizedDappName = requireNonEmptyString(dappName, "dappName");
5489
5911
  const normalizedCacheBaseRoot = resolveArtifactCacheBaseRoot(cacheBaseRoot);
@@ -5504,6 +5926,7 @@ async function installPrivateStateCliArtifacts({
5504
5926
  dappName: normalizedDappName,
5505
5927
  cacheBaseRoot: normalizedCacheBaseRoot,
5506
5928
  source: "drive",
5929
+ groth16CrsVersion,
5507
5930
  }));
5508
5931
  }
5509
5932
 
@@ -5514,6 +5937,7 @@ async function installPrivateStateCliArtifacts({
5514
5937
  dappName: normalizedDappName,
5515
5938
  cacheBaseRoot: normalizedCacheBaseRoot,
5516
5939
  localDeploymentBaseRoot: normalizedLocalDeploymentBaseRoot,
5940
+ groth16CrsVersion,
5517
5941
  }));
5518
5942
  }
5519
5943
  }
@@ -5535,6 +5959,7 @@ async function materializePrivateStateCliDeployment({
5535
5959
  dappName,
5536
5960
  cacheBaseRoot,
5537
5961
  source,
5962
+ groth16CrsVersion,
5538
5963
  }) {
5539
5964
  const normalizedChainId = String(requireChainId(chainId));
5540
5965
  const normalizedDappName = requireNonEmptyString(dappName, "dappName");
@@ -5566,7 +5991,8 @@ async function materializePrivateStateCliDeployment({
5566
5991
  [`groth16.${normalizedChainId}.latest.json`, path.basename(paths.grothManifestPath)],
5567
5992
  ],
5568
5993
  });
5569
- await downloadLatestPublicGroth16MpcArtifacts({
5994
+ await downloadGroth16CrsArtifactsForPrivateState({
5995
+ version: groth16CrsVersion,
5570
5996
  outputDir: paths.rootDir,
5571
5997
  selectedFiles: [
5572
5998
  ["circuit_final.zkey", path.basename(paths.grothZkeyPath)],
@@ -5598,6 +6024,7 @@ async function materializeLocalPrivateStateCliDeployment({
5598
6024
  dappName,
5599
6025
  cacheBaseRoot,
5600
6026
  localDeploymentBaseRoot,
6027
+ groth16CrsVersion,
5601
6028
  }) {
5602
6029
  const normalizedChainId = String(requireChainId(chainId));
5603
6030
  const normalizedDappName = requireNonEmptyString(dappName, "dappName");
@@ -5634,7 +6061,8 @@ async function materializeLocalPrivateStateCliDeployment({
5634
6061
  [path.join(dappDir, `dapp-registration.${normalizedChainId}.json`), path.basename(paths.dappRegistrationPath)],
5635
6062
  ],
5636
6063
  });
5637
- await downloadLatestPublicGroth16MpcArtifacts({
6064
+ await downloadGroth16CrsArtifactsForPrivateState({
6065
+ version: groth16CrsVersion,
5638
6066
  outputDir: paths.rootDir,
5639
6067
  selectedFiles: [
5640
6068
  ["circuit_final.zkey", path.basename(paths.grothZkeyPath)],