From noir
Review Noir circuits for correctness, constraint efficiency, and proof soundness. Use proactively after writing or modifying Noir circuits.
npx claudepluginhub critesjosh/noir-claude-plugin --plugin noirThis skill is limited to using the following tools:
Structured review of Noir zero-knowledge circuits for correctness, constraint efficiency, and proof soundness. This is a **read-only** skill -- it analyzes code and reports findings but does not modify files.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Structured review of Noir zero-knowledge circuits for correctness, constraint efficiency, and proof soundness. This is a read-only skill -- it analyzes code and reports findings but does not modify files.
/review-circuit [file-path]
Examples:
/review-circuit # Review circuit in current context
/review-circuit src/main.nr # Review specific file
/review-circuit circuits/ # Review all circuits in directory
main.nr files within it using Glob: <directory>/**/src/main.nr**/src/main.nrCheck noir_status() to see if repos are synced. If not, run noir_sync_repos().
If a Nargo.toml exists in the project, read it to determine the Noir version in use. If the synced version does not match, warn the user.
Nargo.toml to understand dependenciesBefore flagging issues, verify that patterns and APIs are current:
noir_search_code() to confirm function signatures and patternsnoir_search_stdlib() to verify standard library usageWork through each category systematically. Skip categories that are not applicable to the circuit under review.
pub annotations are correct -- public inputs are minimized, all necessary inputs are publicu8 for small values, u64 for large, Field for arithmeticField is used for arithmetic where possible -- cheapest type, 1 constraint per operationposeidon lib): ~20 constraints, use for ZK-internal hashing (commitments, nullifiers, Merkle trees)std::hash): moderate cost, use for commitments where Poseidon2 is not suitablesha256 lib): ~25,000 constraints, use only when EVM/external compatibility is requiredstd::hash): high cost, general-purposeBoundedVec max sizes are realistic -- oversized maximums waste constraints on unused capacityassert_eq(a, b) is used instead of assert(a == b) -- better error messagesbool types and operators, not Field arithmetic (e.g., !flag | other instead of (1 - flag as Field) * other_field == 0)if/else expressions, not manual selects (e.g., let val = if cond { x } else { y } instead of let val = c * (x - y) + y)assert_eq on a conditional value is better than separate assertions in each branchis_unconstrained() to provide optimized ACIR and Brillig paths where beneficialunsafe {} blocks have a // Safety: comment explaining what property the constrained verification enforcesunsafe {} blocks are followed by assertion or verification logicunconstrained#[oracle(name)] functions have empty bodiesBoundedVec does not need an explicit import (it is in the prelude)pubif/else are valid -- both branches execute in ZK, invalid branches cause circuit failuresClassify every finding into one of these severity levels:
Critical -- Proof unsoundness or information leaks:
High -- Significant correctness or efficiency issues:
Medium -- Best practice violations:
BoundedVec maximumsLow -- Code style or minor improvements:
For each issue found:
Structure every review report as follows:
## Circuit Review: [CircuitName]
### Summary
Brief overview of the circuit's purpose and overall assessment of quality.
### Constraint Analysis
Estimated constraint breakdown by section (if discernible from the code).
Note which hash functions, integer types, and loop bounds dominate the constraint cost.
### Issues Found
#### Critical
- **[Issue Title]**: Description of the problem and its security impact.
- Location: `file:line`
- Current: `code snippet`
- Suggested: `fixed code`
- Why: Explanation of the ZK-specific risk.
#### High
...
#### Medium
...
#### Low
...
### Recommendations
Specific actionable suggestions for improving the circuit beyond fixing flagged issues.
### What's Done Well
Highlight good practices observed in the circuit. Positive reinforcement for correct patterns.
If no issues are found at a given severity level, omit that section rather than printing an empty list.
During review, ask clarifying questions when intent is ambiguous. Examples:
pub input exposes a value that appears to be private. Is this needed by the verifier?"Do not assume -- ask. Incorrect assumptions lead to false positives that erode trust in the review.
These are the most frequently encountered issues in Noir circuits. Pay special attention to each:
Trusting unconstrained output -- unsafe { hint() } without a corresponding assert() means the prover can return any value. This is the single most common soundness bug.
Both branches execute -- In a ZK circuit, if secret { valid_path } else { invalid_path } will execute both branches. If the invalid path triggers an out-of-bounds access or assertion failure, the circuit fails regardless of the condition.
Field overflow -- Field arithmetic wraps at the prime modulus. The expression x + 1 == 0 is satisfiable when x = p - 1. Range checks are required if overflow behavior matters.
Missing pub on inputs -- Forgetting pub on a function parameter means the verifier cannot check that input value. The prover can set it to anything.
SHA-256 for internal hashing -- Using SHA-256 or Keccak for ZK-internal operations (Merkle trees, nullifiers, commitments) is roughly 1000x more expensive in constraints than Poseidon2. Only use SHA-256/Keccak when external compatibility with EVM or other systems is required. Note that SHA-256, Keccak256, and Poseidon2 are all external libraries now, not in the stdlib.
Oversized integer types -- Using u64 when u8 suffices wastes range-check constraints. Each integer operation adds range-check gates proportional to the bit width.
Under-constrained witnesses -- If a witness variable does not participate in any assertion or constraint, the prover can set it to any value. This may allow forging proofs that the verifier accepts.
Leaking private data through returns -- Noir function return values become public circuit outputs. Returning a private witness exposes it to the verifier and anyone who sees the proof.