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,136 @@
|
|
|
1
|
+
import readline from 'node:readline';
|
|
2
|
+
import { renderTerminalBanner } from './banner.mjs';
|
|
3
|
+
import { TerminalSession } from './commands.mjs';
|
|
4
|
+
|
|
5
|
+
const KNOWN_COMMANDS = [
|
|
6
|
+
'quickstart',
|
|
7
|
+
'remember',
|
|
8
|
+
'context',
|
|
9
|
+
'doctor',
|
|
10
|
+
'tree',
|
|
11
|
+
'tui',
|
|
12
|
+
'dashboard',
|
|
13
|
+
'monitor',
|
|
14
|
+
'constellation',
|
|
15
|
+
'matrix',
|
|
16
|
+
'enclave cycle',
|
|
17
|
+
'enclave attest',
|
|
18
|
+
'enclave verify',
|
|
19
|
+
'swarm status',
|
|
20
|
+
'swarm graph',
|
|
21
|
+
'wallet',
|
|
22
|
+
'wallet connect',
|
|
23
|
+
'wallet status',
|
|
24
|
+
'wallet disconnect',
|
|
25
|
+
'solana status',
|
|
26
|
+
'solana stream',
|
|
27
|
+
'solana pulse',
|
|
28
|
+
'solana anchor-bundle',
|
|
29
|
+
'solana submit',
|
|
30
|
+
'solana verify-anchor',
|
|
31
|
+
'export',
|
|
32
|
+
'verify',
|
|
33
|
+
'token',
|
|
34
|
+
'ca',
|
|
35
|
+
'clear',
|
|
36
|
+
'cls',
|
|
37
|
+
'help',
|
|
38
|
+
'exit',
|
|
39
|
+
'quit',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export function createCompleter(session) {
|
|
43
|
+
return function completer(linePartial) {
|
|
44
|
+
const trimmed = linePartial.trimStart();
|
|
45
|
+
const hits = KNOWN_COMMANDS.filter((cmd) => cmd.startsWith(trimmed));
|
|
46
|
+
return [hits.length ? hits : KNOWN_COMMANDS, linePartial];
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function runTerminalConsole({
|
|
51
|
+
bundlePath = '.enigma/bundle.json',
|
|
52
|
+
color = true,
|
|
53
|
+
execCommand = undefined,
|
|
54
|
+
interactive = true,
|
|
55
|
+
io = { stdin: process.stdin, stdout: process.stdout, stderr: process.stderr },
|
|
56
|
+
} = {}) {
|
|
57
|
+
const session = new TerminalSession({ bundlePath, color, io });
|
|
58
|
+
|
|
59
|
+
// If a one-shot command was provided via --exec, run it and return
|
|
60
|
+
if (execCommand) {
|
|
61
|
+
const exitCode = await session.executeCommand(execCommand);
|
|
62
|
+
return exitCode === 'EXIT' ? 0 : (exitCode || 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If not interactive (e.g. CI without TTY), print banner and exit
|
|
66
|
+
if (!interactive || !io.stdin?.isTTY) {
|
|
67
|
+
io.stdout.write(renderTerminalBanner({ color }) + '\n');
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Interactive REPL Loop
|
|
72
|
+
io.stdout.write('\x1b[2J\x1b[0;0H');
|
|
73
|
+
io.stdout.write(renderTerminalBanner({ color }) + '\n\n');
|
|
74
|
+
|
|
75
|
+
const rl = readline.createInterface({
|
|
76
|
+
input: io.stdin,
|
|
77
|
+
output: io.stdout,
|
|
78
|
+
completer: createCompleter(session),
|
|
79
|
+
terminal: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
let closed = false;
|
|
84
|
+
let commandQueue = Promise.resolve();
|
|
85
|
+
|
|
86
|
+
async function promptLoop() {
|
|
87
|
+
if (closed) return;
|
|
88
|
+
const promptStr = await session.renderPrompt();
|
|
89
|
+
rl.setPrompt(promptStr);
|
|
90
|
+
rl.prompt();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
rl.on('line', (line) => {
|
|
94
|
+
commandQueue = commandQueue.then(async () => {
|
|
95
|
+
if (closed) return;
|
|
96
|
+
session.activeAbortController = new AbortController();
|
|
97
|
+
try {
|
|
98
|
+
const result = await session.executeCommand(line, { signal: session.activeAbortController.signal });
|
|
99
|
+
if (result === 'EXIT') {
|
|
100
|
+
closed = true;
|
|
101
|
+
io.stdout.write('\nExiting Enigma Terminal Console. Session closed.\n');
|
|
102
|
+
rl.close();
|
|
103
|
+
resolve(0);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (!session.activeAbortController?.signal?.aborted) {
|
|
108
|
+
io.stderr.write(`\nError: ${err.message}\n`);
|
|
109
|
+
}
|
|
110
|
+
} finally {
|
|
111
|
+
session.activeAbortController = null;
|
|
112
|
+
}
|
|
113
|
+
io.stdout.write('\n');
|
|
114
|
+
await promptLoop();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
rl.on('SIGINT', () => {
|
|
119
|
+
if (session.activeAbortController) {
|
|
120
|
+
session.activeAbortController.abort();
|
|
121
|
+
io.stdout.write('\n^C\n');
|
|
122
|
+
} else {
|
|
123
|
+
closed = true;
|
|
124
|
+
io.stdout.write('\nExiting Enigma Terminal Console.\n');
|
|
125
|
+
rl.close();
|
|
126
|
+
resolve(0);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
rl.on('close', () => {
|
|
130
|
+
closed = true;
|
|
131
|
+
resolve(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
promptLoop();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Enigma Sovereign AI Memory — SignerStore Interface
|
|
2
|
+
// Manages secure automation signer key custody with strict zero-plaintext boundaries.
|
|
3
|
+
// Integrates with Electron safeStorage (OS DPAPI on Windows / Keychain on macOS) and host-bound AES-256-GCM.
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { resolve, dirname } from 'node:path';
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import { encodeBase58 } from './phantom-bridge.mjs';
|
|
10
|
+
|
|
11
|
+
function deriveHostBoundKey(salt = 'enigma-auto-anchor-dpapi-salt-v1') {
|
|
12
|
+
const hostIdentity = [
|
|
13
|
+
os.hostname() || 'localhost',
|
|
14
|
+
os.userInfo()?.username || 'user',
|
|
15
|
+
os.platform() || 'win32',
|
|
16
|
+
os.arch() || 'x64',
|
|
17
|
+
].join(':');
|
|
18
|
+
return crypto.scryptSync(hostIdentity, salt, 32);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function zeroizeBuffer(buf) {
|
|
22
|
+
if (Buffer.isBuffer(buf)) {
|
|
23
|
+
try {
|
|
24
|
+
buf.fill(0);
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SignerStore {
|
|
30
|
+
constructor({ signerPath = '.enigma/auto-anchor/signer.enc.json', safeStorage = undefined, customKey = undefined } = {}) {
|
|
31
|
+
this.signerPath = resolve(signerPath);
|
|
32
|
+
this.safeStorage = safeStorage;
|
|
33
|
+
this.customKey = customKey;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hasSigner() {
|
|
37
|
+
return existsSync(this.signerPath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getPublicKey() {
|
|
41
|
+
if (!this.hasSigner()) return null;
|
|
42
|
+
try {
|
|
43
|
+
const data = JSON.parse(readFileSync(this.signerPath, 'utf8'));
|
|
44
|
+
return data.publicKeyBase58 || null;
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
generateKeypair() {
|
|
51
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
|
|
52
|
+
const rawPubBytes = publicKey.export({ type: 'spki', format: 'der' }).subarray(-32);
|
|
53
|
+
const rawPrivBytes = privateKey.export({ type: 'pkcs8', format: 'der' }).subarray(-32);
|
|
54
|
+
const fullSecretKey = Buffer.concat([rawPrivBytes, rawPubBytes]);
|
|
55
|
+
const publicKeyBase58 = encodeBase58(rawPubBytes);
|
|
56
|
+
return {
|
|
57
|
+
publicKeyBase58,
|
|
58
|
+
publicKeyBuffer: rawPubBytes,
|
|
59
|
+
secretKeyBuffer: fullSecretKey,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
saveSignerSecretKey(secretKeyBuffer, { publicKeyBase58 }) {
|
|
64
|
+
if (!Buffer.isBuffer(secretKeyBuffer) || secretKeyBuffer.length !== 64) {
|
|
65
|
+
throw new Error('Signer secret key must be a valid 64-byte Buffer');
|
|
66
|
+
}
|
|
67
|
+
if (!publicKeyBase58 || typeof publicKeyBase58 !== 'string') {
|
|
68
|
+
throw new Error('Missing valid publicKeyBase58');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const dir = dirname(this.signerPath);
|
|
72
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
if (this.safeStorage && typeof this.safeStorage.encryptString === 'function') {
|
|
75
|
+
const encryptedBuffer = this.safeStorage.encryptString(secretKeyBuffer.toString('hex'));
|
|
76
|
+
const manifest = {
|
|
77
|
+
schema: 'enigma.encrypted_signer_safestorage.v1',
|
|
78
|
+
publicKeyBase58,
|
|
79
|
+
encryptedHex: encryptedBuffer.toString('hex'),
|
|
80
|
+
storage: 'electron-safestorage-dpapi',
|
|
81
|
+
created_at: new Date().toISOString(),
|
|
82
|
+
};
|
|
83
|
+
writeFileSync(this.signerPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Host-bound AES-256-GCM encryption fallback
|
|
88
|
+
const key = this.customKey || deriveHostBoundKey();
|
|
89
|
+
const iv = crypto.randomBytes(12);
|
|
90
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
91
|
+
const encrypted = Buffer.concat([cipher.update(secretKeyBuffer), cipher.final()]);
|
|
92
|
+
const tag = cipher.getAuthTag();
|
|
93
|
+
|
|
94
|
+
const manifest = {
|
|
95
|
+
schema: 'enigma.encrypted_signer_hostbound.v1',
|
|
96
|
+
publicKeyBase58,
|
|
97
|
+
iv: iv.toString('hex'),
|
|
98
|
+
tag: tag.toString('hex'),
|
|
99
|
+
data: encrypted.toString('hex'),
|
|
100
|
+
storage: 'host-bound-aes-256-gcm',
|
|
101
|
+
created_at: new Date().toISOString(),
|
|
102
|
+
};
|
|
103
|
+
writeFileSync(this.signerPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
loadSignerSecretKey() {
|
|
108
|
+
if (!this.hasSigner()) throw new Error('Signer store is empty');
|
|
109
|
+
const data = JSON.parse(readFileSync(this.signerPath, 'utf8'));
|
|
110
|
+
|
|
111
|
+
if (data.schema === 'enigma.encrypted_signer_safestorage.v1') {
|
|
112
|
+
if (!this.safeStorage || typeof this.safeStorage.decryptString !== 'function') {
|
|
113
|
+
throw new Error('Cannot decrypt safeStorage payload outside active Electron session');
|
|
114
|
+
}
|
|
115
|
+
const decryptedHex = this.safeStorage.decryptString(Buffer.from(data.encryptedHex, 'hex'));
|
|
116
|
+
return Buffer.from(decryptedHex, 'hex');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (data.schema === 'enigma.encrypted_signer_hostbound.v1') {
|
|
120
|
+
const key = this.customKey || deriveHostBoundKey();
|
|
121
|
+
const iv = Buffer.from(data.iv, 'hex');
|
|
122
|
+
const tag = Buffer.from(data.tag, 'hex');
|
|
123
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
124
|
+
decipher.setAuthTag(tag);
|
|
125
|
+
return Buffer.concat([decipher.update(Buffer.from(data.data, 'hex')), decipher.final()]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
throw new Error(`Unsupported signer storage schema: ${data.schema}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { OFFICIAL_TOKEN_CA } from './banner.mjs';
|
|
2
|
+
import { ANSI } from '../terminal-tui/merkle-tree-renderer.mjs';
|
|
3
|
+
|
|
4
|
+
export const SOLANA_RPC_CLUSTERS = Object.freeze({
|
|
5
|
+
'mainnet-beta': 'https://api.mainnet-beta.solana.com',
|
|
6
|
+
'devnet': 'https://api.devnet.solana.com',
|
|
7
|
+
'testnet': 'https://api.testnet.solana.com',
|
|
8
|
+
'localnet': 'http://127.0.0.1:8899',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function isCustomRpcUrl(rpcUrl) {
|
|
12
|
+
return typeof rpcUrl === 'string' && rpcUrl.length > 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function redactRpcText(value, resolvedUrl) {
|
|
16
|
+
let text = String(value ?? '');
|
|
17
|
+
if (resolvedUrl) text = text.split(resolvedUrl).join('<custom-rpc>');
|
|
18
|
+
return text.replace(/[a-z][a-z0-9+.-]*:\/\/[^\s/@]+:[^\s/@]+@[^\s)]+/giu, '<custom-rpc>');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function probeSolanaRpc({
|
|
22
|
+
cluster = 'mainnet-beta',
|
|
23
|
+
rpcUrl = undefined,
|
|
24
|
+
timeoutMs = 4000,
|
|
25
|
+
fetchFn = globalThis.fetch,
|
|
26
|
+
} = {}) {
|
|
27
|
+
const customRpc = isCustomRpcUrl(rpcUrl);
|
|
28
|
+
const resolvedUrl = customRpc ? rpcUrl : SOLANA_RPC_CLUSTERS[cluster] || SOLANA_RPC_CLUSTERS['mainnet-beta'];
|
|
29
|
+
const displayedUrl = customRpc ? '<custom-rpc>' : resolvedUrl;
|
|
30
|
+
const started = Date.now();
|
|
31
|
+
|
|
32
|
+
const payload = {
|
|
33
|
+
jsonrpc: '2.0',
|
|
34
|
+
id: 'enigma-probe',
|
|
35
|
+
method: 'getHealth',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
let timer;
|
|
40
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
41
|
+
if (controller) {
|
|
42
|
+
timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const response = await fetchFn(resolvedUrl, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
signal: controller?.signal,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
const latencyMs = Date.now() - started;
|
|
54
|
+
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
cluster,
|
|
59
|
+
rpc_url: displayedUrl,
|
|
60
|
+
error: redactRpcText(`HTTP ${response.status}: ${response.statusText}`, customRpc ? resolvedUrl : undefined),
|
|
61
|
+
latency_ms: latencyMs,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
const isHealthy = data?.result === 'ok' || data?.result === true || (!data?.error && data?.result !== undefined);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
ok: isHealthy,
|
|
70
|
+
cluster,
|
|
71
|
+
rpc_url: displayedUrl,
|
|
72
|
+
health: data?.result ?? (data?.error ? 'error' : 'ok'),
|
|
73
|
+
latency_ms: latencyMs,
|
|
74
|
+
error: data?.error?.message ? redactRpcText(data.error.message, customRpc ? resolvedUrl : undefined) : null,
|
|
75
|
+
};
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
cluster,
|
|
80
|
+
rpc_url: displayedUrl,
|
|
81
|
+
error: err.name === 'AbortError' ? `RPC probe timed out after ${timeoutMs}ms` : redactRpcText(err.message, customRpc ? resolvedUrl : undefined),
|
|
82
|
+
latency_ms: Date.now() - started,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function fetchSolanaStreamMetrics({
|
|
88
|
+
cluster = 'mainnet-beta',
|
|
89
|
+
rpcUrl = undefined,
|
|
90
|
+
timeoutMs = 4000,
|
|
91
|
+
fetchFn = globalThis.fetch,
|
|
92
|
+
} = {}) {
|
|
93
|
+
const customRpc = isCustomRpcUrl(rpcUrl);
|
|
94
|
+
const resolvedUrl = customRpc ? rpcUrl : SOLANA_RPC_CLUSTERS[cluster] || SOLANA_RPC_CLUSTERS['mainnet-beta'];
|
|
95
|
+
const displayedUrl = customRpc ? '<custom-rpc>' : resolvedUrl;
|
|
96
|
+
const started = Date.now();
|
|
97
|
+
|
|
98
|
+
const payload = [
|
|
99
|
+
{ jsonrpc: '2.0', id: 'probe-health', method: 'getHealth' },
|
|
100
|
+
{ jsonrpc: '2.0', id: 'probe-slot', method: 'getSlot' },
|
|
101
|
+
{ jsonrpc: '2.0', id: 'probe-epoch', method: 'getEpochInfo' },
|
|
102
|
+
{ jsonrpc: '2.0', id: 'probe-version', method: 'getVersion' },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
let timer;
|
|
107
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
108
|
+
if (controller) {
|
|
109
|
+
timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const response = await fetchFn(resolvedUrl, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: { 'Content-Type': 'application/json' },
|
|
115
|
+
body: JSON.stringify(payload),
|
|
116
|
+
signal: controller?.signal,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
const latencyMs = Date.now() - started;
|
|
121
|
+
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
cluster,
|
|
126
|
+
rpc_url: displayedUrl,
|
|
127
|
+
latency_ms: latencyMs,
|
|
128
|
+
error: redactRpcText(`HTTP ${response.status}: ${response.statusText}`, customRpc ? resolvedUrl : undefined),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const data = await response.json();
|
|
133
|
+
const results = Array.isArray(data) ? data : [data];
|
|
134
|
+
const healthRes = results.find((r) => r.id === 'probe-health')?.result ?? data?.result;
|
|
135
|
+
const slotRes = results.find((r) => r.id === 'probe-slot')?.result;
|
|
136
|
+
const epochRes = results.find((r) => r.id === 'probe-epoch')?.result;
|
|
137
|
+
const versionRes = results.find((r) => r.id === 'probe-version')?.result;
|
|
138
|
+
|
|
139
|
+
const isHealthy = healthRes === 'ok' || healthRes === true || (slotRes !== undefined && slotRes !== null) || data?.result === 'ok' || data?.result === true;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
ok: isHealthy,
|
|
143
|
+
cluster,
|
|
144
|
+
rpc_url: displayedUrl,
|
|
145
|
+
latency_ms: latencyMs,
|
|
146
|
+
slot: typeof slotRes === 'number' ? slotRes : (typeof epochRes?.absoluteSlot === 'number' ? epochRes.absoluteSlot : null),
|
|
147
|
+
block_height: typeof epochRes?.blockHeight === 'number' ? epochRes.blockHeight : null,
|
|
148
|
+
epoch: typeof epochRes?.epoch === 'number' ? epochRes.epoch : null,
|
|
149
|
+
slot_index: typeof epochRes?.slotIndex === 'number' ? epochRes.slotIndex : null,
|
|
150
|
+
slots_in_epoch: typeof epochRes?.slotsInEpoch === 'number' ? epochRes.slotsInEpoch : null,
|
|
151
|
+
transaction_count: typeof epochRes?.transactionCount === 'number' ? epochRes.transactionCount : null,
|
|
152
|
+
solana_core_version: versionRes?.['solana-core'] ?? (typeof versionRes === 'string' ? versionRes : 'N/A'),
|
|
153
|
+
error: null,
|
|
154
|
+
};
|
|
155
|
+
} catch (err) {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
cluster,
|
|
159
|
+
rpc_url: displayedUrl,
|
|
160
|
+
latency_ms: Date.now() - started,
|
|
161
|
+
slot: null,
|
|
162
|
+
block_height: null,
|
|
163
|
+
epoch: null,
|
|
164
|
+
error: err.name === 'AbortError' ? `RPC probe timed out after ${timeoutMs}ms` : redactRpcText(err.message, customRpc ? resolvedUrl : undefined),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function formatSolanaStatusReport(probeResult, { color = true } = {}) {
|
|
170
|
+
const c = (text, code) => (color ? `${code}${text}${ANSI.reset}` : text);
|
|
171
|
+
|
|
172
|
+
const statusBadge = probeResult.ok
|
|
173
|
+
? `${c('● CONNECTED', `${ANSI.bold}${ANSI.green}`)} (${probeResult.latency_ms}ms)`
|
|
174
|
+
: `${c('○ UNREACHABLE / OFFLINE', `${ANSI.bold}${ANSI.yellow}`)} (${probeResult.error || 'no response'})`;
|
|
175
|
+
|
|
176
|
+
const lines = [
|
|
177
|
+
` ${c('ENIGMA SOLANA CONTEXT LAYER & RPC STATUS', `${ANSI.bold}${ANSI.amber}`)}`,
|
|
178
|
+
` ${c('Cluster:', ANSI.dimText)} ${probeResult.cluster} (${probeResult.rpc_url})`,
|
|
179
|
+
` ${c('RPC Connection:', ANSI.dimText)} ${statusBadge}`,
|
|
180
|
+
` ${c('Token Mint CA:', ANSI.dimText)} ${c(OFFICIAL_TOKEN_CA, `${ANSI.bold}${ANSI.white}`)}`,
|
|
181
|
+
` ${c('Proof Anchoring:', ANSI.dimText)} Solana Memo Program (MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr)`,
|
|
182
|
+
` ${c('Anchor Contracts:', ANSI.dimText)} enigma_nullifier, enigma_context_escrow (in development)`,
|
|
183
|
+
` ${c('Settlement Model:', ANSI.dimText)} M2M context micro-escrows with receipt-hash release`,
|
|
184
|
+
` ${c('Architecture:', ANSI.dimText)} 100% offline standalone vaults; optional on-chain proof anchoring`,
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
return lines.join('\n') + '\n';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function formatSolanaAnchorVerificationReport(report, { color = true } = {}) {
|
|
191
|
+
const c = (text, code) => (color ? `${code}${text}${ANSI.reset}` : text);
|
|
192
|
+
const verified = report?.ok === true && report?.verified_on_chain === true;
|
|
193
|
+
const status = verified
|
|
194
|
+
? c('● VERIFIED', `${ANSI.bold}${ANSI.green}`)
|
|
195
|
+
: c('○ NOT VERIFIED', `${ANSI.bold}${ANSI.yellow}`);
|
|
196
|
+
const confirmation = report?.confirmation_status
|
|
197
|
+
? `${report.confirmation_status}${report.confirmation_depth === null || report.confirmation_depth === undefined ? '' : ` (${report.confirmation_depth} confirmations)`}`
|
|
198
|
+
: 'unknown';
|
|
199
|
+
const lines = [
|
|
200
|
+
` ${c('ENIGMA SOLANA ANCHOR VERIFICATION', `${ANSI.bold}${ANSI.amber}`)}`,
|
|
201
|
+
` ${c('Status:', ANSI.dimText)} ${status}`,
|
|
202
|
+
` ${c('Cluster:', ANSI.dimText)} ${report?.cluster ?? 'unknown'}`,
|
|
203
|
+
` ${c('Signature:', ANSI.dimText)} ${report?.signature ?? 'unknown'}`,
|
|
204
|
+
` ${c('Slot / block time:', ANSI.dimText)} ${report?.slot ?? 'unknown'} / ${report?.block_time ?? 'unknown'}`,
|
|
205
|
+
` ${c('Confirmation:', ANSI.dimText)} ${confirmation}`,
|
|
206
|
+
` ${c('Slot depth:', ANSI.dimText)} ${report?.slot_depth ?? 'unknown'}`,
|
|
207
|
+
` ${c('Artifact hash:', ANSI.dimText)} ${report?.artifact_hash ?? 'unknown'}`,
|
|
208
|
+
` ${c('Proof commitment:', ANSI.dimText)} ${report?.proof_commitment ?? 'unknown'}`,
|
|
209
|
+
` ${c('Verification state:', ANSI.dimText)} ${report?.verification_status ?? 'unknown'}`,
|
|
210
|
+
...(report?.error?.message ? [` ${c('Error:', ANSI.dimText)} ${report.error.message}`] : []),
|
|
211
|
+
` ${c('Boundary:', ANSI.dimText)} public commitment only; no factual-truth, hallucination, provider-deletion, or physical-deletion claim`,
|
|
212
|
+
];
|
|
213
|
+
return lines.join('\n') + '\n';
|
|
214
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Enigma Sovereign AI Memory — Solana Transport Layer
|
|
2
|
+
// Enforces Mainnet genesis verification, dynamic fee computation with 5,000 lamport clamp,
|
|
3
|
+
// SPL Memo program allowlisting, parsed transaction confirmation, and secret key zeroization.
|
|
4
|
+
|
|
5
|
+
import { SOLANA_MAINNET_RPC } from './phantom-bridge.mjs';
|
|
6
|
+
import { sha256Json, PROOF_NETWORK_ANCHOR_BATCH_SCHEMA } from '../proof-network/src/index.js';
|
|
7
|
+
|
|
8
|
+
export const SOLANA_MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
|
|
9
|
+
export const SOLANA_MAINNET_GENESIS_HASH = '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d';
|
|
10
|
+
export const MAX_AUTO_ANCHOR_FEE_LAMPORTS = 5000;
|
|
11
|
+
|
|
12
|
+
export function createSolanaProofMemoRef(schema, artifact, cluster = 'mainnet-beta') {
|
|
13
|
+
const artifactHash = sha256Json(artifact);
|
|
14
|
+
const proofCommitment = sha256Json({
|
|
15
|
+
rail: 'solana-memo-v1',
|
|
16
|
+
cluster,
|
|
17
|
+
artifact_type: schema,
|
|
18
|
+
artifact_hash: artifactHash,
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
v: 1,
|
|
22
|
+
protocol: 'enigma-proof-network',
|
|
23
|
+
rail: 'solana-memo',
|
|
24
|
+
cluster,
|
|
25
|
+
artifact_type: schema,
|
|
26
|
+
artifact_hash: artifactHash,
|
|
27
|
+
proof_commitment: proofCommitment,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class SolanaTransport {
|
|
32
|
+
constructor({
|
|
33
|
+
rpcUrl = SOLANA_MAINNET_RPC,
|
|
34
|
+
cluster = 'mainnet-beta',
|
|
35
|
+
fetchFn = globalThis.fetch,
|
|
36
|
+
maxFeeLamports = MAX_AUTO_ANCHOR_FEE_LAMPORTS,
|
|
37
|
+
} = {}) {
|
|
38
|
+
this.rpcUrl = rpcUrl;
|
|
39
|
+
this.cluster = cluster;
|
|
40
|
+
this.fetchFn = fetchFn;
|
|
41
|
+
this.maxFeeLamports = maxFeeLamports;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async verifyGenesisHash() {
|
|
45
|
+
const res = await this.fetchFn(this.rpcUrl, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 'genesis-check', method: 'getGenesisHash' }),
|
|
49
|
+
});
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
const hash = data?.result;
|
|
52
|
+
if (this.cluster === 'mainnet-beta' && hash !== SOLANA_MAINNET_GENESIS_HASH) {
|
|
53
|
+
throw new Error(`Solana RPC genesis mismatch: got ${hash}, expected ${SOLANA_MAINNET_GENESIS_HASH}`);
|
|
54
|
+
}
|
|
55
|
+
return hash;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getBalance(publicKeyBase58) {
|
|
59
|
+
const res = await this.fetchFn(this.rpcUrl, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 'bal-check', method: 'getBalance', params: [publicKeyBase58] }),
|
|
63
|
+
});
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
const lamports = Number(data?.result?.value ?? 0);
|
|
66
|
+
return { lamports, balanceSol: (lamports / 1e9).toFixed(4) };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async broadcastMemo({ payerSecretKey, memoRef }) {
|
|
70
|
+
if (!Buffer.isBuffer(payerSecretKey) || payerSecretKey.length !== 64) {
|
|
71
|
+
throw new Error('payerSecretKey must be a 64-byte Buffer');
|
|
72
|
+
}
|
|
73
|
+
if (!memoRef || typeof memoRef !== 'object') {
|
|
74
|
+
throw new Error('Invalid memoRef payload');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await this.verifyGenesisHash();
|
|
78
|
+
|
|
79
|
+
// Dynamically load Solana web3 SDK
|
|
80
|
+
const web3 = await import('@solana/web3.js');
|
|
81
|
+
const { Connection, Keypair, PublicKey, Transaction, TransactionInstruction, sendAndConfirmTransaction } = web3;
|
|
82
|
+
|
|
83
|
+
let payer;
|
|
84
|
+
try {
|
|
85
|
+
payer = Keypair.fromSecretKey(payerSecretKey);
|
|
86
|
+
} finally {
|
|
87
|
+
// Memory safety: Zeroize volatile secret key buffer copy after keypair creation
|
|
88
|
+
try {
|
|
89
|
+
payerSecretKey.fill(0);
|
|
90
|
+
} catch {}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const connection = new Connection(this.rpcUrl, 'confirmed');
|
|
94
|
+
const memoBytes = Buffer.from(JSON.stringify(memoRef), 'utf8');
|
|
95
|
+
|
|
96
|
+
const instruction = new TransactionInstruction({
|
|
97
|
+
keys: [],
|
|
98
|
+
programId: new PublicKey(SOLANA_MEMO_PROGRAM_ID),
|
|
99
|
+
data: memoBytes,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const { blockhash } = await connection.getLatestBlockhash('confirmed');
|
|
103
|
+
const transaction = new Transaction({ feePayer: payer.publicKey, recentBlockhash: blockhash }).add(instruction);
|
|
104
|
+
|
|
105
|
+
// Dynamic Fee Query & Clamp Verification
|
|
106
|
+
const message = transaction.compileMessage();
|
|
107
|
+
const feeRes = await connection.getFeeForMessage(message, 'confirmed');
|
|
108
|
+
const estimatedFee = Number(feeRes?.value ?? 5000);
|
|
109
|
+
|
|
110
|
+
if (estimatedFee > this.maxFeeLamports) {
|
|
111
|
+
throw new Error(`Estimated transaction fee (${estimatedFee} lamports) exceeds maximum auto-anchor fee policy (${this.maxFeeLamports} lamports)`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Submit and confirm
|
|
115
|
+
const signature = await sendAndConfirmTransaction(connection, transaction, [payer], { commitment: 'confirmed' });
|
|
116
|
+
|
|
117
|
+
return await this.verifyTransactionOnChain(connection, signature, memoRef, estimatedFee);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async verifyTransactionOnChain(connection, signature, expectedMemoRef, fee = 5000) {
|
|
121
|
+
if (!expectedMemoRef || typeof expectedMemoRef !== 'object') {
|
|
122
|
+
throw new Error('expectedMemoRef must be a canonical memoRef object');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const parsedTx = await connection.getParsedTransaction(signature, {
|
|
126
|
+
commitment: 'confirmed',
|
|
127
|
+
maxSupportedTransactionVersion: 0,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (!parsedTx || parsedTx.meta?.err !== null) {
|
|
131
|
+
throw new Error(`On-chain transaction execution failed: ${signature}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Verify SPL Memo program ID and instruction presence
|
|
135
|
+
const memoIx = parsedTx.transaction?.message?.instructions?.find((ix) => {
|
|
136
|
+
const progId = ix.programId?.toBase58 ? ix.programId.toBase58() : String(ix.programId || '');
|
|
137
|
+
return progId === SOLANA_MEMO_PROGRAM_ID;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!memoIx) {
|
|
141
|
+
throw new Error(`Transaction ${signature} missing verified SPL Memo program instruction`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Extract memo text from parsed or data field
|
|
145
|
+
const rawMemoText = typeof memoIx.parsed === 'string'
|
|
146
|
+
? memoIx.parsed
|
|
147
|
+
: (typeof memoIx.data === 'string' ? Buffer.from(memoIx.data, 'base64').toString('utf8') : '');
|
|
148
|
+
|
|
149
|
+
let observed;
|
|
150
|
+
try {
|
|
151
|
+
observed = JSON.parse(rawMemoText);
|
|
152
|
+
} catch {
|
|
153
|
+
throw new Error('On-chain memo payload is not valid JSON');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Exact field-by-field equality checks against canonical memoRef contract
|
|
157
|
+
if (observed.v !== expectedMemoRef.v) {
|
|
158
|
+
throw new Error(`Memo version mismatch: got ${observed.v}, expected ${expectedMemoRef.v}`);
|
|
159
|
+
}
|
|
160
|
+
if (observed.protocol !== expectedMemoRef.protocol) {
|
|
161
|
+
throw new Error(`Memo protocol mismatch: got ${observed.protocol}, expected ${expectedMemoRef.protocol}`);
|
|
162
|
+
}
|
|
163
|
+
if (observed.rail !== expectedMemoRef.rail) {
|
|
164
|
+
throw new Error(`Memo rail mismatch: got ${observed.rail}, expected ${expectedMemoRef.rail}`);
|
|
165
|
+
}
|
|
166
|
+
if (observed.cluster !== expectedMemoRef.cluster) {
|
|
167
|
+
throw new Error(`Memo cluster mismatch: got ${observed.cluster}, expected ${expectedMemoRef.cluster}`);
|
|
168
|
+
}
|
|
169
|
+
if (observed.artifact_type !== expectedMemoRef.artifact_type) {
|
|
170
|
+
throw new Error(`Memo artifact_type mismatch: got ${observed.artifact_type}, expected ${expectedMemoRef.artifact_type}`);
|
|
171
|
+
}
|
|
172
|
+
if (observed.artifact_hash !== expectedMemoRef.artifact_hash) {
|
|
173
|
+
throw new Error(`Memo artifact_hash mismatch: got ${observed.artifact_hash}, expected ${expectedMemoRef.artifact_hash}`);
|
|
174
|
+
}
|
|
175
|
+
if (observed.proof_commitment !== expectedMemoRef.proof_commitment) {
|
|
176
|
+
throw new Error(`Memo proof_commitment mismatch: got ${observed.proof_commitment}, expected ${expectedMemoRef.proof_commitment}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
signature,
|
|
181
|
+
slot: parsedTx.slot,
|
|
182
|
+
blockTime: parsedTx.blockTime,
|
|
183
|
+
cluster: this.cluster,
|
|
184
|
+
solscanUrl: `https://solscan.io/tx/${signature}`,
|
|
185
|
+
feePaidLamports: parsedTx.meta?.fee ?? fee,
|
|
186
|
+
observedMemo: observed,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|