File download:
R1CS
When we describe any proving system we are basically operating on the concepts of “witness” (or “private input”), “public input” (or “public parameters”) and “constraints”. Constraints is a set of conditions that must be met by a witness in a couple with public input to produce a valid proof.
Usually to describe how constraints are combined (mathematically) with inputs we are using arithmetic circuits. Arithmetic circuit is an acyclic graph that consists of an “input”-vertexes and an “action”-vertexes (also called ” gates”). Each “input”-vertex contains an input value and a direction where it should be applied. On the other hand each gate contains a set of input edges (usually two), a set of output edges (usually zero or one) and an operation to be applied to the inputs. The result of this operation is directed by an output edge if exists.
Next we are going to describe the constraint system by using the vector spaces, matrices, their properties and operations, in particular - inner product: .
R1CS (Rank-1 constrain system) is a way to describe constraints and how they are applied to the witness and the public input. It has the following form: , where is a public vectors that determines the linear combinations of our witness.
Let’s examine several self-describing examples of the simple R1CS circuits.
Simplest circuit
The simplest circuit consist of the only one multiplication gate: . The witness then consists of three values and it has to satisfy our R1CS equation. Then, the tuple is obvious: and .
Hints
More complex circuits require us to examine several hits:
- Boolean check: if we are using the input value that has to represent a boolean [equals to 1 (true) or to 0 (false)] then we should provide a corresponding check to restrict putting any arbitrary value instead of boolean. This can be achieved by including a simple check that or .
- Conditional statement: if our circuit contains a conditional statement that affects the execution flow - we can combine both calculation branches by putting .
- Most circuits require the usage of constants in linear combinations that allows us multiplication by a constant or a constant addition. By putting into witness we can achieve any constant by manipulating the coefficients in .
More complex circuit
Let’s examine the following procedure:
def r(x1,x2,x3):
if x1:
return x2 * x3
else:
return x2 + x3
According to the hints described above, it can be represented as . Also, we have to add the check for boolean properties, so the final constraints can be expressed as follows:
- (binary check)
- (multiplication for “true” branch)
- (selected “true” branch)
- (final equation)
These constraints have to be encoded into the R1CS equation with the witness .
The resulting for each of our four constraints are:
constraint | a | b | c |
---|---|---|---|
1 (binary check) | (0, 0, 1, 0, 0, 0, 0) | (0, 0, 1, 0, 0, 0, 0) | (0, 0, 1, 0, 0, 0, 0) |
2 (multiplication for “true” branch) | (0, 0, 0, 1, 0, 0, 0) | (0, 0, 0, 0, 1, 0, 0) | (0, 0, 0, 0, 0, 1, 0) |
3 (selected “true” branch) | (0, 0, 1, 0, 0, 0, 0) | (0, 0, 0, 0, 0, 1, 0) | (0, 0, 0, 0, 0, 0, 1) |
4 (final equation) | (1, 0, −1, 0, 0, 0, 0) | (0, 0, 0, 1, 1, 0, 0) | (0, 1, 0, 0, 0, 0, −1) |
These separate R1CS constraints can be put together into the 4-dimensional matrix to represent one equation.
Why Rank-1?
You can search for the proof that each R1CS equation can be represented using special matrix, that equals to the outer product of vectors and this matrix rank will be (which means that each column/row can be represented by other column/row by multiplying on a certain constant).
QAP
So, above we described an approach to characterize the polynomial constrains in the R1CS form: . Where the one constraint is represented by a public vectors. In an example with a more complex circuit, we’ve generated 4 constrains, so we will have 4 tuples of vectors - each tuple for its corresponding constraint. Of course, it seems to be very difficult to prove big circuits using only R1CS - that is why we may want to build more efficient system based on R1CS. This system is called Quadratic Arithmetic Program (QAP).
At the beginning, we should recheck our note in previous post, where I’ve mentioned that we can combine all tuples into the corresponding matrices and represent our final R1CS circuit as: . Then, for each column (that corresponds to the witness’s -th value) we want to build a polynomial where for each constraint the equals to the corresponding -th witness element coefficient in the constraint . This can be done using any appropriate interpolation method (and not only on - we can use any fixed set of indexed values for interpolation if corresponding algorithm requires such). Then, receiving polynomials for each column of matrices we can rewrite our R1CS as: evaluated on domain.
Let’s rename the parts of this equation as: , , . Then, our R1CS will have form of .
We can mention that equals to some polynomial that has zeros in . To convince the verifier that we know such polynomial with corresponding zeros, we can use the popular principle - proving the knowledge of such polynomial (where is known for everyone) by proving the knowledge of such polynomials that holds the equation .
The algorithm of proving for this relation lies on the Schwartz–Zippel lemma: for the polynomial of degree and uniformly selected the probability of equals to .
This lemma becomes very useful when we want to prove the knowledge of a polynomial by evaluating it at a random point. If this evaluation can be equal to zero - such proving system does not make any sense, but when the probability of such event is small enough verifier can be sure that evaluation is correct.
So, we consider the following protocol:
- Prover shares the coefficients of named
- Verifier selects random and sends
- Prover responds with the following values .
- Verifier accepts if
Ok, this protocol follows the correctness property, but how about the soundness and zero-knowledge? The proof values queried by the verifier open lots of information about the witness, because the interpolated polynomials over the witness coefficients columns are public and can be calculated by everyone. To deal with this problem we are moving the next protocol called Proof of exponent.
Proof of exponent
Imagine, we have a polynomial and we want to convenience the verifier that it is divisible by the other public polynomial . Then, we consider the following protocol: verifier shares a powers of tau for some generator - the securely generated powers of a randomly selected in such way that nobody knows the itself. Then, prover calculates the polynomial encryption for in the following way: , where is the coefficients of the . The same is done for . The verifier checks that .
Unfortunately, it is not enough to reach the soundness property - prover still can manipulate values of to produce a valid proof where . That is why we should add “shifted” evaluation of our polynomials by some randomly selected (in the same way as powers of tau): verifier additionally shares with the prover, and the prover adds to the response the shifted polynomial encryption . Then, the verifier adds check for . While the raw values of and are not accessible by the prover we reach the soundness property.
Unfortunately (x2), we still have not reached zero-knowledge. Because the and seems to be generated by verifier, we should additionally add our “own shifts” to hide our polynomials in case when the and setup is compromised. It’s actually quite simple: during the proving phase we select random and additionally multiply all our polynomials by it (share instead of and same for other polynomial encryptions). While still verifiable it perfectly hides all information about polynomials even if the raw values of or is known by somebody.