@nori-zk/proof-conversion
Version:
Verifying zkVM proofs inside o1js circuits, to generate Mina compatible proof
144 lines • 5.5 kB
JavaScript
import { Provable, Struct } from 'o1js';
import { FpC, FpU, FpA } from './fp.js';
import { AlmostReducedSum, UnreducedSum, assertMul } from './assert-mul.js';
class Fp2 extends Struct({ c0: FpA.provable, c1: FpA.provable }) {
static zero() {
return new Fp2({ c0: FpC.from(0n), c1: FpC.from(0n) });
}
static one() {
return new Fp2({ c0: FpC.from(1n), c1: FpC.from(0n) });
}
assert_equals(rhs) {
this.c0.assertEquals(rhs.c0);
this.c1.assertEquals(rhs.c1);
}
canonical() {
return { c0: this.c0.assertCanonical(), c1: this.c1.assertCanonical() };
}
static fromUnreduced({ c0, c1 }) {
let [c0A, c0B] = FpA.assertAlmostReduced(c0, c1);
return new Fp2({ c0: c0A, c1: c0B });
}
equals(rhs) {
let a = this.canonical();
let b = rhs.canonical();
return a.c0.equals(b.c0).and(a.c1.equals(b.c1)).toField();
}
neg() {
return new Fp2({ c0: this.c0.neg(), c1: this.c1.neg() });
}
conjugate() {
return new Fp2({ c0: this.c0, c1: this.c1.neg() });
}
add(rhs) {
const c0 = this.c0.add(rhs.c0);
const c1 = this.c1.add(rhs.c1);
return Fp2.fromUnreduced({ c0, c1 });
}
add_fp(rhs) {
const c0 = this.c0.add(rhs);
return new Fp2({ c0: c0.assertAlmostReduced(), c1: this.c1 });
}
sub(rhs) {
const c0 = this.c0.sub(rhs.c0);
const c1 = this.c1.sub(rhs.c1);
return Fp2.fromUnreduced({ c0, c1 });
}
static sum(inputs, operators) {
let c0 = FpA.sum(inputs.map((x) => x.c0), operators);
let c1 = FpA.sum(inputs.map((x) => x.c1), operators);
return Fp2.fromUnreduced({ c0, c1 });
}
mul_by_fp(rhs) {
const c0 = this.c0.mul(rhs);
const c1 = this.c1.mul(rhs);
return Fp2.fromUnreduced({ c0, c1 });
}
mul_by_non_residue() {
return this.mul(new Fp2({ c0: FpC.from(9n), c1: FpC.from(1n) }));
}
// uses the fact that we work over modulus: X^2 + 1
mul(rhs) {
// c0 = a0*b0 - a1*b1
const a0b0 = this.c0.mul(rhs.c0);
const a1b1 = this.c1.mul(rhs.c1);
const c0 = a0b0.sub(a1b1);
// c1 = a0*b1 + a1*b0
// = (a0 + a1)*(b0 + b1) - a0*b0 - a1*b1
// <=>
// (a0 + a1)*(b0 + b1) = c1 + a0*b0 + a1*b1
// strategy: witness c1 and prove the equation above with `assertMul()`
// this saves range checks on a0 + a1, b0 + b1 and c1
let c1 = Provable.witness(FpU.provable, () => {
let [a0, a1] = [this.c0.toBigInt(), this.c1.toBigInt()];
let [b0, b1] = [rhs.c0.toBigInt(), rhs.c1.toBigInt()];
return FpU.from(a0 * b1 + a1 * b0);
});
let sum_a0_a1 = new AlmostReducedSum(this.c0).add(this.c1);
let sum_b0_b1 = new AlmostReducedSum(rhs.c0).add(rhs.c1);
let sum_c1_a0b0_a1b1 = new UnreducedSum(c1).add(a0b0).add(a1b1);
assertMul(sum_a0_a1, sum_b0_b1, sum_c1_a0b0_a1b1);
return Fp2.fromUnreduced({ c0, c1 });
}
square() {
// c0 = a0^2 - a1^2 = (a0 + a1)(a0 - a1)
// c1 = 2*a0*a1 = (a0 + a0)*a1
let [c0, c1] = Provable.witness(Provable.Array(FpU.provable, 2), () => {
let [a0, a1] = [this.c0.toBigInt(), this.c1.toBigInt()];
return [FpU.from(a0 * a0 - a1 * a1), FpU.from(2n * a0 * a1)];
});
let sum_a0_a1 = new AlmostReducedSum(this.c0).add(this.c1);
let diff_a0_a1 = new AlmostReducedSum(this.c0).sub(this.c1);
assertMul(sum_a0_a1, diff_a0_a1, c0);
let sum_a0_a0 = new AlmostReducedSum(this.c0).add(this.c0);
assertMul(sum_a0_a0, this.c1, c1);
return Fp2.fromUnreduced({ c0, c1 });
}
inverse() {
let t0 = this.c0.mul(this.c0);
let t1 = this.c1.mul(this.c1);
// beta = -1
t0 = t0.add(t1);
// TODO this should be doable with 1 assertAlmostReduced + assertMul
let t1A = t0.assertAlmostReduced().inv().assertAlmostReduced();
const c0 = this.c0.mul(t1A);
const c1 = this.c1.mul(t1A).neg();
return Fp2.fromUnreduced({ c0, c1 });
}
static loadFromJson(json) {
const c0 = FpC.provable.fromJSON(json.c0);
const c1 = FpC.provable.fromJSON(json.c1);
return new Fp2({
c0,
c1,
});
}
}
// function main() {
// let c0 = Provable.witness(
// FpC.provable,
// () => FpC.from(21888242871839275222246405745257275088696357297823662689037894645226208581n)
// );
// let c1 = Provable.witness(
// FpC.provable,
// () => FpC.from(21888242871839275246405745257275088696311157297823662689037894645226208581n)
// );
// let fp2 = new Fp2({c0, c1});
// let s = fp2.square();
// let sinv = Provable.witness(Fp2, () => s.inverse());
// sinv.mul(s).assertEquals(Fp2.one());
// }
// (async () => {
// console.time('running Fp2 constant version');
// await main();
// console.timeEnd('running Fp2 constant version');
// console.time('running Fp2 witness generation & checks');
// await Provable.runAndCheck(main);
// console.timeEnd('running Fp2 witness generation & checks');
// console.time('creating Fp2 constraint system');
// let cs = await Provable.constraintSystem(main);
// console.timeEnd('creating Fp2 constraint system');
// console.log(cs.summary());
// })();
export { Fp2 };
//# sourceMappingURL=fp2.js.map