From cd7737ed81f252de875ec6dd4ada90b44468ae81 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Jun 2023 16:21:35 +0200 Subject: [PATCH 01/13] move some snarky functions to "run" namespace and add inProverBlock() --- src/bindings | 2 +- src/lib/provable-context.ts | 8 +++---- src/snarky.d.ts | 44 +++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/bindings b/src/bindings index adaddf68fc..47d9b3baa7 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit adaddf68fc69b549bb0e5a08a01095d435998461 +Subproject commit 47d9b3baa7bfe8b3f8f5291eba1a05c1480e3a2d diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index 62699fa78d..05310af119 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -58,7 +58,7 @@ function inCompileMode() { function asProver(f: () => void) { if (inCheckedComputation()) { - Snarky.asProver(f); + Snarky.run.asProver(f); } else { f(); } @@ -67,7 +67,7 @@ function asProver(f: () => void) { function runAndCheck(f: () => void) { let id = snarkContext.enter({ inCheckedComputation: true }); try { - Snarky.runAndCheck(f); + Snarky.run.runAndCheck(f); } catch (error) { throw prettifyStacktrace(error); } finally { @@ -78,7 +78,7 @@ function runAndCheck(f: () => void) { function runUnchecked(f: () => void) { let id = snarkContext.enter({ inCheckedComputation: true }); try { - Snarky.runUnchecked(f); + Snarky.run.runUnchecked(f); } catch (error) { throw prettifyStacktrace(error); } finally { @@ -90,7 +90,7 @@ function constraintSystem(f: () => T) { let id = snarkContext.enter({ inAnalyze: true, inCheckedComputation: true }); try { let result: T; - let { rows, digest, json } = Snarky.constraintSystem(() => { + let { rows, digest, json } = Snarky.run.constraintSystem(() => { result = f(); }); let { gates, publicInputSize } = gatesFromJson(json); diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 37437a1ace..f630eb60ac 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -163,25 +163,35 @@ declare const Snarky: { * witness a single field element variable */ existsVar(compute: () => FieldConst): FieldVar; + /** - * Runs code as a prover. - */ - asProver(f: () => void): void; - /** - * Runs code and checks its correctness. - */ - runAndCheck(f: () => void): void; - /** - * Runs code in prover mode, without checking correctness. - */ - runUnchecked(f: () => void): void; - /** - * Returns information about the constraint system in the callback function. + * APIs that have to do with running provable code */ - constraintSystem(f: () => void): { - rows: number; - digest: string; - json: JsonConstraintSystem; + run: { + /** + * Runs code as a prover. + */ + asProver(f: () => void): void; + /** + * Check whether we are inside an asProver or exists block + */ + inProverBlock(): boolean; + /** + * Runs code and checks its correctness. + */ + runAndCheck(f: () => void): void; + /** + * Runs code in prover mode, without checking correctness. + */ + runUnchecked(f: () => void): void; + /** + * Returns information about the constraint system in the callback function. + */ + constraintSystem(f: () => void): { + rows: number; + digest: string; + json: JsonConstraintSystem; + }; }; /** From e0256b791ef26134917129786bb1ec052d7d2404 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Jun 2023 17:45:55 +0200 Subject: [PATCH 02/13] handle read_var error in ts --- src/lib/field.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 2ba4e84534..78e2afa306 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -2,8 +2,9 @@ import { Snarky, Provable } from '../snarky.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { defineBinable } from '../bindings/lib/binable.js'; import type { NonNegativeInteger } from '../bindings/crypto/non-negative.js'; -import { asProver } from './provable-context.js'; +import { asProver, inCheckedComputation } from './provable-context.js'; import { Bool } from './bool.js'; +import { assert } from './errors.js'; // external API export { Field }; @@ -204,10 +205,23 @@ class Field { * @return A constant {@link Field} element equivalent to this {@link Field} element. */ toConstant(): ConstantField { + // if this is a constant, return it if (this.isConstant()) return this; - // TODO: fix OCaml error message, `Can't evaluate prover code outside an as_prover block` - let value = Snarky.field.readVar(this.value); - return new Field(value) as ConstantField; + + // a non-constant can only appear inside a checked computation. everything else is a bug. + assert( + inCheckedComputation(), + 'variables only exist inside checked computations' + ); + + // if we are inside an asProver or witness block, read the variable's value and return it as constant + if (Snarky.run.inProverBlock()) { + let value = Snarky.field.readVar(this.value); + return new Field(value) as ConstantField; + } + + // otherwise, calling `toConstant()` is likely a mistake. throw a helpful error message. + throw Error("Can't evaluate prover code outside an as_prover block 🧌"); } /** From d0c9ae8ab7f52131884ea055b2d7a76a9d1650ca Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Jun 2023 21:10:48 +0200 Subject: [PATCH 03/13] detailed error message --- src/lib/field.ts | 56 +++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 78e2afa306..8c0dd825a9 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -191,20 +191,7 @@ class Field { return this.value[0] === FieldType.Constant; } - /** - * Create a {@link Field} element equivalent to this {@link Field} element's value, - * but is a constant. - * See {@link Field.isConstant} for more information about what is a constant {@link Field}. - * - * @example - * ```ts - * const someField = Field(42); - * someField.toConstant().assertEquals(someField); // Always true - * ``` - * - * @return A constant {@link Field} element equivalent to this {@link Field} element. - */ - toConstant(): ConstantField { + #toConstant(name: string): ConstantField { // if this is a constant, return it if (this.isConstant()) return this; @@ -221,7 +208,36 @@ class Field { } // otherwise, calling `toConstant()` is likely a mistake. throw a helpful error message. - throw Error("Can't evaluate prover code outside an as_prover block 🧌"); + throw Error(`x.${name}() was called on a variable x in provable code. +This is not supported, because variables in provable code represent an abstract computation, +which only has actual values attached during proving, but not during compiling the prover key. + +Also, reading out JS values means that whatever you're doing with those values will no longer be +linked to the original variable in the proof, which makes this pattern prone to security holes. + +You can check whether your field element is a variable or a constant using x.isConstant(). + +To inspect values for debugging, use Provable.log(x). For more advanced use cases, +there is \'Provable.asProver(() => { ... })\` which allows you to use x.${name}() inside the callback. +Warning: whatever happens inside asProver() will not be part of the zk proof. +`); + } + + /** + * Create a {@link Field} element equivalent to this {@link Field} element's value, + * but is a constant. + * See {@link Field.isConstant} for more information about what is a constant {@link Field}. + * + * @example + * ```ts + * const someField = Field(42); + * someField.toConstant().assertEquals(someField); // Always true + * ``` + * + * @return A constant {@link Field} element equivalent to this {@link Field} element. + */ + toConstant(): ConstantField { + return this.#toConstant('toConstant'); } /** @@ -238,7 +254,7 @@ class Field { * @return A bigint equivalent to the bigint representation of the Field. */ toBigInt() { - let x = this.toConstant(); + let x = this.#toConstant('toBigInt'); return FieldConst.toBigint(x.value[1]); } @@ -256,7 +272,7 @@ class Field { * @return A string equivalent to the string representation of the Field. */ toString() { - return this.toBigInt().toString(); + return this.#toConstant('toString').toBigInt().toString(); } /** @@ -1125,7 +1141,7 @@ class Field { * @return A string equivalent to the JSON representation of the {@link Field}. */ toJSON() { - return this.toString(); + return this.#toConstant('toJSON').toString(); } /** @@ -1186,7 +1202,7 @@ class Field { * */ static toBytes(x: Field) { - return FieldBinable.toBytes(x); + return [...x.#toConstant('toBytes').value[1]]; } /** @@ -1229,7 +1245,7 @@ class Field { const FieldBinable = defineBinable({ toBytes(t: Field) { - return [...t.toConstant().value[1]]; + return Field.toBytes(t); }, readBytes(bytes, offset) { let uint8array = new Uint8Array(32); From e6736f58eaa6cde8bc63477ceefa6af569f4da5e Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Jun 2023 21:22:12 +0200 Subject: [PATCH 04/13] abstract error message --- src/lib/field.ts | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 8c0dd825a9..5a272b2d62 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -10,7 +10,15 @@ import { assert } from './errors.js'; export { Field }; // internal API -export { ConstantField, FieldType, FieldVar, FieldConst, isField, withMessage }; +export { + ConstantField, + FieldType, + FieldVar, + FieldConst, + isField, + withMessage, + readVarMessage, +}; type FieldConst = Uint8Array; @@ -208,19 +216,7 @@ class Field { } // otherwise, calling `toConstant()` is likely a mistake. throw a helpful error message. - throw Error(`x.${name}() was called on a variable x in provable code. -This is not supported, because variables in provable code represent an abstract computation, -which only has actual values attached during proving, but not during compiling the prover key. - -Also, reading out JS values means that whatever you're doing with those values will no longer be -linked to the original variable in the proof, which makes this pattern prone to security holes. - -You can check whether your field element is a variable or a constant using x.isConstant(). - -To inspect values for debugging, use Provable.log(x). For more advanced use cases, -there is \'Provable.asProver(() => { ... })\` which allows you to use x.${name}() inside the callback. -Warning: whatever happens inside asProver() will not be part of the zk proof. -`); + throw Error(readVarMessage(name, 'x', 'field element')); } /** @@ -1286,3 +1282,23 @@ function withMessage(error: unknown, message?: string) { error.message = `${message}\n${error.message}`; return error; } + +function readVarMessage( + methodName: string, + varName: string, + varDescription: string +) { + return `${varName}.${methodName}() was called on a variable ${varDescription} \`${varName}\` in provable code. +This is not supported, because variables represent an abstract computation, +which only carries actual values during proving, but not during compiling. + +Also, reading out JS values means that whatever you're doing with those values will no longer be +linked to the original variable in the proof, which makes this pattern prone to security holes. + +You can check whether your ${varDescription} is a variable or a constant using ${varName}.isConstant(). + +To inspect values for debugging, use Provable.log(${varName}). For more advanced use cases, +there is \`Provable.asProver(() => { ... })\` which allows you to use ${varName}.${methodName}() inside the callback. +Warning: whatever happens inside asProver() will not be part of the zk proof. +`; +} From 7301062f99203d7e35538259309e335a508a7d04 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Jun 2023 22:18:31 +0200 Subject: [PATCH 05/13] apply better errors to more types --- src/lib/bool.ts | 14 +++++++++++--- src/lib/field.ts | 44 +++++++++++++++++++++++++++----------------- src/lib/scalar.ts | 34 ++++++++++++++++++---------------- src/lib/signature.ts | 9 +++++++-- 4 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/lib/bool.ts b/src/lib/bool.ts index ad02a6c6ef..1c704aa30d 100644 --- a/src/lib/bool.ts +++ b/src/lib/bool.ts @@ -1,5 +1,11 @@ import { Snarky } from '../snarky.js'; -import { Field, FieldConst, FieldType, FieldVar } from './field.js'; +import { + Field, + FieldConst, + FieldType, + FieldVar, + readVarMessage, +} from './field.js'; import { Bool as B } from '../provable/field-bigint.js'; import { defineBinable } from '../bindings/lib/binable.js'; import { NonNegativeInteger } from 'src/bindings/crypto/non-negative.js'; @@ -179,8 +185,10 @@ class Bool { let value: FieldConst; if (this.isConstant()) { value = this.value[1]; - } else { + } else if (Snarky.run.inProverBlock()) { value = Snarky.field.readVar(this.value); + } else { + throw Error(readVarMessage('toBoolean', 'b', 'Bool')); } return FieldConst.equal(value, FieldConst[1]); } @@ -368,7 +376,7 @@ function toBoolean(x: boolean | Bool): boolean { if (typeof x === 'boolean') { return x; } - return (x as Bool).toBoolean(); + return x.toBoolean(); } // TODO: This is duplicated diff --git a/src/lib/field.ts b/src/lib/field.ts index 5a272b2d62..12664bc184 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -18,6 +18,7 @@ export { isField, withMessage, readVarMessage, + toConstantField, }; type FieldConst = Uint8Array; @@ -200,23 +201,7 @@ class Field { } #toConstant(name: string): ConstantField { - // if this is a constant, return it - if (this.isConstant()) return this; - - // a non-constant can only appear inside a checked computation. everything else is a bug. - assert( - inCheckedComputation(), - 'variables only exist inside checked computations' - ); - - // if we are inside an asProver or witness block, read the variable's value and return it as constant - if (Snarky.run.inProverBlock()) { - let value = Snarky.field.readVar(this.value); - return new Field(value) as ConstantField; - } - - // otherwise, calling `toConstant()` is likely a mistake. throw a helpful error message. - throw Error(readVarMessage(name, 'x', 'field element')); + return toConstantField(this, name, 'x', 'field element'); } /** @@ -1283,6 +1268,31 @@ function withMessage(error: unknown, message?: string) { return error; } +function toConstantField( + x: Field, + methodName: string, + varName: string, + varDescription: string +): ConstantField { + // if this is a constant, return it + if (x.isConstant()) return x; + + // a non-constant can only appear inside a checked computation. everything else is a bug. + assert( + inCheckedComputation(), + 'variables only exist inside checked computations' + ); + + // if we are inside an asProver or witness block, read the variable's value and return it as constant + if (Snarky.run.inProverBlock()) { + let value = Snarky.field.readVar(x.value); + return new Field(value) as ConstantField; + } + + // otherwise, calling `toConstant()` is likely a mistake. throw a helpful error message. + throw Error(readVarMessage(methodName, varName, varDescription)); +} + function readVarMessage( methodName: string, varName: string, diff --git a/src/lib/scalar.ts b/src/lib/scalar.ts index 89736a8fb6..dcef9e20d1 100644 --- a/src/lib/scalar.ts +++ b/src/lib/scalar.ts @@ -6,6 +6,9 @@ import { Bool } from './bool.js'; export { Scalar, ScalarConst, unshift, shift }; +// internal API +export { constantScalarToBigint }; + type BoolVar = FieldVar; type ScalarConst = Uint8Array; @@ -106,18 +109,8 @@ class Scalar { // operations on constant scalars - // TODO: this is a static method so that it works on ml scalars as well - static #assertConstantStatic(x: Scalar, name: string): Fq { - if (x.constantValue === undefined) - throw Error( - `Scalar.${name}() is not available in provable code. -That means it can't be called in a @method or similar environment, and there's no alternative implemented to achieve that.` - ); - return ScalarConst.toBigint(x.constantValue); - } - #assertConstant(name: string) { - return Scalar.#assertConstantStatic(this, name); + return constantScalarToBigint(this, `Scalar.${name}`); } /** @@ -138,7 +131,7 @@ That means it can't be called in a @method or similar environment, and there's n */ add(y: Scalar) { let x = this.#assertConstant('add'); - let y0 = Scalar.#assertConstantStatic(y, 'add'); + let y0 = y.#assertConstant('add'); let z = Fq.add(x, y0); return Scalar.from(z); } @@ -150,7 +143,7 @@ That means it can't be called in a @method or similar environment, and there's n */ sub(y: Scalar) { let x = this.#assertConstant('sub'); - let y0 = Scalar.#assertConstantStatic(y, 'sub'); + let y0 = y.#assertConstant('sub'); let z = Fq.sub(x, y0); return Scalar.from(z); } @@ -162,7 +155,7 @@ That means it can't be called in a @method or similar environment, and there's n */ mul(y: Scalar) { let x = this.#assertConstant('mul'); - let y0 = Scalar.#assertConstantStatic(y, 'mul'); + let y0 = y.#assertConstant('mul'); let z = Fq.mul(x, y0); return Scalar.from(z); } @@ -175,7 +168,7 @@ That means it can't be called in a @method or similar environment, and there's n */ div(y: Scalar) { let x = this.#assertConstant('div'); - let y0 = Scalar.#assertConstantStatic(y, 'div'); + let y0 = y.#assertConstant('div'); let z = Fq.div(x, y0); if (z === undefined) throw Error('Scalar.div(): Division by zero'); return Scalar.from(z); @@ -296,7 +289,7 @@ That means it can't be called in a @method or similar environment, and there's n * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Scalar. */ static toJSON(x: Scalar) { - let s = Scalar.#assertConstantStatic(x, 'toJSON'); + let s = x.#assertConstant('toJSON'); return s.toString(); } @@ -360,3 +353,12 @@ function constToBigint(x: ScalarConst): Fq { function constFromBigint(x: Fq) { return Uint8Array.from(Fq.toBytes(x)); } + +function constantScalarToBigint(s: Scalar, name: string) { + if (s.constantValue === undefined) + throw Error( + `${name}() is not available in provable code. +That means it can't be called in a @method or similar environment, and there's no alternative implemented to achieve that.` + ); + return ScalarConst.toBigint(s.constantValue); +} diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 094590222d..e26e68ca2b 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -12,6 +12,8 @@ import { PublicKey as PublicKeyBigint, } from '../provable/curve-bigint.js'; import { prefixes } from '../bindings/crypto/constants.js'; +import { constantScalarToBigint } from './scalar.js'; +import { toConstantField } from './field.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -54,7 +56,7 @@ class PrivateKey extends CircuitValue { * Convert this {@link PrivateKey} to a bigint */ toBigInt() { - return this.s.toBigInt(); + return constantScalarToBigint(this.s, 'PrivateKey.toBigInt'); } /** @@ -100,7 +102,9 @@ class PrivateKey extends CircuitValue { * @returns a base58 encoded string */ static toBase58(privateKey: { s: Scalar }) { - return PrivateKeyBigint.toBase58(privateKey.s.toBigInt()); + return PrivateKeyBigint.toBase58( + constantScalarToBigint(privateKey.s, 'PrivateKey.toBase58') + ); } } @@ -196,6 +200,7 @@ class PublicKey extends CircuitValue { * @returns a base58 encoded {@link PublicKey} */ static toBase58({ x, isOdd }: PublicKey) { + x = toConstantField(x, 'toBase58', 'pk', 'public key'); return PublicKeyBigint.toBase58({ x: x.toBigInt(), isOdd: BoolBigint(isOdd.toBoolean()), From 18bfbeea468f0c07d68b2bfd7df91fb86c58b9de Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 10:01:06 +0200 Subject: [PATCH 06/13] fix private class method bug caused by using Object.create --- src/lib/field.ts | 15 +++++---------- src/lib/hash-input.unit-test.ts | 10 ++++++---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 12664bc184..3009a7367b 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -1183,7 +1183,7 @@ class Field { * */ static toBytes(x: Field) { - return [...x.#toConstant('toBytes').value[1]]; + return FieldBinable.toBytes(x); } /** @@ -1226,17 +1226,12 @@ class Field { const FieldBinable = defineBinable({ toBytes(t: Field) { - return Field.toBytes(t); + return [...toConstantField(t, 'toBytes').value[1]]; }, readBytes(bytes, offset) { let uint8array = new Uint8Array(32); uint8array.set(bytes.slice(offset, offset + 32)); - return [ - Object.assign(Object.create(new Field(1).constructor.prototype), { - value: [0, uint8array], - }) as Field, - offset + 32, - ]; + return [new Field(uint8array), offset + 32]; }, }); @@ -1271,8 +1266,8 @@ function withMessage(error: unknown, message?: string) { function toConstantField( x: Field, methodName: string, - varName: string, - varDescription: string + varName = 'x', + varDescription = 'field element' ): ConstantField { // if this is a constant, return it if (x.isConstant()) return x; diff --git a/src/lib/hash-input.unit-test.ts b/src/lib/hash-input.unit-test.ts index 6277ac13cc..247029c2c7 100644 --- a/src/lib/hash-input.unit-test.ts +++ b/src/lib/hash-input.unit-test.ts @@ -107,11 +107,13 @@ function testInput( // console.log('json', json); let input1 = MlHashInput.from(toInputOcaml(JSON.stringify(json))); let input2 = Module.toInput(value); - // console.log('snarkyjs', JSON.stringify(input2)); + let input1Json = JSON.stringify(input1); + let input2Json = JSON.stringify(input2); + // console.log('snarkyjs', input2Json); // console.log(); - // console.log('protocol', JSON.stringify(input1)); - let ok1 = JSON.stringify(input2) === JSON.stringify(input1); - expect(JSON.stringify(input2)).toEqual(JSON.stringify(input1)); + // console.log('protocol', input1Json); + let ok1 = input1Json === input2Json; + expect(input2Json).toEqual(input1Json); // console.log('ok?', ok1); let fields1 = MlFieldConstArray.from( hashInputFromJson.packInput(MlHashInput.to(input1)) From 376eedd14efac8ee84d322fb07d5b2063879bdcd Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 10:05:35 +0200 Subject: [PATCH 07/13] remove unused imports --- src/lib/account_update.ts | 4 +--- src/lib/account_update.unit-test.ts | 1 - src/lib/zkapp.ts | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index e73156c430..6c9bf9f7c4 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -3,11 +3,10 @@ import { FlexibleProvable, provable, provablePure, - Struct, } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; -import { Ledger, Pickles, Test } from '../snarky.js'; +import { Pickles, Test } from '../snarky.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; import { Types, @@ -30,7 +29,6 @@ import { hashWithPrefix, packToFields } from './hash.js'; import { prefixes } from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; import { assert } from './errors.js'; -import { Ml } from './ml/conversion.js'; import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index de3a6aa028..62f33962b5 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -1,5 +1,4 @@ import { - Ledger, AccountUpdate, PrivateKey, Field, diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index eef40b3c75..cd141ae8d0 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1,4 +1,3 @@ -import { Types } from '../bindings/mina-transaction/types.js'; import { Gate, Pickles, ProvablePure } from '../snarky.js'; import { Field, Bool } from './core.js'; import { From cc5182f8e90162e8b8403b615db51dafe8ab9bfd Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 10:21:54 +0200 Subject: [PATCH 08/13] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e83e6f2e1f..c099a5d3a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `Group` operations now generate a different set of constraints. This breaks deployed contracts, because the circuit changed. https://github.com/o1-labs/snarkyjs/pull/967 +### Changed + +- Improve error message `Can't evaluate prover code outside an as_prover block` https://github.com/o1-labs/snarkyjs/pull/998 + ## [0.11.0](https://github.com/o1-labs/snarkyjs/compare/a632313a...3fbd9678e) ### Breaking changes From 1dad70ea552d8132d52c21e676f1ef13b41729f2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 10:24:34 +0200 Subject: [PATCH 09/13] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 47d9b3baa7..fe3b5e6992 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 47d9b3baa7bfe8b3f8f5291eba1a05c1480e3a2d +Subproject commit fe3b5e699279d19a0676102d90026c50e060e7e5 From 086bd7786c9b07f87abcd3cac7ba14cfeecf4dd6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 10:28:41 +0200 Subject: [PATCH 10/13] fix grammar --- src/lib/field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 3009a7367b..ee4b9dc66e 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -1300,7 +1300,7 @@ which only carries actual values during proving, but not during compiling. Also, reading out JS values means that whatever you're doing with those values will no longer be linked to the original variable in the proof, which makes this pattern prone to security holes. -You can check whether your ${varDescription} is a variable or a constant using ${varName}.isConstant(). +You can check whether your ${varDescription} is a variable or a constant by using ${varName}.isConstant(). To inspect values for debugging, use Provable.log(${varName}). For more advanced use cases, there is \`Provable.asProver(() => { ... })\` which allows you to use ${varName}.${methodName}() inside the callback. From 531034da365b859b6e986b453aa8470143005354 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 12:18:16 +0200 Subject: [PATCH 11/13] fix cloning of primitives --- src/lib/circuit_value.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 0481c95d93..55d94531ce 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -1,6 +1,6 @@ import 'reflect-metadata'; import { ProvablePure } from '../snarky.js'; -import { Field, Bool } from './core.js'; +import { Field, Bool, Scalar, Group } from './core.js'; import { provable, provablePure, @@ -451,21 +451,26 @@ function Struct< return Struct_ as any; } -let primitives = new Set(['Field', 'Bool', 'Scalar', 'Group']); +let primitives = new Set([Field, Bool, Scalar, Group]); +function isPrimitive(obj: any) { + for (let P of primitives) { + if (obj instanceof P) return true; + } +} -// FIXME: the logic in here to check for obj.constructor.name actually doesn't work -// something that works is Field(1).constructor === obj.constructor etc function cloneCircuitValue(obj: T): T { // primitive JS types and functions aren't cloned if (typeof obj !== 'object' || obj === null) return obj; // HACK: callbacks, account udpates if ( - ['GenericArgument', 'Callback'].includes((obj as any).constructor?.name) + obj.constructor?.name.includes('GenericArgument') || + obj.constructor?.name.includes('Callback') ) { return obj; } - if (['AccountUpdate'].includes((obj as any).constructor?.name)) { + if (obj.constructor?.name.includes('AccountUpdate')) { + console.log('cloning', obj.constructor.name); return (obj as any).constructor.clone(obj); } @@ -480,7 +485,9 @@ function cloneCircuitValue(obj: T): T { if (ArrayBuffer.isView(obj)) return new (obj.constructor as any)(obj); // snarkyjs primitives aren't cloned - if (primitives.has((obj as any).constructor.name)) return obj; + if (isPrimitive(obj)) { + return obj; + } // cloning strategy that works for plain objects AND classes whose constructor only assigns properties let propertyDescriptors: Record = {}; From 7a44013c270e306c2ffefda0a82af40ee090fac4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 12:19:04 +0200 Subject: [PATCH 12/13] remove debug log --- src/lib/circuit_value.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 55d94531ce..796d51be12 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -470,7 +470,6 @@ function cloneCircuitValue(obj: T): T { return obj; } if (obj.constructor?.name.includes('AccountUpdate')) { - console.log('cloning', obj.constructor.name); return (obj as any).constructor.clone(obj); } From 8d6fe80637a70dbfd5eaeb727aa031ded539116b Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Jun 2023 14:13:10 +0200 Subject: [PATCH 13/13] explicitly return false --- src/lib/circuit_value.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 796d51be12..b5d6a88112 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -456,6 +456,7 @@ function isPrimitive(obj: any) { for (let P of primitives) { if (obj instanceof P) return true; } + return false; } function cloneCircuitValue(obj: T): T {