Skip to content

Commit

Permalink
precompiles: Implement fp and fp2 elements mapping to curve points
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Sep 18, 2024
1 parent 7c6a3ce commit b53e3f2
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 37 deletions.
2 changes: 2 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ jobs:
prague/eip2537_bls_12_381_precompiles/bls12_g2mul
prague/eip2537_bls_12_381_precompiles/bls12_g1msm
prague/eip2537_bls_12_381_precompiles/bls12_g2msm
prague/eip2537_bls_12_381_precompiles/bls12_map_fp_to_g1
prague/eip2537_bls_12_381_precompiles/bls12_map_fp2_to_g2
- run:
name: "Execution spec tests (develop, blockchain_tests)"
# Tests for in-development EVM revision currently passing.
Expand Down
101 changes: 72 additions & 29 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,57 +12,65 @@ namespace
/// https://eips.ethereum.org/EIPS/eip-2537#field-elements-encoding
constexpr auto FP_BYTES_OFFSET = 64 - 48;

/// Validates that integer encoded in big endian is valid element of BLS12-381 fp field
[[nodiscard]] std::optional<blst_fp> validate_fp(const uint8_t _p[64]) noexcept
{
if (intx::be::unsafe::load<intx::uint512>(_p) < BLS_FIELD_MODULUS)
{
blst_fp p;
blst_fp_from_bendian(&p, &_p[FP_BYTES_OFFSET]);
return p;
}
else
return std::nullopt;
}

/// 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_p1(
const uint8_t _x[64], const uint8_t _y[64]) noexcept
{
constexpr auto is_field_element = [](const uint8_t _p[64]) {
return intx::be::unsafe::load<intx::uint512>(_p) < BLS_FIELD_MODULUS;
};

if (!is_field_element(_x))
const auto x = validate_fp(_x);
if (!x.has_value())
return std::nullopt;
if (!is_field_element(_y))
const auto y = validate_fp(_y);
if (!y.has_value())
return std::nullopt;

blst_fp x;
blst_fp y;
blst_fp_from_bendian(&x, &_x[FP_BYTES_OFFSET]);
blst_fp_from_bendian(&y, &_y[FP_BYTES_OFFSET]);

const blst_p1_affine p0_affine{x, y};
const blst_p1_affine p0_affine{*x, *y};
if (!blst_p1_affine_on_curve(&p0_affine))
return std::nullopt;

return p0_affine;
}

/// Validates that integer encoded in big endian is valid element of BLS12-381 fp2 extension field
[[nodiscard]] std::optional<blst_fp2> validate_fp2(const uint8_t _p[128]) noexcept
{
const auto fp0 = validate_fp(_p);
if (!fp0.has_value())
return std::nullopt;
const auto fp1 = validate_fp(&_p[64]);
if (!fp1.has_value())
return std::nullopt;

return {{*fp0, *fp1}};
}

/// 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))
const auto x = validate_fp2(_x);
if (!x.has_value())
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 auto y = validate_fp2(_y);
if (!y.has_value())
return std::nullopt;

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

Expand Down Expand Up @@ -315,4 +323,39 @@ void store(uint8_t _rx[128], const blst_fp2& _x) noexcept
return true;
}

[[nodiscard]] bool map_fp_to_g1(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _fp[64]) noexcept
{
const auto fp = validate_fp(_fp);
if (!fp.has_value())
return false;

blst_p1 out;
blst_map_to_g1(&out, &*fp);

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

return true;
}

[[nodiscard]] bool map_fp2_to_g2(
uint8_t _rx[128], uint8_t _ry[128], const uint8_t _fp2[128]) noexcept
{
const auto fp2 = validate_fp2(_fp2);
if (!fp2.has_value())
return false;

blst_p2 out;
blst_map_to_g2(&out, &*fp2);

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

return true;
}

} // namespace evmone::crypto::bls
15 changes: 15 additions & 0 deletions lib/evmone_precompiles/bls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,19 @@ inline constexpr auto BLS_FIELD_MODULUS =
[[nodiscard]] bool g2_msm(
uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept;

/// Maps field element of Fp to curve point on BLS12-381 curve G1 subgroup.
///
/// Performs field Fp element check. Returns `false` if an element is not from the field.
/// According to spec
/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-mapping-fp-element-to-g1-point
[[nodiscard]] bool map_fp_to_g1(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _fp[64]) noexcept;

/// Maps field element of Fp2 to curve point on BLS12-381 curve G2 subgroup.
///
/// Performs field Fp2 element check. Returns `false` if an element is not from the field.
/// According to spec
/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-mapping-fp2-element-to-g2-point
[[nodiscard]] bool map_fp2_to_g2(
uint8_t _rx[128], uint8_t _ry[128], const uint8_t _fp[128]) noexcept;

} // namespace evmone::crypto::bls
34 changes: 26 additions & 8 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,14 @@ PrecompileAnalysis bls12_pairing_check_analyze(bytes_view, evmc_revision) noexce

PrecompileAnalysis bls12_map_fp_to_g1_analyze(bytes_view, evmc_revision) noexcept
{
// TODO: Implement
return {GasCostMax, 0};
static constexpr auto BLS12_MAP_FP_TO_G1_PRECOMPILE_GAS = 5500;
return {BLS12_MAP_FP_TO_G1_PRECOMPILE_GAS, 128};
}

PrecompileAnalysis bls12_map_fp2_to_g2_analyze(bytes_view, evmc_revision) noexcept
{
// TODO: Implement
return {GasCostMax, 0};
static constexpr auto BLS12_MAP_FP2_TO_G2_PRECOMPILE_GAS = 75000;
return {BLS12_MAP_FP2_TO_G2_PRECOMPILE_GAS, 256};
}

ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output,
Expand Down Expand Up @@ -461,14 +461,32 @@ ExecutionResult bls12_pairing_check_execute(const uint8_t*, size_t, uint8_t*, si
return {EVMC_PRECOMPILE_FAILURE, 0};
}

ExecutionResult bls12_map_fp_to_g1_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
ExecutionResult bls12_map_fp_to_g1_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 != 64)
return {EVMC_PRECOMPILE_FAILURE, 0};

assert(output_size == 128);

if (!crypto::bls::map_fp_to_g1(output, &output[64], input))
return {EVMC_PRECOMPILE_FAILURE, 0};

return {EVMC_SUCCESS, 128};
}

ExecutionResult bls12_map_fp2_to_g2_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
ExecutionResult bls12_map_fp2_to_g2_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 != 128)
return {EVMC_PRECOMPILE_FAILURE, 0};

assert(output_size == 256);

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

return {EVMC_SUCCESS, 256};
}

namespace
Expand Down
38 changes: 38 additions & 0 deletions test/unittests/precompiles_bls_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,41 @@ TEST(bls, g2_msm_inf_2)
EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, map_fp_to_g1)
{
using namespace evmc::literals;
auto input =
"00000000000000000000000000000000156c8a6a2c184569d69a76be144b5cdc5141d2d2ca4fe341f011e25e3969c55ad9e9b9ce2eb833c81a908e5fa4ac5f03"_hex;
uint8_t rx[64];
uint8_t ry[64];

EXPECT_TRUE(evmone::crypto::bls::map_fp_to_g1(rx, ry, input.data()));

const auto expected_x =
"00000000000000000000000000000000184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba"_hex;
const auto expected_y =
"0000000000000000000000000000000004407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3"_hex;

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

TEST(bls, map_fp2_to_g2)
{
using namespace evmc::literals;
auto input =
"0000000000000000000000000000000007355d25caf6e7f2f0cb2812ca0e513bd026ed09dda65b177500fa31714e09ea0ded3a078b526bed3307f804d4b93b040000000000000000000000000000000002829ce3c021339ccb5caf3e187f6370e1e2a311dec9b75363117063ab2015603ff52c3d3b98f19c2f65575e99e8b78c"_hex;
uint8_t rx[128];
uint8_t ry[128];

EXPECT_TRUE(evmone::crypto::bls::map_fp2_to_g2(rx, ry, input.data()));

const auto expected_x =
"0000000000000000000000000000000000e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb700000000000000000000000000000000126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b"_hex;
const auto expected_y =
"000000000000000000000000000000000caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42000000000000000000000000000000001498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d"_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 b53e3f2

Please sign in to comment.