UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

278 lines (254 loc) 6.85 kB
import { ZkProgram } from '../../proof-system/zkprogram.js'; import { equivalentProvable as equivalent, equivalentAsync, field, fieldWithRng, } from '../../testing/equivalent.js'; import { Fp, mod } from '../../../bindings/crypto/finite-field.js'; import { Field } from '../wrapped.js'; import { Gadgets } from '../gadgets/gadgets.js'; import { Random } from '../../testing/property.js'; import { constraintSystem, contains, equals, ifNotAllConstant, repeat, and, withoutGenerics, } from '../../testing/constraint-system.js'; import { GateType } from '../../../snarky.js'; const maybeField = { ...field, rng: Random.map(Random.oneOf(Random.field, Random.field.invalid), (x) => mod(x, Field.ORDER) ), }; let uint = (length: number) => fieldWithRng(Random.biguint(length)); let Bitwise = ZkProgram({ name: 'bitwise', publicOutput: Field, methods: { xor: { privateInputs: [Field, Field], async method(a: Field, b: Field) { return Gadgets.xor(a, b, 254); }, }, notUnchecked: { privateInputs: [Field], async method(a: Field) { return Gadgets.not(a, 254, false); }, }, notChecked: { privateInputs: [Field], async method(a: Field) { return Gadgets.not(a, 254, true); }, }, and: { privateInputs: [Field, Field], async method(a: Field, b: Field) { return Gadgets.and(a, b, 64); }, }, rot32: { privateInputs: [Field], async method(a: Field) { return Gadgets.rotate32(a, 12, 'left'); }, }, rot64: { privateInputs: [Field], async method(a: Field) { return Gadgets.rotate64(a, 12, 'left'); }, }, leftShift64: { privateInputs: [Field], async method(a: Field) { return Gadgets.leftShift64(a, 12); }, }, leftShift32: { privateInputs: [Field], async method(a: Field) { Gadgets.rangeCheck32(a); return Gadgets.leftShift32(a, 12); }, }, rightShift64: { privateInputs: [Field], async method(a: Field) { return Gadgets.rightShift64(a, 12); }, }, }, }); await Bitwise.compile(); [2, 4, 8, 16, 32, 64, 128].forEach((length) => { equivalent({ from: [uint(length), uint(length)], to: field })( (x, y) => x ^ y, (x, y) => Gadgets.xor(x, y, length) ); equivalent({ from: [uint(length), uint(length)], to: field })( (x, y) => x & y, (x, y) => Gadgets.and(x, y, length) ); // NOT unchecked equivalent({ from: [uint(length)], to: field })( (x) => Fp.not(x, length), (x) => Gadgets.not(x, length, false) ); // NOT checked equivalent({ from: [uint(length)], to: field })( (x) => Fp.not(x, length), (x) => Gadgets.not(x, length, true) ); }); [2, 4, 8, 16, 32, 64].forEach((length) => { equivalent({ from: [uint(length)], to: field })( (x) => Fp.rot(x, 12n, 'left'), (x) => Gadgets.rotate64(x, 12, 'left') ); equivalent({ from: [uint(length)], to: field })( (x) => Fp.leftShift(x, 12), (x) => Gadgets.leftShift64(x, 12) ); equivalent({ from: [uint(length)], to: field })( (x) => Fp.rightShift(x, 12), (x) => Gadgets.rightShift64(x, 12) ); }); [2, 4, 8, 16, 32].forEach((length) => { equivalent({ from: [uint(length)], to: field })( (x) => Fp.rot(x, 12n, 'left', 32n), (x) => Gadgets.rotate32(x, 12, 'left') ); equivalent({ from: [uint(length)], to: field })( (x) => Fp.leftShift(x, 12, 32), (x) => Gadgets.leftShift32(x, 12) ); }); const runs = 2; await equivalentAsync({ from: [uint(64), uint(64)], to: field }, { runs })( (x, y) => { return x ^ y; }, async (x, y) => { let proof = await Bitwise.xor(x, y); return proof.publicOutput; } ); await equivalentAsync({ from: [maybeField], to: field }, { runs })( (x) => { return Fp.not(x, 254); }, async (x) => { let proof = await Bitwise.notUnchecked(x); return proof.publicOutput; } ); await equivalentAsync({ from: [maybeField], to: field }, { runs })( (x) => { if (x > 2n ** 254n) throw Error('Does not fit into 254 bit'); return Fp.not(x, 254); }, async (x) => { let proof = await Bitwise.notChecked(x); return proof.publicOutput; } ); await equivalentAsync({ from: [maybeField, maybeField], to: field }, { runs })( (x, y) => { if (x >= 2n ** 64n || y >= 2n ** 64n) throw Error('Does not fit into 64 bits'); return x & y; }, async (x, y) => { let proof = await Bitwise.and(x, y); return proof.publicOutput; } ); await equivalentAsync({ from: [field], to: field }, { runs })( (x) => { if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); return Fp.rot(x, 12n, 'left'); }, async (x) => { let proof = await Bitwise.rot64(x); return proof.publicOutput; } ); await equivalentAsync({ from: [uint(32)], to: uint(32) }, { runs })( (x) => { return Fp.rot(x, 12n, 'left', 32n); }, async (x) => { let proof = await Bitwise.rot32(x); return proof.publicOutput; } ); await equivalentAsync({ from: [field], to: field }, { runs })( (x) => { if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); return Fp.leftShift(x, 12); }, async (x) => { let proof = await Bitwise.leftShift64(x); return proof.publicOutput; } ); await equivalentAsync({ from: [field], to: field }, { runs })( (x) => { if (x >= 1n << 32n) throw Error('Does not fit into 32 bits'); return Fp.leftShift(x, 12, 32); }, async (x) => { let proof = await Bitwise.leftShift32(x); return proof.publicOutput; } ); await equivalentAsync({ from: [field], to: field }, { runs })( (x) => { if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); return Fp.rightShift(x, 12); }, async (x) => { let proof = await Bitwise.rightShift64(x); return proof.publicOutput; } ); // check that gate chains stay intact function xorChain(bits: number) { return repeat(Math.ceil(bits / 16), 'Xor16').concat('Zero'); } constraintSystem.fromZkProgram( Bitwise, 'xor', ifNotAllConstant(contains(xorChain(254))) ); constraintSystem.fromZkProgram( Bitwise, 'notChecked', ifNotAllConstant(contains(xorChain(254))) ); constraintSystem.fromZkProgram( Bitwise, 'notUnchecked', ifNotAllConstant(contains('Generic')) ); constraintSystem.fromZkProgram( Bitwise, 'and', ifNotAllConstant(contains(xorChain(64))) ); let rotChain: GateType[] = ['Rot64', 'RangeCheck0']; let isJustRotate = ifNotAllConstant( and(contains(rotChain), withoutGenerics(equals(rotChain))) ); constraintSystem.fromZkProgram(Bitwise, 'rot64', isJustRotate); constraintSystem.fromZkProgram(Bitwise, 'leftShift64', isJustRotate); constraintSystem.fromZkProgram(Bitwise, 'rightShift64', isJustRotate);