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.
- package/README.md +76 -24
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +1923 -285
- package/apps/desktop/electron-main.cjs +217 -0
- package/apps/desktop/package.json +12 -0
- package/apps/desktop/src/app.js +264 -7
- package/apps/desktop/src/index.html +3514 -1373
- package/apps/desktop/src/launch-electron.mjs +51 -0
- package/apps/desktop/src/server.mjs +2914 -0
- package/apps/desktop/src/styles.css +2972 -260
- package/apps/desktop/src/zk-browser-prove.mjs +53 -0
- package/apps/desktop/src/zk-state.mjs +1789 -0
- package/apps/gateway/bin/enigma-gateway.mjs +102 -5
- package/apps/gateway/src/server.mjs +271 -8
- package/apps/ios/EnigmaCore/Package.swift +12 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
- package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
- package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
- package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
- package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
- package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
- package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
- package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
- package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
- package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
- package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
- package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
- package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
- package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
- package/apps/native-host/README.md +19 -8
- package/apps/native-host/bin/enigma-native-host.mjs +229 -13
- package/apps/relay/bin/enigma-relay.mjs +103 -5
- package/apps/relay/src/federation-runtime.mjs +618 -0
- package/apps/relay/src/server.mjs +310 -9
- package/apps/verifier/bin/enigma-verify.mjs +327 -11
- package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/vk_bytes.json +32 -0
- package/cortex-v3/proving-assets.json +64 -0
- package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
- package/cortex-v3/zk/action-transition-vk.json +119 -0
- package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
- package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
- package/cortex-v3/zk/intent-witness.mjs +365 -0
- package/cortex-v3/zk/intent-witness.test.mjs +485 -0
- package/cortex-v3/zk/proving-assets.mjs +203 -0
- package/cortex-v3/zk/sale-witness.mjs +783 -0
- package/cortex-v3/zk/sale-witness.test.mjs +784 -0
- package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
- package/cortex-v3/zk/settlement-evidence.mjs +722 -0
- package/cortex-v3/zk/setup-intent.mjs +688 -0
- package/cortex-v3/zk/setup-sale.mjs +666 -0
- package/cortex-v3/zk/setup.mjs +594 -0
- package/cortex-v3/zk/witness.mjs +184 -0
- package/cortex-v3/zk/zk-codec.mjs +232 -0
- package/cortex-v3/zk/zk-codec.test.mjs +293 -0
- package/cortex-v3/zk/zk-settle.mjs +370 -0
- package/cortex-v3/zk/zk-tree.mjs +256 -0
- package/cortex-v3/zk/zk-tree.test.mjs +419 -0
- package/deploy/docker-compose.local-production-simulation.yml +36 -0
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +15 -11
- package/docs/developer-ecosystem.md +15 -13
- package/docs/enigma-memory-ready-conformance.md +11 -9
- package/docs/install-anywhere.md +61 -28
- package/docs/installers-and-desktop.md +8 -7
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/examples/01-quickstart-agent/index.mjs +49 -0
- package/examples/01_agent_memory_quickstart.mjs +57 -0
- package/examples/02-multi-agent-swarm/index.mjs +57 -0
- package/examples/02_cross_model_passport.mjs +64 -0
- package/examples/03-langchain-memory/index.mjs +41 -0
- package/examples/03_poseidon_commitment_verification.mjs +71 -0
- package/examples/04-python-trading-agent/trader.py +49 -0
- package/examples/README.md +27 -0
- package/examples/ci/github-actions.yml +7 -2
- package/package.json +142 -11
- package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
- package/packages/connectors/src/index.js +196 -4
- package/packages/connectors/swarm-router.mjs +168 -0
- package/packages/core/src/index.js +248 -1
- package/packages/core/src/version.mjs +7 -0
- package/packages/dev-tools/package.json +19 -0
- package/packages/dev-tools/src/index.js +4 -0
- package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
- package/packages/dev-tools/src/swarm-simulator.js +101 -0
- package/packages/dev-tools/src/vault-inspector.js +114 -0
- package/packages/dev-tools/src/vector-benchmark.js +100 -0
- package/packages/developer-platform/src/access-credentials.js +341 -0
- package/packages/developer-platform/src/http.js +132 -0
- package/packages/developer-platform/src/index.js +4 -0
- package/packages/developer-platform/src/usage-http.js +60 -0
- package/packages/developer-platform/src/usage.js +295 -0
- package/packages/enclave-runtime/attestation.mjs +159 -0
- package/packages/enclave-runtime/index.mjs +47 -0
- package/packages/enclave-runtime/session-manager.mjs +253 -0
- package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
- package/packages/enigma-reflex/package.json +14 -0
- package/packages/enigma-reflex/src/index.js +204 -0
- package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
- package/packages/enigma-reflex/training/requirements.txt +8 -0
- package/packages/enigma-reflex/training/train.py +314 -0
- package/packages/enigma-weave/LICENSE +22 -0
- package/packages/enigma-weave/UPSTREAM.json +21 -0
- package/packages/enigma-weave/package.json +14 -0
- package/packages/enigma-weave/src/index.js +286 -0
- package/packages/hosted-cloud/src/index.js +80 -5
- package/packages/importers/src/index.js +432 -0
- package/packages/inference-runtime/src/browser.js +401 -0
- package/packages/inference-runtime/src/chat.js +265 -0
- package/packages/inference-runtime/src/code.js +407 -0
- package/packages/inference-runtime/src/contracts.js +162 -0
- package/packages/inference-runtime/src/http.js +232 -0
- package/packages/inference-runtime/src/image.js +186 -0
- package/packages/inference-runtime/src/index.js +10 -0
- package/packages/inference-runtime/src/model-router.js +320 -0
- package/packages/inference-runtime/src/platform.js +125 -0
- package/packages/inference-runtime/src/privacy.js +400 -0
- package/packages/inference-runtime/src/video.js +253 -0
- package/packages/mcp-server/README.md +22 -6
- package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
- package/packages/mcp-server/src/index.js +1418 -105
- package/packages/mcp-server/src/oauth.js +561 -0
- package/packages/mcp-server/src/private-handoff.js +84 -0
- package/packages/mcp-server/src/remote-http.js +273 -0
- package/packages/mcp-server/src/remote-policy.js +72 -0
- package/packages/mcp-server/swarm-bridge.mjs +361 -0
- package/packages/mesh/index.d.ts +283 -0
- package/packages/mesh/package.json +23 -0
- package/packages/mesh/src/crypto.js +189 -0
- package/packages/mesh/src/federation-packets.js +353 -0
- package/packages/mesh/src/gossip.js +311 -0
- package/packages/mesh/src/index.js +6 -0
- package/packages/mesh/src/protocol.js +255 -0
- package/packages/mesh/src/router.js +279 -0
- package/packages/mesh/src/transport.js +306 -0
- package/packages/passport/src/index.js +426 -1
- package/packages/private-economy/src/credits-http.js +100 -0
- package/packages/private-economy/src/credits.js +447 -0
- package/packages/private-economy/src/index.js +5 -0
- package/packages/private-economy/src/payments-http.js +120 -0
- package/packages/private-economy/src/payments.js +509 -0
- package/packages/private-economy/src/x402.js +346 -0
- package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
- package/packages/rag/index.d.ts +182 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
- package/packages/rag/package.json +27 -0
- package/packages/rag/src/blinded-search.js +109 -0
- package/packages/rag/src/bm25.js +169 -0
- package/packages/rag/src/embeddings.js +459 -0
- package/packages/rag/src/hybrid.js +76 -0
- package/packages/rag/src/index.js +38 -0
- package/packages/rag/src/reranker.js +61 -0
- package/packages/rag/src/research.js +107 -0
- package/packages/rag/src/vector-store.js +430 -0
- package/packages/rag/src/verify-model-artifacts.mjs +4 -0
- package/packages/sdk/index.d.ts +760 -0
- package/packages/sdk/package.json +33 -0
- package/packages/sdk/python/README.md +24 -0
- package/packages/sdk/python/enigma_sdk.py +250 -0
- package/packages/sdk/python/pyproject.toml +34 -0
- package/packages/sdk/python/requirements.txt +1 -0
- package/packages/sdk/python/setup.py +20 -0
- package/packages/sdk/src/federation/capability-grant.js +389 -0
- package/packages/sdk/src/federation/federation-router.js +360 -0
- package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
- package/packages/sdk/src/federation/index.js +3 -0
- package/packages/sdk/src/index.js +1796 -0
- package/packages/sdk/src/intelligence/contradiction.js +337 -0
- package/packages/sdk/src/intelligence/decision-engine.js +155 -0
- package/packages/sdk/src/intelligence/index.js +4 -0
- package/packages/sdk/src/intelligence/ontology.js +122 -0
- package/packages/sdk/src/intelligence/temporal.js +123 -0
- package/packages/sdk/src/market-client.js +142 -0
- package/packages/sdk/src/mesh-client.js +110 -0
- package/packages/sdk/src/middleware/index.js +3 -0
- package/packages/sdk/src/middleware/langchain.js +159 -0
- package/packages/sdk/src/middleware/llamaindex.js +101 -0
- package/packages/sdk/src/middleware/vercel-ai.js +112 -0
- package/packages/sdk/src/rag-client.js +85 -0
- package/packages/sdk/src/swarm-orchestrator.js +260 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
- package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
- package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
- package/packages/snapcompact/index.d.ts +284 -0
- package/packages/snapcompact/package.json +25 -0
- package/packages/snapcompact/src/index.js +716 -0
- package/packages/storage/PACKAGE_CONTRACT.md +1 -1
- package/packages/terminal-console/animations.mjs +240 -0
- package/packages/terminal-console/auto-anchor.mjs +220 -0
- package/packages/terminal-console/banner.mjs +91 -0
- package/packages/terminal-console/commands.mjs +459 -0
- package/packages/terminal-console/delegation.mjs +152 -0
- package/packages/terminal-console/index.mjs +5 -0
- package/packages/terminal-console/outbox.mjs +143 -0
- package/packages/terminal-console/phantom-bridge.mjs +637 -0
- package/packages/terminal-console/repl.mjs +136 -0
- package/packages/terminal-console/signer-store.mjs +130 -0
- package/packages/terminal-console/solana-rpc.mjs +214 -0
- package/packages/terminal-console/solana-transport.mjs +189 -0
- package/packages/terminal-tui/dashboard.mjs +214 -0
- package/packages/terminal-tui/index.mjs +28 -0
- package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
- package/packages/terminal-tui/telemetry-hud.mjs +137 -0
- package/packages/vault/index.d.ts +449 -0
- package/packages/vault/package.json +27 -0
- package/packages/vault/src/e2ee.mjs +393 -0
- package/packages/vault/src/enclave.js +481 -0
- package/packages/vault/src/erasure.js +207 -0
- package/packages/vault/src/index.js +1018 -155
- package/packages/vault/src/persistence.js +307 -0
- package/packages/vault/src/poseidon.js +354 -0
- package/packages/vault/src/receipt.js +459 -0
- package/scripts/benchmark-optical-context.mjs +166 -0
- package/scripts/bootstrap-enigma.mjs +502 -0
- package/scripts/build-edge-backend-workers.mjs +20 -5
- package/scripts/build-goal-completion-audit.mjs +72 -25
- package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
- package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +41 -21
- package/scripts/build-operator-evidence-starter.mjs +59 -1
- package/scripts/build-production-backend-env-kit.mjs +2 -0
- package/scripts/build-production-unblocker.mjs +3 -0
- package/scripts/check.mjs +17 -3
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +65 -109
- package/scripts/release-provenance.mjs +6 -0
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/scan-secrets.mjs +1 -0
- package/scripts/simulate-production-env.mjs +7 -2
- package/scripts/validate-hosted-backend-live.mjs +112 -1
- package/specs/antibody-pack-v1.schema.json +95 -0
- package/specs/antigen-envelope-v1.schema.json +81 -0
- package/specs/boundary-manifest-v1.schema.json +35 -35
- package/specs/capsule-v1.schema.json +55 -55
- package/specs/claim-boundary-manifest-v1.schema.json +22 -22
- package/specs/claim-ledger-v1.schema.json +291 -0
- package/specs/context-passport-v1.schema.json +59 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -26
- package/specs/evidence-packet-v1.schema.json +177 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
- package/specs/immune-scan-report-v1.schema.json +112 -0
- package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
- package/specs/memory-atom-v1.schema.json +59 -0
- package/specs/memory-event-v1.schema.json +42 -42
- package/specs/passport-v1.schema.json +50 -50
- package/specs/proof-of-non-use-v1.schema.json +65 -0
- package/specs/quarantine-record-v1.schema.json +126 -0
- package/specs/receipt-v1.schema.json +61 -61
- package/specs/state-checkpoint-v1.schema.json +37 -37
- package/specs/trust-bundle-v1.schema.json +56 -56
- package/specs/trust-card-v1.schema.json +119 -0
- package/docs/proof-network-launch-plan.md +0 -421
- package/packages/metering/PACKAGE_CONTRACT.md +0 -20
- 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
|
+
});
|