UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

403 lines 11.2 kB
import { Snarky } from '../../bindings.js'; import { Field, readVarMessage, withMessage } from './field.js'; import { FieldVar, FieldConst, FieldType } from './core/fieldvar.js'; import { defineBinable } from '../../bindings/lib/binable.js'; import { existsOne } from './core/exists.js'; import { assertMul } from './gadgets/compatible.js'; import { setBoolConstructor } from './core/field-constructor.js'; export { Bool }; /** * A boolean value. You can use it like this: * * ``` * const x = new Bool(true); * ``` * * You can also combine multiple booleans via [[`not`]], [[`and`]], [[`or`]]. * * Use [[assertEquals]] to enforce the value of a Bool. */ class Bool { constructor(x) { if (x instanceof Bool) { this.value = x.value; return; } if (Array.isArray(x)) { this.value = x; return; } this.value = FieldVar.constant(BigInt(x)); } isConstant() { return this.value[0] === FieldType.Constant; } /** * Converts a {@link Bool} to a {@link Field}. `false` becomes 0 and `true` becomes 1. */ toField() { return Bool.toField(this); } /** * @returns a new {@link Bool} that is the negation of this {@link Bool}. */ not() { if (this.isConstant()) { return new Bool(!this.toBoolean()); } // 1 - x let not = new Field(1).sub(this.toField()); return new Bool(not.value); } /** * @param y A {@link Bool} to AND with this {@link Bool}. * @returns a new {@link Bool} that is set to true only if * this {@link Bool} and `y` are also true. */ and(y) { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() && toBoolean(y)); } // x * y return new Bool(this.toField().mul(Bool.toField(y)).value); } /** * @param y a {@link Bool} to OR with this {@link Bool}. * @returns a new {@link Bool} that is set to true if either * this {@link Bool} or `y` is true. */ or(y) { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() || toBoolean(y)); } // 1 - (1 - x)(1 - y) = x + y - xy return this.not().and(new Bool(y).not()).not(); } /** * Whether this Bool implies another Bool `y`. * * This is the same as `x.not().or(y)`: if `x` is true, then `y` must be true for the implication to be true. * * @example * ```ts * let isZero = x.equals(0); * let lessThan10 = x.lessThan(10); * assert(isZero.implies(lessThan10), 'x = 0 implies x < 10'); * ``` */ implies(y) { return this.not().or(y); } /** * Proves that this {@link Bool} is equal to `y`. * @param y a {@link Bool}. */ assertEquals(y, message) { try { if (this.isConstant() && isConstant(y)) { if (this.toBoolean() !== toBoolean(y)) { throw Error(`Bool.assertEquals(): ${this} != ${y}`); } return; } this.toField().assertEquals(Bool.toField(y)); } catch (err) { throw withMessage(err, message); } } /** * Proves that this {@link Bool} is `true`. */ assertTrue(message) { try { if (this.isConstant() && !this.toBoolean()) { throw Error(`Bool.assertTrue(): ${this} != ${true}`); } this.assertEquals(true); } catch (err) { throw withMessage(err, message); } } /** * Proves that this {@link Bool} is `false`. */ assertFalse(message) { try { if (this.isConstant() && this.toBoolean()) { throw Error(`Bool.assertFalse(): ${this} != ${false}`); } this.assertEquals(false); } catch (err) { throw withMessage(err, message); } } /** * Returns true if this {@link Bool} is equal to `y`. * @param y a {@link Bool}. */ equals(y) { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() === toBoolean(y)); } if (isConstant(y)) { if (toBoolean(y)) return this; else return this.not(); } if (this.isConstant()) { return new Bool(y).equals(this); } // 1 - (x - y)^2 = 2xy - x - y + 1 // match snarky logic: // 2x * y === x + y - z // return 1 - z let z = existsOne(() => BigInt(this.toBoolean() !== toBoolean(y))); let x = this.toField(); let y_ = Bool.toField(y); assertMul(x.add(x), y_, x.add(y_).sub(z)); return new Bool(z.value).not(); } /** * Returns the size of this type. */ sizeInFields() { return 1; } /** * Serializes this {@link Bool} into {@link Field} elements. */ toFields() { return Bool.toFields(this); } /** * Serialize the {@link Bool} to a string, e.g. for printing. * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Field. */ toString() { return this.toBoolean().toString(); } /** * Serialize the {@link Bool} to a JSON string. * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Field. */ toJSON() { return this.toBoolean(); } /** * This converts the {@link Bool} to a JS `boolean`. * This can only be called on non-witness values. */ toBoolean() { if (this.isConstant()) { return FieldConst.equal(this.value[1], FieldConst[1]); } if (!Snarky.run.inProverBlock()) { throw Error(readVarMessage('toBoolean', 'b', 'Bool')); } let value = Snarky.field.readVar(this.value); return FieldConst.equal(value, FieldConst[1]); } static toField(x) { return new Field(toFieldVar(x)); } /** * Boolean negation. */ static not(x) { if (x instanceof Bool) { return x.not(); } return new Bool(!x); } /** * Boolean AND operation. */ static and(x, y) { if (x instanceof Bool) { return x.and(y); } return new Bool(x).and(y); } /** * Boolean OR operation. */ static or(x, y) { if (x instanceof Bool) { return x.or(y); } return new Bool(x).or(y); } /** * Boolean AND operation across a list of booleans, returns `Bool(true)` if all elements in the list are true. */ static allTrue(list) { if (list.length === 0) { return new Bool(true); } else { let b = toBool(list[0]); for (let i = 1; i < list.length; i++) { b = b.and(toBool(list[i])); } return b; } } /** * Boolean OR operation across a list of booleans, returns `Bool(true)` if any element in the list is true. */ static anyTrue(list) { if (list.length === 0) { return new Bool(false); } else { let b = toBool(list[0]); for (let i = 1; i < list.length; i++) { b = b.or(toBool(list[i])); } return b; } } /** * Asserts if both {@link Bool} are equal. */ static assertEqual(x, y) { if (x instanceof Bool) { x.assertEquals(y); return; } new Bool(x).assertEquals(y); } /** * Checks two {@link Bool} for equality. */ static equal(x, y) { if (x instanceof Bool) { return x.equals(y); } return new Bool(x).equals(y); } /** * Static method to serialize a {@link Bool} into an array of {@link Field} elements. */ static toFields(x) { return [Bool.toField(x)]; } /** * Static method to serialize a {@link Bool} into its auxiliary data. */ static toAuxiliary(_) { return []; } /** * Creates a data structure from an array of serialized {@link Field} elements. */ static fromFields(fields) { if (fields.length !== 1) { throw Error(`Bool.fromFields(): expected 1 field, got ${fields.length}`); } return new Bool(fields[0].value); } /** * `Provable<Bool>.toValue()` */ static toValue(x) { return x.toBoolean(); } /** * `Provable<Bool>.fromValue()` */ static fromValue(b) { if (typeof b === 'boolean') return new Bool(b); return b; } /** * Serialize a {@link Bool} to a JSON string. * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Field. */ static toJSON(x) { return x.toBoolean(); } /** * Deserialize a JSON structure into a {@link Bool}. * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Field. */ static fromJSON(b) { return new Bool(b); } /** * Returns the size of this type. */ static sizeInFields() { return 1; } static empty() { return new Bool(false); } static toInput(x) { return { packed: [[x.toField(), 1]] }; } static toBytes(b) { return BoolBinable.toBytes(b); } static fromBytes(bytes) { return BoolBinable.fromBytes(bytes); } static readBytes(bytes, offset) { return BoolBinable.readBytes(bytes, offset); } static check(x) { x.toField().assertBool(); } } Bool.sizeInBytes = 1; Bool.Unsafe = { /** * Converts a {@link Field} into a {@link Bool}. This is an **unsafe** operation * as it assumes that the field element is either 0 or 1 (which might not be true). * * Only use this if you have already constrained the Field element to be 0 or 1. * * @param x a {@link Field} */ fromField(x) { return new Bool(x.value); }, }; setBoolConstructor(Bool); const BoolBinable = defineBinable({ toBytes(b) { return [Number(b.toBoolean())]; }, readBytes(bytes, offset) { return [new Bool(!!bytes[offset]), offset + 1]; }, }); // internal helper functions function isConstant(x) { if (typeof x === 'boolean') { return true; } return x.isConstant(); } function toBoolean(x) { if (typeof x === 'boolean') { return x; } return x.toBoolean(); } function toBool(x) { if (x instanceof Bool) return x; return new Bool(x); } function toFieldVar(x) { if (x instanceof Bool) return x.value; return FieldVar.constant(BigInt(x)); } //# sourceMappingURL=bool.js.map