From 53dd2f550cf4c8a5eb66c06a8c6bdddb98e5f1eb Mon Sep 17 00:00:00 2001 From: barriebyron Date: Mon, 12 Feb 2024 18:46:24 -0500 Subject: [PATCH 1/7] HF doc audit recursion --- docs/zkapps/o1js/recursion.mdx | 48 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/zkapps/o1js/recursion.mdx b/docs/zkapps/o1js/recursion.mdx index 01522dac9..ba0f14367 100644 --- a/docs/zkapps/o1js/recursion.mdx +++ b/docs/zkapps/o1js/recursion.mdx @@ -23,30 +23,32 @@ zkApp programmability is not yet available on the Mina Mainnet. You can get star # Recursion -Kimchi, the proof system that backs o1js, supports arbitrary infinite recursive proof construction of circuits through integration with the Pickles recursive system. Recursion is an incredibly powerful primitive that has a wide-array of uses, including: +Kimchi, the proof system that backs o1js, supports arbitrary infinite recursive proof construction of circuits through integration with the Pickles recursive system. + +Recursion is an incredibly powerful primitive that has a wide-array of uses. For example: 1. Mina uses linear recursive proofs to compress the blockchain, an infinitely growing structure, down to a constant size. -2. Mina also uses "rollup-like" tree-based recursive proofs to _in parallel_ compress transactions within blocks down to a constant size. -3. A Mastermind game that uses linear recursive proofs, an example of an application-specific rollup, to progress the state-machine of the application without needing to sync back to the game. -4. App-specific rollups can use recursion to communicate to each other, sort like app chains using IBC or parachains using XCVM to send messages. +2. Mina also uses "rollup-like" tree-based recursive proofs to, _in parallel_, compress transactions within blocks down to a constant size. +3. An app-specific rollup like a Mastermind game that uses linear recursive proofs to progress the state machine of the application without needing to sync back to the game. +4. App-specific rollups can use recursion to communicate to each other, like app chains using Inter-Blockchain Communication protocol [(IBC)](https://cosmos.network/ibc/) (Cosmos) or parachains using Cross-Chain Virtual Machine [(XVM)](https://wiki.polkadot.network/docs/learn-xcm) to send messages. More generally, you can use recursion to verify any zero knowledge program as part of your zkApp. ## ZkProgram Overview :::note -zkProgram has been moved out of the Experimental namespace and is available as a top-level import directly. `Experimental.ZkProgram` is deprecated. +zkProgram is available as a top-level import directly. `Experimental.ZkProgram` is deprecated. -If you are experiencing issues with zkProgram, be sure to update o1js to the latest version. +If you are experiencing issues with zkProgram, be sure to update [o1js](https://github.com/o1-labs/o1js) to the latest version. ::: -In o1js, you can use `ZkProgram()` to define the steps of a recursive program. Just like `SmartContract()` methods, `ZkProgram()` methods have any number of methods and execute off-chain. +In o1js, you can use `ZkProgram()` to define the steps of a recursive program. Like `SmartContract()` methods, `ZkProgram()` methods have any number of methods and execute off-chain. -After performing your desired recursive steps, you can settle the interaction on Mina's blockchain and by embedding the `ZkProgram` within a `SmartContract` method that verifies the underlying proof of execution and extracts the output that can be used elsewhere in the method (like storing the output in app-state, for example). +After performing the desired recursive steps, you can settle the interaction on Mina's blockchain by embedding `ZkProgram` within a `SmartContract` method that verifies the underlying proof of execution and extracts the output that can be used elsewhere in the method (like storing the output in app-state, for example). -Similar to methods within the `SmartContract` class, inputs to `ZkProgram` are _private by default_ and never seen by the Mina network. Unlike `SmartContract` methods, as the zkApp developer you choose the shape of the public input to all the methods within a `ZkProgram`. +Similar to methods within the `SmartContract` class, inputs to `ZkProgram` are _private by default_ and are never seen by the Mina network. Unlike `SmartContract` methods, as the zkApp developer you choose the shape of the public input to all methods within a `ZkProgram`. -## Recursively verifying a simple program in a zkApp +## Example: Recursively verify a simple program in a zkApp This simple example has only one method that proves the public input it received is zero. @@ -69,19 +71,19 @@ const SimpleProgram = ZkProgram({ }); ``` -Next, compile this program: +To compile this program: ```typescript const { verificationKey } = await SimpleProgram.compile(); ``` -Now you can use it to create a proof: +Now, you can use it to create a proof: ```typescript const proof = await SimpleProgram.run(Field(0)); ``` -And verify this proof from within any method of your `SmartContract` class: +To verify this proof from within any method of your `SmartContract` class: ```typescript @method foo(proof: SimpleProgram.Proof) { @@ -93,13 +95,13 @@ And verify this proof from within any method of your `SmartContract` class: } ``` -In this excample, `foo` is taking the `SimpleProgram` proof as a private argument to the method, verifying that the execution was valid, and then using the output. +In this example, `foo` is taking the `SimpleProgram` proof as a private argument to the method, verifying that the execution was valid, and then using the output. -## Recursively verifying a linear recursive program in a zkApp +## Example: Recursively verify a linear recursive program in a zkApp -This example shows a recursive `ZkProgram` that you can use to create recursive zero knowledge proofs. In other proof systems, this is extremely difficult to construct if it is even possible. However, in o1js you can describe a recursive ZkProgram with a simple recursive function. +This example shows a recursive `ZkProgram` that you can use to create recursive zero knowledge proofs. In other proof systems, this is extremely difficult to construct (if it is even possible). In o1js, you can describe a recursive ZkProgram with a simple recursive function. -This program describes a recursive operation of adding one repeatedly to a number. Note that you recursively depend on the older proof as a private argument to your method. +This program describes a recursive operation of adding one repeatedly to a number: ```typescript import { SelfProof, Field, ZkProgram, verify } from 'o1js'; @@ -129,6 +131,8 @@ const AddOne = ZkProgram({ }); ``` +Note that this example recursively depends on the older proof as a private argument to your method. + First, compile this program and make the base proof as before: ```typescript @@ -137,13 +141,13 @@ const { verificationKey } = await AddOne.compile(); const proof = await AddOne.baseCase(Field(0)); ``` -This time use this proof as input to recursively add one again: +This time, use this proof as input to recursively add one again: ```typescript const proof1 = await AddOne.step(Field(1), proof); ``` -And repeat this as many times as you want: +Repeat this as many times as you want: ```typescript const proof2 = await AddOne.step(Field(2), proof1); @@ -159,9 +163,9 @@ Finally, verify the proof from within a SmartContract like the earlier example: } ``` -## Recursively verifying a tree-based recursive program in a zkApp +## Example: Recursively verify a tree-based recursive program in a zkApp -Tree recursion is even more rarely seen in other proof systems and zk toolkits. This is used internally within Mina as part of its decentralized prover and sequencing mechanism for rollups, so it's supported very robustly by Kimchi. +Tree recursion is rarely seen in other proof systems and zk toolkits. Tree recursion is used internally within Mina as part of its decentralized prover and sequencing mechanism for rollups, so it's supported very robustly by Kimchi. This example program describes a very simple rollup for adding numbers: @@ -199,7 +203,7 @@ let RollupAdd = ZkProgram({ ## Bonus: Using ZkPrograms outside of zkApps -You can also use ZkProgram directly to prove and verify arbitrary zero knowledge programs (also known as circuits). +You can also use ZkProgram directly to prove and verify arbitrary zero knowledge programs (also known as circuits): ```typescript const { verificationKey } = await MyProgram.compile(); From 496ae056c962b8203382eb6e053dc3b327b567ce Mon Sep 17 00:00:00 2001 From: barriebyron Date: Mon, 12 Feb 2024 19:52:21 -0500 Subject: [PATCH 2/7] HF doc audit smart contracts --- docs/zkapps/o1js/recursion.mdx | 6 +- docs/zkapps/o1js/smart-contracts.md | 193 ++++++++++++---------------- 2 files changed, 84 insertions(+), 115 deletions(-) diff --git a/docs/zkapps/o1js/recursion.mdx b/docs/zkapps/o1js/recursion.mdx index ba0f14367..97fce6e8b 100644 --- a/docs/zkapps/o1js/recursion.mdx +++ b/docs/zkapps/o1js/recursion.mdx @@ -23,7 +23,7 @@ zkApp programmability is not yet available on the Mina Mainnet. You can get star # Recursion -Kimchi, the proof system that backs o1js, supports arbitrary infinite recursive proof construction of circuits through integration with the Pickles recursive system. +Kimchi, the custom proof system that backs o1js, supports arbitrary infinite recursive proof construction of circuits through integration with the Pickles recursive system. Mina Protocol is the only blockchain that offers infinite recursion. Recursion is an incredibly powerful primitive that has a wide-array of uses. For example: @@ -37,9 +37,7 @@ More generally, you can use recursion to verify any zero knowledge program as pa ## ZkProgram Overview :::note -zkProgram is available as a top-level import directly. `Experimental.ZkProgram` is deprecated. - -If you are experiencing issues with zkProgram, be sure to update [o1js](https://github.com/o1-labs/o1js) to the latest version. +zkProgram is available as a top-level import. `Experimental.ZkProgram` is deprecated. If you are experiencing issues with zkProgram, be sure to update [o1js](https://github.com/o1-labs/o1js) to the latest version. ::: In o1js, you can use `ZkProgram()` to define the steps of a recursive program. Like `SmartContract()` methods, `ZkProgram()` methods have any number of methods and execute off-chain. diff --git a/docs/zkapps/o1js/smart-contracts.md b/docs/zkapps/o1js/smart-contracts.md index 4397f879c..2cffcb00e 100644 --- a/docs/zkapps/o1js/smart-contracts.md +++ b/docs/zkapps/o1js/smart-contracts.md @@ -1,5 +1,5 @@ --- -title: Smart contracts +title: Smart Contracts hide_title: true description: How to create and interact with a smart contract. How to prove an on-chain value. Learn about the public state of a zkApp and private method parameters. keywords: @@ -26,29 +26,15 @@ zkApp programmability is not yet available on the Mina Mainnet. You can get star # Smart Contracts - - -Now that we have covered the basics of writing o1js programs, let's see how to create a smart contract. - -If you haven't yet read [how zkApps work](../how-zkapps-work), please read it first to build your foundational knowledge. - -## SmartContract - -Smart contracts are written by extending the base class `SmartContract`: +You write smart contracts by extending the base class `SmartContract`: ```ts class HelloWorld extends SmartContract {} ``` -The `constructor` of a `SmartContract` is inherited from the base class and should not be overriden. -It takes the zkApp account address (a public key) as its only argument: +The `constructor` of a `SmartContract` is inherited from the base class and cannot be overriden. + +The zkApp account address (a public key) is its only argument: ```ts let zkAppKey = PrivateKey.random(); @@ -57,15 +43,13 @@ let zkAppAddress = PublicKey.fromPrivateKey(zkAppKey); let zkApp = new HelloWorld(zkAppAddress); ``` -Later, you learn how to deploy a smart contract to an on-chain account. +## zkApp Accounts -:::note On Mina, there is no strong distinction between normal "user accounts" and "zkApp accounts". A zkApp account is just a normal account that has a smart contract deployed to it – which essentially just means there's a verification -key stored on the account, which can verify zero knowledge proofs generated +key stored on the account that can verify zero knowledge proofs generated with the smart contract. -::: ## Methods @@ -81,61 +65,41 @@ class HelloWorld extends SmartContract { Within a method, you can use o1js data types and methods to define your custom logic. -Later, you learn how you can... - -- run a method (off-chain) -- create a proof that it executed successfully -- send that proof to the Mina network, to trigger actions like a state change or payment - -To get an idea what "successful execution" means, look at this line in our example above: +To understand what successful execution means, look at this line in the example: ```ts x.mul(2).assertEquals(5); ``` Creating a proof for this method is be possible only if the input `x` satisfies the equation `x * 2 === 5`. This is called a "constraint". -Magically, the proof can be checked without seeing `x` – it's a _private input_. - -The method above is not very meaningful yet. To make it more interesting, you need a way to interact with accounts, and record state on-chain. -Check out the next section for more on that! +Magically, the proof can be checked without seeing `x` because it's a _private input_. -One more note about private inputs: The method above has one input parameter, `x` of type `Field`. In general, arguments can be any of the built-in o1js type that you saw: `Bool`, `UInt64`, `PrivateKey`, etc. From now on, those types are referred to as [structs`](#custom-data-types). +The method has one input parameter, `x` of type `Field`. In general, arguments can be any of the built-in o1js types: `Bool`, `UInt64`, `PrivateKey`, and so on. These types are referred to as [structs`](#custom-data-types). - +## zk-SNARK circuits -:::info +Internally, every `@method` defines a zk-SNARK circuit. From the cryptography standpoint, a smart contract is a collection of circuits, all of which are compiled into a single prover and a verification key. The proof says something to the effect of "I ran one of these methods, with some private input, and it produced this particular set of account updates". In zero knowledge proof terms, the account updates are the _public input_. The proof is accepted on the network only if it verifies against the verification key stored in the account. This verification requirement ensures that the same zkApp code also ran on the end user's device and that the account updates conform to the smart contract's rules. -Under the hood, every `@method` defines a zk-SNARK circuit. From the cryptography standpoint, a smart contract is a collection of circuits, all of which are compiled into a single prover & verification key. The proof says something to the effect of "I ran one of these methods, with some private input, and it produced this particular set of account updates". In ZKP terms, the account updates are the _public input_. The proof will only be accepted on the network if it verifies against the verification key stored in the account. This ensures that indeed, the same code that the zkApp developer wrote also ran on the user's device – thus, the account updates conform to the smart contract's rules. +### @method -::: +Inside a `@method`, things sometimes behave a little differently. - +To construct a circuit which can then be proven, o1js calls into SnarkyML, a language that builds circuits and connects variables and constraints. As a zkApp developer, you must use the methods, functions, and types provided by o1js. Plain JavaScript code does not call into SnarkyML and therefore is not able to construct circuits. -:::tip +When `SmartContract` is compiled into prover and verification keys, methods are in an environment where the method inputs don't have any concrete values attached to them. Instead, they are like mathematical variables `x`, `y`, `z` that are used to build up abstract computations like `x^2 + y^2` by running the method code. -You will find that inside a `@method`, things sometimes behave a little differently. For example, the following code can't be used in a method where `x: Field` is an input parameter: - -```ts -console.log(x.toString()); // don't do this inside a `@method`! 😬 -``` - -This doesn't work because, when we compile the SmartContract into prover and verification keys, we will run your method in an environment where the method inputs don't have any concrete values attached to them. They are like mathematical variables `x`, `y`, `z` which are used to build up abstract computations like `x^2 + y^2`, just by running your method code. - -Therefore, when executing your code and trying to read the value of `x` to turn it into a string via `x.toString()`, it will blow up because such a value can't be found. On the other hand, during proof generation all the variables _have_ actual values attached to them (cryptographers call them "witnesses"); and it makes perfect sense to want to log these values for debugging. -This is why we have a special function for logging stuff from inside your method: +In contrast, all the variables _have_ actual values attached to them (cryptographers call them "witnesses") during proof generation. +To log these values for debugging, use a special function for logging from inside your method: ```ts Provable.log(x); ``` -The API is like that of `console.log`, but it will automatically handle printing o1js data types in a nice format. During SmartContract compilation, it will simply do nothing. -::: +The API is like `console.log`, but it automatically handles printing o1js data types in a readable format. However, it does nothing during `SmartContract` compilation. ## On-chain state -A smart contract can contain **on-chain state**, which is declared as a property -on the class with the `@state` decorator: +A smart contract can contain [on-chain state](/glossary#on-chain-state). Declare it as a property on the class with the `@state` decorator: ```ts class HelloWorld extends SmartContract { @@ -145,9 +109,10 @@ class HelloWorld extends SmartContract { } ``` -Here, `x` is of type `Field`. Like with method inputs, only o1js structs can be used for state variables. -In the current design, the state can consist of at most 8 Fields of 32 bytes each. These states are stored on the zkApp account. -Some structs take up more than one Field: for example, a `PublicKey` needs 2 of the 8 Fields. +Here, `x` is of type `Field`. Like with method inputs, only o1js structs can be used for state variables. The state can consist of at most 8 fields of 32 bytes each. These states are stored on the zkApp account. + +Some structs take up more than one `Field`. For example, a `PublicKey` needs two of the eight fields. + States are initialized with the `State()` function. A method can modify on-chain state by using `this..set()`: @@ -166,7 +131,7 @@ As a zkApp developer, if you add this method to your smart contract, you are say ## Reading state -Often, we also want to _read_ state – check out this example: +This example _reads_ state: ```ts class HelloWorld extends SmartContract { @@ -183,36 +148,54 @@ class HelloWorld extends SmartContract { } ``` -The `increment()` method fetches the current on-chain state `x` with `this.x.get()`. +The `@increment()` method fetches the current on-chain state `x` with `this.x.get()`. Later, it sets the new state to `x + 1` using `this.x.set()`. Simple! -There's another line though, which looks weird at first: +Another line might looks weird at first: ```ts this.x.requireEquals(x); ``` -To understand it, we have to take a step back, and understand what it means to "use an on-chain value" during off-chain execution. +Here's what it means to "use an on-chain value" during off-chain execution. -For sure, when we use an on-chain value, we have to _prove_ that this is the on-chain value. Verification has to fail if it's a different value! Otherwise, a malicious user could modify o1js and make it just use any other value than the current on-chain state – breaking our zkApp. +When you use an on-chain value, you have to _prove_ that this value is the on-chain value. Verification has to fail if it's a different value. Otherwise, a malicious user could modify o1js and make it just use any other value than the current on-chain state – breaking the zkApp. -To prevent that, we link "`x` at proving time" to be the same as "`x` at verification time". We call this a _precondition_ – a condition that is checked by the verifier (a Mina node) when it receives the proof in a transaction. This is what `this.x.requireEquals(x)` does: it adds the precondition that `this.x` – the on-chain state at verification time – has to equal `x` – the value we fetched from the chain on the client-side. In zkSNARK language, `x` becomes part of the public input. +You must link "`x` at proving time" to be the same as "`x` at verification time". This is a _precondition_, a condition that is checked by the verifier (a Mina node) when it receives the proof in a transaction: -Side note: `this..requireEquals` is more flexible than equating with the current value. For example, `this.x.requireEquals(10)` fixes the on-chain `x` to the number `10`. +```ts +this.x.requireEquals(x) +``` -:::note +This code adds the precondition that `this.x` – the on-chain state at verification time – must equal `x` – the value fetched from the chain on the client side. In zkSNARK language, `x` becomes part of the public input. -Why didn't we just make `this.x.get()` add the precondition, automatically, so that you didn't have to write `this.x.requireEquals(x)`? -Well, we like to keep things explicit. The assertion reminds us that we add logic which can make the proof fail: If `x` isn't the same at verification time, the transaction will be rejected. So, reading on-chain values has to be done with care if many users are supposed to read and update state concurrently. It is applicable in some situations, but might cause races, and call for workarounds, in other situations. -One such workaround is the use of actions – see [Actions and Reducer](./actions-and-reducer). +Using `this..requireEquals` is more flexible than equating with the current value. For example, `this.x.requireEquals(10)` fixes the on-chain `x` to the number `10`. -::: +Why not use `this.x.get()` to add the precondition automatically, instead of writing `this.x.requireEquals(x)`? +To keep things explicit. The assertion reminds you to add logic which makes the proof fail: If `x` isn't the same at verification time, the transaction will be rejected. + +So, you must use care to read on-chain values if many users are expected to read and update state concurrently. It is applicable in some situations, but might cause race conditions or call for workarounds, in some situations. +One workaround is to use actions. See [Actions and Reducer](/zkapps/o1js/actions-and-reducer). ## Assertions - +Assertions can be incredibly useful to constrain state updates. -Let's modify the `increment()` method to accept a parameter: +Common assertions you can use are: + + +```ts +x.assertEquals(y); // x = y +x.assertBoolean(); // x = 0 or x = 1 +x.assertLt(y); // x < y +x.assertLte(y); // x <= y +x.assertGt(y); // x > y +x.assertGte(y); // x >= y +``` + +For a full list, see the [o1js reference](/zkapps/o1js-reference). + +To modify the `increment()` method to accept a parameter: ```ts class HelloWorld extends SmartContract { @@ -229,51 +212,34 @@ class HelloWorld extends SmartContract { } ``` -Here, after obtaining the current state `x` and asserting that it equals the on-chain value, we make another assertion: +Here, after obtaining the current state `x` and asserting that it equals the on-chain value, make another assertion: ```ts x.add(1).assertEquals(xPlus1); ``` -If the assertion fails, o1js will throw an error and not submit the transaction. -On the other hand, if it succeeds, it becomes part of the proof that is verified on-chain. - -Because of this, our new version of `increment()` is _guaranteed_ to behave like the previous version: It can only ever -update the state `x` to `x + 1`. - -:::tip -You can add optional failure messages to assertions, to make debugging easier. For example, the above example could be written as: +If the assertion fails, o1js throws an error and does not submit the transaction. +If it succeeds, it becomes part of the proof that is verified on-chain. -```ts -x.add(1).assertEquals(xPlus1, 'x + 1 should equal xPlus1'); -``` +Because of this, the new version of `increment()` is _guaranteed_ to behave like the previous version: It can only ever update the state `x` to `x + 1`. -::: +### Debugging -Assertions can be incredibly useful to constrain state updates. Common assertions you may use are: +Add optional failure messages to assertions to make debugging easier. For example, write the previous example as: - ```ts -x.assertEquals(y); // x = y -x.assertBoolean(); // x = 0 or x = 1 -x.assertLt(y); // x < y -x.assertLte(y); // x <= y -x.assertGt(y); // x > y -x.assertGte(y); // x >= y +x.add(1).assertEquals(xPlus1, 'x + 1 should equal xPlus1'); ``` -For a full list, see the [o1js reference](../o1js-reference). - ## Public and private inputs While the state of a zkApp is **public**, method parameters are **private**. When a smart contract method is called, the proof it produces uses zero knowledge to hide inputs and details of the computation. -The only way method parameters can be exposed is when the computation explicitly exposes them, as -in the last example where the input was directly stored in the public state: `this.x.set(xPlus1);` +The only way method parameters can be exposed is when the computation explicitly exposes them. For example, in the last example the input was directly stored in the public state: `this.x.set(xPlus1);` -For example where this is not the case, define a new method called `incrementSecret()`: +If this were not the case, define a new method called `incrementSecret()`: ```ts class HelloWorld extends SmartContract { @@ -292,10 +258,9 @@ class HelloWorld extends SmartContract { ``` This time, the input is called `secret`. Check that the hash of the secret is equal to the current state `x`. -If this is the case, add 1 to the secret and set `x` to the hash of that. +If this is the case, add `1` to the secret and set `x` to the hash of that. -When running this successfully, it just proves that the code was run with _some_ input `secret` whose hash is `x`, -and that the new `x` is set to `hash(secret + 1)`. +When this code is run successfully, it just proves that the code was run with _some_ input `secret` whose hash is `x` and that the new `x` is set to `hash(secret + 1)`. However, the secret itself remains private, because it can't be deduced from its hash. ## Initializing state @@ -303,7 +268,9 @@ However, the secret itself remains private, because it can't be deduced from its You initialize on-chain state in the `init()` method. Like the constructor, `init()` is predefined on the base `SmartContract` class. -It is called when you deploy your zkApp with the zkApp CLI, for the first time. It won't be called if you upgrade your contract and deploy a second time. +It is called when you deploy your zkApp with the zkApp CLI for the first time. + +It is not called if you upgrade your contract and deploy a second time. You can override this method to add initialization of your on-chain state: ```ts @@ -339,14 +306,17 @@ class OtherContract extends SmartContract { } ``` -When a user calls `HelloWorld.myMethod()`, o1js creates two separate proofs — one for the execution of `myMethod()` as usual, and a _separate_ proof for the execution of `OtherContract.otherMethod()`. +When a user calls `HelloWorld.myMethod()`, o1js creates two separate proofs: + +- One proof for the execution of `myMethod()` as usual +- A _separate_ proof for the execution of `OtherContract.otherMethod()` The `myMethod()` proof: - Computes an appropriate hash of the function signature of `otherMethod()` plus any arguments and return values of that function call. - Guarantees that this hash matches the `callData` field on the account update produced by `otherMethod()` that is made part of `myMethod()`'s public input. -Therefore, when you calling another zkApp method, you effectively prove: "I called a method with this name, on this zkApp account, with this particular arguments and return value." +Therefore, when you call another zkApp method, you effectively prove: "I called a method with this name, on this zkApp account, with this particular arguments and return value." To ensure other methods can use a return value of your `@method`, you must annotate the return value in your TypeScript function signature. @@ -361,15 +331,15 @@ Here's an example of returning a `Bool` called `isSuccess`: ## Custom data types -Smart contract method arguments can be any of the built-in o1js types. +Smart contract method arguments can be any of the built-in [o1js types](/zkapps/o1js/basic-concepts#built-in-data-types). However, what if you want to define your own data type? -You can create a custom data type for your smart contract using the `Struct` function that o1js exposes. To do this, create a class that extends `Struct({ })`. +You can create a custom data type for your smart contract using the `Struct` function that o1js exposes. Create a class that extends `Struct({ })`. Then, inside the object `{ }`, define the fields that you want to use in your custom data type. -For example, if you want to create a custom data type called `Point` to represent a 2D point on a grid. The `Point` struct has no instance methods and is used only to hold information about the `x` and `y` points. -You can create a Point class by creating a new class that extends the `Struct` class: +For example, you can create a custom data type called `Point` to represent a 2D point on a grid. The `Point` struct has no instance methods and is used only to hold information about the `x` and `y` points. +Create a Point class by creating a new class that extends the `Struct` class: ```ts class Point extends Struct({ @@ -402,5 +372,6 @@ export class Grid extends SmartContract { } ``` -Note that your Structs can contain o1js built-in types like `Field`, `Bool`, `UInt64`, etc or even other custom types that you've defined which are based on the `Struct` class. -This allows for great composability and reusability of structs. +Note that your Structs can contain o1js built-in types like `Field`, `Bool`, `UInt64`, and so on, or even other custom types that you've defined which are based on the `Struct` class. + +This flexibility allows for great composability and reusability of structs. From 57cc678874b411065f036e8009ef18a959990a92 Mon Sep 17 00:00:00 2001 From: barriebyron Date: Tue, 13 Feb 2024 14:59:20 -0500 Subject: [PATCH 3/7] minor updates ready for review --- docs/zkapps/o1js/smart-contracts.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/zkapps/o1js/smart-contracts.md b/docs/zkapps/o1js/smart-contracts.md index 2cffcb00e..1fefbcfe5 100644 --- a/docs/zkapps/o1js/smart-contracts.md +++ b/docs/zkapps/o1js/smart-contracts.md @@ -268,9 +268,10 @@ However, the secret itself remains private, because it can't be deduced from its You initialize on-chain state in the `init()` method. Like the constructor, `init()` is predefined on the base `SmartContract` class. -It is called when you deploy your zkApp with the zkApp CLI for the first time. -It is not called if you upgrade your contract and deploy a second time. +- It is called when you deploy your zkApp with the zkApp CLI for the first time. +- It is not called if you upgrade your contract and deploy a second time. + You can override this method to add initialization of your on-chain state: ```ts @@ -306,7 +307,7 @@ class OtherContract extends SmartContract { } ``` -When a user calls `HelloWorld.myMethod()`, o1js creates two separate proofs: +When a zkApp user calls `HelloWorld.myMethod()`, o1js creates two separate proofs: - One proof for the execution of `myMethod()` as usual - A _separate_ proof for the execution of `OtherContract.otherMethod()` From 6f1d880bee9036c536a5e7dfb03320eb510ffa77 Mon Sep 17 00:00:00 2001 From: barriebyron Date: Wed, 14 Feb 2024 16:52:27 -0500 Subject: [PATCH 4/7] change to .mdx file name extension --- docs/zkapps/o1js/{smart-contracts.md => smart-contracts.mdx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zkapps/o1js/{smart-contracts.md => smart-contracts.mdx} (100%) diff --git a/docs/zkapps/o1js/smart-contracts.md b/docs/zkapps/o1js/smart-contracts.mdx similarity index 100% rename from docs/zkapps/o1js/smart-contracts.md rename to docs/zkapps/o1js/smart-contracts.mdx From 78eb4868fa5bd32f6895ce0b58582227faca64b2 Mon Sep 17 00:00:00 2001 From: barriebyron Date: Thu, 15 Feb 2024 09:40:36 -0500 Subject: [PATCH 5/7] remove number of methods --- docs/zkapps/o1js/recursion.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zkapps/o1js/recursion.mdx b/docs/zkapps/o1js/recursion.mdx index 97fce6e8b..1beb664fd 100644 --- a/docs/zkapps/o1js/recursion.mdx +++ b/docs/zkapps/o1js/recursion.mdx @@ -40,7 +40,7 @@ More generally, you can use recursion to verify any zero knowledge program as pa zkProgram is available as a top-level import. `Experimental.ZkProgram` is deprecated. If you are experiencing issues with zkProgram, be sure to update [o1js](https://github.com/o1-labs/o1js) to the latest version. ::: -In o1js, you can use `ZkProgram()` to define the steps of a recursive program. Like `SmartContract()` methods, `ZkProgram()` methods have any number of methods and execute off-chain. +In o1js, you can use `ZkProgram()` to define the steps of a recursive program. Like `SmartContract()` methods, `ZkProgram()` methods execute off-chain. After performing the desired recursive steps, you can settle the interaction on Mina's blockchain by embedding `ZkProgram` within a `SmartContract` method that verifies the underlying proof of execution and extracts the output that can be used elsewhere in the method (like storing the output in app-state, for example). From 391e2710c5275e9f8e934efb81c978c53ec5a47c Mon Sep 17 00:00:00 2001 From: barriebyron Date: Thu, 15 Feb 2024 09:59:32 -0500 Subject: [PATCH 6/7] clarify language --- docs/zkapps/o1js/smart-contracts.mdx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/zkapps/o1js/smart-contracts.mdx b/docs/zkapps/o1js/smart-contracts.mdx index 1fefbcfe5..2f3323e7f 100644 --- a/docs/zkapps/o1js/smart-contracts.mdx +++ b/docs/zkapps/o1js/smart-contracts.mdx @@ -71,7 +71,7 @@ To understand what successful execution means, look at this line in the example: x.mul(2).assertEquals(5); ``` -Creating a proof for this method is be possible only if the input `x` satisfies the equation `x * 2 === 5`. This is called a "constraint". +Creating a proof for this method is possible only if the input `x` satisfies the equation `x * 2 === 5`. This is called a "constraint". Magically, the proof can be checked without seeing `x` because it's a _private input_. The method has one input parameter, `x` of type `Field`. In general, arguments can be any of the built-in o1js types: `Bool`, `UInt64`, `PrivateKey`, and so on. These types are referred to as [structs`](#custom-data-types). @@ -95,7 +95,7 @@ To log these values for debugging, use a special function for logging from insid Provable.log(x); ``` -The API is like `console.log`, but it automatically handles printing o1js data types in a readable format. However, it does nothing during `SmartContract` compilation. +The API is like `console.log`, but it automatically handles printing o1js data types in a readable format. However, the `Provable.log(x)` function does not have any effect while `SmartContract` is being compiled. ## On-chain state @@ -127,7 +127,7 @@ class HelloWorld extends SmartContract { } ``` -As a zkApp developer, if you add this method to your smart contract, you are saying: "Anyone can call this method, to set `x` on the account to any value they want." +As a zkApp developer, if you add this method to your smart contract, you are saying: "Anyone can call this method to set `x` on the account to any value they want." ## Reading state @@ -219,7 +219,7 @@ x.add(1).assertEquals(xPlus1); ``` If the assertion fails, o1js throws an error and does not submit the transaction. -If it succeeds, it becomes part of the proof that is verified on-chain. +If the assertion succeeds, it becomes part of the proof that is verified on-chain. Because of this, the new version of `increment()` is _guaranteed_ to behave like the previous version: It can only ever update the state `x` to `x + 1`. @@ -265,7 +265,7 @@ However, the secret itself remains private, because it can't be deduced from its ## Initializing state -You initialize on-chain state in the `init()` method. +To initialize on-chain state, use the `init()` method. Like the constructor, `init()` is predefined on the base `SmartContract` class. @@ -336,11 +336,14 @@ Smart contract method arguments can be any of the built-in [o1js types](/zkapps/ However, what if you want to define your own data type? -You can create a custom data type for your smart contract using the `Struct` function that o1js exposes. Create a class that extends `Struct({ })`. -Then, inside the object `{ }`, define the fields that you want to use in your custom data type. +You can create a custom data type for your smart contract using the `Struct` function that o1js exposes: -For example, you can create a custom data type called `Point` to represent a 2D point on a grid. The `Point` struct has no instance methods and is used only to hold information about the `x` and `y` points. -Create a Point class by creating a new class that extends the `Struct` class: +1. Create a class that extends `Struct({ })`. +1. Then, inside the object `{ }`, define the fields that you want to use in your custom data type. + +For example, you can create a custom data type called `Point` to represent a 2D point on a grid. The `Point` struct has no instance methods and is used only to hold information about the `x` and `y` points. + +To create the `Point` class, extend the `Struct` class: ```ts class Point extends Struct({ @@ -349,9 +352,9 @@ class Point extends Struct({ }) {} ``` -Now that you have defined your Struct, you can use it in your smart contract for any o1js built-in types. +Now that `Struct` is defined, you can use it in your smart contract for any o1js built-in types. -For example, the following smart contract uses the `Point` Struct defined above as state and as a method argument: +For example, the following smart contract uses the `Point` struct defined earlier as state and as a method argument: ```ts export class Grid extends SmartContract { @@ -373,6 +376,6 @@ export class Grid extends SmartContract { } ``` -Note that your Structs can contain o1js built-in types like `Field`, `Bool`, `UInt64`, and so on, or even other custom types that you've defined which are based on the `Struct` class. +Note that your `Struct` classes can contain o1js built-in types like `Field`, `Bool`, `UInt64`, and so on, or even other custom types that you've defined that are based on the `Struct` class. This flexibility allows for great composability and reusability of structs. From 1d04ecb6bad98a25ffa62ac9945609a84adef162 Mon Sep 17 00:00:00 2001 From: barriebyron Date: Thu, 15 Feb 2024 10:16:19 -0500 Subject: [PATCH 7/7] zkApp account --- docs/zkapps/o1js/smart-contracts.mdx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/zkapps/o1js/smart-contracts.mdx b/docs/zkapps/o1js/smart-contracts.mdx index 2f3323e7f..bfe94f8ea 100644 --- a/docs/zkapps/o1js/smart-contracts.mdx +++ b/docs/zkapps/o1js/smart-contracts.mdx @@ -46,10 +46,13 @@ let zkApp = new HelloWorld(zkAppAddress); ## zkApp Accounts On Mina, there is no strong distinction between normal "user accounts" and -"zkApp accounts". A zkApp account is just a normal account that has a smart -contract deployed to it – which essentially just means there's a verification -key stored on the account that can verify zero knowledge proofs generated -with the smart contract. +"zkApp accounts". A zkApp account: + +- Is an account on the Mina blockchain where a zkApp smart contract is deployed. + +- Has a verification key associated with it. + +The verification key stored on the zkApp account can verify zero knowledge proofs generated with the smart contract. The verification key lives on-chain for a given zkApp account and is used by the Mina network to verify that a zero knowledge proof has met all constraints defined in the prover. See [Prover Function and Verification Key](/zkapps/how-zkapps-work#prover-function-and-verification-key). ## Methods