UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

141 lines (125 loc) 3.82 kB
import { mod } from '../../../bindings/crypto/finite-field.js'; import { Field } from '../wrapped.js'; import { ZkProgram } from '../../proof-system/zkprogram.js'; import { Spec, boolean, equivalentAsync, fieldWithRng } from '../../testing/equivalent.js'; import { Random } from '../../testing/property.js'; import { assert } from '../gadgets/common.js'; import { Gadgets } from '../gadgets/gadgets.js'; import { l } from '../gadgets/range-check.js'; import { constraintSystem, contains, equals, ifNotAllConstant, withoutGenerics, } from '../../testing/constraint-system.js'; let uint = (n: number | bigint): Spec<bigint, Field> => { let uint = Random.bignat((1n << BigInt(n)) - 1n); return fieldWithRng(uint); }; let maybeUint = (n: number | bigint): Spec<bigint, Field> => { let uint = Random.bignat((1n << BigInt(n)) - 1n); return fieldWithRng(Random.map(Random.oneOf(uint, uint.invalid), (x) => mod(x, Field.ORDER))); }; // constraint system sanity check constraintSystem( 'range check 64', { from: [Field] }, Gadgets.rangeCheck64, ifNotAllConstant(withoutGenerics(equals(['RangeCheck0']))) ); constraintSystem( 'range check 8', { from: [Field] }, Gadgets.rangeCheck8, ifNotAllConstant(withoutGenerics(equals(['EndoMulScalar', 'EndoMulScalar']))) ); constraintSystem( 'multi-range check', { from: [Field, Field, Field] }, (x, y, z) => Gadgets.multiRangeCheck([x, y, z]), ifNotAllConstant(contains(['RangeCheck0', 'RangeCheck0', 'RangeCheck1', 'Zero'])) ); constraintSystem( 'compact multi-range check', { from: [Field, Field] }, Gadgets.compactMultiRangeCheck, ifNotAllConstant(contains(['RangeCheck0', 'RangeCheck0', 'RangeCheck1', 'Zero'])) ); // TODO: make a ZkFunction or something that doesn't go through Pickles // -------------------------- // RangeCheck64 Gate // -------------------------- let RangeCheck = ZkProgram({ name: 'range-check', methods: { check64: { privateInputs: [Field], async method(x) { Gadgets.rangeCheck64(x); }, }, check8: { privateInputs: [Field], async method(x) { Gadgets.rangeCheck8(x); }, }, checkMulti: { privateInputs: [Field, Field, Field], async method(x, y, z) { Gadgets.multiRangeCheck([x, y, z]); }, }, checkCompact: { privateInputs: [Field, Field], async method(xy, z) { let [x, y] = Gadgets.compactMultiRangeCheck(xy, z); x.add(y.mul(1n << l)).assertEquals(xy); }, }, }, }); await RangeCheck.compile(); // TODO: we use this as a test because there's no way to check custom gates quickly :( const runs = 2; await equivalentAsync({ from: [maybeUint(64)], to: boolean }, { runs })( (x) => { assert(x < 1n << 64n); return true; }, async (x) => { let { proof } = await RangeCheck.check64(x); return await RangeCheck.verify(proof); } ); await equivalentAsync({ from: [maybeUint(8)], to: boolean }, { runs })( (x) => { assert(x < 1n << 8n); return true; }, async (x) => { let { proof } = await RangeCheck.check8(x); return await RangeCheck.verify(proof); } ); await equivalentAsync({ from: [maybeUint(l), uint(l), uint(l)], to: boolean }, { runs })( (x, y, z) => { assert(!(x >> l) && !(y >> l) && !(z >> l), 'multi: not out of range'); return true; }, async (x, y, z) => { let { proof } = await RangeCheck.checkMulti(x, y, z); return await RangeCheck.verify(proof); } ); await equivalentAsync({ from: [maybeUint(2n * l), uint(l)], to: boolean }, { runs })( (xy, z) => { assert(!(xy >> (2n * l)) && !(z >> l), 'compact: not out of range'); return true; }, async (xy, z) => { let { proof } = await RangeCheck.checkCompact(xy, z); return await RangeCheck.verify(proof); } );