diff --git a/CHANGELOG.md b/CHANGELOG.md index 955c8636c4..75b0d05627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/74948acac...HEAD) +### Breaking changes + +- Change `{SmartContract,ZkProgram}.analyzeMethods()` to be async https://github.com/o1-labs/o1js/pull/1450 + - `Provable.runAndCheck()`, `Provable.constraintSystem()` and `{SmartContract,ZkProgram}.digest()` are also async now + - These changes were made to add internal support for async circuits + - `Provable.runAndCheckSync()` added and immediately deprecated for a smoother upgrade path for tests +- `Reducer.reduce()` requires the maximum number of actions per method as an explicit (optional) argument https://github.com/o1-labs/o1js/pull/1450 + - The default value is 1 and should work for most existing contracts + ### Added - Internal benchmarking tooling to keep track of performance https://github.com/o1-labs/o1js/pull/1481 diff --git a/benchmarks/ecdsa.ts b/benchmarks/ecdsa.ts index 18b8db3666..5bc29a9a87 100644 --- a/benchmarks/ecdsa.ts +++ b/benchmarks/ecdsa.ts @@ -24,11 +24,11 @@ const EcdsaBenchmark = benchmark( 'ecdsa', async (tic, toc) => { tic('build constraint system'); - keccakAndEcdsa.analyzeMethods(); + await keccakAndEcdsa.analyzeMethods(); toc(); tic('witness generation'); - Provable.runAndCheck(() => { + await Provable.runAndCheck(() => { let message_ = Provable.witness(Bytes32.provable, () => message); let signature_ = Provable.witness(Ecdsa.provable, () => signature); let publicKey_ = Provable.witness(Secp256k1.provable, () => publicKey); diff --git a/benchmarks/tsconfig.json b/benchmarks/tsconfig.json index 3d5c7b6534..98fbc7dcbe 100644 --- a/benchmarks/tsconfig.json +++ b/benchmarks/tsconfig.json @@ -3,6 +3,7 @@ "include": ["."], "exclude": [], "compilerOptions": { + "noEmit": true, // no build output, we just want type checking "rootDir": "..", "baseUrl": "..", "paths": { diff --git a/package.json b/package.json index 0c862c3ff5..18c56fa056 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "build:update-bindings": "./src/bindings/scripts/update-o1js-bindings.sh", "build:wasm": "./src/bindings/scripts/update-wasm-and-types.sh", "build:web": "rimraf ./dist/web && node src/build/build-web.js", - "build:examples": "npm run build && rimraf ./dist/examples && npx tsc -p tsconfig.examples.json", + "build:examples": "npm run build && rimraf ./dist/examples && npx tsc -p tsconfig.examples.json && npx tsc -p benchmarks/tsconfig.json", "build:docs": "npx typedoc --tsconfig ./tsconfig.web.json", "prepublish:web": "NODE_ENV=production node src/build/build-web.js", "prepublish:node": "node src/build/copy-artifacts.js && rimraf ./dist/node && npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js && NODE_ENV=production node src/build/build-node.js", diff --git a/src/bindings b/src/bindings index 6b0be7d300..9ef2916c93 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 6b0be7d3005f93f784374c51efadaceb21246241 +Subproject commit 9ef2916c93ac93c7a64f80934178d6ab542b47b0 diff --git a/src/examples/benchmarks/foreign-field.ts b/src/examples/benchmarks/foreign-field.ts index fb32439e0f..30190324ef 100644 --- a/src/examples/benchmarks/foreign-field.ts +++ b/src/examples/benchmarks/foreign-field.ts @@ -21,11 +21,11 @@ main(); console.timeEnd('running constant version'); console.time('running witness generation & checks'); -Provable.runAndCheck(main); +await Provable.runAndCheck(main); console.timeEnd('running witness generation & checks'); console.time('creating constraint system'); -let cs = Provable.constraintSystem(main); +let cs = await Provable.constraintSystem(main); console.timeEnd('creating constraint system'); console.log(cs.summary()); diff --git a/src/examples/benchmarks/hash-witness.ts b/src/examples/benchmarks/hash-witness.ts index 08adaf87f7..69f5107a33 100644 --- a/src/examples/benchmarks/hash-witness.ts +++ b/src/examples/benchmarks/hash-witness.ts @@ -19,5 +19,5 @@ function main(nMuls: number) { } tic('run and check'); -Provable.runAndCheck(() => main(nPermutations)); +await Provable.runAndCheck(() => main(nPermutations)); toc(); diff --git a/src/examples/benchmarks/keccak-witness.ts b/src/examples/benchmarks/keccak-witness.ts index cedd6982ca..e4509c5019 100644 --- a/src/examples/benchmarks/keccak-witness.ts +++ b/src/examples/benchmarks/keccak-witness.ts @@ -3,7 +3,7 @@ import { Hash, Bytes, Provable } from 'o1js'; let Bytes32 = Bytes(32); console.time('keccak witness'); -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { let bytes = Provable.witness(Bytes32.provable, () => Bytes32.random()); Hash.Keccak256.hash(bytes); }); diff --git a/src/examples/benchmarks/mul-web.ts b/src/examples/benchmarks/mul-web.ts index 43c977ff74..8f964a47d0 100644 --- a/src/examples/benchmarks/mul-web.ts +++ b/src/examples/benchmarks/mul-web.ts @@ -20,8 +20,8 @@ function main(nMuls: number) { } } -function getRows(nMuls: number) { - let { rows } = Provable.constraintSystem(() => main(nMuls)); +async function getRows(nMuls: number) { + let { rows } = await Provable.constraintSystem(() => main(nMuls)); return rows; } @@ -52,7 +52,7 @@ function picklesCircuit(nMuls: number) { // the script -console.log('circuit size (without pickles overhead)', getRows(nMuls)); +console.log('circuit size (without pickles overhead)', await getRows(nMuls)); if (withPickles) { let circuit = picklesCircuit(nMuls); diff --git a/src/examples/benchmarks/mul-witness.ts b/src/examples/benchmarks/mul-witness.ts index 17e2165fbd..947360cbd3 100644 --- a/src/examples/benchmarks/mul-witness.ts +++ b/src/examples/benchmarks/mul-witness.ts @@ -19,5 +19,5 @@ function main(nMuls: number) { } tic('run and check'); -Provable.runAndCheck(() => main(nMuls)); +await Provable.runAndCheck(() => main(nMuls)); toc(); diff --git a/src/examples/benchmarks/mul.ts b/src/examples/benchmarks/mul.ts index 3f92f8c27c..ebe407a3e6 100644 --- a/src/examples/benchmarks/mul.ts +++ b/src/examples/benchmarks/mul.ts @@ -19,8 +19,8 @@ function main(nMuls: number) { } } -function getRows(nMuls: number) { - let { rows } = Provable.constraintSystem(() => main(nMuls)); +async function getRows(nMuls: number) { + let { rows } = await Provable.constraintSystem(() => main(nMuls)); return rows; } @@ -48,7 +48,7 @@ function picklesCircuit(nMuls: number) { }); } -console.log('circuit size (without pickles overhead)', getRows(nMuls)); +console.log('circuit size (without pickles overhead)', await getRows(nMuls)); if (withPickles) { let circuit = picklesCircuit(nMuls); diff --git a/src/examples/constraint-system.ts b/src/examples/constraint-system.ts index f2da63ad3b..e6755f3ba4 100644 --- a/src/examples/constraint-system.ts +++ b/src/examples/constraint-system.ts @@ -2,13 +2,15 @@ import { Field, Poseidon, Provable } from 'o1js'; let hash = Poseidon.hash([Field(1), Field(-1)]); -let { rows, digest, publicInputSize, print } = Provable.constraintSystem(() => { - let x = Provable.witness(Field, () => Field(1)); - let y = Provable.witness(Field, () => Field(-1)); - x.add(y).assertEquals(Field(0)); - let z = Poseidon.hash([x, y]); - z.assertEquals(hash); -}); +let { rows, digest, publicInputSize, print } = await Provable.constraintSystem( + () => { + let x = Provable.witness(Field, () => Field(1)); + let y = Provable.witness(Field, () => Field(-1)); + x.add(y).assertEquals(Field(0)); + let z = Poseidon.hash([x, y]); + z.assertEquals(hash); + } +); print(); console.log({ rows, digest, publicInputSize }); diff --git a/src/examples/crypto/ecdsa/run.ts b/src/examples/crypto/ecdsa/run.ts index c3acaa19c2..c342a6433d 100644 --- a/src/examples/crypto/ecdsa/run.ts +++ b/src/examples/crypto/ecdsa/run.ts @@ -13,12 +13,12 @@ let signature = Ecdsa.sign(message.toBytes(), privateKey.toBigInt()); // investigate the constraint system generated by ECDSA verify console.time('ecdsa verify only (build constraint system)'); -let csEcdsa = ecdsa.analyzeMethods(); +let csEcdsa = await ecdsa.analyzeMethods(); console.timeEnd('ecdsa verify only (build constraint system)'); console.log(csEcdsa.verifySignedHash.summary()); console.time('keccak + ecdsa verify (build constraint system)'); -let cs = keccakAndEcdsa.analyzeMethods(); +let cs = await keccakAndEcdsa.analyzeMethods(); console.timeEnd('keccak + ecdsa verify (build constraint system)'); console.log(cs.verifyEcdsa.summary()); diff --git a/src/examples/crypto/foreign-field.ts b/src/examples/crypto/foreign-field.ts index bffdae7654..b71af14c6e 100644 --- a/src/examples/crypto/foreign-field.ts +++ b/src/examples/crypto/foreign-field.ts @@ -96,7 +96,7 @@ class MyContract extends SmartContract { this.x.set(x.assertAlmostReduced()); } } -MyContract.analyzeMethods(); // works +await MyContract.analyzeMethods(); // works // btw - we support any finite field up to 259 bits. for example, the seqp256k1 base field: let Fseqp256k1 = createForeignField((1n << 256n) - (1n << 32n) - 0b1111010001n); diff --git a/src/examples/crypto/sha256/run.ts b/src/examples/crypto/sha256/run.ts index b1d72e3fa6..f506f7fe5d 100644 --- a/src/examples/crypto/sha256/run.ts +++ b/src/examples/crypto/sha256/run.ts @@ -6,7 +6,7 @@ console.timeEnd('compile'); let preimage = Bytes12.fromString('hello world!'); -console.log('sha256 rows:', SHA256Program.analyzeMethods().sha256.rows); +console.log('sha256 rows:', (await SHA256Program.analyzeMethods()).sha256.rows); console.time('prove'); let proof = await SHA256Program.sha256(preimage); diff --git a/src/examples/encryption.ts b/src/examples/encryption.ts index 245bc28708..e6c0b84d7c 100644 --- a/src/examples/encryption.ts +++ b/src/examples/encryption.ts @@ -30,7 +30,7 @@ console.log(`Recovered message: "${decryptedMessage}"`); // the same but in a checked computation -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { // encrypt let cipherText = Encryption.encrypt(messageFields, publicKey); @@ -76,7 +76,7 @@ console.log(`Recovered message: "${decryptedMessage}"`); // the same but in a checked computation -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { // encrypt let cipherText = Encryption.encrypt(messageFields, publicKey); diff --git a/src/examples/internals/advanced-provable-types.ts b/src/examples/internals/advanced-provable-types.ts index dc3b95b530..6f4334a207 100644 --- a/src/examples/internals/advanced-provable-types.ts +++ b/src/examples/internals/advanced-provable-types.ts @@ -61,7 +61,7 @@ expect(accountUpdateRecovered.lazyAuthorization).not.toEqual( * -) witness() and asProver() blocks are executed * -) constraints are checked; failing assertions throw an error */ -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { /** * Provable.witness() is used to introduce all values to the circuit which are not hard-coded constants. * @@ -99,7 +99,7 @@ Provable.runAndCheck(() => { * -) fields don't have actual values attached to them; they're purely abstract variables * -) constraints are not checked */ -let result = Provable.constraintSystem(() => { +let result = await Provable.constraintSystem(() => { /** * In compile mode, witness() returns * - abstract variables without values for fields @@ -140,7 +140,7 @@ console.log( * * This is why we have this custom way of witnessing account updates, with the `skipCheck` option. */ -result = Provable.constraintSystem(() => { +result = await Provable.constraintSystem(() => { let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness( Empty, () => ({ accountUpdate, result: undefined }), @@ -156,7 +156,7 @@ console.log( * To relate an account update to the hash which is the public input, we need to perform the hash in-circuit. * This is takes several 100 constraints, and is basically the minimal size of a zkApp method. */ -result = Provable.constraintSystem(() => { +result = await Provable.constraintSystem(() => { let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness( Empty, () => ({ accountUpdate, result: undefined }), diff --git a/src/examples/matrix-mul.ts b/src/examples/matrix-mul.ts index 63447cc9da..8b9ee39412 100644 --- a/src/examples/matrix-mul.ts +++ b/src/examples/matrix-mul.ts @@ -55,9 +55,9 @@ function circuit(): Field[][] { return matrixMul(x, y); } -let { rows } = Provable.constraintSystem(circuit); +let { rows } = await Provable.constraintSystem(circuit); let result: Field[][]; -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { let result_ = circuit(); Provable.asProver(() => { result = result_.map((x) => x.map((y) => y.toConstant())); diff --git a/src/examples/simple-zkapp.ts b/src/examples/simple-zkapp.ts index 8d6e855eb9..e2768fbe07 100644 --- a/src/examples/simple-zkapp.ts +++ b/src/examples/simple-zkapp.ts @@ -92,7 +92,7 @@ if (doProofs) { await SimpleZkapp.compile(); console.timeEnd('compile'); } else { - SimpleZkapp.analyzeMethods(); + await SimpleZkapp.analyzeMethods(); } console.log('deploy'); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 2b6fae11b5..722caf8a69 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -21,9 +21,9 @@ let tx, balances, oldBalances; let { Dex, DexTokenHolder, getTokenBalances } = createDex(); -TokenContract.analyzeMethods(); -DexTokenHolder.analyzeMethods(); -Dex.analyzeMethods(); +await TokenContract.analyzeMethods(); +await DexTokenHolder.analyzeMethods(); +await Dex.analyzeMethods(); if (proofsEnabled) { tic('compile (token)'); diff --git a/src/examples/zkapps/dex/run-live.ts b/src/examples/zkapps/dex/run-live.ts index 80f915182c..338a4c2931 100644 --- a/src/examples/zkapps/dex/run-live.ts +++ b/src/examples/zkapps/dex/run-live.ts @@ -47,9 +47,9 @@ if (!useCustomLocalNetwork) { await ensureFundedAccount(senderKey.toBase58()); } -TokenContract.analyzeMethods(); -DexTokenHolder.analyzeMethods(); -Dex.analyzeMethods(); +await TokenContract.analyzeMethods(); +await DexTokenHolder.analyzeMethods(); +await Dex.analyzeMethods(); tic('compile (token)'); await TokenContract.compile(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index 0244ae541e..ec164b128a 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -24,7 +24,7 @@ console.log('TOKEN X ID\t', TokenId.toBase58(tokenIds.X)); console.log('TOKEN Y ID\t', TokenId.toBase58(tokenIds.Y)); console.log('-------------------------------------------------'); -TokenContract.analyzeMethods(); +await TokenContract.analyzeMethods(); if (proofsEnabled) { console.log('compile (token)...'); await TokenContract.compile(); @@ -57,8 +57,8 @@ async function main({ withVesting }: { withVesting: boolean }) { let { Dex, DexTokenHolder, getTokenBalances } = createDex(options); // analyze methods for quick error feedback - DexTokenHolder.analyzeMethods(); - Dex.analyzeMethods(); + await DexTokenHolder.analyzeMethods(); + await Dex.analyzeMethods(); if (proofsEnabled) { // compile & deploy all zkApps diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 366ca2c467..87e409abc5 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -38,8 +38,8 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { let { Dex, DexTokenHolder, getTokenBalances } = createDex(options); // analyze methods for quick error feedback - DexTokenHolder.analyzeMethods(); - Dex.analyzeMethods(); + await DexTokenHolder.analyzeMethods(); + await Dex.analyzeMethods(); if (proofsEnabled) { // compile & deploy all zkApps @@ -249,8 +249,8 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { } = createDex(options); // analyze methods for quick error feedback - DexTokenHolder.analyzeMethods(); - Dex.analyzeMethods(); + await DexTokenHolder.analyzeMethods(); + await Dex.analyzeMethods(); // compile & deploy all zkApps console.log('compile (token contract)...'); diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 86c38af844..1740de72b1 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -119,7 +119,7 @@ let zkapp = new ActionsContract(zkappAddress); await ActionsContract.compile(); console.log( `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - ActionsContract.analyzeMethods().assertContainsAddress.rows + (await ActionsContract.analyzeMethods()).assertContainsAddress.rows ); let deployTx = await Mina.transaction(sender, () => zkapp.deploy()); await deployTx.sign([senderKey, zkappKey]).send(); diff --git a/src/examples/zkapps/reducer/map.ts b/src/examples/zkapps/reducer/map.ts index 890555be92..8619d9c347 100644 --- a/src/examples/zkapps/reducer/map.ts +++ b/src/examples/zkapps/reducer/map.ts @@ -86,7 +86,7 @@ let k = 1 << 4; let Local = Mina.LocalBlockchain(); Mina.setActiveInstance(Local); -let cs = StorageContract.analyzeMethods(); +let cs = await StorageContract.analyzeMethods(); console.log(`method size for a "mapping" contract with ${k} entries`); console.log('get rows:', cs['get'].rows); diff --git a/src/examples/zkapps/reducer/reducer-composite.ts b/src/examples/zkapps/reducer/reducer-composite.ts index 20495848fe..1ecdc26ef7 100644 --- a/src/examples/zkapps/reducer/reducer-composite.ts +++ b/src/examples/zkapps/reducer/reducer-composite.ts @@ -7,7 +7,6 @@ import { SmartContract, Mina, AccountUpdate, - isReady, Bool, Struct, Reducer, @@ -16,8 +15,6 @@ import { import assert from 'node:assert/strict'; import { getProfiler } from '../../utils/profiler.js'; -await isReady; - class MaybeIncrement extends Struct({ isIncrement: Bool, otherData: Field, diff --git a/src/examples/zkapps/reducer/reducer.ts b/src/examples/zkapps/reducer/reducer.ts index a9bbbb063d..d5aed207b4 100644 --- a/src/examples/zkapps/reducer/reducer.ts +++ b/src/examples/zkapps/reducer/reducer.ts @@ -83,7 +83,7 @@ if (doProofs) { // TODO: if we don't do this, then `analyzeMethods()` will be called during `runUnchecked()` in the tx callback below, // which currently fails due to `finalize_is_running` in snarky not resetting internal state, and instead setting is_running unconditionally to false, // so we can't nest different snarky circuit runners - CounterZkapp.analyzeMethods(); + await CounterZkapp.analyzeMethods(); } console.log('deploy'); diff --git a/src/examples/zkapps/sudoku/index.ts b/src/examples/zkapps/sudoku/index.ts index 639d3ab7b2..c626f175c2 100644 --- a/src/examples/zkapps/sudoku/index.ts +++ b/src/examples/zkapps/sudoku/index.ts @@ -13,7 +13,7 @@ const zkAppAddress = zkAppPrivateKey.toPublicKey(); // create an instance of the smart contract const zkApp = new SudokuZkApp(zkAppAddress); -let methods = SudokuZkApp.analyzeMethods(); +let methods = await SudokuZkApp.analyzeMethods(); console.log( 'first 5 gates of submitSolution method:', ...methods.submitSolution.gates.slice(0, 5) diff --git a/src/examples/zkprogram/gadgets.ts b/src/examples/zkprogram/gadgets.ts index 1e2508c804..d1bc8e5d50 100644 --- a/src/examples/zkprogram/gadgets.ts +++ b/src/examples/zkprogram/gadgets.ts @@ -1,6 +1,6 @@ import { Field, Provable, Gadgets, ZkProgram } from 'o1js'; -let cs = Provable.constraintSystem(() => { +let cs = await Provable.constraintSystem(() => { let f = Provable.witness(Field, () => Field(12)); let res1 = Gadgets.rotate64(f, 2, 'left'); diff --git a/src/index.ts b/src/index.ts index 6445ab3706..aff4570bca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -110,8 +110,7 @@ export { MerkleMap, MerkleMapWitness } from './lib/merkle-map.js'; export { Nullifier } from './lib/nullifier.js'; -import { ExperimentalZkProgram, ZkProgram } from './lib/proof-system.js'; -export { ZkProgram }; +export { ZkProgram } from './lib/proof-system.js'; export { Crypto } from './lib/crypto.js'; @@ -132,10 +131,6 @@ const Experimental_ = { * (Not unstable in the sense that they are less functional or tested than other parts.) */ namespace Experimental { - /** @deprecated `ZkProgram` has moved out of the Experimental namespace and is now directly available as a top-level import `ZkProgram`. - * The old `Experimental.ZkProgram` API has been deprecated in favor of the new `ZkProgram` top-level import. - */ - export let ZkProgram = ExperimentalZkProgram; export let memoizeWitness = Experimental_.memoizeWitness; } diff --git a/src/lib/account-update.unit-test.ts b/src/lib/account-update.unit-test.ts index 845cd25a48..cd7eb0d13c 100644 --- a/src/lib/account-update.unit-test.ts +++ b/src/lib/account-update.unit-test.ts @@ -6,7 +6,6 @@ import { Mina, Int64, Types, - Provable, } from '../index.js'; import { Test } from '../snarky.js'; import { expect } from 'expect'; @@ -52,18 +51,15 @@ function createAccountUpdate() { { let accountUpdate = createAccountUpdate(); - // TODO remove restriction "This function can't be run outside of a checked computation." - Provable.runAndCheck(() => { - let hash = accountUpdate.hash(); + let hash = accountUpdate.hash(); - // if we clone the accountUpdate, hash should be the same - let accountUpdate2 = AccountUpdate.clone(accountUpdate); - expect(accountUpdate2.hash()).toEqual(hash); + // if we clone the accountUpdate, hash should be the same + let accountUpdate2 = AccountUpdate.clone(accountUpdate); + expect(accountUpdate2.hash()).toEqual(hash); - // if we change something on the cloned accountUpdate, the hash should become different - AccountUpdate.setValue(accountUpdate2.update.appState[0], Field(1)); - expect(accountUpdate2.hash()).not.toEqual(hash); - }); + // if we change something on the cloned accountUpdate, the hash should become different + AccountUpdate.setValue(accountUpdate2.update.appState[0], Field(1)); + expect(accountUpdate2.hash()).not.toEqual(hash); } // converts account update to a public input that's consistent with the ocaml implementation diff --git a/src/lib/circuit-value.test.ts b/src/lib/circuit-value.test.ts index d45c8acf8a..5ee8ceac2a 100644 --- a/src/lib/circuit-value.test.ts +++ b/src/lib/circuit-value.test.ts @@ -15,7 +15,7 @@ describe('circuit', () => { }); it('Provable.if in snark', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let x = Provable.witness(Int64, () => Int64.from(-1)); let y = Provable.witness(Int64, () => Int64.from(-2)); let b = Provable.witness(Bool, () => Bool(true)); @@ -81,7 +81,7 @@ describe('circuit', () => { it('Provable.assertEqual', () => { const FieldAndBool = Struct({ x: Field, b: Bool }); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let x = Provable.witness(Field, () => Field(1)); let b = Provable.witness(Bool, () => Bool(true)); @@ -123,7 +123,7 @@ describe('circuit', () => { }); } - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let x = Provable.witness(Field, () => Field(1)); let b = Provable.witness(Bool, () => Bool(true)); let pk = Provable.witness(PublicKey, () => pk1); diff --git a/src/lib/circuit-value.unit-test.ts b/src/lib/circuit-value.unit-test.ts index 4c7b194f6e..f084c9ffee 100644 --- a/src/lib/circuit-value.unit-test.ts +++ b/src/lib/circuit-value.unit-test.ts @@ -60,12 +60,12 @@ let restored = type.fromFields(fields, aux); expect(JSON.stringify(restored)).toEqual(original); // check -Provable.runAndCheck(() => { +await Provable.runAndCheck(() => { type.check(value); }); // should fail `check` if `check` of subfields doesn't pass -expect(() => +await expect(() => Provable.runAndCheck(() => { let x = Provable.witness(type, () => ({ ...value, @@ -76,7 +76,7 @@ expect(() => ], })); }) -).toThrow(`Constraint unsatisfied`); +).rejects.toThrow(`Constraint unsatisfied`); // class version of `provable` class MyStruct extends Struct({ diff --git a/src/lib/field.unit-test.ts b/src/lib/field.unit-test.ts index 2b955dca85..71607acb14 100644 --- a/src/lib/field.unit-test.ts +++ b/src/lib/field.unit-test.ts @@ -17,6 +17,7 @@ import { bool, Spec, } from './testing/equivalent.js'; +import { runAndCheckSync } from './provable-context.js'; // types Field satisfies Provable; @@ -138,7 +139,7 @@ equivalent({ from: [smallField], to: bool })( // non-constant field vars test(Random.field, (x0, assert) => { - Provable.runAndCheck(() => { + runAndCheckSync(() => { // Var let x = Provable.witness(Field, () => Field(x0)); assert(x.value[0] === FieldType.Var); @@ -177,7 +178,7 @@ test(Random.field, (x0, assert) => { // some provable operations test(Random.field, Random.field, (x0, y0, assert) => { - Provable.runAndCheck(() => { + runAndCheckSync(() => { // equals let x = Provable.witness(Field, () => Field(x0)); let y = Provable.witness(Field, () => Field(y0)); diff --git a/src/lib/foreign-curve.unit-test.ts b/src/lib/foreign-curve.unit-test.ts index 8edb9d5875..d31a6dd76e 100644 --- a/src/lib/foreign-curve.unit-test.ts +++ b/src/lib/foreign-curve.unit-test.ts @@ -32,11 +32,11 @@ main(); console.timeEnd('running constant version'); console.time('running witness generation & checks'); -Provable.runAndCheck(main); +await Provable.runAndCheck(main); console.timeEnd('running witness generation & checks'); console.time('creating constraint system'); -let cs = Provable.constraintSystem(main); +let cs = await Provable.constraintSystem(main); console.timeEnd('creating constraint system'); console.log(cs.summary()); diff --git a/src/lib/foreign-field.unit-test.ts b/src/lib/foreign-field.unit-test.ts index 26f85a5699..9c823e2a00 100644 --- a/src/lib/foreign-field.unit-test.ts +++ b/src/lib/foreign-field.unit-test.ts @@ -160,12 +160,12 @@ function main1() { // check provable and non-provable versions are correct main0(); main1(); -Provable.runAndCheck(main0); -Provable.runAndCheck(main1); +await Provable.runAndCheck(main0); +await Provable.runAndCheck(main1); // using foreign field arithmetic should result in much fewer constraints -let { rows: rows0 } = Provable.constraintSystem(main0); -let { rows: rows1 } = Provable.constraintSystem(main1); +let { rows: rows0 } = await Provable.constraintSystem(main0); +let { rows: rows1 } = await Provable.constraintSystem(main1); expect(rows0 + 100).toBeLessThan(rows1); // test with proving diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index e008a011ef..c9fac69e9b 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -171,11 +171,11 @@ program.rawMethods.ecdsa(); console.timeEnd('ecdsa verify (constant)'); console.time('ecdsa verify (witness gen / check)'); -Provable.runAndCheck(program.rawMethods.ecdsa); +await Provable.runAndCheck(program.rawMethods.ecdsa); console.timeEnd('ecdsa verify (witness gen / check)'); console.time('ecdsa verify (build constraint system)'); -let cs = program.analyzeMethods().ecdsa; +let cs = (await program.analyzeMethods()).ecdsa; console.timeEnd('ecdsa verify (build constraint system)'); console.log(cs.summary()); diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts index f6383a7409..f795ec6cb9 100644 --- a/src/lib/gadgets/sha256.unit-test.ts +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -31,7 +31,7 @@ const Sha256Program = ZkProgram({ }, }); -const RUNS = 5; +const RUNS = 2; await Sha256Program.compile(); diff --git a/src/lib/global-context.ts b/src/lib/global-context.ts index dee457b8db..e07561fc53 100644 --- a/src/lib/global-context.ts +++ b/src/lib/global-context.ts @@ -101,6 +101,8 @@ function get(t: Context.t): C { return current.context; } +// FIXME there are many common scenarios where this error occurs, which weren't expected when this was written +// it should list them and help to resolve them let contextConflictMessage = "It seems you're running multiple provers concurrently within" + ' the same JavaScript thread, which, at the moment, is not supported and would lead to bugs.'; diff --git a/src/lib/group.test.ts b/src/lib/group.test.ts index 1a69cc6e66..e01e736275 100644 --- a/src/lib/group.test.ts +++ b/src/lib/group.test.ts @@ -10,7 +10,7 @@ describe('group', () => { describe('group membership', () => { it('valid element does not throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(Group, () => g); }); }).not.toThrow(); @@ -18,7 +18,7 @@ describe('group', () => { it('valid element does not throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(Group, () => Group.generator); }); }).not.toThrow(); @@ -26,7 +26,7 @@ describe('group', () => { it('Group.zero element does not throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(Group, () => Group.zero); }); }).not.toThrow(); @@ -34,7 +34,7 @@ describe('group', () => { it('invalid group element throws', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(Group, () => Group({ x: 2, y: 2 })); }); }).toThrow(); @@ -44,7 +44,7 @@ describe('group', () => { describe('add', () => { it('g+g does not throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const y = Provable.witness(Group, () => g); x.add(y); @@ -54,7 +54,7 @@ describe('group', () => { it('g+zero = g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); x.add(zero).assertEquals(x); @@ -64,7 +64,7 @@ describe('group', () => { it('zero+g = g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); zero.add(x).assertEquals(x); @@ -74,7 +74,7 @@ describe('group', () => { it('g+(-g) = zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); x.add(x.neg()).assertEquals(zero); @@ -84,7 +84,7 @@ describe('group', () => { it('(-g)+g = zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); x.neg().add(x).assertEquals(zero); @@ -94,7 +94,7 @@ describe('group', () => { it('zero + zero = zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const zero = Provable.witness(Group, () => Group.zero); zero.add(zero).assertEquals(zero); }); @@ -105,8 +105,8 @@ describe('group', () => { describe('sub', () => { it('g-g does not throw', () => { expect(() => { - Provable.runAndCheck(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const y = Provable.witness(Group, () => g); x.sub(y); @@ -117,7 +117,7 @@ describe('group', () => { it('g-zero = g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); x.sub(zero).assertEquals(x); @@ -127,7 +127,7 @@ describe('group', () => { it('zero - g = -g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); const zero = Provable.witness(Group, () => Group.zero); zero.sub(x).assertEquals(x.neg()); @@ -137,7 +137,7 @@ describe('group', () => { it('zero - zero = zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const zero = Provable.witness(Group, () => Group.zero); zero.sub(zero).assertEquals(zero); }); @@ -148,7 +148,7 @@ describe('group', () => { describe('neg', () => { it('neg(g) not to throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); x.neg(); }); @@ -157,7 +157,7 @@ describe('group', () => { it('neg(zero) = zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const zero = Provable.witness(Group, () => Group.zero); zero.neg().assertEquals(zero); }); @@ -168,7 +168,7 @@ describe('group', () => { describe('scale', () => { it('scaling with random Scalar does not throw', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => g); x.scale(Scalar.random()); }); @@ -177,7 +177,7 @@ describe('group', () => { it('x*g+y*g = (x+y)*g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Scalar.from(2); const y = Scalar.from(3); const left = g.scale(x).add(g.scale(y)); @@ -189,7 +189,7 @@ describe('group', () => { it('x*(y*g) = (x*y)*g', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Scalar.from(2); const y = Scalar.from(3); const left = g.scale(y).scale(x); @@ -202,7 +202,7 @@ describe('group', () => { describe('equals', () => { it('should equal true with same group', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => Group.generator); let isEqual = x.equals(Group.generator); Provable.asProver(() => { @@ -212,7 +212,7 @@ describe('group', () => { }); it('should equal false with different group', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => Group.generator); let isEqual = x.equals(g); Provable.asProver(() => { @@ -225,7 +225,7 @@ describe('group', () => { describe('assertEquals', () => { it('should not throw with same group', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => Group.generator); x.assertEquals(Group.generator); }); @@ -234,7 +234,7 @@ describe('group', () => { it('should throw with different group', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Group, () => Group.generator); x.assertEquals(g); }); @@ -244,7 +244,7 @@ describe('group', () => { describe('toJSON', () => { it('fromJSON(g.toJSON) should be the same as g', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( Group, () => Group.fromJSON(Group.generator.toJSON())! @@ -389,7 +389,7 @@ describe('group', () => { describe('Variable/Constant circuit equality ', () => { it('add', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let y = Provable.witness(Group, () => g).add( Provable.witness(Group, () => Group.generator) ); diff --git a/src/lib/group.unit-test.ts b/src/lib/group.unit-test.ts index 5a7102f078..a302814db8 100644 --- a/src/lib/group.unit-test.ts +++ b/src/lib/group.unit-test.ts @@ -54,7 +54,7 @@ function run( ) { let result_out_circuit = f(g1, g2); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let result_in_circuit = f( Provable.witness(Group, () => g1), Provable.witness(Group, () => g2) diff --git a/src/lib/int.test.ts b/src/lib/int.test.ts index 1914a9fede..22e9131f95 100644 --- a/src/lib/int.test.ts +++ b/src/lib/int.test.ts @@ -213,7 +213,7 @@ describe('int', () => { describe('add', () => { it('1+1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.add(y).assertEquals(new UInt64(Field(2))); @@ -223,7 +223,7 @@ describe('int', () => { it('5000+5000=10000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(5000))); const y = Provable.witness(UInt64, () => new UInt64(Field(5000))); x.add(y).assertEquals(new UInt64(Field(10000))); @@ -234,7 +234,7 @@ describe('int', () => { it('(MAXINT/2+MAXINT/2) adds to MAXINT', () => { const n = Field((((1n << 64n) - 2n) / 2n).toString()); expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(n)); const y = Provable.witness(UInt64, () => new UInt64(n)); x.add(y).add(1).assertEquals(UInt64.MAXINT()); @@ -244,7 +244,7 @@ describe('int', () => { it('should throw on overflow addition', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.add(y); @@ -256,7 +256,7 @@ describe('int', () => { describe('sub', () => { it('1-1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.sub(y).assertEquals(new UInt64(Field(0))); @@ -266,7 +266,7 @@ describe('int', () => { it('10000-5000=5000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt64, () => new UInt64(Field(10000)) @@ -279,7 +279,7 @@ describe('int', () => { it('should throw on sub if results in negative number', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(0))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.sub(y); @@ -291,7 +291,7 @@ describe('int', () => { describe('mul', () => { it('1x2=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(2))); x.mul(y).assertEquals(new UInt64(Field(2))); @@ -301,7 +301,7 @@ describe('int', () => { it('1x0=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(0))); x.mul(y).assertEquals(new UInt64(Field(0))); @@ -311,7 +311,7 @@ describe('int', () => { it('1000x1000=1000000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1000))); const y = Provable.witness(UInt64, () => new UInt64(Field(1000))); x.mul(y).assertEquals(new UInt64(Field(1000000))); @@ -321,7 +321,7 @@ describe('int', () => { it('MAXINTx1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.mul(y).assertEquals(UInt64.MAXINT()); @@ -331,7 +331,7 @@ describe('int', () => { it('should throw on overflow multiplication', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(2))); x.mul(y); @@ -343,7 +343,7 @@ describe('int', () => { describe('div', () => { it('2/1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(2))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.div(y).assertEquals(new UInt64(Field(2))); @@ -353,7 +353,7 @@ describe('int', () => { it('0/1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(0))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.div(y).assertEquals(new UInt64(Field(0))); @@ -363,7 +363,7 @@ describe('int', () => { it('2000/1000=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(2000))); const y = Provable.witness(UInt64, () => new UInt64(Field(1000))); x.div(y).assertEquals(new UInt64(Field(2))); @@ -373,7 +373,7 @@ describe('int', () => { it('MAXINT/1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.div(y).assertEquals(UInt64.MAXINT()); @@ -383,7 +383,7 @@ describe('int', () => { it('should throw on division by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(0))); x.div(y); @@ -395,7 +395,7 @@ describe('int', () => { describe('mod', () => { it('1%1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.mod(y).assertEquals(new UInt64(Field(0))); @@ -405,7 +405,7 @@ describe('int', () => { it('500%32=20', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(500))); const y = Provable.witness(UInt64, () => new UInt64(Field(32))); x.mod(y).assertEquals(new UInt64(Field(20))); @@ -415,7 +415,7 @@ describe('int', () => { it('MAXINT%7=1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(7))); x.mod(y).assertEquals(new UInt64(Field(1))); @@ -425,7 +425,7 @@ describe('int', () => { it('should throw on mod by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => new UInt64(Field(0))); x.mod(y).assertEquals(new UInt64(Field(1))); @@ -437,7 +437,7 @@ describe('int', () => { describe('assertLt', () => { it('1<2=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(2))); x.assertLessThan(y); @@ -447,7 +447,7 @@ describe('int', () => { it('1<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertLessThan(y); @@ -457,7 +457,7 @@ describe('int', () => { it('2<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(2))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertLessThan(y); @@ -467,7 +467,7 @@ describe('int', () => { it('1000<100000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1000))); const y = Provable.witness( UInt64, @@ -480,7 +480,7 @@ describe('int', () => { it('100000<1000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt64, () => new UInt64(Field(100000)) @@ -493,7 +493,7 @@ describe('int', () => { it('MAXINT { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => UInt64.MAXINT()); x.assertLessThan(y); @@ -505,7 +505,7 @@ describe('int', () => { describe('assertLte', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertLessThanOrEqual(y); @@ -515,7 +515,7 @@ describe('int', () => { it('2<=1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(2))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertLessThanOrEqual(y); @@ -525,7 +525,7 @@ describe('int', () => { it('1000<=100000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1000))); const y = Provable.witness( UInt64, @@ -538,7 +538,7 @@ describe('int', () => { it('100000<=1000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt64, () => new UInt64(Field(100000)) @@ -551,7 +551,7 @@ describe('int', () => { it('MAXINT<=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => UInt64.MAXINT()); x.assertLessThanOrEqual(y); @@ -563,7 +563,7 @@ describe('int', () => { describe('assertGreaterThan', () => { it('2>1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(2))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertGreaterThan(y); @@ -573,7 +573,7 @@ describe('int', () => { it('1>1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertGreaterThan(y); @@ -583,7 +583,7 @@ describe('int', () => { it('1>2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(2))); x.assertGreaterThan(y); @@ -593,7 +593,7 @@ describe('int', () => { it('100000>1000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt64, () => new UInt64(Field(100000)) @@ -606,7 +606,7 @@ describe('int', () => { it('1000>100000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1000))); const y = Provable.witness( UInt64, @@ -619,7 +619,7 @@ describe('int', () => { it('MAXINT>MAXINT=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => UInt64.MAXINT()); x.assertGreaterThan(y); @@ -631,7 +631,7 @@ describe('int', () => { describe('assertGreaterThanOrEqual', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertGreaterThanOrEqual(y); @@ -641,7 +641,7 @@ describe('int', () => { it('1>=2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1))); const y = Provable.witness(UInt64, () => new UInt64(Field(2))); x.assertGreaterThanOrEqual(y); @@ -651,7 +651,7 @@ describe('int', () => { it('100000>=1000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt64, () => new UInt64(Field(100000)) @@ -664,7 +664,7 @@ describe('int', () => { it('1000>=100000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => new UInt64(Field(1000))); const y = Provable.witness( UInt64, @@ -677,7 +677,7 @@ describe('int', () => { it('MAXINT>=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.MAXINT()); const y = Provable.witness(UInt64, () => UInt64.MAXINT()); x.assertGreaterThanOrEqual(y); @@ -690,7 +690,7 @@ describe('int', () => { describe('fromNumber()', () => { it('should be the same as Field(1)', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.from(1)); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertEquals(y); @@ -700,7 +700,7 @@ describe('int', () => { it('should be the same as 2^53-1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.from(NUMBERMAX) ); @@ -716,7 +716,7 @@ describe('int', () => { describe('fromString()', () => { it('should be the same as Field(1)', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.from('1')); const y = Provable.witness(UInt64, () => new UInt64(Field(1))); x.assertEquals(y); @@ -726,7 +726,7 @@ describe('int', () => { it('should be the same as 2^53-1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt64, () => UInt64.from(String(NUMBERMAX)) ); @@ -1179,7 +1179,7 @@ describe('int', () => { describe('add', () => { it('1+1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.add(y).assertEquals(new UInt32(Field(2))); @@ -1189,7 +1189,7 @@ describe('int', () => { it('5000+5000=10000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(5000))); const y = Provable.witness(UInt32, () => new UInt32(Field(5000))); x.add(y).assertEquals(new UInt32(Field(10000))); @@ -1200,7 +1200,7 @@ describe('int', () => { it('(MAXINT/2+MAXINT/2) adds to MAXINT', () => { const n = Field((((1n << 32n) - 2n) / 2n).toString()); expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(n)); const y = Provable.witness(UInt32, () => new UInt32(n)); x.add(y).add(1).assertEquals(UInt32.MAXINT()); @@ -1210,7 +1210,7 @@ describe('int', () => { it('should throw on overflow addition', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.add(y); @@ -1222,7 +1222,7 @@ describe('int', () => { describe('sub', () => { it('1-1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.sub(y).assertEquals(new UInt32(Field(0))); @@ -1232,7 +1232,7 @@ describe('int', () => { it('10000-5000=5000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt32, () => new UInt32(Field(10000)) @@ -1245,7 +1245,7 @@ describe('int', () => { it('should throw on sub if results in negative number', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(0))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.sub(y); @@ -1257,7 +1257,7 @@ describe('int', () => { describe('mul', () => { it('1x2=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(2))); x.mul(y).assertEquals(new UInt32(Field(2))); @@ -1267,7 +1267,7 @@ describe('int', () => { it('1x0=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(0))); x.mul(y).assertEquals(new UInt32(Field(0))); @@ -1277,7 +1277,7 @@ describe('int', () => { it('1000x1000=1000000', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1000))); const y = Provable.witness(UInt32, () => new UInt32(Field(1000))); x.mul(y).assertEquals(new UInt32(Field(1000000))); @@ -1287,7 +1287,7 @@ describe('int', () => { it('MAXINTx1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.mul(y).assertEquals(UInt32.MAXINT()); @@ -1297,7 +1297,7 @@ describe('int', () => { it('should throw on overflow multiplication', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(2))); x.mul(y); @@ -1309,7 +1309,7 @@ describe('int', () => { describe('div', () => { it('2/1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(2))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.div(y).assertEquals(new UInt32(Field(2))); @@ -1319,7 +1319,7 @@ describe('int', () => { it('0/1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(0))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.div(y).assertEquals(new UInt32(Field(0))); @@ -1329,7 +1329,7 @@ describe('int', () => { it('2000/1000=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(2000))); const y = Provable.witness(UInt32, () => new UInt32(Field(1000))); x.div(y).assertEquals(new UInt32(Field(2))); @@ -1339,7 +1339,7 @@ describe('int', () => { it('MAXINT/1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.div(y).assertEquals(UInt32.MAXINT()); @@ -1349,7 +1349,7 @@ describe('int', () => { it('should throw on division by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(0))); x.div(y); @@ -1361,7 +1361,7 @@ describe('int', () => { describe('mod', () => { it('1%1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.mod(y).assertEquals(new UInt32(Field(0))); @@ -1371,7 +1371,7 @@ describe('int', () => { it('500%32=20', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(500))); const y = Provable.witness(UInt32, () => new UInt32(Field(32))); x.mod(y).assertEquals(new UInt32(Field(20))); @@ -1381,7 +1381,7 @@ describe('int', () => { it('MAXINT%7=3', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(7))); x.mod(y).assertEquals(new UInt32(Field(3))); @@ -1391,7 +1391,7 @@ describe('int', () => { it('should throw on mod by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => new UInt32(Field(0))); x.mod(y).assertEquals(new UInt32(Field(1))); @@ -1403,7 +1403,7 @@ describe('int', () => { describe('assertLt', () => { it('1<2=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(2))); x.assertLessThan(y); @@ -1413,7 +1413,7 @@ describe('int', () => { it('1<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertLessThan(y); @@ -1423,7 +1423,7 @@ describe('int', () => { it('2<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(2))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertLessThan(y); @@ -1433,7 +1433,7 @@ describe('int', () => { it('1000<100000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1000))); const y = Provable.witness( UInt32, @@ -1446,7 +1446,7 @@ describe('int', () => { it('100000<1000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt32, () => new UInt32(Field(100000)) @@ -1459,7 +1459,7 @@ describe('int', () => { it('MAXINT { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => UInt32.MAXINT()); x.assertLessThan(y); @@ -1471,7 +1471,7 @@ describe('int', () => { describe('assertLessThanOrEqual', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertLessThanOrEqual(y); @@ -1481,7 +1481,7 @@ describe('int', () => { it('2<=1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(2))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertLessThanOrEqual(y); @@ -1491,7 +1491,7 @@ describe('int', () => { it('1000<=100000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1000))); const y = Provable.witness( UInt32, @@ -1504,7 +1504,7 @@ describe('int', () => { it('100000<=1000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt32, () => new UInt32(Field(100000)) @@ -1517,7 +1517,7 @@ describe('int', () => { it('MAXINT<=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => UInt32.MAXINT()); x.assertLessThanOrEqual(y); @@ -1529,7 +1529,7 @@ describe('int', () => { describe('assertGreaterThan', () => { it('2>1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(2))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertGreaterThan(y); @@ -1539,7 +1539,7 @@ describe('int', () => { it('1>1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertGreaterThan(y); @@ -1549,7 +1549,7 @@ describe('int', () => { it('1>2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(2))); x.assertGreaterThan(y); @@ -1559,7 +1559,7 @@ describe('int', () => { it('100000>1000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt32, () => new UInt32(Field(100000)) @@ -1572,7 +1572,7 @@ describe('int', () => { it('1000>100000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1000))); const y = Provable.witness( UInt32, @@ -1585,7 +1585,7 @@ describe('int', () => { it('MAXINT>MAXINT=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => UInt32.MAXINT()); x.assertGreaterThan(y); @@ -1597,7 +1597,7 @@ describe('int', () => { describe('assertGreaterThanOrEqual', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertGreaterThanOrEqual(y); @@ -1607,7 +1607,7 @@ describe('int', () => { it('1>=2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1))); const y = Provable.witness(UInt32, () => new UInt32(Field(2))); x.assertGreaterThanOrEqual(y); @@ -1617,7 +1617,7 @@ describe('int', () => { it('100000>=1000=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness( UInt32, () => new UInt32(Field(100000)) @@ -1630,7 +1630,7 @@ describe('int', () => { it('1000>=100000=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => new UInt32(Field(1000))); const y = Provable.witness( UInt32, @@ -1643,7 +1643,7 @@ describe('int', () => { it('MAXINT>=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.MAXINT()); const y = Provable.witness(UInt32, () => UInt32.MAXINT()); x.assertGreaterThanOrEqual(y); @@ -1656,7 +1656,7 @@ describe('int', () => { describe('fromNumber()', () => { it('should be the same as Field(1)', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.from(1)); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertEquals(y); @@ -1666,7 +1666,7 @@ describe('int', () => { it('should be the same as 2^53-1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.from(NUMBERMAX) ); @@ -1682,7 +1682,7 @@ describe('int', () => { describe('fromString()', () => { it('should be the same as Field(1)', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.from('1')); const y = Provable.witness(UInt32, () => new UInt32(Field(1))); x.assertEquals(y); @@ -1692,7 +1692,7 @@ describe('int', () => { it('should be the same as 2^53-1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt32, () => UInt32.from(String(NUMBERMAX)) ); @@ -2145,7 +2145,7 @@ describe('int', () => { describe('add', () => { it('1+1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.add(y).assertEquals(2); @@ -2155,7 +2155,7 @@ describe('int', () => { it('100+100=200', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(100)); x.add(y).assertEquals(new UInt8(200)); @@ -2166,7 +2166,7 @@ describe('int', () => { it('(MAXINT/2+MAXINT/2) adds to MAXINT', () => { const n = ((1n << 8n) - 2n) / 2n; expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(n)); const y = Provable.witness(UInt8, () => new UInt8(n)); x.add(y).add(1).assertEquals(UInt8.MAXINT()); @@ -2176,7 +2176,7 @@ describe('int', () => { it('should throw on overflow addition', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(1)); x.add(y); @@ -2188,7 +2188,7 @@ describe('int', () => { describe('sub', () => { it('1-1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.sub(y).assertEquals(new UInt8(0)); @@ -2198,7 +2198,7 @@ describe('int', () => { it('100-50=50', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(50)); x.sub(y).assertEquals(new UInt8(50)); @@ -2208,7 +2208,7 @@ describe('int', () => { it('should throw on sub if results in negative number', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(0)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.sub(y); @@ -2220,7 +2220,7 @@ describe('int', () => { describe('mul', () => { it('1x2=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(2)); x.mul(y).assertEquals(new UInt8(2)); @@ -2230,7 +2230,7 @@ describe('int', () => { it('1x0=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(0)); x.mul(y).assertEquals(new UInt8(0)); @@ -2240,7 +2240,7 @@ describe('int', () => { it('12x20=240', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(12)); const y = Provable.witness(UInt8, () => new UInt8(20)); x.mul(y).assertEquals(new UInt8(240)); @@ -2250,7 +2250,7 @@ describe('int', () => { it('MAXINTx1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(1)); x.mul(y).assertEquals(UInt8.MAXINT()); @@ -2260,7 +2260,7 @@ describe('int', () => { it('should throw on overflow multiplication', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(2)); x.mul(y); @@ -2272,7 +2272,7 @@ describe('int', () => { describe('div', () => { it('2/1=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(2)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.div(y).assertEquals(new UInt8(2)); @@ -2282,7 +2282,7 @@ describe('int', () => { it('0/1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(0)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.div(y).assertEquals(new UInt8(0)); @@ -2292,7 +2292,7 @@ describe('int', () => { it('20/10=2', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(20)); const y = Provable.witness(UInt8, () => new UInt8(10)); x.div(y).assertEquals(new UInt8(2)); @@ -2302,7 +2302,7 @@ describe('int', () => { it('MAXINT/1=MAXINT', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(1)); x.div(y).assertEquals(UInt8.MAXINT()); @@ -2312,7 +2312,7 @@ describe('int', () => { it('should throw on division by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(0)); x.div(y); @@ -2324,7 +2324,7 @@ describe('int', () => { describe('mod', () => { it('1%1=0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.mod(y).assertEquals(new UInt8(0)); @@ -2334,7 +2334,7 @@ describe('int', () => { it('50%32=18', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(50)); const y = Provable.witness(UInt8, () => new UInt8(32)); x.mod(y).assertEquals(new UInt8(18)); @@ -2344,7 +2344,7 @@ describe('int', () => { it('MAXINT%7=3', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(7)); x.mod(y).assertEquals(new UInt8(3)); @@ -2354,7 +2354,7 @@ describe('int', () => { it('should throw on mod by zero', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => new UInt8(0)); x.mod(y).assertEquals(new UInt8(1)); @@ -2366,7 +2366,7 @@ describe('int', () => { describe('assertLt', () => { it('1<2=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(2)); x.assertLessThan(y); @@ -2376,7 +2376,7 @@ describe('int', () => { it('1<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertLessThan(y); @@ -2386,7 +2386,7 @@ describe('int', () => { it('2<1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(2)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertLessThan(y); @@ -2396,7 +2396,7 @@ describe('int', () => { it('10<100=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(10)); const y = Provable.witness(UInt8, () => new UInt8(100)); x.assertLessThan(y); @@ -2406,7 +2406,7 @@ describe('int', () => { it('100<10=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(10)); x.assertLessThan(y); @@ -2416,7 +2416,7 @@ describe('int', () => { it('MAXINT { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => UInt8.MAXINT()); x.assertLessThan(y); @@ -2428,7 +2428,7 @@ describe('int', () => { describe('assertLessThanOrEqual', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertLessThanOrEqual(y); @@ -2438,7 +2438,7 @@ describe('int', () => { it('2<=1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(2)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertLessThanOrEqual(y); @@ -2448,7 +2448,7 @@ describe('int', () => { it('10<=100=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(10)); const y = Provable.witness(UInt8, () => new UInt8(100)); x.assertLessThanOrEqual(y); @@ -2458,7 +2458,7 @@ describe('int', () => { it('100<=10=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(10)); x.assertLessThanOrEqual(y); @@ -2468,7 +2468,7 @@ describe('int', () => { it('MAXINT<=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => UInt8.MAXINT()); x.assertLessThanOrEqual(y); @@ -2480,7 +2480,7 @@ describe('int', () => { describe('assertGreaterThan', () => { it('2>1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(2)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertGreaterThan(y); @@ -2490,7 +2490,7 @@ describe('int', () => { it('1>1=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertGreaterThan(y); @@ -2500,7 +2500,7 @@ describe('int', () => { it('1>2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(2)); x.assertGreaterThan(y); @@ -2510,7 +2510,7 @@ describe('int', () => { it('100>10=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(10)); x.assertGreaterThan(y); @@ -2520,7 +2520,7 @@ describe('int', () => { it('10>100=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1000)); const y = Provable.witness(UInt8, () => new UInt8(100000)); x.assertGreaterThan(y); @@ -2530,7 +2530,7 @@ describe('int', () => { it('MAXINT>MAXINT=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => UInt8.MAXINT()); x.assertGreaterThan(y); @@ -2542,7 +2542,7 @@ describe('int', () => { describe('assertGreaterThanOrEqual', () => { it('1<=1=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertGreaterThanOrEqual(y); @@ -2552,7 +2552,7 @@ describe('int', () => { it('1>=2=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(1)); const y = Provable.witness(UInt8, () => new UInt8(2)); x.assertGreaterThanOrEqual(y); @@ -2562,7 +2562,7 @@ describe('int', () => { it('100>=10=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(100)); const y = Provable.witness(UInt8, () => new UInt8(10)); x.assertGreaterThanOrEqual(y); @@ -2572,7 +2572,7 @@ describe('int', () => { it('10>=100=false', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => new UInt8(10)); const y = Provable.witness(UInt8, () => new UInt8(100)); x.assertGreaterThanOrEqual(y); @@ -2582,7 +2582,7 @@ describe('int', () => { it('MAXINT>=MAXINT=true', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.MAXINT()); const y = Provable.witness(UInt8, () => UInt8.MAXINT()); x.assertGreaterThanOrEqual(y); @@ -2595,7 +2595,7 @@ describe('int', () => { describe('fromNumber()', () => { it('should be the same as Field(1)', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(UInt8, () => UInt8.from(1)); const y = Provable.witness(UInt8, () => new UInt8(1)); x.assertEquals(y); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 48b2d6698c..1c0d16d07b 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -91,7 +91,7 @@ export { setActiveInstance({ ...activeInstance, async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { - return createTransaction(sender, f, 0); + return await createTransaction(sender, f, 0); }, }); @@ -375,7 +375,8 @@ function Network( }; }, async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { - let tx = createTransaction(sender, f, 0, { + // TODO we run the transcation twice to be able to fetch data in between + let tx = await createTransaction(sender, f, 0, { fetchMode: 'test', isFinalRunOutsideCircuit: false, }); @@ -383,7 +384,7 @@ function Network( let hasProofs = tx.transaction.accountUpdates.some( Authorization.hasLazyProof ); - return createTransaction(sender, f, 1, { + return await createTransaction(sender, f, 1, { fetchMode: 'cached', isFinalRunOutsideCircuit: !hasProofs, }); diff --git a/src/lib/mina/local-blockchain.ts b/src/lib/mina/local-blockchain.ts index afcac1b07f..1d7057c98c 100644 --- a/src/lib/mina/local-blockchain.ts +++ b/src/lib/mina/local-blockchain.ts @@ -306,11 +306,8 @@ function LocalBlockchain({ }; }, async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { - // bad hack: run transaction just to see whether it creates proofs - // if it doesn't, this is the last chance to run SmartContract.runOutsideCircuit, which is supposed to run only once - // TODO: this has obvious holes if multiple zkapps are involved, but not relevant currently because we can't prove with multiple account updates - // and hopefully with upcoming work by Matt we can just run everything in the prover, and nowhere else - let tx = createTransaction(sender, f, 0, { + // TODO we run the transaction twice to match the behaviour of `Network.transaction` + let tx = await createTransaction(sender, f, 0, { isFinalRunOutsideCircuit: false, proofsEnabled: this.proofsEnabled, fetchMode: 'test', @@ -318,7 +315,7 @@ function LocalBlockchain({ let hasProofs = tx.transaction.accountUpdates.some( Authorization.hasLazyProof ); - return createTransaction(sender, f, 1, { + return await createTransaction(sender, f, 1, { isFinalRunOutsideCircuit: !hasProofs, proofsEnabled: this.proofsEnabled, }); diff --git a/src/lib/mina/transaction.ts b/src/lib/mina/transaction.ts index 5ce1dea2fd..8ea80c8267 100644 --- a/src/lib/mina/transaction.ts +++ b/src/lib/mina/transaction.ts @@ -280,7 +280,7 @@ type RejectedTransaction = Pick< errors: string[]; }; -function createTransaction( +async function createTransaction( feePayer: DeprecatedFeePayerSpec, f: () => unknown, numberOfRuns: 0 | 1 | undefined, @@ -289,7 +289,7 @@ function createTransaction( isFinalRunOutsideCircuit = true, proofsEnabled = true, } = {} -): Transaction { +): Promise { if (currentTransaction.has()) { throw new Error('Cannot start new transaction within another transaction'); } @@ -322,32 +322,18 @@ function createTransaction( }); // run circuit - // we have this while(true) loop because one of the smart contracts we're calling inside `f` might be calling - // SmartContract.analyzeMethods, which would be running its methods again inside `Provable.constraintSystem`, which - // would throw an error when nested inside `Provable.runAndCheck`. So if that happens, we have to run `analyzeMethods` first - // and retry `Provable.runAndCheck(f)`. Since at this point in the function, we don't know which smart contracts are involved, - // we created that hack with a `bootstrap()` function that analyzeMethods sticks on the error, to call itself again. try { - let err: any; - while (true) { - if (err !== undefined) err.bootstrap(); - try { - if (fetchMode === 'test') { - Provable.runUnchecked(() => { - f(); - Provable.asProver(() => { - let tx = currentTransaction.get(); - tx.layout.toConstantInPlace(); - }); - }); - } else { - f(); - } - break; - } catch (err_) { - if ((err_ as any)?.bootstrap) err = err_; - else throw err_; - } + if (fetchMode === 'test') { + await Provable.runUnchecked(() => { + f(); + Provable.asProver(() => { + let tx = currentTransaction.get(); + tx.layout.toConstantInPlace(); + }); + }); + } else { + // TODO support async + f(); } } catch (err) { currentTransaction.leave(transactionId); diff --git a/src/lib/ml/consistency.unit-test.ts b/src/lib/ml/consistency.unit-test.ts index c0c6e8aee9..1cf5f60617 100644 --- a/src/lib/ml/consistency.unit-test.ts +++ b/src/lib/ml/consistency.unit-test.ts @@ -94,7 +94,7 @@ test(Random.publicKey, randomTokenId, (publicKey, field) => { }); let parentTokenId = Field(field); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { tokenOwner = Provable.witness(PublicKey, () => tokenOwner); parentTokenId = Provable.witness(Field, () => parentTokenId); diff --git a/src/lib/primitives.test.ts b/src/lib/primitives.test.ts index 816247d379..ce0b5cd24c 100644 --- a/src/lib/primitives.test.ts +++ b/src/lib/primitives.test.ts @@ -18,7 +18,7 @@ describe('bool', () => { }); it('should convert false to Field element 0', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); xFalse.toField().assertEquals(new Field(0)); @@ -27,7 +27,7 @@ describe('bool', () => { }); it('should throw when false toString is compared to Field element other than 0 ', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); xFalse.toField().assertEquals(new Field(1)); }); @@ -36,7 +36,7 @@ describe('bool', () => { it('should convert true to Field element 1', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); xTrue.toField().assertEquals(new Field(1)); }); @@ -45,7 +45,7 @@ describe('bool', () => { it('should throw when true toField is compared to Field element other than 1 ', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); xTrue.toField().assertEquals(new Field(0)); }); @@ -56,7 +56,7 @@ describe('bool', () => { describe('toFields', () => { it('should return an array of Fields', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Bool, () => new Bool(false)); const fieldArr = x.toFields(); const isArr = Array.isArray(fieldArr); @@ -69,7 +69,7 @@ describe('bool', () => { describe('and', () => { it('true "and" true should return true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -80,7 +80,7 @@ describe('bool', () => { it('should throw if true "and" true is compared to false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -91,7 +91,7 @@ describe('bool', () => { it('false "and" false should return false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yFalse = Provable.witness(Bool, () => new Bool(false)); @@ -102,7 +102,7 @@ describe('bool', () => { it('should throw if false "and" false is compared to true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yFalse = Provable.witness(Bool, () => new Bool(false)); @@ -113,7 +113,7 @@ describe('bool', () => { it('false "and" true should return false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -124,7 +124,7 @@ describe('bool', () => { it('should throw if false "and" true is compared to true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -137,7 +137,7 @@ describe('bool', () => { describe('not', () => { it('should return true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); xTrue.toField().assertEquals(new Field(1)); }); @@ -145,7 +145,7 @@ describe('bool', () => { }); it('should return a new bool that is the negation of the input', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); const yFalse = Provable.witness(Bool, () => new Bool(false)); xTrue.not().assertEquals(new Bool(false)); @@ -156,7 +156,7 @@ describe('bool', () => { it('should throw if input.not() is compared to input', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); xTrue.not().assertEquals(xTrue); }); @@ -167,7 +167,7 @@ describe('bool', () => { describe('or', () => { it('true "or" true should return true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -178,7 +178,7 @@ describe('bool', () => { it('should throw if true "or" true is compared to false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xTrue = Provable.witness(Bool, () => new Bool(true)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -189,7 +189,7 @@ describe('bool', () => { it('false "or" false should return false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yFalse = Provable.witness(Bool, () => new Bool(false)); @@ -200,7 +200,7 @@ describe('bool', () => { it('should throw if false "or" false is compared to true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yFalse = Provable.witness(Bool, () => new Bool(false)); @@ -211,7 +211,7 @@ describe('bool', () => { it('false "or" true should return true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const xFalse = Provable.witness(Bool, () => new Bool(false)); const yTrue = Provable.witness(Bool, () => new Bool(true)); @@ -224,7 +224,7 @@ describe('bool', () => { describe('assertEquals', () => { it('should not throw on true "assertEqual" true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Bool, () => new Bool(true)); x.assertEquals(x); @@ -234,7 +234,7 @@ describe('bool', () => { it('should throw on true "assertEquals" false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Bool, () => new Bool(true)); const y = Provable.witness(Bool, () => new Bool(false)); @@ -246,7 +246,7 @@ describe('bool', () => { describe('equals', () => { it('should not throw on true "equals" true', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Bool, () => new Bool(true)); x.equals(x).assertEquals(true); @@ -255,7 +255,7 @@ describe('bool', () => { }); it('should throw on true "equals" false', async () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Bool, () => new Bool(true)); const y = Provable.witness(Bool, () => new Bool(false)); x.equals(y).assertEquals(true); diff --git a/src/lib/proof-system.ts b/src/lib/proof-system.ts index 4682e42a97..e7af194a0a 100644 --- a/src/lib/proof-system.ts +++ b/src/lib/proof-system.ts @@ -44,7 +44,6 @@ export { SelfProof, JsonProof, ZkProgram, - ExperimentalZkProgram, verify, Empty, Undefined, @@ -273,10 +272,10 @@ function ZkProgram< InferProvableOrVoid> > ) => Promise; - digest: () => string; - analyzeMethods: () => { - [I in keyof Types]: ReturnType; - }; + digest: () => Promise; + analyzeMethods: () => Promise<{ + [I in keyof Types]: UnwrapPromise>; + }>; publicInputType: ProvableOrUndefined>; publicOutputType: ProvableOrVoid>; privateInputTypes: { @@ -316,6 +315,7 @@ function ZkProgram< static tag = () => selfTag; } + // TODO remove sort()! Object.keys() has a deterministic order let methodKeys: (keyof Types & string)[] = Object.keys(methods).sort(); // need to have methods in (any) fixed order let methodIntfs = methodKeys.map((key) => sortMethodArguments('program', key, methods[key].privateInputs, SelfProof) @@ -323,14 +323,21 @@ function ZkProgram< let methodFunctions = methodKeys.map((key) => methods[key].method); let maxProofsVerified = getMaxProofsVerified(methodIntfs); - function analyzeMethods() { - return Object.fromEntries( - methodIntfs.map((methodEntry, i) => [ - methodEntry.methodName, - analyzeMethod(publicInputType, methodEntry, methodFunctions[i]), - ]) - ) as any as { - [I in keyof Types]: ReturnType; + async function analyzeMethods() { + let methodsMeta: Record< + string, + UnwrapPromise> + > = {}; + for (let i = 0; i < methodIntfs.length; i++) { + let methodEntry = methodIntfs[i]; + methodsMeta[methodEntry.methodName] = await analyzeMethod( + publicInputType, + methodEntry, + methodFunctions[i] + ); + } + return methodsMeta as { + [I in keyof Types]: UnwrapPromise>; }; } @@ -348,10 +355,8 @@ function ZkProgram< cache = Cache.FileSystemDefault, forceRecompile = false, } = {}) { - let methodsMeta = methodIntfs.map((methodEntry, i) => - analyzeMethod(publicInputType, methodEntry, methodFunctions[i]) - ); - let gates = methodsMeta.map((m) => m.gates); + let methodsMeta = await analyzeMethods(); + let gates = methodKeys.map((k) => methodsMeta[k].gates); let { provers, verify, verificationKey } = await compileProgram({ publicInputType, publicOutputType, @@ -437,14 +442,12 @@ function ZkProgram< return compileOutput.verify(statement, proof.proof); } - function digest() { - let methodData = methodIntfs.map((methodEntry, i) => - analyzeMethod(publicInputType, methodEntry, methodFunctions[i]) - ); - let hash = hashConstant( - Object.values(methodData).map((d) => Field(BigInt('0x' + d.digest))) + async function digest() { + let methodsMeta = await analyzeMethods(); + let digests: Field[] = methodKeys.map((k) => + Field(BigInt('0x' + methodsMeta[k].digest)) ); - return hash.toBigInt().toString(16); + return hashConstant(digests).toBigInt().toString(16); } return Object.assign( @@ -602,7 +605,7 @@ async function compileProgram({ publicInputType: ProvablePure; publicOutputType: ProvablePure; methodIntfs: MethodInterface[]; - methods: ((...args: any) => void)[]; + methods: ((...args: any) => unknown)[]; gates: Gate[][]; proofSystemTag: { name: string }; cache: Cache; @@ -658,15 +661,20 @@ async function compileProgram({ storable: picklesCache, overrideWrapDomain, }); + let { getVerificationKey, provers, verify, tag } = result; + CompiledTag.store(proofSystemTag, tag); + let [, data, hash] = await getVerificationKey(); + let verificationKey = { data, hash: Field(hash) }; + return { + verificationKey, + provers: MlArray.from(provers), + verify, + tag, + }; } finally { snarkContext.leave(id); unsetSrsCache(); } - let { getVerificationKey, provers, verify, tag } = result; - CompiledTag.store(proofSystemTag, tag); - let [, data, hash] = getVerificationKey(); - let verificationKey = { data, hash: Field(hash) }; - return { verificationKey, provers: MlArray.from(provers), verify, tag }; }) ); // wrap provers @@ -698,14 +706,15 @@ async function compileProgram({ }; } -function analyzeMethod( +function analyzeMethod( publicInputType: ProvablePure, methodIntf: MethodInterface, - method: (...args: any) => T + method: (...args: any) => unknown ) { return Provable.constraintSystem(() => { let args = synthesizeMethodArguments(methodIntf, true); let publicInput = emptyWitness(publicInputType); + // note: returning the method result here makes this handle async methods if (publicInputType === Undefined || publicInputType === Void) return method(...args); return method(publicInput, ...args); @@ -715,12 +724,14 @@ function analyzeMethod( function picklesRuleFromFunction( publicInputType: ProvablePure, publicOutputType: ProvablePure, - func: (...args: unknown[]) => any, + func: (...args: unknown[]) => unknown, proofSystemTag: { name: string }, { methodName, witnessArgs, proofArgs, allArgs }: MethodInterface, gates: Gate[] ): Pickles.Rule { - function main(publicInput: MlFieldArray): ReturnType { + async function main( + publicInput: MlFieldArray + ): ReturnType { let { witnesses: argsWithoutPublicInput, inProver } = snarkContext.get(); assert(!(inProver && argsWithoutPublicInput === undefined)); let finalArgs = []; @@ -730,9 +741,14 @@ function picklesRuleFromFunction( let arg = allArgs[i]; if (arg.type === 'witness') { let type = witnessArgs[arg.index]; - finalArgs[i] = Provable.witness(type, () => { - return argsWithoutPublicInput?.[i] ?? emptyValue(type); - }); + try { + finalArgs[i] = Provable.witness(type, () => { + return argsWithoutPublicInput?.[i] ?? emptyValue(type); + }); + } catch (e: any) { + e.message = `Error when witnessing in ${methodName}, argument ${i}: ${e.message}`; + throw e; + } } else if (arg.type === 'proof') { let Proof = proofArgs[arg.index]; let type = getStatementType(Proof); @@ -754,10 +770,10 @@ function picklesRuleFromFunction( } let result: any; if (publicInputType === Undefined || publicInputType === Void) { - result = func(...finalArgs); + result = await func(...finalArgs); } else { let input = fromFieldVars(publicInputType, publicInput); - result = func(input, ...finalArgs); + result = await func(input, ...finalArgs); } // if the public output is empty, we don't evaluate `toFields(result)` to allow the function to return something else in that case let hasPublicOutput = publicOutputType.sizeInFields() !== 0; @@ -942,7 +958,6 @@ ZkProgram.Proof = function < static tag = () => program; }; }; -ExperimentalZkProgram.Proof = ZkProgram.Proof; function dummyProof(maxProofsVerified: 0 | 1 | 2, domainLog2: number) { return withThreadPool( @@ -1086,30 +1101,3 @@ type UnwrapPromise

= P extends Promise ? T : never; type Get = T extends { [K in Key]: infer Value } ? Value : undefined; - -// deprecated experimental API - -function ExperimentalZkProgram< - StatementType extends { - publicInput?: FlexibleProvablePure; - publicOutput?: FlexibleProvablePure; - }, - Types extends { - [I in string]: Tuple; - } ->( - config: StatementType & { - name?: string; - methods: { - [I in keyof Types]: Method< - InferProvableOrUndefined>, - InferProvableOrVoid>, - Types[I] - >; - }; - overrideWrapDomain?: 0 | 1 | 2; - } -) { - let config_ = { ...config, name: config.name ?? `Program${i++}` }; - return ZkProgram(config_); -} diff --git a/src/lib/proof-system.unit-test.ts b/src/lib/proof-system.unit-test.ts index d9572e54d4..fef3c3735e 100644 --- a/src/lib/proof-system.unit-test.ts +++ b/src/lib/proof-system.unit-test.ts @@ -74,7 +74,7 @@ it('pickles rule creation', async () => { let field_: FieldConst = [0, 0n]; let bool_: FieldConst = [0, 0n]; - Provable.runAndCheck(() => { + await Provable.runAndCheck(async () => { // put witnesses in snark context snarkContext.get().witnesses = [dummy, bool]; @@ -82,7 +82,7 @@ it('pickles rule creation', async () => { let { publicOutput: [, publicOutput], shouldVerify: [, shouldVerify], - } = rule.main([0]); + } = await rule.main([0]); // `publicOutput` and `shouldVerify` are as expected Snarky.field.assertEqual(publicOutput, dummy.publicInput.value); @@ -118,7 +118,7 @@ it('can compile program with large input', async () => { }); // regression tests for some zkprograms -const emptyMethodsMetadata = EmptyProgram.analyzeMethods(); +const emptyMethodsMetadata = await EmptyProgram.analyzeMethods(); expect(emptyMethodsMetadata.run).toEqual( expect.objectContaining({ rows: 0, @@ -149,5 +149,6 @@ const CounterProgram = ZkProgram({ }, }); -const incrementMethodMetadata = CounterProgram.analyzeMethods().increment; +const incrementMethodMetadata = (await CounterProgram.analyzeMethods()) + .increment; expect(incrementMethodMetadata).toEqual(expect.objectContaining({ rows: 18 })); diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index 9779fa2ab1..fa0286b24a 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -3,15 +3,17 @@ import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; import { parseHexString32 } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; import { Fp } from '../bindings/crypto/finite-field.js'; +import { MlBool } from './ml/base.js'; // internal API export { snarkContext, SnarkContext, asProver, - runAndCheck, - runUnchecked, + runAndCheckSync, + generateWitness, constraintSystem, + constraintSystemSync, inProver, inAnalyze, inCheckedComputation, @@ -19,6 +21,7 @@ export { inCompileMode, gatesFromJson, printGates, + MlConstraintSystem, }; // global circuit-related context @@ -30,11 +33,14 @@ type SnarkContext = { inCompile?: boolean; inCheckedComputation?: boolean; inAnalyze?: boolean; - inRunAndCheck?: boolean; inWitnessBlock?: boolean; }; let snarkContext = Context.create({ default: {} }); +class MlConstraintSystem { + // opaque +} + // helpers to read circuit context function inProver() { @@ -66,21 +72,33 @@ function asProver(f: () => void) { } } -function runAndCheck(f: () => void) { +async function generateWitness( + f: (() => Promise) | (() => void), + { checkConstraints = true } = {} +) { let id = snarkContext.enter({ inCheckedComputation: true }); try { - Snarky.run.runAndCheck(f); + let finish = Snarky.run.enterGenerateWitness(); + if (!checkConstraints) Snarky.run.setEvalConstraints(MlBool(false)); + await f(); + return finish(); } catch (error) { throw prettifyStacktrace(error); } finally { + if (!checkConstraints) Snarky.run.setEvalConstraints(MlBool(true)); snarkContext.leave(id); } } -function runUnchecked(f: () => void) { +/** + * @deprecated use `generateWitness` instead + */ +function runAndCheckSync(f: () => void) { let id = snarkContext.enter({ inCheckedComputation: true }); try { - Snarky.run.runUnchecked(f); + let finish = Snarky.run.enterGenerateWitness(); + f(); + return finish(); } catch (error) { throw prettifyStacktrace(error); } finally { @@ -88,33 +106,13 @@ function runUnchecked(f: () => void) { } } -function constraintSystem(f: () => T) { +async function constraintSystem(f: (() => Promise) | (() => void)) { let id = snarkContext.enter({ inAnalyze: true, inCheckedComputation: true }); try { - let result: T; - let { rows, digest, json } = Snarky.run.constraintSystem(() => { - result = f(); - }); - let { gates, publicInputSize } = gatesFromJson(json); - return { - rows, - digest, - result: result! as T, - gates, - publicInputSize, - print() { - printGates(gates); - }, - summary() { - let gateTypes: Partial> = {}; - gateTypes['Total rows'] = rows; - for (let gate of gates) { - gateTypes[gate.type] ??= 0; - gateTypes[gate.type]!++; - } - return gateTypes; - }, - }; + let finish = Snarky.run.enterConstraintSystem(); + await f(); + let cs = finish(); + return constraintSystemToJS(cs); } catch (error) { throw prettifyStacktrace(error); } finally { @@ -122,6 +120,51 @@ function constraintSystem(f: () => T) { } } +/** + * helper to bridge transition to async circuits + * @deprecated we must get rid of this + */ +function constraintSystemSync(f: () => void) { + let id = snarkContext.enter({ inAnalyze: true, inCheckedComputation: true }); + try { + let finish = Snarky.run.enterConstraintSystem(); + f(); + let cs = finish(); + return constraintSystemToJS(cs); + } catch (error) { + throw prettifyStacktrace(error); + } finally { + snarkContext.leave(id); + } +} + +function constraintSystemToJS(cs: MlConstraintSystem) { + // toJson also "finalizes" the constraint system, which means + // locking in a potential pending single generic gate + let json = Snarky.constraintSystem.toJson(cs); + let rows = Snarky.constraintSystem.rows(cs); + let digest = Snarky.constraintSystem.digest(cs); + let { gates, publicInputSize } = gatesFromJson(json); + return { + rows, + digest, + gates, + publicInputSize, + print() { + printGates(gates); + }, + summary() { + let gateTypes: Partial> = {}; + gateTypes['Total rows'] = rows; + for (let gate of gates) { + gateTypes[gate.type] ??= 0; + gateTypes[gate.type]!++; + } + return gateTypes; + }, + }; +} + // helpers function gatesFromJson(cs: { gates: JsonGate[]; public_input_size: number }) { diff --git a/src/lib/provable.ts b/src/lib/provable.ts index ea532b1c3a..aafc841875 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -18,9 +18,9 @@ import { inProver, snarkContext, asProver, - runAndCheck, - runUnchecked, constraintSystem, + generateWitness, + runAndCheckSync, } from './provable-context.js'; // external API @@ -151,27 +151,35 @@ const Provable = { * Runs provable code quickly, without creating a proof, but still checking whether constraints are satisfied. * @example * ```ts - * Provable.runAndCheck(() => { + * await Provable.runAndCheck(() => { * // Your code to check here * }); * ``` */ - runAndCheck, + async runAndCheck(f: (() => Promise) | (() => void)) { + await generateWitness(f, { checkConstraints: true }); + }, + /** + * @deprecated use the async `Provable.runAndCheck` instead + */ + runAndCheckSync: runAndCheckSync, /** * Runs provable code quickly, without creating a proof, and not checking whether constraints are satisfied. * @example * ```ts - * Provable.runUnchecked(() => { + * await Provable.runUnchecked(() => { * // Your code to run here * }); * ``` */ - runUnchecked, + async runUnchecked(f: (() => Promise) | (() => void)) { + await generateWitness(f, { checkConstraints: false }); + }, /** * Returns information about the constraints created by the callback function. * @example * ```ts - * const result = Provable.constraintSystem(circuit); + * const result = await Provable.constraintSystem(circuit); * console.log(result); * ``` */ diff --git a/src/lib/provable.unit-test.ts b/src/lib/provable.unit-test.ts index 9c14a0f02c..7484bc91d8 100644 --- a/src/lib/provable.unit-test.ts +++ b/src/lib/provable.unit-test.ts @@ -4,11 +4,11 @@ import { exists } from './gadgets/common.js'; import { Field } from './field.js'; import { expect } from 'expect'; -it('can witness large field array', () => { +await it('can witness large field array', async () => { let N = 100_000; let arr = Array(N).fill(0n); - Provable.runAndCheck(() => { + await Provable.runAndCheck(() => { // with exists let fields = exists(N, () => arr); diff --git a/src/lib/scalar.test.ts b/src/lib/scalar.test.ts index 842ad8e395..f4a0adf212 100644 --- a/src/lib/scalar.test.ts +++ b/src/lib/scalar.test.ts @@ -1,22 +1,12 @@ -import { shutdown, isReady, Field, Bool, Provable, Scalar } from 'o1js'; +import { Field, Provable, Scalar } from 'o1js'; describe('scalar', () => { - beforeAll(async () => { - await isReady; - }); - - afterAll(async () => { - setTimeout(async () => { - await shutdown(); - }, 0); - }); - describe('scalar', () => { describe('Inside circuit', () => { describe('toFields', () => { it('should return an array of Fields', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Scalar, () => Scalar.random()); const fieldArr = x.toFields(); expect(Array.isArray(fieldArr)).toBe(true); @@ -29,7 +19,7 @@ describe('scalar', () => { it('should return the same', () => { expect(() => { let s0 = Scalar.random(); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let s1 = Provable.witness(Scalar, () => s0); Provable.assertEqual(Scalar.fromFields(s1.toFields()), s0); }); @@ -40,7 +30,7 @@ describe('scalar', () => { describe('fromBits', () => { it('should return a Scalar', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(Scalar, () => Scalar.fromBits(Field.random().toBits()) ); @@ -51,7 +41,7 @@ describe('scalar', () => { describe('random', () => { it('two different calls should be different', () => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const x = Provable.witness(Scalar, () => Scalar.random()); const y = Provable.witness(Scalar, () => Scalar.random()); expect(x).not.toEqual(y); diff --git a/src/lib/string.test.ts b/src/lib/string.test.ts index a1c11c1869..9a55e10d84 100644 --- a/src/lib/string.test.ts +++ b/src/lib/string.test.ts @@ -22,7 +22,7 @@ describe('Circuit String', () => { ); expect(str.equals(same_str)).toEqual(Bool(true)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString.fromString( 'Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth' ); @@ -38,7 +38,7 @@ describe('Circuit String', () => { const not_same_str = CircuitString.fromString('size'); expect(str.equals(not_same_str)).toEqual(Bool(false)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = Provable.witness(CircuitString, () => { return CircuitString.fromString('Your size'); }); @@ -62,7 +62,7 @@ describe('Circuit String', () => { ); expect(str.contains(contained_str)).toEqual(new Bool(true)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString.fromString( 'Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth' ); @@ -78,7 +78,7 @@ describe('Circuit String', () => { const not_contained_str = CircuitString.fromString('defhij'); expect(str.contains(not_contained_str)).toEqual(new Bool(false)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString.fromString('abcdefghijklmnop'); const not_contained_str = CircuitString.fromString('defhij'); expect(str.contains(not_contained_str)).toEqual(new Bool(false)); @@ -91,7 +91,7 @@ describe('Circuit String', () => { const contained_str = CircuitString.fromString('ab'); expect(str.contains(contained_str)).toEqual(new Bool(true)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString8.fromString('abcd'); const contained_str = CircuitString.fromString('ab'); expect(str.contains(contained_str)).toEqual(new Bool(true)); @@ -103,7 +103,7 @@ describe('Circuit String', () => { const contained_str = CircuitString8.fromString('ab'); expect(str.contains(contained_str)).toEqual(new Bool(true)); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString.fromString('abcd'); const contained_str = CircuitString8.fromString('ab'); expect(str.contains(contained_str)).toEqual(new Bool(true)); @@ -119,7 +119,7 @@ describe('Circuit String', () => { const str = CircuitString.fromString(js_str); expect(str.toString()).toBe(js_str); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const js_str = 'Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth'; const str = CircuitString.fromString(js_str); @@ -137,7 +137,7 @@ describe('Circuit String', () => { 'Everything we see is a perspective' ); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = CircuitString.fromString( 'Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth' ); @@ -154,7 +154,7 @@ describe('Circuit String', () => { const str2 = CircuitString.fromString('efgh'); expect(str1.append(str2).toString()).toBe('abcdefgh'); - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str1 = CircuitString.fromString('abcd'); const str2 = CircuitString.fromString('efgh'); expect(str1.append(str2).toString()).toBe('abcdefgh'); @@ -165,7 +165,7 @@ describe('Circuit String', () => { /* describe('CircuitString8', () => { test('cannot create more than 8 chars', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { Provable.witness(CircuitString8, () => { return CircuitString8.fromString('More than eight chars'); }); @@ -177,7 +177,7 @@ describe('Circuit String', () => { describe('with invalid input', () => { test.skip('cannot use a character out of range', () => { expect(() => { - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { const str = Provable.witness(CircuitString, () => { return CircuitString.fromCharacters([ new Character(Field(100)), diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts index e01fd0627a..90d2839a4b 100644 --- a/src/lib/testing/constraint-system.ts +++ b/src/lib/testing/constraint-system.ts @@ -12,7 +12,7 @@ import { Tuple } from '../util/types.js'; import { Random } from './random.js'; import { test } from './property.js'; import { Undefined, ZkProgram } from '../proof-system.js'; -import { printGates } from '../provable-context.js'; +import { constraintSystemSync, printGates } from '../provable-context.js'; export { constraintSystem, @@ -62,7 +62,7 @@ function constraintSystem>>( let layouts = args.slice(0, -1); // compute the constraint system - let { gates } = Provable.constraintSystem(() => { + let { gates } = constraintSystemSync(() => { // each random input "layout" has to be instantiated into vars in this circuit let values = types.map((type, i) => instantiate(type, layouts[i]) @@ -316,7 +316,7 @@ constraintSystem.gates = function gates>>( main: (...args: CsParams) => void ) { let types = inputs.from.map(provable); - let { gates } = Provable.constraintSystem(() => { + let { gates } = constraintSystemSync(() => { let values = types.map((type) => Provable.witness(type, (): unknown => { throw Error('not needed'); diff --git a/src/lib/testing/equivalent.ts b/src/lib/testing/equivalent.ts index d97b9fbae2..8d1be369aa 100644 --- a/src/lib/testing/equivalent.ts +++ b/src/lib/testing/equivalent.ts @@ -237,7 +237,7 @@ function equivalentProvable< ); // inside provable code - Provable.runAndCheck(() => { + Provable.runAndCheckSync(() => { let inputWitnesses = inputs2.map((x, i) => { let provable = from[i].provable; return provable !== undefined diff --git a/src/lib/util/assert.ts b/src/lib/util/assert.ts index df3f5a6749..d92057621e 100644 --- a/src/lib/util/assert.ts +++ b/src/lib/util/assert.ts @@ -1,7 +1,12 @@ -export { assert }; +export { assert, assertPromise }; function assert(stmt: boolean, message?: string): asserts stmt { if (!stmt) { throw Error(message ?? 'Assertion failed'); } } + +function assertPromise>(value: T): T { + assert(value instanceof Promise, 'Expected a promise'); + return value; +} diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 97530c32f8..b20c25045a 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -616,7 +616,7 @@ class SmartContract extends SmartContractBase { }; }); // run methods once to get information that we need already at compile time - let methodsMeta = this.analyzeMethods(); + let methodsMeta = await this.analyzeMethods(); let gates = methodIntfs.map((intf) => methodsMeta[intf.methodName].gates); let { verificationKey, provers, verify } = await compileProgram({ publicInputType: ZkappPublicInput, @@ -640,9 +640,9 @@ class SmartContract extends SmartContractBase { * a cached verification key can be used. * @returns the digest, as a hex string */ - static digest() { + static async digest() { // TODO: this should use the method digests in a deterministic order! - let methodData = this.analyzeMethods(); + let methodData = await this.analyzeMethods(); let hash = hashConstant( Object.values(methodData).map((d) => Field(BigInt('0x' + d.digest))) ); @@ -1097,7 +1097,7 @@ super.init(); * - `actions` the number of actions the method dispatches * - `gates` the constraint system, represented as an array of gates */ - static analyzeMethods({ printSummary = false } = {}) { + static async analyzeMethods({ printSummary = false } = {}) { let ZkappClass = this as typeof SmartContract; let methodMetadata = (ZkappClass._methodMetadata ??= {}); let methodIntfs = ZkappClass._methods ?? []; @@ -1105,21 +1105,14 @@ super.init(); !methodIntfs.every((m) => m.methodName in methodMetadata) && !inAnalyze() ) { - if (snarkContext.get().inRunAndCheck) { - let err = new Error( - 'Can not analyze methods inside Provable.runAndCheck, because this creates a circuit nested in another circuit' - ); - // EXCEPT if the code that calls this knows that it can first run `analyzeMethods` OUTSIDE runAndCheck and try again - (err as any).bootstrap = () => ZkappClass.analyzeMethods(); - throw err; - } let id: number; let insideSmartContract = !!smartContractContext.get(); if (insideSmartContract) id = smartContractContext.enter(null); try { for (let methodIntf of methodIntfs) { let accountUpdate: AccountUpdate; - let { rows, digest, result, gates, summary } = analyzeMethod( + let hasReturn = false; + let { rows, digest, gates, summary } = await analyzeMethod( ZkappPublicInput, methodIntf, (publicInput, publicKey, tokenId, ...args) => { @@ -1128,6 +1121,7 @@ super.init(); publicInput, ...args ); + hasReturn = result !== undefined; accountUpdate = instance.#executionState!.accountUpdate; return result; } @@ -1136,7 +1130,7 @@ super.init(); actions: accountUpdate!.body.actions.data.length, rows, digest, - hasReturn: result !== undefined, + hasReturn, gates, }; if (printSummary) console.log(methodIntf.methodName, summary()); @@ -1206,6 +1200,7 @@ type ReducerReturn = { initial: { state: State; actionState: Field }, options?: { maxTransactionsWithActions?: number; + maxActionsPerMethod?: number; skipActionStatePrecondition?: boolean; } ): { state: State; actionState: Field }; @@ -1221,6 +1216,7 @@ type ReducerReturn = { fromActionState: Field, options?: { maxTransactionsWithActions?: number; + maxActionsPerMethod?: number; skipActionStatePrecondition?: boolean; } ): Field; @@ -1283,6 +1279,7 @@ class ${contract.constructor.name} extends SmartContract { { state, actionState }: { state: S; actionState: Field }, { maxTransactionsWithActions = 32, + maxActionsPerMethod = 1, skipActionStatePrecondition = false, } = {} ): { state: S; actionState: Field } { @@ -1292,13 +1289,11 @@ class ${contract.constructor.name} extends SmartContract { Use the optional \`maxTransactionsWithActions\` argument to increase this number.` ); } - let methodData = ( - contract.constructor as typeof SmartContract - ).analyzeMethods(); - let possibleActionsPerTransaction = [ - ...new Set(Object.values(methodData).map((o) => o.actions)).add(0), - ].sort((x, y) => x - y); - + // TODO find out max actions per method automatically? + let possibleActionsPerTransaction = Array.from( + { length: maxActionsPerMethod + 1 }, + (_, i) => i + ); let possibleActionTypes = possibleActionsPerTransaction.map((n) => Provable.Array(reducer.actionType, n) ); @@ -1344,7 +1339,7 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number state = Provable.switch(lengths, stateType, newStates); } if (!skipActionStatePrecondition) { - contract.account.actionState.assertEquals(actionState); + contract.account.actionState.requireEquals(actionState); } return { state, actionState }; }, diff --git a/src/mina b/src/mina index c55d2f3fa5..0ddc4b90f5 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit c55d2f3fa5a4cd362d489633ac320188d62e794c +Subproject commit 0ddc4b90f54b73bb5279db690e74b4b7a0bd8495 diff --git a/src/snarky.d.ts b/src/snarky.d.ts index e11a2ccf63..2c10d27f8d 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -26,6 +26,8 @@ import type { WasmFqSrs, } from './bindings/compiled/node_bindings/plonk_wasm.cjs'; import type { KimchiGateType } from './lib/gates.ts'; +import type { MlConstraintSystem } from './lib/provable-context.ts'; +import type { FieldVector } from './bindings/crypto/bindings/vector.ts'; export { ProvablePure, Provable, Ledger, Pickles, Gate, GateType, getWasm }; @@ -200,21 +202,39 @@ declare const Snarky: { */ inProverBlock(): boolean; /** - * Runs code and checks its correctness. + * Setting that controls whether snarky throws an exception on violated constraint. */ - runAndCheck(f: () => void): void; + setEvalConstraints(value: MlBool): void; /** - * Runs code in prover mode, without checking correctness. + * Starts constraint system runner and returns a function to finish it. */ - runUnchecked(f: () => void): void; + enterConstraintSystem(): () => MlConstraintSystem; /** - * Returns information about the constraint system in the callback function. + * Starts witness generation and returns a function to finish it. */ - constraintSystem(f: () => void): { - rows: number; - digest: string; - json: JsonConstraintSystem; - }; + enterGenerateWitness(): () => [ + _: 0, + public_inputs: FieldVector, + auxiliary_inputs: FieldVector + ]; + }; + + /** + * APIs to interact with a `Backend.R1CS_constraint_system.t` + */ + constraintSystem: { + /** + * Returns the number of rows of the constraint system. + */ + rows(system: MlConstraintSystem): number; + /** + * Returns an md5 digest of the constraint system. + */ + digest(system: MlConstraintSystem): string; + /** + * Returns a JSON representation of the constraint system. + */ + toJson(system: MlConstraintSystem): JsonConstraintSystem; }; /** @@ -735,11 +755,11 @@ declare namespace Pickles { /** * The main circuit functions */ - main: (publicInput: MlArray) => { + main: (publicInput: MlArray) => Promise<{ publicOutput: MlArray; previousStatements: MlArray>; shouldVerify: MlArray; - }; + }>; /** * Feature flags which enable certain custom gates */ @@ -810,7 +830,7 @@ declare const Pickles: { /** * @returns (base64 vk, hash) */ - getVerificationKey: () => [_: 0, data: string, hash: FieldConst]; + getVerificationKey: () => Promise<[_: 0, data: string, hash: FieldConst]>; }; verify( diff --git a/tests/vk-regression/plain-constraint-system.ts b/tests/vk-regression/plain-constraint-system.ts index 7165b751c1..809c3742f8 100644 --- a/tests/vk-regression/plain-constraint-system.ts +++ b/tests/vk-regression/plain-constraint-system.ts @@ -132,7 +132,7 @@ function constraintSystem( let methodKeys = Object.keys(obj); return { - analyzeMethods() { + async analyzeMethods() { let cs: Record< string, { @@ -141,7 +141,7 @@ function constraintSystem( } > = {}; for (let key of methodKeys) { - let { rows, digest } = Provable.constraintSystem(obj[key]); + let { rows, digest } = await Provable.constraintSystem(obj[key]); cs[key] = { digest, rows, @@ -155,6 +155,6 @@ function constraintSystem( }; }, name, - digest: () => name, + digest: async () => name, }; } diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 150f05d5f3..b9070412fa 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -29,11 +29,11 @@ } }, "Membership_": { - "digest": "3c6c74360f42734aa1d3f76f497ec63a0bc39528b47a00c3be887a0b6ef1f8e1", + "digest": "39966549192610ffae8df962a0f868c4079b75133664ffc710c03669724e0b8d", "methods": { "addEntry": { - "rows": 1317, - "digest": "86c4cb3e10764e94741e23cce1342192" + "rows": 5530, + "digest": "db5487ca29e8e519022e1caa25fd4bcf" }, "isMember": { "rows": 433, diff --git a/tests/vk-regression/vk-regression.ts b/tests/vk-regression/vk-regression.ts index 5f9cff83e8..9360bcbbe4 100644 --- a/tests/vk-regression/vk-regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -19,12 +19,14 @@ let dump = process.argv[4] === '--dump'; let jsonPath = process.argv[dump ? 5 : 4]; type MinimumConstraintSystem = { - analyzeMethods(): Record< - string, - { - rows: number; - digest: string; - } + analyzeMethods(): Promise< + Record< + string, + { + rows: number; + digest: string; + } + > >; compile(): Promise<{ verificationKey: { @@ -32,7 +34,7 @@ type MinimumConstraintSystem = { data: string; }; }>; - digest(): string; + digest(): Promise; name: string; }; @@ -88,7 +90,7 @@ async function checkVk(contracts: typeof ConstraintSystems) { verificationKey: { data, hash }, } = await c.compile(); - let methodData = c.analyzeMethods(); + let methodData = await c.analyzeMethods(); for (const methodKey in methodData) { let actualMethod = methodData[methodKey]; @@ -145,8 +147,8 @@ but expected was async function dumpVk(contracts: typeof ConstraintSystems) { let newEntries: typeof RegressionJson = {}; for await (const c of contracts) { - let data = c.analyzeMethods(); - let digest = c.digest(); + let data = await c.analyzeMethods(); + let digest = await c.digest(); let verificationKey: | { data: string; hash: { toString(): string } } | undefined;