LCOV - differential code coverage report
Current view: top level - test/state - precompiles.cpp (source / functions) Coverage Total Hit UBC CBC
Current: DIFF_COVERAGE Lines: 2.0 % 153 3 150 3
Current Date: 2024-03-20 16:29:22 Functions: 3.4 % 29 1 28 1
Baseline: coverage_BASE.lcov
Baseline Date: 2024-03-20 14:19:08

           TLA  Line data    Source code
       1                 : // evmone: Fast Ethereum Virtual Machine implementation
       2                 : // Copyright 2022 The evmone Authors.
       3                 : // SPDX-License-Identifier: Apache-2.0
       4                 : 
       5                 : #include "precompiles.hpp"
       6                 : #include "precompiles_cache.hpp"
       7                 : #include "precompiles_internal.hpp"
       8                 : #include <evmone_precompiles/bn254.hpp>
       9                 : #include <evmone_precompiles/secp256k1.hpp>
      10                 : #include <intx/intx.hpp>
      11                 : #include <bit>
      12                 : #include <cassert>
      13                 : #include <iostream>
      14                 : #include <limits>
      15                 : #include <unordered_map>
      16                 : 
      17                 : #ifdef EVMONE_PRECOMPILES_SILKPRE
      18                 : #include "precompiles_silkpre.hpp"
      19                 : #endif
      20                 : 
      21                 : namespace evmone::state
      22                 : {
      23                 : using namespace evmc::literals;
      24                 : 
      25                 : namespace
      26                 : {
      27                 : constexpr auto GasCostMax = std::numeric_limits<int64_t>::max();
      28                 : 
      29 UBC           0 : inline constexpr int64_t num_words(size_t size_in_bytes) noexcept
      30                 : {
      31               0 :     return static_cast<int64_t>((size_in_bytes + 31) / 32);
      32                 : }
      33                 : 
      34                 : template <int BaseCost, int WordCost>
      35               0 : inline constexpr int64_t cost_per_input_word(size_t input_size) noexcept
      36                 : {
      37               0 :     return BaseCost + WordCost * num_words(input_size);
      38                 : }
      39                 : }  // namespace
      40                 : 
      41               0 : PrecompileAnalysis ecrecover_analyze(bytes_view /*input*/, evmc_revision /*rev*/) noexcept
      42                 : {
      43               0 :     return {3000, 32};
      44                 : }
      45                 : 
      46               0 : PrecompileAnalysis sha256_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
      47                 : {
      48               0 :     return {cost_per_input_word<60, 12>(input.size()), 32};
      49                 : }
      50                 : 
      51               0 : PrecompileAnalysis ripemd160_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
      52                 : {
      53               0 :     return {cost_per_input_word<600, 120>(input.size()), 32};
      54                 : }
      55                 : 
      56               0 : PrecompileAnalysis identity_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
      57                 : {
      58               0 :     return {cost_per_input_word<15, 3>(input.size()), input.size()};
      59                 : }
      60                 : 
      61               0 : PrecompileAnalysis ecadd_analyze(bytes_view /*input*/, evmc_revision rev) noexcept
      62                 : {
      63               0 :     return {rev >= EVMC_ISTANBUL ? 150 : 500, 64};
      64                 : }
      65                 : 
      66               0 : PrecompileAnalysis ecmul_analyze(bytes_view /*input*/, evmc_revision rev) noexcept
      67                 : {
      68               0 :     return {rev >= EVMC_ISTANBUL ? 6000 : 40000, 64};
      69                 : }
      70                 : 
      71               0 : PrecompileAnalysis ecpairing_analyze(bytes_view input, evmc_revision rev) noexcept
      72                 : {
      73               0 :     const auto base_cost = (rev >= EVMC_ISTANBUL) ? 45000 : 100000;
      74               0 :     const auto element_cost = (rev >= EVMC_ISTANBUL) ? 34000 : 80000;
      75               0 :     const auto num_elements = static_cast<int64_t>(input.size() / 192);
      76               0 :     return {base_cost + num_elements * element_cost, 32};
      77                 : }
      78                 : 
      79               0 : PrecompileAnalysis blake2bf_analyze(bytes_view input, evmc_revision) noexcept
      80                 : {
      81               0 :     return {input.size() == 213 ? intx::be::unsafe::load<uint32_t>(input.data()) : GasCostMax, 64};
      82                 : }
      83                 : 
      84               0 : PrecompileAnalysis expmod_analyze(bytes_view input, evmc_revision rev) noexcept
      85                 : {
      86                 :     using namespace intx;
      87                 : 
      88                 :     static constexpr size_t input_header_required_size = 3 * sizeof(uint256);
      89               0 :     const int64_t min_gas = (rev >= EVMC_BERLIN) ? 200 : 0;
      90                 : 
      91               0 :     uint8_t input_header[input_header_required_size]{};
      92               0 :     std::copy_n(input.data(), std::min(input.size(), input_header_required_size), input_header);
      93                 : 
      94               0 :     const auto base_len = be::unsafe::load<uint256>(&input_header[0]);
      95               0 :     const auto exp_len = be::unsafe::load<uint256>(&input_header[32]);
      96               0 :     const auto mod_len = be::unsafe::load<uint256>(&input_header[64]);
      97                 : 
      98               0 :     if (base_len == 0 && mod_len == 0)
      99               0 :         return {min_gas, 0};
     100                 : 
     101                 :     static constexpr auto len_limit = std::numeric_limits<size_t>::max();
     102               0 :     if (base_len > len_limit || exp_len > len_limit || mod_len > len_limit)
     103               0 :         return {GasCostMax, 0};
     104                 : 
     105               0 :     auto adjusted_len = [input](size_t offset, size_t len) {
     106               0 :         const auto head_len = std::min(len, size_t{32});
     107                 :         const auto head_explicit_len =
     108               0 :             std::max(std::min(offset + head_len, input.size()), offset) - offset;
     109               0 :         const bytes_view head_explicit_bytes(&input[offset], head_explicit_len);
     110               0 :         const auto top_byte_index = head_explicit_bytes.find_first_not_of(uint8_t{0});
     111                 :         const size_t exp_bit_width =
     112               0 :             (top_byte_index != bytes_view::npos) ?
     113               0 :                 (head_len - top_byte_index - 1) * 8 +
     114               0 :                     static_cast<size_t>(std::bit_width(head_explicit_bytes[top_byte_index])) :
     115               0 :                 0;
     116                 : 
     117               0 :         return std::max(
     118               0 :             8 * (std::max(len, size_t{32}) - 32) + (std::max(exp_bit_width, size_t{1}) - 1),
     119               0 :             size_t{1});
     120               0 :     };
     121                 : 
     122               0 :     static constexpr auto mult_complexity_eip2565 = [](const uint256& x) noexcept {
     123               0 :         const auto w = (x + 7) >> 3;
     124               0 :         return w * w;
     125                 :     };
     126               0 :     static constexpr auto mult_complexity_eip198 = [](const uint256& x) noexcept {
     127               0 :         const auto x2 = x * x;
     128               0 :         return (x <= 64)   ? x2 :
     129               0 :                (x <= 1024) ? (x2 >> 2) + 96 * x - 3072 :
     130               0 :                              (x2 >> 4) + 480 * x - 199680;
     131                 :     };
     132                 : 
     133               0 :     const auto max_len = std::max(mod_len, base_len);
     134               0 :     const auto adjusted_exp_len = adjusted_len(
     135               0 :         sizeof(input_header) + static_cast<size_t>(base_len), static_cast<size_t>(exp_len));
     136               0 :     const auto gas = (rev >= EVMC_BERLIN) ?
     137               0 :                          mult_complexity_eip2565(max_len) * adjusted_exp_len / 3 :
     138               0 :                          mult_complexity_eip198(max_len) * adjusted_exp_len / 20;
     139               0 :     return {std::max(min_gas, static_cast<int64_t>(std::min(gas, intx::uint256{GasCostMax}))),
     140               0 :         static_cast<size_t>(mod_len)};
     141                 : }
     142                 : 
     143               0 : PrecompileAnalysis point_evaluation_analyze(bytes_view, evmc_revision) noexcept
     144                 : {
     145                 :     static constexpr auto POINT_EVALUATION_PRECOMPILE_GAS = 50000;
     146               0 :     return {POINT_EVALUATION_PRECOMPILE_GAS, 64};
     147                 : }
     148                 : 
     149               0 : ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output,
     150                 :     [[maybe_unused]] size_t output_size) noexcept
     151                 : {
     152               0 :     assert(output_size >= 32);
     153                 : 
     154               0 :     uint8_t input_buffer[128]{};
     155               0 :     if (input_size != 0)
     156               0 :         std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
     157                 : 
     158               0 :     ethash::hash256 h{};
     159               0 :     std::memcpy(h.bytes, input_buffer, sizeof(h));
     160                 : 
     161               0 :     const auto v = intx::be::unsafe::load<intx::uint256>(input_buffer + 32);
     162               0 :     if (v != 27 && v != 28)
     163               0 :         return {EVMC_SUCCESS, 0};
     164               0 :     const bool parity = v == 28;
     165                 : 
     166               0 :     const auto r = intx::be::unsafe::load<intx::uint256>(input_buffer + 64);
     167               0 :     const auto s = intx::be::unsafe::load<intx::uint256>(input_buffer + 96);
     168                 : 
     169               0 :     const auto res = evmmax::secp256k1::ecrecover(h, r, s, parity);
     170               0 :     if (res)
     171                 :     {
     172               0 :         std::memset(output, 0, 12);
     173               0 :         std::memcpy(output + 12, res->bytes, 20);
     174               0 :         return {EVMC_SUCCESS, 32};
     175                 :     }
     176                 :     else
     177               0 :         return {EVMC_SUCCESS, 0};
     178                 : }
     179                 : 
     180               0 : ExecutionResult ecadd_execute(const uint8_t* input, size_t input_size, uint8_t* output,
     181                 :     [[maybe_unused]] size_t output_size) noexcept
     182                 : {
     183               0 :     assert(output_size >= 64);
     184                 : 
     185               0 :     uint8_t input_buffer[128]{};
     186               0 :     if (input_size != 0)
     187               0 :         std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
     188                 : 
     189               0 :     ethash::hash256 h{};
     190               0 :     std::memcpy(h.bytes, input_buffer, sizeof(h));
     191                 : 
     192               0 :     const evmmax::bn254::Point p = {intx::be::unsafe::load<intx::uint256>(input_buffer),
     193               0 :         intx::be::unsafe::load<intx::uint256>(input_buffer + 32)};
     194               0 :     const evmmax::bn254::Point q = {intx::be::unsafe::load<intx::uint256>(input_buffer + 64),
     195               0 :         intx::be::unsafe::load<intx::uint256>(input_buffer + 96)};
     196                 : 
     197               0 :     if (evmmax::bn254::validate(p) && evmmax::bn254::validate(q))
     198                 :     {
     199               0 :         const auto res = evmmax::bn254::add(p, q);
     200               0 :         intx::be::unsafe::store(output, res.x);
     201               0 :         intx::be::unsafe::store(output + 32, res.y);
     202               0 :         return {EVMC_SUCCESS, 64};
     203                 :     }
     204                 :     else
     205               0 :         return {EVMC_PRECOMPILE_FAILURE, 0};
     206                 : }
     207                 : 
     208               0 : ExecutionResult ecmul_execute(const uint8_t* input, size_t input_size, uint8_t* output,
     209                 :     [[maybe_unused]] size_t output_size) noexcept
     210                 : {
     211               0 :     assert(output_size >= 64);
     212                 : 
     213               0 :     uint8_t input_buffer[96]{};
     214               0 :     if (input_size != 0)
     215               0 :         std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
     216                 : 
     217               0 :     ethash::hash256 h{};
     218               0 :     std::memcpy(h.bytes, input_buffer, sizeof(h));
     219                 : 
     220               0 :     const evmmax::bn254::Point p = {intx::be::unsafe::load<intx::uint256>(input_buffer),
     221               0 :         intx::be::unsafe::load<intx::uint256>(input_buffer + 32)};
     222               0 :     const auto c = intx::be::unsafe::load<intx::uint256>(input_buffer + 64);
     223                 : 
     224               0 :     if (evmmax::bn254::validate(p))
     225                 :     {
     226               0 :         const auto res = evmmax::bn254::mul(p, c);
     227               0 :         intx::be::unsafe::store(output, res.x);
     228               0 :         intx::be::unsafe::store(output + 32, res.y);
     229               0 :         return {EVMC_SUCCESS, 64};
     230                 :     }
     231                 :     else
     232               0 :         return {EVMC_PRECOMPILE_FAILURE, 0};
     233                 : }
     234                 : 
     235               0 : ExecutionResult identity_execute(const uint8_t* input, size_t input_size, uint8_t* output,
     236                 :     [[maybe_unused]] size_t output_size) noexcept
     237                 : {
     238               0 :     assert(output_size >= input_size);
     239               0 :     std::copy_n(input, input_size, output);
     240               0 :     return {EVMC_SUCCESS, input_size};
     241                 : }
     242                 : 
     243                 : namespace
     244                 : {
     245                 : struct PrecompileTraits
     246                 : {
     247                 :     decltype(identity_analyze)* analyze = nullptr;
     248                 :     decltype(identity_execute)* execute = nullptr;
     249                 : };
     250                 : 
     251                 : template <PrecompileId Id>
     252               0 : ExecutionResult dummy_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
     253                 : {
     254               0 :     std::cerr << "Precompile " << static_cast<int>(Id) << " not implemented!\n";
     255               0 :     return ExecutionResult{EVMC_INTERNAL_ERROR, 0};
     256                 : }
     257                 : 
     258                 : inline constexpr auto traits = []() noexcept {
     259                 :     std::array<PrecompileTraits, NumPrecompiles> tbl{{
     260                 :         {},  // undefined for 0
     261                 :         {ecrecover_analyze, ecrecover_execute},
     262                 :         {sha256_analyze, dummy_execute<PrecompileId::sha256>},
     263                 :         {ripemd160_analyze, dummy_execute<PrecompileId::ripemd160>},
     264                 :         {identity_analyze, identity_execute},
     265                 :         {expmod_analyze, dummy_execute<PrecompileId::expmod>},
     266                 :         {ecadd_analyze, ecadd_execute},
     267                 :         {ecmul_analyze, ecmul_execute},
     268                 :         {ecpairing_analyze, dummy_execute<PrecompileId::ecpairing>},
     269                 :         {blake2bf_analyze, dummy_execute<PrecompileId::blake2bf>},
     270                 :         {point_evaluation_analyze, dummy_execute<PrecompileId::point_evaluation>},
     271                 :     }};
     272                 : #ifdef EVMONE_PRECOMPILES_SILKPRE
     273                 :     // tbl[static_cast<size_t>(PrecompileId::ecrecover)].execute = silkpre_ecrecover_execute;
     274                 :     tbl[static_cast<size_t>(PrecompileId::sha256)].execute = silkpre_sha256_execute;
     275                 :     tbl[static_cast<size_t>(PrecompileId::ripemd160)].execute = silkpre_ripemd160_execute;
     276                 :     tbl[static_cast<size_t>(PrecompileId::expmod)].execute = silkpre_expmod_execute;
     277                 :     // tbl[static_cast<size_t>(PrecompileId::ecadd)].execute = silkpre_ecadd_execute;
     278                 :     // tbl[static_cast<size_t>(PrecompileId::ecmul)].execute = silkpre_ecmul_execute;
     279                 :     tbl[static_cast<size_t>(PrecompileId::ecpairing)].execute = silkpre_ecpairing_execute;
     280                 :     tbl[static_cast<size_t>(PrecompileId::blake2bf)].execute = silkpre_blake2bf_execute;
     281                 : #endif
     282                 :     return tbl;
     283                 : }();
     284                 : }  // namespace
     285                 : 
     286 CBC         239 : bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept
     287                 : {
     288                 :     // Define compile-time constant,
     289                 :     // TODO(clang18): workaround for Clang Analyzer bug, fixed in clang 18.
     290                 :     //                https://github.com/llvm/llvm-project/issues/59493.
     291                 :     static constexpr evmc::address address_boundary{stdx::to_underlying(PrecompileId::latest)};
     292                 : 
     293             239 :     if (evmc::is_zero(addr) || addr > address_boundary)
     294             239 :         return false;
     295                 : 
     296 UBC           0 :     const auto id = addr.bytes[19];
     297               0 :     if (rev < EVMC_BYZANTIUM && id >= stdx::to_underlying(PrecompileId::since_byzantium))
     298               0 :         return false;
     299                 : 
     300               0 :     if (rev < EVMC_ISTANBUL && id >= stdx::to_underlying(PrecompileId::since_istanbul))
     301               0 :         return false;
     302                 : 
     303               0 :     if (rev < EVMC_CANCUN && id >= stdx::to_underlying(PrecompileId::since_cancun))
     304               0 :         return false;
     305                 : 
     306               0 :     return true;
     307                 : }
     308                 : 
     309               0 : evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) noexcept
     310                 : {
     311               0 :     assert(msg.gas >= 0);
     312                 : 
     313               0 :     const auto id = msg.code_address.bytes[19];
     314               0 :     const auto [analyze, execute] = traits[id];
     315                 : 
     316               0 :     const bytes_view input{msg.input_data, msg.input_size};
     317               0 :     const auto [gas_cost, max_output_size] = analyze(input, rev);
     318               0 :     const auto gas_left = msg.gas - gas_cost;
     319               0 :     if (gas_left < 0)
     320               0 :         return evmc::Result{EVMC_OUT_OF_GAS};
     321                 : 
     322               0 :     static Cache cache;
     323               0 :     if (auto r = cache.find(static_cast<PrecompileId>(id), input, gas_left); r.has_value())
     324               0 :         return std::move(*r);
     325                 : 
     326                 :     // Buffer for the precompile's output.
     327                 :     // Big enough to handle all "expmod" tests, but in case does not match the size requirement
     328                 :     // from analysis, the result will likely be incorrect.
     329                 :     // TODO: Replace with std::pmr::monotonic_buffer_resource?
     330                 :     uint8_t output_buf[4096];
     331               0 :     assert(std::size(output_buf) >= max_output_size);
     332                 : 
     333                 :     const auto [status_code, output_size] =
     334               0 :         execute(msg.input_data, msg.input_size, output_buf, std::size(output_buf));
     335                 : 
     336                 :     evmc::Result result{
     337               0 :         status_code, status_code == EVMC_SUCCESS ? gas_left : 0, 0, output_buf, output_size};
     338                 : 
     339               0 :     cache.insert(static_cast<PrecompileId>(id), input, result);
     340                 : 
     341               0 :     return result;
     342               0 : }
     343                 : }  // namespace evmone::state
        

Generated by: LCOV version 2.0-1