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

Add tutorial 02 to CI #566

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 15 additions & 1 deletion .github/workflows/test-tutorials.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,23 @@ jobs:
- uses: actions/setup-node@v3
with:
# In case of version change, update README.md accordingly
node-version: 18
node-version: 20
- run: |
npm ci
git config --global user.email "[email protected]"
git config --global user.name "Test"
npx ts-node scripts/tutorial-runner.ts docs/zkapps/tutorials/01-hello-world.mdx

private-inputs-and-hash-functions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
# In case of version change, update README.md accordingly
node-version: 20
- run: |
npm ci
git config --global user.email "[email protected]"
git config --global user.name "Test"
npx ts-node scripts/tutorial-runner.ts docs/zkapps/tutorials/02-private-inputs-hash-functions.mdx
268 changes: 172 additions & 96 deletions docs/zkapps/tutorials/02-private-inputs-hash-functions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ zkApp programmability is not yet available on the Mina Mainnet. You can get star

:::


# Tutorial 2: Private Inputs and Hash Functions

In the [Hello World](hello-world) tutorial, you built a basic zkApp smart contract with o1js with a single state variable that could be updated if you knew the square of that number.
Expand All @@ -49,68 +48,75 @@ Ensure your environment meets the [Prerequisites](/zkapps/tutorials#prerequisite

2. Create a project by using the `zk project` command:

```sh
$ zk project 02-private-inputs-and-hash-functions
```

The `zk project` command has the ability to scaffold the UI for your project. For this tutorial, select `none`:

```
? Create an accompanying UI project too? …
next
svelte
nuxt
empty
❯ none
```
The expected output is:

```sh
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit

Success!

Next steps:
cd 02-private-inputs-and-hash-functions
git remote add origin <your-repo-url>
git push -u origin main
```

The `zk project` command creates the `02-private-inputs-and-hash-functions` directory that contains the scaffolding for your project, including tools such as the Prettier code formatting tool, the ESLint static code analysis tool, and the Jest JavaScript testing framework.
<!-- Prerequisites for the tutorial runner - MDX doesn't display these comments but the tutorial runner still picks up the code
```sh
$ npm install -g zkapp-cli
```
--->

```sh
$ zk project 02-private-inputs-and-hash-functions
```

The `zk project` command has the ability to scaffold the UI for your project. For this tutorial, select `none`:

```sh
? Create an accompanying UI project too? …
next
svelte
nuxt
empty
❯ none
```

The expected output is:

```sh
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit

Success!

Next steps:
cd 02-private-inputs-and-hash-functions
git remote add origin <your-repo-url>
git push -u origin main
```

The `zk project` command creates the `02-private-inputs-and-hash-functions` directory that contains the scaffolding for your project, including tools such as the Prettier code formatting tool, the ESLint static code analysis tool, and the Jest JavaScript testing framework.

1. Change into the `02-private-inputs-and-hash-functions` directory and list the contents:

```sh
$ cd 02-private-inputs-and-hash-functions
$ ls
```

The output shows these results:

```sh
LICENSE
README.md
babel.config.cjs
build
config.json
jest-resolver.cjs
jest.config.js
keys
node_modules
package-lock.json
package.json
src
tsconfig.json
```

For this tutorial, you run commands from the root of the `02-private-inputs-and-hash-functions` directory as you work in the `src` directory on files that contain the TypeScript code for the smart contract.
```sh
$ cd 02-private-inputs-and-hash-functions
$ ls
```

The output shows these results:

```sh
LICENSE
README.md
babel.config.cjs
build
config.json
jest-resolver.cjs
jest.config.js
keys
node_modules
package-lock.json
package.json
src
tsconfig.json
```

For this tutorial, you run commands from the root of the `02-private-inputs-and-hash-functions` directory as you work in the `src` directory on files that contain the TypeScript code for the smart contract.

Each time you make updates, then build or deploy, the TypeScript code is compiled into JavaScript in the `build` directory.

Expand All @@ -120,35 +126,35 @@ Start by deleting the default files that come with the new project.

1. To delete the default generated files:

```sh
$ rm src/Add.ts
$ rm src/Add.test.ts
$ rm src/interact.ts
```
```sh
$ rm src/Add.ts
$ rm src/Add.test.ts
$ rm src/interact.ts
```

1. Now, create the new files for your project:

```sh
$ zk file src/IncrementSecret
$ touch src/main.ts
```
```sh
$ zk file src/IncrementSecret
$ touch src/main.ts
```

- The `zk file` command created the `src/IncrementSecret` file and the `src/IncrementSecret.test.ts` test file.
- However, this tutorial does not include writing tests, so you just use the `main.ts` file as a script to interact with the smart contract and observe how it works.
- However, this tutorial does not include writing tests, so you just use the `main.ts` file as a script to interact with the smart contract and observe how it works.

1. Now, open `src/index.ts` in a text editor and change it to look like:

```ts
import { IncrementSecret } from './IncrementSecret.js';

export { IncrementSecret };
```
```ts src/index.ts
1 import { IncrementSecret } from './IncrementSecret.js';
2
3 export { IncrementSecret };
```

The `src/index.ts` file contains all of the exports you want to make available for consumption from outside your smart contract project, such as from a UI.
The `src/index.ts` file contains all of the exports you want to make available for consumption from outside your smart contract project, such as from a UI.

### Copy the example files

This tutorial relies on the completed code in the [02-private-inputs-and-hash-functions/src/](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/) example files.
This tutorial relies on the completed code in the [02-private-inputs-and-hash-functions/src/](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/) example files.

1. First, open the [IncrementSecret.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/IncrementSecret.ts) example file.

Expand All @@ -158,15 +164,15 @@ This tutorial relies on the completed code in the [02-private-inputs-and-hash-fu

1. Copy the entire contents of the file into your smart contract in the `main.ts` file.

Now you are ready to review the imports in the smart contract.
Now you are ready to review the imports in the smart contract.

## Write the smart contract

Now we'll build the smart contract for our application.

### Imports

The `import` statement in the `IncrementSecret.ts` file brings in other packages and dependencies to use in your smart contract.
The `import` statement in the `IncrementSecret.ts` file brings in other packages and dependencies to use in your smart contract.

:::info

Expand All @@ -182,11 +188,10 @@ All functions used inside a smart contract must operate on o1js compatible data

The smart contract called `IncrementSecret` has one element of on-chain state named `x` of type `Field` as defined by following code:

```ts ignore
...
```ts src/IncrementSecret.ts
3 export class IncrementSecret extends SmartContract {
@state(Field) x = State<Field>();
}
4 @state(Field) x = State<Field>();
5 }
```

This code adds the basic structure for the smart contract. You are familiar with the import and export code from [Tutorial 01: Hello World](hello-world).
Expand All @@ -195,12 +200,14 @@ This code adds the basic structure for the smart contract. You are familiar with

The `initState()` method is intended to run once to set up the initial state on the zkApp account.

```ts ignore
...
5
6 @method initState(salt: Field, firstSecret: Field) {
7 this.x.set(Poseidon.hash([ salt, firstSecret ]));
8 }
```ts src/IncrementSecret.ts
3 export class IncrementSecret extends SmartContract {
4 @state(Field) x = State<Field>();
5
6 @method initState(salt: Field, firstSecret: Field) {
7 this.x.set(Poseidon.hash([ salt, firstSecret ]));
8 }
9 }
```

The `initState()` method accepts your secret and adds a `salt` value.
Expand All @@ -211,8 +218,7 @@ These inputs to the `initState()` method are private to whoever initializes the

This method updates the state:

```ts ignore
...
```ts src/IncrementSecret.ts
9
10 @method incrementSecret(salt: Field, secret: Field) {
11 const x = this.x.get();
Expand All @@ -234,24 +240,94 @@ The `incrementSecret()` method checks that the hash of the salt and the secret i
- o1js creates a proof of this fact and a JSON description of the state updates to be made on the zkApp account, such as to store the new hash value.
- Together, this forms a transaction that can be sent to the Mina network to update the zkApp account.

Because zkApp smart contracts are run off chain, your salt and secret remain private and are never transmitted anywhere.
Because zkApp smart contracts are run off chain, your salt and secret remain private and are never transmitted anywhere.

Only the result, updating `x` on-chain state to `hash([ salt, secret + 1])` is revealed. Because the salt and secret can't be deduced from their hash, they remain private.

### About the `salt` argument

Cryptographic salt adds an additional layer of security to a smart contract. The extra `salt` argument prevents a possible attack on the smart contract. If you just use `secret`, the contract is vulnerable to discovery by an attacker. An attacker could try hashing likely secrets and then check if the hash matches the hash stored in the smart contract. If the hash were to match, then the attacker knows they have discovered the secret. This scenario is particularly concerning if the secret is likely to be within a particular subset of possible values, say between 1 and 10,000. In that case, with just 10,000 hashes, the attacker could discover the secret.

Adding salt as a second input to the contract code makes it harder for an attacker to reverse engineer the code and gain access to the contract. Salt makes the contract more secure and helps protect the data stored within it. For optimal security, the salt is known only to you and is typically random.
Adding salt as a second input to the contract code makes it harder for an attacker to reverse engineer the code and gain access to the contract. Salt makes the contract more secure and helps protect the data stored within it. For optimal security, the salt is known only to you and is typically random.

## Main

The `src/main.ts` file is similar to the Hello World tutorial. For a full version, see [main.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/main.ts).
The `src/main.ts` file is similar to the Hello World tutorial. For a full version, see [main.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/main.ts).

For this tutorial, the key parts to discuss are initializing our contract and using the poseidon hash.

The smart contract initialization this time is:

<!--

Prerequisites for the tutorial runner - MDX doesn't display these comments but the tutorial runner still picks up the code
We initialize the main.ts script with the boilerplate from the previous tutorial


```ts src/main.ts
1 import { IncrementSecret } from './IncrementSecret.js';
2 import {
3 isReady,
4 shutdown,
5 Field,
6 Mina,
7 PrivateKey,
8 AccountUpdate,
9 } from 'snarkyjs';
10
11 await isReady;
12
13 console.log('SnarkyJS loaded');
14
15 const useProof = false;
16
17 const Local = Mina.LocalBlockchain({ proofsEnabled: useProof });
18 Mina.setActiveInstance(Local);
19 const { privateKey: deployerKey, publicKey: deployerAccount } =
20 Local.testAccounts[0];
21 const { privateKey: senderKey, publicKey: senderAccount } =
22 Local.testAccounts[1];
23
24 const salt = Field.random();
25
26 // ----------------------------------------------------
27
28 // create a destination we will deploy the smart contract to
29 const zkAppPrivateKey = PrivateKey.random();
30 const zkAppAddress = zkAppPrivateKey.toPublicKey();
31
32 const zkAppInstance = new IncrementSecret(zkAppAddress);
33 const deployTxn = await Mina.transaction(deployerAccount, () => {
34 AccountUpdate.fundNewAccount(deployerAccount);
35 zkAppInstance.deploy();
36 zkAppInstance.initState(salt, Field(750));
37 });
38 await deployTxn.prove();
39 await deployTxn.sign([deployerKey, zkAppPrivateKey]).send();
40
41 // get the initial state of IncrementSecret after deployment
42 const num0 = zkAppInstance.x.get();
43 console.log('state after init:', num0.toString());
44
45 // ----------------------------------------------------
46
47 const txn1 = await Mina.transaction(senderAccount, () => {
48 zkAppInstance.incrementSecret(salt, Field(750));
49 });
50 await txn1.prove();
51 await txn1.sign([senderKey]).send();
52
53 const num1 = zkAppInstance.x.get();
54 console.log('state after txn1:', num1.toString());
55
56 // ----------------------------------------------------
57
58 console.log('Shutting down');
59
60 await shutdown();
```
-->

```ts ignore
...
24 const salt = Field.random();
Expand All @@ -270,7 +346,7 @@ Note that the `initState()` method accepts the salt and the secret. In this case

This code creates a user transaction to update the on-chain state:

```ts
```ts ignore
...
47 const txn1 = await Mina.transaction(senderAccount, () => {
48 zkAppInstance.incrementSecret(salt, Field(750));
Expand Down Expand Up @@ -305,4 +381,4 @@ The `state` strings are different because `Field.random()` generates the salt.

Congratulations! You built a smart contract that uses privacy and hash functions.

To deploy zkApps to a live network, see [Tutorial 3: Deploy to a Live Network](deploying-to-a-network).
To deploy zkApps to a live network, see [Tutorial 3: Deploy to a Live Network](deploying-to-a-network).
Loading