UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

105 lines (88 loc) 2.9 kB
import { Field } from '../wrapped.js'; import { AlmostForeignField, createForeignField } from '../foreign-field.js'; import { Fq } from '../../../bindings/crypto/finite-field.js'; import { expect } from 'expect'; import { bool, equivalentProvable as equivalent, first, spec, throwError, unit, } from '../../testing/equivalent.js'; import { test, Random } from '../../testing/property.js'; import { l } from '../gadgets/range-check.js'; import { ProvablePure } from '../types/provable-intf.js'; // toy example - F_17 class SmallField extends createForeignField(17n) {} let x = SmallField.from(16); x.assertEquals(-1); // 16 = -1 (mod 17) x.mul(x).assertEquals(1); // 16 * 16 = 15 * 17 + 1 = 1 (mod 17) // invalid example - modulus too large expect(() => createForeignField(1n << 260n)).toThrow( 'modulus exceeds the max supported size' ); // real example - foreign field arithmetic in the Pallas scalar field class ForeignScalar extends createForeignField(Fq.modulus) {} // types ForeignScalar.provable satisfies ProvablePure<ForeignScalar>; // basic constructor / IO { let s0 = 1n + ((1n + (1n << l)) << l); let scalar = new ForeignScalar(s0); expect(scalar.value).toEqual([Field(1), Field(1), Field(1)]); expect(scalar.toBigInt()).toEqual(s0); } test(Random.scalar, (x0, assert) => { let x = new ForeignScalar(x0); assert(x.toBigInt() === x0); assert(x.isConstant()); }); // test equivalence of in-SNARK and out-of-SNARK operations let f = spec({ rng: Random.scalar, there: ForeignScalar.from, back: (x: AlmostForeignField) => x.toBigInt(), provable: ForeignScalar.AlmostReduced.provable, }); let u264 = spec({ rng: Random.bignat(1n << 264n), there: ForeignScalar.from, back: (x: ForeignScalar) => x.toBigInt(), provable: ForeignScalar.Unreduced.provable, }); // arithmetic equivalent({ from: [f, f], to: u264 })(Fq.add, (x, y) => x.add(y)); equivalent({ from: [f, f], to: u264 })(Fq.sub, (x, y) => x.sub(y)); equivalent({ from: [f], to: u264 })(Fq.negate, (x) => x.neg()); equivalent({ from: [f, f], to: u264 })(Fq.mul, (x, y) => x.mul(y)); equivalent({ from: [f], to: f })( (x) => Fq.inverse(x) ?? throwError('division by 0'), (x) => x.inv() ); equivalent({ from: [f, f], to: f })( (x, y) => Fq.div(x, y) ?? throwError('division by 0'), (x, y) => x.div(y) ); // equality with a constant equivalent({ from: [f, first(f)], to: bool })( (x, y) => x === y, (x, y) => x.equals(y) ); equivalent({ from: [f, f], to: unit })( (x, y) => x === y || throwError('not equal'), (x, y) => x.assertEquals(y) ); equivalent({ from: [f, first(u264)], to: unit })( (x, y) => x < y || throwError('not less than'), (x, y) => x.assertLessThan(y) ); // toBits / fromBits equivalent({ from: [f], to: f })( (x) => x, (x) => { let bits = x.toBits(); expect(bits.length).toEqual(255); return ForeignScalar.fromBits(bits); } );