From e471ee252b148163b089c584c0635b95fb9fa133 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 15 Jun 2025 15:08:48 -0700 Subject: [PATCH] Strip logger statements if building for prod. --- esbuild.mjs | 1 + src/bin/server.ts | 52 ++++++++++++++++---------------- src/lib/generate/wasm/index.ts | 18 +++++------ src/lib/generate/webgl/index.ts | 40 ++++++++++++------------ src/lib/generate/webgpu/index.ts | 38 +++++++++++------------ src/lib/validate/index.ts | 8 ++--- 6 files changed, 79 insertions(+), 78 deletions(-) diff --git a/esbuild.mjs b/esbuild.mjs index 78747de..6959b14 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -19,6 +19,7 @@ await build({ legalComments: 'inline', outdir: 'dist', target: 'esnext', + dropLabels: process.env.NODE_ENV !== 'production' ? [] : ['LOG'], plugins: [ glsl({ minify: true, diff --git a/src/bin/server.ts b/src/bin/server.ts index 45b3274..034ba2f 100755 --- a/src/bin/server.ts +++ b/src/bin/server.ts @@ -81,16 +81,16 @@ async function loadConfig (): Promise { CONFIG.EFFORT = +(process.env.NANO_POW_EFFORT ?? '') || CONFIG.EFFORT CONFIG.PORT = process.send ? 0 : +(process.env.NANO_POW_PORT ?? '') || CONFIG.PORT logger.isEnabled = CONFIG.DEBUG - if (configFile.length === 0) logger.log('config file not found') - logger.log(`config set: ${JSON.stringify(CONFIG)}`) + if (configFile.length === 0) LOG: logger.log('config file not found') + LOG: logger.log(`config set: ${JSON.stringify(CONFIG)}`) } await loadConfig() // Initialize puppeteer -logger.log('starting work server') +LOG: logger.log('starting work server') const NanoPow = await readFile(new URL('../main.min.js', import.meta.url), 'utf-8') -logger.log('launching puppeteer browser') +LOG: logger.log('launching puppeteer browser') const browser = await launch({ handleSIGHUP: false, handleSIGINT: false, @@ -103,7 +103,7 @@ const browser = await launch({ '--enable-unsafe-webgpu' ] }) -logger.log('creating puppeteer page') +LOG: logger.log('creating puppeteer page') const page = await browser.newPage() const src = `${NanoPow};window.NanoPow=NanoPow;` @@ -115,7 +115,7 @@ const body = ` ` -logger.log('setting puppeteer request interception') +LOG: logger.log('setting puppeteer request interception') await page.setRequestInterception(true) page.on('request', req => { if (req.isInterceptResolutionHandled()) return @@ -126,7 +126,7 @@ page.on('request', req => { } }) page.on('console', msg => logger.log(msg.text())) -logger.log('navigating to https://nanopow.invalid/ to be intercepted') +LOG: logger.log('navigating to https://nanopow.invalid/ to be intercepted') await page.goto('https://nanopow.invalid/') let NanoPowHandle = await page.waitForFunction(async () => { @@ -134,7 +134,7 @@ let NanoPowHandle = await page.waitForFunction(async () => { return window.NanoPow }) -logger.log('puppeteer initialized') +LOG: logger.log('puppeteer initialized') // Track requests by IP address, and let them fall off over time const requests = new Map() @@ -151,36 +151,36 @@ setInterval(() => { async function work (data: unknown): Promise { let resBody = 'request failed' if (data == null || typeof data !== 'object') { - logger.log('Failed to parse request') + LOG: logger.log('Failed to parse request') throw new Error(resBody) } if (Object.getPrototypeOf(data) !== Object.prototype) { - logger.log('Data corrupted.') + LOG: logger.log('Data corrupted.') throw new Error(resBody) } const dataParsed = data as { [key: string]: unknown } let { action, hash, work, difficulty } = dataParsed if (action !== 'work_generate' && action !== 'work_validate') { - logger.log('Action must be work_generate or work_validate.') + LOG: logger.log('Action must be work_generate or work_validate.') throw new Error(resBody) } resBody = `${action} failed` if (typeof hash !== 'string' || !/[A-Fa-f\d]{64}/.test(hash)) { - logger.log('Hash must be hex char(64)') + LOG: logger.log('Hash must be hex char(64)') throw new Error(resBody) } if (difficulty !== undefined && (typeof difficulty !== 'string' || !/^[A-Fa-f\d]{16}$/.test(difficulty)) ) { - logger.log('Difficulty must be hex char(16).') + LOG: logger.log('Difficulty must be hex char(16).') throw new Error(resBody) } if (action === 'work_validate') { if (typeof work !== 'string' || !/^[A-Fa-f\d]{16}$/.test(work)) { - logger.log('Work must be hex char(16).') + LOG: logger.log('Work must be hex char(16).') throw new Error(resBody) } } else { @@ -207,7 +207,7 @@ async function work (data: unknown): Promise { resBody = JSON.stringify(result) return resBody } catch (err) { - logger.log(err) + LOG: logger.log(err) throw new Error(resBody) } } @@ -276,7 +276,7 @@ function isRateLimited (ip: string): boolean { } const client = requests.get(ip) if (client && client.tokens-- <= 0) { - logger.log(`==== Potential Abuse: ${ip} ====`) + LOG: logger.log(`==== Potential Abuse: ${ip} ====`) return true } if (Date.now() - MAX_REQUEST_TIME > (client?.time ?? 0)) { @@ -307,19 +307,19 @@ function listen (): void { } } }) - logger.log(`server listening on ipc`) + LOG: logger.log(`server listening on ipc`) process.send?.({ type: 'listening', data: 'ipc' }) } else { server.listen(CONFIG.PORT, '127.0.0.1', () => { const address = server.address() if (address == null) { - logger.log(`server closed`) + LOG: logger.log(`server closed`) } else if (typeof address === 'string') { - logger.log(`server listening on ${address}`) + LOG: logger.log(`server listening on ${address}`) process.send?.({ type: 'listening', data: address }) } else { CONFIG.PORT = address.port - logger.log(`server listening on port ${address.port}`) + LOG: logger.log(`server listening on port ${address.port}`) process.send?.({ type: 'listening', data: address.port }) } }) @@ -388,11 +388,11 @@ server.on('connection', s => { }) server.on('error', serverErr => { - logger.log('server error', serverErr) + LOG: logger.log('server error', serverErr) try { shutdown() } catch (shutdownErr) { - logger.log('server failed to shut down', shutdownErr) + LOG: logger.log('server failed to shut down', shutdownErr) process.exit(1) } }) @@ -402,15 +402,15 @@ server.on('error', serverErr => { * does not respond within 10 seconds. */ function shutdown (): void { - logger.log('shutdown signal received') + LOG: logger.log('shutdown signal received') const kill = setTimeout((): never => { - logger.log('server unresponsive, forcefully stopped') + LOG: logger.log('server unresponsive, forcefully stopped') process.exit(1) }, 10_000) server.close(async (): Promise => { await browser.close() clearTimeout(kill) - logger.log('server stopped') + LOG: logger.log('server stopped') process.exit(0) }) } @@ -419,7 +419,7 @@ process.on('SIGINT', shutdown) process.on('SIGTERM', shutdown) process.on('SIGHUP', async () => { - logger.log('reloading configuration') + LOG: logger.log('reloading configuration') server.close(async () => { await loadConfig() await page.reload() diff --git a/src/lib/generate/wasm/index.ts b/src/lib/generate/wasm/index.ts index dbd1b01..0c462f2 100644 --- a/src/lib/generate/wasm/index.ts +++ b/src/lib/generate/wasm/index.ts @@ -20,7 +20,7 @@ function setup (): void { try { url = URL.createObjectURL(new Blob([NanoPowWasmWorker], { type: 'text/javascript' })) workers = [] - logger.log(`NanoPow CPU initialized.`) + LOG: logger.log(`NanoPow CPU initialized.`) isReady = true } catch (err) { isReady = false @@ -49,9 +49,9 @@ async function init (hash: bigint, difficulty: bigint, effort: number): Promise< } try { await workersStarted() - logger.log('Workers ready') + LOG: logger.log('Workers ready') } catch (err: any) { - logger.log(err.message) + LOG: logger.log(err.message) throw new Error('Error loading workers') } } @@ -80,10 +80,10 @@ async function dispatch (): Promise { w.onerror = err w.onmessage = (msg) => { const result = msg.data - logger.log(`received result from worker ${i}`) + LOG: logger.log(`received result from worker ${i}`) found(result) } - logger.log(`sending data to worker ${i}`) + LOG: logger.log(`sending data to worker ${i}`) w.postMessage(JSON.stringify(data)) })) } @@ -135,7 +135,7 @@ async function workersStopped (): Promise { export async function generate (hash: bigint, difficulty: bigint, effort: number, debug: boolean): Promise { logger.isEnabled = debug logger.groupStart('NanoPow WASM work_generate') - logger.log('generating') + LOG: logger.log('generating') if (isReady === false) setup() await init(hash, difficulty, effort) @@ -145,14 +145,14 @@ export async function generate (hash: bigint, difficulty: bigint, effort: number work = await dispatch() result = (await NanoPowValidate(work, hash, difficulty, debug)).difficulty } catch (err) { - logger.log(err) + LOG: logger.log(err) } finally { const isStopped = await workersStopped() if (isStopped) { - logger.log('workers stopped') + LOG: logger.log('workers stopped') } else { reset() - logger.log('workers reset') + LOG: logger.log('workers reset') } logger.groupEnd('NanoPow WASM work_generate') } diff --git a/src/lib/generate/webgl/index.ts b/src/lib/generate/webgl/index.ts index c311bda..1f57f70 100644 --- a/src/lib/generate/webgl/index.ts +++ b/src/lib/generate/webgl/index.ts @@ -70,12 +70,12 @@ function createCanvas (size: number): void { throw new Error('NanoPow could not restore WebGL context.') }, 10_000) ev.preventDefault() - logger.log('NanoPow WebGL createCanvas', 'WebGL context lost. Waiting for it to be restored...') + LOG: logger.log('NanoPow WebGL createCanvas', 'WebGL context lost. Waiting for it to be restored...') }) canvas.addEventListener('webglcontextrestored', ev => { window.clearTimeout(isContextLost) isContextLost = 0 - logger.log('NanoPow WebGL createCanvas', 'WebGL context restored. Reinitializing...') + LOG: logger.log('NanoPow WebGL createCanvas', 'WebGL context restored. Reinitializing...') }) } @@ -91,20 +91,20 @@ function createCanvas (size: number): void { if (context == null) throw new Error('WebGL 2 is required') gl = context - logger.log('NanoPow WebGL createCanvas', 'requested size', size) - logger.log('NanoPow WebGL createCanvas', 'MAX_VIEWPORT_DIMS', ...gl.getParameter(gl.MAX_VIEWPORT_DIMS)) + LOG: logger.log('NanoPow WebGL createCanvas', 'requested size', size) + LOG: logger.log('NanoPow WebGL createCanvas', 'MAX_VIEWPORT_DIMS', ...gl.getParameter(gl.MAX_VIEWPORT_DIMS)) size = Math.min(size, ...gl.getParameter(gl.MAX_VIEWPORT_DIMS)) size = Math.floor(size / 0x100) * 0x100 canvas.height = canvas.width = size - logger.log('NanoPow WebGL createCanvas', 'canvas size', canvas.height, canvas.width) - logger.log('NanoPow WebGL createCanvas', 'drawingBuffer size', gl.drawingBufferHeight, gl.drawingBufferWidth) + LOG: logger.log('NanoPow WebGL createCanvas', 'canvas size', canvas.height, canvas.width) + LOG: logger.log('NanoPow WebGL createCanvas', 'drawingBuffer size', gl.drawingBufferHeight, gl.drawingBufferWidth) if (canvas.height !== gl.drawingBufferHeight || canvas.width !== gl.drawingBufferWidth ) { size = Math.floor(Math.min(gl.drawingBufferHeight, gl.drawingBufferWidth) / 0x100) * 0x100 canvas.height = canvas.width = size } - logger.log('NanoPow WebGL createCanvas', 'final size', size) + LOG: logger.log('NanoPow WebGL createCanvas', 'final size', size) logger.groupEnd('NanoPow WebGL createCanvas') } @@ -218,7 +218,7 @@ function compile (): void { // Finalize configuration pixels = new Uint32Array(finalFbo.size.x * finalFbo.size.y * 4) isReady = true - logger.log(`NanoPow WebGL initialized at ${gl.drawingBufferWidth}x${gl.drawingBufferHeight}. Maximum nonces checked per frame: ${gl.drawingBufferWidth * gl.drawingBufferHeight}`) + LOG: logger.log(`NanoPow WebGL initialized at ${gl.drawingBufferWidth}x${gl.drawingBufferHeight}. Maximum nonces checked per frame: ${gl.drawingBufferWidth * gl.drawingBufferHeight}`) } catch (err) { throw new Error('WebGL compilation failed.', { cause: err }) } @@ -274,7 +274,7 @@ function reset (): void { vertexShader = null gl?.deleteProgram(drawProgram) drawProgram = null - logger.log('reset done') + LOG: logger.log('reset done') } function init (hash: Uint32Array, difficulty: bigint): void { @@ -297,7 +297,7 @@ function init (hash: Uint32Array, difficulty: bigint): void { inputHashView.setUint32(i * 16, hash[i]) } inputDifficultyView.setBigUint64(0, difficulty, true) - logger.log('INPUT', inputArray.buffer.slice(0)) + LOG: logger.log('INPUT', inputArray.buffer.slice(0)) gl.bindBuffer(gl.UNIFORM_BUFFER, inputBuffer) gl.bufferSubData(gl.UNIFORM_BUFFER, 0, inputView) gl.bindBuffer(gl.UNIFORM_BUFFER, null) @@ -310,11 +310,11 @@ function draw (seed: bigint, drawFbo: FBO, query: WebGLQuery): void { if (gl == null) throw new Error('WebGL 2 is required to draw') if (drawFbo == null) throw new Error('FBO is required to draw') if (query == null) throw new Error('Query is required to draw') - logger.log(bigintToHex(seed, 16)) + LOG: logger.log(bigintToHex(seed, 16)) // Upload work seed buffer inputSeedView.setBigUint64(0, seed, true) - logger.log('INPUT', inputView.buffer.slice(0)) + LOG: logger.log('INPUT', inputView.buffer.slice(0)) gl.bindBuffer(gl.UNIFORM_BUFFER, inputBuffer) gl.bufferSubData(gl.UNIFORM_BUFFER, 136, inputSeedView) gl.bindBuffer(gl.UNIFORM_BUFFER, null) @@ -386,7 +386,7 @@ function read (drawFbo: FBO): { work: bigint, difficulty: bigint } { // Loop pixels to find one with values for (let i = 0; i < pixels.length; i += 4) { if (pixels[i] || pixels[i + 1] || pixels[i + 2] || pixels[i + 3]) { - logger.log(`rgba(${pixels[i]}, ${pixels[i + 1]}, ${pixels[i + 2]}, ${pixels[i + 3]})`) + LOG: logger.log(`rgba(${pixels[i]}, ${pixels[i + 1]}, ${pixels[i + 2]}, ${pixels[i + 3]})`) resultView.setUint32(0, pixels[i], true) resultView.setUint32(4, pixels[i + 1], true) resultView.setUint32(8, pixels[i + 2], true) @@ -397,7 +397,7 @@ function read (drawFbo: FBO): { work: bigint, difficulty: bigint } { } } } - logger.log(`pixels[${pixels.length}]`, pixels) + LOG: logger.log(`pixels[${pixels.length}]`, pixels) throw new Error('Query reported result but nonce value not found') } @@ -410,10 +410,10 @@ export async function generate (hash: bigint, difficulty: bigint, effort: number let timeout = false const kill = setTimeout(() => { timeout = true - logger.log('timed out') + LOG: logger.log('timed out') }, 60_000) logger.groupStart('NanoPow WebGL work_generate') - logger.log('generating') + LOG: logger.log('generating') // Start drawing to calculate one nonce per pixel let found = false @@ -426,23 +426,23 @@ export async function generate (hash: bigint, difficulty: bigint, effort: number setup(effort) } init(bigintAsUintNArray(hash, 32, 8), difficulty) - logger.log('drawing frame 0') + LOG: logger.log('drawing frame 0') draw(bigintRandom(), drawFbos[0], queries[0]) draw(bigintRandom(), drawFbos[1], queries[1]) draw(bigintRandom(), drawFbos[2], queries[2]) let drawIndex = 3 do { - logger.log(`drawing frame ${drawIndex}`) + LOG: logger.log(`drawing frame ${drawIndex}`) draw(bigintRandom(), drawFbos[drawIndex], queries[drawIndex]) drawIndex = (drawIndex + 1) % 4 - logger.log(`checking frame ${drawIndex}`) + LOG: logger.log(`checking frame ${drawIndex}`) found = await check(queries[drawIndex]) } while (!found && !timeout) if (found) result = read(drawFbos[drawIndex]) isFirstRetry = false } catch (err) { isFirstRetry = !isFirstRetry - logger.log(err) + LOG: logger.log(err) if (!isFirstRetry) { throw new Error('failed to restore context', { cause: err }) } diff --git a/src/lib/generate/webgpu/index.ts b/src/lib/generate/webgpu/index.ts index 46e8903..5b7d4ec 100644 --- a/src/lib/generate/webgpu/index.ts +++ b/src/lib/generate/webgpu/index.ts @@ -33,16 +33,16 @@ let resultBuffers: GPUBuffer[] // Initialize WebGPU async function start (): Promise { if (status === 'Idle') { - logger.log('starting') + LOG: logger.log('starting') status = 'Starting' await getDevice() try { await compile() - logger.log('ready') + LOG: logger.log('ready') status = 'Ready' } catch (err) { status = 'Crashed' - logger.log(err) + LOG: logger.log(err) throw new Error('failed to compile', { cause: err }) } } @@ -64,7 +64,7 @@ async function getDevice () { throw new Error('failed to get device from gpu adapter') } device.lost?.then(async (deviceLostInfo) => { - logger.log('device lost', deviceLostInfo) + LOG: logger.log('device lost', deviceLostInfo) // Set up 30s timeout to prevent long-running calls isContextLost = window.setTimeout(() => { throw new Error('failed to restore device', { cause: deviceLostInfo }) @@ -154,7 +154,7 @@ async function compile () { } async function restore (): Promise { - logger.log('restoring') + LOG: logger.log('restoring') try { status = 'Restoring' for (let i = 0; i < 2; i++) { @@ -169,7 +169,7 @@ async function restore (): Promise { await compile() window.clearTimeout(isContextLost) isContextLost = 0 - logger.log('ready') + LOG: logger.log('ready') status = 'Ready' } catch (err) { status = 'Crashed' @@ -178,7 +178,7 @@ async function restore (): Promise { } async function init (hash: BigUint64Array, difficulty: bigint): Promise { - logger.log('variables initializing') + LOG: logger.log('variables initializing') try { // Save hash data for normal usage and potential recovery efforts hashData.set(hash) @@ -197,19 +197,19 @@ async function init (hash: BigUint64Array, difficulty: bigint): Promise { device.queue.writeBuffer(outputBuffers[0], 0, bufferReset) device.queue.writeBuffer(outputBuffers[1], 0, bufferReset) } catch (err) { - logger.log(err) + LOG: logger.log(err) throw new Error('failed to initialize', { cause: err }) } } async function dispatch (dispatchIndex: number, seed: bigint, effort: number): Promise { - logger.log('dispatching compute pass') + LOG: logger.log('dispatching compute pass') try { - logger.log('seed', bigintToHex(seed, 16)) + LOG: logger.log('seed', bigintToHex(seed, 16)) // Copy seed into INPUT buffer inputDataView.setBigUint64(40, seed, true) - logger.log('INPUT', inputDataView) + LOG: logger.log('INPUT', inputDataView) device.queue.writeBuffer(inputBuffers[dispatchIndex], 40, inputDataView, 40) // Create command encoder to issue commands to GPU and initiate computation @@ -229,23 +229,23 @@ async function dispatch (dispatchIndex: number, seed: bigint, effort: number): P // End computation by passing array of command buffers to command queue for execution device.queue.submit([commandEncoder.finish()]) } catch (err) { - logger.log(err) + LOG: logger.log(err) throw new Error('failed to dispatch compute pass', { cause: err }) } } async function check (dispatchIndex: number): Promise { - logger.log('checking results from compute pass') + LOG: logger.log('checking results from compute pass') try { await resultBuffers[dispatchIndex].mapAsync(GPUMapMode.READ) resultViews[dispatchIndex] = new DataView(resultBuffers[dispatchIndex].getMappedRange().slice(0)) resultBuffers[dispatchIndex].unmap() - logger.log('OUTPUT', resultViews[dispatchIndex]) + LOG: logger.log('OUTPUT', resultViews[dispatchIndex]) if (resultViews[dispatchIndex] == null) throw new Error('failed to get data from resultBuffer.') return !!resultViews[dispatchIndex].getUint32(0, true) } catch (err) { - logger.log(err) + LOG: logger.log(err) throw new Error('failed to read results from compute pass', { cause: err }) } } @@ -254,7 +254,7 @@ async function check (dispatchIndex: number): Promise { * Map CPU buffer to GPU, read results to static result object, and unmap. */ function read (dispatchIndex: number): { work: bigint, difficulty: bigint } { - logger.log('reading results from compute pass') + LOG: logger.log('reading results from compute pass') try { if (resultViews[dispatchIndex] == null) throw new Error('failed to get data from result view') return { @@ -262,7 +262,7 @@ function read (dispatchIndex: number): { work: bigint, difficulty: bigint } { difficulty: resultViews[dispatchIndex].getBigUint64(16, true) } } catch (err) { - logger.log(err) + LOG: logger.log(err) throw new Error('failed to read results from compute pass', { cause: err }) } } @@ -279,7 +279,7 @@ export async function generate (hash: bigint, difficulty: bigint, effort: number throw new Error('timed out') }, 60_000) logger.groupStart('NanoPow WebGPU work_generate') - logger.log('generating') + LOG: logger.log('generating') let found = false let result: { [key: string]: bigint } = {} let isFirstRetry = false @@ -312,7 +312,7 @@ export async function generate (hash: bigint, difficulty: bigint, effort: number } // Only retry once here, allow errors to propagate out to the consumer isFirstRetry = !isFirstRetry - logger.log(err) + LOG: logger.log(err) if (!isFirstRetry) { throw new Error('failed to restore device', { cause: err }) } diff --git a/src/lib/validate/index.ts b/src/lib/validate/index.ts index f7456e5..a5f7067 100644 --- a/src/lib/validate/index.ts +++ b/src/lib/validate/index.ts @@ -95,10 +95,10 @@ function blake2b (work: bigint, hash: bigint): void { function log (work: bigint, hash: bigint, difficulty: bigint): void { logger.groupStart('NanoPow CPU work_validate') - logger.log('NanoPow CPU work_validate', 'work', bigintToHex(work, 16)) - logger.log('NanoPow CPU work_validate', 'hash', bigintToHex(hash, 64)) - logger.log('NanoPow CPU work_validate', 'difficulty', bigintToHex(difficulty, 16)) - logger.log('NanoPow CPU work_validate', 'result', bigintToHex(result, 16)) + LOG: logger.log('NanoPow CPU work_validate', 'work', bigintToHex(work, 16)) + LOG: logger.log('NanoPow CPU work_validate', 'hash', bigintToHex(hash, 64)) + LOG: logger.log('NanoPow CPU work_validate', 'difficulty', bigintToHex(difficulty, 16)) + LOG: logger.log('NanoPow CPU work_validate', 'result', bigintToHex(result, 16)) logger.groupEnd('NanoPow CPU work_validate') } -- 2.47.3