From ca4bc4565a4b9e178f2c9d312c9efdabf416ea42 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 13 Sep 2023 23:20:54 -0400 Subject: [PATCH] unwinding complexity --- src/lib/PackingPlant.ts | 29 +++------ src/lib/packed-types/PackedBool.test.ts | 26 ++------ src/lib/packed-types/PackedBool.ts | 76 +++++++++++------------ src/lib/packed-types/PackedUInt32.test.ts | 28 +++------ src/lib/packed-types/PackedUInt32.ts | 76 +++++++++++------------ tests/provable/end_to_end.test.ts | 3 +- tests/provable/example_circuit.ts | 29 +++++---- 7 files changed, 111 insertions(+), 156 deletions(-) diff --git a/src/lib/PackingPlant.ts b/src/lib/PackingPlant.ts index e61161b..b20906b 100644 --- a/src/lib/PackingPlant.ts +++ b/src/lib/PackingPlant.ts @@ -25,32 +25,22 @@ export function PackingPlant = InferProvable>( static type = provable({ packed: Field }, {}); static l: number = l; packed: Field; - aux: Array; bitSize: bigint = bitSize; - constructor(packed: Field, aux: Array) { - if (aux.length > l) { - throw new Error( - `Length of aux data is too long, input of size ${aux.length} is larger than max allowed ${l}` - ); - } + constructor(packed: Field) { super({ packed }); - this.aux = aux; } - static toAuxiliary(value?: { packed: Field } | undefined): Array { - throw new Error('Must implement toAuxiliary'); - return []; - } - - static pack(aux: Array): Field { + static pack(unpacked: Array): Field { throw new Error('Must implement pack'); let f = Field(0); return f; } static unpack(f: Field) { - return this.toAuxiliary({ packed: f }); + const unpacked = this.toAuxiliary({ packed: f }); + f.assertEquals(this.pack(unpacked)); + return unpacked; } assertEquals(other: Packed_) { @@ -88,19 +78,14 @@ export function MultiPackingPlant< this.aux = aux; } - static toAuxiliary(value?: { packed: Array } | undefined): Array { - throw new Error('Must implement toAuxiliary'); - return []; - } - static pack(aux: Array): Array { throw new Error('Must implement pack'); let f = [Field(0)]; return f; } - static unpack(fields: Array) { - return this.toAuxiliary({ packed: fields }); + static unpack(fields: Array): Array { + throw new Error('Must implement pack'); } assertEquals(other: Packed_) { diff --git a/src/lib/packed-types/PackedBool.test.ts b/src/lib/packed-types/PackedBool.test.ts index 6a548b3..3f70691 100644 --- a/src/lib/packed-types/PackedBool.test.ts +++ b/src/lib/packed-types/PackedBool.test.ts @@ -1,8 +1,7 @@ import { Bool, Provable } from 'o1js'; -import { PackedBoolFactory } from './PackedBool'; +import { PackedBool } from './PackedBool'; describe('PackedBool', () => { - class PackedBool extends PackedBoolFactory(254) {} const booleans = new Array(127).fill([true, false]).flat(); const bools = booleans.map((x) => Bool(x)); describe('Outside of the circuit', () => { @@ -21,19 +20,10 @@ describe('PackedBool', () => { }); describe('Provable Properties', () => { it('#sizeInFields', () => { - class one extends PackedBoolFactory(1) {} - class two_five_four extends PackedBoolFactory(254) {} - - expect(one.sizeInFields()).toBe(1); - expect(two_five_four.sizeInFields()).toBe(1); + expect(PackedBool.sizeInFields()).toBe(1); }); }); describe('Defensive Cases', () => { - it('throws for input >= 255 bools', () => { - expect(() => PackedBoolFactory(254)).not.toThrow(); - expect(() => PackedBoolFactory(255)).toThrow(); - }); - it('initalizes with more input than allowed', () => { const tooMany = [...booleans].concat(false); @@ -59,10 +49,7 @@ describe('PackedBool', () => { it('Initializes', () => { expect(() => { Provable.runAndCheck(() => { - const packedBool = new PackedBool( - outsidePackedBool.packed, - outsidePackedBool.aux - ); + const packedBool = new PackedBool(outsidePackedBool.packed); PackedBool.check({ packed: packedBool.packed }); }); @@ -71,17 +58,14 @@ describe('PackedBool', () => { it('#assertEquals', () => { expect(() => { Provable.runAndCheck(() => { - const packedBool = new PackedBool( - outsidePackedBool.packed, - outsidePackedBool.aux - ); + const packedBool = new PackedBool(outsidePackedBool.packed); packedBool.assertEquals(outsidePackedBool); }); }).not.toThrow(); expect(() => { Provable.runAndCheck(() => { const fakePacked = outsidePackedBool.packed.add(32); - const packedBool = new PackedBool(fakePacked, outsidePackedBool.aux); + const packedBool = new PackedBool(fakePacked); packedBool.assertEquals(outsidePackedBool); }); }).toThrow(); diff --git a/src/lib/packed-types/PackedBool.ts b/src/lib/packed-types/PackedBool.ts index ad7cdb4..fd50000 100644 --- a/src/lib/packed-types/PackedBool.ts +++ b/src/lib/packed-types/PackedBool.ts @@ -1,52 +1,50 @@ import { Field, Provable, Bool } from 'o1js'; import { PackingPlant } from '../PackingPlant.js'; +const L = 254; // 254 1-byte uints fit in one Field const SIZE_IN_BITS = 1n; -export function PackedBoolFactory(l: number) { - class PackedBool_ extends PackingPlant(Bool, l, SIZE_IN_BITS) { - static toAuxiliary(value?: { packed: Field } | undefined): Array { - const auxiliary = Provable.witness(Provable.Array(Bool, l), () => { - let bools_ = new Array(l); - bools_.fill(0n); - let packedN; - if (value && value.packed) { - packedN = value.packed.toBigInt(); - } else { - throw new Error('No Packed Value Provided'); - } - for (let i = 0; i < l; i++) { - bools_[i] = packedN & ((1n << SIZE_IN_BITS) - 1n); - packedN >>= SIZE_IN_BITS; - } - return bools_.map((x) => Bool.fromJSON(Boolean(x))); - }); - return auxiliary; +export class PackedBool extends PackingPlant(Bool, L, SIZE_IN_BITS) { + static pack(aux: Array): Field { + let f = aux[0].toField(); + const n = Math.min(aux.length, L); + for (let i = 1; i < n; i++) { + const c = Field((2n ** SIZE_IN_BITS) ** BigInt(i)); + f = f.add(aux[i].toField().mul(c)); } + return f; + } - static pack(aux: Array): Field { - let f = aux[0].toField(); - const n = Math.min(aux.length, l); - for (let i = 1; i < n; i++) { - const c = Field((2n ** SIZE_IN_BITS) ** BigInt(i)); - f = f.add(aux[i].toField().mul(c)); + static unpack(f: Field): Bool[] { + const unpacked = Provable.witness(Provable.Array(Bool, L), () => { + let bools_ = new Array(L); + bools_.fill(0n); + let packedN; + if (f) { + packedN = f.toBigInt(); + } else { + throw new Error('No Packed Value Provided'); } - return f; - } + for (let i = 0; i < L; i++) { + bools_[i] = packedN & ((1n << SIZE_IN_BITS) - 1n); + packedN >>= SIZE_IN_BITS; + } + return bools_.map((x) => Bool.fromJSON(Boolean(x))); + }); + return unpacked; + } - static fromAuxiliary(aux: Array): PackedBool_ { - const packed = PackedBool_.pack(aux); - return new PackedBool_(packed, aux); - } + static fromBools(bools: Array): PackedBool { + const packed = PackedBool.pack(bools); + return new PackedBool(packed); + } - static fromBooleans(bigints: Array): PackedBool_ { - const uint32s = bigints.map((x) => Bool(x)); - return this.fromAuxiliary(uint32s); - } + static fromBooleans(booleans: Array): PackedBool { + const bools = booleans.map((x) => Bool(x)); + return PackedBool.fromBools(bools); + } - toBooleans(): Array { - return PackedBool_.unpack(this.packed).map((x) => x.toBoolean()); - } + toBooleans(): Array { + return PackedBool.unpack(this.packed).map((x) => x.toBoolean()); } - return PackedBool_; } diff --git a/src/lib/packed-types/PackedUInt32.test.ts b/src/lib/packed-types/PackedUInt32.test.ts index 54459f9..db68ca8 100644 --- a/src/lib/packed-types/PackedUInt32.test.ts +++ b/src/lib/packed-types/PackedUInt32.test.ts @@ -1,8 +1,7 @@ import { Provable, UInt32 } from 'o1js'; -import { PackedUInt32Factory } from './PackedUInt32'; +import { PackedUInt32 } from './PackedUInt32'; describe('PackedUInt32', () => { - class PackedUInt32 extends PackedUInt32Factory(7) {} describe('Outside of the circuit', () => { const bigints = [10n, 2n ** 32n - 1n, 0n, 10n, 2n ** 32n - 100n, 42n, 0n]; const uints = bigints.map((x) => UInt32.from(x)); @@ -22,17 +21,13 @@ describe('PackedUInt32', () => { }); describe('Provable Properties', () => { it('#sizeInFields', () => { - class one extends PackedUInt32Factory(1) {} - class seven extends PackedUInt32Factory(7) {} - - expect(one.sizeInFields()).toBe(1); - expect(seven.sizeInFields()).toBe(1); + expect(PackedUInt32.sizeInFields()).toBe(1); }); }); describe('Defensive Cases', () => { it('throws for input >= 8 uints', () => { - expect(() => PackedUInt32Factory(7)).not.toThrow(); - expect(() => PackedUInt32Factory(8)).toThrow(); + expect(() => PackedUInt32).not.toThrow(); + expect(() => PackedUInt32).toThrow(); }); it('initalizes with more input than allowed', () => { @@ -69,10 +64,7 @@ describe('PackedUInt32', () => { it('Initializes', () => { expect(() => { Provable.runAndCheck(() => { - const packedUInt32 = new PackedUInt32( - outsidePackedUInt.packed, - outsidePackedUInt.aux - ); + const packedUInt32 = new PackedUInt32(outsidePackedUInt.packed); PackedUInt32.check({ packed: packedUInt32.packed }); }); @@ -81,20 +73,14 @@ describe('PackedUInt32', () => { it('#assertEquals', () => { expect(() => { Provable.runAndCheck(() => { - const packedUInt32 = new PackedUInt32( - outsidePackedUInt.packed, - outsidePackedUInt.aux - ); + const packedUInt32 = new PackedUInt32(outsidePackedUInt.packed); packedUInt32.assertEquals(outsidePackedUInt); }); }).not.toThrow(); expect(() => { Provable.runAndCheck(() => { const fakePacked = outsidePackedUInt.packed.add(32); - const packedUInt32 = new PackedUInt32( - fakePacked, - outsidePackedUInt.aux - ); + const packedUInt32 = new PackedUInt32(fakePacked); packedUInt32.assertEquals(outsidePackedUInt); }); }).toThrow(); diff --git a/src/lib/packed-types/PackedUInt32.ts b/src/lib/packed-types/PackedUInt32.ts index 6837a10..a5b1e72 100644 --- a/src/lib/packed-types/PackedUInt32.ts +++ b/src/lib/packed-types/PackedUInt32.ts @@ -1,52 +1,50 @@ import { Field, Provable, UInt32 } from 'o1js'; import { PackingPlant } from '../PackingPlant.js'; +const L = 7; // 7 32-byte uints fit in one Field const SIZE_IN_BITS = 32n; -export function PackedUInt32Factory(l: number) { - class PackedUInt32_ extends PackingPlant(UInt32, l, SIZE_IN_BITS) { - static toAuxiliary(value?: { packed: Field } | undefined): Array { - const auxiliary = Provable.witness(Provable.Array(UInt32, l), () => { - let uints_ = new Array(l); - uints_.fill(0n); - let packedN; - if (value && value.packed) { - packedN = value.packed.toBigInt(); - } else { - throw new Error('No Packed Value Provided'); - } - for (let i = 0; i < l; i++) { - uints_[i] = packedN & ((1n << SIZE_IN_BITS) - 1n); - packedN >>= SIZE_IN_BITS; - } - return uints_.map((x) => UInt32.from(x)); - }); - return auxiliary; +export class PackedUInt32 extends PackingPlant(UInt32, L, SIZE_IN_BITS) { + static pack(unpacked: Array): Field { + let f = unpacked[0].value; + const n = Math.min(unpacked.length, L); + for (let i = 1; i < n; i++) { + const c = Field((2n ** SIZE_IN_BITS) ** BigInt(i)); + f = f.add(unpacked[i].value.mul(c)); } + return f; + } - static pack(aux: Array): Field { - let f = aux[0].value; - const n = Math.min(aux.length, l); - for (let i = 1; i < n; i++) { - const c = Field((2n ** SIZE_IN_BITS) ** BigInt(i)); - f = f.add(aux[i].value.mul(c)); + static unpack(f: Field): UInt32[] { + const auxiliary = Provable.witness(Provable.Array(UInt32, L), () => { + let uints_ = new Array(L); + uints_.fill(0n); + let packedN; + if (f) { + packedN = f.toBigInt(); + } else { + throw new Error('No Packed Value Provided'); } - return f; - } + for (let i = 0; i < L; i++) { + uints_[i] = packedN & ((1n << SIZE_IN_BITS) - 1n); + packedN >>= SIZE_IN_BITS; + } + return uints_.map((x) => UInt32.from(x)); + }); + return auxiliary; + } - static fromAuxiliary(aux: Array): PackedUInt32_ { - const packed = PackedUInt32_.pack(aux); - return new PackedUInt32_(packed, aux); - } + static fromUInt32s(uint32s: Array): PackedUInt32 { + const packed = PackedUInt32.pack(uint32s); + return new PackedUInt32(packed); + } - static fromBigInts(bigints: Array): PackedUInt32_ { - const uint32s = bigints.map((x) => UInt32.from(x)); - return this.fromAuxiliary(uint32s); - } + static fromBigInts(bigints: Array): PackedUInt32 { + const uint32s = bigints.map((x) => UInt32.from(x)); + return PackedUInt32.fromUInt32s(uint32s); + } - toBigInts(): Array { - return PackedUInt32_.unpack(this.packed).map((x) => x.toBigint()); - } + toBigInts(): Array { + return PackedUInt32.unpack(this.packed).map((x) => x.toBigint()); } - return PackedUInt32_; } diff --git a/tests/provable/end_to_end.test.ts b/tests/provable/end_to_end.test.ts index b6cd25c..9308768 100644 --- a/tests/provable/end_to_end.test.ts +++ b/tests/provable/end_to_end.test.ts @@ -19,8 +19,9 @@ describe('End to End Votes Test', () => { }); it('Increments the 0th index', async () => { + initProof.verify(); const proofAux = [1n, 0n]; - const proofVotes = Votes.fromAuxiliary(Votes.fromBigInts(proofAux).aux); + const proofVotes = Votes.fromBigInts(proofAux); const proof = await VotesProgram.incrementIndex0(proofVotes, initProof); proof.verify(); proofVotes.packed.assertEquals(proof.publicInput.packed); diff --git a/tests/provable/example_circuit.ts b/tests/provable/example_circuit.ts index 592200e..a3c0e6d 100644 --- a/tests/provable/example_circuit.ts +++ b/tests/provable/example_circuit.ts @@ -1,7 +1,7 @@ import { Experimental, SelfProof } from 'o1js'; -import { PackedUInt32Factory } from '../../src/lib/packed-types/PackedUInt32'; +import { PackedUInt32 } from '../../src/lib/packed-types/PackedUInt32'; -export class Votes extends PackedUInt32Factory(2) {} +export class Votes extends PackedUInt32 {} export const VotesProgram = Experimental.ZkProgram({ publicInput: Votes, @@ -10,7 +10,7 @@ export const VotesProgram = Experimental.ZkProgram({ init: { privateInputs: [], method(state: Votes) { - const initState = Votes.fromAuxiliary(Votes.fromBigInts([0n, 0n]).aux); + const initState = Votes.fromBigInts([0n, 0n]); state.assertEquals(initState); }, }, @@ -19,19 +19,22 @@ export const VotesProgram = Experimental.ZkProgram({ method(newState: Votes, oldProof: SelfProof) { oldProof.verify(); const expectedAux = Votes.unpack(oldProof.publicInput.packed); + oldProof.publicInput.packed.assertEquals( + Votes.fromUInt32s(expectedAux).packed + ); expectedAux[0] = expectedAux[0].add(1); - newState.packed.assertEquals(Votes.fromAuxiliary(expectedAux).packed); - }, - }, - incrementIndex1: { - privateInputs: [SelfProof], - method(newState: Votes, oldProof: SelfProof) { - oldProof.verify(); - const expectedAux = Votes.unpack(oldProof.publicInput.packed); - expectedAux[1] = expectedAux[1].add(1); - newState.assertEquals(Votes.fromAuxiliary(expectedAux)); + newState.packed.assertEquals(Votes.fromUInt32s(expectedAux).packed); }, }, + // incrementIndex1: { + // privateInputs: [SelfProof], + // method(newState: Votes, oldProof: SelfProof) { + // oldProof.verify(); + // const expectedAux = Votes.unpack(oldProof.publicInput.packed); + // expectedAux[1] = expectedAux[1].add(1); + // newState.packed.assertEquals(Votes.fromAuxiliary(expectedAux).packed); + // }, + // }, }, });