diff --git a/circle.yml b/circle.yml index 9b367bdd5d..44bd6e496e 100644 --- a/circle.yml +++ b/circle.yml @@ -371,7 +371,9 @@ jobs: # Tests for in-development EVM revision currently passing. working_directory: ~/spec-tests/fixtures/state_tests command: > - ~/build/bin/evmone-statetest prague/eip2537_bls_12_381_precompiles/bls12_precompiles_before_fork + ~/build/bin/evmone-statetest + prague/eip2537_bls_12_381_precompiles/bls12_precompiles_before_fork + prague/eip2537_bls_12_381_precompiles/bls12_g1add - collect_coverage_gcc - upload_coverage: flags: execution_spec_tests diff --git a/lib/evmone_precompiles/CMakeLists.txt b/lib/evmone_precompiles/CMakeLists.txt index 1a41ae0253..1edcb62792 100644 --- a/lib/evmone_precompiles/CMakeLists.txt +++ b/lib/evmone_precompiles/CMakeLists.txt @@ -6,11 +6,13 @@ include(blst) add_library(evmone_precompiles STATIC) add_library(evmone::precompiles ALIAS evmone_precompiles) -target_link_libraries(evmone_precompiles PUBLIC evmc::evmc_cpp PRIVATE evmone::evmmax) +target_link_libraries(evmone_precompiles PUBLIC evmc::evmc_cpp PRIVATE evmone::evmmax blst::blst) target_sources( evmone_precompiles PRIVATE blake2b.hpp blake2b.cpp + bls.hpp + bls.cpp bn254.hpp bn254.cpp ecc.hpp diff --git a/lib/evmone_precompiles/bls.cpp b/lib/evmone_precompiles/bls.cpp new file mode 100644 index 0000000000..b795e53bd3 --- /dev/null +++ b/lib/evmone_precompiles/bls.cpp @@ -0,0 +1,71 @@ +#include "bls.hpp" +#include +#include + +namespace evmone::crypto::bls +{ +namespace +{ +/// Offset of the beginning of field element. First 16 bytes must be zero according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#field-elements-encoding +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 validate_point( + 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)) + return std::nullopt; + if (!is_field_element(_y)) + 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}; + if (!blst_p1_affine_on_curve(&p0_affine)) + return std::nullopt; + + return p0_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 +{ + blst_p1_affine result; + blst_p1_to_affine(&result, out); + + std::memset(_rx, 0, FP_BYTES_OFFSET); + blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET], &result.x); + std::memset(_ry, 0, FP_BYTES_OFFSET); + blst_bendian_from_fp(&_ry[FP_BYTES_OFFSET], &result.y); +} +} // 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); + + if (!p0_affine.has_value() || !p1_affine.has_value()) + return false; + + blst_p1 p0; + blst_p1_from_affine(&p0, &*p0_affine); + + blst_p1 out; + blst_p1_add_or_double_affine(&out, &p0, &*p1_affine); + + store_result(_rx, _ry, &out); + + return true; +} +} // namespace evmone::crypto::bls diff --git a/lib/evmone_precompiles/bls.hpp b/lib/evmone_precompiles/bls.hpp new file mode 100644 index 0000000000..7cc403f1a3 --- /dev/null +++ b/lib/evmone_precompiles/bls.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +using namespace intx::literals; + +namespace evmone::crypto::bls +{ +/// The BLS12-381 field prime number. +inline constexpr auto BLS_FIELD_MODULUS = + 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab_u384; + +/// Addition in BLS12-381 curve group. +/// +/// Computes P ⊕ Q for two points in affine coordinates on the BLS12-381 curve, +[[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; + +} // namespace evmone::crypto::bls diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index a258d0a479..21c8056037 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -6,6 +6,7 @@ #include "precompiles_internal.hpp" #include "precompiles_stubs.hpp" #include +#include #include #include #include @@ -155,8 +156,8 @@ PrecompileAnalysis point_evaluation_analyze(bytes_view, evmc_revision) noexcept PrecompileAnalysis bls12_g1add_analyze(bytes_view, evmc_revision) noexcept { - // TODO: Implement - return {GasCostMax, 0}; + static constexpr auto BLS12_G1ADD_PRECOMPILE_GAS = 500; + return {BLS12_G1ADD_PRECOMPILE_GAS, 128}; } PrecompileAnalysis bls12_g1mul_analyze(bytes_view, evmc_revision) noexcept @@ -346,9 +347,18 @@ ExecutionResult blake2bf_execute(const uint8_t* input, [[maybe_unused]] size_t i return {EVMC_SUCCESS, sizeof(h)}; } -ExecutionResult bls12_g1add_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept +ExecutionResult bls12_g1add_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 != 256) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::g1_add(output, &output[64], input, &input[64], &input[128], &input[192])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; } ExecutionResult bls12_g1mul_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 36add75fe8..56796d2ab6 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -47,6 +47,7 @@ target_sources( exportable_fixture.cpp instructions_test.cpp precompiles_blake2b_test.cpp + precompiles_bls_test.cpp precompiles_ripemd160_test.cpp precompiles_sha256_test.cpp state_block_test.cpp diff --git a/test/unittests/precompiles_bls_test.cpp b/test/unittests/precompiles_bls_test.cpp new file mode 100644 index 0000000000..4be6c631e8 --- /dev/null +++ b/test/unittests/precompiles_bls_test.cpp @@ -0,0 +1,72 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +using evmone::test::operator""_hex; + +TEST(bls, g1_add) +{ + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + + const auto expected_x = + "000000000000000000000000000000000a40300ce2dec9888b60690e9a41d3004fda4886854573974fab73b046d3147ba5b7a5bde85279ffede1b45b3918d82d"_hex; + const auto expected_y = + "0000000000000000000000000000000006d3d887e9f53b9ec4eb6cedf5607226754b07c01ace7834f57f3e7315faefb739e59018e22c492006190fba4a870025"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_add_not_on_curve) +{ + { + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6ba"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_FALSE( + evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + } + { + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a22"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_FALSE( + evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + } +}