From 3af55881ec60801c527cd2d4e2c6b7234048809e Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Mon, 1 Jul 2024 18:25:49 +0200 Subject: [PATCH 01/32] make provable class only accept nested provables --- src/lib/provable/types/provable-derivers.ts | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts index b417b23163..d24fc76861 100644 --- a/src/lib/provable/types/provable-derivers.ts +++ b/src/lib/provable/types/provable-derivers.ts @@ -7,9 +7,13 @@ import { InferJson, InferredProvable as GenericInferredProvable, IsPure as GenericIsPure, + NestedProvable as GenericNestedProvable, createHashInput, Constructor, InferValue, + InferJsonNested, + InferValueNested, + InferProvableNested, } from '../../../bindings/lib/provable-generic.js'; import { Tuple } from '../../util/types.js'; import { GenericHashInput } from '../../../bindings/lib/generic.js'; @@ -31,6 +35,7 @@ export { InferJson, InferredProvable, IsPure, + NestedProvable, }; type ProvableExtension<T, TJson = any> = { @@ -54,6 +59,8 @@ type IsPure<T> = GenericIsPure<T, Field>; type HashInput = GenericHashInput<Field>; const HashInput = createHashInput<Field>(); +type NestedProvable = GenericNestedProvable<Field>; + const { provable } = createDerivers<Field>(); function provablePure<A>( @@ -66,13 +73,18 @@ function provableTuple<T extends Tuple<any>>(types: T): InferredProvable<T> { return provable(types) as any; } -function provableFromClass<A, T extends InferProvable<A>>( +function provableFromClass< + A extends NestedProvable, + T extends InferProvableNested<Field, A>, + V extends InferValueNested<Field, A>, + J extends InferJsonNested<Field, A> +>( Class: Constructor<T> & { check?: (x: T) => void; empty?: () => T }, typeObj: A ): IsPure<A> extends true - ? ProvablePureExtended<T, InferValue<A>, InferJson<A>> - : ProvableExtended<T, InferValue<A>, InferJson<A>> { - let raw = provable(typeObj); + ? ProvablePureExtended<T, V, J> + : ProvableExtended<T, V, J> { + let raw: ProvableExtended<T, V, J> = provable(typeObj) as any; return { sizeInFields: raw.sizeInFields, toFields: raw.toFields, @@ -101,7 +113,7 @@ function provableFromClass<A, T extends InferProvable<A>>( ? Class.empty() : construct(Class, raw.empty()); }, - } satisfies ProvableExtended<T, InferValue<A>, InferJson<A>> as any; + } satisfies ProvableExtended<T, V, J> as any; } function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T { From 277eb68c397dfc4d88697055a6e9311593865a28 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Mon, 1 Jul 2024 18:27:20 +0200 Subject: [PATCH 02/32] provable map draft --- src/lib/provable/types/provable-derivers.ts | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts index d24fc76861..e6a69a93ab 100644 --- a/src/lib/provable/types/provable-derivers.ts +++ b/src/lib/provable/types/provable-derivers.ts @@ -25,6 +25,7 @@ export { provablePure, provableTuple, provableFromClass, + provableMap, }; // internal API @@ -120,3 +121,33 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T { let instance = Object.create(Class.prototype); return Object.assign(instance, value); } + +function provableMap< + A extends Provable<any>, + S, + T extends InferProvable<A> = InferProvable<A> +>(base: A, there: (t: T) => S, back: (s: S) => T): Provable<S, InferValue<A>> { + return { + sizeInFields() { + return base.sizeInFields(); + }, + toFields(value) { + return base.toFields(back(value)); + }, + toAuxiliary(value) { + return base.toAuxiliary(value === undefined ? undefined : back(value)); + }, + fromFields(fields, aux) { + return there(base.fromFields(fields, aux)); + }, + check(value) { + base.check(back(value)); + }, + toValue(value) { + return base.toValue(back(value)); + }, + fromValue(value) { + return there(base.fromValue(value)); + }, + }; +} From dea94bc2c961ef2e9a763c9326ea22b5ecd51230 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Mon, 1 Jul 2024 18:27:23 +0200 Subject: [PATCH 03/32] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 1e29577237..dfbbf21788 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 1e29577237e331a1e0040398cc929ff295fd3383 +Subproject commit dfbbf21788c0a178cf483ef7f1b914ec79974f68 From 33236f391ca4391924c3691e74d57b7548604d57 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 2 Jul 2024 10:27:52 +0200 Subject: [PATCH 04/32] extend provable class --- src/lib/provable/types/provable-derivers.ts | 53 +++++++++++++++++++-- src/lib/provable/types/provable-intf.ts | 12 ++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts index e6a69a93ab..60b0c18208 100644 --- a/src/lib/provable/types/provable-derivers.ts +++ b/src/lib/provable/types/provable-derivers.ts @@ -1,4 +1,4 @@ -import { Provable, ProvablePure } from './provable-intf.js'; +import { Provable, ProvableHashable, ProvablePure } from './provable-intf.js'; import type { Field } from '../wrapped.js'; import { createDerivers, @@ -26,6 +26,7 @@ export { provableTuple, provableFromClass, provableMap, + provableExtends, }; // internal API @@ -123,10 +124,14 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T { } function provableMap< - A extends Provable<any>, + A extends ProvableHashable<any>, S, T extends InferProvable<A> = InferProvable<A> ->(base: A, there: (t: T) => S, back: (s: S) => T): Provable<S, InferValue<A>> { +>( + base: A, + there: (t: T) => S, + back: (s: S) => T +): ProvableHashable<S, InferValue<A>> { return { sizeInFields() { return base.sizeInFields(); @@ -149,5 +154,47 @@ function provableMap< fromValue(value) { return there(base.fromValue(value)); }, + empty() { + return there(base.empty()); + }, + toInput(value) { + return base.toInput(back(value)); + }, }; } + +function provableExtends< + A extends ProvableHashable<any>, + T extends InferProvable<A>, + S extends T +>(S: new (t: T) => S, base: A) { + return { + sizeInFields() { + return base.sizeInFields(); + }, + toFields(value: S | T) { + return base.toFields(value); + }, + toAuxiliary(value?: S | T) { + return base.toAuxiliary(value); + }, + fromFields(fields, aux) { + return new S(base.fromFields(fields, aux)); + }, + check(value: S | T) { + base.check(value); + }, + toValue(value: S | T) { + return base.toValue(value); + }, + fromValue(value) { + return new S(base.fromValue(value)); + }, + empty() { + return new S(base.empty()); + }, + toInput(value: S | T) { + return base.toInput(value); + }, + } satisfies ProvableHashable<S, InferValue<A>>; +} diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts index e3c1ea5beb..51d5c43831 100644 --- a/src/lib/provable/types/provable-intf.ts +++ b/src/lib/provable/types/provable-intf.ts @@ -1,6 +1,6 @@ import type { Field } from '../field.js'; -export { Provable, ProvablePure }; +export { Provable, ProvablePure, ProvableWithEmpty, ProvableHashable }; /** * `Provable<T>` is the general interface for provable types in o1js. @@ -86,3 +86,13 @@ type Provable<T, TValue = any> = { type ProvablePure<T, TValue = any> = Omit<Provable<T, TValue>, 'fromFields'> & { fromFields: (fields: Field[]) => T; }; + +type ProvableWithEmpty<T, TValue = any> = Provable<T, TValue> & { + empty: () => T; +}; + +type HashInput = { fields?: Field[]; packed?: [Field, number][] }; + +type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & { + toInput: (x: T) => HashInput; +}; From b745f2a4eda7d253ee889186b87bbe8231a0be63 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 2 Jul 2024 10:33:01 +0200 Subject: [PATCH 05/32] extend account update forest with more convenient methods --- src/lib/mina/account-update.ts | 41 ++++++++++++++++++++++++--- src/lib/mina/token/forest-iterator.ts | 7 +++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts index 7f31de3985..d92e07fbb2 100644 --- a/src/lib/mina/account-update.ts +++ b/src/lib/mina/account-update.ts @@ -3,14 +3,18 @@ import { FlexibleProvable, StructNoJson, } from '../provable/types/struct.js'; -import { provable, provablePure } from '../provable/types/provable-derivers.js'; +import { + provable, + provableExtends, + provablePure, +} from '../provable/types/provable-derivers.js'; import { memoizationContext, memoizeWitness, Provable, } from '../provable/provable.js'; import { Field, Bool } from '../provable/wrapped.js'; -import { Pickles, Test } from '../../snarky.js'; +import { Pickles } from '../../snarky.js'; import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; import { Types, @@ -1343,10 +1347,29 @@ class AccountUpdateForest extends MerkleList.create( AccountUpdateTreeBase, merkleListHash ) { + static provable = provableExtends(AccountUpdateForest, super.provable); + + push(update: AccountUpdate | AccountUpdateTreeBase) { + return super.push( + update instanceof AccountUpdate ? AccountUpdateTree.from(update) : update + ); + } + pushIf(condition: Bool, update: AccountUpdate | AccountUpdateTreeBase) { + return super.pushIf( + condition, + update instanceof AccountUpdate ? AccountUpdateTree.from(update) : update + ); + } + static fromFlatArray(updates: AccountUpdate[]): AccountUpdateForest { let simpleForest = accountUpdatesToCallForest(updates); return this.fromSimpleForest(simpleForest); } + + toFlatArray(mutate = true, depth = 0) { + return AccountUpdateForest.toFlatArray(this, mutate, depth); + } + static toFlatArray( forest: AccountUpdateForestBase, mutate = true, @@ -1385,6 +1408,17 @@ class AccountUpdateForest extends MerkleList.create( }); }); } + + // fix static methods + static empty() { + return AccountUpdateForest.provable.empty(); + } + static from(array: AccountUpdateTreeBase[]) { + return new AccountUpdateForest(super.from(array)); + } + static fromReverse(array: AccountUpdateTreeBase[]) { + return new AccountUpdateForest(super.fromReverse(array)); + } } /** @@ -1590,8 +1624,7 @@ class UnfinishedForest { } toFlatArray(mutate = true, depth = 0): AccountUpdate[] { - if (this.isFinal()) - return AccountUpdateForest.toFlatArray(this.final, mutate, depth); + if (this.isFinal()) return this.final.toFlatArray(mutate, depth); assert(this.isMutable(), 'final or mutable'); let flatUpdates: AccountUpdate[] = []; for (let node of this.mutable) { diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 7918cc629a..aa95731eb0 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -12,8 +12,11 @@ import { MerkleListIterator, MerkleList } from '../../provable/merkle-list.js'; export { TokenAccountUpdateIterator }; -const AccountUpdateIterator = - MerkleListIterator.createFromList(AccountUpdateForest); +const AccountUpdateIterator = MerkleListIterator.create( + AccountUpdateForest.prototype.innerProvable, + AccountUpdateForest._nextHash, + AccountUpdateForest.emptyHash +); class Layer extends Struct({ forest: AccountUpdateIterator.provable, From c93cf54a868360c9d7038926451f266002141fcb Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 00:29:06 +0200 Subject: [PATCH 06/32] unwrap unconstrained when converting to value --- .../mina/actions/offchain-state-serialization.ts | 2 +- src/lib/provable/merkle-tree-indexed.ts | 6 +----- src/lib/provable/types/unconstrained.ts | 13 ++++++------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts index 2bc1df8dd9..e939a49bf4 100644 --- a/src/lib/mina/actions/offchain-state-serialization.ts +++ b/src/lib/mina/actions/offchain-state-serialization.ts @@ -314,7 +314,7 @@ function updateMerkleMap( // update the intermediate tree, save updates for final tree intermediateTree.set(key, value); - updates.push({ key, fullValue: prefix.get() }); + updates.push({ key, fullValue: prefix }); } if (isValidUpdate) { diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts index 262e788dfc..c3b5160ed9 100644 --- a/src/lib/provable/merkle-tree-indexed.ts +++ b/src/lib/provable/merkle-tree-indexed.ts @@ -677,11 +677,7 @@ class Leaf extends Struct({ } static fromStored(leaf: StoredLeaf, sortedIndex: number) { - return { - ...leaf, - index: Unconstrained.from(leaf.index), - sortedIndex: Unconstrained.from(sortedIndex), - }; + return { ...leaf, sortedIndex: sortedIndex }; } } diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts index 857a1d9665..c4a7a4c662 100644 --- a/src/lib/provable/types/unconstrained.ts +++ b/src/lib/provable/types/unconstrained.ts @@ -111,7 +111,7 @@ and Provable.asProver() blocks, which execute outside the proof. }); } - static provable: Provable<Unconstrained<any>, Unconstrained<any>> & { + static provable: UnconstrainedProvable<any> & { toInput: (x: Unconstrained<any>) => { fields?: Field[]; packed?: [Field, number][]; @@ -123,18 +123,15 @@ and Provable.asProver() blocks, which execute outside the proof. toAuxiliary: (t?: any) => [t ?? new Unconstrained(false)], fromFields: (_, [t]) => t, check: () => {}, - toValue: (t) => t, - fromValue: (t) => t, + toValue: (t) => t.get(), + fromValue: (t) => (t instanceof Unconstrained ? t : Unconstrained.from(t)), toInput: () => ({}), empty: (): any => { throw Error('There is no default empty value for Unconstrained.'); }, }; - static provableWithEmpty<T>(empty: T): Provable< - Unconstrained<T>, - Unconstrained<T> - > & { + static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & { toInput: (x: Unconstrained<T>) => { fields?: Field[]; packed?: [Field, number][]; @@ -147,3 +144,5 @@ and Provable.asProver() blocks, which execute outside the proof. }; } } + +type UnconstrainedProvable<T> = Provable<Unconstrained<T>, T>; From 375bbaaaa4d7c91c13b96c0fa3e3100ccb4873fd Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 00:37:50 +0200 Subject: [PATCH 07/32] fixup --- src/lib/provable/merkle-list.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 6144b42e53..5febbe6ffe 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -283,6 +283,9 @@ class MerkleList<T> implements MerkleListBase<T> { assert(this._provable !== undefined, 'MerkleList not initialized'); return this._provable; } + static set provable(_provable: ProvableHashable<MerkleList<T>>) { + this._provable = _provable; + } } // override `instanceof` for subclasses return class MerkleListT extends MerkleListTBase { From 744ec3ef2c19d7ab3f5bf4e25f39769dd32dc313 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 00:49:57 +0200 Subject: [PATCH 08/32] simplify --- src/lib/provable/types/unconstrained.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts index c4a7a4c662..fb923f2517 100644 --- a/src/lib/provable/types/unconstrained.ts +++ b/src/lib/provable/types/unconstrained.ts @@ -95,10 +95,7 @@ and Provable.asProver() blocks, which execute outside the proof. * Create an `Unconstrained` from a witness computation. */ static witness<T>(compute: () => T): Unconstrained<T> { - return witness( - Unconstrained.provable, - () => new Unconstrained(true, compute()) - ); + return witness(Unconstrained.provable, compute); } /** @@ -124,7 +121,7 @@ and Provable.asProver() blocks, which execute outside the proof. fromFields: (_, [t]) => t, check: () => {}, toValue: (t) => t.get(), - fromValue: (t) => (t instanceof Unconstrained ? t : Unconstrained.from(t)), + fromValue: (t) => Unconstrained.from(t), toInput: () => ({}), empty: (): any => { throw Error('There is no default empty value for Unconstrained.'); From f7985547cf88420ab6bbdb700881e3ee690cd9c1 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 01:11:33 +0200 Subject: [PATCH 09/32] allow leaving out .provable in witness --- src/lib/provable/types/provable-intf.ts | 23 ++++++++++++++++++- src/lib/provable/types/unconstrained.ts | 2 +- src/lib/provable/types/witness.ts | 30 +++++++++++++------------ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts index 51d5c43831..100cf8df0c 100644 --- a/src/lib/provable/types/provable-intf.ts +++ b/src/lib/provable/types/provable-intf.ts @@ -1,6 +1,13 @@ import type { Field } from '../field.js'; -export { Provable, ProvablePure, ProvableWithEmpty, ProvableHashable }; +export { + Provable, + ProvablePure, + ProvableWithEmpty, + ProvableHashable, + ProvableType, + ToProvable, +}; /** * `Provable<T>` is the general interface for provable types in o1js. @@ -96,3 +103,17 @@ type HashInput = { fields?: Field[]; packed?: [Field, number][] }; type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & { toInput: (x: T) => HashInput; }; + +// helpers to accept { provable: Type } instead of Type + +type ProvableType<T, V = any> = Provable<T, V> | { provable: Provable<T, V> }; + +type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P } + ? P + : A; + +const ProvableType = { + get<T, V>(type: ProvableType<T, V>): Provable<T, V> { + return 'provable' in type ? type.provable : type; + }, +}; diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts index fb923f2517..1278aaa7e7 100644 --- a/src/lib/provable/types/unconstrained.ts +++ b/src/lib/provable/types/unconstrained.ts @@ -95,7 +95,7 @@ and Provable.asProver() blocks, which execute outside the proof. * Create an `Unconstrained` from a witness computation. */ static witness<T>(compute: () => T): Unconstrained<T> { - return witness(Unconstrained.provable, compute); + return witness(Unconstrained, compute); } /** diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts index 15efb266f4..63de0d75d0 100644 --- a/src/lib/provable/types/witness.ts +++ b/src/lib/provable/types/witness.ts @@ -1,36 +1,38 @@ import type { Field } from '../field.js'; import type { FlexibleProvable, InferProvable } from './struct.js'; -import type { Provable } from './provable-intf.js'; +import { Provable, ProvableType, ToProvable } from './provable-intf.js'; import { inCheckedComputation, snarkContext, } from '../core/provable-context.js'; import { exists, existsAsync } from '../core/exists.js'; -import { From } from '../../../bindings/lib/provable-generic.js'; +import { From, InferValue } from '../../../bindings/lib/provable-generic.js'; import { TupleN } from '../../util/types.js'; import { createField } from '../core/field-constructor.js'; export { witness, witnessAsync, witnessFields }; -function witness<A extends Provable<any, any>, T extends From<A> = From<A>>( - type: A, - compute: () => T -): InferProvable<A> { - type S = InferProvable<A>; +function witness< + A extends ProvableType<any, any>, + T extends From<ToProvable<A>> = From<ToProvable<A>> +>(type: A, compute: () => T): InferProvable<ToProvable<A>> { + type S = InferProvable<ToProvable<A>>; + const provable: Provable<S> = ProvableType.get(type); let ctx = snarkContext.get(); // outside provable code, we just call the callback and return its cloned result if (!inCheckedComputation() || ctx.inWitnessBlock) { - return clone(type, type.fromValue(compute())); + return clone(provable, provable.fromValue(compute())); } let proverValue: S | undefined = undefined; let fields: Field[]; let id = snarkContext.enter({ ...ctx, inWitnessBlock: true }); try { - fields = exists(type.sizeInFields(), () => { - proverValue = type.fromValue(compute()); - let fields = type.toFields(proverValue); + fields = exists(provable.sizeInFields(), () => { + let value = provable.fromValue(compute()); + proverValue = value; + let fields = provable.toFields(value); return fields.map((x) => x.toBigInt()); }); } finally { @@ -38,11 +40,11 @@ function witness<A extends Provable<any, any>, T extends From<A> = From<A>>( } // rebuild the value from its fields (which are now variables) and aux data - let aux = type.toAuxiliary(proverValue); - let value = (type as Provable<S>).fromFields(fields, aux); + let aux = provable.toAuxiliary(proverValue); + let value = provable.fromFields(fields, aux); // add type-specific constraints - type.check(value); + provable.check(value); return value; } From 07d6b647c833d609ab8e2d56eab425d10b844ffb Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 01:18:24 +0200 Subject: [PATCH 10/32] match behaviour in provable async --- src/lib/provable/types/witness.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts index 63de0d75d0..69251ef0a3 100644 --- a/src/lib/provable/types/witness.ts +++ b/src/lib/provable/types/witness.ts @@ -50,25 +50,29 @@ function witness< } async function witnessAsync< - T, - S extends FlexibleProvable<T> = FlexibleProvable<T> ->(type: S, compute: () => Promise<T>): Promise<T> { + A extends ProvableType<any, any>, + T extends From<ToProvable<A>> = From<ToProvable<A>> +>(type: A, compute: () => Promise<T>): Promise<T> { + type S = InferProvable<ToProvable<A>>; + const provable: Provable<S> = ProvableType.get(type); + let ctx = snarkContext.get(); // outside provable code, we just call the callback and return its cloned result if (!inCheckedComputation() || ctx.inWitnessBlock) { let value: T = await compute(); - return clone(type, value); + return clone(provable, provable.fromValue(value)); } - let proverValue: T | undefined = undefined; + let proverValue: S | undefined = undefined; let fields: Field[]; // call into `existsAsync` to witness the raw field elements let id = snarkContext.enter({ ...ctx, inWitnessBlock: true }); try { - fields = await existsAsync(type.sizeInFields(), async () => { - proverValue = await compute(); - let fields = type.toFields(proverValue); + fields = await existsAsync(provable.sizeInFields(), async () => { + let value: S = provable.fromValue(await compute()); + proverValue = value; + let fields = provable.toFields(value); return fields.map((x) => x.toBigInt()); }); } finally { @@ -76,11 +80,11 @@ async function witnessAsync< } // rebuild the value from its fields (which are now variables) and aux data - let aux = type.toAuxiliary(proverValue); - let value = (type as Provable<T>).fromFields(fields, aux); + let aux = provable.toAuxiliary(proverValue); + let value = provable.fromFields(fields, aux); // add type-specific constraints - type.check(value); + provable.check(value); return value; } From d25d20573671c404a2ce680d357d9cad348e0e34 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 01:26:22 +0200 Subject: [PATCH 11/32] start using it --- benchmark/benchmarks/ecdsa.ts | 6 +++--- src/examples/benchmarks/foreign-field.ts | 10 ++-------- src/examples/benchmarks/keccak-witness.ts | 2 +- src/lib/mina/account-update.ts | 4 +--- src/lib/mina/actions/offchain-state.ts | 2 +- src/lib/mina/actions/reducer.ts | 2 +- src/lib/provable/crypto/foreign-ecdsa.ts | 4 ++-- src/lib/provable/gadgets/comparison.ts | 2 +- 8 files changed, 12 insertions(+), 20 deletions(-) diff --git a/benchmark/benchmarks/ecdsa.ts b/benchmark/benchmarks/ecdsa.ts index 35f54b9871..ed7cf80d21 100644 --- a/benchmark/benchmarks/ecdsa.ts +++ b/benchmark/benchmarks/ecdsa.ts @@ -27,9 +27,9 @@ const EcdsaBenchmarks = benchmark( tic('witness generation'); await Provable.runAndCheck(async () => { - let message_ = Provable.witness(Bytes32.provable, () => message); - let signature_ = Provable.witness(Ecdsa.provable, () => signature); - let publicKey_ = Provable.witness(Secp256k1.provable, () => publicKey); + let message_ = Provable.witness(Bytes32, () => message); + let signature_ = Provable.witness(Ecdsa, () => signature); + let publicKey_ = Provable.witness(Secp256k1, () => publicKey); await keccakAndEcdsa.rawMethods.verifyEcdsa( message_, signature_, diff --git a/src/examples/benchmarks/foreign-field.ts b/src/examples/benchmarks/foreign-field.ts index 30190324ef..8f7f8a31fe 100644 --- a/src/examples/benchmarks/foreign-field.ts +++ b/src/examples/benchmarks/foreign-field.ts @@ -5,14 +5,8 @@ class ForeignScalar extends createForeignField( ) {} function main() { - let s = Provable.witness( - ForeignScalar.Canonical.provable, - ForeignScalar.random - ); - let t = Provable.witness( - ForeignScalar.Canonical.provable, - ForeignScalar.random - ); + let s = Provable.witness(ForeignScalar.Canonical, ForeignScalar.random); + let t = Provable.witness(ForeignScalar.Canonical, ForeignScalar.random); s.mul(t); } diff --git a/src/examples/benchmarks/keccak-witness.ts b/src/examples/benchmarks/keccak-witness.ts index e4509c5019..f37c16893f 100644 --- a/src/examples/benchmarks/keccak-witness.ts +++ b/src/examples/benchmarks/keccak-witness.ts @@ -4,7 +4,7 @@ let Bytes32 = Bytes(32); console.time('keccak witness'); await Provable.runAndCheck(() => { - let bytes = Provable.witness(Bytes32.provable, () => Bytes32.random()); + let bytes = Provable.witness(Bytes32, () => Bytes32.random()); Hash.Keccak256.hash(bytes); }); console.timeEnd('keccak witness'); diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts index d92e07fbb2..8c0568b400 100644 --- a/src/lib/mina/account-update.ts +++ b/src/lib/mina/account-update.ts @@ -1579,9 +1579,7 @@ class UnfinishedForest { } witnessHash(): UnfinishedForestFinal { - let final = Provable.witness(AccountUpdateForest.provable, () => - this.finalize() - ); + let final = Provable.witness(AccountUpdateForest, () => this.finalize()); return this.setFinal(final); } diff --git a/src/lib/mina/actions/offchain-state.ts b/src/lib/mina/actions/offchain-state.ts index 18a5064415..2e42f73a5f 100644 --- a/src/lib/mina/actions/offchain-state.ts +++ b/src/lib/mina/actions/offchain-state.ts @@ -237,7 +237,7 @@ function OffchainState< // witness the merkle map & anchor against the onchain root let map = await Provable.witnessAsync( - IndexedMerkleMapN.provable, + IndexedMerkleMapN, async () => (await merkleMaps()).merkleMap ); map.root.assertEquals(stateRoot, 'root mismatch'); diff --git a/src/lib/mina/actions/reducer.ts b/src/lib/mina/actions/reducer.ts index 8be11506fb..43c36f98f0 100644 --- a/src/lib/mina/actions/reducer.ts +++ b/src/lib/mina/actions/reducer.ts @@ -268,7 +268,7 @@ class ${contract.constructor.name} extends SmartContract { config?.fromActionState ?? Actions.emptyActionState() ) {} - let actions = Provable.witness(MerkleActions.provable, () => { + let actions = Provable.witness(MerkleActions, () => { let actionFields = Mina.getActions( contract.address, config, diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index d9840edfe2..bd97164050 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -106,9 +106,9 @@ class EcdsaSignature { * * // ... * // in provable code: create input witnesses (or use method inputs, or constants) - * let pk = Provable.witness(Secp256k1.provable, () => publicKey); + * let pk = Provable.witness(Secp256k1, () => publicKey); * let msg = Provable.witness(Provable.Array(Field, 9), () => messageBytes.map(Field)); - * let sig = Provable.witness(Ecdsa.provable, () => signature); + * let sig = Provable.witness(Ecdsa, () => signature); * * // verify signature * let isValid = sig.verify(msg, pk); diff --git a/src/lib/provable/gadgets/comparison.ts b/src/lib/provable/gadgets/comparison.ts index e784f54caf..648b7507bd 100644 --- a/src/lib/provable/gadgets/comparison.ts +++ b/src/lib/provable/gadgets/comparison.ts @@ -235,7 +235,7 @@ function isOddAndHigh(x: Field) { function fieldToField3(x: Field) { if (x.isConstant()) return Field3.from(x.toBigInt()); - let xBig = witness(Field3.provable, () => Field3.from(x.toBigInt())); + let xBig = witness(Field3, () => x.toBigInt()); multiRangeCheck(xBig); let [x0, x1, x2] = xBig; From 30002309abc84a435b1f071e236a01e3ef8f197e Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Wed, 3 Jul 2024 16:00:39 +0200 Subject: [PATCH 12/32] (maybe revert) some experimenting --- src/examples/crypto/rsa/rsa.ts | 59 ++++++++++++++++++++++--------- src/index.ts | 2 ++ src/lib/provable/foreign-field.ts | 2 +- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts index 8d54376426..78de61b6b9 100644 --- a/src/examples/crypto/rsa/rsa.ts +++ b/src/examples/crypto/rsa/rsa.ts @@ -7,7 +7,7 @@ import { Provable, Struct, Unconstrained, - provable, + provableExtends, } from 'o1js'; export { Bigint2048, rsaVerify65537 }; @@ -19,10 +19,40 @@ const mask = (1n << 116n) - 1n; */ const Field18 = Provable.Array(Field, 18); -class Bigint2048 extends Struct({ - fields: Field18, - value: Unconstrained.provable as Provable<Unconstrained<bigint>>, -}) { +class Bigint2048 { + fields: Field[]; + value: Unconstrained<bigint>; + + // TODO this could be simplified with a Struct-like base class + // TODO map the value type to `bigint` + static provable = provableExtends( + Bigint2048, + // TODO this wrapping Struct should be unnecessary + class extends Struct({ + fields: Field18, + value: Unconstrained.provableWithEmpty(0n), + }) { + // TODO where to add the custom check()? + static check({ fields }: { fields: Field[] }) { + for (let x of fields) { + rangeCheck116(x); + } + } + } + ); + + // TODO constructor could be removed with a Struct-like base class + constructor({ + fields, + value, + }: { + fields: Field[]; + value: Unconstrained<bigint>; + }) { + this.fields = fields; + this.value = value; + } + modMul(x: Bigint2048, y: Bigint2048) { return multiply(x, y, this); } @@ -36,19 +66,13 @@ class Bigint2048 extends Struct({ } static from(x: bigint) { - let fields = []; + let fields: bigint[] = []; let value = x; for (let i = 0; i < 18; i++) { - fields.push(Field(x & mask)); + fields.push(x & mask); x >>= 116n; } - return new Bigint2048({ fields, value: Unconstrained.from(value) }); - } - - static check(x: { fields: Field[] }) { - for (let i = 0; i < 18; i++) { - rangeCheck116(x.fields[i]); - } + return Bigint2048.provable.fromValue({ fields, value }); } } @@ -66,12 +90,15 @@ function multiply( // witness q, r so that x*y = q*p + r // this also adds the range checks in `check()` let { q, r } = Provable.witness( - provable({ q: Bigint2048, r: Bigint2048 }), + // TODO Struct() should be unnecessary + // TODO .provable should be unnecessary + Struct({ q: Bigint2048.provable, r: Bigint2048.provable }), () => { let xy = x.toBigint() * y.toBigint(); let p0 = p.toBigint(); let q = xy / p0; let r = xy - q * p0; + // TODO Bigint2048.from() should be unnecessary return { q: Bigint2048.from(q), r: Bigint2048.from(r) }; } ); @@ -143,7 +170,7 @@ function rsaVerify65537( x = modulus.modMul(x, signature); // check that x == message - Provable.assertEqual(Bigint2048, message, x); + Provable.assertEqual(Bigint2048.provable, message, x); } /** diff --git a/src/index.ts b/src/index.ts index 3778236825..0300581ffe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,8 @@ export type { export { provable, provablePure, + provableMap, + provableExtends, } from './lib/provable/types/provable-derivers.js'; export { Struct } from './lib/provable/types/struct.js'; export { Unconstrained } from './lib/provable/types/unconstrained.js'; diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 802d064a61..167dedb94d 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -622,7 +622,7 @@ function isConstant(x: bigint | number | string | ForeignField) { * ``` * * Similarly, there is a separate class {@link CanonicalForeignField} which represents fully reduced, "canonical" field elements. - * To convert to a canonical field element, use {@link ForeignField.assertCanonical}: + * To convert to a canonical field element, use `ForeignField.assertCanonical()`: * * ```ts * x.assertCanonical(); // asserts x < p; returns `CanonicalForeignField` From 50248551fd8336cfb26052498fe041835ac2dd35 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 10:25:25 +0200 Subject: [PATCH 13/32] remove .provable where not needed --- src/examples/crypto/rsa/run.ts | 6 ++++- .../mina/actions/batch-reducer.unit-test.ts | 4 +--- src/lib/provable/foreign-field.ts | 2 +- src/lib/provable/gadgets/gadgets.ts | 22 +++++++++---------- src/lib/provable/merkle-tree-indexed.ts | 2 +- src/lib/provable/scalar-field.ts | 4 +--- .../test/custom-gates-recursion.unit-test.ts | 9 +++----- src/lib/provable/test/ecdsa.unit-test.ts | 18 +++++---------- .../provable/test/foreign-curve.unit-test.ts | 6 ++--- .../test/foreign-field-gadgets.unit-test.ts | 8 +++---- .../provable/test/merkle-tree.unit-test.ts | 2 +- src/lib/provable/types/witness.ts | 2 +- .../vk-regression/plain-constraint-system.ts | 8 +++---- 13 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/examples/crypto/rsa/run.ts b/src/examples/crypto/rsa/run.ts index 3d27fb7ed9..c2c9b7ed53 100644 --- a/src/examples/crypto/rsa/run.ts +++ b/src/examples/crypto/rsa/run.ts @@ -7,7 +7,11 @@ let rsaZkProgram = ZkProgram({ methods: { verifyRsa65537: { - privateInputs: [Bigint2048, Bigint2048, Bigint2048], + privateInputs: [ + Bigint2048.provable, + Bigint2048.provable, + Bigint2048.provable, + ], async method( message: Bigint2048, diff --git a/src/lib/mina/actions/batch-reducer.unit-test.ts b/src/lib/mina/actions/batch-reducer.unit-test.ts index 98b35e3bb3..7e98a96f40 100644 --- a/src/lib/mina/actions/batch-reducer.unit-test.ts +++ b/src/lib/mina/actions/batch-reducer.unit-test.ts @@ -89,9 +89,7 @@ class UnsafeAirdrop extends SmartContract { @method.returns(MerkleMap.provable) async settleClaims(batch: Batch, proof: BatchProof) { // witness merkle map and require that it matches the onchain root - let eligibleMap = Provable.witness(MerkleMap.provable, () => - eligible.clone() - ); + let eligibleMap = Provable.witness(MerkleMap, () => eligible.clone()); this.eligibleRoot.requireEquals(eligibleMap.root); this.eligibleLength.requireEquals(eligibleMap.length); diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 167dedb94d..1c743713df 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -614,7 +614,7 @@ function isConstant(x: bigint | number | string | ForeignField) { * If you want to do multiplication, you have two options: * - create your field elements using the {@link ForeignField.AlmostReduced} constructor, or using the `.provable` type on that class. * ```ts - * let x = Provable.witness(ForeignField.AlmostReduced.provable, () => ForeignField.from(5)); + * let x = Provable.witness(ForeignField.AlmostReduced, () => 5n); * ``` * - create your field elements normally and convert them using `x.assertAlmostReduced()`. * ```ts diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index c28c3d8ce5..205a0990a6 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -554,8 +554,8 @@ const Gadgets = { * * @example * ```ts - * let x = Provable.witness(Field3.provable, () => Field3.from(9n)); - * let y = Provable.witness(Field3.provable, () => Field3.from(10n)); + * let x = Provable.witness(Field3, () => 9n); + * let y = Provable.witness(Field3, () => 10n); * * // range check x and y * Gadgets.multiRangeCheck(x); @@ -616,9 +616,9 @@ const Gadgets = { * * @example * ```ts - * let x = Provable.witness(Field3.provable, () => Field3.from(4n)); - * let y = Provable.witness(Field3.provable, () => Field3.from(5n)); - * let z = Provable.witness(Field3.provable, () => Field3.from(10n)); + * let x = Provable.witness(Field3, () => 4n); + * let y = Provable.witness(Field3, () => 5n); + * let z = Provable.witness(Field3, () => 10n); * * // range check x, y, z * Gadgets.multiRangeCheck(x); @@ -656,8 +656,8 @@ const Gadgets = { * // example modulus: secp256k1 prime * let f = (1n << 256n) - (1n << 32n) - 0b1111010001n; * - * let x = Provable.witness(Field3.provable, () => Field3.from(f - 1n)); - * let y = Provable.witness(Field3.provable, () => Field3.from(f - 2n)); + * let x = Provable.witness(Field3, () => f - 1n); + * let y = Provable.witness(Field3, () => f - 2n); * * // range check x, y and prove additional bounds x[2] <= f[2] * ForeignField.assertAlmostReduced([x, y], f); @@ -758,9 +758,9 @@ const Gadgets = { * * @example * ```ts - * let x = Provable.witness(Field3.provable, () => Field3.from(4n)); - * let y = Provable.witness(Field3.provable, () => Field3.from(5n)); - * let z = Provable.witness(Field3.provable, () => Field3.from(10n)); + * let x = Provable.witness(Field3, () => 4n); + * let y = Provable.witness(Field3, () => 5n); + * let z = Provable.witness(Field3, () => 10n); * * ForeignField.assertAlmostReduced([x, y, z], f); * @@ -790,7 +790,7 @@ const Gadgets = { * * @example * ```ts - * let x = Provable.witness(Field3.provable, () => Field3.from(0x1235n)); + * let x = Provable.witness(Field3, () => 0x1235n); * * // range check limbs of x * Gadgets.multiRangeCheck(x); diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts index 662e82ac12..4010ee309c 100644 --- a/src/lib/provable/merkle-tree-indexed.ts +++ b/src/lib/provable/merkle-tree-indexed.ts @@ -693,7 +693,7 @@ class Leaf extends Struct({ } static fromStored(leaf: StoredLeaf, sortedIndex: number) { - return { ...leaf, sortedIndex: sortedIndex }; + return { ...leaf, sortedIndex }; } } diff --git a/src/lib/provable/scalar-field.ts b/src/lib/provable/scalar-field.ts index 91c085a025..b1f3343ced 100644 --- a/src/lib/provable/scalar-field.ts +++ b/src/lib/provable/scalar-field.ts @@ -35,9 +35,7 @@ class ScalarField extends createForeignField(Fq.modulus) { if (s.lowBit.isConstant() && s.high254.isConstant()) { return new ScalarField(s.toBigInt()); } - const field = Provable.witness(ScalarField.provable, () => { - return s.toBigInt(); - }); + const field = Provable.witness(ScalarField, () => s.toBigInt()); const foreignField = new ScalarField(field); const scalar = foreignField.toScalar(); Provable.assertEqual(Scalar, s, scalar); diff --git a/src/lib/provable/test/custom-gates-recursion.unit-test.ts b/src/lib/provable/test/custom-gates-recursion.unit-test.ts index 7d1a06eb55..34ed06c60e 100644 --- a/src/lib/provable/test/custom-gates-recursion.unit-test.ts +++ b/src/lib/provable/test/custom-gates-recursion.unit-test.ts @@ -39,12 +39,9 @@ let program = ZkProgram({ privateInputs: [EmptyProof], async method(proof: EmptyProof) { proof.verify(); - let signature_ = Provable.witness( - Ecdsa.Signature.provable, - () => signature - ); - let msgHash_ = Provable.witness(Field3.provable, () => msgHash); - let publicKey_ = Provable.witness(Point.provable, () => publicKey); + let signature_ = Provable.witness(Ecdsa.Signature, () => signature); + let msgHash_ = Provable.witness(Field3, () => msgHash); + let publicKey_ = Provable.witness(Point, () => publicKey); return Ecdsa.verifyV2(Secp256k1, signature_, msgHash_, publicKey_); }, diff --git a/src/lib/provable/test/ecdsa.unit-test.ts b/src/lib/provable/test/ecdsa.unit-test.ts index 89545d9645..68067a60cb 100644 --- a/src/lib/provable/test/ecdsa.unit-test.ts +++ b/src/lib/provable/test/ecdsa.unit-test.ts @@ -147,12 +147,9 @@ let deprecatedVerify = ZkProgram({ ecdsa: { privateInputs: [], async method() { - let signature_ = Provable.witness( - Ecdsa.Signature.provable, - () => signature - ); - let msgHash_ = Provable.witness(Field3.provable, () => msgHash); - let publicKey_ = Provable.witness(Point.provable, () => publicKey); + let signature_ = Provable.witness(Ecdsa.Signature, () => signature); + let msgHash_ = Provable.witness(Field3, () => msgHash); + let publicKey_ = Provable.witness(Point, () => publicKey); return Ecdsa.verify( Secp256k1, @@ -173,12 +170,9 @@ let program = ZkProgram({ ecdsa: { privateInputs: [], async method() { - let signature_ = Provable.witness( - Ecdsa.Signature.provable, - () => signature - ); - let msgHash_ = Provable.witness(Field3.provable, () => msgHash); - let publicKey_ = Provable.witness(Point.provable, () => publicKey); + let signature_ = Provable.witness(Ecdsa.Signature, () => signature); + let msgHash_ = Provable.witness(Field3, () => msgHash); + let publicKey_ = Provable.witness(Point, () => publicKey); return Ecdsa.verifyV2( Secp256k1, diff --git a/src/lib/provable/test/foreign-curve.unit-test.ts b/src/lib/provable/test/foreign-curve.unit-test.ts index 24ce76d979..c7bdaf9c4d 100644 --- a/src/lib/provable/test/foreign-curve.unit-test.ts +++ b/src/lib/provable/test/foreign-curve.unit-test.ts @@ -14,15 +14,15 @@ let scalar = Field.random().toBigInt(); let p = V.toAffine(V.scale(V.fromAffine(h), scalar)); function main() { - let g0 = Provable.witness(Vesta.provable, () => new Vesta(g)); - let one = Provable.witness(Vesta.provable, () => Vesta.generator); + let g0 = Provable.witness(Vesta, () => g); + let one = Provable.witness(Vesta, () => Vesta.generator); let h0 = g0.add(one).double().negate(); Provable.assertEqual(Vesta.provable, h0, new Vesta(h)); h0.assertOnCurve(); h0.assertInSubgroup(); - let scalar0 = Provable.witness(Fp.provable, () => new Fp(scalar)); + let scalar0 = Provable.witness(Fp, () => scalar); let p0 = h0.scale(scalar0); Provable.assertEqual(Vesta.provable, p0, new Vesta(p)); } diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts index c4f4c517d1..211f8fd122 100644 --- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts +++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts @@ -306,8 +306,8 @@ await equivalentAsync({ from: [f, f], to: unit }, { runs })( function assertMulExample(x: Field3, y: Field3, f: bigint) { // witness x^2, y^2 - let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f)); - let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f)); + let x2 = Provable.witness(Field3, () => ForeignField.mul(x, x, f)); + let y2 = Provable.witness(Field3, () => ForeignField.mul(y, y, f)); // assert (x - y) * (x + y) = x^2 - y^2 let xMinusY = ForeignField.Sum(x).sub(y); @@ -318,8 +318,8 @@ function assertMulExample(x: Field3, y: Field3, f: bigint) { function assertMulExampleNaive(x: Field3, y: Field3, f: bigint) { // witness x^2, y^2 - let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f)); - let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f)); + let x2 = Provable.witness(Field3, () => ForeignField.mul(x, x, f)); + let y2 = Provable.witness(Field3, () => ForeignField.mul(y, y, f)); // assert (x - y) * (x + y) = x^2 - y^2 let lhs = ForeignField.mul( diff --git a/src/lib/provable/test/merkle-tree.unit-test.ts b/src/lib/provable/test/merkle-tree.unit-test.ts index 010706179e..8a0e180cc8 100644 --- a/src/lib/provable/test/merkle-tree.unit-test.ts +++ b/src/lib/provable/test/merkle-tree.unit-test.ts @@ -209,7 +209,7 @@ test( // pass the map to a circuit runAndCheckSync(() => { - map = Provable.witness(MerkleMap.provable, () => map); + map = Provable.witness(MerkleMap, () => map); let initialKeysF = initialKeys.map(witness); let keysF = keys.map(witness); let valuesF = values.map(witness); diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts index 69251ef0a3..2e86cac798 100644 --- a/src/lib/provable/types/witness.ts +++ b/src/lib/provable/types/witness.ts @@ -6,7 +6,7 @@ import { snarkContext, } from '../core/provable-context.js'; import { exists, existsAsync } from '../core/exists.js'; -import { From, InferValue } from '../../../bindings/lib/provable-generic.js'; +import { From } from '../../../bindings/lib/provable-generic.js'; import { TupleN } from '../../util/types.js'; import { createField } from '../core/field-constructor.js'; diff --git a/tests/vk-regression/plain-constraint-system.ts b/tests/vk-regression/plain-constraint-system.ts index 740abf170b..b0030807db 100644 --- a/tests/vk-regression/plain-constraint-system.ts +++ b/tests/vk-regression/plain-constraint-system.ts @@ -107,22 +107,22 @@ const bytes32 = Bytes32.from([]); const HashCS = constraintSystem('Hashes', { SHA256() { - let xs = Provable.witness(Bytes32.provable, () => bytes32); + let xs = Provable.witness(Bytes32, () => bytes32); Hash.SHA3_256.hash(xs); }, SHA384() { - let xs = Provable.witness(Bytes32.provable, () => bytes32); + let xs = Provable.witness(Bytes32, () => bytes32); Hash.SHA3_384.hash(xs); }, SHA512() { - let xs = Provable.witness(Bytes32.provable, () => bytes32); + let xs = Provable.witness(Bytes32, () => bytes32); Hash.SHA3_512.hash(xs); }, Keccak256() { - let xs = Provable.witness(Bytes32.provable, () => bytes32); + let xs = Provable.witness(Bytes32, () => bytes32); Hash.Keccak256.hash(xs); }, From 6cf7a65ebbaa616b103a23f41e21502e4e08c5ed Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:01:58 +0200 Subject: [PATCH 14/32] support no .provable in zkprogram public inputs --- src/lib/proof-system/zkprogram.ts | 42 +++++++++++++++------ src/lib/provable/types/provable-derivers.ts | 10 ++++- src/lib/provable/types/provable-intf.ts | 9 ++++- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 8a36c4559c..54644ddbc1 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -9,7 +9,11 @@ import { ProvablePureExtended, Struct, } from '../provable/types/struct.js'; -import { provable, provablePure } from '../provable/types/provable-derivers.js'; +import { + InferProvableType, + provable, + provablePure, +} from '../provable/types/provable-derivers.js'; import { Provable } from '../provable/provable.js'; import { assert, prettifyStacktracePromise } from '../util/errors.js'; import { snarkContext } from '../provable/core/provable-context.js'; @@ -34,7 +38,12 @@ import { setSrsCache, unsetSrsCache, } from '../../bindings/crypto/bindings/srs.js'; -import { ProvablePure } from '../provable/types/provable-intf.js'; +import { + ProvablePure, + ProvableType, + ProvableTypePure, + ToProvable, +} from '../provable/types/provable-intf.js'; import { prefixToField } from '../../bindings/lib/binable.js'; import { prefixes } from '../../bindings/crypto/constants.js'; @@ -530,8 +539,8 @@ let SideloadedTag = { function ZkProgram< StatementType extends { - publicInput?: FlexibleProvablePure<any>; - publicOutput?: FlexibleProvablePure<any>; + publicInput?: ProvableTypePure; + publicOutput?: ProvableTypePure; }, Types extends { // TODO: how to prevent a method called `compile` from type-checking? @@ -588,8 +597,12 @@ function ZkProgram< >; } { let methods = config.methods; - let publicInputType: ProvablePure<any> = config.publicInput ?? Undefined; - let publicOutputType: ProvablePure<any> = config.publicOutput ?? Void; + let publicInputType: ProvablePure<any> = ProvableType.getPure( + config.publicInput ?? Undefined + ); + let publicOutputType: ProvablePure<any> = ProvableType.getPure( + config.publicOutput ?? Void + ); let selfTag = { name: config.name }; type PublicInput = InferProvableOrUndefined< @@ -1470,13 +1483,18 @@ type Prover< ...args: TupleToInstances<Args> ) => Promise<Proof<PublicInput, PublicOutput>>; -type ProvableOrUndefined<A> = A extends undefined ? typeof Undefined : A; -type ProvableOrVoid<A> = A extends undefined ? typeof Void : A; +type ProvableOrUndefined<A extends ProvableType | undefined> = + A extends ProvableType ? ToProvable<A> : typeof Undefined; +type ProvableOrVoid<A extends ProvableType | undefined> = A extends ProvableType + ? ToProvable<A> + : typeof Void; + +type InferProvableOrUndefined<A extends ProvableType | undefined> = + A extends ProvableType ? InferProvableType<A> : undefined; -type InferProvableOrUndefined<A> = A extends undefined - ? undefined - : InferProvable<A>; -type InferProvableOrVoid<A> = A extends undefined ? void : InferProvable<A>; +type InferProvableOrVoid<A> = A extends ProvableType + ? InferProvableType<A> + : void; type UnwrapPromise<P> = P extends Promise<infer T> ? T : never; diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts index ff6f7720d9..5e2476b330 100644 --- a/src/lib/provable/types/provable-derivers.ts +++ b/src/lib/provable/types/provable-derivers.ts @@ -1,4 +1,10 @@ -import { Provable, ProvableHashable, ProvablePure } from './provable-intf.js'; +import { + Provable, + ProvableHashable, + ProvablePure, + ProvableType, + ToProvable, +} from './provable-intf.js'; import type { Field } from '../wrapped.js'; import { createDerivers, @@ -35,6 +41,7 @@ export { NonMethods, HashInput, InferProvable, + InferProvableType, InferJson, InferredProvable, IsPure, @@ -56,6 +63,7 @@ type ProvablePureExtended<T, TValue = any, TJson = any> = ProvablePure< ProvableExtension<T, TJson>; type InferProvable<T> = GenericInferProvable<T, Field>; +type InferProvableType<T extends ProvableType> = InferProvable<ToProvable<T>>; type InferredProvable<T> = GenericInferredProvable<T, Field>; type IsPure<T> = GenericIsPure<T, Field>; type ProvableInferPureFrom<A, T, V> = IsPure<A> extends true diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts index 100cf8df0c..08cd6bcf5b 100644 --- a/src/lib/provable/types/provable-intf.ts +++ b/src/lib/provable/types/provable-intf.ts @@ -6,6 +6,7 @@ export { ProvableWithEmpty, ProvableHashable, ProvableType, + ProvableTypePure, ToProvable, }; @@ -106,7 +107,10 @@ type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & { // helpers to accept { provable: Type } instead of Type -type ProvableType<T, V = any> = Provable<T, V> | { provable: Provable<T, V> }; +type SelfOrOnProperty<A> = { provable: A } | A; + +type ProvableType<T = any, V = any> = SelfOrOnProperty<Provable<T, V>>; +type ProvableTypePure<T = any, V = any> = SelfOrOnProperty<ProvablePure<T, V>>; type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P } ? P @@ -116,4 +120,7 @@ const ProvableType = { get<T, V>(type: ProvableType<T, V>): Provable<T, V> { return 'provable' in type ? type.provable : type; }, + getPure<T, V>(type: ProvableTypePure<T, V>): ProvablePure<T, V> { + return 'provable' in type ? type.provable : type; + }, }; From 7ba888e5556f7c82a988fff083cf6927ddcc0aa4 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:02:11 +0200 Subject: [PATCH 15/32] remove .provable in zkprogram public inputs --- src/examples/crypto/ecdsa/ecdsa.ts | 4 ++-- src/examples/crypto/sha256/sha256.ts | 2 +- .../test/foreign-field-gadgets.unit-test.ts | 2 +- src/lib/provable/test/keccak.unit-test.ts | 4 ++-- src/lib/provable/test/sha256.unit-test.ts | 2 +- src/tests/inductive-proofs-small.ts | 7 +++++-- src/tests/inductive-proofs.ts | 18 ++++++++++++------ 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts index 3d7c1a9d5c..19afc9f071 100644 --- a/src/examples/crypto/ecdsa/ecdsa.ts +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -16,7 +16,7 @@ class Bytes32 extends Bytes(32) {} const keccakAndEcdsa = ZkProgram({ name: 'ecdsa', - publicInput: Bytes32.provable, + publicInput: Bytes32, publicOutput: Bool, methods: { @@ -31,7 +31,7 @@ const keccakAndEcdsa = ZkProgram({ const ecdsa = ZkProgram({ name: 'ecdsa-only', - publicInput: Scalar.provable, + publicInput: Scalar, publicOutput: Bool, methods: { diff --git a/src/examples/crypto/sha256/sha256.ts b/src/examples/crypto/sha256/sha256.ts index a6e335fd7f..c8e83c6483 100644 --- a/src/examples/crypto/sha256/sha256.ts +++ b/src/examples/crypto/sha256/sha256.ts @@ -6,7 +6,7 @@ class Bytes12 extends Bytes(12) {} let SHA256Program = ZkProgram({ name: 'sha256', - publicOutput: Bytes(32).provable, + publicOutput: Bytes(32), methods: { sha256: { privateInputs: [Bytes12.provable], diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts index 211f8fd122..85154874d4 100644 --- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts +++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts @@ -173,7 +173,7 @@ let signs = [1n, -1n, -1n, 1n] satisfies (-1n | 1n)[]; let ffProgram = ZkProgram({ name: 'foreign-field', - publicOutput: Field3.provable, + publicOutput: Field3, methods: { sumchain: { privateInputs: [Provable.Array(Field3.provable, chainLength)], diff --git a/src/lib/provable/test/keccak.unit-test.ts b/src/lib/provable/test/keccak.unit-test.ts index 1678b0a231..92e9eb8854 100644 --- a/src/lib/provable/test/keccak.unit-test.ts +++ b/src/lib/provable/test/keccak.unit-test.ts @@ -121,8 +121,8 @@ const preImageLength = 32; // No need to test Ethereum because it's just a special case of preNist const KeccakProgram = ZkProgram({ name: `keccak-test-${digestLength}`, - publicInput: Bytes(preImageLength).provable, - publicOutput: Bytes(digestLengthBytes).provable, + publicInput: Bytes(preImageLength), + publicOutput: Bytes(digestLengthBytes), methods: { nistSha3: { privateInputs: [], diff --git a/src/lib/provable/test/sha256.unit-test.ts b/src/lib/provable/test/sha256.unit-test.ts index c18d661752..097c6fefdb 100644 --- a/src/lib/provable/test/sha256.unit-test.ts +++ b/src/lib/provable/test/sha256.unit-test.ts @@ -23,7 +23,7 @@ sample(Random.nat(400), 5).forEach((preimageLength) => { const Sha256Program = ZkProgram({ name: `sha256`, - publicOutput: Bytes(32).provable, + publicOutput: Bytes(32), methods: { sha256: { privateInputs: [Bytes(192).provable], diff --git a/src/tests/inductive-proofs-small.ts b/src/tests/inductive-proofs-small.ts index acde6a86f6..f20f7b4fc8 100644 --- a/src/tests/inductive-proofs-small.ts +++ b/src/tests/inductive-proofs-small.ts @@ -9,7 +9,7 @@ let MaxProofsVerifiedOne = ZkProgram({ baseCase: { privateInputs: [], - method(publicInput: Field) { + async method(publicInput: Field) { publicInput.assertEquals(Field(0)); }, }, @@ -17,7 +17,10 @@ let MaxProofsVerifiedOne = ZkProgram({ mergeOne: { privateInputs: [SelfProof], - method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) { + async method( + publicInput: Field, + earlierProof: SelfProof<Field, undefined> + ) { earlierProof.verify(); earlierProof.publicInput.add(1).assertEquals(publicInput); }, diff --git a/src/tests/inductive-proofs.ts b/src/tests/inductive-proofs.ts index 101487dae5..ebf22754f5 100644 --- a/src/tests/inductive-proofs.ts +++ b/src/tests/inductive-proofs.ts @@ -9,7 +9,7 @@ let MaxProofsVerifiedZero = ZkProgram({ baseCase: { privateInputs: [], - method(publicInput: Field) { + async method(publicInput: Field) { publicInput.assertEquals(Field(0)); }, }, @@ -24,7 +24,7 @@ let MaxProofsVerifiedOne = ZkProgram({ baseCase: { privateInputs: [], - method(publicInput: Field) { + async method(publicInput: Field) { publicInput.assertEquals(Field(0)); }, }, @@ -32,7 +32,10 @@ let MaxProofsVerifiedOne = ZkProgram({ mergeOne: { privateInputs: [SelfProof], - method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) { + async method( + publicInput: Field, + earlierProof: SelfProof<Field, undefined> + ) { earlierProof.verify(); earlierProof.publicInput.add(1).assertEquals(publicInput); }, @@ -48,7 +51,7 @@ let MaxProofsVerifiedTwo = ZkProgram({ baseCase: { privateInputs: [], - method(publicInput: Field) { + async method(publicInput: Field) { publicInput.assertEquals(Field(0)); }, }, @@ -56,7 +59,10 @@ let MaxProofsVerifiedTwo = ZkProgram({ mergeOne: { privateInputs: [SelfProof], - method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) { + async method( + publicInput: Field, + earlierProof: SelfProof<Field, undefined> + ) { earlierProof.verify(); earlierProof.publicInput.add(1).assertEquals(publicInput); }, @@ -65,7 +71,7 @@ let MaxProofsVerifiedTwo = ZkProgram({ mergeTwo: { privateInputs: [SelfProof, SelfProof], - method( + async method( publicInput: Field, p1: SelfProof<Field, undefined>, p2: SelfProof<Field, undefined> From aaf0263e624c5c8a60d4dd55b7fb6f34a1d78477 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:18:18 +0200 Subject: [PATCH 16/32] support no .provable in zkprogram private inputs --- src/lib/proof-system/zkprogram.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 54644ddbc1..11f183a342 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -544,6 +544,7 @@ function ZkProgram< }, Types extends { // TODO: how to prevent a method called `compile` from type-checking? + // TODO: solution: put method calls on a separate namespace! like `await program.prove.myMethod()` [I in string]: Tuple<PrivateInput>; } >( @@ -777,8 +778,8 @@ function ZkProgram< type ZkProgram< S extends { - publicInput?: FlexibleProvablePure<any>; - publicOutput?: FlexibleProvablePure<any>; + publicInput?: ProvableTypePure; + publicOutput?: ProvableTypePure; }, T extends { [I in string]: Tuple<PrivateInput>; @@ -1438,7 +1439,9 @@ function Prover<ProverData>() { type Infer<T> = T extends Subclass<typeof ProofBase> ? InstanceType<T> - : InferProvable<T>; + : T extends ProvableType + ? InferProvableType<T> + : never; type Tuple<T> = [T, ...T[]] | []; type TupleToInstances<T> = { @@ -1451,7 +1454,7 @@ type Subclass<Class extends new (...args: any) => any> = (new ( [K in keyof Class]: Class[K]; } & { prototype: InstanceType<Class> }; -type PrivateInput = Provable<any> | Subclass<typeof ProofBase>; +type PrivateInput = ProvableType | Subclass<typeof ProofBase>; type Method< PublicInput, From 9f6eec2f1b17529dc2050264842ffa5850aa4a52 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:18:30 +0200 Subject: [PATCH 17/32] remove .provable in zkprogram private inputs --- src/examples/crypto/ecdsa/ecdsa.ts | 4 ++-- src/examples/crypto/rsa/run.ts | 6 +----- src/examples/crypto/sha256/sha256.ts | 2 +- src/lib/provable/merkle-tree-indexed.ts | 2 +- src/lib/provable/test/arithmetic.unit-test.ts | 5 +---- .../provable/test/foreign-field-gadgets.unit-test.ts | 10 +++++----- src/lib/provable/test/sha256.unit-test.ts | 2 +- tests/vk-regression/diverse-zk-program.ts | 8 ++------ 8 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts index 19afc9f071..020dbf9d83 100644 --- a/src/examples/crypto/ecdsa/ecdsa.ts +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -21,7 +21,7 @@ const keccakAndEcdsa = ZkProgram({ methods: { verifyEcdsa: { - privateInputs: [Ecdsa.provable, Secp256k1.provable], + privateInputs: [Ecdsa, Secp256k1], async method(message: Bytes32, signature: Ecdsa, publicKey: Secp256k1) { return signature.verifyV2(message, publicKey); }, @@ -36,7 +36,7 @@ const ecdsa = ZkProgram({ methods: { verifySignedHash: { - privateInputs: [Ecdsa.provable, Secp256k1.provable], + privateInputs: [Ecdsa, Secp256k1], async method(message: Scalar, signature: Ecdsa, publicKey: Secp256k1) { return signature.verifySignedHashV2(message, publicKey); }, diff --git a/src/examples/crypto/rsa/run.ts b/src/examples/crypto/rsa/run.ts index c2c9b7ed53..3d27fb7ed9 100644 --- a/src/examples/crypto/rsa/run.ts +++ b/src/examples/crypto/rsa/run.ts @@ -7,11 +7,7 @@ let rsaZkProgram = ZkProgram({ methods: { verifyRsa65537: { - privateInputs: [ - Bigint2048.provable, - Bigint2048.provable, - Bigint2048.provable, - ], + privateInputs: [Bigint2048, Bigint2048, Bigint2048], async method( message: Bigint2048, diff --git a/src/examples/crypto/sha256/sha256.ts b/src/examples/crypto/sha256/sha256.ts index c8e83c6483..5ffe43c333 100644 --- a/src/examples/crypto/sha256/sha256.ts +++ b/src/examples/crypto/sha256/sha256.ts @@ -9,7 +9,7 @@ let SHA256Program = ZkProgram({ publicOutput: Bytes(32), methods: { sha256: { - privateInputs: [Bytes12.provable], + privateInputs: [Bytes12], async method(xs: Bytes12) { return Gadgets.SHA256.hash(xs); }, diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts index 4010ee309c..568a47798a 100644 --- a/src/lib/provable/merkle-tree-indexed.ts +++ b/src/lib/provable/merkle-tree-indexed.ts @@ -36,7 +36,7 @@ export { Leaf }; * ZkProgram({ * methods: { * test: { - * privateInputs: [MerkleMap.provable, Field], + * privateInputs: [MerkleMap, Field], * * method(map: MerkleMap, key: Field) { * // get the value associated with `key` diff --git a/src/lib/provable/test/arithmetic.unit-test.ts b/src/lib/provable/test/arithmetic.unit-test.ts index ec02667fc3..3ce9ce07f0 100644 --- a/src/lib/provable/test/arithmetic.unit-test.ts +++ b/src/lib/provable/test/arithmetic.unit-test.ts @@ -12,10 +12,7 @@ import { assert } from '../gadgets/common.js'; let Arithmetic = ZkProgram({ name: 'arithmetic', - publicOutput: provable({ - remainder: Field, - quotient: Field, - }), + publicOutput: provable({ remainder: Field, quotient: Field }), methods: { divMod32: { privateInputs: [Field], diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts index 85154874d4..460e84c611 100644 --- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts +++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts @@ -182,32 +182,32 @@ let ffProgram = ZkProgram({ }, }, mulWithBoundsCheck: { - privateInputs: [Field3.provable, Field3.provable], + privateInputs: [Field3, Field3], async method(x, y) { ForeignField.assertAlmostReduced([x, y], F.modulus); return ForeignField.mul(x, y, F.modulus); }, }, mul: { - privateInputs: [Field3.provable, Field3.provable], + privateInputs: [Field3, Field3], async method(x, y) { return ForeignField.mul(x, y, F.modulus); }, }, inv: { - privateInputs: [Field3.provable], + privateInputs: [Field3], async method(x) { return ForeignField.inv(x, F.modulus); }, }, div: { - privateInputs: [Field3.provable, Field3.provable], + privateInputs: [Field3, Field3], async method(x, y) { return ForeignField.div(x, y, F.modulus); }, }, assertLessThan: { - privateInputs: [Field3.provable, Field3.provable], + privateInputs: [Field3, Field3], async method(x, y) { ForeignField.assertLessThan(x, y); return x; diff --git a/src/lib/provable/test/sha256.unit-test.ts b/src/lib/provable/test/sha256.unit-test.ts index 097c6fefdb..16233721e9 100644 --- a/src/lib/provable/test/sha256.unit-test.ts +++ b/src/lib/provable/test/sha256.unit-test.ts @@ -26,7 +26,7 @@ const Sha256Program = ZkProgram({ publicOutput: Bytes(32), methods: { sha256: { - privateInputs: [Bytes(192).provable], + privateInputs: [Bytes(192)], async method(preImage: Bytes) { return Gadgets.SHA256.hash(preImage); }, diff --git a/tests/vk-regression/diverse-zk-program.ts b/tests/vk-regression/diverse-zk-program.ts index 69333903cf..08649dc844 100644 --- a/tests/vk-regression/diverse-zk-program.ts +++ b/tests/vk-regression/diverse-zk-program.ts @@ -32,11 +32,7 @@ const diverse = ZkProgram({ methods: { // foreign field / curve ops, multi-range checks ecdsa: { - privateInputs: [ - Secp256k1Scalar.provable, - Secp256k1Signature.provable, - Secp256k1.provable, - ], + privateInputs: [Secp256k1Scalar, Secp256k1Signature, Secp256k1], async method( message: Secp256k1Scalar, signature: Secp256k1Signature, @@ -48,7 +44,7 @@ const diverse = ZkProgram({ // bitwise gadgets sha3: { - privateInputs: [Bytes128.provable], + privateInputs: [Bytes128], async method(xs: Bytes128) { Hash.SHA3_256.hash(xs); }, From 46f017821ca920eb414dedc2e19729c5fc42a033 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:56:04 +0200 Subject: [PATCH 18/32] support no .provable in Provable utils --- src/lib/provable/provable.ts | 64 +++++++++++++++++++------------- src/lib/provable/types/struct.ts | 4 +- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/lib/provable/provable.ts b/src/lib/provable/provable.ts index 58f98565b7..456ea56007 100644 --- a/src/lib/provable/provable.ts +++ b/src/lib/provable/provable.ts @@ -5,13 +5,17 @@ */ import { Bool } from './bool.js'; import { Field } from './field.js'; -import type { Provable as Provable_ } from './types/provable-intf.js'; -import type { FlexibleProvable, ProvableExtended } from './types/struct.js'; +import { Provable as Provable_, ProvableType } from './types/provable-intf.js'; +import type { + FlexibleProvable, + FlexibleProvableType, + ProvableExtended, +} from './types/struct.js'; import { Context } from '../util/global-context.js'; import { HashInput, InferJson, - InferProvable, + InferProvableType, InferredProvable, } from './types/provable-derivers.js'; import { @@ -23,6 +27,7 @@ import { } from './core/provable-context.js'; import { witness, witnessAsync, witnessFields } from './types/witness.js'; import { InferValue } from '../../bindings/lib/provable-generic.js'; +import { ToProvable } from '../../lib/provable/types/provable-intf.js'; // external API export { Provable }; @@ -229,7 +234,8 @@ const Provable = { /** * Returns a constant version of a provable type. */ - toConstant<T>(type: Provable<T>, value: T) { + toConstant<T>(type: ProvableType<T>, value: T) { + type = ProvableType.get(type); return type.fromFields( type.toFields(value).map((x) => x.toConstant()), type.toAuxiliary(value) @@ -241,7 +247,7 @@ type ToFieldable = { toFields(): Field[] }; // general provable methods -function assertEqual<T>(type: FlexibleProvable<T>, x: T, y: T): void; +function assertEqual<T>(type: FlexibleProvableType<T>, x: T, y: T): void; function assertEqual<T extends ToFieldable>(x: T, y: T): void; function assertEqual(typeOrX: any, xOrY: any, yOrUndefined?: any) { if (yOrUndefined === undefined) { @@ -258,7 +264,8 @@ function assertEqualImplicit<T extends ToFieldable>(x: T, y: T) { xs[i].assertEquals(ys[i]); } } -function assertEqualExplicit<T>(type: Provable<T>, x: T, y: T) { +function assertEqualExplicit<T>(type: ProvableType<T>, x: T, y: T) { + type = ProvableType.get(type); let xs = type.toFields(x); let ys = type.toFields(y); for (let i = 0; i < xs.length; i++) { @@ -266,7 +273,7 @@ function assertEqualExplicit<T>(type: Provable<T>, x: T, y: T) { } } -function equal<T>(type: FlexibleProvable<T>, x: T, y: T): Bool; +function equal<T>(type: FlexibleProvableType<T>, x: T, y: T): Bool; function equal<T extends ToFieldable>(x: T, y: T): Bool; function equal(typeOrX: any, xOrY: any, yOrUndefined?: any) { if (yOrUndefined === undefined) { @@ -284,13 +291,14 @@ function equalImplicit<T extends ToFieldable>(x: T, y: T) { checkLength('Provable.equal', xs, ys); return xs.map((x, i) => x.equals(ys[i])).reduce(Bool.and); } -function equalExplicit<T>(type: Provable<T>, x: T, y: T) { +function equalExplicit<T>(type: ProvableType<T>, x: T, y: T) { + type = ProvableType.get(type); let xs = type.toFields(x); let ys = type.toFields(y); return xs.map((x, i) => x.equals(ys[i])).reduce(Bool.and); } -function if_<T>(condition: Bool, type: FlexibleProvable<T>, x: T, y: T): T; +function if_<T>(condition: Bool, type: FlexibleProvableType<T>, x: T, y: T): T; function if_<T extends ToFieldable>(condition: Bool, x: T, y: T): T; function if_(condition: Bool, typeOrX: any, xOrY: any, yOrUndefined?: any) { if (yOrUndefined === undefined) { @@ -312,7 +320,8 @@ function ifField(b: Field, x: Field, y: Field) { return b.mul(x.sub(y)).add(y).seal(); } -function ifExplicit<T>(condition: Bool, type: Provable<T>, x: T, y: T): T { +function ifExplicit<T>(condition: Bool, type: ProvableType<T>, x: T, y: T): T { + type = ProvableType.get(type); let xs = type.toFields(x); let ys = type.toFields(y); let b = condition.toField(); @@ -352,12 +361,13 @@ function ifImplicit<T extends ToFieldable>(condition: Bool, x: T, y: T): T { return ifExplicit(condition, type as any as Provable<T>, x, y); } -function switch_<T, A extends FlexibleProvable<T>>( +function switch_<T, A extends FlexibleProvableType<T>>( mask: Bool[], type: A, values: T[], { allowNonExclusive = false } = {} ): T { + let type_ = ProvableType.get(type as ProvableType<T>); // picks the value at the index where mask is true let nValues = values.length; if (mask.length !== nValues) @@ -375,35 +385,37 @@ function switch_<T, A extends FlexibleProvable<T>>( }; if (mask.every((b) => b.toField().isConstant())) checkMask(); else Provable.asProver(checkMask); - let size = type.sizeInFields(); + let size = type_.sizeInFields(); let fields = Array(size).fill(new Field(0)); for (let i = 0; i < nValues; i++) { - let valueFields = type.toFields(values[i]); + let valueFields = type_.toFields(values[i]); let maskField = mask[i].toField(); for (let j = 0; j < size; j++) { let maybeField = valueFields[j].mul(maskField); fields[j] = fields[j].add(maybeField); } } - let aux = auxiliary(type as Provable<T>, () => { + let aux = auxiliary(type_, () => { let i = mask.findIndex((b) => b.toBoolean()); if (i === -1) return undefined; return values[i]; }); - return (type as Provable<T>).fromFields(fields, aux); + return type_.fromFields(fields, aux); } function assertEqualIf< - A extends Provable<any>, - T extends InferProvable<A> = InferProvable<A> + A extends ProvableType<any>, + T extends InferProvableType<A> = InferProvableType<A> >(enabled: Bool, type: A, x: T, y: T) { // if the condition is disabled, we check the trivial identity x === x instead let xOrY = ifExplicit<T>(enabled, type, y, x); assertEqual(type, x, xOrY); } -function isConstant<T>(type: Provable<T>, x: T): boolean { - return type.toFields(x).every((x) => x.isConstant()); +function isConstant<T>(type: ProvableType<T>, x: T): boolean { + return ProvableType.get(type) + .toFields(x) + .every((x) => x.isConstant()); } // logging in provable code @@ -498,14 +510,16 @@ function getBlindingValue() { } // TODO this should return a class, like Struct, so you can just use `class Array3 extends Provable.Array(Field, 3) {}` -function provableArray<A extends FlexibleProvable<any>>( +function provableArray<A extends FlexibleProvableType<any>>( elementType: A, length: number -): InferredProvable<A[]> { - type T = InferProvable<A>; - type TValue = InferValue<A>; - type TJson = InferJson<A>; - let type = elementType as ProvableExtended<T, TValue, TJson>; +): InferredProvable<ToProvable<A>[]> { + type T = InferProvableType<A>; + type TValue = InferValue<ToProvable<A>>; + type TJson = InferJson<ToProvable<A>>; + let type = ProvableType.get( + elementType as ProvableType<T> + ) as ProvableExtended<T, TValue, TJson>; return { /** * Returns the size of this structure in {@link Field} elements. diff --git a/src/lib/provable/types/struct.ts b/src/lib/provable/types/struct.ts index 96d962d4e6..4569997eeb 100644 --- a/src/lib/provable/types/struct.ts +++ b/src/lib/provable/types/struct.ts @@ -13,7 +13,7 @@ import type { } from './provable-derivers.js'; import { Provable } from '../provable.js'; import { DynamicProof, Proof } from '../../proof-system/zkprogram.js'; -import { ProvablePure } from './provable-intf.js'; +import { ProvablePure, ProvableType } from './provable-intf.js'; import { From, InferValue } from '../../../bindings/lib/provable-generic.js'; // external API @@ -23,6 +23,7 @@ export { Struct, FlexibleProvable, FlexibleProvablePure, + FlexibleProvableType, }; // internal API @@ -59,6 +60,7 @@ type StructPure<T> = ProvablePureExtended<NonMethods<T>> & Constructor<T> & { _isStruct: true }; type FlexibleProvable<T> = Provable<T> | Struct<T>; type FlexibleProvablePure<T> = ProvablePure<T> | StructPure<T>; +type FlexibleProvableType<T> = ProvableType<T> | Struct<T>; type Constructor<T> = new (...args: any) => T; type AnyConstructor = Constructor<any>; From 6aa3cabbb9bbe8b66ba85fe8a7770bda0229d0d3 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 11:56:12 +0200 Subject: [PATCH 19/32] remove .provable in Provable utils --- src/examples/crypto/rsa/rsa.ts | 2 +- src/lib/mina/actions/batch-reducer.ts | 2 +- src/lib/mina/actions/offchain-state-rollup.ts | 8 ++------ src/lib/provable/crypto/foreign-curve.ts | 2 +- src/lib/provable/foreign-field.ts | 8 ++------ src/lib/provable/gadgets/elliptic-curve.ts | 15 +++++++-------- src/lib/provable/test/foreign-curve.unit-test.ts | 4 ++-- .../test/foreign-field-gadgets.unit-test.ts | 4 ++-- src/lib/provable/test/merkle-tree.unit-test.ts | 2 +- 9 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts index 78de61b6b9..50db5691e0 100644 --- a/src/examples/crypto/rsa/rsa.ts +++ b/src/examples/crypto/rsa/rsa.ts @@ -170,7 +170,7 @@ function rsaVerify65537( x = modulus.modMul(x, signature); // check that x == message - Provable.assertEqual(Bigint2048.provable, message, x); + Provable.assertEqual(Bigint2048, message, x); } /** diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts index b02351c25e..17f2a49db4 100644 --- a/src/lib/mina/actions/batch-reducer.ts +++ b/src/lib/mina/actions/batch-reducer.ts @@ -385,7 +385,7 @@ class BatchReducer< // we make it easier to write the reducer code by making sure dummy actions have dummy values hashedAction = Provable.if( isDummy, - HashedActionT.provable, + HashedActionT, emptyHashedAction, hashedAction ); diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts index 133d5d75bb..411606a819 100644 --- a/src/lib/mina/actions/offchain-state-rollup.ts +++ b/src/lib/mina/actions/offchain-state-rollup.ts @@ -169,7 +169,7 @@ function OffchainStateRollup({ */ firstBatch: { // [actions, tree] - privateInputs: [ActionIterator.provable, IndexedMerkleMapN.provable], + privateInputs: [ActionIterator, IndexedMerkleMapN], async method( stateA: OffchainStateCommitments, @@ -189,11 +189,7 @@ function OffchainStateRollup({ */ nextBatch: { // [actions, tree, proof] - privateInputs: [ - ActionIterator.provable, - IndexedMerkleMapN.provable, - SelfProof, - ], + privateInputs: [ActionIterator, IndexedMerkleMapN, SelfProof], async method( stateA: OffchainStateCommitments, diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 323478b87a..855e3e9514 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -97,7 +97,7 @@ class ForeignCurve { * See {@link FieldVar} to understand constants vs variables. */ isConstant() { - return Provable.isConstant(this.Constructor.provable, this); + return Provable.isConstant(this.Constructor, this); } /** diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 1c743713df..4f6a736c5a 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -303,11 +303,7 @@ class ForeignField { } return new this.Constructor.Canonical(this.value); } - Provable.assertEqual( - this.Constructor.provable, - this, - new this.Constructor(y) - ); + Provable.assertEqual(this.Constructor, this, new this.Constructor(y)); if (isConstant(y) || y instanceof this.Constructor.Canonical) { return new this.Constructor.Canonical(this.value); } else if (y instanceof this.Constructor.AlmostReduced) { @@ -612,7 +608,7 @@ function isConstant(x: bigint | number | string | ForeignField) { * * This function returns the `Unreduced` class, which will cause the minimum amount of range checks to be created by default. * If you want to do multiplication, you have two options: - * - create your field elements using the {@link ForeignField.AlmostReduced} constructor, or using the `.provable` type on that class. + * - create your field elements using the {@link ForeignField.AlmostReduced} constructor. * ```ts * let x = Provable.witness(ForeignField.AlmostReduced, () => 5n); * ``` diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts index afab631f6e..2b70b82b87 100644 --- a/src/lib/provable/gadgets/elliptic-curve.ts +++ b/src/lib/provable/gadgets/elliptic-curve.ts @@ -281,7 +281,7 @@ function verifyEcdsaGeneric( // if we allowed non-canonical Rx, the prover could make verify() return false on a valid signature, by adding a multiple of `Curve.order` to Rx. ForeignField.assertLessThan(Rx, Curve.order); - return Provable.equal(Field3.provable, Rx, r); + return Provable.equal(Field3, Rx, r); } /** @@ -545,7 +545,7 @@ function multiScalarMul( let added = add(sum, sjP, Curve); // handle degenerate case (if sj = 0, Gj is all zeros and the add result is garbage) - sum = Provable.if(sj.equals(0), Point.provable, sum, added); + sum = Provable.if(sj.equals(0), Point, sum, added); } } @@ -576,7 +576,7 @@ function multiScalarMul( function negateIf(condition: Field, P: Point, f: bigint) { let y = Provable.if( Bool.Unsafe.fromField(condition), - Field3.provable, + Field3, ForeignField.negate(P.y, f), P.y ); @@ -621,13 +621,13 @@ function decomposeNoRangeCheck(Curve: CurveAffine, s: Field3) { // prove that s1*lambda = s - s0 let lambda = Provable.if( Bool.Unsafe.fromField(s1Negative), - Field3.provable, + Field3, Field3.from(Curve.Scalar.negate(Curve.Endo.scalar)), Field3.from(Curve.Endo.scalar) ); let rhs = Provable.if( Bool.Unsafe.fromField(s0Negative), - Field3.provable, + Field3, ForeignField.Sum(s).add(s0).finish(Curve.order), ForeignField.Sum(s).sub(s0).finish(Curve.order) ); @@ -768,7 +768,7 @@ const Point = { toBigint({ x, y }: Point) { return { x: Field3.toBigint(x), y: Field3.toBigint(y), infinity: false }; }, - isConstant: (P: Point) => Provable.isConstant(Point.provable, P), + isConstant: (P: Point) => Provable.isConstant(Point, P), /** * Random point on the curve. @@ -787,8 +787,7 @@ const EcdsaSignature = { toBigint({ r, s }: Ecdsa.Signature): Ecdsa.signature { return { r: Field3.toBigint(r), s: Field3.toBigint(s) }; }, - isConstant: (S: Ecdsa.Signature) => - Provable.isConstant(EcdsaSignature.provable, S), + isConstant: (S: Ecdsa.Signature) => Provable.isConstant(EcdsaSignature, S), /** * Create an {@link EcdsaSignature} from a raw 130-char hex string as used in diff --git a/src/lib/provable/test/foreign-curve.unit-test.ts b/src/lib/provable/test/foreign-curve.unit-test.ts index c7bdaf9c4d..80921d0ea0 100644 --- a/src/lib/provable/test/foreign-curve.unit-test.ts +++ b/src/lib/provable/test/foreign-curve.unit-test.ts @@ -17,14 +17,14 @@ function main() { let g0 = Provable.witness(Vesta, () => g); let one = Provable.witness(Vesta, () => Vesta.generator); let h0 = g0.add(one).double().negate(); - Provable.assertEqual(Vesta.provable, h0, new Vesta(h)); + Provable.assertEqual(Vesta, h0, new Vesta(h)); h0.assertOnCurve(); h0.assertInSubgroup(); let scalar0 = Provable.witness(Fp, () => scalar); let p0 = h0.scale(scalar0); - Provable.assertEqual(Vesta.provable, p0, new Vesta(p)); + Provable.assertEqual(Vesta, p0, new Vesta(p)); } console.time('running constant version'); diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts index 460e84c611..d5ec5724ba 100644 --- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts +++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts @@ -176,7 +176,7 @@ let ffProgram = ZkProgram({ publicOutput: Field3, methods: { sumchain: { - privateInputs: [Provable.Array(Field3.provable, chainLength)], + privateInputs: [Provable.Array(Field3, chainLength)], async method(xs) { return ForeignField.sum(xs, signs, F.modulus); }, @@ -328,7 +328,7 @@ function assertMulExampleNaive(x: Field3, y: Field3, f: bigint) { f ); let rhs = ForeignField.sub(x2, y2, f); - Provable.assertEqual(Field3.provable, lhs, rhs); + Provable.assertEqual(Field3, lhs, rhs); } let from2 = { from: [f, f] satisfies AnyTuple }; diff --git a/src/lib/provable/test/merkle-tree.unit-test.ts b/src/lib/provable/test/merkle-tree.unit-test.ts index 8a0e180cc8..427fdfef53 100644 --- a/src/lib/provable/test/merkle-tree.unit-test.ts +++ b/src/lib/provable/test/merkle-tree.unit-test.ts @@ -249,7 +249,7 @@ test( // move the map back to constants Provable.asProver(() => { - map = Provable.toConstant(MerkleMap.provable, map); + map = Provable.toConstant(MerkleMap, map); }); }); From 161e92cf72380939ef07b2caf6d644105ca32773 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 12:52:52 +0200 Subject: [PATCH 20/32] accept no .provable in merkle list and some other generic provable types --- src/lib/proof-system/zkprogram.ts | 4 ++-- src/lib/provable/gadgets/elliptic-curve.ts | 14 +++++------ src/lib/provable/merkle-list.ts | 28 ++++++++++++++-------- src/lib/provable/packed.ts | 23 +++++++++++------- src/lib/provable/types/provable-intf.ts | 24 ++++++++++++------- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 11f183a342..4f9057ba1b 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -598,10 +598,10 @@ function ZkProgram< >; } { let methods = config.methods; - let publicInputType: ProvablePure<any> = ProvableType.getPure( + let publicInputType: ProvablePure<any> = ProvableType.get( config.publicInput ?? Undefined ); - let publicOutputType: ProvablePure<any> = ProvableType.getPure( + let publicOutputType: ProvablePure<any> = ProvableType.get( config.publicOutput ?? Void ); diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts index 2b70b82b87..91806c9e62 100644 --- a/src/lib/provable/gadgets/elliptic-curve.ts +++ b/src/lib/provable/gadgets/elliptic-curve.ts @@ -21,6 +21,7 @@ import { arrayGet, assertNotVectorEquals } from './basic.js'; import { sliceField3 } from './bit-slices.js'; import { Hashed } from '../packed.js'; import { exists } from '../core/exists.js'; +import { ProvableType } from '../types/provable-intf.js'; // external API export { EllipticCurve, Point, Ecdsa }; @@ -502,7 +503,7 @@ function multiScalarMul( // hash points to make array access more efficient // a Point is 6 field elements, the hash is just 1 field element - const HashedPoint = Hashed.create(Point.provable); + const HashedPoint = Hashed.create(Point); // initialize sum to the initial aggregator, which is expected to be unrelated to any point that this gadget is used with // note: this is a trick to ensure _completeness_ of the gadget @@ -529,16 +530,12 @@ function multiScalarMul( sjP = windowSize === 1 ? points[j] - : arrayGetGeneric( - HashedPoint.provable, - hashedTables[j], - sj - ).unhash(); + : arrayGetGeneric(HashedPoint, hashedTables[j], sj).unhash(); } else { sjP = windowSize === 1 ? points[j] - : arrayGetGeneric(Point.provable, tables[j], sj); + : arrayGetGeneric(Point, tables[j], sj); } // ec addition @@ -743,7 +740,8 @@ function simpleMapToCurve(x: bigint, Curve: CurveAffine) { * * Assumes that index is in [0, n), returns an unconstrained result otherwise. */ -function arrayGetGeneric<T>(type: Provable<T>, array: T[], index: Field) { +function arrayGetGeneric<T>(type: ProvableType<T>, array: T[], index: Field) { + type = ProvableType.get(type); // witness result let a = Provable.witness(type, () => array[Number(index)]); let aFields = type.toFields(a); diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 839501f573..7222907c00 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -5,6 +5,7 @@ import { assert } from './gadgets/common.js'; import { provableFromClass } from './types/provable-derivers.js'; import { Poseidon, packToFields, ProvableHashable } from './crypto/poseidon.js'; import { Unconstrained } from './types/unconstrained.js'; +import { ProvableType, WithProvable } from './types/provable-intf.js'; export { MerkleListBase, @@ -280,8 +281,10 @@ class MerkleList<T> implements MerkleListBase<T> { * ``` */ static create<T>( - type: ProvableHashable<T>, - nextHash: (hash: Field, value: T) => Field = merkleListHash(type), + type: WithProvable<ProvableHashable<T>>, + nextHash: (hash: Field, value: T) => Field = merkleListHash( + ProvableType.get(type) + ), emptyHash_ = emptyHash ): typeof MerkleList<T> & { // override static methods with strict types @@ -290,8 +293,10 @@ class MerkleList<T> implements MerkleListBase<T> { fromReverse: (array: T[]) => MerkleList<T>; provable: ProvableHashable<MerkleList<T>>; } { + let provable = ProvableType.get(type); + class MerkleListTBase extends MerkleList<T> { - static _innerProvable = type; + static _innerProvable = provable; static _provable = provableFromClass(MerkleListTBase, { hash: Field, @@ -309,7 +314,7 @@ class MerkleList<T> implements MerkleListBase<T> { array = [...array].reverse(); let { hash, data } = withHashes(array, nextHash, emptyHash_); let unconstrained = Unconstrained.witness(() => - data.map((x) => toConstant(type, x)) + data.map((x) => toConstant(provable, x)) ); return new this({ data: unconstrained, hash }); } @@ -317,7 +322,7 @@ class MerkleList<T> implements MerkleListBase<T> { static fromReverse(array: T[]): MerkleList<T> { let { hash, data } = withHashes(array, nextHash, emptyHash_); let unconstrained = Unconstrained.witness(() => - data.map((x) => toConstant(type, x)) + data.map((x) => toConstant(provable, x)) ); return new this({ data: unconstrained, hash }); } @@ -618,8 +623,10 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> { * Create a Merkle array type */ static create<T>( - type: ProvableHashable<T>, - nextHash: (hash: Field, value: T) => Field = merkleListHash(type), + type: WithProvable<ProvableHashable<T>>, + nextHash: (hash: Field, value: T) => Field = merkleListHash( + ProvableType.get(type) + ), emptyHash_ = emptyHash ): typeof MerkleListIterator<T> & { from: (array: T[]) => MerkleListIterator<T>; @@ -628,8 +635,9 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> { empty: () => MerkleListIterator<T>; provable: ProvableHashable<MerkleListIterator<T>>; } { + let provable = ProvableType.get(type); return class Iterator extends MerkleListIterator<T> { - static _innerProvable = type; + static _innerProvable = ProvableType.get(provable); static _provable = provableFromClass(Iterator, { hash: Field, @@ -646,7 +654,7 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> { static from(array: T[]): MerkleListIterator<T> { let { hash, data } = withHashes(array, nextHash, emptyHash_); let unconstrained = Unconstrained.witness(() => - data.map((x) => toConstant(type, x)) + data.map((x) => toConstant(provable, x)) ); return this.startIterating({ data: unconstrained, hash }); } @@ -655,7 +663,7 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> { array = [...array].reverse(); let { hash, data } = withHashes(array, nextHash, emptyHash_); let unconstrained = Unconstrained.witness(() => - data.map((x) => toConstant(type, x)) + data.map((x) => toConstant(provable, x)) ); return this.startIteratingFromLast({ data: unconstrained, hash }); } diff --git a/src/lib/provable/packed.ts b/src/lib/provable/packed.ts index aa59809ff0..f7cf395736 100644 --- a/src/lib/provable/packed.ts +++ b/src/lib/provable/packed.ts @@ -6,6 +6,7 @@ import { assert } from './gadgets/common.js'; import { Poseidon, ProvableHashable, packToFields } from './crypto/poseidon.js'; import { Provable } from './provable.js'; import { fields, modifiedField } from './types/fields.js'; +import { ProvableType, WithProvable } from './types/provable-intf.js'; export { Packed, Hashed }; @@ -49,22 +50,25 @@ class Packed<T> { /** * Create a packed representation of `type`. You can then use `PackedType.pack(x)` to pack a value. */ - static create<T>(type: ProvableExtended<T>): typeof Packed<T> & { + static create<T>( + type: WithProvable<ProvableExtended<T>> + ): typeof Packed<T> & { provable: ProvableHashable<Packed<T>>; } { + let provable = ProvableType.get(type); // compute size of packed representation - let input = type.toInput(type.empty()); + let input = provable.toInput(provable.empty()); let packedSize = countFields(input); return class Packed_ extends Packed<T> { - static _innerProvable = type; + static _innerProvable = provable; static _provable = provableFromClass(Packed_, { packed: fields(packedSize), value: Unconstrained.provable, }) as ProvableHashable<Packed<T>>; static empty(): Packed<T> { - return Packed_.pack(type.empty()); + return Packed_.pack(provable.empty()); } static get provable() { @@ -179,25 +183,26 @@ class Hashed<T> { * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`. */ static create<T>( - type: ProvableHashable<T>, + type: WithProvable<ProvableHashable<T>>, hash?: (t: T) => Field ): typeof Hashed<T> & { provable: ProvableHashable<Hashed<T>>; empty(): Hashed<T>; } { - let _hash = hash ?? ((t: T) => Poseidon.hashPacked(type, t)); + let provable = ProvableType.get(type); + let _hash = hash ?? ((t: T) => Poseidon.hashPacked(provable, t)); return class Hashed_ extends Hashed<T> { - static _innerProvable = type; + static _innerProvable = provable; static _provable = provableFromClass(Hashed_, { - hash: modifiedField({ empty: () => _hash(type.empty()) }), + hash: modifiedField({ empty: () => _hash(provable.empty()) }), value: Unconstrained.provable, }) as ProvableHashable<Hashed<T>>; static _hash = _hash satisfies (t: T) => Field; static empty(): Hashed<T> { - let empty = type.empty(); + let empty = provable.empty(); return new this(_hash(empty), Unconstrained.from(empty)); } diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts index 08cd6bcf5b..0a1b582017 100644 --- a/src/lib/provable/types/provable-intf.ts +++ b/src/lib/provable/types/provable-intf.ts @@ -8,6 +8,7 @@ export { ProvableType, ProvableTypePure, ToProvable, + WithProvable, }; /** @@ -107,20 +108,25 @@ type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & { // helpers to accept { provable: Type } instead of Type -type SelfOrOnProperty<A> = { provable: A } | A; +type WithProvable<A> = { provable: A } | A; -type ProvableType<T = any, V = any> = SelfOrOnProperty<Provable<T, V>>; -type ProvableTypePure<T = any, V = any> = SelfOrOnProperty<ProvablePure<T, V>>; +type ProvableType<T = any, V = any> = WithProvable<Provable<T, V>>; +type ProvableTypePure<T = any, V = any> = WithProvable<ProvablePure<T, V>>; -type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P } +type ToProvable<A extends WithProvable<any>> = A extends { + provable: infer P; +} ? P : A; const ProvableType = { - get<T, V>(type: ProvableType<T, V>): Provable<T, V> { - return 'provable' in type ? type.provable : type; - }, - getPure<T, V>(type: ProvableTypePure<T, V>): ProvablePure<T, V> { - return 'provable' in type ? type.provable : type; + get<A extends WithProvable<any>>(type: A): ToProvable<A> { + return ( + (typeof type === 'object' || typeof type === 'function') && + type !== null && + 'provable' in type + ? type.provable + : type + ) as ToProvable<A>; }, }; From ca43a435db62f3dc3e29139833c02c7ceefd9d6e Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 12:53:23 +0200 Subject: [PATCH 21/32] remove .provable in merkle lists --- src/lib/mina/actions/action-types.ts | 10 +++------- src/lib/mina/actions/offchain-state-rollup.ts | 4 ++-- src/lib/mina/actions/offchain-state-serialization.ts | 2 +- src/lib/mina/actions/reducer.ts | 6 +++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/lib/mina/actions/action-types.ts b/src/lib/mina/actions/action-types.ts index 604e344f51..7048edc321 100644 --- a/src/lib/mina/actions/action-types.ts +++ b/src/lib/mina/actions/action-types.ts @@ -23,7 +23,7 @@ function MerkleActions<A extends Actionable<any>>( fromActionState?: Field ) { return MerkleList.create( - MerkleActionList(actionType).provable, + MerkleActionList(actionType), (hash, actions) => hashWithPrefix(prefixes.sequenceEvents, [hash, actions.hash]), fromActionState ?? emptyActionState @@ -35,7 +35,7 @@ type MerkleActionList<T> = MerkleList<Hashed<T>>; function MerkleActionList<A extends Actionable<any>>(actionType: A) { return MerkleList.create( - HashedAction(actionType).provable, + HashedAction(actionType), (hash, action) => hashWithPrefix(prefixes.sequenceEvents, [hash, action.hash]), emptyActionsHash @@ -92,9 +92,5 @@ function MerkleActionHashes(fromActionState?: Field) { type FlatActions<T> = MerkleList<Hashed<T>>; function FlatActions<A extends Actionable<any>>(actionType: A) { - const HashedAction = Hashed.create( - actionType as Actionable<InferProvable<A>>, - (action) => hashWithPrefix(prefixes.event, actionType.toFields(action)) - ); - return MerkleList.create(HashedAction.provable); + return MerkleList.create(HashedAction(actionType)); } diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts index 411606a819..4335c46c18 100644 --- a/src/lib/mina/actions/offchain-state-rollup.ts +++ b/src/lib/mina/actions/offchain-state-rollup.ts @@ -22,7 +22,7 @@ import { getProofsEnabled } from '../mina.js'; export { OffchainStateRollup, OffchainStateCommitments }; class ActionIterator extends MerkleListIterator.create( - ActionList.provable, + ActionList, (hash: Field, actions: ActionList) => Actions.updateSequenceState(hash, actions.hash), // we don't have to care about the initial hash here because we will just step forward @@ -319,7 +319,7 @@ function OffchainStateRollup({ // also moves the original iterator forward to start after the slice function sliceActions(actions: ActionIterator, batchSize: number) { class ActionListsList extends MerkleList.create( - ActionList.provable, + ActionList, (hash: Field, actions: ActionList) => Actions.updateSequenceState(hash, actions.hash), actions.currentHash diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts index e939a49bf4..c7a66be90a 100644 --- a/src/lib/mina/actions/offchain-state-serialization.ts +++ b/src/lib/mina/actions/offchain-state-serialization.ts @@ -230,7 +230,7 @@ async function fetchMerkleLeaves( } ): Promise<MerkleList<MerkleList<MerkleLeaf>>> { class MerkleActions extends MerkleList.create( - ActionList.provable, + ActionList, (hash: Field, actions: ActionList) => Actions.updateSequenceState(hash, actions.hash), // if no "start" action hash was specified, this means we are fetching the entire history of actions, which started from the empty action state hash diff --git a/src/lib/mina/actions/reducer.ts b/src/lib/mina/actions/reducer.ts index f006244f78..106bb9cb11 100644 --- a/src/lib/mina/actions/reducer.ts +++ b/src/lib/mina/actions/reducer.ts @@ -65,8 +65,8 @@ type ReducerReturn<Action> = { * ); * ``` * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. */ reduce<State>( @@ -262,7 +262,7 @@ class ${contract.constructor.name} extends SmartContract { ) {} class MerkleActions extends MerkleList.create( - ActionList.provable, + ActionList, (hash: Field, actions: ActionList) => Actions.updateSequenceState(hash, actions.hash), // if no "start" action hash was specified, this means we are fetching the entire history of actions, which started from the empty action state hash From b77ce2a49e2f91644051f9d4e025f13b213a9eac Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 13:16:18 +0200 Subject: [PATCH 22/32] handle .provable in provableClass and nested types --- src/bindings | 2 +- src/lib/provable/crypto/foreign-curve.ts | 10 ++-------- src/lib/provable/crypto/foreign-ecdsa.ts | 8 ++++---- src/lib/provable/merkle-list.ts | 10 ++++++---- src/lib/provable/packed.ts | 8 ++++---- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/bindings b/src/bindings index 0609a32ba8..da12fd9782 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 0609a32ba8e9ca53a9c72d8166afffedab473c5a +Subproject commit da12fd9782abd699f3f5e1225963d735c0c55dfe diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 855e3e9514..af2604f53c 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -318,10 +318,7 @@ function createForeignCurve(params: CurveParams): typeof ForeignCurve { static _Bigint = BigintCurve; static _Field = Field; static _Scalar = Scalar; - static _provable = provableFromClass(Curve, { - x: Field.provable, - y: Field.provable, - }); + static _provable = provableFromClass(Curve, { x: Field, y: Field }); } return Curve; @@ -354,10 +351,7 @@ function createForeignCurveV2(params: CurveParams): typeof ForeignCurveV2 { static _Bigint = BigintCurve; static _Field = Field; static _Scalar = Scalar; - static _provable = provableFromClass(Curve, { - x: Field.provable, - y: Field.provable, - }); + static _provable = provableFromClass(Curve, { x: Field, y: Field }); } return Curve; diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index bd97164050..300da146c2 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -245,8 +245,8 @@ function createEcdsa( class Signature extends EcdsaSignature { static _Curve = Curve; static _provable = provableFromClass(Signature, { - r: Curve.Scalar.provable, - s: Curve.Scalar.provable, + r: Curve.Scalar, + s: Curve.Scalar, }); } @@ -266,8 +266,8 @@ function createEcdsaV2( class Signature extends EcdsaSignatureV2 { static _Curve = Curve; static _provable = provableFromClass(Signature, { - r: Curve.Scalar.provable, - s: Curve.Scalar.provable, + r: Curve.Scalar, + s: Curve.Scalar, }); } diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 7222907c00..79208b2bd4 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -300,8 +300,10 @@ class MerkleList<T> implements MerkleListBase<T> { static _provable = provableFromClass(MerkleListTBase, { hash: Field, - data: Unconstrained.provable, - }) as ProvableHashable<MerkleList<T>>; + data: Unconstrained, + }) satisfies ProvableHashable<MerkleList<T>> as ProvableHashable< + MerkleList<T> + >; static _nextHash = nextHash; static _emptyHash = emptyHash_; @@ -641,9 +643,9 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> { static _provable = provableFromClass(Iterator, { hash: Field, - data: Unconstrained.provable, + data: Unconstrained, currentHash: Field, - currentIndex: Unconstrained.provable, + currentIndex: Unconstrained, }) satisfies ProvableHashable<MerkleListIterator<T>> as ProvableHashable< MerkleListIterator<T> >; diff --git a/src/lib/provable/packed.ts b/src/lib/provable/packed.ts index f7cf395736..e1b4cfe682 100644 --- a/src/lib/provable/packed.ts +++ b/src/lib/provable/packed.ts @@ -64,8 +64,8 @@ class Packed<T> { static _innerProvable = provable; static _provable = provableFromClass(Packed_, { packed: fields(packedSize), - value: Unconstrained.provable, - }) as ProvableHashable<Packed<T>>; + value: Unconstrained, + }) satisfies ProvableHashable<Packed<T>> as ProvableHashable<Packed<T>>; static empty(): Packed<T> { return Packed_.pack(provable.empty()); @@ -196,8 +196,8 @@ class Hashed<T> { static _innerProvable = provable; static _provable = provableFromClass(Hashed_, { hash: modifiedField({ empty: () => _hash(provable.empty()) }), - value: Unconstrained.provable, - }) as ProvableHashable<Hashed<T>>; + value: Unconstrained, + }) satisfies ProvableHashable<Hashed<T>> as ProvableHashable<Hashed<T>>; static _hash = _hash satisfies (t: T) => Field; From 30c454e9c226fad556b2ca47c6706e7ad80bec79 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 13:48:35 +0200 Subject: [PATCH 23/32] unrelated fix --- src/lib/provable/gadgets/foreign-field.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/provable/gadgets/foreign-field.ts b/src/lib/provable/gadgets/foreign-field.ts index 611189c18a..db48d1c4f1 100644 --- a/src/lib/provable/gadgets/foreign-field.ts +++ b/src/lib/provable/gadgets/foreign-field.ts @@ -432,11 +432,7 @@ function equals(x: Field3, c: bigint, f: bigint) { } } -const provableLimb = modifiedField({ - toInput(x) { - return { packed: [[x, Number(l)]] }; - }, -}); +const provableLimb = modifiedField({}); const Field3 = { /** From 38d8feafbd07930bfd636e801a1aea7aeaf3ebae Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 13:53:32 +0200 Subject: [PATCH 24/32] handle .provable in struct and provable() --- src/bindings | 2 +- src/lib/mina/account-update.ts | 6 +++--- src/lib/mina/actions/batch-reducer.ts | 2 +- src/lib/mina/actions/offchain-state-rollup.ts | 2 +- src/lib/mina/token/forest-iterator.ts | 2 +- src/lib/mina/zkapp.ts | 2 +- src/lib/provable/gadgets/elliptic-curve.ts | 4 ++-- src/lib/provable/merkle-list.ts | 2 +- src/lib/provable/types/unconstrained.ts | 1 + 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/bindings b/src/bindings index da12fd9782..671219a155 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit da12fd9782abd699f3f5e1225963d735c0c55dfe +Subproject commit 671219a155311b8335db535596b4dfa3b61fef11 diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts index 3b45304b5f..5c5ca57250 100644 --- a/src/lib/mina/account-update.ts +++ b/src/lib/mina/account-update.ts @@ -1357,7 +1357,7 @@ type AccountUpdateForestBase = MerkleListBase<AccountUpdateTreeBase>; const AccountUpdateTreeBase = StructNoJson({ id: RandomId, - accountUpdate: HashedAccountUpdate.provable, + accountUpdate: HashedAccountUpdate, children: MerkleListBase<AccountUpdateTreeBase>(), }); @@ -1467,8 +1467,8 @@ class AccountUpdateForest extends MerkleList.create( */ class AccountUpdateTree extends StructNoJson({ id: RandomId, - accountUpdate: HashedAccountUpdate.provable, - children: AccountUpdateForest.provable, + accountUpdate: HashedAccountUpdate, + children: AccountUpdateForest, }) { /** * Create a tree of account updates which only consists of a root. diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts index 17f2a49db4..cf7c26da0e 100644 --- a/src/lib/mina/actions/batch-reducer.ts +++ b/src/lib/mina/actions/batch-reducer.ts @@ -549,7 +549,7 @@ function ActionBatch<A extends Actionable<any>>(actionType: A) { processedActionState: Field, onchainActionState: Field, onchainStack: Field, - stack: MerkleActions(actionType).provable, + stack: MerkleActions(actionType), isRecursive: Bool, witnesses: Unconstrained.provableWithEmpty<ActionWitnesses>([]), }); diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts index 4335c46c18..d69595ad7b 100644 --- a/src/lib/mina/actions/offchain-state-rollup.ts +++ b/src/lib/mina/actions/offchain-state-rollup.ts @@ -42,7 +42,7 @@ class OffchainStateCommitments extends Struct({ root: Field, length: Field, // TODO: make zkprogram support auxiliary data in public inputs - // actionState: ActionIterator.provable, + // actionState: ActionIterator, actionState: Field, }) { static emptyFromHeight(height: number) { diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index aa95731eb0..257a19c984 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -19,7 +19,7 @@ const AccountUpdateIterator = MerkleListIterator.create( ); class Layer extends Struct({ - forest: AccountUpdateIterator.provable, + forest: AccountUpdateIterator, mayUseToken: AccountUpdate.MayUseToken.type, }) {} const ParentLayers = MerkleList.create<Layer>(Layer); diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts index e453c561e1..94c71a809d 100644 --- a/src/lib/mina/zkapp.ts +++ b/src/lib/mina/zkapp.ts @@ -462,7 +462,7 @@ function wrapMethod( }>( provable({ result: methodIntf.returnType ?? provable(null), - children: AccountUpdateForest.provable, + children: AccountUpdateForest, }), runCalledContract, { skipCheck: true } diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts index 91806c9e62..c0e8f515bc 100644 --- a/src/lib/provable/gadgets/elliptic-curve.ts +++ b/src/lib/provable/gadgets/elliptic-curve.ts @@ -775,7 +775,7 @@ const Point = { return Point.from(random(Curve)); }, - provable: provable({ x: Field3.provable, y: Field3.provable }), + provable: provable({ x: Field3, y: Field3 }), }; const EcdsaSignature = { @@ -804,7 +804,7 @@ const EcdsaSignature = { return EcdsaSignature.from({ r, s }); }, - provable: provable({ r: Field3.provable, s: Field3.provable }), + provable: provable({ r: Field3, s: Field3 }), }; const Ecdsa = { diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 79208b2bd4..24b3ae7851 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -44,7 +44,7 @@ type MerkleListBase<T> = { }; function MerkleListBase<T>(): ProvableHashable<MerkleListBase<T>> { - return class extends Struct({ hash: Field, data: Unconstrained.provable }) { + return class extends Struct({ hash: Field, data: Unconstrained }) { static empty(): MerkleListBase<T> { return { hash: emptyHash, data: Unconstrained.from([]) }; } diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts index 1278aaa7e7..1532065d3c 100644 --- a/src/lib/provable/types/unconstrained.ts +++ b/src/lib/provable/types/unconstrained.ts @@ -128,6 +128,7 @@ and Provable.asProver() blocks, which execute outside the proof. }, }; + // TODO rename static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & { toInput: (x: Unconstrained<T>) => { fields?: Field[]; From 049582966be6676eff5320a8290c4c9be9e65ebd Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 13:58:55 +0200 Subject: [PATCH 25/32] rename --- src/examples/crypto/rsa/rsa.ts | 5 ++--- src/lib/mina/actions/batch-reducer.ts | 4 ++-- src/lib/mina/actions/offchain-state-serialization.ts | 2 +- src/lib/provable/merkle-tree-indexed.ts | 6 +++--- src/lib/provable/types/unconstrained.ts | 10 ++++++++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts index 50db5691e0..95be654ac3 100644 --- a/src/examples/crypto/rsa/rsa.ts +++ b/src/examples/crypto/rsa/rsa.ts @@ -30,7 +30,7 @@ class Bigint2048 { // TODO this wrapping Struct should be unnecessary class extends Struct({ fields: Field18, - value: Unconstrained.provableWithEmpty(0n), + value: Unconstrained.withEmpty(0n), }) { // TODO where to add the custom check()? static check({ fields }: { fields: Field[] }) { @@ -91,8 +91,7 @@ function multiply( // this also adds the range checks in `check()` let { q, r } = Provable.witness( // TODO Struct() should be unnecessary - // TODO .provable should be unnecessary - Struct({ q: Bigint2048.provable, r: Bigint2048.provable }), + Struct({ q: Bigint2048, r: Bigint2048 }), () => { let xy = x.toBigint() * y.toBigint(); let p0 = p.toBigint(); diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts index cf7c26da0e..90fb2da199 100644 --- a/src/lib/mina/actions/batch-reducer.ts +++ b/src/lib/mina/actions/batch-reducer.ts @@ -551,7 +551,7 @@ function ActionBatch<A extends Actionable<any>>(actionType: A) { onchainStack: Field, stack: MerkleActions(actionType), isRecursive: Bool, - witnesses: Unconstrained.provableWithEmpty<ActionWitnesses>([]), + witnesses: Unconstrained.withEmpty<ActionWitnesses>([]), }); } @@ -769,7 +769,7 @@ function actionStackProgram(maxUpdatesPerProof: number) { privateInputs: [ SelfProof, Bool, - Unconstrained.provableWithEmpty<ActionWitnesses>([]), + Unconstrained.withEmpty<ActionWitnesses>([]), ], async method( diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts index c7a66be90a..c9d46d9626 100644 --- a/src/lib/mina/actions/offchain-state-serialization.ts +++ b/src/lib/mina/actions/offchain-state-serialization.ts @@ -137,7 +137,7 @@ class MerkleLeaf extends Struct({ value: Field, usesPreviousValue: Bool, previousValue: Field, - prefix: Unconstrained.provableWithEmpty<Field[]>([]), + prefix: Unconstrained.withEmpty<Field[]>([]), }) { static fromAction(action: Field[]) { assert(action.length >= 4, 'invalid action size'); diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts index 568a47798a..0389a85f68 100644 --- a/src/lib/provable/merkle-tree-indexed.ts +++ b/src/lib/provable/merkle-tree-indexed.ts @@ -73,7 +73,7 @@ function IndexedMerkleMap(height: number): typeof IndexedMerkleMapBase { const provableBase = { root: Field, length: Field, - data: Unconstrained.provableWithEmpty({ + data: Unconstrained.withEmpty({ nodes: [] as (bigint | undefined)[][], sortedLeaves: [] as StoredLeaf[], }), @@ -656,8 +656,8 @@ class Leaf extends Struct({ nextKey: Field, // auxiliary data that tells us where the leaf is stored - index: Unconstrained.provableWithEmpty(0), - sortedIndex: Unconstrained.provableWithEmpty(0), + index: Unconstrained.withEmpty(0), + sortedIndex: Unconstrained.withEmpty(0), }) { /** * Compute a leaf node: the hash of a leaf that becomes part of the Merkle tree. diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts index 1532065d3c..efb3cd4eba 100644 --- a/src/lib/provable/types/unconstrained.ts +++ b/src/lib/provable/types/unconstrained.ts @@ -128,8 +128,7 @@ and Provable.asProver() blocks, which execute outside the proof. }, }; - // TODO rename - static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & { + static withEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & { toInput: (x: Unconstrained<T>) => { fields?: Field[]; packed?: [Field, number][]; @@ -141,6 +140,13 @@ and Provable.asProver() blocks, which execute outside the proof. empty: () => Unconstrained.from(empty), }; } + + /** + * @deprecated + */ + static provableWithEmpty<T>(empty: T) { + return Unconstrained.withEmpty(empty); + } } type UnconstrainedProvable<T> = Provable<Unconstrained<T>, T>; From 0f4e42ddc6e2d647af02847f1e4843601dc4406f Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 14:21:14 +0200 Subject: [PATCH 26/32] support no .provable in state, offchain state, method returns and batch reducer --- src/examples/crypto/foreign-field.ts | 2 +- src/lib/mina/actions/action-types.ts | 15 +++++++++------ src/lib/mina/actions/batch-reducer.ts | 10 ++++++++-- src/lib/mina/actions/batch-reducer.unit-test.ts | 2 +- .../mina/actions/offchain-state-serialization.ts | 14 +++++++++++--- src/lib/mina/actions/offchain-state.ts | 4 ++++ src/lib/mina/state.ts | 10 ++++++++-- src/lib/mina/zkapp.ts | 11 ++++++++--- src/lib/provable/crypto/poseidon.ts | 5 +++-- src/lib/provable/option.ts | 5 +++-- 10 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/examples/crypto/foreign-field.ts b/src/examples/crypto/foreign-field.ts index 044d8ebda6..c8860275c8 100644 --- a/src/examples/crypto/foreign-field.ts +++ b/src/examples/crypto/foreign-field.ts @@ -88,7 +88,7 @@ assert(uCanonical instanceof SmallField.Canonical); class AlmostSmallField extends SmallField.AlmostReduced {} class MyContract extends SmartContract { - @state(AlmostSmallField.provable) x = State<AlmostSmallField>(); + @state(AlmostSmallField) x = State<AlmostSmallField>(); @method async myMethod(y: AlmostSmallField) { let x = y.mul(2); diff --git a/src/lib/mina/actions/action-types.ts b/src/lib/mina/actions/action-types.ts index 7048edc321..fc0d26b00a 100644 --- a/src/lib/mina/actions/action-types.ts +++ b/src/lib/mina/actions/action-types.ts @@ -6,6 +6,7 @@ import { Actions } from '../account-update.js'; import { Hashed } from '../../provable/packed.js'; import { hashWithPrefix } from '../../provable/crypto/poseidon.js'; import { prefixes } from '../../../bindings/crypto/constants.js'; +import { ProvableType } from '../../provable/types/provable-intf.js'; export { MerkleActions, MerkleActionHashes, HashedAction, FlatActions }; export { emptyActionState, emptyActionsHash }; @@ -45,8 +46,9 @@ function MerkleActionList<A extends Actionable<any>>(actionType: A) { type HashedAction<T> = Hashed<T>; function HashedAction<A extends Actionable<any>>(actionType: A) { - return Hashed.create(actionType as Actionable<InferProvable<A>>, (action) => - hashWithPrefix(prefixes.event, actionType.toFields(action)) + let type = ProvableType.get(actionType as Actionable<InferProvable<A>>); + return Hashed.create(type, (action) => + hashWithPrefix(prefixes.event, type.toFields(action)) ); } @@ -55,14 +57,15 @@ function actionFieldsToMerkleList<T>( fields: bigint[][][], fromActionState?: bigint ) { - const HashedActionT = HashedAction(actionType); - const MerkleActionListT = MerkleActionList(actionType); + let type = ProvableType.get(actionType); + const HashedActionT = HashedAction(type); + const MerkleActionListT = MerkleActionList(type); const MerkleActionsT = MerkleActions( - actionType, + type, fromActionState ? Field(fromActionState) : undefined ); let actions = fields.map((event) => - event.map((action) => actionType.fromFields(action.map(Field))) + event.map((action) => type.fromFields(action.map(Field))) ); let hashes = actions.map((as) => as.map((a) => HashedActionT.hash(a))); return MerkleActionsT.from(hashes.map((h) => MerkleActionListT.from(h))); diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts index 90fb2da199..c29cb040e1 100644 --- a/src/lib/mina/actions/batch-reducer.ts +++ b/src/lib/mina/actions/batch-reducer.ts @@ -24,6 +24,11 @@ import { MerkleActions, emptyActionState, } from './action-types.js'; +import { + ProvableHashable, + ProvablePure, + ProvableType, +} from '../../provable/types/provable-intf.js'; // external API export { BatchReducer, ActionBatch }; @@ -57,7 +62,7 @@ class BatchReducer< Action = InferProvable<ActionType> > { batchSize: BatchSize; - actionType: Actionable<Action>; + actionType: ProvableHashable<Action> & ProvablePure<Action>; Batch: ReturnType<typeof ActionBatch>; program: ActionStackProgram; @@ -133,7 +138,8 @@ class BatchReducer< maxActionsPerUpdate?: number; }) { this.batchSize = batchSize; - this.actionType = actionType as Actionable<Action>; + this.actionType = ProvableType.get(actionType) as ProvableHashable<Action> & + ProvablePure<Action>; this.Batch = ActionBatch(this.actionType); this.maxUpdatesFinalProof = maxUpdatesFinalProof; diff --git a/src/lib/mina/actions/batch-reducer.unit-test.ts b/src/lib/mina/actions/batch-reducer.unit-test.ts index 7e98a96f40..ff06ffd90f 100644 --- a/src/lib/mina/actions/batch-reducer.unit-test.ts +++ b/src/lib/mina/actions/batch-reducer.unit-test.ts @@ -86,7 +86,7 @@ class UnsafeAirdrop extends SmartContract { * * Note: This two-step process is necessary so that multiple users can claim concurrently. */ - @method.returns(MerkleMap.provable) + @method.returns(MerkleMap) async settleClaims(batch: Batch, proof: BatchProof) { // witness merkle map and require that it matches the onchain root let eligibleMap = Provable.witness(MerkleMap, () => eligible.clone()); diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts index c9d46d9626..bf76b73311 100644 --- a/src/lib/mina/actions/offchain-state-serialization.ts +++ b/src/lib/mina/actions/offchain-state-serialization.ts @@ -6,7 +6,11 @@ * if we only need to prove that (key, value) are part of it. */ -import { ProvablePure } from '../../provable/types/provable-intf.js'; +import { + ProvablePure, + ProvableType, + WithProvable, +} from '../../provable/types/provable-intf.js'; import { Poseidon, ProvableHashable, @@ -45,7 +49,9 @@ export { }; type Action = [...Field[], Field, Field]; -type Actionable<T, V = any> = ProvableHashable<T, V> & ProvablePure<T, V>; +type Actionable<T, V = any> = WithProvable< + ProvableHashable<T, V> & ProvablePure<T, V> +>; function toKeyHash<K, KeyType extends Actionable<K> | undefined>( prefix: Field, @@ -70,6 +76,7 @@ function toAction<K, V, KeyType extends Actionable<K> | undefined>({ value: V; previousValue?: Option<V>; }): Action { + valueType = ProvableType.get(valueType); let valueSize = valueType.sizeInFields(); let padding = valueSize % 2 === 0 ? [] : [Field(0)]; @@ -100,6 +107,7 @@ function fromActionWithoutHashes<V>( valueType: Actionable<V>, action: Field[] ): V { + valueType = ProvableType.get(valueType); let valueSize = valueType.sizeInFields(); let paddingSize = valueSize % 2 === 0 ? 0 : 1; assert(action.length === valueSize + paddingSize, 'invalid action size'); @@ -121,7 +129,7 @@ function hashPackedWithPrefix<T, Type extends Actionable<T> | undefined>( // hash value if a type was passed in if (type !== undefined) { - let input = type.toInput(value as T); + let input = ProvableType.get(type).toInput(value as T); let packed = packToFields(input); state = Poseidon.update(state, packed); } diff --git a/src/lib/mina/actions/offchain-state.ts b/src/lib/mina/actions/offchain-state.ts index 2b5f598b59..090b7d3496 100644 --- a/src/lib/mina/actions/offchain-state.ts +++ b/src/lib/mina/actions/offchain-state.ts @@ -27,6 +27,7 @@ import { Poseidon } from '../../provable/crypto/poseidon.js'; import { contract } from '../smart-contract-context.js'; import { IndexedMerkleMap } from '../../provable/merkle-tree-indexed.js'; import { assertDefined } from '../../util/assert.js'; +import { ProvableType } from '../../provable/types/provable-intf.js'; // external API export { OffchainState, OffchainStateCommitments }; @@ -291,6 +292,7 @@ function OffchainState< index: number, type: Actionable<T, TValue> ): OffchainField<T, TValue> { + type = ProvableType.get(type); const prefix = Field(index); let optionType = Option(type); @@ -340,6 +342,8 @@ function OffchainState< keyType: Actionable<K>, valueType: Actionable<V, VValue> ): OffchainMap<K, V, VValue> { + keyType = ProvableType.get(keyType); + valueType = ProvableType.get(valueType); const prefix = Field(index); let optionType = Option(valueType); diff --git a/src/lib/mina/state.ts b/src/lib/mina/state.ts index 9c9f39eb09..43b480cb2e 100644 --- a/src/lib/mina/state.ts +++ b/src/lib/mina/state.ts @@ -7,7 +7,11 @@ import { SmartContract } from './zkapp.js'; import { Account } from './account.js'; import { Provable } from '../provable/provable.js'; import { Field } from '../provable/wrapped.js'; -import { ProvablePure } from '../provable/types/provable-intf.js'; +import { + ProvablePure, + ProvableType, + ProvableTypePure, +} from '../provable/types/provable-intf.js'; import { ensureConsistentPrecondition } from './precondition.js'; import { Bool } from '../provable/wrapped.js'; @@ -98,7 +102,9 @@ function State<A>(defaultValue?: A): State<A> { * ``` * */ -function state<A>(stateType: FlexibleProvablePure<A>) { +function state<A>(type: ProvableTypePure<A> | FlexibleProvablePure<A>) { + let stateType = ProvableType.get(type); + return function ( target: SmartContract & { constructor: any }, key: string, diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts index 94c71a809d..7b58bfc361 100644 --- a/src/lib/mina/zkapp.ts +++ b/src/lib/mina/zkapp.ts @@ -74,7 +74,7 @@ import { smartContractContext, } from './smart-contract-context.js'; import { assertPromise } from '../util/assert.js'; -import { ProvablePure } from '../provable/types/provable-intf.js'; +import { ProvablePure, ProvableType } from '../provable/types/provable-intf.js'; import { getReducer, Reducer } from './actions/reducer.js'; import { provable } from '../provable/types/provable-derivers.js'; @@ -174,7 +174,7 @@ function method<K extends string, T extends SmartContract>( method.returns = function < K extends string, T extends SmartContract, - R extends Provable<any> + R extends ProvableType >(returnType: R) { return function decorateMethod( target: T & { @@ -183,7 +183,12 @@ method.returns = function < methodName: K & string & keyof T, descriptor: PropertyDescriptor ) { - return method(target as any, methodName, descriptor, returnType); + return method( + target as any, + methodName, + descriptor, + ProvableType.get(returnType) + ); }; }; diff --git a/src/lib/provable/crypto/poseidon.ts b/src/lib/provable/crypto/poseidon.ts index c3425f0f66..7d74672820 100644 --- a/src/lib/provable/crypto/poseidon.ts +++ b/src/lib/provable/crypto/poseidon.ts @@ -9,6 +9,7 @@ import { assert } from '../../util/errors.js'; import { rangeCheckN } from '../gadgets/range-check.js'; import { TupleN } from '../../util/types.js'; import { Group } from '../group.js'; +import { ProvableType, WithProvable } from '../types/provable-intf.js'; // external API export { Poseidon, TokenSymbol }; @@ -136,8 +137,8 @@ const Poseidon = { * field elements as possible. This saves constraints because packing has a much * lower per-field element cost than hashing. */ - hashPacked<T>(type: Hashable<T>, value: T) { - let input = type.toInput(value); + hashPacked<T>(type: WithProvable<Hashable<T>>, value: T) { + let input = ProvableType.get(type).toInput(value); let packed = packToFields(input); return Poseidon.hash(packed); }, diff --git a/src/lib/provable/option.ts b/src/lib/provable/option.ts index cfd324e07f..86a71fb524 100644 --- a/src/lib/provable/option.ts +++ b/src/lib/provable/option.ts @@ -4,6 +4,7 @@ import { Provable } from './provable.js'; import { InferProvable, Struct } from './types/struct.js'; import { provable, ProvableInferPureFrom } from './types/provable-derivers.js'; import { Bool } from './wrapped.js'; +import { ProvableType } from './types/provable-intf.js'; export { Option, OptionOrValue }; @@ -34,7 +35,7 @@ type OptionOrValue<T, V> = * let zero: UInt64 = none.orElse(0n); // specify a default value * ``` */ -function Option<A extends Provable<any, any>>( +function Option<A extends ProvableType>( type: A ): ProvableInferPureFrom< A, @@ -59,7 +60,7 @@ function Option<A extends Provable<any, any>>( } { type T = InferProvable<A>; type V = InferValue<A>; - let strictType: Provable<T, V> = type; + let strictType: Provable<T, V> = ProvableType.get(type); // construct a provable with a JS type of `T | undefined` const PlainOption: Provable< From 8c3410ca901580facd8e7d84867f5dcec30b2109 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 14:24:50 +0200 Subject: [PATCH 27/32] Revert "(maybe revert) some experimenting" This reverts commit 30002309abc84a435b1f071e236a01e3ef8f197e. --- src/examples/crypto/rsa/rsa.ts | 60 ++++++++-------------------------- src/index.ts | 2 -- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts index 95be654ac3..7e2a17395d 100644 --- a/src/examples/crypto/rsa/rsa.ts +++ b/src/examples/crypto/rsa/rsa.ts @@ -1,14 +1,7 @@ /** * RSA signature verification with o1js */ -import { - Field, - Gadgets, - Provable, - Struct, - Unconstrained, - provableExtends, -} from 'o1js'; +import { Field, Gadgets, Provable, Struct, Unconstrained } from 'o1js'; export { Bigint2048, rsaVerify65537 }; @@ -19,40 +12,10 @@ const mask = (1n << 116n) - 1n; */ const Field18 = Provable.Array(Field, 18); -class Bigint2048 { - fields: Field[]; - value: Unconstrained<bigint>; - - // TODO this could be simplified with a Struct-like base class - // TODO map the value type to `bigint` - static provable = provableExtends( - Bigint2048, - // TODO this wrapping Struct should be unnecessary - class extends Struct({ - fields: Field18, - value: Unconstrained.withEmpty(0n), - }) { - // TODO where to add the custom check()? - static check({ fields }: { fields: Field[] }) { - for (let x of fields) { - rangeCheck116(x); - } - } - } - ); - - // TODO constructor could be removed with a Struct-like base class - constructor({ - fields, - value, - }: { - fields: Field[]; - value: Unconstrained<bigint>; - }) { - this.fields = fields; - this.value = value; - } - +class Bigint2048 extends Struct({ + fields: Field18, + value: Unconstrained.withEmpty(0n), +}) { modMul(x: Bigint2048, y: Bigint2048) { return multiply(x, y, this); } @@ -66,13 +29,19 @@ class Bigint2048 { } static from(x: bigint) { - let fields: bigint[] = []; + let fields = []; let value = x; for (let i = 0; i < 18; i++) { - fields.push(x & mask); + fields.push(Field(x & mask)); x >>= 116n; } - return Bigint2048.provable.fromValue({ fields, value }); + return new Bigint2048({ fields, value: Unconstrained.from(value) }); + } + + static check(x: { fields: Field[] }) { + for (let i = 0; i < 18; i++) { + rangeCheck116(x.fields[i]); + } } } @@ -97,7 +66,6 @@ function multiply( let p0 = p.toBigint(); let q = xy / p0; let r = xy - q * p0; - // TODO Bigint2048.from() should be unnecessary return { q: Bigint2048.from(q), r: Bigint2048.from(r) }; } ); diff --git a/src/index.ts b/src/index.ts index 5819569f66..0cd002a58d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,8 +40,6 @@ export type { export { provable, provablePure, - provableMap, - provableExtends, } from './lib/provable/types/provable-derivers.js'; export { Struct } from './lib/provable/types/struct.js'; export { Unconstrained } from './lib/provable/types/unconstrained.js'; From 440a22c2ad0fd9a27eb0a0139a1ae02e31d6e753 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 14:34:13 +0200 Subject: [PATCH 28/32] revert unecessary diff --- src/lib/proof-system/zkprogram.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 4f9057ba1b..fe497a7a65 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -1486,18 +1486,15 @@ type Prover< ...args: TupleToInstances<Args> ) => Promise<Proof<PublicInput, PublicOutput>>; -type ProvableOrUndefined<A extends ProvableType | undefined> = - A extends ProvableType ? ToProvable<A> : typeof Undefined; -type ProvableOrVoid<A extends ProvableType | undefined> = A extends ProvableType - ? ToProvable<A> - : typeof Void; - -type InferProvableOrUndefined<A extends ProvableType | undefined> = - A extends ProvableType ? InferProvableType<A> : undefined; - -type InferProvableOrVoid<A> = A extends ProvableType - ? InferProvableType<A> - : void; +type ProvableOrUndefined<A> = A extends undefined + ? typeof Undefined + : ToProvable<A>; +type ProvableOrVoid<A> = A extends undefined ? typeof Void : ToProvable<A>; + +type InferProvableOrUndefined<A> = A extends undefined + ? undefined + : InferProvable<A>; +type InferProvableOrVoid<A> = A extends undefined ? void : InferProvable<A>; type UnwrapPromise<P> = P extends Promise<infer T> ? T : never; From 02ec0ff66426d4c031ac758749b0bc39b22fcd31 Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 14:34:46 +0200 Subject: [PATCH 29/32] delete unused and obvious function --- src/lib/provable/types/provable-derivers.ts | 41 --------------------- 1 file changed, 41 deletions(-) diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts index 5e2476b330..4cb5178d47 100644 --- a/src/lib/provable/types/provable-derivers.ts +++ b/src/lib/provable/types/provable-derivers.ts @@ -32,7 +32,6 @@ export { provablePure, provableTuple, provableFromClass, - provableMap, provableExtends, }; @@ -135,46 +134,6 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T { return Object.assign(instance, value); } -function provableMap< - A extends ProvableHashable<any>, - S, - T extends InferProvable<A> = InferProvable<A> ->( - base: A, - there: (t: T) => S, - back: (s: S) => T -): ProvableHashable<S, InferValue<A>> { - return { - sizeInFields() { - return base.sizeInFields(); - }, - toFields(value) { - return base.toFields(back(value)); - }, - toAuxiliary(value) { - return base.toAuxiliary(value === undefined ? undefined : back(value)); - }, - fromFields(fields, aux) { - return there(base.fromFields(fields, aux)); - }, - check(value) { - base.check(back(value)); - }, - toValue(value) { - return base.toValue(back(value)); - }, - fromValue(value) { - return there(base.fromValue(value)); - }, - empty() { - return there(base.empty()); - }, - toInput(value) { - return base.toInput(back(value)); - }, - }; -} - function provableExtends< A extends ProvableHashable<any>, T extends InferProvable<A>, From a1e269ae0d5e1949d65ba7bef3b949a57d00876a Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 15:03:13 +0200 Subject: [PATCH 30/32] add and fix test --- src/bindings | 2 +- src/lib/provable/test/struct.unit-test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bindings b/src/bindings index 671219a155..30d98a1cfd 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 671219a155311b8335db535596b4dfa3b61fef11 +Subproject commit 30d98a1cfd065728fe6e7d16038a6f2f01e53d7d diff --git a/src/lib/provable/test/struct.unit-test.ts b/src/lib/provable/test/struct.unit-test.ts index 8aaef12fe8..0acbd21192 100644 --- a/src/lib/provable/test/struct.unit-test.ts +++ b/src/lib/provable/test/struct.unit-test.ts @@ -24,8 +24,8 @@ import { modifiedField } from '../types/fields.js'; let type = provable({ nested: { a: Number, b: Boolean }, other: String, - pk: PublicKey, - bool: Bool, + pk: { provable: PublicKey }, + bool: { provable: Bool }, uint: [UInt32, UInt32], }); @@ -132,14 +132,14 @@ class MyStruct extends Struct({ nested: { a: Number, b: Boolean }, other: String, pk: PublicKey, - uint: [UInt32, UInt32], + uint: [UInt32, { provable: UInt32 }], }) {} class MyStructPure extends Struct({ nested: { a: Field, b: UInt32 }, other: Field, pk: PublicKey, - uint: [UInt32, UInt32], + uint: [UInt32, { provable: UInt32 }], }) {} // Struct.fromValue() works on both js and provable inputs From 852a7125391a59e578f6a880be88e84fc917e8ca Mon Sep 17 00:00:00 2001 From: Gregor <gregor.mitscha-baude@gmx.at> Date: Tue, 16 Jul 2024 16:53:25 +0200 Subject: [PATCH 31/32] revert fix because it's breaking, stupid me --- src/lib/provable/gadgets/foreign-field.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/gadgets/foreign-field.ts b/src/lib/provable/gadgets/foreign-field.ts index db48d1c4f1..948a9b77f7 100644 --- a/src/lib/provable/gadgets/foreign-field.ts +++ b/src/lib/provable/gadgets/foreign-field.ts @@ -431,8 +431,13 @@ function equals(x: Field3, c: bigint, f: bigint) { return x012.equals(c); } } - -const provableLimb = modifiedField({}); +// TODO: remove this in v2!!! +// having a `toInput()` method without a corresponding `check()` is begging for a vulnerability (which the current code has) +const provableLimb = modifiedField({ + toInput(x) { + return { packed: [[x, Number(l)]] }; + }, +}); const Field3 = { /** From 301226dcfd89ad45589e6fdefc38141edf2b0041 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at> Date: Tue, 23 Jul 2024 10:30:10 +0200 Subject: [PATCH 32/32] changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 635cc09787..4733f19576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed -- Reduced maximum bit length for `xor`, `not`, and `and`, operations from 254 to 240 bits to improve performance and simplify implementation. https://github.com/o1-labs/o1js/pull/1745 +- Reduced maximum bit length for `xor`, `not`, and `and`, operations from 254 to 240 bits to prevent overflow vulnerabilities. https://github.com/o1-labs/o1js/pull/1745 +- Allow using `Type` instead of `Type.provable` in APIs that expect a provable type https://github.com/o1-labs/o1js/pull/1751 + - Example: `Provable.witness(Bytes32, () => bytes)` +- Automatically wrap and unwrap `Unconstrained` in `fromValue` and `toValue`, so that we don't need to deal with "unconstrained" values outside provable code https://github.com/o1-labs/o1js/pull/1751 ## [1.5.0](https://github.com/o1-labs/o1js/compare/ed198f305...1c736add) - 2024-07-09