5.1 · The Discrete Log gadget

Before we can start talking about our goal, which is to prove that K=o·T in zero-knowledge, it makes sense to review some math and theories that will help us with the proof.

1 · Zeros and a poles

Simply put, on an ordinary polynomial, a zero is a point where the function vanishes and a pole is a point where the function blows up.

For example, the polynomial below has 2 as a zero of multiplicity 1, and 5 as a zero of multiplicity 3.

f1(x)=(x2)(x5)3

and the rational function below has 5 as a pole of order 3.

f2(x)=1(x5)3

For a rational function f on an elliptic curve:

For the two functions above:

ord2(f1)=1,ord5(f1)=3,ord5(f2)=3

2 · Divisors: definitions

Let's play with E, the toy elliptic curve y2=x3+2x+11 over 𝔽103, whose 97 points are all multiples of T=(2,34).

A divisor on E is a finite formal sum

D=PEnP[P],nP

On this curve, we could have for instance:

D=3[T][4T]+2[8T]

Its degree is

degD=PnP

which for that divisor is

degD=31+2=4

For a nonzero rational function f𝔽103(E)×, its principal divisor is

div(f)=PEordP(f)[P]

with ordP(f) positive at a zero and negative at a pole. A principal divisor always has degree zero.

For example, the line function

(x,y)=yλxμ

meets the curve at three points, whose sum is O, so

div()=[P]+[Q]+[(P+Q)]3[O]

On this curve the line through T and 4T meets it again at −5T = (70,26). Its slope and intercept are one subtraction and one inversion, every step modulo 103:

λ=5034722=1670=12μ=3412·2=10=y12x10=y+91x+93

and it does vanish at the third point, −5T = (70,26):

2612·7010=0

so its divisor is

div(y+91x+93)=[T]+[4T]+[5T]3[O],deg=1+1+13=0

3 · Weil reciprocity

Reading a function at a divisor means evaluating it at every point of that divisor and multiplying them if the multiplicity is positive or dividing them if negative:

f(D)=Pf(P)nP,D=PnP[P]

Weil reciprocity states that for two nonzero rational functions the two evaluations below must agree:

f(divg)=g(divf)

Notice that neither side has to know how the other was built.

Take f(x), monic of degree four in x alone. Every factor is a vertical (x = 2, x = 25, and so on, is a zero), so it vanishes at a point and its reflection in the elliptic curve, and all of its poles sit at O:

f(x)=(x2)(x25)(x72)(x85)=x4+22x3+6x2+53x+90

Each root is the x coordinate of two points of the curve, one the reflection of the other, and every one of them is a multiple of T:

T=(2,34),T=(2,69)2T=(25,37),2T=(25,66)4T=(72,50),4T=(72,53)8T=(85,23),8T=(85,80)

Therefore:

div(f)=[T]+[T]+[2T]+[2T]+[4T]+[4T]+[8T]+[8T]8[O]

and the line through the three points A = (4,17), B = (6,41), C = (31,32):

(x,y)=y12x72
div()=[A]+[B]+[C]3[O]

Reading f at the three points of div ℓ, and at the eight points of div f:

f(div)=f(4)·f(6)·f(31)=2·80·16=88(divf)=(T)·(T)·(2T)·(2T)·(4T)·(4T)·(8T)·(8T)=41·76·77·3·41·44·64·18=88

We get the same result!

4 · The logarithmic derivative

The result above is nice but still not usable. What if we could take the logarithmic derivative on both sides of the Weil Reciprocity equation considering that we have as functions a polynomial and a line? Let's consider a generic line passing through A,B,C and a generic polynomial with zeros at P, each with multiplicity nP.

The divisor of the line is [A]+[B]+[C]3[O] and the divisor of f is PnP[P](PnP)[O], so applying the Weil reciprocity and reading each function at the other's divisor gives

f(A)f(B)f(C)=P(P)nP

Now record that if we have an ordinary polynomial function (which is the case of a line and a nonzero polynomial), then:

F(z)=i(zzi)mi

gives

F(z)F(z)=imizzi

which is simply proven by:

F(z)=imi(zzi)mi1ji(zzj)mj

and dividing that by F(z) leaves only the sum term:

F(z)F(z)=imi(zzi)mi1ji(zzj)mjj(zzj)mj=imizzi

Now take ddμlog of both sides and develop them side by side. The line is read the way the implementation reads it, (P)=λxP+μyP, so that d(P)/dμ=1 and no sign survives the differentiation:

L(μ)=f(A)f(B)f(C)R(μ)=P(P)nPL(μ)L(μ)=cddμlogf(c)R(μ)R(μ)=PnPddμlog(P)=cf(c)f(c)dxcdμ=PnP(P)d(P)dμ=cf(c)f(c)(m(c)λ)=PnP(P)

Since we have:

2ydydμ=(3x2+2)dxcdμon the curvedydμ=λdxcdμ+1on the line

then, writing the curve's tangent slope at c as m(c)=3x2+22y, the first row reads dy/dμ=m(c)dxc/dμ. Substituting it into the second and solving for the speed:

m(c)dxcdμ=λdxcdμ+1(m(c)λ)dxcdμ=1dxcdμ=1m(c)λ

What do we finally achieved after looking at the left and right side now?

q(A)+q(B)+q(C)=PnP(P),q(c)=f(c)f(c)(m(c)λ)

Is that true and does that hold still? On the quartic order polynomial and the line above, both sides come to the same field element:

f(x)=4x3+66x2+12x+53
q(A)=742(5612)=50q(B)=6980(3412)=42q(C)=8716(6612)=82q(A)+q(B)+q(C)=50+42+82=71

and on the other side, one term per zero of f:

1(T)=162=51(T)=127=421(2T)=126=41(2T)=1100=341(4T)=162=51(4T)=159=71(8T)=139=371(8T)=185=40P1(P)=5+42+4+34+5+7+37+40=71

Everything so far holds for any f and the formula below is very powerful!

q(A)+q(B)+q(C)=PnP(P),q(c)=f(c)f(c)(m(c)λ),m(c)=3x2+22y

Before we proceed, let's stop for a minute and think about what this equation is telling us. The first thing we see is that everything is linear. We have just sums of factors. The second thing is that we manage to make a sum related to the zeros of the polynomials as only three sums in the left side, no matter the size of the polynomial and its roots! So what we could naturally ask is what what if we could encode the secret value that we want to prove as a polynomial? And that is exactly what we will see next.

For that we will keep using a small example representing our elliptic curve and constants and apply all the operations that the code would be doing in the real proof to visualize what is happening. Therefore, consider that we have:

quantityvalue
curvey² = x³ + 2x + 11
coordinatesF103
scalarsZ/97Z
generatorT = (2,34)
order97, prime
witnesso = 47
public pointK = o T = (7,33)
digit sum7
All 96 points of the curve
all 96 points of the curve, in order of their multiple:

  1T (2,34)    2^0 T     25T (82,9)              49T (24,9)              73T (26,51)
  2T (25,37)   2^1 T     26T (71,97)             50T (7,70)    -K        74T (100,94)
  3T (84,18)             27T (10,102)            51T (14,65)   merge     75T (42,34)
  4T (72,50)   2^2 T     28T (86,76)             52T (20,74)             76T (59,69)
  5T (70,77)             29T (41,101)            53T (16,15)             77T (51,10)
  6T (97,68)   merge     30T (6,41)    B         54T (18,76)             78T (68,74)
  7T (80,40)             31T (53,57)             55T (40,98)             79T (52,98)
  8T (85,23)   2^3 T     32T (1,23)              56T (49,82)             80T (4,17)    A
  9T (66,75)             33T (15,29)             57T (17,23)   merge     81T (92,10)
 10T (98,59)             34T (38,67)             58T (47,102)            82T (102,27)
 11T (62,92)             35T (76,87)             59T (19,25)             83T (96,90)
 12T (46,102)            36T (63,10)             60T (11,98)             84T (31,32)   C
 13T (31,71)             37T (11,5)              61T (63,93)             85T (46,1)
 14T (96,13)             38T (19,78)             62T (76,16)             86T (62,11)
 15T (102,76)            39T (47,1)              63T (38,36)             87T (98,44)
 16T (92,93)   2^4 T     40T (17,80)   merge     64T (15,74)             88T (66,28)
 17T (4,86)              41T (49,21)             65T (1,80)              89T (85,80)
 18T (52,5)              42T (40,5)              66T (53,46)             90T (80,63)
 19T (68,29)             43T (18,27)             67T (6,62)              91T (97,35)
 20T (51,93)             44T (16,88)             68T (41,2)              92T (70,26)
 21T (59,34)             45T (20,29)             69T (86,27)             93T (72,53)
 22T (42,69)             46T (14,38)             70T (10,1)              94T (84,85)
 23T (100,9)             47T (7,33)    K = 47T   71T (71,6)              95T (25,66)
 24T (26,52)   merge     48T (24,94)             72T (82,94)             96T (2,69)

1 · Decompose the scalar

We will first fix on how many values we want to represent our secret scalar and then choose the coefficients di such that they simultaneously satisfy two conditions:

i=06di2i=oandi=06di=7

For our scalar o = 47, the decomposition procedure starts from the ordinary little-endian bits

47=(101111)2(1,1,1,1,0,1,0)

and then moves weight between adjacent slots until the coefficient sum is exactly 7:

(1,1,1,1,0,1,0)(1,1,1,1,2,0,0)(1,1,1,3,1,0,0)

Weighted sum. 1+2+4+3·8+16=47

Digit sum. 1+1+1+3+1=7

As you noticed, we can re-write our point K by multiplying the weighted sum for the base point scaled by a factor. Let's do it.

2 · The interesting set S

Define Ti=2iT. Since

K=T0+T1+T2+3T3+T4

move K to the left:

(K)+T0+T1+T2+T3+T3+T3+T4=O

And now all points sum to the identity and we have the interesting set below.

S={K, T, 2T, 4T, 8T, 8T, 8T, 16T}andPSP=O

There are eight finite points (counting the multiplicity). Seven from the digit sum, plus the single point K.

Great! We have the set of points (related to our secret scalar) which we want to be the roots of a polynomial function so that we don't need to evaluate that function on those roots but actually in three points (a line) only by using the Weil reciprocity.

3 · The polynomial

Given the eight points of S, which sum to O:

S={K, T, 2T, 4T, 8T, 8T, 8T, 16T}andPSP=O

Our problem is to find one function f whose zeros are exactly those points, with those multiplicities, and no pole except at infinity:

div(f)=[K]+[T]+[2T]+[4T]+3[8T]+[16T]8[O]

First, let's see how the shape of f looks like. The elliptic curve equation replaces every y2, so nothing above the first power of y survives and the final form of the divisor should look like:

f(x,y)=a(x)+yb(x)

The solution is simple in theory but could be cumbersome to achieve in practice. One way to understand the problem is by looking at the degree that our polynomial should have (shown by the equation below) and by looking how many equations we have (given by the points of our interesting set).

max(2dega,3+2degb)=|S|=8
dega=4,degb=2,5+3=8 coefficients

As we can see, we have as many equations as unknows and the problem basically becomes just answering the question: how do I interpolate a polynomial of degree N (so N+1 variables) given that I have N+1 points? There are many methods like the Lagrange interpolation, Newton interpolation, solve the Vandermonde matrix system and so on. What makes more sense numerically though is to use the Barycentric Lagrange interpolation, which is exactly what the code does.

Instead of going through all the tiring matrices multiplications and how that works, we will jump to the answer and just verify that it is correct for our example:

a(x)=52x4+71x3+66x2+x+37b(x)=101x2+97x+82

and they are correct because they verify:

f(K)=a(7)+70b(7)=43+60=0f(T)=a(2)+34b(2)=55+48=0f(2T)=a(25)+37b(25)=47+56=0f(4T)=a(72)+50b(72)=94+9=0f(8T)=a(85)+23b(85)=28+75=0f(16T)=a(92)+93b(92)=90+13=0

Now we want to use the equation that we previously derived:

q(A)+q(B)+q(C)=PnP(P),q(c)=f(c)f(c)(m(c)λ),m(c)=3x2+22y

Now let's see if this equation balances and then let's discuss what was achieved here.

First let's draw 2 points (A and B) and derive the third one (C), which is determined because they are in a line. Let's use A=(4,17) and B=(6,41), so C=(A+B)=(31,32), and the line through them is y=12x+72.

Now the derivative of f is:

f(c)=a(x)+m(c)b(x)+yb(x)
a(x)=2x3+7x2+29x+1,b(x)=99x+97

And finally we can just evaluate the equation on each of those values:

a(x)=2x3+7x2+29x+1,b(x)=99x+97
f(A)=48+56·26+17·81=100,f(B)=35+34·77+41·73=84,f(C)=53+66·34+32·76=94
q(A)=10031(5612)=10025=4,q(B)=8433(3412)=845=58,q(C)=9417(6612)=9494=1
q(A)+q(B)+q(C)=4+58+1=63

Now the other side:

1(T)=162=5,1(2T)=126=4,1(4T)=162=5,3(8T)=339=8,1(16T)=153=35,1(K)=186=6
PnP(P)=5+4+5+8+35+6=63

and we see that they balance. But how is the proof using this result? How does the prover enter the information about K=oT ? How does the verifier get convinced that the equation above is satisfied, and what is it that he evaluates?

To answer those questions we first have to look on what the prover and the verifier are doing:

From the prover and verifier perspectives

The first thing that we need to have in mind is that those calculations are part of a circuit. We are not going to reveal the secret values but they are going to be commited into a point using the Pedersen Vector Commitment and at the same time they enter as the wires of the circuit, which the verifier never sees. The prover will then fold all the secret information of all the circuits together in the terms of the Generalized Bulletproofs (GBP). The verifier then will only be able to check if folded values (which are blinded) really satisfies the GBP equation. Another way of thinking about it is considering that the verifier must verify the R1CS equation Aw.Bw=Cw , with the matrices A,B,C being public. If the prover folds w into a GBP proof and commit to its value, the verifer can be convinced that the prover provided a valid w which satisfies the (A,B,C) matrices because the verifier can use those matrices to reconstruct the challenges on the witness and only a vector w, that satisfies that system can balance the equation provided by the verifier's challenge.

the proverthe verifier
decomposes o into the digits dk doesn't know the decomposition
builds f on S, normalises it, and writes the digits and the coefficients into the slots of a commitment receives those commitments and keeps them to apply some checks later
sample A and B off the transcript, then calculates C=(A+B) and the line λ, μ also calculates A, B, C, λ, μ independently from the prover
evaluates q(A), q(B), q(C) and 1(K), and puts the four quotients on wires (the multiplication vectors of our circuit) recomputes the public weights 1(2kT), one per digit slot
fills the gadget's multiplication rows rebuilds those same rows, in the same order, and folds them into one multiexponentiation

The commitments

Now let's look how the prover folds the polynomials (containing the information of the zeros of our set of interest, which is the decomposition of our secret value) and folds the point K=oT

Selene, 256 slots per commitment, a 253-bit scalar:

slotswhat they hold
0.0 − 0.252the 253 balanced digits
0.253 − 0.2542 padding slots
0.255the opened point, x
1.0the divisor's y coefficient
1.1 − 1.125its 125 yx coefficients
1.126 − 1.251its 126 x coefficients
1.252its constant coefficient
1.253 − 1.254zero fill
1.255the opened point, y

Here are the C commitments and what they contain:

curvecommitmentswhat they holdwhich proof
SeleneC0 – C2the tree floors
SeleneC3 – C4input 0: o_blind on Tthe input tuple
SeleneC5 – C6input 0: i_blind on Uthe input tuple
SeleneC7input 0: i_blind on V (divisor only)the input tuple
SeleneC8 – C9input 0: i_blind_blind on Tthe input tuple
SeleneC10 – C11input 0: c_blind on Gthe input tuple
SeleneC12 – C13opens the blind of Helios C0the path
SeleneC14 – C15opens the blind of Helios C1the path
HeliosC0 – C2the tree floors
HeliosC3 – C6opens the blind of Selene C0the path
HeliosC7 – C10opens the blind of Selene C1the path
HeliosC11 – C14opens the blind of Selene C2the path

The cost of discrete log

steprowsof which multiplications
on_curve, on K's two coordinates103
q(A), q(B), q(C), one divisor evaluation each93
1(K), one inverse31
the identity itself, one linear row10
the whole gadget237

So we finally created a mechanism to prove that K=oT via circuits where the prover does it in zero-knowledge and the verifier is convinced about it because he provides the circuits (or constraints) that the prover must obey.

A real dlog in a proof

The transaction is 8872f037e2…, one input and 6 tree layers; this is the opening of o_blind on T.

o=0x02c7030d…,K=(0x5d227f2f…,0x6988e5e8…)
253 digit slots=118 ones+133 zeros+{d242=93,d243=42},idi=253
dega=127,degb=125

The three challenge points and their line, drawn from the transcript:

A=(0x0802a079…,0x0128d052…),B=(0x09e628ea…,0x6ca93949…),C=(0x0c32729f…,0x36719a0b…)
λ=0x73714cc2…,μ=0x3b7a5df3…

and the identity, with the first of the 253 public weights:

1(T)=0x18d3fce4…
q(A)+q(B)+q(C)=0x6844881e…idi(2iT)+1(K)=0x6373bdcd…+0x04d0ca50…=0x6844881e…