Skip to content

Commit

Permalink
unwinding complexity
Browse files Browse the repository at this point in the history
  • Loading branch information
45930 committed Sep 14, 2023
1 parent f43a87a commit ca4bc45
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 156 deletions.
29 changes: 7 additions & 22 deletions src/lib/PackingPlant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,22 @@ export function PackingPlant<A, T extends InferProvable<A> = InferProvable<A>>(
static type = provable({ packed: Field }, {});
static l: number = l;
packed: Field;
aux: Array<T>;
bitSize: bigint = bitSize;

constructor(packed: Field, aux: Array<T>) {
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<T> {
throw new Error('Must implement toAuxiliary');
return [];
}

static pack(aux: Array<T>): Field {
static pack(unpacked: Array<T>): 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_) {
Expand Down Expand Up @@ -88,19 +78,14 @@ export function MultiPackingPlant<
this.aux = aux;
}

static toAuxiliary(value?: { packed: Array<Field> } | undefined): Array<T> {
throw new Error('Must implement toAuxiliary');
return [];
}

static pack(aux: Array<T>): Array<Field> {
throw new Error('Must implement pack');
let f = [Field(0)];
return f;
}

static unpack(fields: Array<Field>) {
return this.toAuxiliary({ packed: fields });
static unpack(fields: Array<Field>): Array<T> {
throw new Error('Must implement pack');
}

assertEquals(other: Packed_) {
Expand Down
26 changes: 5 additions & 21 deletions src/lib/packed-types/PackedBool.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -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);

Expand All @@ -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 });
});
Expand All @@ -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();
Expand Down
76 changes: 37 additions & 39 deletions src/lib/packed-types/PackedBool.ts
Original file line number Diff line number Diff line change
@@ -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<Bool> {
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<Bool>): 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<Bool>): 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<Bool>): PackedBool_ {
const packed = PackedBool_.pack(aux);
return new PackedBool_(packed, aux);
}
static fromBools(bools: Array<Bool>): PackedBool {
const packed = PackedBool.pack(bools);
return new PackedBool(packed);
}

static fromBooleans(bigints: Array<boolean>): PackedBool_ {
const uint32s = bigints.map((x) => Bool(x));
return this.fromAuxiliary(uint32s);
}
static fromBooleans(booleans: Array<boolean>): PackedBool {
const bools = booleans.map((x) => Bool(x));
return PackedBool.fromBools(bools);
}

toBooleans(): Array<boolean> {
return PackedBool_.unpack(this.packed).map((x) => x.toBoolean());
}
toBooleans(): Array<boolean> {
return PackedBool.unpack(this.packed).map((x) => x.toBoolean());
}
return PackedBool_;
}
28 changes: 7 additions & 21 deletions src/lib/packed-types/PackedUInt32.test.ts
Original file line number Diff line number Diff line change
@@ -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));
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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 });
});
Expand All @@ -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();
Expand Down
76 changes: 37 additions & 39 deletions src/lib/packed-types/PackedUInt32.ts
Original file line number Diff line number Diff line change
@@ -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<UInt32> {
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<UInt32>): 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<UInt32>): 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<UInt32>): PackedUInt32_ {
const packed = PackedUInt32_.pack(aux);
return new PackedUInt32_(packed, aux);
}
static fromUInt32s(uint32s: Array<UInt32>): PackedUInt32 {
const packed = PackedUInt32.pack(uint32s);
return new PackedUInt32(packed);
}

static fromBigInts(bigints: Array<bigint>): PackedUInt32_ {
const uint32s = bigints.map((x) => UInt32.from(x));
return this.fromAuxiliary(uint32s);
}
static fromBigInts(bigints: Array<bigint>): PackedUInt32 {
const uint32s = bigints.map((x) => UInt32.from(x));
return PackedUInt32.fromUInt32s(uint32s);
}

toBigInts(): Array<bigint> {
return PackedUInt32_.unpack(this.packed).map((x) => x.toBigint());
}
toBigInts(): Array<bigint> {
return PackedUInt32.unpack(this.packed).map((x) => x.toBigint());
}
return PackedUInt32_;
}
3 changes: 2 additions & 1 deletion tests/provable/end_to_end.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit ca4bc45

Please sign in to comment.