//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { ApiSupport, bigintFrom, bigintToHex, SEND } from '#utils'
+import { ApiSupport, SEND } from '#utils'
type ApiSupportedTypes = keyof typeof ApiSupport
return {
api: this.api,
debug: this.debug,
- difficulty: bigintToHex(this.difficulty, 16),
+ difficulty: this.difficulty.toString(16).padStart(16, '0'),
effort: this.effort
}
}
static #getValidDifficulty (input: Record<string, unknown>): bigint {
if (input != null && input.difficulty != null) {
if (typeof input.difficulty === 'string') {
- try {
- input.difficulty = bigintFrom(input.difficulty, 'hex')
- } catch { }
+ input.difficulty = input.difficulty.trim().toLowerCase().replace(/n$/, '')
+ input.difficulty = /^0[box]/i.test(input.difficulty as string) ? input.difficulty : `0x${input.difficulty}`
+ input.difficulty = BigInt(input.difficulty as string)
}
if (typeof input.difficulty !== 'bigint') {
- throw new Error(`Invalid difficulty (${typeof input.difficulty})${input.difficulty}`)
+ throw new Error(`Invalid difficulty ${input.difficulty}`)
}
- if (input.difficulty < 0x0n || input.difficulty > SEND) {
- throw new Error(`Invalid difficulty ${bigintToHex(input.difficulty, 16)}`)
+ if (input.difficulty < 0n || input.difficulty > SEND) {
+ throw new Error(`Invalid difficulty ${input.difficulty.toString(16).padStart(16, '0')}`)
}
return input.difficulty
}
import { NanoPowConfig } from '#lib/config'
import { NanoPowCpu, NanoPowWasm, NanoPowWebgl, NanoPowWebgpu } from '#lib/generate'
import { NanoPowValidate } from '#lib/validate'
-import { Bytes, Cache, Logger, Queue, bigintFrom } from '#utils'
+import { Bytes, Cache, Logger, Queue } from '#utils'
import { WorkErrorResponse, WorkGenerateResponse, WorkValidateResponse } from 'nano-pow'
const logger = new Logger()
const q = new Queue()
export async function generate (hash: unknown, options?: unknown): Promise<WorkGenerateResponse | WorkErrorResponse> {
+ if (typeof hash === 'bigint') {
+ hash = hash.toString(16).padStart(64, '0')
+ }
+ if (typeof hash !== 'string') {
+ throw new TypeError('invalid hash', { cause: hash })
+ }
+ const hashBytes = Bytes(hash, 32)
+ const { api, debug, difficulty, effort } = await NanoPowConfig(options)
+ LOG: logger.isEnabled = debug
+ const cached = Cache.search(hash, difficulty)
+ if (cached) {
+ LOG: logger.log('found work in cache')
+ const { valid } = NanoPowValidate(Bytes(cached.work, 8), hashBytes, difficulty, 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)
+ }
+ }
+ }
return q.add(async (): Promise<WorkGenerateResponse | WorkErrorResponse> => {
try {
- const { api, debug, difficulty, effort } = await NanoPowConfig(options)
- LOG: logger.isEnabled = debug
- const bigintHash = bigintFrom(hash, 'hex')
- const cached = Cache.search(bigintHash, difficulty)
- if (cached) {
- LOG: logger.log('found work in cache')
- const { valid } = NanoPowValidate(Bytes(cached.work, 8), Bytes(cached.hash, 32), bigintFrom(cached.difficulty, 'hex'), debug)
- if (valid === '1') {
- return cached
- } else {
- try {
- Cache.delete(bigintHash)
- } catch (err) {
- LOG: logger.log('error deleting invalid work from cache, continuing with generate', err)
- }
- }
- }
- const hashBytes = Bytes(bigintHash, 32)
switch (api) {
case 'webgpu': {
return Cache.store(await NanoPowWebgpu(hashBytes, difficulty, effort, debug))
return byteLength
}
-export function bigintFrom (value: bigint | boolean | number | string | unknown, type?: 'bin' | 'oct' | 'hex'): bigint {
- if (typeof value === 'bigint') {
- return value
- } else if (typeof value === 'boolean' || typeof value === 'number') {
- return BigInt(value)
- } else if (typeof value === 'string') {
- const v = value.trim().replace(/n$/, '')
- if (/^0[Bb][01]+$/.test(v)
- || /^0[Oo][0-7]+$/.test(v)
- || /^0[Xx][A-Fa-f\d]+$/.test(v)
- || /^\d+$/.test(v)) {
- return BigInt(v)
- }
- if (type === 'bin' && /^[01]+$/.test(v)) {
- return BigInt(`0b${v}`)
- }
- if (type === 'oct' && /^[0-7]+$/.test(v)) {
- return BigInt(`0o${v}`)
- }
- if (type === 'hex' || /^\d*[A-Fa-f]+\d*$/.test(v)) {
- return BigInt(`0x${v}`)
- }
- }
- throw new TypeError(`can't convert string to BigInt`)
-}
-
export function bigintRandom (max: bigint = 0xFFFFFFFFFFFFFFFFn): bigint {
if (typeof max !== 'bigint' || max < 1n) {
throw new TypeError('Invalid max value')
if (typeof input === 'number') {
input = BigInt(input | 0)
}
- if (typeof input === 'bigint') {
- input = input.toString(16)
+ if (typeof input === 'string') {
+ input = input.trim().toLowerCase().replace(/n$/, '')
+ input = /^0[box]/i.test(input as string) ? input : `0x${input}`
+ input = BigInt(input as string)
}
- if (typeof input === 'string' && input.length & 1) {
- input = `0${input}`
- }
- if (typeof input !== 'string' || !/^([a-f0-9]{2})+$/i.test(input)) {
+ if (typeof input !== 'bigint' || input < 0n) {
throw new TypeError('invalid Bytes input', { cause: input })
}
if (typeof minLength !== 'number' || minLength < 1) {
throw new TypeError('invalid minLength', { cause: minLength })
}
- const bytes = input.match(/.{2}/g)?.map(int => parseInt(int, 16)) ?? []
- const filler = new Array(minLength > bytes.length ? minLength - bytes.length : 0)
- return new Uint8Array([...filler, ...bytes])
+ const bytes: number[] = []
+ for (let i = 0; i < minLength || input > 0n; i++) {
+ bytes.push(Number(input & 0xffn))
+ input >>= 8n
+ }
+ return new Uint8Array(bytes.reverse())
}
/**
removeItem(STORAGE_KEY)
}
-function get (hash: bigint, difficulty: bigint): WorkGenerateResponse | null {
+function get (hash: string, difficulty: bigint): WorkGenerateResponse | null {
const item = getItem(STORAGE_KEY)
if (item == null) return null
const cache: WorkGenerateResponse[] = JSON.parse(item)
- const match = cache.find(c => eq(c.hash, hash) && eq(c.difficulty, difficulty))
+ const match = cache.find(c => c.hash === hash && eq(c.difficulty, difficulty))
return match ?? null
}
-function remove (hash: bigint): void {
+function remove (hash: string): void {
const item = getItem(STORAGE_KEY)
if (item == null) return
const cache = JSON.parse(item) as WorkGenerateResponse[]
for (let i = 0; i < cache.length; i++) {
- if (eq(cache[i].hash, hash)) {
+ if (cache[i].hash === hash) {
cache.splice(i, 1)
}
}