LCOV - differential code coverage report
Current view: top level - test/statetest - statetest_loader.cpp (source / functions) Coverage Total Hit UBC GBC CBC
Current: DIFF_COVERAGE Lines: 60.5 % 258 156 102 4 152
Current Date: 2024-03-20 16:29:22 Functions: 72.7 % 22 16 6 16
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 "../utils/stdx/utility.hpp"
       6                 : #include "../utils/utils.hpp"
       7                 : #include "statetest.hpp"
       8                 : #include <evmone/eof.hpp>
       9                 : #include <nlohmann/json.hpp>
      10                 : 
      11                 : namespace evmone::test
      12                 : {
      13                 : namespace json = nlohmann;
      14                 : using evmc::from_hex;
      15                 : 
      16                 : template <>
      17 CBC          55 : uint8_t from_json<uint8_t>(const json::json& j)
      18                 : {
      19              55 :     const auto ret = std::stoul(j.get<std::string>(), nullptr, 16);
      20              55 :     if (ret > std::numeric_limits<uint8_t>::max())
      21 UBC           0 :         throw std::out_of_range("from_json<uint8_t>: value > 0xFF");
      22                 : 
      23 CBC          55 :     return static_cast<uint8_t>(ret);
      24                 : }
      25                 : 
      26                 : template <typename T>
      27             848 : static std::optional<T> integer_from_json(const json::json& j)
      28                 : {
      29             848 :     if (j.is_number_integer())
      30 UBC           0 :         return j.get<T>();
      31                 : 
      32 CBC         848 :     if (!j.is_string())
      33 UBC           0 :         return {};
      34                 : 
      35 CBC         848 :     const auto s = j.get<std::string>();
      36                 : 
      37                 :     // Always load integers as unsigned and cast to the required type.
      38                 :     // This will work for cases where a test case uses uint64 timestamps while we use int64.
      39                 :     // TODO: Change timestamp type to uint64.
      40             848 :     size_t num_processed = 0;
      41             848 :     const auto v = static_cast<T>(std::stoull(s, &num_processed, 0));
      42             848 :     if (num_processed == 0 || num_processed != s.size())
      43 UBC           0 :         return {};
      44 CBC         848 :     return v;
      45             848 : }
      46                 : 
      47                 : template <>
      48             318 : int64_t from_json<int64_t>(const json::json& j)
      49                 : {
      50             318 :     const auto v = integer_from_json<int64_t>(j);
      51             318 :     if (!v.has_value())
      52 UBC           0 :         throw std::invalid_argument("from_json<int64_t>: must be integer or string of integer");
      53 CBC         318 :     return *v;
      54                 : }
      55                 : 
      56                 : template <>
      57             530 : uint64_t from_json<uint64_t>(const json::json& j)
      58                 : {
      59             530 :     const auto v = integer_from_json<uint64_t>(j);
      60             530 :     if (!v.has_value())
      61 UBC           0 :         throw std::invalid_argument("from_json<uint64_t>: must be integer or string of integer");
      62 CBC         530 :     return *v;
      63                 : }
      64                 : 
      65                 : template <>
      66             212 : bytes from_json<bytes>(const json::json& j)
      67                 : {
      68             212 :     return from_hex(j.get<std::string>()).value();
      69                 : }
      70                 : 
      71                 : template <>
      72             318 : address from_json<address>(const json::json& j)
      73                 : {
      74             318 :     return evmc::from_hex<address>(j.get<std::string>()).value();
      75                 : }
      76                 : 
      77                 : template <>
      78             672 : hash256 from_json<hash256>(const json::json& j)
      79                 : {
      80                 :     // Special case to handle "0". Required by exec-spec-tests.
      81                 :     // TODO: Get rid of it.
      82             672 :     if (j.is_string() && (j == "0" || j == "0x0"))
      83 UBC           0 :         return 0x00_bytes32;
      84                 :     else
      85 CBC         672 :         return evmc::from_hex<hash256>(j.get<std::string>()).value();
      86                 : }
      87                 : 
      88                 : template <>
      89             372 : intx::uint256 from_json<intx::uint256>(const json::json& j)
      90                 : {
      91             372 :     const auto s = j.get<std::string>();
      92             372 :     if (s.starts_with("0x:bigint "))
      93 UBC           0 :         return std::numeric_limits<intx::uint256>::max();  // Fake it
      94 CBC         372 :     return intx::from_string<intx::uint256>(s);
      95             372 : }
      96                 : 
      97                 : template <>
      98               1 : state::AccessList from_json<state::AccessList>(const json::json& j)
      99                 : {
     100               1 :     state::AccessList o;
     101               1 :     for (const auto& a : j)
     102                 :     {
     103 UBC           0 :         std::vector<bytes32> storage_access_list;
     104               0 :         for (const auto& storage_key : a.at("storageKeys"))
     105               0 :             storage_access_list.emplace_back(from_json<bytes32>(storage_key));
     106               0 :         o.emplace_back(from_json<address>(a.at("address")), std::move(storage_access_list));
     107               0 :     }
     108 CBC           1 :     return o;
     109 UBC           0 : }
     110                 : 
     111                 : // Based on calculateEIP1559BaseFee from ethereum/retesteth
     112 CBC          53 : inline uint64_t calculate_current_base_fee_eip1559(
     113                 :     uint64_t parent_gas_used, uint64_t parent_gas_limit, uint64_t parent_base_fee)
     114                 : {
     115                 :     // TODO: Make sure that 64-bit precision is good enough.
     116                 :     static constexpr auto BASE_FEE_MAX_CHANGE_DENOMINATOR = 8;
     117                 :     static constexpr auto ELASTICITY_MULTIPLIER = 2;
     118                 : 
     119              53 :     uint64_t base_fee = 0;
     120                 : 
     121              53 :     const auto parent_gas_target = parent_gas_limit / ELASTICITY_MULTIPLIER;
     122                 : 
     123              53 :     if (parent_gas_used == parent_gas_target)
     124 UBC           0 :         base_fee = parent_base_fee;
     125 CBC          53 :     else if (parent_gas_used > parent_gas_target)
     126                 :     {
     127 UBC           0 :         const auto gas_used_delta = parent_gas_used - parent_gas_target;
     128               0 :         const auto formula =
     129               0 :             parent_base_fee * gas_used_delta / parent_gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR;
     130               0 :         const auto base_fee_per_gas_delta = formula > 1 ? formula : 1;
     131               0 :         base_fee = parent_base_fee + base_fee_per_gas_delta;
     132                 :     }
     133                 :     else
     134                 :     {
     135 CBC          53 :         const auto gas_used_delta = parent_gas_target - parent_gas_used;
     136                 : 
     137                 :         const auto base_fee_per_gas_delta_u128 =
     138                 :             intx::uint128(parent_base_fee, 0) * intx::uint128(gas_used_delta, 0) /
     139              53 :             parent_gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR;
     140                 : 
     141              53 :         const auto base_fee_per_gas_delta = base_fee_per_gas_delta_u128[0];
     142              53 :         if (parent_base_fee > base_fee_per_gas_delta)
     143              53 :             base_fee = parent_base_fee - base_fee_per_gas_delta;
     144                 :         else
     145 UBC           0 :             base_fee = 0;
     146                 :     }
     147 CBC          53 :     return base_fee;
     148                 : }
     149                 : 
     150                 : template <>
     151 UBC           0 : state::Withdrawal from_json<state::Withdrawal>(const json::json& j)
     152                 : {
     153               0 :     return {from_json<uint64_t>(j.at("index")), from_json<uint64_t>(j.at("validatorIndex")),
     154               0 :         from_json<evmc::address>(j.at("address")), from_json<uint64_t>(j.at("amount"))};
     155                 : }
     156                 : 
     157                 : template <>
     158 CBC          53 : state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
     159                 : {
     160              53 :     evmc::bytes32 prev_randao;
     161              53 :     int64_t current_difficulty = 0;
     162              53 :     int64_t parent_difficulty = 0;
     163              53 :     const auto prev_randao_it = j.find("currentRandom");
     164              53 :     const auto current_difficulty_it = j.find("currentDifficulty");
     165              53 :     const auto parent_difficulty_it = j.find("parentDifficulty");
     166                 : 
     167              53 :     if (current_difficulty_it != j.end())
     168              53 :         current_difficulty = from_json<int64_t>(*current_difficulty_it);
     169              53 :     if (parent_difficulty_it != j.end())
     170 UBC           0 :         parent_difficulty = from_json<int64_t>(*parent_difficulty_it);
     171                 : 
     172                 :     // When it's not defined init it with difficulty value.
     173 CBC          53 :     if (prev_randao_it != j.end())
     174              53 :         prev_randao = from_json<bytes32>(*prev_randao_it);
     175 UBC           0 :     else if (current_difficulty_it != j.end())
     176               0 :         prev_randao = from_json<bytes32>(*current_difficulty_it);
     177               0 :     else if (parent_difficulty_it != j.end())
     178               0 :         prev_randao = from_json<bytes32>(*parent_difficulty_it);
     179                 : 
     180 CBC          53 :     hash256 parent_uncle_hash;
     181              53 :     const auto parent_uncle_hash_it = j.find("parentUncleHash");
     182              53 :     if (parent_uncle_hash_it != j.end())
     183 UBC           0 :         parent_uncle_hash = from_json<hash256>(*parent_uncle_hash_it);
     184                 : 
     185 CBC          53 :     uint64_t base_fee = 0;
     186              53 :     if (j.contains("currentBaseFee"))
     187 UBC           0 :         base_fee = from_json<uint64_t>(j.at("currentBaseFee"));
     188 CBC          53 :     else if (j.contains("parentBaseFee"))
     189                 :     {
     190              53 :         base_fee = calculate_current_base_fee_eip1559(from_json<uint64_t>(j.at("parentGasUsed")),
     191              53 :             from_json<uint64_t>(j.at("parentGasLimit")),
     192              53 :             from_json<uint64_t>(j.at("parentBaseFee")));
     193                 :     }
     194                 : 
     195              53 :     std::vector<state::Withdrawal> withdrawals;
     196              53 :     if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end())
     197                 :     {
     198              53 :         for (const auto& withdrawal : *withdrawals_it)
     199 UBC           0 :             withdrawals.push_back(from_json<state::Withdrawal>(withdrawal));
     200                 :     }
     201                 : 
     202 CBC          53 :     std::unordered_map<int64_t, hash256> block_hashes;
     203              53 :     if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end())
     204                 :     {
     205             106 :         for (const auto& [j_num, j_hash] : block_hashes_it->items())
     206             106 :             block_hashes[from_json<int64_t>(j_num)] = from_json<hash256>(j_hash);
     207                 :     }
     208                 : 
     209              53 :     std::vector<state::Ommer> ommers;
     210              53 :     if (const auto ommers_it = j.find("ommers"); ommers_it != j.end())
     211                 :     {
     212 UBC           0 :         for (const auto& ommer : *ommers_it)
     213                 :         {
     214               0 :             ommers.push_back(
     215               0 :                 {from_json<evmc::address>(ommer.at("address")), ommer.at("delta").get<uint32_t>()});
     216                 :         }
     217                 :     }
     218                 : 
     219 CBC          53 :     int64_t parent_timestamp = 0;
     220              53 :     auto parent_timestamp_it = j.find("parentTimestamp");
     221              53 :     if (parent_timestamp_it != j.end())
     222 UBC           0 :         parent_timestamp = from_json<int64_t>(*parent_timestamp_it);
     223                 : 
     224 CBC          53 :     uint64_t excess_blob_gas = 0;
     225              53 :     if (const auto it = j.find("parentExcessBlobGas"); it != j.end())
     226                 :     {
     227              53 :         const auto parent_excess_blob_gas = from_json<uint64_t>(*it);
     228              53 :         const auto parent_blob_gas_used = from_json<uint64_t>(j.at("parentBlobGasUsed"));
     229                 :         static constexpr uint64_t TARGET_BLOB_GAS_PER_BLOCK = 0x60000;
     230              53 :         excess_blob_gas =
     231              53 :             std::max(parent_excess_blob_gas + parent_blob_gas_used, TARGET_BLOB_GAS_PER_BLOCK) -
     232                 :             TARGET_BLOB_GAS_PER_BLOCK;
     233                 :     }
     234 UBC           0 :     else if (const auto it2 = j.find("currentExcessBlobGas"); it2 != j.end())
     235                 :     {
     236               0 :         excess_blob_gas = from_json<uint64_t>(*it2);
     237                 :     }
     238                 : 
     239                 :     return state::BlockInfo{
     240 CBC          53 :         .number = from_json<int64_t>(j.at("currentNumber")),
     241              53 :         .timestamp = from_json<int64_t>(j.at("currentTimestamp")),
     242                 :         .parent_timestamp = parent_timestamp,
     243              53 :         .gas_limit = from_json<int64_t>(j.at("currentGasLimit")),
     244              53 :         .coinbase = from_json<evmc::address>(j.at("currentCoinbase")),
     245                 :         .difficulty = current_difficulty,
     246                 :         .parent_difficulty = parent_difficulty,
     247                 :         .parent_ommers_hash = parent_uncle_hash,
     248                 :         .prev_randao = prev_randao,
     249                 :         .parent_beacon_block_root = {},
     250                 :         .base_fee = base_fee,
     251                 :         .excess_blob_gas = excess_blob_gas,
     252              53 :         .blob_base_fee = state::compute_blob_gas_price(excess_blob_gas),
     253              53 :         .ommers = std::move(ommers),
     254              53 :         .withdrawals = std::move(withdrawals),
     255              53 :         .known_block_hashes = std::move(block_hashes),
     256             318 :     };
     257              53 : }
     258                 : 
     259                 : template <>
     260              53 : state::State from_json<state::State>(const json::json& j)
     261                 : {
     262              53 :     state::State o;
     263             212 :     for (const auto& [j_addr, j_acc] : j.items())
     264                 :     {
     265             318 :         auto& acc = o.insert(from_json<address>(j_addr),
     266             159 :             {.nonce = from_json<uint64_t>(j_acc.at("nonce")),
     267             159 :                 .balance = from_json<intx::uint256>(j_acc.at("balance")),
     268             159 :                 .code = from_json<bytes>(j_acc.at("code"))});
     269                 : 
     270             159 :         if (const auto storage_it = j_acc.find("storage"); storage_it != j_acc.end())
     271                 :         {
     272             442 :             for (const auto& [j_key, j_value] : storage_it->items())
     273                 :             {
     274             283 :                 if (const auto value = from_json<bytes32>(j_value); !is_zero(value))
     275             283 :                     acc.storage.insert(
     276             566 :                         {from_json<bytes32>(j_key), {.current = value, .original = value}});
     277             159 :             }
     278                 :         }
     279              53 :     }
     280              53 :     return o;
     281 UBC           0 : }
     282                 : 
     283                 : /// Load common parts of Transaction or TestMultiTransaction.
     284 CBC          53 : static void from_json_tx_common(const json::json& j, state::Transaction& o)
     285                 : {
     286              53 :     o.sender = from_json<evmc::address>(j.at("sender"));
     287              53 :     o.nonce = from_json<uint64_t>(j.at("nonce"));
     288                 : 
     289              53 :     if (const auto to_it = j.find("to"); to_it != j.end() && !to_it->get<std::string>().empty())
     290              53 :         o.to = from_json<evmc::address>(*to_it);
     291                 : 
     292              53 :     if (const auto gas_price_it = j.find("gasPrice"); gas_price_it != j.end())
     293                 :     {
     294 GBC          52 :         o.type = state::Transaction::Type::legacy;
     295              52 :         o.max_gas_price = from_json<intx::uint256>(*gas_price_it);
     296              52 :         o.max_priority_gas_price = o.max_gas_price;
     297              52 :         if (j.contains("maxFeePerGas") || j.contains("maxPriorityFeePerGas"))
     298                 :         {
     299 UBC           0 :             throw std::invalid_argument(
     300               0 :                 "invalid transaction: contains both legacy and EIP-1559 fees");
     301                 :         }
     302                 :     }
     303                 :     else
     304                 :     {
     305 CBC           1 :         o.type = state::Transaction::Type::eip1559;
     306               1 :         o.max_gas_price = from_json<intx::uint256>(j.at("maxFeePerGas"));
     307               1 :         o.max_priority_gas_price = from_json<intx::uint256>(j.at("maxPriorityFeePerGas"));
     308                 :     }
     309                 : 
     310              53 :     if (const auto it = j.find("maxFeePerBlobGas"); it != j.end())
     311 UBC           0 :         o.max_blob_gas_price = from_json<intx::uint256>(*it);
     312                 : 
     313 CBC          53 :     if (const auto it = j.find("blobVersionedHashes"); it != j.end())
     314                 :     {
     315 UBC           0 :         o.type = state::Transaction::Type::blob;
     316               0 :         for (const auto& hash : *it)
     317               0 :             o.blob_hashes.push_back(from_json<bytes32>(hash));
     318                 :     }
     319 CBC          53 : }
     320                 : 
     321                 : template <>
     322              53 : state::Transaction from_json<state::Transaction>(const json::json& j)
     323                 : {
     324              53 :     state::Transaction o;
     325              53 :     from_json_tx_common(j, o);
     326              53 :     if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end())
     327               1 :         o.chain_id = from_json<uint8_t>(*chain_id_it);
     328                 : 
     329              53 :     if (const auto it = j.find("data"); it != j.end())
     330 UBC           0 :         o.data = from_json<bytes>(*it);
     331                 :     else
     332 CBC          53 :         o.data = from_json<bytes>(j.at("input"));
     333                 : 
     334              53 :     if (const auto it = j.find("gasLimit"); it != j.end())
     335 UBC           0 :         o.gas_limit = from_json<int64_t>(*it);
     336                 :     else
     337 CBC          53 :         o.gas_limit = from_json<int64_t>(j.at("gas"));
     338                 : 
     339              53 :     o.value = from_json<intx::uint256>(j.at("value"));
     340                 : 
     341              53 :     if (const auto ac_it = j.find("accessList"); ac_it != j.end())
     342                 :     {
     343               1 :         o.access_list = from_json<state::AccessList>(*ac_it);
     344               1 :         if (o.type == state::Transaction::Type::legacy)  // Upgrade tx type if tx has access list
     345 UBC           0 :             o.type = state::Transaction::Type::access_list;
     346                 :     }
     347                 : 
     348 CBC          53 :     if (const auto type_it = j.find("type"); type_it != j.end())
     349                 :     {
     350               1 :         const auto infered_type = stdx::to_underlying(o.type);
     351               1 :         const auto type = from_json<uint8_t>(*type_it);
     352               1 :         if (type != infered_type)
     353 UBC           0 :             throw std::invalid_argument("wrong transaction type: " + std::to_string(type) +
     354               0 :                                         ", expected: " + std::to_string(infered_type));
     355                 :     }
     356                 : 
     357 CBC          53 :     o.nonce = from_json<uint64_t>(j.at("nonce"));
     358              53 :     o.r = from_json<intx::uint256>(j.at("r"));
     359              53 :     o.s = from_json<intx::uint256>(j.at("s"));
     360              53 :     o.v = from_json<uint8_t>(j.at("v"));
     361                 : 
     362              53 :     return o;
     363 UBC           0 : }
     364                 : 
     365               0 : static void from_json(const json::json& j, TestMultiTransaction& o)
     366                 : {
     367               0 :     from_json_tx_common(j, o);
     368                 : 
     369               0 :     for (const auto& j_data : j.at("data"))
     370               0 :         o.inputs.emplace_back(from_json<bytes>(j_data));
     371                 : 
     372               0 :     if (const auto ac_it = j.find("accessLists"); ac_it != j.end())
     373                 :     {
     374               0 :         for (const auto& j_access_list : *ac_it)
     375               0 :             o.access_lists.emplace_back(from_json<state::AccessList>(j_access_list));
     376               0 :         if (o.type == state::Transaction::Type::legacy)  // Upgrade tx type if tx has access lists
     377               0 :             o.type = state::Transaction::Type::access_list;
     378                 :     }
     379                 : 
     380               0 :     for (const auto& j_gas_limit : j.at("gasLimit"))
     381               0 :         o.gas_limits.emplace_back(from_json<int64_t>(j_gas_limit));
     382                 : 
     383               0 :     for (const auto& j_value : j.at("value"))
     384               0 :         o.values.emplace_back(from_json<intx::uint256>(j_value));
     385               0 : }
     386                 : 
     387               0 : static void from_json(const json::json& j, TestMultiTransaction::Indexes& o)
     388                 : {
     389               0 :     o.input = j.at("data").get<size_t>();
     390               0 :     o.gas_limit = j.at("gas").get<size_t>();
     391               0 :     o.value = j.at("value").get<size_t>();
     392               0 : }
     393                 : 
     394               0 : static void from_json(const json::json& j, StateTransitionTest::Case::Expectation& o)
     395                 : {
     396               0 :     o.indexes = j.at("indexes").get<TestMultiTransaction::Indexes>();
     397               0 :     o.state_hash = from_json<hash256>(j.at("hash"));
     398               0 :     o.logs_hash = from_json<hash256>(j.at("logs"));
     399               0 :     o.exception = j.contains("expectException");
     400               0 : }
     401                 : 
     402               0 : static void from_json(const json::json& j, StateTransitionTest& o)
     403                 : {
     404               0 :     if (!j.is_object() || j.empty())
     405               0 :         throw std::invalid_argument{"JSON test must be an object with single key of the test name"};
     406                 : 
     407               0 :     const auto& j_t = *j.begin();  // Content is in a dict with the test name.
     408                 : 
     409               0 :     o.pre_state = from_json<state::State>(j_t.at("pre"));
     410                 : 
     411               0 :     o.multi_tx = j_t.at("transaction").get<TestMultiTransaction>();
     412                 : 
     413               0 :     o.block = from_json<state::BlockInfo>(j_t.at("env"));
     414                 : 
     415               0 :     if (const auto info_it = j_t.find("_info"); info_it != j_t.end())
     416                 :     {
     417               0 :         if (const auto labels_it = info_it->find("labels"); labels_it != info_it->end())
     418                 :         {
     419               0 :             for (const auto& [j_id, j_label] : labels_it->items())
     420               0 :                 o.input_labels.emplace(from_json<uint64_t>(j_id), j_label);
     421                 :         }
     422                 :     }
     423                 : 
     424               0 :     for (const auto& [rev_name, expectations] : j_t.at("post").items())
     425                 :     {
     426                 :         // TODO(c++20): Use emplace_back with aggregate initialization.
     427               0 :         o.cases.push_back({to_rev(rev_name),
     428                 :             expectations.get<std::vector<StateTransitionTest::Case::Expectation>>()});
     429               0 :     }
     430               0 : }
     431                 : 
     432               0 : StateTransitionTest load_state_test(std::istream& input)
     433                 : {
     434               0 :     return json::json::parse(input).get<StateTransitionTest>();
     435                 : }
     436                 : 
     437 CBC          53 : void validate_state(const state::State& state, evmc_revision rev)
     438                 : {
     439             212 :     for (const auto& [addr, acc] : state.get_accounts())
     440                 :     {
     441                 :         // TODO: Check for empty accounts after Paris.
     442                 :         //       https://github.com/ethereum/tests/issues/1331
     443             159 :         if (is_eof_container(acc.code))
     444                 :         {
     445 UBC           0 :             if (rev >= EVMC_PRAGUE)
     446                 :             {
     447               0 :                 if (const auto result = validate_eof(rev, acc.code);
     448                 :                     result != EOFValidationError::success)
     449                 :                 {
     450               0 :                     throw std::invalid_argument(
     451               0 :                         "EOF container at " + hex0x(addr) +
     452               0 :                         " is invalid: " + std::string(get_error_message(result)));
     453                 :                 }
     454                 :             }
     455                 :             else
     456                 :             {
     457               0 :                 throw std::invalid_argument("unexpected code with EOF prefix at " + hex0x(addr));
     458                 :             }
     459                 :         }
     460                 : 
     461 CBC         442 :         for (const auto& [key, value] : acc.storage)
     462                 :         {
     463             283 :             if (is_zero(value.original))
     464                 :             {
     465 UBC           0 :                 throw std::invalid_argument{"account " + hex0x(addr) +
     466               0 :                                             " contains invalid zero-value storage entry " +
     467               0 :                                             hex0x(key)};
     468                 :             }
     469                 :         }
     470                 :     }
     471 CBC          53 : }
     472                 : }  // namespace evmone::test
        

Generated by: LCOV version 2.0-1