Noir Language Documentation

repository·master·Indexed 23 days ago

https://github.com/noir-lang/noir

Noir is a Domain Specific Language (DSL) for SNARK proving systems, designed to be compatible with any Abstract Circuit Intermediate Representation (ACIR) compatible proving system. The ecosystem includes ACIR for constrained bytecode, the Abstract Circuit Virtual Machine (ACVM) for partial witness generation, and the Brillig VM for unconstrained bytecode execution. It supports a hybrid runtime where normal functions are compiled to ACIR and unconstrained functions are compiled to Brillig bytecode to optimize circuit efficiency and handle complex logic.

Tokens
154.5K
Snippets
356
Records
926
Agent score
79%

What's inside Noir

  1. Use cryptographic primitives in Noir

    master

    Noir provides a standard library of cryptographic primitives that can be used directly in your circuits. These primitives are being progressively added to the language.

    Important Note on Backends: Some cryptographic methods are implemented via the Aztec backend rather than being performed natively using Noir code. If you switch to a different backend, these specific methods may not be available. Always verify backend compatibility when relying on specific cryptographic functions.

  2. Understand the Noir integration test directory structure

    master

    The Noir compiler integration tests are organized into specific directories based on the expected outcome of the compilation and execution lifecycle. This structure helps distinguish between syntax/logic errors, valid but empty circuits, and fully functional programs.

    Test Categories

    • compile_failure: Programs containing invalid or unsatisfiable Noir code that the compiler is expected to reject.
    • compile_success_empty: Valid, satisfiable Noir code that is expected to compile down to an empty circuit (no opcodes).
    • compile_success_contract: Valid Noir code that represents a contract.
    • execution_success: Valid, satisfiable Noir code that contains opcodes and is expected to execute successfully.
    • execution_stack_overflow: A temporary directory for tests that trigger stack overflows. These tests are not currently run in local or CI environments and must have an associated open issue. Once the stack overflow is resolved, they should be moved to the appropriate category above.
  3. Understand the ACVM project structure

    master

    The ACVM (Abstract Circuit Virtual Machine) repository is organized into several specialized components that handle circuit representation, execution, and serialization:

    • acir/: Defines and implements the Abstract Circuit Intermediate Representation (ACIR).
    • acvm/: Implements the ACVM, which is responsible for executing ACIR.
    • brillig/: Defines and implements unconstrained Brillig opcodes.
    • brillig_vm/: Implements the Brillig VM, which is responsible for executing Brillig.
    • msgpack_tagged/: Provides a tagged-map serialization format designed for evolvable bytecode (used in conjunction with the msgpack_tagged_derive proc-macro).
  4. Use acvm_js to execute ACIR programs

    master
    The acvm_js package allows you to execute an Abstract Circuit Intermediate Representation (ACIR) program. This process involves generating an initial witness from a provided set of inputs and calculating a partial witness. The resulting partial witness can subsequently be used with an ACVM backend to create a proof of execution.
  5. What is Brillig and when to use it

    master

    Brillig is a general virtual machine (VM) architecture designed for use with NP-complete circuit languages (like Noir). While ACIR is used for constrained (circuit) bytecode, Brillig is used for unconstrained bytecode.

    Use Cases

    1. Unconstrained Execution: Allows developers to generate witnesses from code that does not generate constraints. This is critical for:

      • Discretionary Choice Functions: Performing logic like note selection in ZK protocols where the selection algorithm itself doesn't need a proof, only the resulting outputs need to be constrained.
      • Improving Circuit Efficiency: Performing expensive operations (like bitwise decomposition of a finite field into a byte array) in an unconstrained environment and then injecting the results back into the circuit via arithmetic constraints.
    2. Attributing Incorrectness: In distributed environments like blockchains, Brillig helps distinguish between:

      • Runtime Errors: A user providing input that causes a function to revert (e.g., a failed assert).
      • Invalid Proofs: A prover deliberately assigning incorrect witness values to a legitimate transaction.

    By using a VM that operates as a zero-knowledge circuit, the system can use a public failure flag to signal whether a VM execution reverted, allowing honest users to always produce a valid proof even in error states.

  6. What is Abstract Circuit Intermediate Representation (ACIR)?

    master

    ACIR (Abstract Circuit Intermediate Representation) serves as the bridge between high-level frontends (like the Noir programming language) and low-level proving systems (like Aztec's Barretenberg).

    It provides a generic, open-source intermediate representation that is agnostic to specific proving systems. ACIR bytecode acts as the link between the output of a compiler (e.g., Noir) and the input required by a proving system backend.

    Key components include:

    • Abstract Circuit: A computation model where gates (opcodes) are connected by wires (witnesses). It is represented as a Directed Acyclic Graph (DAG).
    • Intermediate Representation: The bytecode that translates user-specific computations into a format suitable for proving systems.
  7. What is CtString and when to use it

    master

    A CtString is a compile-time, dynamically-sized string type used within comptime code. Unlike str<N> or fmtstr<N, T>, the size of a CtString does not need to be specified in its type. This makes it ideal for general string handling or formatting items at compile-time where the final length is unknown.

    You can leverage the formatting capabilities of fmtstr by formatting into a fmtstr first and then converting the result into a CtString using the AsCtString trait.

  8. What is a Vector in Noir?

    master

    A vector is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they do not own the underlying data, they cannot be returned from a circuit.

    Vectors can be treated as arrays without a constrained size. In Noir, the syntax @[..] is used to create an immutable, growable vector. To write a vector literal, use a preceding at sign, such as @[0; 2] or @[1, 2, 3].

    fn main() -> pub u32 {
        let mut vector: [Field] = @[0; 2];
    
        let mut new_vector = vector.push_back(6);
        new_vector.len()
    }