Skip to content

Commit

Permalink
precompiles: Implement bls g1 add.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Sep 3, 2024
1 parent 112147e commit 9d4a9bc
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cmake/blst.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 3 additions & 1 deletion lib/evmone_precompiles/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "bls.hpp"
#include <blst.hpp>

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);

const blst::blst_p1_affine p0_affine{x0, y0};
if (!blst::blst_p1_affine_on_curve(&p0_affine))
return false;

const 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
19 changes: 19 additions & 0 deletions lib/evmone_precompiles/bls.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <intx/intx.hpp>

// TODO(intx): Add ""_u384.
inline constexpr auto operator""_u384(const char* s)
{
return intx::from_string<intx::uint384>(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
47 changes: 47 additions & 0 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
#include "precompiles_internal.hpp"
#include "precompiles_stubs.hpp"
#include <evmone_precompiles/blake2b.hpp>
#include <evmone_precompiles/bls.hpp>
#include <evmone_precompiles/bn254.hpp>
#include <evmone_precompiles/ripemd160.hpp>
#include <evmone_precompiles/secp256k1.hpp>
#include <evmone_precompiles/sha256.hpp>
#include <intx/intx.hpp>
#include <algorithm>
#include <array>
#include <bit>
#include <cassert>
#include <limits>
#include <span>

#ifdef EVMONE_PRECOMPILES_SILKPRE
#include "precompiles_silkpre.hpp"
Expand Down Expand Up @@ -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

Check warning on line 159 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L159

Added line #L159 was not covered by tests
{
static constexpr auto BLS12_G1ADD_PRECOMPILE_GAS = 500;
return {BLS12_G1ADD_PRECOMPILE_GAS, 128};

Check warning on line 162 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L162

Added line #L162 was not covered by tests
}

ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output,
[[maybe_unused]] size_t output_size) noexcept
{
Expand Down Expand Up @@ -292,6 +301,40 @@ ExecutionResult blake2bf_execute(const uint8_t* input, [[maybe_unused]] size_t i
return {EVMC_SUCCESS, sizeof(h)};
}

ExecutionResult bls12_g1add_execute(

Check warning on line 304 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L304

Added line #L304 was not covered by tests
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};

Check warning on line 311 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L310-L311

Added lines #L310 - L311 were not covered by tests

if (output_size != 128)
return {EVMC_PRECOMPILE_FAILURE, 0};

Check warning on line 314 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L313-L314

Added lines #L313 - L314 were not covered by tests

memset(output, 0, output_size);

Check warning on line 316 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L316

Added line #L316 was not covered by tests

constexpr auto verify_element = [](const uint8_t* p) {
return intx::be::unsafe::load<intx::uint512>(p) < crypto::bls::FieldPrime;

Check warning on line 319 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L318-L319

Added lines #L318 - L319 were not covered by tests
};

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};

Check warning on line 329 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L322-L329

Added lines #L322 - L329 were not covered by tests

if (!crypto::bls::g1_add(&output[16], &output[16 + 64], &input[16], &input[16 + 64],

Check warning on line 331 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L331

Added line #L331 was not covered by tests
&input[16 + 128], &input[16 + 192]))
return {EVMC_PRECOMPILE_FAILURE, 0};

Check warning on line 333 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L333

Added line #L333 was not covered by tests

return {EVMC_SUCCESS, 128};

Check warning on line 335 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L335

Added line #L335 was not covered by tests
}

namespace
{
struct PrecompileTraits
Expand All @@ -313,6 +356,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<size_t>(PrecompileId::ecrecover)].execute = silkpre_ecrecover_execute;
Expand Down Expand Up @@ -348,6 +392,9 @@ bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept
if (rev < EVMC_CANCUN && id >= stdx::to_underlying(PrecompileId::since_cancun))
return false;

if (rev < EVMC_PRAGUE && id >= stdx::to_underlying(PrecompileId::since_prague))
return false;

return true;
}

Expand Down
4 changes: 3 additions & 1 deletion test/state/precompiles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions test/state/precompiles_internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions test/unittests/precompiles_bls_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2024 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <evmc/bytes.hpp>
#include <evmone_precompiles/bls.hpp>
#include <gtest/gtest.h>
#include <test/utils/utils.hpp>
#include <array>

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);
}

TEST(bls, g1_add_not_on_curve)
{
{
const auto x0 =
"17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6ba"_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_FALSE(
evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data()));
}
{
const auto x0 =
"17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex;
const auto y0 =
"08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex;
const auto x1 =
"112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex;
const auto y1 =
"186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a22"_hex;

uint8_t rx[48];
uint8_t ry[48];

EXPECT_FALSE(
evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data()));
}
}
4 changes: 3 additions & 1 deletion test/unittests/state_precompiles_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ TEST(state_precompiles, is_precompile)
// Cancun:
EXPECT_EQ(is_precompile(rev, 0x0a_address), rev >= EVMC_CANCUN);

// Prague:
EXPECT_EQ(is_precompile(rev, 0x0b_address), rev >= EVMC_PRAGUE);

// Future?
EXPECT_FALSE(is_precompile(rev, 0x0b_address));
EXPECT_FALSE(is_precompile(rev, 0x0c_address));
EXPECT_FALSE(is_precompile(rev, 0x0d_address));
EXPECT_FALSE(is_precompile(rev, 0x0e_address));
Expand Down

0 comments on commit 9d4a9bc

Please sign in to comment.