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 BLS multi scalar multiplication #1010

Merged
merged 1 commit into from
Sep 18, 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
2 changes: 2 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ jobs:
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
- run:
name: "Execution spec tests (develop, blockchain_tests)"
# Tests for in-development EVM revision currently passing.
Expand Down
132 changes: 132 additions & 0 deletions lib/evmone_precompiles/bls.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "bls.hpp"
#include <blst.h>
#include <memory>
#include <optional>
#include <vector>

namespace evmone::crypto::bls
{
Expand Down Expand Up @@ -183,4 +185,134 @@ void store(uint8_t _rx[128], const blst_fp2& _x) noexcept
return true;
}

[[nodiscard]] bool g1_msm(
uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept
{
constexpr auto SINGLE_ENTRY_SIZE = (64 * 2 + 32);
assert(size % SINGLE_ENTRY_SIZE == 0);
auto npoints = size / SINGLE_ENTRY_SIZE;

std::vector<blst_p1_affine> p1_affines;
std::vector<const blst_p1_affine*> p1_affine_ptrs;
p1_affines.reserve(npoints);
p1_affine_ptrs.reserve(npoints);

std::vector<blst_scalar> scalars;
std::vector<const uint8_t*> scalars_ptrs;
scalars.reserve(npoints);
scalars_ptrs.reserve(npoints);

auto ptr = _xycs;
for (size_t i = 0; i < npoints; ++i)
{
const auto p_affine = validate_p1(ptr, &ptr[64]);
if (!p_affine.has_value())
return false;

if (!blst_p1_affine_in_g1(&*p_affine))
return false;

// Point at infinity must be filtered out for BLST library.
if (blst_p1_affine_is_inf(&*p_affine))
continue;

const auto& p = p1_affines.emplace_back(*p_affine);
p1_affine_ptrs.emplace_back(&p);

blst_scalar scalar;
blst_scalar_from_bendian(&scalar, &ptr[128]);
const auto& s = scalars.emplace_back(scalar);
scalars_ptrs.emplace_back(s.b);

ptr += SINGLE_ENTRY_SIZE;
}

npoints = p1_affine_ptrs.size();

if (npoints == 0)
{
memset(_rx, 0, 64);
memset(_ry, 0, 64);
return true;
}

const auto scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t);
const auto scratch_space = std::make_unique_for_overwrite<limb_t[]>(scratch_size);
blst_p1 out;
blst_p1s_mult_pippenger(
&out, p1_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get());

blst_p1_affine result;
blst_p1_to_affine(&result, &out);
store(_rx, result.x);
store(_ry, result.y);

return true;
}

[[nodiscard]] bool g2_msm(
uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept
{
constexpr auto SINGLE_ENTRY_SIZE = (128 * 2 + 32);
assert(size % SINGLE_ENTRY_SIZE == 0);
auto npoints = size / SINGLE_ENTRY_SIZE;

std::vector<blst_p2_affine> p2_affines;
std::vector<const blst_p2_affine*> p2_affine_ptrs;
p2_affines.reserve(npoints);
p2_affine_ptrs.reserve(npoints);

std::vector<blst_scalar> scalars;
std::vector<const uint8_t*> scalars_ptrs;
scalars.reserve(npoints);
scalars_ptrs.reserve(npoints);

auto ptr = _xycs;
for (size_t i = 0; i < npoints; ++i)
{
const auto p_affine = validate_p2(ptr, &ptr[128]);
if (!p_affine.has_value())
return false;

if (!blst_p2_affine_in_g2(&*p_affine))
return false;

// Point at infinity must be filtered out for BLST library.
if (blst_p2_affine_is_inf(&*p_affine))
continue;

const auto& p = p2_affines.emplace_back(*p_affine);
p2_affine_ptrs.emplace_back(&p);

blst_scalar scalar;
blst_scalar_from_bendian(&scalar, &ptr[256]);
const auto& s = scalars.emplace_back(scalar);
scalars_ptrs.emplace_back(s.b);

ptr += SINGLE_ENTRY_SIZE;
}

npoints = p2_affine_ptrs.size();

if (npoints == 0)
{
memset(_rx, 0, 128);
memset(_ry, 0, 128);
return true;
}

const auto scratch_size = blst_p2s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t);
const auto scratch_space = std::make_unique_for_overwrite<limb_t[]>(scratch_size);
blst_p2 out;
blst_p2s_mult_pippenger(
&out, p2_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get());

blst_p2_affine result;
blst_p2_to_affine(&result, &out);
store(_rx, result.x);
store(_ry, result.y);

return true;
}

} // namespace evmone::crypto::bls
16 changes: 16 additions & 0 deletions lib/evmone_precompiles/bls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,20 @@ inline constexpr auto BLS_FIELD_MODULUS =
[[nodiscard]] bool g2_mul(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x[128],
const uint8_t _y[128], const uint8_t _c[32]) noexcept;

/// Multi scalar multiplication in BLS12-381 curve G1 subgroup.
///
/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve, performs
/// subgroup check according to spec
/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-msm
[[nodiscard]] bool g1_msm(
uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept;

/// Multi scalar multiplication in BLS12-381 curve G2 subgroup.
///
/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve over G2 extension
/// field, performs subgroup check according to spec
/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-msm
[[nodiscard]] bool g2_msm(
uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept;

} // namespace evmone::crypto::bls
63 changes: 53 additions & 10 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ inline constexpr int64_t cost_per_input_word(size_t input_size) noexcept
{
return BaseCost + WordCost * num_words(input_size);
}

int64_t bls_msm_cost(size_t k, int64_t multiplication_cost) noexcept
{
assert(k > 0);

static constexpr int64_t MULTIPLIER = 1000;
static constexpr int16_t DISCOUNT[128] = {1200, 888, 764, 641, 594, 547, 500, 453, 438, 423,
408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285,
281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248,
247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222,
221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208,
208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195,
194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182,
181, 180, 179, 179, 178, 177, 176, 176, 175, 174};

const auto d = DISCOUNT[std::min(k, std::size(DISCOUNT)) - 1];
return (static_cast<int64_t>(k) * multiplication_cost * d) / MULTIPLIER;
}

} // namespace

PrecompileAnalysis ecrecover_analyze(bytes_view /*input*/, evmc_revision /*rev*/) noexcept
Expand Down Expand Up @@ -166,10 +185,13 @@ PrecompileAnalysis bls12_g1mul_analyze(bytes_view, evmc_revision) noexcept
return {BLS12_G1MUL_PRECOMPILE_GAS, 128};
}

PrecompileAnalysis bls12_g1msm_analyze(bytes_view, evmc_revision) noexcept
PrecompileAnalysis bls12_g1msm_analyze(bytes_view input, evmc_revision) noexcept
{
// TODO: Implement
return {GasCostMax, 0};
if (input.empty() || input.size() % 160 != 0)
return {GasCostMax, 0};

static constexpr auto BLS12_G1MUL_PRECOMPILE_GAS = 12000;
return {bls_msm_cost(input.size() / 160, BLS12_G1MUL_PRECOMPILE_GAS), 128};
}

PrecompileAnalysis bls12_g2add_analyze(bytes_view, evmc_revision) noexcept
Expand All @@ -184,10 +206,13 @@ PrecompileAnalysis bls12_g2mul_analyze(bytes_view, evmc_revision) noexcept
return {BLS12_G2MUL_PRECOMPILE_GAS, 256};
}

PrecompileAnalysis bls12_g2msm_analyze(bytes_view, evmc_revision) noexcept
PrecompileAnalysis bls12_g2msm_analyze(bytes_view input, evmc_revision) noexcept
{
// TODO: Implement
return {GasCostMax, 0};
if (input.empty() || input.size() % 288 != 0)
return {GasCostMax, 0};

static constexpr auto BLS12_G2MUL_PRECOMPILE_GAS = 45000;
return {bls_msm_cost(input.size() / 288, BLS12_G2MUL_PRECOMPILE_GAS), 256};
}

PrecompileAnalysis bls12_pairing_check_analyze(bytes_view, evmc_revision) noexcept
Expand Down Expand Up @@ -375,9 +400,18 @@ ExecutionResult bls12_g1mul_execute(const uint8_t* input, size_t input_size, uin
return {EVMC_SUCCESS, 128};
}

ExecutionResult bls12_g1msm_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
ExecutionResult bls12_g1msm_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 % 160 != 0)
return {EVMC_PRECOMPILE_FAILURE, 0};

assert(output_size == 128);

if (!crypto::bls::g1_msm(output, &output[64], input, input_size))
return {EVMC_PRECOMPILE_FAILURE, 0};

return {EVMC_SUCCESS, 128};
}

ExecutionResult bls12_g2add_execute(const uint8_t* input, size_t input_size, uint8_t* output,
Expand Down Expand Up @@ -408,9 +442,18 @@ ExecutionResult bls12_g2mul_execute(const uint8_t* input, size_t input_size, uin
return {EVMC_SUCCESS, 256};
}

ExecutionResult bls12_g2msm_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
ExecutionResult bls12_g2msm_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 % 288 != 0)
return {EVMC_PRECOMPILE_FAILURE, 0};

assert(output_size == 256);

if (!crypto::bls::g2_msm(output, &output[128], input, input_size))
return {EVMC_PRECOMPILE_FAILURE, 0};

return {EVMC_SUCCESS, 256};
}

ExecutionResult bls12_pairing_check_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
Expand Down
117 changes: 117 additions & 0 deletions test/unittests/precompiles_bls_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,120 @@ TEST(bls, g2_mul)
EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g1_msm)
{
using namespace evmc::literals;
auto input =
"0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f17"
"1bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e"
"30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000"
"00000000000000000000000000000000000000000000000002"_hex;
uint8_t rx[64];
uint8_t ry[64];

EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex;
const auto expected_y =
"00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g1_msm_inf_0)
{
using namespace evmc::literals;
auto input =
"0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e10000000000000000000000000000000000000000000000000000000000000000"_hex;
uint8_t rx[64];
uint8_t ry[64];

EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex;
const auto expected_y =
"00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g1_msm_inf_2)
{
using namespace evmc::literals;
auto input =
"0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex;
uint8_t rx[64];
uint8_t ry[64];

EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex;
const auto expected_y =
"00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g2_msm)
{
using namespace evmc::literals;
auto input =
"00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002"_hex;
uint8_t rx[128];
uint8_t ry[128];

EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex;
const auto expected_y =
"000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g2_msm_inf_0)
{
using namespace evmc::literals;
auto input =
"00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000000"_hex;
uint8_t rx[128];
uint8_t ry[128];

EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex;
const auto expected_y =
"000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}

TEST(bls, g2_msm_inf_2)
{
using namespace evmc::literals;
auto input =
"00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex;
uint8_t rx[128];
uint8_t ry[128];

EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size()));

const auto expected_x =
"000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex;
const auto expected_y =
"000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex;

EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x);
EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y);
}