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

Remove experimental callback #1430

Merged
merged 9 commits into from
Feb 13, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Adds `AccountUpdateTree` and `AccountUpdateForest`, new classes that represent a layout of account updates explicitly
- Both of the new types are now accepted as inputs to `approve()`
- `accountUpdate.extractTree()` to obtain the tree associated with an account update in the current transaction context.
- Remove `Experimental.Callback` API https://github.com/o1-labs/o1js/pull/1430

### Added

Expand Down
158 changes: 48 additions & 110 deletions src/examples/zkapps/token_with_proofs.ts
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored this example, which was using callbacks, to use the new TokenContract instead

Original file line number Diff line number Diff line change
@@ -1,98 +1,51 @@
import {
isReady,
method,
Mina,
AccountUpdate,
PrivateKey,
SmartContract,
PublicKey,
UInt64,
shutdown,
Int64,
Experimental,
Permissions,
DeployArgs,
VerificationKey,
TokenId,
TokenContract,
AccountUpdateForest,
} from 'o1js';

await isReady;

class TokenContract extends SmartContract {
deploy(args: DeployArgs) {
super.deploy(args);
this.setPermissions({
...Permissions.default(),
access: Permissions.proofOrSignature(),
});
this.balance.addInPlace(UInt64.from(initialBalance));
}

@method tokenDeploy(deployer: PrivateKey, verificationKey: VerificationKey) {
let address = deployer.toPublicKey();
let tokenId = this.token.id;
let deployUpdate = Experimental.createChildAccountUpdate(
this.self,
address,
tokenId
);
deployUpdate.account.permissions.set(Permissions.default());
deployUpdate.account.verificationKey.set(verificationKey);
deployUpdate.sign(deployer);
class Token extends TokenContract {
@method
approveBase(forest: AccountUpdateForest) {
this.checkZeroBalanceChange(forest);
}

@method mint(receiverAddress: PublicKey) {
let amount = UInt64.from(1_000_000);
let amount = 1_000_000;
this.token.mint({ address: receiverAddress, amount });
}

@method burn(receiverAddress: PublicKey) {
let amount = UInt64.from(1_000);
let amount = 1_000;
this.token.burn({ address: receiverAddress, amount });
}

@method sendTokens(
senderAddress: PublicKey,
receiverAddress: PublicKey,
callback: Experimental.Callback<any>
) {
// TODO use token contract methods for approve
let senderAccountUpdate = this.approve(callback) as AccountUpdate;
let amount = UInt64.from(1_000);
let negativeAmount = Int64.fromObject(
senderAccountUpdate.body.balanceChange
);
negativeAmount.assertEquals(Int64.from(amount).neg());
let tokenId = this.token.id;
senderAccountUpdate.body.tokenId.assertEquals(tokenId);
senderAccountUpdate.body.publicKey.assertEquals(senderAddress);
let receiverAccountUpdate = Experimental.createChildAccountUpdate(
this.self,
receiverAddress,
tokenId
);
receiverAccountUpdate.balance.addInPlace(amount);
}
}

class ZkAppB extends SmartContract {
@method approveSend() {
let amount = UInt64.from(1_000);
this.balance.subInPlace(amount);
this.balance.subInPlace(1_000);
}
}

class ZkAppC extends SmartContract {
@method approveSend() {
let amount = UInt64.from(1_000);
this.balance.subInPlace(amount);
this.balance.subInPlace(1_000);
}
}

let Local = Mina.LocalBlockchain();
Mina.setActiveInstance(Local);

let feePayer = Local.testAccounts[0].privateKey;
let [
{ publicKey: sender, privateKey: senderKey },
{ publicKey: tokenAccount1 },
] = Local.testAccounts;
let initialBalance = 10_000_000;

let tokenZkAppKey = PrivateKey.random();
Expand All @@ -104,10 +57,7 @@ let zkAppCAddress = zkAppCKey.toPublicKey();
let zkAppBKey = PrivateKey.random();
let zkAppBAddress = zkAppBKey.toPublicKey();

let tokenAccount1Key = Local.testAccounts[1].privateKey;
let tokenAccount1 = tokenAccount1Key.toPublicKey();

let tokenZkApp = new TokenContract(tokenZkAppAddress);
let tokenZkApp = new Token(tokenZkAppAddress);
let tokenId = tokenZkApp.token.id;

let zkAppB = new ZkAppB(zkAppBAddress, tokenId);
Expand All @@ -118,86 +68,74 @@ console.log('tokenZkAppAddress', tokenZkAppAddress.toBase58());
console.log('zkAppB', zkAppBAddress.toBase58());
console.log('zkAppC', zkAppCAddress.toBase58());
console.log('receiverAddress', tokenAccount1.toBase58());
console.log('feePayer', feePayer.toPublicKey().toBase58());
console.log('feePayer', sender.toBase58());
console.log('-------------------------------------------');

console.log('compile (TokenContract)');
await TokenContract.compile();
await Token.compile();
console.log('compile (ZkAppB)');
await ZkAppB.compile();
console.log('compile (ZkAppC)');
await ZkAppC.compile();

console.log('deploy tokenZkApp');
tx = await Local.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer, { initialBalance });
tokenZkApp.deploy({ zkappKey: tokenZkAppKey });
tx = await Mina.transaction(sender, () => {
tokenZkApp.deploy();
AccountUpdate.fundNewAccount(sender).send({
to: tokenZkApp.self,
amount: initialBalance,
});
});
await tx.send();

console.log('deploy zkAppB');
tx = await Local.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer);
tokenZkApp.tokenDeploy(zkAppBKey, ZkAppB._verificationKey!);
await tx.sign([senderKey, tokenZkAppKey]).send();

console.log('deploy zkAppB and zkAppC');
tx = await Mina.transaction(sender, () => {
AccountUpdate.fundNewAccount(sender, 2);
zkAppC.deploy();
zkAppB.deploy();
tokenZkApp.approveAccountUpdates([zkAppC.self, zkAppB.self]);
});
console.log('deploy zkAppB (proof)');
console.log('deploy zkAppB and zkAppC (proof)');
await tx.prove();
await tx.send();

console.log('deploy zkAppC');
tx = await Local.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer);
tokenZkApp.tokenDeploy(zkAppCKey, ZkAppC._verificationKey!);
});
console.log('deploy zkAppC (proof)');
await tx.prove();
await tx.send();
await tx.sign([senderKey, zkAppBKey, zkAppCKey]).send();

console.log('mint token to zkAppB');
tx = await Local.transaction(feePayer, () => {
tx = await Mina.transaction(sender, () => {
tokenZkApp.mint(zkAppBAddress);
});
await tx.prove();
await tx.send();
await tx.sign([senderKey]).send();

console.log('approve send from zkAppB');
tx = await Local.transaction(feePayer, () => {
let approveSendingCallback = Experimental.Callback.create(
zkAppB,
'approveSend',
[]
);
// we call the token contract with the callback
tokenZkApp.sendTokens(zkAppBAddress, zkAppCAddress, approveSendingCallback);
tx = await Mina.transaction(sender, () => {
zkAppB.approveSend();

// we call the token contract with the self update
tokenZkApp.transfer(zkAppB.self, zkAppCAddress, 1_000);
});
console.log('approve send (proof)');
await tx.prove();
await tx.send();
await tx.sign([senderKey]).send();

console.log(
`zkAppC's balance for tokenId: ${TokenId.toBase58(tokenId)}`,
Mina.getBalance(zkAppCAddress, tokenId).value.toBigInt()
);

console.log('approve send from zkAppC');
tx = await Local.transaction(feePayer, () => {
tx = await Mina.transaction(sender, () => {
// Pay for tokenAccount1's account creation
AccountUpdate.fundNewAccount(feePayer);
let approveSendingCallback = Experimental.Callback.create(
zkAppC,
'approveSend',
[]
);
// we call the token contract with the callback
tokenZkApp.sendTokens(zkAppCAddress, tokenAccount1, approveSendingCallback);
AccountUpdate.fundNewAccount(sender);
zkAppC.approveSend();

// we call the token contract with the tree
tokenZkApp.transfer(zkAppC.self, tokenAccount1, 1_000);
});
console.log('approve send (proof)');
await tx.prove();
await tx.send();
await tx.sign([senderKey]).send();

console.log(
`tokenAccount1's balance for tokenId: ${TokenId.toBase58(tokenId)}`,
Mina.getBalance(tokenAccount1, tokenId).value.toBigInt()
);

shutdown();
9 changes: 0 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,13 @@ export { ZkProgram };
export { Crypto } from './lib/crypto.js';

// experimental APIs
import { Callback } from './lib/zkapp.js';
import { createChildAccountUpdate } from './lib/account_update.js';
import { memoizeWitness } from './lib/provable.js';
export { Experimental };

const Experimental_ = {
Callback,
createChildAccountUpdate,
memoizeWitness,
};

type Callback_<Result> = Callback<Result>;

/**
* This module exposes APIs that are unstable, in the sense that the API surface is expected to change.
* (Not unstable in the sense that they are less functional or tested than other parts.)
Expand All @@ -132,10 +126,7 @@ namespace Experimental {
* The old `Experimental.ZkProgram` API has been deprecated in favor of the new `ZkProgram` top-level import.
*/
export let ZkProgram = ExperimentalZkProgram;
export let createChildAccountUpdate = Experimental_.createChildAccountUpdate;
export let memoizeWitness = Experimental_.memoizeWitness;
export let Callback = Experimental_.Callback;
export type Callback<Result> = Callback_<Result>;
}

Error.stackTraceLimit = 100000;
Expand Down
13 changes: 0 additions & 13 deletions src/lib/account_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ export {
TokenId,
Token,
CallForest,
createChildAccountUpdate,
zkAppProver,
dummySignature,
LazyProof,
Expand Down Expand Up @@ -1888,18 +1887,6 @@ class AccountUpdateLayout {
}
}

// TODO remove
function createChildAccountUpdate(
parent: AccountUpdate,
childAddress: PublicKey,
tokenId?: Field
) {
let child = AccountUpdate.defaultAccountUpdate(childAddress, tokenId);
child.body.callDepth = parent.body.callDepth + 1;
AccountUpdate.unlink(child);
return child;
}

// authorization

type ZkappCommand = {
Expand Down
7 changes: 0 additions & 7 deletions src/lib/circuit_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,13 +626,6 @@ function cloneCircuitValue<T>(obj: T): T {
// primitive JS types and functions aren't cloned
if (typeof obj !== 'object' || obj === null) return obj;

// HACK: callbacks
if (
obj.constructor?.name.includes('GenericArgument') ||
obj.constructor?.name.includes('Callback')
) {
return obj;
}
// classes that define clone() are cloned using that method
if (obj.constructor !== undefined && 'clone' in obj.constructor) {
return (obj as any).constructor.clone(obj);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/mina/token/token-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ abstract class TokenContract extends SmartContract {
transfer(
from: PublicKey | AccountUpdate,
to: PublicKey | AccountUpdate,
amount: UInt64
amount: UInt64 | number | bigint
) {
// coerce the inputs to AccountUpdate and pass to `approveUpdates()`
let tokenId = this.token.id;
Expand Down
Loading
Loading