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