Skip to content

Commit a7a6b10

Browse files
committed
Implement range proof
1 parent 67287ab commit a7a6b10

6 files changed

Lines changed: 664 additions & 1 deletion

File tree

pkg/proofs/errors.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package proofs
2+
3+
import "fmt"
4+
5+
var (
6+
// ErrNumPuzzlesAndWitnesses is returned if the number of puzzles is not equal to the number of witnesses.
7+
ErrNumPuzzlesAndWitnesses = fmt.Errorf("number of puzzles is not equal to number of witnesses")
8+
// ErrSampleY is returned if the drowning term y can't be sampled.
9+
ErrSampleY = fmt.Errorf("unable to sample drowning term y")
10+
// ErrComputeD is returned if D can't be computed.
11+
ErrComputeD = fmt.Errorf("unable to compute D")
12+
// ErrGenerateRandomness is returned if the randomness can't be generated.
13+
ErrGenerateRandomness = fmt.Errorf("unable to generate randomness")
14+
// ErrInvalidBit is returned if the bit is neither 0 nor 1.
15+
ErrInvalidBit = fmt.Errorf("bit is neither 0 nor 1")
16+
// ErrNumPuzzlesAndValues is returned if the number of puzzles is not equal to the number of values.
17+
ErrNumPuzzlesAndValues = fmt.Errorf("number of puzzles is not equal to number of values")
18+
// ErrComputeFiPrime is returned if Fi' can't be computed.
19+
ErrComputeFiPrime = fmt.Errorf("unable to compute Fi'")
20+
// ErrGenerateRandomBytes is returned if the random bytes can't be generated.
21+
ErrGenerateRandomBytes = fmt.Errorf("unable to generate random bytes")
22+
)

pkg/proofs/range_proof.go

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package proofs
2+
3+
import (
4+
"crypto/rand"
5+
"math/big"
6+
7+
"github.com/primefactor-io/lhtlp/pkg/params"
8+
"github.com/primefactor-io/lhtlp/pkg/puzzle"
9+
"github.com/primefactor-io/lhtlp/pkg/utils"
10+
)
11+
12+
// Range proof is an instance of a Range proof.
13+
type RangeProof struct {
14+
// D is the array that contains all puzzles.
15+
D []*puzzle.Puzzle
16+
// Values is the array that contains the individual puzzle values.
17+
Values []*PuzzleValues
18+
}
19+
20+
// NewRangeProof creates a new instance of a Range proof.
21+
func NewRangeProof(d []*puzzle.Puzzle, values []*PuzzleValues) *RangeProof {
22+
return &RangeProof{
23+
D: d,
24+
Values: values,
25+
}
26+
}
27+
28+
// PuzzleValues is an instance of a puzzle's values.
29+
type PuzzleValues struct {
30+
// X is the puzzle's underlying plaintext value.
31+
X *big.Int
32+
// R is the puzzle's nonce.
33+
R *big.Int
34+
}
35+
36+
// NewPuzzleValues creates a new instance of a puzzle's values.
37+
func NewPuzzleValues(x, r *big.Int) *PuzzleValues {
38+
return &PuzzleValues{
39+
X: x,
40+
R: r,
41+
}
42+
}
43+
44+
// GenerateRangeProof generates a Range proof which proves that all the puzzle's
45+
// plaintext values (their x values) are an element of {0, ..., q} and in the
46+
// range [-(q / 2), (q / 2)].
47+
// Returns an error if the proof generation fails.
48+
func GenerateRangeProof(bits int, params *params.Params, z []*puzzle.Puzzle, q *big.Int, wit []*PuzzleValues) (*RangeProof, error) {
49+
k := bits
50+
numPuzzles := len(z)
51+
52+
if len(wit) != len(z) {
53+
return nil, ErrNumPuzzlesAndWitnesses
54+
}
55+
56+
l := new(big.Int).SetInt64(int64(numPuzzles)) // l
57+
l4 := new(big.Int).Mul(big.NewInt(4), l) // 4 * l
58+
b := new(big.Int).Div(q, big.NewInt(2)) // q / 2
59+
m := new(big.Int).Mul(b, l4) // (q / 2) * 4 * l = L
60+
n := new(big.Int).Div(m, big.NewInt(4)) // L / 4
61+
n2 := new(big.Int).Mul(big.NewInt(2), n) // 2 * (L / 4)
62+
63+
// Compute puzzles with drowning terms.
64+
y := make([]*big.Int, k)
65+
rPrime := make([]*big.Int, k)
66+
d := make([]*puzzle.Puzzle, k)
67+
68+
for i := range k {
69+
// Sample random drowning term y_i in [0, 2 * (L / 4)).
70+
yi, err := rand.Int(rand.Reader, n2)
71+
if err != nil {
72+
return nil, ErrSampleY
73+
}
74+
75+
// Compute D_i and r_i'.
76+
di, riPrime, err := puzzle.GeneratePuzzleAndReturnNonce(params, yi)
77+
if err != nil {
78+
return nil, ErrComputeD
79+
}
80+
81+
d[i] = di
82+
y[i] = yi
83+
rPrime[i] = riPrime
84+
}
85+
86+
// Compute puzzle values v and w.
87+
values := make([]*PuzzleValues, k)
88+
89+
// Generate randomness via Fiat-Shamir transform.
90+
t, err := proofDataToHashBytes(k, numPuzzles, z, d)
91+
if err != nil {
92+
return nil, ErrGenerateRandomness
93+
}
94+
95+
for i := range k {
96+
xjSum := big.NewInt(0)
97+
rjSum := big.NewInt(0)
98+
99+
for j := range numPuzzles {
100+
index := (i * numPuzzles) + j
101+
bit := utils.BytesToBit(t, index)
102+
switch bit {
103+
case 0:
104+
continue
105+
case 1:
106+
xj := wit[j].X
107+
xjSum = new(big.Int).Add(xjSum, xj) // x_{j-1} + x_j
108+
109+
rj := wit[j].R
110+
rjSum = new(big.Int).Add(rjSum, rj) // r_{j-1} + r_j
111+
default:
112+
// Bit value is neither 0 nor 1.
113+
return nil, ErrInvalidBit
114+
}
115+
}
116+
117+
yi := y[i]
118+
vi := new(big.Int).Add(yi, xjSum)
119+
120+
riPrime := rPrime[i]
121+
wi := new(big.Int).Add(riPrime, rjSum)
122+
123+
values[i] = NewPuzzleValues(vi, wi)
124+
}
125+
126+
proof := NewRangeProof(d, values)
127+
128+
return proof, nil
129+
}
130+
131+
// VerifyRangePoof verifies a Range proof which proves that all the puzzle's
132+
// plaintext values (their x values) are an element of {0, ..., q} and in the
133+
// range [-(q / 2), (q / 2)].
134+
// Returns an error if the proof verification fails.
135+
func VerifyRangePoof(proof *RangeProof, bits int, params *params.Params, z []*puzzle.Puzzle, q *big.Int) (bool, error) {
136+
k := bits
137+
numPuzzles := len(z)
138+
139+
if len(proof.D) != len(proof.Values) {
140+
return false, ErrNumPuzzlesAndValues
141+
}
142+
143+
zero := big.NewInt(0)
144+
l := new(big.Int).SetInt64(int64(numPuzzles)) // l
145+
l4 := new(big.Int).Mul(big.NewInt(4), l) // 4 * l
146+
b := new(big.Int).Div(q, big.NewInt(2)) // q / 2
147+
m := new(big.Int).Mul(b, l4) // (q / 2) * 4 * l = L
148+
n := new(big.Int).Div(m, big.NewInt(2)) // L / 2
149+
n2 := new(big.Int).Mul(big.NewInt(2), n) // 2 * (L / 2)
150+
151+
// (Re)Generate randomness via Fiat-Shamir transform.
152+
t, err := proofDataToHashBytes(k, numPuzzles, z, proof.D)
153+
if err != nil {
154+
return false, ErrGenerateRandomness
155+
}
156+
157+
for i := range k {
158+
vi := proof.Values[i].X
159+
wi := proof.Values[i].R
160+
161+
// Check if v_i is an element of {0, ..., 2 * (L / 2)}.
162+
isViInSet := vi.Cmp(zero) >= 0 && vi.Cmp(n2) <= 0
163+
if !isViInSet {
164+
return false, nil
165+
}
166+
167+
// Compute F_i.
168+
zjuProduct := big.NewInt(1)
169+
zjvProduct := big.NewInt(1)
170+
171+
for j := range numPuzzles {
172+
index := (i * numPuzzles) + j
173+
bit := utils.BytesToBit(t, index)
174+
switch bit {
175+
case 0:
176+
continue
177+
case 1:
178+
zju := z[j].U
179+
in1 := new(big.Int).Mul(zjuProduct, zju) // Z_{j-1}.u * Z_j.u
180+
zjuProduct = new(big.Int).Mod(in1, params.N) // Z_{j-1}.u * Z_j.u mod n
181+
182+
zjv := z[j].V
183+
in2 := new(big.Int).Mul(zjvProduct, zjv) // Z_{j-1}.v * Z_j.v
184+
zjvProduct = new(big.Int).Mod(in2, params.NExpY) // Z_{j-1}.v * Z_j.v mod n^y
185+
default:
186+
// Bit value is neither 0 nor 1.
187+
return false, ErrInvalidBit
188+
}
189+
}
190+
191+
diu := proof.D[i].U
192+
in1 := new(big.Int).Mul(diu, zjuProduct) // D_i.U * (... * Z_{j-1}.u) * Z_j.u)
193+
fiu := new(big.Int).Mod(in1, params.N) // D_i.U * (... * Z_{j-1}.u) * Z_j.u) mod n
194+
195+
div := proof.D[i].V
196+
in2 := new(big.Int).Mul(div, zjvProduct) // D_i.V * (... * Z_{j-1}.v) * Z_j.v
197+
fiv := new(big.Int).Mod(in2, params.NExpY) // D_i.V * (... * Z_{j-1}.v) * Z_j.v mod n^y
198+
199+
fi := puzzle.NewPuzzle(fiu, fiv)
200+
201+
fiPrime, err := puzzle.GeneratePuzzleWithCustomNonce(params, wi, vi)
202+
if err != nil {
203+
return false, ErrComputeFiPrime
204+
}
205+
206+
// Check if puzzles are equal.
207+
if !fi.Equal(fiPrime) {
208+
return false, nil
209+
}
210+
}
211+
212+
return true, nil
213+
}
214+
215+
// proofDataToHashBytes implements the Fiat-Shamir transform to derive an array
216+
// of bytes.
217+
// Returns an error if random bytes can't be derived from the proof data.
218+
func proofDataToHashBytes(k, l int, z []*puzzle.Puzzle, d []*puzzle.Puzzle) ([]byte, error) {
219+
var seed []byte
220+
221+
// Puzzles (Z).
222+
for _, puzzle := range z {
223+
seed = append(seed, puzzle.U.Bytes()...)
224+
seed = append(seed, puzzle.V.Bytes()...)
225+
}
226+
// Puzzles (D).
227+
for _, puzzle := range d {
228+
seed = append(seed, puzzle.U.Bytes()...)
229+
seed = append(seed, puzzle.V.Bytes()...)
230+
}
231+
232+
numBits := k * l
233+
randBytes, err := utils.GenerateRandomBytesSeeded(seed, numBits)
234+
if err != nil {
235+
return nil, ErrGenerateRandomBytes
236+
}
237+
238+
return randBytes, nil
239+
}

0 commit comments

Comments
 (0)