@tokamak-private-dapps/private-state-cli 2.1.2 → 2.2.0

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/lib/runtime.mjs CHANGED
@@ -82,9 +82,12 @@ import {
82
82
  installPrivateStateCliArtifacts,
83
83
  installTokamakCliRuntimeForPrivateState,
84
84
  inspectGroth16Runtime,
85
+ normalizeInstallMode,
85
86
  parseJsonReport,
86
87
  printDoctorHumanReport,
88
+ privateStateCliArtifactRequiredFiles,
87
89
  privateStateCliArtifactPaths,
90
+ PRIVATE_STATE_INSTALL_MODES,
88
91
  readTokamakCliPackageReport,
89
92
  requireActiveTokamakCliRuntimeRoot,
90
93
  resolveActiveGroth16ProverRuntime,
@@ -99,6 +102,7 @@ import {
99
102
  PRIVATE_STATE_CLI_COMMANDS,
100
103
  PRIVATE_STATE_CLI_FIELD_CATALOG,
101
104
  privateStateCliCommandDisplay,
105
+ privateStateCliCommandInstallMode,
102
106
  privateStateCliCommandOptionKeys,
103
107
  privateStateCliCommandRequiredOptionKeys,
104
108
  privateStateCliCommandSynopsis,
@@ -299,7 +303,7 @@ const ACTION_IMPACT_SUMMARIES = Object.freeze({
299
303
  "No private note owner, value, salt, counterparty, or note provenance is created by this action.",
300
304
  ],
301
305
  noteProvenance: "Not applicable for this bridge-edge action.",
302
- cexWarning: "Do not use a centralized-exchange controlled address as a self-custody bridge source.",
306
+ exchangeControlledAddressWarning: "Do not use an exchange-controlled address as a self-custody bridge source.",
303
307
  policy: "No channel policy is accepted by this action.",
304
308
  },
305
309
  "account-withdraw-bridge": {
@@ -316,7 +320,7 @@ const ACTION_IMPACT_SUMMARIES = Object.freeze({
316
320
  "The private note path that produced any prior channel balance is not reconstructed from this event alone.",
317
321
  ],
318
322
  noteProvenance: "Public observers cannot reconstruct prior internal note provenance from this withdrawal alone.",
319
- cexWarning: "Do not use a centralized-exchange deposit address as the direct bridge withdrawal target unless the user has explicitly accepted the compliance implications. Prefer a self-custody L1 wallet.",
323
+ exchangeControlledAddressWarning: "Do not use an exchange deposit address as the direct bridge withdrawal target unless the user has explicitly accepted the compliance implications. Prefer a self-custody L1 wallet.",
320
324
  policy: "No channel policy is accepted by this action.",
321
325
  },
322
326
  "channel-join": {
@@ -472,8 +476,8 @@ function printActionImpactSummary(summary, details) {
472
476
  `- Secret recovery: Losing wallet secrets, viewing keys, or spending keys can prevent note discovery or note use. The CLI cannot recover lost secrets.`,
473
477
  `- Channel policy: ${summary.policy}`,
474
478
  ];
475
- if (summary.cexWarning) {
476
- lines.push(`- CEX address warning: ${summary.cexWarning}`);
479
+ if (summary.exchangeControlledAddressWarning) {
480
+ lines.push(`- Exchange-controlled address warning: ${summary.exchangeControlledAddressWarning}`);
477
481
  }
478
482
  lines.push(`- Confirmation: pass --acknowledge-action-impact or type the exact confirmation phrase when prompted.`);
479
483
  console.error(lines.join("\n"));
@@ -509,22 +513,36 @@ function normalizeDAppPolicySnapshot({
509
513
  };
510
514
  }
511
515
 
512
- async function prepareDeploymentArtifacts(chainId) {
516
+ function prepareDeploymentArtifacts(chainId, { mode = PRIVATE_STATE_INSTALL_MODES.FULL } = {}) {
513
517
  const normalizedChainId = Number(chainId);
514
- const existingPaths = flatDeploymentArtifactPathsByChainId.get(normalizedChainId);
515
- if (existingPaths) {
516
- return existingPaths.rootDir;
518
+ const normalizedMode = normalizeInstallMode(mode);
519
+ const existingEntry = flatDeploymentArtifactPathsByChainId.get(normalizedChainId);
520
+ if (existingEntry?.preparedModes.has(normalizedMode)) {
521
+ return existingEntry.paths.rootDir;
517
522
  }
518
523
 
519
524
  const cacheBaseRoot = resolveArtifactCacheBaseRoot();
520
- const artifactPaths = privateStateCliArtifactPaths(cacheBaseRoot, normalizedChainId);
521
- requireInstalledDeploymentArtifacts(artifactPaths, normalizedChainId);
522
- flatDeploymentArtifactPathsByChainId.set(normalizedChainId, artifactPaths);
525
+ const artifactPaths = existingEntry?.paths ?? privateStateCliArtifactPaths(cacheBaseRoot, normalizedChainId);
526
+ requireInstalledDeploymentArtifacts(artifactPaths, normalizedChainId, normalizedMode);
527
+ const preparedModes = existingEntry?.preparedModes ?? new Set();
528
+ preparedModes.add(normalizedMode);
529
+ flatDeploymentArtifactPathsByChainId.set(normalizedChainId, {
530
+ paths: artifactPaths,
531
+ preparedModes,
532
+ });
523
533
  return artifactPaths.rootDir;
524
534
  }
525
535
 
536
+ function prepareDeploymentArtifactsForCommand(commandId, chainId) {
537
+ const command = PRIVATE_STATE_CLI_COMMANDS.find((entry) => entry.id === commandId);
538
+ expect(command, `Missing CLI command metadata for ${commandId}.`);
539
+ const mode = privateStateCliCommandInstallMode(command);
540
+ expect(mode !== "none", `${privateStateCliCommandDisplay(command)} does not require installed deployment artifacts.`);
541
+ return prepareDeploymentArtifacts(chainId, { mode });
542
+ }
543
+
526
544
  function flatDeploymentArtifactPathsForChainId(chainId) {
527
- return flatDeploymentArtifactPathsByChainId.get(Number(chainId)) ?? null;
545
+ return flatDeploymentArtifactPathsByChainId.get(Number(chainId))?.paths ?? null;
528
546
  }
529
547
 
530
548
  function requireFlatDeploymentArtifactPathsForChainId(chainId) {
@@ -535,29 +553,22 @@ function requireFlatDeploymentArtifactPathsForChainId(chainId) {
535
553
  return paths;
536
554
  }
537
555
 
538
- function requireInstalledDeploymentArtifacts(artifactPaths, chainId) {
539
- const requiredFiles = [
540
- artifactPaths.bridgeDeploymentPath,
541
- artifactPaths.bridgeAbiManifestPath,
542
- artifactPaths.grothManifestPath,
543
- artifactPaths.grothZkeyPath,
544
- artifactPaths.dappDeploymentPath,
545
- artifactPaths.dappStorageLayoutPath,
546
- artifactPaths.privateStateControllerAbiPath,
547
- artifactPaths.dappRegistrationPath,
548
- ];
556
+ function requireInstalledDeploymentArtifacts(artifactPaths, chainId, mode) {
557
+ const requiredFiles = privateStateCliArtifactRequiredFiles(artifactPaths, mode);
549
558
  try {
550
- for (const filePath of requiredFiles) {
551
- if (!fs.existsSync(filePath)) {
552
- throw new Error(`Missing ${filePath}.`);
559
+ for (const entry of requiredFiles) {
560
+ if (!fs.existsSync(entry.path)) {
561
+ throw new Error(`Missing ${entry.label}: ${entry.path}.`);
553
562
  }
554
563
  }
555
564
  } catch (error) {
556
565
  throw cliError(
557
566
  CLI_ERROR_CODES.MISSING_DEPLOYMENT_ARTIFACTS,
558
567
  [
559
- `Missing installed deployment artifacts for chain ${chainId} under ${artifactPaths.rootDir}.`,
560
- "Run install before running private-state CLI commands for this network.",
568
+ `Missing ${mode} installed deployment artifacts for chain ${chainId} under ${artifactPaths.rootDir}.`,
569
+ mode === PRIVATE_STATE_INSTALL_MODES.FULL
570
+ ? "Run install before running private-state CLI commands that write channel state."
571
+ : "Run install --read-only before running private-state CLI commands that read channel state.",
561
572
  `Original error: ${error.message}`,
562
573
  ].join(" "),
563
574
  );
@@ -2450,22 +2461,33 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
2450
2461
  }
2451
2462
 
2452
2463
  async function handleInstallZkEvm({ args }) {
2453
- const selectedVersions = await resolvePrivateStateInstallRuntimeVersions(args);
2454
- const tokamakCliRuntime = await installTokamakCliRuntimeForPrivateState({
2455
- version: selectedVersions.tokamak,
2456
- docker: Boolean(args.docker),
2457
- });
2458
- const groth16Runtime = await installGroth16RuntimeForPrivateState({
2459
- version: selectedVersions.groth16,
2460
- docker: Boolean(args.docker),
2461
- });
2464
+ const installMode = args.readOnly === true
2465
+ ? PRIVATE_STATE_INSTALL_MODES.READ_ONLY
2466
+ : PRIVATE_STATE_INSTALL_MODES.FULL;
2467
+ const selectedVersions = installMode === PRIVATE_STATE_INSTALL_MODES.FULL
2468
+ ? await resolvePrivateStateInstallRuntimeVersions(args)
2469
+ : null;
2470
+ const tokamakCliRuntime = installMode === PRIVATE_STATE_INSTALL_MODES.FULL
2471
+ ? await installTokamakCliRuntimeForPrivateState({
2472
+ version: selectedVersions.tokamak,
2473
+ docker: Boolean(args.docker),
2474
+ })
2475
+ : null;
2476
+ const groth16Runtime = installMode === PRIVATE_STATE_INSTALL_MODES.FULL
2477
+ ? await installGroth16RuntimeForPrivateState({
2478
+ version: selectedVersions.groth16,
2479
+ docker: Boolean(args.docker),
2480
+ })
2481
+ : null;
2462
2482
  const localDeploymentBaseRoot = args.includeLocalArtifacts ? process.cwd() : null;
2463
2483
  const deploymentArtifacts = await installPrivateStateCliArtifacts({
2464
2484
  dappName: PRIVATE_STATE_DAPP_LABEL,
2485
+ installMode,
2465
2486
  localDeploymentBaseRoot,
2466
- groth16CrsVersion: groth16Runtime.compatibleBackendVersion,
2487
+ groth16CrsVersion: groth16Runtime?.compatibleBackendVersion ?? null,
2467
2488
  });
2468
2489
  const installManifest = writePrivateStateCliInstallManifest({
2490
+ installMode,
2469
2491
  dockerRequested: Boolean(args.docker),
2470
2492
  includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
2471
2493
  localDeploymentBaseRoot,
@@ -2476,9 +2498,10 @@ async function handleInstallZkEvm({ args }) {
2476
2498
  });
2477
2499
  printJson({
2478
2500
  action: "install",
2501
+ installMode,
2479
2502
  selectedVersions,
2480
- tokamakCli: tokamakCliRuntime.entryPath,
2481
- runtimeRoot: tokamakCliRuntime.runtimeRoot,
2503
+ tokamakCli: tokamakCliRuntime?.entryPath ?? null,
2504
+ runtimeRoot: tokamakCliRuntime?.runtimeRoot ?? null,
2482
2505
  tokamakCliRuntime,
2483
2506
  groth16Runtime,
2484
2507
  docker: Boolean(args.docker),
@@ -3435,15 +3458,19 @@ async function handleGuide({ args }) {
3435
3458
  guide.state.deploymentArtifacts = artifactState;
3436
3459
  guide.checks.push(guideCheck(
3437
3460
  "installed deployment artifacts",
3438
- artifactState.installed ? "ok" : "missing",
3461
+ artifactState.readOnlyInstalled ? "ok" : "missing",
3439
3462
  {
3440
3463
  chainId: networkRuntime.network.chainId,
3441
3464
  rootDir: artifactState.rootDir,
3442
- missingFiles: artifactState.missingFiles,
3465
+ missingFiles: artifactState.readOnlyMissingFiles,
3466
+ fullMissingFiles: artifactState.fullMissingFiles,
3443
3467
  },
3444
3468
  ));
3445
- if (artifactState.installed) {
3446
- flatDeploymentArtifactPathsByChainId.set(Number(networkRuntime.network.chainId), artifactState.paths);
3469
+ if (artifactState.readOnlyInstalled) {
3470
+ flatDeploymentArtifactPathsByChainId.set(Number(networkRuntime.network.chainId), {
3471
+ paths: artifactState.paths,
3472
+ preparedModes: new Set([PRIVATE_STATE_INSTALL_MODES.READ_ONLY]),
3473
+ });
3447
3474
  }
3448
3475
 
3449
3476
  const provider = networkRuntime.provider;
@@ -3452,7 +3479,7 @@ async function handleGuide({ args }) {
3452
3479
  channelName: String(args.channelName),
3453
3480
  network: networkRuntime.network,
3454
3481
  provider,
3455
- artifactsInstalled: artifactState.installed,
3482
+ artifactsInstalled: artifactState.readOnlyInstalled,
3456
3483
  });
3457
3484
  guide.checks.push(guideCheck(
3458
3485
  "channel",
@@ -3474,7 +3501,7 @@ async function handleGuide({ args }) {
3474
3501
  networkName,
3475
3502
  network: networkRuntime.network,
3476
3503
  provider,
3477
- artifactsInstalled: artifactState.installed,
3504
+ artifactsInstalled: artifactState.readOnlyInstalled,
3478
3505
  });
3479
3506
  guide.checks.push(guideCheck(
3480
3507
  "local account secret",
@@ -3493,7 +3520,7 @@ async function handleGuide({ args }) {
3493
3520
  walletName: String(args.wallet),
3494
3521
  networkName,
3495
3522
  provider,
3496
- artifactsInstalled: artifactState.installed,
3523
+ artifactsInstalled: artifactState.readOnlyInstalled,
3497
3524
  });
3498
3525
  guide.checks.push(guideCheck(
3499
3526
  "local wallet",
@@ -3596,21 +3623,20 @@ function inspectGuideNetworkRuntime(networkName) {
3596
3623
 
3597
3624
  function inspectGuideDeploymentArtifacts(chainId) {
3598
3625
  const paths = privateStateCliArtifactPaths(resolveArtifactCacheBaseRoot(), chainId);
3599
- const requiredFiles = [
3600
- paths.bridgeDeploymentPath,
3601
- paths.bridgeAbiManifestPath,
3602
- paths.grothManifestPath,
3603
- paths.grothZkeyPath,
3604
- paths.dappDeploymentPath,
3605
- paths.dappStorageLayoutPath,
3606
- paths.privateStateControllerAbiPath,
3607
- paths.dappRegistrationPath,
3608
- ];
3609
- const missingFiles = requiredFiles.filter((filePath) => !fs.existsSync(filePath));
3626
+ const readOnlyMissingFiles = privateStateCliArtifactRequiredFiles(paths, PRIVATE_STATE_INSTALL_MODES.READ_ONLY)
3627
+ .map((entry) => entry.path)
3628
+ .filter((filePath) => !fs.existsSync(filePath));
3629
+ const fullMissingFiles = privateStateCliArtifactRequiredFiles(paths, PRIVATE_STATE_INSTALL_MODES.FULL)
3630
+ .map((entry) => entry.path)
3631
+ .filter((filePath) => !fs.existsSync(filePath));
3610
3632
  return {
3611
- installed: missingFiles.length === 0,
3633
+ installed: readOnlyMissingFiles.length === 0,
3634
+ readOnlyInstalled: readOnlyMissingFiles.length === 0,
3635
+ fullInstalled: fullMissingFiles.length === 0,
3612
3636
  rootDir: paths.rootDir,
3613
- missingFiles,
3637
+ missingFiles: readOnlyMissingFiles,
3638
+ readOnlyMissingFiles,
3639
+ fullMissingFiles,
3614
3640
  paths,
3615
3641
  };
3616
3642
  }
@@ -9976,6 +10002,20 @@ function assertWalletChannelMoveArgs(args, commandName) {
9976
10002
 
9977
10003
  function assertInstallZkEvmArgs(args) {
9978
10004
  assertAllowedCommandSchema(args, "install");
10005
+ if (args.readOnly !== undefined && args.readOnly !== true) {
10006
+ throw new Error("install option --read-only does not accept a value.");
10007
+ }
10008
+ if (args.readOnly === true && args.docker !== undefined) {
10009
+ throw new Error("install --read-only does not accept --docker because proof runtimes are not installed.");
10010
+ }
10011
+ if (args.readOnly === true && args.groth16CliVersion !== undefined) {
10012
+ throw new Error("install --read-only does not accept --groth16-cli-version because Groth16 is not installed.");
10013
+ }
10014
+ if (args.readOnly === true && args.tokamakZkEvmCliVersion !== undefined) {
10015
+ throw new Error(
10016
+ "install --read-only does not accept --tokamak-zk-evm-cli-version because Tokamak zk-EVM is not installed.",
10017
+ );
10018
+ }
9979
10019
  if (args.groth16CliVersion !== undefined) {
9980
10020
  requireSemverVersion(args.groth16CliVersion, "--groth16-cli-version");
9981
10021
  }
@@ -10919,7 +10959,7 @@ function ensureDir(dirPath) {
10919
10959
  fs.mkdirSync(dirPath, { recursive: true });
10920
10960
  }
10921
10961
 
10922
- function loadExplicitCommandRuntime(args, { staticNetwork = false } = {}) {
10962
+ function loadExplicitCommandRuntime(args, { staticNetwork = false, prepareArtifacts = false } = {}) {
10923
10963
  const networkName = requireNetworkName(args);
10924
10964
  const network = {
10925
10965
  ...resolveCliNetwork(networkName),
@@ -10930,6 +10970,7 @@ function loadExplicitCommandRuntime(args, { staticNetwork = false } = {}) {
10930
10970
  const provider = staticNetwork
10931
10971
  ? new JsonRpcProvider(rpcConfig.rpcUrl, Number(network.chainId), { staticNetwork: true })
10932
10972
  : new JsonRpcProvider(rpcConfig.rpcUrl);
10973
+ if (prepareArtifacts) prepareDeploymentArtifactsForCommand(args.command, network.chainId);
10933
10974
  return {
10934
10975
  network,
10935
10976
  rpcUrl: rpcConfig.rpcUrl,
@@ -10950,7 +10991,7 @@ async function assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl }
10950
10991
  );
10951
10992
  }
10952
10993
 
10953
- function loadWalletCommandRuntime(args) {
10994
+ function loadWalletCommandRuntime(args, { prepareArtifacts = false } = {}) {
10954
10995
  const networkName = requireNetworkName(args);
10955
10996
  loadWalletMetadata(requireWalletName(args), networkName);
10956
10997
  const network = {
@@ -10959,6 +11000,7 @@ function loadWalletCommandRuntime(args) {
10959
11000
  };
10960
11001
  const rpcConfig = resolveCommandRpcConfig(args);
10961
11002
  setActiveRpcLogConfig(rpcConfig);
11003
+ if (prepareArtifacts) prepareDeploymentArtifactsForCommand(args.command, network.chainId);
10962
11004
  return {
10963
11005
  network,
10964
11006
  rpcConfig,
@@ -11525,7 +11567,6 @@ export {
11525
11567
  handleJoinChannel,
11526
11568
  loadExplicitCommandRuntime,
11527
11569
  loadWalletCommandRuntime,
11528
- prepareDeploymentArtifacts,
11529
11570
  assertProviderChainIdMatchesNetwork,
11530
11571
  formatCliErrorForDisplay,
11531
11572
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokamak-private-dapps/private-state-cli",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
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",