Circuit Bugs¶
Introduction¶
Circuits are the constraint layer of a ZK system. They encode which execution traces are accepted by the verifier and which values remain private.
There are many circuit DSLs and ZK programming systems, such as Circom, Cairo, Noir, Leo, ZoKrates, Lurk, and Chiquito. Regardless of syntax, the core security question is whether the intended statement is exactly captured by constraints.
Practical bugs usually fall into three classes: under-constrained circuits that break soundness, over-constrained circuits that break completeness, and privacy leaks that break zero knowledge.
Review dimensions¶
- Soundness: Can an invalid statement still produce a valid proof?
- Completeness: Can every valid statement still produce a valid proof?
- Zero knowledge: Does the proof or public statement leak private witness data?
- Integration: Does the proved statement match the SDK, verifier, smart contract, and deployment artifacts?
Taxonomy¶
Soundness¶
- General Logic Bug
- Arithmetic Over/Under Flow
- Mismatched Type or Length
- Non-determinism
- Assigned but not Constrained
- Cryptographic Primitive Misuse
- Compiler Optimization
- Trusted Setup Error
Completeness¶
Zero knowledge¶
High-effort manual review areas¶
Some bugs are difficult to detect by reading circuit source alone and should be supported by negative tests, witness mutation, public input mutation, generated constraint inspection, or cross-layer test vectors:
- under-constrained public inputs and witness assignments;
- over-constrained edge cases that block valid witnesses;
- finite-field arithmetic and non-native field errors;
- bit decomposition and range-check errors;
- cryptographic primitive misuse, including hash functions, commitments, nullifiers, Merkle proofs, signatures, curve operations, and non-native arithmetic;
- Fiat-Shamir transcript or proof-system configuration errors;
- semantic mismatch between the intended protocol and the proven statement;
- incorrect integration across circuits, SDKs, contracts, and verifier artifacts.