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 packagetype
(required) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contractauthors
(optional) - authors of the projectcompiler_version
- specifies the version of the compiler to use. This is enforced by the compiler and follow's Rust's versioning, so acompiler_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 operatorsdescription
(optional)entry
(optional) - a relative filepath to use as the entry point into your package (overrides the default ofsrc/lib.nr
orsrc/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:
-
Noir creates a proof that
x
, which holds the value of1
, andy
, which holds the value of2
, is not equal. This inequality constraint is due to the lineassert(x != y)
. -
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:
-
Noir checks in the proofs directory for a proof file with the project name (eg. test_project.proof)
-
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.