github.com/ledgerwatch/erigon-lib@v1.0.0/pedersen_hash/elliptic_curve.inl (about)

     1  #include "error_handling.h"
     2  
     3  namespace starkware {
     4  
     5  template <typename FieldElementT>
     6  auto EcPoint<FieldElementT>::Double(const FieldElementT& alpha) const -> EcPoint {
     7    // Doubling a point cannot be done by adding the point to itself with the function AddPoints
     8    // because this function assumes that it gets distinct points. Usually, in order to sum two
     9    // points, one should draw a straight line containing these points, find the third point in the
    10    // intersection of the line and the curve, and then negate the y coordinate. In the special case
    11    // where the two points are the same point, one should draw the line that intersects the elliptic
    12    // curve "twice" at that point. This means that the slope of the line should be equal to the slope
    13    // of the curve at this point. That is, the derivative of the function
    14    // y = sqrt(x^3 + alpha * x + beta), which is slope = dy/dx = (3 * x^2 + alpha)/(2 * y). Note that
    15    // if y = 0 then the point is a 2-torsion (doubling it gives infinity). The line is then given by
    16    // y = slope * x + y_intercept. The third intersection point is found using the equation that is
    17    // true for all cases: slope^2 = x_1 + x_2 + x_3 (where x_1, x_2 and x_3 are the x coordinates of
    18    // three points in the intersection of the curve with a line).
    19    ASSERT(y != FieldElementT::Zero(), "Tangent slope of 2 torsion point is infinite.");
    20    const auto x_squared = x * x;
    21    const FieldElementT tangent_slope = (x_squared + x_squared + x_squared + alpha) / (y + y);
    22    const FieldElementT x2 = tangent_slope * tangent_slope - (x + x);
    23    const FieldElementT y2 = tangent_slope * (x - x2) - y;
    24    return {x2, y2};
    25  }
    26  
    27  template <typename FieldElementT>
    28  auto EcPoint<FieldElementT>::operator+(const EcPoint& rhs) const -> EcPoint {
    29    ASSERT(this->x != rhs.x, "x values should be different for arbitrary points");
    30    // To sum two points, one should draw a straight line containing these points, find the
    31    // third point in the intersection of the line and the curve, and then negate the y coordinate.
    32    // Notice that if x_1 = x_2 then either they are the same point or their sum is infinity. This
    33    // function doesn't deal with these cases. The straight line is given by the equation:
    34    // y = slope * x + y_intercept. The x coordinate of the third point is found by solving the system
    35    // of equations:
    36  
    37    // y = slope * x + y_intercept
    38    // y^2 = x^3 + alpha * x + beta
    39  
    40    // These equations yield:
    41    // (slope * x + y_intercept)^2 = x^3 + alpha * x + beta
    42    // ==> x^3 - slope^2 * x^2 + (alpha  - 2 * slope * y_intercept) * x + (beta - y_intercept^2) = 0
    43  
    44    // This is a monic polynomial in x whose roots are exactly the x coordinates of the three
    45    // intersection points of the line with the curve. Thus it is equal to the polynomial:
    46    // (x - x_1) * (x - x_2) * (x - x_3)
    47    // where x1, x2, x3 are the x coordinates of those points.
    48    // Notice that the equality of the coefficient of the x^2 term yields:
    49    // slope^2 = x_1 + x_2 + x_3.
    50    const FieldElementT slope = (this->y - rhs.y) / (this->x - rhs.x);
    51    const FieldElementT x3 = slope * slope - this->x - rhs.x;
    52    const FieldElementT y3 = slope * (this->x - x3) - this->y;
    53    return {x3, y3};
    54  }
    55  
    56  template <typename FieldElementT>
    57  auto EcPoint<FieldElementT>::GetPointFromX(
    58      const FieldElementT& x, const FieldElementT& alpha, const FieldElementT& beta)
    59      -> std::optional<EcPoint> {
    60    const FieldElementT y_squared = x * x * x + alpha * x + beta;
    61    if (!y_squared.IsSquare()) {
    62      return std::nullopt;
    63    }
    64    return {{x, y_squared.Sqrt()}};
    65  }
    66  
    67  template <typename FieldElementT>
    68  auto EcPoint<FieldElementT>::Random(
    69      const FieldElementT& alpha, const FieldElementT& beta, Prng* prng) -> EcPoint {
    70    // Each iteration has probability of ~1/2 to fail. Thus the probability of failing 100 iterations
    71    // is negligible.
    72    for (size_t i = 0; i < 100; ++i) {
    73      const FieldElementT x = FieldElementT::RandomElement(prng);
    74      const std::optional<EcPoint> pt = GetPointFromX(x, alpha, beta);
    75      if (pt.has_value()) {
    76        // Change the sign of the returned y coordinate with probability 1/2.
    77        if (prng->RandomUint64(0, 1) == 1) {
    78          return -*pt;
    79        }
    80        return *pt;
    81      }
    82    }
    83    ASSERT(false, "No random point found.");
    84  }
    85  
    86  template <typename FieldElementT>
    87  template <typename OtherFieldElementT>
    88  EcPoint<OtherFieldElementT> EcPoint<FieldElementT>::ConvertTo() const {
    89    return EcPoint<OtherFieldElementT>(OtherFieldElementT(x), OtherFieldElementT(y));
    90  }
    91  
    92  template <typename FieldElementT>
    93  template <size_t N>
    94  EcPoint<FieldElementT> EcPoint<FieldElementT>::MultiplyByScalar(
    95      const BigInt<N>& scalar, const FieldElementT& alpha) const {
    96    std::optional<EcPoint<FieldElementT>> res;
    97    EcPoint<FieldElementT> power = *this;
    98    for (const auto& b : scalar.ToBoolVector()) {
    99      if (b) {
   100        res = power.AddOptionalPoint(res, alpha);
   101      }
   102      // If power == -power, then power + power == zero, and will remain zero (so res will not
   103      // change) until the end of the for loop. Therefore there is no point to keep looping.
   104      if (power == -power) {
   105        break;
   106      }
   107      power = power.Double(alpha);
   108    }
   109    ASSERT(res.has_value(), "Result of multiplication is the curve's zero element.");
   110    return *res;
   111  }
   112  
   113  template <typename FieldElementT>
   114  std::optional<EcPoint<FieldElementT>> EcPoint<FieldElementT>::AddOptionalPoint(
   115      const std::optional<EcPoint<FieldElementT>>& point, const FieldElementT& alpha) const {
   116    if (!point) {
   117      return *this;
   118    }
   119    // If a == -b, then a+b == zero element.
   120    if (*point == -*this) {
   121      return std::nullopt;
   122    }
   123    if (*point == *this) {
   124      return point->Double(alpha);
   125    }
   126    return *point + *this;
   127  }
   128  
   129  }  // namespace starkware