mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-27 22:28:21 +00:00
c4805e0262
* issue/issue-281-create_binding_to_pedersen_hash * Add //nolint * Add more nolints * move nolint * Remove nolit * Add gcc install * Upd .ci * Remove staticcheck * Add envs * try to exclude pedersen_hash from test * try to fix mac os build * Add include for mac os * Add include for mac os * Fix runner_os * remove test for macos * Change restrictions * restrict tests to ubuntu * Try test windows * Add build constraint
103 lines
3.9 KiB
C++
103 lines
3.9 KiB
C++
#include "prime_field_element.h"
|
|
|
|
namespace starkware {
|
|
|
|
PrimeFieldElement PrimeFieldElement::RandomElement(Prng* prng) {
|
|
constexpr size_t kMostSignificantLimb = ValueType::LimbCount() - 1;
|
|
static_assert(
|
|
kModulus[kMostSignificantLimb] != 0, "We assume kModulus[kMostSignificantLimb] is not zero");
|
|
constexpr uint64_t kBitsMask = (Pow2(Log2Floor(kModulus[kMostSignificantLimb]) + 1)) - 1;
|
|
|
|
PrimeFieldElement random_element = PrimeFieldElement::Zero();
|
|
do {
|
|
random_element.value_ = ValueType::RandomBigInt(prng);
|
|
random_element.value_[kMostSignificantLimb] &= kBitsMask;
|
|
} while (random_element.value_ >= kModulus); // Required to enforce uniformity.
|
|
|
|
return random_element;
|
|
}
|
|
|
|
PrimeFieldElement PrimeFieldElement::Pow(const std::vector<bool>& exponent_bits) const {
|
|
return GenericPow(
|
|
*this, exponent_bits, PrimeFieldElement::One(),
|
|
[](const PrimeFieldElement& multiplier, PrimeFieldElement* dst) {
|
|
*dst = *dst * multiplier;
|
|
});
|
|
}
|
|
|
|
PrimeFieldElement PrimeFieldElement::Pow(const uint64_t exponent) const {
|
|
return Pow(BigInt<1>(exponent).ToBoolVector());
|
|
}
|
|
|
|
bool PrimeFieldElement::IsSquare() const {
|
|
if (*this == PrimeFieldElement::Zero()) {
|
|
return true;
|
|
}
|
|
|
|
// value is a square if and only if value^((p-1) / 2) = 1.
|
|
return Pow(kHalfMultiplicativeGroupSize.ToBoolVector()) == PrimeFieldElement::One();
|
|
}
|
|
|
|
PrimeFieldElement PrimeFieldElement::Sqrt() const {
|
|
if (*this == PrimeFieldElement::Zero()) {
|
|
return PrimeFieldElement::Zero();
|
|
}
|
|
|
|
// We use the following algorithm to compute the square root of the element:
|
|
// Let v be the input, let +r and -r be the roots of v and consider the ring
|
|
// R := F[x] / (x^2 - v).
|
|
//
|
|
// This ring is isomorphic to the ring F x F where the isomorphism is given by the map
|
|
// a*x + b --> (ar + b, -ar + b) (recall that we don't know r, so we cannot compute this map).
|
|
//
|
|
// Pick a random element x + b in R, and compute (x + b)^((p-1)/2). Let's say that the result is
|
|
// c*x + d.
|
|
// Taking a random element in F to the power of (p-1)/2 gives +1 or -1 with probability
|
|
// 0.5. Since R is isomorphic to F x F (where multiplication is pointwise), the result of the
|
|
// computation will be one of the four pairs:
|
|
// (+1, +1), (-1, -1), (+1, -1), (-1, +1).
|
|
//
|
|
// If the result is (+1, +1) or (-1, -1) (which are the elements (0*x + 1) and (0*x - 1) in R) -
|
|
// try again with another random element.
|
|
//
|
|
// If the result is (+1, -1) then cr + d = 1 and -cr + d = -1. Therefore r = c^{-1} and d=0. In
|
|
// the second case -r = c^{-1}. In both cases c^{-1} will be the returned root.
|
|
|
|
// Store an element in R as a pair: first * x + second.
|
|
using RingElement = std::pair<PrimeFieldElement, PrimeFieldElement>;
|
|
const RingElement one{PrimeFieldElement::Zero(), PrimeFieldElement::One()};
|
|
const RingElement minus_one{PrimeFieldElement::Zero(), -PrimeFieldElement::One()};
|
|
|
|
auto mult = [this](const RingElement& multiplier, RingElement* dst) {
|
|
// Compute res * multiplier in the ring.
|
|
auto res_first = multiplier.first * dst->second + multiplier.second * dst->first;
|
|
auto res_second = multiplier.first * dst->first * *this + multiplier.second * dst->second;
|
|
*dst = {res_first, res_second};
|
|
};
|
|
|
|
// Compute q = (p - 1) / 2 and get its bits.
|
|
const std::vector<bool> q_bits = kHalfMultiplicativeGroupSize.ToBoolVector();
|
|
|
|
Prng prng;
|
|
while (true) {
|
|
// Pick a random element (x + b) in R.
|
|
RingElement random_element{PrimeFieldElement::One(), PrimeFieldElement::RandomElement(&prng)};
|
|
|
|
// Compute the exponentiation: random_element ^ ((p-1) / 2).
|
|
RingElement res = GenericPow(random_element, q_bits, one, mult);
|
|
|
|
// If res is either 1 or -1, try again.
|
|
if (res == one || res == minus_one) {
|
|
continue;
|
|
}
|
|
|
|
const PrimeFieldElement root = res.first.Inverse();
|
|
|
|
ASSERT(root * root == *this, "value does not have a square root.");
|
|
|
|
return root;
|
|
}
|
|
}
|
|
|
|
} // namespace starkware
|