From 331ceec8d1e1914d0d76a412e68c969859dbe99d Mon Sep 17 00:00:00 2001 From: cedoor Date: Wed, 21 Feb 2024 16:36:33 +0000 Subject: [PATCH 1/9] feat(utils): add new utility modules for errors and types Semaphore recently added a new package with some utility functions. The first modules in the package, which allow type checking and type error handling, can however also be used outside Semaphore. ZK-Kit is a better place for that kind of function. BREAKING CHANGE: `isStringifiedBigint` has been renamed to `isStringifiedBigInt` re #177 --- packages/eddsa-poseidon/src/eddsa-poseidon.ts | 4 +- packages/eddsa-poseidon/src/utils.ts | 6 +- packages/utils/README.md | 9 + packages/utils/package.json | 10 + packages/utils/rollup.config.ts | 10 +- packages/utils/src/error-handlers.ts | 168 +++++++++++++++++ packages/utils/src/index.ts | 7 +- packages/utils/src/number-checks.ts | 25 --- packages/utils/src/type-checks.ts | 177 ++++++++++++++++++ packages/utils/tests/error-handlers.test.ts | 166 ++++++++++++++++ packages/utils/tests/type-checks.test.ts | 136 ++++++++++++++ 11 files changed, 685 insertions(+), 33 deletions(-) create mode 100644 packages/utils/src/error-handlers.ts delete mode 100644 packages/utils/src/number-checks.ts create mode 100644 packages/utils/src/type-checks.ts create mode 100644 packages/utils/tests/error-handlers.test.ts create mode 100644 packages/utils/tests/type-checks.test.ts diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon.ts b/packages/eddsa-poseidon/src/eddsa-poseidon.ts index 829ce8e6e..52f207986 100644 --- a/packages/eddsa-poseidon/src/eddsa-poseidon.ts +++ b/packages/eddsa-poseidon/src/eddsa-poseidon.ts @@ -14,7 +14,7 @@ import { BigNumberish, F1Field, isHexadecimal, - isStringifiedBigint, + isStringifiedBigInt, leBigIntToBuffer, leBufferToBigInt, scalar @@ -169,7 +169,7 @@ export function packPublicKey(publicKey: Point): string { export function unpackPublicKey(publicKey: BigNumber): Point { if ( typeof publicKey !== "bigint" && - (typeof publicKey !== "string" || !isStringifiedBigint(publicKey)) && + (typeof publicKey !== "string" || !isStringifiedBigInt(publicKey)) && (typeof publicKey !== "string" || !isHexadecimal(publicKey)) ) { throw new TypeError("Invalid public key type") diff --git a/packages/eddsa-poseidon/src/utils.ts b/packages/eddsa-poseidon/src/utils.ts index 70924352d..3d78a5840 100644 --- a/packages/eddsa-poseidon/src/utils.ts +++ b/packages/eddsa-poseidon/src/utils.ts @@ -5,7 +5,7 @@ import { bigNumberishToBuffer, bufferToBigInt, isBigNumberish, - isStringifiedBigint + isStringifiedBigInt } from "@zk-kit/utils" import { Signature } from "./types" @@ -29,7 +29,7 @@ export function pruneBuffer(buff: Buffer): Buffer { * @returns True if the object is a valid point, false otherwise. */ export function isPoint(point: Point): boolean { - return Array.isArray(point) && point.length === 2 && isStringifiedBigint(point[0]) && isStringifiedBigint(point[1]) + return Array.isArray(point) && point.length === 2 && isStringifiedBigInt(point[0]) && isStringifiedBigInt(point[1]) } /** @@ -43,7 +43,7 @@ export function isSignature(signature: Signature): boolean { Object.prototype.hasOwnProperty.call(signature, "R8") && Object.prototype.hasOwnProperty.call(signature, "S") && isPoint(signature.R8) && - isStringifiedBigint(signature.S) + isStringifiedBigInt(signature.S) ) } diff --git a/packages/utils/README.md b/packages/utils/README.md index 849a9fe56..4d31958d5 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -76,4 +76,13 @@ or [JSDelivr](https://www.jsdelivr.com/): ## 📜 Usage +```typescript +// You can import modules from the main bundle. +import { errorHandlers, typeChecks } from "@zk-kit/utils" + +// Or by using conditional exports. +import { requireNumber } from "@zk-kit/utils/error-handlers" +import { isNumber } from "@zk-kit/utils/type-checks" +``` + For more information on the functions provided by `@zk-kit/utils`, please refer to the [documentation](https://zkkit.pse.dev/modules/_zk_kit_utils.html). diff --git a/packages/utils/package.json b/packages/utils/package.json index d41106c89..6c871c189 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -14,6 +14,16 @@ "types": "./dist/types/index.d.ts", "require": "./dist/index.cjs", "default": "./dist/index.js" + }, + "./error-handlers": { + "types": "./dist/types/error-handlers.d.ts", + "require": "./dist/lib.commonjs/error-handlers.cjs", + "default": "./dist/lib.esm/error-handlers.js" + }, + "./type-checks": { + "types": "./dist/types/type-checks.d.ts", + "require": "./dist/lib.commonjs/type-checks.cjs", + "default": "./dist/lib.esm/type-checks.js" } }, "files": [ diff --git a/packages/utils/rollup.config.ts b/packages/utils/rollup.config.ts index bcb6c93a8..06d28553e 100644 --- a/packages/utils/rollup.config.ts +++ b/packages/utils/rollup.config.ts @@ -16,7 +16,15 @@ export default { input: "src/index.ts", output: [ { file: pkg.exports["."].require, format: "cjs", banner }, - { file: pkg.exports["."].default, format: "es", banner } + { file: pkg.exports["."].default, format: "es", banner }, + { + dir: "./dist/lib.commonjs", + format: "cjs", + banner, + preserveModules: true, + entryFileNames: "[name].cjs" + }, + { dir: "./dist/lib.esm", format: "es", banner, preserveModules: true } ], external: [], plugins: [typescript({ tsconfig: "./build.tsconfig.json" }), cleanup({ comments: "jsdoc" })] diff --git a/packages/utils/src/error-handlers.ts b/packages/utils/src/error-handlers.ts new file mode 100644 index 000000000..72be0ffc3 --- /dev/null +++ b/packages/utils/src/error-handlers.ts @@ -0,0 +1,168 @@ +/** + * @module ErrorHandlers + * This module is designed to provide utility functions for validating + * function parameters. It includes functions that throw type errors if + * the parameters do not meet specified criteria, such as being defined, + * a number, a string, a function, or an array. This module helps ensure + * that functions receive the correct types of inputs, enhancing code + * reliability and reducing runtime errors. + */ + +import { + SupportedType, + isArray, + isBigInt, + isBigNumberish, + isDefined, + isFunction, + isHexadecimal, + isNumber, + isObject, + isString, + isStringifiedBigInt, + isSupportedType, + isType, + isUint8Array +} from "./type-checks" + +/** + * It throws a type error if the parameter value has not been defined. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireDefined(parameterValue: any, parameterName: string) { + if (!isDefined(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not defined`) + } +} + +/** + * It throws a type error if the parameter value is not a number. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireNumber(parameterValue: number, parameterName: string) { + if (!isNumber(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a number`) + } +} + +/** + * It throws a type error if the parameter value is not a string. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireString(parameterValue: string, parameterName: string) { + if (!isString(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a string`) + } +} + +/** + * It throws a type error if the parameter value is not a function. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireFunction(parameterValue: Function, parameterName: string) { + if (!isFunction(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a function`) + } +} + +/** + * It throws a type error if the parameter value is not an array. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireArray(parameterValue: any[], parameterName: string) { + if (!isArray(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not an array`) + } +} + +/** + * It throws a type error if the parameter value is not a uint8array. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireUint8Array(parameterValue: Uint8Array, parameterName: string) { + if (!isUint8Array(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a Uint8Array`) + } +} + +/** + * It throws a type error if the parameter value is not an object. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireObject(parameterValue: object, parameterName: string) { + if (!isObject(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not an object`) + } +} + +/** + * It throws a type error if the parameter value is not a bigint. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireBigInt(parameterValue: bigint, parameterName: string) { + if (!isBigInt(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a bigint`) + } +} + +/** + * It throws a type error if the parameter value is not a stringified bigint. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireStringifiedBigInt(parameterValue: string, parameterName: string) { + if (!isStringifiedBigInt(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a stringified bigint`) + } +} + +/** + * It throws a type error if the parameter value is not a hexadecimal. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireHexadecimal(parameterValue: string, parameterName: string) { + if (!isHexadecimal(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a hexadecimal`) + } +} + +/** + * It throws a type error if the parameter value is not a bignumber-ish. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireBigNumberish(parameterValue: any, parameterName: string) { + if (!isBigNumberish(parameterValue)) { + throw new TypeError(`Parameter '${parameterName}' is not a bignumber-ish`) + } +} + +/** + * It throws a type error if the parameter value type is not part of the list of types. + * @param parameterValue The parameter value. + * @param parameterName The parameter name. + */ +export function requireTypes(parameterValue: any, parameterName: string, types: SupportedType[]) { + for (const type of types) { + if (!isSupportedType(type)) { + throw new Error(`Type '${type}' is not supported`) + } + } + + for (const type of types) { + if (isType(parameterValue, type)) { + return + } + } + + throw new TypeError(`Parameter '${parameterName}' is none of the following types: ${types.join(", ")}`) +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 2e3427102..a77a6b49d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,8 +1,11 @@ import F1Field from "./f1-field" import * as scalar from "./scalar" +import * as typeChecks from "./type-checks" +import * as errorHandlers from "./error-handlers" -export { F1Field, scalar } +export { F1Field, scalar, typeChecks, errorHandlers } export * from "./types" -export * from "./number-checks" export * from "./conversions" export * from "./groth16-proof" +export * from "./type-checks" +export * from "./error-handlers" diff --git a/packages/utils/src/number-checks.ts b/packages/utils/src/number-checks.ts deleted file mode 100644 index ac2e443f1..000000000 --- a/packages/utils/src/number-checks.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BigNumber, BigNumberish } from "./types" - -export function isStringifiedBigint(s: BigNumber | string): boolean { - try { - BigInt(s) - - return true - } catch (e) { - return false - } -} - -export function isHexadecimal(s: string) { - return /^(0x|0X)[0-9a-fA-F]+$/.test(s) -} - -export function isBigNumberish(value: BigNumberish): boolean { - return ( - typeof value === "number" || - typeof value === "bigint" || - (typeof value === "string" && isStringifiedBigint(value)) || - (typeof value === "string" && isHexadecimal(value)) || - Buffer.isBuffer(value) - ) -} diff --git a/packages/utils/src/type-checks.ts b/packages/utils/src/type-checks.ts new file mode 100644 index 000000000..a8ecec897 --- /dev/null +++ b/packages/utils/src/type-checks.ts @@ -0,0 +1,177 @@ +/** + * @module TypeChecks + * This module provides utility functions to check data types. + * It defines a set of supported types and includes functions to check if + * a value is defined and if it matches a supported type. These functions + * are useful for type checking and validation in the other libraries, + * enhancing code robustness and reliability. + */ + +// The list of types supported by this utility functions. +const supportedTypes = [ + "number", + "string", + "function", + "array", + "uint8array", + "object", + "bigint", + "stringified-bigint", + "hexadecimal", + "bignumberish" +] as const + +// Type extracted from the list above. +export type SupportedType = (typeof supportedTypes)[number] + +/** + * It returns true if the value is defined, false otherwise. + * @param value The value to be checked. + */ +export function isDefined(value: any): boolean { + return typeof value !== "undefined" +} + +/** + * It returns true if the value is a number, false otherwise. + * @param value The value to be checked. + */ +export function isNumber(value: any): boolean { + return typeof value === "number" +} + +/** + * It returns true if the value is a string, false otherwise. + * @param value The value to be checked. + */ +export function isString(value: any): boolean { + return typeof value === "string" +} + +/** + * It returns true if the value is a function, false otherwise. + * @param value The value to be checked. + */ +export function isFunction(value: any): boolean { + return typeof value === "function" +} + +/** + * It returns true if the value is an array, false otherwise. + * @param value The value to be checked. + */ +export function isArray(value: any): boolean { + return typeof value === "object" && Array.isArray(value) +} + +/** + * It returns true if the value is a uint8array, false otherwise. + * @param value The value to be checked. + */ +export function isUint8Array(value: any): boolean { + return value instanceof Uint8Array +} + +/** + * It returns true if the value is an object, false otherwise. + * @param value The value to be checked. + */ +export function isObject(value: any): boolean { + return typeof value === "object" +} + +/** + * It returns true if the value is a bigint, false otherwise. + * @param value The value to be checked. + */ +export function isBigInt(value: any): boolean { + return typeof value === "bigint" +} + +/** + * Checks if the given value is a string that represents a valid bigint. + * @param value The value to be checked if it's a stringified bigint. + */ +export function isStringifiedBigInt(value: any): boolean { + // Check if value is a string first. + if (!isString(value)) { + return false + } + + try { + // Attempt to convert the string to BigInt. + BigInt(value) + + return true + } catch { + return false + } +} + +/** + * Checks if a string is a valid hexadecimal representation. + * The string must start with '0x' or '0X' followed by one or more hexadecimal digits (0-9, a-f, A-F). + * @param value The string to be tested. + */ +export function isHexadecimal(value: any) { + return /^(0x|0X)[0-9a-fA-F]+$/.test(value) +} + +/** + * Checks if the given value can be considered as "BigNumber-ish". + * A value is considered "BigNumber-ish" if it meets + * any of the following conditions: it's a number, a bigint, a string + * that can be converted to a bigint, a hexadecimal + * string, or a Buffer object. + * @param value The value to check. + */ +export function isBigNumberish(value: any): boolean { + return ( + isNumber(value) || + isBigInt(value) || + isStringifiedBigInt(value) || + isHexadecimal(value) || + Buffer.isBuffer(value) + ) +} + +/** + * It returns true if the value type is the same as the type passed + * as the second parameter, false otherwise. + * @param type The expected type. + */ +export function isType(value: any, type: SupportedType): boolean { + switch (type) { + case "number": + return isNumber(value) + case "string": + return isString(value) + case "function": + return isFunction(value) + case "array": + return isArray(value) + case "uint8array": + return isUint8Array(value) + case "object": + return isObject(value) + case "bigint": + return isBigInt(value) + case "stringified-bigint": + return isStringifiedBigInt(value) + case "hexadecimal": + return isHexadecimal(value) + case "bignumberish": + return isBigNumberish(value) + default: + return false + } +} + +/** + * Return true if the type is being supported by this utility + * functions, false otherwise. + * @param type The type to be checked. + */ +export function isSupportedType(type: string): type is SupportedType { + return (supportedTypes as readonly string[]).includes(type) +} diff --git a/packages/utils/tests/error-handlers.test.ts b/packages/utils/tests/error-handlers.test.ts new file mode 100644 index 000000000..23cc67156 --- /dev/null +++ b/packages/utils/tests/error-handlers.test.ts @@ -0,0 +1,166 @@ +import { + requireArray, + requireBigInt, + requireBigNumberish, + requireDefined, + requireFunction, + requireHexadecimal, + requireNumber, + requireObject, + requireString, + requireStringifiedBigInt, + requireTypes, + requireUint8Array +} from "../src" + +describe("# error-handlers", () => { + it("Should throw an error if the parameter is not defined", () => { + const fun = () => requireDefined(undefined as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not defined") + }) + + it("Should not throw an error if the parameter is defined", () => { + const fun = () => requireDefined(1, "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a number", () => { + const fun = () => requireNumber("euo" as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a number") + }) + + it("Should not throw an error if the parameter is a number", () => { + const fun = () => requireNumber(1, "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a string", () => { + const fun = () => requireString(1 as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a string") + }) + + it("Should not throw an error if the parameter is a string", () => { + const fun = () => requireString("string", "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not an array", () => { + const fun = () => requireArray(1 as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not an array") + }) + + it("Should not throw an error if the parameter is an array", () => { + const fun = () => requireArray([], "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a uint8array", () => { + const fun = () => requireUint8Array([] as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a Uint8Array") + }) + + it("Should not throw an error if the parameter is a uint8array", () => { + const fun = () => requireUint8Array(new Uint8Array([]), "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a function", () => { + const fun = () => requireFunction(1 as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a function") + }) + + it("Should not throw an error if the parameter is a function", () => { + const fun = () => requireFunction(() => true, "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not an object", () => { + const fun = () => requireObject(1 as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not an object") + }) + + it("Should not throw an error if the parameter is an object", () => { + const fun = () => requireObject({}, "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a bigint", () => { + const fun = () => requireBigInt(1 as any, "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a bigint") + }) + + it("Should not throw an error if the parameter is a bigint", () => { + const fun = () => requireBigInt(BigInt(1), "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a stringified bigint", () => { + const fun = () => requireStringifiedBigInt("string", "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a stringified bigint") + }) + + it("Should not throw an error if the parameter is a stringified bigint", () => { + const fun = () => requireStringifiedBigInt("1", "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a hexadecimal", () => { + const fun = () => requireHexadecimal("12", "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a hexadecimal") + }) + + it("Should not throw an error if the parameter is a hexadecimal", () => { + const fun = () => requireStringifiedBigInt("0x12", "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is not a bignumber-ish", () => { + const fun = () => requireBigNumberish("string", "parameter") + + expect(fun).toThrow("Parameter 'parameter' is not a bignumber-ish") + }) + + it("Should not throw an error if the parameter is a bignumber-ish", () => { + const fun = () => requireBigNumberish("0x12", "parameter") + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter is neither a function nor a number", () => { + const fun = () => requireTypes("string", "parameter", ["function", "number"]) + + expect(fun).toThrow("Parameter 'parameter' is none of the following types: function, number") + }) + + it("Should not throw an error if the parameter is either a string or an array", () => { + const fun = () => requireTypes("string", "parameter", ["string", "array"]) + + expect(fun).not.toThrow() + }) + + it("Should throw an error if the parameter types are not supported", () => { + const fun = () => requireTypes("string", "parameter", ["string", "type" as any]) + + expect(fun).toThrow("Type 'type' is not supported") + }) +}) diff --git a/packages/utils/tests/type-checks.test.ts b/packages/utils/tests/type-checks.test.ts new file mode 100644 index 000000000..cc8eb8c15 --- /dev/null +++ b/packages/utils/tests/type-checks.test.ts @@ -0,0 +1,136 @@ +import { + isArray, + isBigInt, + isBigNumberish, + isFunction, + isHexadecimal, + isNumber, + isObject, + isString, + isStringifiedBigInt, + isSupportedType, + isType, + isUint8Array +} from "../src" + +describe("# type-checks", () => { + it("Should return true if the value is a number", () => { + expect(isNumber(1)).toBeTruthy() + }) + + it("Should return false if the value is not a number", () => { + expect(isNumber("string")).toBeFalsy() + }) + + it("Should return true if the value is a string", () => { + expect(isString("string")).toBeTruthy() + }) + + it("Should return false if the value is not a string", () => { + expect(isString(1)).toBeFalsy() + }) + + it("Should return true if the value is a function", () => { + expect(isFunction(() => true)).toBeTruthy() + }) + + it("Should return false if the value is not a function", () => { + expect(isFunction(1)).toBeFalsy() + }) + + it("Should return true if the value is an array", () => { + expect(isArray([])).toBeTruthy() + }) + + it("Should return false if the value is not an array", () => { + expect(isArray(1)).toBeFalsy() + }) + + it("Should return true if the value is a uint8array", () => { + expect(isUint8Array(new Uint8Array([]))).toBeTruthy() + }) + + it("Should return false if the value is not a uint8array", () => { + expect(isUint8Array(1)).toBeFalsy() + }) + + it("Should return true if the value is an object", () => { + expect(isObject({})).toBeTruthy() + }) + + it("Should return false if the value is not an object", () => { + expect(isObject(1)).toBeFalsy() + }) + + it("Should return true if the value is a bigint", () => { + expect(isBigInt(BigInt(1))).toBeTruthy() + }) + + it("Should return false if the value is not a bigint", () => { + expect(isBigInt(1)).toBeFalsy() + }) + + it("Should return true if the value is a stringified bigint", () => { + expect(isStringifiedBigInt("1242342342342342")).toBeTruthy() + expect(isStringifiedBigInt("0x12")).toBeTruthy() + }) + + it("Should return false if the value is not a stringified bigint", () => { + expect(isStringifiedBigInt(1)).toBeFalsy() + }) + + it("Should return true if the value is a hexadecimal", () => { + expect(isHexadecimal("0x12")).toBeTruthy() + }) + + it("Should return false if the value is not a hexadecimal", () => { + expect(isHexadecimal("12")).toBeFalsy() + }) + + it("Should return true if the value is a bignumber-ish", () => { + expect(isBigNumberish(1)).toBeTruthy() + expect(isBigNumberish("1")).toBeTruthy() + expect(isBigNumberish(BigInt("1"))).toBeTruthy() + expect(isBigNumberish("0x12")).toBeTruthy() + expect(isBigNumberish(Buffer.from("0x12"))).toBeTruthy() + }) + + it("Should return false if the value is not a bignumber-ish", () => { + expect(isHexadecimal("string")).toBeFalsy() + }) + + it("Should return true if the value type is the one expected", () => { + expect(isType(1, "number")).toBeTruthy() + expect(isType("string", "string")).toBeTruthy() + expect(isType(() => true, "function")).toBeTruthy() + expect(isType([], "array")).toBeTruthy() + expect(isType(new Uint8Array([]), "uint8array")).toBeTruthy() + expect(isType({}, "object")).toBeTruthy() + expect(isType(BigInt(1), "bigint")).toBeTruthy() + expect(isType("1242342342342342", "stringified-bigint")).toBeTruthy() + expect(isType("0x12", "hexadecimal")).toBeTruthy() + expect(isType(1, "bignumberish")).toBeTruthy() + }) + + it("Should return false if the value type is not the one expected or is not supported", () => { + expect(isType("string", "number")).toBeFalsy() + expect(isType(1, "string")).toBeFalsy() + expect(isType(1, "function")).toBeFalsy() + expect(isType(1, "array")).toBeFalsy() + expect(isType(1, "uint8array")).toBeFalsy() + expect(isType(1, "object")).toBeFalsy() + expect(isType(1, "bigint")).toBeFalsy() + expect(isType(1, "stringified-bigint")).toBeFalsy() + expect(isType(1, "hexadecimal")).toBeFalsy() + expect(isType("string", "bignumberish")).toBeFalsy() + expect(isType(1, "type" as any)).toBeFalsy() + }) + + it("Should return true if the type is supported", () => { + expect(isSupportedType("number")).toBeTruthy() + }) + + it("Should return false if the type is not supported", () => { + expect(isSupportedType("type")).toBeFalsy() + }) +}) From 0f2ee83b4b671e0c1a57d1870da4bbaa4b5d3764 Mon Sep 17 00:00:00 2001 From: cedoor Date: Wed, 21 Feb 2024 16:59:13 +0000 Subject: [PATCH 2/9] build(utils): remove declarations when using output.dir option re #177 --- packages/utils/rollup.config.ts | 44 ++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/utils/rollup.config.ts b/packages/utils/rollup.config.ts index 06d28553e..d2d41b2f9 100644 --- a/packages/utils/rollup.config.ts +++ b/packages/utils/rollup.config.ts @@ -12,20 +12,30 @@ const banner = `/** * @see [Github]{@link ${pkg.homepage}} */` -export default { - input: "src/index.ts", - output: [ - { file: pkg.exports["."].require, format: "cjs", banner }, - { file: pkg.exports["."].default, format: "es", banner }, - { - dir: "./dist/lib.commonjs", - format: "cjs", - banner, - preserveModules: true, - entryFileNames: "[name].cjs" - }, - { dir: "./dist/lib.esm", format: "es", banner, preserveModules: true } - ], - external: [], - plugins: [typescript({ tsconfig: "./build.tsconfig.json" }), cleanup({ comments: "jsdoc" })] -} +export default [ + { + input: "src/index.ts", + output: [ + { file: pkg.exports["."].require, format: "cjs", banner }, + { file: pkg.exports["."].default, format: "es", banner } + ], + plugins: [typescript({ tsconfig: "./build.tsconfig.json" }), cleanup({ comments: "jsdoc" })] + }, + { + input: "src/index.ts", + output: [ + { + dir: "./dist/lib.commonjs", + format: "cjs", + banner, + preserveModules: true, + entryFileNames: "[name].cjs" + }, + { dir: "./dist/lib.esm", format: "es", banner, preserveModules: true } + ], + plugins: [ + typescript({ tsconfig: "./build.tsconfig.json", declaration: false, declarationDir: undefined }), + cleanup({ comments: "jsdoc" }) + ] + } +] From 309510b7a5f1a09e6c4f4740bdf5340e5b9756c4 Mon Sep 17 00:00:00 2001 From: cedoor Date: Thu, 29 Feb 2024 16:21:41 +0000 Subject: [PATCH 3/9] refactor(utils): re-use zk-kit utility functions re #177 --- jest.config.ts | 3 +- packages/eddsa-poseidon/src/eddsa-poseidon.ts | 49 +++++++------------ packages/eddsa-poseidon/src/utils.ts | 32 +++++------- packages/eddsa-poseidon/tests/index.test.ts | 6 +-- packages/utils/package.json | 21 ++++++++ packages/utils/src/conversions.ts | 13 ++--- packages/utils/src/index.ts | 6 ++- .../src/{groth16-proof.ts => packing.ts} | 10 +++- packages/utils/src/types/index.ts | 2 +- ...{groth16-proof.test.ts => packing.test.ts} | 6 +-- yarn.lock | 2 + 11 files changed, 80 insertions(+), 70 deletions(-) rename packages/utils/src/{groth16-proof.ts => packing.ts} (83%) rename packages/utils/tests/{groth16-proof.test.ts => packing.test.ts} (87%) diff --git a/jest.config.ts b/jest.config.ts index e09ff02b9..04aee3890 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -12,7 +12,8 @@ const projects: any = fs rootDir: `packages/${name}`, displayName: name, moduleNameMapper: { - "@zk-kit/(.*)": "/../$1/src/index.ts" // Interdependency packages. + "@zk-kit/(.*)/(.*)": "/../$1/src/$2.ts", + "@zk-kit/(.*)": "/../$1/src/index.ts" } })) diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon.ts b/packages/eddsa-poseidon/src/eddsa-poseidon.ts index 52f207986..b25f49ff6 100644 --- a/packages/eddsa-poseidon/src/eddsa-poseidon.ts +++ b/packages/eddsa-poseidon/src/eddsa-poseidon.ts @@ -9,20 +9,15 @@ import { subOrder, unpackPoint } from "@zk-kit/baby-jubjub" -import { - BigNumber, - BigNumberish, - F1Field, - isHexadecimal, - isStringifiedBigInt, - leBigIntToBuffer, - leBufferToBigInt, - scalar -} from "@zk-kit/utils" +import type { BigNumberish } from "@zk-kit/utils" +import { bigNumberishToBigInt, leBigIntToBuffer, leBufferToBigInt } from "@zk-kit/utils/conversions" +import { requireBigNumberish } from "@zk-kit/utils/error-handlers" +import F1Field from "@zk-kit/utils/f1-field" +import * as scalar from "@zk-kit/utils/scalar" import { poseidon5 } from "poseidon-lite/poseidon5" import blake from "./blake" import { Signature } from "./types" -import * as utils from "./utils" +import { checkMessage, checkPrivateKey, isPoint, isSignature, pruneBuffer } from "./utils" /** * Derives a secret scalar from a given EdDSA private key. @@ -37,12 +32,12 @@ import * as utils from "./utils" */ export function deriveSecretScalar(privateKey: BigNumberish): string { // Convert the private key to buffer. - privateKey = utils.checkPrivateKey(privateKey) + privateKey = checkPrivateKey(privateKey) let hash = blake(privateKey) hash = hash.slice(0, 32) - hash = utils.pruneBuffer(hash) + hash = pruneBuffer(hash) return scalar.shiftRight(leBufferToBigInt(hash), BigInt(3)).toString() } @@ -74,14 +69,14 @@ export function derivePublicKey(privateKey: BigNumberish): Point { */ export function signMessage(privateKey: BigNumberish, message: BigNumberish): Signature { // Convert the private key to buffer. - privateKey = utils.checkPrivateKey(privateKey) + privateKey = checkPrivateKey(privateKey) // Convert the message to big integer. - message = utils.checkMessage(message) + message = checkMessage(message) const hash = blake(privateKey) - const sBuff = utils.pruneBuffer(hash.slice(0, 32)) + const sBuff = pruneBuffer(hash.slice(0, 32)) const s = leBufferToBigInt(sBuff) const A = mulPointEscalar(Base8, scalar.shiftRight(s, BigInt(3))) @@ -112,8 +107,8 @@ export function signMessage(privateKey: BigNumberish, message: BigNumberish): Si */ export function verifySignature(message: BigNumberish, signature: Signature, publicKey: Point): boolean { if ( - !utils.isPoint(publicKey) || - !utils.isSignature(signature) || + !isPoint(publicKey) || + !isSignature(signature) || !inCurve(signature.R8) || !inCurve(publicKey) || BigInt(signature.S) >= subOrder @@ -122,7 +117,7 @@ export function verifySignature(message: BigNumberish, signature: Signature, pub } // Convert the message to big integer. - message = utils.checkMessage(message) + message = checkMessage(message) // Convert the signature values to big integers for calculations. const _signature: Signature = { @@ -150,7 +145,7 @@ export function verifySignature(message: BigNumberish, signature: Signature, pub * @returns A string representation of the packed public key. */ export function packPublicKey(publicKey: Point): string { - if (!utils.isPoint(publicKey) || !inCurve(publicKey)) { + if (!isPoint(publicKey) || !inCurve(publicKey)) { throw new Error("Invalid public key") } @@ -163,19 +158,13 @@ export function packPublicKey(publicKey: Point): string { /** * Unpacks a public key from its packed string representation back to its original point form on the Baby Jubjub curve. * This function checks for the validity of the input format before attempting to unpack. - * @param publicKey The packed public key as a string or bigint. + * @param publicKey The packed public key as a bignumberish. * @returns The unpacked public key as a point. */ -export function unpackPublicKey(publicKey: BigNumber): Point { - if ( - typeof publicKey !== "bigint" && - (typeof publicKey !== "string" || !isStringifiedBigInt(publicKey)) && - (typeof publicKey !== "string" || !isHexadecimal(publicKey)) - ) { - throw new TypeError("Invalid public key type") - } +export function unpackPublicKey(publicKey: BigNumberish): Point { + requireBigNumberish(publicKey, "publicKey") - const unpackedPublicKey = unpackPoint(BigInt(publicKey)) + const unpackedPublicKey = unpackPoint(bigNumberishToBigInt(publicKey)) if (unpackedPublicKey === null) { throw new Error("Invalid public key") diff --git a/packages/eddsa-poseidon/src/utils.ts b/packages/eddsa-poseidon/src/utils.ts index 3d78a5840..3ad65941d 100644 --- a/packages/eddsa-poseidon/src/utils.ts +++ b/packages/eddsa-poseidon/src/utils.ts @@ -1,12 +1,8 @@ import { Point } from "@zk-kit/baby-jubjub" -import { - BigNumberish, - bigNumberishToBigInt, - bigNumberishToBuffer, - bufferToBigInt, - isBigNumberish, - isStringifiedBigInt -} from "@zk-kit/utils" +import { requireTypes, type BigNumberish, isBigNumberish } from "@zk-kit/utils" +import { bigNumberishToBigInt, bigNumberishToBuffer, bufferToBigInt } from "@zk-kit/utils/conversions" +import { requireBigNumberish } from "@zk-kit/utils/error-handlers" +import { isArray, isObject, isStringifiedBigInt } from "@zk-kit/utils/type-checks" import { Signature } from "./types" /** @@ -29,7 +25,7 @@ export function pruneBuffer(buff: Buffer): Buffer { * @returns True if the object is a valid point, false otherwise. */ export function isPoint(point: Point): boolean { - return Array.isArray(point) && point.length === 2 && isStringifiedBigInt(point[0]) && isStringifiedBigInt(point[1]) + return isArray(point) && point.length === 2 && isStringifiedBigInt(point[0]) && isStringifiedBigInt(point[1]) } /** @@ -39,7 +35,7 @@ export function isPoint(point: Point): boolean { */ export function isSignature(signature: Signature): boolean { return ( - typeof signature === "object" && + isObject(signature) && Object.prototype.hasOwnProperty.call(signature, "R8") && Object.prototype.hasOwnProperty.call(signature, "S") && isPoint(signature.R8) && @@ -53,15 +49,13 @@ export function isSignature(signature: Signature): boolean { * @returns The private key as a Buffer. */ export function checkPrivateKey(privateKey: BigNumberish): Buffer { + requireTypes(privateKey, "privateKey", ["bignumberish", "string"]) + if (isBigNumberish(privateKey)) { return bigNumberishToBuffer(privateKey) } - if (typeof privateKey !== "string") { - throw TypeError("Invalid private key type. Supported types: number, bigint, buffer, string.") - } - - return Buffer.from(privateKey) + return Buffer.from(privateKey as string) } /** @@ -70,13 +64,11 @@ export function checkPrivateKey(privateKey: BigNumberish): Buffer { * @returns The message as a bigint. */ export function checkMessage(message: BigNumberish): bigint { + requireTypes(message, "message", ["bignumberish", "string"]) + if (isBigNumberish(message)) { return bigNumberishToBigInt(message) } - if (typeof message !== "string") { - throw TypeError("Invalid message type. Supported types: number, bigint, buffer, string.") - } - - return bufferToBigInt(Buffer.from(message)) + return bufferToBigInt(Buffer.from(message as string)) } diff --git a/packages/eddsa-poseidon/tests/index.test.ts b/packages/eddsa-poseidon/tests/index.test.ts index 7f90dd407..b779fd178 100644 --- a/packages/eddsa-poseidon/tests/index.test.ts +++ b/packages/eddsa-poseidon/tests/index.test.ts @@ -74,7 +74,7 @@ describe("EdDSAPoseidon", () => { const fun = () => derivePublicKey(privateKey as any) - expect(fun).toThrow("Invalid private key type.") + expect(fun).toThrow(`Parameter 'privateKey' is none of the following types: bignumberish, string`) }) it("Should sign a message (bigint)", async () => { @@ -140,7 +140,7 @@ describe("EdDSAPoseidon", () => { const fun = () => signMessage(privateKey, message as any) - expect(fun).toThrow("Invalid message type.") + expect(fun).toThrow(`Parameter 'message' is none of the following types: bignumberish, string`) }) it("Should verify a signature", async () => { @@ -241,7 +241,7 @@ describe("EdDSAPoseidon", () => { it("Should not unpack a public key if the public key type is not supported", async () => { const fun = () => unpackPublicKey("e") - expect(fun).toThrow("Invalid public key") + expect(fun).toThrow(`Parameter 'publicKey' is not a bignumber-ish`) }) it("Should not unpack a public key if the public key does not correspond to a valid point on the curve", async () => { diff --git a/packages/utils/package.json b/packages/utils/package.json index 6c871c189..81975baf1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -24,6 +24,26 @@ "types": "./dist/types/type-checks.d.ts", "require": "./dist/lib.commonjs/type-checks.cjs", "default": "./dist/lib.esm/type-checks.js" + }, + "./conversions": { + "types": "./dist/types/conversions.d.ts", + "require": "./dist/lib.commonjs/conversions.cjs", + "default": "./dist/lib.esm/conversions.js" + }, + "./packing": { + "types": "./dist/types/packing.d.ts", + "require": "./dist/lib.commonjs/packing.cjs", + "default": "./dist/lib.esm/packing.js" + }, + "./scalar": { + "types": "./dist/types/scalar.d.ts", + "require": "./dist/lib.commonjs/scalar.cjs", + "default": "./dist/lib.esm/scalar.js" + }, + "./f1-field": { + "types": "./dist/types/f1-field.d.ts", + "require": "./dist/lib.commonjs/f1-field.cjs", + "default": "./dist/lib.esm/f1-field.js" } }, "files": [ @@ -48,6 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", + "@types/snarkjs": "^0", "rimraf": "^5.0.5", "rollup": "^4.12.0", "rollup-plugin-cleanup": "^3.2.1", diff --git a/packages/utils/src/conversions.ts b/packages/utils/src/conversions.ts index de31ef452..148f6252e 100644 --- a/packages/utils/src/conversions.ts +++ b/packages/utils/src/conversions.ts @@ -12,8 +12,8 @@ * the order of bytes is always big-endian. */ -import { BigNumberish } from "./types" -import { isHexadecimal, isStringifiedBigint } from "./number-checks" +import { isBigInt, isHexadecimal, isNumber, isStringifiedBigInt } from "./type-checks" +import { BigNumber, BigNumberish } from "./types" /** * Converts a bigint to a hexadecimal string. @@ -161,13 +161,8 @@ export function bigIntToBuffer(n: bigint): Buffer { * @returns The bigint representation of the BigNumberish value. */ export function bigNumberishToBigInt(n: BigNumberish): bigint { - if ( - typeof n === "number" || - typeof n === "bigint" || - (typeof n === "string" && isStringifiedBigint(n)) || - (typeof n === "string" && isHexadecimal(n)) - ) { - return BigInt(n) + if (isNumber(n) || isBigInt(n) || isStringifiedBigInt(n) || isHexadecimal(n)) { + return BigInt(n as BigNumber | number) } return bufferToBigInt(n as Buffer) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index a77a6b49d..a24e46f7d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,11 +1,13 @@ import F1Field from "./f1-field" import * as scalar from "./scalar" +import * as conversions from "./conversions" +import * as packing from "./packing" import * as typeChecks from "./type-checks" import * as errorHandlers from "./error-handlers" -export { F1Field, scalar, typeChecks, errorHandlers } +export { F1Field, scalar, conversions, packing, typeChecks, errorHandlers } export * from "./types" export * from "./conversions" -export * from "./groth16-proof" +export * from "./packing" export * from "./type-checks" export * from "./error-handlers" diff --git a/packages/utils/src/groth16-proof.ts b/packages/utils/src/packing.ts similarity index 83% rename from packages/utils/src/groth16-proof.ts rename to packages/utils/src/packing.ts index a309fddb4..062f73dcf 100644 --- a/packages/utils/src/groth16-proof.ts +++ b/packages/utils/src/packing.ts @@ -1,4 +1,12 @@ -import { Groth16Proof } from "snarkjs" +/** + * @module Packing + * + * This module provides utility functions to pack and unpack + * various types of objects, making it easier to export or use + * them externally. + */ + +import type { Groth16Proof } from "snarkjs" import { PackedGroth16Proof } from "./types" /** diff --git a/packages/utils/src/types/index.ts b/packages/utils/src/types/index.ts index 1777d4fa6..9596659a0 100644 --- a/packages/utils/src/types/index.ts +++ b/packages/utils/src/types/index.ts @@ -1,4 +1,4 @@ -import { NumericString } from "snarkjs" +import type { NumericString } from "snarkjs" export type BigNumber = bigint | string diff --git a/packages/utils/tests/groth16-proof.test.ts b/packages/utils/tests/packing.test.ts similarity index 87% rename from packages/utils/tests/groth16-proof.test.ts rename to packages/utils/tests/packing.test.ts index 18f80de7f..51ff70dd1 100644 --- a/packages/utils/tests/groth16-proof.test.ts +++ b/packages/utils/tests/packing.test.ts @@ -1,8 +1,8 @@ -import { packGroth16Proof, unpackGroth16Proof } from "../src/groth16-proof" +import { packGroth16Proof, unpackGroth16Proof } from "../src/packing" import { PackedGroth16Proof } from "../src/types" -describe("Groth16 Proof", () => { - describe("packGroth16Proof/unpackGroth16Proof", () => { +describe("Packing", () => { + describe("# packGroth16Proof/unpackGroth16Proof", () => { it("Should return a packed proof", async () => { const packedGroth16Proof: PackedGroth16Proof = [ "17455271319858434926499425298909035958151998411844026248469741053531717339265", diff --git a/yarn.lock b/yarn.lock index 17c1c907e..052d8c281 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3801,10 +3801,12 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^11.1.6" + "@types/snarkjs": "npm:^0" rimraf: "npm:^5.0.5" rollup: "npm:^4.12.0" rollup-plugin-cleanup: "npm:^3.2.1" rollup-plugin-polyfill-node: "npm:^0.13.0" + snarkjs: "npm:^0.7.3" languageName: unknown linkType: soft From 759f1223a2dcd7104796f85cebc8278ffbde616f Mon Sep 17 00:00:00 2001 From: cedoor Date: Thu, 29 Feb 2024 16:22:53 +0000 Subject: [PATCH 4/9] chore: update lock file --- yarn.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 052d8c281..7e26e7051 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3806,7 +3806,6 @@ __metadata: rollup: "npm:^4.12.0" rollup-plugin-cleanup: "npm:^3.2.1" rollup-plugin-polyfill-node: "npm:^0.13.0" - snarkjs: "npm:^0.7.3" languageName: unknown linkType: soft From 3d6c6df2ae1f923139bebd1d9b271eb952149dac Mon Sep 17 00:00:00 2001 From: cedoor Date: Thu, 29 Feb 2024 16:49:13 +0000 Subject: [PATCH 5/9] style(eddsa-poseidon): fix lint error --- packages/eddsa-poseidon/src/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/eddsa-poseidon/src/utils.ts b/packages/eddsa-poseidon/src/utils.ts index 3ad65941d..c1b467a7e 100644 --- a/packages/eddsa-poseidon/src/utils.ts +++ b/packages/eddsa-poseidon/src/utils.ts @@ -1,7 +1,6 @@ import { Point } from "@zk-kit/baby-jubjub" -import { requireTypes, type BigNumberish, isBigNumberish } from "@zk-kit/utils" +import { isBigNumberish, requireTypes, type BigNumberish } from "@zk-kit/utils" import { bigNumberishToBigInt, bigNumberishToBuffer, bufferToBigInt } from "@zk-kit/utils/conversions" -import { requireBigNumberish } from "@zk-kit/utils/error-handlers" import { isArray, isObject, isStringifiedBigInt } from "@zk-kit/utils/type-checks" import { Signature } from "./types" From 30be5b81e82210da8e30f82539d1e6cc4b0312f6 Mon Sep 17 00:00:00 2001 From: cedoor Date: Sat, 2 Mar 2024 11:31:56 +0000 Subject: [PATCH 6/9] refactor(utils): rename packing to proof-packing re #177 --- packages/utils/package.json | 8 ++++---- packages/utils/src/index.ts | 4 ++-- packages/utils/src/{packing.ts => proof-packing.ts} | 2 +- .../tests/{packing.test.ts => proof-packing.test.ts} | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename packages/utils/src/{packing.ts => proof-packing.ts} (98%) rename packages/utils/tests/{packing.test.ts => proof-packing.test.ts} (94%) diff --git a/packages/utils/package.json b/packages/utils/package.json index 81975baf1..849f2ab4e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -30,10 +30,10 @@ "require": "./dist/lib.commonjs/conversions.cjs", "default": "./dist/lib.esm/conversions.js" }, - "./packing": { - "types": "./dist/types/packing.d.ts", - "require": "./dist/lib.commonjs/packing.cjs", - "default": "./dist/lib.esm/packing.js" + "./proof-packing": { + "types": "./dist/types/proof-packing.d.ts", + "require": "./dist/lib.commonjs/proof-packing.cjs", + "default": "./dist/lib.esm/proof-packing.js" }, "./scalar": { "types": "./dist/types/scalar.d.ts", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index a24e46f7d..e76758855 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,13 +1,13 @@ import F1Field from "./f1-field" import * as scalar from "./scalar" import * as conversions from "./conversions" -import * as packing from "./packing" +import * as packing from "./proof-packing" import * as typeChecks from "./type-checks" import * as errorHandlers from "./error-handlers" export { F1Field, scalar, conversions, packing, typeChecks, errorHandlers } export * from "./types" export * from "./conversions" -export * from "./packing" +export * from "./proof-packing" export * from "./type-checks" export * from "./error-handlers" diff --git a/packages/utils/src/packing.ts b/packages/utils/src/proof-packing.ts similarity index 98% rename from packages/utils/src/packing.ts rename to packages/utils/src/proof-packing.ts index 062f73dcf..ba189d37e 100644 --- a/packages/utils/src/packing.ts +++ b/packages/utils/src/proof-packing.ts @@ -1,5 +1,5 @@ /** - * @module Packing + * @module ProofPacking * * This module provides utility functions to pack and unpack * various types of objects, making it easier to export or use diff --git a/packages/utils/tests/packing.test.ts b/packages/utils/tests/proof-packing.test.ts similarity index 94% rename from packages/utils/tests/packing.test.ts rename to packages/utils/tests/proof-packing.test.ts index 51ff70dd1..04ebc05a4 100644 --- a/packages/utils/tests/packing.test.ts +++ b/packages/utils/tests/proof-packing.test.ts @@ -1,4 +1,4 @@ -import { packGroth16Proof, unpackGroth16Proof } from "../src/packing" +import { packGroth16Proof, unpackGroth16Proof } from "../src/proof-packing" import { PackedGroth16Proof } from "../src/types" describe("Packing", () => { From 6e81e2f710dae855f9e1787990cba2f7de4e0dce Mon Sep 17 00:00:00 2001 From: cedoor Date: Sat, 2 Mar 2024 11:36:46 +0000 Subject: [PATCH 7/9] docs(utils): add throws js-doc tag re #177 --- packages/utils/src/error-handlers.ts | 24 ++++++++++++------------ packages/utils/src/type-checks.ts | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/utils/src/error-handlers.ts b/packages/utils/src/error-handlers.ts index 72be0ffc3..6393a66e1 100644 --- a/packages/utils/src/error-handlers.ts +++ b/packages/utils/src/error-handlers.ts @@ -26,7 +26,7 @@ import { } from "./type-checks" /** - * It throws a type error if the parameter value has not been defined. + * @throws Throws a type error if the parameter value has not been defined. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -37,7 +37,7 @@ export function requireDefined(parameterValue: any, parameterName: string) { } /** - * It throws a type error if the parameter value is not a number. + * @throws Throws a type error if the parameter value is not a number. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -48,7 +48,7 @@ export function requireNumber(parameterValue: number, parameterName: string) { } /** - * It throws a type error if the parameter value is not a string. + * @throws Throws a type error if the parameter value is not a string. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -59,7 +59,7 @@ export function requireString(parameterValue: string, parameterName: string) { } /** - * It throws a type error if the parameter value is not a function. + * @throws Throws a type error if the parameter value is not a function. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -70,7 +70,7 @@ export function requireFunction(parameterValue: Function, parameterName: string) } /** - * It throws a type error if the parameter value is not an array. + * @throws Throws a type error if the parameter value is not an array. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -81,7 +81,7 @@ export function requireArray(parameterValue: any[], parameterName: string) { } /** - * It throws a type error if the parameter value is not a uint8array. + * @throws Throws a type error if the parameter value is not a uint8array. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -92,7 +92,7 @@ export function requireUint8Array(parameterValue: Uint8Array, parameterName: str } /** - * It throws a type error if the parameter value is not an object. + * @throws Throws a type error if the parameter value is not an object. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -103,7 +103,7 @@ export function requireObject(parameterValue: object, parameterName: string) { } /** - * It throws a type error if the parameter value is not a bigint. + * @throws Throws a type error if the parameter value is not a bigint. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -114,7 +114,7 @@ export function requireBigInt(parameterValue: bigint, parameterName: string) { } /** - * It throws a type error if the parameter value is not a stringified bigint. + * @throws Throws a type error if the parameter value is not a stringified bigint. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -125,7 +125,7 @@ export function requireStringifiedBigInt(parameterValue: string, parameterName: } /** - * It throws a type error if the parameter value is not a hexadecimal. + * @throws Throws a type error if the parameter value is not a hexadecimal. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -136,7 +136,7 @@ export function requireHexadecimal(parameterValue: string, parameterName: string } /** - * It throws a type error if the parameter value is not a bignumber-ish. + * @throws Throws a type error if the parameter value is not a bignumber-ish. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ @@ -147,7 +147,7 @@ export function requireBigNumberish(parameterValue: any, parameterName: string) } /** - * It throws a type error if the parameter value type is not part of the list of types. + * @throws Throws a type error if the parameter value type is not part of the list of types. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ diff --git a/packages/utils/src/type-checks.ts b/packages/utils/src/type-checks.ts index a8ecec897..1e969009c 100644 --- a/packages/utils/src/type-checks.ts +++ b/packages/utils/src/type-checks.ts @@ -25,7 +25,7 @@ const supportedTypes = [ export type SupportedType = (typeof supportedTypes)[number] /** - * It returns true if the value is defined, false otherwise. + * Returns true if the value is defined, false otherwise. * @param value The value to be checked. */ export function isDefined(value: any): boolean { @@ -33,7 +33,7 @@ export function isDefined(value: any): boolean { } /** - * It returns true if the value is a number, false otherwise. + * Returns true if the value is a number, false otherwise. * @param value The value to be checked. */ export function isNumber(value: any): boolean { @@ -41,7 +41,7 @@ export function isNumber(value: any): boolean { } /** - * It returns true if the value is a string, false otherwise. + * Returns true if the value is a string, false otherwise. * @param value The value to be checked. */ export function isString(value: any): boolean { @@ -49,7 +49,7 @@ export function isString(value: any): boolean { } /** - * It returns true if the value is a function, false otherwise. + * Returns true if the value is a function, false otherwise. * @param value The value to be checked. */ export function isFunction(value: any): boolean { @@ -57,7 +57,7 @@ export function isFunction(value: any): boolean { } /** - * It returns true if the value is an array, false otherwise. + * Returns true if the value is an array, false otherwise. * @param value The value to be checked. */ export function isArray(value: any): boolean { @@ -65,7 +65,7 @@ export function isArray(value: any): boolean { } /** - * It returns true if the value is a uint8array, false otherwise. + * Returns true if the value is a uint8array, false otherwise. * @param value The value to be checked. */ export function isUint8Array(value: any): boolean { @@ -73,7 +73,7 @@ export function isUint8Array(value: any): boolean { } /** - * It returns true if the value is an object, false otherwise. + * Returns true if the value is an object, false otherwise. * @param value The value to be checked. */ export function isObject(value: any): boolean { @@ -81,7 +81,7 @@ export function isObject(value: any): boolean { } /** - * It returns true if the value is a bigint, false otherwise. + * Returns true if the value is a bigint, false otherwise. * @param value The value to be checked. */ export function isBigInt(value: any): boolean { @@ -136,7 +136,7 @@ export function isBigNumberish(value: any): boolean { } /** - * It returns true if the value type is the same as the type passed + * Returns true if the value type is the same as the type passed * as the second parameter, false otherwise. * @param type The expected type. */ @@ -168,7 +168,7 @@ export function isType(value: any, type: SupportedType): boolean { } /** - * Return true if the type is being supported by this utility + * Returns true if the type is being supported by this utility * functions, false otherwise. * @param type The type to be checked. */ From 932b4c519b9ec28aeb44ae0e2cd53b60a576858e Mon Sep 17 00:00:00 2001 From: cedoor Date: Tue, 5 Mar 2024 17:27:55 +0000 Subject: [PATCH 8/9] refactor(utils): update error messages re #177 --- packages/utils/src/error-handlers.ts | 25 ++++++++++++++++--------- packages/utils/src/type-checks.ts | 3 ++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/utils/src/error-handlers.ts b/packages/utils/src/error-handlers.ts index 6393a66e1..2513dc2c5 100644 --- a/packages/utils/src/error-handlers.ts +++ b/packages/utils/src/error-handlers.ts @@ -43,7 +43,7 @@ export function requireDefined(parameterValue: any, parameterName: string) { */ export function requireNumber(parameterValue: number, parameterName: string) { if (!isNumber(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a number`) + throw new TypeError(`Parameter '${parameterName}' is not a number, but received type: ${typeof parameterValue}`) } } @@ -54,7 +54,7 @@ export function requireNumber(parameterValue: number, parameterName: string) { */ export function requireString(parameterValue: string, parameterName: string) { if (!isString(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a string`) + throw new TypeError(`Parameter '${parameterName}' is not a string, but received type: ${typeof parameterValue}`) } } @@ -65,7 +65,9 @@ export function requireString(parameterValue: string, parameterName: string) { */ export function requireFunction(parameterValue: Function, parameterName: string) { if (!isFunction(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a function`) + throw new TypeError( + `Parameter '${parameterName}' is not a function, but received type: ${typeof parameterValue}` + ) } } @@ -76,7 +78,7 @@ export function requireFunction(parameterValue: Function, parameterName: string) */ export function requireArray(parameterValue: any[], parameterName: string) { if (!isArray(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not an array`) + throw new TypeError(`Parameter '${parameterName}' is not an array, but received type: ${typeof parameterValue}`) } } @@ -87,18 +89,23 @@ export function requireArray(parameterValue: any[], parameterName: string) { */ export function requireUint8Array(parameterValue: Uint8Array, parameterName: string) { if (!isUint8Array(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a Uint8Array`) + throw new TypeError( + `Parameter '${parameterName}' is not a Uint8Array, but received type: ${typeof parameterValue}` + ) } } /** * @throws Throws a type error if the parameter value is not an object. + * Please, note that arrays are also objects in JavaScript. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ export function requireObject(parameterValue: object, parameterName: string) { if (!isObject(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not an object`) + throw new TypeError( + `Parameter '${parameterName}' is not an object, but received type: ${typeof parameterValue}` + ) } } @@ -109,7 +116,7 @@ export function requireObject(parameterValue: object, parameterName: string) { */ export function requireBigInt(parameterValue: bigint, parameterName: string) { if (!isBigInt(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a bigint`) + throw new TypeError(`Parameter '${parameterName}' is not a bigint, but received type: ${typeof parameterValue}`) } } @@ -125,13 +132,13 @@ export function requireStringifiedBigInt(parameterValue: string, parameterName: } /** - * @throws Throws a type error if the parameter value is not a hexadecimal. + * @throws Throws a type error if the parameter value is not a hexadecimal string. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ export function requireHexadecimal(parameterValue: string, parameterName: string) { if (!isHexadecimal(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a hexadecimal`) + throw new TypeError(`Parameter '${parameterName}' is not a hexadecimal string`) } } diff --git a/packages/utils/src/type-checks.ts b/packages/utils/src/type-checks.ts index 1e969009c..027de5d6c 100644 --- a/packages/utils/src/type-checks.ts +++ b/packages/utils/src/type-checks.ts @@ -74,6 +74,7 @@ export function isUint8Array(value: any): boolean { /** * Returns true if the value is an object, false otherwise. + * Please, note that arrays are also objects in JavaScript. * @param value The value to be checked. */ export function isObject(value: any): boolean { @@ -109,7 +110,7 @@ export function isStringifiedBigInt(value: any): boolean { } /** - * Checks if a string is a valid hexadecimal representation. + * Checks if a string is a valid hexadecimal string representation. * The string must start with '0x' or '0X' followed by one or more hexadecimal digits (0-9, a-f, A-F). * @param value The string to be tested. */ From 2dde56a468e1b31a4ee24f5a761a40169077c6f0 Mon Sep 17 00:00:00 2001 From: cedoor Date: Wed, 6 Mar 2024 11:40:47 +0000 Subject: [PATCH 9/9] refactor(utils): rename errors re object checks re #178 --- packages/utils/src/error-handlers.ts | 24 ++++++---------- packages/utils/src/type-checks.ts | 32 ++++++++++----------- packages/utils/tests/error-handlers.test.ts | 10 +++---- packages/utils/tests/type-checks.test.ts | 16 +++++------ 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/packages/utils/src/error-handlers.ts b/packages/utils/src/error-handlers.ts index 2513dc2c5..31ae942a4 100644 --- a/packages/utils/src/error-handlers.ts +++ b/packages/utils/src/error-handlers.ts @@ -43,7 +43,7 @@ export function requireDefined(parameterValue: any, parameterName: string) { */ export function requireNumber(parameterValue: number, parameterName: string) { if (!isNumber(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a number, but received type: ${typeof parameterValue}`) + throw new TypeError(`Parameter '${parameterName}' is not a number, received type: ${typeof parameterValue}`) } } @@ -54,7 +54,7 @@ export function requireNumber(parameterValue: number, parameterName: string) { */ export function requireString(parameterValue: string, parameterName: string) { if (!isString(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a string, but received type: ${typeof parameterValue}`) + throw new TypeError(`Parameter '${parameterName}' is not a string, received type: ${typeof parameterValue}`) } } @@ -65,33 +65,29 @@ export function requireString(parameterValue: string, parameterName: string) { */ export function requireFunction(parameterValue: Function, parameterName: string) { if (!isFunction(parameterValue)) { - throw new TypeError( - `Parameter '${parameterName}' is not a function, but received type: ${typeof parameterValue}` - ) + throw new TypeError(`Parameter '${parameterName}' is not a function, received type: ${typeof parameterValue}`) } } /** - * @throws Throws a type error if the parameter value is not an array. + * @throws Throws a type error if the parameter value is not an Array. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ export function requireArray(parameterValue: any[], parameterName: string) { if (!isArray(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not an array, but received type: ${typeof parameterValue}`) + throw new TypeError(`Parameter '${parameterName}' is not an Array instance`) } } /** - * @throws Throws a type error if the parameter value is not a uint8array. + * @throws Throws a type error if the parameter value is not a Uint8Array. * @param parameterValue The parameter value. * @param parameterName The parameter name. */ export function requireUint8Array(parameterValue: Uint8Array, parameterName: string) { if (!isUint8Array(parameterValue)) { - throw new TypeError( - `Parameter '${parameterName}' is not a Uint8Array, but received type: ${typeof parameterValue}` - ) + throw new TypeError(`Parameter '${parameterName}' is not a Uint8Array instance`) } } @@ -103,9 +99,7 @@ export function requireUint8Array(parameterValue: Uint8Array, parameterName: str */ export function requireObject(parameterValue: object, parameterName: string) { if (!isObject(parameterValue)) { - throw new TypeError( - `Parameter '${parameterName}' is not an object, but received type: ${typeof parameterValue}` - ) + throw new TypeError(`Parameter '${parameterName}' is not an object, received type: ${typeof parameterValue}`) } } @@ -116,7 +110,7 @@ export function requireObject(parameterValue: object, parameterName: string) { */ export function requireBigInt(parameterValue: bigint, parameterName: string) { if (!isBigInt(parameterValue)) { - throw new TypeError(`Parameter '${parameterName}' is not a bigint, but received type: ${typeof parameterValue}`) + throw new TypeError(`Parameter '${parameterName}' is not a bigint, received type: ${typeof parameterValue}`) } } diff --git a/packages/utils/src/type-checks.ts b/packages/utils/src/type-checks.ts index 027de5d6c..98464b6ad 100644 --- a/packages/utils/src/type-checks.ts +++ b/packages/utils/src/type-checks.ts @@ -12,8 +12,8 @@ const supportedTypes = [ "number", "string", "function", - "array", - "uint8array", + "Array", + "Uint8Array", "object", "bigint", "stringified-bigint", @@ -57,28 +57,28 @@ export function isFunction(value: any): boolean { } /** - * Returns true if the value is an array, false otherwise. + * Returns true if the value is an object, false otherwise. + * Please, note that arrays are also objects in JavaScript. * @param value The value to be checked. */ -export function isArray(value: any): boolean { - return typeof value === "object" && Array.isArray(value) +export function isObject(value: any): boolean { + return typeof value === "object" } /** - * Returns true if the value is a uint8array, false otherwise. + * Returns true if the value is an Array instance, false otherwise. * @param value The value to be checked. */ -export function isUint8Array(value: any): boolean { - return value instanceof Uint8Array +export function isArray(value: any): boolean { + return isObject(value) && Array.isArray(value) } /** - * Returns true if the value is an object, false otherwise. - * Please, note that arrays are also objects in JavaScript. + * Returns true if the value is a Uint8Array instance, false otherwise. * @param value The value to be checked. */ -export function isObject(value: any): boolean { - return typeof value === "object" +export function isUint8Array(value: any): boolean { + return value instanceof Uint8Array } /** @@ -119,8 +119,8 @@ export function isHexadecimal(value: any) { } /** - * Checks if the given value can be considered as "BigNumber-ish". - * A value is considered "BigNumber-ish" if it meets + * Checks if the given value can be considered as BigNumberish. + * A value is considered BigNumberish if it meets * any of the following conditions: it's a number, a bigint, a string * that can be converted to a bigint, a hexadecimal * string, or a Buffer object. @@ -149,9 +149,9 @@ export function isType(value: any, type: SupportedType): boolean { return isString(value) case "function": return isFunction(value) - case "array": + case "Array": return isArray(value) - case "uint8array": + case "Uint8Array": return isUint8Array(value) case "object": return isObject(value) diff --git a/packages/utils/tests/error-handlers.test.ts b/packages/utils/tests/error-handlers.test.ts index 23cc67156..0433d191e 100644 --- a/packages/utils/tests/error-handlers.test.ts +++ b/packages/utils/tests/error-handlers.test.ts @@ -53,7 +53,7 @@ describe("# error-handlers", () => { it("Should throw an error if the parameter is not an array", () => { const fun = () => requireArray(1 as any, "parameter") - expect(fun).toThrow("Parameter 'parameter' is not an array") + expect(fun).toThrow("Parameter 'parameter' is not an Array instance") }) it("Should not throw an error if the parameter is an array", () => { @@ -62,13 +62,13 @@ describe("# error-handlers", () => { expect(fun).not.toThrow() }) - it("Should throw an error if the parameter is not a uint8array", () => { + it("Should throw an error if the parameter is not a Uint8Array", () => { const fun = () => requireUint8Array([] as any, "parameter") - expect(fun).toThrow("Parameter 'parameter' is not a Uint8Array") + expect(fun).toThrow("Parameter 'parameter' is not a Uint8Array instance") }) - it("Should not throw an error if the parameter is a uint8array", () => { + it("Should not throw an error if the parameter is a Uint8Array", () => { const fun = () => requireUint8Array(new Uint8Array([]), "parameter") expect(fun).not.toThrow() @@ -153,7 +153,7 @@ describe("# error-handlers", () => { }) it("Should not throw an error if the parameter is either a string or an array", () => { - const fun = () => requireTypes("string", "parameter", ["string", "array"]) + const fun = () => requireTypes("string", "parameter", ["string", "Array"]) expect(fun).not.toThrow() }) diff --git a/packages/utils/tests/type-checks.test.ts b/packages/utils/tests/type-checks.test.ts index cc8eb8c15..4c546b22a 100644 --- a/packages/utils/tests/type-checks.test.ts +++ b/packages/utils/tests/type-checks.test.ts @@ -38,19 +38,19 @@ describe("# type-checks", () => { expect(isFunction(1)).toBeFalsy() }) - it("Should return true if the value is an array", () => { + it("Should return true if the value is an Array instance", () => { expect(isArray([])).toBeTruthy() }) - it("Should return false if the value is not an array", () => { + it("Should return false if the value is not an Array instance", () => { expect(isArray(1)).toBeFalsy() }) - it("Should return true if the value is a uint8array", () => { + it("Should return true if the value is a Uint8Array instance", () => { expect(isUint8Array(new Uint8Array([]))).toBeTruthy() }) - it("Should return false if the value is not a uint8array", () => { + it("Should return false if the value is not a Uint8Array", () => { expect(isUint8Array(1)).toBeFalsy() }) @@ -103,8 +103,8 @@ describe("# type-checks", () => { expect(isType(1, "number")).toBeTruthy() expect(isType("string", "string")).toBeTruthy() expect(isType(() => true, "function")).toBeTruthy() - expect(isType([], "array")).toBeTruthy() - expect(isType(new Uint8Array([]), "uint8array")).toBeTruthy() + expect(isType([], "Array")).toBeTruthy() + expect(isType(new Uint8Array([]), "Uint8Array")).toBeTruthy() expect(isType({}, "object")).toBeTruthy() expect(isType(BigInt(1), "bigint")).toBeTruthy() expect(isType("1242342342342342", "stringified-bigint")).toBeTruthy() @@ -116,8 +116,8 @@ describe("# type-checks", () => { expect(isType("string", "number")).toBeFalsy() expect(isType(1, "string")).toBeFalsy() expect(isType(1, "function")).toBeFalsy() - expect(isType(1, "array")).toBeFalsy() - expect(isType(1, "uint8array")).toBeFalsy() + expect(isType(1, "Array")).toBeFalsy() + expect(isType(1, "Uint8Array")).toBeFalsy() expect(isType(1, "object")).toBeFalsy() expect(isType(1, "bigint")).toBeFalsy() expect(isType(1, "stringified-bigint")).toBeFalsy()