LCOV - differential code coverage report
Current view: top level - test/state - host.cpp (source / functions) Coverage Total Hit UBC GBC CBC
Current: DIFF_COVERAGE Lines: 33.2 % 244 81 163 9 72
Current Date: 2024-03-20 16:29:22 Functions: 42.9 % 21 9 12 1 8
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 "host.hpp"
       6                 : #include "precompiles.hpp"
       7                 : #include "rlp.hpp"
       8                 : #include <evmone/eof.hpp>
       9                 : 
      10                 : namespace evmone::state
      11                 : {
      12 UBC           0 : bool Host::account_exists(const address& addr) const noexcept
      13                 : {
      14               0 :     const auto* const acc = m_state.find(addr);
      15               0 :     return acc != nullptr && (m_rev < EVMC_SPURIOUS_DRAGON || !acc->is_empty());
      16                 : }
      17                 : 
      18 GBC           2 : bytes32 Host::get_storage(const address& addr, const bytes32& key) const noexcept
      19                 : {
      20               2 :     const auto& acc = m_state.get(addr);
      21               2 :     if (const auto it = acc.storage.find(key); it != acc.storage.end())
      22               2 :         return it->second.current;
      23 UBC           0 :     return {};
      24                 : }
      25                 : 
      26 CBC         286 : evmc_storage_status Host::set_storage(
      27                 :     const address& addr, const bytes32& key, const bytes32& value) noexcept
      28                 : {
      29                 :     // Follow EVMC documentation https://evmc.ethereum.org/storagestatus.html#autotoc_md3
      30                 :     // and EIP-2200 specification https://eips.ethereum.org/EIPS/eip-2200.
      31                 : 
      32             286 :     auto& storage_slot = m_state.get(addr).storage[key];
      33             286 :     const auto& [current, original, _] = storage_slot;
      34                 : 
      35             286 :     const auto dirty = original != current;
      36             286 :     const auto restored = original == value;
      37             286 :     const auto current_is_zero = is_zero(current);
      38             286 :     const auto value_is_zero = is_zero(value);
      39                 : 
      40             286 :     auto status = EVMC_STORAGE_ASSIGNED;  // All other cases.
      41             286 :     if (!dirty && !restored)
      42                 :     {
      43             263 :         if (current_is_zero)
      44               4 :             status = EVMC_STORAGE_ADDED;  // 0 → 0 → Z
      45             259 :         else if (value_is_zero)
      46              60 :             status = EVMC_STORAGE_DELETED;  // X → X → 0
      47                 :         else
      48             199 :             status = EVMC_STORAGE_MODIFIED;  // X → X → Z
      49                 :     }
      50 GBC          23 :     else if (dirty && !restored)
      51                 :     {
      52               3 :         if (current_is_zero && !value_is_zero)
      53 UBC           0 :             status = EVMC_STORAGE_DELETED_ADDED;  // X → 0 → Z
      54 GBC           3 :         else if (!current_is_zero && value_is_zero)
      55               1 :             status = EVMC_STORAGE_MODIFIED_DELETED;  // X → Y → 0
      56                 :     }
      57              20 :     else if (dirty && restored)
      58                 :     {
      59 UBC           0 :         if (current_is_zero)
      60               0 :             status = EVMC_STORAGE_DELETED_RESTORED;  // X → 0 → X
      61               0 :         else if (value_is_zero)
      62               0 :             status = EVMC_STORAGE_ADDED_DELETED;  // 0 → Y → 0
      63                 :         else
      64               0 :             status = EVMC_STORAGE_MODIFIED_RESTORED;  // X → Y → X
      65                 :     }
      66                 : 
      67                 :     // In Berlin this is handled in access_storage().
      68 CBC         286 :     if (m_rev < EVMC_BERLIN)
      69 UBC           0 :         m_state.journal_storage_change(addr, key, storage_slot);
      70 CBC         286 :     storage_slot.current = value;  // Update current value.
      71             286 :     return status;
      72                 : }
      73                 : 
      74 UBC           0 : uint256be Host::get_balance(const address& addr) const noexcept
      75                 : {
      76               0 :     const auto* const acc = m_state.find(addr);
      77               0 :     return (acc != nullptr) ? intx::be::store<uint256be>(acc->balance) : uint256be{};
      78                 : }
      79                 : 
      80               0 : size_t Host::get_code_size(const address& addr) const noexcept
      81                 : {
      82               0 :     const auto* const acc = m_state.find(addr);
      83               0 :     return (acc != nullptr) ? acc->code.size() : 0;
      84                 : }
      85                 : 
      86               0 : bytes32 Host::get_code_hash(const address& addr) const noexcept
      87                 : {
      88                 :     // TODO: Cache code hash. It will be needed also to compute the MPT hash.
      89               0 :     const auto* const acc = m_state.find(addr);
      90               0 :     return (acc != nullptr && !acc->is_empty()) ? keccak256(acc->code) : bytes32{};
      91                 : }
      92                 : 
      93               0 : size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_data,
      94                 :     size_t buffer_size) const noexcept
      95                 : {
      96               0 :     const auto* const acc = m_state.find(addr);
      97               0 :     const auto code = (acc != nullptr) ? bytes_view{acc->code} : bytes_view{};
      98               0 :     const auto code_slice = code.substr(std::min(code_offset, code.size()));
      99               0 :     const auto num_bytes = std::min(buffer_size, code_slice.size());
     100               0 :     std::copy_n(code_slice.begin(), num_bytes, buffer_data);
     101               0 :     return num_bytes;
     102                 : }
     103                 : 
     104               0 : bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcept
     105                 : {
     106               0 :     if (m_state.find(beneficiary) == nullptr)
     107               0 :         m_state.journal_create(beneficiary, false);
     108               0 :     auto& acc = m_state.get(addr);
     109               0 :     const auto balance = acc.balance;
     110               0 :     auto& beneficiary_acc = m_state.touch(beneficiary);
     111                 : 
     112               0 :     m_state.journal_balance_change(beneficiary, beneficiary_acc.balance);
     113               0 :     m_state.journal_balance_change(addr, balance);
     114                 : 
     115               0 :     if (m_rev >= EVMC_CANCUN && !acc.just_created)
     116                 :     {
     117                 :         // EIP-6780:
     118                 :         // "SELFDESTRUCT is executed in a transaction that is not the same
     119                 :         // as the contract invoking SELFDESTRUCT was created"
     120               0 :         acc.balance = 0;
     121               0 :         beneficiary_acc.balance += balance;  // Keep balance if acc is the beneficiary.
     122                 : 
     123                 :         // Return "selfdestruct not registered".
     124                 :         // In practice this affects only refunds before Cancun.
     125               0 :         return false;
     126                 :     }
     127                 : 
     128                 :     // Transfer may happen multiple times per single account as account's balance
     129                 :     // can be increased with a call following previous selfdestruct.
     130               0 :     beneficiary_acc.balance += balance;
     131               0 :     acc.balance = 0;  // Zero balance if acc is the beneficiary.
     132                 : 
     133                 :     // Mark the destruction if not done already.
     134               0 :     if (!acc.destructed)
     135                 :     {
     136               0 :         m_state.journal_destruct(addr);
     137               0 :         acc.destructed = true;
     138               0 :         return true;
     139                 :     }
     140               0 :     return false;
     141                 : }
     142                 : 
     143               0 : address compute_create_address(const address& sender, uint64_t sender_nonce) noexcept
     144                 : {
     145                 :     // TODO: Compute CREATE address without using RLP library.
     146               0 :     const auto rlp_list = rlp::encode_tuple(sender, sender_nonce);
     147               0 :     const auto base_hash = keccak256(rlp_list);
     148               0 :     address addr;
     149               0 :     std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes);
     150               0 :     return addr;
     151               0 : }
     152                 : 
     153               0 : address compute_create2_address(
     154                 :     const address& sender, const bytes32& salt, bytes_view init_code) noexcept
     155                 : {
     156               0 :     const auto init_code_hash = keccak256(init_code);
     157                 :     uint8_t buffer[1 + sizeof(sender) + sizeof(salt) + sizeof(init_code_hash)];
     158                 :     static_assert(std::size(buffer) == 85);
     159               0 :     auto it = std::begin(buffer);
     160               0 :     *it++ = 0xff;
     161               0 :     it = std::copy_n(sender.bytes, sizeof(sender), it);
     162               0 :     it = std::copy_n(salt.bytes, sizeof(salt), it);
     163               0 :     std::copy_n(init_code_hash.bytes, sizeof(init_code_hash), it);
     164               0 :     const auto base_hash = keccak256({buffer, std::size(buffer)});
     165               0 :     address addr;
     166               0 :     std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes);
     167               0 :     return addr;
     168                 : }
     169                 : 
     170 CBC         105 : std::optional<evmc_message> Host::prepare_message(evmc_message msg)
     171                 : {
     172             105 :     if (msg.depth == 0 || msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2)
     173                 :     {
     174              53 :         auto& sender_acc = m_state.get(msg.sender);
     175              53 :         const auto sender_nonce = sender_acc.nonce;
     176                 : 
     177                 :         // EIP-2681 (already checked for depth 0 during transaction validation).
     178              53 :         if (sender_nonce == Account::NonceMax)
     179 UBC           0 :             return {};  // Light early exception.
     180                 : 
     181 CBC          53 :         if (msg.depth != 0)
     182 UBC           0 :             m_state.journal_bump_nonce(msg.sender);
     183 CBC          53 :         ++sender_acc.nonce;  // Bump sender nonce.
     184                 : 
     185              53 :         if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2)
     186                 :         {
     187                 :             // Compute and set the address of the account being created.
     188 UBC           0 :             assert(msg.recipient == address{});
     189               0 :             assert(msg.code_address == address{});
     190               0 :             msg.recipient = (msg.kind == EVMC_CREATE) ?
     191               0 :                                 compute_create_address(msg.sender, sender_nonce) :
     192               0 :                                 compute_create2_address(
     193                 :                                     msg.sender, msg.create2_salt, {msg.input_data, msg.input_size});
     194                 : 
     195                 :             // By EIP-2929, the access to new created address is never reverted.
     196               0 :             access_account(msg.recipient);
     197                 :         }
     198                 :     }
     199                 : 
     200 CBC         105 :     return msg;
     201                 : }
     202                 : 
     203 UBC           0 : evmc::Result Host::create(const evmc_message& msg) noexcept
     204                 : {
     205               0 :     assert(msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2);
     206                 : 
     207                 :     // Check collision as defined in pseudo-EIP https://github.com/ethereum/EIPs/issues/684.
     208                 :     // All combinations of conditions (nonce, code, storage) are tested.
     209                 :     // TODO(EVMC): Add specific error codes for creation failures.
     210               0 :     if (const auto collision_acc = m_state.find(msg.recipient);
     211               0 :         collision_acc != nullptr && (collision_acc->nonce != 0 || !collision_acc->code.empty()))
     212               0 :         return evmc::Result{EVMC_FAILURE};
     213                 : 
     214                 :     // TODO: msg.recipient lookup is done 3x here.
     215               0 :     const bool exists = m_state.find(msg.recipient) != nullptr;
     216               0 :     auto& new_acc = m_state.get_or_insert(msg.recipient);
     217               0 :     m_state.journal_create(msg.recipient, exists);
     218               0 :     assert(new_acc.nonce == 0);
     219               0 :     if (m_rev >= EVMC_SPURIOUS_DRAGON)
     220               0 :         new_acc.nonce = 1;  // No need to journal: create revert will 0 the nonce.
     221                 : 
     222               0 :     new_acc.just_created = true;
     223                 : 
     224                 :     // Clear the new account storage, but keep the access status (from tx access list).
     225                 :     // This is only needed for tests and cannot happen in real networks.
     226               0 :     for (auto& [k, v] : new_acc.storage) [[unlikely]]
     227                 :     {
     228               0 :         m_state.journal_storage_change(msg.recipient, k, v);
     229               0 :         v = StorageValue{.access_status = v.access_status};
     230                 :     }
     231                 : 
     232               0 :     auto& sender_acc = m_state.get(msg.sender);  // TODO: Duplicated account lookup.
     233               0 :     const auto value = intx::be::load<intx::uint256>(msg.value);
     234               0 :     assert(sender_acc.balance >= value && "EVM must guarantee balance");
     235               0 :     m_state.journal_balance_change(msg.sender, sender_acc.balance);
     236               0 :     m_state.journal_balance_change(msg.recipient, new_acc.balance);
     237               0 :     sender_acc.balance -= value;
     238               0 :     new_acc.balance += value;  // The new account may be prefunded.
     239                 : 
     240               0 :     auto create_msg = msg;
     241               0 :     const bytes_view initcode{msg.input_data, msg.input_size};
     242               0 :     create_msg.input_data = nullptr;
     243               0 :     create_msg.input_size = 0;
     244                 : 
     245               0 :     if (m_rev >= EVMC_PRAGUE && (is_eof_container(initcode) || is_eof_container(sender_acc.code)))
     246                 :     {
     247               0 :         if (validate_eof(m_rev, initcode) != EOFValidationError::success)
     248               0 :             return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE};
     249                 :     }
     250                 : 
     251               0 :     auto result = m_vm.execute(*this, m_rev, create_msg, msg.input_data, msg.input_size);
     252               0 :     if (result.status_code != EVMC_SUCCESS)
     253                 :     {
     254               0 :         result.create_address = msg.recipient;
     255               0 :         return result;
     256                 :     }
     257                 : 
     258               0 :     auto gas_left = result.gas_left;
     259               0 :     assert(gas_left >= 0);
     260                 : 
     261               0 :     const bytes_view code{result.output_data, result.output_size};
     262               0 :     if (m_rev >= EVMC_SPURIOUS_DRAGON && code.size() > max_code_size)
     263               0 :         return evmc::Result{EVMC_FAILURE};
     264                 : 
     265                 :     // Code deployment cost.
     266               0 :     const auto cost = std::ssize(code) * 200;
     267               0 :     gas_left -= cost;
     268               0 :     if (gas_left < 0)
     269                 :     {
     270               0 :         return (m_rev == EVMC_FRONTIER) ?
     271               0 :                    evmc::Result{EVMC_SUCCESS, result.gas_left, result.gas_refund, msg.recipient} :
     272               0 :                    evmc::Result{EVMC_FAILURE};
     273                 :     }
     274                 : 
     275               0 :     if (m_rev >= EVMC_PRAGUE && (is_eof_container(initcode) || is_eof_container(code)))
     276                 :     {
     277               0 :         if (validate_eof(m_rev, code) != EOFValidationError::success)
     278               0 :             return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE};
     279                 :     }
     280               0 :     else if (m_rev >= EVMC_LONDON && !code.empty() && code[0] == 0xEF)  // Reject EF code.
     281               0 :         return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE};
     282                 : 
     283               0 :     new_acc.code = code;
     284                 : 
     285               0 :     return evmc::Result{result.status_code, gas_left, result.gas_refund, msg.recipient};
     286               0 : }
     287                 : 
     288 CBC         105 : evmc::Result Host::execute_message(const evmc_message& msg) noexcept
     289                 : {
     290             105 :     if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2)
     291 UBC           0 :         return create(msg);
     292                 : 
     293 CBC         105 :     if (msg.kind == EVMC_CALL)
     294                 :     {
     295              79 :         const auto exists = m_state.find(msg.recipient) != nullptr;
     296              79 :         if (!exists)
     297 UBC           0 :             m_state.journal_create(msg.recipient, exists);
     298                 :     }
     299                 : 
     300 CBC         105 :     assert(msg.kind != EVMC_CALL || evmc::address{msg.recipient} == msg.code_address);
     301                 :     auto* const dst_acc =
     302             105 :         (msg.kind == EVMC_CALL) ? &m_state.touch(msg.recipient) : m_state.find(msg.code_address);
     303                 : 
     304             105 :     if (msg.kind == EVMC_CALL && !evmc::is_zero(msg.value))
     305                 :     {
     306                 :         // Transfer value: sender → recipient.
     307                 :         // The sender's balance is already checked therefore the sender account must exist.
     308 UBC           0 :         const auto value = intx::be::load<intx::uint256>(msg.value);
     309               0 :         assert(m_state.get(msg.sender).balance >= value);
     310               0 :         m_state.journal_balance_change(msg.sender, m_state.get(msg.sender).balance);
     311               0 :         m_state.journal_balance_change(msg.recipient, dst_acc->balance);
     312               0 :         m_state.get(msg.sender).balance -= value;
     313               0 :         dst_acc->balance += value;
     314                 :     }
     315                 : 
     316 CBC         105 :     if (is_precompile(m_rev, msg.code_address))
     317 UBC           0 :         return call_precompile(m_rev, msg);
     318                 : 
     319 CBC         105 :     const auto code = dst_acc != nullptr ? bytes_view{dst_acc->code} : bytes_view{};
     320             105 :     return m_vm.execute(*this, m_rev, msg, code.data(), code.size());
     321                 : }
     322                 : 
     323             105 : evmc::Result Host::call(const evmc_message& orig_msg) noexcept
     324                 : {
     325             105 :     const auto msg = prepare_message(orig_msg);
     326             105 :     if (!msg.has_value())
     327 UBC           0 :         return evmc::Result{EVMC_FAILURE, orig_msg.gas};  // Light exception.
     328                 : 
     329 CBC         105 :     const auto logs_checkpoint = m_logs.size();
     330             105 :     const auto state_checkpoint = m_state.checkpoint();
     331                 : 
     332             105 :     auto result = execute_message(*msg);
     333                 : 
     334             105 :     if (result.status_code != EVMC_SUCCESS)
     335                 :     {
     336                 :         static constexpr auto addr_03 = 0x03_address;
     337              34 :         auto* const acc_03 = m_state.find(addr_03);
     338              34 :         const auto is_03_touched = acc_03 != nullptr && acc_03->erase_if_empty;
     339                 : 
     340                 :         // Revert.
     341              34 :         m_state.rollback(state_checkpoint);
     342              34 :         m_logs.resize(logs_checkpoint);
     343                 : 
     344                 :         // The 0x03 quirk: the touch on this address is never reverted.
     345              34 :         if (is_03_touched && m_rev >= EVMC_SPURIOUS_DRAGON)
     346 UBC           0 :             m_state.touch(addr_03);
     347                 :     }
     348 CBC         105 :     return result;
     349             105 : }
     350                 : 
     351 UBC           0 : evmc_tx_context Host::get_tx_context() const noexcept
     352                 : {
     353                 :     // TODO: The effective gas price is already computed in transaction validation.
     354                 :     // TODO: The effective gas price calculation is broken for system calls (gas price 0).
     355               0 :     assert(m_tx.max_gas_price >= m_block.base_fee || m_tx.max_gas_price == 0);
     356                 :     const auto priority_gas_price =
     357               0 :         std::min(m_tx.max_priority_gas_price, m_tx.max_gas_price - m_block.base_fee);
     358               0 :     const auto effective_gas_price = m_block.base_fee + priority_gas_price;
     359                 : 
     360                 :     return evmc_tx_context{
     361               0 :         intx::be::store<uint256be>(effective_gas_price),  // By EIP-1559.
     362               0 :         m_tx.sender,
     363               0 :         m_block.coinbase,
     364               0 :         m_block.number,
     365               0 :         m_block.timestamp,
     366               0 :         m_block.gas_limit,
     367               0 :         m_block.prev_randao,
     368               0 :         0x01_bytes32,  // Chain ID is expected to be 1.
     369               0 :         uint256be{m_block.base_fee},
     370               0 :         intx::be::store<uint256be>(m_block.blob_base_fee),
     371               0 :         m_tx.blob_hashes.data(),
     372               0 :         m_tx.blob_hashes.size(),
     373               0 :     };
     374                 : }
     375                 : 
     376               0 : bytes32 Host::get_block_hash(int64_t block_number) const noexcept
     377                 : {
     378               0 :     if (const auto& it = m_block.known_block_hashes.find(block_number);
     379               0 :         it != m_block.known_block_hashes.end())
     380               0 :         return it->second;
     381                 : 
     382                 :     // Convention for testing: if the block hash in unknown return the predefined "fake" value.
     383                 :     // https://github.com/ethereum/go-ethereum/blob/v1.12.2/tests/state_test_util.go#L432
     384               0 :     const auto s = std::to_string(block_number);
     385               0 :     return keccak256({reinterpret_cast<const uint8_t*>(s.data()), s.size()});
     386               0 : }
     387                 : 
     388               0 : void Host::emit_log(const address& addr, const uint8_t* data, size_t data_size,
     389                 :     const bytes32 topics[], size_t topics_count) noexcept
     390                 : {
     391               0 :     m_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
     392               0 : }
     393                 : 
     394 CBC         158 : evmc_access_status Host::access_account(const address& addr) noexcept
     395                 : {
     396             158 :     if (m_rev < EVMC_BERLIN)
     397 UBC           0 :         return EVMC_ACCESS_COLD;  // Ignore before Berlin.
     398                 : 
     399 CBC         158 :     auto& acc = m_state.get_or_insert(addr, {.erase_if_empty = true});
     400                 : 
     401             158 :     if (acc.access_status == EVMC_ACCESS_WARM || is_precompile(m_rev, addr))
     402              24 :         return EVMC_ACCESS_WARM;
     403                 : 
     404             134 :     m_state.journal_access_account(addr);
     405             134 :     acc.access_status = EVMC_ACCESS_WARM;
     406             134 :     return EVMC_ACCESS_COLD;
     407                 : }
     408                 : 
     409             288 : evmc_access_status Host::access_storage(const address& addr, const bytes32& key) noexcept
     410                 : {
     411             288 :     auto& storage_slot = m_state.get(addr).storage[key];
     412             288 :     m_state.journal_storage_change(addr, key, storage_slot);
     413             288 :     return std::exchange(storage_slot.access_status, EVMC_ACCESS_WARM);
     414                 : }
     415                 : 
     416                 : 
     417             178 : evmc::bytes32 Host::get_transient_storage(const address& addr, const bytes32& key) const noexcept
     418                 : {
     419             178 :     const auto& acc = m_state.get(addr);
     420             178 :     const auto it = acc.transient_storage.find(key);
     421             356 :     return it != acc.transient_storage.end() ? it->second : bytes32{};
     422                 : }
     423                 : 
     424             153 : void Host::set_transient_storage(
     425                 :     const address& addr, const bytes32& key, const bytes32& value) noexcept
     426                 : {
     427             153 :     auto& slot = m_state.get(addr).transient_storage[key];
     428             153 :     m_state.journal_transient_storage_change(addr, key, slot);
     429             153 :     slot = value;
     430             153 : }
     431                 : }  // namespace evmone::state
        

Generated by: LCOV version 2.0-1