mirror of
https://github.com/status-im/research.git
synced 2025-01-27 15:25:58 +00:00
Removed py_pairing (moved to own repo)
This commit is contained in:
parent
1afb999131
commit
984767ef3c
@ -1,22 +0,0 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Vitalik Buterin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1 +0,0 @@
|
||||
Implements optimal ate pairings over the bn\_128 curve.
|
@ -1,3 +0,0 @@
|
||||
from .optimized_curve import *
|
||||
from .optimized_field_elements import *
|
||||
from .optimized_pairing import *
|
@ -1,119 +0,0 @@
|
||||
from bn128_field_elements import field_modulus, FQ
|
||||
from optimized_field_elements import FQ2, FQ12
|
||||
# from bn128_field_elements import FQ2, FQ12
|
||||
|
||||
curve_order = 21888242871839275222246405745257275088548364400416034343698204186575808495617
|
||||
|
||||
# Curve order should be prime
|
||||
assert pow(2, curve_order, curve_order) == 2
|
||||
# Curve order should be a factor of field_modulus**12 - 1
|
||||
assert (field_modulus ** 12 - 1) % curve_order == 0
|
||||
|
||||
# Curve is y**2 = x**3 + 3
|
||||
b = FQ(3)
|
||||
# Twisted curve over FQ**2
|
||||
b2 = FQ2([3, 0]) / FQ2([9, 1])
|
||||
# Extension curve over FQ**12; same b value as over FQ
|
||||
b12 = FQ12([3] + [0] * 11)
|
||||
|
||||
# Generator for curve over FQ
|
||||
G1 = (FQ(1), FQ(2))
|
||||
# Generator for twisted curve over FQ2
|
||||
G2 = (FQ2([10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634]),
|
||||
FQ2([8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531]))
|
||||
|
||||
# Check that a point is on the curve defined by y**2 == x**3 + b
|
||||
def is_on_curve(pt, b):
|
||||
if pt is None:
|
||||
return True
|
||||
x, y = pt
|
||||
return y**2 - x**3 == b
|
||||
|
||||
assert is_on_curve(G1, b)
|
||||
assert is_on_curve(G2, b2)
|
||||
|
||||
# Elliptic curve doubling
|
||||
def double(pt):
|
||||
x, y = pt
|
||||
l = 3 * x**2 / (2 * y)
|
||||
newx = l**2 - 2 * x
|
||||
newy = -l * newx + l * x - y
|
||||
return newx, newy
|
||||
|
||||
# Elliptic curve addition
|
||||
def add(p1, p2):
|
||||
if p1 is None or p2 is None:
|
||||
return p1 if p2 is None else p2
|
||||
x1, y1 = p1
|
||||
x2, y2 = p2
|
||||
if x2 == x1 and y2 == y1:
|
||||
return double(p1)
|
||||
elif x2 == x1:
|
||||
return None
|
||||
else:
|
||||
l = (y2 - y1) / (x2 - x1)
|
||||
newx = l**2 - x1 - x2
|
||||
newy = -l * newx + l * x1 - y1
|
||||
assert newy == (-l * newx + l * x2 - y2)
|
||||
return (newx, newy)
|
||||
|
||||
# Elliptic curve point multiplication
|
||||
def multiply(pt, n):
|
||||
if n == 0:
|
||||
return None
|
||||
elif n == 1:
|
||||
return pt
|
||||
elif not n % 2:
|
||||
return multiply(double(pt), n // 2)
|
||||
else:
|
||||
return add(multiply(double(pt), int(n // 2)), pt)
|
||||
|
||||
# Check that the G1 curve works fine
|
||||
assert add(add(double(G1), G1), G1) == double(double(G1))
|
||||
assert double(G1) != G1
|
||||
assert add(multiply(G1, 9), multiply(G1, 5)) == add(multiply(G1, 12), multiply(G1, 2))
|
||||
assert multiply(G1, curve_order) is None
|
||||
|
||||
# Check that the G2 curve works fine
|
||||
assert add(add(double(G2), G2), G2) == double(double(G2))
|
||||
assert double(G2) != G2
|
||||
assert add(multiply(G2, 9), multiply(G2, 5)) == add(multiply(G2, 12), multiply(G2, 2))
|
||||
assert multiply(G2, curve_order) is None
|
||||
assert multiply(G2, 2 * field_modulus - curve_order) is not None
|
||||
assert is_on_curve(multiply(G2, 9), b2)
|
||||
|
||||
# "Twist" a point in E(FQ2) into a point in E(FQ12)
|
||||
w = FQ12([0, 1] + [0] * 10)
|
||||
|
||||
# Convert P => -P
|
||||
def neg(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
x, y = pt
|
||||
return (x, -y)
|
||||
|
||||
def twist(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
_x, _y = pt
|
||||
# Field isomorphism from Z[p] / x**2 to Z[p] / x**2 - 18*x + 82
|
||||
xcoeffs = [_x.coeffs[0] - _x.coeffs[1] * 9, _x.coeffs[1]]
|
||||
ycoeffs = [_y.coeffs[0] - _y.coeffs[1] * 9, _y.coeffs[1]]
|
||||
# Isomorphism into subfield of Z[p] / w**12 - 18 * w**6 + 82,
|
||||
# where w**6 = x
|
||||
nx = FQ12([xcoeffs[0]] + [0] * 5 + [xcoeffs[1]] + [0] * 5)
|
||||
ny = FQ12([ycoeffs[0]] + [0] * 5 + [ycoeffs[1]] + [0] * 5)
|
||||
# Divide x coord by w**2 and y coord by w**3
|
||||
return (nx * w **2, ny * w**3)
|
||||
|
||||
# Check that the twist creates a point that is on the curve
|
||||
assert is_on_curve(twist(G2), b12)
|
||||
|
||||
# Check that the G12 curve works fine
|
||||
|
||||
G12 = twist(G2)
|
||||
assert add(add(double(G12), G12), G12) == double(double(G12))
|
||||
assert double(G12) != G12
|
||||
assert add(multiply(G12, 9), multiply(G12, 5)) == add(multiply(G12, 12), multiply(G12, 2))
|
||||
assert is_on_curve(multiply(G12, 9), b12)
|
||||
assert multiply(G12, curve_order) is None
|
@ -1,272 +0,0 @@
|
||||
import sys
|
||||
sys.setrecursionlimit(10000)
|
||||
|
||||
# python3 compatibility
|
||||
try:
|
||||
foo = long
|
||||
except:
|
||||
long = int
|
||||
|
||||
# The prime modulus of the field
|
||||
field_modulus = 21888242871839275222246405745257275088696311157297823662689037894645226208583
|
||||
# See, it's prime!
|
||||
assert pow(2, field_modulus, field_modulus) == 2
|
||||
|
||||
# The modulus of the polynomial in this representation of FQ12
|
||||
FQ12_modulus_coeffs = [82, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0] # Implied + [1]
|
||||
|
||||
# Extended euclidean algorithm to find modular inverses for
|
||||
# integers
|
||||
def inv(a, n):
|
||||
if a == 0:
|
||||
return 0
|
||||
lm, hm = 1, 0
|
||||
low, high = a % n, n
|
||||
while low > 1:
|
||||
r = high//low
|
||||
nm, new = hm-lm*r, high-low*r
|
||||
lm, low, hm, high = nm, new, lm, low
|
||||
return lm % n
|
||||
|
||||
# A class for field elements in FQ. Wrap a number in this class,
|
||||
# and it becomes a field element.
|
||||
class FQ():
|
||||
def __init__(self, n):
|
||||
if isinstance(n, self.__class__):
|
||||
self.n = n.n
|
||||
else:
|
||||
self.n = n % field_modulus
|
||||
assert isinstance(self.n, (int, long))
|
||||
|
||||
def __add__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
return FQ((self.n + on) % field_modulus)
|
||||
|
||||
def __mul__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
return FQ((self.n * on) % field_modulus)
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self * other
|
||||
|
||||
def __radd__(self, other):
|
||||
return self + other
|
||||
|
||||
def __rsub__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
return FQ((on - self.n) % field_modulus)
|
||||
|
||||
def __sub__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
return FQ((self.n - on) % field_modulus)
|
||||
|
||||
def __div__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
assert isinstance(on, (int, long))
|
||||
return FQ(self.n * inv(on, field_modulus) % field_modulus)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self.__div__(other)
|
||||
|
||||
def __rdiv__(self, other):
|
||||
on = other.n if isinstance(other, FQ) else other
|
||||
assert isinstance(on, (int, long)), on
|
||||
return FQ(inv(self.n, field_modulus) * on % field_modulus)
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
return self.__rdiv__(other)
|
||||
|
||||
def __pow__(self, other):
|
||||
if other == 0:
|
||||
return FQ(1)
|
||||
elif other == 1:
|
||||
return FQ(self.n)
|
||||
elif other % 2 == 0:
|
||||
return (self * self) ** (other // 2)
|
||||
else:
|
||||
return ((self * self) ** int(other // 2)) * self
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, FQ):
|
||||
return self.n == other.n
|
||||
else:
|
||||
return self.n == other
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __neg__(self):
|
||||
return FQ(-self.n)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.n)
|
||||
|
||||
@classmethod
|
||||
def one(cls):
|
||||
return cls(1)
|
||||
|
||||
@classmethod
|
||||
def zero(cls):
|
||||
return cls(0)
|
||||
|
||||
# Check that the field works fine
|
||||
assert FQ(2) * FQ(2) == FQ(4)
|
||||
assert FQ(2) / FQ(7) + FQ(9) / FQ(7) == FQ(11) / FQ(7)
|
||||
assert FQ(2) * FQ(7) + FQ(9) * FQ(7) == FQ(11) * FQ(7)
|
||||
assert FQ(9) ** field_modulus == FQ(9)
|
||||
|
||||
# Utility methods for polynomial math
|
||||
def deg(p):
|
||||
d = len(p) - 1
|
||||
while p[d] == 0 and d:
|
||||
d -= 1
|
||||
return d
|
||||
|
||||
def poly_rounded_div(a, b):
|
||||
dega = deg(a)
|
||||
degb = deg(b)
|
||||
temp = [x for x in a]
|
||||
o = [0 for x in a]
|
||||
for i in range(dega - degb, -1, -1):
|
||||
o[i] += temp[degb + i] / b[degb]
|
||||
for c in range(degb + 1):
|
||||
temp[c + i] -= o[c]
|
||||
return o[:deg(o)+1]
|
||||
|
||||
# A class for elements in polynomial extension fields
|
||||
class FQP():
|
||||
def __init__(self, coeffs, modulus_coeffs):
|
||||
assert len(coeffs) == len(modulus_coeffs)
|
||||
self.coeffs = [FQ(c) for c in coeffs]
|
||||
# The coefficients of the modulus, without the leading [1]
|
||||
self.modulus_coeffs = modulus_coeffs
|
||||
# The degree of the extension field
|
||||
self.degree = len(self.modulus_coeffs)
|
||||
|
||||
def __add__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
return self.__class__([x+y for x,y in zip(self.coeffs, other.coeffs)])
|
||||
|
||||
def __sub__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
return self.__class__([x-y for x,y in zip(self.coeffs, other.coeffs)])
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, (FQ, int, long)):
|
||||
return self.__class__([c * other for c in self.coeffs])
|
||||
else:
|
||||
assert isinstance(other, self.__class__)
|
||||
b = [FQ(0) for i in range(self.degree * 2 - 1)]
|
||||
for i in range(self.degree):
|
||||
for j in range(self.degree):
|
||||
b[i + j] += self.coeffs[i] * other.coeffs[j]
|
||||
while len(b) > self.degree:
|
||||
exp, top = len(b) - self.degree - 1, b.pop()
|
||||
for i in range(self.degree):
|
||||
b[exp + i] -= top * FQ(self.modulus_coeffs[i])
|
||||
return self.__class__(b)
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self * other
|
||||
|
||||
def __div__(self, other):
|
||||
if isinstance(other, (FQ, int, long)):
|
||||
return self.__class__([c / other for c in self.coeffs])
|
||||
else:
|
||||
assert isinstance(other, self.__class__)
|
||||
return self * other.inv()
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self.__div__(other)
|
||||
|
||||
def __pow__(self, other):
|
||||
if other == 0:
|
||||
return self.__class__([1] + [0] * (self.degree - 1))
|
||||
elif other == 1:
|
||||
return self.__class__(self.coeffs)
|
||||
elif other % 2 == 0:
|
||||
return (self * self) ** (other // 2)
|
||||
else:
|
||||
return ((self * self) ** int(other // 2)) * self
|
||||
|
||||
# Extended euclidean algorithm used to find the modular inverse
|
||||
def inv(self):
|
||||
lm, hm = [1] + [0] * self.degree, [0] * (self.degree + 1)
|
||||
low, high = self.coeffs + [0], self.modulus_coeffs + [1]
|
||||
while deg(low):
|
||||
r = poly_rounded_div(high, low)
|
||||
r += [0] * (self.degree + 1 - len(r))
|
||||
nm = [x for x in hm]
|
||||
new = [x for x in high]
|
||||
assert len(lm) == len(hm) == len(low) == len(high) == len(nm) == len(new) == self.degree + 1
|
||||
for i in range(self.degree + 1):
|
||||
for j in range(self.degree + 1 - i):
|
||||
nm[i+j] -= lm[i] * r[j]
|
||||
new[i+j] -= low[i] * r[j]
|
||||
lm, low, hm, high = nm, new, lm, low
|
||||
return self.__class__(lm[:self.degree]) / low[0]
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.coeffs)
|
||||
|
||||
def __eq__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
for c1, c2 in zip(self.coeffs, other.coeffs):
|
||||
if c1 != c2:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __neg__(self):
|
||||
return self.__class__([-c for c in self.coeffs])
|
||||
|
||||
@classmethod
|
||||
def one(cls):
|
||||
return cls([1] + [0] * (cls.degree - 1))
|
||||
|
||||
@classmethod
|
||||
def zero(cls):
|
||||
return cls([0] * cls.degree)
|
||||
|
||||
# The quadratic extension field
|
||||
class FQ2(FQP):
|
||||
def __init__(self, coeffs):
|
||||
self.coeffs = [FQ(c) for c in coeffs]
|
||||
self.modulus_coeffs = [1, 0]
|
||||
self.degree = 2
|
||||
self.__class__.degree = 2
|
||||
|
||||
x = FQ2([1, 0])
|
||||
f = FQ2([1, 2])
|
||||
fpx = FQ2([2, 2])
|
||||
one = FQ2.one()
|
||||
|
||||
# Check that the field works fine
|
||||
assert x + f == fpx
|
||||
assert f / f == one
|
||||
assert one / f + x / f == (one + x) / f
|
||||
assert one * f + x * f == (one + x) * f
|
||||
assert x ** (field_modulus ** 2 - 1) == one
|
||||
|
||||
# The 12th-degree extension field
|
||||
class FQ12(FQP):
|
||||
def __init__(self, coeffs):
|
||||
self.coeffs = [FQ(c) for c in coeffs]
|
||||
self.modulus_coeffs = FQ12_modulus_coeffs
|
||||
self.degree = 12
|
||||
self.__class__.degree = 12
|
||||
|
||||
x = FQ12([1] + [0] * 11)
|
||||
f = FQ12([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
fpx = FQ12([2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
one = FQ12.one()
|
||||
|
||||
# Check that the field works fine
|
||||
assert x + f == fpx
|
||||
assert f / f == one
|
||||
assert one / f + x / f == (one + x) / f
|
||||
assert one * f + x * f == (one + x) * f
|
||||
# This check takes too long
|
||||
# assert x ** (field_modulus ** 12 - 1) == one
|
@ -1,72 +0,0 @@
|
||||
from bn128_curve import double, add, multiply, is_on_curve, neg, twist, b, b2, b12, curve_order, G1, G2, G12
|
||||
from bn128_field_elements import field_modulus, FQ
|
||||
from optimized_field_elements import FQ2, FQ12
|
||||
|
||||
ate_loop_count = 29793968203157093288
|
||||
log_ate_loop_count = 63
|
||||
|
||||
# Create a function representing the line between P1 and P2,
|
||||
# and evaluate it at T
|
||||
def linefunc(P1, P2, T):
|
||||
assert P1 and P2 and T # No points-at-infinity allowed, sorry
|
||||
x1, y1 = P1
|
||||
x2, y2 = P2
|
||||
xt, yt = T
|
||||
if x1 != x2:
|
||||
m = (y2 - y1) / (x2 - x1)
|
||||
return m * (xt - x1) - (yt - y1)
|
||||
elif y1 == y2:
|
||||
m = 3 * x1**2 / (2 * y1)
|
||||
return m * (xt - x1) - (yt - y1)
|
||||
else:
|
||||
return xt - x1
|
||||
|
||||
def cast_point_to_fq12(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
x, y = pt
|
||||
return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11))
|
||||
|
||||
# Check consistency of the "line function"
|
||||
one, two, three = G1, double(G1), multiply(G1, 3)
|
||||
negone, negtwo, negthree = multiply(G1, curve_order - 1), multiply(G1, curve_order - 2), multiply(G1, curve_order - 3)
|
||||
|
||||
assert linefunc(one, two, one) == FQ(0)
|
||||
assert linefunc(one, two, two) == FQ(0)
|
||||
assert linefunc(one, two, three) != FQ(0)
|
||||
assert linefunc(one, two, negthree) == FQ(0)
|
||||
assert linefunc(one, negone, one) == FQ(0)
|
||||
assert linefunc(one, negone, negone) == FQ(0)
|
||||
assert linefunc(one, negone, two) != FQ(0)
|
||||
assert linefunc(one, one, one) == FQ(0)
|
||||
assert linefunc(one, one, two) != FQ(0)
|
||||
assert linefunc(one, one, negtwo) == FQ(0)
|
||||
|
||||
# Main miller loop
|
||||
def miller_loop(Q, P):
|
||||
if Q is None or P is None:
|
||||
return FQ12.one()
|
||||
R = Q
|
||||
f = FQ12.one()
|
||||
for i in range(log_ate_loop_count, -1, -1):
|
||||
f = f * f * linefunc(R, R, P)
|
||||
R = double(R)
|
||||
if ate_loop_count & (2**i):
|
||||
f = f * linefunc(R, Q, P)
|
||||
R = add(R, Q)
|
||||
# assert R == multiply(Q, ate_loop_count)
|
||||
Q1 = (Q[0] ** field_modulus, Q[1] ** field_modulus)
|
||||
# assert is_on_curve(Q1, b12)
|
||||
nQ2 = (Q1[0] ** field_modulus, -Q1[1] ** field_modulus)
|
||||
# assert is_on_curve(nQ2, b12)
|
||||
f = f * linefunc(R, Q1, P)
|
||||
R = add(R, Q1)
|
||||
f = f * linefunc(R, nQ2, P)
|
||||
# R = add(R, nQ2) This line is in many specifications but it technically does nothing
|
||||
return f ** ((field_modulus ** 12 - 1) // curve_order)
|
||||
|
||||
# Pairing computation
|
||||
def pairing(Q, P):
|
||||
assert is_on_curve(Q, b2)
|
||||
assert is_on_curve(P, b)
|
||||
return miller_loop(twist(Q), cast_point_to_fq12(P))
|
@ -1,143 +0,0 @@
|
||||
from .bn128_field_elements import field_modulus, FQ
|
||||
from .optimized_field_elements import FQ2, FQ12
|
||||
# from bn128_field_elements import FQ2, FQ12
|
||||
|
||||
curve_order = 21888242871839275222246405745257275088548364400416034343698204186575808495617
|
||||
|
||||
# Curve order should be prime
|
||||
assert pow(2, curve_order, curve_order) == 2
|
||||
# Curve order should be a factor of field_modulus**12 - 1
|
||||
assert (field_modulus ** 12 - 1) % curve_order == 0
|
||||
|
||||
# Curve is y**2 = x**3 + 3
|
||||
b = FQ(3)
|
||||
# Twisted curve over FQ**2
|
||||
b2 = FQ2([3, 0]) / FQ2([9, 1])
|
||||
# Extension curve over FQ**12; same b value as over FQ
|
||||
b12 = FQ12([3] + [0] * 11)
|
||||
|
||||
# Generator for curve over FQ
|
||||
G1 = (FQ(1), FQ(2), FQ(1))
|
||||
# Generator for twisted curve over FQ2
|
||||
G2 = (FQ2([10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634]),
|
||||
FQ2([8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531]), FQ2.one())
|
||||
|
||||
# Check that a point is on the curve defined by y**2 == x**3 + b
|
||||
def is_on_curve(pt, b):
|
||||
if pt[-1] == pt[-1].__class__.zero():
|
||||
return True
|
||||
x, y, z = pt
|
||||
return y**2 * z - x**3 == b * z**3
|
||||
|
||||
assert is_on_curve(G1, b)
|
||||
assert is_on_curve(G2, b2)
|
||||
|
||||
# Elliptic curve doubling
|
||||
def double(pt):
|
||||
x, y, z = pt
|
||||
W = 3 * x * x
|
||||
S = y * z
|
||||
B = x * y * S
|
||||
H = W * W - 8 * B
|
||||
S_squared = S * S
|
||||
newx = 2 * H * S
|
||||
newy = W * (4 * B - H) - 8 * y * y * S_squared
|
||||
newz = 8 * S * S_squared
|
||||
return newx, newy, newz
|
||||
|
||||
# Elliptic curve addition
|
||||
def add(p1, p2):
|
||||
one, zero = p1[0].__class__.one(), p1[0].__class__.zero()
|
||||
if p1[2] == zero or p2[2] == zero:
|
||||
return p1 if p2[2] == zero else p2
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
U1 = y2 * z1
|
||||
U2 = y1 * z2
|
||||
V1 = x2 * z1
|
||||
V2 = x1 * z2
|
||||
if V1 == V2 and U1 == U2:
|
||||
return double(p1)
|
||||
elif V1 == V2:
|
||||
return (one, one, zero)
|
||||
U = U1 - U2
|
||||
V = V1 - V2
|
||||
V_squared = V * V
|
||||
V_squared_times_V2 = V_squared * V2
|
||||
V_cubed = V * V_squared
|
||||
W = z1 * z2
|
||||
A = U * U * W - V_cubed - 2 * V_squared_times_V2
|
||||
newx = V * A
|
||||
newy = U * (V_squared_times_V2 - A) - V_cubed * U2
|
||||
newz = V_cubed * W
|
||||
return (newx, newy, newz)
|
||||
|
||||
# Elliptic curve point multiplication
|
||||
def multiply(pt, n):
|
||||
if n == 0:
|
||||
return (pt[0].__class__.one(), pt[0].__class__.one(), pt[0].__class__.zero())
|
||||
elif n == 1:
|
||||
return pt
|
||||
elif not n % 2:
|
||||
return multiply(double(pt), n // 2)
|
||||
else:
|
||||
return add(multiply(double(pt), int(n // 2)), pt)
|
||||
|
||||
def eq(p1, p2):
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
return x1 * z2 == x2 * z1 and y1 * z2 == y2 * z1
|
||||
|
||||
def normalize(pt):
|
||||
x, y, z = pt
|
||||
return (x / z, y / z)
|
||||
|
||||
# Check that the G1 curve works fine
|
||||
assert eq(add(add(double(G1), G1), G1), double(double(G1)))
|
||||
assert not eq(double(G1), G1)
|
||||
assert eq(add(multiply(G1, 9), multiply(G1, 5)), add(multiply(G1, 12), multiply(G1, 2)))
|
||||
assert eq(multiply(G1, curve_order), (1, 1, 0))
|
||||
|
||||
# Check that the G2 curve works fine
|
||||
assert eq(add(add(double(G2), G2), G2), double(double(G2)))
|
||||
assert not eq(double(G2), G2)
|
||||
assert eq(add(multiply(G2, 9), multiply(G2, 5)), add(multiply(G2, 12), multiply(G2, 2)))
|
||||
assert eq(multiply(G2, curve_order), (1, 1, 0))
|
||||
assert not eq(multiply(G2, 2 * field_modulus - curve_order), (1, 1, 0))
|
||||
assert is_on_curve(multiply(G2, 9), b2)
|
||||
|
||||
# "Twist" a point in E(FQ2) into a point in E(FQ12)
|
||||
w = FQ12([0, 1] + [0] * 10)
|
||||
|
||||
# Convert P => -P
|
||||
def neg(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
x, y, z = pt
|
||||
return (x, -y, z)
|
||||
|
||||
def twist(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
_x, _y, _z = pt
|
||||
# Field isomorphism from Z[p] / x**2 to Z[p] / x**2 - 18*x + 82
|
||||
xcoeffs = [_x.coeffs[0] - _x.coeffs[1] * 9, _x.coeffs[1]]
|
||||
ycoeffs = [_y.coeffs[0] - _y.coeffs[1] * 9, _y.coeffs[1]]
|
||||
zcoeffs = [_z.coeffs[0] - _z.coeffs[1] * 9, _z.coeffs[1]]
|
||||
x, y, z = _x - _y * 9, _y, _z
|
||||
nx = FQ12([xcoeffs[0]] + [0] * 5 + [xcoeffs[1]] + [0] * 5)
|
||||
ny = FQ12([ycoeffs[0]] + [0] * 5 + [ycoeffs[1]] + [0] * 5)
|
||||
nz = FQ12([zcoeffs[0]] + [0] * 5 + [zcoeffs[1]] + [0] * 5)
|
||||
return (nx * w **2, ny * w**3, nz)
|
||||
|
||||
# Check that the twist creates a point that is on the curve
|
||||
assert is_on_curve(twist(G2), b12)
|
||||
|
||||
# Check that the G12 curve works fine
|
||||
|
||||
G12 = twist(G2)
|
||||
assert eq(add(add(double(G12), G12), G12), double(double(G12)))
|
||||
assert not eq(double(G12), G12)
|
||||
assert eq(add(multiply(G12, 9), multiply(G12, 5)), add(multiply(G12, 12), multiply(G12, 2)))
|
||||
assert is_on_curve(multiply(G12, 9), b12)
|
||||
assert eq(multiply(G12, curve_order), (1, 1, 0))
|
@ -1,197 +0,0 @@
|
||||
field_modulus = 21888242871839275222246405745257275088696311157297823662689037894645226208583
|
||||
FQ12_modulus_coeffs = [82, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0] # Implied + [1]
|
||||
FQ12_mc_tuples = [(i, c) for i, c in enumerate(FQ12_modulus_coeffs) if c]
|
||||
|
||||
# python3 compatibility
|
||||
try:
|
||||
foo = long
|
||||
except:
|
||||
long = int
|
||||
|
||||
# Extended euclidean algorithm to find modular inverses for
|
||||
# integers
|
||||
def prime_field_inv(a, n):
|
||||
if a == 0:
|
||||
return 0
|
||||
lm, hm = 1, 0
|
||||
low, high = a % n, n
|
||||
while low > 1:
|
||||
r = high//low
|
||||
nm, new = hm-lm*r, high-low*r
|
||||
lm, low, hm, high = nm, new, lm, low
|
||||
return lm % n
|
||||
|
||||
# Utility methods for polynomial math
|
||||
def deg(p):
|
||||
d = len(p) - 1
|
||||
while p[d] == 0 and d:
|
||||
d -= 1
|
||||
return d
|
||||
|
||||
def poly_rounded_div(a, b):
|
||||
dega = deg(a)
|
||||
degb = deg(b)
|
||||
temp = [x for x in a]
|
||||
o = [0 for x in a]
|
||||
for i in range(dega - degb, -1, -1):
|
||||
o[i] = (o[i] + temp[degb + i] * prime_field_inv(b[degb], field_modulus))
|
||||
for c in range(degb + 1):
|
||||
temp[c + i] = (temp[c + i] - o[c])
|
||||
return [x % field_modulus for x in o[:deg(o)+1]]
|
||||
|
||||
def karatsuba(a, b, c, d):
|
||||
L = len(a)
|
||||
EXTENDED_LEN = L * 2 - 1
|
||||
# phi = (a+b)(c+d)
|
||||
# psi = (a-b)(c-d)
|
||||
phi, psi, bd2 = [0] * EXTENDED_LEN, [0] * EXTENDED_LEN, [0] * EXTENDED_LEN
|
||||
for i in range(L):
|
||||
for j in range(L):
|
||||
phi[i + j] += (a[i] + b[i]) * (c[j] + d[j])
|
||||
psi[i + j] += (a[i] - b[i]) * (c[j] - d[j])
|
||||
bd2[i + j] += b[i] * d[j] * 2
|
||||
o = [0] * (L * 4 - 1)
|
||||
# L = (phi + psi - bd2) / 2
|
||||
# M = (phi - psi) / 2
|
||||
# H = bd2 / 2
|
||||
for i in range(L * 2 - 1):
|
||||
o[i] += phi[i] + psi[i] - bd2[i]
|
||||
o[i + L] += phi[i] - psi[i]
|
||||
o[i + L * 2] += bd2[i]
|
||||
inv_2 = (field_modulus + 1) // 2
|
||||
return [a * inv_2 if a % 2 else a // 2 for a in o]
|
||||
|
||||
o = karatsuba([1, 3], [3, 1], [1, 3], [3, 1])
|
||||
assert [x % field_modulus for x in o] == [1, 6, 15, 20, 15, 6, 1]
|
||||
|
||||
# A class for elements in polynomial extension fields
|
||||
class FQP():
|
||||
def __init__(self, coeffs, modulus_coeffs):
|
||||
assert len(coeffs) == len(modulus_coeffs)
|
||||
self.coeffs = coeffs
|
||||
# The coefficients of the modulus, without the leading [1]
|
||||
self.modulus_coeffs = modulus_coeffs
|
||||
# The degree of the extension field
|
||||
self.degree = len(self.modulus_coeffs)
|
||||
|
||||
def __add__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
return self.__class__([(x+y) % field_modulus for x,y in zip(self.coeffs, other.coeffs)])
|
||||
|
||||
def __sub__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
return self.__class__([(x-y) % field_modulus for x,y in zip(self.coeffs, other.coeffs)])
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, (int, long)):
|
||||
return self.__class__([c * other % field_modulus for c in self.coeffs])
|
||||
else:
|
||||
# assert isinstance(other, self.__class__)
|
||||
b = [0] * (self.degree * 2 - 1)
|
||||
inner_enumerate = list(enumerate(other.coeffs))
|
||||
for i, eli in enumerate(self.coeffs):
|
||||
for j, elj in inner_enumerate:
|
||||
b[i + j] += eli * elj
|
||||
# MID = len(self.coeffs) // 2
|
||||
# b = karatsuba(self.coeffs[:MID], self.coeffs[MID:], other.coeffs[:MID], other.coeffs[MID:])
|
||||
for exp in range(self.degree - 2, -1, -1):
|
||||
top = b.pop()
|
||||
for i, c in self.mc_tuples:
|
||||
b[exp + i] -= top * c
|
||||
return self.__class__([x % field_modulus for x in b])
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self * other
|
||||
|
||||
def __div__(self, other):
|
||||
if isinstance(other, (int, long)):
|
||||
return self.__class__([c * prime_field_inv(other, field_modulus) % field_modulus for c in self.coeffs])
|
||||
else:
|
||||
assert isinstance(other, self.__class__)
|
||||
return self * other.inv()
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self.__div__(other)
|
||||
|
||||
def __pow__(self, other):
|
||||
o = self.__class__([1] + [0] * (self.degree - 1))
|
||||
t = self
|
||||
while other > 0:
|
||||
if other & 1:
|
||||
o = o * t
|
||||
other >>= 1
|
||||
t = t * t
|
||||
return o
|
||||
|
||||
# Extended euclidean algorithm used to find the modular inverse
|
||||
def inv(self):
|
||||
lm, hm = [1] + [0] * self.degree, [0] * (self.degree + 1)
|
||||
low, high = self.coeffs + [0], self.modulus_coeffs + [1]
|
||||
while deg(low):
|
||||
r = poly_rounded_div(high, low)
|
||||
r += [0] * (self.degree + 1 - len(r))
|
||||
nm = [x for x in hm]
|
||||
new = [x for x in high]
|
||||
# assert len(lm) == len(hm) == len(low) == len(high) == len(nm) == len(new) == self.degree + 1
|
||||
for i in range(self.degree + 1):
|
||||
for j in range(self.degree + 1 - i):
|
||||
nm[i+j] -= lm[i] * r[j]
|
||||
new[i+j] -= low[i] * r[j]
|
||||
nm = [x % field_modulus for x in nm]
|
||||
new = [x % field_modulus for x in new]
|
||||
lm, low, hm, high = nm, new, lm, low
|
||||
return self.__class__(lm[:self.degree]) / low[0]
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.coeffs)
|
||||
|
||||
def __eq__(self, other):
|
||||
assert isinstance(other, self.__class__)
|
||||
for c1, c2 in zip(self.coeffs, other.coeffs):
|
||||
if c1 != c2:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __neg__(self):
|
||||
return self.__class__([-c for c in self.coeffs])
|
||||
|
||||
@classmethod
|
||||
def one(cls):
|
||||
return cls([1] + [0] * (cls.degree - 1))
|
||||
|
||||
@classmethod
|
||||
def zero(cls):
|
||||
return cls([0] * cls.degree)
|
||||
|
||||
# The quadratic extension field
|
||||
class FQ2(FQP):
|
||||
def __init__(self, coeffs):
|
||||
self.coeffs = coeffs
|
||||
self.modulus_coeffs = [1, 0]
|
||||
self.mc_tuples = [(0, 1)]
|
||||
self.degree = 2
|
||||
self.__class__.degree = 2
|
||||
|
||||
x = FQ2([1, 0])
|
||||
f = FQ2([1, 2])
|
||||
fpx = FQ2([2, 2])
|
||||
one = FQ2.one()
|
||||
|
||||
# Check that the field works fine
|
||||
assert x + f == fpx
|
||||
assert f / f == one
|
||||
assert one / f + x / f == (one + x) / f
|
||||
assert one * f + x * f == (one + x) * f
|
||||
assert x ** (field_modulus ** 2 - 1) == one
|
||||
|
||||
# The 12th-degree extension field
|
||||
class FQ12(FQP):
|
||||
def __init__(self, coeffs):
|
||||
self.coeffs = coeffs
|
||||
self.modulus_coeffs = FQ12_modulus_coeffs
|
||||
self.mc_tuples = FQ12_mc_tuples
|
||||
self.degree = 12
|
||||
self.__class__.degree = 12
|
@ -1,114 +0,0 @@
|
||||
from .optimized_curve import double, add, multiply, is_on_curve, neg, twist, b, b2, b12, curve_order, G1, G2, G12, normalize
|
||||
from .bn128_field_elements import field_modulus, FQ
|
||||
from .optimized_field_elements import FQ2, FQ12
|
||||
|
||||
ate_loop_count = 29793968203157093288
|
||||
log_ate_loop_count = 63
|
||||
pseudo_binary_encoding = [0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
|
||||
0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
|
||||
1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
|
||||
1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1]
|
||||
|
||||
assert sum([e * 2**i for i, e in enumerate(pseudo_binary_encoding)]) == ate_loop_count
|
||||
|
||||
|
||||
def normalize1(p):
|
||||
x, y = normalize(p)
|
||||
return x, y, x.__class__.one()
|
||||
|
||||
# Create a function representing the line between P1 and P2,
|
||||
# and evaluate it at T. Returns a numerator and a denominator
|
||||
# to avoid unneeded divisions
|
||||
def linefunc(P1, P2, T):
|
||||
zero = P1[0].__class__.zero()
|
||||
x1, y1, z1 = P1
|
||||
x2, y2, z2 = P2
|
||||
xt, yt, zt = T
|
||||
# points in projective coords: (x / z, y / z)
|
||||
# hence, m = (y2/z2 - y1/z1) / (x2/z2 - x1/z1)
|
||||
# multiply numerator and denominator by z1z2 to get values below
|
||||
m_numerator = y2 * z1 - y1 * z2
|
||||
m_denominator = x2 * z1 - x1 * z2
|
||||
if m_denominator != zero:
|
||||
# m * ((xt/zt) - (x1/z1)) - ((yt/zt) - (y1/z1))
|
||||
return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \
|
||||
m_denominator * zt * z1
|
||||
elif m_numerator == zero:
|
||||
# m = 3(x/z)^2 / 2(y/z), multiply num and den by z**2
|
||||
m_numerator = 3 * x1 * x1
|
||||
m_denominator = 2 * y1 * z1
|
||||
return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \
|
||||
m_denominator * zt * z1
|
||||
else:
|
||||
return xt * z1 - x1 * zt, z1 * zt
|
||||
|
||||
def cast_point_to_fq12(pt):
|
||||
if pt is None:
|
||||
return None
|
||||
x, y, z = pt
|
||||
return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11), FQ12([z.n] + [0] * 11))
|
||||
|
||||
# Check consistency of the "line function"
|
||||
one, two, three = G1, double(G1), multiply(G1, 3)
|
||||
negone, negtwo, negthree = multiply(G1, curve_order - 1), multiply(G1, curve_order - 2), multiply(G1, curve_order - 3)
|
||||
|
||||
assert linefunc(one, two, one)[0] == FQ(0)
|
||||
assert linefunc(one, two, two)[0] == FQ(0)
|
||||
assert linefunc(one, two, three)[0] != FQ(0)
|
||||
assert linefunc(one, two, negthree)[0] == FQ(0)
|
||||
assert linefunc(one, negone, one)[0] == FQ(0)
|
||||
assert linefunc(one, negone, negone)[0] == FQ(0)
|
||||
assert linefunc(one, negone, two)[0] != FQ(0)
|
||||
assert linefunc(one, one, one)[0] == FQ(0)
|
||||
assert linefunc(one, one, two)[0] != FQ(0)
|
||||
assert linefunc(one, one, negtwo)[0] == FQ(0)
|
||||
|
||||
# Main miller loop
|
||||
def miller_loop(Q, P, final_exponentiate=True):
|
||||
if Q is None or P is None:
|
||||
return FQ12.one()
|
||||
R = Q
|
||||
f_num, f_den = FQ12.one(), FQ12.one()
|
||||
for b in pseudo_binary_encoding[63::-1]:
|
||||
#for i in range(log_ate_loop_count, -1, -1):
|
||||
_n, _d = linefunc(R, R, P)
|
||||
f_num = f_num * f_num * _n
|
||||
f_den = f_den * f_den * _d
|
||||
R = double(R)
|
||||
#if ate_loop_count & (2**i):
|
||||
if b == 1:
|
||||
_n, _d = linefunc(R, Q, P)
|
||||
f_num = f_num * _n
|
||||
f_den = f_den * _d
|
||||
R = add(R, Q)
|
||||
elif b == -1:
|
||||
nQ = neg(Q)
|
||||
_n, _d = linefunc(R, nQ, P)
|
||||
f_num = f_num * _n
|
||||
f_den = f_den * _d
|
||||
R = add(R, nQ)
|
||||
# assert R == multiply(Q, ate_loop_count)
|
||||
Q1 = (Q[0] ** field_modulus, Q[1] ** field_modulus, Q[2] ** field_modulus)
|
||||
# assert is_on_curve(Q1, b12)
|
||||
nQ2 = (Q1[0] ** field_modulus, -Q1[1] ** field_modulus, Q1[2] ** field_modulus)
|
||||
# assert is_on_curve(nQ2, b12)
|
||||
_n1, _d1 = linefunc(R, Q1, P)
|
||||
R = add(R, Q1)
|
||||
_n2, _d2 = linefunc(R, nQ2, P)
|
||||
f = f_num * _n1 * _n2 / (f_den * _d1 * _d2)
|
||||
# R = add(R, nQ2) This line is in many specifications but it technically does nothing
|
||||
if final_exponentiate:
|
||||
return f ** ((field_modulus ** 12 - 1) // curve_order)
|
||||
else:
|
||||
return f
|
||||
|
||||
# Pairing computation
|
||||
def pairing(Q, P, final_exponentiate=True):
|
||||
assert is_on_curve(Q, b2)
|
||||
assert is_on_curve(P, b)
|
||||
if P[-1] == P[-1].__class__.zero() or Q[-1] == Q[-1].__class__.zero():
|
||||
return FQ12.one()
|
||||
return miller_loop(twist(Q), cast_point_to_fq12(P), final_exponentiate=final_exponentiate)
|
||||
|
||||
def final_exponentiate(p):
|
||||
return p ** ((field_modulus ** 12 - 1) // curve_order)
|
@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
with open('README.md') as f:
|
||||
readme = f.read()
|
||||
|
||||
with open('LICENSE') as f:
|
||||
license = f.read()
|
||||
|
||||
setup(
|
||||
name='py_pairing',
|
||||
version='0.0.1',
|
||||
description='Optimal ate pairings over alt_bn128',
|
||||
long_description=readme,
|
||||
author='Vitalik Buterin',
|
||||
author_email='',
|
||||
url='https://github.com/ethereum/research',
|
||||
license=license,
|
||||
packages=find_packages(exclude=('tests', 'docs')),
|
||||
install_requires=[
|
||||
'ethereum == 1.3.7',
|
||||
'nose',
|
||||
],
|
||||
)
|
@ -1,29 +0,0 @@
|
||||
from py_pairing.optimized_pairing import pairing, neg, G2, G1, multiply, FQ12, curve_order
|
||||
# from bn128_pairing import pairing, neg, G2, G1, multiply, FQ12, curve_order
|
||||
import time
|
||||
|
||||
a = time.time()
|
||||
print('Starting tests')
|
||||
p1 = pairing(G2, G1)
|
||||
pn1 = pairing(G2, neg(G1))
|
||||
assert p1 * pn1 == FQ12.one()
|
||||
print('Pairing check against negative in G1 passed')
|
||||
np1 = pairing(neg(G2), G1)
|
||||
assert p1 * np1 == FQ12.one()
|
||||
assert pn1 == np1
|
||||
print('Pairing check against negative in G2 passed')
|
||||
assert p1 ** curve_order == FQ12.one()
|
||||
print('Pairing output has correct order')
|
||||
p2 = pairing(G2, multiply(G1, 2))
|
||||
assert p1 * p1 == p2
|
||||
print('Pairing bilinearity in G1 passed')
|
||||
assert p1 != p2 and p1 != np1 and p2 != np1
|
||||
print('Pairing is non-degenerate')
|
||||
po2 = pairing(multiply(G2, 2), G1)
|
||||
assert p1 * p1 == po2
|
||||
print('Pairing bilinearity in G2 passed')
|
||||
p3 = pairing(multiply(G2, 27), multiply(G1, 37))
|
||||
po3 = pairing(G2, multiply(G1, 999))
|
||||
assert p3 == po3
|
||||
print('Composite check passed')
|
||||
print('Total time: %.3f' % (time.time() - a))
|
Loading…
x
Reference in New Issue
Block a user