diff --git a/circle.yml b/circle.yml index b29e1ef39f..8fb4f6580e 100644 --- a/circle.yml +++ b/circle.yml @@ -372,15 +372,7 @@ jobs: working_directory: ~/spec-tests/fixtures/state_tests command: > ~/build/bin/evmone-statetest - 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 - 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 + prague/eip2537_bls_12_381_precompiles - 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 4bcddbb847..9a377c44f7 100644 --- a/lib/evmone_precompiles/bls.cpp +++ b/lib/evmone_precompiles/bls.cpp @@ -356,4 +356,63 @@ void store(uint8_t _rx[128], const blst_fp2& _x) noexcept return true; } +[[nodiscard]] bool pairing_check(uint8_t _r[32], const uint8_t* _pairs, size_t size) noexcept +{ + static constexpr auto FP_SIZE = 64; + static constexpr auto FP2_SIZE = 2 * FP_SIZE; + static constexpr auto P1_SIZE = 2 * FP_SIZE; + static constexpr auto P2_SIZE = 2 * FP2_SIZE; + static constexpr auto PAIR_SIZE = P1_SIZE + P2_SIZE; + assert(size % PAIR_SIZE == 0); + + const auto npairs = size / PAIR_SIZE; + auto ptr = _pairs; + auto has_inf = false; + blst_fp12 acc = *blst_fp12_one(); + + const auto Qlines = std::make_unique_for_overwrite(68); + + for (size_t i = 0; i < npairs; ++i) + { + const auto P_affine = validate_p1(ptr, &ptr[FP_SIZE]); + if (!P_affine.has_value()) + return false; + + const auto Q_affine = validate_p2(&ptr[P1_SIZE], &ptr[P1_SIZE + FP2_SIZE]); + if (!Q_affine.has_value()) + return false; + + if (!blst_p1_affine_in_g1(&*P_affine)) + return false; + + if (!blst_p2_affine_in_g2(&*Q_affine)) + return false; + + if (blst_p1_affine_is_inf(&*P_affine) || blst_p2_affine_is_inf(&*Q_affine)) + has_inf = true; + + if (!has_inf) + { + blst_precompute_lines(Qlines.get(), &*Q_affine); + + blst_fp12 ml_res; + blst_miller_loop_lines(&ml_res, Qlines.get(), &*P_affine); + blst_fp12_mul(&acc, &acc, &ml_res); + } + + ptr += PAIR_SIZE; + } + + bool result = true; + if (!has_inf) // if any point-at-infinity encountered the answer is 1, so skip final exp. + { + blst_final_exp(&acc, &acc); + result = blst_fp12_is_one(&acc); + } + + std::memset(_r, 0, 31); + _r[31] = result ? 1 : 0; + return true; +} + } // namespace evmone::crypto::bls diff --git a/lib/evmone_precompiles/bls.hpp b/lib/evmone_precompiles/bls.hpp index 0daeb3e038..eac6b4ada0 100644 --- a/lib/evmone_precompiles/bls.hpp +++ b/lib/evmone_precompiles/bls.hpp @@ -70,4 +70,10 @@ inline constexpr auto BLS_FIELD_MODULUS = [[nodiscard]] bool map_fp2_to_g2( uint8_t _rx[128], uint8_t _ry[128], const uint8_t _fp[128]) noexcept; +/// Computes pairing for pairs of P and Q point from G1 and G2 accordingly. +/// +/// Performs filed and groups check for both input points. Returns 'false' if any of requirement is +/// not met according to spec https://eips.ethereum.org/EIPS/eip-2537#abi-for-pairing-check +[[nodiscard]] bool pairing_check(uint8_t _r[32], const uint8_t* _pairs, size_t size) noexcept; + } // namespace evmone::crypto::bls diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 71521cc308..8e11353ff4 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -215,10 +215,20 @@ PrecompileAnalysis bls12_g2msm_analyze(bytes_view input, evmc_revision) noexcept return {bls_msm_cost(input.size() / 288, BLS12_G2MUL_PRECOMPILE_GAS), 256}; } -PrecompileAnalysis bls12_pairing_check_analyze(bytes_view, evmc_revision) noexcept +PrecompileAnalysis bls12_pairing_check_analyze(bytes_view input, evmc_revision) noexcept { - // TODO: Implement - return {GasCostMax, 0}; + static constexpr auto PAIR_SIZE = 384; + + if (input.empty() || input.size() % PAIR_SIZE != 0) + return {GasCostMax, 0}; + + const auto npairs = static_cast(input.size()) / PAIR_SIZE; + + static constexpr auto BLS12_PAIRING_CHECK_BASE_FEE_PRECOMPILE_GAS = 65000; + static constexpr auto BLS12_PAIRING_CHECK_FEE_PRECOMPILE_GAS = 43000; + return {BLS12_PAIRING_CHECK_BASE_FEE_PRECOMPILE_GAS + + BLS12_PAIRING_CHECK_FEE_PRECOMPILE_GAS * npairs, + 32}; } PrecompileAnalysis bls12_map_fp_to_g1_analyze(bytes_view, evmc_revision) noexcept @@ -456,9 +466,18 @@ ExecutionResult bls12_g2msm_execute(const uint8_t* input, size_t input_size, uin return {EVMC_SUCCESS, 256}; } -ExecutionResult bls12_pairing_check_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept +ExecutionResult bls12_pairing_check_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 % 384 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 32); + + if (!crypto::bls::pairing_check(output, input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 32}; } ExecutionResult bls12_map_fp_to_g1_execute(const uint8_t* input, size_t input_size, uint8_t* output, diff --git a/test/unittests/precompiles_bls_test.cpp b/test/unittests/precompiles_bls_test.cpp index 378dff8c5b..228428edcf 100644 --- a/test/unittests/precompiles_bls_test.cpp +++ b/test/unittests/precompiles_bls_test.cpp @@ -294,3 +294,17 @@ TEST(bls, map_fp2_to_g2) EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); } + +TEST(bls, paring_check) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca942600000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a2100000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be000000000000000000000000000000000a40300ce2dec9888b60690e9a41d3004fda4886854573974fab73b046d3147ba5b7a5bde85279ffede1b45b3918d82d0000000000000000000000000000000006d3d887e9f53b9ec4eb6cedf5607226754b07c01ace7834f57f3e7315faefb739e59018e22c492006190fba4a87002500000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000d1b3cc2c7027888be51d9ef691d77bcb679afda66c73f17f9ee3837a55024f78c71363275a75d75d86bab79f74782aa0000000000000000000000000000000013fa4d4a0ad8b1ce186ed5061789213d993923066dddaf1040bc3ff59f825c78df74f2d75467e25e0f55f8a00fa030ed"_hex; + uint8_t r[32]; + + EXPECT_TRUE(evmone::crypto::bls::pairing_check(r, input.data(), input.size())); + + const auto expected = "0000000000000000000000000000000000000000000000000000000000000001"_hex; + + EXPECT_EQ(evmc::bytes_view(r, sizeof r), expected); +}