verifyhash 0.1.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.
Files changed (154) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +883 -0
  3. package/cli/abi/ContributionRegistry.json +881 -0
  4. package/cli/agent.js +2173 -0
  5. package/cli/anchor-artifact.js +853 -0
  6. package/cli/anchor.js +400 -0
  7. package/cli/claim.js +881 -0
  8. package/cli/core/agent-commit.js +448 -0
  9. package/cli/core/agent-session.js +598 -0
  10. package/cli/core/anchor-binding.js +663 -0
  11. package/cli/core/attestation.js +580 -0
  12. package/cli/core/evidence-plans.js +495 -0
  13. package/cli/core/fixtures/evidence-plans/baseline.json +19 -0
  14. package/cli/core/fulfill-intake.js +1082 -0
  15. package/cli/core/go-live-preflight.js +481 -0
  16. package/cli/core/license.js +534 -0
  17. package/cli/core/manifest.js +243 -0
  18. package/cli/core/packetseal.js +591 -0
  19. package/cli/core/registryArtifact.js +49 -0
  20. package/cli/core/revocation.js +539 -0
  21. package/cli/core/rfc3161.js +389 -0
  22. package/cli/core/timestamp.js +482 -0
  23. package/cli/core/trust-asof.js +479 -0
  24. package/cli/dataset.js +2950 -0
  25. package/cli/evidence.js +2227 -0
  26. package/cli/fulfill-webhook-http.js +438 -0
  27. package/cli/git.js +220 -0
  28. package/cli/hash.js +550 -0
  29. package/cli/identity.js +1072 -0
  30. package/cli/journal-cli.js +1110 -0
  31. package/cli/journal-log.js +454 -0
  32. package/cli/journal.js +334 -0
  33. package/cli/lineage.js +447 -0
  34. package/cli/list.js +287 -0
  35. package/cli/parcel.js +1509 -0
  36. package/cli/proof.js +578 -0
  37. package/cli/prove.js +300 -0
  38. package/cli/receipt.js +631 -0
  39. package/cli/registry.js +331 -0
  40. package/cli/reputation.js +344 -0
  41. package/cli/revocation.js +495 -0
  42. package/cli/serve-verify-http.js +298 -0
  43. package/cli/serve-verify.js +333 -0
  44. package/cli/show.js +339 -0
  45. package/cli/verify.js +383 -0
  46. package/cli/vh.js +3927 -0
  47. package/docs/ADOPT.md +183 -0
  48. package/docs/ADOPTION.json +11 -0
  49. package/docs/AGENTTRACE.md +247 -0
  50. package/docs/ANCHORING.md +167 -0
  51. package/docs/AUDIT.md +55 -0
  52. package/docs/CONFORMANCE.md +107 -0
  53. package/docs/DATALEDGER.md +638 -0
  54. package/docs/DECIDE.md +47 -0
  55. package/docs/DECISIONS-PENDING.md +27 -0
  56. package/docs/DEPLOY-PUBLIC-SITE.md +301 -0
  57. package/docs/ENGINE-LEDGER.json +12 -0
  58. package/docs/EVIDENCE.md +519 -0
  59. package/docs/GO-LIVE.md +66 -0
  60. package/docs/IDENTITY.md +123 -0
  61. package/docs/INDEPENDENT-VERIFICATION.md +377 -0
  62. package/docs/INTEGRITY-JOURNAL.md +337 -0
  63. package/docs/KEY-LIFECYCLE.md +179 -0
  64. package/docs/LICENSING.md +46 -0
  65. package/docs/LINEAGE.md +307 -0
  66. package/docs/LOOP-AUDIT-2026-07-03.json +580 -0
  67. package/docs/LOOP-HARDENING-PLAN.md +44 -0
  68. package/docs/MERKLE-LEAVES.md +113 -0
  69. package/docs/METRICS.jsonl +31 -0
  70. package/docs/MORNING.md +204 -0
  71. package/docs/PILOT.md +444 -0
  72. package/docs/PROOFPARCEL.md +227 -0
  73. package/docs/PROOFS.md +262 -0
  74. package/docs/RECEIPTS.md +341 -0
  75. package/docs/REPUTATION.md +158 -0
  76. package/docs/SDK.md +301 -0
  77. package/docs/STRATEGY-ARCHIVE.md +5055 -0
  78. package/docs/SUPERVISOR-RUNBOOK.md +52 -0
  79. package/docs/TRUST-BOUNDARIES.md +335 -0
  80. package/docs/TRUSTLEDGER.md +1976 -0
  81. package/docs/USAGE-BUDGET.json +121 -0
  82. package/docs/VERIFY-SERVICE.md +168 -0
  83. package/index.js +160 -0
  84. package/package.json +41 -0
  85. package/trustledger/build-standalone.js +796 -0
  86. package/trustledger/cli.js +3179 -0
  87. package/trustledger/close.js +391 -0
  88. package/trustledger/corpus.js +159 -0
  89. package/trustledger/dist/BUILD-PROVENANCE.json +99 -0
  90. package/trustledger/dist/trustledger-standalone.html +6197 -0
  91. package/trustledger/dist/trustledger-standalone.html.sha256 +1 -0
  92. package/trustledger/door-core.js +442 -0
  93. package/trustledger/fixtures/bank.csv +7 -0
  94. package/trustledger/fixtures/bank.malformed.csv +3 -0
  95. package/trustledger/fixtures/bank.noalias.csv +5 -0
  96. package/trustledger/fixtures/bank.ofx +34 -0
  97. package/trustledger/fixtures/bank.real.csv +5 -0
  98. package/trustledger/fixtures/corpus/_shared/prior-close.json +22 -0
  99. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/inputs.json +14 -0
  100. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/meta.json +7 -0
  101. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/inputs.json +14 -0
  102. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/meta.json +7 -0
  103. package/trustledger/fixtures/corpus/continuity-break--benign-twin/inputs.json +15 -0
  104. package/trustledger/fixtures/corpus/continuity-break--benign-twin/meta.json +7 -0
  105. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/inputs.json +15 -0
  106. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/meta.json +7 -0
  107. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/inputs.json +13 -0
  108. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/meta.json +7 -0
  109. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/inputs.json +13 -0
  110. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/meta.json +7 -0
  111. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/inputs.json +15 -0
  112. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/meta.json +7 -0
  113. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/inputs.json +15 -0
  114. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/meta.json +7 -0
  115. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/inputs.json +16 -0
  116. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/meta.json +7 -0
  117. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/inputs.json +13 -0
  118. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/meta.json +7 -0
  119. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/inputs.json +13 -0
  120. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/meta.json +7 -0
  121. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/inputs.json +13 -0
  122. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/meta.json +7 -0
  123. package/trustledger/fixtures/e2e/bank.aliased.csv +4 -0
  124. package/trustledger/fixtures/e2e/bank.csv +4 -0
  125. package/trustledger/fixtures/e2e/bank.nsf.csv +4 -0
  126. package/trustledger/fixtures/e2e/quickbooks.csv +6 -0
  127. package/trustledger/fixtures/e2e/quickbooks.nsf.csv +8 -0
  128. package/trustledger/fixtures/e2e/rentroll.csv +6 -0
  129. package/trustledger/fixtures/e2e/rentroll.nsf.csv +8 -0
  130. package/trustledger/fixtures/e2e/rentroll.short.csv +5 -0
  131. package/trustledger/fixtures/plans/baseline.json +25 -0
  132. package/trustledger/fixtures/plans/price-binding.example.json +27 -0
  133. package/trustledger/fixtures/policy/ambiguous-deposit-example.json +12 -0
  134. package/trustledger/fixtures/policy/baseline.json +19 -0
  135. package/trustledger/fixtures/policy/ca-example.json +12 -0
  136. package/trustledger/fixtures/policy/negative-tenant-ledger-example.json +12 -0
  137. package/trustledger/fixtures/policy/owner-overdraw-example.json +12 -0
  138. package/trustledger/fixtures/quickbooks.csv +7 -0
  139. package/trustledger/fixtures/quickbooks.real.csv +5 -0
  140. package/trustledger/fixtures/rentroll.csv +6 -0
  141. package/trustledger/fixtures/rentroll.real.csv +4 -0
  142. package/trustledger/ingest.js +1163 -0
  143. package/trustledger/lib/policy-bundled-loader.js +44 -0
  144. package/trustledger/lib/sha256-vendored.js +227 -0
  145. package/trustledger/license.js +563 -0
  146. package/trustledger/match.js +551 -0
  147. package/trustledger/plans.js +551 -0
  148. package/trustledger/policy.js +398 -0
  149. package/trustledger/public/index.html +512 -0
  150. package/trustledger/reconcile.js +1486 -0
  151. package/trustledger/report.js +887 -0
  152. package/trustledger/seal.js +854 -0
  153. package/trustledger/server.js +391 -0
  154. package/trustledger/valueproof.js +350 -0
package/cli/prove.js ADDED
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+
3
+ // `vh prove <file> --root <repoDir>` — prove a single file belongs to an anchored repo root.
4
+ //
5
+ // The big idea of EPIC-2: you don't have to anchor every file in a repository. You anchor the
6
+ // repo's *Merkle root* once (one hash, one tx) and later prove that any individual file was part
7
+ // of that exact snapshot by producing a Merkle proof — a short list of sibling hashes that folds
8
+ // the file's leaf up to the anchored root. The on-chain `verifyLeaf(root, leaf, proof)` replays
9
+ // that fold with sorted-pair hashing and returns true iff the file really was in the tree.
10
+ //
11
+ // The flow:
12
+ // 1. Hash the repo root directory into its sorted-leaf Merkle root and per-file leaves
13
+ // (cli/hash.js — the exact same construction `vh anchor <dir>` and `verifyLeaf` agree on).
14
+ // 2. Locate <file> within that tree and generate its Merkle proof.
15
+ // 3. Either *print* the proof (`--dry-run`, no key/network) or *check it on-chain*:
16
+ // - read the registry: confirm the root is actually anchored (else there is nothing to
17
+ // prove the file against), then
18
+ // - call `verifyLeaf(root, leaf, proof)` and report ACCEPTED / REJECTED.
19
+ // 4. (Optional) `--anchor` first submits the root via anchor() so a fresh repo can be proven in
20
+ // one shot; this is the only path that needs a signer.
21
+ //
22
+ // Why this is tamper-evident: the leaf is the file's PATH-BOUND digest
23
+ // leaf = keccak256(DIR_LEAF_DOMAIN ++ relPath ++ 0x00 ++ keccak256(file bytes)).
24
+ // Change one byte of the file and keccak256(file bytes) changes; rename the file and relPath
25
+ // changes; either way the leaf changes, so the proof (built for the *original* leaf) no longer
26
+ // folds to the anchored root and `verifyLeaf` returns false. That property is exactly what the
27
+ // test pins down. (The leaf binds the path, so a single file's proof is tied to its location in
28
+ // the repo, not just its bytes.)
29
+ //
30
+ // Split into pure pieces (buildProof) and an on-chain runner (runProve) so the end-to-end test can
31
+ // drive it against a live hardhat node and assert the contract's verifyLeaf verdict directly.
32
+
33
+ const path = require("path");
34
+ const fs = require("fs");
35
+ const { hashDir } = require("./hash");
36
+ const { buildProofArtifact, writeProofArtifact } = require("./proof");
37
+
38
+ const ARTIFACT = require("./core/registryArtifact");
39
+ const ABI = ARTIFACT.abi;
40
+
41
+ /**
42
+ * Build (purely, no network) a Merkle proof for `filePath` within the repo at `rootDir`.
43
+ *
44
+ * Returns the directory's anchored root, the file's PATH-BOUND leaf
45
+ * (= keccak256(DIR_LEAF_DOMAIN ++ relPath ++ 0x00 ++ keccak256(file bytes))), and the proof.
46
+ * Replaying `proof` against `leaf` with sorted-pair hashing reproduces `root`, so the proof is
47
+ * exactly what the contract's `verifyLeaf` accepts (it tags the leaf with LEAF_TAG itself). The
48
+ * raw content digest is also returned as `contentHash` for display.
49
+ *
50
+ * @param {object} opts
51
+ * @param {string} opts.file path to a file that must live under rootDir
52
+ * @param {string} opts.rootDir the repository root directory to anchor/prove against
53
+ * @returns {{
54
+ * root: string,
55
+ * leaf: string, // path-bound leaf (what on-chain verifyLeaf consumes)
56
+ * contentHash: string, // bare keccak256(file bytes), for display
57
+ * proof: string[],
58
+ * file: string, // path of the file relative to rootDir (forward slashes)
59
+ * rootDir: string,
60
+ * fileCount: number, // number of files (leaves) in the tree
61
+ * }}
62
+ */
63
+ function buildProof(opts) {
64
+ const { file, rootDir } = opts;
65
+ if (!file) throw new Error("prove requires a <file>");
66
+ if (!rootDir) {
67
+ throw new Error("no repo root: pass --root <dir> (the repository root to prove against)");
68
+ }
69
+
70
+ const rootStat = fs.statSync(rootDir);
71
+ if (!rootStat.isDirectory()) {
72
+ throw new Error(`--root must be a directory (the repo root), got: ${rootDir}`);
73
+ }
74
+
75
+ // Resolve the target file to an absolute path and confirm it is a regular file that actually
76
+ // sits *inside* the repo root. Proving a file that isn't in the tree is meaningless, and we want
77
+ // a clear error rather than a confusing "not found in tree" later.
78
+ const absRoot = path.resolve(rootDir);
79
+ const absFile = path.resolve(path.isAbsolute(file) ? file : path.join(absRoot, file));
80
+ const fileStat = fs.statSync(absFile);
81
+ if (!fileStat.isFile()) {
82
+ throw new Error(`not a regular file: ${absFile}`);
83
+ }
84
+ const rel = path.relative(absRoot, absFile);
85
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
86
+ throw new Error(`file is not inside the repo root: ${absFile} (root: ${absRoot})`);
87
+ }
88
+
89
+ const { root, leaves, proofFor, leafFor } = hashDir(absRoot);
90
+ // The leaf the contract verifies is the PATH-BOUND digest, not the bare content hash. Look it up
91
+ // (and the proof) by the file's relative path so duplicate-content files still resolve to *this*
92
+ // file's position in the tree. proofFor/leafFor accept a rel path, an absolute path, or a leaf.
93
+ const leaf = leafFor(rel);
94
+ const proof = proofFor(rel);
95
+ // The bare content digest is the matching entry's contentHash; surface it for transparency, and
96
+ // report the file's normalized (forward-slash) relative path.
97
+ const entry = leaves.find((l) => l.leaf === leaf);
98
+
99
+ return {
100
+ root,
101
+ leaf,
102
+ contentHash: entry ? entry.contentHash : null,
103
+ proof,
104
+ file: entry ? entry.path : rel,
105
+ rootDir: absRoot,
106
+ fileCount: leaves.length,
107
+ };
108
+ }
109
+
110
+ /** Render a built proof as the multi-line block `--dry-run` (and the runner) prints. */
111
+ function formatProof(p, extra) {
112
+ const lines = [
113
+ ` repo root dir: ${p.rootDir} (${p.fileCount} files)`,
114
+ ` file: ${p.file}`,
115
+ ` merkle root: ${p.root}`,
116
+ ` content hash: ${p.contentHash}`,
117
+ ` leaf (path-bound): ${p.leaf}`,
118
+ ` proof (${p.proof.length} sibling${p.proof.length === 1 ? "" : "s"}):`,
119
+ ];
120
+ if (p.proof.length === 0) {
121
+ lines.push(" (none — single-file tree: leaf == root)");
122
+ } else {
123
+ for (const h of p.proof) lines.push(` ${h}`);
124
+ }
125
+ if (extra && extra.length) {
126
+ lines.push("");
127
+ for (const l of extra) lines.push(l);
128
+ }
129
+ return lines.join("\n");
130
+ }
131
+
132
+ /**
133
+ * Run the prove command end to end.
134
+ *
135
+ * In `--dry-run` mode it only builds + prints the proof (no key, no network) and returns
136
+ * `{ dryRun: true, ...proof }`.
137
+ *
138
+ * Otherwise it talks to the deployed ContributionRegistry:
139
+ * - If `anchorFirst` is set, it submits the root via anchor() with `signer` (the only path that
140
+ * needs a key), so a brand-new repo can be anchored and proven in one command.
141
+ * - It confirms the root is anchored on-chain (via isAnchored) — there is nothing to prove a
142
+ * file against if the root was never anchored.
143
+ * - It calls `verifyLeaf(root, leaf, proof)` on-chain and reports the contract's verdict.
144
+ *
145
+ * @param {object} opts
146
+ * @param {string} opts.file
147
+ * @param {string} opts.rootDir
148
+ * @param {string} opts.contractAddress
149
+ * @param {object} opts.provider ethers v6 Provider (read-only; required)
150
+ * @param {boolean}[opts.dryRun]
151
+ * @param {boolean}[opts.anchorFirst] anchor the root before proving (needs a signer)
152
+ * @param {object} [opts.signer] ethers Signer (required iff anchorFirst)
153
+ * @param {boolean}[opts.iUnderstandMainnet] forwarded to anchor()'s chainId guard
154
+ * @param {string} [opts.out] write a portable, self-contained proof artifact here
155
+ * (caller-chosen path — never silently the cwd). Works on
156
+ * the no-key `--dry-run`/build path; `vh verify-proof <p>`
157
+ * independently verifies it offline + on-chain.
158
+ * @param {object} [opts.ethers] ethers v6 module
159
+ * @param {(s:string)=>void}[opts.log] sink for human output (defaults to process.stdout)
160
+ * @returns {Promise<object>} result describing what happened
161
+ */
162
+ async function runProve(opts) {
163
+ const ethersLib = opts.ethers || require("ethers");
164
+ const log = opts.log || ((s) => process.stdout.write(s));
165
+
166
+ const built = buildProof({ file: opts.file, rootDir: opts.rootDir });
167
+
168
+ // The contract rejects a zero hash, and a zero leaf/root means an empty or degenerate tree.
169
+ if (/^0x0{64}$/i.test(built.root)) {
170
+ throw new Error("refusing to prove against the zero root (contract rejects it)");
171
+ }
172
+
173
+ if (opts.dryRun) {
174
+ // The build path needs no key and no network. If asked, write the portable proof artifact here so
175
+ // a fresh repo's proof can be exported and handed to a third party who only has an RPC URL.
176
+ let artifactPath = null;
177
+ if (opts.out) {
178
+ artifactPath = _writeArtifact(built, opts);
179
+ log(`Wrote portable proof artifact: ${artifactPath}\n`);
180
+ }
181
+ log(formatProof(built) + "\n");
182
+ return { dryRun: true, ...built, out: artifactPath };
183
+ }
184
+
185
+ const { contractAddress, provider } = opts;
186
+ if (!contractAddress) {
187
+ throw new Error(
188
+ "no contract address: pass --contract <address> or set VH_CONTRACT in the environment"
189
+ );
190
+ }
191
+ if (!ethersLib.isAddress(contractAddress)) {
192
+ throw new Error(`invalid contract address: ${contractAddress}`);
193
+ }
194
+ if (!provider) {
195
+ throw new Error("no provider: pass --rpc <url> or set VH_RPC_URL / AMOY_RPC_URL");
196
+ }
197
+
198
+ const address = ethersLib.getAddress(contractAddress);
199
+ const readContract = new ethersLib.Contract(address, ABI, provider);
200
+
201
+ // Optionally anchor the root first so a fresh repo can be proven in one shot.
202
+ let anchored = null;
203
+ if (opts.anchorFirst) {
204
+ if (!opts.signer) {
205
+ throw new Error("--anchor needs a signer (set PRIVATE_KEY) to submit the root");
206
+ }
207
+ // Reuse the audited anchor flow (chainId guard, event parsing) instead of re-implementing it.
208
+ const { runAnchor } = require("./anchor");
209
+ const anchorRes = await runAnchor({
210
+ path: built.rootDir,
211
+ contractAddress: address,
212
+ provider,
213
+ signer: opts.signer,
214
+ iUnderstandMainnet: opts.iUnderstandMainnet,
215
+ ethers: ethersLib,
216
+ log: () => {}, // keep prove's own output clean; we summarize below
217
+ });
218
+ anchored = anchorRes.anchored;
219
+ log(`Anchored repo root ${built.root} (${built.fileCount} files).\n`);
220
+ }
221
+
222
+ // There is nothing to prove a file against if the root was never anchored.
223
+ const rootIsAnchored = await readContract.isAnchored(built.root);
224
+ if (!rootIsAnchored) {
225
+ throw new Error(
226
+ `repo root ${built.root} is not anchored on-chain; ` +
227
+ "anchor it first (`vh anchor <repoDir>` or pass --anchor) before proving a file against it."
228
+ );
229
+ }
230
+
231
+ // The acceptance criterion: the *on-chain* verifyLeaf must accept a genuine file's proof.
232
+ const accepted = await readContract.verifyLeaf(built.root, built.leaf, built.proof);
233
+
234
+ // If asked, export the portable proof artifact here too — recording the resolved contract address
235
+ // and chainId so the artifact is fully self-describing for `vh verify-proof`.
236
+ let artifactPath = null;
237
+ if (opts.out) {
238
+ let chainId;
239
+ try {
240
+ const net = await provider.getNetwork();
241
+ chainId = net.chainId;
242
+ } catch (_) {
243
+ chainId = undefined; // chainId is an optional hint; never fail the prove over it
244
+ }
245
+ artifactPath = _writeArtifact(built, opts, { contractAddress: address, chainId });
246
+ }
247
+
248
+ const extra = [
249
+ ` root anchored: yes`,
250
+ ` verifyLeaf: ${accepted ? "ACCEPTED" : "REJECTED"}`,
251
+ ];
252
+ if (artifactPath) {
253
+ extra.push(` proof artifact: ${artifactPath} (verify with \`vh verify-proof\`)`);
254
+ }
255
+ if (!accepted) {
256
+ extra.push(
257
+ " The on-chain verifyLeaf rejected this proof: the file does not match the anchored",
258
+ " repo root (it was modified/tampered, or it was not part of the anchored snapshot)."
259
+ );
260
+ }
261
+ log(formatProof(built, extra) + "\n");
262
+
263
+ return {
264
+ dryRun: false,
265
+ ...built,
266
+ contractAddress: address,
267
+ rootIsAnchored: true,
268
+ accepted,
269
+ anchored,
270
+ out: artifactPath,
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Build and write the portable proof artifact for `built` to `opts.out`. Validates the destination
276
+ * path is a non-empty string (an `--out` with no/empty value hard-errors, parser parity with the
277
+ * other commands) and resolves it to an absolute path so the caller always learns the exact file
278
+ * written — never silently dropping it into an ambiguous cwd.
279
+ *
280
+ * @param {object} built a buildProof() result
281
+ * @param {object} opts the runProve opts (carries `out`)
282
+ * @param {object} [ctx] optional on-chain context { contractAddress, chainId } to record
283
+ * @returns {string} the absolute path written
284
+ */
285
+ function _writeArtifact(built, opts, ctx = {}) {
286
+ if (typeof opts.out !== "string" || opts.out.trim() === "") {
287
+ throw new Error("--out requires a destination file path");
288
+ }
289
+ const outPath = path.resolve(opts.out);
290
+ const artifact = buildProofArtifact(built, ctx);
291
+ writeProofArtifact(artifact, outPath);
292
+ return outPath;
293
+ }
294
+
295
+ module.exports = {
296
+ buildProof,
297
+ runProve,
298
+ formatProof,
299
+ ABI,
300
+ };