☰
Bulletproofs
In Layman's terms
Before we start, let's recall our goal with rangeproofs: prove that the amount that we are sending lies between and atomic units (piconeros) (and consequently it is not negative as well).
Explaining Bulletproofs (BP) in layman's terms is not an easy task so let's do justice to the saying "a picture is worth a thousand words" to get the motivations to do it. Below you will find an illustration of a standard one input and two outputs transaction using confidential transactions versus bulletproofs.
As you remember from Borromean signatures, we had to store one commitment for each bit of the amount, but what if we could prove that we know all the values without actually storing all of them? This is the main idea of Bulletproofs. We will do some math tricks to prove that we know a very long vector without actually storing it.
Math behind
Before I start, I would like to emphasize that it is only my understanding of how Bulletproofs work and it does not cover all its aspects and it may not have the depth that you are looking for. There are actually much better materials available, from which I built my understanding of BP. Please look at the Resources page to explore them. With that said, let's start just another text from someone trying to introduce BP.
If we look at the "ecdhInfo" of a transaction, we will see that the amount information is public stored at the blockchain (not explicitly but as a commitment). Let's start from there. Suppose we have a transaction with commitment where is the blinding factor and is the amount. Our objective with Bulletproofs (a form of rangeproofs) is to prove, in a very compact way, without revealing , that it lies between and where in Monero.
Let's start structuring the problem in math terms. We want to prove that and in order to do so, it suffices to prove that the following three statements are true:
- , where is the binary vector representing and is the power of two vector where
We observe that if the vector is not made of 0s and 1s, then the product won't be 0. Now we just need to find a way to combine these three statements into one so we can simply verify whether that new equation created holds. Let's start by slightly modifying 2) and 3) with the goal of letting it as a dot product that is equal to 0.
We will make use of the Schwartz-Zippel lemma which roughly states that the probability of finding a root for a polynome of order between all the possible numbers is roughly . As we are playing with modular arithmetics, is pretty big and finding a root is very unlikely unless all the coefficients are zero. (For a random different from 0 and given the polynome , if with a negligible probability otherwise.)
- , where is the binary vector representing and is the power of two vector where
Now let's rewrite the terms as a polynomial equation:
How to expand the equation and find delta
Applying some math manipulations, it is possible to achieve the independent term as we want (see Dalek's equations):
Finally, we want to combine these terms into a single inner product. Our goal is to rearrange the inner product above so that terms involving appear only on the left-hand side, terms involving appear only on the right-hand side, and non-secret terms (which the verifier can compute on its own) are factored out into a new term .
First, break the statement into simpler terms, then rearrange: To combine the terms on the right-hand side, add to each side, then simplify: Combining all non-secret terms outside the inner product we finally obtain This is equivalent to the original inner-product equation, but has a single inner product with on the left, on the right, and non-secret terms factored out. Let's call the left-hand side of the single inner product equation "unblinded" and the right-hand side "unblinded" , such that
After some math and rearranging the terms in order to have a dot product between terms only dependent on and , we achieve the following equation:
Let's now name the left side of the equation and observe that it is only a function of while the right side is now named and is a function of (the result of the inner product is a scalar).
We name these terms unblinded because we cannot send them to the verifier without exposing and . If we did so, we would reveal the value that we are trying to hide. So, in order to make it zero-knowledge, we need to blind these terms and make commitments to them. Let's start by rewritting the left and right side of our dot product with a blinding vector defined as and as:
Now we have our inner product as one equation and if we can prove that it is true than we would achieve our goal of proving that lies between and . Now the tricky and ingenious part comes to play. Our inner product is a function of and which are pretty big vectors (128 scalars each) and we don't want to store them in the blockchain, otherwise we would not get any advantage in relation to the previous Sigma protocol or Confidential Transactions that we had before. The idea here is to find a way to store the proofs in a manner that it scales under in space (which is the case of Confidential Transactions). It turns out that it is possible to do it in , which is a great improvement! For instance, for a vector of n=128 scalars, we could store only 14 scalars.
Let's see how Monero specifically does it by first considering the general ideas and equations and then a small example to understand it.
We will use the method 'Divide and Conquer' so at each step we will reduce our problem into a similar problem but with a smaller size until we reach a trivial solution and we can propagate back the final result to solve the big problem.
So, in the beginning, we have our vectors and and its inner product .
Let's define as a function of , and . Where is a point known by the verifier and prover.
Notice that this is the main equation that we want to prove its validity. If this equation holds, then we know that the creator knows and and its inner product. (Moreover, we also know that is made of bits and lies on the desired range). Our goal will be to store less data than and themselves while letting the verifier know that this equation is true. Only the value P will be stored in the blockchain.
Now let's define the variables that will be in the recursion, where is a scalar that depends on and and the sub-indices and represent the lower and higher part of the vector when split into two parts, respectively:
Notice again that we are trying to find a way to store as less information as possible in the blockchain while maintaining a solid mathematical proof of the inner product , where , and initially. According to the above definitions, let's see how we can express and : Let's start by expressing , and separately. Now let's try to express everything together to see how looks.Let's take a step back and look what we have accomplished.
- We started storing P in the blockchain (which is the commitment to , and our inner product).
- At each round we stored the points and (typically for a vector containing 128 scalars, we will make 7 rounds and store 7 values for and each) and we cut by half the dimension of ,,, (without storing them in the blockchain).
- By the end of each round, we can compute which depends on the previous , and and known values of .
- In the last round, is trivially calculated as the last dot product will be a number and we will therefore know its value.
- We store the last and scalars so the verifier can reconstruct the tests (as he knows how to construct and ) without knowing the whole scalar vectors and .
- By knowing the last , we can reconstruct all previous and verify that our initial equation holds.
Finally, we can rewrite the above procedure into one equation in order to test if P is true as informed.
Where the scalars and and the point vectors and are stored in the blockchain. The values of can be retrieved as a function of and for each step as part of the non-interactive Fiat-Shamir protocol.
Finally, we store in the blockchain for the bulletproof range-proofs:
- : a vector of points with commitments to and hiding values .
- : a point which represents the commitment to and and hiding value .
- : a point which represents the commitment to and and hiding value .
- : a point which represents the commitment to and hiding value .
- : a point which represents the commitment to and hiding value .
- : a scalar, which represents the hiding value related to , , and .
- : a scalar, which represents the hiding value related to and .
- : a vector of points of size containing the proving steps for the inner product.
- : a vector of points of size containing the proving steps for the inner product.
- : a scalar which represents the value of in the last step of the inner product.
- : a scalar which represents the value of in the last step of the inner product.
- : a scalar, which represents the inner product value to be tested.