A range proof in the context of inner product arguments is a proof that the scalar v has been committed to V and v is less than 2n for some non-negative integer n.
This article shows how the Bulletproofs paper constructs such a proof. The high level idea is that if we can prove that a vector aL consists only of ones and zeros and that aL is the binary representation of v, then v must be less than 2n. This is analogous to saying that a number that fits in an 8 bit unsigned integer must be less than 256.
The advantage of using Bulletproofs for range proofs is that the range proof can be directly constructed without the need of an arithmetic circuit.
Monero uses Bulletproof Range Proofs (the algorithm presented here) to ensure that the sum of transactions is not negative (in a finite field, the negative numbers are the elements greater than p/2 as they are additive inverses of the elements less than or equal p/2 where p is the field order).
yn is an n dimensional vector [1,y,y2,y3,...,yn−1]
y−n is an n dimensional vector [1,y−1,y−2,...,y−(n−1)]
Note that yn∘y−n=1n.
Range proof overview
Proving that V is a commitment to a scalar with a value less than 2n requires proving the following:
aL is binary (only holds values 0 and 1).
The inner product ⟨aL,2n⟩=v.
The second point is easy to prove, we do a normal inner product proof then reveal 2n is one of the vectors in the commitment – or have the verifier construct the commitment of 2n themselves. However, proving that aL is binary without an arithmetic circuit requires a couple algebraic tricks.
Four useful tricks
The bulletproofs paper implicitly uses four algebraic tricks that are best taught explicitly before looking at the range proof algorithm directly.
1. Proving aL is binary
The statement aL is binary is equivalent to the following two assertions:
aR=aL−1n
aL∘aR=0n
For example, if aL=[1,0,0,1] then aR=[0,−1,−1,0].
In this case, aL∘aR=0n because
[1,0,0,1]∘[0,−1,−1,0]=[0,0,0,0]=0n
Now consider a case where aL is not binary, for example [2,1,0,0]. aR will be [1,0,−1,−1] The Hadamard product of aL and aR will be [2,0,0,0]=0n.
More generally, if aL has a non-binary entry, that entry will be subtracted by 1, and the resulting entry in aR will be non-zero. When the Hadamard product is computed, then at that particular index, aL and aR will both be non-zero and the product will be non-zero, meaning aL∘aR=0n.
However, if a particular entry in aL is 1, then aR will be 0 at that index so that the Hadamard product at that index will be zero, too.
Finally, if a particular entry in aL is 0, then aR will be −1 at that index and their element-wise product will still be zero at that index.
Therefore, if aL is binary and aR is computed as aR=aL−1, then aL∘aR=0n.
2. Proving a vector is all zero
Suppose we wish to prove that the Pedersen commitment A holds a zero vector. We create the Pedersen commitment A=⟨a,G⟩+αB and wish to prove to a verifier that a=0n.
It might seem sufficient to simply send the blinding term α, but to make our solution more composable, we do not want to reveal the blinding term because that might affect other commitments we have created.
Instead, the prover sends A to the verifier, and the verifier responds a vector full of random values r. The prover must now prove that
⟨a,r⟩=0
Note that this is a probabilistic test. It is possible, with negligible probability, that ⟨a,r⟩=0 for a=0n, but it is not possible for the prover to forge such an a because they do not know in advance what r will be.
However, transmitting r requires O(n) communication overhead, so the verifier instead only sends a single random element y and the prover computes yn and uses yn as a the random vector.
Then, the prover proves that ⟨a,yn⟩=0.
3. Proving an inner product is of the form ⟨aL,aR∘yn⟩ where y is chosen by the verifier and the prover computes yn
We don’t yet have a mechanism to prove that aL∘aR=0n, as that is a Hadamard product, not an inner product. However, stating the vector aL∘aR is identically 0n is the same as stating that ⟨aL∘aR,yn⟩=0. By the inner product rules, we can move aR to the other side of the inner product and we now have ⟨aL,aR∘yn⟩=0.
The verifier will receive commitments to aL and aR, not aR∘yn. It will be up to the verifier to construct a commitment to aR∘yn so they are convinced the prover used aR∘yn as the second vector in the inner product.
The key trick we rely on is that the prover uses the basis vectors G and H to commit their vectors, but the verifier uses G and H∘y−n.
When the prover sends the evaluation ru, the prover must ensure that yn terms will cancel with the y−n in the verifier’s basis vector y−n∘H.
Specifically, the prover constructs the commitments
AS=⟨aL,G⟩+⟨aR,H⟩+αB=⟨sL,G⟩+⟨sR,H⟩+βB
And sends (A,S) to the verifier. There is no need to commit and send V because it is zero in this case.
The prover’s polynomials will be
l(x)r(x)=aL+sLx=aR∘yn+sR∘ynx
Crucially, the prover has Hadamard multiplied sR by yn. Previously, r(x) was computed as r(x)=aR∘yn+sRx (without the yn∘sR. This will later allow all of the yn terms to be canceled when the verifier computes the commitment ⟨ru,y−n∘H⟩. Under the hood, ru is (aR+sRu)∘yn so the yn will cancel when the verifier computes ⟨(aR+sRu)∘yn,y−n∘H⟩, i.e.
However, the prover cannot compute l(x) or r(x) yet because the verifier hasn’t sent y yet. Therefore, after receiving (A,S) the verifier sends y and the prover computes yn and computes the polynomial t(x):
t(x)=⟨l(x),r(x)⟩=⟨aL,aR∘yn⟩+t1x+t2x2
where
t1t2=⟨aL,sR∘yn⟩+⟨sL,aR∘yn⟩=⟨sL,sR∘yn⟩
The prover commits to the coefficients t1 and t2 as
T1T2=t1G+τ1B=t2G+τ2B
and sends (T1,T2) to the verifier. The verifier responds with u and the prover evaluates the vector polynomials l(x) and r(x):
Note that πt only includes the blinding terms for t1 and t2. In the previous implementation, πt was computed as γ+τ1u+τ2u2, where γ is the blinding term for V, which is also the constant coefficient of the polynomial t(x).
There is no blinding term γ because there is no commitment to V, i.e. v is not secret – it is 0. The prover sends (lu,ru,tu,πlr,πt) and the verifier checks that:
The first crucial difference is that the commitment to ru is done with respect to the basis vector y−n∘H instead of H for the reasons discussed earlier.
Second, tuG=?T1u+T2u2−πtB has no constant commitment. Normally, the equation is tuG=?V+T1u+T2u2−πtB, but V is a commitment to 0 in this case.
In general, if V contains values known to the verifier, the verifier can construct the commitment to V as we show in the next section.
4. Proving an inner product when an additive public constant is involved
As alluded in the section above, the verifier can reconstruct commitments if the verifier knows the underlying vector.
For example, suppose we are proving that
⟨aL+j,aR∘yn+k⟩=vz
where j and k are a vectors known to the verifier and z is a scalar known to the verifier in advance. Unlike yn, these vectors and scalar are known before the proof begins. Note that k is not Hadamard multiplied by yn in this example.
The prover still only commits to the secret values aL, aR and v as usual:
ASV=⟨aL,G⟩+⟨aR,H⟩+αB=⟨sL,G⟩+⟨sR,H⟩+βB=vG+γB
As usual, the polynomials l(x) and r(x) are such that the constant term is the vector from the original inner product and the linear terms are sL and sR. Upon receiving y from the verifier, the prover computes yn and crafts but does not evaluate l(x) and r(x):
Note that the constant term in πt is γz. The prover sends (lu,ru,tu,πlr,πt). Finally, the verifier computes:
tuA+Su+commitment to j in l(x)⟨j,G⟩+commitment to k in r(x)⟨k,y−n∘H⟩tuG=?⟨lu,ru⟩=?⟨lu,G⟩+⟨ru,y−n∘H⟩+πlrB=?Vz+T1u+T2u2−πtB
lu and ru contain j and k respectively, but A and S do not. Hence, the verifier computes commitments to those vectors and adds them to the commitments A and S. In the case of k, the basis vector y−n∘H will cause k to become k∘y−n, so the commitment must be computed with respect to y−n∘H. Finally, the blinding term πt contains vz but V does not contain z. Therefore, the prover must multiply V by z.
By computing ⟨j,G⟩, ⟨k,yn∘H⟩ and Vz, the verifier can be sure the inner product computation actually included those terms.
Range proof
To prove that V is a value less than 2n we have three things to prove:
the inner product ⟨aL,2n⟩=V, i.e. aL is the binary representation of v
aR=aL−1
aL∘aR=0
The last two claims are not directly in the form of an inner product. However, we can modify them slightly to accomplish this. What we are really saying is that the vectors
aR−aL+1
aL∘aR
are both 0n. We can use the trick from a previous section to prove that they are zero. That is, the the prover needs to establish that
⟨aL∘aR,yn⟩=0
and
⟨aR−aL+1,yn⟩=0
where yn is the random vector derived from the y value sent from the verifier.
The original bulletproofs paper slightly modifies the first claim as follows so that we can use the third trick in the previous section:
⟨aL,aR∘yn⟩=0
Therefore, the prover has three inner products to establish:
⟨aL,2n⟩=v
⟨aL,aR∘yn⟩=0n
⟨aL−1n−aR,yn⟩=0n
Combining three inner products into one
The three inner products can be combined into a single one using a random linear combination with randomness z provided from the verifier.
The terms in boxes below contain values known to the verifier, so we will construct our verification algorithm to explicitly check for those values. That is, the verifier will compute commitments to the values in the boxed terms, not the prover:
There is no need to commit to t0 – observe that it is exactly the inner product we are trying to prove, so the verifier already has the commitment as V.
The verifier sends randomness u and the prover computes
Recall that the prover did not commit the entire vectors they used for the left and right side of the inner product, but only aL and bL. The rest of the vectors were additive public vectors known to the verifier, so the verifier reconstructed the commitments to the vectors by constructing commitments to the constant terms and adding them to the commitment of the secret vectors supplied by the prover.
By way of reminder, here is the original inner product with the values known to the verifier boxed:
⟨aL+−z⋅1n,yn∘aR+yn⋅z+z2⋅2n⟩=z2⋅v+δ(y,z)
The reader is encouraged to verify that the boxed terms (values known to the verifier) in the original product were reconstructed by the verifier in the boxed terms in the set of equality checks above.
By replicating a portion of the prover’s computation, the verifier asserts that the prover actually carried out the computation as claimed.
Correctness of the verification algorithm
We now show that the final verification checks are identically correct if the prover was honest.
Below we show the exact algebra, but intuitively the verifier is “reconstructing” the left vector in the inner product aL−z⋅1n, the right vector in the inner product yn∘aR+yn⋅z+z2⋅2n and the output z2v+δ(y,z).
The verifier is not given commitments to aL−z⋅1n and yn∘aR+yn⋅z+z2⋅2n but to aL and aR. Similarly, the verifier is not given a commitment to the output z2v+δ(y,z) but only to v.
The additive terms and the terms element-wise multiplied by yn must be reconstructed by the verifier.
Correctness of tu=t(u)
For the tu=?⟨lu,ru⟩ check, this is true by definition, as that is how the prover computed tu.
Correctness of the committed l(x) and r(x) with respect to A and S
For A+Su+⟨−z⋅1n,G⟩+⟨z⋅yn+z2⋅2n,Hy−1⟩=?⟨lu,G⟩+⟨ru,Hy−1⟩+πlrB
However, such algebra would be extremely messy. Instead, we observe that z2v+δ(y,z)G is the constant term of the vector polynomial inner product of of ⟨l(x),r(x)⟩. To cancel out the blinding term in γB in V, observe that πt contains z2γ, so this will cancel with the gamma term in Vz2=(vG+γB)z2.
Since Pedersen commitments are additively homomorphic, the verifier can simply compute and add δ(y,z)G to Vz2 to compute the commitment to the constant term of the polynomial t(x).
Logarithmic-sized range proof
We can reduce the size of the data transmission by sending a commitment C to lu and ru and proving that the committed vectors have inner product tu using the logarithmic-sized proof, and then verifying that
A+Su+⟨−z⋅1n,G⟩+⟨z⋅yn+z2⋅2n,Hy−1⟩−πlrB=?C
and
tu=?⟨lu,ru⟩
with respect to the basis vectors G and Hy−1.
Using the range proof algorithm for the subset sum
The subset sum problem asks, "given a set of numbers, does a subset (possibly including the entire set) sum up to k? For example if k=16 and the set is {3,5,7,11} the answer is yes because 5+11=16. However if k=13, then the answer is no.
The subset sum problem is NP-Complete, meaning that, similar to a Boolean circuit or arithmetic circuit, it can represent any problem in NP. That is, any problem in NP can be rewritten (the technical word is “reduced”) to a subset sum instance.
By replacing 2n with [3,5,7,11], we can prove we know a solution to a subset sum without revealing the answer. Specifically, the prover would know that aL=[0,1,0,1] if k=16. In general, a one entry in aL means we include that element in the subset and a zero means it is not included in the subset.
Therefore, Bulletproofs are capable of proving knowledge of any witness for any problem in NP.
Appendix: Derivation of combining three inner products into one