enigma-memory 0.1.18 → 0.1.22

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 (276) hide show
  1. package/README.md +76 -24
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +1923 -285
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/docker-compose.local-production-simulation.yml +36 -0
  74. package/docs/browser-extension-install.md +8 -6
  75. package/docs/client-connectors.md +15 -11
  76. package/docs/developer-ecosystem.md +15 -13
  77. package/docs/enigma-memory-ready-conformance.md +11 -9
  78. package/docs/install-anywhere.md +61 -28
  79. package/docs/installers-and-desktop.md +8 -7
  80. package/docs/novelty-invention-candidates.md +161 -161
  81. package/docs/proof-network-claim-boundaries.md +320 -318
  82. package/examples/01-quickstart-agent/index.mjs +49 -0
  83. package/examples/01_agent_memory_quickstart.mjs +57 -0
  84. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  85. package/examples/02_cross_model_passport.mjs +64 -0
  86. package/examples/03-langchain-memory/index.mjs +41 -0
  87. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  88. package/examples/04-python-trading-agent/trader.py +49 -0
  89. package/examples/README.md +27 -0
  90. package/examples/ci/github-actions.yml +7 -2
  91. package/package.json +142 -11
  92. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  93. package/packages/connectors/src/index.js +196 -4
  94. package/packages/connectors/swarm-router.mjs +168 -0
  95. package/packages/core/src/index.js +248 -1
  96. package/packages/core/src/version.mjs +7 -0
  97. package/packages/dev-tools/package.json +19 -0
  98. package/packages/dev-tools/src/index.js +4 -0
  99. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  100. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  101. package/packages/dev-tools/src/vault-inspector.js +114 -0
  102. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  103. package/packages/developer-platform/src/access-credentials.js +341 -0
  104. package/packages/developer-platform/src/http.js +132 -0
  105. package/packages/developer-platform/src/index.js +4 -0
  106. package/packages/developer-platform/src/usage-http.js +60 -0
  107. package/packages/developer-platform/src/usage.js +295 -0
  108. package/packages/enclave-runtime/attestation.mjs +159 -0
  109. package/packages/enclave-runtime/index.mjs +47 -0
  110. package/packages/enclave-runtime/session-manager.mjs +253 -0
  111. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  112. package/packages/enigma-reflex/package.json +14 -0
  113. package/packages/enigma-reflex/src/index.js +204 -0
  114. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  115. package/packages/enigma-reflex/training/requirements.txt +8 -0
  116. package/packages/enigma-reflex/training/train.py +314 -0
  117. package/packages/enigma-weave/LICENSE +22 -0
  118. package/packages/enigma-weave/UPSTREAM.json +21 -0
  119. package/packages/enigma-weave/package.json +14 -0
  120. package/packages/enigma-weave/src/index.js +286 -0
  121. package/packages/hosted-cloud/src/index.js +80 -5
  122. package/packages/importers/src/index.js +432 -0
  123. package/packages/inference-runtime/src/browser.js +401 -0
  124. package/packages/inference-runtime/src/chat.js +265 -0
  125. package/packages/inference-runtime/src/code.js +407 -0
  126. package/packages/inference-runtime/src/contracts.js +162 -0
  127. package/packages/inference-runtime/src/http.js +232 -0
  128. package/packages/inference-runtime/src/image.js +186 -0
  129. package/packages/inference-runtime/src/index.js +10 -0
  130. package/packages/inference-runtime/src/model-router.js +320 -0
  131. package/packages/inference-runtime/src/platform.js +125 -0
  132. package/packages/inference-runtime/src/privacy.js +400 -0
  133. package/packages/inference-runtime/src/video.js +253 -0
  134. package/packages/mcp-server/README.md +22 -6
  135. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  136. package/packages/mcp-server/src/index.js +1418 -105
  137. package/packages/mcp-server/src/oauth.js +561 -0
  138. package/packages/mcp-server/src/private-handoff.js +84 -0
  139. package/packages/mcp-server/src/remote-http.js +273 -0
  140. package/packages/mcp-server/src/remote-policy.js +72 -0
  141. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  142. package/packages/mesh/index.d.ts +283 -0
  143. package/packages/mesh/package.json +23 -0
  144. package/packages/mesh/src/crypto.js +189 -0
  145. package/packages/mesh/src/federation-packets.js +353 -0
  146. package/packages/mesh/src/gossip.js +311 -0
  147. package/packages/mesh/src/index.js +6 -0
  148. package/packages/mesh/src/protocol.js +255 -0
  149. package/packages/mesh/src/router.js +279 -0
  150. package/packages/mesh/src/transport.js +306 -0
  151. package/packages/passport/src/index.js +426 -1
  152. package/packages/private-economy/src/credits-http.js +100 -0
  153. package/packages/private-economy/src/credits.js +447 -0
  154. package/packages/private-economy/src/index.js +5 -0
  155. package/packages/private-economy/src/payments-http.js +120 -0
  156. package/packages/private-economy/src/payments.js +509 -0
  157. package/packages/private-economy/src/x402.js +346 -0
  158. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  159. package/packages/rag/index.d.ts +182 -0
  160. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  161. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  162. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  163. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  164. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  165. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  166. package/packages/rag/package.json +27 -0
  167. package/packages/rag/src/blinded-search.js +109 -0
  168. package/packages/rag/src/bm25.js +169 -0
  169. package/packages/rag/src/embeddings.js +459 -0
  170. package/packages/rag/src/hybrid.js +76 -0
  171. package/packages/rag/src/index.js +38 -0
  172. package/packages/rag/src/reranker.js +61 -0
  173. package/packages/rag/src/research.js +107 -0
  174. package/packages/rag/src/vector-store.js +430 -0
  175. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  176. package/packages/sdk/index.d.ts +760 -0
  177. package/packages/sdk/package.json +33 -0
  178. package/packages/sdk/python/README.md +24 -0
  179. package/packages/sdk/python/enigma_sdk.py +250 -0
  180. package/packages/sdk/python/pyproject.toml +34 -0
  181. package/packages/sdk/python/requirements.txt +1 -0
  182. package/packages/sdk/python/setup.py +20 -0
  183. package/packages/sdk/src/federation/capability-grant.js +389 -0
  184. package/packages/sdk/src/federation/federation-router.js +360 -0
  185. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  186. package/packages/sdk/src/federation/index.js +3 -0
  187. package/packages/sdk/src/index.js +1796 -0
  188. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  189. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  190. package/packages/sdk/src/intelligence/index.js +4 -0
  191. package/packages/sdk/src/intelligence/ontology.js +122 -0
  192. package/packages/sdk/src/intelligence/temporal.js +123 -0
  193. package/packages/sdk/src/market-client.js +142 -0
  194. package/packages/sdk/src/mesh-client.js +110 -0
  195. package/packages/sdk/src/middleware/index.js +3 -0
  196. package/packages/sdk/src/middleware/langchain.js +159 -0
  197. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  198. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  199. package/packages/sdk/src/rag-client.js +85 -0
  200. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  201. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  202. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  203. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  204. package/packages/snapcompact/index.d.ts +284 -0
  205. package/packages/snapcompact/package.json +25 -0
  206. package/packages/snapcompact/src/index.js +716 -0
  207. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  208. package/packages/terminal-console/animations.mjs +240 -0
  209. package/packages/terminal-console/auto-anchor.mjs +220 -0
  210. package/packages/terminal-console/banner.mjs +91 -0
  211. package/packages/terminal-console/commands.mjs +459 -0
  212. package/packages/terminal-console/delegation.mjs +152 -0
  213. package/packages/terminal-console/index.mjs +5 -0
  214. package/packages/terminal-console/outbox.mjs +143 -0
  215. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  216. package/packages/terminal-console/repl.mjs +136 -0
  217. package/packages/terminal-console/signer-store.mjs +130 -0
  218. package/packages/terminal-console/solana-rpc.mjs +214 -0
  219. package/packages/terminal-console/solana-transport.mjs +189 -0
  220. package/packages/terminal-tui/dashboard.mjs +214 -0
  221. package/packages/terminal-tui/index.mjs +28 -0
  222. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  223. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  224. package/packages/vault/index.d.ts +449 -0
  225. package/packages/vault/package.json +27 -0
  226. package/packages/vault/src/e2ee.mjs +393 -0
  227. package/packages/vault/src/enclave.js +481 -0
  228. package/packages/vault/src/erasure.js +207 -0
  229. package/packages/vault/src/index.js +1018 -155
  230. package/packages/vault/src/persistence.js +307 -0
  231. package/packages/vault/src/poseidon.js +354 -0
  232. package/packages/vault/src/receipt.js +459 -0
  233. package/scripts/benchmark-optical-context.mjs +166 -0
  234. package/scripts/bootstrap-enigma.mjs +502 -0
  235. package/scripts/build-edge-backend-workers.mjs +20 -5
  236. package/scripts/build-goal-completion-audit.mjs +72 -25
  237. package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
  238. package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
  239. package/scripts/build-hosted-probe-worker.mjs +19 -4
  240. package/scripts/build-installer-assets.mjs +41 -21
  241. package/scripts/build-operator-evidence-starter.mjs +59 -1
  242. package/scripts/build-production-backend-env-kit.mjs +2 -0
  243. package/scripts/build-production-unblocker.mjs +3 -0
  244. package/scripts/check.mjs +17 -3
  245. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  246. package/scripts/install-enigma-local.mjs +18 -5
  247. package/scripts/release-audit.mjs +65 -109
  248. package/scripts/release-provenance.mjs +6 -0
  249. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  250. package/scripts/scan-secrets.mjs +1 -0
  251. package/scripts/simulate-production-env.mjs +7 -2
  252. package/scripts/validate-hosted-backend-live.mjs +112 -1
  253. package/specs/antibody-pack-v1.schema.json +95 -0
  254. package/specs/antigen-envelope-v1.schema.json +81 -0
  255. package/specs/boundary-manifest-v1.schema.json +35 -35
  256. package/specs/capsule-v1.schema.json +55 -55
  257. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  258. package/specs/claim-ledger-v1.schema.json +291 -0
  259. package/specs/context-passport-v1.schema.json +59 -0
  260. package/specs/deletion-tombstone-v1.schema.json +26 -26
  261. package/specs/evidence-packet-v1.schema.json +177 -0
  262. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  263. package/specs/immune-scan-report-v1.schema.json +112 -0
  264. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  265. package/specs/memory-atom-v1.schema.json +59 -0
  266. package/specs/memory-event-v1.schema.json +42 -42
  267. package/specs/passport-v1.schema.json +50 -50
  268. package/specs/proof-of-non-use-v1.schema.json +65 -0
  269. package/specs/quarantine-record-v1.schema.json +126 -0
  270. package/specs/receipt-v1.schema.json +61 -61
  271. package/specs/state-checkpoint-v1.schema.json +37 -37
  272. package/specs/trust-bundle-v1.schema.json +56 -56
  273. package/specs/trust-card-v1.schema.json +119 -0
  274. package/docs/proof-network-launch-plan.md +0 -421
  275. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  276. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -0,0 +1,666 @@
1
+ /**
2
+ * setup-sale.mjs — idempotent Groth16 ceremony driver for the Enigma
3
+ * ZKCP "Sealed Sale" circuit (circuits/sealed_sale_release.circom,
4
+ * SealedSaleRelease: note membership + Poseidon-CTR encryption consistency +
5
+ * key commitment + recipient-bound BabyJubJub envelope + sale-terms binding).
6
+ *
7
+ * Binding spec (private-sale path — do not drift): public inputs in exact order
8
+ * [noteRoot, payloadCommitPos, cipherCommit, keyCommit, saleTermsHash, envelopeCommit]
9
+ * → IC length 7.
10
+ * cipherCommit was promoted to a PUBLIC input (fund-loss fix): the proof now
11
+ * binds the ciphertext fold on-chain, so the buyer asserts
12
+ * fold(delivered ciphertext) == cipherCommit BEFORE paying.
13
+ * saleTermsHash = Poseidon9(DOMAIN_SALE, sellerLo, sellerHi, buyerPubX,
14
+ * buyerPubY, mintLo, mintHi, price, expirySlot) — the buyer pubkey is bound
15
+ * into the terms (fund-loss fix: a receipt can only claim that buyer's escrow).
16
+ * The on-chain VK PDA (seeds [b"verifying_key", 3u64.to_be_bytes()]) holds
17
+ * exactly 7 IC slots — the exported sale_vk_bytes.json fills all 7 with real
18
+ * points (no zero-padding).
19
+ *
20
+ * Cross-agent constant: DOMAIN_SALE = ASCII "enigma_sale"
21
+ * (0x656e69676d615f73616c65) as decimal = 122622912455040968075340901
22
+ * (87-bit; the circuit binds this exact value in saleTermsHash).
23
+ *
24
+ * Pipeline:
25
+ * 1. Poll (default up to 30 min) for circuits/build/sealed_sale_release.r1cs
26
+ * (produced concurrently by SaleCircuit).
27
+ * 2. Read the constraint count from the r1cs binary header (authoritative).
28
+ * 3. REUSE the existing ptau/powersOfTau28_hez_final_18.ptau — NO download.
29
+ * ~25-30k constraints expected; 2^18 covers it with margin and is
30
+ * r1cs-independent.
31
+ * 4. groth16 setup → zkey contribute ("enigma-sale-dev-1", random entropy)
32
+ * → zkey beacon (random 32B, 10 iterations) → export verificationkey →
33
+ * circuits/build/sale_verification_key.json.
34
+ * 5. vkFromSnarkjs (asserts IC == 7) → circuits/build/sale_vk_bytes.json
35
+ * (raw-be-hex-v1; all 7 on-chain IC slots are real points).
36
+ * 6. Proof-of-life: poll for zk/sale-witness.mjs (SaleWitness runs
37
+ * concurrently), build the module's self-contained demo fixture
38
+ * (buildSaleDemoFixture), groth16 fullProve against the final zkey +
39
+ * compiled wasm, groth16 verify against the exported verification key.
40
+ * Prints timings.
41
+ *
42
+ * sale_verification_key.json + sale_vk_bytes.json are COMMITTABLE;
43
+ * *.zkey, zk-setup-sale-state.json, ptau/ are gitignored.
44
+ *
45
+ * CLAIM BOUNDARY: single-party local setup = development only; production
46
+ * requires a multi-party ceremony.
47
+ *
48
+ * Usage:
49
+ * node zk/setup-sale.mjs [--r1cs <path>] [--build-dir <dir>]
50
+ * [--ptau-dir <dir>] [--poll-minutes <n>] [--witness-poll-minutes <n>]
51
+ * [--witness <path>] [--force] [--skip-proof-of-life]
52
+ *
53
+ * Exit codes: 0 success · 3 blocked (r1cs or witness prerequisite missing) ·
54
+ * 4 ptau missing · 1 other failure.
55
+ */
56
+
57
+ import { spawnSync } from "node:child_process";
58
+ import { createHash, randomBytes } from "node:crypto";
59
+ import {
60
+ existsSync,
61
+ mkdirSync,
62
+ readFileSync,
63
+ renameSync,
64
+ rmSync,
65
+ statSync,
66
+ writeFileSync,
67
+ } from "node:fs";
68
+ import path from "node:path";
69
+ import { fileURLToPath, pathToFileURL } from "node:url";
70
+
71
+ import { vkFromSnarkjs } from "./zk-codec.mjs";
72
+
73
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
74
+ const CORTEX_V3 = path.resolve(HERE, "..");
75
+
76
+ // --- Sealed-sale circuit contract (binding spec — do not drift) --------------
77
+ const CIRCUIT_NAME = "sealed_sale_release";
78
+ const CIRCUIT_VERSION = 3; // settle_sale VK PDA seed (3u64.to_be_bytes())
79
+ // [noteRoot, payloadCommitPos, cipherCommit, keyCommit, saleTermsHash, envelopeCommit]
80
+ const NUM_PUBLIC_INPUTS = 6;
81
+ const EXPECTED_IC = 1 + NUM_PUBLIC_INPUTS; // 7
82
+ const ONCHAIN_VK_IC_SLOTS = 7; // VK account holds 7 slots — all real, no padding
83
+ const G1_BYTES = 64;
84
+ const POWER = 18; // reuse ptau/powersOfTau28_hez_final_18.ptau (2^18 >> ~2^15 need)
85
+ const PTAU_MIN_BYTES = 100 * 1024 * 1024; // size sanity floor (hez_final_18 ≈ 145MB)
86
+ // DOMAIN_SALE documented for cross-agent consistency (bound inside the circuit
87
+ // and the witness module — not needed by this driver itself).
88
+ export const DOMAIN_SALE = 122622912455040968075340901n; // ASCII "enigma_sale"
89
+
90
+ const DEFAULTS = {
91
+ r1cs: path.join(CORTEX_V3, "circuits", "build", `${CIRCUIT_NAME}.r1cs`),
92
+ buildDir: path.join(CORTEX_V3, "circuits", "build"),
93
+ ptauDir: path.join(CORTEX_V3, "ptau"),
94
+ witness: path.join(HERE, "sale-witness.mjs"),
95
+ pollMinutes: 30,
96
+ witnessPollMinutes: 20,
97
+ force: false,
98
+ skipProofOfLife: false,
99
+ };
100
+
101
+ const log = (msg) =>
102
+ console.log(`[setup-sale ${new Date().toISOString()}] ${msg}`);
103
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
104
+
105
+ class BlockingError extends Error {
106
+ constructor(message, exitCode) {
107
+ super(message);
108
+ this.exitCode = exitCode;
109
+ }
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // CLI
114
+ // ---------------------------------------------------------------------------
115
+
116
+ function parseArgs(argv) {
117
+ const opts = { ...DEFAULTS };
118
+ for (let i = 0; i < argv.length; i++) {
119
+ const a = argv[i];
120
+ const next = () => {
121
+ if (i + 1 >= argv.length) throw new Error(`missing value for ${a}`);
122
+ return argv[++i];
123
+ };
124
+ switch (a) {
125
+ case "--r1cs":
126
+ opts.r1cs = path.resolve(next());
127
+ break;
128
+ case "--build-dir":
129
+ opts.buildDir = path.resolve(next());
130
+ break;
131
+ case "--ptau-dir":
132
+ opts.ptauDir = path.resolve(next());
133
+ break;
134
+ case "--witness":
135
+ opts.witness = path.resolve(next());
136
+ break;
137
+ case "--poll-minutes":
138
+ opts.pollMinutes = Number(next());
139
+ break;
140
+ case "--witness-poll-minutes":
141
+ opts.witnessPollMinutes = Number(next());
142
+ break;
143
+ case "--force":
144
+ opts.force = true;
145
+ break;
146
+ case "--skip-proof-of-life":
147
+ opts.skipProofOfLife = true;
148
+ break;
149
+ default:
150
+ throw new Error(`unknown argument: ${a}`);
151
+ }
152
+ }
153
+ return opts;
154
+ }
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // snarkjs CLI runner (no npx/.cmd shims — run the CLI entrypoint with node)
158
+ // ---------------------------------------------------------------------------
159
+
160
+ function snarkjsCliPath() {
161
+ const p = path.join(CORTEX_V3, "node_modules", "snarkjs", "build", "cli.cjs");
162
+ if (!existsSync(p)) {
163
+ throw new Error(
164
+ `snarkjs CLI not found at ${p} — run "npm install -D snarkjs" in cortex-v3`,
165
+ );
166
+ }
167
+ return p;
168
+ }
169
+
170
+ function snarkjs(args, { quiet = false } = {}) {
171
+ const fullArgs = [snarkjsCliPath(), ...args];
172
+ log(`$ node ${path.basename(snarkjsCliPath())} ${args.join(" ")}`);
173
+ const res = spawnSync(process.execPath, fullArgs, {
174
+ cwd: CORTEX_V3,
175
+ stdio: quiet ? "pipe" : "inherit",
176
+ encoding: "utf8",
177
+ maxBuffer: 64 * 1024 * 1024,
178
+ });
179
+ if (res.error) throw res.error;
180
+ if (res.status !== 0) {
181
+ throw new Error(
182
+ `snarkjs ${args[0]} exited with code ${res.status}\n${res.stderr ?? ""}`,
183
+ );
184
+ }
185
+ return (res.stdout ?? "") + (res.stderr ?? "");
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // r1cs inspection — parse the binary header directly (authoritative)
190
+ // ---------------------------------------------------------------------------
191
+
192
+ function readR1csHeader(r1csPath) {
193
+ const fd = readFileSync(r1csPath);
194
+ if (fd.length < 12 || fd.toString("ascii", 0, 4) !== "r1cs") {
195
+ throw new Error(`${r1csPath} is not an r1cs file (bad magic)`);
196
+ }
197
+ const nSections = fd.readUInt32LE(8);
198
+ let offset = 12;
199
+ for (let s = 0; s < nSections; s++) {
200
+ const sectionType = fd.readUInt32LE(offset);
201
+ const sectionSize = Number(fd.readBigUInt64LE(offset + 4));
202
+ if (sectionType === 1) {
203
+ const base = offset + 12;
204
+ const fieldSize = fd.readUInt32LE(base);
205
+ let p = base + 4 + fieldSize;
206
+ const nWires = fd.readUInt32LE(p);
207
+ const nPubOut = fd.readUInt32LE(p + 4);
208
+ const nPubIn = fd.readUInt32LE(p + 8);
209
+ const nPrvIn = fd.readUInt32LE(p + 12);
210
+ const nLabels = Number(fd.readBigUInt64LE(p + 16));
211
+ const nConstraints = fd.readUInt32LE(p + 24);
212
+ return { nWires, nPubOut, nPubIn, nPrvIn, nLabels, nConstraints };
213
+ }
214
+ offset += 12 + sectionSize;
215
+ }
216
+ throw new Error(`${r1csPath}: header section (type 1) not found`);
217
+ }
218
+
219
+ // ---------------------------------------------------------------------------
220
+ // ptau header — sanity-check that the reused file's power covers the circuit
221
+ // ---------------------------------------------------------------------------
222
+
223
+ function readPtauPower(ptauPath) {
224
+ const fd = readFileSync(ptauPath, { flag: "r" });
225
+ if (fd.length < 12 || fd.toString("ascii", 0, 4) !== "ptau") {
226
+ throw new Error(`${ptauPath} is not a ptau file (bad magic)`);
227
+ }
228
+ const nSections = fd.readUInt32LE(8);
229
+ let offset = 12;
230
+ for (let s = 0; s < nSections; s++) {
231
+ const sectionType = fd.readUInt32LE(offset);
232
+ const sectionSize = Number(fd.readBigUInt64LE(offset + 4));
233
+ if (sectionType === 1) {
234
+ const base = offset + 12;
235
+ const fieldSize = fd.readUInt32LE(base);
236
+ const power = fd.readUInt32LE(base + 4 + fieldSize);
237
+ return power;
238
+ }
239
+ offset += 12 + sectionSize;
240
+ }
241
+ throw new Error(`${ptauPath}: header section (type 1) not found`);
242
+ }
243
+
244
+ // ---------------------------------------------------------------------------
245
+ // Stage 1: wait for the r1cs
246
+ // ---------------------------------------------------------------------------
247
+
248
+ async function waitForFile(filePath, pollMinutes, what, owner) {
249
+ if (existsSync(filePath)) {
250
+ log(`${what} present: ${filePath}`);
251
+ return;
252
+ }
253
+ log(
254
+ `waiting up to ${pollMinutes} min for ${filePath} (${owner} runs concurrently)...`,
255
+ );
256
+ const deadline = Date.now() + pollMinutes * 60_000;
257
+ while (Date.now() < deadline) {
258
+ if (existsSync(filePath)) {
259
+ // Guard against a partially-written file: size must be stable.
260
+ const s1 = statSync(filePath).size;
261
+ await sleep(2000);
262
+ const s2 = statSync(filePath).size;
263
+ if (s1 === s2 && s2 > 0) {
264
+ log(`${what} appeared (${s2} bytes, stable)`);
265
+ return;
266
+ }
267
+ }
268
+ await sleep(15_000);
269
+ }
270
+ throw new BlockingError(
271
+ `BLOCKED: ${filePath} did not appear within ${pollMinutes} minutes.\n` +
272
+ `Prerequisite owner: ${owner}.\n` +
273
+ `Re-run "node zk/setup-sale.mjs" once it exists — all completed stages are reused.`,
274
+ 3,
275
+ );
276
+ }
277
+
278
+ // ---------------------------------------------------------------------------
279
+ // Stage 2: ptau — REUSE the existing 2^18 powers-of-tau (no download)
280
+ // ---------------------------------------------------------------------------
281
+
282
+ function acquirePtau(ptauDir, nConstraints) {
283
+ const dest = path.join(ptauDir, `powersOfTau28_hez_final_${POWER}.ptau`);
284
+ if (!existsSync(dest)) {
285
+ throw new BlockingError(
286
+ `BLOCKED: required powers-of-tau not found at ${dest}.\n` +
287
+ `This ceremony reuses the repo-local ptau (no download). Restore it via\n` +
288
+ `"node zk/setup.mjs" (which acquires the same file) or copy it into ptau/.`,
289
+ 4,
290
+ );
291
+ }
292
+ const size = statSync(dest).size;
293
+ if (size < PTAU_MIN_BYTES) {
294
+ throw new BlockingError(
295
+ `BLOCKED: ${dest} fails size sanity (${size} < ${PTAU_MIN_BYTES}) — truncated?`,
296
+ 4,
297
+ );
298
+ }
299
+ const power = readPtauPower(dest);
300
+ if (2 ** power <= nConstraints) {
301
+ throw new Error(
302
+ `constraint count ${nConstraints} does not fit in the 2^${power} powers-of-tau at ${dest}`,
303
+ );
304
+ }
305
+ log(
306
+ `reusing existing ptau: ${dest} (${(size / 1024 / 1024).toFixed(1)}MB, ` +
307
+ `2^${power}; ${nConstraints} constraints fit)`,
308
+ );
309
+ return { path: dest, provenance: "reused repo-local file (no download)", size, power };
310
+ }
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Stages 3-5: circuit-specific ceremony
314
+ // ---------------------------------------------------------------------------
315
+
316
+ function stagePaths(buildDir, r1csPath) {
317
+ const stem = path.basename(r1csPath).replace(/\.r1cs$/, "");
318
+ return {
319
+ zkey0: path.join(buildDir, `${stem}_0000.zkey`),
320
+ zkey1: path.join(buildDir, `${stem}_0001.zkey`),
321
+ // Final proving key name is CONTRACT: proof-of-life + Main's e2e load <stem>.zkey.
322
+ zkeyFinal: path.join(buildDir, `${stem}.zkey`),
323
+ vkJson: path.join(buildDir, "sale_verification_key.json"),
324
+ vkBytes: path.join(buildDir, "sale_vk_bytes.json"),
325
+ };
326
+ }
327
+
328
+ function runCeremony(r1csPath, ptauPath, paths, force) {
329
+ mkdirSync(path.dirname(paths.zkey0), { recursive: true });
330
+ if (force) {
331
+ for (const f of Object.values(paths)) rmSync(f, { force: true });
332
+ }
333
+ if (!existsSync(paths.zkey0)) {
334
+ snarkjs(["groth16", "setup", r1csPath, ptauPath, paths.zkey0]);
335
+ } else {
336
+ log(`reusing ${paths.zkey0}`);
337
+ }
338
+ if (!existsSync(paths.zkey1)) {
339
+ snarkjs([
340
+ "zkey", "contribute", paths.zkey0, paths.zkey1,
341
+ "--name=enigma-sale-dev-1",
342
+ `-e=${randomBytes(32).toString("hex")}`,
343
+ ]);
344
+ } else {
345
+ log(`reusing ${paths.zkey1}`);
346
+ }
347
+ if (!existsSync(paths.zkeyFinal)) {
348
+ const beacon = randomBytes(32).toString("hex");
349
+ snarkjs(["zkey", "beacon", paths.zkey1, paths.zkeyFinal, beacon, "10"]);
350
+ log(`zkey beacon applied (dev beacon ${beacon})`);
351
+ } else {
352
+ log(`reusing ${paths.zkeyFinal}`);
353
+ }
354
+ if (!existsSync(paths.vkJson)) {
355
+ snarkjs(["zkey", "export", "verificationkey", paths.zkeyFinal, paths.vkJson]);
356
+ } else {
357
+ log(`reusing ${paths.vkJson}`);
358
+ }
359
+ }
360
+
361
+ function exportVkBytes(paths, meta) {
362
+ const vkJson = JSON.parse(readFileSync(paths.vkJson, "utf8"));
363
+ // Throws unless IC == 7 (ic[0] + 6 public inputs).
364
+ const vk = vkFromSnarkjs(vkJson, { expectedIc: EXPECTED_IC, g2LimbOrder: "c1c0" });
365
+ const hex = (b) => b.toString("hex"); // plain hex strings (zk-codec convention)
366
+ // The on-chain VK PDA (circuit_version = 3) holds 7 IC slots; the sale
367
+ // circuit has exactly 7 — the pad loop below is a no-op kept for shape safety.
368
+ const icPadded = vk.ic.map(hex);
369
+ while (icPadded.length < ONCHAIN_VK_IC_SLOTS) {
370
+ icPadded.push(Buffer.alloc(G1_BYTES).toString("hex"));
371
+ }
372
+ if (icPadded.length !== ONCHAIN_VK_IC_SLOTS) {
373
+ throw new Error(`internal: ic padding overflow (${icPadded.length})`);
374
+ }
375
+ const out = {
376
+ format: "raw-be-hex-v1",
377
+ curve: "bn128",
378
+ g2LimbOrder: "c1c0", // alt_bn128 / EIP-196 (im,re)
379
+ proofNote: "pi_a is raw — the on-chain program negates A",
380
+ circuit: CIRCUIT_NAME,
381
+ circuitVersion: CIRCUIT_VERSION,
382
+ numPublicInputs: NUM_PUBLIC_INPUTS,
383
+ icCount: vk.ic.length, // 7 real IC points from the ceremony
384
+ icPaddedTo: ONCHAIN_VK_IC_SLOTS, // 7 — no zero-padding needed
385
+ alphaG1: hex(vk.alphaG1),
386
+ betaG2: hex(vk.betaG2),
387
+ gammaG2: hex(vk.gammaG2),
388
+ deltaG2: hex(vk.deltaG2),
389
+ ic: icPadded,
390
+ ceremony: "development single-party",
391
+ ceremonyDetail: meta,
392
+ source: path.basename(paths.vkJson),
393
+ createdAt: new Date().toISOString(),
394
+ };
395
+ const tmp = `${paths.vkBytes}.tmp`;
396
+ writeFileSync(tmp, JSON.stringify(out, null, 2) + "\n");
397
+ renameSync(tmp, paths.vkBytes);
398
+ return out;
399
+ }
400
+
401
+ // ---------------------------------------------------------------------------
402
+ // r1cs fingerprint — ceremony artifacts are only valid for the exact r1cs
403
+ // they were derived from. If the r1cs changes (circuit recompile), every
404
+ // circuit-specific stage MUST be redone; the ptau is r1cs-independent.
405
+ // ---------------------------------------------------------------------------
406
+
407
+ function sha256File(p) {
408
+ return createHash("sha256").update(readFileSync(p)).digest("hex");
409
+ }
410
+
411
+ function statePath(buildDir) {
412
+ // Distinct from setup.mjs's zk-setup-state.json (action-transition) and
413
+ // setup-intent.mjs's zk-setup-intent-state.json.
414
+ return path.join(buildDir, "zk-setup-sale-state.json");
415
+ }
416
+
417
+ function readState(buildDir) {
418
+ const p = statePath(buildDir);
419
+ if (!existsSync(p)) return null;
420
+ try {
421
+ return JSON.parse(readFileSync(p, "utf8"));
422
+ } catch {
423
+ return null;
424
+ }
425
+ }
426
+
427
+ function writeState(buildDir, state) {
428
+ const tmp = `${statePath(buildDir)}.tmp`;
429
+ writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
430
+ renameSync(tmp, statePath(buildDir));
431
+ }
432
+
433
+ /** Delete circuit-specific artifacts; returns list of removed paths. */
434
+ function purgeStageArtifacts(paths) {
435
+ const removed = [];
436
+ for (const key of ["zkey0", "zkey1", "zkeyFinal", "vkJson", "vkBytes"]) {
437
+ if (existsSync(paths[key])) {
438
+ rmSync(paths[key], { force: true });
439
+ removed.push(paths[key]);
440
+ }
441
+ }
442
+ return removed;
443
+ }
444
+
445
+ // ---------------------------------------------------------------------------
446
+ // Stage 6: proof-of-life — fullProve + verify with SaleWitness's demo fixture
447
+ // ---------------------------------------------------------------------------
448
+
449
+ // The demo fixture is owned by zk/sale-witness.mjs (SaleWitness), which knows
450
+ // the note tree, Poseidon-CTR plaintext/ciphertext chunks, the BabyJubJub
451
+ // buyer keypair and the ephemeral envelope — building all of that here would
452
+ // duplicate the module under test. Contract:
453
+ // buildSaleDemoFixture() → { input, expectedPublic }
454
+ // input — complete circom witness input (private + public signals)
455
+ // expectedPublic — the 5 public values in circuit order
456
+ // SALE_PUBLIC_SIGNALS — the 5 public signal names in the same order
457
+ // fullProve/verify below is self-checking against both.
458
+
459
+ async function proofOfLife(opts, paths) {
460
+ await waitForFile(
461
+ opts.witness,
462
+ opts.witnessPollMinutes,
463
+ "sale witness module",
464
+ "SaleWitness (zk/sale-witness.mjs)",
465
+ );
466
+ const wasmPath = path.join(
467
+ opts.buildDir,
468
+ `${CIRCUIT_NAME}_js`,
469
+ `${CIRCUIT_NAME}.wasm`,
470
+ );
471
+ if (!existsSync(wasmPath)) {
472
+ throw new BlockingError(
473
+ `BLOCKED: ${wasmPath} missing (SaleCircuit compile output).`,
474
+ 3,
475
+ );
476
+ }
477
+
478
+ // snarkjs ESM entry has named exports only (no default).
479
+ const { groth16 } = await import("snarkjs");
480
+ const witnessMod = await import(pathToFileURL(opts.witness).href);
481
+ const { buildSaleDemoFixture, SALE_PUBLIC_SIGNALS } = witnessMod;
482
+ if (typeof buildSaleDemoFixture !== "function") {
483
+ throw new Error(`${opts.witness} does not export buildSaleDemoFixture`);
484
+ }
485
+ const publicSignalNames = SALE_PUBLIC_SIGNALS ?? [
486
+ "noteRoot",
487
+ "payloadCommitPos",
488
+ "cipherCommit",
489
+ "keyCommit",
490
+ "saleTermsHash",
491
+ "envelopeCommit",
492
+ ];
493
+
494
+ const fixture = await buildSaleDemoFixture();
495
+ const { input, expectedPublic } = fixture ?? {};
496
+ if (!input || typeof input !== "object") {
497
+ throw new Error("buildSaleDemoFixture() did not return { input, ... }");
498
+ }
499
+
500
+ const t0 = Date.now();
501
+ const { proof, publicSignals } = await groth16.fullProve(
502
+ input,
503
+ wasmPath,
504
+ paths.zkeyFinal,
505
+ );
506
+ const proveMs = Date.now() - t0;
507
+
508
+ // The builder's declared public signals MUST match what the prover emits, in
509
+ // the binding order [noteRoot, payloadCommitPos, cipherCommit, keyCommit,
510
+ // saleTermsHash, envelopeCommit].
511
+ if (publicSignals.length !== NUM_PUBLIC_INPUTS) {
512
+ throw new Error(
513
+ `fullProve emitted ${publicSignals.length} public signals, expected ${NUM_PUBLIC_INPUTS}`,
514
+ );
515
+ }
516
+ for (let i = 0; i < NUM_PUBLIC_INPUTS; i++) {
517
+ const declared = String(input[publicSignalNames[i]]);
518
+ if (declared !== String(publicSignals[i])) {
519
+ throw new Error(
520
+ `public signal ${publicSignalNames[i]} mismatch: witness ${declared} != prover ${publicSignals[i]}`,
521
+ );
522
+ }
523
+ if (expectedPublic && String(expectedPublic[i]) !== String(publicSignals[i])) {
524
+ throw new Error(
525
+ `public signal ${publicSignalNames[i]} mismatch: fixture expectedPublic ${expectedPublic[i]} != prover ${publicSignals[i]}`,
526
+ );
527
+ }
528
+ }
529
+
530
+ const t1 = Date.now();
531
+ const vkJsonObj = JSON.parse(readFileSync(paths.vkJson, "utf8"));
532
+ const ok = await groth16.verify(vkJsonObj, publicSignals, proof);
533
+ const verifyMs = Date.now() - t1;
534
+ if (!ok) {
535
+ throw new Error("PROOF-OF-LIFE FAILED: groth16.verify returned false");
536
+ }
537
+ log(
538
+ `proof-of-life OK — fullProve ${proveMs}ms, verify ${verifyMs}ms ` +
539
+ `(public signals [${publicSignalNames.join(", ")}] match witness)`,
540
+ );
541
+ return { proveMs, verifyMs, publicSignals };
542
+ }
543
+
544
+ // ---------------------------------------------------------------------------
545
+ // main
546
+ // ---------------------------------------------------------------------------
547
+
548
+ async function main() {
549
+ const opts = parseArgs(process.argv.slice(2));
550
+ const paths = stagePaths(opts.buildDir, opts.r1cs);
551
+
552
+ await waitForFile(
553
+ opts.r1cs,
554
+ opts.pollMinutes,
555
+ "r1cs",
556
+ "SaleCircuit (circuits/sealed_sale_release.circom → circom compile)",
557
+ );
558
+
559
+ const r1csSha256 = sha256File(opts.r1cs);
560
+ log(`r1cs sha256: ${r1csSha256}`);
561
+ const priorState = readState(opts.buildDir);
562
+
563
+ if (opts.force || priorState?.r1csSha256 !== r1csSha256) {
564
+ const reason = opts.force
565
+ ? "--force"
566
+ : priorState
567
+ ? `r1cs changed (${priorState.r1csSha256} → ${r1csSha256})`
568
+ : "no prior setup state";
569
+ const removed = purgeStageArtifacts(paths);
570
+ if (removed.length > 0) {
571
+ log(
572
+ `${reason}: purged ${removed.length} stale circuit-specific artifact(s); ` +
573
+ `ptau reused (r1cs-independent)`,
574
+ );
575
+ }
576
+ }
577
+
578
+ const header = readR1csHeader(opts.r1cs);
579
+ log(
580
+ `r1cs: ${header.nConstraints} constraints, ${header.nWires} wires, ` +
581
+ `${header.nPubIn} pub-in, ${header.nPubOut} pub-out`,
582
+ );
583
+ const impliedIc = 1 + header.nPubIn + header.nPubOut;
584
+ if (impliedIc !== EXPECTED_IC) {
585
+ log(
586
+ `WARNING: r1cs implies IC length ${impliedIc} (1 + ${header.nPubIn} + ${header.nPubOut}) ` +
587
+ `but the sealed-sale contract expects ${EXPECTED_IC}; vk export will fail if the vk disagrees`,
588
+ );
589
+ }
590
+
591
+ const ptau = acquirePtau(opts.ptauDir, header.nConstraints);
592
+ log(`ptau provenance: ${ptau.provenance} (${(ptau.size / 1024 / 1024).toFixed(1)}MB)`);
593
+
594
+ const freshArtifacts =
595
+ existsSync(paths.vkJson) &&
596
+ existsSync(paths.vkBytes) &&
597
+ existsSync(paths.zkeyFinal);
598
+ if (!freshArtifacts) {
599
+ runCeremony(opts.r1cs, ptau.path, paths, opts.force);
600
+ exportVkBytes(paths, {
601
+ ptau: ptau.provenance,
602
+ ptauBytes: ptau.size,
603
+ ptauPower: ptau.power,
604
+ r1csSha256,
605
+ constraintCount: header.nConstraints,
606
+ contribution:
607
+ "enigma-sale-dev-1 (random entropy) + random beacon (10 iterations)",
608
+ });
609
+ } else {
610
+ log(
611
+ `ceremony artifacts already complete for r1cs ${r1csSha256} — reusing ` +
612
+ `(zkey + sale_verification_key.json + sale_vk_bytes.json)`,
613
+ );
614
+ }
615
+
616
+ // Persist the ceremony fingerprint NOW, so a proof-of-life failure does not
617
+ // force a ceremony redo on the next run (artifacts are already r1cs-bound).
618
+ writeState(opts.buildDir, {
619
+ r1csSha256,
620
+ constraintCount: header.nConstraints,
621
+ powerOfTau: ptau.power,
622
+ ptau: ptau.provenance,
623
+ zkeyFinal: path.basename(paths.zkeyFinal),
624
+ icCount: EXPECTED_IC,
625
+ icPaddedTo: ONCHAIN_VK_IC_SLOTS,
626
+ completedAt: new Date().toISOString(),
627
+ });
628
+
629
+ if (opts.skipProofOfLife) {
630
+ log("--skip-proof-of-life: ceremony complete, skipping fullProve/verify.");
631
+ } else {
632
+ const pol = await proofOfLife(opts, paths);
633
+ writeState(opts.buildDir, {
634
+ r1csSha256,
635
+ constraintCount: header.nConstraints,
636
+ powerOfTau: ptau.power,
637
+ ptau: ptau.provenance,
638
+ zkeyFinal: path.basename(paths.zkeyFinal),
639
+ icCount: EXPECTED_IC,
640
+ icPaddedTo: ONCHAIN_VK_IC_SLOTS,
641
+ proofOfLife: {
642
+ ok: true,
643
+ proveMs: pol.proveMs,
644
+ verifyMs: pol.verifyMs,
645
+ at: new Date().toISOString(),
646
+ },
647
+ completedAt: new Date().toISOString(),
648
+ });
649
+ }
650
+ log("setup-sale complete.");
651
+ }
652
+
653
+ // snarkjs's in-process JS API (proof-of-life) keeps worker threads alive;
654
+ // exit explicitly once the driver has finished.
655
+ main()
656
+ .then(() => process.exit(0))
657
+ .catch((err) => {
658
+ if (err instanceof BlockingError) {
659
+ console.error(`\n${err.message}`);
660
+ process.exit(err.exitCode);
661
+ }
662
+ console.error(
663
+ `\nsetup-sale.mjs FAILED: ${err.stack ?? err.message ?? err}`,
664
+ );
665
+ process.exit(1);
666
+ });