#!/usr/bin/env node // A GPU worker stand-in that speaks the miner's serve protocol (ready / job / done / found / prepare / prepared / // quit) and misbehaves on command, so the miner's fault guards and the app's watchdog can be measured on a Mac with // no faulty GPU at hand. It never hashes: every `done` is invented, every `found` is wrong on purpose. // // FAKE_WORKER_CTL= the file's first word is the mode, re-read before every job: // ok every job: `done` after 100 ms with the job's nonce count as hashes (about 167 MH/s at 2^24 nonces) // slow `done` after 5,000 ms (a self-heal build inside a job, a game on the GPU) // fast `done` after 0.3 ms with the full count: a runtime answering success while running nothing // (the 4 October 2026 gfx1036 fault) // zero `done` after 100 ms with 0 hashes: the worker completes jobs and produces nothing // silent never answers a job (the queue fills and nothing comes back) // badfound a `found` with a wrong hash before every `done`: a worker on a wrong program // preparefail every `prepare` is answered `prepare-failed`; jobs run as in ok // exit the process exits with code 7 at the next job // The mode is read from argv `--mode ` when no control file is set. `--serve` is accepted and ignored. // Lines on stderr are prefixed `fake-worker:` and never match a miner pattern. import { readFileSync, existsSync, writeSync } from 'node:fs'; import { createInterface } from 'node:readline'; const ctl = process.env.FAKE_WORKER_CTL; const argMode = (() => { const i = process.argv.indexOf('--mode'); return i >= 0 ? process.argv[i + 1] : 'ok'; })(); function mode() { if (ctl && existsSync(ctl)) { const m = readFileSync(ctl, 'utf8').trim().split(/\s+/)[0]; if (m) return m; } return argMode; } // synchronous writes: a piped stdout is asynchronous in Node and process.exit would drop pending lines const out = (s) => { writeSync(1, s + '\n'); }; const sleep = (ms) => new Promise(r => setTimeout(r, ms)); out('ready fake Fake_GPU dataset-log2 28 batch 16777216 prepare 1'); const queue = []; let running = false; async function pump() { if (running) return; running = true; while (queue.length) { const f = queue.shift(); const [id, , , nonceStart, nonces] = f; const m = mode(); if (m === 'exit') process.exit(7); if (m === 'silent') { await sleep(1_000_000); continue; } const t0 = performance.now(); let hashes = Number(nonces); let ms = 100; if (m === 'slow') ms = 5000; else if (m === 'fast') ms = 0.3; else if (m === 'zero') hashes = 0; await sleep(ms); if (m === 'badfound') out(`found ${id} ${BigInt(nonceStart) + 17n} 0000000000000001`); out(`done ${id} ${hashes} ${(performance.now() - t0).toFixed(2)}`); } running = false; } const rl = createInterface({ input: process.stdin }); rl.on('line', (line) => { const f = line.trim().split(/\s+/); switch (f[0]) { case 'job': queue.push(f.slice(1)); pump(); break; case 'prepare': if (mode() === 'preparefail') out(`prepare-failed ${f[1]} ${f[2]} fake worker refuses`); else setTimeout(() => out(`prepared ${f[1]} ${f[2]} 50 fake resident 2 programs 2 datasets`), 50); break; case 'quit': process.exit(0); default: break; } }); rl.on('close', () => process.exit(0));