# Plonky2

## For users of Plonky2.

This tutorial corresponds to the `v0.2.2`

version of Plonky2.

## Introductionβ

We will walk through the process of how to create a circuit using the Plonky2 library that proves a correct Merkle tree inclusion proof. The complete and finished code for the circuit can be found here along with other example projects in our sindri-resources repository. You can also check out the Plonky2 overview from Polymer Labs for a general overview of how to use Plonky2. An overview of the role that Merkle trees and Merkle inclusion proofs serve in the blockchain space can be found here.

## Merkle Tree Circuitβ

The Merkle tree circuit implementation is based on the sample Plonky2 circuits provided by Hashcloak. Using this implementation requires the following steps:

- Importing the leaves for the leaf node layer of the Merkle tree and selecting the index of the leaf for which we want to prove inclusion. Along with the leaf nodes, this leaf index and corresponding Merkle path will be included as public inputs to the circuit.
- Constructing a full Merkle tree from the leaf nodes.
We use the
`merkle_tree.rs`

file to generate the hashes for every node in the Merkle tree and also to generate a Merkle path for the leaf whose inclusion is being proved. This file is based on the on Hashcloak's implementation. - Importing the node hashes and the the Merkle path indices as public inputs to the circuit. The Merkle root hash is the last public input to the circuit and is comprised of four 64-bit words.
- Proving the inclusion of the leaf in the Merkle tree using the Plonky2 Merkle verification circuit.

For this tutorial, we will use a 1024 leaf Merkle tree and prove membership for the first leaf. Note that the input values are all modulo the Goldilocks 64-bit prime, which defines the prime field for Plonky2.

## Circuit Configurationβ

The circuit is organized in a directory containing the following files:

`βββ πcircuit`

β βββ πCargo.toml

β βββ πrust-toolchain

β βββ πsindri.json

β βββ πsrc

β βββ πlib.rs

β βββ πmerkle_tree.rs

The `src/lib.rs`

contains a prove method for the circuit function.
The Plonky2 circuit construction needs to be defined inside of this prove method.
The prove method is responsible for constructing the arithmetic circuit, loading any input data, configuring the partial witness, and proving the circuit.
Helper methods can be defined in the `lib.rs`

or in a separate module and imported into the `lib.rs`

file.
For instance, the merkle_tree.rs module contains methods to generate the root hash of a merkle tree along with along with a merkle path and required hashes to verify a Merkle proof.
The helper function verify_merkle_proof_circuit creates the arithmetic circuit for verifying a Merkle tree proof using the Plonky2 CircuitBuilder struct.

For detailed descriptions of the other files, you can click through the different tabs here.

- sindri.json
- Cargo.toml
- Circuit Definition

For any circuit using a Sindri supported framework, you will need to include a `sindri.json`

file defining certain metadata about your circuit.
There are a few key pieces of information here that are required:

- The
`packageName`

and`plonky2Version`

must be included and match the values in your`Cargo.toml`

file. We only support Plonky2 versions`v0.2`

,`v0.2.1`

, and`v0.2.2`

. - The
`provingScheme`

and`circuitType`

must both be set to`plonky2`

. - The
`structName`

must be the path to the circuit struct in your`lib.rs`

file which implements the`prove`

method. To meet the requirements of the interface, the`structName`

in your sindri.json must match the name of your circuit struct.

The package name in your `Cargo.toml`

file should match the name in the `sindri.json`

file.

`[package]`

name = "merkle_tree"

version = "0.1.0"

edition = "2021"

[dependencies]

itertools = "0.13.0"

num = "0.4.3"

plonky2 = "0.2.2"

serde = { version = "1.0.204", features = ["derive"] }

serde_json = "1.0.122"

Your core circuit logic must be defined in the prove method of your circuit struct. This prove method must include the following components:

- Code to import input data and format it to Goldilocks Field field elements.

- Creating a circuit scaffold using the Plonky2 CircuitBuilder struct.

You can optionally define a helper method that builds the circuit.

- Instantiating the Plonky2 partial witness and populating it with public inputs.

- Invoking the prove() method on the CircuitBuilder struct. The prove method takes in the partial witness as its sole input.

- Return an instance of your circuit struct.

## Circuit explanationβ

## Sindri APIβ

We'll walk through the steps to upload your Plonky2 circuit to Sindri, compile it, generate proofs, and verify proofs. We will use a Rust script to interact with the Sindri API.

### Setupβ

Clone the Sindri resources repository and move into the `merkle_tree`

tutorial.

`git clone https://github.com/Sindri-Labs/sindri-resources.git`

cd sindri-resources/circuit_tutorials/plonky2/merkle_tree

Make sure that the `merkle_tree`

project directory contains the following files:

`π¦ merkle_tree`

βββ πCargo.toml

βββ πinput_1024.json

βββ πcircuit

β βββ πCargo.toml

β βββ πrust-toolchain

β βββ πsindri.json

β βββ πsrc

β βββ πlib.rs

β βββ πmerkle_tree.rs

βββ πREADME.md

βββ πrust-toolchain

βββ πsample.env

βββ πsrc

βββ πmain.rs

The circuit described above is contained in the `merkle_tree_circuit`

directory.

You'll also need your Sindri API key to authenticate all requests to the API, so make sure you have this ready (see #api-authentication).
Modify the `sample.env`

file to include your API key and rename it to `.env`

.

### Upload & Compileβ

In your main function, we have three methods:

- 'compile_circuit' to upload the circuit to Sindri and compile it.
- 'prove_circuit' which generate a proof for the circuit.
- 'verify_proof' to verify the proof.

## Compileβ

Running compile will upload the merkle_tree_circuit files to Sindri.
If the compile checks pass, a `json`

file will be outputted to `data/compile_out.json`

.

`{`

"circuit_id": "bacaa9ca-f1c2-4c28-84c8-117f2548d60e",

"circuit_name": "merkle_tree_circuit",

"circuit_type": "plonky2",

"compute_time": "P0DT00H00M59.673416S",

"compute_time_sec": 59.673416,

"compute_times": {

"clean_up": 4.49532,

"compile": 22.33991,

"precompilation_checks": 37.0868,

"save_results": 0.24671,

"total": 64.16873

},

"date_created": "2024-08-13T19:00:04.989Z",

"error": null,

"file_size": 2992277,

"has_smart_contract_verifier": false,

"has_verification_key": false,

"meta": {},

"num_proofs": 0,

"plonky2_version": "0.2.2",

"project_name": "merkle_tree_circuit",

"proving_scheme": "plonky2",

"public": false,

"queue_time": "P0DT00H00M01.002722S",

"queue_time_sec": 1.002722,

"status": "Ready",

"struct_name": "merkle_tree::MerkleTreeCircuit",

"tags": ["latest"],

"team": "****",

"team_avatar_url": "****",

"team_slug": "****",

"uploaded_file_name": "filename.filetype",

"verification_key": null,

"warnings": null

}

## Proveβ

The prove_circuit method requires a path to a valid input file.
The `input_1024.json`

file contains 1024 leaf values and the index of the desired leaf node for which to prove inclusion.

`{"inputs":[17385365412,13508786373,...,14749629153,3531383147], "index": 0}`

The prove_circuit method will pull the circuit ID from the `compile_out.json`

file.
The proof data is returned to the user in the `data/proof_out.json`

file.
The `proofs`

field of this JSON file contains everything that we need to verify the proof locally.

For Plonky2 proofs, we need three Rust structs to verify the proof:

- The
`ProofWithPublicInputs`

struct. - The
`CommonCircuitData`

struct. - The
`VerifierOnlyCircuitData`

struct.

These three structs are encoded as base64 strings.
The base64 strings are quite large for Plonky2 circuits, so they've been replaced with "****" in the sample `proof_out.json`

fiel shown below.

` "circuit_id": "66d36825-3e72-4444-84d4-430ff402ffc1",`

"circuit_name": "merkle_tree_circuit",

"circuit_team": "roy-sindri",

"circuit_team_avatar_url": "https://gravatar.com/avatar/758874998f5bd0c393da094e1967a72b?s=400&d=identicon&r=x",

"circuit_team_slug": "roy-sindri",

"circuit_type": "plonky2",

"compute_time": "P0DT00H00M01.203837S",

"compute_time_sec": 1.203837,

"compute_times": {

"clean_up": 0.18932,

"file_setup": 0.46645,

"prove": 0.04134,

"save_results": 0.50672,

"total": 1.20384

},

"date_created": "2024-08-30T08:50:40.932Z",

"error": null,

"file_size": 100671,

"has_smart_contract_calldata": false,

"has_verification_key": false,

"meta": {},

"perform_verify": false,

"project_name": "merkle_tree_circuit",

"proof": {

"common": "****"

"proof": "****"

"verifier_data": "****"

},

"proof_id": "8695040a-b1d8-4e74-95c1-1cb8441e8150",

"public": null,

"queue_time": "P0DT00H00M01.486846S",

"queue_time_sec": 1.486846,

"smart_contract_calldata": null,

"status": "Ready",

"team": "roy-sindri",

"team_avatar_url": "https://gravatar.com/avatar/758874998f5bd0c393da094e1967a72b?s=400&d=identicon&r=x",

"team_slug": "roy-sindri",

"verification_key": null,

"warnings": null

}

### Verifyβ

The verify_proof method comprises several steps:

- Extracts the
`proof`

field from the`proof_out.json`

file. - Deserializes the base64 strings to the serialized Plonky2 types byte arrays.
- Defines a Gate Serializer for the Plonky2 circuit used for generating the proof.
This is necessary to deserialize the
`CommonCircuitData`

struct. - Deserialzies the byte arrays to their respective Plonky2 types.
will take the proof data from the
`proof_out.json`

file, deserialize the base64 strings, to their Plonky2 typeserify the proof locally. - Verifies the proof using the
`verify`

method from the Plonky2 API.

Running the `verify_proof`

method will show that the proof was verified successfully.

In order to run the code for the Merkle tree circuit, run the following command from the root of the `merkle_tree`

directory:

`cargo run --release`