blindseal 0.5.0 → 0.5.1
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/dashboard/assets/blindseal_verifier_bg-BYF7rQG1.wasm +0 -0
- package/dashboard/assets/geist-400-gapTbOY8.woff2 +0 -0
- package/dashboard/assets/geist-500-uokXdC-Q.woff2 +0 -0
- package/dashboard/assets/geist-600-DFOURf8L.woff2 +0 -0
- package/dashboard/assets/geist-mono-400-DTRLJnHl.woff2 +0 -0
- package/dashboard/assets/geist-mono-600-V-KvD_pi.woff2 +0 -0
- package/dashboard/assets/index-ErSVkiAF.css +1 -0
- package/dashboard/assets/index-cOSTL8eT.js +1 -0
- package/dashboard/assets/index-p0eB3k2y.js +70 -0
- package/dashboard/index.html +76 -0
- package/dist/attest-VWYZAOLY.js +346 -0
- package/dist/{audit-C756XFXR.js → audit-G4DDMAXH.js} +1 -1
- package/dist/chunk-6EU4GH4P.js +513 -0
- package/dist/chunk-B44GZXDX.js +136 -0
- package/dist/chunk-CIUQ6WHJ.js +88 -0
- package/dist/{chunk-BTIXDNTV.js → chunk-DJL2E6RF.js} +69 -6
- package/dist/{chunk-PQ457W6T.js → chunk-VSOUJM6X.js} +1 -0
- package/dist/{chunk-JJY62YXJ.js → chunk-ZZTWGFH2.js} +0 -20
- package/dist/dashboard-J6BA7OBL.js +105 -0
- package/dist/decide.js +1 -1
- package/dist/index.js +122 -1307
- package/dist/{init.js → init-ZP3SLHM3.js} +122 -36
- package/dist/run-YQBNEPSD.js +17 -0
- package/dist/{submit-2UALCRX2.js → submit-D6MQTAXT.js} +1 -1
- package/dist/verify-RISAGKXC.js +98 -0
- package/fixtures/attestation-sample.json +25 -0
- package/fixtures/attestation-tampered.json +29 -0
- package/fixtures/attestations.json +174 -0
- package/fixtures/audit.json +1810 -0
- package/fixtures/decisions.json +9899 -0
- package/fixtures/policy.json +216 -0
- package/package.json +8 -8
- package/dist/attest-QLIE2OSQ.js +0 -195
- package/dist/verify-HO4IU7XV.js +0 -45
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="bs-shell" data-shell="sidebar">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Blindseal</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-p0eB3k2y.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-ErSVkiAF.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body class="bs-mounted">
|
|
11
|
+
<!-- The dashboard frame (src/kit/shell.js, DECISIONS D06) is static: BS.mountShell adopts these
|
|
12
|
+
nodes and only wires them (nav, pill, rail, cover, status). Runtime values are blank here.
|
|
13
|
+
React's root container is <main id="main-content"> inside .bs-work (src/main.tsx). -->
|
|
14
|
+
<div class="bs-app" data-shell="sidebar">
|
|
15
|
+
<a class="skip-link" href="#main-content">Skip to content</a>
|
|
16
|
+
<aside class="bs-side" aria-label="Blindseal">
|
|
17
|
+
<div class="bs-side__brand">
|
|
18
|
+
<a class="bs-wordmark" href="#/decisions" data-nav-link="decisions" aria-label="Blindseal, decisions"><svg xmlns="http://www.w3.org/2000/svg" class="bs-mark bs-mark--A" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.800" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="10.25"></circle><rect x="7.25" y="10.6" width="9.5" height="2.8" rx="1.4"></rect></svg><span class="bs-wordmark__word">Blindseal</span></a>
|
|
19
|
+
<span class="chip outline">local</span>
|
|
20
|
+
</div>
|
|
21
|
+
<nav class="bs-side__nav" aria-label="Sections">
|
|
22
|
+
<p class="bs-nav__label">Dashboard</p>
|
|
23
|
+
<div class="bs-nav">
|
|
24
|
+
<span class="bs-nav__pill" aria-hidden="true"></span>
|
|
25
|
+
<a class="bs-navitem" href="#/decisions" data-nav="decisions" aria-label="Decisions"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list" data-lucide="list" aria-hidden="true" focusable="false"><path d="M3 5h.01"></path><path d="M3 12h.01"></path><path d="M3 19h.01"></path><path d="M8 5h13"></path><path d="M8 12h13"></path><path d="M8 19h13"></path></svg></span><span class="bs-navitem__t">Decisions</span></a>
|
|
26
|
+
<a class="bs-navitem" href="#/audit" data-nav="audit" aria-label="Audit"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link" data-lucide="link" aria-hidden="true" focusable="false"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></span><span class="bs-navitem__t">Audit</span></a>
|
|
27
|
+
<a class="bs-navitem" href="#/policy" data-nav="policy" aria-label="Policy"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scroll-text" data-lucide="scroll-text" aria-hidden="true" focusable="false"><path d="M15 12h-5"></path><path d="M15 8h-5"></path><path d="M19 17V5a2 2 0 0 0-2-2H4"></path><path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"></path></svg></span><span class="bs-navitem__t">Policy</span></a>
|
|
28
|
+
<a class="bs-navitem" href="#/attestations" data-nav="attestations" aria-label="Attestations"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-stamp" data-lucide="stamp" aria-hidden="true" focusable="false"><path d="M14 13V8.5C14 7 15 7 15 5a3 3 0 0 0-6 0c0 2 1 2 1 3.5V13"></path><path d="M20 15.5a2.5 2.5 0 0 0-2.5-2.5h-11A2.5 2.5 0 0 0 4 15.5V17a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1z"></path><path d="M5 22h14"></path></svg></span><span class="bs-navitem__t">Attestations</span></a>
|
|
29
|
+
<a class="bs-navitem" href="#/settings" data-nav="settings" aria-label="Settings"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings" data-lucide="settings" aria-hidden="true" focusable="false"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"></path><circle cx="12" cy="12" r="3"></circle></svg></span><span class="bs-navitem__t">Settings</span></a>
|
|
30
|
+
<div class="bs-nav__ink" aria-hidden="true">
|
|
31
|
+
<span class="bs-navitem" data-nav="decisions"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list" data-lucide="list" aria-hidden="true" focusable="false"><path d="M3 5h.01"></path><path d="M3 12h.01"></path><path d="M3 19h.01"></path><path d="M8 5h13"></path><path d="M8 12h13"></path><path d="M8 19h13"></path></svg></span><span class="bs-navitem__t">Decisions</span></span>
|
|
32
|
+
<span class="bs-navitem" data-nav="audit"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link" data-lucide="link" aria-hidden="true" focusable="false"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></span><span class="bs-navitem__t">Audit</span></span>
|
|
33
|
+
<span class="bs-navitem" data-nav="policy"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scroll-text" data-lucide="scroll-text" aria-hidden="true" focusable="false"><path d="M15 12h-5"></path><path d="M15 8h-5"></path><path d="M19 17V5a2 2 0 0 0-2-2H4"></path><path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"></path></svg></span><span class="bs-navitem__t">Policy</span></span>
|
|
34
|
+
<span class="bs-navitem" data-nav="attestations"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-stamp" data-lucide="stamp" aria-hidden="true" focusable="false"><path d="M14 13V8.5C14 7 15 7 15 5a3 3 0 0 0-6 0c0 2 1 2 1 3.5V13"></path><path d="M20 15.5a2.5 2.5 0 0 0-2.5-2.5h-11A2.5 2.5 0 0 0 4 15.5V17a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1z"></path><path d="M5 22h14"></path></svg></span><span class="bs-navitem__t">Attestations</span></span>
|
|
35
|
+
<span class="bs-navitem" data-nav="settings"><span class="bs-navitem__g"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings" data-lucide="settings" aria-hidden="true" focusable="false"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"></path><circle cx="12" cy="12" r="3"></circle></svg></span><span class="bs-navitem__t">Settings</span></span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</nav>
|
|
39
|
+
<div class="bs-side__foot" data-foot>
|
|
40
|
+
<div class="bs-proxy">
|
|
41
|
+
<span class="bs-proxy__k"><span class="live-dot" aria-hidden="true"></span>Proxy</span>
|
|
42
|
+
<button type="button" class="hash" data-hash data-label="Proxy signer (ed25519)" aria-label="Proxy signer (ed25519) . Copy"><span class="hash__v"></span></button>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="bs-side__footrow">
|
|
45
|
+
<span class="bs-side__ver" data-ver></span>
|
|
46
|
+
<button type="button" class="icon-btn" data-collapse aria-pressed="false" aria-label="Collapse the sidebar" title="Collapse the sidebar"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-left" data-lucide="panel-left" aria-hidden="true" focusable="false"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M9 3v18"></path></svg></button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</aside>
|
|
50
|
+
<div class="bs-work">
|
|
51
|
+
<header class="glass-bar glass-bar--top bs-top">
|
|
52
|
+
<div class="bs-top__lead">
|
|
53
|
+
<p class="bs-top__title" data-title></p>
|
|
54
|
+
<span class="bs-top__actions" data-actions></span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="bs-top__right">
|
|
57
|
+
<button type="button" class="conn" data-conn data-state="connected" aria-live="polite"><span class="live-dot" aria-hidden="true"></span><span data-conn-label>Connected</span><span class="conn__addr" data-conn-addr>127.0.0.1:7777</span></button>
|
|
58
|
+
</div>
|
|
59
|
+
</header>
|
|
60
|
+
<main id="main-content" class="bs-main" tabindex="-1"></main>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="bs-cover" aria-hidden="true"><div class="bs-cover__ind"><svg xmlns="http://www.w3.org/2000/svg" class="bs-mark bs-mark--A" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.286" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="10.25"></circle><rect x="7.25" y="10.6" width="9.5" height="2.8" rx="1.4"></rect></svg><div class="track"><span></span></div></div></div>
|
|
63
|
+
<footer class="glass-bar glass-bar--bottom bs-status" aria-label="Proxy status">
|
|
64
|
+
<span class="bs-sb" data-sb-conn data-state="connected"><span class="live-dot" aria-hidden="true"></span><span class="bs-sb__v" data-sb-conn-label>Screening</span></span>
|
|
65
|
+
<span class="bs-sb"><span class="bs-sb__k">audit #</span><span class="bs-sb__v num" data-sb-audit></span><span data-sb-audit-hash><button type="button" class="hash" data-hash data-label="Audit head hash" aria-label="Audit head hash . Copy"><span class="hash__v"></span></button></span></span>
|
|
66
|
+
<span class="bs-sb"><span class="bs-sb__k">policy</span><span data-sb-policy><button type="button" class="hash" data-hash data-label="Policy root" aria-label="Policy root . Copy"><span class="hash__v"></span></button></span></span>
|
|
67
|
+
<span class="bs-sb"><span class="bs-sb__k">detector</span><span class="bs-sb__v" data-sb-detector></span></span>
|
|
68
|
+
<span class="bs-sb"><span class="bs-sb__k">L3</span><span class="bs-sb__v" data-sb-l3></span></span>
|
|
69
|
+
<span class="bs-status__spacer"></span>
|
|
70
|
+
<a class="glass-item" href="#privacy" data-stub="Privacy">Privacy</a>
|
|
71
|
+
<a class="glass-item" href="https://blindseal.tech/docs">Docs</a>
|
|
72
|
+
<a class="glass-item" href="#help" data-stub="Help">Help</a>
|
|
73
|
+
</footer>
|
|
74
|
+
</div>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
makeLayer0Result,
|
|
4
|
+
runLayer0,
|
|
5
|
+
runLayer1
|
|
6
|
+
} from "./chunk-XV57KLEO.js";
|
|
7
|
+
import {
|
|
8
|
+
rpcUrl
|
|
9
|
+
} from "./chunk-B44GZXDX.js";
|
|
10
|
+
import {
|
|
11
|
+
openAudit
|
|
12
|
+
} from "./chunk-VSOUJM6X.js";
|
|
13
|
+
import {
|
|
14
|
+
blindsealHome
|
|
15
|
+
} from "./chunk-CIUQ6WHJ.js";
|
|
16
|
+
import "./chunk-PDX44BCA.js";
|
|
17
|
+
|
|
18
|
+
// src/cli/attest.ts
|
|
19
|
+
import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
|
|
20
|
+
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
21
|
+
import { spawnSync } from "child_process";
|
|
22
|
+
import os from "os";
|
|
23
|
+
import path2 from "path";
|
|
24
|
+
|
|
25
|
+
// src/chain/solana.ts
|
|
26
|
+
var CHAIN_ID_SOLANA_MAINNET = 1;
|
|
27
|
+
var SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
28
|
+
var TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
29
|
+
var RECIPIENT_KEYS = ["recipient", "to", "destination", "dest", "receiver", "toPubkey", "toAddress"];
|
|
30
|
+
var AMOUNT_KEYS = ["amount", "lamports", "value", "tokenAmount", "quantity"];
|
|
31
|
+
var MINT_KEYS = ["mint", "token", "tokenMint", "mintAddress"];
|
|
32
|
+
var AUTHORITY_KEYS = ["authority", "owner", "from", "source", "payer", "fromPubkey", "signer"];
|
|
33
|
+
var PROGRAM_KEYS = ["program", "programId", "programAddress"];
|
|
34
|
+
var TRANSFER_TOOL = /(?:^|_|\b)(?:transfer|send|pay|spl[_-]?transfer|transfer[_-]?checked)(?:_|\b|$)/i;
|
|
35
|
+
function firstString(obj, keys) {
|
|
36
|
+
for (const k of keys) {
|
|
37
|
+
const v = obj[k];
|
|
38
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
39
|
+
if (typeof v === "number" && Number.isFinite(v)) return String(v);
|
|
40
|
+
if (typeof v === "bigint") return v.toString();
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
function parseSolanaAction(toolName, args, opts = {}) {
|
|
45
|
+
if (!args || typeof args !== "object") return null;
|
|
46
|
+
const a = args;
|
|
47
|
+
const amount = firstString(a, AMOUNT_KEYS);
|
|
48
|
+
const recipient = firstString(a, RECIPIENT_KEYS);
|
|
49
|
+
const explicitProgram = firstString(a, PROGRAM_KEYS);
|
|
50
|
+
const looksLikeTransfer = TRANSFER_TOOL.test(toolName);
|
|
51
|
+
if (!looksLikeTransfer && !explicitProgram) return null;
|
|
52
|
+
if (recipient === void 0 && amount === void 0 && explicitProgram === void 0) return null;
|
|
53
|
+
const mint = firstString(a, MINT_KEYS);
|
|
54
|
+
const sourceAuthority = firstString(a, AUTHORITY_KEYS);
|
|
55
|
+
void opts;
|
|
56
|
+
const chainId = CHAIN_ID_SOLANA_MAINNET;
|
|
57
|
+
const targetProgram = explicitProgram ?? (mint ? TOKEN_PROGRAM : SYSTEM_PROGRAM);
|
|
58
|
+
const instruction = typeof a.instruction === "string" && a.instruction || typeof a.method === "string" && a.method || (mint ? "transferChecked" : "transfer");
|
|
59
|
+
return {
|
|
60
|
+
chain: "solana",
|
|
61
|
+
chainId,
|
|
62
|
+
targetProgram,
|
|
63
|
+
instruction,
|
|
64
|
+
recipient: recipient ?? "",
|
|
65
|
+
mint: mint ?? "",
|
|
66
|
+
amount: amount ?? "",
|
|
67
|
+
sourceAuthority: sourceAuthority ?? ""
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/prover/resolve.ts
|
|
72
|
+
import { createHash, randomBytes } from "crypto";
|
|
73
|
+
import { chmodSync, existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
74
|
+
import path from "path";
|
|
75
|
+
|
|
76
|
+
// src/prover/manifest.json
|
|
77
|
+
var manifest_default = {
|
|
78
|
+
version: "0.5.1",
|
|
79
|
+
release: "https://github.com/blindseal-dev/blindseal/releases/download/prover-v0.5.1",
|
|
80
|
+
assets: {
|
|
81
|
+
"darwin-arm64": {
|
|
82
|
+
name: "blindseal-prover-darwin-arm64",
|
|
83
|
+
sha256: "5a4cdfd6c1c6cca33e2d106966b0e0db81016a35fa47a31918bc9817556061d4"
|
|
84
|
+
},
|
|
85
|
+
"linux-x64": {
|
|
86
|
+
name: "blindseal-prover-linux-x64",
|
|
87
|
+
sha256: "TBD"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// src/prover/resolve.ts
|
|
93
|
+
var PROVER_MANIFEST = manifest_default;
|
|
94
|
+
var ProverUnavailableError = class extends Error {
|
|
95
|
+
constructor(platform) {
|
|
96
|
+
super(proverUnavailableMessage(platform));
|
|
97
|
+
this.name = "ProverUnavailableError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function proverUnavailableMessage(platform) {
|
|
101
|
+
return `blindseal attest: no prover binary for ${platform}. Build it: git clone https://github.com/blindseal-dev/blindseal && cd blindseal && cargo build --release -p blindseal-prover, then pass --prover <path> or set BLINDSEAL_PROVER.`;
|
|
102
|
+
}
|
|
103
|
+
function proverPlatform(platform = process.platform, arch = process.arch) {
|
|
104
|
+
return `${platform}-${arch}`;
|
|
105
|
+
}
|
|
106
|
+
function findRepoProver(from) {
|
|
107
|
+
let dir = from;
|
|
108
|
+
for (let i = 0; i < 7; i++) {
|
|
109
|
+
const c = path.join(dir, "crates", "prover", "target", "release", "blindseal-prover");
|
|
110
|
+
if (existsSync(c)) return c;
|
|
111
|
+
const parent = path.dirname(dir);
|
|
112
|
+
if (parent === dir) break;
|
|
113
|
+
dir = parent;
|
|
114
|
+
}
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
async function resolveProver(opts = {}) {
|
|
118
|
+
const env = opts.env ?? process.env;
|
|
119
|
+
const manifest = opts.manifest ?? PROVER_MANIFEST;
|
|
120
|
+
const platform = opts.platform ?? proverPlatform();
|
|
121
|
+
const home = opts.home ?? blindsealHome();
|
|
122
|
+
const log = opts.log ?? ((line) => process.stdout.write(line + "\n"));
|
|
123
|
+
if (opts.explicit) {
|
|
124
|
+
if (!existsSync(opts.explicit)) throw new Error(`--prover ${opts.explicit}: no such file`);
|
|
125
|
+
return opts.explicit;
|
|
126
|
+
}
|
|
127
|
+
if (env.BLINDSEAL_PROVER) {
|
|
128
|
+
if (!existsSync(env.BLINDSEAL_PROVER)) throw new Error(`BLINDSEAL_PROVER=${env.BLINDSEAL_PROVER}: no such file`);
|
|
129
|
+
return env.BLINDSEAL_PROVER;
|
|
130
|
+
}
|
|
131
|
+
const binDir = path.join(home, "bin");
|
|
132
|
+
const installed = path.join(binDir, `blindseal-prover-${manifest.version}`);
|
|
133
|
+
if (existsSync(installed)) return installed;
|
|
134
|
+
const repo = findRepoProver(opts.cwd ?? process.cwd());
|
|
135
|
+
if (repo) return repo;
|
|
136
|
+
const asset = manifest.assets[platform];
|
|
137
|
+
if (!asset || !/^[0-9a-f]{64}$/i.test(asset.sha256)) throw new ProverUnavailableError(platform);
|
|
138
|
+
const url = `${manifest.release.replace(/\/$/, "")}/${asset.name}`;
|
|
139
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
140
|
+
let bytes;
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetchImpl(url);
|
|
143
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
144
|
+
bytes = Buffer.from(await res.arrayBuffer());
|
|
145
|
+
} catch {
|
|
146
|
+
throw new ProverUnavailableError(platform);
|
|
147
|
+
}
|
|
148
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
149
|
+
if (digest !== asset.sha256.toLowerCase()) throw new ProverUnavailableError(platform);
|
|
150
|
+
mkdirSync(binDir, { recursive: true, mode: 448 });
|
|
151
|
+
const tmp = path.join(binDir, `.${asset.name}.${randomBytes(4).toString("hex")}.part`);
|
|
152
|
+
try {
|
|
153
|
+
writeFileSync(tmp, bytes, { mode: 493 });
|
|
154
|
+
chmodSync(tmp, 493);
|
|
155
|
+
renameSync(tmp, installed);
|
|
156
|
+
} catch (e) {
|
|
157
|
+
try {
|
|
158
|
+
rmSync(tmp, { force: true });
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
throw e;
|
|
162
|
+
}
|
|
163
|
+
log(`blindseal attest: downloaded prover ${manifest.version} for ${platform} \u2192 ${installed}`);
|
|
164
|
+
return installed;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/cli/attest.ts
|
|
168
|
+
var HELP = `blindseal attest \u2014 screen a tool call, record the decision, prove it
|
|
169
|
+
|
|
170
|
+
Usage:
|
|
171
|
+
blindseal attest <toolcall.json> --out <attestation.json> --target-program <program id> [flags]
|
|
172
|
+
|
|
173
|
+
Flags
|
|
174
|
+
--out <file> where to write attestation.json (default ./attestation.json)
|
|
175
|
+
--target-program <id> the gated program the action targets (base58)
|
|
176
|
+
--instruction <16 hex> instruction discriminator (default: transfer-gate gated_transfer)
|
|
177
|
+
--expiry <slot> expirySlot (default: current slot + 1500, fetched from the RPC)
|
|
178
|
+
--no-expiry write the "no expiry" sentinel (2000000000) instead of fetching a slot
|
|
179
|
+
--prover <path> prover binary (default: $BLINDSEAL_PROVER, then ~/.blindseal/bin/,
|
|
180
|
+
downloaded from the GitHub release on first use)
|
|
181
|
+
--audit-dir <dir> audit base dir (default ~/.blindseal)
|
|
182
|
+
--help, -h this help
|
|
183
|
+
|
|
184
|
+
Only clean verdicts are attested. The decision is appended to the hash-chained audit log first.
|
|
185
|
+
RPC for the slot: $BLINDSEAL_RPC_URL, else the public mainnet endpoint.
|
|
186
|
+
`;
|
|
187
|
+
var GATED_TRANSFER_DISC = "0x981894646560fc2b";
|
|
188
|
+
var NO_EXPIRY_SLOT = "2000000000";
|
|
189
|
+
var EXPIRY_WINDOW_SLOTS = 1500;
|
|
190
|
+
function parse(argv) {
|
|
191
|
+
const o = { out: "attestation.json", instruction: GATED_TRANSFER_DISC, noExpiry: false, auditDir: path2.join(os.homedir(), ".blindseal") };
|
|
192
|
+
for (let i = 0; i < argv.length; i++) {
|
|
193
|
+
const a = argv[i];
|
|
194
|
+
if (a === "--help" || a === "-h") o.help = true;
|
|
195
|
+
else if (a === "--out") o.out = argv[++i];
|
|
196
|
+
else if (a === "--target-program") o.targetProgram = argv[++i];
|
|
197
|
+
else if (a === "--instruction") o.instruction = argv[++i];
|
|
198
|
+
else if (a === "--expiry") o.expiry = argv[++i];
|
|
199
|
+
else if (a === "--no-expiry") o.noExpiry = true;
|
|
200
|
+
else if (a === "--prover") o.prover = argv[++i];
|
|
201
|
+
else if (a === "--audit-dir") o.auditDir = argv[++i];
|
|
202
|
+
else if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
|
|
203
|
+
else o.file = a;
|
|
204
|
+
}
|
|
205
|
+
return o;
|
|
206
|
+
}
|
|
207
|
+
async function fetchCurrentSlot(fetchImpl = fetch) {
|
|
208
|
+
const url = rpcUrl();
|
|
209
|
+
if (!url) throw new Error("RPC reads are disabled (BLINDSEAL_RPC_URL=off)");
|
|
210
|
+
const res = await fetchImpl(url, {
|
|
211
|
+
method: "POST",
|
|
212
|
+
headers: { "content-type": "application/json" },
|
|
213
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getSlot", params: [{ commitment: "confirmed" }] }),
|
|
214
|
+
signal: AbortSignal.timeout(1e4)
|
|
215
|
+
});
|
|
216
|
+
if (!res.ok) throw new Error(`${url} answered HTTP ${res.status}`);
|
|
217
|
+
const j = await res.json();
|
|
218
|
+
if (j.error) throw new Error(`getSlot: ${j.error.message}`);
|
|
219
|
+
if (typeof j.result !== "number") throw new Error("getSlot returned no slot");
|
|
220
|
+
return j.result;
|
|
221
|
+
}
|
|
222
|
+
async function resolveExpiry(o, fetchImpl = fetch) {
|
|
223
|
+
if (o.expiry !== void 0 && o.noExpiry) throw new Error("--expiry and --no-expiry are mutually exclusive");
|
|
224
|
+
if (o.noExpiry) return NO_EXPIRY_SLOT;
|
|
225
|
+
if (o.expiry !== void 0) {
|
|
226
|
+
if (!/^\d+$/.test(o.expiry)) throw new Error(`--expiry must be a slot number, got "${o.expiry}"`);
|
|
227
|
+
return o.expiry;
|
|
228
|
+
}
|
|
229
|
+
let slot;
|
|
230
|
+
try {
|
|
231
|
+
slot = await fetchCurrentSlot(fetchImpl);
|
|
232
|
+
} catch (e) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`could not fetch the current slot (${e.message}). Pass --expiry <slot> to set it explicitly, or --no-expiry for an attestation that never expires. Set BLINDSEAL_RPC_URL to use your own RPC endpoint.`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return String(slot + EXPIRY_WINDOW_SLOTS);
|
|
238
|
+
}
|
|
239
|
+
async function runAttestCli(argv) {
|
|
240
|
+
let o;
|
|
241
|
+
try {
|
|
242
|
+
o = parse(argv);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
process.stderr.write(`blindseal attest: ${e.message}
|
|
245
|
+
${HELP}`);
|
|
246
|
+
return 2;
|
|
247
|
+
}
|
|
248
|
+
if (o.help || !o.file) {
|
|
249
|
+
process.stdout.write(HELP);
|
|
250
|
+
return o.help ? 0 : 2;
|
|
251
|
+
}
|
|
252
|
+
if (!o.targetProgram) {
|
|
253
|
+
process.stderr.write("blindseal attest: --target-program is required\n");
|
|
254
|
+
return 2;
|
|
255
|
+
}
|
|
256
|
+
let prover;
|
|
257
|
+
try {
|
|
258
|
+
prover = await resolveProver({ explicit: o.prover });
|
|
259
|
+
} catch (e) {
|
|
260
|
+
if (e instanceof ProverUnavailableError) {
|
|
261
|
+
process.stderr.write(e.message + "\n");
|
|
262
|
+
return 1;
|
|
263
|
+
}
|
|
264
|
+
process.stderr.write(`blindseal attest: ${e.message}
|
|
265
|
+
`);
|
|
266
|
+
return 1;
|
|
267
|
+
}
|
|
268
|
+
let expiry;
|
|
269
|
+
try {
|
|
270
|
+
expiry = await resolveExpiry(o);
|
|
271
|
+
} catch (e) {
|
|
272
|
+
process.stderr.write(`blindseal attest: ${e.message}
|
|
273
|
+
`);
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
const raw = readFileSync(o.file, "utf8");
|
|
277
|
+
const call = JSON.parse(raw);
|
|
278
|
+
const payload = JSON.stringify({ tool: call.toolName, args: call.args });
|
|
279
|
+
const payloadHash = "0x" + createHash2("sha256").update(payload).digest("hex");
|
|
280
|
+
const l0 = runLayer0(payload);
|
|
281
|
+
const result = l0.fired ? makeLayer0Result(l0) : runLayer1(payload);
|
|
282
|
+
const verdict = result.verdict;
|
|
283
|
+
const decisionId = "d_" + randomBytes2(8).toString("hex");
|
|
284
|
+
const audit = openAudit(o.auditDir);
|
|
285
|
+
const entry = audit.chain.append({ decisionId, verdict, payloadHash });
|
|
286
|
+
process.stdout.write(`decision ${decisionId}: ${verdict} (audit #${entry.index} ${entry.hash.slice(0, 18)}\u2026)
|
|
287
|
+
`);
|
|
288
|
+
if (verdict !== "clean") {
|
|
289
|
+
process.stderr.write(`blindseal attest: verdict is ${verdict} (${result.detectedPatterns.join(", ")}); no attestation for a non-clean call
|
|
290
|
+
`);
|
|
291
|
+
return 1;
|
|
292
|
+
}
|
|
293
|
+
const action = parseSolanaAction(call.toolName, call.args);
|
|
294
|
+
if (!action) {
|
|
295
|
+
process.stderr.write("blindseal attest: the call is not a recognizable on-chain action\n");
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
if (!action.recipient || !action.amount || !action.sourceAuthority) {
|
|
299
|
+
process.stderr.write("blindseal attest: action needs recipient, amount and source authority\n");
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
const tmpPayload = path2.join(os.tmpdir(), `blindseal-payload-${decisionId}.json`);
|
|
303
|
+
writeFileSync2(tmpPayload, payload);
|
|
304
|
+
const args = [
|
|
305
|
+
"--payload",
|
|
306
|
+
tmpPayload,
|
|
307
|
+
"--out",
|
|
308
|
+
o.out,
|
|
309
|
+
"--decision-id",
|
|
310
|
+
decisionId,
|
|
311
|
+
"--chain-id",
|
|
312
|
+
String(action.chainId),
|
|
313
|
+
"--target-program",
|
|
314
|
+
o.targetProgram,
|
|
315
|
+
"--source-authority",
|
|
316
|
+
action.sourceAuthority,
|
|
317
|
+
"--recipient",
|
|
318
|
+
action.recipient,
|
|
319
|
+
"--mint",
|
|
320
|
+
action.mint,
|
|
321
|
+
"--amount",
|
|
322
|
+
action.amount,
|
|
323
|
+
"--instruction",
|
|
324
|
+
o.instruction,
|
|
325
|
+
"--expiry",
|
|
326
|
+
expiry
|
|
327
|
+
];
|
|
328
|
+
process.stdout.write(`proving (this takes a while on CPU)\u2026
|
|
329
|
+
`);
|
|
330
|
+
const r = spawnSync(prover, args, { stdio: "inherit" });
|
|
331
|
+
if (r.status !== 0) {
|
|
332
|
+
process.stderr.write(`blindseal attest: prover exited ${r.status}
|
|
333
|
+
`);
|
|
334
|
+
return 1;
|
|
335
|
+
}
|
|
336
|
+
process.stdout.write(`attestation written: ${o.out} (decision ${decisionId}, payloadHash ${payloadHash}, expirySlot ${expiry}${expiry === NO_EXPIRY_SLOT ? " = no expiry" : ""})
|
|
337
|
+
`);
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
export {
|
|
341
|
+
EXPIRY_WINDOW_SLOTS,
|
|
342
|
+
NO_EXPIRY_SLOT,
|
|
343
|
+
fetchCurrentSlot,
|
|
344
|
+
resolveExpiry,
|
|
345
|
+
runAttestCli
|
|
346
|
+
};
|