From 0447493f6c858ab0f288382f89e7187b2e330b23 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Sun, 13 Jan 2019 20:24:15 +0530 Subject: [PATCH 01/12] Remove Fields Redundancy --- py_ecc/fields/__init__.py | 13 + py_ecc/fields/field_elements.py | 355 +++++++++++++++++++ py_ecc/fields/field_properties.py | 28 ++ py_ecc/fields/optimized_field_elements.py | 395 ++++++++++++++++++++++ py_ecc/utils.py | 54 +++ setup.py | 2 + tests/test_field_elements.py | 67 ++++ 7 files changed, 914 insertions(+) create mode 100644 py_ecc/fields/__init__.py create mode 100644 py_ecc/fields/field_elements.py create mode 100644 py_ecc/fields/field_properties.py create mode 100644 py_ecc/fields/optimized_field_elements.py create mode 100644 py_ecc/utils.py create mode 100644 tests/test_field_elements.py diff --git a/py_ecc/fields/__init__.py b/py_ecc/fields/__init__.py new file mode 100644 index 00000000..3c3813f0 --- /dev/null +++ b/py_ecc/fields/__init__.py @@ -0,0 +1,13 @@ +from .field_elements import ( # noqa: F401 + FQ, + FQP, + FQ2, + FQ12, +) + +from .optimized_field_elements import ( # noqa: F401 + FQ as optimized_FQ, + FQP as optimized_FQP, + FQ2 as optimized_FQ2, + FQ12 as optimized_FQ12, +) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py new file mode 100644 index 00000000..e0a7160f --- /dev/null +++ b/py_ecc/fields/field_elements.py @@ -0,0 +1,355 @@ +from typing import ( + cast, + List, + Sequence, + Union, +) + + +from py_ecc.fields.field_properties import ( + field_properties, +) + +from py_ecc.utils import ( + deg, + poly_rounded_div, + prime_field_inv, +) + + +IntOrFQ = Union[int, "FQ"] + + +class FQ(object): + """ + A class for field elements in FQ. Wrap a number in this class, + and it becomes a field element. + """ + n = None # type: int + field_modulus = None + curve_name = None + + def __init__(self, val: IntOrFQ, curve_name: str) -> None: + """ + curve_name can be either 'bn128' or 'bls12_381' + This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS + and FQ12_MODULUS_COEFFS from the curve properties + """ + self.curve_name = curve_name + self.field_modulus = field_properties[curve_name]["field_modulus"] + + if isinstance(val, FQ): + self.n = val.n + elif isinstance(val, int): + self.n = val % self.field_modulus + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(val)) + ) + + def __add__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n + on) % self.field_modulus, self.curve_name) + + def __mul__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n * on) % self.field_modulus, self.curve_name) + + def __rmul__(self, other: IntOrFQ) -> "FQ": + return self * other + + def __radd__(self, other: IntOrFQ) -> "FQ": + return self + other + + def __rsub__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((on - self.n) % self.field_modulus, self.curve_name) + + def __sub__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n - on) % self.field_modulus, self.curve_name) + + def __div__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ( + self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus, + self.curve_name + ) + + def __truediv__(self, other: IntOrFQ) -> "FQ": + return self.__div__(other) + + def __rdiv__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ( + prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus, + self.curve_name + ) + + def __rtruediv__(self, other: IntOrFQ) -> "FQ": + return self.__rdiv__(other) + + def __pow__(self, other: int) -> "FQ": + if other == 0: + return FQ(1, self.curve_name) + elif other == 1: + return FQ(self.n, self.curve_name) + elif other % 2 == 0: + return (self * self) ** (other // 2) + else: + return ((self * self) ** int(other // 2)) * self + + def __eq__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + if isinstance(other, FQ): + return self.n == other.n + elif isinstance(other, int): + return self.n == other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + def __ne__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + return not self == other + + def __neg__(self) -> "FQ": + return FQ(-self.n, self.curve_name) + + def __repr__(self) -> str: + return repr(self.n) + + def __int__(self) -> int: + return self.n + + @classmethod + def one(cls, curve_name: str) -> "FQ": + return cls(1, curve_name) + + @classmethod + def zero(cls, curve_name: str) -> "FQ": + return cls(0, curve_name) + + +int_types_or_FQ = (int, FQ) + + +class FQP(object): + """ + A class for elements in polynomial extension fields + """ + degree = 0 + curve_name = None + + def __init__(self, + coeffs: Sequence[IntOrFQ], + curve_name: str, + modulus_coeffs: Sequence[IntOrFQ]=None) -> None: + if len(coeffs) != len(modulus_coeffs): + raise Exception( + "coeffs and modulus_coeffs aren't of the same length" + ) + self.coeffs = tuple(FQ(c, curve_name) for c in coeffs) + self.curve_name = curve_name + # 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: "FQP") -> "FQP": + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + return type(self)([x + y for x, y in zip(self.coeffs, other.coeffs)], self.curve_name) + + def __sub__(self, other: "FQP") -> "FQP": + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + return type(self)([x - y for x, y in zip(self.coeffs, other.coeffs)], self.curve_name) + + def __mul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + if isinstance(other, int) or isinstance(other, FQ): + return type(self)([c * other for c in self.coeffs], self.curve_name) + elif isinstance(other, FQP): + b = [FQ(0, self.curve_name) 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], self.curve_name) + return type(self)(b, self.curve_name) + else: + raise ValueError( + "Expected an int or FQ object or FQP object, but got object of type {}" + .format(type(other)) + ) + + def __rmul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + return self * other + + def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + if isinstance(other, int_types_or_FQ): + return type(self)([c / other for c in self.coeffs], self.curve_name) + elif isinstance(other, FQP): + return self * other.inv() + else: + raise ValueError( + "Expected an int or FQ object or FQP object, but got object of type {}" + .format(type(other)) + ) + + def __truediv__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + return self.__div__(other) + + def __pow__(self, other: int) -> "FQP": + if other == 0: + return type(self)([1] + [0] * (self.degree - 1), self.curve_name) + elif other == 1: + return type(self)(self.coeffs, self.curve_name) + 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) -> "FQP": + lm, hm = ( + [1] + [0] * self.degree, + [0] * (self.degree + 1), + ) + low, high = ( + # Ignore mypy yelling about the inner types for the tuples being incompatible + cast(List[IntOrFQ], list(self.coeffs + (0,))), # type: ignore + cast(List[IntOrFQ], list(self.modulus_coeffs + (1,))), # type: ignore + ) + while deg(low): + r = cast(List[IntOrFQ], list(poly_rounded_div(high, low))) + r += [0] * (self.degree + 1 - len(r)) + nm = [x for x in hm] + new = [x for x in high] + if len(set( + [len(lm), len(hm), len(low), len(high), len(nm), len(new), self.degree + 1] + )) != 1: + raise Exception("Mismatch between the lengths of lm, hm, low, high, nm, new") + + for i in range(self.degree + 1): + for j in range(self.degree + 1 - i): + nm[i + j] -= lm[i] * int(r[j]) + new[i + j] -= low[i] * int(r[j]) + lm, low, hm, high = nm, new, lm, low + return type(self)(lm[:self.degree], self.curve_name) / low[0] + + def __repr__(self) -> str: + return repr(self.coeffs) + + def __eq__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + for c1, c2 in zip(self.coeffs, other.coeffs): + if c1 != c2: + return False + return True + + def __ne__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + return not self == other + + def __neg__(self) -> "FQP": + return type(self)([-c for c in self.coeffs], self.curve_name) + + @classmethod + def one(cls, curve_name: str) -> "FQP": + return cls([1] + [0] * (cls.degree - 1), curve_name) + + @classmethod + def zero(cls, curve_name: str) -> "FQP": + return cls([0] * cls.degree, curve_name) + + +class FQ2(FQP): + """ + The quadratic extension field + """ + degree = 2 + + def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: + FQ2_MODULUS_COEFFS = field_properties[curve_name]["fq2_modulus_coeffs"] + super().__init__(coeffs, curve_name, FQ2_MODULUS_COEFFS) + + +class FQ12(FQP): + """ + The 12th-degree extension field + """ + degree = 12 + + def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: + FQ12_MODULUS_COEFFS = field_properties[curve_name]["fq12_modulus_coeffs"] + super().__init__(coeffs, curve_name, FQ12_MODULUS_COEFFS) diff --git a/py_ecc/fields/field_properties.py b/py_ecc/fields/field_properties.py new file mode 100644 index 00000000..3ca362e8 --- /dev/null +++ b/py_ecc/fields/field_properties.py @@ -0,0 +1,28 @@ +from typing import ( + Dict, + Tuple, +) + +from mypy_extensions import TypedDict + + +Curve_Field_Properties = TypedDict('Curve_Field_Properties', + { + 'field_modulus': int, + 'fq2_modulus_coeffs': Tuple[int, ...], + 'fq12_modulus_coeffs': Tuple[int, ...], + }) +Field_Properties = Dict[str, Curve_Field_Properties] + +field_properties = { + "bn128": { + "field_modulus": 21888242871839275222246405745257275088696311157297823662689037894645226208583, # noqa: E501 + "fq2_modulus_coeffs": (1, 0), + "fq12_modulus_coeffs": (82, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0), # Implied + [1] + }, + "bls12_381": { + "field_modulus": 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787, # noqa: E501 + "fq2_modulus_coeffs": (1, 0), + "fq12_modulus_coeffs": (2, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0), # Implied + [1] + }, +} # type: Field_Properties diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py new file mode 100644 index 00000000..25b55b26 --- /dev/null +++ b/py_ecc/fields/optimized_field_elements.py @@ -0,0 +1,395 @@ +from typing import ( # noqa: F401 + cast, + List, + Sequence, + Tuple, + Union, +) + + +from py_ecc.fields.field_properties import ( + field_properties, +) + +from py_ecc.utils import ( + deg, + prime_field_inv, +) + + +IntOrFQ = Union[int, "FQ"] + + +class FQ(object): + """ + A class for field elements in FQ. Wrap a number in this class, + and it becomes a field element. + """ + n = None # type: int + field_modulus = None + curve_name = None + + def __init__(self, val: IntOrFQ, curve_name: str) -> None: + """ + curve_name can be either 'bn128' or 'bls12_381' + This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS + and FQ12_MODULUS_COEFFS from the curve properties + """ + self.curve_name = curve_name + self.field_modulus = field_properties[curve_name]["field_modulus"] + + if isinstance(val, FQ): + self.n = val.n + elif isinstance(val, int): + self.n = val % self.field_modulus + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(val)) + ) + + def __add__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n + on) % self.field_modulus, self.curve_name) + + def __mul__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n * on) % self.field_modulus, self.curve_name) + + def __rmul__(self, other: IntOrFQ) -> "FQ": + return self * other + + def __radd__(self, other: IntOrFQ) -> "FQ": + return self + other + + def __rsub__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((on - self.n) % self.field_modulus, self.curve_name) + + def __sub__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ((self.n - on) % self.field_modulus, self.curve_name) + + def __mod__(self, other: Union[int, "FQ"]) -> "FQ": + return self.__mod__(other) + + def __div__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ( + self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus, + self.curve_name + ) + + def __truediv__(self, other: IntOrFQ) -> "FQ": + return self.__div__(other) + + def __rdiv__(self, other: IntOrFQ) -> "FQ": + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + return FQ( + prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus, + self.curve_name + ) + + def __rtruediv__(self, other: IntOrFQ) -> "FQ": + return self.__rdiv__(other) + + def __pow__(self, other: int) -> "FQ": + if other == 0: + return FQ(1, self.curve_name) + elif other == 1: + return FQ(self.n, self.curve_name) + elif other % 2 == 0: + return (self * self) ** (other // 2) + else: + return ((self * self) ** int(other // 2)) * self + + def __eq__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + if isinstance(other, FQ): + return self.n == other.n + elif isinstance(other, int): + return self.n == other + else: + raise ValueError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + + def __ne__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + return not self == other + + def __neg__(self) -> "FQ": + return FQ(-self.n, self.curve_name) + + def __repr__(self) -> str: + return repr(self.n) + + def __int__(self) -> int: + return self.n + + @classmethod + def one(cls, curve_name: str) -> "FQ": + return cls(1, curve_name) + + @classmethod + def zero(cls, curve_name: str) -> "FQ": + return cls(0, curve_name) + + +class FQP(object): + """ + A class for elements in polynomial extension fields + """ + degree = 0 # type: int + mc_tuples = None # type: List[Tuple[int, int]] + curve_name = None + field_modulus = None + + def __init__(self, + coeffs: Sequence[IntOrFQ], + curve_name: str, + modulus_coeffs: Sequence[IntOrFQ]=None) -> None: + """ + curve_name can be either 'bn128' or 'bls12_381' + This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS + and FQ12_MODULUS_COEFFS from the curve properties + """ + self.curve_name = curve_name + self.field_modulus = field_properties[curve_name]["field_modulus"] + + if len(coeffs) != len(modulus_coeffs): + raise Exception( + "coeffs and modulus_coeffs aren't of the same length" + ) + self.coeffs = tuple(coeffs) + # The coefficients of the modulus, without the leading [1] + self.modulus_coeffs = tuple(modulus_coeffs) + # The degree of the extension field + self.degree = len(self.modulus_coeffs) + + def __add__(self, other: "FQP") -> "FQP": + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + return type(self)([ + int(x + y) % self.field_modulus + for x, y + in zip(self.coeffs, other.coeffs) + ], self.curve_name) + + def __sub__(self, other: "FQP") -> "FQP": + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + return type(self)([ + int(x - y) % self.field_modulus + for x, y + in zip(self.coeffs, other.coeffs) + ], self.curve_name) + + def __mod__(self, other: Union[int, "FQP"]) -> "FQP": + return self.__mod__(other) + + def __mul__(self, other: Union[int, "FQP"]) -> "FQP": + if isinstance(other, int): + return type(self)([ + int(c) * other % self.field_modulus + for c + in self.coeffs + ], self.curve_name) + elif isinstance(other, FQP): + 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] += int(eli * elj) + # MID = len(self.coeffs) // 2 + 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 type(self)([x % self.field_modulus for x in b], self.curve_name) + else: + raise ValueError( + "Expected an int or FQP object, but got object of type {}" + .format(type(other)) + ) + + def __rmul__(self, other: Union[int, "FQP"]) -> "FQP": + return self * other + + def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + if isinstance(other, int): + return type(self)([ + int(c) * prime_field_inv(other, self.field_modulus) % self.field_modulus + for c + in self.coeffs + ], self.curve_name) + elif isinstance(other, type(self)): + return self * other.inv() + else: + raise ValueError( + "Expected an int or FQP object, but got object of type {}" + .format(type(other)) + ) + + def __truediv__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": + return self.__div__(other) + + def __pow__(self, other: int) -> "FQP": + o = type(self)([1] + [0] * (self.degree - 1), self.curve_name) + t = self + while other > 0: + if other & 1: + o = o * t + other >>= 1 + t = t * t + return o + + def optimized_poly_rounded_div(self, + a: Sequence[IntOrFQ], + b: Sequence[IntOrFQ]) -> Sequence[IntOrFQ]: + 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] = int(o[i] + temp[degb + i] * prime_field_inv(int(b[degb]), self.field_modulus)) + for c in range(degb + 1): + temp[c + i] = (temp[c + i] - o[c]) + return [x % self.field_modulus for x in o[:deg(o) + 1]] + + # Extended euclidean algorithm used to find the modular inverse + def inv(self) -> "FQP": + lm, hm = [1] + [0] * self.degree, [0] * (self.degree + 1) + low, high = ( + cast(List[IntOrFQ], list(self.coeffs + (0,))), + cast(List[IntOrFQ], list(self.modulus_coeffs + (1,))), + ) + low, high = list(self.coeffs + (0,)), self.modulus_coeffs + (1,) # type: ignore + while deg(low): + r = cast(List[IntOrFQ], list(self.optimized_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 # noqa: E501 + for i in range(self.degree + 1): + for j in range(self.degree + 1 - i): + nm[i + j] -= lm[i] * int(r[j]) + new[i + j] -= low[i] * r[j] + nm = [x % self.field_modulus for x in nm] + new = [int(x) % self.field_modulus for x in new] + lm, low, hm, high = nm, new, lm, low + return type(self)(lm[:self.degree], self.curve_name) / low[0] + + def __repr__(self) -> str: + return repr(self.coeffs) + + def __eq__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + if not isinstance(other, type(self)): + raise ValueError( + "Expected an FQP object, but got object of type {}" + .format(type(other)) + ) + + for c1, c2 in zip(self.coeffs, other.coeffs): + if c1 != c2: + return False + return True + + def __ne__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 + return not self == other + + def __neg__(self) -> "FQP": + return type(self)([-c for c in self.coeffs], self.curve_name) + + @classmethod + def one(cls, curve_name: str) -> "FQP": + return cls([1] + [0] * (cls.degree - 1), curve_name) + + @classmethod + def zero(cls, curve_name: str) -> "FQP": + return cls([0] * cls.degree, curve_name) + + +class FQ2(FQP): + """ + The quadratic extension field + """ + degree = 2 + + def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: + FQ2_MODULUS_COEFFS = field_properties[curve_name]["fq2_modulus_coeffs"] + self.mc_tuples = [(i, c) for i, c in enumerate(FQ2_MODULUS_COEFFS) if c] + super().__init__(coeffs, curve_name, FQ2_MODULUS_COEFFS) + + +class FQ12(FQP): + """ + The 12th-degree extension field + """ + degree = 12 + + def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: + FQ12_MODULUS_COEFFS = field_properties[curve_name]["fq12_modulus_coeffs"] + self.mc_tuples = [(i, c) for i, c in enumerate(FQ12_MODULUS_COEFFS) if c] + super().__init__(coeffs, curve_name, FQ12_MODULUS_COEFFS) diff --git a/py_ecc/utils.py b/py_ecc/utils.py new file mode 100644 index 00000000..b1eb6eff --- /dev/null +++ b/py_ecc/utils.py @@ -0,0 +1,54 @@ +from typing import ( + cast, + Tuple, + Sequence, + Union, + TYPE_CHECKING, +) + +if TYPE_CHECKING: + from py_ecc.field_elements import ( # noqa: F401 + FQ, + ) + from py_ecc.optimized_field_elements import ( # noqa: F401 + FQ as optimized_FQ, + ) + + +IntOrFQ = Union[int, "FQ"] + + +def prime_field_inv(a: int, n: int) -> int: + """ + Extended euclidean algorithm to find modular inverses for integers + """ + 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: Sequence[Union[int, "FQ", "optimized_FQ"]]) -> int: + d = len(p) - 1 + while p[d] == 0 and d: + d -= 1 + return d + + +def poly_rounded_div(a: Sequence[IntOrFQ], + b: Sequence[IntOrFQ]) -> Tuple[IntOrFQ]: + 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] += int(temp[degb + i] / b[degb]) + for c in range(degb + 1): + temp[c + i] -= o[c] + return cast(Tuple[IntOrFQ], tuple(o[:deg(o) + 1])) diff --git a/setup.py b/setup.py index b5879966..a4458a25 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ 'lint': [ "flake8==3.4.1", "mypy==0.641", + "mypy-extensions>=0.4.1", ], 'dev': [ "bumpversion>=0.5.3,<1", @@ -44,6 +45,7 @@ packages=find_packages(exclude=('tests', 'docs')), package_data={'py_ecc': ['py.typed']}, install_requires=[ + "mypy-extensions>=0.4.1", ], python_requires='>=3.5, <4', extras_require=extras_require, diff --git a/tests/test_field_elements.py b/tests/test_field_elements.py new file mode 100644 index 00000000..be77506d --- /dev/null +++ b/tests/test_field_elements.py @@ -0,0 +1,67 @@ +import pytest + +from py_ecc.fields import ( + field_elements, + optimized_field_elements, +) +from py_ecc.fields.field_properties import ( + field_properties, +) + + +# Tests both field_elements and optimized_field_elements +@pytest.fixture(params=[field_elements, optimized_field_elements]) +def lib(request): + return request.param + + +@pytest.fixture +def FQ(lib): + return lib.FQ + + +@pytest.fixture +def FQ2(lib): + return lib.FQ2 + + +@pytest.fixture +def FQ12(lib): + return lib.FQ12 + + +def test_FQ_object(FQ): + for curve_name in ("bn128", "bls12_381"): + field_modulus = field_properties[curve_name]["field_modulus"] + assert FQ(2, curve_name) * FQ(2, curve_name) == FQ(4, curve_name) + assert FQ(2, curve_name) / FQ(7, curve_name) + FQ(9, curve_name) / FQ(7, curve_name) == FQ(11, curve_name) / FQ(7, curve_name) + assert FQ(2, curve_name) * FQ(7, curve_name) + FQ(9, curve_name) * FQ(7, curve_name) == FQ(11, curve_name) * FQ(7, curve_name) + assert FQ(9, curve_name) ** field_modulus == FQ(9, curve_name) + + +def test_FQ2_object(FQ2): + for curve_name in ("bn128", "bls12_381"): + field_modulus = field_properties[curve_name]["field_modulus"] + x = FQ2([1, 0], curve_name) + f = FQ2([1, 2], curve_name) + fpx = FQ2([2, 2], curve_name) + one = FQ2.one(curve_name) + 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 + + +def test_FQ12_object(FQ12): + for curve_name in ("bn128", "bls12_381"): + x = FQ12([1] + [0] * 11, curve_name) + f = FQ12([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], curve_name) + fpx = FQ12([2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], curve_name) + one = FQ12.one(curve_name) + 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 From 08eb068d9c4440300953a9ef010467c54bd5a551 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Tue, 22 Jan 2019 21:50:37 +0530 Subject: [PATCH 02/12] Change ValueError to TypeError wherever apt --- py_ecc/fields/field_elements.py | 26 +++++++++++------------ py_ecc/fields/optimized_field_elements.py | 26 +++++++++++------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index e0a7160f..046cd612 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -43,7 +43,7 @@ def __init__(self, val: IntOrFQ, curve_name: str) -> None: elif isinstance(val, int): self.n = val % self.field_modulus else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(val)) ) @@ -54,7 +54,7 @@ def __add__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -67,7 +67,7 @@ def __mul__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -86,7 +86,7 @@ def __rsub__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -99,7 +99,7 @@ def __sub__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -112,7 +112,7 @@ def __div__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -131,7 +131,7 @@ def __rdiv__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -160,7 +160,7 @@ def __eq__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/py elif isinstance(other, int): return self.n == other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -213,7 +213,7 @@ def __init__(self, def __add__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) @@ -222,7 +222,7 @@ def __add__(self, other: "FQP") -> "FQP": def __sub__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) @@ -243,7 +243,7 @@ def __mul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": b[exp + i] -= top * FQ(self.modulus_coeffs[i], self.curve_name) return type(self)(b, self.curve_name) else: - raise ValueError( + raise TypeError( "Expected an int or FQ object or FQP object, but got object of type {}" .format(type(other)) ) @@ -257,7 +257,7 @@ def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": elif isinstance(other, FQP): return self * other.inv() else: - raise ValueError( + raise TypeError( "Expected an int or FQ object or FQP object, but got object of type {}" .format(type(other)) ) @@ -308,7 +308,7 @@ def __repr__(self) -> str: def __eq__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 25b55b26..59119b83 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -43,7 +43,7 @@ def __init__(self, val: IntOrFQ, curve_name: str) -> None: elif isinstance(val, int): self.n = val % self.field_modulus else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(val)) ) @@ -54,7 +54,7 @@ def __add__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -67,7 +67,7 @@ def __mul__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -86,7 +86,7 @@ def __rsub__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -99,7 +99,7 @@ def __sub__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -115,7 +115,7 @@ def __div__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -134,7 +134,7 @@ def __rdiv__(self, other: IntOrFQ) -> "FQ": elif isinstance(other, int): on = other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -163,7 +163,7 @@ def __eq__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/py elif isinstance(other, int): return self.n == other else: - raise ValueError( + raise TypeError( "Expected an int or FQ object, but got object of type {}" .format(type(other)) ) @@ -222,7 +222,7 @@ def __init__(self, def __add__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) @@ -235,7 +235,7 @@ def __add__(self, other: "FQP") -> "FQP": def __sub__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) @@ -269,7 +269,7 @@ def __mul__(self, other: Union[int, "FQP"]) -> "FQP": b[exp + i] -= top * c return type(self)([x % self.field_modulus for x in b], self.curve_name) else: - raise ValueError( + raise TypeError( "Expected an int or FQP object, but got object of type {}" .format(type(other)) ) @@ -287,7 +287,7 @@ def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": elif isinstance(other, type(self)): return self * other.inv() else: - raise ValueError( + raise TypeError( "Expected an int or FQP object, but got object of type {}" .format(type(other)) ) @@ -346,7 +346,7 @@ def __repr__(self) -> str: def __eq__(self, other: "FQP") -> bool: # type: ignore # https://github.com/python/mypy/issues/2783 # noqa: E501 if not isinstance(other, type(self)): - raise ValueError( + raise TypeError( "Expected an FQP object, but got object of type {}" .format(type(other)) ) From 28c0a9ddc376e8a745128d2bd774d33748600743 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Tue, 22 Jan 2019 22:32:24 +0530 Subject: [PATCH 03/12] Add minor fixes and adjustments --- py_ecc/fields/field_elements.py | 4 ++-- py_ecc/fields/optimized_field_elements.py | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index 046cd612..2df2da53 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -207,7 +207,7 @@ def __init__(self, self.coeffs = tuple(FQ(c, curve_name) for c in coeffs) self.curve_name = curve_name # The coefficients of the modulus, without the leading [1] - self.modulus_coeffs = modulus_coeffs + self.modulus_coeffs = tuple(modulus_coeffs) # The degree of the extension field self.degree = len(self.modulus_coeffs) @@ -230,7 +230,7 @@ def __sub__(self, other: "FQP") -> "FQP": return type(self)([x - y for x, y in zip(self.coeffs, other.coeffs)], self.curve_name) def __mul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": - if isinstance(other, int) or isinstance(other, FQ): + if isinstance(other, int_types_or_FQ): return type(self)([c * other for c in self.coeffs], self.curve_name) elif isinstance(other, FQP): b = [FQ(0, self.curve_name) for i in range(self.degree * 2 - 1)] diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 59119b83..311ff86a 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -207,14 +207,14 @@ def __init__(self, This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS and FQ12_MODULUS_COEFFS from the curve properties """ - self.curve_name = curve_name - self.field_modulus = field_properties[curve_name]["field_modulus"] - if len(coeffs) != len(modulus_coeffs): raise Exception( "coeffs and modulus_coeffs aren't of the same length" ) + # Not converting coeffs to FQ or explicitly making them integers for performance reasons self.coeffs = tuple(coeffs) + self.curve_name = curve_name + self.field_modulus = field_properties[curve_name]["field_modulus"] # The coefficients of the modulus, without the leading [1] self.modulus_coeffs = tuple(modulus_coeffs) # The degree of the extension field @@ -325,7 +325,6 @@ def inv(self) -> "FQP": cast(List[IntOrFQ], list(self.coeffs + (0,))), cast(List[IntOrFQ], list(self.modulus_coeffs + (1,))), ) - low, high = list(self.coeffs + (0,)), self.modulus_coeffs + (1,) # type: ignore while deg(low): r = cast(List[IntOrFQ], list(self.optimized_poly_rounded_div(high, low))) r += [0] * (self.degree + 1 - len(r)) From 5e5e734acc9ff7219a592f90821578f69f518fad Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Tue, 22 Jan 2019 22:39:33 +0530 Subject: [PATCH 04/12] Replace modulo operation recursion with NotImplementedError --- py_ecc/fields/optimized_field_elements.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 311ff86a..1a491448 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -106,8 +106,8 @@ def __sub__(self, other: IntOrFQ) -> "FQ": return FQ((self.n - on) % self.field_modulus, self.curve_name) - def __mod__(self, other: Union[int, "FQ"]) -> "FQ": - return self.__mod__(other) + def __mod__(self, other: IntOrFQ) -> "FQ": + raise NotImplementedError("Modulo Operation not yet supported by fields") def __div__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -247,7 +247,7 @@ def __sub__(self, other: "FQP") -> "FQP": ], self.curve_name) def __mod__(self, other: Union[int, "FQP"]) -> "FQP": - return self.__mod__(other) + raise NotImplementedError("Modulo Operation not yet supported by fields") def __mul__(self, other: Union[int, "FQP"]) -> "FQP": if isinstance(other, int): From fd7ef53c5c66a35efc41dbbe15e614570b881852 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Fri, 25 Jan 2019 17:46:18 +0530 Subject: [PATCH 05/12] Improve readability by avoiding passing curve_name in all methods --- py_ecc/fields/__init__.py | 59 +++++++++++- py_ecc/fields/field_elements.py | 109 ++++++++++++---------- py_ecc/fields/optimized_field_elements.py | 103 ++++++++++---------- tests/test_bn128_and_bls12_381.py | 55 ++++++++++- 4 files changed, 219 insertions(+), 107 deletions(-) diff --git a/py_ecc/fields/__init__.py b/py_ecc/fields/__init__.py index 3c3813f0..4f169a5f 100644 --- a/py_ecc/fields/__init__.py +++ b/py_ecc/fields/__init__.py @@ -1,13 +1,68 @@ -from .field_elements import ( # noqa: F401 +from .field_elements import ( FQ, FQP, FQ2, FQ12, ) -from .optimized_field_elements import ( # noqa: F401 +from .optimized_field_elements import ( FQ as optimized_FQ, FQP as optimized_FQP, FQ2 as optimized_FQ2, FQ12 as optimized_FQ12, ) + + +# Create seperate classes for all Fields for each curve + +bn128_FQ = type("bn128_FQ", (FQ,), {'curve_name': "bn128"}) +bn128_FQP = type("bn128_FQP", (FQP,), {'curve_name': "bn128"}) +bn128_FQ2 = type("bn128_FQ2", (FQ2,), {'curve_name': "bn128"}) +bn128_FQ12 = type("bn128_FQ12", (FQ12,), {'curve_name': "bn128"}) + +bls12_381_FQ = type("bls12_381_FQ", (FQ,), {'curve_name': "bls12_381"}) +bls12_381_FQP = type("bls12_381_FQP", (FQP,), {'curve_name': "bls12_381"}) +bls12_381_FQ2 = type("bls12_381_FQ2", (FQ2,), {'curve_name': "bls12_381"}) +bls12_381_FQ12 = type("bls12_381_FQ12", (FQ12,), {'curve_name': "bls12_381"}) + +optimized_bn128_FQ = type( + "optimized_bn128_FQ", + (optimized_FQ,), + {'curve_name': "bn128"} +) +optimized_bn128_FQP = type( + "optimized_bn128_FQP", + (optimized_FQP,), + {'curve_name': "bn128"} +) +optimized_bn128_FQ2 = type( + "optimized_bn128_FQ2", + (optimized_FQ2,), + {'curve_name': "bn128"} +) +optimized_bn128_FQ12 = type( + "optimized_bn128_FQ12", + (optimized_FQ12,), + {'curve_name': "bn128"} +) + +optimized_bls12_381_FQ = type( + "optimized_bls12_381_FQ", + (optimized_FQ,), + {'curve_name': "bls12_381"} +) +optimized_bls12_381_FQP = type( + "optimized_bls12_381_FQP", + (optimized_FQP,), + {'curve_name': "bls12_381"} +) +optimized_bls12_381_FQ2 = type( + "optimized_bls12_381_FQ2", + (optimized_FQ2,), + {'curve_name': "bls12_381"} +) +optimized_bls12_381_FQ12 = type( + "optimized_bls12_381_FQ12", + (optimized_FQ12,), + {'curve_name': "bls12_381"} +) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index 2df2da53..23f6dc57 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -27,16 +27,16 @@ class FQ(object): """ n = None # type: int field_modulus = None + # curve_name can be either 'bn128' or 'bls12_381' + # This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS + # and FQ12_MODULUS_COEFFS from the curve properties curve_name = None - def __init__(self, val: IntOrFQ, curve_name: str) -> None: - """ - curve_name can be either 'bn128' or 'bls12_381' - This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS - and FQ12_MODULUS_COEFFS from the curve properties - """ - self.curve_name = curve_name - self.field_modulus = field_properties[curve_name]["field_modulus"] + def __init__(self, val: IntOrFQ) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + self.field_modulus = field_properties[self.curve_name]["field_modulus"] if isinstance(val, FQ): self.n = val.n @@ -59,7 +59,7 @@ def __add__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n + on) % self.field_modulus, self.curve_name) + return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -72,7 +72,7 @@ def __mul__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n * on) % self.field_modulus, self.curve_name) + return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": return self * other @@ -91,7 +91,7 @@ def __rsub__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((on - self.n) % self.field_modulus, self.curve_name) + return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -104,7 +104,7 @@ def __sub__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n - on) % self.field_modulus, self.curve_name) + return type(self)((self.n - on) % self.field_modulus) def __div__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -117,9 +117,8 @@ def __div__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ( - self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus, - self.curve_name + return type(self)( + self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) def __truediv__(self, other: IntOrFQ) -> "FQ": @@ -136,9 +135,8 @@ def __rdiv__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ( - prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus, - self.curve_name + return type(self)( + prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus ) def __rtruediv__(self, other: IntOrFQ) -> "FQ": @@ -146,9 +144,9 @@ def __rtruediv__(self, other: IntOrFQ) -> "FQ": def __pow__(self, other: int) -> "FQ": if other == 0: - return FQ(1, self.curve_name) + return type(self)(1) elif other == 1: - return FQ(self.n, self.curve_name) + return type(self)(self.n) elif other % 2 == 0: return (self * self) ** (other // 2) else: @@ -169,7 +167,7 @@ def __ne__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/ return not self == other def __neg__(self) -> "FQ": - return FQ(-self.n, self.curve_name) + return type(self)(-self.n) def __repr__(self) -> str: return repr(self.n) @@ -178,12 +176,12 @@ def __int__(self) -> int: return self.n @classmethod - def one(cls, curve_name: str) -> "FQ": - return cls(1, curve_name) + def one(cls) -> "FQ": + return cls(1) @classmethod - def zero(cls, curve_name: str) -> "FQ": - return cls(0, curve_name) + def zero(cls) -> "FQ": + return cls(0) int_types_or_FQ = (int, FQ) @@ -198,14 +196,21 @@ class FQP(object): def __init__(self, coeffs: Sequence[IntOrFQ], - curve_name: str, modulus_coeffs: Sequence[IntOrFQ]=None) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + if len(coeffs) != len(modulus_coeffs): raise Exception( "coeffs and modulus_coeffs aren't of the same length" ) - self.coeffs = tuple(FQ(c, curve_name) for c in coeffs) - self.curve_name = curve_name + # Encoding all coefficients in type FQ (in regards to the curve name too) + self.FQP_corresponding_FQ_class = type( + self.curve_name + "_FQ", + (FQ,), + {'curve_name': self.curve_name} + ) + self.coeffs = tuple(self.FQP_corresponding_FQ_class(c) for c in coeffs) # The coefficients of the modulus, without the leading [1] self.modulus_coeffs = tuple(modulus_coeffs) # The degree of the extension field @@ -218,7 +223,7 @@ def __add__(self, other: "FQP") -> "FQP": .format(type(other)) ) - return type(self)([x + y for x, y in zip(self.coeffs, other.coeffs)], self.curve_name) + return type(self)([x + y for x, y in zip(self.coeffs, other.coeffs)]) def __sub__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): @@ -227,21 +232,21 @@ def __sub__(self, other: "FQP") -> "FQP": .format(type(other)) ) - return type(self)([x - y for x, y in zip(self.coeffs, other.coeffs)], self.curve_name) + return type(self)([x - y for x, y in zip(self.coeffs, other.coeffs)]) def __mul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": if isinstance(other, int_types_or_FQ): - return type(self)([c * other for c in self.coeffs], self.curve_name) + return type(self)([c * other for c in self.coeffs]) elif isinstance(other, FQP): - b = [FQ(0, self.curve_name) for i in range(self.degree * 2 - 1)] + b = [self.FQP_corresponding_FQ_class(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], self.curve_name) - return type(self)(b, self.curve_name) + b[exp + i] -= top * self.FQP_corresponding_FQ_class(self.modulus_coeffs[i]) + return type(self)(b) else: raise TypeError( "Expected an int or FQ object or FQP object, but got object of type {}" @@ -253,7 +258,7 @@ def __rmul__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": if isinstance(other, int_types_or_FQ): - return type(self)([c / other for c in self.coeffs], self.curve_name) + return type(self)([c / other for c in self.coeffs]) elif isinstance(other, FQP): return self * other.inv() else: @@ -267,9 +272,9 @@ def __truediv__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": def __pow__(self, other: int) -> "FQP": if other == 0: - return type(self)([1] + [0] * (self.degree - 1), self.curve_name) + return type(self)([1] + [0] * (self.degree - 1)) elif other == 1: - return type(self)(self.coeffs, self.curve_name) + return type(self)(self.coeffs) elif other % 2 == 0: return (self * self) ** (other // 2) else: @@ -301,7 +306,7 @@ def inv(self) -> "FQP": nm[i + j] -= lm[i] * int(r[j]) new[i + j] -= low[i] * int(r[j]) lm, low, hm, high = nm, new, lm, low - return type(self)(lm[:self.degree], self.curve_name) / low[0] + return type(self)(lm[:self.degree]) / low[0] def __repr__(self) -> str: return repr(self.coeffs) @@ -322,15 +327,15 @@ def __ne__(self, other: "FQP") -> bool: # type: ignore # https://github.com/ return not self == other def __neg__(self) -> "FQP": - return type(self)([-c for c in self.coeffs], self.curve_name) + return type(self)([-c for c in self.coeffs]) @classmethod - def one(cls, curve_name: str) -> "FQP": - return cls([1] + [0] * (cls.degree - 1), curve_name) + def one(cls) -> "FQP": + return cls([1] + [0] * (cls.degree - 1)) @classmethod - def zero(cls, curve_name: str) -> "FQP": - return cls([0] * cls.degree, curve_name) + def zero(cls) -> "FQP": + return cls([0] * cls.degree) class FQ2(FQP): @@ -339,9 +344,12 @@ class FQ2(FQP): """ degree = 2 - def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: - FQ2_MODULUS_COEFFS = field_properties[curve_name]["fq2_modulus_coeffs"] - super().__init__(coeffs, curve_name, FQ2_MODULUS_COEFFS) + def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + FQ2_MODULUS_COEFFS = field_properties[self.curve_name]["fq2_modulus_coeffs"] + super().__init__(coeffs, FQ2_MODULUS_COEFFS) class FQ12(FQP): @@ -350,6 +358,9 @@ class FQ12(FQP): """ degree = 12 - def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: - FQ12_MODULUS_COEFFS = field_properties[curve_name]["fq12_modulus_coeffs"] - super().__init__(coeffs, curve_name, FQ12_MODULUS_COEFFS) + def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + FQ12_MODULUS_COEFFS = field_properties[self.curve_name]["fq12_modulus_coeffs"] + super().__init__(coeffs, FQ12_MODULUS_COEFFS) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 1a491448..f4e81b83 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -6,7 +6,6 @@ Union, ) - from py_ecc.fields.field_properties import ( field_properties, ) @@ -27,16 +26,16 @@ class FQ(object): """ n = None # type: int field_modulus = None + # curve_name can be either 'bn128' or 'bls12_381' + # This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS + # and FQ12_MODULUS_COEFFS from the curve properties curve_name = None - def __init__(self, val: IntOrFQ, curve_name: str) -> None: - """ - curve_name can be either 'bn128' or 'bls12_381' - This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS - and FQ12_MODULUS_COEFFS from the curve properties - """ - self.curve_name = curve_name - self.field_modulus = field_properties[curve_name]["field_modulus"] + def __init__(self, val: IntOrFQ) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + self.field_modulus = field_properties[self.curve_name]["field_modulus"] if isinstance(val, FQ): self.n = val.n @@ -59,7 +58,7 @@ def __add__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n + on) % self.field_modulus, self.curve_name) + return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -72,7 +71,7 @@ def __mul__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n * on) % self.field_modulus, self.curve_name) + return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": return self * other @@ -91,7 +90,7 @@ def __rsub__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((on - self.n) % self.field_modulus, self.curve_name) + return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": if isinstance(other, FQ): @@ -104,7 +103,7 @@ def __sub__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ((self.n - on) % self.field_modulus, self.curve_name) + return type(self)((self.n - on) % self.field_modulus) def __mod__(self, other: IntOrFQ) -> "FQ": raise NotImplementedError("Modulo Operation not yet supported by fields") @@ -120,9 +119,8 @@ def __div__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ( - self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus, - self.curve_name + return type(self)( + self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) def __truediv__(self, other: IntOrFQ) -> "FQ": @@ -139,9 +137,8 @@ def __rdiv__(self, other: IntOrFQ) -> "FQ": .format(type(other)) ) - return FQ( - prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus, - self.curve_name + return type(self)( + prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus ) def __rtruediv__(self, other: IntOrFQ) -> "FQ": @@ -149,9 +146,9 @@ def __rtruediv__(self, other: IntOrFQ) -> "FQ": def __pow__(self, other: int) -> "FQ": if other == 0: - return FQ(1, self.curve_name) + return type(self)(1) elif other == 1: - return FQ(self.n, self.curve_name) + return type(self)(self.n) elif other % 2 == 0: return (self * self) ** (other // 2) else: @@ -172,7 +169,7 @@ def __ne__(self, other: IntOrFQ) -> bool: # type:ignore # https://github.com/ return not self == other def __neg__(self) -> "FQ": - return FQ(-self.n, self.curve_name) + return type(self)(-self.n) def __repr__(self) -> str: return repr(self.n) @@ -181,12 +178,12 @@ def __int__(self) -> int: return self.n @classmethod - def one(cls, curve_name: str) -> "FQ": - return cls(1, curve_name) + def one(cls) -> "FQ": + return cls(1) @classmethod - def zero(cls, curve_name: str) -> "FQ": - return cls(0, curve_name) + def zero(cls) -> "FQ": + return cls(0) class FQP(object): @@ -200,21 +197,17 @@ class FQP(object): def __init__(self, coeffs: Sequence[IntOrFQ], - curve_name: str, modulus_coeffs: Sequence[IntOrFQ]=None) -> None: - """ - curve_name can be either 'bn128' or 'bls12_381' - This is needed to obtain field_modulus, FQ2_MODULUS_COEFFS - and FQ12_MODULUS_COEFFS from the curve properties - """ + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + if len(coeffs) != len(modulus_coeffs): raise Exception( "coeffs and modulus_coeffs aren't of the same length" ) # Not converting coeffs to FQ or explicitly making them integers for performance reasons self.coeffs = tuple(coeffs) - self.curve_name = curve_name - self.field_modulus = field_properties[curve_name]["field_modulus"] + self.field_modulus = field_properties[self.curve_name]["field_modulus"] # The coefficients of the modulus, without the leading [1] self.modulus_coeffs = tuple(modulus_coeffs) # The degree of the extension field @@ -231,7 +224,7 @@ def __add__(self, other: "FQP") -> "FQP": int(x + y) % self.field_modulus for x, y in zip(self.coeffs, other.coeffs) - ], self.curve_name) + ]) def __sub__(self, other: "FQP") -> "FQP": if not isinstance(other, type(self)): @@ -244,7 +237,7 @@ def __sub__(self, other: "FQP") -> "FQP": int(x - y) % self.field_modulus for x, y in zip(self.coeffs, other.coeffs) - ], self.curve_name) + ]) def __mod__(self, other: Union[int, "FQP"]) -> "FQP": raise NotImplementedError("Modulo Operation not yet supported by fields") @@ -255,7 +248,7 @@ def __mul__(self, other: Union[int, "FQP"]) -> "FQP": int(c) * other % self.field_modulus for c in self.coeffs - ], self.curve_name) + ]) elif isinstance(other, FQP): b = [0] * (self.degree * 2 - 1) inner_enumerate = list(enumerate(other.coeffs)) @@ -267,7 +260,7 @@ def __mul__(self, other: Union[int, "FQP"]) -> "FQP": top = b.pop() for i, c in self.mc_tuples: b[exp + i] -= top * c - return type(self)([x % self.field_modulus for x in b], self.curve_name) + return type(self)([x % self.field_modulus for x in b]) else: raise TypeError( "Expected an int or FQP object, but got object of type {}" @@ -283,7 +276,7 @@ def __div__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": int(c) * prime_field_inv(other, self.field_modulus) % self.field_modulus for c in self.coeffs - ], self.curve_name) + ]) elif isinstance(other, type(self)): return self * other.inv() else: @@ -296,7 +289,7 @@ def __truediv__(self, other: Union[int, "FQ", "FQP"]) -> "FQP": return self.__div__(other) def __pow__(self, other: int) -> "FQP": - o = type(self)([1] + [0] * (self.degree - 1), self.curve_name) + o = type(self)([1] + [0] * (self.degree - 1)) t = self while other > 0: if other & 1: @@ -338,7 +331,7 @@ def inv(self) -> "FQP": nm = [x % self.field_modulus for x in nm] new = [int(x) % self.field_modulus for x in new] lm, low, hm, high = nm, new, lm, low - return type(self)(lm[:self.degree], self.curve_name) / low[0] + return type(self)(lm[:self.degree]) / low[0] def __repr__(self) -> str: return repr(self.coeffs) @@ -359,15 +352,15 @@ def __ne__(self, other: "FQP") -> bool: # type: ignore # https://github.com/ return not self == other def __neg__(self) -> "FQP": - return type(self)([-c for c in self.coeffs], self.curve_name) + return type(self)([-c for c in self.coeffs]) @classmethod - def one(cls, curve_name: str) -> "FQP": - return cls([1] + [0] * (cls.degree - 1), curve_name) + def one(cls) -> "FQP": + return cls([1] + [0] * (cls.degree - 1)) @classmethod - def zero(cls, curve_name: str) -> "FQP": - return cls([0] * cls.degree, curve_name) + def zero(cls) -> "FQP": + return cls([0] * cls.degree) class FQ2(FQP): @@ -376,10 +369,13 @@ class FQ2(FQP): """ degree = 2 - def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: - FQ2_MODULUS_COEFFS = field_properties[curve_name]["fq2_modulus_coeffs"] + def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + FQ2_MODULUS_COEFFS = field_properties[self.curve_name]["fq2_modulus_coeffs"] self.mc_tuples = [(i, c) for i, c in enumerate(FQ2_MODULUS_COEFFS) if c] - super().__init__(coeffs, curve_name, FQ2_MODULUS_COEFFS) + super().__init__(coeffs, FQ2_MODULUS_COEFFS) class FQ12(FQP): @@ -388,7 +384,10 @@ class FQ12(FQP): """ degree = 12 - def __init__(self, coeffs: Sequence[IntOrFQ], curve_name: str) -> None: - FQ12_MODULUS_COEFFS = field_properties[curve_name]["fq12_modulus_coeffs"] + def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: + if self.curve_name is None: + raise AttributeError("Curve Name hasn't been specified") + + FQ12_MODULUS_COEFFS = field_properties[self.curve_name]["fq12_modulus_coeffs"] self.mc_tuples = [(i, c) for i, c in enumerate(FQ12_MODULUS_COEFFS) if c] - super().__init__(coeffs, curve_name, FQ12_MODULUS_COEFFS) + super().__init__(coeffs, FQ12_MODULUS_COEFFS) diff --git a/tests/test_bn128_and_bls12_381.py b/tests/test_bn128_and_bls12_381.py index 93342b76..155d4b68 100644 --- a/tests/test_bn128_and_bls12_381.py +++ b/tests/test_bn128_and_bls12_381.py @@ -2,7 +2,27 @@ import pytest -from py_ecc import bn128, optimized_bn128, bls12_381, optimized_bls12_381 +from py_ecc import ( + bn128, + optimized_bn128, + bls12_381, + optimized_bls12_381, +) + +from py_ecc.fields import ( + bls12_381_FQ, + bls12_381_FQ2, + bls12_381_FQ12, + bn128_FQ, + bn128_FQ2, + bn128_FQ12, + optimized_bls12_381_FQ, + optimized_bls12_381_FQ2, + optimized_bls12_381_FQ12, + optimized_bn128_FQ, + optimized_bn128_FQ2, + optimized_bn128_FQ12, +) @pytest.fixture(params=[bn128, optimized_bn128, bls12_381, optimized_bls12_381]) @@ -12,17 +32,44 @@ def lib(request): @pytest.fixture def FQ(lib): - return lib.FQ + if lib == bn128: + return bn128_FQ + elif lib == optimized_bn128: + return optimized_bn128_FQ + elif lib == bls12_381: + return bls12_381_FQ + elif lib == optimized_bls12_381: + return optimized_bls12_381_FQ + else: + raise Exception("Library Not Found") @pytest.fixture def FQ2(lib): - return lib.FQ2 + if lib == bn128: + return bn128_FQ2 + elif lib == optimized_bn128: + return optimized_bn128_FQ2 + elif lib == bls12_381: + return bls12_381_FQ2 + elif lib == optimized_bls12_381: + return optimized_bls12_381_FQ2 + else: + raise Exception("Library Not Found") @pytest.fixture def FQ12(lib): - return lib.FQ12 + if lib == bn128: + return bn128_FQ12 + elif lib == optimized_bn128: + return optimized_bn128_FQ12 + elif lib == bls12_381: + return bls12_381_FQ12 + elif lib == optimized_bls12_381: + return optimized_bls12_381_FQ12 + else: + raise Exception("Library Not Found") @pytest.fixture From efedeefd89112c9544deb926e713a38683f7e49a Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Fri, 25 Jan 2019 18:20:53 +0530 Subject: [PATCH 06/12] Integrate Fields module with curves --- py_ecc/bls12_381/bls12_381_curve.py | 9 ++- py_ecc/bls12_381/bls12_381_pairing.py | 7 +- py_ecc/bn128/bn128_curve.py | 11 +-- py_ecc/bn128/bn128_pairing.py | 9 ++- py_ecc/optimized_bls12_381/optimized_curve.py | 9 ++- .../optimized_bls12_381/optimized_pairing.py | 7 +- py_ecc/optimized_bn128/optimized_curve.py | 11 +-- py_ecc/optimized_bn128/optimized_pairing.py | 9 ++- tests/test_field_elements.py | 67 ------------------- 9 files changed, 48 insertions(+), 91 deletions(-) delete mode 100644 tests/test_field_elements.py diff --git a/py_ecc/bls12_381/bls12_381_curve.py b/py_ecc/bls12_381/bls12_381_curve.py index 7a8105d7..9bda1d41 100644 --- a/py_ecc/bls12_381/bls12_381_curve.py +++ b/py_ecc/bls12_381/bls12_381_curve.py @@ -1,10 +1,13 @@ from __future__ import absolute_import +from py_ecc.fields import ( + bls12_381_FQ as FQ, + bls12_381_FQ2 as FQ2, + bls12_381_FQ12 as FQ12, +) + from .bls12_381_field_elements import ( field_modulus, - FQ, - FQ2, - FQ12, ) diff --git a/py_ecc/bls12_381/bls12_381_pairing.py b/py_ecc/bls12_381/bls12_381_pairing.py index 21ec4f22..29da476c 100644 --- a/py_ecc/bls12_381/bls12_381_pairing.py +++ b/py_ecc/bls12_381/bls12_381_pairing.py @@ -1,5 +1,10 @@ from __future__ import absolute_import +from py_ecc.fields import ( + bls12_381_FQ as FQ, + bls12_381_FQ12 as FQ12, +) + from .bls12_381_curve import ( double, add, @@ -13,8 +18,6 @@ ) from .bls12_381_field_elements import ( field_modulus, - FQ, - FQ12, ) diff --git a/py_ecc/bn128/bn128_curve.py b/py_ecc/bn128/bn128_curve.py index 6cca05fe..bb598681 100644 --- a/py_ecc/bn128/bn128_curve.py +++ b/py_ecc/bn128/bn128_curve.py @@ -4,6 +4,13 @@ cast, ) +from py_ecc.fields import ( + bn128_FQ as FQ, + bn128_FQ2 as FQ2, + bn128_FQ12 as FQ12, + bn128_FQP as FQP, +) + from py_ecc.typing import ( Field, GeneralPoint, @@ -12,10 +19,6 @@ from .bn128_field_elements import ( field_modulus, - FQ, - FQ2, - FQ12, - FQP, ) diff --git a/py_ecc/bn128/bn128_pairing.py b/py_ecc/bn128/bn128_pairing.py index bc070266..04f480e1 100644 --- a/py_ecc/bn128/bn128_pairing.py +++ b/py_ecc/bn128/bn128_pairing.py @@ -4,6 +4,12 @@ cast, ) +from py_ecc.fields import ( + bn128_FQ as FQ, + bn128_FQ12 as FQ12, + bn128_FQP as FQP, +) + from py_ecc.typing import ( Field, FQPoint2D, @@ -25,9 +31,6 @@ ) from .bn128_field_elements import ( field_modulus, - FQ, - FQ12, - FQP, ) diff --git a/py_ecc/optimized_bls12_381/optimized_curve.py b/py_ecc/optimized_bls12_381/optimized_curve.py index 64b8d846..b86c99fc 100644 --- a/py_ecc/optimized_bls12_381/optimized_curve.py +++ b/py_ecc/optimized_bls12_381/optimized_curve.py @@ -1,10 +1,13 @@ from __future__ import absolute_import +from py_ecc.fields import ( + optimized_bls12_381_FQ as FQ, + optimized_bls12_381_FQ2 as FQ2, + optimized_bls12_381_FQ12 as FQ12, +) + from .optimized_field_elements import ( - FQ2, - FQ12, field_modulus, - FQ, ) diff --git a/py_ecc/optimized_bls12_381/optimized_pairing.py b/py_ecc/optimized_bls12_381/optimized_pairing.py index 430e3a4a..0e1bd737 100644 --- a/py_ecc/optimized_bls12_381/optimized_pairing.py +++ b/py_ecc/optimized_bls12_381/optimized_pairing.py @@ -1,5 +1,10 @@ from __future__ import absolute_import +from py_ecc.fields import ( + optimized_bls12_381_FQ as FQ, + optimized_bls12_381_FQ12 as FQ12, +) + from .optimized_curve import ( double, add, @@ -14,9 +19,7 @@ normalize, ) from .optimized_field_elements import ( - FQ12, field_modulus, - FQ, ) diff --git a/py_ecc/optimized_bn128/optimized_curve.py b/py_ecc/optimized_bn128/optimized_curve.py index 484456c7..ac787ebf 100644 --- a/py_ecc/optimized_bn128/optimized_curve.py +++ b/py_ecc/optimized_bn128/optimized_curve.py @@ -4,12 +4,15 @@ cast, ) +from py_ecc.fields import ( + optimized_bn128_FQ as FQ, + optimized_bn128_FQP as FQP, + optimized_bn128_FQ2 as FQ2, + optimized_bn128_FQ12 as FQ12, +) + from .optimized_field_elements import ( field_modulus, - FQ, - FQ2, - FQ12, - FQP, ) from py_ecc.typing import ( diff --git a/py_ecc/optimized_bn128/optimized_pairing.py b/py_ecc/optimized_bn128/optimized_pairing.py index 8289d548..fa87b315 100644 --- a/py_ecc/optimized_bn128/optimized_pairing.py +++ b/py_ecc/optimized_bn128/optimized_pairing.py @@ -1,5 +1,11 @@ from __future__ import absolute_import +from py_ecc.fields import ( + optimized_bn128_FQ as FQ, + optimized_bn128_FQP as FQP, + optimized_bn128_FQ12 as FQ12, +) + from py_ecc.typing import ( Optimized_Field, Optimized_FQPoint3D, @@ -23,9 +29,6 @@ ) from .optimized_field_elements import ( field_modulus, - FQ, - FQ12, - FQP, ) diff --git a/tests/test_field_elements.py b/tests/test_field_elements.py deleted file mode 100644 index be77506d..00000000 --- a/tests/test_field_elements.py +++ /dev/null @@ -1,67 +0,0 @@ -import pytest - -from py_ecc.fields import ( - field_elements, - optimized_field_elements, -) -from py_ecc.fields.field_properties import ( - field_properties, -) - - -# Tests both field_elements and optimized_field_elements -@pytest.fixture(params=[field_elements, optimized_field_elements]) -def lib(request): - return request.param - - -@pytest.fixture -def FQ(lib): - return lib.FQ - - -@pytest.fixture -def FQ2(lib): - return lib.FQ2 - - -@pytest.fixture -def FQ12(lib): - return lib.FQ12 - - -def test_FQ_object(FQ): - for curve_name in ("bn128", "bls12_381"): - field_modulus = field_properties[curve_name]["field_modulus"] - assert FQ(2, curve_name) * FQ(2, curve_name) == FQ(4, curve_name) - assert FQ(2, curve_name) / FQ(7, curve_name) + FQ(9, curve_name) / FQ(7, curve_name) == FQ(11, curve_name) / FQ(7, curve_name) - assert FQ(2, curve_name) * FQ(7, curve_name) + FQ(9, curve_name) * FQ(7, curve_name) == FQ(11, curve_name) * FQ(7, curve_name) - assert FQ(9, curve_name) ** field_modulus == FQ(9, curve_name) - - -def test_FQ2_object(FQ2): - for curve_name in ("bn128", "bls12_381"): - field_modulus = field_properties[curve_name]["field_modulus"] - x = FQ2([1, 0], curve_name) - f = FQ2([1, 2], curve_name) - fpx = FQ2([2, 2], curve_name) - one = FQ2.one(curve_name) - 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 - - -def test_FQ12_object(FQ12): - for curve_name in ("bn128", "bls12_381"): - x = FQ12([1] + [0] * 11, curve_name) - f = FQ12([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], curve_name) - fpx = FQ12([2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], curve_name) - one = FQ12.one(curve_name) - 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 From b343f38bca5ac7e7cfb13a81fc13e00d7f9f49fc Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Fri, 25 Jan 2019 19:02:00 +0530 Subject: [PATCH 07/12] Remove mypy and update flake8 version --- setup.py | 2 +- tox.ini | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a4458a25..0018399b 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ "pytest-xdist==1.26.0" ], 'lint': [ - "flake8==3.4.1", + "flake8==3.5.0", "mypy==0.641", "mypy-extensions>=0.4.1", ], diff --git a/tox.ini b/tox.ini index b02e57df..306b88ce 100644 --- a/tox.ini +++ b/tox.ini @@ -25,6 +25,4 @@ basepython=python extras=lint commands= flake8 {toxinidir}/py_ecc - mypy --strict --follow-imports=silent --ignore-missing-imports --no-strict-optional -p py_ecc.bn128 - mypy --strict --follow-imports=silent --ignore-missing-imports --no-strict-optional -p py_ecc.optimized_bn128 - mypy --strict --follow-imports=silent --ignore-missing-imports --no-strict-optional -p py_ecc.secp256k1 + flake8 {toxinidir}/tests From 6cba6b1627e6a4b6ec96279ccf9a43a8052cd62f Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Fri, 25 Jan 2019 22:26:19 +0530 Subject: [PATCH 08/12] Add normalize_FQ_point function to FQ --- py_ecc/fields/field_elements.py | 81 ++++++----------------- py_ecc/fields/optimized_field_elements.py | 81 ++++++----------------- 2 files changed, 42 insertions(+), 120 deletions(-) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index 23f6dc57..d3db7275 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -20,6 +20,21 @@ IntOrFQ = Union[int, "FQ"] +def normalize_FQ_point(value: IntOrFQ) -> int: + """ + Normalize FQ Point or any integer to integer + """ + if isinstance(value, int): + return value + elif isinstance(value, FQ): + return value.n + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(value)) + ) + + class FQ(object): """ A class for field elements in FQ. Wrap a number in this class, @@ -49,29 +64,11 @@ def __init__(self, val: IntOrFQ) -> None: ) def __add__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": @@ -81,42 +78,15 @@ def __radd__(self, other: IntOrFQ) -> "FQ": return self + other def __rsub__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n - on) % self.field_modulus) def __div__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)( self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) @@ -125,16 +95,7 @@ def __truediv__(self, other: IntOrFQ) -> "FQ": return self.__div__(other) def __rdiv__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)( prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus ) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index f4e81b83..4692270a 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -19,6 +19,21 @@ IntOrFQ = Union[int, "FQ"] +def normalize_FQ_point(value: IntOrFQ) -> int: + """ + Normalize FQ Point or any integer to integer + """ + if isinstance(value, int): + return value + elif isinstance(value, FQ): + return value.n + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(value)) + ) + + class FQ(object): """ A class for field elements in FQ. Wrap a number in this class, @@ -48,29 +63,11 @@ def __init__(self, val: IntOrFQ) -> None: ) def __add__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": @@ -80,45 +77,18 @@ def __radd__(self, other: IntOrFQ) -> "FQ": return self + other def __rsub__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)((self.n - on) % self.field_modulus) def __mod__(self, other: IntOrFQ) -> "FQ": raise NotImplementedError("Modulo Operation not yet supported by fields") def __div__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)( self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) @@ -127,16 +97,7 @@ def __truediv__(self, other: IntOrFQ) -> "FQ": return self.__div__(other) def __rdiv__(self, other: IntOrFQ) -> "FQ": - if isinstance(other, FQ): - on = other.n - elif isinstance(other, int): - on = other - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(other)) - ) - + on = normalize_FQ_point(other) return type(self)( prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus ) From f91a953275717e0d030c8cccc665db76117557c3 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Fri, 25 Jan 2019 22:58:03 +0530 Subject: [PATCH 09/12] Show which among lm,hm,nm,low,high,new are not of same length --- py_ecc/fields/field_elements.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index d3db7275..eb5dd525 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -257,10 +257,19 @@ def inv(self) -> "FQP": r += [0] * (self.degree + 1 - len(r)) nm = [x for x in hm] new = [x for x in high] - if len(set( - [len(lm), len(hm), len(low), len(high), len(nm), len(new), self.degree + 1] - )) != 1: - raise Exception("Mismatch between the lengths of lm, hm, low, high, nm, new") + + if len(lm) != self.degree + 1: + raise Exception("Length of lm is not {}".format(self.degree + 1)) + elif len(hm) != self.degree + 1: + raise Exception("Length of hm is not {}".format(self.degree + 1)) + elif len(nm) != self.degree + 1: + raise Exception("Length of nm is not {}".format(self.degree + 1)) + elif len(low) != self.degree + 1: + raise Exception("Length of low is not {}".format(self.degree + 1)) + elif len(high) != self.degree + 1: + raise Exception("Length of high is not {}".format(self.degree + 1)) + elif len(new) != self.degree + 1: + raise Exception("Length of new is not {}".format(self.degree + 1)) for i in range(self.degree + 1): for j in range(self.degree + 1 - i): From 3db80852d14a73da8c182a2eb08e1277aecadc7a Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Wed, 27 Feb 2019 12:49:39 +0530 Subject: [PATCH 10/12] Include changes of PR 47 --- py_ecc/fields/optimized_field_elements.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 4692270a..6a1e43e8 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -334,6 +334,10 @@ def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if isinstance(coeffs[0], int): + coeffs = tuple(coeff % field_modulus for coeff in coeffs) + else: + coeffs = tuple(coeffs) FQ2_MODULUS_COEFFS = field_properties[self.curve_name]["fq2_modulus_coeffs"] self.mc_tuples = [(i, c) for i, c in enumerate(FQ2_MODULUS_COEFFS) if c] super().__init__(coeffs, FQ2_MODULUS_COEFFS) @@ -349,6 +353,10 @@ def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if isinstance(coeffs[0], int): + coeffs = tuple(coeff % field_modulus for coeff in coeffs) + else: + coeffs = tuple(coeffs) FQ12_MODULUS_COEFFS = field_properties[self.curve_name]["fq12_modulus_coeffs"] self.mc_tuples = [(i, c) for i, c in enumerate(FQ12_MODULUS_COEFFS) if c] super().__init__(coeffs, FQ12_MODULUS_COEFFS) From d31e9908b0831aa6b7c7659f3ebb0799808b7f9e Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Wed, 27 Feb 2019 12:56:49 +0530 Subject: [PATCH 11/12] Add optimizations --- py_ecc/fields/__init__.py | 124 +++++++++++++++++++--- py_ecc/fields/field_elements.py | 28 ++--- py_ecc/fields/optimized_field_elements.py | 41 ++++--- 3 files changed, 142 insertions(+), 51 deletions(-) diff --git a/py_ecc/fields/__init__.py b/py_ecc/fields/__init__.py index 4f169a5f..36258773 100644 --- a/py_ecc/fields/__init__.py +++ b/py_ecc/fields/__init__.py @@ -5,6 +5,10 @@ FQ12, ) +from .field_properties import ( + field_properties, +) + from .optimized_field_elements import ( FQ as optimized_FQ, FQP as optimized_FQP, @@ -15,54 +19,142 @@ # Create seperate classes for all Fields for each curve -bn128_FQ = type("bn128_FQ", (FQ,), {'curve_name': "bn128"}) -bn128_FQP = type("bn128_FQP", (FQP,), {'curve_name': "bn128"}) -bn128_FQ2 = type("bn128_FQ2", (FQ2,), {'curve_name': "bn128"}) -bn128_FQ12 = type("bn128_FQ12", (FQ12,), {'curve_name': "bn128"}) +bn128_FQ = type( + "bn128_FQ", + (FQ,), + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + } +) +bn128_FQP = type( + "bn128_FQP", + (FQP,), + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + } +) +bn128_FQ2 = type( + "bn128_FQ2", + (FQ2,), + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + 'FQ2_MODULUS_COEFFS': field_properties["bn128"]["fq2_modulus_coeffs"], + } +) +bn128_FQ12 = type( + "bn128_FQ12", + (FQ12,), + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + 'FQ12_MODULUS_COEFFS': field_properties["bn128"]["fq12_modulus_coeffs"], + } +) -bls12_381_FQ = type("bls12_381_FQ", (FQ,), {'curve_name': "bls12_381"}) -bls12_381_FQP = type("bls12_381_FQP", (FQP,), {'curve_name': "bls12_381"}) -bls12_381_FQ2 = type("bls12_381_FQ2", (FQ2,), {'curve_name': "bls12_381"}) -bls12_381_FQ12 = type("bls12_381_FQ12", (FQ12,), {'curve_name': "bls12_381"}) +bls12_381_FQ = type( + "bls12_381_FQ", + (FQ,), + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + } +) +bls12_381_FQP = type( + "bls12_381_FQP", + (FQP,), + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + } +) +bls12_381_FQ2 = type( + "bls12_381_FQ2", + (FQ2,), + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + 'FQ2_MODULUS_COEFFS': field_properties["bls12_381"]["fq2_modulus_coeffs"], + } +) +bls12_381_FQ12 = type( + "bls12_381_FQ12", + (FQ12,), + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + 'FQ12_MODULUS_COEFFS': field_properties["bls12_381"]["fq12_modulus_coeffs"], + } +) optimized_bn128_FQ = type( "optimized_bn128_FQ", (optimized_FQ,), - {'curve_name': "bn128"} + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + } ) optimized_bn128_FQP = type( "optimized_bn128_FQP", (optimized_FQP,), - {'curve_name': "bn128"} + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + } ) optimized_bn128_FQ2 = type( "optimized_bn128_FQ2", (optimized_FQ2,), - {'curve_name': "bn128"} + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + 'FQ2_MODULUS_COEFFS': field_properties["bn128"]["fq2_modulus_coeffs"], + } ) optimized_bn128_FQ12 = type( "optimized_bn128_FQ12", (optimized_FQ12,), - {'curve_name': "bn128"} + { + 'curve_name': "bn128", + 'field_modulus': field_properties["bn128"]["field_modulus"], + 'FQ12_MODULUS_COEFFS': field_properties["bn128"]["fq12_modulus_coeffs"], + } ) optimized_bls12_381_FQ = type( "optimized_bls12_381_FQ", (optimized_FQ,), - {'curve_name': "bls12_381"} + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + } ) optimized_bls12_381_FQP = type( "optimized_bls12_381_FQP", (optimized_FQP,), - {'curve_name': "bls12_381"} + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + } ) optimized_bls12_381_FQ2 = type( "optimized_bls12_381_FQ2", (optimized_FQ2,), - {'curve_name': "bls12_381"} + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + 'FQ2_MODULUS_COEFFS': field_properties["bls12_381"]["fq2_modulus_coeffs"], + } ) optimized_bls12_381_FQ12 = type( "optimized_bls12_381_FQ12", (optimized_FQ12,), - {'curve_name': "bls12_381"} + { + 'curve_name': "bls12_381", + 'field_modulus': field_properties["bls12_381"]["field_modulus"], + 'FQ12_MODULUS_COEFFS': field_properties["bls12_381"]["fq12_modulus_coeffs"], + } ) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index eb5dd525..cd8db3fe 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -5,11 +5,6 @@ Union, ) - -from py_ecc.fields.field_properties import ( - field_properties, -) - from py_ecc.utils import ( deg, poly_rounded_div, @@ -50,8 +45,8 @@ class FQ(object): def __init__(self, val: IntOrFQ) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") - - self.field_modulus = field_properties[self.curve_name]["field_modulus"] + if self.field_modulus is None: + raise AttributeError("Field Modulus hasn't been specified") if isinstance(val, FQ): self.n = val.n @@ -154,12 +149,15 @@ class FQP(object): """ degree = 0 curve_name = None + field_modulus = None def __init__(self, coeffs: Sequence[IntOrFQ], modulus_coeffs: Sequence[IntOrFQ]=None) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.field_modulus is None: + raise AttributeError("Field Modulus hasn't been specified") if len(coeffs) != len(modulus_coeffs): raise Exception( @@ -167,9 +165,9 @@ def __init__(self, ) # Encoding all coefficients in type FQ (in regards to the curve name too) self.FQP_corresponding_FQ_class = type( - self.curve_name + "_FQ", + "FQP_corresponding_FQ_class_" + self.curve_name, (FQ,), - {'curve_name': self.curve_name} + {'curve_name': self.curve_name, 'field_modulus': self.field_modulus} ) self.coeffs = tuple(self.FQP_corresponding_FQ_class(c) for c in coeffs) # The coefficients of the modulus, without the leading [1] @@ -313,13 +311,15 @@ class FQ2(FQP): The quadratic extension field """ degree = 2 + FQ2_MODULUS_COEFFS = None def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.FQ2_MODULUS_COEFFS is None: + raise AttributeError("FQ2 Modulus Coeffs haven't been specified") - FQ2_MODULUS_COEFFS = field_properties[self.curve_name]["fq2_modulus_coeffs"] - super().__init__(coeffs, FQ2_MODULUS_COEFFS) + super().__init__(coeffs, self.FQ2_MODULUS_COEFFS) class FQ12(FQP): @@ -327,10 +327,12 @@ class FQ12(FQP): The 12th-degree extension field """ degree = 12 + FQ12_MODULUS_COEFFS = None def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.FQ12_MODULUS_COEFFS is None: + raise AttributeError("FQ12 Modulus Coeffs haven't been specified") - FQ12_MODULUS_COEFFS = field_properties[self.curve_name]["fq12_modulus_coeffs"] - super().__init__(coeffs, FQ12_MODULUS_COEFFS) + super().__init__(coeffs, self.FQ12_MODULUS_COEFFS) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 6a1e43e8..9e48ce82 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -6,10 +6,6 @@ Union, ) -from py_ecc.fields.field_properties import ( - field_properties, -) - from py_ecc.utils import ( deg, prime_field_inv, @@ -49,8 +45,8 @@ class FQ(object): def __init__(self, val: IntOrFQ) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") - - self.field_modulus = field_properties[self.curve_name]["field_modulus"] + if self.field_modulus is None: + raise AttributeError("Field Modulus hasn't been specified") if isinstance(val, FQ): self.n = val.n @@ -161,14 +157,19 @@ def __init__(self, modulus_coeffs: Sequence[IntOrFQ]=None) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.field_modulus is None: + raise AttributeError("Field Modulus hasn't been specified") if len(coeffs) != len(modulus_coeffs): raise Exception( "coeffs and modulus_coeffs aren't of the same length" ) + # Not converting coeffs to FQ or explicitly making them integers for performance reasons - self.coeffs = tuple(coeffs) - self.field_modulus = field_properties[self.curve_name]["field_modulus"] + if isinstance(coeffs[0], int): + self.coeffs = tuple(coeff % self.field_modulus for coeff in coeffs) + else: + self.coeffs = tuple(coeffs) # The coefficients of the modulus, without the leading [1] self.modulus_coeffs = tuple(modulus_coeffs) # The degree of the extension field @@ -329,18 +330,16 @@ class FQ2(FQP): The quadratic extension field """ degree = 2 + FQ2_MODULUS_COEFFS = None def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.FQ2_MODULUS_COEFFS is None: + raise AttributeError("FQ2 Modulus Coeffs haven't been specified") - if isinstance(coeffs[0], int): - coeffs = tuple(coeff % field_modulus for coeff in coeffs) - else: - coeffs = tuple(coeffs) - FQ2_MODULUS_COEFFS = field_properties[self.curve_name]["fq2_modulus_coeffs"] - self.mc_tuples = [(i, c) for i, c in enumerate(FQ2_MODULUS_COEFFS) if c] - super().__init__(coeffs, FQ2_MODULUS_COEFFS) + self.mc_tuples = [(i, c) for i, c in enumerate(self.FQ2_MODULUS_COEFFS) if c] + super().__init__(coeffs, self.FQ2_MODULUS_COEFFS) class FQ12(FQP): @@ -348,15 +347,13 @@ class FQ12(FQP): The 12th-degree extension field """ degree = 12 + FQ12_MODULUS_COEFFS = None def __init__(self, coeffs: Sequence[IntOrFQ]) -> None: if self.curve_name is None: raise AttributeError("Curve Name hasn't been specified") + if self.FQ12_MODULUS_COEFFS is None: + raise AttributeError("FQ12 Modulus Coeffs haven't been specified") - if isinstance(coeffs[0], int): - coeffs = tuple(coeff % field_modulus for coeff in coeffs) - else: - coeffs = tuple(coeffs) - FQ12_MODULUS_COEFFS = field_properties[self.curve_name]["fq12_modulus_coeffs"] - self.mc_tuples = [(i, c) for i, c in enumerate(FQ12_MODULUS_COEFFS) if c] - super().__init__(coeffs, FQ12_MODULUS_COEFFS) + self.mc_tuples = [(i, c) for i, c in enumerate(self.FQ12_MODULUS_COEFFS) if c] + super().__init__(coeffs, self.FQ12_MODULUS_COEFFS) From 0f19487d7ea88531f31acf554eb9edbbfc198543 Mon Sep 17 00:00:00 2001 From: Bhargavasomu Date: Wed, 27 Feb 2019 19:25:26 +0530 Subject: [PATCH 12/12] Remove calling normalize_FQ_point function for performance reasons --- py_ecc/fields/field_elements.py | 81 +++++++++++++++++------ py_ecc/fields/optimized_field_elements.py | 81 +++++++++++++++++------ 2 files changed, 120 insertions(+), 42 deletions(-) diff --git a/py_ecc/fields/field_elements.py b/py_ecc/fields/field_elements.py index cd8db3fe..716be2c3 100644 --- a/py_ecc/fields/field_elements.py +++ b/py_ecc/fields/field_elements.py @@ -15,21 +15,6 @@ IntOrFQ = Union[int, "FQ"] -def normalize_FQ_point(value: IntOrFQ) -> int: - """ - Normalize FQ Point or any integer to integer - """ - if isinstance(value, int): - return value - elif isinstance(value, FQ): - return value.n - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(value)) - ) - - class FQ(object): """ A class for field elements in FQ. Wrap a number in this class, @@ -59,11 +44,29 @@ def __init__(self, val: IntOrFQ) -> None: ) def __add__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": @@ -73,15 +76,42 @@ def __radd__(self, other: IntOrFQ) -> "FQ": return self + other def __rsub__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n - on) % self.field_modulus) def __div__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)( self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) @@ -90,7 +120,16 @@ def __truediv__(self, other: IntOrFQ) -> "FQ": return self.__div__(other) def __rdiv__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)( prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus ) diff --git a/py_ecc/fields/optimized_field_elements.py b/py_ecc/fields/optimized_field_elements.py index 9e48ce82..114eecf0 100644 --- a/py_ecc/fields/optimized_field_elements.py +++ b/py_ecc/fields/optimized_field_elements.py @@ -15,21 +15,6 @@ IntOrFQ = Union[int, "FQ"] -def normalize_FQ_point(value: IntOrFQ) -> int: - """ - Normalize FQ Point or any integer to integer - """ - if isinstance(value, int): - return value - elif isinstance(value, FQ): - return value.n - else: - raise TypeError( - "Expected an int or FQ object, but got object of type {}" - .format(type(value)) - ) - - class FQ(object): """ A class for field elements in FQ. Wrap a number in this class, @@ -59,11 +44,29 @@ def __init__(self, val: IntOrFQ) -> None: ) def __add__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n + on) % self.field_modulus) def __mul__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n * on) % self.field_modulus) def __rmul__(self, other: IntOrFQ) -> "FQ": @@ -73,18 +76,45 @@ def __radd__(self, other: IntOrFQ) -> "FQ": return self + other def __rsub__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((on - self.n) % self.field_modulus) def __sub__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)((self.n - on) % self.field_modulus) def __mod__(self, other: IntOrFQ) -> "FQ": raise NotImplementedError("Modulo Operation not yet supported by fields") def __div__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)( self.n * prime_field_inv(on, self.field_modulus) % self.field_modulus ) @@ -93,7 +123,16 @@ def __truediv__(self, other: IntOrFQ) -> "FQ": return self.__div__(other) def __rdiv__(self, other: IntOrFQ) -> "FQ": - on = normalize_FQ_point(other) + if isinstance(other, FQ): + on = other.n + elif isinstance(other, int): + on = other + else: + raise TypeError( + "Expected an int or FQ object, but got object of type {}" + .format(type(other)) + ) + return type(self)( prime_field_inv(self.n, self.field_modulus) * on % self.field_modulus )