From de4c1674071fa35bb26f89e57c8322ad51cdf16d Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 18 Sep 2024 13:14:30 +0200 Subject: [PATCH] precompiles: Implement `fp` and `fp2` elements mapping to curve points --- circle.yml | 2 + lib/evmone_precompiles/bls.cpp | 101 +++++++++++++++++------- lib/evmone_precompiles/bls.hpp | 15 ++++ test/state/precompiles.cpp | 34 ++++++-- test/unittests/precompiles_bls_test.cpp | 38 +++++++++ 5 files changed, 153 insertions(+), 37 deletions(-) diff --git a/circle.yml b/circle.yml index e5f792f784..b29e1ef39f 100644 --- a/circle.yml +++ b/circle.yml @@ -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. diff --git a/lib/evmone_precompiles/bls.cpp b/lib/evmone_precompiles/bls.cpp index 6afa04e670..9fc371a2d8 100644 --- a/lib/evmone_precompiles/bls.cpp +++ b/lib/evmone_precompiles/bls.cpp @@ -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 validate_fp_field_element(const uint8_t _p[64]) +{ + if (intx::be::unsafe::load(_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 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(_p) < BLS_FIELD_MODULUS; - }; - - if (!is_field_element(_x)) + const auto x = validate_fp_field_element(_x); + if (!x.has_value()) return std::nullopt; - if (!is_field_element(_y)) + const auto y = validate_fp_field_element(_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 validate_fp2_field_element(const uint8_t _p[128]) +{ + const auto fp0 = validate_fp_field_element(_p); + if (!fp0.has_value()) + return std::nullopt; + const auto fp1 = validate_fp_field_element(&_p[64]); + if (!fp1.has_value()) + return std::nullopt; + + return {{std::move(*fp0), std::move(*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 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(_p) < BLS_FIELD_MODULUS && - intx::be::unsafe::load(&_p[64]) < BLS_FIELD_MODULUS; - }; - - if (!is_field_element(_x)) - return std::nullopt; - if (!is_field_element(_y)) + const auto x = validate_fp2_field_element(_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_field_element(_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; @@ -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_field_element(_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_field_element(_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 diff --git a/lib/evmone_precompiles/bls.hpp b/lib/evmone_precompiles/bls.hpp index 725bea500e..2cb37330d8 100644 --- a/lib/evmone_precompiles/bls.hpp +++ b/lib/evmone_precompiles/bls.hpp @@ -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 diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 81c1d3576e..71521cc308 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -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, @@ -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 diff --git a/test/unittests/precompiles_bls_test.cpp b/test/unittests/precompiles_bls_test.cpp index 8b7f50c145..378dff8c5b 100644 --- a/test/unittests/precompiles_bls_test.cpp +++ b/test/unittests/precompiles_bls_test.cpp @@ -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); +}