5/5
## Understanding Circuit Inputs in Noir Every Noir program, or circuit, has a primary entry point defined by the `main` function. Let's consider a typical `main.nr` file from a simple Noir project: ```noir fn main(x: Field, y: pub Field) { assert(x != y); } ``` The parameters passed to this `main` function, `x` and `y` in this instance, represent the inputs to our circuit. Noir distinguishes between two fundamental types of inputs: private and public. * **Private Inputs:** By default, inputs in Noir are private. This means their values are known exclusively to the **prover** – the entity responsible for generating the cryptographic proof. In the example above, `x: Field` is a private input because it lacks any modifying keywords. The prover will use the value of `x`, but it will not be revealed to the verifier or embedded directly in the proof in an unencrypted manner. * **Public Inputs:** An input becomes a **public input** when it is preceded by the `pub` keyword. Public inputs are known to both the **prover** and the **verifier**. In our example, `y: pub Field` is a public input. This distinction is crucial for the zero-knowledge proof lifecycle: * When **creating a proof**, the prover supplies values for all inputs, both private and public. * When **verifying a proof**, especially in on-chain scenarios, the verifier must be provided with the public inputs alongside the proof itself. The verification algorithm then checks that these supplied public inputs correspond to the ones used by the prover during proof generation. ## The `Field` Type: Noir's Native Numeric Data Type The `Field` type is the most prevalent variable type you'll encounter for numeric operations within Noir. Conceptually, a `Field` represents an integer, but with a critical characteristic: it has a **maximum value**. This maximum is determined by what's known as the **field modulus** or **field size**. Think of it as a set of integers within a specific range, operating under modular arithmetic. This is analogous to how `uint256` in Solidity has a maximum value of `2^256 - 1`. The official Noir documentation (specifically `noir-lang.org/docs/noir/concepts/data_types/fields`) elaborates: >"The field type corresponds to the native field type of the proving backend." And further: >"The size of a Noir field depends on the elliptic curve's finite field for the proving backend adopted." Essentially, `Field` is optimized for the underlying cryptographic machinery. It supports standard integer arithmetic operations and is often the default numeric type if no other specific integer type is declared. ## Choosing Between `Field` and `Integer` Types in Noir While `Field` is the workhorse, Noir also supports more conventional `Integer` types such as `u8`, `u16`, `u32`, and `u64` (with a maximum bit size typically up to 64 bits for standard integer types). Understanding when to use `Field` versus an `Integer` type is key for both correctness and efficiency. * **Efficiency:** `Field` types are generally more efficient for proving computations. This is because they directly map to the native operations of the proving system. * **`Integer` Types and Range Constraints:** The Noir documentation (under "Integers" - `noir-lang.org/docs/noir/concepts/data_types/integers`) describes an integer type as a "range constrained field type." This means that when you use a type like `u64`, the Noir compiler and proving system must introduce **extra range constraints**. These constraints ensure that the value indeed fits within the specified bit-width (e.g., 0 to `2^64 - 1` for `u64`). These additional constraints add computational overhead, making `Integer` types less efficient than `Field` for general arithmetic where specific bit-widths aren't strictly necessary. * **Default Behavior:** If you define an integer literal in Noir without explicitly assigning a type, it will default to `Field`. For instance: ```noir // From Noir Documentation (Integers section) // INFO // When an integer is defined in Noir without a specific type, it will default to Field. let my_number = 5; // my_number is of type Field ``` * **The Crucial Role of `Integer` Types: Comparisons:** A primary reason to opt for `Integer` types over `Field` is when you need to perform **ordered comparisons**, such as "less than" (`<`) or "greater than" (`>`). `Field` types, due to their nature of operating within a finite field (and thus wrapping around due to modular arithmetic), do **not** have an inherent, straightforward ordering suitable for such comparisons. You cannot directly or reliably assert that one `Field` element is arithmetically "greater" or "less" than another for range-checking purposes. If your logic requires checking if `a < b`, you would typically declare `a` and `b` as `Integer` types (e.g., `u64`). * **Revisiting Our Example:** In our initial `main` function, `assert(x != y);`, we are only checking for inequality. Since no ordered comparison is involved, using `Field` types for `x` and `y` is appropriate and efficient. ## Best Practices and Resources for Noir Data Types The underlying mathematics of finite fields can be intricate. While a deep understanding isn't always required for basic Noir development, knowing where to find information and who to ask is vital. * **Consult the Documentation:** The official Noir documentation (`noir-lang.org/docs/noir/`) should always be your first point of reference when unsure about which data type to use or how specific types behave. * **Leverage AI Assistants:** Tools like ChatGPT or Claude are becoming increasingly adept at understanding newer programming languages like Noir. They can be helpful for grasping the mathematical concepts behind fields or for getting explanations on when and why to use different data types. * **Engage with the Community:** For project-specific questions, complex scenarios, or when documentation and AI fall short, the Noir community is an excellent resource. Consider asking in the official Noir Discord server or the Aztec Discord, where team members and experienced community developers can provide guidance. ## `Field` vs. `Integer`: A Quick Comparison To summarize the key distinctions between `Field` and `Integer` types in Noir: | Feature | `Field` | `Integer` (e.g., `u64`) | | :---------------- | :---------------------------------------------------------------------- | :-------------------------------------------------------------- | | **Nature** | Native numeric type of the proving backend; represents an element in a finite field | A `Field` type with additional range constraints | | **Efficiency** | Generally more efficient for proving, especially for arithmetic operations | Less efficient due to the overhead of extra range constraints | | **Ordering** | No inherent arithmetic ordering (cannot reliably use `<` or `>`) | Has defined ordering (suitable for `<` or `>` comparisons) | | **Default** | Yes, if an integer literal's type is unspecified | No | | **Primary Use** | General arithmetic, equality/inequality checks, cryptographic operations | Operations requiring specific bit-widths, ordered comparisons | By understanding these differences, you can write Noir circuits that are not only correct but also optimized for efficient proof generation. Prioritize `Field` for most numerical tasks, but switch to `Integer` types when bit-width constraints or ordered comparisons are essential.
A strategic guide to Understanding Circuit Inputs in Noir - Learn to define circuit interactions in Noir by distinguishing private inputs, known only by the prover, from public inputs shared with the verifier. Master the choice between Noir's efficient `Field` type for general computation and `Integer` types for essential range-specific operations and comparisons.
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