Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

precompiles: Implement EIP-2537's bls12_g1add #982

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
71 changes: 71 additions & 0 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "bls.hpp"
#include <blst.h>
#include <optional>

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<blst_p1_affine> 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<intx::uint512>(_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
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>

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
18 changes: 14 additions & 4 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
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 =
"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()));
}
}