Skip to content

Commit

Permalink
Merge pull request #1485 from o1-labs/feature/ts-gadgets
Browse files Browse the repository at this point in the history
TS core gadget cleanup and implementations
  • Loading branch information
mitschabaude committed Mar 17, 2024
2 parents 7dd739b + fdce521 commit 02f2ffb
Show file tree
Hide file tree
Showing 23 changed files with 762 additions and 273 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- This prevents you from accidentally creating a `UInt64` without proving that it fits in 64 bits
- Equivalent changes were made to `UInt32`
- Fixed vulnerability in `Field.to/fromBits()` outlined in [#1023](https://github.com/o1-labs/o1js/issues/1023) by imposing a limit of 254 bits https://github.com/o1-labs/o1js/pull/1461
- Remove `Field.rangeCheckHelper()` which was too low-level and easy to misuse https://github.com/o1-labs/o1js/pull/1485
- Also, rename the misleadingly named `Gadgets.isInRangeN()` to `Gadgets.isDefinitelyInRangeN()`
- Rename `Bool.Unsafe.ofField()` to `Bool.Unsafe.fromField()` https://github.com/o1-labs/o1js/pull/1485
- Replace the namespaced type exports `Gadgets.Field3` and `Gadgets.ForeignField.Sum` with `Field3` and `ForeignFieldSum`
- Unfortunately, the namespace didn't play well with auto-imports in TypeScript

### Added

Expand Down
47 changes: 31 additions & 16 deletions src/lib/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import {
FieldType,
FieldVar,
readVarMessage,
withMessage,
} from './field.js';
import { Bool as B } from '../provable/field-bigint.js';
import { defineBinable } from '../bindings/lib/binable.js';
import { NonNegativeInteger } from '../bindings/crypto/non-negative.js';
import { asProver } from './provable-context.js';
import { existsOne } from './gadgets/common.js';
import { assertMul } from './gadgets/compatible.js';

export { BoolVar, Bool };

Expand Down Expand Up @@ -63,7 +66,9 @@ class Bool {
if (this.isConstant()) {
return new Bool(!this.toBoolean());
}
return new Bool(Snarky.bool.not(this.value));
// 1 - x
let not = new Field(1).sub(this.toField());
return new Bool(not.value);
}

/**
Expand All @@ -75,7 +80,8 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() && toBoolean(y));
}
return new Bool(Snarky.bool.and(this.value, toFieldVar(y)));
// x * y
return new Bool(this.toField().mul(Bool.toField(y)).value);
}

/**
Expand All @@ -87,7 +93,8 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() || toBoolean(y));
}
return new Bool(Snarky.bool.or(this.value, toFieldVar(y)));
// 1 - (1 - x)(1 - y) = x + y - xy
return this.not().and(new Bool(y).not()).not();
}

/**
Expand All @@ -102,7 +109,7 @@ class Bool {
}
return;
}
Snarky.bool.assertEqual(this.value, toFieldVar(y));
this.toField().assertEquals(Bool.toField(y));
} catch (err) {
throw withMessage(err, message);
}
Expand Down Expand Up @@ -144,7 +151,22 @@ class Bool {
if (this.isConstant() && isConstant(y)) {
return new Bool(this.toBoolean() === toBoolean(y));
}
return new Bool(Snarky.bool.equals(this.value, toFieldVar(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();
}

/**
Expand Down Expand Up @@ -321,19 +343,19 @@ class Bool {
static sizeInBytes = 1;

static check(x: Bool): void {
Snarky.field.assertBoolean(x.value);
x.toField().assertBool();
}

static Unsafe = {
/**
* Converts a {@link Field} into a {@link Bool}. This is a **dangerous** operation
* 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 with constants or if you have already constrained the Field element to be 0 or 1.
* Only use this if you have already constrained the Field element to be 0 or 1.
*
* @param x a {@link Field}
*/
ofField(x: Field) {
fromField(x: Field) {
asProver(() => {
let x0 = x.toBigInt();
if (x0 !== 0n && x0 !== 1n)
Expand Down Expand Up @@ -374,10 +396,3 @@ 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;
error.message = `${message}\n${error.message}`;
return error;
}
Loading

0 comments on commit 02f2ffb

Please sign in to comment.