UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

137 lines (134 loc) 5.04 kB
import { HostedFunnel, resolveCopyToken, resolveSimBase } from "./bin-69rn2kv7.js"; import { parseArgs } from "./bin-1gc4zavq.js"; import { CliError, EXIT } from "./bin-t9ggwnv5.js"; import { GRADE_SIGN_PREFIX, ROOT_HASH_PREFIX, certifiedRoot } from "./bin-2ywrx58g.js"; // src/cli/commands/verify.ts import { createPublicKey, verify as ed25519Verify } from "node:crypto"; var VERDICT_EXIT = { VERIFIED: 0, UNVERIFIED: EXIT.GENERIC, KEY_RETIRED: EXIT.GENERIC, NOT_FOUND: EXIT.NOT_FOUND }; function parseProofRef(input) { const trimmed = input.trim(); if (trimmed === "") throw new CliError({ code: "USAGE", exit: EXIT.USAGE, message: "verify needs a proof URL or id" }); const m = /\/proof\/([^/?#]+)/.exec(trimmed); if (m !== null) return decodeURIComponent(m[1]); if (/^https?:\/\//i.test(trimmed)) throw new CliError({ code: "USAGE", exit: EXIT.USAGE, message: `not a proof URL: ${JSON.stringify(trimmed)}`, hint: "pass a proof URL (https://kestrel.markets/proof/<id>) or a bare proof id" }); return trimmed; } function verifyProofSignature(proof, keys) { const key = keys.find((k) => k.kid === proof.kid); if (key === undefined) return "KEY_RETIRED"; try { const publicKey = createPublicKey({ key: { ...key.public_jwk }, format: "jwk" }); const signature = Buffer.from(proof.signature, "base64url"); const bareRoot = proof.root.startsWith(ROOT_HASH_PREFIX) ? proof.root.slice(ROOT_HASH_PREFIX.length) : proof.root; const message = Buffer.from(`${GRADE_SIGN_PREFIX}.${bareRoot}`); const ok = ed25519Verify(null, message, publicKey, signature); return ok ? "VERIFIED" : "UNVERIFIED"; } catch { return "UNVERIFIED"; } } async function recomputeNumbersBindRoot(proof) { if (proof.coveredFields === undefined) return "unknown"; try { const bareRoot = proof.root.startsWith(ROOT_HASH_PREFIX) ? proof.root.slice(ROOT_HASH_PREFIX.length) : proof.root; const recomputed = await certifiedRoot(proof.coveredFields); return recomputed === bareRoot ? "yes" : "no"; } catch { return "unknown"; } } function needsVerifyKeyUrl(verdict) { return verdict === "UNVERIFIED" || verdict === "KEY_RETIRED"; } function emit(ctx, report, notice) { if (ctx.mode === "json") { process.stdout.write(JSON.stringify({ schema: "kestrel.verify/v1", ...notice !== undefined ? { notice } : {}, ...report }) + ` `); return; } if (notice !== undefined) process.stdout.write(`note: ${notice} `); const parts = [report.verdict, `proof=${report.proof_id}`]; if (report.kid !== undefined) parts.push(`kid=${report.kid}`); if (report.epoch !== undefined) parts.push(`epoch=${report.epoch}`); if (report.verdict === "UNVERIFIED" && report.numbers_bind_root === "no") parts.push("note=published-numbers-do-not-rebuild-the-signed-root"); else if (report.verdict === "UNVERIFIED" && report.claimed_verified === true) parts.push("note=the-proof-claimed-verified-but-its-signature-did-not-re-verify"); if (report.numbers_bind_root !== undefined) parts.push(`numbers-bind-root=${report.numbers_bind_root}`); if (needsVerifyKeyUrl(report.verdict) && report.verify_key_url !== undefined) parts.push(`verify-key=${report.verify_key_url}`); process.stdout.write(parts.join("\t") + ` `); } async function runVerify(client, id, ctx, forward, notice) { const proof = await client.publishedProof(id, forward); if (proof === null) { emit(ctx, { verdict: "NOT_FOUND", proof_id: id }, notice); return VERDICT_EXIT.NOT_FOUND; } const keys = await client.verifyKeys(forward); const signatureVerdict = verifyProofSignature(proof, keys); const numbersBind = await recomputeNumbersBindRoot(proof); const verdict = numbersBind === "no" ? "UNVERIFIED" : signatureVerdict; emit(ctx, { verdict, proof_id: proof.proof_id, kid: proof.kid, epoch: proof.epoch, claimed_verified: proof.verificationClaim, numbers_bind_root: numbersBind, ...needsVerifyKeyUrl(verdict) ? { verify_key_url: client.verifyKeysUrl } : {} }, notice); return VERDICT_EXIT[verdict]; } async function verifyCommand(argv, ctx, globals, deps = {}) { const { positionals, flags } = parseArgs(argv, new Set, new Set(["copy-token"])); if (positionals.length === 0) throw new CliError({ code: "USAGE", exit: EXIT.USAGE, message: "verify needs a proof URL or id", hint: "usage: kestrel verify <https://kestrel.markets/proof/<id> | <id>>" }); const env = deps.env ?? process.env; const id = parseProofRef(positionals[0]); const copyToken = resolveCopyToken(flags, env); const forward = copyToken !== undefined ? { copyToken } : {}; const base = resolveSimBase(globals, env); const client = new HostedFunnel({ baseUrl: base, ...deps.fetch !== undefined ? { fetch: deps.fetch } : {} }); return runVerify(client, id, ctx, forward); } export { parseProofRef, verifyProofSignature, recomputeNumbersBindRoot, runVerify, verifyCommand };