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
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
|
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
|
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
|
- run_proofs: &run_proofs
|
||||||
name: Correctness proofs for optimization rules
|
name: Correctness proofs for optimization rules
|
||||||
@ -69,6 +70,7 @@ defaults:
|
|||||||
- artifacts_executables_ossfuzz: &artifacts_executables_ossfuzz
|
- artifacts_executables_ossfuzz: &artifacts_executables_ossfuzz
|
||||||
root: build
|
root: build
|
||||||
paths:
|
paths:
|
||||||
|
- test/tools/ossfuzz/abiv2_proto_ossfuzz
|
||||||
- test/tools/ossfuzz/const_opt_ossfuzz
|
- test/tools/ossfuzz/const_opt_ossfuzz
|
||||||
- test/tools/ossfuzz/solc_noopt_ossfuzz
|
- test/tools/ossfuzz/solc_noopt_ossfuzz
|
||||||
- test/tools/ossfuzz/solc_opt_ossfuzz
|
- test/tools/ossfuzz/solc_opt_ossfuzz
|
||||||
|
@ -11,7 +11,7 @@ eth_policy()
|
|||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.5.11")
|
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")
|
if (${CMAKE_VERSION} VERSION_LESS "3.9.0")
|
||||||
# needed for the big endian test for older cmake versions
|
# 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
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
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:
|
All other code is licensed under GPL version 3:
|
||||||
|
|
||||||
)"};
|
)"};
|
||||||
|
@ -30,7 +30,8 @@ add_executable(soltest ${sources} ${headers}
|
|||||||
${libsolidity_sources} ${libsolidity_headers}
|
${libsolidity_sources} ${libsolidity_headers}
|
||||||
${libsolidity_util_sources} ${libsolidity_util_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)
|
# Special compilation flag for Visual Studio (version 2019 at least affected)
|
||||||
# in order to compile SolidityEndToEndTest.cpp, which is quite huge.
|
# in order to compile SolidityEndToEndTest.cpp, which is quite huge.
|
||||||
@ -50,3 +51,4 @@ if (NOT Boost_USE_STATIC_LIBS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(tools)
|
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/test/framework.hpp>
|
||||||
#include <boost/algorithm/string/replace.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 <cstdlib>
|
||||||
|
|
||||||
#include <chrono>
|
#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/YulOptimizerTest.cpp
|
||||||
../libyul/YulInterpreterTest.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)
|
if (OSSFUZZ)
|
||||||
add_custom_target(ossfuzz_proto)
|
add_custom_target(ossfuzz_proto)
|
||||||
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
|
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
|
||||||
|
|
||||||
|
add_custom_target(ossfuzz_abiv2)
|
||||||
|
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (OSSFUZZ)
|
if (OSSFUZZ)
|
||||||
@ -49,6 +52,26 @@ if (OSSFUZZ)
|
|||||||
protobuf-mutator.a
|
protobuf-mutator.a
|
||||||
protobuf.a
|
protobuf.a
|
||||||
FuzzingEngine.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()
|
else()
|
||||||
add_library(solc_opt_ossfuzz
|
add_library(solc_opt_ossfuzz
|
||||||
solc_opt_ossfuzz.cpp
|
solc_opt_ossfuzz.cpp
|
||||||
@ -99,4 +122,23 @@ else()
|
|||||||
# protobuf-mutator.a
|
# protobuf-mutator.a
|
||||||
# protobuf.a
|
# protobuf.a
|
||||||
# FuzzingEngine.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()
|
endif()
|
||||||
|
@ -27,7 +27,7 @@ dev::bytes SolidityCompilationFramework::compileContract(
|
|||||||
);
|
);
|
||||||
std::cerr << "Compiling contract failed" << std::endl;
|
std::cerr << "Compiling contract failed" << std::endl;
|
||||||
}
|
}
|
||||||
dev::eth::LinkerObject obj = m_compiler.runtimeObject(
|
dev::eth::LinkerObject obj = m_compiler.object(
|
||||||
_contractName.empty() ?
|
_contractName.empty() ?
|
||||||
m_compiler.lastContractName() :
|
m_compiler.lastContractName() :
|
||||||
_contractName
|
_contractName
|
||||||
|
@ -28,7 +28,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
dev::solidity::CompilerStack m_compiler;
|
dev::solidity::CompilerStack m_compiler;
|
||||||
langutil::EVMVersion m_evmVersion;
|
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/>.
|
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/abiV2FuzzerCommon.h>
|
||||||
#include <test/tools/ossfuzz/protoToAbiV2.h>
|
#include <test/tools/ossfuzz/protoToAbiV2.h>
|
||||||
|
#include <evmone/evmone.h>
|
||||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
static evmc::vm evmone = evmc::vm{evmc_create_evmone()};
|
||||||
|
|
||||||
using namespace dev::test::abiv2fuzzer;
|
using namespace dev::test::abiv2fuzzer;
|
||||||
|
using namespace dev::test;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace std;
|
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)
|
DEFINE_PROTO_FUZZER(Contract const& _input)
|
||||||
{
|
{
|
||||||
string contract_source = ProtoConverter{}.contractToString(_input);
|
string contract_source = ProtoConverter{}.contractToString(_input);
|
||||||
@ -44,17 +115,48 @@ DEFINE_PROTO_FUZZER(Contract const& _input)
|
|||||||
{
|
{
|
||||||
// Compile contract generated by the proto fuzzer
|
// Compile contract generated by the proto fuzzer
|
||||||
SolidityCompilationFramework solCompilationFramework;
|
SolidityCompilationFramework solCompilationFramework;
|
||||||
std::string contractName = ":Factory";
|
std::string contractName = ":C";
|
||||||
byteCode = solCompilationFramework.compileContract(contract_source, contractName);
|
byteCode = solCompilationFramework.compileContract(contract_source, contractName);
|
||||||
Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers();
|
Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers();
|
||||||
// We always call the function test() that is defined in proto converter template
|
// We always call the function test() that is defined in proto converter template
|
||||||
hexEncodedInput = methodIdentifiers["test()"].asString();
|
hexEncodedInput = methodIdentifiers["test()"].asString();
|
||||||
}
|
}
|
||||||
catch (...)
|
// Ignore compilation failures
|
||||||
|
catch (Exception const&)
|
||||||
{
|
{
|
||||||
cout << contract_source << endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
// TODO: Call evmone wrapper here
|
|
||||||
return;
|
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