Skip to content
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

precompiles: Implement EIP-2537's bls12_g2add #995

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ jobs:
prague/eip2537_bls_12_381_precompiles/bls12_precompiles_before_fork
prague/eip2537_bls_12_381_precompiles/bls12_g1add
prague/eip2537_bls_12_381_precompiles/bls12_g1mul
prague/eip2537_bls_12_381_precompiles/bls12_g2add
- run:
name: "Execution spec tests (develop, blockchain_tests)"
# Tests for in-development EVM revision currently passing.
Expand Down
90 changes: 77 additions & 13 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ constexpr auto FP_BYTES_OFFSET = 64 - 48;

/// Validates p1 affine point. Checks that point coordinates are from the BLS12-381 field and
/// that the point is on curve. https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-addition
[[nodiscard]] std::optional<blst_p1_affine> validate_point(
[[nodiscard]] std::optional<blst_p1_affine> validate_p1(
const uint8_t _x[64], const uint8_t _y[64]) noexcept
{
constexpr auto is_field_element = [](const uint8_t _p[64]) {
Expand All @@ -36,24 +36,58 @@ constexpr auto FP_BYTES_OFFSET = 64 - 48;
return p0_affine;
}

/// Stores p1 point in two 64-bytes arrays with big endian encoding zero padded.
void store_result(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
/// Validates p2 affine point. Checks that point coordinates are from the BLS12-381 field and
/// that the point is on curve. https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-addition
[[nodiscard]] std::optional<blst_p2_affine> validate_p2(
const uint8_t _x[128], const uint8_t _y[128]) noexcept
{
blst_p1_affine result;
blst_p1_to_affine(&result, out);
constexpr auto is_field_element = [](const uint8_t _p[128]) {
return intx::be::unsafe::load<intx::uint512>(_p) < BLS_FIELD_MODULUS &&
intx::be::unsafe::load<intx::uint512>(&_p[64]) < BLS_FIELD_MODULUS;
};

if (!is_field_element(_x))
return std::nullopt;
if (!is_field_element(_y))
return std::nullopt;

blst_fp x0;
blst_fp x1;
blst_fp y0;
blst_fp y1;
blst_fp_from_bendian(&x0, &_x[FP_BYTES_OFFSET]);
blst_fp_from_bendian(&x1, &_x[FP_BYTES_OFFSET + 64]);
blst_fp_from_bendian(&y0, &_y[FP_BYTES_OFFSET]);
blst_fp_from_bendian(&y1, &_y[FP_BYTES_OFFSET + 64]);

const blst_p2_affine p_affine{{x0, x1}, {y0, y1}};
if (!blst_p2_affine_on_curve(&p_affine))
return std::nullopt;

return p_affine;
}

/// Stores fp in 64-bytes array with big endian encoding zero padded.
void store(uint8_t _rx[64], const blst_fp& _x) noexcept
{
std::memset(_rx, 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET], &result.x);
std::memset(_ry, 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_ry[FP_BYTES_OFFSET], &result.y);
blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET], &_x);
}

/// Stores fp2 in 128-bytes array with big endian encoding zero padded.
void store(uint8_t _rx[128], const blst_fp2& _x) noexcept
{
store(_rx, _x.fp[0]);
store(&_rx[64], _x.fp[1]);
}

} // namespace

[[nodiscard]] bool g1_add(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x0[64],
const uint8_t _y0[64], const uint8_t _x1[64], const uint8_t _y1[64]) noexcept
{
const auto p0_affine = validate_point(_x0, _y0);
const auto p1_affine = validate_point(_x1, _y1);
const auto p0_affine = validate_p1(_x0, _y0);
const auto p1_affine = validate_p1(_x1, _y1);

if (!p0_affine.has_value() || !p1_affine.has_value())
return false;
Expand All @@ -64,7 +98,10 @@ void store_result(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
blst_p1 out;
blst_p1_add_or_double_affine(&out, &p0, &*p1_affine);

store_result(_rx, _ry, &out);
blst_p1_affine result;
blst_p1_to_affine(&result, &out);
store(_rx, result.x);
store(_ry, result.y);

return true;
}
Expand All @@ -75,7 +112,7 @@ void store_result(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
blst_scalar scalar;
blst_scalar_from_bendian(&scalar, _c);

const auto p_affine = validate_point(_x, _y);
const auto p_affine = validate_p1(_x, _y);
if (!p_affine.has_value())
return false;

Expand All @@ -88,8 +125,35 @@ void store_result(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
blst_p1 out;
blst_p1_mult(&out, &p, scalar.b, 256);

store_result(_rx, _ry, &out);
blst_p1_affine result;
blst_p1_to_affine(&result, &out);
store(_rx, result.x);
store(_ry, result.y);

return true;
}

[[nodiscard]] bool g2_add(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x0[128],
const uint8_t _y0[128], const uint8_t _x1[128], const uint8_t _y1[128]) noexcept
{
const auto p0_affine = validate_p2(_x0, _y0);
const auto p1_affine = validate_p2(_x1, _y1);

if (!p0_affine.has_value() || !p1_affine.has_value())
return false;

blst_p2 p0;
blst_p2_from_affine(&p0, &*p0_affine);

blst_p2 out;
blst_p2_add_or_double_affine(&out, &p0, &*p1_affine);

blst_p2_affine result;
blst_p2_to_affine(&result, &out);
store(_rx, result.x);
store(_ry, result.y);

return true;
}

} // namespace evmone::crypto::bls
8 changes: 8 additions & 0 deletions lib/evmone_precompiles/bls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ inline constexpr auto BLS_FIELD_MODULUS =
[[nodiscard]] bool g1_mul(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x[64],
const uint8_t _y[64], const uint8_t _c[32]) noexcept;

/// Addition in BLS12-381 curve group over G2 extension field.
///
/// Computes P ⊕ Q for two points in affine coordinates on the BLS12-381 curve over G2 extension
/// field, performs subgroup checks for both points according to spec
/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-addition
[[nodiscard]] bool g2_add(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x0[128],
const uint8_t _y0[128], const uint8_t _x1[128], const uint8_t _y1[128]) noexcept;

} // namespace evmone::crypto::bls
17 changes: 13 additions & 4 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ PrecompileAnalysis bls12_g1msm_analyze(bytes_view, evmc_revision) noexcept

PrecompileAnalysis bls12_g2add_analyze(bytes_view, evmc_revision) noexcept
{
// TODO: Implement
return {GasCostMax, 0};
static constexpr auto BLS12_G2ADD_PRECOMPILE_GAS = 800;
return {BLS12_G2ADD_PRECOMPILE_GAS, 256};
}

PrecompileAnalysis bls12_g2mul_analyze(bytes_view, evmc_revision) noexcept
Expand Down Expand Up @@ -380,9 +380,18 @@ ExecutionResult bls12_g1msm_execute(const uint8_t*, size_t, uint8_t*, size_t) no
return {EVMC_PRECOMPILE_FAILURE, 0};
}

ExecutionResult bls12_g2add_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
ExecutionResult bls12_g2add_execute(const uint8_t* input, size_t input_size, uint8_t* output,
[[maybe_unused]] size_t output_size) noexcept
{
return {EVMC_PRECOMPILE_FAILURE, 0};
if (input_size != 512)
return {EVMC_PRECOMPILE_FAILURE, 0};

assert(output_size == 256);

if (!crypto::bls::g2_add(output, &output[128], input, &input[128], &input[256], &input[384]))
return {EVMC_PRECOMPILE_FAILURE, 0};

return {EVMC_SUCCESS, 256};
}

ExecutionResult bls12_g2mul_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
Expand Down
25 changes: 25 additions & 0 deletions test/unittests/precompiles_bls_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,28 @@ TEST(bls, g1_mul)
EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g2_add)
{
const auto x0 =
"00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e"_hex;
const auto y0 =
"000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be"_hex;
const auto x1 =
"00000000000000000000000000000000103121a2ceaae586d240843a398967325f8eb5a93e8fea99b62b9f88d8556c80dd726a4b30e84a36eeabaf3592937f2700000000000000000000000000000000086b990f3da2aeac0a36143b7d7c824428215140db1bb859338764cb58458f081d92664f9053b50b3fbd2e4723121b68"_hex;
const auto y1 =
"000000000000000000000000000000000f9e7ba9a86a8f7624aa2b42dcc8772e1af4ae115685e60abc2c9b90242167acef3d0be4050bf935eed7c3b6fc7ba77e000000000000000000000000000000000d22c3652d0dc6f0fc9316e14268477c2049ef772e852108d269d9c38dba1d4802e8dae479818184c08f9a569d878451"_hex;

uint8_t rx[128];
uint8_t ry[128];

EXPECT_TRUE(evmone::crypto::bls::g2_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data()));

const auto expected_x =
"000000000000000000000000000000000b54a8a7b08bd6827ed9a797de216b8c9057b3a9ca93e2f88e7f04f19accc42da90d883632b9ca4dc38d013f71ede4db00000000000000000000000000000000077eba4eecf0bd764dce8ed5f45040dd8f3b3427cb35230509482c14651713282946306247866dfe39a8e33016fcbe52"_hex;
const auto expected_y =
"0000000000000000000000000000000014e60a76a29ef85cbd69f251b9f29147b67cfe3ed2823d3f9776b3a0efd2731941d47436dc6d2b58d9e65f8438bad073000000000000000000000000000000001586c3c910d95754fef7a732df78e279c3d37431c6a2b77e67a00c7c130a8fcd4d19f159cbeb997a178108fffffcbd20"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}