-
Notifications
You must be signed in to change notification settings - Fork 325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: mul with endomorphism #4538
Changes from 23 commits
87b1340
20a1a1a
4c9bcc5
834aa95
c9c1530
ee4ed46
1718e13
dd132fc
4c31fc9
5c3783e
9c08a58
9a28126
2a38c9a
f1396c9
3aa3ce2
b95502f
4313e8b
637c531
07c6c59
58693b9
a828c5b
4bfd00b
3347344
2c2e5d3
161262e
f48445f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -329,8 +329,29 @@ template <class Params_> struct alignas(32) field { | |
// if the modulus is a 256-bit integer, we need to use a basis where g1, g2 have been shifted by 2^384 | ||
if constexpr (Params::modulus_3 >= 0x4000000000000000ULL) { | ||
split_into_endomorphism_scalars_384(k, k1, k2); | ||
return; | ||
} else { | ||
std::pair<std::array<uint64_t, 2>, std::array<uint64_t, 2>> ret = | ||
split_into_endomorphism_scalars_no_shift(k); | ||
k1.data[0] = ret.first[0]; | ||
k1.data[1] = ret.first[1]; | ||
|
||
// TODO(AD): We should move away from this hack by adapting split_into_endomorphism_scalars_no_shift | ||
#if !defined(__clang__) && defined(__GNUC__) | ||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Warray-bounds" | ||
#endif | ||
k2.data[0] = ret.second[0]; // NOLINT | ||
k2.data[1] = ret.second[1]; | ||
#if !defined(__clang__) && defined(__GNUC__) | ||
#pragma GCC diagnostic pop | ||
#endif | ||
} | ||
} | ||
|
||
static std::pair<std::array<uint64_t, 2>, std::array<uint64_t, 2>> split_into_endomorphism_scalars_no_shift( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it called "no_shift"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll just call it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can specify that it works for values less than 256 bits if you want, but shift means something else, so it's a bit confusing |
||
const field& k) | ||
{ | ||
static_assert(Params::modulus_3 < 0x4000000000000000ULL); | ||
field input = k.reduce_once(); | ||
|
||
constexpr field endo_g1 = { Params::endo_g1_lo, Params::endo_g1_mid, Params::endo_g1_hi, 0 }; | ||
|
@@ -369,15 +390,14 @@ template <class Params_> struct alignas(32) field { | |
field t1 = (q2_lo - q1_lo).reduce_once(); | ||
field beta = cube_root_of_unity(); | ||
field t2 = (t1 * beta + input).reduce_once(); | ||
k2.data[0] = t1.data[0]; | ||
k2.data[1] = t1.data[1]; | ||
k1.data[0] = t2.data[0]; | ||
k1.data[1] = t2.data[1]; | ||
return { | ||
{ t2.data[0], t2.data[1] }, | ||
{ t1.data[0], t1.data[1] }, | ||
}; | ||
} | ||
|
||
static void split_into_endomorphism_scalars_384(const field& input, field& k1_out, field& k2_out) | ||
{ | ||
|
||
constexpr field minus_b1f{ | ||
Params::endo_minus_b1_lo, | ||
Params::endo_minus_b1_mid, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" | ||
#include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" | ||
#include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" | ||
#include "barretenberg/ecc/groups/element.hpp" | ||
#include "barretenberg/serialize/test_helper.hpp" | ||
#include <fstream> | ||
|
||
|
@@ -129,3 +130,72 @@ TEST(AffineElement, Msgpack) | |
auto [actual, expected] = msgpack_roundtrip(secp256k1::g1::affine_element{ 1, 1 }); | ||
EXPECT_EQ(actual, expected); | ||
} | ||
|
||
namespace bb::group_elements { | ||
// Kludge to access mul_without_endomorphism; | ||
class TestElementPrivate { | ||
public: | ||
template <typename Element, typename Scalar> | ||
static Element mul_without_endomorphism(const Element& element, const Scalar& scalar) | ||
{ | ||
return element.mul_without_endomorphism(scalar); | ||
} | ||
template <typename Element, typename Scalar> | ||
static Element mul_with_endomorphism(const Element& element, const Scalar& scalar) | ||
{ | ||
return element.mul_with_endomorphism(scalar); | ||
} | ||
}; | ||
} // namespace bb::group_elements | ||
|
||
TEST(AffineElement, EndoMulMatchesNonEndo) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please expand the names of the tests? If a person has no context, they have to look inside the test to understand what's happening |
||
{ | ||
for (int i = 0; i < 100; i++) { | ||
auto x1 = bb::group_elements::element(grumpkin::g1::affine_element::random_element()); | ||
auto f1 = grumpkin::fr::random_element(); | ||
auto r1 = bb::group_elements::TestElementPrivate::mul_without_endomorphism(x1, f1); | ||
auto r2 = bb::group_elements::TestElementPrivate::mul_with_endomorphism(x1, f1); | ||
EXPECT_EQ(r1, r2); | ||
} | ||
} | ||
|
||
TEST(AffineElement, InfinityMul) | ||
{ | ||
auto result = grumpkin::g1::affine_element::infinity() * grumpkin::fr::random_element(); | ||
EXPECT_TRUE(result.is_point_at_infinity()); | ||
} | ||
|
||
TEST(AffineElement, BatchMulMatchesMul) | ||
{ | ||
constexpr size_t num_points = 1024; | ||
std::vector<grumpkin::g1::affine_element> affine_points; | ||
for (size_t i = 0; i < num_points; ++i) { | ||
affine_points.emplace_back(grumpkin::g1::affine_element::random_element()); | ||
} | ||
grumpkin::fr exponent = grumpkin::fr::random_element(); | ||
std::vector<grumpkin::g1::affine_element> result = | ||
grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, exponent); | ||
size_t i = 0; | ||
for (grumpkin::g1::affine_element& el : result) { | ||
EXPECT_EQ(el, affine_points[i] * exponent); | ||
Rumata888 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
i++; | ||
} | ||
} | ||
|
||
TEST(AffineElement, InfinityBatchMul) | ||
{ | ||
constexpr size_t num_points = 1024; | ||
std::vector<grumpkin::g1::affine_element> affine_points; | ||
for (size_t i = 0; i < num_points; ++i) { | ||
affine_points.emplace_back(grumpkin::g1::affine_element::infinity()); | ||
} | ||
grumpkin::fr exponent = grumpkin::fr::random_element(); | ||
std::vector<grumpkin::g1::affine_element> result = | ||
grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, exponent); | ||
for (grumpkin::g1::affine_element& el : result) { | ||
EXPECT_TRUE(el.is_point_at_infinity()); | ||
if (!el.is_point_at_infinity()) { | ||
break; // dont spam with errors | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just lets us remote benchmark other presets + build folders