Skip to main content
Version: v0.28.0

Project Breakdown

This section breaks down our hello world program from the previous section. We elaborate on the project structure and what the prove and verify commands did.

Anatomy of a Nargo Project

Upon creating a new project with nargo new and building the in/output files with nargo check commands, you would get a minimal Nargo project of the following structure:

- src
- Prover.toml
- Verifier.toml
- Nargo.toml

The source directory src holds the source code for your Noir program. By default only a main.nr file will be generated within it.

Prover.toml

Prover.toml is used for specifying the input values for executing and proving the program. You can specify toml files with different names by using the --prover-name or -p flags, see the Prover section below. Optionally you may specify expected output values for prove-time checking as well.

Verifier.toml

Verifier.toml contains public in/output values computed when executing the Noir program.

Nargo.toml

Nargo.toml contains the environmental options of your project. It contains a "package" section and a "dependencies" section.

Example Nargo.toml:

[package]
name = "noir_starter"
type = "bin"
authors = ["Alice"]
compiler_version = "0.9.0"
description = "Getting started with Noir"
entry = "circuit/main.nr"
license = "MIT"

[dependencies]
ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"}

Nargo.toml for a workspace will look a bit different. For example:

[workspace]
members = ["crates/a", "crates/b"]
default-member = "crates/a"

Package section

The package section defines a number of fields including:

  • name (required) - the name of the package
  • type (required) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract
  • authors (optional) - authors of the project
  • compiler_version - specifies the version of the compiler to use. This is enforced by the compiler and follow's Rust's versioning, so a compiler_version = 0.18.0 will enforce Nargo version 0.18.0, compiler_version = ^0.18.0 will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how Rust handles these operators
  • description (optional)
  • entry (optional) - a relative filepath to use as the entry point into your package (overrides the default of src/lib.nr or src/main.nr)
  • backend (optional)
  • license (optional)

Dependencies section

This is where you will specify any dependencies for your project. See the Dependencies page for more info.

./proofs/ and ./contract/ directories will not be immediately visible until you create a proof or verifier contract respectively.

main.nr

The main.nr file contains a main method, this method is the entry point into your Noir program.

In our sample program, main.nr looks like this:

fn main(x : Field, y : Field) {
assert(x != y);
}

The parameters x and y can be seen as the API for the program and must be supplied by the prover. Since neither x nor y is marked as public, the verifier does not supply any inputs, when verifying the proof.

The prover supplies the values for x and y in the Prover.toml file.

As for the program body, assert ensures that the condition to be satisfied (e.g. x != y) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof).

Prover.toml

The Prover.toml file is a file which the prover uses to supply his witness values(both private and public).

In our hello world program the Prover.toml file looks like this:

x = "1"
y = "2"

When the command nargo prove is executed, two processes happen:

  1. Noir creates a proof that x, which holds the value of 1, and y, which holds the value of 2, is not equal. This inequality constraint is due to the line assert(x != y).

  2. Noir creates and stores the proof of this statement in the proofs directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at ./proofs/private_voting.proof. Opening this file will display the proof in hex format.

Arrays of Structs

The following code shows how to pass an array of structs to a Noir program to generate a proof.

// main.nr
struct Foo {
bar: Field,
baz: Field,
}

fn main(foos: [Foo; 3]) -> pub Field {
foos[2].bar + foos[2].baz
}

Prover.toml:

[[foos]] # foos[0]
bar = 0
baz = 0

[[foos]] # foos[1]
bar = 0
baz = 0

[[foos]] # foos[2]
bar = 1
baz = 2

Custom toml files

You can specify a toml file with a different name to use for proving by using the --prover-name or -p flags.

This command looks for proof inputs in the default Prover.toml and generates the proof and saves it at ./proofs/<project-name>.proof:

nargo prove

This command looks for proof inputs in the custom OtherProver.toml and generates proof and saves it at ./proofs/<project-name>.proof:

nargo prove -p OtherProver

Verifying a Proof

When the command nargo verify is executed, two processes happen:

  1. Noir checks in the proofs directory for a proof file with the project name (eg. test_project.proof)

  2. If that file is found, the proof's validity is checked

Note: The validity of the proof is linked to the current Noir program; if the program is changed and the verifier verifies the proof, it will fail because the proof is not valid for the modified Noir program.

In production, the prover and the verifier are usually two separate entities. A prover would retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the verifier. The verifier would then retrieve the public inputs, usually from external sources, and verify the validity of the proof against it.

Take a private asset transfer as an example:

A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof and submit it to the verifier smart contract.

The verifier contract would then draw the user's encrypted balance directly from the blockchain and verify the proof submitted against it. If the verification passes, additional functions in the verifier contract could trigger (e.g. approve the asset transfer).

Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code.