UNPKG

@zkpersona/noir-helpers

Version:

Helpers utilities for Noir proofs and input generation.

826 lines (812 loc) 30.6 kB
import { BackendOptions, UltraPlonkBackend, UltraHonkBackend, ProofData } from '@aztec/bb.js'; import { InputMap, Noir, CompiledCircuit } from '@noir-lang/noir_js'; import { Options } from 'json2toml'; import { inspect } from 'node:util'; import { z } from 'zod'; type Bit = 0 | 1; /** * Options for configuring circuit behavior and optimization settings. * These options affect how the circuit is compiled and optimized. */ type CircuitOptions = { /** * @description Whether to produce SNARK friendly proofs * * @default false */ recursive: boolean; }; /** * Configuration for the proving backend system. * This type defines the complete setup for the proving system, including * backend selection, optimization options, and circuit-specific settings. */ type ProvingBackend = { /** * The type of proving backend to use. * * @default 'plonk' * @remarks * - 'honk': Uses the HONK proving system * - 'plonk': Uses the PLONK proving system * - 'all': Initialize all available proving systems */ type?: 'honk' | 'plonk' | 'all'; /** * Additional options specific to the selected backend. * These options are passed directly to the underlying proving system. * * @default undefined * @see {@link BackendOptions} for available options */ options?: BackendOptions; /** * Circuit-specific configuration options. * Controls how the circuit is compiled and optimized. * * @default undefined * @see {@link CircuitOptions} for available options */ circuitOptions?: CircuitOptions; }; /** * @description The value of an input to a circuit. */ type InputValue = InputMap[string]; /** * A high-performance zero-knowledge prover supporting multiple proving systems. * This class provides a unified interface for generating and verifying * zero-knowledge proofs using either PLONK or HONK proving systems. * * @example * ```typescript * // Initialize the prover with a compiled circuit * const circuit = await Noir.compile(circuitSource); * const prover = new Prover(circuit, { * type: 'plonk', * circuitOptions: { recursive: true } * }); * * try { * // Generate a proof * const proof = await prover.fullProve(inputs); * * // Verify the proof * const isValid = await prover.verify(proof); * * console.log('Proof verification:', isValid); * } finally { * // Clean up resources * await prover.destroy(); * } * ``` */ declare class Prover { plonk?: UltraPlonkBackend; honk?: UltraHonkBackend; noir: Noir; provingBackend: ProvingBackend; /** * Creates a new instance of the Prover. * * @param circuit - The compiled Noir circuit to use for proving * @param provingBackend - Configuration for the proving backend * * @throws {Error} If the circuit bytecode is invalid or malformed * @throws {Error} If the proving backend configuration is invalid * @throws {Error} If backend initialization fails */ constructor(circuit: CompiledCircuit, provingBackend: ProvingBackend); /** * Simulates the circuit execution to generate a witness. * This method executes the circuit with the given inputs to produce * a witness that can be used for proof generation. * * @param input - The input values for the circuit * @returns A promise that resolves to an object containing: * - witness: The generated witness as a Uint8Array * - returnValue: The circuit's return value * * @throws {Error} If the circuit execution fails * @throws {Error} If the input values are invalid or malformed * @throws {Error} If witness generation fails */ simulateWitness(input: InputMap): Promise<{ witness: Uint8Array; returnValue: InputValue; }>; /** * Generates a zero-knowledge proof from a witness. * * @param witness - The witness generated from circuit execution * @param provingBackend - Optional override for the proving backend configuration * @returns A promise that resolves to the generated proof * * @throws {Error} If the witness is invalid or malformed * @throws {Error} If the proving backend is not initialized * @throws {Error} If proof generation fails */ prove(witness: Uint8Array, provingBackend?: ProvingBackend): Promise<ProofData>; /** * Generates a complete zero-knowledge proof from circuit inputs. * This is a convenience method that combines witness generation and proof generation. * * @param input - The input values for the circuit * @param provingBackend - Optional override for the proving backend configuration * @returns A promise that resolves to the generated proof * * @throws {Error} If witness generation fails * @throws {Error} If proof generation fails * @throws {Error} If the input values are invalid */ fullProve(input: InputMap, provingBackend?: ProvingBackend): Promise<ProofData>; /** * Verifies a zero-knowledge proof. * * @param proof - The proof to verify * @param provingBackend - Optional override for the proving backend configuration * @returns A promise that resolves to true if the proof is valid, false otherwise * * @throws {Error} If the proof is invalid or malformed * @throws {Error} If the proving backend is not initialized * @throws {Error} If verification fails */ verify(proof: ProofData, provingBackend?: ProvingBackend): Promise<boolean>; /** * Cleans up resources used by the proving backends. * This method should be called when the prover is no longer needed * to free up system resources. It is recommended to call this method * in a finally block or when the prover instance is being disposed. * * @returns A promise that resolves when cleanup is complete * * @throws {Error} If resource cleanup fails */ destroy(): Promise<void>; /** * Gets the appropriate proving backend based on the specified type. * This method handles backend selection and initialization state checking. * * @param backendType - Optional override for the backend type * @returns The initialized proving backend * * @throws {Error} If the backend type is invalid * @throws {Error} If the requested backend is not initialized * * @private */ private getProvingBackend; } /** * Generates a TOML file from a JSON object. * * @param data - The JSON object to convert to TOML. * @param filePath - The absolute path to the TOML file. * @param formatterOpts {@link Options} - Optional formatter options. * * @throws Will throw an error if the provided filePath is not absolute, * or if an error occurs during file system operations. */ declare const generateToml: (data: object, filePath: string, formatterOpts?: Options) => void; declare class FixedSizeArray<T extends DataType, N extends number> { private readonly length; private items; constructor(length: N, items: T[]); len(): number; toArray(): T[]; get(index: number): T; at(index: number): T; set(index: number, item: T): void; forEach(callback: (item: T, index: number) => void): void; map<U>(callback: (item: T, index: number) => U): U[]; toCircuitInputs(): InputValue[]; } /** * A boolean value type that provides type-safe boolean operations and serialization. * This class wraps a primitive boolean value and provides methods for boolean algebra * and comparison operations. */ declare class Bool { /** The underlying boolean value */ private readonly val; /** * Creates a new boolean instance with the specified value. * * @param value - The boolean value to initialize the instance with */ constructor(value: boolean); /** * Returns the underlying boolean value. * * @returns The boolean value */ value(): boolean; /** * Compares this boolean with another for equality. * * @param other - The boolean to compare with * @returns true if both booleans have the same value, false otherwise */ eq(other: Bool): boolean; /** * Performs a logical NOT operation on this boolean. * Returns a new boolean instance with the negated value. * * @returns A new boolean instance with the opposite value */ not(): Bool; /** * Converts the boolean to its Circuit Input representation. * Returns the underlying primitive boolean value. * * @returns The boolean value as a primitive */ toCircuitInputs(): boolean; } /** * A bounded vector implementation that maintains a fixed maximum capacity * while allowing dynamic growth up to that capacity. This class provides * a type-safe, resizable array-like structure with a strict upper bound * on the number of elements it can contain. * * @template T - The type of elements stored in the vector, must extend DataType * @template N - The type of the maximum size of the vector, must extend number */ declare class BoundedVec<T extends DataType, N extends number> { /** Maximum number of elements the vector can hold */ private readonly maxSize; /** Current number of elements in the vector */ private length; /** Internal storage for vector elements */ private items; /** * Creates a new bounded vector with the specified maximum size. * The vector is initialized with default values up to the maximum size. * * @param maxSize - The maximum number of elements the vector can hold * @param defaultValue - Default value for initialization * @param initialItems - Initial items to add to the vector * @throws {Error} If maxSize is negative */ constructor(maxSize: N, defaultValue: T, initialItems?: T[]); /** * Returns the current number of elements in the vector. * * @returns The number of elements currently stored in the vector */ len(): number; /** * Returns the maximum number of elements the vector can hold. * * @returns The maximum capacity of the vector */ maxLen(): number; /** * Checks if the vector is empty. * * @returns true if the vector contains no elements, false otherwise */ private isEmpty; /** * Checks if the vector has reached its maximum capacity. * * @returns true if the vector is at maximum capacity, false otherwise */ private isFull; /** * Returns the internal storage for the vector. */ storage(): T[]; /** * Retrieves the element at the specified index. * * @param index - The zero-based index of the element to retrieve * @returns The element at the specified index * @throws {Error} If the index is out of bounds (index < 0 or index >= length) */ get(index: number): T; /** * Retrieves the element at the specified index, supporting negative indices. * Negative indices count from the end of the vector (-1 is the last element). * * @param index - The index of the element to retrieve (can be negative) * @returns The element at the specified index * @throws {Error} If the index is out of bounds after adjustment */ at(index: number): T; /** * Adds an element to the end of the vector. * * @param item - The element to add to the vector * @throws {Error} If the vector is at maximum capacity */ push(item: T): void; /** * Sets the element at the specified index to the given value. * * @param index - The zero-based index where the element should be set * @param item - The new value to set at the specified index * @throws {Error} If the index is out of bounds (index < 0 or index >= length) */ set(index: number, item: T): void; /** * Removes and returns the last element of the vector. * * @returns The last element of the vector * @throws {Error} If the vector is empty */ pop(): T; /** * Extends the vector by adding all elements from the given array. * * @param arr - The array of elements to add to the vector * @throws {Error} If adding the elements would exceed the vector's maximum capacity */ extendFromArray(arr: T[]): void; /** * Extends the vector by adding all elements from another bounded vector. * * @param vec - The bounded vector whose elements should be added * @throws {Error} If adding the elements would exceed the vector's maximum capacity */ extendFromVec(vec: BoundedVec<T, N>): void; /** * Returns a copy of the vector's elements as an array. * Only includes elements up to the current length. * * @returns An array containing the vector's elements */ private toArray; /** * Converts the BoundedVec to a Circuit Input representation. * The JSON object contains both the elements and the current length. * * @returns Noir Circuit Input representation of the BoundedVec */ toCircuitInputs(): { storage: InputValue[]; len: number; }; } declare const fieldInputSchema: z.ZodUnion<[z.ZodNumber, z.ZodBigInt, z.ZodEffects<z.ZodString, string, string>]>; declare const FieldValidator: z.ZodEffects<z.ZodEffects<z.ZodUnion<[z.ZodNumber, z.ZodBigInt, z.ZodEffects<z.ZodString, string, string>]>, bigint, string | number | bigint>, bigint, string | number | bigint>; declare const IntegerValidator: (min: bigint, max: bigint) => z.ZodEffects<z.ZodEffects<z.ZodUnion<[z.ZodNumber, z.ZodBigInt, z.ZodEffects<z.ZodString, string, string>]>, bigint, string | number | bigint>, bigint, string | number | bigint>; type FieldInput = z.infer<typeof fieldInputSchema>; type IntegerInput = z.infer<typeof fieldInputSchema>; /** * Represents a finite field element used in noir. * * The Field class implements operations and conversions for field elements that are compatible * with Noir's default proving backend with the Grumpkin curve, and fields size of 254-bits. * * This class provides: * - Core conversions between different representations (bits, bytes, radix) * - Mathematical operations (addition, subtraction, multiplication, division) * - Cryptographic operations (sign determination) * - Validation and safety checks * - Serialization methods * * @example * ```typescript * const field = new Field(42n); * const bits = field.toLeBits(8); // [0, 1, 0, 1, 0, 1, 0, 0] * const sum = field.add(58n); // new Field(100n) * ``` */ declare class Field { /** The internal bigint representation of the field value */ readonly value: bigint; /** The maximum bit size of a Field */ static readonly MAX_BIT_SIZE = 254n; /** The modulus of the field */ static readonly MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n; /** * Creates a new Field instance from various input types. * * @param input - The input value to convert to a field element * @throws Error if the input cannot be parsed as a valid field value */ constructor(input: FieldInput); [inspect.custom](): string; /** * Internal helper function to compute the log2 of a large value (0-2^254). * * @param value - The value to compute the log2 of * @returns The log2 of the value */ private log2; /** * Converts the field value to an array of bits in little-endian order. * * @param length - The number of bits to extract * @returns An array of bits (0 or 1) in little-endian order * @throws Error if length is negative or exceeds Field.MAX_BIT_SIZE or if length is less than the minimum required bits to represent the value */ toLeBits<N extends number>(length: N): Bit[]; /** * Converts the field value to an array of bits in big-endian order. * * @param length - The number of bits to extract * @returns An array of bits (0 or 1) in big-endian order * @throws Error if length is negative or exceeds Field.MAX_BIT_SIZE or if length is less than the minimum required bits to represent the value */ toBeBits<N extends number>(length: N): Bit[]; /** * Converts the field value to an array of bytes in little-endian order. * * @param length - The number of bytes to extract * @returns An array of bytes in little-endian order * @throws Error if length is negative or exceeds Field.MAX_BIT_SIZE or if length is less than the minimum required bytes to represent the value */ toLeBytes<N extends number>(length: N): number[]; /** * Converts the field value to an array of bytes in big-endian order. * * @param length - The number of bytes to extract * @returns An array of bytes in big-endian order, sized by the length parameter * @throws Error if length is negative, or exceeds maximum bit size or if length is less than the minimum required bytes to represent the value */ toBeBytes<N extends number>(length: N): number[]; /** * Calculates the minimum length needed to represent a value in a given radix, rounded up to the nearest power of 2. * * This function is used internally to determine the minimum number of digits required to represent * a field value in a given base (radix). The result is always rounded up to the nearest power of 2 * to ensure consistent sizing for cryptographic operations. * * @param value - The bigint value to calculate the representation length for * @param radix - The base to represent the value in (must be a power of 2) * @returns The minimum number of digits needed to represent the value in the given radix, rounded up to the nearest power of 2 * @throws {Error} If the value is negative * @throws {Error} If the radix is not a power of 2 * @throws {Error} If the radix is less than 2 */ private minRadixLength; /** * Converts the field value to an array of digits in little-endian order using the specified radix. * * @param radix - The base to use for conversion * @param length - The number of digits to extract * @returns An array of digits in little-endian order * @throws Error if radix is invalid or length is negative or if length is less than the minimum required digits to represent the value, or if length is greater than 256 */ toLeRadix(radix: number, length: number): number[]; /** * Converts the field value to an array of digits in big-endian order using the specified radix. * * @param radix - The base to use for conversion (must be a power of 2 between 2 and 256) * @param length - The number of digits to extract * @returns An array of digits in big-endian order * @throws Error if radix is invalid or length is negative or if length is less than the minimum required digits to represent the value, or if length is greater than 256 */ toBeRadix(radix: number, length: number): number[]; /** * Raises the field value to the specified power. * * @param exponent - The power to raise to * @returns A new Field instance with the result * @throws Error if exponent is negative or greater than equal to 2^32 */ pow32(exponent: this): Field; /** * Asserts that the field value does not exceed the specified bit size. * * @param bitSize - The maximum allowed bit size * @throws Error if the field value exceeds the specified bit size */ assertMaxBitSize(bitSize: number): void; /** * Returns the sign of the field value (0 or 1). * This is used in cryptographic operations to determine the sign of a field element. * * @returns 0 if the value is even, 1 if odd */ sgn0(): Bit; /** * Returns a string representation of the field value. * * @returns The field value as a decimal string */ toString(): string; /** * Converts the field value to a hexadecimal string. * * @param length - Optional length in bytes to pad the hex string to * @returns The field value as a hexadecimal string with '0x' prefix */ toHex(): string; /** * Converts the field value to its Circuit Input representation. * Returns the field value as a hexadecimal string. * */ toCircuitInputs(): string; /** * Checks if this field value equals another value. * * @param other - The value to compare against (can be Field, number, string, or bigint) * @returns true if the values are equal */ equals(other: Field | number | string | bigint): boolean; /** * Adds another value to this field value. * * @param input - The value to add (can be Field, number, string, or bigint) * @returns A new Field instance with the sum */ add(input: Field | number | string | bigint): Field; /** * Subtracts another value from this field value. * * @param input - The value to subtract (can be Field, number, string, or bigint) * @returns A new Field instance with the difference */ sub(input: Field | number | string | bigint): Field; /** * Multiplies this field value by another value. * * @param input - The value to multiply by (can be Field, number, string, or bigint) * @returns A new Field instance with the product */ mul(input: Field | number | string | bigint): Field; /** * Computes the modular inverse of a field element. * * @param a - The field element to compute the inverse of * @param mod - The modulus to use for the computation * * @returns The modular inverse of the field element */ private modInv; /** * Divides this field value by another value. * * @param input - The value to divide by (can be Field, number, string, or bigint) * @returns A new Field instance with the quotient * @throws Error if dividing by zero */ div(input: Field | number | string | bigint): Field; /** * Computes the remainder of division of this field value by another value. * * @param input - The value to divide by (can be Field, number, string, or bigint) * @returns A new Field instance with the remainder * @throws Error if dividing by zero */ mod(input: Field | number | string | bigint): Field; /** * Creates a copy of this field value. * * @returns A new Field instance with the same value */ clone(): Field; static modBeBits(): Bit[]; static modBeBytes(): number[]; static modLeBits(): Bit[]; static modLeBytes(): number[]; static modNumBits(): bigint; } /** * Abstract base class for all integer types with fixed bit-widths. * Provides common functionality for arithmetic operations and value validation. * * @template T - The concrete integer type extending this class */ declare abstract class AbstractInteger extends Field { /** Maximum value that can be represented by this integer type */ protected static MAX_VALUE: bigint; /** Minimum value that can be represented by this integer type */ protected static MIN_VALUE: bigint; /** * Gets the minimum value that can be represented by this integer type. * @returns The minimum value as a bigint */ protected min(): bigint; /** * Gets the maximum value that can be represented by this integer type. * @returns The maximum value as a bigint */ protected max(): bigint; /** * Creates a new instance of the concrete integer type. * @param value - The value to initialize the new instance with * @returns A new instance of the concrete integer type */ protected newInstance(value: IntegerInput | Field): this; /** * Constructs a new integer instance with the given value. * Validates that the input value is within the allowed range. * @param input - The value to initialize this integer with * @throws {ZodError} If the input value is outside the allowed range */ constructor(input: IntegerInput); /** * Adds another integer to this one with overflow checking. * @param other - The integer to add * @returns A new integer representing the sum * @throws {Error} If the result would overflow */ add(other: this | IntegerInput): this; /** * Subtracts another integer from this one with overflow checking. * @param other - The integer to subtract * @returns A new integer representing the difference * @throws {Error} If the result would overflow */ sub(other: this | IntegerInput): this; /** * Multiplies this integer by another with overflow checking. * @param other - The integer to multiply by * @returns A new integer representing the product * @throws {Error} If the result would overflow */ mul(other: this | IntegerInput): this; /** * Divides this integer by another with overflow checking. * @param other - The integer to divide by * @returns A new integer representing the quotient * @throws {Error} If the result would overflow or if dividing by zero */ div(other: this | IntegerInput): this; /** * Computes the remainder of dividing this integer by another. * @param other - The integer to divide by * @returns A new integer representing the remainder * @throws {Error} If dividing by zero */ mod(other: this | IntegerInput): this; /** * Performs wrapping addition of two integers. * If the result would overflow, it wraps around to the minimum value. * @param other - The integer to add * @returns A new integer representing the wrapped sum */ wrappingAdd(other: this): this; /** * Performs wrapping subtraction of two integers. * If the result would underflow, it wraps around to the maximum value. * @param other - The integer to subtract * @returns A new integer representing the wrapped difference */ wrappingSub(other: this): this; /** * Performs wrapping multiplication of two integers. * If the result would overflow, it wraps around to the minimum value. * @param other - The integer to multiply by * @returns A new integer representing the wrapped product */ wrappingMul(other: this): this; } /** * 1-bit unsigned integer type. * Range: 0 to 1 */ declare class U1 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 8-bit unsigned integer type. * Range: 0 to 255 */ declare class U8 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 16-bit unsigned integer type. * Range: 0 to 65,535 */ declare class U16 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 32-bit unsigned integer type. * Range: 0 to 4,294,967,295 */ declare class U32 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 64-bit unsigned integer type. * Range: 0 to 18,446,744,073,709,551,615 */ declare class U64 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 1-bit signed integer type. * Range: -1 to 0 */ declare class I1 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 8-bit signed integer type. * Range: -128 to 127 */ declare class I8 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 16-bit signed integer type. * Range: -32,768 to 32,767 */ declare class I16 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 32-bit signed integer type. * Range: -2,147,483,648 to 2,147,483,647 */ declare class I32 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * 64-bit signed integer type. * Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 */ declare class I64 extends AbstractInteger { protected static MAX_VALUE: bigint; protected static MIN_VALUE: bigint; } /** * A string value type that provides type-safe string operations and byte conversion. * This class wraps a primitive string value and provides methods for string comparison * and UTF-8 byte array conversion. */ declare class Str { /** The underlying string value */ private readonly val; /** * Creates a new string instance with the specified value. * * @param value - The string value to initialize the instance with */ constructor(value: string); /** * Returns the underlying string value. * * @returns The string value */ value(): string; /** * Returns the length of the string. * * @returns The number of characters in the string */ len(): number; /** * Converts the string to an array of UTF-8 encoded bytes. * Each byte is represented as a U8 integer value. * * @returns An array of U8 integers representing the UTF-8 encoded bytes */ asBytes(): U8[]; /** * Compares this string with another for equality. * * @param other - The string to compare with * @returns true if both strings have the same value, false otherwise */ eq(other: Str): boolean; /** * Converts the string to its Circuit Input representation. * Returns the underlying primitive string value. */ toCircuitInputs(): string; } type DataType = Field | Bool | Str | BoundedVec<DataType, number> | FixedSizeArray<DataType, number> | StructMap; type StructMap = { [key: string]: DataType; }; declare const getInputRepresentation: (value: DataType) => InputValue; declare function toCircuitInputs(value: StructMap): InputMap; export { AbstractInteger, type Bit, Bool, BoundedVec, type DataType, Field, type FieldInput, FieldValidator, FixedSizeArray, I1, I16, I32, I64, I8, type InputValue, type IntegerInput, IntegerValidator, Prover, type ProvingBackend, Str, type StructMap, U1, U16, U32, U64, U8, generateToml, getInputRepresentation, toCircuitInputs };