diff --git a/cmake/blst.cmake b/cmake/blst.cmake index 52e15439dc..9ddbdae57b 100644 --- a/cmake/blst.cmake +++ b/cmake/blst.cmake @@ -5,7 +5,7 @@ if(MSVC) set(BLST_BUILD_SCRIPT build.bat) elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") # Don't pass CC to the script because it doesn't work. - set(BLST_BUILD_SCRIPT ./build.sh -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(BLST_BUILD_SCRIPT ./build.sh) else() # Pass CC and AR, this supports cross-compilation. # Pass CFLAGS as part of CC (e.g. to pass -m32). Using CFLAGS directly overwrites blst's default. 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..6e1dac9878 --- /dev/null +++ b/lib/evmone_precompiles/bls.cpp @@ -0,0 +1,42 @@ +#include "bls.hpp" +#include + +namespace evmone::crypto::bls +{ +bool g1_add(uint8_t* _rx, uint8_t* _ry, const uint8_t _x0[48], const uint8_t _y0[48], + const uint8_t _x1[48], const uint8_t _y1[48]) +{ + blst::blst_fp x0; + blst::blst_fp y0; + blst::blst_fp x1; + blst::blst_fp y1; + blst::blst_fp_from_bendian(&x0, _x0); + blst::blst_fp_from_bendian(&y0, _y0); + blst::blst_fp_from_bendian(&x1, _x1); + blst::blst_fp_from_bendian(&y1, _y1); + + blst::blst_p1_affine p0_affine{x0, y0}; + if (!blst::blst_p1_affine_on_curve(&p0_affine)) + return false; + + blst::blst_p1_affine p1_affine{x1, y1}; + if (!blst::blst_p1_affine_on_curve(&p1_affine)) + return false; + + blst::blst_p1 p0; + blst::blst_p1 p1; + blst::blst_p1_from_affine(&p0, &p0_affine); + blst::blst_p1_from_affine(&p1, &p1_affine); + + blst::blst_p1 out; + blst::blst_p1_add_or_double(&out, &p0, &p1); + + blst::blst_p1_affine result; + blst::blst_p1_to_affine(&result, &out); + + blst::blst_bendian_from_fp(_rx, &result.x); + blst::blst_bendian_from_fp(_ry, &result.y); + + 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..44e5fd3dd2 --- /dev/null +++ b/lib/evmone_precompiles/bls.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +// TODO(intx): Add ""_u384. +inline constexpr auto operator""_u384(const char* s) +{ + return intx::from_string(s); +} + +namespace evmone::crypto::bls +{ +inline constexpr auto FieldPrime = + 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab_u384; + +bool g1_add(uint8_t* _rx, uint8_t* _ry, const uint8_t _x0[48], const uint8_t _y0[48], + const uint8_t _x1[48], const uint8_t _y1[48]); + +} // namespace evmone::crypto::bls diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 37cea02ae2..d4d5c2125e 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -6,15 +6,18 @@ #include "precompiles_internal.hpp" #include "precompiles_stubs.hpp" #include +#include #include #include #include #include #include +#include #include #include #include #include +#include #ifdef EVMONE_PRECOMPILES_SILKPRE #include "precompiles_silkpre.hpp" @@ -153,6 +156,12 @@ PrecompileAnalysis point_evaluation_analyze(bytes_view, evmc_revision) noexcept return {POINT_EVALUATION_PRECOMPILE_GAS, 64}; } +PrecompileAnalysis bls12_g1add_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_G1ADD_PRECOMPILE_GAS = 500; + return {BLS12_G1ADD_PRECOMPILE_GAS, 128}; +} + ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output, [[maybe_unused]] size_t output_size) noexcept { @@ -292,6 +301,50 @@ ExecutionResult blake2bf_execute(const uint8_t* input, [[maybe_unused]] size_t i return {EVMC_SUCCESS, sizeof(h)}; } +ExecutionResult bls12_g1add_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept +{ + static_assert(std::endian::native == std::endian::little, + "bls12_g1add only works correctly on little-endian architectures"); + + if (input_size != 256) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + if (output_size != 128) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + memset(output, 0, output_size); + + constexpr auto verify_element = [](const uint8_t* p) { + constexpr auto check_zero_padded = [](const uint8_t* v) { + std::span s(v, 16); + if (!std::all_of(s.begin(), s.end(), [](const auto& i) { return i == 0; })) + return false; + return true; + }; + constexpr auto is_field_element = [](const uint8_t* v) { + return intx::be::unsafe::load(v) < crypto::bls::FieldPrime; + }; + + return check_zero_padded(p) && is_field_element(&p[16]); + }; + + if (!verify_element(input)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + if (!verify_element(&input[64])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + if (!verify_element(&input[128])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + if (!verify_element(&input[192])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + if (!crypto::bls::g1_add(&output[16], &output[16 + 64], &input[16], &input[16 + 64], + &input[16 + 128], &input[16 + 192])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; +} + namespace { struct PrecompileTraits @@ -313,6 +366,7 @@ inline constexpr auto traits = []() noexcept { {ecpairing_analyze, ecpairing_stub}, {blake2bf_analyze, blake2bf_execute}, {point_evaluation_analyze, point_evaluation_stub}, + {bls12_g1add_analyze, bls12_g1add_execute}, }}; #ifdef EVMONE_PRECOMPILES_SILKPRE // tbl[static_cast(PrecompileId::ecrecover)].execute = silkpre_ecrecover_execute; diff --git a/test/state/precompiles.hpp b/test/state/precompiles.hpp index 501cb33f57..b814e3569e 100644 --- a/test/state/precompiles.hpp +++ b/test/state/precompiles.hpp @@ -22,11 +22,13 @@ enum class PrecompileId : uint8_t ecpairing = 0x08, blake2bf = 0x09, point_evaluation = 0x0a, + bls12_g1add = 0x0b, since_byzantium = expmod, ///< The first precompile introduced in Byzantium. since_istanbul = blake2bf, ///< The first precompile introduced in Istanbul. since_cancun = point_evaluation, ///< The first precompile introduced in Cancun. - latest = point_evaluation ///< The latest introduced precompile (highest address). + since_prague = bls12_g1add, ///< The first precompile introduced in Prague. + latest = bls12_g1add ///< The latest introduced precompile (highest address). }; /// The total number of known precompiles ids, including 0. diff --git a/test/state/precompiles_internal.hpp b/test/state/precompiles_internal.hpp index 4a7307a015..b90c190667 100644 --- a/test/state/precompiles_internal.hpp +++ b/test/state/precompiles_internal.hpp @@ -29,6 +29,7 @@ PrecompileAnalysis ecmul_analyze(evmc::bytes_view input, evmc_revision rev) noex PrecompileAnalysis ecpairing_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; PrecompileAnalysis blake2bf_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; PrecompileAnalysis point_evaluation_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g1add_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; ExecutionResult ecrecover_execute( const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; @@ -44,4 +45,6 @@ ExecutionResult ecmul_execute( const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; ExecutionResult blake2bf_execute( const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g1add_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; } // namespace evmone::state 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..64da9d9fdf --- /dev/null +++ b/test/unittests/precompiles_bls_test.cpp @@ -0,0 +1,36 @@ +// 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 = + "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y0 = + "08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21"_hex; + + uint8_t rx[48]; + uint8_t ry[48]; + + EXPECT_TRUE(evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + + const auto expected_x = + "0a40300ce2dec9888b60690e9a41d3004fda4886854573974fab73b046d3147ba5b7a5bde85279ffede1b45b3918d82d"_hex; + const auto expected_y = + "06d3d887e9f53b9ec4eb6cedf5607226754b07c01ace7834f57f3e7315faefb739e59018e22c492006190fba4a870025"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +}