// sarcina.mjs: the REPLAY workload of the market flow (roadmap point 28): a deterministic task anyone can recompute, so a challenger // can dispute a wrong result with the hash it recomputed and an arbiter (or any reader) can see who is right. keccak-chain: start from a // 32-byte seed and apply keccak256 `iters` times. The spec is public (its keccak256 is the job's specHash); the result is the JSON // bytes { task, seed, iters, digest } whose keccak256 is the job's resultHash. Only node:crypto: keccak-256 is the dependency-free // BigInt implementation of the Cloud gateway (cloud-gateway/gateway.mjs), copied here so the public component has no package to install; // its positive control is the recorded testnet evidence itself (resultHash computed on chain = the replay here, proba-judecata.mjs S1/S3). import crypto from 'node:crypto'; // ----- keccak-256 (FIPS 202 permutation, keccak padding 0x01..0x80, rate 136) ----- const MASK = (1n << 64n) - 1n; const RC = [0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n, 0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n, 0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an, 0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n, 0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an, 0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n]; const ROT = [[0, 36, 3, 41, 18], [1, 44, 10, 45, 2], [62, 6, 43, 15, 61], [28, 55, 25, 21, 56], [27, 20, 39, 8, 14]]; const rotl = (v, n) => (n === 0 ? v : (((v << BigInt(n)) | (v >> BigInt(64 - n))) & MASK)); function keccakF(A) { for (let rr = 0; rr < 24; rr++) { const C = new Array(5), D = new Array(5); for (let x = 0; x < 5; x++) C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; for (let x = 0; x < 5; x++) D[x] = C[(x + 4) % 5] ^ rotl(C[(x + 1) % 5], 1); for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) A[x + 5 * y] ^= D[x]; const B = new Array(25); for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) B[y + 5 * ((2 * x + 3 * y) % 5)] = rotl(A[x + 5 * y], ROT[x][y]); for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) A[x + 5 * y] = B[x + 5 * y] ^ ((~B[((x + 1) % 5) + 5 * y] & MASK) & B[((x + 2) % 5) + 5 * y]); A[0] ^= RC[rr]; } } export function keccak256(bytes) { const rate = 136; const A = new Array(25).fill(0n); const msg = Buffer.from(bytes); const padded = Buffer.concat([msg, Buffer.alloc(rate - (msg.length % rate))]); padded[msg.length] |= 0x01; padded[padded.length - 1] |= 0x80; for (let off = 0; off < padded.length; off += rate) { for (let i = 0; i < rate / 8; i++) A[i] ^= padded.readBigUInt64LE(off + i * 8); keccakF(A); } const out = Buffer.alloc(32); for (let i = 0; i < 4; i++) out.writeBigUInt64LE(A[i], i * 8); return out; } export const TASK = 'keccak-chain'; const hex = (b) => '0x' + Buffer.from(b).toString('hex'); export const keccak = (b) => hex(keccak256(b)); /** Canonical JSON of a spec or a result: keys sorted, no whitespace. */ export const canonical = (x) => JSON.stringify(x, Object.keys(x).sort()); export function specNou({ iters = 20000, seed = hex(crypto.randomBytes(32)) } = {}) { return { task: TASK, seed, iters }; } export const specHash = (spec) => keccak(Buffer.from(canonical(spec), 'utf8')); /** Runs the task: the digest after `iters` keccak256 applications to the seed. */ export function ruleaza(spec) { if (spec.task !== TASK) throw new Error('unknown task ' + spec.task); if (!/^0x[0-9a-f]{64}$/.test(spec.seed) || !Number.isInteger(spec.iters) || spec.iters < 1 || spec.iters > 5e6) throw new Error('bad spec'); let d = Buffer.from(spec.seed.slice(2), 'hex'); for (let i = 0; i < spec.iters; i++) d = keccak256(d); return { task: TASK, seed: spec.seed, iters: spec.iters, digest: hex(d) }; } /** The result bytes a provider submits (and a challenger recomputes), and their on-chain hash. */ export const rezultatOcteti = (r) => Buffer.from(canonical(r), 'utf8'); export const rezultatHash = (r) => keccak(rezultatOcteti(r)); /** The SHA-256 of this program's own source, for the `programSha256` of the execution envelope. */ export const programSha256 = () => '0x' + crypto.createHash('sha256').update(Buffer.from(ruleaza.toString() + keccak.toString(), 'utf8')).digest('hex');