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