Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async circuit, pt. 3 - contract methods #1477

Merged
merged 20 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### Breaking changes

- **Async circuits**. Require all smart contract and zkprogram methods to be async https://github.com/o1-labs/o1js/pull/1477
- This change allows you to use `await` inside your methods. Change the method signature by adding the `async` keyword.
- Don't forget to add `await` to all contract calls! `await MyContract.myMethod();`
- To declare a return value from a method, use the new `@method.returns()` decorator
- Require the callback to `Mina.transaction()` to be async https://github.com/o1-labs/o1js/pull/1468
- This change was done in support to support async contract methods
- 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

- `Provable.witnessAsync()` to introduce provable values from an async callback https://github.com/o1-labs/o1js/pull/1468
- Internal benchmarking tooling to keep track of performance https://github.com/o1-labs/o1js/pull/1481
- Add `toInput` method for `Group` instance https://github.com/o1-labs/o1js/pull/1483

Expand All @@ -50,7 +53,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### Added

- `Provable.witnessAsync()` to introduce provable values from an async callback https://github.com/o1-labs/o1js/pull/1468
- Support for custom network identifiers other than `mainnet` or `testnet` https://github.com/o1-labs/o1js/pull/1444
- `PrivateKey.randomKeypair()` to generate private and public key in one command https://github.com/o1-labs/o1js/pull/1446
- `setNumberOfWorkers()` to allow developer to override the number of workers used during compilation and proof generation/verification https://github.com/o1-labs/o1js/pull/1456
Expand Down
5 changes: 0 additions & 5 deletions run-mina-integration-tests.sh

This file was deleted.

2 changes: 1 addition & 1 deletion src/examples/benchmarks/mul-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function picklesCircuit(nMuls: number) {
methods: {
run: {
privateInputs: [],
method() {
async method() {
main(nMuls);
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/examples/benchmarks/mul.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function picklesCircuit(nMuls: number) {
methods: {
run: {
privateInputs: [],
method() {
async method() {
main(nMuls);
},
},
Expand Down
20 changes: 10 additions & 10 deletions src/examples/circuit-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as assert from 'assert/strict';

// circuit which tests a couple of string features
class MyContract extends SmartContract {
@method checkString(s: CircuitString) {
@method async checkString(s: CircuitString) {
let sWithExclamation = s.append(CircuitString.fromString('!'));
sWithExclamation
.equals(CircuitString.fromString('a string!'))
Expand All @@ -26,21 +26,21 @@ console.log('compile...');
await MyContract.compile();
// should work
console.log('prove...');
let tx = await Mina.transaction(async () => {
new MyContract(address).checkString(CircuitString.fromString('a string'));
});
let tx = await Mina.transaction(() =>
new MyContract(address).checkString(CircuitString.fromString('a string'))
);
await tx.prove();
console.log('test 1 - ok');
// should work
tx = await Mina.transaction(async () => {
new MyContract(address).checkString(CircuitString.fromString('some string'));
});
tx = await Mina.transaction(() =>
new MyContract(address).checkString(CircuitString.fromString('some string'))
);
await tx.prove();
console.log('test 2 - ok');
// should fail
let fails = await Mina.transaction(async () => {
new MyContract(address).checkString(CircuitString.fromString('different'));
})
let fails = await Mina.transaction(() =>
new MyContract(address).checkString(CircuitString.fromString('different'))
)
.then(() => false)
.catch(() => true);
if (!fails) Error('proof was supposed to fail');
Expand Down
11 changes: 5 additions & 6 deletions src/examples/commonjs.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ class SimpleZkapp extends SmartContract {
this.x.set(initialState);
}

update(y) {
async update(y) {
this.emitEvent('update', y);
this.emitEvent('update', y);
this.account.balance.assertEquals(this.account.balance.get());
let x = this.x.get();
this.x.assertEquals(x);
this.account.balance.requireEquals(this.account.balance.get());
let x = this.x.getAndRequireEquals();
this.x.set(x.add(y));
}
}
Expand Down Expand Up @@ -58,14 +57,14 @@ async function main() {
console.log('deploy');
let tx = await Mina.transaction(feePayer, async () => {
AccountUpdate.fundNewAccount(feePayer);
zkapp.deploy();
await zkapp.deploy();
});
await tx.sign([feePayerKey, zkappKey]).send();

console.log('initial state: ' + zkapp.x.get());

console.log('update');
tx = await Mina.transaction(feePayer, async () => zkapp.update(Field(3)));
tx = await Mina.transaction(feePayer, () => zkapp.update(Field(3)));
await tx.prove();
await tx.sign([feePayerKey]).send();
console.log('final state: ' + zkapp.x.get());
Expand Down
4 changes: 2 additions & 2 deletions src/examples/crypto/ecdsa/ecdsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const keccakAndEcdsa = ZkProgram({
methods: {
verifyEcdsa: {
privateInputs: [Ecdsa.provable, Secp256k1.provable],
method(message: Bytes32, signature: Ecdsa, publicKey: Secp256k1) {
async method(message: Bytes32, signature: Ecdsa, publicKey: Secp256k1) {
return signature.verify(message, publicKey);
},
},
Expand All @@ -37,7 +37,7 @@ const ecdsa = ZkProgram({
methods: {
verifySignedHash: {
privateInputs: [Ecdsa.provable, Secp256k1.provable],
method(message: Scalar, signature: Ecdsa, publicKey: Secp256k1) {
async method(message: Scalar, signature: Ecdsa, publicKey: Secp256k1) {
return signature.verifySignedHash(message, publicKey);
},
},
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 @@ -90,7 +90,7 @@ class AlmostSmallField extends SmallField.AlmostReduced {}
class MyContract extends SmartContract {
@state(AlmostSmallField.provable) x = State<AlmostSmallField>();

@method myMethod(y: AlmostSmallField) {
@method async myMethod(y: AlmostSmallField) {
let x = y.mul(2);
Provable.log(x);
this.x.set(x.assertAlmostReduced());
Expand Down
2 changes: 1 addition & 1 deletion src/examples/crypto/sha256/sha256.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let SHA256Program = ZkProgram({
methods: {
sha256: {
privateInputs: [Bytes12.provable],
method(xs: Bytes12) {
async method(xs: Bytes12) {
return Gadgets.SHA256.hash(xs);
},
},
Expand Down
12 changes: 6 additions & 6 deletions src/examples/internals/advanced-provable-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ console.log(
*
* This is why we have this custom way of witnessing account updates, with the `skipCheck` option.
*/
result = await Provable.constraintSystem(() => {
let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness(
result = await Provable.constraintSystem(async () => {
let { accountUpdate: accountUpdateWitness } = await AccountUpdate.witness(
Empty,
() => ({ accountUpdate, result: undefined }),
async () => ({ accountUpdate, result: undefined }),
{ skipCheck: true }
);
Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate);
Expand All @@ -156,10 +156,10 @@ 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 = await Provable.constraintSystem(() => {
let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness(
result = await Provable.constraintSystem(async () => {
let { accountUpdate: accountUpdateWitness } = await AccountUpdate.witness(
Empty,
() => ({ accountUpdate, result: undefined }),
async () => ({ accountUpdate, result: undefined }),
{ skipCheck: true }
);
accountUpdateWitness.hash();
Expand Down
8 changes: 4 additions & 4 deletions src/examples/nullifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PayoutOnlyOnce extends SmartContract {
@state(Field) nullifierRoot = State<Field>();
@state(Field) nullifierMessage = State<Field>();

@method payout(nullifier: Nullifier) {
@method async payout(nullifier: Nullifier) {
let nullifierRoot = this.nullifierRoot.getAndRequireEquals();
let nullifierMessage = this.nullifierMessage.getAndRequireEquals();

Expand Down Expand Up @@ -75,7 +75,7 @@ console.log('deploy');
let tx = await Mina.transaction(sender, async () => {
let senderUpdate = AccountUpdate.fundNewAccount(sender);
senderUpdate.send({ to: zkappAddress, amount: initialBalance });
zkapp.deploy({ zkappKey });
await zkapp.deploy({ zkappKey });

zkapp.nullifierRoot.set(NullifierTree.getRoot());
zkapp.nullifierMessage.set(nullifierMessage);
Expand All @@ -96,7 +96,7 @@ console.log(jsonNullifier);
console.log('pay out');
tx = await Mina.transaction(sender, async () => {
AccountUpdate.fundNewAccount(sender);
zkapp.payout(Nullifier.fromJSON(jsonNullifier));
await zkapp.payout(Nullifier.fromJSON(jsonNullifier));
});
await tx.prove();
await tx.sign([senderKey]).send();
Expand All @@ -110,7 +110,7 @@ console.log('trying second pay out');

try {
tx = await Mina.transaction(sender, async () => {
zkapp.payout(Nullifier.fromJSON(jsonNullifier));
await zkapp.payout(Nullifier.fromJSON(jsonNullifier));
});

await tx.prove();
Expand Down
18 changes: 4 additions & 14 deletions src/examples/simple-zkapp-berkeley.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,9 @@ import {
SmartContract,
Mina,
AccountUpdate,
isReady,
shutdown,
DeployArgs,
fetchAccount,
} from 'o1js';

await isReady;

// a very simple SmartContract
class SimpleZkapp extends SmartContract {
@state(Field) x = State<Field>();
Expand All @@ -33,9 +28,8 @@ class SimpleZkapp extends SmartContract {
this.x.set(initialState);
}

@method update(y: Field) {
let x = this.x.get();
this.x.assertEquals(x);
@method async update(y: Field) {
let x = this.x.getAndRequireEquals();
y.assertGreaterThan(0);
this.x.set(x.add(y));
}
Expand Down Expand Up @@ -85,7 +79,7 @@ if (!isDeployed) {
{ sender: feePayerAddress, fee: transactionFee },
async () => {
AccountUpdate.fundNewAccount(feePayerAddress);
zkapp.deploy({ verificationKey });
await zkapp.deploy({ verificationKey });
}
);
// if you want to inspect the transaction, you can print it out:
Expand All @@ -102,9 +96,7 @@ if (isDeployed) {
console.log(`Found deployed zkapp, updating state ${x} -> ${x.add(10)}.`);
let transaction = await Mina.transaction(
{ sender: feePayerAddress, fee: transactionFee },
async () => {
zkapp.update(Field(10));
}
() => zkapp.update(Field(10))
);
// fill in the proof - this can take a while...
console.log('Creating an execution proof...');
Expand All @@ -117,5 +109,3 @@ if (isDeployed) {
console.log('Sending the transaction...');
await transaction.sign([feePayerKey]).send();
}

shutdown();
6 changes: 3 additions & 3 deletions src/examples/simple-zkapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class SimpleZkapp extends SmartContract {
this.x.set(initialState);
}

update(y) {
async update(y) {
this.emitEvent('update', y);
this.emitEvent('update', y);
this.account.balance.assertEquals(this.account.balance.get());
Expand Down Expand Up @@ -61,14 +61,14 @@ await SimpleZkapp.compile();
console.log('deploy');
let tx = await Mina.transaction(feePayer, async () => {
AccountUpdate.fundNewAccount(feePayer);
zkapp.deploy();
await zkapp.deploy();
});
await tx.sign([feePayerKey, zkappKey]).send();

console.log('initial state: ' + zkapp.x.get());

console.log('update');
tx = await Mina.transaction(feePayer, async () => zkapp.update(Field(3)));
tx = await Mina.transaction(feePayer, () => zkapp.update(Field(3)));
await tx.prove();
await tx.sign([feePayerKey]).send();
console.log('final state: ' + zkapp.x.get());
Expand Down
17 changes: 10 additions & 7 deletions src/examples/simple-zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ class SimpleZkapp extends SmartContract {

events = { update: Field, payout: UInt64, payoutReceiver: PublicKey };

@method init() {
@method
async init() {
super.init();
this.x.set(initialState);
}

@method update(y: Field): Field {
@method.returns(Field)
async update(y: Field) {
this.account.provedState.requireEquals(Bool(true));
this.network.timestamp.requireBetween(beforeGenesis, UInt64.MAXINT());
this.emitEvent('update', y);
Expand All @@ -42,7 +44,8 @@ class SimpleZkapp extends SmartContract {
* This method allows a certain privileged account to claim half of the zkapp balance, but only once
* @param caller the privileged account
*/
@method payout(caller: PrivateKey) {
@method
async payout(caller: PrivateKey) {
this.account.provedState.requireEquals(Bool(true));

// check that caller is the privileged account
Expand Down Expand Up @@ -112,7 +115,7 @@ console.log('account state is proved:', account.zkapp?.provedState.toBoolean());

console.log('update');
tx = await Mina.transaction(sender, async () => {
zkapp.update(Field(3));
await zkapp.update(Field(3));
});
await tx.prove();
await tx.sign([senderKey]).send();
Expand All @@ -128,7 +131,7 @@ await tx.sign([senderKey]).send();
console.log('payout');
tx = await Mina.transaction(sender, async () => {
AccountUpdate.fundNewAccount(sender);
zkapp.payout(privilegedKey);
await zkapp.payout(privilegedKey);
});
await tx.prove();
await tx.sign([senderKey]).send();
Expand All @@ -139,7 +142,7 @@ console.log(`final balance: ${zkapp.account.balance.get().div(1e9)} MINA`);

console.log('try to payout a second time..');
tx = await Mina.transaction(sender, async () => {
zkapp.payout(privilegedKey);
await zkapp.payout(privilegedKey);
});
try {
await tx.prove();
Expand All @@ -151,7 +154,7 @@ try {
console.log('try to payout to a different account..');
try {
tx = await Mina.transaction(sender, async () => {
zkapp.payout(Local.testAccounts[2].privateKey);
await zkapp.payout(Local.testAccounts[2].privateKey);
});
await tx.prove();
await tx.sign([senderKey]).send();
Expand Down
Loading
Loading