5/5
## Initializing Your ZK-Panagram Project with Nargo This lesson guides you through the initial setup of "Panagram," a word-guessing game powered by ZK-SNARKs. Our first step is to create a new Nargo project, which will house our Noir circuit. To begin, we use the `nargo new` command. Nargo package names have a specific requirement: they cannot contain hyphens. While an initial attempt might be `nargo new zk-panagram`, this will result in an error. The correct command to create the project is: ```bash nargo new zk_panagram ``` This command initializes a new Nargo project named `zk_panagram`. Once the project is created, navigate into the project directory: ```bash cd zk_panagram ``` To start working with the project files, open the directory in your preferred code editor. For instance, if you are using VS Code, you can use: ```bash code . ``` You can also chain these commands for convenience: `cd zk_panagram && code .`. The core Noir circuit code, which defines the zero-knowledge logic, will reside in the `src/main.nr` file. ## Designing the Panagram Noir Circuit Logic The central functionality of our Panagram application involves comparing a user's guessed word against the secret answer. The critical aspect here is leveraging zero-knowledge proofs to perform this comparison without revealing the secret answer itself. To achieve this, we will hash both the user's `guess` and the secret `answer`. The Noir circuit will then compare these two hashes. * The hash of the user's guess (`guess_hash`) will be a **private input** to the circuit. This input is derived from the user's private guess and is not revealed publicly. * The hash of the correct secret word (`answer_hash`) will be a **public input** to the circuit. This hash is known by the verifier (e.g., it can be embedded in the smart contract or frontend) but, importantly, does not reveal the actual secret word. **A Note on Security (Brute-Force Attacks):** It's important to acknowledge that using a simple hash of the answer in this manner can be vulnerable to brute-force attacks if the set of possible answers is small. An attacker could pre-compute hashes of common words and compare them against the public `answer_hash` to deduce the secret. While this vulnerability is recognized, addressing it is outside the scope of this particular demonstration, which focuses on the fundamental components of building a ZK application. Let's modify the default `src/main.nr` file to implement our Panagram logic: The default `main.nr` often looks like this: ```noir // fn main(x: Field, y: pub Field) { // assert(x != y); // } ``` We will update it as follows for our Panagram circuit: ```noir fn main(guess_hash: Field, answer_hash: pub Field) { assert(guess_hash == answer_hash); // Asserts that the hash of the guess matches the public hash of the answer } // The #[test] block is commented out for this stage. // #[test] // fn test_main() { // main(guess_hash: 1, answer_hash: 2); // Parameters would be updated to match new function signature // } ``` In this circuit: * `guess_hash: Field`: This defines a private input named `guess_hash` of type `Field`. It represents the hash of the word the user has guessed. * `answer_hash: pub Field`: This defines a public input named `answer_hash` of type `Field`. It represents the hash of the correct secret word, which is known publicly. * `assert(guess_hash == answer_hash);`: This is the core constraint of our circuit. The proof generated by this circuit will only be valid if the private `guess_hash` is identical to the public `answer_hash`. This circuit provides the basic structure for our ZK proof. It will undergo minor modifications later when integrating with smart contracts, but this version serves as our starting point. ## Compiling Your Circuit and Generating the Verifier Smart Contract With the Noir circuit defined, the next steps involve compiling it into a format understandable by proving systems and then generating a smart contract capable of verifying proofs on-chain. We will use two primary tools for this: `nargo` (the Noir language toolkit) and `bb` (the Barretenberg CLI). **Step 1: Compile the Noir Circuit to ACIR (Bytecode)** The first step is to compile our `src/main.nr` circuit into ACIR (Abstract Circuit Intermediate Representation), which is essentially the bytecode for our circuit. Execute the following command in your project's root directory: ```bash nargo compile ``` This command processes `src/main.nr` and its dependencies. Upon successful compilation, it creates a `target/` directory. Inside this directory, you will find `zk_panagram.json`, which is the ACIR file for our circuit. At this stage, commands like `nargo check` (for creating a `Prover.toml` file) or `nargo execute` (for generating a witness) are not necessary for our current goal of generating the verifier contract. **Step 2: Generate the Verification Key (VK)** The Verification Key (VK) is a crucial piece of data required by the verifier (in our case, a smart contract) to check the validity of proofs generated for our circuit. We use the `bb` (Barretenberg CLI) to generate this key. The command to generate the VK is: ```bash bb write_vk --oracle_hash keccak -b ./target/zk_panagram.json -o ./target ``` Let's break down this command: * `write_vk`: This is the `bb` subcommand to generate a verification key. * `--oracle-hash keccak`: This flag specifies that the Keccak hash function should be used for certain internal cryptographic operations within the proving system related to oracles. Using Keccak is often preferred for compatibility and efficiency with Ethereum smart contracts, which have native support for Keccak256. (Note: While there might be verbal mentions of `oracle_hash` with an underscore, the successfully executed command uses `--oracle-hash` with a hyphen.) * `-b ./target/zk_panagram.json`: This specifies the path to the input bytecode file (our ACIR file) generated by `nargo compile`. * `-o ./target`: This specifies the output directory where the verification key file will be saved. The generated file will be named `vk`. **Step 3: Generate the Solidity Verifier Smart Contract** Finally, we generate the Solidity smart contract that will be deployed on-chain to verify proofs submitted by users. This contract uses the Verification Key generated in the previous step. The `bb` command to generate the verifier contract is: ```bash bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol ``` Breaking down this command: * `write_solidity_verifier`: This `bb` subcommand instructs Barretenberg to generate a Solidity verifier contract. * `-k ./target/vk`: This specifies the path to the verification key file (`vk`) that we generated earlier. * `-o ./target/Verifier.sol`: This specifies the output path and filename for the generated Solidity verifier contract. The contract will be saved as `Verifier.sol` in the `target/` directory. The output, `Verifier.sol`, will contain the necessary Solidity code, including the `HonkVerificationKey` library (or similar, depending on the proving system version) and the public `verify` function. This `verify` function will take a proof as input and return true if the proof is valid for our circuit and false otherwise. ## Key Artifacts Generated in Your Project After completing the compilation and generation steps, your `target/` directory will contain several important files: * **`zk_panagram.json`**: This is the ACIR (Abstract Circuit Intermediate Representation) file. It's the compiled bytecode of your Noir circuit from `src/main.nr`. * **`vk`**: This file contains the Verification Key for your circuit. The verifier smart contract uses this key to validate proofs. * **`Verifier.sol`**: This is the Solidity smart contract code. It includes the logic to verify proofs generated for your `zk_panagram` circuit on an Ethereum-compatible blockchain. These artifacts are essential for integrating your zero-knowledge circuit with a blockchain application. ## What's Next: Building the Panagram Application Logic Having successfully set up our Nargo project, designed the basic Noir circuit, and generated the verifier smart contract, we've laid the foundational backend for our ZK-Panagram application. The subsequent phase of development will focus on building the main application smart contract. This contract will: * Manage the core game logic of Panagram. * Potentially handle interactions like minting NFTs to winners or runners-up. * Crucially, it will utilize the `Verifier.sol` contract (generated in this lesson) to verify the ZK proofs submitted by users. This ensures that users can prove they've correctly guessed the Panagram word without revealing their actual guess on-chain until the proof is successfully verified. This lesson has effectively covered the initial, yet critical, steps of defining the private computation in Noir and producing the necessary artifacts (bytecode, VK, and verifier contract) to bridge this computation with a blockchain environment.
This lesson guides you through the initial setup of "Panagram," a word-guessing game powered by ZK-SNARKs. Our first step is to create a new Nargo project, which will house our Noir circuit.
To begin, we use the nargo new
command. Nargo package names have a specific requirement: they cannot contain hyphens. While an initial attempt might be nargo new zk-panagram
, this will result in an error.
The correct command to create the project is:
This command initializes a new Nargo project named zk_panagram
.
Once the project is created, navigate into the project directory:
To start working with the project files, open the directory in your preferred code editor. For instance, if you are using VS Code, you can use:
You can also chain these commands for convenience: cd zk_panagram && code .
.
The core Noir circuit code, which defines the zero-knowledge logic, will reside in the src/main.nr
file.
The central functionality of our Panagram application involves comparing a user's guessed word against the secret answer. The critical aspect here is leveraging zero-knowledge proofs to perform this comparison without revealing the secret answer itself.
To achieve this, we will hash both the user's guess
and the secret answer
. The Noir circuit will then compare these two hashes.
The hash of the user's guess (guess_hash
) will be a private input to the circuit. This input is derived from the user's private guess and is not revealed publicly.
The hash of the correct secret word (answer_hash
) will be a public input to the circuit. This hash is known by the verifier (e.g., it can be embedded in the smart contract or frontend) but, importantly, does not reveal the actual secret word.
A Note on Security (Brute-Force Attacks):
It's important to acknowledge that using a simple hash of the answer in this manner can be vulnerable to brute-force attacks if the set of possible answers is small. An attacker could pre-compute hashes of common words and compare them against the public answer_hash
to deduce the secret. While this vulnerability is recognized, addressing it is outside the scope of this particular demonstration, which focuses on the fundamental components of building a ZK application.
Let's modify the default src/main.nr
file to implement our Panagram logic:
The default main.nr
often looks like this:
We will update it as follows for our Panagram circuit:
In this circuit:
guess_hash: Field
: This defines a private input named guess_hash
of type Field
. It represents the hash of the word the user has guessed.
answer_hash: pub Field
: This defines a public input named answer_hash
of type Field
. It represents the hash of the correct secret word, which is known publicly.
assert(guess_hash == answer_hash);
: This is the core constraint of our circuit. The proof generated by this circuit will only be valid if the private guess_hash
is identical to the public answer_hash
.
This circuit provides the basic structure for our ZK proof. It will undergo minor modifications later when integrating with smart contracts, but this version serves as our starting point.
With the Noir circuit defined, the next steps involve compiling it into a format understandable by proving systems and then generating a smart contract capable of verifying proofs on-chain. We will use two primary tools for this: nargo
(the Noir language toolkit) and bb
(the Barretenberg CLI).
Step 1: Compile the Noir Circuit to ACIR (Bytecode)
The first step is to compile our src/main.nr
circuit into ACIR (Abstract Circuit Intermediate Representation), which is essentially the bytecode for our circuit.
Execute the following command in your project's root directory:
This command processes src/main.nr
and its dependencies. Upon successful compilation, it creates a target/
directory. Inside this directory, you will find zk_panagram.json
, which is the ACIR file for our circuit.
At this stage, commands like nargo check
(for creating a Prover.toml
file) or nargo execute
(for generating a witness) are not necessary for our current goal of generating the verifier contract.
Step 2: Generate the Verification Key (VK)
The Verification Key (VK) is a crucial piece of data required by the verifier (in our case, a smart contract) to check the validity of proofs generated for our circuit. We use the bb
(Barretenberg CLI) to generate this key.
The command to generate the VK is:
Let's break down this command:
write_vk
: This is the bb
subcommand to generate a verification key.
--oracle-hash keccak
: This flag specifies that the Keccak hash function should be used for certain internal cryptographic operations within the proving system related to oracles. Using Keccak is often preferred for compatibility and efficiency with Ethereum smart contracts, which have native support for Keccak256. (Note: While there might be verbal mentions of oracle_hash
with an underscore, the successfully executed command uses --oracle-hash
with a hyphen.)
-b ./target/zk_panagram.json
: This specifies the path to the input bytecode file (our ACIR file) generated by nargo compile
.
-o ./target
: This specifies the output directory where the verification key file will be saved. The generated file will be named vk
.
Step 3: Generate the Solidity Verifier Smart Contract
Finally, we generate the Solidity smart contract that will be deployed on-chain to verify proofs submitted by users. This contract uses the Verification Key generated in the previous step.
The bb
command to generate the verifier contract is:
Breaking down this command:
write_solidity_verifier
: This bb
subcommand instructs Barretenberg to generate a Solidity verifier contract.
-k ./target/vk
: This specifies the path to the verification key file (vk
) that we generated earlier.
-o ./target/Verifier.sol
: This specifies the output path and filename for the generated Solidity verifier contract. The contract will be saved as Verifier.sol
in the target/
directory.
The output, Verifier.sol
, will contain the necessary Solidity code, including the HonkVerificationKey
library (or similar, depending on the proving system version) and the public verify
function. This verify
function will take a proof as input and return true if the proof is valid for our circuit and false otherwise.
After completing the compilation and generation steps, your target/
directory will contain several important files:
zk_panagram.json
: This is the ACIR (Abstract Circuit Intermediate Representation) file. It's the compiled bytecode of your Noir circuit from src/main.nr
.
vk
: This file contains the Verification Key for your circuit. The verifier smart contract uses this key to validate proofs.
Verifier.sol
: This is the Solidity smart contract code. It includes the logic to verify proofs generated for your zk_panagram
circuit on an Ethereum-compatible blockchain.
These artifacts are essential for integrating your zero-knowledge circuit with a blockchain application.
Having successfully set up our Nargo project, designed the basic Noir circuit, and generated the verifier smart contract, we've laid the foundational backend for our ZK-Panagram application.
The subsequent phase of development will focus on building the main application smart contract. This contract will:
Manage the core game logic of Panagram.
Potentially handle interactions like minting NFTs to winners or runners-up.
Crucially, it will utilize the Verifier.sol
contract (generated in this lesson) to verify the ZK proofs submitted by users. This ensures that users can prove they've correctly guessed the Panagram word without revealing their actual guess on-chain until the proof is successfully verified.
This lesson has effectively covered the initial, yet critical, steps of defining the private computation in Noir and producing the necessary artifacts (bytecode, VK, and verifier contract) to bridge this computation with a blockchain environment.
A practical primer on Initializing Your ZK-Panagram Project with Nargo - Kickstart your ZK-Panagram word game by setting up a Nargo project and crafting the core Noir circuit for private guess verification via hash comparison. This lesson also guides you through compiling the circuit and generating the vital verification key and Solidity verifier contract.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Noir syntax
Create a witness, a proof, and Solidity verifier contracts
Use the Poseidon commitment scheme
Create ZK circuits and build a full ZK protocol
ZK Merkle trees and hashing in Noir
Verify signatures without revealing the signer
Build the backend for a full-stack ZK application with noir.js and bb.js
How to create proofs and verify them in a front-end
Last updated on June 12, 2025
Duration: 6min
Duration: 1h 11min
Duration: 2h 12min
Duration: 3h 19min
Course Overview
About the course
Noir syntax
Create a witness, a proof, and Solidity verifier contracts
Use the Poseidon commitment scheme
Create ZK circuits and build a full ZK protocol
ZK Merkle trees and hashing in Noir
Verify signatures without revealing the signer
Build the backend for a full-stack ZK application with noir.js and bb.js
How to create proofs and verify them in a front-end
Last updated on June 12, 2025