🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@orangecheck/stamp-core

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@orangecheck/stamp-core - npm Package Compare versions

Comparing version
0.1.1
to
0.1.2
+1
-1
dist/index.js

@@ -230,3 +230,3 @@ 'use strict';

try {
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash);
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id);
} catch (e) {

@@ -233,0 +233,0 @@ return err("E_BAD_ANCHOR", `anchor verifier threw: ${e.message}`);

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts","../src/canonical.ts","../src/stamp.ts"],"names":["sha256"],"mappings":"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","file":"index.js","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n","// Canonical message + RFC 8785 JSON canonicalization for OC Stamp.\n//\n// Two distinct canonicalizers live here:\n//\n// 1. canonicalMessage(input) — the exact byte sequence the signer signs via\n// BIP-322. LF-separated, no trailing LF after signed_at. SPEC §3.1.\n//\n// 2. canonicalizeEnvelope(env) — RFC 8785 JSON canonicalization of the wire\n// envelope, used for stable serialization / equality checks. SPEC §5.\n//\n// Only (1) participates in the id derivation. (2) is for test-vector byte\n// equality and any future operation that needs deterministic envelope bytes.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport type { CanonicalMessageInput, StampEnvelope } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonical message (SPEC §3.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalMessage(input: CanonicalMessageInput): string {\n // The domain separator `oc-stamp:v1` commits the whole signing ceremony to\n // this spec version. Future incompatible changes MUST bump the version\n // number here so a v1 signature can never be replayed against a v2 domain.\n return [\n 'oc-stamp:v1',\n `address: ${input.address}`,\n `content_hash: ${input.content_hash}`,\n `content_length: ${input.content_length}`,\n `content_mime: ${input.content_mime}`,\n `signed_at: ${input.signed_at}`,\n ].join('\\n');\n}\n\n/**\n * Check whether a CanonicalMessageInput is well-formed per the spec's field\n * rules (§3, §4). Returns `{ok: true}` if every field is valid, or\n * `{ok: false, reason}` pointing at the first violation.\n *\n * Called by `stamp()` before producing the canonical bytes. Exposed for\n * UIs that want to preview validity without throwing.\n */\nexport function validateCanonicalInput(input: CanonicalMessageInput):\n | { ok: true }\n | { ok: false, reason: string } {\n // No control chars or whitespace breaks in fields — any of those would\n // invalidate the 6-line structure and produce an id nobody else can\n // reconstruct. The whole canonical message is ASCII-safe by design.\n if (!input.address || /[\\s\\x00-\\x1f]/.test(input.address)) {\n return { ok: false, reason: 'address must be non-empty with no whitespace or control chars' };\n }\n if (!/^sha256:[0-9a-f]{64}$/.test(input.content_hash)) {\n return { ok: false, reason: 'content_hash must match /^sha256:[0-9a-f]{64}$/' };\n }\n if (!Number.isInteger(input.content_length) || input.content_length < 0) {\n return { ok: false, reason: 'content_length must be a non-negative integer' };\n }\n // MIME per RFC 6838: \"type/subtype[+suffix][;params]\". We accept anything\n // that's non-empty and control-char-free; fine-grained parsing is out of\n // scope and would reject legitimate media types.\n if (!input.content_mime || /[\\x00-\\x1f]/.test(input.content_mime)) {\n return { ok: false, reason: 'content_mime must be non-empty with no control chars' };\n }\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(input.signed_at)) {\n return { ok: false, reason: 'signed_at must be ISO 8601 UTC ending in Z' };\n }\n return { ok: true };\n}\n\nexport function canonicalMessageBytes(input: CanonicalMessageInput): Uint8Array {\n return new TextEncoder().encode(canonicalMessage(input));\n}\n\n/**\n * Compute the envelope id as lowercase hex of sha256(canonical_message_bytes).\n * Pure function — the single source of truth for identity derivation.\n */\nexport function computeEnvelopeId(input: CanonicalMessageInput): string {\n const bytes = canonicalMessageBytes(input);\n return hexEncode(sha256(bytes));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Envelope canonicalization (SPEC §5; RFC 8785)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport function canonicalizeEnvelope(env: StampEnvelope): string {\n return canonicalize(env as unknown as JsonValue);\n}\n\nexport function canonicalEnvelopeBytes(env: StampEnvelope): Uint8Array {\n return new TextEncoder().encode(canonicalize(env as unknown as JsonValue) + '\\n');\n}\n\nexport function canonicalize(value: JsonValue): string {\n return encode(value);\n}\n\nfunction encode(v: JsonValue): string {\n if (v === null) return 'null';\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (typeof v === 'number') return encodeNumber(v);\n if (typeof v === 'string') return encodeString(v);\n if (Array.isArray(v)) {\n const parts: string[] = [];\n for (const item of v) parts.push(encode(item));\n return '[' + parts.join(',') + ']';\n }\n if (typeof v === 'object') {\n const keys = Object.keys(v).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const inner = v[k];\n if (inner === undefined) continue;\n parts.push(encodeString(k) + ':' + encode(inner as JsonValue));\n }\n return '{' + parts.join(',') + '}';\n }\n throw new Error('cannot canonicalize value of type ' + typeof v);\n}\n\nfunction encodeNumber(n: number): string {\n if (!Number.isFinite(n)) throw new Error('non-finite number not JSON-canonicalizable');\n if (Object.is(n, -0)) return '0';\n if (Number.isInteger(n) && Math.abs(n) < 1e21) {\n return String(n);\n }\n return JSON.stringify(n);\n}\n\nconst ESCAPE_TABLE: Record<string, string> = {\n '\\\\': '\\\\\\\\',\n '\"': '\\\\\"',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n};\n\nfunction encodeString(s: string): string {\n let out = '\"';\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const code = ch.charCodeAt(0);\n if (ESCAPE_TABLE[ch]) {\n out += ESCAPE_TABLE[ch];\n } else if (code < 0x20) {\n out += '\\\\u' + code.toString(16).padStart(4, '0');\n } else {\n out += ch;\n }\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Small utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst HEX_TABLE = '0123456789abcdef';\n\nexport function hexEncode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n out += HEX_TABLE[(b >> 4) & 0x0f]! + HEX_TABLE[b & 0x0f]!;\n }\n return out;\n}\n\nexport function sha256Hex(bytes: Uint8Array): string {\n return hexEncode(sha256(bytes));\n}\n","// stamp() and verify() — reference implementation of OC Stamp v1. SPEC §4, §8.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport {\n canonicalMessage,\n canonicalMessageBytes,\n computeEnvelopeId,\n hexEncode,\n validateCanonicalInput,\n} from './canonical.js';\nimport {\n ENVELOPE_VERSION,\n type StampEnvelope,\n type StampInput,\n type VerifyErrorCode,\n type VerifyInput,\n type VerifyOk,\n type VerifyResult,\n} from './types.js';\n\n/**\n * Compute `{hash, length}` for raw bytes. Convenience over the common\n * `sha256(bytes)` pattern that prefixes with `sha256:` and returns lowercase\n * hex. Pass the result to `stamp({ content: hashContent(bytes), mime })` when\n * you don't want stamp() to hash the bytes itself.\n */\nexport function hashContent(bytes: Uint8Array): { hash: string; length: number } {\n return {\n hash: 'sha256:' + hexEncode(sha256(bytes)),\n length: bytes.byteLength,\n };\n}\n\nexport class StampError extends Error {\n code: VerifyErrorCode;\n constructor(code: VerifyErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = 'StampError';\n }\n}\n\n/**\n * Build and sign a stamp envelope. Returns the envelope with sig populated but\n * ots left null — OTS calendar submission is a separate step (see\n * @orangecheck/stamp-ots).\n */\nexport async function stamp(input: StampInput): Promise<StampEnvelope> {\n // Resolve content hash + length. Two modes: raw bytes (we hash) or\n // pre-computed {hash, length} (we trust the caller).\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n // Pre-computed. Caller must have used SHA-256 and declared length correctly.\n // Guard against the easy typos.\n const h = input.content.hash;\n if (!h.startsWith('sha256:') || h.length !== 'sha256:'.length + 64) {\n throw new StampError(\n 'E_MALFORMED',\n 'content.hash must be \"sha256:\" + 64 lowercase hex chars'\n );\n }\n contentHash = h;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new StampError('E_MALFORMED', 'content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canon = {\n address: input.signer.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n };\n\n const v = validateCanonicalInput(canon);\n if (!v.ok) {\n // Validation rejects inputs that would silently break canonical-message\n // reconstructability downstream (whitespace in address, wrong hash\n // prefix, floats in length, etc.). Catching them here prevents a\n // signed envelope that no verifier can accept.\n throw new StampError('E_MALFORMED', v.reason);\n }\n\n const id = computeEnvelopeId(canon);\n\n // BIP-322 signs the ASCII hex representation of id. Hex is chosen (not\n // raw bytes) so wallets that render the signed message to the user show\n // something legible. The id itself commits to every field in the canonical\n // message so this is a transitive commitment to the full signing ceremony.\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: StampEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'stamp',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: {\n address: input.signer.address,\n alg: 'bip322',\n },\n signed_at: signedAt,\n stake: input.stake ?? null,\n ots: null,\n sig: {\n alg: 'bip322',\n pubkey: input.signer.address,\n value: sigValue,\n },\n };\n\n return envelope;\n}\n\n/**\n * Verify a stamp envelope. See SPEC §8 for the full algorithm. Returns a\n * structured result; callers can match on `result.ok` and drill into\n * `result.code` for the specific failure.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const env = input.envelope;\n\n // 1. Version check.\n if (env.v !== ENVELOPE_VERSION) {\n return err('E_UNSUPPORTED_VERSION', `envelope version ${env.v} not supported by this implementation`);\n }\n\n // 2. Shape check.\n const shape = checkShape(env);\n if (shape) return shape;\n\n // 3. Canonical message reconstruction + id check.\n const canon = {\n address: env.signer.address,\n content_hash: env.content.hash,\n content_length: env.content.length,\n content_mime: env.content.mime,\n signed_at: env.signed_at,\n };\n const reconstructedMessage = canonicalMessage(canon);\n const reconstructedId = hexEncode(sha256(canonicalMessageBytes(canon)));\n if (reconstructedId !== env.id) {\n return err('E_BAD_ID', `reconstructed id (${reconstructedId}) does not match envelope.id (${env.id})`);\n }\n\n // 4. Signature verify.\n if (!input.skipSignatureVerification) {\n if (!input.verifyBip322) {\n return err('E_BAD_SIG', 'no BIP-322 verifier supplied');\n }\n const ok = await input.verifyBip322(env.id, env.sig.value, env.signer.address);\n if (!ok) return err('E_BAD_SIG', 'BIP-322 signature did not verify');\n }\n\n // 5. Anchor verify.\n let anchor: VerifyOk['anchor'];\n if (env.ots === null) {\n anchor = { status: 'none' };\n } else if (env.ots.status === 'pending') {\n anchor = { status: 'pending' };\n } else {\n // confirmed\n const h = env.ots.block_height;\n const hash = env.ots.block_hash;\n if (h === null || hash === null) {\n return err('E_BAD_ANCHOR', 'confirmed OTS proof missing block_height or block_hash');\n }\n let verified = false;\n if (input.verifyOtsAnchor) {\n try {\n verified = await input.verifyOtsAnchor(env.ots.proof, h, hash);\n } catch (e) {\n return err('E_BAD_ANCHOR', `anchor verifier threw: ${(e as Error).message}`);\n }\n if (!verified) return err('E_BAD_ANCHOR', 'OTS proof did not chain to declared Bitcoin block header');\n }\n // If no anchor verifier was supplied, we accept on shape. The result\n // reports verified: false so callers can decide what to do.\n anchor = { status: 'confirmed', blockHeight: h, blockHash: hash, verified };\n }\n\n // 6. Content bytes check (if supplied).\n if (input.content) {\n const actualHash = 'sha256:' + hexEncode(sha256(input.content));\n if (actualHash !== env.content.hash) {\n return err('E_BAD_CONTENT', `content hash (${actualHash}) does not match envelope.content.hash (${env.content.hash})`);\n }\n }\n\n return {\n ok: true,\n envelope: env,\n canonicalMessage: reconstructedMessage,\n id: env.id,\n anchor,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction checkShape(env: StampEnvelope): VerifyResult | null {\n if (env.kind !== 'stamp') return err('E_MALFORMED', 'envelope.kind must be \"stamp\"');\n if (!env.id || typeof env.id !== 'string' || !/^[0-9a-f]{64}$/.test(env.id)) {\n return err('E_MALFORMED', 'envelope.id must be 64 lowercase hex chars');\n }\n if (!env.content || typeof env.content.hash !== 'string' || !env.content.hash.startsWith('sha256:')) {\n return err('E_MALFORMED', 'envelope.content.hash must start with \"sha256:\"');\n }\n if (!Number.isInteger(env.content.length) || env.content.length < 0) {\n return err('E_MALFORMED', 'envelope.content.length must be a non-negative integer');\n }\n if (typeof env.content.mime !== 'string' || env.content.mime.length === 0) {\n return err('E_MALFORMED', 'envelope.content.mime must be a non-empty string');\n }\n if (!env.signer || typeof env.signer.address !== 'string' || env.signer.alg !== 'bip322') {\n return err('E_MALFORMED', 'envelope.signer invalid');\n }\n if (env.signer.address !== env.sig.pubkey) {\n return err('E_MALFORMED', 'envelope.signer.address must equal envelope.sig.pubkey');\n }\n if (typeof env.signed_at !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(env.signed_at)) {\n return err('E_MALFORMED', 'envelope.signed_at must be ISO 8601 UTC ending in Z');\n }\n if (!env.sig || env.sig.alg !== 'bip322' || typeof env.sig.value !== 'string') {\n return err('E_MALFORMED', 'envelope.sig invalid');\n }\n return null;\n}\n\nfunction err(code: VerifyErrorCode, message: string): VerifyResult {\n return { ok: false, code, message };\n}\n\nfunction isoUtcSeconds(d: Date): string {\n // Drop milliseconds; SPEC allows ms-precision but we default to seconds\n // for shorter wallet-signing prompts. Callers needing millisecond\n // precision can pass a pre-formatted ISO string via a future field.\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n"]}
{"version":3,"sources":["../src/types.ts","../src/canonical.ts","../src/stamp.ts"],"names":["sha256"],"mappings":";;;;;AAEO,IAAM,gBAAA,GAAmB;ACmBzB,SAAS,iBAAiB,KAAA,EAAsC;AAInE,EAAA,OAAO;AAAA,IACH,aAAA;AAAA,IACA,CAAA,SAAA,EAAY,MAAM,OAAO,CAAA,CAAA;AAAA,IACzB,CAAA,cAAA,EAAiB,MAAM,YAAY,CAAA,CAAA;AAAA,IACnC,CAAA,gBAAA,EAAmB,MAAM,cAAc,CAAA,CAAA;AAAA,IACvC,CAAA,cAAA,EAAiB,MAAM,YAAY,CAAA,CAAA;AAAA,IACnC,CAAA,WAAA,EAAc,MAAM,SAAS,CAAA;AAAA,GACjC,CAAE,KAAK,IAAI,CAAA;AACf;AAUO,SAAS,uBAAuB,KAAA,EAEH;AAIhC,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,gBAAgB,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,EAAG;AACvD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,+DAAA,EAAgE;AAAA,EAChG;AACA,EAAA,IAAI,CAAC,uBAAA,CAAwB,IAAA,CAAK,KAAA,CAAM,YAAY,CAAA,EAAG;AACnD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,iDAAA,EAAkD;AAAA,EAClF;AACA,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,KAAA,CAAM,cAAc,CAAA,IAAK,KAAA,CAAM,iBAAiB,CAAA,EAAG;AACrE,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,+CAAA,EAAgD;AAAA,EAChF;AAIA,EAAA,IAAI,CAAC,KAAA,CAAM,YAAA,IAAgB,cAAc,IAAA,CAAK,KAAA,CAAM,YAAY,CAAA,EAAG;AAC/D,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,sDAAA,EAAuD;AAAA,EACvF;AACA,EAAA,IAAI,CAAC,gDAAA,CAAiD,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AACzE,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,4CAAA,EAA6C;AAAA,EAC7E;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AACtB;AAEO,SAAS,sBAAsB,KAAA,EAA0C;AAC5E,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,gBAAA,CAAiB,KAAK,CAAC,CAAA;AAC3D;AAMO,SAAS,kBAAkB,KAAA,EAAsC;AACpE,EAAA,MAAM,KAAA,GAAQ,sBAAsB,KAAK,CAAA;AACzC,EAAA,OAAO,SAAA,CAAUA,aAAA,CAAO,KAAK,CAAC,CAAA;AAClC;AAcO,SAAS,qBAAqB,GAAA,EAA4B;AAC7D,EAAA,OAAO,aAAa,GAA2B,CAAA;AACnD;AAEO,SAAS,uBAAuB,GAAA,EAAgC;AACnE,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,OAAO,YAAA,CAAa,GAA2B,IAAI,IAAI,CAAA;AACpF;AAEO,SAAS,aAAa,KAAA,EAA0B;AACnD,EAAA,OAAO,OAAO,KAAK,CAAA;AACvB;AAEA,SAAS,OAAO,CAAA,EAAsB;AAClC,EAAA,IAAI,CAAA,KAAM,MAAM,OAAO,MAAA;AACvB,EAAA,IAAI,OAAO,CAAA,KAAM,SAAA,EAAW,OAAO,IAAI,MAAA,GAAS,OAAA;AAChD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,aAAa,CAAC,CAAA;AAChD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,aAAa,CAAC,CAAA;AAChD,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG;AAClB,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,KAAA,MAAW,QAAQ,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAC7C,IAAA,OAAO,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA;AAAA,EACnC;AACA,EAAA,IAAI,OAAO,MAAM,QAAA,EAAU;AACvB,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAC,EAAE,IAAA,EAAK;AACjC,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AAClB,MAAA,MAAM,KAAA,GAAQ,EAAE,CAAC,CAAA;AACjB,MAAA,IAAI,UAAU,MAAA,EAAW;AACzB,MAAA,KAAA,CAAM,KAAK,YAAA,CAAa,CAAC,IAAI,GAAA,GAAM,MAAA,CAAO,KAAkB,CAAC,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA;AAAA,EACnC;AACA,EAAA,MAAM,IAAI,KAAA,CAAM,oCAAA,GAAuC,OAAO,CAAC,CAAA;AACnE;AAEA,SAAS,aAAa,CAAA,EAAmB;AACrC,EAAA,IAAI,CAAC,OAAO,QAAA,CAAS,CAAC,GAAG,MAAM,IAAI,MAAM,4CAA4C,CAAA;AACrF,EAAA,IAAI,MAAA,CAAO,EAAA,CAAG,CAAA,EAAG,EAAE,GAAG,OAAO,GAAA;AAC7B,EAAA,IAAI,MAAA,CAAO,UAAU,CAAC,CAAA,IAAK,KAAK,GAAA,CAAI,CAAC,IAAI,IAAA,EAAM;AAC3C,IAAA,OAAO,OAAO,CAAC,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,IAAA,CAAK,UAAU,CAAC,CAAA;AAC3B;AAEA,IAAM,YAAA,GAAuC;AAAA,EACzC,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,GAAA,EAAM;AACV,CAAA;AAEA,SAAS,aAAa,CAAA,EAAmB;AACrC,EAAA,IAAI,GAAA,GAAM,GAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,MAAM,EAAA,GAAK,EAAE,CAAC,CAAA;AACd,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,UAAA,CAAW,CAAC,CAAA;AAC5B,IAAA,IAAI,YAAA,CAAa,EAAE,CAAA,EAAG;AAClB,MAAA,GAAA,IAAO,aAAa,EAAE,CAAA;AAAA,IAC1B,CAAA,MAAA,IAAW,OAAO,EAAA,EAAM;AACpB,MAAA,GAAA,IAAO,QAAQ,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,IACpD,CAAA,MAAO;AACH,MAAA,GAAA,IAAO,EAAA;AAAA,IACX;AAAA,EACJ;AACA,EAAA,GAAA,IAAO,GAAA;AACP,EAAA,OAAO,GAAA;AACX;AAMA,IAAM,SAAA,GAAY,kBAAA;AAEX,SAAS,UAAU,KAAA,EAA2B;AACjD,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACnC,IAAA,MAAM,CAAA,GAAI,MAAM,CAAC,CAAA;AACjB,IAAA,GAAA,IAAO,UAAW,CAAA,IAAK,CAAA,GAAK,EAAI,CAAA,GAAK,SAAA,CAAU,IAAI,EAAI,CAAA;AAAA,EAC3D;AACA,EAAA,OAAO,GAAA;AACX;AAEO,SAAS,UAAU,KAAA,EAA2B;AACjD,EAAA,OAAO,SAAA,CAAUA,aAAA,CAAO,KAAK,CAAC,CAAA;AAClC;AC5JO,SAAS,YAAY,KAAA,EAAqD;AAC7E,EAAA,OAAO;AAAA,IACH,IAAA,EAAM,SAAA,GAAY,SAAA,CAAUA,aAAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IACzC,QAAQ,KAAA,CAAM;AAAA,GAClB;AACJ;AAEO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAElC,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAChD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AAAA,EAChB;AACJ;AAOA,eAAsB,MAAM,KAAA,EAA2C;AAGnE,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,KAAA,CAAM,mBAAmB,UAAA,EAAY;AACrC,IAAA,WAAA,GAAc,SAAA,GAAY,SAAA,CAAUA,aAAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AACzD,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,UAAA;AAAA,EAClC,CAAA,MAAO;AAGH,IAAA,MAAM,CAAA,GAAI,MAAM,OAAA,CAAQ,IAAA;AACxB,IAAA,IAAI,CAAC,EAAE,UAAA,CAAW,SAAS,KAAK,CAAA,CAAE,MAAA,KAAW,SAAA,CAAU,MAAA,GAAS,EAAA,EAAI;AAChE,MAAA,MAAM,IAAI,UAAA;AAAA,QACN,aAAA;AAAA,QACA;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,WAAA,GAAc,CAAA;AACd,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,MAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,aAAa,CAAA,IAAK,gBAAgB,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,UAAA,CAAW,aAAA,EAAe,+CAA+C,CAAA;AAAA,IACvF;AAAA,EACJ;AAEA,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAE3D,EAAA,MAAM,KAAA,GAAQ;AAAA,IACV,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,IACtB,YAAA,EAAc,WAAA;AAAA,IACd,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAc,KAAA,CAAM,IAAA;AAAA,IACpB,SAAA,EAAW;AAAA,GACf;AAEA,EAAA,MAAM,CAAA,GAAI,uBAAuB,KAAK,CAAA;AACtC,EAAA,IAAI,CAAC,EAAE,EAAA,EAAI;AAKP,IAAA,MAAM,IAAI,UAAA,CAAW,aAAA,EAAe,CAAA,CAAE,MAAM,CAAA;AAAA,EAChD;AAEA,EAAA,MAAM,EAAA,GAAK,kBAAkB,KAAK,CAAA;AAMlC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,CAAO,YAAY,EAAE,CAAA;AAElD,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC5B,CAAA,EAAG,gBAAA;AAAA,IACH,IAAA,EAAM,OAAA;AAAA,IACN,EAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,MAAA,EAAQ,aAAA;AAAA,MACR,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,GAAA,EAAK,MAAM,GAAA,IAAO;AAAA,KACtB;AAAA,IACA,MAAA,EAAQ;AAAA,MACJ,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,MACtB,GAAA,EAAK;AAAA,KACT;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,KAAA,EAAO,MAAM,KAAA,IAAS,IAAA;AAAA,IACtB,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK;AAAA,MACD,GAAA,EAAK,QAAA;AAAA,MACL,MAAA,EAAQ,MAAM,MAAA,CAAO,OAAA;AAAA,MACrB,KAAA,EAAO;AAAA;AACX,GACJ;AAEA,EAAA,OAAO,QAAA;AACX;AAOA,eAAsB,OAAO,KAAA,EAA2C;AACpE,EAAA,MAAM,MAAM,KAAA,CAAM,QAAA;AAGlB,EAAA,IAAI,GAAA,CAAI,MAAM,gBAAA,EAAkB;AAC5B,IAAA,OAAO,GAAA,CAAI,uBAAA,EAAyB,CAAA,iBAAA,EAAoB,GAAA,CAAI,CAAC,CAAA,qCAAA,CAAuC,CAAA;AAAA,EACxG;AAGA,EAAA,MAAM,KAAA,GAAQ,WAAW,GAAG,CAAA;AAC5B,EAAA,IAAI,OAAO,OAAO,KAAA;AAGlB,EAAA,MAAM,KAAA,GAAQ;AAAA,IACV,OAAA,EAAS,IAAI,MAAA,CAAO,OAAA;AAAA,IACpB,YAAA,EAAc,IAAI,OAAA,CAAQ,IAAA;AAAA,IAC1B,cAAA,EAAgB,IAAI,OAAA,CAAQ,MAAA;AAAA,IAC5B,YAAA,EAAc,IAAI,OAAA,CAAQ,IAAA;AAAA,IAC1B,WAAW,GAAA,CAAI;AAAA,GACnB;AACA,EAAA,MAAM,oBAAA,GAAuB,iBAAiB,KAAK,CAAA;AACnD,EAAA,MAAM,kBAAkB,SAAA,CAAUA,aAAAA,CAAO,qBAAA,CAAsB,KAAK,CAAC,CAAC,CAAA;AACtE,EAAA,IAAI,eAAA,KAAoB,IAAI,EAAA,EAAI;AAC5B,IAAA,OAAO,IAAI,UAAA,EAAY,CAAA,kBAAA,EAAqB,eAAe,CAAA,8BAAA,EAAiC,GAAA,CAAI,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,EACzG;AAGA,EAAA,IAAI,CAAC,MAAM,yBAAA,EAA2B;AAClC,IAAA,IAAI,CAAC,MAAM,YAAA,EAAc;AACrB,MAAA,OAAO,GAAA,CAAI,aAAa,8BAA8B,CAAA;AAAA,IAC1D;AACA,IAAA,MAAM,EAAA,GAAK,MAAM,KAAA,CAAM,YAAA,CAAa,GAAA,CAAI,EAAA,EAAI,GAAA,CAAI,GAAA,CAAI,KAAA,EAAO,GAAA,CAAI,MAAA,CAAO,OAAO,CAAA;AAC7E,IAAA,IAAI,CAAC,EAAA,EAAI,OAAO,GAAA,CAAI,aAAa,kCAAkC,CAAA;AAAA,EACvE;AAGA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,GAAA,CAAI,QAAQ,IAAA,EAAM;AAClB,IAAA,MAAA,GAAS,EAAE,QAAQ,MAAA,EAAO;AAAA,EAC9B,CAAA,MAAA,IAAW,GAAA,CAAI,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACrC,IAAA,MAAA,GAAS,EAAE,QAAQ,SAAA,EAAU;AAAA,EACjC,CAAA,MAAO;AAEH,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,YAAA;AAClB,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,UAAA;AACrB,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,IAAA,KAAS,IAAA,EAAM;AAC7B,MAAA,OAAO,GAAA,CAAI,gBAAgB,wDAAwD,CAAA;AAAA,IACvF;AACA,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,IAAI,MAAM,eAAA,EAAiB;AACvB,MAAA,IAAI;AACA,QAAA,QAAA,GAAW,MAAM,MAAM,eAAA,CAAgB,GAAA,CAAI,IAAI,KAAA,EAAO,CAAA,EAAG,IAAA,EAAM,GAAA,CAAI,EAAE,CAAA;AAAA,MACzE,SAAS,CAAA,EAAG;AACR,QAAA,OAAO,GAAA,CAAI,cAAA,EAAgB,CAAA,uBAAA,EAA2B,CAAA,CAAY,OAAO,CAAA,CAAE,CAAA;AAAA,MAC/E;AACA,MAAA,IAAI,CAAC,QAAA,EAAU,OAAO,GAAA,CAAI,gBAAgB,0DAA0D,CAAA;AAAA,IACxG;AAGA,IAAA,MAAA,GAAS,EAAE,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,SAAA,EAAW,MAAM,QAAA,EAAS;AAAA,EAC9E;AAGA,EAAA,IAAI,MAAM,OAAA,EAAS;AACf,IAAA,MAAM,aAAa,SAAA,GAAY,SAAA,CAAUA,aAAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AAC9D,IAAA,IAAI,UAAA,KAAe,GAAA,CAAI,OAAA,CAAQ,IAAA,EAAM;AACjC,MAAA,OAAO,GAAA,CAAI,iBAAiB,CAAA,cAAA,EAAiB,UAAU,2CAA2C,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACzH;AAAA,EACJ;AAEA,EAAA,OAAO;AAAA,IACH,EAAA,EAAI,IAAA;AAAA,IACJ,QAAA,EAAU,GAAA;AAAA,IACV,gBAAA,EAAkB,oBAAA;AAAA,IAClB,IAAI,GAAA,CAAI,EAAA;AAAA,IACR;AAAA,GACJ;AACJ;AAIA,SAAS,WAAW,GAAA,EAAyC;AACzD,EAAA,IAAI,IAAI,IAAA,KAAS,OAAA,EAAS,OAAO,GAAA,CAAI,eAAe,+BAA+B,CAAA;AACnF,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,IAAM,OAAO,GAAA,CAAI,EAAA,KAAO,QAAA,IAAY,CAAC,gBAAA,CAAiB,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,EAAG;AACzE,IAAA,OAAO,GAAA,CAAI,eAAe,4CAA4C,CAAA;AAAA,EAC1E;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,OAAA,IAAW,OAAO,IAAI,OAAA,CAAQ,IAAA,KAAS,QAAA,IAAY,CAAC,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,SAAS,CAAA,EAAG;AACjG,IAAA,OAAO,GAAA,CAAI,eAAe,iDAAiD,CAAA;AAAA,EAC/E;AACA,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,OAAA,CAAQ,MAAM,CAAA,IAAK,GAAA,CAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACjE,IAAA,OAAO,GAAA,CAAI,eAAe,wDAAwD,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,OAAO,IAAI,OAAA,CAAQ,IAAA,KAAS,YAAY,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AACvE,IAAA,OAAO,GAAA,CAAI,eAAe,kDAAkD,CAAA;AAAA,EAChF;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,MAAA,IAAU,OAAO,GAAA,CAAI,MAAA,CAAO,OAAA,KAAY,QAAA,IAAY,GAAA,CAAI,MAAA,CAAO,GAAA,KAAQ,QAAA,EAAU;AACtF,IAAA,OAAO,GAAA,CAAI,eAAe,yBAAyB,CAAA;AAAA,EACvD;AACA,EAAA,IAAI,GAAA,CAAI,MAAA,CAAO,OAAA,KAAY,GAAA,CAAI,IAAI,MAAA,EAAQ;AACvC,IAAA,OAAO,GAAA,CAAI,eAAe,wDAAwD,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,OAAO,IAAI,SAAA,KAAc,QAAA,IAAY,CAAC,gDAAA,CAAiD,IAAA,CAAK,GAAA,CAAI,SAAS,CAAA,EAAG;AAC5G,IAAA,OAAO,GAAA,CAAI,eAAe,qDAAqD,CAAA;AAAA,EACnF;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,GAAA,CAAI,GAAA,KAAQ,QAAA,IAAY,OAAO,GAAA,CAAI,GAAA,CAAI,KAAA,KAAU,QAAA,EAAU;AAC3E,IAAA,OAAO,GAAA,CAAI,eAAe,sBAAsB,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,GAAA,CAAI,MAAuB,OAAA,EAA+B;AAC/D,EAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,IAAA,EAAM,OAAA,EAAQ;AACtC;AAEA,SAAS,cAAc,CAAA,EAAiB;AAIpC,EAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,WAAW,GAAG,CAAA;AACjD","file":"index.js","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * `envelopeId` is the 64-char hex envelope id (equals the 32-byte digest committed\n * by the OTS proof, hex-encoded). Passing it explicitly lets the verifier avoid\n * the broken \"look up the digest from the block hash\" dance the old adapter\n * tried to do.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (\n proofB64: string,\n blockHeight: number,\n blockHash: string,\n envelopeId: string\n ) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n","// Canonical message + RFC 8785 JSON canonicalization for OC Stamp.\n//\n// Two distinct canonicalizers live here:\n//\n// 1. canonicalMessage(input) — the exact byte sequence the signer signs via\n// BIP-322. LF-separated, no trailing LF after signed_at. SPEC §3.1.\n//\n// 2. canonicalizeEnvelope(env) — RFC 8785 JSON canonicalization of the wire\n// envelope, used for stable serialization / equality checks. SPEC §5.\n//\n// Only (1) participates in the id derivation. (2) is for test-vector byte\n// equality and any future operation that needs deterministic envelope bytes.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport type { CanonicalMessageInput, StampEnvelope } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonical message (SPEC §3.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalMessage(input: CanonicalMessageInput): string {\n // The domain separator `oc-stamp:v1` commits the whole signing ceremony to\n // this spec version. Future incompatible changes MUST bump the version\n // number here so a v1 signature can never be replayed against a v2 domain.\n return [\n 'oc-stamp:v1',\n `address: ${input.address}`,\n `content_hash: ${input.content_hash}`,\n `content_length: ${input.content_length}`,\n `content_mime: ${input.content_mime}`,\n `signed_at: ${input.signed_at}`,\n ].join('\\n');\n}\n\n/**\n * Check whether a CanonicalMessageInput is well-formed per the spec's field\n * rules (§3, §4). Returns `{ok: true}` if every field is valid, or\n * `{ok: false, reason}` pointing at the first violation.\n *\n * Called by `stamp()` before producing the canonical bytes. Exposed for\n * UIs that want to preview validity without throwing.\n */\nexport function validateCanonicalInput(input: CanonicalMessageInput):\n | { ok: true }\n | { ok: false, reason: string } {\n // No control chars or whitespace breaks in fields — any of those would\n // invalidate the 6-line structure and produce an id nobody else can\n // reconstruct. The whole canonical message is ASCII-safe by design.\n if (!input.address || /[\\s\\x00-\\x1f]/.test(input.address)) {\n return { ok: false, reason: 'address must be non-empty with no whitespace or control chars' };\n }\n if (!/^sha256:[0-9a-f]{64}$/.test(input.content_hash)) {\n return { ok: false, reason: 'content_hash must match /^sha256:[0-9a-f]{64}$/' };\n }\n if (!Number.isInteger(input.content_length) || input.content_length < 0) {\n return { ok: false, reason: 'content_length must be a non-negative integer' };\n }\n // MIME per RFC 6838: \"type/subtype[+suffix][;params]\". We accept anything\n // that's non-empty and control-char-free; fine-grained parsing is out of\n // scope and would reject legitimate media types.\n if (!input.content_mime || /[\\x00-\\x1f]/.test(input.content_mime)) {\n return { ok: false, reason: 'content_mime must be non-empty with no control chars' };\n }\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(input.signed_at)) {\n return { ok: false, reason: 'signed_at must be ISO 8601 UTC ending in Z' };\n }\n return { ok: true };\n}\n\nexport function canonicalMessageBytes(input: CanonicalMessageInput): Uint8Array {\n return new TextEncoder().encode(canonicalMessage(input));\n}\n\n/**\n * Compute the envelope id as lowercase hex of sha256(canonical_message_bytes).\n * Pure function — the single source of truth for identity derivation.\n */\nexport function computeEnvelopeId(input: CanonicalMessageInput): string {\n const bytes = canonicalMessageBytes(input);\n return hexEncode(sha256(bytes));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Envelope canonicalization (SPEC §5; RFC 8785)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport function canonicalizeEnvelope(env: StampEnvelope): string {\n return canonicalize(env as unknown as JsonValue);\n}\n\nexport function canonicalEnvelopeBytes(env: StampEnvelope): Uint8Array {\n return new TextEncoder().encode(canonicalize(env as unknown as JsonValue) + '\\n');\n}\n\nexport function canonicalize(value: JsonValue): string {\n return encode(value);\n}\n\nfunction encode(v: JsonValue): string {\n if (v === null) return 'null';\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (typeof v === 'number') return encodeNumber(v);\n if (typeof v === 'string') return encodeString(v);\n if (Array.isArray(v)) {\n const parts: string[] = [];\n for (const item of v) parts.push(encode(item));\n return '[' + parts.join(',') + ']';\n }\n if (typeof v === 'object') {\n const keys = Object.keys(v).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const inner = v[k];\n if (inner === undefined) continue;\n parts.push(encodeString(k) + ':' + encode(inner as JsonValue));\n }\n return '{' + parts.join(',') + '}';\n }\n throw new Error('cannot canonicalize value of type ' + typeof v);\n}\n\nfunction encodeNumber(n: number): string {\n if (!Number.isFinite(n)) throw new Error('non-finite number not JSON-canonicalizable');\n if (Object.is(n, -0)) return '0';\n if (Number.isInteger(n) && Math.abs(n) < 1e21) {\n return String(n);\n }\n return JSON.stringify(n);\n}\n\nconst ESCAPE_TABLE: Record<string, string> = {\n '\\\\': '\\\\\\\\',\n '\"': '\\\\\"',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n};\n\nfunction encodeString(s: string): string {\n let out = '\"';\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const code = ch.charCodeAt(0);\n if (ESCAPE_TABLE[ch]) {\n out += ESCAPE_TABLE[ch];\n } else if (code < 0x20) {\n out += '\\\\u' + code.toString(16).padStart(4, '0');\n } else {\n out += ch;\n }\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Small utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst HEX_TABLE = '0123456789abcdef';\n\nexport function hexEncode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n out += HEX_TABLE[(b >> 4) & 0x0f]! + HEX_TABLE[b & 0x0f]!;\n }\n return out;\n}\n\nexport function sha256Hex(bytes: Uint8Array): string {\n return hexEncode(sha256(bytes));\n}\n","// stamp() and verify() — reference implementation of OC Stamp v1. SPEC §4, §8.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport {\n canonicalMessage,\n canonicalMessageBytes,\n computeEnvelopeId,\n hexEncode,\n validateCanonicalInput,\n} from './canonical.js';\nimport {\n ENVELOPE_VERSION,\n type StampEnvelope,\n type StampInput,\n type VerifyErrorCode,\n type VerifyInput,\n type VerifyOk,\n type VerifyResult,\n} from './types.js';\n\n/**\n * Compute `{hash, length}` for raw bytes. Convenience over the common\n * `sha256(bytes)` pattern that prefixes with `sha256:` and returns lowercase\n * hex. Pass the result to `stamp({ content: hashContent(bytes), mime })` when\n * you don't want stamp() to hash the bytes itself.\n */\nexport function hashContent(bytes: Uint8Array): { hash: string; length: number } {\n return {\n hash: 'sha256:' + hexEncode(sha256(bytes)),\n length: bytes.byteLength,\n };\n}\n\nexport class StampError extends Error {\n code: VerifyErrorCode;\n constructor(code: VerifyErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = 'StampError';\n }\n}\n\n/**\n * Build and sign a stamp envelope. Returns the envelope with sig populated but\n * ots left null — OTS calendar submission is a separate step (see\n * @orangecheck/stamp-ots).\n */\nexport async function stamp(input: StampInput): Promise<StampEnvelope> {\n // Resolve content hash + length. Two modes: raw bytes (we hash) or\n // pre-computed {hash, length} (we trust the caller).\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n // Pre-computed. Caller must have used SHA-256 and declared length correctly.\n // Guard against the easy typos.\n const h = input.content.hash;\n if (!h.startsWith('sha256:') || h.length !== 'sha256:'.length + 64) {\n throw new StampError(\n 'E_MALFORMED',\n 'content.hash must be \"sha256:\" + 64 lowercase hex chars'\n );\n }\n contentHash = h;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new StampError('E_MALFORMED', 'content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canon = {\n address: input.signer.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n };\n\n const v = validateCanonicalInput(canon);\n if (!v.ok) {\n // Validation rejects inputs that would silently break canonical-message\n // reconstructability downstream (whitespace in address, wrong hash\n // prefix, floats in length, etc.). Catching them here prevents a\n // signed envelope that no verifier can accept.\n throw new StampError('E_MALFORMED', v.reason);\n }\n\n const id = computeEnvelopeId(canon);\n\n // BIP-322 signs the ASCII hex representation of id. Hex is chosen (not\n // raw bytes) so wallets that render the signed message to the user show\n // something legible. The id itself commits to every field in the canonical\n // message so this is a transitive commitment to the full signing ceremony.\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: StampEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'stamp',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: {\n address: input.signer.address,\n alg: 'bip322',\n },\n signed_at: signedAt,\n stake: input.stake ?? null,\n ots: null,\n sig: {\n alg: 'bip322',\n pubkey: input.signer.address,\n value: sigValue,\n },\n };\n\n return envelope;\n}\n\n/**\n * Verify a stamp envelope. See SPEC §8 for the full algorithm. Returns a\n * structured result; callers can match on `result.ok` and drill into\n * `result.code` for the specific failure.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const env = input.envelope;\n\n // 1. Version check.\n if (env.v !== ENVELOPE_VERSION) {\n return err('E_UNSUPPORTED_VERSION', `envelope version ${env.v} not supported by this implementation`);\n }\n\n // 2. Shape check.\n const shape = checkShape(env);\n if (shape) return shape;\n\n // 3. Canonical message reconstruction + id check.\n const canon = {\n address: env.signer.address,\n content_hash: env.content.hash,\n content_length: env.content.length,\n content_mime: env.content.mime,\n signed_at: env.signed_at,\n };\n const reconstructedMessage = canonicalMessage(canon);\n const reconstructedId = hexEncode(sha256(canonicalMessageBytes(canon)));\n if (reconstructedId !== env.id) {\n return err('E_BAD_ID', `reconstructed id (${reconstructedId}) does not match envelope.id (${env.id})`);\n }\n\n // 4. Signature verify.\n if (!input.skipSignatureVerification) {\n if (!input.verifyBip322) {\n return err('E_BAD_SIG', 'no BIP-322 verifier supplied');\n }\n const ok = await input.verifyBip322(env.id, env.sig.value, env.signer.address);\n if (!ok) return err('E_BAD_SIG', 'BIP-322 signature did not verify');\n }\n\n // 5. Anchor verify.\n let anchor: VerifyOk['anchor'];\n if (env.ots === null) {\n anchor = { status: 'none' };\n } else if (env.ots.status === 'pending') {\n anchor = { status: 'pending' };\n } else {\n // confirmed\n const h = env.ots.block_height;\n const hash = env.ots.block_hash;\n if (h === null || hash === null) {\n return err('E_BAD_ANCHOR', 'confirmed OTS proof missing block_height or block_hash');\n }\n let verified = false;\n if (input.verifyOtsAnchor) {\n try {\n verified = await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id);\n } catch (e) {\n return err('E_BAD_ANCHOR', `anchor verifier threw: ${(e as Error).message}`);\n }\n if (!verified) return err('E_BAD_ANCHOR', 'OTS proof did not chain to declared Bitcoin block header');\n }\n // If no anchor verifier was supplied, we accept on shape. The result\n // reports verified: false so callers can decide what to do.\n anchor = { status: 'confirmed', blockHeight: h, blockHash: hash, verified };\n }\n\n // 6. Content bytes check (if supplied).\n if (input.content) {\n const actualHash = 'sha256:' + hexEncode(sha256(input.content));\n if (actualHash !== env.content.hash) {\n return err('E_BAD_CONTENT', `content hash (${actualHash}) does not match envelope.content.hash (${env.content.hash})`);\n }\n }\n\n return {\n ok: true,\n envelope: env,\n canonicalMessage: reconstructedMessage,\n id: env.id,\n anchor,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction checkShape(env: StampEnvelope): VerifyResult | null {\n if (env.kind !== 'stamp') return err('E_MALFORMED', 'envelope.kind must be \"stamp\"');\n if (!env.id || typeof env.id !== 'string' || !/^[0-9a-f]{64}$/.test(env.id)) {\n return err('E_MALFORMED', 'envelope.id must be 64 lowercase hex chars');\n }\n if (!env.content || typeof env.content.hash !== 'string' || !env.content.hash.startsWith('sha256:')) {\n return err('E_MALFORMED', 'envelope.content.hash must start with \"sha256:\"');\n }\n if (!Number.isInteger(env.content.length) || env.content.length < 0) {\n return err('E_MALFORMED', 'envelope.content.length must be a non-negative integer');\n }\n if (typeof env.content.mime !== 'string' || env.content.mime.length === 0) {\n return err('E_MALFORMED', 'envelope.content.mime must be a non-empty string');\n }\n if (!env.signer || typeof env.signer.address !== 'string' || env.signer.alg !== 'bip322') {\n return err('E_MALFORMED', 'envelope.signer invalid');\n }\n if (env.signer.address !== env.sig.pubkey) {\n return err('E_MALFORMED', 'envelope.signer.address must equal envelope.sig.pubkey');\n }\n if (typeof env.signed_at !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(env.signed_at)) {\n return err('E_MALFORMED', 'envelope.signed_at must be ISO 8601 UTC ending in Z');\n }\n if (!env.sig || env.sig.alg !== 'bip322' || typeof env.sig.value !== 'string') {\n return err('E_MALFORMED', 'envelope.sig invalid');\n }\n return null;\n}\n\nfunction err(code: VerifyErrorCode, message: string): VerifyResult {\n return { ok: false, code, message };\n}\n\nfunction isoUtcSeconds(d: Date): string {\n // Drop milliseconds; SPEC allows ms-precision but we default to seconds\n // for shorter wallet-signing prompts. Callers needing millisecond\n // precision can pass a pre-formatted ISO string via a future field.\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n"]}

@@ -228,3 +228,3 @@ import { sha256 } from '@noble/hashes/sha256';

try {
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash);
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id);
} catch (e) {

@@ -231,0 +231,0 @@ return err("E_BAD_ANCHOR", `anchor verifier threw: ${e.message}`);

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts","../src/canonical.ts","../src/stamp.ts"],"names":["sha256"],"mappings":"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","file":"index.mjs","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n","// Canonical message + RFC 8785 JSON canonicalization for OC Stamp.\n//\n// Two distinct canonicalizers live here:\n//\n// 1. canonicalMessage(input) — the exact byte sequence the signer signs via\n// BIP-322. LF-separated, no trailing LF after signed_at. SPEC §3.1.\n//\n// 2. canonicalizeEnvelope(env) — RFC 8785 JSON canonicalization of the wire\n// envelope, used for stable serialization / equality checks. SPEC §5.\n//\n// Only (1) participates in the id derivation. (2) is for test-vector byte\n// equality and any future operation that needs deterministic envelope bytes.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport type { CanonicalMessageInput, StampEnvelope } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonical message (SPEC §3.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalMessage(input: CanonicalMessageInput): string {\n // The domain separator `oc-stamp:v1` commits the whole signing ceremony to\n // this spec version. Future incompatible changes MUST bump the version\n // number here so a v1 signature can never be replayed against a v2 domain.\n return [\n 'oc-stamp:v1',\n `address: ${input.address}`,\n `content_hash: ${input.content_hash}`,\n `content_length: ${input.content_length}`,\n `content_mime: ${input.content_mime}`,\n `signed_at: ${input.signed_at}`,\n ].join('\\n');\n}\n\n/**\n * Check whether a CanonicalMessageInput is well-formed per the spec's field\n * rules (§3, §4). Returns `{ok: true}` if every field is valid, or\n * `{ok: false, reason}` pointing at the first violation.\n *\n * Called by `stamp()` before producing the canonical bytes. Exposed for\n * UIs that want to preview validity without throwing.\n */\nexport function validateCanonicalInput(input: CanonicalMessageInput):\n | { ok: true }\n | { ok: false, reason: string } {\n // No control chars or whitespace breaks in fields — any of those would\n // invalidate the 6-line structure and produce an id nobody else can\n // reconstruct. The whole canonical message is ASCII-safe by design.\n if (!input.address || /[\\s\\x00-\\x1f]/.test(input.address)) {\n return { ok: false, reason: 'address must be non-empty with no whitespace or control chars' };\n }\n if (!/^sha256:[0-9a-f]{64}$/.test(input.content_hash)) {\n return { ok: false, reason: 'content_hash must match /^sha256:[0-9a-f]{64}$/' };\n }\n if (!Number.isInteger(input.content_length) || input.content_length < 0) {\n return { ok: false, reason: 'content_length must be a non-negative integer' };\n }\n // MIME per RFC 6838: \"type/subtype[+suffix][;params]\". We accept anything\n // that's non-empty and control-char-free; fine-grained parsing is out of\n // scope and would reject legitimate media types.\n if (!input.content_mime || /[\\x00-\\x1f]/.test(input.content_mime)) {\n return { ok: false, reason: 'content_mime must be non-empty with no control chars' };\n }\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(input.signed_at)) {\n return { ok: false, reason: 'signed_at must be ISO 8601 UTC ending in Z' };\n }\n return { ok: true };\n}\n\nexport function canonicalMessageBytes(input: CanonicalMessageInput): Uint8Array {\n return new TextEncoder().encode(canonicalMessage(input));\n}\n\n/**\n * Compute the envelope id as lowercase hex of sha256(canonical_message_bytes).\n * Pure function — the single source of truth for identity derivation.\n */\nexport function computeEnvelopeId(input: CanonicalMessageInput): string {\n const bytes = canonicalMessageBytes(input);\n return hexEncode(sha256(bytes));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Envelope canonicalization (SPEC §5; RFC 8785)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport function canonicalizeEnvelope(env: StampEnvelope): string {\n return canonicalize(env as unknown as JsonValue);\n}\n\nexport function canonicalEnvelopeBytes(env: StampEnvelope): Uint8Array {\n return new TextEncoder().encode(canonicalize(env as unknown as JsonValue) + '\\n');\n}\n\nexport function canonicalize(value: JsonValue): string {\n return encode(value);\n}\n\nfunction encode(v: JsonValue): string {\n if (v === null) return 'null';\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (typeof v === 'number') return encodeNumber(v);\n if (typeof v === 'string') return encodeString(v);\n if (Array.isArray(v)) {\n const parts: string[] = [];\n for (const item of v) parts.push(encode(item));\n return '[' + parts.join(',') + ']';\n }\n if (typeof v === 'object') {\n const keys = Object.keys(v).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const inner = v[k];\n if (inner === undefined) continue;\n parts.push(encodeString(k) + ':' + encode(inner as JsonValue));\n }\n return '{' + parts.join(',') + '}';\n }\n throw new Error('cannot canonicalize value of type ' + typeof v);\n}\n\nfunction encodeNumber(n: number): string {\n if (!Number.isFinite(n)) throw new Error('non-finite number not JSON-canonicalizable');\n if (Object.is(n, -0)) return '0';\n if (Number.isInteger(n) && Math.abs(n) < 1e21) {\n return String(n);\n }\n return JSON.stringify(n);\n}\n\nconst ESCAPE_TABLE: Record<string, string> = {\n '\\\\': '\\\\\\\\',\n '\"': '\\\\\"',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n};\n\nfunction encodeString(s: string): string {\n let out = '\"';\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const code = ch.charCodeAt(0);\n if (ESCAPE_TABLE[ch]) {\n out += ESCAPE_TABLE[ch];\n } else if (code < 0x20) {\n out += '\\\\u' + code.toString(16).padStart(4, '0');\n } else {\n out += ch;\n }\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Small utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst HEX_TABLE = '0123456789abcdef';\n\nexport function hexEncode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n out += HEX_TABLE[(b >> 4) & 0x0f]! + HEX_TABLE[b & 0x0f]!;\n }\n return out;\n}\n\nexport function sha256Hex(bytes: Uint8Array): string {\n return hexEncode(sha256(bytes));\n}\n","// stamp() and verify() — reference implementation of OC Stamp v1. SPEC §4, §8.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport {\n canonicalMessage,\n canonicalMessageBytes,\n computeEnvelopeId,\n hexEncode,\n validateCanonicalInput,\n} from './canonical.js';\nimport {\n ENVELOPE_VERSION,\n type StampEnvelope,\n type StampInput,\n type VerifyErrorCode,\n type VerifyInput,\n type VerifyOk,\n type VerifyResult,\n} from './types.js';\n\n/**\n * Compute `{hash, length}` for raw bytes. Convenience over the common\n * `sha256(bytes)` pattern that prefixes with `sha256:` and returns lowercase\n * hex. Pass the result to `stamp({ content: hashContent(bytes), mime })` when\n * you don't want stamp() to hash the bytes itself.\n */\nexport function hashContent(bytes: Uint8Array): { hash: string; length: number } {\n return {\n hash: 'sha256:' + hexEncode(sha256(bytes)),\n length: bytes.byteLength,\n };\n}\n\nexport class StampError extends Error {\n code: VerifyErrorCode;\n constructor(code: VerifyErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = 'StampError';\n }\n}\n\n/**\n * Build and sign a stamp envelope. Returns the envelope with sig populated but\n * ots left null — OTS calendar submission is a separate step (see\n * @orangecheck/stamp-ots).\n */\nexport async function stamp(input: StampInput): Promise<StampEnvelope> {\n // Resolve content hash + length. Two modes: raw bytes (we hash) or\n // pre-computed {hash, length} (we trust the caller).\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n // Pre-computed. Caller must have used SHA-256 and declared length correctly.\n // Guard against the easy typos.\n const h = input.content.hash;\n if (!h.startsWith('sha256:') || h.length !== 'sha256:'.length + 64) {\n throw new StampError(\n 'E_MALFORMED',\n 'content.hash must be \"sha256:\" + 64 lowercase hex chars'\n );\n }\n contentHash = h;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new StampError('E_MALFORMED', 'content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canon = {\n address: input.signer.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n };\n\n const v = validateCanonicalInput(canon);\n if (!v.ok) {\n // Validation rejects inputs that would silently break canonical-message\n // reconstructability downstream (whitespace in address, wrong hash\n // prefix, floats in length, etc.). Catching them here prevents a\n // signed envelope that no verifier can accept.\n throw new StampError('E_MALFORMED', v.reason);\n }\n\n const id = computeEnvelopeId(canon);\n\n // BIP-322 signs the ASCII hex representation of id. Hex is chosen (not\n // raw bytes) so wallets that render the signed message to the user show\n // something legible. The id itself commits to every field in the canonical\n // message so this is a transitive commitment to the full signing ceremony.\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: StampEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'stamp',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: {\n address: input.signer.address,\n alg: 'bip322',\n },\n signed_at: signedAt,\n stake: input.stake ?? null,\n ots: null,\n sig: {\n alg: 'bip322',\n pubkey: input.signer.address,\n value: sigValue,\n },\n };\n\n return envelope;\n}\n\n/**\n * Verify a stamp envelope. See SPEC §8 for the full algorithm. Returns a\n * structured result; callers can match on `result.ok` and drill into\n * `result.code` for the specific failure.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const env = input.envelope;\n\n // 1. Version check.\n if (env.v !== ENVELOPE_VERSION) {\n return err('E_UNSUPPORTED_VERSION', `envelope version ${env.v} not supported by this implementation`);\n }\n\n // 2. Shape check.\n const shape = checkShape(env);\n if (shape) return shape;\n\n // 3. Canonical message reconstruction + id check.\n const canon = {\n address: env.signer.address,\n content_hash: env.content.hash,\n content_length: env.content.length,\n content_mime: env.content.mime,\n signed_at: env.signed_at,\n };\n const reconstructedMessage = canonicalMessage(canon);\n const reconstructedId = hexEncode(sha256(canonicalMessageBytes(canon)));\n if (reconstructedId !== env.id) {\n return err('E_BAD_ID', `reconstructed id (${reconstructedId}) does not match envelope.id (${env.id})`);\n }\n\n // 4. Signature verify.\n if (!input.skipSignatureVerification) {\n if (!input.verifyBip322) {\n return err('E_BAD_SIG', 'no BIP-322 verifier supplied');\n }\n const ok = await input.verifyBip322(env.id, env.sig.value, env.signer.address);\n if (!ok) return err('E_BAD_SIG', 'BIP-322 signature did not verify');\n }\n\n // 5. Anchor verify.\n let anchor: VerifyOk['anchor'];\n if (env.ots === null) {\n anchor = { status: 'none' };\n } else if (env.ots.status === 'pending') {\n anchor = { status: 'pending' };\n } else {\n // confirmed\n const h = env.ots.block_height;\n const hash = env.ots.block_hash;\n if (h === null || hash === null) {\n return err('E_BAD_ANCHOR', 'confirmed OTS proof missing block_height or block_hash');\n }\n let verified = false;\n if (input.verifyOtsAnchor) {\n try {\n verified = await input.verifyOtsAnchor(env.ots.proof, h, hash);\n } catch (e) {\n return err('E_BAD_ANCHOR', `anchor verifier threw: ${(e as Error).message}`);\n }\n if (!verified) return err('E_BAD_ANCHOR', 'OTS proof did not chain to declared Bitcoin block header');\n }\n // If no anchor verifier was supplied, we accept on shape. The result\n // reports verified: false so callers can decide what to do.\n anchor = { status: 'confirmed', blockHeight: h, blockHash: hash, verified };\n }\n\n // 6. Content bytes check (if supplied).\n if (input.content) {\n const actualHash = 'sha256:' + hexEncode(sha256(input.content));\n if (actualHash !== env.content.hash) {\n return err('E_BAD_CONTENT', `content hash (${actualHash}) does not match envelope.content.hash (${env.content.hash})`);\n }\n }\n\n return {\n ok: true,\n envelope: env,\n canonicalMessage: reconstructedMessage,\n id: env.id,\n anchor,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction checkShape(env: StampEnvelope): VerifyResult | null {\n if (env.kind !== 'stamp') return err('E_MALFORMED', 'envelope.kind must be \"stamp\"');\n if (!env.id || typeof env.id !== 'string' || !/^[0-9a-f]{64}$/.test(env.id)) {\n return err('E_MALFORMED', 'envelope.id must be 64 lowercase hex chars');\n }\n if (!env.content || typeof env.content.hash !== 'string' || !env.content.hash.startsWith('sha256:')) {\n return err('E_MALFORMED', 'envelope.content.hash must start with \"sha256:\"');\n }\n if (!Number.isInteger(env.content.length) || env.content.length < 0) {\n return err('E_MALFORMED', 'envelope.content.length must be a non-negative integer');\n }\n if (typeof env.content.mime !== 'string' || env.content.mime.length === 0) {\n return err('E_MALFORMED', 'envelope.content.mime must be a non-empty string');\n }\n if (!env.signer || typeof env.signer.address !== 'string' || env.signer.alg !== 'bip322') {\n return err('E_MALFORMED', 'envelope.signer invalid');\n }\n if (env.signer.address !== env.sig.pubkey) {\n return err('E_MALFORMED', 'envelope.signer.address must equal envelope.sig.pubkey');\n }\n if (typeof env.signed_at !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(env.signed_at)) {\n return err('E_MALFORMED', 'envelope.signed_at must be ISO 8601 UTC ending in Z');\n }\n if (!env.sig || env.sig.alg !== 'bip322' || typeof env.sig.value !== 'string') {\n return err('E_MALFORMED', 'envelope.sig invalid');\n }\n return null;\n}\n\nfunction err(code: VerifyErrorCode, message: string): VerifyResult {\n return { ok: false, code, message };\n}\n\nfunction isoUtcSeconds(d: Date): string {\n // Drop milliseconds; SPEC allows ms-precision but we default to seconds\n // for shorter wallet-signing prompts. Callers needing millisecond\n // precision can pass a pre-formatted ISO string via a future field.\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n"]}
{"version":3,"sources":["../src/types.ts","../src/canonical.ts","../src/stamp.ts"],"names":["sha256"],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB;ACmBzB,SAAS,iBAAiB,KAAA,EAAsC;AAInE,EAAA,OAAO;AAAA,IACH,aAAA;AAAA,IACA,CAAA,SAAA,EAAY,MAAM,OAAO,CAAA,CAAA;AAAA,IACzB,CAAA,cAAA,EAAiB,MAAM,YAAY,CAAA,CAAA;AAAA,IACnC,CAAA,gBAAA,EAAmB,MAAM,cAAc,CAAA,CAAA;AAAA,IACvC,CAAA,cAAA,EAAiB,MAAM,YAAY,CAAA,CAAA;AAAA,IACnC,CAAA,WAAA,EAAc,MAAM,SAAS,CAAA;AAAA,GACjC,CAAE,KAAK,IAAI,CAAA;AACf;AAUO,SAAS,uBAAuB,KAAA,EAEH;AAIhC,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,gBAAgB,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,EAAG;AACvD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,+DAAA,EAAgE;AAAA,EAChG;AACA,EAAA,IAAI,CAAC,uBAAA,CAAwB,IAAA,CAAK,KAAA,CAAM,YAAY,CAAA,EAAG;AACnD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,iDAAA,EAAkD;AAAA,EAClF;AACA,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,KAAA,CAAM,cAAc,CAAA,IAAK,KAAA,CAAM,iBAAiB,CAAA,EAAG;AACrE,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,+CAAA,EAAgD;AAAA,EAChF;AAIA,EAAA,IAAI,CAAC,KAAA,CAAM,YAAA,IAAgB,cAAc,IAAA,CAAK,KAAA,CAAM,YAAY,CAAA,EAAG;AAC/D,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,sDAAA,EAAuD;AAAA,EACvF;AACA,EAAA,IAAI,CAAC,gDAAA,CAAiD,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AACzE,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,4CAAA,EAA6C;AAAA,EAC7E;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AACtB;AAEO,SAAS,sBAAsB,KAAA,EAA0C;AAC5E,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,gBAAA,CAAiB,KAAK,CAAC,CAAA;AAC3D;AAMO,SAAS,kBAAkB,KAAA,EAAsC;AACpE,EAAA,MAAM,KAAA,GAAQ,sBAAsB,KAAK,CAAA;AACzC,EAAA,OAAO,SAAA,CAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAClC;AAcO,SAAS,qBAAqB,GAAA,EAA4B;AAC7D,EAAA,OAAO,aAAa,GAA2B,CAAA;AACnD;AAEO,SAAS,uBAAuB,GAAA,EAAgC;AACnE,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,OAAO,YAAA,CAAa,GAA2B,IAAI,IAAI,CAAA;AACpF;AAEO,SAAS,aAAa,KAAA,EAA0B;AACnD,EAAA,OAAO,OAAO,KAAK,CAAA;AACvB;AAEA,SAAS,OAAO,CAAA,EAAsB;AAClC,EAAA,IAAI,CAAA,KAAM,MAAM,OAAO,MAAA;AACvB,EAAA,IAAI,OAAO,CAAA,KAAM,SAAA,EAAW,OAAO,IAAI,MAAA,GAAS,OAAA;AAChD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,aAAa,CAAC,CAAA;AAChD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,aAAa,CAAC,CAAA;AAChD,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG;AAClB,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,KAAA,MAAW,QAAQ,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAC7C,IAAA,OAAO,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA;AAAA,EACnC;AACA,EAAA,IAAI,OAAO,MAAM,QAAA,EAAU;AACvB,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAC,EAAE,IAAA,EAAK;AACjC,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AAClB,MAAA,MAAM,KAAA,GAAQ,EAAE,CAAC,CAAA;AACjB,MAAA,IAAI,UAAU,MAAA,EAAW;AACzB,MAAA,KAAA,CAAM,KAAK,YAAA,CAAa,CAAC,IAAI,GAAA,GAAM,MAAA,CAAO,KAAkB,CAAC,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA;AAAA,EACnC;AACA,EAAA,MAAM,IAAI,KAAA,CAAM,oCAAA,GAAuC,OAAO,CAAC,CAAA;AACnE;AAEA,SAAS,aAAa,CAAA,EAAmB;AACrC,EAAA,IAAI,CAAC,OAAO,QAAA,CAAS,CAAC,GAAG,MAAM,IAAI,MAAM,4CAA4C,CAAA;AACrF,EAAA,IAAI,MAAA,CAAO,EAAA,CAAG,CAAA,EAAG,EAAE,GAAG,OAAO,GAAA;AAC7B,EAAA,IAAI,MAAA,CAAO,UAAU,CAAC,CAAA,IAAK,KAAK,GAAA,CAAI,CAAC,IAAI,IAAA,EAAM;AAC3C,IAAA,OAAO,OAAO,CAAC,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,IAAA,CAAK,UAAU,CAAC,CAAA;AAC3B;AAEA,IAAM,YAAA,GAAuC;AAAA,EACzC,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,GAAA,EAAM;AACV,CAAA;AAEA,SAAS,aAAa,CAAA,EAAmB;AACrC,EAAA,IAAI,GAAA,GAAM,GAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,MAAM,EAAA,GAAK,EAAE,CAAC,CAAA;AACd,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,UAAA,CAAW,CAAC,CAAA;AAC5B,IAAA,IAAI,YAAA,CAAa,EAAE,CAAA,EAAG;AAClB,MAAA,GAAA,IAAO,aAAa,EAAE,CAAA;AAAA,IAC1B,CAAA,MAAA,IAAW,OAAO,EAAA,EAAM;AACpB,MAAA,GAAA,IAAO,QAAQ,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,IACpD,CAAA,MAAO;AACH,MAAA,GAAA,IAAO,EAAA;AAAA,IACX;AAAA,EACJ;AACA,EAAA,GAAA,IAAO,GAAA;AACP,EAAA,OAAO,GAAA;AACX;AAMA,IAAM,SAAA,GAAY,kBAAA;AAEX,SAAS,UAAU,KAAA,EAA2B;AACjD,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACnC,IAAA,MAAM,CAAA,GAAI,MAAM,CAAC,CAAA;AACjB,IAAA,GAAA,IAAO,UAAW,CAAA,IAAK,CAAA,GAAK,EAAI,CAAA,GAAK,SAAA,CAAU,IAAI,EAAI,CAAA;AAAA,EAC3D;AACA,EAAA,OAAO,GAAA;AACX;AAEO,SAAS,UAAU,KAAA,EAA2B;AACjD,EAAA,OAAO,SAAA,CAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAClC;AC5JO,SAAS,YAAY,KAAA,EAAqD;AAC7E,EAAA,OAAO;AAAA,IACH,IAAA,EAAM,SAAA,GAAY,SAAA,CAAUA,MAAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IACzC,QAAQ,KAAA,CAAM;AAAA,GAClB;AACJ;AAEO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAElC,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAChD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AAAA,EAChB;AACJ;AAOA,eAAsB,MAAM,KAAA,EAA2C;AAGnE,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,KAAA,CAAM,mBAAmB,UAAA,EAAY;AACrC,IAAA,WAAA,GAAc,SAAA,GAAY,SAAA,CAAUA,MAAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AACzD,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,UAAA;AAAA,EAClC,CAAA,MAAO;AAGH,IAAA,MAAM,CAAA,GAAI,MAAM,OAAA,CAAQ,IAAA;AACxB,IAAA,IAAI,CAAC,EAAE,UAAA,CAAW,SAAS,KAAK,CAAA,CAAE,MAAA,KAAW,SAAA,CAAU,MAAA,GAAS,EAAA,EAAI;AAChE,MAAA,MAAM,IAAI,UAAA;AAAA,QACN,aAAA;AAAA,QACA;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,WAAA,GAAc,CAAA;AACd,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,MAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,aAAa,CAAA,IAAK,gBAAgB,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,UAAA,CAAW,aAAA,EAAe,+CAA+C,CAAA;AAAA,IACvF;AAAA,EACJ;AAEA,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAE3D,EAAA,MAAM,KAAA,GAAQ;AAAA,IACV,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,IACtB,YAAA,EAAc,WAAA;AAAA,IACd,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAc,KAAA,CAAM,IAAA;AAAA,IACpB,SAAA,EAAW;AAAA,GACf;AAEA,EAAA,MAAM,CAAA,GAAI,uBAAuB,KAAK,CAAA;AACtC,EAAA,IAAI,CAAC,EAAE,EAAA,EAAI;AAKP,IAAA,MAAM,IAAI,UAAA,CAAW,aAAA,EAAe,CAAA,CAAE,MAAM,CAAA;AAAA,EAChD;AAEA,EAAA,MAAM,EAAA,GAAK,kBAAkB,KAAK,CAAA;AAMlC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,CAAO,YAAY,EAAE,CAAA;AAElD,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC5B,CAAA,EAAG,gBAAA;AAAA,IACH,IAAA,EAAM,OAAA;AAAA,IACN,EAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,MAAA,EAAQ,aAAA;AAAA,MACR,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,GAAA,EAAK,MAAM,GAAA,IAAO;AAAA,KACtB;AAAA,IACA,MAAA,EAAQ;AAAA,MACJ,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,MACtB,GAAA,EAAK;AAAA,KACT;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,KAAA,EAAO,MAAM,KAAA,IAAS,IAAA;AAAA,IACtB,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK;AAAA,MACD,GAAA,EAAK,QAAA;AAAA,MACL,MAAA,EAAQ,MAAM,MAAA,CAAO,OAAA;AAAA,MACrB,KAAA,EAAO;AAAA;AACX,GACJ;AAEA,EAAA,OAAO,QAAA;AACX;AAOA,eAAsB,OAAO,KAAA,EAA2C;AACpE,EAAA,MAAM,MAAM,KAAA,CAAM,QAAA;AAGlB,EAAA,IAAI,GAAA,CAAI,MAAM,gBAAA,EAAkB;AAC5B,IAAA,OAAO,GAAA,CAAI,uBAAA,EAAyB,CAAA,iBAAA,EAAoB,GAAA,CAAI,CAAC,CAAA,qCAAA,CAAuC,CAAA;AAAA,EACxG;AAGA,EAAA,MAAM,KAAA,GAAQ,WAAW,GAAG,CAAA;AAC5B,EAAA,IAAI,OAAO,OAAO,KAAA;AAGlB,EAAA,MAAM,KAAA,GAAQ;AAAA,IACV,OAAA,EAAS,IAAI,MAAA,CAAO,OAAA;AAAA,IACpB,YAAA,EAAc,IAAI,OAAA,CAAQ,IAAA;AAAA,IAC1B,cAAA,EAAgB,IAAI,OAAA,CAAQ,MAAA;AAAA,IAC5B,YAAA,EAAc,IAAI,OAAA,CAAQ,IAAA;AAAA,IAC1B,WAAW,GAAA,CAAI;AAAA,GACnB;AACA,EAAA,MAAM,oBAAA,GAAuB,iBAAiB,KAAK,CAAA;AACnD,EAAA,MAAM,kBAAkB,SAAA,CAAUA,MAAAA,CAAO,qBAAA,CAAsB,KAAK,CAAC,CAAC,CAAA;AACtE,EAAA,IAAI,eAAA,KAAoB,IAAI,EAAA,EAAI;AAC5B,IAAA,OAAO,IAAI,UAAA,EAAY,CAAA,kBAAA,EAAqB,eAAe,CAAA,8BAAA,EAAiC,GAAA,CAAI,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,EACzG;AAGA,EAAA,IAAI,CAAC,MAAM,yBAAA,EAA2B;AAClC,IAAA,IAAI,CAAC,MAAM,YAAA,EAAc;AACrB,MAAA,OAAO,GAAA,CAAI,aAAa,8BAA8B,CAAA;AAAA,IAC1D;AACA,IAAA,MAAM,EAAA,GAAK,MAAM,KAAA,CAAM,YAAA,CAAa,GAAA,CAAI,EAAA,EAAI,GAAA,CAAI,GAAA,CAAI,KAAA,EAAO,GAAA,CAAI,MAAA,CAAO,OAAO,CAAA;AAC7E,IAAA,IAAI,CAAC,EAAA,EAAI,OAAO,GAAA,CAAI,aAAa,kCAAkC,CAAA;AAAA,EACvE;AAGA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,GAAA,CAAI,QAAQ,IAAA,EAAM;AAClB,IAAA,MAAA,GAAS,EAAE,QAAQ,MAAA,EAAO;AAAA,EAC9B,CAAA,MAAA,IAAW,GAAA,CAAI,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACrC,IAAA,MAAA,GAAS,EAAE,QAAQ,SAAA,EAAU;AAAA,EACjC,CAAA,MAAO;AAEH,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,YAAA;AAClB,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,UAAA;AACrB,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,IAAA,KAAS,IAAA,EAAM;AAC7B,MAAA,OAAO,GAAA,CAAI,gBAAgB,wDAAwD,CAAA;AAAA,IACvF;AACA,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,IAAI,MAAM,eAAA,EAAiB;AACvB,MAAA,IAAI;AACA,QAAA,QAAA,GAAW,MAAM,MAAM,eAAA,CAAgB,GAAA,CAAI,IAAI,KAAA,EAAO,CAAA,EAAG,IAAA,EAAM,GAAA,CAAI,EAAE,CAAA;AAAA,MACzE,SAAS,CAAA,EAAG;AACR,QAAA,OAAO,GAAA,CAAI,cAAA,EAAgB,CAAA,uBAAA,EAA2B,CAAA,CAAY,OAAO,CAAA,CAAE,CAAA;AAAA,MAC/E;AACA,MAAA,IAAI,CAAC,QAAA,EAAU,OAAO,GAAA,CAAI,gBAAgB,0DAA0D,CAAA;AAAA,IACxG;AAGA,IAAA,MAAA,GAAS,EAAE,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,SAAA,EAAW,MAAM,QAAA,EAAS;AAAA,EAC9E;AAGA,EAAA,IAAI,MAAM,OAAA,EAAS;AACf,IAAA,MAAM,aAAa,SAAA,GAAY,SAAA,CAAUA,MAAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AAC9D,IAAA,IAAI,UAAA,KAAe,GAAA,CAAI,OAAA,CAAQ,IAAA,EAAM;AACjC,MAAA,OAAO,GAAA,CAAI,iBAAiB,CAAA,cAAA,EAAiB,UAAU,2CAA2C,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACzH;AAAA,EACJ;AAEA,EAAA,OAAO;AAAA,IACH,EAAA,EAAI,IAAA;AAAA,IACJ,QAAA,EAAU,GAAA;AAAA,IACV,gBAAA,EAAkB,oBAAA;AAAA,IAClB,IAAI,GAAA,CAAI,EAAA;AAAA,IACR;AAAA,GACJ;AACJ;AAIA,SAAS,WAAW,GAAA,EAAyC;AACzD,EAAA,IAAI,IAAI,IAAA,KAAS,OAAA,EAAS,OAAO,GAAA,CAAI,eAAe,+BAA+B,CAAA;AACnF,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,IAAM,OAAO,GAAA,CAAI,EAAA,KAAO,QAAA,IAAY,CAAC,gBAAA,CAAiB,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,EAAG;AACzE,IAAA,OAAO,GAAA,CAAI,eAAe,4CAA4C,CAAA;AAAA,EAC1E;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,OAAA,IAAW,OAAO,IAAI,OAAA,CAAQ,IAAA,KAAS,QAAA,IAAY,CAAC,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,SAAS,CAAA,EAAG;AACjG,IAAA,OAAO,GAAA,CAAI,eAAe,iDAAiD,CAAA;AAAA,EAC/E;AACA,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,OAAA,CAAQ,MAAM,CAAA,IAAK,GAAA,CAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACjE,IAAA,OAAO,GAAA,CAAI,eAAe,wDAAwD,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,OAAO,IAAI,OAAA,CAAQ,IAAA,KAAS,YAAY,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AACvE,IAAA,OAAO,GAAA,CAAI,eAAe,kDAAkD,CAAA;AAAA,EAChF;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,MAAA,IAAU,OAAO,GAAA,CAAI,MAAA,CAAO,OAAA,KAAY,QAAA,IAAY,GAAA,CAAI,MAAA,CAAO,GAAA,KAAQ,QAAA,EAAU;AACtF,IAAA,OAAO,GAAA,CAAI,eAAe,yBAAyB,CAAA;AAAA,EACvD;AACA,EAAA,IAAI,GAAA,CAAI,MAAA,CAAO,OAAA,KAAY,GAAA,CAAI,IAAI,MAAA,EAAQ;AACvC,IAAA,OAAO,GAAA,CAAI,eAAe,wDAAwD,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,OAAO,IAAI,SAAA,KAAc,QAAA,IAAY,CAAC,gDAAA,CAAiD,IAAA,CAAK,GAAA,CAAI,SAAS,CAAA,EAAG;AAC5G,IAAA,OAAO,GAAA,CAAI,eAAe,qDAAqD,CAAA;AAAA,EACnF;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,GAAA,CAAI,GAAA,KAAQ,QAAA,IAAY,OAAO,GAAA,CAAI,GAAA,CAAI,KAAA,KAAU,QAAA,EAAU;AAC3E,IAAA,OAAO,GAAA,CAAI,eAAe,sBAAsB,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,GAAA,CAAI,MAAuB,OAAA,EAA+B;AAC/D,EAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,IAAA,EAAM,OAAA,EAAQ;AACtC;AAEA,SAAS,cAAc,CAAA,EAAiB;AAIpC,EAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,WAAW,GAAG,CAAA;AACjD","file":"index.mjs","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * `envelopeId` is the 64-char hex envelope id (equals the 32-byte digest committed\n * by the OTS proof, hex-encoded). Passing it explicitly lets the verifier avoid\n * the broken \"look up the digest from the block hash\" dance the old adapter\n * tried to do.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (\n proofB64: string,\n blockHeight: number,\n blockHash: string,\n envelopeId: string\n ) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n","// Canonical message + RFC 8785 JSON canonicalization for OC Stamp.\n//\n// Two distinct canonicalizers live here:\n//\n// 1. canonicalMessage(input) — the exact byte sequence the signer signs via\n// BIP-322. LF-separated, no trailing LF after signed_at. SPEC §3.1.\n//\n// 2. canonicalizeEnvelope(env) — RFC 8785 JSON canonicalization of the wire\n// envelope, used for stable serialization / equality checks. SPEC §5.\n//\n// Only (1) participates in the id derivation. (2) is for test-vector byte\n// equality and any future operation that needs deterministic envelope bytes.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport type { CanonicalMessageInput, StampEnvelope } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonical message (SPEC §3.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalMessage(input: CanonicalMessageInput): string {\n // The domain separator `oc-stamp:v1` commits the whole signing ceremony to\n // this spec version. Future incompatible changes MUST bump the version\n // number here so a v1 signature can never be replayed against a v2 domain.\n return [\n 'oc-stamp:v1',\n `address: ${input.address}`,\n `content_hash: ${input.content_hash}`,\n `content_length: ${input.content_length}`,\n `content_mime: ${input.content_mime}`,\n `signed_at: ${input.signed_at}`,\n ].join('\\n');\n}\n\n/**\n * Check whether a CanonicalMessageInput is well-formed per the spec's field\n * rules (§3, §4). Returns `{ok: true}` if every field is valid, or\n * `{ok: false, reason}` pointing at the first violation.\n *\n * Called by `stamp()` before producing the canonical bytes. Exposed for\n * UIs that want to preview validity without throwing.\n */\nexport function validateCanonicalInput(input: CanonicalMessageInput):\n | { ok: true }\n | { ok: false, reason: string } {\n // No control chars or whitespace breaks in fields — any of those would\n // invalidate the 6-line structure and produce an id nobody else can\n // reconstruct. The whole canonical message is ASCII-safe by design.\n if (!input.address || /[\\s\\x00-\\x1f]/.test(input.address)) {\n return { ok: false, reason: 'address must be non-empty with no whitespace or control chars' };\n }\n if (!/^sha256:[0-9a-f]{64}$/.test(input.content_hash)) {\n return { ok: false, reason: 'content_hash must match /^sha256:[0-9a-f]{64}$/' };\n }\n if (!Number.isInteger(input.content_length) || input.content_length < 0) {\n return { ok: false, reason: 'content_length must be a non-negative integer' };\n }\n // MIME per RFC 6838: \"type/subtype[+suffix][;params]\". We accept anything\n // that's non-empty and control-char-free; fine-grained parsing is out of\n // scope and would reject legitimate media types.\n if (!input.content_mime || /[\\x00-\\x1f]/.test(input.content_mime)) {\n return { ok: false, reason: 'content_mime must be non-empty with no control chars' };\n }\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(input.signed_at)) {\n return { ok: false, reason: 'signed_at must be ISO 8601 UTC ending in Z' };\n }\n return { ok: true };\n}\n\nexport function canonicalMessageBytes(input: CanonicalMessageInput): Uint8Array {\n return new TextEncoder().encode(canonicalMessage(input));\n}\n\n/**\n * Compute the envelope id as lowercase hex of sha256(canonical_message_bytes).\n * Pure function — the single source of truth for identity derivation.\n */\nexport function computeEnvelopeId(input: CanonicalMessageInput): string {\n const bytes = canonicalMessageBytes(input);\n return hexEncode(sha256(bytes));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Envelope canonicalization (SPEC §5; RFC 8785)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport function canonicalizeEnvelope(env: StampEnvelope): string {\n return canonicalize(env as unknown as JsonValue);\n}\n\nexport function canonicalEnvelopeBytes(env: StampEnvelope): Uint8Array {\n return new TextEncoder().encode(canonicalize(env as unknown as JsonValue) + '\\n');\n}\n\nexport function canonicalize(value: JsonValue): string {\n return encode(value);\n}\n\nfunction encode(v: JsonValue): string {\n if (v === null) return 'null';\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (typeof v === 'number') return encodeNumber(v);\n if (typeof v === 'string') return encodeString(v);\n if (Array.isArray(v)) {\n const parts: string[] = [];\n for (const item of v) parts.push(encode(item));\n return '[' + parts.join(',') + ']';\n }\n if (typeof v === 'object') {\n const keys = Object.keys(v).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const inner = v[k];\n if (inner === undefined) continue;\n parts.push(encodeString(k) + ':' + encode(inner as JsonValue));\n }\n return '{' + parts.join(',') + '}';\n }\n throw new Error('cannot canonicalize value of type ' + typeof v);\n}\n\nfunction encodeNumber(n: number): string {\n if (!Number.isFinite(n)) throw new Error('non-finite number not JSON-canonicalizable');\n if (Object.is(n, -0)) return '0';\n if (Number.isInteger(n) && Math.abs(n) < 1e21) {\n return String(n);\n }\n return JSON.stringify(n);\n}\n\nconst ESCAPE_TABLE: Record<string, string> = {\n '\\\\': '\\\\\\\\',\n '\"': '\\\\\"',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n};\n\nfunction encodeString(s: string): string {\n let out = '\"';\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const code = ch.charCodeAt(0);\n if (ESCAPE_TABLE[ch]) {\n out += ESCAPE_TABLE[ch];\n } else if (code < 0x20) {\n out += '\\\\u' + code.toString(16).padStart(4, '0');\n } else {\n out += ch;\n }\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Small utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst HEX_TABLE = '0123456789abcdef';\n\nexport function hexEncode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n out += HEX_TABLE[(b >> 4) & 0x0f]! + HEX_TABLE[b & 0x0f]!;\n }\n return out;\n}\n\nexport function sha256Hex(bytes: Uint8Array): string {\n return hexEncode(sha256(bytes));\n}\n","// stamp() and verify() — reference implementation of OC Stamp v1. SPEC §4, §8.\n\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport {\n canonicalMessage,\n canonicalMessageBytes,\n computeEnvelopeId,\n hexEncode,\n validateCanonicalInput,\n} from './canonical.js';\nimport {\n ENVELOPE_VERSION,\n type StampEnvelope,\n type StampInput,\n type VerifyErrorCode,\n type VerifyInput,\n type VerifyOk,\n type VerifyResult,\n} from './types.js';\n\n/**\n * Compute `{hash, length}` for raw bytes. Convenience over the common\n * `sha256(bytes)` pattern that prefixes with `sha256:` and returns lowercase\n * hex. Pass the result to `stamp({ content: hashContent(bytes), mime })` when\n * you don't want stamp() to hash the bytes itself.\n */\nexport function hashContent(bytes: Uint8Array): { hash: string; length: number } {\n return {\n hash: 'sha256:' + hexEncode(sha256(bytes)),\n length: bytes.byteLength,\n };\n}\n\nexport class StampError extends Error {\n code: VerifyErrorCode;\n constructor(code: VerifyErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = 'StampError';\n }\n}\n\n/**\n * Build and sign a stamp envelope. Returns the envelope with sig populated but\n * ots left null — OTS calendar submission is a separate step (see\n * @orangecheck/stamp-ots).\n */\nexport async function stamp(input: StampInput): Promise<StampEnvelope> {\n // Resolve content hash + length. Two modes: raw bytes (we hash) or\n // pre-computed {hash, length} (we trust the caller).\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n // Pre-computed. Caller must have used SHA-256 and declared length correctly.\n // Guard against the easy typos.\n const h = input.content.hash;\n if (!h.startsWith('sha256:') || h.length !== 'sha256:'.length + 64) {\n throw new StampError(\n 'E_MALFORMED',\n 'content.hash must be \"sha256:\" + 64 lowercase hex chars'\n );\n }\n contentHash = h;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new StampError('E_MALFORMED', 'content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canon = {\n address: input.signer.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n };\n\n const v = validateCanonicalInput(canon);\n if (!v.ok) {\n // Validation rejects inputs that would silently break canonical-message\n // reconstructability downstream (whitespace in address, wrong hash\n // prefix, floats in length, etc.). Catching them here prevents a\n // signed envelope that no verifier can accept.\n throw new StampError('E_MALFORMED', v.reason);\n }\n\n const id = computeEnvelopeId(canon);\n\n // BIP-322 signs the ASCII hex representation of id. Hex is chosen (not\n // raw bytes) so wallets that render the signed message to the user show\n // something legible. The id itself commits to every field in the canonical\n // message so this is a transitive commitment to the full signing ceremony.\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: StampEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'stamp',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: {\n address: input.signer.address,\n alg: 'bip322',\n },\n signed_at: signedAt,\n stake: input.stake ?? null,\n ots: null,\n sig: {\n alg: 'bip322',\n pubkey: input.signer.address,\n value: sigValue,\n },\n };\n\n return envelope;\n}\n\n/**\n * Verify a stamp envelope. See SPEC §8 for the full algorithm. Returns a\n * structured result; callers can match on `result.ok` and drill into\n * `result.code` for the specific failure.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const env = input.envelope;\n\n // 1. Version check.\n if (env.v !== ENVELOPE_VERSION) {\n return err('E_UNSUPPORTED_VERSION', `envelope version ${env.v} not supported by this implementation`);\n }\n\n // 2. Shape check.\n const shape = checkShape(env);\n if (shape) return shape;\n\n // 3. Canonical message reconstruction + id check.\n const canon = {\n address: env.signer.address,\n content_hash: env.content.hash,\n content_length: env.content.length,\n content_mime: env.content.mime,\n signed_at: env.signed_at,\n };\n const reconstructedMessage = canonicalMessage(canon);\n const reconstructedId = hexEncode(sha256(canonicalMessageBytes(canon)));\n if (reconstructedId !== env.id) {\n return err('E_BAD_ID', `reconstructed id (${reconstructedId}) does not match envelope.id (${env.id})`);\n }\n\n // 4. Signature verify.\n if (!input.skipSignatureVerification) {\n if (!input.verifyBip322) {\n return err('E_BAD_SIG', 'no BIP-322 verifier supplied');\n }\n const ok = await input.verifyBip322(env.id, env.sig.value, env.signer.address);\n if (!ok) return err('E_BAD_SIG', 'BIP-322 signature did not verify');\n }\n\n // 5. Anchor verify.\n let anchor: VerifyOk['anchor'];\n if (env.ots === null) {\n anchor = { status: 'none' };\n } else if (env.ots.status === 'pending') {\n anchor = { status: 'pending' };\n } else {\n // confirmed\n const h = env.ots.block_height;\n const hash = env.ots.block_hash;\n if (h === null || hash === null) {\n return err('E_BAD_ANCHOR', 'confirmed OTS proof missing block_height or block_hash');\n }\n let verified = false;\n if (input.verifyOtsAnchor) {\n try {\n verified = await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id);\n } catch (e) {\n return err('E_BAD_ANCHOR', `anchor verifier threw: ${(e as Error).message}`);\n }\n if (!verified) return err('E_BAD_ANCHOR', 'OTS proof did not chain to declared Bitcoin block header');\n }\n // If no anchor verifier was supplied, we accept on shape. The result\n // reports verified: false so callers can decide what to do.\n anchor = { status: 'confirmed', blockHeight: h, blockHash: hash, verified };\n }\n\n // 6. Content bytes check (if supplied).\n if (input.content) {\n const actualHash = 'sha256:' + hexEncode(sha256(input.content));\n if (actualHash !== env.content.hash) {\n return err('E_BAD_CONTENT', `content hash (${actualHash}) does not match envelope.content.hash (${env.content.hash})`);\n }\n }\n\n return {\n ok: true,\n envelope: env,\n canonicalMessage: reconstructedMessage,\n id: env.id,\n anchor,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction checkShape(env: StampEnvelope): VerifyResult | null {\n if (env.kind !== 'stamp') return err('E_MALFORMED', 'envelope.kind must be \"stamp\"');\n if (!env.id || typeof env.id !== 'string' || !/^[0-9a-f]{64}$/.test(env.id)) {\n return err('E_MALFORMED', 'envelope.id must be 64 lowercase hex chars');\n }\n if (!env.content || typeof env.content.hash !== 'string' || !env.content.hash.startsWith('sha256:')) {\n return err('E_MALFORMED', 'envelope.content.hash must start with \"sha256:\"');\n }\n if (!Number.isInteger(env.content.length) || env.content.length < 0) {\n return err('E_MALFORMED', 'envelope.content.length must be a non-negative integer');\n }\n if (typeof env.content.mime !== 'string' || env.content.mime.length === 0) {\n return err('E_MALFORMED', 'envelope.content.mime must be a non-empty string');\n }\n if (!env.signer || typeof env.signer.address !== 'string' || env.signer.alg !== 'bip322') {\n return err('E_MALFORMED', 'envelope.signer invalid');\n }\n if (env.signer.address !== env.sig.pubkey) {\n return err('E_MALFORMED', 'envelope.signer.address must equal envelope.sig.pubkey');\n }\n if (typeof env.signed_at !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(env.signed_at)) {\n return err('E_MALFORMED', 'envelope.signed_at must be ISO 8601 UTC ending in Z');\n }\n if (!env.sig || env.sig.alg !== 'bip322' || typeof env.sig.value !== 'string') {\n return err('E_MALFORMED', 'envelope.sig invalid');\n }\n return null;\n}\n\nfunction err(code: VerifyErrorCode, message: string): VerifyResult {\n return { ok: false, code, message };\n}\n\nfunction isoUtcSeconds(d: Date): string {\n // Drop milliseconds; SPEC allows ms-precision but we default to seconds\n // for shorter wallet-signing prompts. Callers needing millisecond\n // precision can pass a pre-formatted ISO string via a future field.\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n"]}

@@ -68,3 +68,3 @@ declare const ENVELOPE_VERSION: 1;

verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string, envelopeId: string) => Promise<boolean>;
skipSignatureVerification?: boolean;

@@ -71,0 +71,0 @@ }

@@ -68,3 +68,3 @@ declare const ENVELOPE_VERSION: 1;

verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string, envelopeId: string) => Promise<boolean>;
skipSignatureVerification?: boolean;

@@ -71,0 +71,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n"]}
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * `envelopeId` is the 64-char hex envelope id (equals the 32-byte digest committed\n * by the OTS proof, hex-encoded). Passing it explicitly lets the verifier avoid\n * the broken \"look up the digest from the block hash\" dance the old adapter\n * tried to do.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (\n proofB64: string,\n blockHeight: number,\n blockHash: string,\n envelopeId: string\n ) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n"]}

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n"]}
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Stamp v1 envelopes. See SPEC.md §4.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind = 'stamp';\n\nexport interface StampContent {\n /** \"sha256:<64-hex>\" */\n hash: string;\n length: number;\n /** RFC 6838 media type. \"application/octet-stream\" if unknown. */\n mime: string;\n /** Optional pointer — not cryptographic. */\n ref: string | null;\n}\n\nexport interface StampSigner {\n address: string;\n alg: 'bip322';\n}\n\nexport interface StampStake {\n /** SHA-256 hex of an OrangeCheck canonical message. */\n attestation_id: string;\n sats_bonded: number;\n days_unspent: number;\n}\n\nexport type OtsStatus = 'pending' | 'confirmed';\n\nexport interface StampOts {\n status: OtsStatus;\n /** Base64-encoded OpenTimestamps proof. */\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n /** ISO 8601 UTC of the upgrade; null while pending. */\n upgraded_at: string | null;\n}\n\nexport interface StampSignature {\n alg: 'bip322';\n pubkey: string; // MUST equal signer.address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport interface StampEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: EnvelopeKind;\n id: string; // 64-hex of sha256(canonical_message)\n content: StampContent;\n signer: StampSigner;\n signed_at: string; // ISO 8601 UTC\n stake: StampStake | null;\n ots: StampOts | null;\n sig: StampSignature;\n}\n\n// Inputs to stamp() / verify().\n\nexport interface CanonicalMessageInput {\n address: string;\n /** \"sha256:<64-hex>\" */\n content_hash: string;\n content_length: number;\n /** \"application/octet-stream\" if unknown. */\n content_mime: string;\n /** ISO 8601 UTC. */\n signed_at: string;\n}\n\nexport interface StampInput {\n /** The bytes being stamped, or their pre-computed hash. Exactly one is required. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n ref?: string | null;\n signer: {\n address: string;\n /**\n * Returns a BIP-322 base64 signature of the given message bytes as UTF-8.\n * The caller supplies the wallet adapter. Called once per stamp() invocation.\n */\n signMessage: (msg: string) => Promise<string>;\n };\n stake?: StampStake | null;\n /** Defaults to now. */\n signedAt?: Date;\n}\n\nexport interface VerifyInput {\n envelope: StampEnvelope;\n /**\n * Optional: the content bytes. If present, verify() additionally checks that\n * sha256(content) matches envelope.content.hash.\n */\n content?: Uint8Array;\n /**\n * Verifier for BIP-322. Plugged in by the caller because signature verification\n * libraries differ across Node, browser, and CLI environments.\n */\n verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;\n /**\n * Optional hook for OTS anchor verification. If present and ots.status === \"confirmed\",\n * the caller-supplied function walks the proof and returns true iff it chains to a\n * real Bitcoin block header at the declared height.\n *\n * `envelopeId` is the 64-char hex envelope id (equals the 32-byte digest committed\n * by the OTS proof, hex-encoded). Passing it explicitly lets the verifier avoid\n * the broken \"look up the digest from the block hash\" dance the old adapter\n * tried to do.\n *\n * The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on\n * shape alone — useful for preview UIs. Callers that attach legal or economic\n * weight MUST supply this hook.\n */\n verifyOtsAnchor?: (\n proofB64: string,\n blockHeight: number,\n blockHash: string,\n envelopeId: string\n ) => Promise<boolean>;\n /**\n * Skip the BIP-322 signature verification. Default false. Useful for test vectors\n * where signatures are placeholders, or for preview UIs that re-verify separately.\n */\n skipSignatureVerification?: boolean;\n}\n\nexport type VerifyErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_ANCHOR'\n | 'E_NO_ANCHOR'\n | 'E_BAD_CONTENT'\n | 'E_STAKE_UNMET'\n | 'E_CALENDAR_UNREACHABLE';\n\nexport interface VerifyOk {\n ok: true;\n envelope: StampEnvelope;\n canonicalMessage: string;\n id: string;\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: boolean };\n}\n\nexport interface VerifyErr {\n ok: false;\n code: VerifyErrorCode;\n message: string;\n}\n\nexport type VerifyResult = VerifyOk | VerifyErr;\n"]}
{
"name": "@orangecheck/stamp-core",
"version": "0.1.1",
"version": "0.1.2",
"description": "OC Stamp canonical message, envelope format, stamp and verify. See https://github.com/orangecheck/oc-stamp-protocol.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -195,7 +195,11 @@ import { describe, expect, it } from 'vitest';

let called = false;
let receivedEnvelopeId = '';
let receivedBlockHeight = 0;
const r = await verify({
envelope: env,
skipSignatureVerification: true,
verifyOtsAnchor: async () => {
verifyOtsAnchor: async (_proof, blockHeight, _blockHash, envelopeId) => {
called = true;
receivedEnvelopeId = envelopeId;
receivedBlockHeight = blockHeight;
return true;

@@ -205,2 +209,7 @@ },

expect(called).toBe(true);
// The hook MUST receive the envelope id — that's what the anchor
// commits to. The old adapter tried to derive it from block_hash
// (nonsense); the fix is passing it explicitly.
expect(receivedEnvelopeId).toBe(env.id);
expect(receivedBlockHeight).toBe(890123);
expect(r.ok).toBe(true);

@@ -207,0 +216,0 @@ if (r.ok && r.anchor.status === 'confirmed') {

@@ -184,3 +184,3 @@ // stamp() and verify() — reference implementation of OC Stamp v1. SPEC §4, §8.

try {
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash);
verified = await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id);
} catch (e) {

@@ -187,0 +187,0 @@ return err('E_BAD_ANCHOR', `anchor verifier threw: ${(e as Error).message}`);

@@ -108,2 +108,7 @@ // Wire types for OC Stamp v1 envelopes. See SPEC.md §4.

*
* `envelopeId` is the 64-char hex envelope id (equals the 32-byte digest committed
* by the OTS proof, hex-encoded). Passing it explicitly lets the verifier avoid
* the broken "look up the digest from the block hash" dance the old adapter
* tried to do.
*
* The default behavior (anchor hook omitted) is to ACCEPT a confirmed envelope on

@@ -113,3 +118,8 @@ * shape alone — useful for preview UIs. Callers that attach legal or economic

*/
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;
verifyOtsAnchor?: (
proofB64: string,
blockHeight: number,
blockHash: string,
envelopeId: string
) => Promise<boolean>;
/**

@@ -116,0 +126,0 @@ * Skip the BIP-322 signature verification. Default false. Useful for test vectors