diff --git a/docs/zkapps/tutorials/06-offchain-storage.mdx b/docs/zkapps/tutorials/06-offchain-storage.mdx index 468af01bd..48b6ccddf 100644 --- a/docs/zkapps/tutorials/06-offchain-storage.mdx +++ b/docs/zkapps/tutorials/06-offchain-storage.mdx @@ -152,6 +152,7 @@ For all projects, you run `zk` commands from the root of your project directory. $ rm src/interact.ts $ zk file src/NumberTreeContract $ touch src/main.ts + $ touch src/utils.ts ``` 1. Edit `index.ts` to import and export your new smart contract: @@ -162,6 +163,119 @@ For all projects, you run `zk` commands from the root of your project directory. export { NumberTreeContract }; ``` +1. Edit `utils.ts` to export required functions for `main.ts`: + + ```ts + import { Field, Mina, PrivateKey, PublicKey, fetchAccount } from 'o1js'; + + export const loopUntilAccountExists = async ({ + account, + eachTimeNotExist, + isZkAppAccount, + }: { + account: PublicKey; + eachTimeNotExist: () => void; + isZkAppAccount: boolean; + }) => { + for (;;) { + let response = await fetchAccount({ publicKey: account }); + let accountExists = response.error == null; + if (isZkAppAccount && response.account?.zkapp) { + accountExists = accountExists && response.account.zkapp.appState != null; + } + if (!accountExists) { + await eachTimeNotExist(); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } else { + return response.account!; + } + } + }; + + interface ToString { + toString: () => string; + } + + type FetchedAccountResponse = Awaited>; + type FetchedAccount = NonNullable; + + export const makeAndSendTransaction = async ({ + feePayerPrivateKey, + zkAppPublicKey, + mutateZkApp, + transactionFee, + getState, + statesEqual, + }: { + feePayerPrivateKey: PrivateKey; + zkAppPublicKey: PublicKey; + mutateZkApp: () => void; + transactionFee: number; + getState: () => State; + statesEqual: (state1: State, state2: State) => boolean; + }) => { + const initialState = getState(); + + await fetchAccount({ publicKey: feePayerPrivateKey.toPublicKey() }); + + let transaction = await Mina.transaction( + { feePayerKey: feePayerPrivateKey, fee: transactionFee }, + () => { + mutateZkApp(); + } + ); + + console.log('Creating an execution proof...'); + const time0 = Date.now(); + await transaction.prove(); + const time1 = Date.now(); + console.log('creating proof took', (time1 - time0) / 1e3, 'seconds'); + + console.log('Sending the transaction...'); + const res = await transaction.send(); + const hash = await res.hash(); + if (hash == null) { + console.log('error sending transaction (see above)'); + } else { + console.log( + 'See transaction at', + 'https://berkeley.minaexplorer.com/transaction/' + hash + ); + } + + let state = getState(); + + let stateChanged = false; + while (!stateChanged) { + console.log( + 'waiting for zkApp state to change... (current state: ', + state.toString() + ')' + ); + await new Promise((resolve) => setTimeout(resolve, 5000)); + await fetchAccount({ publicKey: zkAppPublicKey }); + state = await getState(); + stateChanged = !statesEqual(initialState, state); + } + }; + + export const zkAppNeedsInitialization = async ({ + zkAppAccount, + }: { + zkAppAccount: FetchedAccount; + }) => { + console.warn( + 'warning: using a `utils.ts` written before `isProved` made available. Check https://docs.minaprotocol.com/zkapps/tutorials/deploying-to-a-live-network for updates' + ); + + const allZeros = zkAppAccount.zkapp?.appState!.every((f: Field) => + f.equals(Field(0)).toBoolean() + ); + const needsInitialization = allZeros; + return needsInitialization; + }; + + ``` + 1. Now, add the experimental library for the off-chain storage server: ```sh @@ -280,13 +394,13 @@ Initialize the zkApp state for these three values by setting: storedNewRootSignature: Signature ) { const storedRoot = this.storageTreeRoot.get(); - this.storageTreeRoot.assertEquals(storedRoot); + this.storageTreeRoot.requireEquals(storedRoot); let storedNumber = this.storageNumber.get(); - this.storageNumber.assertEquals(storedNumber); + this.storageNumber.requireEquals(storedNumber); let storageServerPublicKey = this.storageServerPublicKey.get(); - this.storageServerPublicKey.assertEquals(storageServerPublicKey); + this.storageServerPublicKey.requireEquals(storageServerPublicKey); let leaf = [oldNum]; let newLeaf = [num]; diff --git a/examples/zkapps/06-offchain-storage/offchain-storage-zkapp/contracts/src/NumberTreeContract.ts b/examples/zkapps/06-offchain-storage/offchain-storage-zkapp/contracts/src/NumberTreeContract.ts index 15dbb2d38..43b7456a7 100644 --- a/examples/zkapps/06-offchain-storage/offchain-storage-zkapp/contracts/src/NumberTreeContract.ts +++ b/examples/zkapps/06-offchain-storage/offchain-storage-zkapp/contracts/src/NumberTreeContract.ts @@ -47,13 +47,13 @@ export class NumberTreeContract extends SmartContract { storedNewRootSignature: Signature ) { const storedRoot = this.storageTreeRoot.get(); - this.storageTreeRoot.assertEquals(storedRoot); + this.storageTreeRoot.requireEquals(storedRoot); let storedNumber = this.storageNumber.get(); - this.storageNumber.assertEquals(storedNumber); + this.storageNumber.requireEquals(storedNumber); let storageServerPublicKey = this.storageServerPublicKey.get(); - this.storageServerPublicKey.assertEquals(storageServerPublicKey); + this.storageServerPublicKey.requireEquals(storageServerPublicKey); let leaf = [oldNum]; let newLeaf = [num];