diff --git a/.circleci/config.yml b/.circleci/config.yml index e28725192..0389fa17d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,20 +9,20 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-5 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:2d306b8da3485c2584a8868d656dc36c1ae50f003ff085ad2e904e312534b9b7" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:da44d7f78e093f7f0415abf07f7c1fd1c2ed4fa65fefea428821a05186c42ec9" ubuntu-2004-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-5 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4fbc7a99dd0b204fef587856d89640e4b2060d459ba15c32b89733b2a6054d7f" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c78dd9c48d393b57afe053aeb2d0d358a9f31ac85039a181724c2f8408d0bcf8" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-8 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:42f47b7ddafbf57b4e48357022cf34dc38ae477b05ddc2210e7ed68d821c2019" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-9 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:5078e1d74ab6f4329e9218c2d8c0ebe2d42817a3d4c3c62ce887100cbe9bc739" emscripten-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:emscripten-4 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:434719d8104cab47712dd1f56f255994d04eb65b802c0d382790071c1a0c074b" + # solbuildpackpusher/solidity-buildpack-deps:emscripten-5 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d28afb9624c2352ea40f157d1a321ffac77f54a21e33a8e8744f9126b780ded4" orbs: win: circleci/windows@2.2.0 diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 2845b224a..466a32130 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -55,12 +55,12 @@ then rm -rf z3-4.8.10-x64-osx-10.15.7 # evmone - wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz - tar xzpf evmone-0.4.0-darwin-x86_64.tar.gz -C /usr/local - rm -f evmone-0.4.0-darwin-x86_64.tar.gz + wget https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz + tar xzpf evmone-0.7.0-darwin-x86_64.tar.gz -C /usr/local + rm -f evmone-0.7.0-darwin-x86_64.tar.gz # hera - wget https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-darwin-x86_64.tar.gz - tar xzpf hera-0.3.2-darwin-x86_64.tar.gz -C /usr/local - rm -f hera-0.3.2-darwin-x86_64.tar.gz + wget https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz + tar xzpf hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz -C /usr/local + rm -f hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz fi diff --git a/docs/contributing.rst b/docs/contributing.rst index a98adf286..8953ad68e 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -112,7 +112,7 @@ starting from the current directory. The required file is called ``libevmone.so` ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, ``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from -`GitHub `_ +`GitHub `_ and place it in the project root path or inside the ``deps`` folder. If the ``libz3`` library is not installed on your system, you should disable the diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index ba69dab34..ea911d3da 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.5.0/evmone-0.5.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mkdir deps mv bin/evmone.dll deps diff --git a/test/Common.h b/test/Common.h index 5448c043c..4dfaeaed5 100644 --- a/test/Common.h +++ b/test/Common.h @@ -31,19 +31,19 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip"; static constexpr auto heraFilename = "hera.dll"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2-evmc8.tar.gz"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.dylib"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-darwin-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-linux-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.so"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-linux-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-linux-x86_64.tar.gz"; #endif struct ConfigException : public util::Exception {}; diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 4aad3c994..03c1d5029 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -148,6 +148,8 @@ void EVMHost::reset() recorded_selfdestructs.clear(); // Clear call records recorded_calls.clear(); + // Clear EIP-2929 account access indicator + recorded_account_accesses.clear(); // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). // NOTE: keep this in sync with `EVMHost::call` below. @@ -273,6 +275,14 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value); } + // Populate the access access list. + // Note, this will also properly touch the created address. + // TODO: support a user supplied access list too + if (m_evmRevision >= EVMC_BERLIN) + { + access_account(message.sender); + access_account(message.destination); + } evmc::address currentAddress = m_currentAddress; m_currentAddress = message.destination; evmc::result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size()); diff --git a/test/evmc/README.md b/test/evmc/README.md index 655eae4bc..68bcdd92b 100644 --- a/test/evmc/README.md +++ b/test/evmc/README.md @@ -1,5 +1,5 @@ # EVMC -This is an import of [EVMC](https://github.com/ethereum/evmc) version [7.5.0](https://github.com/ethereum/evmc/releases/tag/v7.5.0). +This is an import of [EVMC](https://github.com/ethereum/evmc) version [8.0.0](https://github.com/ethereum/evmc/releases/tag/v8.0.0). Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 88be51b61..5de5f5dae 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 7 + EVMC_ABI_VERSION = 8 }; @@ -603,6 +603,52 @@ typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context, const evmc_bytes32 topics[], size_t topics_count); +/** + * Access status per EIP-2929: Gas cost increases for state access opcodes. + */ +enum evmc_access_status +{ + /** + * The entry hasn't been accessed before – it's the first access. + */ + EVMC_ACCESS_COLD = 0, + + /** + * The entry is already in accessed_addresses or accessed_storage_keys. + */ + EVMC_ACCESS_WARM = 1 +}; + +/** + * Access account callback function. + * + * This callback function is used by a VM to add the given address + * to accessed_addresses substate (EIP-2929). + * + * @param context The Host execution context. + * @param address The address of the account. + * @return EVMC_ACCESS_WARM if accessed_addresses already contained the address + * or EVMC_ACCESS_COLD otherwise. + */ +typedef enum evmc_access_status (*evmc_access_account_fn)(struct evmc_host_context* context, + const evmc_address* address); + +/** + * Access storage callback function. + * + * This callback function is used by a VM to add the given account storage entry + * to accessed_storage_keys substate (EIP-2929). + * + * @param context The Host execution context. + * @param address The address of the account. + * @param key The index of the account's storage entry. + * @return EVMC_ACCESS_WARM if accessed_storage_keys already contained the key + * or EVMC_ACCESS_COLD otherwise. + */ +typedef enum evmc_access_status (*evmc_access_storage_fn)(struct evmc_host_context* context, + const evmc_address* address, + const evmc_bytes32* key); + /** * Pointer to the callback function supporting EVM calls. * @@ -658,6 +704,12 @@ struct evmc_host_interface /** Emit log callback function. */ evmc_emit_log_fn emit_log; + + /** Access account callback function. */ + evmc_access_account_fn access_account; + + /** Access storage callback function. */ + evmc_access_storage_fn access_storage; }; diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index fe72656ce..6d0bac9f2 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -465,6 +465,12 @@ public: size_t data_size, const bytes32 topics[], size_t num_topics) noexcept = 0; + + /// @copydoc evmc_host_interface::access_account + virtual evmc_access_status access_account(const address& addr) noexcept = 0; + + /// @copydoc evmc_host_interface::access_storage + virtual evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept = 0; }; @@ -564,6 +570,16 @@ public: { host->emit_log(context, &addr, data, data_size, topics, topics_count); } + + evmc_access_status access_account(const address& address) noexcept final + { + return host->access_account(context, &address); + } + + evmc_access_status access_storage(const address& address, const bytes32& key) noexcept final + { + return host->access_storage(context, &address, &key); + } }; @@ -805,6 +821,18 @@ inline void emit_log(evmc_host_context* h, Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics), num_topics); } + +inline evmc_access_status access_account(evmc_host_context* h, const evmc_address* addr) noexcept +{ + return Host::from_context(h)->access_account(*addr); +} + +inline evmc_access_status access_storage(evmc_host_context* h, + const evmc_address* addr, + const evmc_bytes32* key) noexcept +{ + return Host::from_context(h)->access_storage(*addr, *key); +} } // namespace internal inline const evmc_host_interface& Host::get_interface() noexcept @@ -815,7 +843,9 @@ inline const evmc_host_interface& Host::get_interface() noexcept ::evmc::internal::get_code_size, ::evmc::internal::get_code_hash, ::evmc::internal::copy_code, ::evmc::internal::selfdestruct, ::evmc::internal::call, ::evmc::internal::get_tx_context, - ::evmc::internal::get_block_hash, ::evmc::internal::emit_log}; + ::evmc::internal::get_block_hash, ::evmc::internal::emit_log, + ::evmc::internal::access_account, ::evmc::internal::access_storage, + }; return interface; } } // namespace evmc diff --git a/test/evmc/mocked_host.hpp b/test/evmc/mocked_host.hpp index 257de7a7c..c7d99e6ad 100644 --- a/test/evmc/mocked_host.hpp +++ b/test/evmc/mocked_host.hpp @@ -24,6 +24,9 @@ struct storage_value /// True means this value has been modified already by the current transaction. bool dirty{false}; + /// Is the storage key cold or warm. + evmc_access_status access_status{EVMC_ACCESS_COLD}; + /// Default constructor. storage_value() noexcept = default; @@ -31,6 +34,11 @@ struct storage_value storage_value(const bytes32& _value, bool _dirty = false) noexcept // NOLINT : value{_value}, dirty{_dirty} {} + + /// Constructor with initial access status. + storage_value(const bytes32& _value, evmc_access_status _access_status) noexcept + : value{_value}, access_status{_access_status} + {} }; /// Mocked account. @@ -84,7 +92,7 @@ public: }; /// SELFDESTRUCT record. - struct selfdestuct_record + struct selfdestruct_record { /// The address of the account which has self-destructed. address selfdestructed; @@ -93,7 +101,7 @@ public: address beneficiary; /// Equal operator. - bool operator==(const selfdestuct_record& other) const noexcept + bool operator==(const selfdestruct_record& other) const noexcept { return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary; } @@ -132,13 +140,12 @@ public: std::vector recorded_logs; /// The record of all SELFDESTRUCTs from the selfdestruct() method. - std::vector recorded_selfdestructs; + std::vector recorded_selfdestructs; private: /// The copy of call inputs for the recorded_calls record. std::vector m_recorded_calls_inputs; -public: /// Record an account access. /// @param addr The address of the accessed account. void record_account_access(const address& addr) const @@ -150,6 +157,7 @@ public: recorded_account_accesses.emplace_back(addr); } +public: /// Returns true if an account exists (EVMC Host method). bool account_exists(const address& addr) const noexcept override { @@ -313,5 +321,59 @@ public: { recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}}); } + + /// Record an account access. + /// + /// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. It will record the account + /// access in MockedHost::recorded_account_accesses and return previous access status. + /// This methods returns ::EVMC_ACCESS_WARM for known addresses of precompiles. + /// The EIP-2929 specifies that evmc_message::sender and evmc_message::destination are always + /// ::EVMC_ACCESS_WARM. Therefore, you should init the MockedHost with: + /// + /// mocked_host.access_account(msg.sender); + /// mocked_host.access_account(msg.destination); + /// + /// The same way you can mock transaction access list (EIP-2930) for account addresses. + /// + /// @param addr The address of the accessed account. + /// @returns The ::EVMC_ACCESS_WARM if the account has been accessed before, + /// the ::EVMC_ACCESS_COLD otherwise. + evmc_access_status access_account(const address& addr) noexcept override + { + // Check if the address have been already accessed. + const auto already_accessed = + std::find(recorded_account_accesses.begin(), recorded_account_accesses.end(), addr) != + recorded_account_accesses.end(); + + record_account_access(addr); + + // Accessing precompiled contracts is always warm. + if (addr >= 0x0000000000000000000000000000000000000001_address && + addr <= 0x0000000000000000000000000000000000000009_address) + return EVMC_ACCESS_WARM; + + return already_accessed ? EVMC_ACCESS_WARM : EVMC_ACCESS_COLD; + } + + /// Access the account's storage value at the given key. + /// + /// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. In records that the given + /// account's storage key has been access and returns the previous access status. + /// To mock storage access list (EIP-2930), you can pre-init account's storage values with + /// the ::EVMC_ACCESS_WARM flag: + /// + /// mocked_host.accounts[msg.destination].storage[key] = {value, EVMC_ACCESS_WARM}; + /// + /// @param addr The account address. + /// @param key The account's storage key. + /// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed before, + /// the ::EVMC_ACCESS_COLD otherwise. + evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override + { + auto& value = accounts[addr].storage[key]; + const auto access_status = value.access_status; + value.access_status = EVMC_ACCESS_WARM; + return access_status; + } }; } // namespace evmc