Skip to content

Commit

Permalink
Add evmc and host interface implementation. Modify fuzzer harness to …
Browse files Browse the repository at this point in the history
…make use of evmc host/vm.
  • Loading branch information
Christian Parpart authored and bshastry committed Jul 17, 2019
1 parent 4fa7800 commit 810a0de
Show file tree
Hide file tree
Showing 21 changed files with 2,869 additions and 14 deletions.
4 changes: 3 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions cmake/templates/license.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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:

)"};
Expand Down
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -50,3 +51,4 @@ if (NOT Boost_USE_STATIC_LIBS)
endif()

add_subdirectory(tools)
add_subdirectory(evmc)
215 changes: 215 additions & 0 deletions test/EVMHost.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 810a0de

Please sign in to comment.