\r
# dotenv environment variables file\r
.env\r
-env.*\r
\r
# Dependency directories\r
node_modules/\r
--- /dev/null
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { ERROR_UNKNOWN, } from './errors'
+
+/**
+ * The module's only import, installed as AssemblyScript's `abort` by
+ * `use: ["abort=src/assembly/abort/abort"]` in `asconfig.json`.
+ *
+ * It takes four integers and no pointers so the host never reads WASM memory
+ * to report an error unlike the default `abort` which decodes a UTF-16 string
+ * from linear memory using pointer arithmetic.
+ */
+//@ts-expect-error
+@external('env', 'abort')
+declare function env_abort (code: i32, line: i32, detail: i32, context: i32): void
+
+let errorCode: i32 = ERROR_UNKNOWN
+let errorDetail: i32 = 0
+let errorContext: i32 = 0
+
+/**
+ * Reads the pending code, resets it, and hands it to the host. Resetting
+ * *before* the call matters: `env_abort` throws, so nothing after it runs, and
+ * a stale code would otherwise be attributed to the next uncoded abort.
+ */
+export function abort (message: usize, fileName: usize, line: u32, col: u32): void {
+ const code = errorCode
+ const detail = errorDetail
+ const context = errorContext
+ errorCode = ERROR_UNKNOWN
+ errorDetail = 0
+ errorContext = 0
+ env_abort(code, i32(line), detail, context)
+}
+
+/**
+ * Throws, reporting `code` and up to two context integers to the host.
+ *
+ * **Must stay `@inline`.** AssemblyScript fills in the line number at the
+ * `throw`, so inlining is what makes the host report the caller's line rather
+ * than this file's. Also do not pass anything to `Error()` since that will flag
+ * to AssemblyScript that String functionality should be included in the runtime
+ * stub.
+ */
+//@ts-expect-error
+@inline
+export function raise (code: i32, a: i32 = 0, b: i32 = 0): void {
+ errorCode = code
+ errorDetail = a
+ errorContext = b
+ throw new Error()
+}
Mutex.lock()
try {
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_PRV(i)(privateKey[i])
+ MemoryBuffers.INPUT_PRV(i, privateKey[i])
}
wasm_derive()
for (let i = 0; i < KEY_BYTELENGTH; i++) {
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { Nano25519WasmError } from './primordials'
+export class Nano25519TypeError extends TypeError { }
+export class Nano25519WasmError extends Error { }
/**
* Message text for the codes in `src/assembly/errors.ts`. The numbers are the
* handler has to exist *before* instantiation: the `equalbytes` guard runs in
* the start function, so it can abort while `exports` is still undefined.
*/
-export const ERROR_UNKNOWN = 0
-export const ERROR_INVALID_PUBLIC_KEY = 1
-export const ERROR_INVALID_MESSAGE_LENGTH = 2
-export const ERROR_INVALID_BLOCK_COUNT = 3
-export const ERROR_INDEX_OUT_OF_RANGE = 4
-export const ERROR_BYTE_OUT_OF_RANGE = 5
-export const ERROR_SELFTEST_XPASS = 6
-export const ERROR_SELFTEST_XFAIL_ONE = 7
-export const ERROR_SELFTEST_XFAIL_PAIR = 8
+export const errorCodes: Record<string, number> = {
+ ERROR_UNKNOWN: 0,
+ ERROR_INVALID_PUBLIC_KEY: 1,
+ ERROR_INVALID_MESSAGE_LENGTH: 2,
+ ERROR_INVALID_BLOCK_COUNT: 3,
+ ERROR_INDEX_OUT_OF_RANGE: 4,
+ ERROR_BYTE_OUT_OF_RANGE: 5,
+ ERROR_SELFTEST_XPASS: 6,
+ ERROR_SELFTEST_XFAIL_ONE: 7,
+ ERROR_SELFTEST_XFAIL_PAIR: 8,
+}
const VECTORS = ['zeros', 'ones', 'spread']
/** Buffer ids from `src/assembly/errors.ts`, in value order. */
const BUFFERS = ['INPUT_MSG', 'INPUT_PRV', 'INPUT_PUB', 'INPUT_SIG', 'OUTPUT_DERIVE', 'OUTPUT_SIGN', 'OUTPUT_VERIFY']
-/**
- * Source file per code. `env.abort` no longer receives a file pointer, so the
- * code carries that instead — which holds only while **each code is raised
- * from exactly one file**. Reusing a code across files makes this lie.
- */
-const FILES: Record<number, string> = {
- [ERROR_INVALID_PUBLIC_KEY]: 'src/assembly/index.ts',
- [ERROR_INVALID_MESSAGE_LENGTH]: 'src/assembly/index.ts',
- [ERROR_INVALID_BLOCK_COUNT]: 'src/assembly/index.ts',
- [ERROR_INDEX_OUT_OF_RANGE]: 'src/assembly/index.ts',
- [ERROR_BYTE_OUT_OF_RANGE]: 'src/assembly/index.ts',
- [ERROR_SELFTEST_XPASS]: 'src/assembly/tests.ts',
- [ERROR_SELFTEST_XFAIL_ONE]: 'src/assembly/tests.ts',
- [ERROR_SELFTEST_XFAIL_PAIR]: 'src/assembly/tests.ts'
-}
-
/**
* Rebuilds a self-test vector. These are the same three definitions as
* `src/assembly/tests.ts`; keeping them here is what lets the host report the
const name = VECTORS[v] ?? `vector ${v}`
const expected = vector(v, length)
const actual = vector(v, length)
- if (code === ERROR_SELFTEST_XFAIL_ONE) {
+ if (code === errorCodes.ERROR_SELFTEST_XFAIL_ONE) {
const position = (detail >> 8) & 0xff
const mask = detail & 0xff
actual[position] ^= mask
return `equalbytes build error from "${name}": expected fail [${expected}]; actual pass [${actual}] (byte ${position} ^ 0x${mask.toString(16).padStart(2, '0')})`
}
- if (code === ERROR_SELFTEST_XFAIL_PAIR) {
+ if (code === errorCodes.ERROR_SELFTEST_XFAIL_PAIR) {
const i = (detail >> 8) & 0xff
const j = detail & 0xff
actual[i] ^= 0x80
return `equalbytes build error from "${name}": expected pass [${expected}]; actual fail`
}
-/** Turns the four integers `env.abort` reports into a message. */
-export function describe (code: number, line: number, a: number, b: number): string {
- let text: string
+/**
+ * Turns the four integers reported by `env.abort` into a message. `line` only
+ * locates an ERROR_UNKNOWN abort: every coded site reports the `throw` inside
+ * `raise` because AssemblyScript fixes the line before inlining, so the code
+ * and its details are the location for coded errors.
+ */
+function describe (code: number, line: number, detail: number, context: number): string {
switch (code) {
- case ERROR_INVALID_PUBLIC_KEY:
- text = 'Invalid public key'
- break
- case ERROR_INVALID_MESSAGE_LENGTH:
- text = `Invalid message length: ${a} is not in [0, ${b}]`
- break
- case ERROR_INVALID_BLOCK_COUNT:
- text = `Invalid block count: ${a} is not in [1, ${b}]`
- break
- case ERROR_INDEX_OUT_OF_RANGE:
- text = `Buffer index out of range for ${BUFFERS[a] ?? `buffer ${a}`}: ${b}`
- break
- case ERROR_BYTE_OUT_OF_RANGE:
- text = `Byte value out of range for ${BUFFERS[a] ?? `buffer ${a}`}: ${b} is not in [0, 255]`
- break
- case ERROR_SELFTEST_XPASS:
- case ERROR_SELFTEST_XFAIL_ONE:
- case ERROR_SELFTEST_XFAIL_PAIR:
- text = selftest(code, a, b, 32)
- break
+ case errorCodes.ERROR_INVALID_PUBLIC_KEY:
+ return 'Invalid public key'
+ case errorCodes.ERROR_INVALID_MESSAGE_LENGTH:
+ return `Invalid message length: ${detail} is not in [0, ${context}]`
+ case errorCodes.ERROR_INVALID_BLOCK_COUNT:
+ return `Invalid block count: ${detail} is not in [1, ${context}]`
+ case errorCodes.ERROR_INDEX_OUT_OF_RANGE:
+ return `Buffer index out of range for ${BUFFERS[detail] ?? `buffer ${detail}`}: ${context}`
+ case errorCodes.ERROR_BYTE_OUT_OF_RANGE:
+ return `Byte value out of range for ${BUFFERS[detail] ?? `buffer ${detail}`}: ${context} is not in [0, 255]`
+ case errorCodes.ERROR_SELFTEST_XPASS:
+ case errorCodes.ERROR_SELFTEST_XFAIL_ONE:
+ case errorCodes.ERROR_SELFTEST_XFAIL_PAIR:
+ return selftest(code, detail, context, 32)
default:
- text = `Unrecognized wasm abort (code ${code}, a ${a}, b ${b})`
+ return `Unrecognized wasm abort (code ${code}, a ${detail}, b ${context}), row ${line}`
}
- // `line` only locates an ERROR_UNKNOWN abort. Every coded site reports the
- // `throw` inside `raise`, because AssemblyScript fixes the line before
- // inlining — so for coded errors the code and its details are the location.
- return code === ERROR_UNKNOWN
- ? `${text}, row ${line}`
- : `${text}, ${FILES[code] ?? 'unknown file'}`
}
/** The module's only import. Takes four integers and never reads wasm memory. */
* bundler will order that dependency first, and any call it makes into here
* during its own evaluation will read captures that are still unassigned.
*/
-
export type Bytes = Uint8Array<ArrayBuffer>
+export const isArray = Array.isArray
-/** Declared here to avoid import loops. */
-export class Nano25519TypeError extends TypeError { }
-export class Nano25519WasmError extends Error { }
-
-const isArray = Array.isArray
-
-/** `TypedArray` constructors, bound before anything can replace the global. */
+/** `TypedArray` constructor, bound before anything can replace the global. */
const Bytes = Uint8Array
-const Words = Uint16Array
-const Longs = Uint32Array
-
const TypedArray = Object.getPrototypeOf(Bytes.prototype)
const bytesSet = Bytes.prototype.set
const getBufferByteLength = get<number>(ArrayBuffer.prototype, 'byteLength')
/** Reads internal slot `[[ByteLength]]`. */
const getByteLength = get<number>(TypedArray, 'byteLength')
-/** Reads the live backing store of a `WebAssembly.Memory`. */
-const getMemoryBuffer = get<ArrayBuffer>(WebAssembly.Memory.prototype, 'buffer')
-
-/** Reads one unsigned 32-bit word from `buffer` at byte `offset`. */
-function readLong (buffer: ArrayBufferLike, offset: number): number {
- return new Longs(buffer)[offset >>> 2]
-}
-
-const fromCharCode = String.fromCharCode
-/**
- * Reads a UTF-16 string of `length` code units from `buffer` at `offset`.
- * Used only by the WASM abort handler. Builds the string one code unit at a
- * time rather than spreading a subarray, which would run the iterator and
- * risk a stack overflow on a long message.
- */
-function utf16 (buffer: ArrayBufferLike, offset: number, length: number): string {
- const words = new Words(buffer)
- let string = ''
- for (let i = 0; i < length; i++) {
- string += fromCharCode(words[offset + i])
- }
- return string
-}
/** True for a genuine `ArrayBuffer`, false for a `SharedArrayBuffer`. */
function isArrayBuffer (a: unknown): a is ArrayBuffer {
* Reads internal slot `[[TypedArrayName]]`, so a subclass overriding
* `Symbol.toStringTag` cannot pass, and a look-alike object cannot either.
*/
-function isBytes (a: unknown): a is Bytes {
+export function isBytes (a: unknown): a is Bytes {
return getTag.call(a) === 'Uint8Array' && isArrayBuffer(getBuffer.call(a))
}
-/**
- * The live backing buffer of `memory`. Read through the captured accessor
- * because every host read and write goes through it: a patched getter would
- * redirect the whole I/O path to a decoy buffer, and the detachment check
- * cannot notice because both of its operands come from the same getter.
- */
-function memoryBuffer (memory: WebAssembly.Memory): ArrayBuffer {
- return getMemoryBuffer.call(memory)
-}
-
-/** Checks if WASM memory buffer was detached. */
-function validateMemoryBuffer (memory: WebAssembly.Memory, bytes: Uint8Array): void {
- if (getMemoryBuffer.call(memory) !== getBuffer.call(bytes)) {
- throw new Nano25519TypeError('WASM memory buffer detached')
- }
-}
-
/** Captured version of `new Uint8Array(byteLength)`. */
-function allocate (length: number): Bytes {
+export function allocate (length: number): Bytes {
return new Bytes(length)
}
/** Captured version of `Uint8Array.prototype.byteLength`. */
-function byteLength (bytes: Uint8Array): number {
+export function byteLength (bytes: Uint8Array): number {
return getByteLength.call(bytes)
}
* `Symbol.species`, so it cannot hand control to a caller-supplied subclass,
* and it sizes the copy from an internal slot rather than a spoofable `length`.
*/
-function copy (src: Uint8Array): Bytes
+export function copy (src: Uint8Array): Bytes
/** `target.set(source, offset)` through the captured method. */
-function copy (dst: Uint8Array, src: Uint8Array, offset?: number): void
-function copy (a: Uint8Array, b?: Uint8Array, o?: number): void | Bytes {
+export function copy (dst: Uint8Array, src: Uint8Array, offset?: number): void
+export function copy (a: Uint8Array, b?: Uint8Array, o?: number): void | Bytes {
if (b) {
return bytesSet.call(a, b, o)
}
}
/** Captured version of `target.fill(value, start, end)`. */
-function fill (target: Uint8Array, value: number, start?: number, end?: number): void {
+export function fill (target: Uint8Array, value: number, start?: number, end?: number): void {
bytesFill.call(target, value, start, end)
}
/** Captured version of `new Uint8Array(buffer)`. */
-function view (buffer: ArrayBuffer): Bytes {
+export function view (buffer: ArrayBuffer): Bytes {
return new Bytes(buffer)
}
NIBBLE[UPPER[i]] = i
}
+/** Lowercase hexadecimal encoding of `bytes`. */
+export function bytesToHex (bytes: Uint8Array): string {
+ const byteLength = getByteLength.call(bytes)
+ let hex = ''
+ for (let i = 0; i < byteLength; i++) {
+ const byte = bytes[i]
+ hex += LOWER[byte >> 4] + LOWER[byte & 15]
+ }
+ return hex
+}
+
/**
* Decodes hex string to Bytes. Validates input directly instead of using a
* patchable `RegExp.prototype.test()`.
}
return bytes
}
-
-/** Lowercase hexadecimal encoding of `bytes`. */
-function bytesToHex (bytes: Uint8Array): string {
- const byteLength = getByteLength.call(bytes)
- let hex = ''
- for (let i = 0; i < byteLength; i++) {
- const byte = bytes[i]
- hex += LOWER[byte >> 4] + LOWER[byte & 15]
- }
- return hex
-}
-
/**
* Accepts an unknown input; checks that it is a representation of bytes using a
* hex string, ArrayBuffer, or Uint8Array; and validates that the byte length of
* @param {unknown} value User input to validate
* @returns User input bytes copied to a new Uint8Array for internal use.
*/
-function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Bytes {
+export function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Bytes {
if (typeof value === 'string') {
if (value.length < (byteLengthMin << 1) || value.length > (byteLengthMax << 1)) {
throw new TypeError(`Invalid hexadecimal length ${value.length} for ${name}`)
}
return bytes
}
-
-export { allocate, byteLength, bytesToHex, copy, fill, isArray, isBytes, memoryBuffer, normalize, readLong, utf16, validateMemoryBuffer, view }
//! SPDX-License-Identifier: GPL-3.0-or-later
import { allocate, byteLength, bytesToHex, copy, fill, isBytes, normalize } from './primordials'
-import { clearMemory, constants, MemoryBuffers, Mutex, sign as wasm_sign } from './wasm'
+import { MemoryBuffers, Mutex, clearMemory, constants, sign as wasm_sign } from './wasm'
const { KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, SIGNATURE_BYTELENGTH, } = constants
Mutex.lock()
try {
for (let i = 0; i < message.byteLength; i++) {
- MemoryBuffers.INPUT_MSG(i)(message[i])
+ MemoryBuffers.INPUT_MSG(i, message[i])
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_PRV(i)(privateKey[i])
+ MemoryBuffers.INPUT_PRV(i, privateKey[i])
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_PUB(i)(publicKey[i])
+ MemoryBuffers.INPUT_PUB(i, publicKey[i])
}
wasm_sign(message.byteLength)
for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) {
//! SPDX-License-Identifier: GPL-3.0-or-later
import { isArray, normalize } from './primordials'
-import { clearMemory, constants, MemoryBuffers, Mutex, verify as wasm_verify, verify_blocks as wasm_verify_blocks, } from './wasm'
+import { MemoryBuffers, Mutex, clearMemory, constants, verify as wasm_verify, verify_blocks as wasm_verify_blocks, } from './wasm'
const { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, SIGNATURE_BYTELENGTH, } = constants
const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub)
const signature = normalize('signature', SIGNATURE_BYTELENGTH, SIGNATURE_BYTELENGTH, sig)
for (let i = 0; i < message.byteLength; i++) {
- MemoryBuffers.INPUT_MSG(i)(message[i])
+ MemoryBuffers.INPUT_MSG(i, message[i])
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_PUB(i)(publicKey[i])
+ MemoryBuffers.INPUT_PUB(i, publicKey[i])
}
for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_SIG(i)(signature[i])
+ MemoryBuffers.INPUT_SIG(i, signature[i])
}
wasm_verify(message.byteLength)
const verified = MemoryBuffers.OUTPUT_VERIFY(0)
try {
const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub)
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_PUB(i)(publicKey[i])
+ MemoryBuffers.INPUT_PUB(i, publicKey[i])
}
for (let b = 0, p = 0; b < count; b++) {
const block = blocks[b]
const signature = normalize('signature', SIGNATURE_BYTELENGTH, SIGNATURE_BYTELENGTH, block.signature)
const hash = normalize('hash', BLOCKHASH_BYTELENGTH, BLOCKHASH_BYTELENGTH, block.hash)
for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_MSG(p + i)(signature[i])
+ MemoryBuffers.INPUT_MSG(p + i, signature[i])
}
p += SIGNATURE_BYTELENGTH
for (let i = 0; i < BLOCKHASH_BYTELENGTH; i++) {
- MemoryBuffers.INPUT_MSG(p + i)(hash[i])
+ MemoryBuffers.INPUT_MSG(p + i, hash[i])
}
p += BLOCKHASH_BYTELENGTH
}
//@ts-expect-error
import nano25519_wasm from '../../build/nano25519.wasm'
-import { Nano25519TypeError, copy } from './primordials'
-import * as errors from './errors'
+import { Nano25519TypeError, abort, errorCodes } from './errors'
+import { copy } from './primordials'
type Exports = WebAssembly.Instance['exports'] & {
clearMemory: () => void
MAX_VERIFY_BLOCKS: WebAssembly.Global
MAX_VERIFY_BLOCKS_BYTELENGTH: WebAssembly.Global
MAX_MESSAGE_BYTELENGTH: WebAssembly.Global
+ BUFFER_INPUT_MSG: WebAssembly.Global
+ BUFFER_INPUT_PRV: WebAssembly.Global
+ BUFFER_INPUT_PUB: WebAssembly.Global
+ BUFFER_INPUT_SIG: WebAssembly.Global
+ BUFFER_OUTPUT_DERIVE: WebAssembly.Global
+ BUFFER_OUTPUT_SIGN: WebAssembly.Global
+ BUFFER_OUTPUT_VERIFY: WebAssembly.Global
+ ERROR_BYTE_OUT_OF_RANGE: WebAssembly.Global
+ ERROR_INDEX_OUT_OF_RANGE: WebAssembly.Global
+ ERROR_INVALID_BLOCK_COUNT: WebAssembly.Global
+ ERROR_INVALID_MESSAGE_LENGTH: WebAssembly.Global
+ ERROR_INVALID_PUBLIC_KEY: WebAssembly.Global
+ ERROR_SELFTEST_XFAIL_ONE: WebAssembly.Global
+ ERROR_SELFTEST_XFAIL_PAIR: WebAssembly.Global
+ ERROR_SELFTEST_XPASS: WebAssembly.Global
+ ERROR_UNKNOWN: WebAssembly.Global
}
let locked = false
const wasm = copy(nano25519_wasm)
const module = new WebAssembly.Module(wasm)
-
-const instance = new WebAssembly.Instance(module, { env: { abort: errors.abort } })
+const instance = new WebAssembly.Instance(module, { env: { abort } })
const exports = instance.exports as Exports
/**
* The error codes are a contract between `src/assembly/errors.ts` and
* `src/lib/errors.ts`. The host table cannot be read from the module, because
- * the handler has to exist before instantiation, so it is checked against the
- * module here instead. A mismatch means one side was renumbered alone.
+ * the handler has to exist before instantiation, so it is checked that it
+ * matches against the module here instead.
*/
-for (const name of Object.keys(errors)) {
- if (!name.startsWith('ERROR_')) continue
- const global = exports[name] as WebAssembly.Global | undefined
- if (global == null) throw new Nano25519TypeError(`Missing wasm error code ${name}`)
- if (global.value !== (errors as Record<string, unknown>)[name]) {
- throw new Nano25519TypeError(`Error code ${name} is ${global.value} in wasm, ${(errors as Record<string, unknown>)[name]} in host`)
+for (const errorCode of Object.keys(errorCodes)) {
+ if (errorCode.startsWith('ERROR_')) {
+ const wasmErrorCode = exports[errorCode]
+ if (wasmErrorCode == null || !('value' in wasmErrorCode)) {
+ throw new Nano25519TypeError(`Missing WASM error code ${errorCode}`)
+ }
+ if (wasmErrorCode.value !== errorCodes[errorCode]) {
+ throw new Nano25519TypeError(`Error code ${errorCode} is ${wasmErrorCode.value} in wasm, ${(errorCodes as Record<string, unknown>)[errorCode]} in host`)
+ }
}
}
}
static get INPUT_MSG () {
this.#check()
- return (i: number) => (v: number) => exports.setInputMsgByte(i, v)
+ return exports.setInputMsgByte
}
static get INPUT_PRV () {
this.#check()
- return (i: number) => (v: number) => exports.setInputPrvByte(i, v)
+ return exports.setInputPrvByte
}
static get INPUT_PUB () {
this.#check()
- return (i: number) => (v: number) => exports.setInputPubByte(i, v)
+ return exports.setInputPubByte
}
static get INPUT_SIG () {
this.#check()
- return (i: number) => (v: number) => exports.setInputSigByte(i, v)
+ return exports.setInputSigByte
}
static get OUTPUT_DERIVE () {
this.#check()
- return (i: number) => exports.getOutputDeriveByte(i)
+ return exports.getOutputDeriveByte
}
static get OUTPUT_SIGN () {
this.#check()
- return (i: number) => exports.getOutputSignByte(i)
+ return exports.getOutputSignByte
}
static get OUTPUT_VERIFY () {
this.#check()
- return (i: number) => exports.getOutputVerifyByte(i)
+ return exports.getOutputVerifyByte
}
}
try {
result = sign(NANO_ORG_VECTOR.blockHash, NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey.replace('3', '1'))
+ console.log(result)
result = false
} catch (err) {
+ console.log(err)
if (err != null && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
- result = err.constructor.name === 'Nano25519WasmError' && err.message === 'Invalid public key, src/assembly/index.ts'
+ result = err.constructor.name === 'Nano25519WasmError' && err.message === 'Invalid public key'
} else {
result = false
}