Skip to content

Commit

Permalink
Merge pull request #1430 from o1-labs/refactor/remove-callback
Browse files Browse the repository at this point in the history
Remove experimental callback
  • Loading branch information
mitschabaude authored Feb 13, 2024
2 parents 9d43f86 + d7d9712 commit 723f776
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 265 deletions.
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
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

0 comments on commit 723f776

Please sign in to comment.