o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
146 lines • 6.77 kB
JavaScript
/**
* Basic gadgets that only use generic gates, and are compatible with (create the same constraints as)
* `plonk_constraint_system.ml` / R1CS_constraint_system.
*/
import { Fp } from '../../../bindings/crypto/finite-field.js';
import { FieldVar } from '../core/fieldvar.js';
import { assert } from './common.js';
import { Gates } from '../gates.js';
import { ScaledVar, emptyCell, reduceToScaledVar } from './basic.js';
import { Snarky } from '../../../bindings.js';
export { assertMulCompatible as assertMul, assertSquareCompatible as assertSquare, assertBooleanCompatible as assertBoolean, assertEqualCompatible as assertEqual, };
let { isVar, getVar, isConst, getConst } = ScaledVar;
/**
* Assert multiplication constraint, `x * y === z`
*/
function assertMulCompatible(x, y, z) {
// this faithfully implements snarky's `assert_r1cs`,
// see `R1CS_constraint_system.add_constraint` -> `Snarky_backendless.Constraint.R1CS`
let xv = reduceToScaledVar(x);
let yv = reduceToScaledVar(y);
let zv = reduceToScaledVar(z);
// three variables
if (isVar(xv) && isVar(yv) && isVar(zv)) {
let [[sx, x], [sy, y], [sz, z]] = [getVar(xv), getVar(yv), getVar(zv)];
// -sx sy * x y + sz z = 0
return Gates.generic({ left: 0n, right: 0n, out: sz, mul: -sx * sy, const: 0n }, { left: x, right: y, out: z });
}
// two variables, one constant
if (isVar(xv) && isVar(yv) && isConst(zv)) {
let [[sx, x], [sy, y], sz] = [getVar(xv), getVar(yv), getConst(zv)];
// sx sy * x y - sz = 0
return Gates.generic({ left: 0n, right: 0n, out: 0n, mul: sx * sy, const: -sz }, { left: x, right: y, out: emptyCell() });
}
if (isVar(xv) && isConst(yv) && isVar(zv)) {
let [[sx, x], sy, [sz, z]] = [getVar(xv), getConst(yv), getVar(zv)];
// sx sy * x - sz z = 0
return Gates.generic({ left: sx * sy, right: 0n, out: -sz, mul: 0n, const: 0n }, { left: x, right: emptyCell(), out: z });
}
if (isConst(xv) && isVar(yv) && isVar(zv)) {
let [sx, [sy, y], [sz, z]] = [getConst(xv), getVar(yv), getVar(zv)];
// sx sy * y - sz z = 0
return Gates.generic({ left: 0n, right: sx * sy, out: -sz, mul: 0n, const: 0n }, { left: emptyCell(), right: y, out: z });
}
// two constants, one variable
if (isVar(xv) && isConst(yv) && isConst(zv)) {
let [[sx, x], sy, sz] = [getVar(xv), getConst(yv), getConst(zv)];
// sx sy * x - sz = 0
return Gates.generic({ left: sx * sy, right: 0n, out: 0n, mul: 0n, const: -sz }, { left: x, right: emptyCell(), out: emptyCell() });
}
if (isConst(xv) && isVar(yv) && isConst(zv)) {
let [sx, [sy, y], sz] = [getConst(xv), getVar(yv), getConst(zv)];
// sx sy * y - sz = 0
return Gates.generic({ left: 0n, right: sx * sy, out: 0n, mul: 0n, const: -sz }, { left: emptyCell(), right: y, out: emptyCell() });
}
if (isConst(xv) && isConst(yv) && isVar(zv)) {
let [sx, sy, [sz, z]] = [getConst(xv), getConst(yv), getVar(zv)];
// sz z - sx sy = 0
return Gates.generic({ left: 0n, right: 0n, out: sz, mul: 0n, const: -sx * sy }, { left: emptyCell(), right: emptyCell(), out: z });
}
// three constants
if (isConst(xv) && isConst(yv) && isConst(zv)) {
let [sx, sy, sz] = [getConst(xv), getConst(yv), getConst(zv)];
assert(Fp.equal(Fp.mul(sx, sy), sz), `assertMul(): ${sx} * ${sy} !== ${sz}`);
return;
}
// sadly TS doesn't know that this was exhaustive
assert(false, `assertMul(): unreachable`);
}
/**
* Assert square, `x^2 === z`
*/
function assertSquareCompatible(x, z) {
let xv = reduceToScaledVar(x);
let zv = reduceToScaledVar(z);
if (isVar(xv) && isVar(zv)) {
let [[sx, x], [sz, z]] = [getVar(xv), getVar(zv)];
// -sz * z + (sx)^2 * x*x = 0
return Gates.generic({ left: 0n, right: 0n, out: -sz, mul: sx ** 2n, const: 0n }, { left: x, right: x, out: z });
}
if (isVar(xv) && isConst(zv)) {
let [[sx, x], sz] = [getVar(xv), getConst(zv)];
// (sx)^2 * x*x - sz = 0
return Gates.generic({ left: 0n, right: 0n, out: 0n, mul: sx ** 2n, const: -sz }, { left: x, right: x, out: emptyCell() });
}
if (isConst(xv) && isVar(zv)) {
let [sx, [sz, z]] = [getConst(xv), getVar(zv)];
// sz * z - (sx)^2 = 0
return Gates.generic({ left: 0n, right: 0n, out: sz, mul: 0n, const: -(sx ** 2n) }, { left: emptyCell(), right: emptyCell(), out: z });
}
if (isConst(xv) && isConst(zv)) {
let [sx, sz] = [getConst(xv), getConst(zv)];
assert(Fp.equal(Fp.square(sx), sz), `assertSquare(): ${sx}^2 !== ${sz}`);
return;
}
// sadly TS doesn't know that this was exhaustive
assert(false, `assertSquare(): unreachable`);
}
/**
* Assert that x is either 0 or 1, `x^2 === x`
*/
function assertBooleanCompatible(x) {
let xv = reduceToScaledVar(x);
if (isVar(xv)) {
let [s, x] = getVar(xv);
// -s*x + s^2 * x^2 = 0
return Gates.generic({ left: -s, right: 0n, out: 0n, mul: s ** 2n, const: 0n }, { left: x, right: x, out: emptyCell() });
}
let x0 = getConst(xv);
assert(Fp.equal(Fp.square(x0), x0), `assertBoolean(): ${x} is not 0 or 1`);
}
/**
* Assert equality, `x === y`
*/
function assertEqualCompatible(x, y) {
// TODO not optimal for a case like `x + y === c*z`,
// where this reduces x + y and then is still not able to just use a wire
let yv = reduceToScaledVar(y);
let xv = reduceToScaledVar(x);
if (isVar(xv) && isVar(yv)) {
let [[sx, x], [sy, y]] = [getVar(xv), getVar(yv)];
if (sx === sy) {
// x === y, so use a wire
return Snarky.field.assertEqual(x, y);
}
// sx * x - sy * y = 0
return Gates.generic({ left: sx, right: -sy, out: 0n, mul: 0n, const: 0n }, { left: x, right: y, out: emptyCell() });
}
if (isVar(xv) && isConst(yv)) {
let [[sx, x], sy] = [getVar(xv), getConst(yv)];
// x === sy / sx, call into snarky to use its constants table
return Snarky.field.assertEqual(FieldVar.scale(sx, x), FieldVar.constant(sy));
}
if (isConst(xv) && isVar(yv)) {
let [sx, [sy, y]] = [getConst(xv), getVar(yv)];
// sx / sy === y, call into snarky to use its constants table
return Snarky.field.assertEqual(FieldVar.constant(sx), FieldVar.scale(sy, y));
}
if (isConst(xv) && isConst(yv)) {
let [sx, sy] = [getConst(xv), getConst(yv)];
assert(Fp.equal(sx, sy), `assertEqual(): ${sx} !== ${sy}`);
return;
}
// sadly TS doesn't know that this was exhaustive
assert(false, `assertEqual(): unreachable`);
}
//# sourceMappingURL=compatible.js.map