o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
368 lines (367 loc) • 14.3 kB
TypeScript
import { FiniteField } from '../../bindings/crypto/finite-field.js';
import { Field } from './field.js';
import { Bool } from './bool.js';
import { Tuple, TupleMap } from '../util/types.js';
import { Field3 } from './gadgets/foreign-field.js';
import { ProvablePureExtended } from './types/struct.js';
export { createForeignField };
export type { ForeignField, UnreducedForeignField, AlmostForeignField, CanonicalForeignField };
declare class ForeignField {
static _Bigint: FiniteField | undefined;
static _modulus: bigint | undefined;
static get Bigint(): {
modulus: bigint;
sizeInBits: number;
t: bigint;
M: bigint;
twoadicRoot: bigint;
mod(x: bigint): bigint;
add(x: bigint, y: bigint): bigint;
not(x: bigint, bits: number): bigint;
negate(x: bigint): bigint;
sub(x: bigint, y: bigint): bigint;
mul(x: bigint, y: bigint): bigint;
inverse: (x: bigint) => bigint | undefined;
div(x: bigint, y: bigint): bigint | undefined;
square(x: bigint): bigint;
isSquare(x: bigint): boolean;
sqrt(x: bigint): bigint | undefined;
power(x: bigint, n: bigint): bigint;
dot(x: bigint[], y: bigint[]): bigint;
equal(x: bigint, y: bigint): boolean;
isEven(x: bigint): boolean;
random(): bigint;
fromNumber(x: number): bigint;
fromBigint(x: bigint): bigint;
rot(x: bigint, bits: bigint, direction?: "left" | "right", maxBits?: bigint): bigint;
leftShift(x: bigint, bits: number, maxBitSize?: number): bigint;
rightShift(x: bigint, bits: number): bigint;
};
static get modulus(): bigint;
get modulus(): bigint;
static get sizeInBits(): number;
/**
* The internal representation of a foreign field element, as a tuple of 3 limbs.
*/
value: Field3;
get Constructor(): typeof ForeignField;
/**
* Sibling classes that represent different ranges of field elements.
*/
static _variants: {
unreduced: typeof UnreducedForeignField;
almostReduced: typeof AlmostForeignField;
canonical: typeof CanonicalForeignField;
} | undefined;
/**
* Constructor for unreduced field elements.
*/
static get Unreduced(): typeof UnreducedForeignField;
/**
* Constructor for field elements that are "almost reduced", i.e. lie in the range [0, 2^ceil(log2(p))).
*/
static get AlmostReduced(): typeof AlmostForeignField;
/**
* Constructor for field elements that are fully reduced, i.e. lie in the range [0, p).
*/
static get Canonical(): typeof CanonicalForeignField;
/**
* Create a new {@link ForeignField} from a bigint, number, string or another ForeignField.
* @example
* ```ts
* let x = new ForeignField(5);
* ```
*
* Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants.
*
* - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately.
* - When constructing from a `Field3` array, ensure all elements are valid Field elements and range checked.
* - Ensure constants are correctly reduced to the modulus of the field.
*/
constructor(x: ForeignField | Field3 | bigint | number | string);
/**
* Coerce the input to a {@link ForeignField}.
*/
static from(x: bigint | number | string): CanonicalForeignField;
static from(x: ForeignField | bigint | number | string): ForeignField;
/**
* @internal
* Checks whether this field element is a constant.
*
* See {@link FieldVar} to understand constants vs variables.
*/
isConstant(): boolean;
/**
* @internal
* Convert this field element to a constant.
*
* See {@link FieldVar} to understand constants vs variables.
*
* **Warning**: This function is only useful in {@link Provable.witness} or {@link Provable.asProver} blocks,
* that is, in situations where the prover computes a value outside provable code.
*/
toConstant(): ForeignField;
/**
* Convert this field element to a bigint.
*/
toBigInt(): bigint;
/**
* Assert that this field element lies in the range [0, 2^k),
* where k = ceil(log2(p)) and p is the foreign field modulus.
*
* Returns the field element as a {@link AlmostForeignField}.
*
* For a more efficient version of this for multiple field elements, see {@link assertAlmostReduced}.
*
* Note: this does not ensure that the field elements is in the canonical range [0, p).
* To assert that stronger property, there is {@link assertCanonical}.
* You should typically use {@link assertAlmostReduced} though, because it is cheaper to prove and sufficient for
* ensuring validity of all our non-native field arithmetic methods.
*/
assertAlmostReduced(): AlmostForeignField;
/**
* Assert that one or more field elements lie in the range [0, 2^k),
* where k = ceil(log2(p)) and p is the foreign field modulus.
*
* This is most efficient than when checking a multiple of 3 field elements at once.
*/
static assertAlmostReduced<T extends Tuple<ForeignField>>(...xs: T): TupleMap<T, AlmostForeignField>;
/**
* Assert that this field element is fully reduced,
* i.e. lies in the range [0, p), where p is the foreign field modulus.
*
* Returns the field element as a {@link CanonicalForeignField}.
*/
assertCanonical(): CanonicalForeignField;
/**
* Finite field addition
* @example
* ```ts
* x.add(2); // x + 2 mod p
* ```
*/
add(y: ForeignField | bigint | number): UnreducedForeignField;
/**
* Finite field negation
* @example
* ```ts
* x.neg(); // -x mod p = p - x
* ```
*/
neg(): AlmostForeignField;
/**
* Finite field subtraction
* @example
* ```ts
* x.sub(1); // x - 1 mod p
* ```
*/
sub(y: ForeignField | bigint | number): UnreducedForeignField;
/**
* Sum (or difference) of multiple finite field elements.
*
* @example
* ```ts
* let z = ForeignField.sum([3, 2, 1], [-1, 1]); // 3 - 2 + 1
* z.assertEquals(2);
* ```
*
* This method expects a list of ForeignField-like values, `x0,...,xn`,
* and a list of "operations" `op1,...,opn` where every op is 1 or -1 (plus or minus),
* and returns
*
* `x0 + op1*x1 + ... + opn*xn`
*
* where the sum is computed in finite field arithmetic.
*
* **Important:** For more than two summands, this is significantly more efficient
* than chaining calls to {@link ForeignField.add} and {@link ForeignField.sub}.
*
*/
static sum(xs: (ForeignField | bigint | number)[], operations: (1 | -1)[]): UnreducedForeignField;
/**
* Assert equality with a ForeignField-like value
*
* @example
* ```ts
* x.assertEquals(0, "x is zero");
* ```
*
* Since asserting equality can also serve as a range check,
* this method returns `x` with the appropriate type:
*
* @example
* ```ts
* let xChecked = x.assertEquals(1, "x is 1");
* xChecked satisfies CanonicalForeignField;
* ```
*/
assertEquals(y: bigint | number | CanonicalForeignField, message?: string): CanonicalForeignField;
assertEquals(y: AlmostForeignField, message?: string): AlmostForeignField;
assertEquals(y: ForeignField, message?: string): ForeignField;
/**
* Assert that this field element is less than a constant c: `x < c`.
*
* The constant must satisfy `0 <= c < 2^264`, otherwise an error is thrown.
*
* @example
* ```ts
* x.assertLessThan(10);
* ```
*/
assertLessThan(c: bigint | number, message?: string): void;
/**
* Unpack a field element to its bits, as a {@link Bool}[] array.
*
* This method is provable!
*/
toBits(length?: number): Bool[];
/**
* Create a field element from its bits, as a `Bool[]` array.
*
* This method is provable!
*/
static fromBits(bits: Bool[]): AlmostForeignField;
static random(): CanonicalForeignField;
/**
* Instance version of `Provable<ForeignField>.toFields`, see {@link Provable.toFields}
*/
toFields(): Field[];
static check(_: ForeignField): void;
static _provable: any;
/**
* `Provable<ForeignField>`, see {@link Provable}
*/
static get provable(): any;
}
declare class ForeignFieldWithMul extends ForeignField {
/**
* Finite field multiplication
* @example
* ```ts
* x.mul(y); // x*y mod p
* ```
*/
mul(y: AlmostForeignField | bigint | number): UnreducedForeignField;
/**
* Multiplicative inverse in the finite field
* @example
* ```ts
* let z = x.inv(); // 1/x mod p
* z.mul(x).assertEquals(1);
* ```
*/
inv(): AlmostForeignField;
/**
* Division in the finite field, i.e. `x*y^(-1) mod p` where `y^(-1)` is the finite field inverse.
* @example
* ```ts
* let z = x.div(y); // x/y mod p
* z.mul(y).assertEquals(x);
* ```
*/
div(y: AlmostForeignField | bigint | number): AlmostForeignField;
}
declare class UnreducedForeignField extends ForeignField {
type: 'Unreduced' | 'AlmostReduced' | 'FullyReduced';
static _provable: ProvablePureExtended<UnreducedForeignField, bigint, string> | undefined;
static get provable(): ProvablePureExtended<UnreducedForeignField, bigint, string>;
static check(x: ForeignField): void;
}
declare class AlmostForeignField extends ForeignFieldWithMul {
type: 'AlmostReduced' | 'FullyReduced';
constructor(x: AlmostForeignField | Field3 | bigint | number | string);
static _provable: ProvablePureExtended<AlmostForeignField, bigint, string> | undefined;
static get provable(): ProvablePureExtended<AlmostForeignField, bigint, string>;
static check(x: ForeignField): void;
/**
* Coerce the input to an {@link AlmostForeignField} without additional assertions.
*
* **Warning:** Only use if you know what you're doing.
*/
static unsafeFrom(x: ForeignField): AlmostForeignField;
/**
* Check equality with a constant value.
*
* @example
* ```ts
* let isXZero = x.equals(0);
* ```
*/
equals(y: bigint | number): Bool;
}
declare class CanonicalForeignField extends ForeignFieldWithMul {
type: "FullyReduced";
constructor(x: CanonicalForeignField | Field3 | bigint | number | string);
static _provable: ProvablePureExtended<CanonicalForeignField, bigint, string> | undefined;
static get provable(): ProvablePureExtended<CanonicalForeignField, bigint, string>;
static check(x: ForeignField): void;
/**
* Coerce the input to a {@link CanonicalForeignField} without additional assertions.
*
* **Warning:** Only use if you know what you're doing.
*/
static unsafeFrom(x: ForeignField): CanonicalForeignField;
/**
* Check equality with a ForeignField-like value.
*
* @example
* ```ts
* let isEqual = x.equals(y);
* ```
*
* Note: This method only exists on canonical fields; on unreduced fields, it would be easy to
* misuse, because not being exactly equal does not imply being unequal modulo p.
*/
equals(y: CanonicalForeignField | bigint | number): Bool;
}
/**
* Create a class representing a prime order finite field, which is different from the native {@link Field}.
*
* ```ts
* const SmallField = createForeignField(17n); // the finite field F_17
* ```
*
* `createForeignField(p)` takes the prime modulus `p` of the finite field as input, as a bigint.
* We support prime moduli up to a size of 259 bits.
*
* The returned {@link ForeignField} class supports arithmetic modulo `p` (addition and multiplication),
* as well as helper methods like `assertEquals()` and `equals()`.
*
* _Advanced details:_
*
* Internally, a foreign field element is represented as three native field elements, each of which
* represents a limb of 88 bits. Therefore, being a valid foreign field element means that all 3 limbs
* fit in 88 bits, and the foreign field element altogether is smaller than the modulus p.
*
* Since the full `x < p` check is expensive, by default we only prove a weaker assertion, `x < 2^ceil(log2(p))`,
* see {@link ForeignField.assertAlmostReduced} for more details.
*
* This weaker assumption is what we call "almost reduced", and it is represented by the {@link AlmostForeignField} class.
* Note that only {@link AlmostForeignField} supports multiplication and inversion, while {@link UnreducedForeignField}
* only supports addition and subtraction.
*
* This function returns the `Unreduced` class, which will cause the minimum amount of range checks to be created by default.
* If you want to do multiplication, you have two options:
* - create your field elements using the {@link ForeignField.AlmostReduced} constructor.
* ```ts
* let x = Provable.witness(ForeignField.AlmostReduced, () => 5n);
* ```
* - create your field elements normally and convert them using `x.assertAlmostReduced()`.
* ```ts
* let xChecked = x.assertAlmostReduced(); // asserts x < 2^ceil(log2(p)); returns `AlmostForeignField`
* ```
*
* Similarly, there is a separate class {@link CanonicalForeignField} which represents fully reduced, "canonical" field elements.
* To convert to a canonical field element, use `ForeignField.assertCanonical()`:
*
* ```ts
* x.assertCanonical(); // asserts x < p; returns `CanonicalForeignField`
* ```
* You will likely not need canonical fields most of the time.
*
* Base types for all of these classes are separately exported as {@link UnreducedForeignField}, {@link AlmostForeignField} and {@link CanonicalForeignField}.,
*
* @param modulus the modulus of the finite field you are instantiating
*/
declare function createForeignField(modulus: bigint): typeof UnreducedForeignField;