Skip to content

Commit

Permalink
Refactor precompiles detection
Browse files Browse the repository at this point in the history
Extract a precompile address recognition into new is_precompile()
function and use it in all places where this information is needed.

Improve maintenance of "precompile ids" by adding aliases to the enum.

Add unit tests for is_precompile().
  • Loading branch information
chfast committed Nov 23, 2023
1 parent 0bcf0c5 commit 33a3504
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 19 deletions.
6 changes: 3 additions & 3 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ evmc::Result Host::execute_message(const evmc_message& msg) noexcept
dst_acc->balance += value;
}

if (auto precompiled_result = call_precompile(m_rev, msg); precompiled_result.has_value())
return std::move(*precompiled_result);
if (is_precompile(m_rev, msg.code_address))
return call_precompile(m_rev, msg);

// Copy of the code. Revert will invalidate the account.
const auto code = dst_acc != nullptr ? dst_acc->code : bytes{};
Expand Down Expand Up @@ -350,7 +350,7 @@ evmc_access_status Host::access_account(const address& addr) noexcept
const auto status = std::exchange(acc.access_status, EVMC_ACCESS_WARM);

// Overwrite status for precompiled contracts: they are always warm.
if (status == EVMC_ACCESS_COLD && addr >= 0x01_address && addr <= 0x09_address)
if (status == EVMC_ACCESS_COLD && is_precompile(m_rev, addr))
return EVMC_ACCESS_WARM;

return status;
Expand Down
30 changes: 18 additions & 12 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,25 +278,31 @@ inline constexpr auto traits = []() noexcept {
}();
} // namespace

std::optional<evmc::Result> call_precompile(evmc_revision rev, const evmc_message& msg) noexcept
bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept
{
// Define compile-time constant,
// TODO: workaround for Clang Analyzer bug https://github.com/llvm/llvm-project/issues/59493.
static constexpr evmc::address address_boundary{NumPrecompiles};
// TODO(clang18): workaround for Clang Analyzer bug, fixed in clang 18.
// https://github.com/llvm/llvm-project/issues/59493.
static constexpr evmc::address address_boundary{stdx::to_underlying(PrecompileId::latest)};

if (evmc::is_zero(msg.code_address) || msg.code_address >= address_boundary)
return {};
if (evmc::is_zero(addr) || addr > address_boundary)
return false;

const auto id = msg.code_address.bytes[19];
if (rev < EVMC_BYZANTIUM && id > 4)
return {};
const auto id = addr.bytes[19];
if (rev < EVMC_BYZANTIUM && id >= stdx::to_underlying(PrecompileId::since_byzantium))
return false;

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

if (rev < EVMC_ISTANBUL && id > 8)
return {};
return true;
}

assert(id > 0);
evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) noexcept
{
assert(msg.gas >= 0);

const auto id = msg.code_address.bytes[19];
const auto [analyze, execute] = traits[id];

const bytes_view input{msg.input_data, msg.input_size};
Expand All @@ -307,7 +313,7 @@ std::optional<evmc::Result> call_precompile(evmc_revision rev, const evmc_messag

static Cache cache;
if (auto r = cache.find(static_cast<PrecompileId>(id), input, gas_left); r.has_value())
return r;
return std::move(*r);

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

// Buffer for the precompile's output.
// Big enough to handle all "expmod" tests, but in case does not match the size requirement
Expand Down
17 changes: 13 additions & 4 deletions test/state/precompiles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

namespace evmone::state
{
/// The total number of known precompiles ids, including 0.
inline constexpr std::size_t NumPrecompiles = 10;

/// The precompile identifiers and their corresponding addresses.
enum class PrecompileId : uint8_t
{
ecrecover = 0x01,
Expand All @@ -23,13 +21,24 @@ enum class PrecompileId : uint8_t
ecmul = 0x07,
ecpairing = 0x08,
blake2bf = 0x09,

since_byzantium = expmod, ///< The first precompile introduced in Byzantium.
since_istanbul = blake2bf, ///< The first precompile introduced in Istanbul.
latest = blake2bf ///< The latest introduced precompile (highest address).
};

/// The total number of known precompiles ids, including 0.
inline constexpr std::size_t NumPrecompiles = stdx::to_underlying(PrecompileId::latest) + 1;

struct ExecutionResult
{
evmc_status_code status_code;
size_t output_size;
};

std::optional<evmc::Result> call_precompile(evmc_revision rev, const evmc_message& msg) noexcept;
/// Checks if the address @p addr is considered a precompiled contract in the revision @p rev.
bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept;

/// Executes the message to a precompiled contract (msg.code_address must be a precompile).
evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) 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 @@ -44,6 +44,7 @@ target_sources(
state_mpt_hash_test.cpp
state_mpt_test.cpp
state_new_account_address_test.cpp
state_precompiles_test.cpp
state_rlp_test.cpp
state_transition.hpp
state_transition.cpp
Expand Down
42 changes: 42 additions & 0 deletions test/unittests/state_precompiles_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <test/state/precompiles.hpp>

using namespace evmc;
using namespace evmone::state;

TEST(state_precompiles, is_precompile)
{
for (int r = 0; r <= EVMC_MAX_REVISION; ++r)
{
const auto rev = static_cast<evmc_revision>(r);

EXPECT_FALSE(is_precompile(rev, 0x00_address));

// Frontier:
EXPECT_TRUE(is_precompile(rev, 0x01_address));
EXPECT_TRUE(is_precompile(rev, 0x02_address));
EXPECT_TRUE(is_precompile(rev, 0x03_address));
EXPECT_TRUE(is_precompile(rev, 0x04_address));

// Byzantium:
EXPECT_EQ(is_precompile(rev, 0x05_address), rev >= EVMC_BYZANTIUM);
EXPECT_EQ(is_precompile(rev, 0x06_address), rev >= EVMC_BYZANTIUM);
EXPECT_EQ(is_precompile(rev, 0x07_address), rev >= EVMC_BYZANTIUM);
EXPECT_EQ(is_precompile(rev, 0x08_address), rev >= EVMC_BYZANTIUM);

// Istanbul:
EXPECT_EQ(is_precompile(rev, 0x09_address), rev >= EVMC_ISTANBUL);

// Future?
EXPECT_FALSE(is_precompile(rev, 0x0a_address));
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));
EXPECT_FALSE(is_precompile(rev, 0x0f_address));
}
}

0 comments on commit 33a3504

Please sign in to comment.