mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add evmc and host interface implementation. Modify fuzzer harness to make use of evmc host/vm.
This commit is contained in:
parent
4fa7800458
commit
810a0de1aa
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
)"};
|
||||
|
@ -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)
|
||||
|
215
test/EVMHost.cpp
Normal file
215
test/EVMHost.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* EVM execution host, i.e. component that implements a simulated Ethereum blockchain
|
||||
* for testing purposes.
|
||||
*/
|
||||
|
||||
#include <test/EVMHost.h>
|
||||
|
||||
#include <test/evmc/helpers.hpp>
|
||||
#include <test/evmc/loader.h>
|
||||
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libdevcore/Assertions.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
#include <libdevcore/picosha2.h>
|
||||
|
||||
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;
|
||||
}
|
175
test/EVMHost.h
Normal file
175
test/EVMHost.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* EVM execution host, i.e. component that implements a simulated Ethereum blockchain
|
||||
* for testing purposes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <test/evmc/evmc.hpp>
|
||||
#include <test/evmc/evmc.h>
|
||||
#include <test/evmc/helpers.hpp>
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <libdevcore/FixedHash.h>
|
||||
|
||||
|
||||
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<evmc_bytes32, evmc_bytes32> storage;
|
||||
};
|
||||
|
||||
struct LogEntry
|
||||
{
|
||||
Address address;
|
||||
std::vector<h256> topics;
|
||||
bytes data;
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
size_t blockNumber;
|
||||
uint64_t timestamp;
|
||||
std::map<evmc_address, Account> accounts;
|
||||
std::vector<LogEntry> 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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -27,6 +27,10 @@
|
||||
#include <boost/test/framework.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <test/evmc/evmc.hpp>
|
||||
#include <test/evmc/loader.h>
|
||||
#include <test/evmc/helpers.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <chrono>
|
||||
|
23
test/evmc/CMakeLists.txt
Normal file
23
test/evmc/CMakeLists.txt
Normal file
@ -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)
|
202
test/evmc/LICENSE
Normal file
202
test/evmc/LICENSE
Normal file
@ -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.
|
1029
test/evmc/evmc.h
Normal file
1029
test/evmc/evmc.h
Normal file
File diff suppressed because it is too large
Load Diff
365
test/evmc/evmc.hpp
Normal file
365
test/evmc/evmc.hpp
Normal file
@ -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 <evmc/evmc.h>
|
||||
#include <evmc/helpers.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
/// 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<evmc_result&>(*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<std::pair<const char*, const char*>> 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<Host*>(h)->account_exists(*addr);
|
||||
}
|
||||
inline evmc_bytes32 get_storage(evmc_context* h,
|
||||
const evmc_address* addr,
|
||||
const evmc_bytes32* key) noexcept
|
||||
{
|
||||
return static_cast<Host*>(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<Host*>(h)->set_storage(*addr, *key, *value);
|
||||
}
|
||||
inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_balance(*addr);
|
||||
}
|
||||
inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_code_size(*addr);
|
||||
}
|
||||
inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(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<Host*>(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<Host*>(h)->selfdestruct(*addr, *beneficiary);
|
||||
}
|
||||
inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->call(*msg).release_raw();
|
||||
}
|
||||
inline evmc_tx_context get_tx_context(evmc_context* h) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_tx_context();
|
||||
}
|
||||
inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept
|
||||
{
|
||||
return static_cast<Host*>(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<Host*>(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
|
171
test/evmc/helpers.h
Normal file
171
test/evmc/helpers.h
Normal file
@ -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 <evmc/evmc.h>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
98
test/evmc/helpers.hpp
Normal file
98
test/evmc/helpers.hpp
Normal file
@ -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 <evmc/evmc.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
/// The comparator for std::map<evmc_address, ...>.
|
||||
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<evmc_bytes32, ...>.
|
||||
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<evmc_address>
|
||||
{
|
||||
/// Hash operator using FNV1a.
|
||||
std::enable_if<sizeof(size_t) == 8, std::size_t>::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<evmc_bytes32>
|
||||
{
|
||||
/// Hash operator using FNV1a.
|
||||
std::enable_if<sizeof(size_t) == 8, std::size_t>::type operator()(const evmc_bytes32& s) const
|
||||
noexcept
|
||||
{
|
||||
return fnv1a_64(s.bytes, sizeof(s.bytes));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
/** @} */
|
240
test/evmc/loader.c
Normal file
240
test/evmc/loader.c
Normal file
@ -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 <evmc/loader.h>
|
||||
|
||||
#include <evmc/evmc.h>
|
||||
#include <evmc/helpers.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(EVMC_LOADER_MOCK)
|
||||
#include "../../test/unittests/loader_mock.h"
|
||||
#elif _WIN32
|
||||
#include <Windows.h>
|
||||
#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 <dlfcn.h>
|
||||
#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;
|
||||
}
|
133
test/evmc/loader.h
Normal file
133
test/evmc/loader.h
Normal file
@ -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
|
||||
|
||||
/** @} */
|
36
test/evmc/utils.h
Normal file
36
test/evmc/utils.h
Normal file
@ -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
|
||||
|
||||
/** @} */
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,86 @@
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <test/EVMHost.h>
|
||||
#include <test/tools/ossfuzz/abiV2FuzzerCommon.h>
|
||||
#include <test/tools/ossfuzz/protoToAbiV2.h>
|
||||
#include <evmone/evmone.h>
|
||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||
#include <fstream>
|
||||
|
||||
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<int64_t>::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"
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user