diff --git a/circle.yml b/circle.yml index 3aa950f73b..e5f792f784 100644 --- a/circle.yml +++ b/circle.yml @@ -377,6 +377,8 @@ jobs: prague/eip2537_bls_12_381_precompiles/bls12_g1mul prague/eip2537_bls_12_381_precompiles/bls12_g2add prague/eip2537_bls_12_381_precompiles/bls12_g2mul + prague/eip2537_bls_12_381_precompiles/bls12_g1msm + prague/eip2537_bls_12_381_precompiles/bls12_g2msm - 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 49eb5af0b7..6afa04e670 100644 --- a/lib/evmone_precompiles/bls.cpp +++ b/lib/evmone_precompiles/bls.cpp @@ -1,6 +1,8 @@ #include "bls.hpp" #include +#include #include +#include namespace evmone::crypto::bls { @@ -183,4 +185,134 @@ void store(uint8_t _rx[128], const blst_fp2& _x) noexcept return true; } +[[nodiscard]] bool g1_msm( + uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept +{ + constexpr auto SINGLE_ENTRY_SIZE = (64 * 2 + 32); + assert(size % SINGLE_ENTRY_SIZE == 0); + auto npoints = size / SINGLE_ENTRY_SIZE; + + std::vector p1_affines; + std::vector p1_affine_ptrs; + p1_affines.reserve(npoints); + p1_affine_ptrs.reserve(npoints); + + std::vector scalars; + std::vector scalars_ptrs; + scalars.reserve(npoints); + scalars_ptrs.reserve(npoints); + + auto ptr = _xycs; + for (size_t i = 0; i < npoints; ++i) + { + const auto p_affine = validate_p1(ptr, &ptr[64]); + if (!p_affine.has_value()) + return false; + + if (!blst_p1_affine_in_g1(&*p_affine)) + return false; + + // Point at infinity must be filtered out for BLST library. + if (blst_p1_affine_is_inf(&*p_affine)) + continue; + + const auto& p = p1_affines.emplace_back(*p_affine); + p1_affine_ptrs.emplace_back(&p); + + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, &ptr[128]); + const auto& s = scalars.emplace_back(scalar); + scalars_ptrs.emplace_back(s.b); + + ptr += SINGLE_ENTRY_SIZE; + } + + npoints = p1_affine_ptrs.size(); + + if (npoints == 0) + { + memset(_rx, 0, 64); + memset(_ry, 0, 64); + return true; + } + + const auto scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t); + const auto scratch_space = std::make_unique_for_overwrite(scratch_size); + blst_p1 out; + blst_p1s_mult_pippenger( + &out, p1_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get()); + + blst_p1_affine result; + blst_p1_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g2_msm( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept +{ + constexpr auto SINGLE_ENTRY_SIZE = (128 * 2 + 32); + assert(size % SINGLE_ENTRY_SIZE == 0); + auto npoints = size / SINGLE_ENTRY_SIZE; + + std::vector p2_affines; + std::vector p2_affine_ptrs; + p2_affines.reserve(npoints); + p2_affine_ptrs.reserve(npoints); + + std::vector scalars; + std::vector scalars_ptrs; + scalars.reserve(npoints); + scalars_ptrs.reserve(npoints); + + auto ptr = _xycs; + for (size_t i = 0; i < npoints; ++i) + { + const auto p_affine = validate_p2(ptr, &ptr[128]); + if (!p_affine.has_value()) + return false; + + if (!blst_p2_affine_in_g2(&*p_affine)) + return false; + + // Point at infinity must be filtered out for BLST library. + if (blst_p2_affine_is_inf(&*p_affine)) + continue; + + const auto& p = p2_affines.emplace_back(*p_affine); + p2_affine_ptrs.emplace_back(&p); + + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, &ptr[256]); + const auto& s = scalars.emplace_back(scalar); + scalars_ptrs.emplace_back(s.b); + + ptr += SINGLE_ENTRY_SIZE; + } + + npoints = p2_affine_ptrs.size(); + + if (npoints == 0) + { + memset(_rx, 0, 128); + memset(_ry, 0, 128); + return true; + } + + const auto scratch_size = blst_p2s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t); + const auto scratch_space = std::make_unique_for_overwrite(scratch_size); + blst_p2 out; + blst_p2s_mult_pippenger( + &out, p2_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get()); + + 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 1a139a5376..725bea500e 100644 --- a/lib/evmone_precompiles/bls.hpp +++ b/lib/evmone_precompiles/bls.hpp @@ -39,4 +39,20 @@ inline constexpr auto BLS_FIELD_MODULUS = [[nodiscard]] bool g2_mul(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x[128], const uint8_t _y[128], const uint8_t _c[32]) noexcept; +/// Multi scalar multiplication in BLS12-381 curve G1 subgroup. +/// +/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve, performs +/// subgroup check according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-msm +[[nodiscard]] bool g1_msm( + uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept; + +/// Multi scalar multiplication in BLS12-381 curve G2 subgroup. +/// +/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve over G2 extension +/// field, performs subgroup check according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-msm +[[nodiscard]] bool g2_msm( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept; + } // namespace evmone::crypto::bls diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 31a93a57ff..81c1d3576e 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -41,6 +41,25 @@ inline constexpr int64_t cost_per_input_word(size_t input_size) noexcept { return BaseCost + WordCost * num_words(input_size); } + +int64_t bls_msm_cost(size_t k, int64_t multiplication_cost) noexcept +{ + assert(k > 0); + + static constexpr int64_t MULTIPLIER = 1000; + static constexpr int16_t DISCOUNT[128] = {1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, + 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, + 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, + 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, + 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, + 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, + 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, + 181, 180, 179, 179, 178, 177, 176, 176, 175, 174}; + + const auto d = DISCOUNT[std::min(k, std::size(DISCOUNT)) - 1]; + return (static_cast(k) * multiplication_cost * d) / MULTIPLIER; +} + } // namespace PrecompileAnalysis ecrecover_analyze(bytes_view /*input*/, evmc_revision /*rev*/) noexcept @@ -166,10 +185,13 @@ PrecompileAnalysis bls12_g1mul_analyze(bytes_view, evmc_revision) noexcept return {BLS12_G1MUL_PRECOMPILE_GAS, 128}; } -PrecompileAnalysis bls12_g1msm_analyze(bytes_view, evmc_revision) noexcept +PrecompileAnalysis bls12_g1msm_analyze(bytes_view input, evmc_revision) noexcept { - // TODO: Implement - return {GasCostMax, 0}; + if (input.empty() || input.size() % 160 != 0) + return {GasCostMax, 0}; + + static constexpr auto BLS12_G1MUL_PRECOMPILE_GAS = 12000; + return {bls_msm_cost(input.size() / 160, BLS12_G1MUL_PRECOMPILE_GAS), 128}; } PrecompileAnalysis bls12_g2add_analyze(bytes_view, evmc_revision) noexcept @@ -184,10 +206,13 @@ PrecompileAnalysis bls12_g2mul_analyze(bytes_view, evmc_revision) noexcept return {BLS12_G2MUL_PRECOMPILE_GAS, 256}; } -PrecompileAnalysis bls12_g2msm_analyze(bytes_view, evmc_revision) noexcept +PrecompileAnalysis bls12_g2msm_analyze(bytes_view input, evmc_revision) noexcept { - // TODO: Implement - return {GasCostMax, 0}; + if (input.empty() || input.size() % 288 != 0) + return {GasCostMax, 0}; + + static constexpr auto BLS12_G2MUL_PRECOMPILE_GAS = 45000; + return {bls_msm_cost(input.size() / 288, BLS12_G2MUL_PRECOMPILE_GAS), 256}; } PrecompileAnalysis bls12_pairing_check_analyze(bytes_view, evmc_revision) noexcept @@ -375,9 +400,18 @@ ExecutionResult bls12_g1mul_execute(const uint8_t* input, size_t input_size, uin return {EVMC_SUCCESS, 128}; } -ExecutionResult bls12_g1msm_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept +ExecutionResult bls12_g1msm_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 % 160 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::g1_msm(output, &output[64], input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; } ExecutionResult bls12_g2add_execute(const uint8_t* input, size_t input_size, uint8_t* output, @@ -408,9 +442,18 @@ ExecutionResult bls12_g2mul_execute(const uint8_t* input, size_t input_size, uin return {EVMC_SUCCESS, 256}; } -ExecutionResult bls12_g2msm_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept +ExecutionResult bls12_g2msm_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 % 288 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 256); + + if (!crypto::bls::g2_msm(output, &output[128], input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 256}; } ExecutionResult bls12_pairing_check_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept diff --git a/test/unittests/precompiles_bls_test.cpp b/test/unittests/precompiles_bls_test.cpp index fd4ae58116..8b7f50c145 100644 --- a/test/unittests/precompiles_bls_test.cpp +++ b/test/unittests/precompiles_bls_test.cpp @@ -139,3 +139,120 @@ TEST(bls, g2_mul) EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); } + +TEST(bls, g1_msm) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f17" + "1bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e" + "30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000" + "00000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_msm_inf_0) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e10000000000000000000000000000000000000000000000000000000000000000"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_msm_inf_2) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm_inf_0) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000000"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm_inf_2) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +}