Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove private class fields #1319

Merged
merged 7 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 19 additions & 25 deletions src/lib/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { defineBinable } from '../bindings/lib/binable.js';
import { NonNegativeInteger } from '../bindings/crypto/non-negative.js';
import { asProver } from './provable-context.js';

export { BoolVar, Bool, isBool };
export { BoolVar, Bool };

// same representation, but use a different name to communicate intent / constraints
type BoolVar = FieldVar;
Expand All @@ -34,7 +34,7 @@ class Bool {
value: BoolVar;

constructor(x: boolean | Bool | BoolVar) {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
this.value = x.value;
return;
}
Expand Down Expand Up @@ -75,7 +75,7 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() && toBoolean(y));
}
return new Bool(Snarky.bool.and(this.value, Bool.#toVar(y)));
return new Bool(Snarky.bool.and(this.value, toFieldVar(y)));
}

/**
Expand All @@ -87,7 +87,7 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() || toBoolean(y));
}
return new Bool(Snarky.bool.or(this.value, Bool.#toVar(y)));
return new Bool(Snarky.bool.or(this.value, toFieldVar(y)));
}

/**
Expand All @@ -102,7 +102,7 @@ class Bool {
}
return;
}
Snarky.bool.assertEqual(this.value, Bool.#toVar(y));
Snarky.bool.assertEqual(this.value, toFieldVar(y));
} catch (err) {
throw withMessage(err, message);
}
Expand Down Expand Up @@ -144,7 +144,7 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() === toBoolean(y));
}
return new Bool(Snarky.bool.equals(this.value, Bool.#toVar(y)));
return new Bool(Snarky.bool.equals(this.value, toFieldVar(y)));
}

/**
Expand Down Expand Up @@ -194,14 +194,14 @@ class Bool {
}

static toField(x: Bool | boolean): Field {
return new Field(Bool.#toVar(x));
return new Field(toFieldVar(x));
}

/**
* Boolean negation.
*/
static not(x: Bool | boolean): Bool {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
return x.not();
}
return new Bool(!x);
Expand All @@ -211,7 +211,7 @@ class Bool {
* Boolean AND operation.
*/
static and(x: Bool | boolean, y: Bool | boolean): Bool {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
return x.and(y);
}
return new Bool(x).and(y);
Expand All @@ -221,7 +221,7 @@ class Bool {
* Boolean OR operation.
*/
static or(x: Bool | boolean, y: Bool | boolean): Bool {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
return x.or(y);
}
return new Bool(x).or(y);
Expand All @@ -231,7 +231,7 @@ class Bool {
* Asserts if both {@link Bool} are equal.
*/
static assertEqual(x: Bool, y: Bool | boolean): void {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
x.assertEquals(y);
return;
}
Expand All @@ -242,7 +242,7 @@ class Bool {
* Checks two {@link Bool} for equality.
*/
static equal(x: Bool | boolean, y: Bool | boolean): Bool {
if (Bool.#isBool(x)) {
if (x instanceof Bool) {
return x.equals(y);
}
return new Bool(x).equals(y);
Expand Down Expand Up @@ -342,15 +342,6 @@ class Bool {
return new Bool(x.value);
},
};

static #isBool(x: boolean | Bool | BoolVar): x is Bool {
return x instanceof Bool;
}

static #toVar(x: boolean | Bool): BoolVar {
if (Bool.#isBool(x)) return x.value;
return FieldVar.constant(B(x));
}
}

const BoolBinable = defineBinable({
Expand All @@ -362,6 +353,8 @@ const BoolBinable = defineBinable({
},
});

// internal helper functions

function isConstant(x: boolean | Bool): x is boolean | ConstantBool {
if (typeof x === 'boolean') {
return true;
Expand All @@ -370,17 +363,18 @@ function isConstant(x: boolean | Bool): x is boolean | ConstantBool {
return x.isConstant();
}

function isBool(x: unknown) {
return x instanceof Bool;
}

function toBoolean(x: boolean | Bool): boolean {
if (typeof x === 'boolean') {
return x;
}
return x.toBoolean();
}

function toFieldVar(x: boolean | Bool): BoolVar {
if (x instanceof Bool) return x.value;
return FieldVar.constant(B(x));
}

// TODO: This is duplicated
function withMessage(error: unknown, message?: string) {
if (message === undefined || !(error instanceof Error)) return error;
Expand Down
99 changes: 47 additions & 52 deletions src/lib/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export {
ConstantField,
VarField,
VarFieldVar,
isField,
withMessage,
readVarMessage,
toConstantField,
Expand Down Expand Up @@ -156,7 +155,7 @@ class Field {
* Coerce anything "field-like" (bigint, number, string, and {@link Field}) to a Field.
*/
constructor(x: bigint | number | string | Field | FieldVar | FieldConst) {
if (Field.#isField(x)) {
if (x instanceof Field) {
this.value = x.value;
return;
}
Expand All @@ -176,21 +175,9 @@ class Field {
}

// helpers
static #isField(
x: bigint | number | string | Field | FieldVar | FieldConst
): x is Field {
return x instanceof Field;
}
static #toConst(x: bigint | number | string | ConstantField): FieldConst {
if (Field.#isField(x)) return x.value[1];
return FieldConst.fromBigint(Fp(x));
}
static #toVar(x: bigint | number | string | Field): FieldVar {
if (Field.#isField(x)) return x.value;
return FieldVar.constant(Fp(x));
}

static from(x: bigint | number | string | Field): Field {
if (Field.#isField(x)) return x;
if (x instanceof Field) return x;
return new Field(x);
}

Expand All @@ -216,10 +203,6 @@ class Field {
return this.value[0] === FieldType.Constant;
}

#toConstant(name: string): ConstantField {
return toConstantField(this, name, 'x', 'field element');
}

/**
* Create a {@link Field} element equivalent to this {@link Field} element's value,
* but is a constant.
Expand All @@ -234,7 +217,7 @@ class Field {
* @return A constant {@link Field} element equivalent to this {@link Field} element.
*/
toConstant(): ConstantField {
return this.#toConstant('toConstant');
return toConstant(this, 'toConstant');
}

/**
Expand All @@ -251,7 +234,7 @@ class Field {
* @return A bigint equivalent to the bigint representation of the Field.
*/
toBigInt() {
let x = this.#toConstant('toBigInt');
let x = toConstant(this, 'toBigInt');
return FieldConst.toBigint(x.value[1]);
}

Expand All @@ -269,7 +252,7 @@ class Field {
* @return A string equivalent to the string representation of the Field.
*/
toString() {
return this.#toConstant('toString').toBigInt().toString();
return toConstant(this, 'toString').toBigInt().toString();
}

/**
Expand All @@ -290,7 +273,7 @@ class Field {
}
return;
}
Snarky.field.assertEqual(this.value, Field.#toVar(y));
Snarky.field.assertEqual(this.value, toFieldVar(y));
} catch (err) {
throw withMessage(err, message);
}
Expand Down Expand Up @@ -329,7 +312,7 @@ class Field {
return new Field(Fp.add(this.toBigInt(), toFp(y)));
}
// return new AST node Add(x, y)
let z = Snarky.field.add(this.value, Field.#toVar(y));
let z = Snarky.field.add(this.value, toFieldVar(y));
return new Field(z);
}

Expand Down Expand Up @@ -456,7 +439,7 @@ class Field {
}
// if one of the factors is constant, return Scale AST node
if (isConstant(y)) {
let z = Snarky.field.scale(Field.#toConst(y), this.value);
let z = Snarky.field.scale(toFieldConst(y), this.value);
return new Field(z);
}
if (this.isConstant()) {
Expand Down Expand Up @@ -664,24 +647,6 @@ class Field {
return new Field(xMinusY).isZero();
}

// internal base method for all comparisons
#compare(y: FieldVar) {
// TODO: support all bit lengths
let maxLength = Fp.sizeInBits - 2;
asProver(() => {
let actualLength = Math.max(
this.toBigInt().toString(2).length,
new Field(y).toBigInt().toString(2).length
);
if (actualLength > maxLength)
throw Error(
`Provable comparison functions can only be used on Fields of size <= ${maxLength} bits, got ${actualLength} bits.`
);
});
let [, less, lessOrEqual] = Snarky.field.compare(maxLength, this.value, y);
return { less: new Bool(less), lessOrEqual: new Bool(lessOrEqual) };
}

/**
* Check if this {@link Field} is less than another "field-like" value.
* Returns a {@link Bool}, which is a provable type and can be used prove to the validity of this statement.
Expand Down Expand Up @@ -709,7 +674,7 @@ class Field {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBigInt() < toFp(y));
}
return this.#compare(Field.#toVar(y)).less;
return compare(this, toFieldVar(y)).less;
}

/**
Expand Down Expand Up @@ -739,7 +704,7 @@ class Field {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBigInt() <= toFp(y));
}
return this.#compare(Field.#toVar(y)).lessOrEqual;
return compare(this, toFieldVar(y)).lessOrEqual;
}

/**
Expand Down Expand Up @@ -817,7 +782,7 @@ class Field {
}
return;
}
let { less } = this.#compare(Field.#toVar(y));
let { less } = compare(this, toFieldVar(y));
less.assertTrue();
} catch (err) {
throw withMessage(err, message);
Expand Down Expand Up @@ -845,7 +810,7 @@ class Field {
}
return;
}
let { lessOrEqual } = this.#compare(Field.#toVar(y));
let { lessOrEqual } = compare(this, toFieldVar(y));
lessOrEqual.assertTrue();
} catch (err) {
throw withMessage(err, message);
Expand Down Expand Up @@ -1165,7 +1130,7 @@ class Field {
* @return A string equivalent to the JSON representation of the {@link Field}.
*/
toJSON() {
return this.#toConstant('toJSON').toString();
return toConstant(this, 'toJSON').toString();
}

/**
Expand Down Expand Up @@ -1279,9 +1244,7 @@ const FieldBinable = defineBinable({
},
});

function isField(x: unknown): x is Field {
return x instanceof Field;
}
// internal helper functions

function isConstant(
x: bigint | number | string | Field
Expand All @@ -1301,12 +1264,40 @@ function toFp(x: bigint | number | string | Field): Fp {
return (x as Field).toBigInt();
}

function toFieldConst(x: bigint | number | string | ConstantField): FieldConst {
if (x instanceof Field) return x.value[1];
return FieldConst.fromBigint(Fp(x));
}

function toFieldVar(x: bigint | number | string | Field): FieldVar {
if (x instanceof Field) return x.value;
return FieldVar.constant(Fp(x));
}

function withMessage(error: unknown, message?: string) {
if (message === undefined || !(error instanceof Error)) return error;
error.message = `${message}\n${error.message}`;
return error;
}

// internal base method for all comparisons
function compare(x: Field, y: FieldVar) {
// TODO: support all bit lengths
let maxLength = Fp.sizeInBits - 2;
asProver(() => {
let actualLength = Math.max(
x.toBigInt().toString(2).length,
new Field(y).toBigInt().toString(2).length
);
if (actualLength > maxLength)
throw Error(
`Provable comparison functions can only be used on Fields of size <= ${maxLength} bits, got ${actualLength} bits.`
);
});
let [, less, lessOrEqual] = Snarky.field.compare(maxLength, x.value, y);
return { less: new Bool(less), lessOrEqual: new Bool(lessOrEqual) };
}

function checkBitLength(
name: string,
length: number,
Expand All @@ -1320,6 +1311,10 @@ function checkBitLength(
throw Error(`${name}: bit length must be non-negative, got ${length}`);
}

function toConstant(x: Field, name: string): ConstantField {
return toConstantField(x, name, 'x', 'field element');
}

function toConstantField(
x: Field,
methodName: string,
Expand Down
Loading