@tokamak-private-dapps/private-state-cli 0.1.9 → 1.0.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/CHANGELOG.md +80 -0
- package/README.md +65 -25
- package/cli-assistant.html +59 -413
- package/lib/private-state-cli-command-registry.mjs +352 -0
- package/lib/private-state-cli-shared.mjs +7 -7
- package/lib/private-state-note-delivery.mjs +415 -0
- package/lib/private-state-runtime-management.mjs +1311 -0
- package/lib/private-state-tokamak-helpers.mjs +184 -0
- package/package.json +1 -1
- package/private-state-bridge-cli.mjs +2096 -2151
|
@@ -0,0 +1,1311 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { fetchNpmPackageMetadata } from "@tokamak-private-dapps/common-library/npm-registry";
|
|
9
|
+
import {
|
|
10
|
+
normalizePackageVersionToCompatibleBackendVersion,
|
|
11
|
+
readTokamakZkEvmCompatibleBackendVersionFromPackageJson,
|
|
12
|
+
requireCanonicalCompatibleBackendVersion,
|
|
13
|
+
requireExactSemverVersion,
|
|
14
|
+
} from "@tokamak-private-dapps/common-library/proof-backend-versioning";
|
|
15
|
+
import {
|
|
16
|
+
resolveTokamakCliEntryPath,
|
|
17
|
+
resolveTokamakCliPackageRoot as resolveBundledTokamakCliPackageRoot,
|
|
18
|
+
} from "@tokamak-private-dapps/common-library/tokamak-runtime-paths";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_PUBLIC_ARTIFACT_INDEX_FILE_ID,
|
|
21
|
+
defaultArtifactCacheBaseRoot,
|
|
22
|
+
fetchPublicArtifactIndex,
|
|
23
|
+
materializeSelectedDriveFiles,
|
|
24
|
+
materializeSelectedLocalFiles,
|
|
25
|
+
requireChainId,
|
|
26
|
+
requireLatestTimestampLabel,
|
|
27
|
+
requireNonEmptyString,
|
|
28
|
+
resolveArtifactCacheBaseRoot as resolveGenericArtifactCacheBaseRoot,
|
|
29
|
+
} from "@tokamak-private-dapps/common-library/artifact-cache";
|
|
30
|
+
import {
|
|
31
|
+
PUBLIC_GROTH16_MPC_DRIVE_FOLDER_ID,
|
|
32
|
+
downloadLatestPublicGroth16MpcArtifacts,
|
|
33
|
+
downloadPublicGroth16MpcArtifactsByVersion,
|
|
34
|
+
readGroth16CompatibleBackendVersionFromPackageJson,
|
|
35
|
+
requireCanonicalGroth16CompatibleBackendVersion,
|
|
36
|
+
} from "@tokamak-private-dapps/groth16/public-drive-crs";
|
|
37
|
+
|
|
38
|
+
const require = createRequire(import.meta.url);
|
|
39
|
+
const privateStateCliPackageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
40
|
+
const defaultCommandCwd = process.cwd();
|
|
41
|
+
const PRIVATE_STATE_DAPP_LABEL = "private-state";
|
|
42
|
+
const DOCKER_CUDA_PROBE_IMAGE = "nvidia/cuda:12.2.0-base-ubuntu22.04";
|
|
43
|
+
const DOCTOR_GPU_PROBE_TIMEOUT_MS = 120000;
|
|
44
|
+
const GROTH16_PACKAGE_NAME = "@tokamak-private-dapps/groth16";
|
|
45
|
+
const TOKAMAK_ZKEVM_CLI_PACKAGE_NAME = "@tokamak-zk-evm/cli";
|
|
46
|
+
|
|
47
|
+
function expect(condition, message) {
|
|
48
|
+
if (!condition) {
|
|
49
|
+
throw message instanceof Error ? message : new Error(message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readJson(filePath) {
|
|
54
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readJsonIfExists(filePath) {
|
|
58
|
+
return fs.existsSync(filePath) ? readJson(filePath) : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function writeJson(filePath, value) {
|
|
62
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
63
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function run(command, args, { cwd = defaultCommandCwd, env = process.env, quiet = false } = {}) {
|
|
67
|
+
const result = spawnSync(command, args, {
|
|
68
|
+
cwd,
|
|
69
|
+
env,
|
|
70
|
+
encoding: "utf8",
|
|
71
|
+
stdio: quiet ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
72
|
+
});
|
|
73
|
+
if (result.error) {
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
if (result.status !== 0) {
|
|
77
|
+
throw new Error([
|
|
78
|
+
`${command} ${args.join(" ")} failed with exit code ${result.status ?? "unknown"}.`,
|
|
79
|
+
quiet && result.stdout?.trim() ? `stdout:\n${result.stdout}` : null,
|
|
80
|
+
quiet && result.stderr?.trim() ? `stderr:\n${result.stderr}` : null,
|
|
81
|
+
].filter(Boolean).join("\n"));
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function runCaptured(command, args, { cwd = defaultCommandCwd, env = process.env } = {}) {
|
|
87
|
+
const result = spawnSync(command, args, {
|
|
88
|
+
cwd,
|
|
89
|
+
env,
|
|
90
|
+
encoding: "utf8",
|
|
91
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
92
|
+
});
|
|
93
|
+
if (result.error) {
|
|
94
|
+
throw result.error;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
status: result.status ?? 1,
|
|
98
|
+
stdout: result.stdout ?? "",
|
|
99
|
+
stderr: result.stderr ?? "",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function requireSemverVersion(value, label) {
|
|
104
|
+
return requireExactSemverVersion(value, label);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readTokamakCliPackageReport(packageRoot = null) {
|
|
108
|
+
try {
|
|
109
|
+
const resolvedPackageRoot = packageRoot ?? resolveActiveTokamakCliPackageRoot();
|
|
110
|
+
const packageJsonPath = path.join(resolvedPackageRoot, "package.json");
|
|
111
|
+
const packageJson = readJson(packageJsonPath);
|
|
112
|
+
const report = readPackageReport({
|
|
113
|
+
name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
114
|
+
packageJsonPath,
|
|
115
|
+
packageJson,
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
...report,
|
|
119
|
+
compatibleBackendVersion: readTokamakZkEvmCompatibleBackendVersionFromPackageJson(
|
|
120
|
+
packageJson,
|
|
121
|
+
TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
127
|
+
version: null,
|
|
128
|
+
packageRoot: null,
|
|
129
|
+
compatibleBackendVersion: null,
|
|
130
|
+
error: error.message,
|
|
131
|
+
ok: false,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function printDoctorHumanReport(report) {
|
|
136
|
+
const rows = buildDoctorHumanRows(report);
|
|
137
|
+
const lines = [
|
|
138
|
+
"Private-state CLI doctor",
|
|
139
|
+
`Status: ${report.ok ? "OK" : "FAIL"}`,
|
|
140
|
+
`Generated: ${report.generatedAt}`,
|
|
141
|
+
`Package: ${report.package.name}@${report.package.version ?? "unknown"}`,
|
|
142
|
+
`Install manifest: ${report.installManifest.exists ? "found" : "missing"} (${report.installManifest.path})`,
|
|
143
|
+
"",
|
|
144
|
+
formatDoctorTable(rows),
|
|
145
|
+
"",
|
|
146
|
+
"Run `doctor --json` for the full machine-readable report.",
|
|
147
|
+
];
|
|
148
|
+
console.log(lines.join("\n"));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildDoctorHumanRows(report) {
|
|
152
|
+
const dependencySummary = report.dependencies
|
|
153
|
+
.map((entry) => `${entry.name}@${entry.version ?? "unknown"}${entry.installVersion ? ` install=${entry.installVersion}` : ""}`)
|
|
154
|
+
.join("; ");
|
|
155
|
+
const selectedVersionDetails = report.checks
|
|
156
|
+
.find((check) => check.name === "selected proof backend runtime versions")
|
|
157
|
+
?.details ?? [];
|
|
158
|
+
const selectedVersionSummary = selectedVersionDetails
|
|
159
|
+
.map((entry) => [
|
|
160
|
+
`${entry.name}:`,
|
|
161
|
+
`selected=${entry.selectedVersion ?? "none"}`,
|
|
162
|
+
`installed=${entry.installedVersion ?? "missing"}`,
|
|
163
|
+
`cbv=${entry.compatibleBackendVersion ?? "missing"}`,
|
|
164
|
+
entry.crsCompatibleBackendVersion ? `crs=${entry.crsCompatibleBackendVersion}` : null,
|
|
165
|
+
].filter(Boolean).join(" "))
|
|
166
|
+
.join("; ");
|
|
167
|
+
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
check: "dependency packages",
|
|
171
|
+
status: doctorStatus(report.checks.find((check) => check.name === "dependency package versions")?.ok),
|
|
172
|
+
detail: dependencySummary || "no dependency report",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
check: "selected backend versions",
|
|
176
|
+
status: doctorStatus(report.checks.find((check) => check.name === "selected proof backend runtime versions")?.ok),
|
|
177
|
+
detail: selectedVersionSummary || "no selected runtime version pin",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
check: "tokamak zk-evm runtime",
|
|
181
|
+
status: doctorStatus(report.tokamakCli.installed),
|
|
182
|
+
detail: [
|
|
183
|
+
`package=${report.tokamakCli.packageVersion ?? "missing"}`,
|
|
184
|
+
`cbv=${report.tokamakCli.compatibleBackendVersion ?? "missing"}`,
|
|
185
|
+
`runtime=${report.tokamakCli.runtimeRoot ?? "missing"}`,
|
|
186
|
+
`doctorStatus=${report.tokamakCli.doctor.status}`,
|
|
187
|
+
].join(" "),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
check: "docker gpu readiness",
|
|
191
|
+
status: report.gpuDockerReadiness.skipped ? "SKIP" : doctorStatus(report.gpuDockerReadiness.ok),
|
|
192
|
+
detail: report.gpuDockerReadiness.skipped
|
|
193
|
+
? "live GPU probe skipped; run `doctor --gpu` to check host NVIDIA and Docker GPU access"
|
|
194
|
+
: [
|
|
195
|
+
`expectedUseGpus=${formatDoctorBool(report.gpuDockerReadiness.expectedUseGpus)}`,
|
|
196
|
+
`liveUseGpus=${formatDoctorBool(report.gpuDockerReadiness.liveUseGpus)}`,
|
|
197
|
+
report.gpuDockerReadiness.mismatchError,
|
|
198
|
+
].filter(Boolean).join(" "),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
check: "groth16 runtime",
|
|
202
|
+
status: doctorStatus(report.groth16Runtime.installed),
|
|
203
|
+
detail: [
|
|
204
|
+
`package=${report.groth16Runtime.packageVersion ?? "missing"}`,
|
|
205
|
+
`cbv=${report.groth16Runtime.compatibleBackendVersion ?? "missing"}`,
|
|
206
|
+
`crs=${report.groth16Runtime.crsCompatibleBackendVersion ?? "missing"}`,
|
|
207
|
+
`workspace=${report.groth16Runtime.workspaceRoot ?? "missing"}`,
|
|
208
|
+
`doctorStatus=${report.groth16Runtime.doctor.status}`,
|
|
209
|
+
].join(" "),
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function formatDoctorTable(rows) {
|
|
215
|
+
const headers = ["Check", "Status", "Detail"];
|
|
216
|
+
const checkWidth = Math.max(headers[0].length, ...rows.map((row) => row.check.length));
|
|
217
|
+
const statusWidth = Math.max(headers[1].length, ...rows.map((row) => row.status.length));
|
|
218
|
+
const header = [
|
|
219
|
+
headers[0].padEnd(checkWidth),
|
|
220
|
+
headers[1].padEnd(statusWidth),
|
|
221
|
+
headers[2],
|
|
222
|
+
].join(" ");
|
|
223
|
+
const separator = [
|
|
224
|
+
"-".repeat(checkWidth),
|
|
225
|
+
"-".repeat(statusWidth),
|
|
226
|
+
"-".repeat(headers[2].length),
|
|
227
|
+
].join(" ");
|
|
228
|
+
return [
|
|
229
|
+
header,
|
|
230
|
+
separator,
|
|
231
|
+
...rows.map((row) => [
|
|
232
|
+
row.check.padEnd(checkWidth),
|
|
233
|
+
row.status.padEnd(statusWidth),
|
|
234
|
+
row.detail,
|
|
235
|
+
].join(" ")),
|
|
236
|
+
].join("\n");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function doctorStatus(ok) {
|
|
240
|
+
if (ok === true) return "OK";
|
|
241
|
+
if (ok === false) return "FAIL";
|
|
242
|
+
return "UNKNOWN";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function formatDoctorBool(value) {
|
|
246
|
+
return value === true ? "true" : "false";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function resolveArtifactCacheBaseRoot(
|
|
250
|
+
cacheBaseRoot = process.env.PRIVATE_STATE_ARTIFACT_CACHE_ROOT
|
|
251
|
+
?? process.env.TOKAMAK_PRIVATE_CHANNELS_ROOT
|
|
252
|
+
?? defaultArtifactCacheBaseRoot(),
|
|
253
|
+
) {
|
|
254
|
+
return resolveGenericArtifactCacheBaseRoot(cacheBaseRoot);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function privateStateCliArtifactRoot(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
258
|
+
return path.join(resolveArtifactCacheBaseRoot(cacheBaseRoot), "dapps", "private-state");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function privateStateCliRuntimeRoot(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
262
|
+
return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "runtimes");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function privateStateCliArtifactChainDir(cacheBaseRoot = resolveArtifactCacheBaseRoot(), chainId) {
|
|
266
|
+
return path.join(privateStateCliArtifactRoot(cacheBaseRoot), `chain-id-${requireChainId(chainId)}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function privateStateCliArtifactPaths(cacheBaseRoot = resolveArtifactCacheBaseRoot(), chainId) {
|
|
270
|
+
const normalizedChainId = requireChainId(chainId);
|
|
271
|
+
const rootDir = privateStateCliArtifactChainDir(cacheBaseRoot, normalizedChainId);
|
|
272
|
+
return {
|
|
273
|
+
rootDir,
|
|
274
|
+
bridgeDeploymentPath: path.join(rootDir, `bridge.${normalizedChainId}.json`),
|
|
275
|
+
bridgeAbiManifestPath: path.join(rootDir, `bridge-abi-manifest.${normalizedChainId}.json`),
|
|
276
|
+
grothManifestPath: path.join(rootDir, `groth16.${normalizedChainId}.latest.json`),
|
|
277
|
+
grothZkeyPath: path.join(rootDir, "circuit_final.zkey"),
|
|
278
|
+
dappDeploymentPath: path.join(rootDir, `deployment.${normalizedChainId}.latest.json`),
|
|
279
|
+
dappStorageLayoutPath: path.join(rootDir, `storage-layout.${normalizedChainId}.latest.json`),
|
|
280
|
+
privateStateControllerAbiPath: path.join(rootDir, "PrivateStateController.callable-abi.json"),
|
|
281
|
+
dappRegistrationPath: path.join(rootDir, `dapp-registration.${normalizedChainId}.json`),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function privateStateCliInstallManifestPath(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
286
|
+
return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "install-manifest.json");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function readPrivateStateCliInstallManifest(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
290
|
+
return readJsonIfExists(privateStateCliInstallManifestPath(cacheBaseRoot));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function writePrivateStateCliInstallManifest({
|
|
294
|
+
dockerRequested,
|
|
295
|
+
includeLocalArtifacts,
|
|
296
|
+
localDeploymentBaseRoot,
|
|
297
|
+
deploymentArtifacts,
|
|
298
|
+
selectedVersions,
|
|
299
|
+
tokamakCliRuntime,
|
|
300
|
+
groth16Runtime,
|
|
301
|
+
}) {
|
|
302
|
+
const manifestPath = privateStateCliInstallManifestPath(deploymentArtifacts.cacheBaseRoot);
|
|
303
|
+
const manifest = {
|
|
304
|
+
installedAt: new Date().toISOString(),
|
|
305
|
+
package: summarizePackageReport(readPackageReport({
|
|
306
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
307
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
308
|
+
})),
|
|
309
|
+
dependencies: collectDependencyPackageReports().map(summarizePackageReport),
|
|
310
|
+
install: {
|
|
311
|
+
dockerRequested,
|
|
312
|
+
includeLocalArtifacts,
|
|
313
|
+
localDeploymentBaseRoot,
|
|
314
|
+
artifactCacheRoot: deploymentArtifacts.cacheBaseRoot,
|
|
315
|
+
selectedVersions,
|
|
316
|
+
tokamakCliRuntime,
|
|
317
|
+
groth16Runtime,
|
|
318
|
+
installedDeploymentArtifacts: deploymentArtifacts.installed.map((entry) => ({
|
|
319
|
+
chainId: entry.chainId,
|
|
320
|
+
source: entry.source,
|
|
321
|
+
bridgeTimestamp: entry.bridgeTimestamp,
|
|
322
|
+
dappTimestamp: entry.dappTimestamp,
|
|
323
|
+
})),
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
writeJson(manifestPath, manifest);
|
|
327
|
+
return { manifestPath, manifest };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function summarizePackageReport(report) {
|
|
331
|
+
return {
|
|
332
|
+
name: report.name,
|
|
333
|
+
version: report.version,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function buildDoctorReport({ probeGpu = false } = {}) {
|
|
338
|
+
const cacheBaseRoot = resolveArtifactCacheBaseRoot();
|
|
339
|
+
const installManifestPath = privateStateCliInstallManifestPath(cacheBaseRoot);
|
|
340
|
+
const installManifest = readJsonIfExists(installManifestPath);
|
|
341
|
+
const dependencyReports = collectDependencyPackageReports(installManifest);
|
|
342
|
+
const tokamakCli = inspectTokamakCliRuntime();
|
|
343
|
+
const groth16Runtime = inspectGroth16Runtime();
|
|
344
|
+
const gpuDockerReadiness = probeGpu
|
|
345
|
+
? inspectGpuDockerReadiness(tokamakCli)
|
|
346
|
+
: buildSkippedGpuDockerReadiness(tokamakCli);
|
|
347
|
+
const selectedRuntimeVersionCheck = buildSelectedRuntimeVersionCheck({
|
|
348
|
+
installManifest,
|
|
349
|
+
tokamakCli,
|
|
350
|
+
groth16Runtime,
|
|
351
|
+
});
|
|
352
|
+
const checks = [
|
|
353
|
+
{
|
|
354
|
+
name: "dependency package versions",
|
|
355
|
+
ok: dependencyReports.every((entry) => entry.ok),
|
|
356
|
+
details: dependencyReports.map((entry) => ({
|
|
357
|
+
name: entry.name,
|
|
358
|
+
currentVersion: entry.version,
|
|
359
|
+
installVersion: entry.installVersion,
|
|
360
|
+
ok: entry.ok,
|
|
361
|
+
error: entry.error,
|
|
362
|
+
})),
|
|
363
|
+
},
|
|
364
|
+
selectedRuntimeVersionCheck,
|
|
365
|
+
{
|
|
366
|
+
name: "tokamak zk-evm runtime",
|
|
367
|
+
ok: tokamakCli.installed,
|
|
368
|
+
details: {
|
|
369
|
+
doctorStatus: tokamakCli.doctor.status,
|
|
370
|
+
runtimeRoot: tokamakCli.runtimeRoot,
|
|
371
|
+
installations: tokamakCli.installations.map(({ platform, installMode, packageVersion, docker }) => ({
|
|
372
|
+
platform,
|
|
373
|
+
installMode,
|
|
374
|
+
packageVersion,
|
|
375
|
+
dockerEnvironment: docker?.dockerEnvironment ?? null,
|
|
376
|
+
useGpus: docker?.useGpus ?? null,
|
|
377
|
+
})),
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "tokamak docker gpu readiness",
|
|
382
|
+
ok: gpuDockerReadiness.ok,
|
|
383
|
+
details: {
|
|
384
|
+
expectedUseGpus: gpuDockerReadiness.expectedUseGpus,
|
|
385
|
+
liveUseGpus: gpuDockerReadiness.liveUseGpus,
|
|
386
|
+
skipped: gpuDockerReadiness.skipped,
|
|
387
|
+
mismatch: gpuDockerReadiness.mismatch,
|
|
388
|
+
mismatchError: gpuDockerReadiness.mismatchError,
|
|
389
|
+
hostNvidiaSmi: gpuDockerReadiness.hostNvidiaSmi
|
|
390
|
+
? summarizeProbeResult(gpuDockerReadiness.hostNvidiaSmi)
|
|
391
|
+
: null,
|
|
392
|
+
dockerNvidiaSmi: gpuDockerReadiness.dockerNvidiaSmi
|
|
393
|
+
? summarizeProbeResult(gpuDockerReadiness.dockerNvidiaSmi)
|
|
394
|
+
: null,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "groth16 runtime",
|
|
399
|
+
ok: groth16Runtime.installed,
|
|
400
|
+
details: {
|
|
401
|
+
packageRoot: groth16Runtime.packageRoot,
|
|
402
|
+
workspaceRoot: groth16Runtime.workspaceRoot,
|
|
403
|
+
doctorStatus: groth16Runtime.doctor.status,
|
|
404
|
+
checks: groth16Runtime.checks,
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
action: "doctor",
|
|
411
|
+
ok: checks.every((check) => check.ok),
|
|
412
|
+
generatedAt: new Date().toISOString(),
|
|
413
|
+
package: readPackageReport({
|
|
414
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
415
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
416
|
+
}),
|
|
417
|
+
installManifest: {
|
|
418
|
+
path: installManifestPath,
|
|
419
|
+
exists: Boolean(installManifest),
|
|
420
|
+
installedAt: installManifest?.installedAt ?? null,
|
|
421
|
+
dockerRequested: installManifest?.install?.dockerRequested ?? null,
|
|
422
|
+
includeLocalArtifacts: installManifest?.install?.includeLocalArtifacts ?? null,
|
|
423
|
+
selectedVersions: installManifest?.install?.selectedVersions ?? null,
|
|
424
|
+
tokamakCliRuntime: installManifest?.install?.tokamakCliRuntime ?? null,
|
|
425
|
+
groth16Runtime: installManifest?.install?.groth16Runtime ?? null,
|
|
426
|
+
},
|
|
427
|
+
dependencies: dependencyReports,
|
|
428
|
+
tokamakCli,
|
|
429
|
+
groth16Runtime,
|
|
430
|
+
gpuDockerReadiness,
|
|
431
|
+
checks,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function buildSkippedGpuDockerReadiness(tokamakCli) {
|
|
436
|
+
return {
|
|
437
|
+
ok: true,
|
|
438
|
+
skipped: true,
|
|
439
|
+
expectedUseGpus: Boolean(tokamakCli.cudaCompatible),
|
|
440
|
+
liveUseGpus: null,
|
|
441
|
+
mismatch: false,
|
|
442
|
+
mismatchError: null,
|
|
443
|
+
probeImage: DOCKER_CUDA_PROBE_IMAGE,
|
|
444
|
+
hostNvidiaSmi: null,
|
|
445
|
+
dockerNvidiaSmi: null,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function buildSelectedRuntimeVersionCheck({ installManifest, tokamakCli, groth16Runtime }) {
|
|
450
|
+
const selectedVersions = installManifest?.install?.selectedVersions ?? null;
|
|
451
|
+
const selectedTokamakCompatibleBackendVersion = selectedVersions?.tokamak
|
|
452
|
+
? normalizePackageVersionToCompatibleBackendVersion(
|
|
453
|
+
selectedVersions.tokamak,
|
|
454
|
+
"selected Tokamak zk-EVM CLI version",
|
|
455
|
+
)
|
|
456
|
+
: null;
|
|
457
|
+
const selectedGroth16CompatibleBackendVersion = selectedVersions?.groth16
|
|
458
|
+
? normalizePackageVersionToCompatibleBackendVersion(selectedVersions.groth16, "selected Groth16 CLI version")
|
|
459
|
+
: null;
|
|
460
|
+
const details = [
|
|
461
|
+
{
|
|
462
|
+
name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
463
|
+
selectedVersion: selectedVersions?.tokamak ?? null,
|
|
464
|
+
selectedCompatibleBackendVersion: selectedTokamakCompatibleBackendVersion,
|
|
465
|
+
installedVersion: tokamakCli.packageVersion ?? null,
|
|
466
|
+
compatibleBackendVersion: tokamakCli.compatibleBackendVersion ?? null,
|
|
467
|
+
ok: !selectedVersions?.tokamak
|
|
468
|
+
|| (
|
|
469
|
+
selectedVersions.tokamak === tokamakCli.packageVersion
|
|
470
|
+
&& selectedTokamakCompatibleBackendVersion === tokamakCli.compatibleBackendVersion
|
|
471
|
+
),
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: GROTH16_PACKAGE_NAME,
|
|
475
|
+
selectedVersion: selectedVersions?.groth16 ?? null,
|
|
476
|
+
selectedCompatibleBackendVersion: selectedGroth16CompatibleBackendVersion,
|
|
477
|
+
installedVersion: groth16Runtime.packageVersion ?? null,
|
|
478
|
+
compatibleBackendVersion: groth16Runtime.compatibleBackendVersion ?? null,
|
|
479
|
+
crsVersion: groth16Runtime.crsVersion ?? null,
|
|
480
|
+
crsCompatibleBackendVersion: groth16Runtime.crsCompatibleBackendVersion ?? null,
|
|
481
|
+
ok: !selectedVersions?.groth16
|
|
482
|
+
|| (
|
|
483
|
+
selectedVersions.groth16 === groth16Runtime.packageVersion
|
|
484
|
+
&& selectedGroth16CompatibleBackendVersion === groth16Runtime.compatibleBackendVersion
|
|
485
|
+
&& selectedGroth16CompatibleBackendVersion === groth16Runtime.crsCompatibleBackendVersion
|
|
486
|
+
),
|
|
487
|
+
},
|
|
488
|
+
];
|
|
489
|
+
return {
|
|
490
|
+
name: "selected proof backend runtime versions",
|
|
491
|
+
ok: details.every((entry) => entry.ok),
|
|
492
|
+
details,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function resolvePrivateStateInstallRuntimeVersions(args) {
|
|
497
|
+
const [groth16, tokamak] = await Promise.all([
|
|
498
|
+
resolveRequestedGroth16PackageVersion(args.groth16CliVersion),
|
|
499
|
+
resolveRequestedNpmPackageVersion({
|
|
500
|
+
packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
501
|
+
requestedVersion: args.tokamakZkEvmCliVersion,
|
|
502
|
+
optionName: "--tokamak-zk-evm-cli-version",
|
|
503
|
+
}),
|
|
504
|
+
]);
|
|
505
|
+
return { groth16, tokamak };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async function resolveRequestedGroth16PackageVersion(requestedVersion) {
|
|
509
|
+
if (requestedVersion !== undefined && requestedVersion !== null) {
|
|
510
|
+
return resolveRequestedNpmPackageVersion({
|
|
511
|
+
packageName: GROTH16_PACKAGE_NAME,
|
|
512
|
+
requestedVersion,
|
|
513
|
+
optionName: "--groth16-cli-version",
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const bundledPackageJson = readJson(path.join(resolveGroth16PackageRoot(), "package.json"));
|
|
518
|
+
return requireSemverVersion(bundledPackageJson.version, `${GROTH16_PACKAGE_NAME} bundled package version`);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async function resolveRequestedNpmPackageVersion({ packageName, requestedVersion, optionName }) {
|
|
522
|
+
const metadata = await fetchNpmPackageMetadata(packageName);
|
|
523
|
+
if (requestedVersion === undefined || requestedVersion === null) {
|
|
524
|
+
return requireSemverVersion(metadata?.["dist-tags"]?.latest, `${packageName} npm latest version`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const normalizedVersion = requireSemverVersion(requestedVersion, optionName);
|
|
528
|
+
if (!metadata.versions?.[normalizedVersion]) {
|
|
529
|
+
throw new Error(`npm package ${packageName} does not contain version ${normalizedVersion}.`);
|
|
530
|
+
}
|
|
531
|
+
return normalizedVersion;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async function installTokamakCliRuntimeForPrivateState({ version, docker }) {
|
|
535
|
+
const packageInstall = installManagedNpmPackage({
|
|
536
|
+
packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
537
|
+
version,
|
|
538
|
+
});
|
|
539
|
+
const invocation = buildTokamakCliInvocationForPackageRoot(packageInstall.packageRoot);
|
|
540
|
+
const installArgs = [...invocation.args, "--install"];
|
|
541
|
+
if (docker) {
|
|
542
|
+
installArgs.push("--docker");
|
|
543
|
+
}
|
|
544
|
+
run(invocation.command, installArgs, { cwd: packageInstall.packageRoot });
|
|
545
|
+
const doctor = runCaptured(invocation.command, [...invocation.args, "--doctor"], {
|
|
546
|
+
cwd: packageInstall.packageRoot,
|
|
547
|
+
});
|
|
548
|
+
const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
|
|
549
|
+
const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
|
|
550
|
+
const compatibleBackendVersion = readTokamakCliPackageCompatibleBackendVersion(packageInstall.packageRoot);
|
|
551
|
+
expect(
|
|
552
|
+
doctor.status === 0 && runtimeRoot,
|
|
553
|
+
[
|
|
554
|
+
"Tokamak zk-EVM CLI install completed, but tokamak-cli --doctor did not report a healthy runtime.",
|
|
555
|
+
doctorOutput.trim(),
|
|
556
|
+
].filter(Boolean).join(" "),
|
|
557
|
+
);
|
|
558
|
+
return {
|
|
559
|
+
packageName: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
560
|
+
packageVersion: version,
|
|
561
|
+
compatibleBackendVersion,
|
|
562
|
+
packageRoot: packageInstall.packageRoot,
|
|
563
|
+
entryPath: invocation.entryPath,
|
|
564
|
+
installPrefix: packageInstall.installPrefix,
|
|
565
|
+
runtimeRoot,
|
|
566
|
+
dockerRequested: Boolean(docker),
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async function installGroth16RuntimeForPrivateState({ version, docker }) {
|
|
571
|
+
const packageInstall = resolveGroth16RuntimePackageInstall(version);
|
|
572
|
+
const packageRoot = packageInstall.packageRoot;
|
|
573
|
+
const entryPath = resolveGroth16CliEntryPath(packageRoot);
|
|
574
|
+
const args = [entryPath, "--install", "--no-setup"];
|
|
575
|
+
if (docker) {
|
|
576
|
+
args.push("--docker");
|
|
577
|
+
}
|
|
578
|
+
run(process.execPath, args, { cwd: packageRoot });
|
|
579
|
+
const compatibleBackendVersion = readGroth16PackageCompatibleBackendVersion(packageRoot);
|
|
580
|
+
const crsInstall = await installGroth16CrsForPrivateStateVersion(compatibleBackendVersion);
|
|
581
|
+
const runtime = inspectGroth16Runtime({ packageRoot });
|
|
582
|
+
expect(runtime.installed, "Groth16 runtime install completed, but tokamak-groth16 --doctor still reports an unhealthy runtime.");
|
|
583
|
+
return {
|
|
584
|
+
...runtime,
|
|
585
|
+
packageName: GROTH16_PACKAGE_NAME,
|
|
586
|
+
packageVersion: version,
|
|
587
|
+
compatibleBackendVersion,
|
|
588
|
+
packageRoot,
|
|
589
|
+
entryPath,
|
|
590
|
+
installPrefix: packageInstall.installPrefix,
|
|
591
|
+
crsVersion: crsInstall.version,
|
|
592
|
+
crs: crsInstall,
|
|
593
|
+
dockerRequested: Boolean(docker),
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function resolveGroth16RuntimePackageInstall(version) {
|
|
598
|
+
const normalizedVersion = requireSemverVersion(version, `${GROTH16_PACKAGE_NAME} version`);
|
|
599
|
+
const bundledPackageRoot = resolveGroth16PackageRoot();
|
|
600
|
+
const bundledPackageJson = readJson(path.join(bundledPackageRoot, "package.json"));
|
|
601
|
+
if (bundledPackageJson.name === GROTH16_PACKAGE_NAME && bundledPackageJson.version === normalizedVersion) {
|
|
602
|
+
return {
|
|
603
|
+
packageName: GROTH16_PACKAGE_NAME,
|
|
604
|
+
version: normalizedVersion,
|
|
605
|
+
installPrefix: null,
|
|
606
|
+
packageRoot: bundledPackageRoot,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return installManagedNpmPackage({
|
|
611
|
+
packageName: GROTH16_PACKAGE_NAME,
|
|
612
|
+
version: normalizedVersion,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
async function installGroth16CrsForPrivateStateVersion(version) {
|
|
617
|
+
const workspaceRoot = defaultGroth16WorkspaceRoot();
|
|
618
|
+
const crsDir = path.join(workspaceRoot, "crs");
|
|
619
|
+
const existingInstall = readExistingGroth16CrsInstall({ version, crsDir });
|
|
620
|
+
if (existingInstall) {
|
|
621
|
+
return existingInstall;
|
|
622
|
+
}
|
|
623
|
+
const crsInstall = await downloadPublicGroth16MpcArtifactsByVersion({
|
|
624
|
+
version,
|
|
625
|
+
outputDir: crsDir,
|
|
626
|
+
selectedFiles: [
|
|
627
|
+
"circuit_final.zkey",
|
|
628
|
+
"verification_key.json",
|
|
629
|
+
"metadata.json",
|
|
630
|
+
"zkey_provenance.json",
|
|
631
|
+
],
|
|
632
|
+
});
|
|
633
|
+
const manifestPath = path.join(workspaceRoot, "install-manifest.json");
|
|
634
|
+
const manifest = readJsonIfExists(manifestPath) ?? {};
|
|
635
|
+
writeJson(manifestPath, {
|
|
636
|
+
...manifest,
|
|
637
|
+
workspaceRoot,
|
|
638
|
+
crsSource: "public-drive-mpc",
|
|
639
|
+
crs: crsInstall,
|
|
640
|
+
});
|
|
641
|
+
return crsInstall;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function readExistingGroth16CrsInstall({ version, crsDir }) {
|
|
645
|
+
const normalizedVersion = requireCanonicalGroth16CompatibleBackendVersion(version, "Groth16 MPC CRS version");
|
|
646
|
+
const selectedFiles = [
|
|
647
|
+
"circuit_final.zkey",
|
|
648
|
+
"verification_key.json",
|
|
649
|
+
"metadata.json",
|
|
650
|
+
"zkey_provenance.json",
|
|
651
|
+
];
|
|
652
|
+
const targetPaths = selectedFiles.map((fileName) => path.join(crsDir, fileName));
|
|
653
|
+
if (!targetPaths.every((targetPath) => fs.existsSync(targetPath))) {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
const metadata = readJson(path.join(crsDir, "metadata.json"));
|
|
657
|
+
let metadataVersion;
|
|
658
|
+
try {
|
|
659
|
+
metadataVersion = requireCanonicalGroth16CompatibleBackendVersion(
|
|
660
|
+
metadata.compatibleBackendVersion,
|
|
661
|
+
"installed Groth16 MPC CRS version",
|
|
662
|
+
);
|
|
663
|
+
} catch {
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
if (metadataVersion !== normalizedVersion) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
const provenance = readJson(path.join(crsDir, "zkey_provenance.json"));
|
|
670
|
+
return {
|
|
671
|
+
source: "local-cache",
|
|
672
|
+
archiveName: provenance.published_archive_name ?? null,
|
|
673
|
+
archiveFileId: parseDriveFileIdFromDownloadUrl(provenance.zkey_download_url),
|
|
674
|
+
folderUrl: provenance.published_folder_url ?? null,
|
|
675
|
+
version: normalizedVersion,
|
|
676
|
+
installedFiles: selectedFiles.map((archivePath, index) => ({
|
|
677
|
+
archivePath,
|
|
678
|
+
targetPath: targetPaths[index],
|
|
679
|
+
})),
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function parseDriveFileIdFromDownloadUrl(value) {
|
|
684
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
return new URL(value).searchParams.get("id");
|
|
689
|
+
} catch {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function installManagedNpmPackage({ packageName, version, cacheBaseRoot = resolveArtifactCacheBaseRoot() }) {
|
|
695
|
+
const normalizedPackageName = requireNonEmptyString(packageName, "packageName");
|
|
696
|
+
const normalizedVersion = requireSemverVersion(version, `${normalizedPackageName} version`);
|
|
697
|
+
const installPrefix = managedNpmPackageInstallPrefix({
|
|
698
|
+
packageName: normalizedPackageName,
|
|
699
|
+
version: normalizedVersion,
|
|
700
|
+
cacheBaseRoot,
|
|
701
|
+
});
|
|
702
|
+
fs.mkdirSync(installPrefix, { recursive: true });
|
|
703
|
+
run("npm", [
|
|
704
|
+
"install",
|
|
705
|
+
"--prefix",
|
|
706
|
+
installPrefix,
|
|
707
|
+
"--omit=dev",
|
|
708
|
+
"--no-audit",
|
|
709
|
+
"--fund=false",
|
|
710
|
+
`${normalizedPackageName}@${normalizedVersion}`,
|
|
711
|
+
]);
|
|
712
|
+
const packageRoot = path.join(installPrefix, "node_modules", ...normalizedPackageName.split("/"));
|
|
713
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
714
|
+
const packageJson = readJson(packageJsonPath);
|
|
715
|
+
expect(
|
|
716
|
+
packageJson.name === normalizedPackageName && packageJson.version === normalizedVersion,
|
|
717
|
+
`Installed package ${packageJsonPath} does not match ${normalizedPackageName}@${normalizedVersion}.`,
|
|
718
|
+
);
|
|
719
|
+
return {
|
|
720
|
+
packageName: normalizedPackageName,
|
|
721
|
+
version: normalizedVersion,
|
|
722
|
+
installPrefix,
|
|
723
|
+
packageRoot,
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function managedNpmPackageInstallPrefix({ packageName, version, cacheBaseRoot = resolveArtifactCacheBaseRoot() }) {
|
|
728
|
+
const safePackageName = requireNonEmptyString(packageName, "packageName")
|
|
729
|
+
.replace(/^@/, "")
|
|
730
|
+
.replace(/[^A-Za-z0-9._-]+/g, "__");
|
|
731
|
+
return path.join(privateStateCliRuntimeRoot(cacheBaseRoot), "npm", safePackageName, requireSemverVersion(version, "version"));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
async function downloadGroth16CrsArtifactsForPrivateState({
|
|
735
|
+
version,
|
|
736
|
+
outputDir,
|
|
737
|
+
selectedFiles,
|
|
738
|
+
}) {
|
|
739
|
+
if (version === undefined || version === null) {
|
|
740
|
+
return downloadLatestPublicGroth16MpcArtifacts({ outputDir, selectedFiles });
|
|
741
|
+
}
|
|
742
|
+
return downloadPublicGroth16MpcArtifactsByVersion({ version, outputDir, selectedFiles });
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function collectDependencyPackageReports(installManifest = null) {
|
|
746
|
+
const installVersions = new Map(
|
|
747
|
+
Array.isArray(installManifest?.dependencies)
|
|
748
|
+
? installManifest.dependencies.map((entry) => [entry.name, entry.version])
|
|
749
|
+
: [],
|
|
750
|
+
);
|
|
751
|
+
const targets = [
|
|
752
|
+
{
|
|
753
|
+
name: TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
754
|
+
packageJsonPath: path.join(resolveBundledTokamakCliPackageRoot(), "package.json"),
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
name: GROTH16_PACKAGE_NAME,
|
|
758
|
+
resolveTarget: "@tokamak-private-dapps/groth16/public-drive-crs",
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
name: "@tokamak-private-dapps/common-library",
|
|
762
|
+
resolveTarget: "@tokamak-private-dapps/common-library/artifact-cache",
|
|
763
|
+
},
|
|
764
|
+
{ name: "tokamak-l2js", resolveTarget: "tokamak-l2js" },
|
|
765
|
+
];
|
|
766
|
+
|
|
767
|
+
return targets.map((target) => {
|
|
768
|
+
const report = readPackageReport(target);
|
|
769
|
+
const installVersion = installVersions.get(report.name) ?? null;
|
|
770
|
+
return {
|
|
771
|
+
...report,
|
|
772
|
+
installVersion,
|
|
773
|
+
ok: Boolean(report.version) && (installVersion === null || installVersion === report.version),
|
|
774
|
+
};
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function readPackageReport({ name, packageJsonPath = null, packageJson = null, resolveTarget = null }) {
|
|
779
|
+
try {
|
|
780
|
+
const resolvedPackageJsonPath = packageJsonPath
|
|
781
|
+
? path.resolve(packageJsonPath)
|
|
782
|
+
: findPackageJsonForName(path.dirname(require.resolve(resolveTarget ?? name)), name);
|
|
783
|
+
const resolvedPackageJson = packageJson ?? readJson(resolvedPackageJsonPath);
|
|
784
|
+
return {
|
|
785
|
+
name: resolvedPackageJson.name ?? name,
|
|
786
|
+
version: resolvedPackageJson.version ?? null,
|
|
787
|
+
packageRoot: path.dirname(resolvedPackageJsonPath),
|
|
788
|
+
error: null,
|
|
789
|
+
};
|
|
790
|
+
} catch (error) {
|
|
791
|
+
return {
|
|
792
|
+
name,
|
|
793
|
+
version: null,
|
|
794
|
+
packageRoot: null,
|
|
795
|
+
error: error.message,
|
|
796
|
+
ok: false,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function findPackageJsonForName(startDir, expectedName) {
|
|
802
|
+
let current = path.resolve(startDir);
|
|
803
|
+
while (current !== path.dirname(current)) {
|
|
804
|
+
const candidate = path.join(current, "package.json");
|
|
805
|
+
if (fs.existsSync(candidate)) {
|
|
806
|
+
const packageJson = readJson(candidate);
|
|
807
|
+
if (packageJson.name === expectedName) {
|
|
808
|
+
return candidate;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
current = path.dirname(current);
|
|
812
|
+
}
|
|
813
|
+
throw new Error(`Cannot locate package.json for ${expectedName} above ${startDir}.`);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function resolveGroth16PackageRoot() {
|
|
817
|
+
const publicDriveCrsPath = require.resolve("@tokamak-private-dapps/groth16/public-drive-crs");
|
|
818
|
+
return path.dirname(findPackageJsonForName(path.dirname(publicDriveCrsPath), "@tokamak-private-dapps/groth16"));
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function readGroth16PackageCompatibleBackendVersion(packageRoot = resolveActiveGroth16PackageRoot()) {
|
|
822
|
+
return readGroth16CompatibleBackendVersionFromPackageJson(
|
|
823
|
+
readJson(path.join(packageRoot, "package.json")),
|
|
824
|
+
GROTH16_PACKAGE_NAME,
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function readTokamakCliPackageCompatibleBackendVersion(packageRoot = resolveActiveTokamakCliPackageRoot()) {
|
|
829
|
+
return readTokamakZkEvmCompatibleBackendVersionFromPackageJson(
|
|
830
|
+
readJson(path.join(packageRoot, "package.json")),
|
|
831
|
+
TOKAMAK_ZKEVM_CLI_PACKAGE_NAME,
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function resolveActiveGroth16PackageRoot() {
|
|
836
|
+
const manifestPackageRoot = readPrivateStateCliInstallManifest()?.install?.groth16Runtime?.packageRoot;
|
|
837
|
+
if (manifestPackageRoot && fs.existsSync(path.join(manifestPackageRoot, "package.json"))) {
|
|
838
|
+
return manifestPackageRoot;
|
|
839
|
+
}
|
|
840
|
+
return resolveGroth16PackageRoot();
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function resolveGroth16CliEntryPath(packageRoot = resolveGroth16PackageRoot()) {
|
|
844
|
+
return path.join(packageRoot, "cli", "tokamak-groth16-cli.mjs");
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function defaultGroth16WorkspaceRoot() {
|
|
848
|
+
return path.join(os.homedir(), "tokamak-private-channels", "groth16");
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function resolveGroth16ProofManifestPath() {
|
|
852
|
+
return path.join(defaultGroth16WorkspaceRoot(), "proof", "proof-manifest.json");
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function resolveActiveGroth16ProverRuntime() {
|
|
856
|
+
const packageRoot = resolveActiveGroth16PackageRoot();
|
|
857
|
+
return {
|
|
858
|
+
packageRoot,
|
|
859
|
+
entryPath: resolveGroth16CliEntryPath(packageRoot),
|
|
860
|
+
proofManifestPath: resolveGroth16ProofManifestPath(),
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function inspectGroth16Runtime({ packageRoot = resolveActiveGroth16PackageRoot() } = {}) {
|
|
865
|
+
const entryPath = resolveGroth16CliEntryPath(packageRoot);
|
|
866
|
+
const doctor = runCaptured(process.execPath, [entryPath, "--doctor", "--verbose"], { cwd: packageRoot });
|
|
867
|
+
const stdout = stripAnsi(doctor.stdout).trim();
|
|
868
|
+
const stderr = stripAnsi(doctor.stderr).trim();
|
|
869
|
+
const report = parseJsonReport(stdout);
|
|
870
|
+
const workspaceRoot = report?.workspaceRoot ?? defaultGroth16WorkspaceRoot();
|
|
871
|
+
const workspaceManifest = readJsonIfExists(path.join(workspaceRoot, "install-manifest.json"));
|
|
872
|
+
const crsVersion = workspaceManifest?.crs?.version ?? null;
|
|
873
|
+
const packageReport = readPackageReport({
|
|
874
|
+
name: GROTH16_PACKAGE_NAME,
|
|
875
|
+
packageJsonPath: path.join(packageRoot, "package.json"),
|
|
876
|
+
});
|
|
877
|
+
const compatibleBackendVersion = readGroth16PackageCompatibleBackendVersion(packageRoot);
|
|
878
|
+
const crsCompatibleBackendVersion = crsVersion
|
|
879
|
+
? requireCanonicalGroth16CompatibleBackendVersion(crsVersion, "installed Groth16 CRS version")
|
|
880
|
+
: null;
|
|
881
|
+
return {
|
|
882
|
+
installed: doctor.status === 0 && report?.ok === true,
|
|
883
|
+
packageVersion: packageReport.version,
|
|
884
|
+
compatibleBackendVersion,
|
|
885
|
+
packageRoot,
|
|
886
|
+
entryPath,
|
|
887
|
+
workspaceRoot: report?.workspaceRoot ?? null,
|
|
888
|
+
crsVersion,
|
|
889
|
+
crsCompatibleBackendVersion,
|
|
890
|
+
crs: workspaceManifest?.crs ?? null,
|
|
891
|
+
checks: report?.checks ?? [],
|
|
892
|
+
doctor: {
|
|
893
|
+
status: doctor.status,
|
|
894
|
+
stdout,
|
|
895
|
+
stderr,
|
|
896
|
+
},
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function resolveActiveTokamakCliPackageRoot() {
|
|
901
|
+
const manifestPackageRoot = readPrivateStateCliInstallManifest()?.install?.tokamakCliRuntime?.packageRoot;
|
|
902
|
+
if (manifestPackageRoot && fs.existsSync(path.join(manifestPackageRoot, "package.json"))) {
|
|
903
|
+
return manifestPackageRoot;
|
|
904
|
+
}
|
|
905
|
+
return resolveBundledTokamakCliPackageRoot();
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function buildTokamakCliInvocationForPackageRoot(packageRoot = resolveActiveTokamakCliPackageRoot()) {
|
|
909
|
+
const resolvedPackageRoot = path.resolve(packageRoot);
|
|
910
|
+
const entryPath = resolvedPackageRoot === resolveBundledTokamakCliPackageRoot()
|
|
911
|
+
? resolveTokamakCliEntryPath()
|
|
912
|
+
: path.join(resolvedPackageRoot, "dist", "cli.js");
|
|
913
|
+
return {
|
|
914
|
+
command: process.execPath,
|
|
915
|
+
args: [entryPath],
|
|
916
|
+
entryPath,
|
|
917
|
+
packageRoot: resolvedPackageRoot,
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function resolveActiveTokamakCliInvocation() {
|
|
922
|
+
return buildTokamakCliInvocationForPackageRoot();
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function resolveTokamakCliResourceDirForRuntimeRoot(runtimeRoot, ...segments) {
|
|
926
|
+
return path.join(runtimeRoot, "resource", ...segments);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function requireActiveTokamakCliRuntimeRoot() {
|
|
930
|
+
const runtime = inspectTokamakCliRuntime();
|
|
931
|
+
expect(runtime.runtimeRoot, "Unable to resolve the installed Tokamak zk-EVM runtime root. Run install first.");
|
|
932
|
+
return runtime.runtimeRoot;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function inspectTokamakCliRuntime({ packageRoot = resolveActiveTokamakCliPackageRoot() } = {}) {
|
|
936
|
+
const invocation = buildTokamakCliInvocationForPackageRoot(packageRoot);
|
|
937
|
+
const packageReport = readTokamakCliPackageReport(invocation.packageRoot);
|
|
938
|
+
const doctor = runCaptured(invocation.command, [...invocation.args, "--doctor"], {
|
|
939
|
+
cwd: invocation.packageRoot,
|
|
940
|
+
});
|
|
941
|
+
const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
|
|
942
|
+
const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
|
|
943
|
+
const cacheRoot = resolveTokamakCliCacheRoot();
|
|
944
|
+
const installations = readTokamakCliInstallations(cacheRoot);
|
|
945
|
+
const dockerModeInstalled = installations.some((entry) => entry.installMode === "docker" || entry.docker);
|
|
946
|
+
const cudaCompatible = installations.some((entry) => entry.docker?.useGpus === true);
|
|
947
|
+
|
|
948
|
+
return {
|
|
949
|
+
installed: doctor.status === 0 || installations.length > 0,
|
|
950
|
+
packageRoot: invocation.packageRoot,
|
|
951
|
+
entryPath: invocation.entryPath,
|
|
952
|
+
cacheRoot,
|
|
953
|
+
runtimeRoot,
|
|
954
|
+
packageVersion: packageReport.version,
|
|
955
|
+
compatibleBackendVersion: packageReport.compatibleBackendVersion,
|
|
956
|
+
packageError: packageReport.error,
|
|
957
|
+
dockerModeInstalled,
|
|
958
|
+
cudaCompatible,
|
|
959
|
+
doctor: {
|
|
960
|
+
status: doctor.status,
|
|
961
|
+
stdout: stripAnsi(doctor.stdout).trim(),
|
|
962
|
+
stderr: stripAnsi(doctor.stderr).trim(),
|
|
963
|
+
},
|
|
964
|
+
installations,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function inspectGpuDockerReadiness(tokamakCli) {
|
|
969
|
+
const hostNvidiaSmi = runProbe("nvidia-smi", ["--query-gpu=name,driver_version", "--format=csv,noheader"]);
|
|
970
|
+
const dockerNvidiaSmi = runProbe("docker", [
|
|
971
|
+
"run",
|
|
972
|
+
"--rm",
|
|
973
|
+
"--gpus",
|
|
974
|
+
"all",
|
|
975
|
+
DOCKER_CUDA_PROBE_IMAGE,
|
|
976
|
+
"nvidia-smi",
|
|
977
|
+
]);
|
|
978
|
+
const expectedUseGpus = Boolean(tokamakCli.cudaCompatible);
|
|
979
|
+
const liveUseGpus = hostNvidiaSmi.ok && dockerNvidiaSmi.ok;
|
|
980
|
+
const mismatch = expectedUseGpus !== liveUseGpus;
|
|
981
|
+
return {
|
|
982
|
+
ok: !mismatch,
|
|
983
|
+
skipped: false,
|
|
984
|
+
expectedUseGpus,
|
|
985
|
+
liveUseGpus,
|
|
986
|
+
mismatch,
|
|
987
|
+
mismatchError: mismatch
|
|
988
|
+
? [
|
|
989
|
+
"Tokamak CLI Docker GPU metadata does not match live NVIDIA/Docker GPU probes.",
|
|
990
|
+
`metadata useGpus=${expectedUseGpus}; live useGpus=${liveUseGpus}.`,
|
|
991
|
+
].join(" ")
|
|
992
|
+
: null,
|
|
993
|
+
probeImage: DOCKER_CUDA_PROBE_IMAGE,
|
|
994
|
+
hostNvidiaSmi,
|
|
995
|
+
dockerNvidiaSmi,
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function runProbe(command, args) {
|
|
1000
|
+
const result = spawnSync(command, args, {
|
|
1001
|
+
encoding: "utf8",
|
|
1002
|
+
timeout: DOCTOR_GPU_PROBE_TIMEOUT_MS,
|
|
1003
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1004
|
+
});
|
|
1005
|
+
return {
|
|
1006
|
+
command,
|
|
1007
|
+
args,
|
|
1008
|
+
ok: !result.error && result.status === 0,
|
|
1009
|
+
status: result.status,
|
|
1010
|
+
signal: result.signal,
|
|
1011
|
+
error: result.error ? result.error.message : null,
|
|
1012
|
+
stdout: stripAnsi(result.stdout ?? "").trim(),
|
|
1013
|
+
stderr: stripAnsi(result.stderr ?? "").trim(),
|
|
1014
|
+
timedOut: result.error?.code === "ETIMEDOUT",
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function summarizeProbeResult(result) {
|
|
1019
|
+
return {
|
|
1020
|
+
command: [result.command, ...result.args].join(" "),
|
|
1021
|
+
ok: result.ok,
|
|
1022
|
+
status: result.status,
|
|
1023
|
+
signal: result.signal,
|
|
1024
|
+
error: result.error,
|
|
1025
|
+
timedOut: result.timedOut,
|
|
1026
|
+
stdout: truncateText(result.stdout, 2000),
|
|
1027
|
+
stderr: truncateText(result.stderr, 2000),
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function truncateText(value, maxLength) {
|
|
1032
|
+
const text = String(value ?? "");
|
|
1033
|
+
if (text.length <= maxLength) {
|
|
1034
|
+
return text;
|
|
1035
|
+
}
|
|
1036
|
+
return `${text.slice(0, maxLength)}...`;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function parseJsonReport(value) {
|
|
1040
|
+
try {
|
|
1041
|
+
return JSON.parse(value);
|
|
1042
|
+
} catch {
|
|
1043
|
+
return null;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function resolveTokamakCliCacheRoot() {
|
|
1048
|
+
return path.resolve(process.env.TOKAMAK_ZKEVM_CLI_CACHE_DIR ?? path.join(os.homedir(), ".tokamak-zk-evm"));
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function readTokamakCliInstallations(cacheRoot) {
|
|
1052
|
+
if (!fs.existsSync(cacheRoot)) {
|
|
1053
|
+
return [];
|
|
1054
|
+
}
|
|
1055
|
+
return fs.readdirSync(cacheRoot, { withFileTypes: true })
|
|
1056
|
+
.filter((entry) => entry.isDirectory())
|
|
1057
|
+
.map((entry) => {
|
|
1058
|
+
const platformDir = path.join(cacheRoot, entry.name);
|
|
1059
|
+
const statePath = path.join(platformDir, "installation.json");
|
|
1060
|
+
if (!fs.existsSync(statePath)) {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
const state = readJsonIfExists(statePath);
|
|
1064
|
+
const dockerBootstrapPath = path.join(platformDir, "docker", "bootstrap.json");
|
|
1065
|
+
const docker = readJsonIfExists(dockerBootstrapPath);
|
|
1066
|
+
return {
|
|
1067
|
+
platform: entry.name,
|
|
1068
|
+
statePath,
|
|
1069
|
+
runtimeRoot: path.join(platformDir, "runtime"),
|
|
1070
|
+
installMode: state?.installMode ?? (docker ? "docker" : null),
|
|
1071
|
+
packageVersion: state?.packageVersion ?? docker?.packageVersion ?? null,
|
|
1072
|
+
installedAt: state?.installedAt ?? null,
|
|
1073
|
+
dockerBootstrapPath,
|
|
1074
|
+
docker,
|
|
1075
|
+
};
|
|
1076
|
+
})
|
|
1077
|
+
.filter(Boolean);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function parseRuntimeRootFromTokamakDoctorOutput(output) {
|
|
1081
|
+
const match = String(output ?? "").match(/^\[ ok \] Runtime workspace:\s*(.+)$/m);
|
|
1082
|
+
return match ? path.resolve(match[1].trim()) : null;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function stripAnsi(value) {
|
|
1086
|
+
return String(value ?? "").replace(/\u001b\[[0-9;]*m/g, "");
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
async function installPrivateStateCliArtifacts({
|
|
1090
|
+
dappName,
|
|
1091
|
+
indexFileId = process.env.PRIVATE_STATE_DRIVE_ARTIFACT_INDEX_FILE_ID
|
|
1092
|
+
?? process.env.TOKAMAK_ARTIFACT_INDEX_FILE_ID
|
|
1093
|
+
?? DEFAULT_PUBLIC_ARTIFACT_INDEX_FILE_ID,
|
|
1094
|
+
cacheBaseRoot,
|
|
1095
|
+
localDeploymentBaseRoot,
|
|
1096
|
+
localChainIds = [31337],
|
|
1097
|
+
groth16CrsVersion,
|
|
1098
|
+
} = {}) {
|
|
1099
|
+
const normalizedDappName = requireNonEmptyString(dappName, "dappName");
|
|
1100
|
+
const normalizedCacheBaseRoot = resolveArtifactCacheBaseRoot(cacheBaseRoot);
|
|
1101
|
+
const normalizedLocalDeploymentBaseRoot = localDeploymentBaseRoot
|
|
1102
|
+
? path.resolve(localDeploymentBaseRoot)
|
|
1103
|
+
: null;
|
|
1104
|
+
const index = await fetchPublicArtifactIndex(indexFileId);
|
|
1105
|
+
const installed = [];
|
|
1106
|
+
|
|
1107
|
+
for (const chainId of Object.keys(index.chains).sort(compareChainIds)) {
|
|
1108
|
+
const chain = index.chains[chainId];
|
|
1109
|
+
if (!chain?.bridge?.timestamp || !chain?.bridge?.files || !chain.dapps?.[normalizedDappName]) {
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
installed.push(await materializePrivateStateCliDeployment({
|
|
1113
|
+
index,
|
|
1114
|
+
chainId,
|
|
1115
|
+
dappName: normalizedDappName,
|
|
1116
|
+
cacheBaseRoot: normalizedCacheBaseRoot,
|
|
1117
|
+
source: "drive",
|
|
1118
|
+
groth16CrsVersion,
|
|
1119
|
+
}));
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (normalizedLocalDeploymentBaseRoot) {
|
|
1123
|
+
for (const chainId of localChainIds) {
|
|
1124
|
+
installed.push(await materializeLocalPrivateStateCliDeployment({
|
|
1125
|
+
chainId,
|
|
1126
|
+
dappName: normalizedDappName,
|
|
1127
|
+
cacheBaseRoot: normalizedCacheBaseRoot,
|
|
1128
|
+
localDeploymentBaseRoot: normalizedLocalDeploymentBaseRoot,
|
|
1129
|
+
groth16CrsVersion,
|
|
1130
|
+
}));
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if (installed.length === 0) {
|
|
1135
|
+
throw new Error(`No installable artifacts found for ${normalizedDappName}.`);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return {
|
|
1139
|
+
cacheBaseRoot: normalizedCacheBaseRoot,
|
|
1140
|
+
artifactRoot: privateStateCliArtifactRoot(normalizedCacheBaseRoot),
|
|
1141
|
+
installed,
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
async function materializePrivateStateCliDeployment({
|
|
1146
|
+
index,
|
|
1147
|
+
chainId,
|
|
1148
|
+
dappName,
|
|
1149
|
+
cacheBaseRoot,
|
|
1150
|
+
source,
|
|
1151
|
+
groth16CrsVersion,
|
|
1152
|
+
}) {
|
|
1153
|
+
const normalizedChainId = String(requireChainId(chainId));
|
|
1154
|
+
const normalizedDappName = requireNonEmptyString(dappName, "dappName");
|
|
1155
|
+
const chain = index.chains[normalizedChainId];
|
|
1156
|
+
if (!chain) {
|
|
1157
|
+
throw new Error(`Drive artifact index does not contain chain ${normalizedChainId}.`);
|
|
1158
|
+
}
|
|
1159
|
+
if (!chain.bridge?.timestamp || !chain.bridge?.files) {
|
|
1160
|
+
throw new Error(`Drive artifact index is missing bridge artifacts for chain ${normalizedChainId}.`);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const dapp = chain.dapps?.[normalizedDappName];
|
|
1164
|
+
if (!dapp?.timestamp || !dapp?.files) {
|
|
1165
|
+
throw new Error(
|
|
1166
|
+
`Drive artifact index is missing ${normalizedDappName} artifacts for chain ${normalizedChainId}.`,
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const paths = privateStateCliArtifactPaths(cacheBaseRoot, normalizedChainId);
|
|
1171
|
+
fs.rmSync(paths.rootDir, { recursive: true, force: true });
|
|
1172
|
+
fs.mkdirSync(paths.rootDir, { recursive: true });
|
|
1173
|
+
|
|
1174
|
+
await materializeSelectedDriveFiles({
|
|
1175
|
+
targetDir: paths.rootDir,
|
|
1176
|
+
files: chain.bridge.files,
|
|
1177
|
+
selectedFiles: privateStateBridgeArtifactSelections(normalizedChainId, paths),
|
|
1178
|
+
});
|
|
1179
|
+
await materializeFlatGroth16Zkey({ paths, groth16CrsVersion });
|
|
1180
|
+
await materializeSelectedDriveFiles({
|
|
1181
|
+
targetDir: paths.rootDir,
|
|
1182
|
+
files: dapp.files,
|
|
1183
|
+
selectedFiles: privateStateDappArtifactSelections(normalizedChainId, paths),
|
|
1184
|
+
});
|
|
1185
|
+
rewriteFlatGroth16Manifest(paths.grothManifestPath, paths.grothZkeyPath);
|
|
1186
|
+
|
|
1187
|
+
return {
|
|
1188
|
+
chainId: Number(normalizedChainId),
|
|
1189
|
+
source,
|
|
1190
|
+
artifactDir: paths.rootDir,
|
|
1191
|
+
bridgeTimestamp: chain.bridge.timestamp,
|
|
1192
|
+
dappTimestamp: dapp.timestamp,
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
async function materializeLocalPrivateStateCliDeployment({
|
|
1197
|
+
chainId,
|
|
1198
|
+
dappName,
|
|
1199
|
+
cacheBaseRoot,
|
|
1200
|
+
localDeploymentBaseRoot,
|
|
1201
|
+
groth16CrsVersion,
|
|
1202
|
+
}) {
|
|
1203
|
+
const normalizedChainId = String(requireChainId(chainId));
|
|
1204
|
+
const normalizedDappName = requireNonEmptyString(dappName, "dappName");
|
|
1205
|
+
const bridgeRoot = path.join(
|
|
1206
|
+
localDeploymentBaseRoot,
|
|
1207
|
+
"deployment",
|
|
1208
|
+
`chain-id-${normalizedChainId}`,
|
|
1209
|
+
"bridge",
|
|
1210
|
+
);
|
|
1211
|
+
const dappRoot = path.join(
|
|
1212
|
+
localDeploymentBaseRoot,
|
|
1213
|
+
"deployment",
|
|
1214
|
+
`chain-id-${normalizedChainId}`,
|
|
1215
|
+
"dapps",
|
|
1216
|
+
normalizedDappName,
|
|
1217
|
+
);
|
|
1218
|
+
const bridgeTimestamp = requireLatestTimestampLabel(bridgeRoot, `bridge artifacts for chain ${normalizedChainId}`);
|
|
1219
|
+
const dappTimestamp = requireLatestTimestampLabel(dappRoot, `${normalizedDappName} artifacts for chain ${normalizedChainId}`);
|
|
1220
|
+
const bridgeDir = path.join(bridgeRoot, bridgeTimestamp);
|
|
1221
|
+
const dappDir = path.join(dappRoot, dappTimestamp);
|
|
1222
|
+
const paths = privateStateCliArtifactPaths(cacheBaseRoot, normalizedChainId);
|
|
1223
|
+
fs.rmSync(paths.rootDir, { recursive: true, force: true });
|
|
1224
|
+
fs.mkdirSync(paths.rootDir, { recursive: true });
|
|
1225
|
+
|
|
1226
|
+
materializeSelectedLocalFiles({
|
|
1227
|
+
targetDir: paths.rootDir,
|
|
1228
|
+
selectedFiles: [
|
|
1229
|
+
...localizeArtifactSelections(bridgeDir, privateStateBridgeArtifactSelections(normalizedChainId, paths)),
|
|
1230
|
+
...localizeArtifactSelections(dappDir, privateStateDappArtifactSelections(normalizedChainId, paths)),
|
|
1231
|
+
],
|
|
1232
|
+
});
|
|
1233
|
+
await materializeFlatGroth16Zkey({ paths, groth16CrsVersion });
|
|
1234
|
+
rewriteFlatGroth16Manifest(paths.grothManifestPath, paths.grothZkeyPath);
|
|
1235
|
+
|
|
1236
|
+
return {
|
|
1237
|
+
chainId: Number(normalizedChainId),
|
|
1238
|
+
source: "local",
|
|
1239
|
+
artifactDir: paths.rootDir,
|
|
1240
|
+
bridgeTimestamp,
|
|
1241
|
+
dappTimestamp,
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function privateStateBridgeArtifactSelections(chainId, paths) {
|
|
1246
|
+
return [
|
|
1247
|
+
[`bridge.${chainId}.json`, path.basename(paths.bridgeDeploymentPath)],
|
|
1248
|
+
[`bridge-abi-manifest.${chainId}.json`, path.basename(paths.bridgeAbiManifestPath)],
|
|
1249
|
+
[`groth16.${chainId}.latest.json`, path.basename(paths.grothManifestPath)],
|
|
1250
|
+
];
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function privateStateDappArtifactSelections(chainId, paths) {
|
|
1254
|
+
return [
|
|
1255
|
+
[`deployment.${chainId}.latest.json`, path.basename(paths.dappDeploymentPath)],
|
|
1256
|
+
[`storage-layout.${chainId}.latest.json`, path.basename(paths.dappStorageLayoutPath)],
|
|
1257
|
+
["PrivateStateController.callable-abi.json", path.basename(paths.privateStateControllerAbiPath)],
|
|
1258
|
+
[`dapp-registration.${chainId}.json`, path.basename(paths.dappRegistrationPath)],
|
|
1259
|
+
];
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function localizeArtifactSelections(sourceDir, selections) {
|
|
1263
|
+
return selections.map(([sourceName, targetName]) => [path.join(sourceDir, sourceName), targetName]);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
async function materializeFlatGroth16Zkey({ paths, groth16CrsVersion }) {
|
|
1267
|
+
await downloadGroth16CrsArtifactsForPrivateState({
|
|
1268
|
+
version: groth16CrsVersion,
|
|
1269
|
+
outputDir: paths.rootDir,
|
|
1270
|
+
selectedFiles: [
|
|
1271
|
+
["circuit_final.zkey", path.basename(paths.grothZkeyPath)],
|
|
1272
|
+
],
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function rewriteFlatGroth16Manifest(manifestPath, zkeyPath) {
|
|
1277
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
1278
|
+
manifest.artifactDir = ".";
|
|
1279
|
+
manifest.grothArtifactSource = "public-drive-mpc";
|
|
1280
|
+
manifest.publicGroth16MpcDriveFolderId = PUBLIC_GROTH16_MPC_DRIVE_FOLDER_ID;
|
|
1281
|
+
manifest.artifacts = {
|
|
1282
|
+
...manifest.artifacts,
|
|
1283
|
+
zkeyPath: path.basename(zkeyPath),
|
|
1284
|
+
metadataPath: null,
|
|
1285
|
+
verificationKeyPath: null,
|
|
1286
|
+
zkeyProvenancePath: null,
|
|
1287
|
+
};
|
|
1288
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function compareChainIds(left, right) {
|
|
1292
|
+
return Number(left) - Number(right);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
export {
|
|
1296
|
+
buildDoctorReport,
|
|
1297
|
+
printDoctorHumanReport,
|
|
1298
|
+
resolvePrivateStateInstallRuntimeVersions,
|
|
1299
|
+
installTokamakCliRuntimeForPrivateState,
|
|
1300
|
+
installGroth16RuntimeForPrivateState,
|
|
1301
|
+
installPrivateStateCliArtifacts,
|
|
1302
|
+
writePrivateStateCliInstallManifest,
|
|
1303
|
+
resolveArtifactCacheBaseRoot,
|
|
1304
|
+
privateStateCliArtifactPaths,
|
|
1305
|
+
inspectGroth16Runtime,
|
|
1306
|
+
resolveActiveGroth16ProverRuntime,
|
|
1307
|
+
resolveActiveTokamakCliInvocation,
|
|
1308
|
+
readTokamakCliPackageReport,
|
|
1309
|
+
requireActiveTokamakCliRuntimeRoot,
|
|
1310
|
+
resolveTokamakCliResourceDirForRuntimeRoot,
|
|
1311
|
+
};
|