// This worker just sits on another thread and waits for message to solve // challenges so that we dont block the render thread import { type WorkerRequest, type SolutionMessage, WorkerMessageType, WorkerResponseType, ChallengeStrategy, } from "~/types/pow"; const worker_name = getWorkerName(); let solver: SolverModule | null = null; let atomic_nonce: Int32Array | null = null; let atomic_solution: Int32Array | null = null; async function loadWasmSolver(module: WebAssembly.Module) { if (atomic_nonce === null || atomic_solution === null) { throw createError("Atomics not initialized"); } console.debug(`[${worker_name}]: Loading WASM solver`); solver = await WebAssembly.instantiate(module, { env: { __get_solution: () => Atomics.load(atomic_solution!, 0), __set_solution: (value: number) => Atomics.store(atomic_solution!, 0, value), __cmpxchg_solution: (expected: number, replacement: number) => Atomics.compareExchange(atomic_solution!, 0, expected, replacement), __fetch_add_nonce: (value: number) => Atomics.add(atomic_nonce!, 0, value), __log: (ptr: number, len: number) => { const string_data = new Uint8Array(solver!.exports.memory.buffer, ptr, len); console.log(`[${worker_name}]: ${new TextDecoder().decode(string_data)}`); }, } }) as unknown as SolverModule; console.debug(`[${worker_name}]: WASM solver loaded`); } onmessage = async (event: MessageEvent) => { if (event.data.type === WorkerMessageType.Init) { console.log(`[${worker_name}]: Initializing...`); atomic_nonce = new Int32Array(event.data.sab, 0, 1); atomic_solution = new Int32Array(event.data.sab, 4, 1); try { await loadWasmSolver(event.data.module); } catch (error: any) { console.error(`[${worker_name}]: Failed to load WASM solver:`, error); postMessage({ type: WorkerResponseType.Error, error: `Could not load WASM solver: ${error.message}`, } as SolutionMessage); return; } if (!solver) { console.error(`[${worker_name}]: Failed to load WASM solver`); postMessage({ type: WorkerResponseType.Error, error: "Failed to load WASM solver", } as SolutionMessage); return; } postMessage({ type: WorkerResponseType.Ok, } as SolutionMessage); return; } if (!solver) { postMessage({ type: WorkerResponseType.Error, error: "WASM solver not loaded", } as SolutionMessage); return; } const { strategy } = event.data; const encoder = new TextEncoder(); let solution: number; let target: string = event.data.target; let target_bytes, target_ptr; let memory; switch (strategy) { case ChallengeStrategy.LeadingZeroes: const { difficulty } = event.data; console.debug(`[${worker_name}]: recieved ${strategy} challenge: ${target}, difficulty: ${difficulty}`); target_bytes = encoder.encode(target); target_ptr = solver.exports.malloc(target_bytes.length); if (target_ptr === 0 || target_ptr === null) { console.error(`[${worker_name}]: Failed to allocate memory for challenge string`); postMessage({ type: WorkerResponseType.Error, error: "Failed to allocate memory for challenge string", } as SolutionMessage); return; } memory = new Uint8Array(solver.exports.memory.buffer); memory.set(target_bytes, target_ptr); solution = solver.exports.solve_leaading_zeroes_challenge( target_ptr, target.length, difficulty, ); console.debug(`[${worker_name}]: WASM solver found nonce: ${solution}`); break; case ChallengeStrategy.TargetNumber: const { salt } = event.data; console.debug(`[${worker_name}]: recieved ${strategy} challenge: ${target}, salt: ${salt}`); const salt_bytes = encoder.encode(salt); target_bytes = encoder.encode(target); const salt_ptr = solver.exports.malloc(salt_bytes.length); if (salt_ptr === 0 || salt_ptr === null) { console.error(`[${worker_name}]: Failed to allocate memory for salt string`); postMessage({ type: WorkerResponseType.Error, error: "Failed to allocate memory for salt string", } as SolutionMessage); return; } target_ptr = solver.exports.malloc(target_bytes.length); if (target_ptr === 0 || target_ptr === null) { console.error(`[${worker_name}]: Failed to allocate memory for target string`); postMessage({ type: WorkerResponseType.Error, error: "Failed to allocate memory for target string", } as SolutionMessage); return; } memory = new Uint8Array(solver.exports.memory.buffer); memory.set(salt_bytes, salt_ptr); memory.set(target_bytes, target_ptr); solution = solver.exports.solve_target_number_challenge( target_ptr, target_bytes.length, salt_ptr, salt_bytes.length, ); console.debug(`[${worker_name}]: WASM solver found nonce: ${solution}`); break; } // we are just assuming that if its less than -1, its the min i32 if (solution < 0) { return postMessage({ type: WorkerResponseType.Error, error: "failed to solve challenge", } as SolutionMessage); } postMessage({ type: WorkerResponseType.Solution, nonce: solution === -1 ? null : solution.toString() } as SolutionMessage); };