Smart Contract Auditor & Web3 Security Researcher. ZK enthusiast | Solidity • Foundry • Noir Auditing to secure the decentralized future.
Hey there, devs! If you've got some Rust under your belt but zero-knowledge (ZK) proofs feel like black magic, you're in the right place. I'm going to walk you through Noir step by step, like we're pair-programming a ZK circuit. We'll start simple and ramp up, focusing on the essentials without any unnecessary hype. By the end, you'll have a solid foundation to build your own proofs. Let's dive in.
Noir is a domain-specific language (DSL) designed for writing zero-knowledge circuits. It lets you describe computations that can be proven without revealing private data, all in a Rust-like syntax that's familiar and safe. Developed by Aztec Labs, Noir compiles your code into an intermediate representation called ACIR (Abstract Circuit Intermediate Representation), which can then be used with various proving backends to generate and verify proofs.
Why does Noir exist? ZK proofs are powerful for privacy-preserving apps like proving you're over 18 without showing your ID but writing them from scratch is tedious and error-prone. Noir abstracts away the low-level cryptography, letting you focus on logic while ensuring your circuits are sound and efficient. It fits into the zkSNARK ecosystem by targeting proving systems like PLONK variants, enabling succinct proofs that are fast to verify (e.g., on-chain).
Key differences:
Noir: The language itself, where you write circuits using Rust-inspired syntax.
Barretenberg: The default proving backend, a high-performance C++ library that handles proof generation and verification using the UltraHONK scheme (more on that later).
nargo: The CLI tool for managing Noir projects think of it as Cargo for ZK circuits. It handles compilation, execution, and testing.
How does Noir compare to other ZK languages?

Noir shines if you're coming from app development and want to dip into ZK without a PhD in math. It's backend-agnostic in theory (ACIR can target others), but defaults to Barretenberg for UltraHONK efficiency.
Let's get your environment ready. We'll install nargo (the Noir CLI) and bb (Barretenberg's CLI for proving/verifying).
Rust (stable, via rustup).
A Unix-like OS (macOS, Linux; Windows via WSL).
Git for cloning examples later.
Install nargo using the noirup script:
bash
curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash
This fetches and installs the latest stable version. Restart your terminal after.
Test the installation:
bash
nargo --version
You should see something like nargo 0.XX.0 (version as of 2025).
Install bb (Barretenberg CLI) using bbup:
bash
curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash
This sets up the proving backend. Test with:
bash
bb --version
Expect output like bb version X.Y.Z.
If you hit issues (e.g., permissions), check the official docs for manual builds. Now you're set let's code.
We'll build a simple circuit: prove you're at least 18 without revealing your exact age. Age is a private input; the minimum age (18) is public for verification.
First, create a new project:
bash
nargo new age_verifier
cd age_verifier
src/main.nr: Your circuit code.
Nargo.toml: Project config (name, dependencies leave it default for now).
After commands: Prover.toml (inputs), target/ (artifacts like compiled circuit and witness).
Edit src/main.nr:
fn main(age: Field, min_age: pub Field) {
assert(age >= min_age);
}
Explanation:
fn main: Entry point for the circuit. Inputs are fields (modulo a large prime for arithmetic).
age: Field: Private input (default; hidden in proofs).
min_age: pub Field: Public input (visible to verifiers).
assert(age >= min_age): Enforces the constraint. If false during execution, it fails. In ZK, this translates to arithmetic constraints proving the inequality holds without revealing age.
Constraints are the heart of ZK: they define what must be true for the proof to be valid. Public inputs allow verifiers to check against known values (e.g., min_age=18).
Noir handles under-the-hood details like field arithmetic safety.
Compile to check syntax and generate ACIR:
bash
nargo compile
Output: ./target/age_verifier.json the compiled ACIR circuit (JSON representation of constraints).
This also creates Prover.toml if missing. Edit it for inputs:
age = "25" # Private
min_age = "18" # Public
Execute to compute the witness (proof inputs):
bash
nargo execute
Output: ./target/age_verifier.gzcompressed witness file (execution trace).
Explanation: Execution runs the circuit locally to generate the witness, which includes private values and intermediate computations. The ACIR defines the circuit structure; witness proves a valid execution.
Use bb to prove:
bb prove -b ./target/age_verifier.json -w ./target/age_verifier.gz -o ./target
Output: ./target/proof (the proof) and ./target/public_inputs (public values like min_age).
Explanation: This generates a zkSNARK proof attesting the circuit executed correctly. "Scheme is: ultra_honk" means Barretenberg uses UltraHONK a sumcheck-based variant of HONK (itself a PLONK evolution). It's efficient for large circuits, supports custom gates/lookups, and avoids FFTs for better performance in recursive proofs. No trusted setup needed, unlike some PLONK modes.
Generate the verification key (VK):
bash
bb write_vk -b ./target/age_verifier.json -o ./target
Output: ./target/vk
Explanation: The VK is derived from the circuit (ACIR) and commits to its structure. It's public and used to verify proofs without knowing privates. Security: VKs are circuit-specific using the wrong one could accept invalid proofs. Always regenerate for circuit changes. Store VKs securely; tampering could compromise verification (though UltraHONK's design mitigates some risks).
Verify:
bb verify -k ./target/vk -p ./target/proof -i ./target/public_inputs
Success output: Something like "Proof verified successfully."

This checks the proof against the VK and public inputs. Fast and constant-time ideal for on-chain use.
nargo new <project> && cd <project>
Edit src/main.nr and Prover.toml

Proof Pipeline (Text Diagram):
Code (main.nr) --> Compile (nargo compile) --> ACIR (.json) Inputs (Prover.toml) --> Execute (nargo execute) --> Witness (.gz) ACIR + Witness --> Prove (bb prove) --> Proof + Public Inputs ACIR --> Write VK (bb write_vk) --> VK Proof + VK + Public Inputs --> Verify (bb verify) --> True/False
ZK is powerful, but mistakes can leak data or allow forgery. Key tips:
Don't leak privates: Mark inputs as pub only if they should be revealed (e.g., min_age). Accidentally making age public defeats ZK.
Sound constraints: Ensure asserts cover all cases underconstrained circuits might pass invalid inputs (e.g., add range checks for age < 150).
VK binding: VKs tie to specific circuits. Reuse or mismatch could validate wrong proofs. Always regenerate and verify VK hashes.
Side channels: Avoid variable-time ops in circuits (Noir handles most).
Audits: For production, audit circuits tools like nargo test help, but formal verification is ideal.
General: Assume adversaries know the circuit; security relies on proper constraints and honest provers.
Official Docs: https://noir-lang.org/docs (start with tutorials).
GitHub Repo: https://github.com/noir-lang/noir (issues, examples).
Awesome Noir: https://github.com/noir-lang/awesome-noir (curated projects, libs).
Aztec Grants: https://aztec.network/grants for funding ideas.
Community: Join Aztec's forum or Discord for help.
Experiment, break things (safely), and ping me if stuck. You're on your way to ZK mastery keep coding!
Hey there, devs! If you've got some Rust under your belt but zero-knowledge (ZK) proofs feel like black magic, you're in the right place. I'm going to walk you through Noir step by step, like we're pair-programming a ZK circuit. We'll start simple and ramp up, focusing on the essentials without any unnecessary hype. By the end, you'll have a solid foundation to build your own proofs. Let's dive in.
Noir is a domain-specific language (DSL) designed for writing zero-knowledge circuits. It lets you describe computations that can be proven without revealing private data, all in a Rust-like syntax that's familiar and safe. Developed by Aztec Labs, Noir compiles your code into an intermediate representation called ACIR (Abstract Circuit Intermediate Representation), which can then be used with various proving backends to generate and verify proofs.
Why does Noir exist? ZK proofs are powerful for privacy-preserving apps like proving you're over 18 without showing your ID but writing them from scratch is tedious and error-prone. Noir abstracts away the low-level cryptography, letting you focus on logic while ensuring your circuits are sound and efficient. It fits into the zkSNARK ecosystem by targeting proving systems like PLONK variants, enabling succinct proofs that are fast to verify (e.g., on-chain).
Key differences:
Noir: The language itself, where you write circuits using Rust-inspired syntax.
Barretenberg: The default proving backend, a high-performance C++ library that handles proof generation and verification using the UltraHONK scheme (more on that later).
nargo: The CLI tool for managing Noir projects think of it as Cargo for ZK circuits. It handles compilation, execution, and testing.
How does Noir compare to other ZK languages?

Noir shines if you're coming from app development and want to dip into ZK without a PhD in math. It's backend-agnostic in theory (ACIR can target others), but defaults to Barretenberg for UltraHONK efficiency.
Let's get your environment ready. We'll install nargo (the Noir CLI) and bb (Barretenberg's CLI for proving/verifying).
Rust (stable, via rustup).
A Unix-like OS (macOS, Linux; Windows via WSL).
Git for cloning examples later.
Install nargo using the noirup script:
bash
curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash
This fetches and installs the latest stable version. Restart your terminal after.
Test the installation:
bash
nargo --version
You should see something like nargo 0.XX.0 (version as of 2025).
Install bb (Barretenberg CLI) using bbup:
bash
curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash
This sets up the proving backend. Test with:
bash
bb --version
Expect output like bb version X.Y.Z.
If you hit issues (e.g., permissions), check the official docs for manual builds. Now you're set let's code.
We'll build a simple circuit: prove you're at least 18 without revealing your exact age. Age is a private input; the minimum age (18) is public for verification.
First, create a new project:
bash
nargo new age_verifier
cd age_verifier
src/main.nr: Your circuit code.
Nargo.toml: Project config (name, dependencies leave it default for now).
After commands: Prover.toml (inputs), target/ (artifacts like compiled circuit and witness).
Edit src/main.nr:
fn main(age: Field, min_age: pub Field) {
assert(age >= min_age);
}
Explanation:
fn main: Entry point for the circuit. Inputs are fields (modulo a large prime for arithmetic).
age: Field: Private input (default; hidden in proofs).
min_age: pub Field: Public input (visible to verifiers).
assert(age >= min_age): Enforces the constraint. If false during execution, it fails. In ZK, this translates to arithmetic constraints proving the inequality holds without revealing age.
Constraints are the heart of ZK: they define what must be true for the proof to be valid. Public inputs allow verifiers to check against known values (e.g., min_age=18).
Noir handles under-the-hood details like field arithmetic safety.
Compile to check syntax and generate ACIR:
bash
nargo compile
Output: ./target/age_verifier.json the compiled ACIR circuit (JSON representation of constraints).
This also creates Prover.toml if missing. Edit it for inputs:
age = "25" # Private
min_age = "18" # Public
Execute to compute the witness (proof inputs):
bash
nargo execute
Output: ./target/age_verifier.gzcompressed witness file (execution trace).
Explanation: Execution runs the circuit locally to generate the witness, which includes private values and intermediate computations. The ACIR defines the circuit structure; witness proves a valid execution.
Use bb to prove:
bb prove -b ./target/age_verifier.json -w ./target/age_verifier.gz -o ./target
Output: ./target/proof (the proof) and ./target/public_inputs (public values like min_age).
Explanation: This generates a zkSNARK proof attesting the circuit executed correctly. "Scheme is: ultra_honk" means Barretenberg uses UltraHONK a sumcheck-based variant of HONK (itself a PLONK evolution). It's efficient for large circuits, supports custom gates/lookups, and avoids FFTs for better performance in recursive proofs. No trusted setup needed, unlike some PLONK modes.
Generate the verification key (VK):
bash
bb write_vk -b ./target/age_verifier.json -o ./target
Output: ./target/vk
Explanation: The VK is derived from the circuit (ACIR) and commits to its structure. It's public and used to verify proofs without knowing privates. Security: VKs are circuit-specific using the wrong one could accept invalid proofs. Always regenerate for circuit changes. Store VKs securely; tampering could compromise verification (though UltraHONK's design mitigates some risks).
Verify:
bb verify -k ./target/vk -p ./target/proof -i ./target/public_inputs
Success output: Something like "Proof verified successfully."

This checks the proof against the VK and public inputs. Fast and constant-time ideal for on-chain use.
nargo new <project> && cd <project>
Edit src/main.nr and Prover.toml

Proof Pipeline (Text Diagram):
Code (main.nr) --> Compile (nargo compile) --> ACIR (.json) Inputs (Prover.toml) --> Execute (nargo execute) --> Witness (.gz) ACIR + Witness --> Prove (bb prove) --> Proof + Public Inputs ACIR --> Write VK (bb write_vk) --> VK Proof + VK + Public Inputs --> Verify (bb verify) --> True/False
ZK is powerful, but mistakes can leak data or allow forgery. Key tips:
Don't leak privates: Mark inputs as pub only if they should be revealed (e.g., min_age). Accidentally making age public defeats ZK.
Sound constraints: Ensure asserts cover all cases underconstrained circuits might pass invalid inputs (e.g., add range checks for age < 150).
VK binding: VKs tie to specific circuits. Reuse or mismatch could validate wrong proofs. Always regenerate and verify VK hashes.
Side channels: Avoid variable-time ops in circuits (Noir handles most).
Audits: For production, audit circuits tools like nargo test help, but formal verification is ideal.
General: Assume adversaries know the circuit; security relies on proper constraints and honest provers.
Official Docs: https://noir-lang.org/docs (start with tutorials).
GitHub Repo: https://github.com/noir-lang/noir (issues, examples).
Awesome Noir: https://github.com/noir-lang/awesome-noir (curated projects, libs).
Aztec Grants: https://aztec.network/grants for funding ideas.
Community: Join Aztec's forum or Discord for help.
Experiment, break things (safely), and ping me if stuck. You're on your way to ZK mastery keep coding!
Smart Contract Auditor & Web3 Security Researcher. ZK enthusiast | Solidity • Foundry • Noir Auditing to secure the decentralized future.
Share Dialog
Share Dialog

Subscribe to 0xArektQ

Subscribe to 0xArektQ
<100 subscribers
<100 subscribers
No activity yet