Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
LABS
Guides

How to Define Statements Proven by ZK

A technical guide for developers on formally defining computational statements for Zero-Knowledge proofs. Covers arithmetization, R1CS, and practical examples.
Chainscore © 2026
introduction
FOUNDATIONS

How to Define Statements Proven by ZK

Zero-knowledge proofs verify computational statements without revealing the inputs. This guide explains how to formally define the statement you want to prove.

A zero-knowledge proof is a cryptographic protocol where a prover convinces a verifier that a specific statement is true, without revealing any information beyond the statement's validity. The core of any ZK application is the precise definition of this statement, often called a circuit or a constraint system. This definition translates a real-world claim—like "I know the preimage to this hash" or "this transaction is valid"—into a format a ZK proving system can understand and verify.

The statement is defined as a relationship between a public instance and a private witness. The instance is the known, verifiable data (e.g., a public hash output, a Merkle root, or a final state). The witness is the secret data known only to the prover that satisfies the statement (e.g., the preimage, the Merkle path, or the intermediate computation). For example, to prove knowledge of a password w for hash h, the instance is h and the witness is w, constrained by the condition SHA256(w) == h.

In practice, you define this relationship using a domain-specific language (DSL) or library for your chosen proving system. For a zk-SNARK using Circom, you write a circuit file that defines component templates and constraints. In R1CS-based systems, you model the statement as a set of arithmetic constraints. For zk-STARKs or Plonky2, you often define a computation trace or polynomial constraints. The output is a set of proving and verification keys specific to your statement.

Consider a practical example: proving you are over 18 without revealing your birthdate. The public instance is today's date and the minimum birthdate (e.g., 2006-04-01). The private witness is your actual birthdate. The circuit's logic would constrain that today - birthdate > 18 years. The verifier only learns the proof and the public instance, gaining confidence in the statement's truth without seeing the sensitive witness data.

Common pitfalls in statement definition include creating constraints that are too restrictive or too permissive, which can break security or functionality. It's also critical to ensure all operations are within the proving system's finite field arithmetic, meaning you must handle comparisons and non-arithmetic operations (like SHA256) using specially designed circuit components. Libraries like circomlib offer pre-built templates for hashes, comparators, and Merkle trees.

Once your statement is correctly defined and compiled, it generates the proving/verification key pair. The prover uses the witness to generate a proof, which is verified against the instance and the verification key. This process enables trustless verification in applications like private transactions on Zcash, validity proofs for zkRollups like zkSync, and anonymous credentials, forming the foundation of scalable and private blockchain systems.

prerequisites
FOUNDATIONAL CONCEPTS

Prerequisites

Before defining statements for zero-knowledge proofs, you need a solid grasp of the underlying cryptographic primitives and computational models.

Zero-knowledge proofs (ZKPs) are cryptographic protocols that allow one party (the prover) to convince another party (the verifier) that a statement is true without revealing any information beyond the validity of the statement itself. The core prerequisite is understanding the statement itself. In ZK terms, a statement is a claim about a secret witness w satisfying a public relation R. Formally, you prove knowledge of w such that R(x, w) = 1, where x is the public input. This framework separates what is public from what must remain private.

You must be comfortable with the computational model used to express R. Most modern ZK systems like Circom, Noir, or zkSNARKs circuits compile your logic into arithmetic constraints over a finite field. This means your statement's conditions—checks, computations, and business logic—must be expressed as polynomial equations. For example, proving you know the pre-image w of a hash x = SHA256(w) requires expressing the SHA256 algorithm as a series of field operations within a circuit.

A strong foundation in the specific proof system is essential. Are you using a zk-SNARK (e.g., Groth16, PLONK) or a zk-STARK? Each has different requirements for setup (trusted or transparent), proof size, and verification speed. Your statement's structure and the tools you use (like the Circom compiler or the arkworks libraries) will be dictated by this choice. Familiarity with the Rank-1 Constraint System (R1CS) or Plonkish arithmetization is often necessary to write efficient circuits.

Finally, practical implementation requires setting up a development environment. This typically involves installing a toolkit like circom and snarkjs, or a framework like Halo2. You'll need to write your constraint system, generate proving and verification keys, and understand how to handle public and private inputs programmatically. The statement you define in code must be a precise, unambiguous translation of your logical claim into the language of constraints understood by your chosen proof backend.

statement-fundamentals
ZK PROOF FUNDAMENTALS

What is a ZK Statement?

A ZK statement is the precise claim about a computation that a zero-knowledge proof verifies without revealing the underlying data.

In zero-knowledge proofs (ZKPs), a statement is the specific, verifiable claim that a prover asserts to be true. It's the central question the proof answers, such as "I know a secret value x such that SHA256(x) = 0xabc..." or "I executed this program correctly with valid private inputs." The statement defines the NP relation—the connection between a public statement and a private witness—that the proof system will work with. Crucially, the proof reveals the statement's truth while keeping the witness (the secret data proving it) completely hidden.

Defining a clear statement is the first step in constructing any ZK application. In circuit-based systems like Circom or Halo2, you formalize this statement as a set of constraints or a Rank-1 Constraint System (R1CS). For example, to prove you are over 18 without revealing your birthdate, your statement could be: "I possess a signed credential where current_year - birth_year > 18. The public inputs are the current year and the verifier's public key; the private witness is your actual birthdate and the credential signature.

The power of a ZK statement lies in its flexibility. You can prove statements about correct execution of arbitrary computations, membership in a Merkle tree, valid token ownership, or compliance with a regulation. Protocols like zk-SNARKs and zk-STARKs provide the cryptographic machinery to generate a small proof for these statements. The verifier only needs the public statement and the proof, not the private inputs, enabling trustless verification in blockchain environments like Ethereum or zkRollups.

To implement this, developers use domain-specific languages (DSLs). Here's a conceptual snippet in Circom defining a statement for a hash preimage:

circom
template HashPreimage() {
    signal input preimage; // Private witness
    signal input hash;     // Public input (the claimed hash output)

    // Constraint: The hash of the preimage must equal the public hash
    component hasher = SHA256();
    hasher.in <== preimage;
    hash === hasher.out;
}

The statement is: "Does there exist a preimage such that SHA256(preimage) equals the public hash?" The circuit enforces this logical relationship.

Ultimately, a well-defined ZK statement bridges high-level intent with cryptographic proof. It must be unambiguous and computationally checkable. As ZK technology evolves, frameworks are making statement definition more accessible, moving from low-level circuit writing to higher-level abstractions using languages like Noir or Leo, which allow developers to express intent while the compiler handles the constraint system generation.

key-concepts
ZK PROOF FUNDAMENTALS

Core Concepts for Statement Definition

Learn the essential building blocks for expressing and proving computational statements with zero-knowledge proofs.

arithmetization-process
FOUNDATION

Step 1: Arithmetization

Arithmetization is the process of translating a computational statement into a set of polynomial equations that a zero-knowledge proof system can verify. This is the foundational step for any ZK proof.

In zero-knowledge proofs, you cannot directly prove the execution of a program. Instead, you must first convert your statement—such as "I know an input x such that C(x) = y" for a circuit C—into an algebraic representation. This process is called arithmetization. It transforms the logic of computation into constraints over a finite field, typically expressed as polynomials. The most common paradigms for this are R1CS (Rank-1 Constraint System) and Plonkish arithmetization, which underpin protocols like Groth16 and Plonk.

Consider a simple example: proving you know the factors of a number without revealing them. For a public number N=15, you want to prove knowledge of private integers a and b such that a * b == 15. In arithmetization, this multiplicative constraint is encoded into one or more polynomial equations. For instance, in an R1CS system, you would define vectors representing the variables (1, a, b) and create matrices such that the constraint A*s * B*s - C*s = 0 holds only when a * b = 15, where s is the witness vector and * denotes a dot product.

The output of arithmetization is a set of constraints that define the NP relation for your proof. The prover's secret witness must satisfy all constraints. For a zk-SNARK, these polynomial constraints are then compiled into a Quadratic Arithmetic Program (QAP), which allows for efficient cryptographic verification. The complexity and number of these constraints directly impact prover time and proof size, making efficient arithmetization critical for performance.

In practice, developers use high-level domain-specific languages (DSLs) like Circom or ZoKrates to define their circuits. These tools handle the arithmetization automatically. For example, a Circom circuit for our factorization statement would look like:

circom
template Multiplier() {
    signal private input a;
    signal private input b;
    signal output c;
    c <== a * b;
}
component main = Multiplier();

Compiling this template generates the R1CS constraints and witness calculation logic, abstracting away the underlying polynomial math.

Choosing the right arithmetization method depends on your application. R1CS is well-understood and efficient for certain proof systems but requires a trusted setup. Plonkish arithmetization, used in protocols like Plonk and Halo2, offers universal trusted setups and more flexible custom gates. The choice influences your proof system's features, such as support for recursion or the ability to efficiently prove blockchain state transitions.

COMPARISON

Circuit Representation Formats

Different formats for defining and serializing zero-knowledge proof statements.

Feature / MetricR1CSPlonkish ArithmetizationAIR (Algebraic Intermediate Representation)Custom DSL (e.g., Circom, Cairo)

Primary Use Case

General-purpose circuits (Groth16, Marlin)

Universal SNARKs (Plonk, Halo2)

STARK proofs

High-level circuit development

Underlying Structure

Rank-1 Constraint System

Custom gates & copy constraints

Execution trace with polynomial constraints

Compiler-defined intermediate representation

Constraint Form

A * B = C

Multiplicative & additive custom gates

Low-degree polynomial identity over trace columns

Varies by language semantics

Witness Encoding

Variable assignments

Witness column assignments

Trace column assignments

Compiler-managed witness generation

Toolchain Maturity

High (libsnark, bellman)

High (Halo2, arkworks)

High (Winterfell, Starky)

Medium (language-specific)

Developer Abstraction

Low-level

Medium-level

Medium-level

High-level

Succinct Proof Size

~1.3 KB (Groth16)

~0.9 KB (Plonk)

~40-100 KB

Depends on backend prover

Trusted Setup Required

Depends on backend

r1cs-walkthrough
FROM COMPUTATION TO CONSTRAINTS

Step 2: Defining R1CS Constraints

Transform your computational statement into a set of rank-1 constraint system (R1CS) equations, the mathematical foundation for a zero-knowledge proof.

An R1CS is a system of quadratic equations that define the correct execution of a program. It represents your computation as a set of constraints over a set of variables, where each constraint must be of the form (A · s) * (B · s) = (C · s). Here, s is the witness vector containing all public inputs, private inputs, and intermediate variables. The vectors A, B, and C define linear combinations of these witness elements. Your goal is to encode the logic "if this computation is correct, then these equations hold."

To define constraints, you first model your statement as an arithmetic circuit, a graph where nodes are addition or multiplication gates over a finite field. For example, to prove you know x such that x² * x - x + 5 = 35 (i.e., x³ - x + 5 = 35), you'd create intermediate variables. Let s = [1, x, y, z, out] where y = x², z = y * x, and out is the public output 35. The constraints are: 1. (x) * (x) = (y), 2. (y) * (x) = (z), 3. (z - x + 5) * (1) = (out). This ensures the circuit is satisfied only if the original cubic equation holds.

In practice, you use a library like arkworks (Rust) or circom (domain-specific language) to define these constraints programmatically. You declare your public and private variables, then write constraints that relate them. The underlying proving system (like Groth16 or Marlin) will compile these constraints into the final proving and verification keys. A critical step is ensuring your constraints are non-degenerate—they should be satisfiable only by the valid witness, with no extraneous solutions that could allow a prover to cheat.

The complexity and number of constraints directly impact proof generation time and size. Efficient constraint design minimizes multiplicative gates. For instance, a boolean constraint a*(1-a)=0 ensures a variable a is either 0 or 1. Range checks or hash functions (like Poseidon or MiMC) are implemented as pre-defined constraint templates. Always audit your constraints: a mistake here means the proof system could validate an incorrect statement, breaking the protocol's soundness.

COMMON USE CASES

Practical Statement Examples

Understanding the Basics

A ZK statement is a claim about data that can be proven true without revealing the data itself. Think of it as a sealed envelope with a notary stamp confirming the contents meet a rule, without opening it.

Simple Example: Age Verification

  • Statement: "I am over 18 years old."
  • Private Input: Your birth date.
  • Public Input: Today's date.
  • Proof: A ZK proof that shows the difference between the two dates is >18 years, without revealing your actual birth date.

This concept is foundational for privacy-preserving applications like private voting, credential checks, and anonymous transactions.

common-pitfalls
ZK PROOF DESIGN

Common Mistakes and Pitfalls

Designing effective zero-knowledge proofs requires precise statement formulation. These common errors can lead to insecure or inefficient systems.

01

Overly Broad or Vague Statements

A proof statement must be a specific, verifiable claim. A vague statement like "I know a secret" is useless. Instead, define the exact computation, such as "I know a preimage x such that SHA256(x) = 0xabc...".

  • Problem: Ambiguity prevents the verifier from checking the intended property.
  • Solution: Formulate the statement as a precise mathematical relation or program output.
02

Proving Public Knowledge

ZK proofs are for verifying secret knowledge. A common mistake is to prove something already known to or computable by the verifier.

  • Example: Proving y = x + 5 where x is public. The verifier can compute this directly, wasting gas and proving nothing secret.
  • Rule: The witness (private input) must contain information the verifier cannot derive alone.
03

Ignoring Circuit Constraints

In frameworks like Circom or Halo2, you define constraints in an arithmetic circuit. A mismatch between the high-level intent and the low-level constraints creates logical holes.

  • Pitfall: Forgetting to enforce a range check can allow an attacker to submit a negative number that wraps modulo the circuit's prime field.
  • Best Practice: Audit constraints to ensure they are necessary and sufficient for the statement.
04

Leaking Secrets via Public Inputs

The public inputs to a ZK proof are visible on-chain. Accidentally including secret data here completely breaks privacy.

  • Real Error: Passing a secret key or a user's private balance as a public input.
  • Defense: Rigorously separate the proof's public statement (what is verified) from its private witness (what is kept hidden). Use tools to automatically check for witness leakage.
05

Inconsistent Prime Field Arithmetic

ZK circuits operate over a finite field (e.g., BN254's ~254-bit field). Applying standard integer logic can cause overflows and incorrect proofs.

  • Example: Assuming a > b without a range-check circuit. Field elements wrap, so (p-1) > 0 is false.
  • Mitigation: Use circuit libraries for comparisons, non-native arithmetic (e.g., with big integers), and ensure all business logic respects field semantics.
06

Failing to Verify On-Chain

A proof is only useful if the verifier contract checks it correctly. Common pitfalls include:

  • Not verifying the proof against the correct verifying key.
  • Using stale or incorrect public inputs in the contract call.
  • Forgetting to check the proof output against expected state changes.
  • Action: Treat the on-chain verification function as a critical security gate. Write comprehensive tests for the verifier.
ZK PROOFS

Frequently Asked Questions

Common questions about defining and working with statements for zero-knowledge proofs, from circuit design to proof generation.

In zero-knowledge proofs, a statement is the specific claim being proven. It's a mathematical assertion that the prover convinces the verifier is true, without revealing the underlying secret data (the witness).

For example, in a ZK-SNARK for a private transaction:

  • Statement: "I know a secret spending key that authorizes this transaction, and the new balance commitments are valid, without revealing my old balance or the transaction amount."
  • This statement is encoded as a set of constraints or equations in an arithmetic circuit. The prover generates a proof that they possess a valid witness (the secret key, old balance) satisfying all constraints, proving the statement's truth.
conclusion
ZK PROOF FUNDAMENTALS

Conclusion and Next Steps

This guide has explored the core components of defining statements for zero-knowledge proofs. The next step is to apply this knowledge to build and verify proofs.

Defining the correct statement is the foundational step in any ZK application. A well-defined R1CS or Plonkish constraint system, a clear public/private witness split, and a precise arithmetic circuit are prerequisites for generating a valid proof. Tools like circom, Noir, or gnark provide the frameworks to translate your logic into these provable formats. Remember, the prover's goal is to demonstrate knowledge of a private witness w that satisfies the public statement x and relation R, without revealing w itself.

To move from theory to practice, start by implementing a simple circuit. For example, prove you know the preimage to a hash without revealing it. Using circom, you would define a template that takes a private input preimage and a public input hash, then constrains the output of a SHA256 component to equal the public hash. Compiling this circuit generates the prover and verifier keys, and the snarkjs library can then be used to generate and verify a proof against specific inputs.

Your development workflow should follow a clear path: 1) Circuit Design: Model your problem as arithmetic constraints. 2) Circuit Compilation: Use a ZK DSL to compile to intermediate representations. 3) Trusted Setup: Participate in a Powers of Tau ceremony or use a universal setup for your proving system. 4) Proof Generation: Compute the witness and generate the proof using the prover key. 5) Verification: Validate the proof on-chain or off-chain using the verifier key and public inputs.

For further learning, explore advanced topics like recursive proofs (proofs of proofs), lookup arguments for efficient non-arithmetic operations, and custom gate design in Plonk. Engage with the community by reviewing circuits on platforms like zkREPL and contributing to open-source projects. The Zero Knowledge Podcast and ZK Whiteboard Sessions are excellent resources for staying current on research and applications.

Finally, always prioritize security and auditability. ZK circuits are complex and subtle bugs can compromise the entire system. Use formal verification tools where possible, write extensive tests for your circuits, and consider professional audits before deployment. The power of zero-knowledge proofs is immense, but it is matched by the responsibility of correct implementation.

How to Define Statements Proven by ZK Proofs | ChainScore Guides