Skip to content

Commit

Permalink
precompiles: Implement bls g2 add
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Sep 11, 2024
1 parent acfc76d commit 2824aa9
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 11 deletions.
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
- collect_coverage_gcc
- upload_coverage:
flags: execution_spec_tests
Expand Down
84 changes: 77 additions & 7 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,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 @@ -38,8 +38,39 @@ constexpr auto FP_BYTES_OFFSET = 64 - 48;
return p0_affine;
}

/// 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
{
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 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
void store_result_g1(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
{
blst_p1_affine result;
blst_p1_to_affine(&result, out);
Expand All @@ -49,13 +80,31 @@ void store_result(uint8_t _rx[64], uint8_t _ry[64], const blst_p1* out) noexcept
std::memset(_ry, 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_ry[FP_BYTES_OFFSET], &result.y);
}

/// Stores p2 point in two 128-bytes arrays with big endian encoding zero padded.
void store_result_g2(uint8_t _rx[128], uint8_t _ry[128], const blst_p2* out) noexcept
{
blst_p2_affine result;
blst_p2_to_affine(&result, out);

std::memset(_rx, 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET], &result.x.fp[0]);
std::memset(&_rx[64], 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET + 64], &result.x.fp[1]);

std::memset(_ry, 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_ry[FP_BYTES_OFFSET], &result.y.fp[0]);
std::memset(&_ry[64], 0, FP_BYTES_OFFSET);
blst_bendian_from_fp(&_ry[FP_BYTES_OFFSET + 64], &result.y.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 @@ -66,7 +115,7 @@ 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);
store_result_g1(_rx, _ry, &out);

return true;
}
Expand All @@ -77,7 +126,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 @@ -90,8 +139,29 @@ 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);
store_result_g1(_rx, _ry, &out);

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);

store_result_g2(_rx, _ry, &out);

return true;
}

} // namespace evmone::crypto::bls
7 changes: 7 additions & 0 deletions lib/evmone_precompiles/bls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,11 @@ 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,
[[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);
}

0 comments on commit 2824aa9

Please sign in to comment.