Skip to content

Commit

Permalink
Merge pull request #1477 from o1-labs/feature/async-methods-for-real
Browse files Browse the repository at this point in the history
Async circuit, pt. 3 - contract methods
  • Loading branch information
mitschabaude committed Mar 8, 2024
2 parents 9c2935a + 0a3df06 commit 7f750b6
Show file tree
Hide file tree
Showing 86 changed files with 706 additions and 1,515 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ 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
- Remove `this.sender` which unintuitively did not prove that its value was the actual sender of the transaction https://github.com/o1-labs/o1js/pull/1464 [@julio4](https://github.com/julio4)
Replaced by more explicit APIs:
Expand All @@ -38,6 +40,7 @@ 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
- 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 @@ -58,7 +61,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

0 comments on commit 7f750b6

Please sign in to comment.