import { NanoPowConfig } from '#lib/config'
import { NanoPowCpu, NanoPowWasm, NanoPowWebgl, NanoPowWebgpu } from '#lib/generate'
import { NanoPowValidate } from '#lib/validate'
-import { WorkErrorResponse, WorkGenerateResponse, WorkValidateResponse } from '#types'
import { bigintFrom, Cache, Logger, Queue } from '#utils'
++import { WorkErrorResponse, WorkGenerateResponse, WorkValidateResponse } from 'nano-pow'
const logger = new Logger()
const q = new Queue()
try {
const { api, debug, difficulty, effort } = await NanoPowConfig(options)
LOG: logger.isEnabled = debug
- const cached = Cache.search(hash, difficulty)
+ const bigintHash = bigintFrom(hash, 'hex')
+ const cached = Cache.search(bigintHash, difficulty)
if (cached) {
LOG: logger.log('found work in cache')
- return cached
+ const { valid } = NanoPowValidate(bigintFrom(cached.work, 'hex'), bigintFrom(cached.hash, 'hex'), bigintFrom(cached.difficulty, 'hex'), debug)
+ if (valid === '1') {
+ return cached
+ } else {
+ try {
+ Cache.delete(hash)
+ } catch (err) {
+ LOG: logger.log('error deleting invalid work from cache, continuing with generate', err)
+ }
+ }
}
switch (api) {
case 'webgpu': {
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { WorkGenerateResponse } from "#types"
-import { bigintFrom } from "#utils"
+import { WorkGenerateResponse } from 'nano-pow'
-export class Cache {
+const STORAGE_KEY = 'NanoPowCache'
- static clear (): void {
- this.#removeItem('NanoPowCache')
- }
+let storage: { [key: string]: string } = {}
- static delete (hash: unknown): void {
- const bigintHash = bigintFrom(hash, 'hex')
- const item = this.#getItem('NanoPowCache')
- if (item == null) return
- const cache = JSON.parse(item) as WorkGenerateResponse[]
- for (let i = 0; i < cache.length; i++) {
- if (bigintFrom(cache[i].hash, 'hex') === bigintHash) {
- cache.splice(i, 1)
- }
- }
- this.#setItem('NanoPowCache', JSON.stringify(cache))
- }
+function getItem (key: string): string | null {
+ if (globalThis?.localStorage) return globalThis.localStorage.getItem(key)
+ return storage[key]
+}
- static search (hash: unknown, difficulty: bigint): WorkGenerateResponse | null {
- const bigintHash = bigintFrom(hash, 'hex')
- const item = this.#getItem('NanoPowCache')
- if (item) {
- const cache = JSON.parse(item) as WorkGenerateResponse[]
- for (const c of cache) {
- if (bigintFrom(c.hash, 'hex') === bigintHash && bigintFrom(c.difficulty, 'hex') >= difficulty) {
- return c
- }
- }
+function removeItem (key: string): void {
+ if (globalThis?.localStorage) return globalThis.localStorage.removeItem(key)
+ storage = {}
+}
+
+function setItem (key: string, item: string): void {
+ if (globalThis?.localStorage) return globalThis.localStorage.setItem(key, item)
+ storage[key] = item
+}
+
+export function clear (): void {
+ removeItem(STORAGE_KEY)
+}
+
- export function search (hash: bigint, difficulty: bigint): WorkGenerateResponse | null {
++export function get (hash: bigint, difficulty: bigint): WorkGenerateResponse | null {
+ const item = getItem(STORAGE_KEY)
- if (item) {
- const cache = JSON.parse(item) as WorkGenerateResponse[]
- for (const c of cache) {
- if (BigInt(`0x${c.hash}`) === hash && BigInt(`0x${c.difficulty}`) >= difficulty) {
- return c
- }
++ if (item == null) return null
++ const cache: WorkGenerateResponse[] = JSON.parse(item)
++ for (const c of cache) {
++ if (BigInt(`0x${c.hash}`) === hash && BigInt(`0x${c.difficulty}`) >= difficulty) {
++ return c
}
- return null
}
+ return null
+}
- export function store (result: WorkGenerateResponse): WorkGenerateResponse {
- static store (result: WorkGenerateResponse): WorkGenerateResponse {
- const item = this.#getItem('NanoPowCache')
- const cache = JSON.parse(item ?? '[]') as WorkGenerateResponse[]
- if (cache.push(result) > 1000) cache.shift()
- this.#setItem('NanoPowCache', JSON.stringify(cache))
- return result
++export function remove (hash: bigint): void {
+ const item = getItem(STORAGE_KEY)
- const cache = JSON.parse(item ?? '[]') as WorkGenerateResponse[]
++ if (item == null) return
++ const cache = JSON.parse(item) as WorkGenerateResponse[]
++ for (let i = 0; i < cache.length; i++) {
++ if (BigInt(`0x${cache[i].hash}`) === hash) {
++ cache.splice(i, 1)
++ }
+ }
++ setItem('NanoPowCache', JSON.stringify(cache))
++}
+
- static #storage: { [key: string]: string } = {}
- static #getItem (key: string): string | null {
- if (globalThis?.localStorage) return globalThis.localStorage.getItem(key)
- return this.#storage[key]
- }
- static #removeItem (key: string): void {
- if (globalThis?.localStorage) return globalThis.localStorage.removeItem(key)
- this.#storage = {}
- }
- static #setItem (key: string, item: string): void {
- if (globalThis?.localStorage) return globalThis.localStorage.setItem(key, item)
- this.#storage[key] = item
- }
++export function set (result: WorkGenerateResponse): WorkGenerateResponse {
++ const item = getItem(STORAGE_KEY) ?? '[]'
++ const cache: WorkGenerateResponse[] = JSON.parse(item)
+ if (cache.push(result) > 1000) cache.shift()
+ setItem(STORAGE_KEY, JSON.stringify(cache))
+ return result
}
- export const Cache = { clear, search, store }
+
++export const Cache = { clear, delete: remove, search: get, store: set }
const glSize = (canvas => {
const gl = canvas.getContext('webgl2')
- const MAX_VIEWPORT_DIMS = gl.getParameter(gl.MAX_VIEWPORT_DIMS)
- canvas.height = MAX_VIEWPORT_DIMS?.[0] ?? 0x1000
- canvas.width = MAX_VIEWPORT_DIMS?.[1] ?? 0x1000
- return Math.min(gl.drawingBufferHeight, gl.drawingBufferWidth)
+ const MAX_VIEWPORT_DIMS = gl?.getParameter(gl.MAX_VIEWPORT_DIMS) ?? [0x1000, 0x1000]
+ const size = Math.min(0x2000, ...MAX_VIEWPORT_DIMS)
+ canvas.height = canvas.width = size
+ return gl?.drawingBufferHeight < gl?.drawingBufferWidth ? gl?.drawingBufferHeight : gl?.drawingBufferWidth
})(new OffscreenCanvas(0, 0))
- function random(size = 64) {
+ function random (size = 64) {
let hex = ''
while (hex.length < size) {
hex += crypto.randomUUID().replace(/-.*-/g, '')