diff --git a/.circleci/config.yml b/.circleci/config.yml index d88cb0269..e06c53bde 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,8 +36,9 @@ defaults: mkdir -p build cd build protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz + protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS - make ossfuzz ossfuzz_proto -j4 + make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4 - run_proofs: &run_proofs name: Correctness proofs for optimization rules @@ -69,6 +70,7 @@ defaults: - artifacts_executables_ossfuzz: &artifacts_executables_ossfuzz root: build paths: + - test/tools/ossfuzz/abiv2_proto_ossfuzz - test/tools/ossfuzz/const_opt_ossfuzz - test/tools/ossfuzz/solc_noopt_ossfuzz - test/tools/ossfuzz/solc_opt_ossfuzz diff --git a/CMakeLists.txt b/CMakeLists.txt index acf3c39e2..4f545697f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ eth_policy() # project name and version should be set after cmake_policy CMP0048 set(PROJECT_VERSION "0.5.11") -project(solidity VERSION ${PROJECT_VERSION} LANGUAGES CXX) +project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) if (${CMAKE_VERSION} VERSION_LESS "3.9.0") # needed for the big endian test for older cmake versions diff --git a/cmake/templates/license.h.in b/cmake/templates/license.h.in index f2c766965..65276d8b4 100644 --- a/cmake/templates/license.h.in +++ b/cmake/templates/license.h.in @@ -97,6 +97,22 @@ scanner/token: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +evmc: + The code in test/evmc is licensed under the Apache License version 2: + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + All other code is licensed under GPL version 3: )"}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a666fb5f4..eb72ae0be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,7 +30,8 @@ add_executable(soltest ${sources} ${headers} ${libsolidity_sources} ${libsolidity_headers} ${libsolidity_util_sources} ${libsolidity_util_headers} ) -target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm devcore Boost::boost Boost::program_options Boost::unit_test_framework) +target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm devcore Boost::boost Boost::program_options Boost::unit_test_framework evmc) + # Special compilation flag for Visual Studio (version 2019 at least affected) # in order to compile SolidityEndToEndTest.cpp, which is quite huge. @@ -50,3 +51,4 @@ if (NOT Boost_USE_STATIC_LIBS) endif() add_subdirectory(tools) +add_subdirectory(evmc) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp new file mode 100644 index 000000000..43e7d693c --- /dev/null +++ b/test/EVMHost.cpp @@ -0,0 +1,215 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * EVM execution host, i.e. component that implements a simulated Ethereum blockchain + * for testing purposes. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::test; + +EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm& _vmInstance): + m_vm(_vmInstance) +{ + if (_evmVersion == langutil::EVMVersion::homestead()) + m_evmVersion = EVMC_HOMESTEAD; + else if (_evmVersion == langutil::EVMVersion::tangerineWhistle()) + m_evmVersion = EVMC_TANGERINE_WHISTLE; + else if (_evmVersion == langutil::EVMVersion::spuriousDragon()) + m_evmVersion = EVMC_SPURIOUS_DRAGON; + else if (_evmVersion == langutil::EVMVersion::byzantium()) + m_evmVersion = EVMC_BYZANTIUM; + else if (_evmVersion == langutil::EVMVersion::constantinople()) + m_evmVersion = EVMC_CONSTANTINOPLE; + else //if (_evmVersion == langutil::EVMVersion::petersburg()) + m_evmVersion = EVMC_PETERSBURG; +} + +evmc_storage_status EVMHost::set_storage(const evmc_address& _addr, const evmc_bytes32& _key, const evmc_bytes32& _value) noexcept +{ + evmc_bytes32 previousValue = m_state.accounts[_addr].storage[_key]; + m_state.accounts[_addr].storage[_key] = _value; + + // TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used + if (previousValue == _value) + return EVMC_STORAGE_UNCHANGED; + else if (previousValue == evmc_bytes32{}) + return EVMC_STORAGE_ADDED; + else if (_value == evmc_bytes32{}) + return EVMC_STORAGE_DELETED; + else + return EVMC_STORAGE_MODIFIED; + +} + +void EVMHost::selfdestruct(const evmc_address& _addr, const evmc_address& _beneficiary) noexcept +{ + // TODO actual selfdestruct is even more complicated. + evmc_uint256be balance = m_state.accounts[_addr].balance; + m_state.accounts.erase(_addr); + m_state.accounts[_beneficiary].balance = balance; +} + +evmc::result EVMHost::call(evmc_message const& _message) noexcept +{ + if (_message.destination == convertToEVMC(Address(2))) + return precompileSha256(_message); + + State stateBackup = m_state; + + u256 value{convertFromEVMC(_message.value)}; + Account& sender = m_state.accounts[_message.sender]; + + bytes code; + + evmc_message message = _message; + if (message.kind == EVMC_CREATE) + { + // TODO this is not the right formula + // TODO is the nonce incremented on failure, too? + Address createAddress(keccak256( + bytes(begin(message.sender.bytes), end(message.sender.bytes)) + + asBytes(to_string(sender.nonce++)) + )); + message.destination = convertToEVMC(createAddress); + code = bytes(message.input_data, message.input_data + message.input_size); + } + else if (message.kind == EVMC_DELEGATECALL) + { + code = m_state.accounts[message.destination].code; + message.destination = m_currentAddress; + } + else if (message.kind == EVMC_CALLCODE) + { + code = m_state.accounts[message.destination].code; + message.destination = m_currentAddress; + } + else + code = m_state.accounts[message.destination].code; + //TODO CREATE2 + + Account& destination = m_state.accounts[message.destination]; + + if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) + { + sender.balance = convertToEVMC(u256(convertFromEVMC(sender.balance)) - value); + destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value); + } + + evmc_address currentAddress = m_currentAddress; + m_currentAddress = message.destination; + evmc::result result = m_vm.execute(*this, m_evmVersion, message, code.data(), code.size()); + m_currentAddress = currentAddress; + + if (result.status_code != EVMC_SUCCESS) + m_state = stateBackup; + else if (message.kind == EVMC_CREATE) + { + result.create_address = message.destination; + destination.code = bytes(result.output_data, result.output_data + result.output_size); + destination.codeHash = convertToEVMC(keccak256(destination.code)); + } + + return result; +} + +evmc_tx_context EVMHost::get_tx_context() noexcept +{ + evmc_tx_context ctx = {}; + ctx.block_timestamp = m_state.timestamp; + ctx.block_number = m_state.blockNumber; + ctx.block_coinbase = m_coinbase; + ctx.block_difficulty = convertToEVMC(u256("200000000")); + ctx.block_gas_limit = 20000000; + ctx.tx_gas_price = convertToEVMC(u256("3000000000")); + ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292")); + return ctx; +} + +evmc_bytes32 EVMHost::get_block_hash(int64_t _number) noexcept +{ + return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); +} + +void EVMHost::emit_log( + evmc_address const& _addr, + uint8_t const* _data, + size_t _dataSize, + evmc_bytes32 const _topics[], + size_t _topicsCount +) noexcept +{ + LogEntry entry; + entry.address = convertFromEVMC(_addr); + for (size_t i = 0; i < _topicsCount; ++i) + entry.topics.emplace_back(convertFromEVMC(_topics[i])); + entry.data = bytes(_data, _data + _dataSize); + m_state.logs.emplace_back(std::move(entry)); +} + + +Address EVMHost::convertFromEVMC(evmc_address const& _addr) +{ + return Address(bytes(begin(_addr.bytes), end(_addr.bytes))); +} + +evmc_address EVMHost::convertToEVMC(Address const& _addr) +{ + evmc_address a; + for (size_t i = 0; i < 20; ++i) + a.bytes[i] = _addr[i]; + return a; +} + +h256 EVMHost::convertFromEVMC(evmc_bytes32 const& _data) +{ + return h256(bytes(begin(_data.bytes), end(_data.bytes))); +} + +evmc_bytes32 EVMHost::convertToEVMC(h256 const& _data) +{ + evmc_bytes32 d; + for (size_t i = 0; i < 32; ++i) + d.bytes[i] = _data[i]; + return d; +} + +evmc::result EVMHost::precompileSha256(evmc_message const& _message) noexcept +{ + // static data so that we do not need a release routine... + bytes static hash; + hash = picosha2::hash256(bytes( + _message.input_data, + _message.input_data + _message.input_size + )); + + evmc::result result({}); + result.output_data = hash.data(); + result.output_size = hash.size(); + return result; +} diff --git a/test/EVMHost.h b/test/EVMHost.h new file mode 100644 index 000000000..8ee28e592 --- /dev/null +++ b/test/EVMHost.h @@ -0,0 +1,175 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * EVM execution host, i.e. component that implements a simulated Ethereum blockchain + * for testing purposes. + */ + +#pragma once + +#include +#include +#include + +#include + +#include + + +namespace dev +{ +namespace test +{ +using Address = h160; + +class EVMHost: public evmc::Host +{ +public: + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::vm& _vmInstance); + + struct Account + { + evmc_uint256be balance = {}; + size_t nonce = 0; + bytes code; + evmc_bytes32 codeHash = {}; + std::map storage; + }; + + struct LogEntry + { + Address address; + std::vector topics; + bytes data; + }; + + struct State + { + size_t blockNumber; + uint64_t timestamp; + std::map accounts; + std::vector logs; + }; + + Account* account(evmc_address const& _address) + { + auto it = m_state.accounts.find(_address); + return it == m_state.accounts.end() ? nullptr : &it->second; + } + + void reset() { m_state = State{}; m_currentAddress = {}; } + void newBlock() + { + m_state.blockNumber++; + m_state.timestamp += 15; + m_state.logs.clear(); + } + + bool account_exists(evmc_address const& _addr) noexcept final + { + return account(_addr) != nullptr; + } + + evmc_bytes32 get_storage(evmc_address const& _addr, evmc_bytes32 const& _key) noexcept final + { + if (Account* acc = account(_addr)) + return acc->storage[_key]; + return {}; + } + + evmc_storage_status set_storage( + evmc_address const& _addr, + evmc_bytes32 const& _key, + evmc_bytes32 const& _value + ) noexcept; + + evmc_uint256be get_balance(evmc_address const& _addr) noexcept final + { + if (Account const* acc = account(_addr)) + return acc->balance; + return {}; + } + + size_t get_code_size(evmc_address const& _addr) noexcept final + { + if (Account const* acc = account(_addr)) + return acc->code.size(); + return 0; + } + + evmc_bytes32 get_code_hash(evmc_address const& _addr) noexcept final + { + if (Account const* acc = account(_addr)) + return acc->codeHash; + return {}; + } + + size_t copy_code( + evmc_address const& _addr, + size_t _codeOffset, + uint8_t* _bufferData, + size_t _bufferSize + ) noexcept final + { + size_t i = 0; + if (Account const* acc = account(_addr)) + for (; i < _bufferSize && _codeOffset + i < acc->code.size(); i++) + _bufferData[i] = acc->code[_codeOffset + i]; + return i; + } + + void selfdestruct(evmc_address const& _addr, evmc_address const& _beneficiary) noexcept; + + evmc::result call(evmc_message const& _message) noexcept; + + evmc_tx_context get_tx_context() noexcept; + + evmc_bytes32 get_block_hash(int64_t number) noexcept; + + void emit_log( + evmc_address const& _addr, + uint8_t const* _data, + size_t _dataSize, + evmc_bytes32 const _topics[], + size_t _topicsCount + ) noexcept; + + evmc_revision getRevision() + { + return m_evmVersion; + } + + static Address convertFromEVMC(evmc_address const& _addr); + static evmc_address convertToEVMC(Address const& _addr); + static h256 convertFromEVMC(evmc_bytes32 const& _data); + static evmc_bytes32 convertToEVMC(h256 const& _data); + + + State m_state; + evmc_address m_currentAddress = {}; + evmc_address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878")); + +private: + evmc::result precompileSha256(evmc_message const& _message) noexcept; + + evmc::vm& m_vm; + evmc_revision m_evmVersion; +}; + + +} +} diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index ccb5f6119..2539b06cb 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -27,6 +27,10 @@ #include #include +#include +#include +#include + #include #include diff --git a/test/evmc/CMakeLists.txt b/test/evmc/CMakeLists.txt new file mode 100644 index 000000000..c08306e18 --- /dev/null +++ b/test/evmc/CMakeLists.txt @@ -0,0 +1,23 @@ +# EVMC: Ethereum Client-VM Connector API. +# Copyright 2016-2019 The EVMC Authors. +# Licensed under the Apache License, Version 2.0. + + +add_library(evmc_loader STATIC loader.h loader.c) +set_target_properties(evmc_loader PROPERTIES LINKER_LANGUAGE C) +target_link_libraries(evmc_loader INTERFACE ${CMAKE_DL_LIBS}) +target_include_directories(evmc_loader PUBLIC ${PROJECT_SOURCE_DIR}/test/) +set_target_properties(evmc_loader PROPERTIES + POSITION_INDEPENDENT_CODE TRUE +) + +add_library(evmc INTERFACE) +target_sources(evmc INTERFACE + ${PROJECT_SOURCE_DIR}/test/evmc/evmc.h + ${PROJECT_SOURCE_DIR}/test/evmc/evmc.hpp + ${PROJECT_SOURCE_DIR}/test/evmc/helpers.h + ${PROJECT_SOURCE_DIR}/test/evmc/helpers.hpp + ${PROJECT_SOURCE_DIR}/test/evmc/utils.h +) +target_include_directories(evmc INTERFACE ${PROJECT_SOURCE_DIR}/test/) +target_link_libraries(evmc INTERFACE evmc_loader) \ No newline at end of file diff --git a/test/evmc/LICENSE b/test/evmc/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/test/evmc/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h new file mode 100644 index 000000000..81cba6cff --- /dev/null +++ b/test/evmc/evmc.h @@ -0,0 +1,1029 @@ +/** + * EVMC: Ethereum Client-VM Connector API + * + * @copyright + * Copyright 2016-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + * + * @defgroup EVMC EVMC + * @{ + */ +#ifndef EVMC_H +#define EVMC_H + +#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 6) +/** + * Portable declaration of "deprecated" attribute. + * + * Available for clang and GCC 6+ compilers. The older GCC compilers know + * this attribute, but it cannot be applied to enum elements. + */ +#define EVMC_DEPRECATED __attribute__((deprecated)) +#else +#define EVMC_DEPRECATED +#endif + + +#include /* Definition of bool, true and false. */ +#include /* Definition of size_t. */ +#include /* Definition of int64_t, uint64_t. */ + +#if __cplusplus +extern "C" { +#endif + +/* BEGIN Python CFFI declarations */ + +enum +{ + /** + * The EVMC ABI version number of the interface declared in this file. + * + * The EVMC ABI version always equals the major version number of the EVMC project. + * The Host SHOULD check if the ABI versions match when dynamically loading VMs. + * + * @see @ref versioning + */ + EVMC_ABI_VERSION = 6 +}; + + +/** + * The fixed size array of 32 bytes. + * + * 32 bytes of data capable of storing e.g. 256-bit hashes. + */ +typedef struct evmc_bytes32 +{ + /** The 32 bytes. */ + uint8_t bytes[32]; +} evmc_bytes32; + +/** + * The alias for evmc_bytes32 to represent a big-endian 256-bit integer. + */ +typedef struct evmc_bytes32 evmc_uint256be; + +/** Big-endian 160-bit hash suitable for keeping an Ethereum address. */ +typedef struct evmc_address +{ + /** The 20 bytes of the hash. */ + uint8_t bytes[20]; +} evmc_address; + +/** The kind of call-like instruction. */ +enum evmc_call_kind +{ + EVMC_CALL = 0, /**< Request CALL. */ + EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. Valid since Homestead. + The value param ignored. */ + EVMC_CALLCODE = 2, /**< Request CALLCODE. */ + EVMC_CREATE = 3, /**< Request CREATE. */ + EVMC_CREATE2 = 4 /**< Request CREATE2. Valid since Constantinople.*/ +}; + +/** The flags for ::evmc_message. */ +enum evmc_flags +{ + EVMC_STATIC = 1 /**< Static call mode. */ +}; + +/** + * The message describing an EVM call, + * including a zero-depth calls from a transaction origin. + */ +struct evmc_message +{ + /** The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used. */ + enum evmc_call_kind kind; + + /** + * Additional flags modifying the call execution behavior. + * In the current version the only valid values are ::EVMC_STATIC or 0. + */ + uint32_t flags; + + /** The call depth. */ + int32_t depth; + + /** The amount of gas for message execution. */ + int64_t gas; + + /** The destination of the message. */ + evmc_address destination; + + /** The sender of the message. */ + evmc_address sender; + + /** + * The message input data. + * + * This MAY be NULL. + */ + const uint8_t* input_data; + + /** + * The size of the message input data. + * + * If input_data is NULL this MUST be 0. + */ + size_t input_size; + + /** + * The amount of Ether transferred with the message. + */ + evmc_uint256be value; + + /** + * The optional value used in new contract address construction. + * + * Ignored unless kind is EVMC_CREATE2. + */ + evmc_bytes32 create2_salt; +}; + + +/** The transaction and block data for execution. */ +struct evmc_tx_context +{ + evmc_uint256be tx_gas_price; /**< The transaction gas price. */ + evmc_address tx_origin; /**< The transaction origin account. */ + evmc_address block_coinbase; /**< The miner of the block. */ + int64_t block_number; /**< The block number. */ + int64_t block_timestamp; /**< The block timestamp. */ + int64_t block_gas_limit; /**< The block gas limit. */ + evmc_uint256be block_difficulty; /**< The block difficulty. */ +}; + +struct evmc_context; + +/** + * Get transaction context callback function. + * + * This callback function is used by an EVM to retrieve the transaction and + * block context. + * + * @param context The pointer to the Host execution context. + * @return The transaction context. + */ +typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context); + +/** + * Get block hash callback function. + * + * This callback function is used by a VM to query the hash of the header of the given block. + * If the information about the requested block is not available, then this is signalled by + * returning null bytes. + * + * @param context The pointer to the Host execution context. + * @param number The block number. + * @return The block hash or null bytes + * if the information about the block is not available. + */ +typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number); + +/** + * The execution status code. + * + * Successful execution is represented by ::EVMC_SUCCESS having value 0. + * + * Positive values represent failures defined by VM specifications with generic + * ::EVMC_FAILURE code of value 1. + * + * Status codes with negative values represent VM internal errors + * not provided by EVM specifications. These errors MUST not be passed back + * to the caller. They MAY be handled by the Client in predefined manner + * (see e.g. ::EVMC_REJECTED), otherwise internal errors are not recoverable. + * The generic representant of errors is ::EVMC_INTERNAL_ERROR but + * an EVM implementation MAY return negative status codes that are not defined + * in the EVMC documentation. + * + * @note + * In case new status codes are needed, please create an issue or pull request + * in the EVMC repository (https://github.com/ethereum/evmc). + */ +enum evmc_status_code +{ + /** Execution finished with success. */ + EVMC_SUCCESS = 0, + + /** Generic execution failure. */ + EVMC_FAILURE = 1, + + /** + * Execution terminated with REVERT opcode. + * + * In this case the amount of gas left MAY be non-zero and additional output + * data MAY be provided in ::evmc_result. + */ + EVMC_REVERT = 2, + + /** The execution has run out of gas. */ + EVMC_OUT_OF_GAS = 3, + + /** + * The designated INVALID instruction has been hit during execution. + * + * The EIP-141 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) + * defines the instruction 0xfe as INVALID instruction to indicate execution + * abortion coming from high-level languages. This status code is reported + * in case this INVALID instruction has been encountered. + */ + EVMC_INVALID_INSTRUCTION = 4, + + /** An undefined instruction has been encountered. */ + EVMC_UNDEFINED_INSTRUCTION = 5, + + /** + * The execution has attempted to put more items on the EVM stack + * than the specified limit. + */ + EVMC_STACK_OVERFLOW = 6, + + /** Execution of an opcode has required more items on the EVM stack. */ + EVMC_STACK_UNDERFLOW = 7, + + /** Execution has violated the jump destination restrictions. */ + EVMC_BAD_JUMP_DESTINATION = 8, + + /** + * Tried to read outside memory bounds. + * + * An example is RETURNDATACOPY reading past the available buffer. + */ + EVMC_INVALID_MEMORY_ACCESS = 9, + + /** Call depth has exceeded the limit (if any) */ + EVMC_CALL_DEPTH_EXCEEDED = 10, + + /** Tried to execute an operation which is restricted in static mode. */ + EVMC_STATIC_MODE_VIOLATION = 11, + + /** + * A call to a precompiled or system contract has ended with a failure. + * + * An example: elliptic curve functions handed invalid EC points. + */ + EVMC_PRECOMPILE_FAILURE = 12, + + /** + * Contract validation has failed (e.g. due to EVM 1.5 jump validity, + * Casper's purity checker or ewasm contract rules). + */ + EVMC_CONTRACT_VALIDATION_FAILURE = 13, + + /** + * An argument to a state accessing method has a value outside of the + * accepted range of values. + */ + EVMC_ARGUMENT_OUT_OF_RANGE = 14, + + /** + * A WebAssembly `unreachable` instruction has been hit during execution. + */ + EVMC_WASM_UNREACHABLE_INSTRUCTION = 15, + + /** + * A WebAssembly trap has been hit during execution. This can be for many + * reasons, including division by zero, validation errors, etc. + */ + EVMC_WASM_TRAP = 16, + + /** EVM implementation generic internal error. */ + EVMC_INTERNAL_ERROR = -1, + + /** + * The execution of the given code and/or message has been rejected + * by the EVM implementation. + * + * This error SHOULD be used to signal that the EVM is not able to or + * willing to execute the given code type or message. + * If an EVM returns the ::EVMC_REJECTED status code, + * the Client MAY try to execute it in other EVM implementation. + * For example, the Client tries running a code in the EVM 1.5. If the + * code is not supported there, the execution falls back to the EVM 1.0. + */ + EVMC_REJECTED = -2 +}; + +/* Forward declaration. */ +struct evmc_result; + +/** + * Releases resources assigned to an execution result. + * + * This function releases memory (and other resources, if any) assigned to the + * specified execution result making the result object invalid. + * + * @param result The execution result which resources are to be released. The + * result itself it not modified by this function, but becomes + * invalid and user MUST discard it as well. + * This MUST NOT be NULL. + * + * @note + * The result is passed by pointer to avoid (shallow) copy of the ::evmc_result + * struct. Think of this as the best possible C language approximation to + * passing objects by reference. + */ +typedef void (*evmc_release_result_fn)(const struct evmc_result* result); + +/** The EVM code execution result. */ +struct evmc_result +{ + /** The execution status code. */ + enum evmc_status_code status_code; + + /** + * The amount of gas left after the execution. + * + * If evmc_result::code is not ::EVMC_SUCCESS nor ::EVMC_REVERT + * the value MUST be 0. + */ + int64_t gas_left; + + /** + * The reference to output data. + * + * The output contains data coming from RETURN opcode (iff evmc_result::code + * field is ::EVMC_SUCCESS) or from REVERT opcode. + * + * The memory containing the output data is owned by EVM and has to be + * freed with evmc_result::release(). + * + * This MAY be NULL. + */ + const uint8_t* output_data; + + /** + * The size of the output data. + * + * If output_data is NULL this MUST be 0. + */ + size_t output_size; + + /** + * The method releasing all resources associated with the result object. + * + * This method (function pointer) is optional (MAY be NULL) and MAY be set + * by the VM implementation. If set it MUST be called by the user once to + * release memory and other resources associated with the result object. + * Once the resources are released the result object MUST NOT be used again. + * + * The suggested code pattern for releasing execution results: + * @code + * struct evmc_result result = ...; + * if (result.release) + * result.release(&result); + * @endcode + * + * @note + * It works similarly to C++ virtual destructor. Attaching the release + * function to the result itself allows VM composition. + */ + evmc_release_result_fn release; + + /** + * The address of the contract created by create instructions. + * + * This field has valid value only if: + * - it is a result of the Host method evmc_host_interface::call + * - and the result describes successful contract creation + * (evmc_result::status_code is ::EVMC_SUCCESS). + * In all other cases the address MUST be null bytes. + */ + evmc_address create_address; + + /** + * Reserved data that MAY be used by a evmc_result object creator. + * + * This reserved 4 bytes together with 20 bytes from create_address form + * 24 bytes of memory called "optional data" within evmc_result struct + * to be optionally used by the evmc_result object creator. + * + * @see evmc_result_optional_data, evmc_get_optional_data(). + * + * Also extends the size of the evmc_result to 64 bytes (full cache line). + */ + uint8_t padding[4]; +}; + + +/** + * Check account existence callback function. + * + * This callback function is used by the VM to check if + * there exists an account at given address. + * @param context The pointer to the Host execution context. + * @param address The address of the account the query is about. + * @return true if exists, false otherwise. + */ +typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address); + +/** + * Get storage callback function. + * + * This callback function is used by a VM to query the given account storage entry. + * + * @param context The Host execution context. + * @param address The address of the account. + * @param key The index of the account's storage entry. + * @return The storage value at the given storage key or null bytes + * if the account does not exist. + */ +typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context, + const evmc_address* address, + const evmc_bytes32* key); + + +/** + * The effect of an attempt to modify a contract storage item. + * + * For the purpose of explaining the meaning of each element, the following + * notation is used: + * - 0 is zero value, + * - X != 0 (X is any value other than 0), + * - Y != X, Y != 0 (Y is any value other than X and 0), + * - Z != Y (Z is any value other than Y), + * - the "->" means the change from one value to another. + */ +enum evmc_storage_status +{ + /** + * The value of a storage item has been left unchanged: 0 -> 0 and X -> X. + */ + EVMC_STORAGE_UNCHANGED = 0, + + /** + * The value of a storage item has been modified: X -> Y. + */ + EVMC_STORAGE_MODIFIED = 1, + + /** + * A storage item has been modified after being modified before: X -> Y -> Z. + */ + EVMC_STORAGE_MODIFIED_AGAIN = 2, + + /** + * A new storage item has been added: 0 -> X. + */ + EVMC_STORAGE_ADDED = 3, + + /** + * A storage item has been deleted: X -> 0. + */ + EVMC_STORAGE_DELETED = 4 +}; + + +/** + * Set storage callback function. + * + * This callback function is used by a VM to update the given account storage entry. + * The VM MUST make sure that the account exists. This requirement is only a formality because + * VM implementations only modify storage of the account of the current execution context + * (i.e. referenced by evmc_message::destination). + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @param key The index of the storage entry. + * @param value The value to be stored. + * @return The effect on the storage item. + */ +typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* context, + const evmc_address* address, + const evmc_bytes32* key, + const evmc_bytes32* value); + +/** + * Get balance callback function. + * + * This callback function is used by a VM to query the balance of the given account. + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @return The balance of the given account or 0 if the account does not exist. + */ +typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, + const evmc_address* address); + +/** + * Get code size callback function. + * + * This callback function is used by a VM to get the size of the code stored + * in the account at the given address. + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @return The size of the code in the account or 0 if the account does not exist. + */ +typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address); + +/** + * Get code size callback function. + * + * This callback function is used by a VM to get the keccak256 hash of the code stored + * in the account at the given address. For existing accounts not having a code, this + * function returns keccak256 hash of empty data. + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @return The hash of the code in the account or null bytes if the account does not exist. + */ +typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context, + const evmc_address* address); + +/** + * Copy code callback function. + * + * This callback function is used by an EVM to request a copy of the code + * of the given account to the memory buffer provided by the EVM. + * The Client MUST copy the requested code, starting with the given offset, + * to the provided memory buffer up to the size of the buffer or the size of + * the code, whichever is smaller. + * + * @param context The pointer to the Client execution context. + * @see ::evmc_context. + * @param address The address of the account. + * @param code_offset The offset of the code to copy. + * @param buffer_data The pointer to the memory buffer allocated by the EVM + * to store a copy of the requested code. + * @param buffer_size The size of the memory buffer. + * @return The number of bytes copied to the buffer by the Client. + */ +typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, + const evmc_address* address, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size); + +/** + * Selfdestruct callback function. + * + * This callback function is used by an EVM to SELFDESTRUCT given contract. + * The execution of the contract will not be stopped, that is up to the EVM. + * + * @param context The pointer to the Host execution context. + * @see ::evmc_context. + * @param address The address of the contract to be selfdestructed. + * @param beneficiary The address where the remaining ETH is going to be + * transferred. + */ +typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context, + const evmc_address* address, + const evmc_address* beneficiary); + +/** + * Log callback function. + * + * This callback function is used by an EVM to inform about a LOG that happened + * during an EVM bytecode execution. + * @param context The pointer to the Host execution context. + * @see ::evmc_context. + * @param address The address of the contract that generated the log. + * @param data The pointer to unindexed data attached to the log. + * @param data_size The length of the data. + * @param topics The pointer to the array of topics attached to the log. + * @param topics_count The number of the topics. Valid values are between + * 0 and 4 inclusively. + */ +typedef void (*evmc_emit_log_fn)(struct evmc_context* context, + const evmc_address* address, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t topics_count); + +/** + * Pointer to the callback function supporting EVM calls. + * + * @param context The pointer to the Host execution context. + * @param msg The call parameters. + * @return The result of the call. + */ +typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context, + const struct evmc_message* msg); + +/** + * The Host interface. + * + * The set of all callback functions expected by VM instances. This is C + * realisation of vtable for OOP interface (only virtual methods, no data). + * Host implementations SHOULD create constant singletons of this (similarly + * to vtables) to lower the maintenance and memory management cost. + */ +struct evmc_host_interface +{ + /** Check account existence callback function. */ + evmc_account_exists_fn account_exists; + + /** Get storage callback function. */ + evmc_get_storage_fn get_storage; + + /** Set storage callback function. */ + evmc_set_storage_fn set_storage; + + /** Get balance callback function. */ + evmc_get_balance_fn get_balance; + + /** Get code size callback function. */ + evmc_get_code_size_fn get_code_size; + + /** Get code hash callback function. */ + evmc_get_code_hash_fn get_code_hash; + + /** Copy code callback function. */ + evmc_copy_code_fn copy_code; + + /** Selfdestruct callback function. */ + evmc_selfdestruct_fn selfdestruct; + + /** Call callback function. */ + evmc_call_fn call; + + /** Get transaction context callback function. */ + evmc_get_tx_context_fn get_tx_context; + + /** Get block hash callback function. */ + evmc_get_block_hash_fn get_block_hash; + + /** Emit log callback function. */ + evmc_emit_log_fn emit_log; +}; + + +/** + * Execution context managed by the Host. + * + * The Host MUST pass the pointer to the execution context to ::evmc_execute_fn. + * The VM MUST pass the same pointer back to the Host in every callback function. + * The context MUST contain at least the function table defining + * the context callback interface. + * Optionally, the Host MAY include in the context additional data. + */ +struct evmc_context +{ + /** The Host interface. */ + const struct evmc_host_interface* host; +}; + + +/* Forward declaration. */ +struct evmc_instance; + +/** + * Destroys the EVM instance. + * + * @param evm The EVM instance to be destroyed. + */ +typedef void (*evmc_destroy_fn)(struct evmc_instance* evm); + +/** + * Possible outcomes of evmc_set_option. + */ +enum evmc_set_option_result +{ + EVMC_SET_OPTION_SUCCESS = 0, + EVMC_SET_OPTION_INVALID_NAME = 1, + EVMC_SET_OPTION_INVALID_VALUE = 2 +}; + +/** + * Configures the EVM instance. + * + * Allows modifying options of the EVM instance. + * Options: + * - code cache behavior: on, off, read-only, ... + * - optimizations, + * + * @param evm The EVM instance to be configured. + * @param name The option name. NULL-terminated string. Cannot be NULL. + * @param value The new option value. NULL-terminated string. Cannot be NULL. + * @return The outcome of the operation. + */ +typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm, + char const* name, + char const* value); + + +/** + * EVM revision. + * + * The revision of the EVM specification based on the Ethereum + * upgrade / hard fork codenames. + */ +enum evmc_revision +{ + /** + * The Frontier revision. + * + * The one Ethereum launched with. + */ + EVMC_FRONTIER = 0, + + /** + * The Homestead revision. + * + * https://eips.ethereum.org/EIPS/eip-606 + */ + EVMC_HOMESTEAD = 1, + + /** + * The Tangerine Whistle revision. + * + * https://eips.ethereum.org/EIPS/eip-608 + */ + EVMC_TANGERINE_WHISTLE = 2, + + /** + * The Spurious Dragon revision. + * + * https://eips.ethereum.org/EIPS/eip-607 + */ + EVMC_SPURIOUS_DRAGON = 3, + + /** + * The Byzantium revision. + * + * https://eips.ethereum.org/EIPS/eip-609 + */ + EVMC_BYZANTIUM = 4, + + /** + * The Constantinople revision. + * + * https://eips.ethereum.org/EIPS/eip-1013 + */ + EVMC_CONSTANTINOPLE = 5, + + /** + * The Petersburg revision. + * + * Other names: Constantinople2, ConstantinopleFix. + * https://eips.ethereum.org/EIPS/eip-1716 + */ + EVMC_PETERSBURG = 6, + + /** + * The Istanbul revision. + * + * The spec draft: https://eips.ethereum.org/EIPS/eip-1679. + */ + EVMC_ISTANBUL = 7, + + /** The maximum EVM revision supported. */ + EVMC_MAX_REVISION = EVMC_ISTANBUL, + + + /** + * Reserved for the post-Constantinople upgrade. + * + * @deprecated Replaced with ::EVMC_PETERSBURG. + */ + EVMC_CONSTANTINOPLE2 EVMC_DEPRECATED = EVMC_PETERSBURG, + + /** + * The latests EVM revision supported. + * + * @deprecated Replaced with ::EVMC_MAX_REVISION. + */ + EVMC_LATEST_REVISION EVMC_DEPRECATED = EVMC_MAX_REVISION +}; + + +/** + * Executes the given code using the input from the message. + * + * This function MAY be invoked multiple times for a single VM instance. + * + * @param instance The VM instance. + * @param context The pointer to the Client execution context to be passed + * to the callback functions. See ::evmc_context. + * @param rev Requested EVM specification revision. + * @param msg Call parameters. See ::evmc_message. + * @param code Reference to the code to be executed. + * @param code_size The length of the code. + * @return The execution result. + */ +typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance, + struct evmc_context* context, + enum evmc_revision rev, + const struct evmc_message* msg, + uint8_t const* code, + size_t code_size); + +/** + * Possible capabilities of a VM. + */ +enum evmc_capabilities +{ + /** + * The VM is capable of executing EVM1 bytecode. + */ + EVMC_CAPABILITY_EVM1 = (1u << 0), + + /** + * The VM is capable of executing ewasm bytecode. + */ + EVMC_CAPABILITY_EWASM = (1u << 1), + + /** + * The VM is capable of executing the precompiled contracts + * defined for the range of destination addresses. + * + * The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies + * the range 0x000...0000 - 0x000...ffff of addresses + * reserved for precompiled and system contracts. + * + * This capability is **experimental** and MAY be removed without notice. + */ + EVMC_CAPABILITY_PRECOMPILES = (1u << 2) +}; + +/** + * Alias for unsigned integer representing a set of bit flags of EVMC capabilities. + * + * @see evmc_capabilities + */ +typedef uint32_t evmc_capabilities_flagset; + +/** + * Return the supported capabilities of the VM instance. + * + * This function MAY be invoked multiple times for a single VM instance, + * and its value MAY be influenced by calls to evmc_instance::set_option. + * + * @param instance The EVM instance. + * @return The supported capabilities of the VM. @see evmc_capabilities. + */ +typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance); + +/** The opaque type representing a Client-side tracer object. */ +struct evmc_tracer_context; + +/** + * The callback to trace instructions execution in an EVM. + * + * This function informs the Client what instruction has been executed in the EVM implementation + * and what are the results of executing this particular instruction. + * The message level information (like call depth, destination address, etc.) are not provided here. + * This piece of information can be acquired by inspecting messages being sent to the EVM in + * ::evmc_execute_fn and the results of the messages execution. + * + * @param context The pointer to the Client-side tracing context. This allows to + * implement the tracer in OOP manner. + * @param code_offset The current instruction position in the code. + * @param status_code The status code of the instruction execution. + * @param gas_left The amount of the gas left after the instruction execution. + * @param stack_num_items The current EVM stack height after the instruction execution. + * @param pushed_stack_item The top EVM stack item pushed as the result of the instruction + * execution. This value is null when the instruction does not push + * anything to the stack. + * @param memory_size The size of the EVM memory after the instruction execution. + * @param changed_memory_offset The offset in number of bytes of the beginning of the memory area + * modified as the result of the instruction execution. + * The Client MAY use this information together with + * @p changed_memory_size and @p changed_memory to incrementally + * update the copy of the full VM's memory. + * @param changed_memory_size The size of the memory area modified as the result of + * the instruction execution. + * @param changed_memory The pointer to the memory area modified as the result of + * the instruction execution. + * The Client MAY access the pointed memory area + * (limited by the @p changed_memory_size) only during the current + * execution of the evmc_trace_callback(). + * The pointer MUST NOT be stored by the Client. + * The Client MUST NOT assume that + * `changed_memory - changed_memory_offset` is a valid base pointer + * of the VM memory. + */ +typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context, + size_t code_offset, + enum evmc_status_code status_code, + int64_t gas_left, + size_t stack_num_items, + const evmc_uint256be* pushed_stack_item, + size_t memory_size, + size_t changed_memory_offset, + size_t changed_memory_size, + const uint8_t* changed_memory); + +/** + * Sets the EVM instruction tracer. + * + * When the tracer is set in the EVM instance, the EVM SHOULD call back the tracer with information + * about instructions execution in the EVM. + * @see ::evmc_trace_callback. + * + * This will overwrite the previous settings (the callback and the context). + * + * @param instance The EVM instance. + * @param callback The tracer callback function. This argument MAY be NULL to disable previously + * set tracer. + * @param context The Client-side tracer context. This argument MAY be NULL in case the tracer + * does not require any context. This argument MUST be NULL if the callback + * argument is NULL. + */ +typedef void (*evmc_set_tracer_fn)(struct evmc_instance* instance, + evmc_trace_callback callback, + struct evmc_tracer_context* context); + + +/** + * The EVM instance. + * + * Defines the base struct of the VM implementation. + */ +struct evmc_instance +{ + /** + * EVMC ABI version implemented by the VM instance. + * + * Can be used to detect ABI incompatibilities. + * The EVMC ABI version represented by this file is in ::EVMC_ABI_VERSION. + */ + const int abi_version; + + /** + * The name of the EVMC VM implementation. + * + * It MUST be a NULL-terminated not empty string. + * The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed). + */ + const char* name; + + /** + * The version of the EVMC VM implementation, e.g. "1.2.3b4". + * + * It MUST be a NULL-terminated not empty string. + * The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed). + */ + const char* version; + + /** + * Pointer to function destroying the EVM instance. + * + * This is a mandatory method and MUST NOT be set to NULL. + */ + evmc_destroy_fn destroy; + + /** + * Pointer to function executing a code by the EVM instance. + * + * This is a mandatory method and MUST NOT be set to NULL. + */ + evmc_execute_fn execute; + + /** + * A method returning capabilities supported by the VM instance. + * + * The value returned MAY change when different options are set via the set_option() method. + * + * A Client SHOULD only rely on the value returned if it has queried it after + * it has called the set_option(). + * + * This is a mandatory method and MUST NOT be set to NULL. + */ + evmc_get_capabilities_fn get_capabilities; + + /** + * Optional pointer to function setting the EVM instruction tracer. + * + * If the EVM does not support this feature the pointer can be NULL. + */ + evmc_set_tracer_fn set_tracer; + + /** + * Optional pointer to function modifying VM's options. + * + * If the VM does not support this feature the pointer can be NULL. + */ + evmc_set_option_fn set_option; +}; + +/* END Python CFFI declarations */ + +#if EVMC_DOCUMENTATION +/** + * Example of a function creating an instance of an example EVM implementation. + * + * Each EVM implementation MUST provide a function returning an EVM instance. + * The function SHOULD be named `evmc_create_(void)`. If the VM name contains hyphens + * replaces them with underscores in the function names. + * + * @par Binaries naming convention + * For VMs distributed as shared libraries, the name of the library SHOULD match the VM name. + * The convetional library filename prefixes and extensions SHOULD be ignored by the Client. + * For example, the shared library with the "beta-interpreter" implementation may be named + * `libbeta-interpreter.so`. + * + * @return EVM instance or NULL indicating instance creation failure. + */ +struct evmc_instance* evmc_create_example_vm(void); +#endif + +#if __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp new file mode 100644 index 000000000..7d2344cdb --- /dev/null +++ b/test/evmc/evmc.hpp @@ -0,0 +1,365 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ +#pragma once + +#include +#include + +#include +#include + +/// EVMC C++ API - wrappers and bindings for C++ +/// @ingroup cpp +namespace evmc +{ +/// @copydoc evmc_result +/// +/// This is a RAII wrapper for evmc_result and objects of this type +/// automatically release attached resources. +class result : private evmc_result +{ +public: + using evmc_result::create_address; + using evmc_result::gas_left; + using evmc_result::output_data; + using evmc_result::output_size; + using evmc_result::status_code; + + /// Converting constructor from raw evmc_result. + explicit result(evmc_result const& res) noexcept : evmc_result{res} {} + + /// Destructor responsible for automatically releasing attached resources. + ~result() noexcept + { + if (release) + release(this); + } + + /// Move constructor. + result(result&& other) noexcept : evmc_result{other} + { + other.release = nullptr; // Disable releasing of the rvalue object. + } + + /// Move assignment operator. + /// + /// The self-assigment MUST never happen. + /// + /// @param other The other result object. + /// @return The reference to the left-hand side object. + result& operator=(result&& other) noexcept + { + this->~result(); // Release this object. + static_cast(*this) = other; // Copy data. + other.release = nullptr; // Disable releasing of the rvalue object. + return *this; + } + + /// Releases the ownership and returns the raw copy of evmc_result. + /// + /// This method drops the ownership of the result + /// (result's resources are not going to be released when this object is destructed). + /// It is the caller's responsibility having the returned copy of the result to release it. + /// This object MUST NOT be used after this method is invoked. + /// + /// @return The copy of this object converted to raw evmc_result. + evmc_result release_raw() noexcept + { + const auto out = evmc_result{*this}; // Copy data. + this->release = nullptr; // Disable releasing of this object. + return out; + } +}; + +/// @copybrief evmc_instance +/// +/// This is a RAII wrapper for evmc_instance and objects of this type +/// automatically destroys the VM instance. +class vm +{ +public: + /// Converting constructor from evmc_instance. + explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {} + + /// Destructor responsible for automatically destroying the VM instance. + ~vm() noexcept { m_instance->destroy(m_instance); } + + /// The constructor that captures a VM instance and configures the instance + /// with provided list of options. + vm(evmc_instance* instance, + std::initializer_list> options) noexcept + : m_instance{instance} + { + for (auto option : options) + set_option(option.first, option.second); + } + + /// Checks whenever the VM instance is ABI compatible with the current EVMC API. + bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } + + /// @copydoc evmc_instance::name + char const* name() const noexcept { return m_instance->name; } + + /// @copydoc evmc_instance::version + char const* version() const noexcept { return m_instance->version; } + + /// @copydoc evmc::instance::get_capabilities + evmc_capabilities_flagset get_capabilities() const noexcept + { + return m_instance->get_capabilities(m_instance); + } + + /// @copydoc evmc_set_option() + evmc_set_option_result set_option(const char name[], const char value[]) noexcept + { + return evmc_set_option(m_instance, name, value); + } + + /// @copydoc evmc_execute() + result execute(evmc_context& ctx, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{m_instance->execute(m_instance, &ctx, rev, &msg, code, code_size)}; + } + +private: + evmc_instance* const m_instance = nullptr; +}; + +/// The EVMC Host interface +class HostInterface +{ +public: + virtual ~HostInterface() noexcept = default; + + /// @copydoc evmc_host_interface::account_exists + virtual bool account_exists(const evmc_address& addr) noexcept = 0; + + /// @copydoc evmc_host_interface::get_storage + virtual evmc_bytes32 get_storage(const evmc_address& addr, + const evmc_bytes32& key) noexcept = 0; + + /// @copydoc evmc_host_interface::set_storage + virtual evmc_storage_status set_storage(const evmc_address& addr, + const evmc_bytes32& key, + const evmc_bytes32& value) noexcept = 0; + + /// @copydoc evmc_host_interface::get_balance + virtual evmc_uint256be get_balance(const evmc_address& addr) noexcept = 0; + + /// @copydoc evmc_host_interface::get_code_size + virtual size_t get_code_size(const evmc_address& addr) noexcept = 0; + + /// @copydoc evmc_host_interface::get_code_hash + virtual evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept = 0; + + /// @copydoc evmc_host_interface::copy_code + virtual size_t copy_code(const evmc_address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept = 0; + + /// @copydoc evmc_host_interface::selfdestruct + virtual void selfdestruct(const evmc_address& addr, + const evmc_address& beneficiary) noexcept = 0; + + /// @copydoc evmc_host_interface::call + virtual result call(const evmc_message& msg) noexcept = 0; + + /// @copydoc evmc_host_interface::get_tx_context + virtual evmc_tx_context get_tx_context() noexcept = 0; + + /// @copydoc evmc_host_interface::get_block_hash + virtual evmc_bytes32 get_block_hash(int64_t block_number) noexcept = 0; + + /// @copydoc evmc_host_interface::emit_log + virtual void emit_log(const evmc_address& addr, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t num_topics) noexcept = 0; +}; + + +/// Wrapper around EVMC host context / host interface. +/// +/// To be used by VM implementations as better alternative to using ::evmc_context directly. +class HostContext : public HostInterface +{ + evmc_context* context = nullptr; + evmc_tx_context tx_context = {}; + +public: + /// Implicit converting constructor from evmc_context. + HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT + + bool account_exists(const evmc_address& address) noexcept final + { + return context->host->account_exists(context, &address); + } + + evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept final + { + return context->host->get_storage(context, &address, &key); + } + + evmc_storage_status set_storage(const evmc_address& address, + const evmc_bytes32& key, + const evmc_bytes32& value) noexcept final + { + return context->host->set_storage(context, &address, &key, &value); + } + + evmc_uint256be get_balance(const evmc_address& address) noexcept final + { + return context->host->get_balance(context, &address); + } + + size_t get_code_size(const evmc_address& address) noexcept final + { + return context->host->get_code_size(context, &address); + } + + evmc_bytes32 get_code_hash(const evmc_address& address) noexcept final + { + return context->host->get_code_hash(context, &address); + } + + size_t copy_code(const evmc_address& address, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept final + { + return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); + } + + void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) noexcept final + { + context->host->selfdestruct(context, &address, &beneficiary); + } + + result call(const evmc_message& message) noexcept final + { + return result{context->host->call(context, &message)}; + } + + /// @copydoc HostInterface::get_tx_context() + /// + /// The implementation caches the received transaction context + /// by assuming that the block timestamp should never be zero. + /// + /// @return The cached transaction context. + evmc_tx_context get_tx_context() noexcept final + { + if (tx_context.block_timestamp == 0) + tx_context = context->host->get_tx_context(context); + return tx_context; + } + + evmc_bytes32 get_block_hash(int64_t number) noexcept final + { + return context->host->get_block_hash(context, number); + } + + void emit_log(const evmc_address& address, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t topics_count) noexcept final + { + context->host->emit_log(context, &address, data, data_size, topics, topics_count); + } +}; + +/// Abstract class to be used by Host implementations. +/// +/// When implementing EVMC Host, you can directly inherit from the evmc::Host class. +/// This way your implementation will be simpler by avoiding manual handling +/// of the ::evmc_context and the ::evmc_context::host. +class Host : public HostInterface, public evmc_context +{ +public: + inline Host() noexcept; +}; + +namespace internal +{ +inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->account_exists(*addr); +} +inline evmc_bytes32 get_storage(evmc_context* h, + const evmc_address* addr, + const evmc_bytes32* key) noexcept +{ + return static_cast(h)->get_storage(*addr, *key); +} +inline evmc_storage_status set_storage(evmc_context* h, + const evmc_address* addr, + const evmc_bytes32* key, + const evmc_bytes32* value) noexcept +{ + return static_cast(h)->set_storage(*addr, *key, *value); +} +inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_balance(*addr); +} +inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_code_size(*addr); +} +inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_code_hash(*addr); +} +inline size_t copy_code(evmc_context* h, + const evmc_address* addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept +{ + return static_cast(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); +} +inline void selfdestruct(evmc_context* h, + const evmc_address* addr, + const evmc_address* beneficiary) noexcept +{ + static_cast(h)->selfdestruct(*addr, *beneficiary); +} +inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept +{ + return static_cast(h)->call(*msg).release_raw(); +} +inline evmc_tx_context get_tx_context(evmc_context* h) noexcept +{ + return static_cast(h)->get_tx_context(); +} +inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept +{ + return static_cast(h)->get_block_hash(block_number); +} +inline void emit_log(evmc_context* h, + const evmc_address* addr, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t num_topics) noexcept +{ + static_cast(h)->emit_log(*addr, data, data_size, topics, num_topics); +} + +constexpr evmc_host_interface interface{ + account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash, + copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log, +}; +} // namespace internal + +inline Host::Host() noexcept : evmc_context{&internal::interface} {} + +} // namespace evmc diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h new file mode 100644 index 000000000..e43cfd76a --- /dev/null +++ b/test/evmc/helpers.h @@ -0,0 +1,171 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * EVMC Helpers + * + * A collection of C helper functions for invoking a VM instance methods. + * These are convenient for languages where invoking function pointers + * is "ugly" or impossible (such as Go). + * + * It also contains helpers (overloaded operators) for using EVMC types effectively in C++. + * + * @defgroup helpers EVMC Helpers + * @{ + */ +#pragma once + +#include + +/** + * Returns true if the VM instance has a compatible ABI version. + */ +static inline int evmc_is_abi_compatible(struct evmc_instance* instance) +{ + return instance->abi_version == EVMC_ABI_VERSION; +} + +/** + * Returns the name of the VM instance. + */ +static inline const char* evmc_vm_name(struct evmc_instance* instance) +{ + return instance->name; +} + +/** + * Returns the version of the VM instance. + */ +static inline const char* evmc_vm_version(struct evmc_instance* instance) +{ + return instance->version; +} + +/** + * Checks if the VM instance has the given capability. + * + * @see evmc_get_capabilities_fn + */ +static inline bool evmc_vm_has_capability(struct evmc_instance* vm, + enum evmc_capabilities capability) +{ + return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0; +} + +/** + * Destroys the VM instance. + * + * @see evmc_destroy_fn + */ +static inline void evmc_destroy(struct evmc_instance* instance) +{ + instance->destroy(instance); +} + +/** + * Sets the option for the VM instance, if the feature is supported by the VM. + * + * @see evmc_set_option_fn + */ +static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance, + char const* name, + char const* value) +{ + if (instance->set_option) + return instance->set_option(instance, name, value); + return EVMC_SET_OPTION_INVALID_NAME; +} + +/** + * Sets the tracer callback for the VM instance, if the feature is supported by the VM. + * + * @see evmc_set_tracer_fn + */ +static inline void evmc_set_tracer(struct evmc_instance* instance, + evmc_trace_callback callback, + struct evmc_tracer_context* context) +{ + if (instance->set_tracer) + instance->set_tracer(instance, callback, context); +} + +/** + * Executes code in the VM instance. + * + * @see evmc_execute_fn. + */ +static inline struct evmc_result evmc_execute(struct evmc_instance* instance, + struct evmc_context* context, + enum evmc_revision rev, + const struct evmc_message* msg, + uint8_t const* code, + size_t code_size) +{ + return instance->execute(instance, context, rev, msg, code, code_size); +} + +/** + * Releases the resources allocated to the execution result. + * + * @param result The result object to be released. MUST NOT be NULL. + * + * @see evmc_result::release() evmc_release_result_fn + */ +static inline void evmc_release_result(struct evmc_result* result) +{ + if (result->release) + result->release(result); +} + + +/** + * Helpers for optional storage of evmc_result. + * + * In some contexts (i.e. evmc_result::create_address is unused) objects of + * type evmc_result contains a memory storage that MAY be used by the object + * owner. This group defines helper types and functions for accessing + * the optional storage. + * + * @defgroup result_optional_storage Result Optional Storage + * @{ + */ + +/** + * The union representing evmc_result "optional storage". + * + * The evmc_result struct contains 24 bytes of optional storage that can be + * reused by the object creator if the object does not contain + * evmc_result::create_address. + * + * A VM implementation MAY use this memory to keep additional data + * when returning result from evmc_execute_fn(). + * The host application MAY use this memory to keep additional data + * when returning result of performed calls from evmc_call_fn(). + * + * @see evmc_get_optional_storage(), evmc_get_const_optional_storage(). + */ +union evmc_result_optional_storage +{ + uint8_t bytes[24]; /**< 24 bytes of optional storage. */ + void* pointer; /**< Optional pointer. */ +}; + +/** Provides read-write access to evmc_result "optional storage". */ +static inline union evmc_result_optional_storage* evmc_get_optional_storage( + struct evmc_result* result) +{ + return (union evmc_result_optional_storage*)&result->create_address; +} + +/** Provides read-only access to evmc_result "optional storage". */ +static inline const union evmc_result_optional_storage* evmc_get_const_optional_storage( + const struct evmc_result* result) +{ + return (const union evmc_result_optional_storage*)&result->create_address; +} + +/** @} */ + +/** @} */ diff --git a/test/evmc/helpers.hpp b/test/evmc/helpers.hpp new file mode 100644 index 000000000..1b420b2e5 --- /dev/null +++ b/test/evmc/helpers.hpp @@ -0,0 +1,98 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * @file + * A collection of helpers (overloaded operators) for using EVMC types effectively in C++. + * + * @addtogroup helpers + * @{ + */ +#pragma once + +#include + +#include +#include + +/// The comparator for std::map. +inline bool operator<(const evmc_address& a, const evmc_address& b) +{ + return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; +} + +/// The comparator for std::map. +inline bool operator<(const evmc_bytes32& a, const evmc_bytes32& b) +{ + return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; +} + +/// The comparator for equality. +inline bool operator==(const evmc_address& a, const evmc_address& b) +{ + return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; +} + +/// The comparator for equality. +inline bool operator==(const evmc_bytes32& a, const evmc_bytes32& b) +{ + return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; +} + +/// Check if the address is zero (all bytes are zeros). +inline bool is_zero(const evmc_address& address) noexcept +{ + return address == evmc_address{}; +} + +/// Check if the hash is zero (all bytes are zeros). +inline bool is_zero(const evmc_bytes32& x) noexcept +{ + return x == evmc_bytes32{}; +} + +/// FNV1a hash function with 64-bit result. +inline uint64_t fnv1a_64(const uint8_t* ptr, size_t len) +{ + constexpr uint64_t prime = 1099511628211ULL; + constexpr uint64_t offset_basis = 14695981039346656037ULL; + + uint64_t ret = offset_basis; + for (size_t i = 0; i < len; i++) + { + ret ^= ptr[i]; + ret *= prime; + } + return ret; +} + +namespace std +{ +/// Hash operator template specialization for evmc_address needed for unordered containers. +template <> +struct hash +{ + /// Hash operator using FNV1a. + std::enable_if::type operator()(const evmc_address& s) const + noexcept + { + return fnv1a_64(s.bytes, sizeof(s.bytes)); + } +}; + +/// Hash operator template needed for std::unordered_set and others using hashes. +template <> +struct hash +{ + /// Hash operator using FNV1a. + std::enable_if::type operator()(const evmc_bytes32& s) const + noexcept + { + return fnv1a_64(s.bytes, sizeof(s.bytes)); + } +}; +} // namespace std + +/** @} */ diff --git a/test/evmc/loader.c b/test/evmc/loader.c new file mode 100644 index 000000000..51d7a85fb --- /dev/null +++ b/test/evmc/loader.c @@ -0,0 +1,240 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(EVMC_LOADER_MOCK) +#include "../../test/unittests/loader_mock.h" +#elif _WIN32 +#include +#define DLL_HANDLE HMODULE +#define DLL_OPEN(filename) LoadLibrary(filename) +#define DLL_CLOSE(handle) FreeLibrary(handle) +#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) GetProcAddress(handle, name) +#define DLL_GET_ERROR_MSG() NULL +#else +#include +#define DLL_HANDLE void* +#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY) +#define DLL_CLOSE(handle) dlclose(handle) +#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name) +#define DLL_GET_ERROR_MSG() dlerror() +#endif + +#ifdef __has_attribute +#if __has_attribute(format) +#define ATTR_FORMAT(archetype, string_index, first_to_check) \ + __attribute__((format(archetype, string_index, first_to_check))) +#endif +#endif + +#ifndef ATTR_FORMAT +#define ATTR_FORMAT(...) +#endif + +#if _WIN32 +#define strcpy_sx strcpy_s +#else +/* + * Limited variant of strcpy_s(). + * + * Provided for C standard libraries where strcpy_s() is not available. + * The availability check might need to adjusted for other C standard library implementations. + */ +#if !defined(EVMC_LOADER_MOCK) +static +#endif + int + strcpy_sx(char* restrict dest, size_t destsz, const char* restrict src) +{ + size_t len = strlen(src); + if (len >= destsz) + { + // The input src will not fit into the dest buffer. + // Set the first byte of the dest to null to make it effectively empty string + // and return error. + dest[0] = 0; + return 1; + } + memcpy(dest, src, len); + dest[len] = 0; + return 0; +} +#endif + +#define PATH_MAX_LENGTH 4096 + +static const char* last_error_msg = NULL; + +#define LAST_ERROR_MSG_BUFFER_SIZE 511 + +// Buffer for formatted error messages. +// It has one null byte extra to avoid buffer read overflow during concurrent access. +static char last_error_msg_buffer[LAST_ERROR_MSG_BUFFER_SIZE + 1]; + +ATTR_FORMAT(printf, 2, 3) +static enum evmc_loader_error_code set_error(enum evmc_loader_error_code error_code, + const char* format, + ...) +{ + va_list args; + va_start(args, format); + if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) < + LAST_ERROR_MSG_BUFFER_SIZE) + last_error_msg = last_error_msg_buffer; + va_end(args); + return error_code; +} + + +evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code) +{ + last_error_msg = NULL; // Reset last error. + enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; + evmc_create_fn create_fn = NULL; + + if (!filename) + { + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, "invalid argument: file name cannot be null"); + goto exit; + } + + const size_t length = strlen(filename); + if (length == 0) + { + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, "invalid argument: file name cannot be empty"); + goto exit; + } + else if (length > PATH_MAX_LENGTH) + { + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, + "invalid argument: file name is too long (%d, maximum allowed length is %d)", + (int)length, PATH_MAX_LENGTH); + goto exit; + } + + DLL_HANDLE handle = DLL_OPEN(filename); + if (!handle) + { + // Get error message if available. + last_error_msg = DLL_GET_ERROR_MSG(); + if (last_error_msg) + ec = EVMC_LOADER_CANNOT_OPEN; + else + ec = set_error(EVMC_LOADER_CANNOT_OPEN, "cannot open %s", filename); + goto exit; + } + + // Create name buffer with the prefix. + const char prefix[] = "evmc_create_"; + const size_t prefix_length = strlen(prefix); + char prefixed_name[sizeof(prefix) + PATH_MAX_LENGTH]; + strcpy_sx(prefixed_name, sizeof(prefixed_name), prefix); + + // Find filename in the path. + const char* sep_pos = strrchr(filename, '/'); +#if _WIN32 + // On Windows check also Windows classic path separator. + const char* sep_pos_windows = strrchr(filename, '\\'); + sep_pos = sep_pos_windows > sep_pos ? sep_pos_windows : sep_pos; +#endif + const char* name_pos = sep_pos ? sep_pos + 1 : filename; + + // Skip "lib" prefix if present. + const char lib_prefix[] = "lib"; + const size_t lib_prefix_length = strlen(lib_prefix); + if (strncmp(name_pos, lib_prefix, lib_prefix_length) == 0) + name_pos += lib_prefix_length; + + char* base_name = prefixed_name + prefix_length; + strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos); + + // Trim the file extension. + char* ext_pos = strrchr(prefixed_name, '.'); + if (ext_pos) + *ext_pos = 0; + + // Replace all "-" with "_". + char* dash_pos = base_name; + while ((dash_pos = strchr(dash_pos, '-')) != NULL) + *dash_pos++ = '_'; + + // Search for the built function name. + while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL) + { + // Shorten the base name by skipping the `word_` segment. + const char* shorter_name_pos = strchr(base_name, '_'); + if (!shorter_name_pos) + break; + + memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1); + } + + if (!create_fn) + create_fn = DLL_GET_CREATE_FN(handle, "evmc_create"); + + if (!create_fn) + { + DLL_CLOSE(handle); + ec = set_error(EVMC_LOADER_SYMBOL_NOT_FOUND, "EVMC create function not found in %s", + filename); + } + +exit: + if (error_code) + *error_code = ec; + return create_fn; +} + +const char* evmc_last_error_msg() +{ + const char* m = last_error_msg; + last_error_msg = NULL; + return m; +} + +struct evmc_instance* evmc_load_and_create(const char* filename, + enum evmc_loader_error_code* error_code) +{ + // First load the DLL. This also resets the last_error_msg; + evmc_create_fn create_fn = evmc_load(filename, error_code); + + if (!create_fn) + return NULL; + + enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; + + struct evmc_instance* instance = create_fn(); + if (!instance) + { + ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, + "creating EVMC instance of %s has failed", filename); + goto exit; + } + + if (!evmc_is_abi_compatible(instance)) + { + ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, + "EVMC ABI version %d of %s mismatches the expected version %d", + instance->abi_version, filename, EVMC_ABI_VERSION); + evmc_destroy(instance); + instance = NULL; + goto exit; + } + +exit: + if (error_code) + *error_code = ec; + + return instance; +} diff --git a/test/evmc/loader.h b/test/evmc/loader.h new file mode 100644 index 000000000..d8531af1d --- /dev/null +++ b/test/evmc/loader.h @@ -0,0 +1,133 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * EVMC Loader Library + * + * The EVMC Loader Library supports loading VMs implemented as Dynamically Loaded Libraries + * (DLLs, shared objects). + * + * @defgroup loader EVMC Loader + * @{ + */ +#pragma once + +#if __cplusplus +extern "C" { +#endif + +/** The function pointer type for EVMC create functions. */ +typedef struct evmc_instance* (*evmc_create_fn)(void); + +/** Error codes for the EVMC loader. */ +enum evmc_loader_error_code +{ + /** The loader succeeded. */ + EVMC_LOADER_SUCCESS = 0, + + /** The loader cannot open the given file name. */ + EVMC_LOADER_CANNOT_OPEN = 1, + + /** The VM create function not found. */ + EVMC_LOADER_SYMBOL_NOT_FOUND = 2, + + /** The invalid argument value provided. */ + EVMC_LOADER_INVALID_ARGUMENT = 3, + + /** The creation of a VM instance has failed. */ + EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, + + /** The ABI version of the VM instance has mismatched. */ + EVMC_LOADER_ABI_VERSION_MISMATCH = 5 +}; + +/** + * Dynamically loads the EVMC module with a VM implementation. + * + * This function tries to open a dynamically loaded library (DLL) at the given `filename`. + * On UNIX-like systems dlopen() function is used. On Windows LoadLibrary() function is used. + * + * If the file does not exist or is not a valid shared library the ::EVMC_LOADER_CANNOT_OPEN error + * code is signaled and NULL is returned. + * + * After the DLL is successfully loaded the function tries to find the EVM create function in the + * library. The `filename` is used to guess the EVM name and the name of the create function. + * The create function name is constructed by the following rules. Consider example path: + * "/ethereum/libexample-interpreter.so". + * - the filename is taken from the path: + * "libexample-interpreter.so", + * - the "lib" prefix and file extension are stripped from the name: + * "example-interpreter" + * - all "-" are replaced with "_" to construct _base name_: + * "example_interpreter", + * - the function name "evmc_create_" + _base name_ is searched in the library: + * "evmc_create_example_interpreter", + * - if function not found, the _base name_ is shorten by skipping the first word separated by "_": + * "interpreter", + * - then, the function of the shorter name "evmc_create_" + _base name_ is searched in the library: + * "evmc_create_interpreter", + * - the name shortening continues until a function is found or the name cannot be shorten more, + * - lastly, when no function found, the function name "evmc_create" is searched in the library. + * + * If the create function is found in the library, the pointer to the function is returned. + * Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. + * + * It is safe to call this function with the same filename argument multiple times + * (the DLL is not going to be loaded multiple times). + * + * @param filename The null terminated path (absolute or relative) to an EVMC module + * (dynamically loaded library) containing the VM implementation. + * If the value is NULL, an empty C-string or longer than the path maximum length + * the ::EVMC_LOADER_INVALID_ARGUMENT is signaled. + * @param error_code The pointer to the error code. If not NULL the value is set to + * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. + * @return The pointer to the EVM create function or NULL in case of error. + */ +evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code); + +/** + * Dynamically loads the EVMC module and creates the VM instance. + * + * This is a macro for creating the VM instance with the function returned from evmc_load(). + * The function signals the same errors as evmc_load() and additionally: + * - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL, + * - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different + * from the ABI version of this library (::EVMC_ABI_VERSION). + * + * It is safe to call this function with the same filename argument multiple times: + * the DLL is not going to be loaded multiple times, but the function will return new VM instance + * each time. + * + * @param filename The null terminated path (absolute or relative) to an EVMC module + * (dynamically loaded library) containing the VM implementation. + * If the value is NULL, an empty C-string or longer than the path maximum length + * the ::EVMC_LOADER_INVALID_ARGUMENT is signaled. + * @param error_code The pointer to the error code. If not NULL the value is set to + * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. + * @return The pointer to the created VM or NULL in case of error. + */ +struct evmc_instance* evmc_load_and_create(const char* filename, + enum evmc_loader_error_code* error_code); + +/** + * Returns the human-readable message describing the most recent error + * that occurred in EVMC loading since the last call to this function. + * + * In case any loading function returned ::EVMC_LOADER_SUCCESS this function always returns NULL. + * In case of error code other than success returned, this function MAY return the error message. + * Calling this function "consumes" the error message and the function will return NULL + * from subsequent invocations. + * This function is not thread-safe. + * + * @return Error message or NULL if no additional information is available. + * The returned pointer MUST NOT be freed by the caller. + */ +const char* evmc_last_error_msg(); + +#if __cplusplus +} +#endif + +/** @} */ diff --git a/test/evmc/utils.h b/test/evmc/utils.h new file mode 100644 index 000000000..03715c0b0 --- /dev/null +++ b/test/evmc/utils.h @@ -0,0 +1,36 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +/** + * @file + * A collection of helper macros to handle some non-portable features of C/C++ compilers. + * + * @addtogroup helpers + * @{ + */ + +/** + * @def EVMC_EXPORT + * Marks a function to be exported from a shared library. + */ +#if defined _MSC_VER || defined __MINGW32__ +#define EVMC_EXPORT __declspec(dllexport) +#else +#define EVMC_EXPORT __attribute__((visibility("default"))) +#endif + +/** + * @def EVMC_NOEXCEPT + * Safe way of marking a function with `noexcept` C++ specifier. + */ +#if __cplusplus +#define EVMC_NOEXCEPT noexcept +#else +#define EVMC_NOEXCEPT +#endif + +/** @} */ diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index b6d6478a7..4bcf9cfaf 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -34,4 +34,4 @@ add_executable(isoltest ../libyul/YulOptimizerTest.cpp ../libyul/YulInterpreterTest.cpp ) -target_link_libraries(isoltest PRIVATE libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework) +target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework) diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 74d9f7f29..7e857c6d0 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -9,8 +9,11 @@ add_dependencies(ossfuzz ) if (OSSFUZZ) -add_custom_target(ossfuzz_proto) -add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz) + add_custom_target(ossfuzz_proto) + add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz) + + add_custom_target(ossfuzz_abiv2) + add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz) endif() if (OSSFUZZ) @@ -49,6 +52,26 @@ if (OSSFUZZ) protobuf-mutator.a protobuf.a FuzzingEngine.a) + + add_executable(abiv2_proto_ossfuzz + ../../EVMHost.cpp + abiV2ProtoFuzzer.cpp + abiV2FuzzerCommon.cpp + protoToAbiV2.cpp + abiV2Proto.pb.cc + ) + target_include_directories(abiv2_proto_ossfuzz PRIVATE + /src/LPM/external.protobuf/include + /src/libprotobuf-mutator + /src/evmone/include + ) + target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity + evmone intx ethash keccak evmc-instructions evmc + protobuf-mutator-libfuzzer.a + protobuf-mutator.a + protobuf.a + FuzzingEngine.a + ) else() add_library(solc_opt_ossfuzz solc_opt_ossfuzz.cpp @@ -99,4 +122,23 @@ else() # protobuf-mutator.a # protobuf.a # FuzzingEngine.a) +# add_executable(abiv2_proto_ossfuzz +# ../../EVMHost.cpp +# abiV2ProtoFuzzer.cpp +# abiV2FuzzerCommon.cpp +# protoToAbiV2.cpp +# abiV2Proto.pb.cc +# ) +# target_include_directories(abiv2_proto_ossfuzz PRIVATE +# /src/LPM/external.protobuf/include +# /src/libprotobuf-mutator +# /src/evmone/include +# ) +# target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity +# evmone intx ethash keccak evmc-instructions evmc +# protobuf-mutator-libfuzzer.a +# protobuf-mutator.a +# protobuf.a +# FuzzingEngine.a +# ) endif() diff --git a/test/tools/ossfuzz/abiV2FuzzerCommon.cpp b/test/tools/ossfuzz/abiV2FuzzerCommon.cpp index 8d691c288..45947ddc0 100644 --- a/test/tools/ossfuzz/abiV2FuzzerCommon.cpp +++ b/test/tools/ossfuzz/abiV2FuzzerCommon.cpp @@ -27,7 +27,7 @@ dev::bytes SolidityCompilationFramework::compileContract( ); std::cerr << "Compiling contract failed" << std::endl; } - dev::eth::LinkerObject obj = m_compiler.runtimeObject( + dev::eth::LinkerObject obj = m_compiler.object( _contractName.empty() ? m_compiler.lastContractName() : _contractName diff --git a/test/tools/ossfuzz/abiV2FuzzerCommon.h b/test/tools/ossfuzz/abiV2FuzzerCommon.h index 7a49cb8e5..79bb3b657 100644 --- a/test/tools/ossfuzz/abiV2FuzzerCommon.h +++ b/test/tools/ossfuzz/abiV2FuzzerCommon.h @@ -28,7 +28,7 @@ public: protected: dev::solidity::CompilerStack m_compiler; langutil::EVMVersion m_evmVersion; - dev::solidity::OptimiserSettings m_optimiserSettings = dev::solidity::OptimiserSettings::full(); + dev::solidity::OptimiserSettings m_optimiserSettings = dev::solidity::OptimiserSettings::none(); }; } } diff --git a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp index 2146d5ba5..e646c64fd 100644 --- a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp +++ b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp @@ -15,15 +15,86 @@ along with solidity. If not, see . */ +#include #include #include +#include #include #include +static evmc::vm evmone = evmc::vm{evmc_create_evmone()}; + using namespace dev::test::abiv2fuzzer; +using namespace dev::test; using namespace dev; using namespace std; +namespace +{ +/// Test function returns a uint256 value +static size_t const expectedOutputLength = 32; +/// Expected output value is decimal 1000 or hex 03E8 +static uint8_t const expectedOutput[expectedOutputLength] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, u'\x03', u'\xe8' +}; + +/// Compares the contents of the memory address pointed to +/// by `_result` of `_length` bytes to the expected output. +/// Returns true if `_result` matches expected output, false +/// otherwise. +bool isOutputExpected(uint8_t const* _result, size_t _length) +{ + if (_length != expectedOutputLength) + return false; + + return (memcmp(_result, expectedOutput, expectedOutputLength) == 0); +} + +/// Accepts a reference to a user-specified input and returns an +/// evmc_message with all of its fields zero initialized except +/// gas and input fields. +/// The gas field is set to the maximum permissible value so that we +/// don't run into out of gas errors. The input field is copied from +/// user input. +evmc_message initializeMessage(dev::bytes const& _input) +{ + // Zero initialize all message fields + evmc_message msg = {}; + // Gas available (value of type int64_t) is set to its maximum + // value. + msg.gas = std::numeric_limits::max(); + msg.input_data = _input.data(); + msg.input_size = _input.size(); + return msg; +} + +/// Accepts host context implementation, and keccak256 hash of the function +/// to be called at a specified address in the simulated blockchain as +/// input and returns the result of the execution of the called function. +evmc::result executeContract( + EVMHost& _hostContext, + dev::bytes const& _functionHash, + evmc_address _deployedAddress +) +{ + evmc_message message = initializeMessage(_functionHash); + message.destination = _deployedAddress; + message.kind = EVMC_CALL; + return _hostContext.call(message); +} + +/// Accepts a reference to host context implementation and byte code +/// as input and deploys it on the simulated blockchain. Returns the +/// result of deployment. +evmc::result deployContract(EVMHost& _hostContext, dev::bytes const& _code) +{ + evmc_message message = initializeMessage(_code); + message.kind = EVMC_CREATE; + return _hostContext.call(message); +} +} + DEFINE_PROTO_FUZZER(Contract const& _input) { string contract_source = ProtoConverter{}.contractToString(_input); @@ -44,17 +115,48 @@ DEFINE_PROTO_FUZZER(Contract const& _input) { // Compile contract generated by the proto fuzzer SolidityCompilationFramework solCompilationFramework; - std::string contractName = ":Factory"; + std::string contractName = ":C"; byteCode = solCompilationFramework.compileContract(contract_source, contractName); Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers(); // We always call the function test() that is defined in proto converter template hexEncodedInput = methodIdentifiers["test()"].asString(); } - catch (...) + // Ignore compilation failures + catch (Exception const&) { - cout << contract_source << endl; - throw; + return; } - // TODO: Call evmone wrapper here - return; + + if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_CODE")) + { + ofstream of(dump_path); + of << toHex(byteCode); + } + + // We target the default EVM which is the latest + langutil::EVMVersion version = {}; + EVMHost hostContext(version, evmone); + + // Deploy contract and signal failure if deploy failed + evmc::result createResult = deployContract(hostContext, byteCode); + solAssert( + createResult.status_code == EVMC_SUCCESS, + "Proto ABIv2 Fuzzer: Contract creation failed" + ); + + // Execute test function and signal failure if EVM reverted or + // did not return expected output on successful execution. + evmc::result callResult = executeContract( + hostContext, + fromHex(hexEncodedInput), + createResult.create_address + ); + + // We don't care about EVM One failures other than EVMC_REVERT + solAssert(callResult.status_code != EVMC_REVERT, "Proto ABIv2 fuzzer: EVM One reverted"); + if (callResult.status_code == EVMC_SUCCESS) + solAssert( + isOutputExpected(callResult.output_data, callResult.output_size), + "Proto ABIv2 fuzzer: ABIv2 coding failure found" + ); } \ No newline at end of file