Construct Zero Knowledge Proof: From arithmetic circuit to verification
Zero Knowledge Proof is an advanced cryptographic technique gaining more attention in recent times. There are various cryptographic methods invented to implement zero knowledge proofs including and not limited to zk-SNARKS, zk-STARK, Recursive ZKP etc.
Why zero knowledge proofs?
Zero knowledge proof is a cryptographic method that enables one party to communicate the knowledge of a private information to another party without revealing the actual private information to another party.
Using zero knowledge proofs , a “Prover” can provide a short proof to the “Verifier” that the Prover has a specific knowledge of some private information without revealing the actual information. For example, a Prover can prove to the Verifier that the Prover knows all the prime factors of a 10 digit number (which requires expensive computation) without revealing the factors themselves.
Key properties of zero knowledge proofs:
Below are the key properties that Zero Knowledge Proofs must satisfy,
Completeness: If a statement is true, an honest prover can convince an honest verifier that the statement is true Soundness: If a statement is false, a dishonest prover cannot convince an honest verifier that the statement is true Zero-Knowledge: If a statement is true, the verifier learns nothing other than the fact that the statement is true
Pre-requisite knowledge for deeper understanding: We will cover the implementation of a basic zero knowledge circuit in this blogpost. To understand how Zero Knowledge Proofs work from first principles, I recommend understanding/revising the following math topics in depth,
- (Number Theory) modular arithmetic, Euler’s totient function, Fermat’s Little Theorem the Chinese Remainder Theorem.
- (Abstract Algebra) Groups, rings, fields, finite fields and elliptic curve groups.
- (Cryptography) hash functions, symmetric key cryptography, public key cryptography, digital signatures, bilinear pairings, Quadratic Arithmetic Programs (QAP)
Participating Agents:
Prover: The prover is the entity that possesses a certain knowledge. The prover’s goal is to convince the verifier that they indeed have this knowledge without revealing the information itself
Verifier: The verifier is the entity to whom the prover wants to prove the possession of knowledge. The verifier receives the proof generated by the prover, and by using the verification key (generated by the trusted setup in case of groth16), verifies the validity of the proof without uncovering the actual knowledge
Trusted Setup: In the context of zk-SNARKs, there’s usually a “trusted setup phase” that generates the proving key and the verification key. This setup must be executed correctly and securely. The key security aspect of the trusted setup phase is that, after generating the keys, all the secret randomness (termed as toxic waste) used in this phase should be destroyed to maintain the zero-knowledge property
The detailed explanation of the trusted setup phase is beyond the scope of this blogpost. I’ll be covering it in the next part of this series. Here is a link to learn more about trusted setup:
Generating zero knowledge proof to prove the knowledge of roots of Cubic Polynomial
Assume a cubic equation of the format, (a * x^3) + (b * x^2) + (c * x) + d = 0
where a, b, c, d
are the coefficients and x
is the variable
We will create zero knowledge proof to prove that we know the roots of the cubic polynomial to a verifier without revealing the actual value of the roots to the verifier.
Task: to prove the knowledge of roots of a cubic polynomial, implementing Groth16 (zk-SNARK proving scheme) using the following tools: Circom and snarkjs,
zk-SNARK: is the abbreviation of “Zero Knowledge Succinct Non-Interactive Argument of Knowledge”
Groth16: is one of the most famous zkSNARK proving schemes. Groth16 has a smaller proof size, a faster proving & verification speed
Circom: is a rust-based compiler used to define arithmetic circuits. It provides a high-level language for constructing circuits with constraints and compiling the circuits
snarkjs: is a JavaScript library for generating and verifying zero-knowledge proofs. It’s an important tool for working with circuits compiled with Circom
Overview of steps involved in generating zk proofs:
Installing the required packages
- Defining the arithmetic circuit
- Compiling the circuit
- Add the inputs
- Compute the witness
- Trusted setup ceremony (applicable to Groth16 proving scheme)
- Create and export the verification key
- Generate the zero knowledge Proof
- Verify the Proof
Installing the required npm packages
|
|
Note: if node is not installed in the machine, refer this link to install node before installing the required npm packages
1. Defining the Arithmetic Circuit
Arithmetic circuits form the basic of generating zero knowledge proofs. The first part of creating a zero-knowledge proof using zk-SNARK is to define an arithmetic circuit. Below are the key components of an arithmetic circuit,
Inputs: can be either private or public. Private inputs are known only to the prover. Public inputs are known to both the prover and the verifier.
Signals: are the variables in our circuit. They can be inputs, outputs, or intermediate values computed within the circuit.
Constraints: are the core building blocks of an arithmetic circuit. Constraints are equations that define the relationships between the inputs and outputs of the circuit. Each constraint in an arithmetic circuit creates a rule that the computation must follow
Knowledge:
|
|
step1: Identify the computation: x^3 + 13x^2 + 47x + 35 = 0
step2: Express the computation as a set of equations:
|
|
step3: writing the equations as constraints in Circom
Note:Only quadratic expressions are allowed to be included in constraints. Other arithmetic expressions beyond quadratic or using other arithmetic operators like division or power are not allowed as constraints.
|
|
step4: Define the input signals, intermediate signals and output signals in Circom
|
|
step5: Complete circuit in Circom: Let’s name the file as circuit.circom
and save it in the project directory
|
|
We created a template circuit CubicRoot
above using the signals and constraints
In Circom, we call the template thru the component main and pass the public inputs a, b, c, d
to the circuit
2. Compile the circuit
|
|
View information about the circuit:
|
|
3. Add inputs
- for easier comprehension,
x^3 + 13x^2 + 47x + 35 = (x + 1) * (x + 5) * (x + 7)
- x is private input.
- Private knowledge: The roots are {-1, -5, -7} coefficients
a, b, c, d
are public inputs - Let’s create proof to prove the knowledge of the root -1
|
|
4. Generate Witness
|
|
5. Trusted Setup Ceremony
- In this blogpost, we primarily focus on how to construct an arithmetic circuit and implement it using groth16 scheme to generate zero knowledge proofs.
- In the context of zk-SNARKs, there’s usually a “trusted setup phase” that generates the proving key and the verification key.
- This setup must be executed correctly and securely
- We use the
circuit.r1cs
file to start the trusted setup ceremony and createcircuit_0000.zkey
- In this example, for demonstration purposes, we contribute only 3 rounds in the ceremony.
- In real world applications,
- eg: in KZG Ceremony, it is more than 100k contributions link to KZG contribution updates : https://ceremony.ethereum.org/
Refer the official Snarkjs link for detailed explanation of the steps : https://github.com/iden3/snarkjs#guide
|
|
6. Create and export the verification key
|
|
7. Generate the zero knowledge Proof (using prover key: circuit_final.zkey
)
|
|
public.json
will have the values of the output y for the given private input x (-1 in this example) and the public inputspublic.json
file. We can see the values of output y public inputs (values of coefficients)a, b, c, d
in order,
[ "0", "1", "13", "47", "35"]
8. Verify the Proof
|
|
Here we provided the value of private input x = -1 which is one of the roots of the Polynomial equation x^3 + 13x^2 + 47x + 35 = 0
We can prove the knowledge of the other roots by providing private input x = -5
and x = -7
. Thus we can generate zk proofs for the all the roots of the Cubic equation and prove the knowledge of the roots to the verifier without actually sharing the roots (private inputs).
Note: We can also build a Circom template to evaluate a particular polynomial using a circuit, provide all the three roots at once as private inputs and generate a single zero knowledge proof as well. The circuit constructed in this blogpost is a simple one for demonstration purposes.
Possible mistakes during circuit construction
Just ensure to avoid the below mistakes while constructing arithmetic circuits,
- Constraint Mismatch: describing more or fewer constraints than necessary for the circuit. This can often lead to issues when trying to generate or verify proofs.
- Input and Output Size Mismatch: mismatch between the size of inputs and outputs
- Incorrect Proving and Verification Keys: This error occurs when there is a mix-up with the proving and verification keys during the trusted setup
- Incorrect Inputs to the Prover and Verifier: This issue arises when the inputs to the prover or verifier are incorrect, or the format of the inputs does not match the expected format