Skip to content

Commit

Permalink
Merge pull request #1450 from o1-labs/feature/async-circuits
Browse files Browse the repository at this point in the history
Async circuits, pt. 1 - async circuit runners
  • Loading branch information
mitschabaude authored Mar 7, 2024
2 parents 5a1bb8b + 3cbc4b5 commit 84233e9
Show file tree
Hide file tree
Showing 63 changed files with 549 additions and 510 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/ecdsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions benchmarks/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"include": ["."],
"exclude": [],
"compilerOptions": {
"noEmit": true, // no build output, we just want type checking
"rootDir": "..",
"baseUrl": "..",
"paths": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions src/examples/benchmarks/foreign-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());
2 changes: 1 addition & 1 deletion src/examples/benchmarks/hash-witness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ function main(nMuls: number) {
}

tic('run and check');
Provable.runAndCheck(() => main(nPermutations));
await Provable.runAndCheck(() => main(nPermutations));
toc();
2 changes: 1 addition & 1 deletion src/examples/benchmarks/keccak-witness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
6 changes: 3 additions & 3 deletions src/examples/benchmarks/mul-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/examples/benchmarks/mul-witness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ function main(nMuls: number) {
}

tic('run and check');
Provable.runAndCheck(() => main(nMuls));
await Provable.runAndCheck(() => main(nMuls));
toc();
6 changes: 3 additions & 3 deletions src/examples/benchmarks/mul.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down
16 changes: 9 additions & 7 deletions src/examples/constraint-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
4 changes: 2 additions & 2 deletions src/examples/crypto/ecdsa/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
2 changes: 1 addition & 1 deletion src/examples/crypto/foreign-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/examples/crypto/sha256/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/examples/encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions src/examples/internals/advanced-provable-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }),
Expand All @@ -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 }),
Expand Down
4 changes: 2 additions & 2 deletions src/examples/matrix-mul.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
2 changes: 1 addition & 1 deletion src/examples/simple-zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ if (doProofs) {
await SimpleZkapp.compile();
console.timeEnd('compile');
} else {
SimpleZkapp.analyzeMethods();
await SimpleZkapp.analyzeMethods();
}

console.log('deploy');
Expand Down
6 changes: 3 additions & 3 deletions src/examples/zkapps/dex/happy-path-with-proofs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)');
Expand Down
6 changes: 3 additions & 3 deletions src/examples/zkapps/dex/run-live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
6 changes: 3 additions & 3 deletions src/examples/zkapps/dex/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/examples/zkapps/dex/upgradability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)...');
Expand Down
2 changes: 1 addition & 1 deletion src/examples/zkapps/reducer/actions-as-merkle-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/examples/zkapps/reducer/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 0 additions & 3 deletions src/examples/zkapps/reducer/reducer-composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
SmartContract,
Mina,
AccountUpdate,
isReady,
Bool,
Struct,
Reducer,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/examples/zkapps/reducer/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion src/examples/zkapps/sudoku/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/examples/zkprogram/gadgets.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down
Loading

0 comments on commit 84233e9

Please sign in to comment.