diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 818c574..e15928e 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,7 +16,7 @@ module.exports = { ecmaVersion: 'latest', }, plugins: ['@typescript-eslint', 'o1js'], - ignorePatterns: ['README.md'], + ignorePatterns: ['*.md'], rules: { 'no-constant-condition': 'off', 'prefer-const': 'off', diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6490c3b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## v0.5 + +- Making PackedString class 31 characters instead of 15 + - This has implications in any source code using the default packed string implementation + - Unpacked character arrays will now be 31 characters long, so code expecting a 15-character array may break! diff --git a/package.json b/package.json index 57383c5..2bde520 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "o1js-pack", - "version": "0.4.4", + "version": "0.5.0", "description": "", "author": "45930", "license": "Apache-2.0", diff --git a/src/lib/PackingPlant.ts b/src/lib/PackingPlant.ts index 7ad518d..fb56b6b 100644 --- a/src/lib/PackingPlant.ts +++ b/src/lib/PackingPlant.ts @@ -92,6 +92,14 @@ export function PackingPlant = InferProvable>( return unpacked; } + // NOTE: adding to fields here breaks the proof generation. Probably not overriding it correctly + /** + * @returns array of single Field element which constitute the packed object + */ + toFields(): Array { + return [this.packed]; + } + assertEquals(other: Packed_) { this.packed.assertEquals(other.packed); } @@ -216,6 +224,14 @@ export function MultiPackingPlant< return uints_; } + // NOTE: adding to fields here breaks the proof generation. Probably not overriding it correctly + /** + * @returns array of Field elements which constitute the multi-packed object + */ + toFields(): Array { + return this.packed; + } + assertEquals(other: Packed_) { for (let x = 0; x < n; x++) { this.packed[x].assertEquals(other.packed[x]); diff --git a/src/lib/packed-types/PackedString.test.ts b/src/lib/packed-types/PackedString.test.ts index ced12c7..1417ca5 100644 --- a/src/lib/packed-types/PackedString.test.ts +++ b/src/lib/packed-types/PackedString.test.ts @@ -4,7 +4,7 @@ import { PackedStringFactory, MultiPackedStringFactory } from './PackedString'; describe('PackedString', () => { describe('Outside of the Circuit', () => { const vitalik_dot_eth = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; - class EthAddressString extends MultiPackedStringFactory(3) {} + class EthAddressString extends MultiPackedStringFactory(2) {} let characters: Array = []; beforeEach(() => { @@ -30,17 +30,48 @@ describe('PackedString', () => { ); const packed = EthAddressString.pack(unpacked); - expect(packed.length).toBe(Math.ceil(vitalik_dot_eth.length / 15)); + expect(packed.length).toBe(Math.ceil(vitalik_dot_eth.length / 31)); expect(unpacked.length).toBe(EthAddressString.l); - expect(unpacked.length).toBe(45); + expect(unpacked.length).toBe(62); expect(unpacked.slice(0, characters.length).toString()).toBe( characters.toString() ); }); + + it('#toFields', () => { + class Single extends PackedStringFactory() {} + class Double extends MultiPackedStringFactory(2) {} + const abbaPacked = '1633837665'; + + expect(Single.fromString('abba').toFields().toString()).toBe( + [Single.fromString('abba').packed].toString() + ); + expect(Single.fromString('abba').toFields().toString()).toBe(abbaPacked); + expect( + Single.unpack(Field(abbaPacked)).slice(0, 4).join('').toString() + ).toBe('abba'); + + const abbaAlot = 'abba'.repeat(10); + const abbaAlotPacked1 = + '173830008860859097861870638220020642754739197069143560335286125952244015713'; + const abbaAlotPacked2 = '1796423510984151425377'; + expect(Double.fromString(abbaAlot).toFields().toString()).toBe( + [Double.fromString(abbaAlot).packed].toString() + ); + expect(Double.fromString(abbaAlot).toFields().toString()).toBe( + abbaAlotPacked1 + ',' + abbaAlotPacked2 + ); + expect( + Double.unpack([Field(abbaAlotPacked1), Field(abbaAlotPacked2)]) + .slice(0, 40) + .join('') + .toString() + ).toBe(abbaAlot); + }); }); describe('Provable Properties', () => { it('#sizeInFields', () => { - class one extends PackedStringFactory(15) {} + class one extends PackedStringFactory(31) {} class two extends MultiPackedStringFactory(2) {} class three extends MultiPackedStringFactory(3) {} @@ -67,7 +98,7 @@ describe('PackedString', () => { }).toThrow(); }); it('Exceeds maximum size string', () => { - const tooLong = 'too long!'.repeat(20); + const tooLong = 'too long!'.repeat(50); class MaxString extends MultiPackedStringFactory(8) {} expect(() => { MaxString.fromString(tooLong); diff --git a/src/lib/packed-types/PackedString.ts b/src/lib/packed-types/PackedString.ts index 85b8c19..8fdbab0 100644 --- a/src/lib/packed-types/PackedString.ts +++ b/src/lib/packed-types/PackedString.ts @@ -1,9 +1,9 @@ import { Field, Provable, Character, Poseidon } from 'o1js'; import { PackingPlant, MultiPackingPlant } from '../PackingPlant.js'; -const SIZE_IN_BITS = 16n; -const L = 15; // Default to one-field worth of characters -const CHARS_PER_FIELD = 15; +const SIZE_IN_BITS = 8n; +const L = 31; // Default to one-field worth of characters +const CHARS_PER_FIELD = 31; export function PackedStringFactory(l: number = L) { class PackedString_ extends PackingPlant(Character, l, SIZE_IN_BITS) { diff --git a/tests/provable/example_packed_string_circuit.ts b/tests/provable/example_packed_string_circuit.ts index 630239c..b6e63dd 100644 --- a/tests/provable/example_packed_string_circuit.ts +++ b/tests/provable/example_packed_string_circuit.ts @@ -6,9 +6,9 @@ import { Provable, Field, } from 'o1js'; -import { MultiPackedStringFactory } from '../../src/lib/packed-types/PackedString'; +import { PackedStringFactory } from '../../src/lib/packed-types/PackedString'; -export class TextInput extends MultiPackedStringFactory(3) {} +export class TextInput extends PackedStringFactory() {} export const TextInputProgram = ZkProgram({ name: 'TextInputProgram', @@ -23,7 +23,7 @@ export const TextInputProgram = ZkProgram({ }, }, changeFirstLetter: { - privateInputs: [SelfProof, Provable.Array(Character, 45), Character], + privateInputs: [SelfProof, Provable.Array(Character, 31), Character], method( newState: TextInput, oldProof: SelfProof,