From f738a2e8853d4dd7292dd43f5b1f85f92309c271 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Thu, 9 Jul 2026 06:59:33 -0700 Subject: [PATCH] Validate block hash range. Adjust bigint hex validation. --- src/lib/index.ts | 20 ++++++++++++-------- src/utils/bigint.ts | 7 +++++-- src/utils/index.ts | 10 +++++----- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 660ee59..994494a 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,14 +4,16 @@ import { NanoPowConfig } from '#lib/config' import { NanoPowCpu, NanoPowWasm, NanoPowWebgl, NanoPowWebgpu } from '#lib/generate' import { NanoPowValidate } from '#lib/validate' -import { big, Cache, Logger, Queue } from '#utils' +import { big, Cache, Logger, MAX_HASH, 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 { +export async function generate (blockhash: unknown, options?: unknown): Promise { const { api, debug, difficulty, effort } = await NanoPowConfig(options) + const hash = big(blockhash) + if (hash < 0 || hash > MAX_HASH) throw RangeError('invalid block hash', { cause: blockhash }) LOG: logger.isEnabled = debug const cached = Cache.search(hash, difficulty) if (cached) { @@ -31,16 +33,16 @@ export async function generate (hash: unknown, options?: unknown): Promise { +export async function validate (work: unknown, blockhash: unknown, options?: unknown): Promise { try { + const hash = big(blockhash) + if (hash < 0 || hash > MAX_HASH) throw RangeError('invalid block hash', { cause: blockhash }) const { debug, difficulty } = await NanoPowConfig(options) - const result = await NanoPowValidate(big(work), big(hash), difficulty, debug) + const result = await NanoPowValidate(big(work), hash, difficulty, debug) return result } catch (e: any) { return { error: (typeof e === 'string' ? e : (e?.message ?? '')) } diff --git a/src/utils/bigint.ts b/src/utils/bigint.ts index cc7dd4e..7c3019b 100644 --- a/src/utils/bigint.ts +++ b/src/utils/bigint.ts @@ -1,6 +1,8 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later +import { isHexN } from '#utils' + export function big (value: bigint | boolean | number | string | unknown): bigint { switch (typeof value) { case 'bigint': @@ -9,11 +11,12 @@ export function big (value: bigint | boolean | number | string | unknown): bigin return BigInt(value) } case 'string': { - const v = value.trim().replace(/^0[Xx]/, '').replace(/n$/, '') + const v = value.trim().replace(/^0[Xx]/, '').replace(/[Nn]$/, '') + if (!isHexN(v, null)) throw TypeError('invalid hex string', { cause: value }) return BigInt(`0x${v}`) } default: { - throw new TypeError(`can't convert value to BigInt`) + throw TypeError(`can't convert value to BigInt`) } } } diff --git a/src/utils/index.ts b/src/utils/index.ts index dcf6726..cb8b7ff 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -9,6 +9,7 @@ export * from './queue' export const SEND = 0xfffffff800000000n as const export const RECEIVE = 0xfffffe0000000000n as const +export const MAX_HASH = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn as const type Averages = { count: number, @@ -33,16 +34,15 @@ type Averages = { /** * Checks if value is a string representing an N-byte hexadecimal number. * -* @param {unknown} value - Value to check. +* @param {unknown} value - String to check. Be sure to trim whitespace, hex '0x' prefix, and bigint 'n' suffix. * @param {(number|null)} byteLength - Number of bytes the string should represent, or 'null' for any number greater than zero. * @returns True if value is a 2N-character hexadecimal string, else false. */ export function isHexN (value: unknown, byteLength: number | null): value is string { if (typeof value !== 'string') return false - const v = value.replace(/^0[Xx]/, '') - const length = byteLength === null ? '+' : `{${2 * byteLength}}` - const r = new RegExp(`^[A-Fa-f\\d]${length}\$`) - return r.test(v) + const length = byteLength === null ? '+' : `{${byteLength << 1}}` + const r = new RegExp(`^[0-9a-f]${length}$`, 'i') + return r.test(value) } /** -- 2.52.0