Skip to content
This repository was archived by the owner on Oct 28, 2024. It is now read-only.

Commit b4ad19e

Browse files
authored
Merge pull request #51 from HarryR/python-field
Added FQ from py_ecc, changed shamir poly tests to use it
2 parents 5ed7df4 + d7626ba commit b4ad19e

File tree

4 files changed

+210
-65
lines changed

4 files changed

+210
-65
lines changed

ethsnarks/field.py

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Code copied from https://github.com/ethereum/py_ecc/blob/master/py_ecc/bn128/bn128_curve.py
2+
#
3+
# The MIT License (MIT)
4+
#
5+
# Copyright (c) 2015 Vitalik Buterin
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
# THE SOFTWARE.
24+
#
25+
26+
import sys
27+
from random import randint
28+
29+
# python3 compatibility
30+
if sys.version_info.major == 2:
31+
int_types = (int, long) # noqa: F821
32+
else:
33+
int_types = (int,)
34+
35+
36+
SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617
37+
38+
# Extended euclidean algorithm to find modular inverses for
39+
# integers
40+
def inv(a, n):
41+
if a == 0:
42+
return 0
43+
lm, hm = 1, 0
44+
low, high = a % n, n
45+
while low > 1:
46+
r = high // low
47+
nm, new = hm - lm * r, high - low * r
48+
lm, low, hm, high = nm, new, lm, low
49+
return lm % n
50+
51+
52+
# A class for field elements in FQ. Wrap a number in this class,
53+
# and it becomes a field element.
54+
class FQ(object):
55+
__slots__ = ('n', 'm')
56+
57+
def __init__(self, n, field_modulus=SNARK_SCALAR_FIELD):
58+
self.m = field_modulus
59+
if isinstance(n, self.__class__):
60+
self.n = n.n
61+
else:
62+
self.n = n % self.m
63+
assert isinstance(self.n, int_types)
64+
65+
def _other_n(self, other):
66+
if isinstance(other, FQ):
67+
if other.m != self.m:
68+
raise RuntimeError("Other field element has different modulus")
69+
return other.n
70+
if not isinstance(other, int_types):
71+
raise RuntimeError("Not a valid value type: " + str(type(other).__name__))
72+
return other
73+
74+
def __add__(self, other):
75+
on = self._other_n(other)
76+
return FQ((self.n + on) % self.m, self.m)
77+
78+
def __mul__(self, other):
79+
on = self._other_n(other)
80+
return FQ((self.n * on) % self.m, self.m)
81+
82+
def __rmul__(self, other):
83+
return self * other
84+
85+
def __radd__(self, other):
86+
return self + other
87+
88+
def __rsub__(self, other):
89+
on = self._other_n(other)
90+
return FQ((on - self.n) % self.m, self.m)
91+
92+
def __sub__(self, other):
93+
on = self._other_n(other)
94+
return FQ((self.n - on) % self.m, self.m)
95+
96+
def __div__(self, other):
97+
on = self._other_n(other)
98+
return FQ(self.n * inv(on, self.m) % self.m, self.m)
99+
100+
def __truediv__(self, other):
101+
return self.__div__(other)
102+
103+
def __rdiv__(self, other):
104+
on = self._other_n(other)
105+
return FQ(inv(self.n, self.m) * on % self.m, self.m)
106+
107+
def __rtruediv__(self, other):
108+
return self.__rdiv__(other)
109+
110+
def __pow__(self, other):
111+
if other == 0:
112+
return FQ(1, self.m)
113+
elif other == 1:
114+
return FQ(self.n, self.m)
115+
elif other % 2 == 0:
116+
return (self * self) ** (other // 2)
117+
else:
118+
return ((self * self) ** int(other // 2)) * self
119+
120+
def __eq__(self, other):
121+
if other == 0.:
122+
other = 0
123+
return self.n == self._other_n(other)
124+
125+
def __ne__(self, other):
126+
return not self == other
127+
128+
def __neg__(self):
129+
return FQ(-self.n, self.m)
130+
131+
def __repr__(self):
132+
return repr(self.n)
133+
134+
@classmethod
135+
def random(cls, modulus=SNARK_SCALAR_FIELD):
136+
return FQ(randint(1, modulus - 1), modulus)
137+
138+
@classmethod
139+
def one(cls, modulus=SNARK_SCALAR_FIELD):
140+
return cls(1, modulus)
141+
142+
@classmethod
143+
def zero(cls, modulus=SNARK_SCALAR_FIELD):
144+
return cls(0, modulus)

ethsnarks/r1cs.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from py_ecc.bn128 import curve_order
1+
from .field import FQ
22

33
def r1cs_constraint(a, b, c):
4-
assert ((((a * b) % curve_order) - c) % curve_order) == 0
4+
if not isinstance(a, FQ):
5+
a = FQ(a)
6+
if not isinstance(b, FQ):
7+
b = FQ(b)
8+
if not isinstance(c, FQ):
9+
c = FQ(c)
10+
if not a * b == c:
11+
raise RuntimeError("R1CS Constraint Failed!")

ethsnarks/shamirspoly.py

+11-21
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,26 @@
11
# Copyright (c) 2018 HarryR
22
# License: LGPL-3.0+
33

4-
from random import randint
5-
from py_ecc.bn128 import curve_order
4+
from .field import FQ
65

76

8-
def randn(n):
9-
assert n > 2
10-
return randint(1, n-1)
11-
12-
13-
def randq():
14-
return randn(curve_order)
15-
16-
17-
def shamirs_poly_n(x, a, n):
7+
def shamirs_poly(x, a):
188
assert isinstance(a, (list,tuple))
199
assert len(a) >= 2
10+
assert isinstance(x, FQ)
2011

2112
result = a[0]
2213
x_pow_i = x
2314

2415
for i, a_i in list(enumerate(a))[1:]:
25-
ai_mul_xi = (a_i * x_pow_i) % n
26-
result = (result + ai_mul_xi) % n
16+
assert isinstance(a_i, FQ)
17+
ai_mul_xi = a_i * x_pow_i
18+
result = result + ai_mul_xi
2719
x_pow_i *= x
2820

2921
return result
3022

3123

32-
def shamirs_poly(x, a):
33-
return shamirs_poly_n(x, a, curve_order)
34-
35-
3624
"""
3725
def lagrange(points, x):
3826
# Borrowed from: https://gist.github.com/melpomene/2482930
@@ -54,12 +42,14 @@ def g(i, n):
5442
return total
5543
"""
5644

57-
def lagrange(points, x, mod=curve_order):
45+
def lagrange(points, x):
5846
# Borrowed from: https://gist.github.com/melpomene/2482930
5947
total = 0
6048
n = len(points)
6149
for i in range(n):
6250
xi, yi = points[i]
51+
assert isinstance(xi, FQ)
52+
assert isinstance(yi, FQ)
6353
def g(i, n):
6454
tot_mul = 1
6555
for j in range(n):
@@ -68,7 +58,7 @@ def g(i, n):
6858
xj, yj = points[j]
6959
# Use integer division here, versus SciPy's which uses floating point division
7060
# This loses precision with large values of P, which can't be easily recovered
71-
tot_mul = (tot_mul * ( (x - xj) // (xi - xj) )) % mod
61+
tot_mul = tot_mul * ( (x - xj) / (xi - xj) )
7262
return tot_mul
73-
total = total + (yi * g(i, n)) % mod
63+
total = total + (yi * g(i, n))
7464
return total

test/test_shamir_poly.py

+46-42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

3-
from ethsnarks.shamirspoly import lagrange, shamirs_poly, randq, randn, shamirs_poly_n
3+
from ethsnarks.shamirspoly import lagrange, shamirs_poly
4+
from ethsnarks.field import FQ
45
from ethsnarks.r1cs import r1cs_constraint
56

67
from scipy.interpolate import lagrange as scipy_lagrange
@@ -14,39 +15,41 @@ class ShamirPolyTests(unittest.TestCase):
1415
def test_fromdocs(self):
1516
p = 100003
1617
k = 4
17-
a = [6257, 85026, 44499, 14701]
18+
a = [FQ(6257, p), FQ(85026, p), FQ(44499, p), FQ(14701, p)]
1819
F = lambda i, x: a[i] * (x**i)
1920
X = lambda x: a[0] + F(1, x) + F(2, x) + F(3, x)
2021
# Create the shares
21-
Sx = range(1, 5)
22+
Sx = [_ for _ in range(1, 5)]
2223
Sy = [X(_) for _ in Sx]
2324
for x, y in zip(Sx, Sy):
24-
print(x, y)
25-
z = shamirs_poly_n(x, a, p)
26-
assert z == y % p
25+
z = shamirs_poly(FQ(x, p), a)
26+
assert z == y
2727
# Then recover secret
28-
assert a[0] == int(scipy_lagrange(Sx, Sy).c[-1])
28+
result = int(scipy_lagrange(Sx, [_.n for _ in Sy]).c[-1]) % p
29+
assert a[0] == result
30+
2931

3032
def test_fromdocs2(self):
3133
p = 100003
3234
k = 4
33-
a = [randn(p) for _ in range(0, k)] # [6257, 85026, 44499, 14701]
35+
a = [FQ.random(p) for _ in range(0, k)] # [6257, 85026, 44499, 14701]
3436
F = lambda i, x: a[i] * (x**i)
3537
X = lambda x: a[0] + F(1, x) + F(2, x) + F(3, x)
3638
# Create the shares
3739
Sx = range(1, 5)
3840
Sy = [X(_) for _ in Sx]
3941
for x, y in zip(Sx, Sy):
40-
z = shamirs_poly_n(x, a, p)
41-
assert z == y % p
42+
z = shamirs_poly(FQ(x, p), a)
43+
assert z == y
4244
# Then recover secret
43-
assert a[0] == int(scipy_lagrange(Sx, Sy).c[-1])
45+
result = int(scipy_lagrange(Sx, [_.n for _ in Sy]).c[-1]) % p
46+
assert a[0] == result
4447

4548
def test_random(self):
4649
# Randomized tests
4750
for _ in range(0, 10):
48-
alpha = [randq() for _ in range(0, 4)]
49-
points = [(i, shamirs_poly(i, alpha))
51+
alpha = [FQ.random() for _ in range(0, 4)]
52+
points = [(FQ(i), shamirs_poly(FQ(i), alpha))
5053
for i in range(0, len(alpha))]
5154
assert alpha[0] == lagrange(points, 0)
5255
assert alpha[0] != lagrange(points[1:], 0)
@@ -55,57 +58,58 @@ def test_random(self):
5558
def test_random_small(self):
5659
q = 100003
5760
for _ in range(0, 10):
58-
alpha = [randn(q) for _ in range(0, 4)]
59-
points = [(i, shamirs_poly_n(i, alpha, q))
61+
alpha = [FQ.random(q) for _ in range(0, 4)]
62+
points = [(FQ(i, q), shamirs_poly(FQ(i, q), alpha))
6063
for i in range(0, len(alpha))]
61-
assert alpha[0] == lagrange(points, 0, q)
62-
assert alpha[0] != lagrange(points[1:], 0, q)
63-
assert alpha[0] != lagrange(points[2:], 0, q)
64+
assert alpha[0] == lagrange(points, 0)
65+
assert alpha[0] != lagrange(points[1:], 0)
66+
assert alpha[0] != lagrange(points[2:], 0)
6467

6568
# XXX: scipy's lagrange has floating point precision for large numbers
6669
points_x, points_y = unzip(points)
67-
interpolation = scipy_lagrange(points_x, points_y)
70+
interpolation = scipy_lagrange([_.n for _ in points_x], [_.n for _ in points_y])
6871
assert int(interpolation.c[-1]) == alpha[0]
6972

7073
def test_static(self):
7174
# Verify against static test vectors
72-
alpha = [6808181831819141657160280673506432691407806061837762993142662373500430825792,
73-
4138536697521448323155976179625860582331141320072618244300034508091478437877,
74-
20259243729221075783953642258755031830946498253783650311586175820530608751936,
75-
11227115470523445882235139084890542822660569362938710556861479160600812964997]
76-
points = [(i, shamirs_poly(i, alpha)) for i in range(0, len(alpha))]
77-
test_points = [(0, 6808181831819141657160280673506432691407806061837762993142662373500430825792),
78-
(1, 20544834857245836424258632451520592838797650598216707762192147676147522484985),
79-
(2, 10833210933219706719196668784844423052753721417299010433393634464005858464330),
80-
(3, 1259517139202877390892412692306630092142705895884865660519589327528699562575)]
75+
alpha = [FQ(6808181831819141657160280673506432691407806061837762993142662373500430825792),
76+
FQ(4138536697521448323155976179625860582331141320072618244300034508091478437877),
77+
FQ(20259243729221075783953642258755031830946498253783650311586175820530608751936),
78+
FQ(11227115470523445882235139084890542822660569362938710556861479160600812964997)]
79+
points = [(FQ(i), shamirs_poly(FQ(i), alpha)) for i in range(0, len(alpha))]
80+
test_points = [(FQ(0), FQ(6808181831819141657160280673506432691407806061837762993142662373500430825792)),
81+
(FQ(1), FQ(20544834857245836424258632451520592838797650598216707762192147676147522484985)),
82+
(FQ(2), FQ(10833210933219706719196668784844423052753721417299010433393634464005858464330)),
83+
(FQ(3), FQ(1259517139202877390892412692306630092142705895884865660519589327528699562575))]
8184
assert points == test_points
8285
assert alpha[0] == lagrange(points, 0)
8386

8487
def test_poly_constraints(self):
85-
I = 14107816444829002666153088737167870815199986768206359534115449255516606414458
88+
I = FQ(14107816444829002666153088737167870815199986768206359534115449255516606414458)
8689

8790
A = [
88-
17167899297711346731111134130539302906398306115120667361324654931060817769652,
89-
20692971555607562002131076139518972969954851288604992618106076572794460197154,
90-
11669314840495787582492056085422064523948780040073924653117253046363337959489,
91-
15735493254427804666818698471807230275586165984790760178845725782084556556535
91+
FQ(17167899297711346731111134130539302906398306115120667361324654931060817769652),
92+
FQ(20692971555607562002131076139518972969954851288604992618106076572794460197154),
93+
FQ(11669314840495787582492056085422064523948780040073924653117253046363337959489),
94+
FQ(15735493254427804666818698471807230275586165984790760178845725782084556556535)
9295
]
9396

9497
S = [
95-
1,
96-
14107816444829002666153088737167870815199986768206359534115449255516606414458,
97-
5067932324814081810415131337916762410698113547031307360925810907599032394672,
98-
13201723662860499519704937914604513230538757359894370890481138582074383924053
98+
FQ(1),
99+
FQ(14107816444829002666153088737167870815199986768206359534115449255516606414458),
100+
FQ(5067932324814081810415131337916762410698113547031307360925810907599032394672),
101+
FQ(13201723662860499519704937914604513230538757359894370890481138582074383924053)
99102
]
100103

101104
T = [
102-
17167899297711346731111134130539302906398306115120667361324654931060817769652,
103-
13947767308050706262790594447202101821284419559667543268525874072511409211876,
104-
12611939459072476081893406313696501745859572890147944797422704733972764295239,
105-
1657398546220101661314129991806057074896482894269175421846876999103942041527
105+
FQ(17167899297711346731111134130539302906398306115120667361324654931060817769652),
106+
FQ(13947767308050706262790594447202101821284419559667543268525874072511409211876),
107+
FQ(12611939459072476081893406313696501745859572890147944797422704733972764295239),
108+
FQ(1657398546220101661314129991806057074896482894269175421846876999103942041527)
106109
]
107110

108-
assert shamirs_poly(I, A) == T[-1]
111+
result = shamirs_poly(I, A)
112+
assert result == T[-1]
109113

110114
for i in range(0, len(A)):
111115
if i == 0:

0 commit comments

Comments
 (0)