UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

1,023 lines 47.3 kB
/** * Wrapper file for various gadgets, with a namespace and doccomments. */ import { compactMultiRangeCheck, multiRangeCheck, rangeCheck8, rangeCheck16, rangeCheck32, rangeCheck64, rangeCheckN, isDefinitelyInRangeN, l2Mask, lMask, l2, l, l3, } from './range-check.js'; import { not, rotate32, rotate64, xor, and, or, leftShift64, rightShift64, leftShift32, } from './bitwise.js'; import { ForeignField, Field3, Sum as ForeignFieldSum } from './foreign-field.js'; import { divMod32, addMod32, divMod64, addMod64 } from './arithmetic.js'; import { SHA2 } from './sha2.js'; import { SHA256 } from './sha256.js'; import { BLAKE2B } from './blake2b.js'; import { rangeCheck3x12, inTable } from './lookup.js'; import { arrayGet, arrayGetGeneric } from './basic.js'; import { sliceField3 } from './bit-slices.js'; export { Gadgets, Field3, ForeignFieldSum }; const Gadgets = { /** * Get value from array at a Field element index, in O(n) constraints, where n is the array length. * * **Warning**: This gadget assumes that the index is within the array bounds `[0, n)`, * and returns an unconstrained result otherwise. * To use it with an index that is not already guaranteed to be within the array bounds, you should add a suitable range check. * * ```ts * let array = Provable.witnessFields(3, () => [1n, 2n, 3n]); * let index = Provable.witness(Field, () => 1n); * * let value = Gadgets.arrayGet(array, index); * ``` * * **Note**: This saves n constraints compared to `Provable.switch(array.map((_, i) => index.equals(i)), type, array)`. */ arrayGet, /** * Get value from array in O(n) constraints. * * Assumes that index is in [0, n), returns an unconstrained result otherwise. */ arrayGetGeneric, /** * Asserts that the input value is in the range [0, 2^64). * * This function proves that the provided field element can be represented with 64 bits. * If the field element exceeds 64 bits, an error is thrown. * * @param x - The value to be range-checked. * * @throws Throws an error if the input value exceeds 64 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(12345678n)); * Gadgets.rangeCheck64(x); // successfully proves 64-bit range * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * Gadgets.rangeCheck64(xLarge); // throws an error since input exceeds 64 bits * ``` * * **Note**: Small "negative" field element inputs are interpreted as large integers close to the field size, * and don't pass the 64-bit check. If you want to prove that a value lies in the int64 range [-2^63, 2^63), * you could use `rangeCheck64(x.add(1n << 63n))`. * * _Advanced usage_: This returns the 4 highest limbs of x, in reverse order, i.e. [x52, x40, x28, x16]. * This is useful if you want to do a range check for 52, 40, 28, or 16 bits instead of 64, * by constraining some of the returned limbs to be 0. */ rangeCheck64(x) { return rangeCheck64(x); }, /** * Asserts that the input value is in the range [0, 2^32). * * This function proves that the provided field element can be represented with 32 bits. * If the field element exceeds 32 bits, an error is thrown. * * @param x - The value to be range-checked. * * @throws Throws an error if the input value exceeds 32 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(12345678n)); * Gadgets.rangeCheck32(x); // successfully proves 32-bit range * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * Gadgets.rangeCheck32(xLarge); // throws an error since input exceeds 32 bits * ``` * * **Note**: Small "negative" field element inputs are interpreted as large integers close to the field size, * and don't pass the 32-bit check. If you want to prove that a value lies in the int32 range [-2^31, 2^31), * you could use `rangeCheck32(x.add(1n << 31n))`. */ rangeCheck32(x) { return rangeCheck32(x); }, /** * Asserts that the input value is in the range [0, 2^n). `n` must be a multiple of 16. * * This function proves that the provided field element can be represented with `n` bits. * If the field element exceeds `n` bits, an error is thrown. * * @param x - The value to be range-checked. * @param n - The number of bits to be considered for the range check. * @param message - Optional message to be displayed when the range check fails. * * @throws Throws an error if the input value exceeds `n` bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(12345678n)); * Gadgets.rangeCheckN(32, x); // successfully proves 32-bit range * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * Gadgets.rangeCheckN(32, xLarge); // throws an error since input exceeds 32 bits * ``` */ rangeCheckN(n, x, message) { return rangeCheckN(n, x, message); }, /** * Returns a boolean which being true proves that x is in the range [0, 2^n). * * **Beware**: The output being false does **not** prove that x is not in the range [0, 2^n). * This should not be viewed as a standalone provable method but as an advanced helper function * for gadgets which need a weakened form of range check. * * @param x - The value to be weakly range-checked. * @param n - The number of bits to be considered for the range check. * * @returns a Bool that is definitely only true if the input is in the range [0, 2^n), * but could also be false _even if_ the input is in the range [0, 2^n). * * @example * ```ts * const x = Provable.witness(Field, () => Field(12345678n)); * let definitelyInRange = Gadgets.isDefinitelyInRangeN(32, x); // could be true or false * ``` */ isDefinitelyInRangeN(n, x) { return isDefinitelyInRangeN(n, x); }, /* * Asserts that the input value is in the range [0, 2^16). * * See {@link Gadgets.rangeCheck64} for analogous details and usage examples. */ rangeCheck16(x) { return rangeCheck16(x); }, /** * Asserts that the input value is in the range [0, 2^8). * * See {@link Gadgets.rangeCheck64} for analogous details and usage examples. */ rangeCheck8(x) { return rangeCheck8(x); }, /** * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, * with the distinction that the bits are circulated to the opposite end of a 64-bit representation rather than being discarded. * For a left rotation, this means that bits shifted off the left end reappear at the right end. * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. * * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. * * **Important:** The gadget assumes that its input is at most 64 bits in size. * * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the rotation. * To safely use `rotate64()`, you need to make sure that the value passed in is range-checked to 64 bits; * for example, using {@link Gadgets.rangeCheck64}. * * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) * * @param field {@link Field} element to rotate. * @param bits amount of bits to rotate this {@link Field} element with. * @param direction left or right rotation direction. * * @throws Throws an error if the input value exceeds 64 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(0b001100)); * const y = Gadgets.rotate64(x, 2, 'left'); // left rotation by 2 bits * const z = Gadgets.rotate64(x, 2, 'right'); // right rotation by 2 bits * y.assertEquals(0b110000); * z.assertEquals(0b000011); * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * Gadgets.rotate64(xLarge, 32, "left"); // throws an error since input exceeds 64 bits * ``` */ rotate64(field, bits, direction = 'left') { return rotate64(field, bits, direction); }, /** * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, * with the distinction that the bits are circulated to the opposite end of a 32-bit representation rather than being discarded. * For a left rotation, this means that bits shifted off the left end reappear at the right end. * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. * * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. * * **Important:** The gadget assumes that its input is at most 32 bits in size. * * If the input exceeds 32 bits, the gadget is invalid and fails to prove correct execution of the rotation. * To safely use `rotate32()`, you need to make sure that the value passed in is range-checked to 32 bits; * for example, using {@link Gadgets.rangeCheck32}. * * * @param field {@link Field} element to rotate. * @param bits amount of bits to rotate this {@link Field} element with. * @param direction left or right rotation direction. * * @throws Throws an error if the input value exceeds 32 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(0b001100)); * const y = Gadgets.rotate32(x, 2, 'left'); // left rotation by 2 bits * const z = Gadgets.rotate32(x, 2, 'right'); // right rotation by 2 bits * y.assertEquals(0b110000); * z.assertEquals(0b000011); * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * Gadgets.rotate32(xLarge, 32, "left"); // throws an error since input exceeds 32 bits * ``` */ rotate32(field, bits, direction = 'left') { return rotate32(field, bits, direction); }, /** * Bitwise XOR gadget on {@link Field} elements. Equivalent to the [bitwise XOR `^` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR). * A XOR gate works by comparing two bits and returning `1` if two bits differ, and `0` if two bits are equal. * * This gadget builds a chain of XOR gates recursively. Each XOR gate can verify 16 bit at most. If your input elements exceed 16 bit, another XOR gate will be added to the chain. * * The `length` parameter lets you define how many bits should be compared. `length` is rounded to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. * * **Note:** Specifying a larger `length` parameter adds additional constraints. * * It is also important to mention that specifying a smaller `length` allows the verifier to infer the length of the original input data (e.g. smaller than 16 bit if only one XOR gate has been used). * A zkApp developer should consider these implications when choosing the `length` parameter and carefully weigh the trade-off between increased amount of constraints and security. * * **Important:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. * * For example, with `length = 2` (`paddedLength = 16`), `xor()` will fail for any input that is larger than `2**16`. * * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#xor-1) * * @param a {@link Field} element to compare. * @param b {@link Field} element to compare. * @param length amount of bits to compare. * * @throws Throws an error if the input values exceed `2^paddedLength - 1`. * * @example * ```ts * let a = Field(0b0101); * let b = Field(0b0011); * * let c = Gadgets.xor(a, b, 4); // xor-ing 4 bits * c.assertEquals(0b0110); * ``` */ xor(a, b, length) { return xor(a, b, length); }, /** * Bitwise NOT gate on {@link Field} elements. Similar to the [bitwise * NOT `~` operator in JavaScript](https://developer.mozilla.org/en-US/docs/ * Web/JavaScript/Reference/Operators/Bitwise_NOT). * * **Note:** The NOT gate only operates over the amount * of bits specified by the `length` parameter. * * A NOT gate works by returning `1` in each bit position if the * corresponding bit of the operand is `0`, and returning `0` if the * corresponding bit of the operand is `1`. * * The `length` parameter lets you define how many bits to NOT. * * **Note:** Specifying a larger `length` parameter adds additional constraints. The operation will fail if the length or the input value is larger than 254. * * NOT is implemented in two different ways. If the `checked` parameter is set to `true` * the {@link Gadgets.xor} gadget is reused with a second argument to be an * all one bitmask the same length. This approach needs as many rows as an XOR would need * for a single negation. If the `checked` parameter is set to `false`, NOT is * implemented as a subtraction of the input from the all one bitmask. This * implementation is returned by default if no `checked` parameter is provided. * * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#not) * * @example * ```ts * // not-ing 4 bits with the unchecked version * let a = Field(0b0101); * let b = Gadgets.not(a,4,false); * * b.assertEquals(0b1010); * * // not-ing 4 bits with the checked version utilizing the xor gadget * let a = Field(0b0101); * let b = Gadgets.not(a,4,true); * * b.assertEquals(0b1010); * ``` * * @param a - The value to apply NOT to. The operation will fail if the value is larger than 254. * @param length - The number of bits to be considered for the NOT operation. * @param checked - Optional boolean to determine if the checked or unchecked not implementation is used. If it * is set to `true` the {@link Gadgets.xor} gadget is reused. If it is set to `false`, NOT is implemented * as a subtraction of the input from the all one bitmask. It is set to `false` by default if no parameter is provided. * * @throws Throws an error if the input value exceeds 254 bits. */ not(a, length, checked = false) { return not(a, length, checked); }, /** * Performs a left shift operation on the provided {@link Field} element. * This operation is similar to the `<<` shift operation in JavaScript, * where bits are shifted to the left, and the overflowing bits are discarded. * * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. * * **Important:** The gadgets assumes that its input is at most 64 bits in size. * * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the shift. * Therefore, to safely use `leftShift()`, you need to make sure that the values passed in are range checked to 64 bits. * For example, this can be done with {@link Gadgets.rangeCheck64}. * * @param field {@link Field} element to shift. * @param bits Amount of bits to shift the {@link Field} element to the left. The amount should be between 0 and 64 (or else the shift will fail). * * @throws Throws an error if the input value exceeds 64 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary * const y = Gadgets.leftShift64(x, 2); // left shift by 2 bits * y.assertEquals(0b110000); // 48 in binary * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * leftShift64(xLarge, 32); // throws an error since input exceeds 64 bits * ``` */ leftShift64(field, bits) { return leftShift64(field, bits); }, /** * Performs a left shift operation on the provided {@link Field} element. * This operation is similar to the `<<` shift operation in JavaScript, * where bits are shifted to the left, and the overflowing bits are discarded. * * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. * * **Important:** The gadgets assumes that its input is at most 32 bits in size. * * The output is range checked to 32 bits. * * @param field {@link Field} element to shift. * @param bits Amount of bits to shift the {@link Field} element to the left. The amount should be between 0 and 32 (or else the shift will fail). * * @example * ```ts * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary * const y = Gadgets.leftShift32(x, 2); // left shift by 2 bits * y.assertEquals(0b110000); // 48 in binary * ``` */ leftShift32(field, bits) { return leftShift32(field, bits); }, /** * Performs a right shift operation on the provided {@link Field} element. * This is similar to the `>>` shift operation in JavaScript, where bits are moved to the right. * The `rightShift64` function utilizes the rotation method internally to implement this operation. * * * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. * * **Important:** The gadgets assumes that its input is at most 64 bits in size. * * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the shift. * To safely use `rightShift64()`, you need to make sure that the value passed in is range-checked to 64 bits; * for example, using {@link Gadgets.rangeCheck64}. * * @param field {@link Field} element to shift. * @param bits Amount of bits to shift the {@link Field} element to the right. The amount should be between 0 and 64 (or else the shift will fail). * * @throws Throws an error if the input value exceeds 64 bits. * * @example * ```ts * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary * const y = Gadgets.rightShift64(x, 2); // right shift by 2 bits * y.assertEquals(0b000011); // 3 in binary * * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); * rightShift64(xLarge, 32); // throws an error since input exceeds 64 bits * ``` */ rightShift64(field, bits) { return rightShift64(field, bits); }, /** * Bitwise AND gadget on {@link Field} elements. Equivalent to the [bitwise AND `&` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND). * The AND gate works by comparing two bits and returning `1` if both bits are `1`, and `0` otherwise. * * It can be checked by a double generic gate that verifies the following relationship between the values * below (in the process it also invokes the {@link Gadgets.xor} gadget which will create additional constraints depending on `length`). * * The generic gate verifies:\ * `a + b = sum` and the conjunction equation `2 * and = sum - xor`\ * Where:\ * `a + b = sum`\ * `a ^ b = xor`\ * `a & b = and` * * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) * * The `length` parameter lets you define how many bits should be compared. `length` is rounded * to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values * are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. * * **Note:** Specifying a larger `length` parameter adds additional constraints. * * **Note:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. * For example, with `length = 2` (`paddedLength = 16`), `and()` will fail for any input that is larger than `2**16`. * * @example * ```typescript * let a = Field(3); // ... 000011 * let b = Field(5); // ... 000101 * * let c = Gadgets.and(a, b, 2); // ... 000001 * c.assertEquals(1); * ``` */ and(a, b, length) { return and(a, b, length); }, /** * Bitwise OR gadget on {@link Field} elements. Equivalent to the [bitwise OR `|` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR). * The OR gate works by comparing two bits and returning `1` if at least one bit is `1`, and `0` otherwise. * * The `length` parameter lets you define how many bits should be compared. `length` is rounded * to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values * are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. * * **Note:** Specifying a larger `length` parameter adds additional constraints. * * **Note:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. * For example, with `length = 2` (`paddedLength = 16`), `and()` will fail for any input that is larger than `2**16`. * * @example * ```typescript * let a = Field.from(3); // ... 000011 * let b = Field.from(5); // ... 000101 * * let c = Gadgets.or(a, b, 16); // ... 000111 * c.assertEquals(7); * ``` */ or(a, b, length) { return or(a, b, length); }, /** * Multi-range check. * * Proves that x, y, z are all in the range [0, 2^88). * * This takes 4 rows, so it checks 88*3/4 = 66 bits per row. This is slightly more efficient * than 64-bit range checks, which can do 64 bits in 1 row. * * In particular, the 3x88-bit range check supports bigints up to 264 bits, which in turn is enough * to support foreign field multiplication with moduli up to 2^259. * * @example * ```ts * Gadgets.multiRangeCheck([x, y, z]); * ``` * * @throws Throws an error if one of the input values exceeds 88 bits. */ multiRangeCheck(limbs) { multiRangeCheck(limbs); }, /** * Compact multi-range check * * This is a variant of {@link multiRangeCheck} where the first two variables are passed in * combined form xy = x + 2^88*y. * * The gadget * - splits up xy into x and y * - proves that xy = x + 2^88*y * - proves that x, y, z are all in the range [0, 2^88). * * The split form [x, y, z] is returned. * * @example * ```ts * let [x, y] = Gadgets.compactMultiRangeCheck([xy, z]); * ``` * * @throws Throws an error if `xy` exceeds 2*88 = 176 bits, or if z exceeds 88 bits. */ compactMultiRangeCheck(xy, z) { return compactMultiRangeCheck(xy, z); }, /** * Checks that three {@link Field} elements are in the range [0, 2^12) (using only one row). * * Internally, this gadget relies on the 12-bit [range check table](https://github.com/o1-labs/proof-systems/blob/master/kimchi/src/circuits/lookup/tables/mod.rs). * All three inputs are checked to be included in that table. * * It's possible to use this as a range check for bit lengths n < 12, by passing in _two values_. * - the value to be checked, `x`, to prove that x in [0, 2^12) * - x scaled by 2^(12 - n), to prove that either x in [0, 2^n) or `x * 2^(12 - n)` overflows the field size (which is excluded by the first check) * * Note that both of these checks are necessary to prove x in [0, 2^n). * * You can find more details about lookups in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=lookup%20gate#lookup) * * @param v0 - The first {@link Field} element to be checked. * @param v1 - The second {@link Field} element to be checked. * @param v2 - The third {@link Field} element to be checked. * * @throws Throws an error if one of the input values exceeds 2^12. * * @example * ```typescript * let a = Field(4000); * rangeCheck3x12(a, Field(0), Field(0)); // works, since `a` is less than 12 bits * * let aScaled = a.mul(1 << 4); // scale `a`, to assert that it's less than 8 bits * rangeCheck3x12(a, aScaled, Field(0)); // throws an error, since `a` is greater than 8 bits (and so `aScaled` is greater than 12 bits) * ``` */ rangeCheck3x12(v0, v1, v2) { return rangeCheck3x12(v0, v1, v2); }, /** * In-circuit check that up to 3 pairs of index and value are in the runtime * table given by the identifier. Each given pair is a tuple composed of a * bigint and a Field. * * Internally, it creates a lookup gate for the three pairs. If fewer pairs are * given, the remaining pairs are duplicates of the first one. * * @param id * @param pair0 * @param pair1 * @param pair2 */ inTable(id, pair0, pair1, pair2) { return inTable(id, pair0, pair1, pair2); }, /** * Gadgets for foreign field operations. * * A _foreign field_ is a finite field different from the native field of the proof system. * * The `ForeignField` namespace exposes operations like modular addition and multiplication, * which work for any finite field of size less than 2^259. * * Foreign field elements are represented as 3 limbs of native field elements. * Each limb holds 88 bits of the total, in little-endian order. * * All `ForeignField` gadgets expect that their input limbs are constrained to the range [0, 2^88). * Range checks on outputs are added by the gadget itself. */ ForeignField: { /** * Foreign field addition: `x + y mod f` * * The modulus `f` does not need to be prime. * * Inputs and outputs are 3-tuples of native Fields. * Each input limb is assumed to be in the range [0, 2^88), and the gadget is invalid if this is not the case. * The result limbs are guaranteed to be in the same range. * * @example * ```ts * let x = Provable.witness(Field3, () => 9n); * let y = Provable.witness(Field3, () => 10n); * * // range check x and y * Gadgets.multiRangeCheck(x); * Gadgets.multiRangeCheck(y); * * // compute x + y mod 17 * let z = ForeignField.add(x, y, 17n); * * Provable.log(z); // ['2', '0', '0'] = limb representation of 2 = 9 + 10 mod 17 * ``` * * **Warning**: The gadget does not assume that inputs are reduced modulo f, * and does not prove that the result is reduced modulo f. * It only guarantees that the result is in the correct residue class. * * @param x left summand * @param y right summand * @param f modulus * @returns x + y mod f */ add(x, y, f) { return ForeignField.add(x, y, f); }, /** * Check whether `x = c mod f` * * `c` is a constant, and we require `c` in `[0, f)` * * Assumes that `x` is almost reduced modulo `f`, so we know that `x` might be `c` or `c + f`, but not `c + 2f`, `c + 3f`, ... */ equals(x, c, f) { return ForeignField.equals(x, c, f); }, /** * Foreign field subtraction: `x - y mod f` * * See {@link Gadgets.ForeignField.add} for assumptions and usage examples. * * @throws fails if `x - y < -f`, where the result cannot be brought back to a positive number by adding `f` once. */ sub(x, y, f) { return ForeignField.sub(x, y, f); }, /** * Foreign field negation: `-x mod f = f - x` * * See {@link ForeignField.add} for assumptions and usage examples. * * @throws fails if `x > f`, where `f - x < 0`. */ neg(x, f) { return ForeignField.negate(x, f); }, /** * Foreign field sum: `xs[0] + signs[0] * xs[1] + ... + signs[n-1] * xs[n] mod f` * * This gadget takes a list of inputs and a list of signs (of size one less than the inputs), * and computes a chain of additions or subtractions, depending on the sign. * A sign is of type `1n | -1n`, where `1n` represents addition and `-1n` represents subtraction. * * **Note**: For 3 or more inputs, `sum()` uses fewer constraints than a sequence of `add()` and `sub()` calls, * because we can avoid range checks on intermediate results. * * See {@link Gadgets.ForeignField.add} for assumptions on inputs. * * @example * ```ts * let x = Provable.witness(Field3, () => 4n); * let y = Provable.witness(Field3, () => 5n); * let z = Provable.witness(Field3, () => 10n); * * // range check x, y, z * Gadgets.multiRangeCheck(x); * Gadgets.multiRangeCheck(y); * Gadgets.multiRangeCheck(z); * * // compute x + y - z mod 17 * let sum = ForeignField.sum([x, y, z], [1n, -1n], 17n); * * Provable.log(sum); // ['16', '0', '0'] = limb representation of 16 = 4 + 5 - 10 mod 17 * ``` */ sum(xs, signs, f) { return ForeignField.sum(xs, signs, f); }, /** * @internal * * Foreign field multiplication: `x * y mod f` * * The modulus `f` does not need to be prime, but has to be smaller than 2^259. * * **Assumptions**: In addition to the assumption that input limbs are in the range [0, 2^88), as in all foreign field gadgets, * this assumes an additional bound on the inputs: `x * y < 2^264 * p`, where p is the native modulus. * We usually assert this bound by proving that `x[2] < f[2] + 1`, where `x[2]` is the most significant limb of x. * To do this, we use an 88-bit range check on `2^88 - x[2] - (f[2] + 1)`, and same for y. * The implication is that x and y are _almost_ reduced modulo f. * * All of the above assumptions are checked by {@link Gadgets.ForeignField.assertAlmostReduced}. * * **Warning**: This gadget does not add the extra bound check on the result. * So, to use the result in another foreign field multiplication, you have to add the bound check on it yourself, again. * * @example * ```ts * // example modulus: secp256k1 prime * let f = (1n << 256n) - (1n << 32n) - 0b1111010001n; * * let x = Provable.witness(Field3, () => f - 1n); * let y = Provable.witness(Field3, () => f - 2n); * * // range check x, y and prove additional bounds x[2] <= f[2] * ForeignField.assertAlmostReduced([x, y], f); * * // compute x * y mod f * let z = ForeignField.mul(x, y, f); * * Provable.log(z); // ['2', '0', '0'] = limb representation of 2 = (-1)*(-2) mod f * ``` */ mul(x, y, f) { return ForeignField.mul(x, y, f); }, /** * @internal * * Foreign field inverse: `x^(-1) mod f` * * See {@link Gadgets.ForeignField.mul} for assumptions on inputs and usage examples. * * This gadget adds an extra bound check on the result, so it can be used directly in another foreign field multiplication. */ inv(x, f) { return ForeignField.inv(x, f); }, /** * @internal * * Foreign field division: `x * y^(-1) mod f` * * See {@link Gadgets.ForeignField.mul} for assumptions on inputs and usage examples. * * This gadget adds an extra bound check on the result, so it can be used directly in another foreign field multiplication. * * @throws Different than {@link Gadgets.ForeignField.mul}, this fails on unreduced input `x`, because it checks that `x === (x/y)*y` and the right side will be reduced. */ div(x, y, f) { return ForeignField.div(x, y, f); }, /** * @internal * * Optimized multiplication of sums in a foreign field, for example: `(x - y)*z = a + b + c mod f` * * Note: This is much more efficient than using {@link Gadgets.ForeignField.add} and {@link Gadgets.ForeignField.sub} separately to * compute the multiplication inputs and outputs, and then using {@link Gadgets.ForeignField.mul} to constrain the result. * * The sums passed into this method are "lazy sums" created with {@link Gadgets.ForeignField.Sum}. * You can also pass in plain {@link Field3} elements. * * **Assumptions**: The assumptions on the _summands_ are analogous to the assumptions described in {@link Gadgets.ForeignField.mul}: * - each summand's limbs are in the range [0, 2^88) * - summands that are part of a multiplication input satisfy `x[2] <= f[2]` * * @throws if the modulus is so large that the second assumption no longer suffices for validity of the multiplication. * For small sums and moduli < 2^256, this will not fail. * * @throws if the provided multiplication result is not correct modulo f. * * @example * ```ts * // range-check x, y, z, a, b, c * ForeignField.assertAlmostReduced([x, y, z], f); * Gadgets.multiRangeCheck(a); * Gadgets.multiRangeCheck(b); * Gadgets.multiRangeCheck(c); * * // create lazy input sums * let xMinusY = ForeignField.Sum(x).sub(y); * let aPlusBPlusC = ForeignField.Sum(a).add(b).add(c); * * // assert that (x - y)*z = a + b + c mod f * ForeignField.assertMul(xMinusY, z, aPlusBPlusC, f); * ``` */ assertMul(x, y, z, f, message) { return ForeignField.assertMul(x, y, z, f, message); }, /** * @internal * * Lazy sum of {@link Field3} elements, which can be used as input to {@link Gadgets.ForeignField.assertMul}. */ Sum(x) { return ForeignField.Sum(x); }, /** * @internal * * Prove that each of the given {@link Field3} elements is "almost" reduced modulo f, * i.e., satisfies the assumptions required by {@link Gadgets.ForeignField.mul} and other gadgets: * - each limb is in the range [0, 2^88) * - the most significant limb is less or equal than the modulus, x[2] <= f[2] * * **Note**: This method is most efficient when the number of input elements is a multiple of 3. * * @throws if any of the assumptions is violated. * * @example * ```ts * let x = Provable.witness(Field3, () => 4n); * let y = Provable.witness(Field3, () => 5n); * let z = Provable.witness(Field3, () => 10n); * * ForeignField.assertAlmostReduced([x, y, z], f); * * // now we can use x, y, z as inputs to foreign field multiplication * let xy = ForeignField.mul(x, y, f); * let xyz = ForeignField.mul(xy, z, f); * * // since xy is an input to another multiplication, we need to prove that it is almost reduced again! * ForeignField.assertAlmostReduced([xy], f); // TODO: would be more efficient to batch this with 2 other elements * ``` */ assertAlmostReduced(xs, f, { skipMrc = false } = {}) { ForeignField.assertAlmostReduced(xs, f, skipMrc); }, /** * Prove that x < f for any constant f < 2^264, or for another `Field3` f. * * If f is a finite field modulus, this means that the given field element is fully reduced modulo f. * This is a stronger statement than {@link ForeignField.assertAlmostReduced} * and also uses more constraints; it should not be needed in most use cases. * * **Note**: This assumes that the limbs of x are in the range [0, 2^88), in contrast to * {@link ForeignField.assertAlmostReduced} which adds that check itself. * * @throws if x is greater or equal to f. * * @example * ```ts * let x = Provable.witness(Field3, () => 0x1235n); * * // range check limbs of x * Gadgets.multiRangeCheck(x); * * // prove that x is fully reduced mod f * Gadgets.ForeignField.assertLessThan(x, f); * ``` */ assertLessThan(x, f) { ForeignField.assertLessThan(x, f); }, /** * Prove that x <= f for any constant f < 2^264, or for another `Field3` f. * * See {@link ForeignField.assertLessThan} for details and usage examples. */ assertLessThanOrEqual(x, f) { ForeignField.assertLessThanOrEqual(x, f); }, /** * Proves that x is equal to y. */ assertEquals(x, y) { ForeignField.assertEquals(x, y); }, /** * Convert x, which may be unreduced, to a canonical representative xR < f * such that x = xR mod f * * Note: This method is complete, it works for all unreduced field elements. * It can therefore be used to protect against incompleteness of field operations in other places. */ toCanonical(x, f) { return ForeignField.toCanonical(x, f); }, /** * Provable method for slicing a 3x88-bit bigint into smaller bit chunks of length `chunkSize` * * This serves as a range check that the input is in [0, 2^maxBits) */ sliceField3, }, /** * Helper methods to interact with 3-limb vectors of Fields. * * **Note:** This interface does not contain any provable methods. */ Field3, /** * Division modulo 2^32. The operation decomposes a {@link Field} element in the range [0, 2^64) into two 32-bit limbs, `remainder` and `quotient`, using the following equation: `n = quotient * 2^32 + remainder`. * * **Note:** The gadget acts as a proof that the input is in the range [0, 2^64). If the input exceeds 64 bits, the gadget fails. * * Asserts that both `remainder` and `quotient` are in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. * * @example * ```ts * let n = Field((1n << 32n) + 8n) * let { remainder, quotient } = Gadgets.divMod32(n); * // remainder = 8, quotient = 1 * * n.assertEquals(quotient.mul(1n << 32n).add(remainder)); * ``` */ divMod32, /** * Addition modulo 2^32. The operation adds two {@link Field} elements in the range [0, 2^32) and returns the result modulo 2^32. * * Asserts that the result is in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. * * It uses {@link Gadgets.divMod32} internally by adding the two {@link Field} elements and then decomposing the result into `remainder` and `quotient` and returning the `remainder`. * * **Note:** The gadget assumes both inputs to be in the range [0, 2^64). When called with non-range-checked inputs, be aware that the sum `a + b` can overflow the native field and the gadget can succeed but return an invalid result. * * @example * ```ts * let a = Field(8n); * let b = Field(1n << 32n); * * Gadgets.addMod32(a, b).assertEquals(Field(8n)); * ``` * */ addMod32, /** * Division modulo 2^64. The operation decomposes a {@link Field} element in the range [0, 2^128) into two 64-bit limbs, `remainder` and `quotient`, using the following equation: `n = quotient * 2^64 + remainder`. * * **Note:** The gadget acts as a proof that the input is in the range [0, 2^128). If the input exceeds 128 bits, the gadget fails. * * Asserts that both `remainder` and `quotient` are in the range [0, 2^64) using {@link Gadgets.rangeCheck64}. * * @example * ```ts * let n = Field((1n << 64n) + 8n) * let { remainder, quotient } = Gadgets.divMod64(n); * // remainder = 8, quotient = 1 * * n.assertEquals(quotient.mul(1n << 64n).add(remainder)); * ``` */ divMod64, /** * Addition modulo 2^64. The operation adds two {@link Field} elements in the range [0, 2^64) and returns the result modulo 2^64. * * Asserts that the result is in the range [0, 2^64) using {@link Gadgets.rangeCheck64}. * * It uses {@link Gadgets.divMod64} internally by adding the two {@link Field} elements and then decomposing the result into `remainder` and `quotient` and returning the `remainder`. * * **Note:** The gadget assumes both inputs to be in the range [0, 2^64). When called with non-range-checked inputs, be aware that the sum `a + b` can overflow the native field and the gadget can succeed but return an invalid result. * * @example * ```ts * let a = Field(8n); * let b = Field(1n << 64n); * * Gadgets.addMod64(a, b).assertEquals(Field(8n)); * ``` */ addMod64, /** * * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. * * Applies the SHA2-256 hash function to a list of byte-sized elements. * * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. * * Produces an output of {@link Bytes} that conforms to the chosen bit length. * * @param data - {@link Bytes} representing the message to hash. * * ```ts * let preimage = Bytes.fromString("hello world"); * let digest = Gadgets.SHA256.hash(preimage); * ``` * @deprecated {@link SHA256} is deprecated in favor of {@link SHA2}, which supports more variants of the hash function. */ SHA256: SHA256, /** * Implementation of the [SHA2 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function * with 224 | 256 | 384 | 512 bit output. * * Applies the SHA2 hash function to a list of byte-sized elements. * * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. * * Produces an output of {@link Bytes} that conforms to the chosen bit length. * * @param length - 224 | 256 | 384 | 512 representing the length of the hash. * @param data - {@link Bytes} representing the message to hash. * * ```ts * let preimage = Bytes.fromString("hello world"); * let digest = Gadgets.SHA2.hash(512, preimage); * ``` * */ SHA2: SHA2, /** * Implementation of the [BLAKE2b hash function.](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2) Hash function with arbitrary length output. * * Applies the BLAKE2b hash function to a list of byte-sized elements. * * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. * * Produces an output of {@link Bytes} that conforms to the chosen digest length. * * @param data - {@link Bytes} representing the message to hash. * * ```ts * let preimage = Bytes.fromString("hello world"); * let digest = Gadgets.BLAKE2b.hash(preimage); * ``` * */ BLAKE2B: BLAKE2B, /** * Default limb size constants mostly used in range checks. */ Constants: { l2Mask, l, l2, l3, lMask, }, }; //# sourceMappingURL=gadgets.js.map