]> git.codecow.com Git - nano-pow.git/commitdiff
Validate block hash range. Adjust bigint hex validation.
authorChris Duncan <chris@codecow.com>
Thu, 9 Jul 2026 13:59:33 +0000 (06:59 -0700)
committerChris Duncan <chris@codecow.com>
Fri, 10 Jul 2026 05:20:50 +0000 (22:20 -0700)
src/lib/index.ts
src/utils/bigint.ts
src/utils/index.ts

index 660ee59734d55e77cb2d11ff8de060397c8416b9..994494a1705f9bd4c043a28bada45fefcbc0276c 100644 (file)
@@ -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<WorkGenerateResponse | WorkErrorResponse> {
+export async function generate (blockhash: unknown, options?: unknown): Promise<WorkGenerateResponse | WorkErrorResponse> {
        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<WorkG
                try {
                        switch (api) {
                                case 'webgpu': {
-                                       return Cache.store(await NanoPowWebgpu(big(hash), difficulty, effort, debug))
+                                       return Cache.store(await NanoPowWebgpu(hash, difficulty, effort, debug))
                                }
                                case 'webgl': {
-                                       return Cache.store(await NanoPowWebgl(big(hash), difficulty, effort, debug))
+                                       return Cache.store(await NanoPowWebgl(hash, difficulty, effort, debug))
                                }
                                case 'wasm': {
-                                       return Cache.store(await NanoPowWasm(big(hash), difficulty, effort, debug))
+                                       return Cache.store(await NanoPowWasm(hash, difficulty, effort, debug))
                                }
                                default: {
-                                       return Cache.store(await NanoPowCpu(big(hash), difficulty, debug))
+                                       return Cache.store(await NanoPowCpu(hash, difficulty, debug))
                                }
                        }
                } catch (e: any) {
@@ -49,10 +51,12 @@ export async function generate (hash: unknown, options?: unknown): Promise<WorkG
        })
 }
 
-export async function validate (work: unknown, hash: unknown, options?: unknown): Promise<WorkValidateResponse | WorkErrorResponse> {
+export async function validate (work: unknown, blockhash: unknown, options?: unknown): Promise<WorkValidateResponse | WorkErrorResponse> {
        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 ?? '')) }
index cc7dd4ed0b48788e22854e7798abe9231a265d0a..7c3019b8e83ff9daa888358afbef13723b35e3e1 100644 (file)
@@ -1,6 +1,8 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
 //! 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`)
                }
        }
 }
index dcf6726d20b6fca781540008c8e411dc1aacbad0..cb8b7ffbd1c9976f2b7eea3bbc732e5d8c9cbfea 100644 (file)
@@ -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)
 }
 
 /**