From 3be4d7fbcc5af9799919300c48342677bc16806e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 09:09:40 +0100 Subject: [PATCH 01/12] start packed provable type --- src/lib/circuit_value.ts | 8 +++++- src/lib/provable-types/fields.ts | 44 +++++++++++++++++++++++++++++++ src/lib/provable-types/packed.ts | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/lib/provable-types/fields.ts create mode 100644 src/lib/provable-types/packed.ts diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d42f267cdf..6720c343cb 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -555,12 +555,18 @@ and Provable.asProver() blocks, which execute outside the proof. ); } - static provable: Provable> = { + static provable: Provable> & { + toInput: (x: Unconstrained) => { + fields?: Field[]; + packed?: [Field, number][]; + }; + } = { sizeInFields: () => 0, toFields: () => [], toAuxiliary: (t?: any) => [t ?? new Unconstrained(false)], fromFields: (_, [t]) => t, check: () => {}, + toInput: () => ({}), }; } diff --git a/src/lib/provable-types/fields.ts b/src/lib/provable-types/fields.ts new file mode 100644 index 0000000000..385c7d0cae --- /dev/null +++ b/src/lib/provable-types/fields.ts @@ -0,0 +1,44 @@ +import { ProvablePureExtended } from '../circuit_value.js'; +import { Field } from '../field.js'; + +export { modifiedField, fields }; + +const zero = new Field(0); + +// provable for a single field element + +const ProvableField: ProvablePureExtended = { + sizeInFields: () => 1, + toFields: (x) => [x], + toAuxiliary: () => [], + fromFields: ([x]) => x, + check: () => {}, + toInput: (x) => ({ fields: [x] }), + toJSON: Field.toJSON, + fromJSON: Field.fromJSON, + empty: () => zero, +}; + +function modifiedField( + methods: Partial> +): ProvablePureExtended { + return Object.assign({}, ProvableField, methods); +} + +// provable for a fixed-size array of field elements + +let id = (t: T) => t; + +function fields(length: number): ProvablePureExtended { + return { + sizeInFields: () => length, + toFields: id, + toAuxiliary: () => [], + fromFields: id, + check: () => {}, + toInput: (x) => ({ fields: x }), + toJSON: (x) => x.map(Field.toJSON), + fromJSON: (x) => x.map(Field.fromJSON), + empty: () => new Array(length).fill(zero), + }; +} diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts new file mode 100644 index 0000000000..f4ee1d8603 --- /dev/null +++ b/src/lib/provable-types/packed.ts @@ -0,0 +1,45 @@ +import { + HashInput, + InferProvable, + ProvableExtended, + Unconstrained, + provable, +} from '../circuit_value.js'; +import { Field } from '../field.js'; +import { Provable } from '../provable.js'; +import { fields } from './fields.js'; + +export { provablePacked, Packed }; + +type Packed = { packed: Field[]; value: Unconstrained }; + +type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + +function provablePacked>( + type: A +): ProvableHashable>> { + // compute packed size + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return provable({ + packed: fields(packedSize), + value: Unconstrained.provable, + }); +} + +function countFields(input: HashInput) { + let n = input.fields?.length ?? 0; + let pendingBits = 0; + + for (let [, bits] of input.packed ?? []) { + pendingBits += bits; + if (pendingBits >= Field.sizeInBits) { + n++; + pendingBits = bits; + } + } + if (pendingBits > 0) n++; + + return n; +} From b8aea8d3a6cfa26073d44b13f6127d3f5d23903a Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 09:11:10 +0100 Subject: [PATCH 02/12] foreign field to input --- src/lib/gadgets/foreign-field.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 51b815dfc8..ce90b7947d 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -10,6 +10,7 @@ import { Bool } from '../bool.js'; import { Unconstrained } from '../circuit_value.js'; import { Field } from '../field.js'; import { Gates, foreignFieldAdd } from '../gates.js'; +import { modifiedField } from '../provable-types/fields.js'; import { Tuple, TupleN } from '../util/types.js'; import { assertOneOf } from './basic.js'; import { assert, bitSlice, exists, toVar, toVars } from './common.js'; @@ -427,6 +428,12 @@ function equals(x: Field3, c: bigint, f: bigint) { } } +const provableLimb = modifiedField({ + toInput(x) { + return { packed: [[x, Number(l)]] }; + }, +}); + const Field3 = { /** * Turn a bigint into a 3-tuple of Fields @@ -462,7 +469,7 @@ const Field3 = { * Note: Witnessing this creates a plain tuple of field elements without any implicit * range checks. */ - provable: provableTuple([Field, Field, Field]), + provable: provableTuple([provableLimb, provableLimb, provableLimb]), }; type Field2 = [Field, Field]; From a05c9dc6f0ba5d0af4e160f56437864d9679ab9e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:02:21 +0100 Subject: [PATCH 03/12] flesh out Packed class --- src/lib/provable-types/packed.ts | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index f4ee1d8603..d0e6a01c3e 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -6,18 +6,62 @@ import { provable, } from '../circuit_value.js'; import { Field } from '../field.js'; +import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; import { fields } from './fields.js'; export { provablePacked, Packed }; -type Packed = { packed: Field[]; value: Unconstrained }; +class Packed { + packed: Field[]; + value: Unconstrained; + type: ProvableExtended; + + private constructor( + packed: Field[], + value: Unconstrained, + type: ProvableExtended + ) { + this.packed = packed; + this.value = value; + this.type = type; + } + + static pack(type: ProvableExtended, x: T): Packed { + let input = type.toInput(x); + let packed = packToFields(input); + return new Packed(packed, Unconstrained.from(x), type); + } + + unpack(): T { + let value = Provable.witness(this.type, () => this.value.get()); + + // prove that the value packs to the packed fields + let input = this.type.toInput(value); + let packed = packToFields(input); + for (let i = 0; i < this.packed.length; i++) { + this.packed[i].assertEquals(packed[i]); + } + + return value; + } + + toFields(): Field[] { + return this.packed; + } + + hash() { + return Poseidon.hash(this.packed); + } +} + +type PackedBase = { packed: Field[]; value: Unconstrained }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; function provablePacked>( type: A -): ProvableHashable>> { +): ProvableHashable>> { // compute packed size let input = type.toInput(type.empty()); let packedSize = countFields(input); From 6ea442bd364cd81cec17946c77efcea97e757a88 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:42:45 +0100 Subject: [PATCH 04/12] further tweaks to packed --- src/lib/provable-types/packed.ts | 74 +++++++++++++++++++------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index d0e6a01c3e..fb7db86e0e 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -1,43 +1,39 @@ +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; import { HashInput, - InferProvable, ProvableExtended, Unconstrained, - provable, } from '../circuit_value.js'; import { Field } from '../field.js'; +import { assert } from '../gadgets/common.js'; import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; import { fields } from './fields.js'; -export { provablePacked, Packed }; +export { Packed }; class Packed { packed: Field[]; value: Unconstrained; - type: ProvableExtended; - private constructor( - packed: Field[], - value: Unconstrained, - type: ProvableExtended - ) { + constructor(packed: Field[], value: Unconstrained) { this.packed = packed; this.value = value; - this.type = type; } - static pack(type: ProvableExtended, x: T): Packed { - let input = type.toInput(x); + static pack(x: T): Packed { + let input = this.innerProvable.toInput(x); let packed = packToFields(input); - return new Packed(packed, Unconstrained.from(x), type); + return new this(packed, Unconstrained.from(x)); } unpack(): T { - let value = Provable.witness(this.type, () => this.value.get()); + let value = Provable.witness(this.Constructor.innerProvable, () => + this.value.get() + ); // prove that the value packs to the packed fields - let input = this.type.toInput(value); + let input = this.Constructor.innerProvable.toInput(value); let packed = packToFields(input); for (let i = 0; i < this.packed.length; i++) { this.packed[i].assertEquals(packed[i]); @@ -53,25 +49,45 @@ class Packed { hash() { return Poseidon.hash(this.packed); } -} -type PackedBase = { packed: Field[]; value: Unconstrained }; + static createProvable( + type: ProvableExtended + ): ProvableHashable> { + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return provableFromClass(this, { + packed: fields(packedSize), + value: Unconstrained.provable, + }) as ProvableHashable>; + } -type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableExtended | undefined; + + get Constructor(): typeof Packed { + return this.constructor as typeof Packed; + } -function provablePacked>( - type: A -): ProvableHashable>> { - // compute packed size - let input = type.toInput(type.empty()); - let packedSize = countFields(input); - - return provable({ - packed: fields(packedSize), - value: Unconstrained.provable, - }); + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'Packed not initialized'); + return this._provable; + } + static get innerProvable(): ProvableExtended { + assert(this._innerProvable !== undefined, 'Packed not initialized'); + return this._innerProvable; + } + + static create(type: ProvableExtended): typeof Packed { + return class Packed_ extends Packed { + static _provable = Packed_.createProvable(type); + static _innerProvable = type; + }; + } } +type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + function countFields(input: HashInput) { let n = input.fields?.length ?? 0; let pendingBits = 0; From 08981587b6e996d587107497c56da7528659a7cb Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:56:55 +0100 Subject: [PATCH 05/12] document Packed --- src/lib/provable-types/packed.ts | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index fb7db86e0e..52d52604af 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -12,6 +12,39 @@ import { fields } from './fields.js'; export { Packed }; +/** + * Packed is a "packed" representation of any type T. + * + * "Packed" means that field elements which take up fewer than 254 bits are packed together into + * as few field elements as possible. + * + * For example, you can pack several Bools (1 bit) or UInt32s (32 bits) into a single field element. + * + * Using a packed representation can make sense in provable code where the number of constraints + * depends on the number of field elements per value. + * + * For example, `Provable.if(bool, x, y)` takes O(n) constraints, where n is the number of field + * elements in x and y. + * + * Usage: + * + * ```ts + * // define a packed type from a type + * let PackedType = Packed.create(MyType); + * + * // pack a value + * let packed = PackedType.pack(value); + * + * // ... operations on packed values, more efficient than on plain values ... + * + * // unpack a value + * let value = packed.unpack(); + * ``` + * + * **Warning**: Packing only makes sense where packing actually reduces the number of field elements. + * For example, it doesn't make sense to pack a _single_ Bool, because it will be 1 field element before + * and after packing. On the other hand, it does makes sense to pack a type that holds 10 or 20 Bools. + */ class Packed { packed: Field[]; value: Unconstrained; From 60e43ba96ee64392d91d73ad1691b69911ee57f5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:59:01 +0100 Subject: [PATCH 06/12] use packing in ecdsa to save 2k constraints --- src/lib/gadgets/elliptic-curve.ts | 15 ++++++++++++++- tests/vk-regression/vk-regression.json | 20 ++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index 78ca4dbdff..865113fc2f 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -19,6 +19,7 @@ import { provable } from '../circuit_value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; import { arrayGet, assertBoolean } from './basic.js'; import { sliceField3 } from './bit-slices.js'; +import { Packed } from '../provable-types/packed.js'; // external API export { EllipticCurve, Point, Ecdsa }; @@ -413,6 +414,14 @@ function multiScalarMul( sliceField3(s, { maxBits, chunkSize: windowSizes[i] }) ); + // pack points to make array access more efficient + // a Point is 6 x 88-bit field elements, which are packed into 3 field elements + const PackedPoint = Packed.create(Point.provable); + + let packedTables = tables.map((table) => + table.map((point) => PackedPoint.pack(point)) + ); + ia ??= initialAggregator(Curve); let sum = Point.from(ia); @@ -426,7 +435,11 @@ function multiScalarMul( let sjP = windowSize === 1 ? points[j] - : arrayGetGeneric(Point.provable, tables[j], sj); + : arrayGetGeneric( + PackedPoint.provable, + packedTables[j], + sj + ).unpack(); // ec addition let added = add(sum, sjP, Curve); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index b112f092a7..acadee1f6b 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -236,29 +236,29 @@ } }, "ecdsa-only": { - "digest": "1e6bef24a2e02b573363f3512822d401d53ec7220c8d5837cd49691c19723028", + "digest": "529de0fe555c4359b6a25097cc0c873736532f64c045155f68aa868517632e4", "methods": { "verifySignedHash": { - "rows": 30680, - "digest": "5fe00efee7ecfb82b3ca69c8dd23d07f" + "rows": 28334, + "digest": "e8bcb74aa4278de6e4c31d9fec724f81" } }, "verificationKey": { - "data": "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", - "hash": "13090090603196708539583054794074329820941412942119534377592583560995629520780" + "data": "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", + "hash": "15135058674307280144061130356720654447302609112077173286437081384990231021958" } }, "ecdsa": { - "digest": "3e80a93d93a93f6152d70a9c21a2979ff0094c2d648a67c6d2daa4b2b0d18309", + "digest": "20942077320476aefd62a3271f60caf17cd442039d7572529bb77315db971d17", "methods": { "verifyEcdsa": { - "rows": 45178, - "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" + "rows": 42832, + "digest": "29cb625b78e1ccf5dc4e897ecf08aca1" } }, "verificationKey": { - "data": "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", - "hash": "25447212082831819715054236631079960883754611880602728284997977929479384060913" + "data": "AACzYt9qtBkn6y40KDH0lkzRSMBh3W41urWE6j0PSP2KB9GxsBAHAI3uzay1Vqyc+LMXeANSzNcoYSYnZts9Pk0nFNjCZ84EnGkie609NhFB8tU9k5Vkoqw3jihdsoJEUy6GK0H30dl/7H1rGxsx6Ec05aaFhiPw6t0jLxF1kj4uIeipqOScf8snKuzywk02FqvRxSHlk9pkEsUOvpNIwywxzhvHjWgXEQzROQF8v6q5R/1aJk3swpM1iRct9URLIjdin4GWyDB9279EZ6D6avFW2l7WuMJG++xBqGsNKZUgNM4WkUGNfCd+m42hJgt46eOy89db672su0n24IZG9tAsgQl8vPsVKfsTvTWlMj6/jISm7Dcctr1rZpSb8hRPsQstlfqMw3q6qijtTkFiMsdGRwJ6LNukSFUxOarhVsfREQngJufm4IxFpJJMR5F1DFSDPiOPuylEqXzke+j078Y4vr+QRo07YRlsoEv4a6ChcxMd3uu5Oami+D747/YVaS8kLd/3bO+WFpubID5fv4F7/JO4Fy/O7n1waPpNnzi/PZRlHVlwzNVAs09OmTmgzNM4/jAJBO9lRgCFA1SW0BADAPTtx6awEs5YE+0tR5a7p7CfPT1VFfmXMrPTFT28zhMB2O61CaxBQq2JnVDGEMkGv02l8N7aYF3VQegIRYngqyfPFNzYqZw3swyXzQ3nvZqWU2ARuzo1BgMrvnDgW1H+AMbKbNGU7IYXIYaLfTR9S7qrUbHESHac4wo9J9HmRiU1/IQdyr5LldYkzYtZOrjM4SzBkYYVtpSH7Sopij/TTy0U9CXNle7iCnZQS/72C8kwyJ+BGqpULLkSWhHoj+U9GSW9UgDHZ62jRTzvuZz5QaX/hYOmpNChNMFS1zoDYVE7ZIzVQKX03IDkzHAVJCXggwhQO3NK6OGhlP7A/heM6zgiR3/LlOa8uW4fcow50XC3280SDziK0Uczab3zlYXPPH6KqGPJfnftgwuvcHsRgddOWDVfEH3Q9mAj0y1R1FopRW/nurKTXW1Jd2ynRfFzZEHwsThtseMqao6axN9LHycl6U73Wa8oENskNHqVbv3NWvs0qyF+iknwgRG9nI9WKg2qDXacvJQHRIiBHfPZ3G52Z2lTf6OGg/elBurqGhA2wdDAQrBIWJwiTClONbV+8yR/4Md7aPi44E4XICLpHhE5hzko7ePy9cwh3oXy3btBt0urRwrl4d/jhHvoYt1eE2inNWEOYdlkXFUDlDErwOpFVsyQon0G25zNLAcVaZgdJLWueU1y3G0XkfHRqMZ8eV1iNAegPCCNRCvJ6SVsSwcQ67s45a8VqFxSSW0F65bDCI6Ue3Hwpb1RFKbfSIJbPyUrVSq5K99wUJ01O93Kn8LQlrAbjHWo5Za+tW0a/+Qlbr5E2eSEge+ldnbMbA9rcJwZf4bT457dBXMdlD7mECIDZtD8M/KLeyzMEinDzPfqnwZjU2ifxs6gaJPXOQAWPzbCm/z2vGlRbXDGZF6yTbLTdjzviuPhVtb7bzsZW2AYC+TlZqb4qm9MAVsH5rX3OZmvvmw5oRKeSj+FFD7uSRwfutDGC99i93uptU8syL/8Tr8xU3atxITlSqHqG+rVGWdLO9i3iq38zXgXbvZacrc3CMF5QBIM8yZXNslXH5k39D5SqubSHBWTqAJ1I0heOjaIHQGLROBYLn178tckBxfKQ2UpyfkvMw1Waw+fp5f64Ce+5bmYyZr6Dhmw/xcoAihjUsEqoecrLuGPp6qI4hQt9qOnVrAxHzwwtJGxcqoiCbe1mgz0fxMCt/i0z3ygdqAn20DKPHuBdqgVUFwx2T7Ac9fUCf3RHMq34onrr2nLHc038GYedmlFjoUZStujGwA8tSwLWyuWZTDVV+ZaW92qkhmrACog6NwhR6SEjQgsMRCVBQZzYirZxyulYmcNWH6BUmnLLFsn3GbS40xUr70gujEPnjZUK/ExGRfUPOfrYYb8mAciE9nP8OeK/UI+zjJy6Qp8mMroFw7gVHCfDtKTeQFt4JV3zubGsD7jypquHKCqPewhgn9tZ1UIsKIQB7+hBwDHzhlOZ2FfR4eLwQkO8sz275tpjHDAqX/TBWWRVg/yBDii0CWN4bP8UuX36jZKZboJUxIkM1xThiGZM2/oMbe5cZyjgrBR3P21wiDHAAlsHkaMfJgkVLqvZOw8hflKRIMa2dEYo5voD6aV30sATHQLoV0o+MlV3WA38RA+23Jqt1g+UZ7ReAuDP88jXhqWFcIvWHrJG0oy+rpAPQU/38vhIxbl//lirsirdVK2LrU47CC1f9/pRi07vTnvAm+n02dhwriqpwOmI2o2OU4mO0q96pCueKjAttkXgz+NSIJzcwprvNyE9UtKWswmIQg=", + "hash": "6055195771858836384417585817623837831345819726617689986064045716966540411450" } }, "sha256": { From 44f44c7160f34117804330e8b3acca0bdade272d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:02:00 +0100 Subject: [PATCH 07/12] export packed --- CHANGELOG.md | 1 + src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dadf1323e4..d6795f5e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1375 ### Fixed diff --git a/src/index.ts b/src/index.ts index 47d35c815a..57cf62e9ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,7 @@ export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; export { UInt32, UInt64, Int64, Sign, UInt8 } from './lib/int.js'; export { Bytes } from './lib/provable-types/provable-types.js'; +export { Packed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; From 7ae44dc120a88b9c91a8366a3f665be9f4d5ac18 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:22:49 +0100 Subject: [PATCH 08/12] simplify, prettify --- src/lib/provable-types/packed.ts | 47 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 52d52604af..2fcb67280d 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -49,17 +49,40 @@ class Packed { packed: Field[]; value: Unconstrained; + /** + * Create a packed representation of `type`. You can then use `PackedType.pack(x)` to pack a value. + */ + static create(type: ProvableExtended): typeof Packed { + // compute size of packed representation + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return class Packed_ extends Packed { + static _innerProvable = type; + static _provable = provableFromClass(Packed_, { + packed: fields(packedSize), + value: Unconstrained.provable, + }) as ProvableHashable>; + }; + } + constructor(packed: Field[], value: Unconstrained) { this.packed = packed; this.value = value; } + /** + * Pack a value. + */ static pack(x: T): Packed { let input = this.innerProvable.toInput(x); let packed = packToFields(input); return new this(packed, Unconstrained.from(x)); } + /** + * Unpack a value. + */ unpack(): T { let value = Provable.witness(this.Constructor.innerProvable, () => this.value.get() @@ -79,22 +102,7 @@ class Packed { return this.packed; } - hash() { - return Poseidon.hash(this.packed); - } - - static createProvable( - type: ProvableExtended - ): ProvableHashable> { - let input = type.toInput(type.empty()); - let packedSize = countFields(input); - - return provableFromClass(this, { - packed: fields(packedSize), - value: Unconstrained.provable, - }) as ProvableHashable>; - } - + // dynamic subclassing infra static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableExtended | undefined; @@ -110,13 +118,6 @@ class Packed { assert(this._innerProvable !== undefined, 'Packed not initialized'); return this._innerProvable; } - - static create(type: ProvableExtended): typeof Packed { - return class Packed_ extends Packed { - static _provable = Packed_.createProvable(type); - static _innerProvable = type; - }; - } } type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; From 0df7242bc7c7bb1f4009f19f79ae45773b42e73d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:26:26 +0100 Subject: [PATCH 09/12] it's a breaking change actually --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6795f5e86..a080488d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +### Breaking changes + +- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 + ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 From eb8854c8cc97918ca0c2721938f2b0a2be375a96 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 12:53:04 +0100 Subject: [PATCH 10/12] changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a080488d7a..9d8107dab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 -- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1375 +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 ### Fixed From 58e7128cd1dff1244b2d64556c371bae1cd5e627 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 08:46:47 +0100 Subject: [PATCH 11/12] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a884dc593d..a9354195b4 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit a9354195b4bb369cb9da827ccdfe81628385c1b6 From 51c55587907167dbcc6fa798e68ae3d45590fe69 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 09:23:36 +0100 Subject: [PATCH 12/12] fix changelog --- CHANGELOG.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 229892c5e7..e76325b791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/be748e42e...HEAD) +### Breaking changes + +- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 + ### Changed - Improve performance of Wasm Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 @@ -26,27 +30,24 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Target `network ID` configuration. https://github.com/o1-labs/o1js/pull/1387 - - Defaults to the `testnet` (as it was before). +- Configurable `networkId` when declaring a Mina instance. https://github.com/o1-labs/o1js/pull/1387 + - Defaults to `"testnet"`, the other option is `"mainnet"` + - The `networkId` parameter influences the algorithm used for signatures, and ensures that testnet transactions can't be replayed on mainnet +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) -### Breaking changes +### Added + +- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 + +### Changed - `Mina.accountCreationFee()` is deprecated in favor of `Mina.getNetworkConstants().accountCreationFee`. https://github.com/o1-labs/o1js/pull/1367 - `Mina.getNetworkConstants()` returns: - [default](https://github.com/o1-labs/o1js/pull/1367/files#diff-ef2c3547d64a8eaa8253cd82b3623288f3271e14f1dc893a0a3ddc1ff4b9688fR7) network constants if used outside of the transaction scope. - [actual](https://github.com/o1-labs/o1js/pull/1367/files#diff-437f2c15df7c90ad8154c5de1677ec0838d51859bcc0a0cefd8a0424b5736f31R1051) network constants if used within the transaction scope. -### Breaking changes - -- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 - -### Added - -- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 -- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 - ### Fixed - Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364