diff --git a/.gitmodules b/.gitmodules index aa1fc4065..0ddb5c183 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,6 @@ [submodule "test/tests"] path = test/jsontests url = https://github.com/ethereum/tests.git -[submodule "hera"] - path = hera - url = https://github.com/ewasm/hera [submodule "cpp-ethereum"] path = cpp-ethereum url = https://github.com/ethereum/cpp-ethereum.git diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 120000 index c0a44c6bc..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -cpp-ethereum/CMakeLists.txt \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..646a1cb39 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,173 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for cpp-ethereum. +# +# The documentation for cpp-ethereum is hosted at http://cpp-ethereum.org +# +# ------------------------------------------------------------------------------ +# This file is part of cpp-ethereum. +# +# cpp-ethereum 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. +# +# cpp-ethereum 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 cpp-ethereum. If not, see +# +# (c) 2014-2016 cpp-ethereum contributors. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.5.1) + +if (NOT DEFINED CMAKE_TOOLCHAIN_FILE) + # The default toolchain file configures compilers and build environment. + # This configuration is also used by hunter to build dependencies. + # CMake will cache this value, not need to explictly specify CACHE param. + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/cmake/toolchain.cmake) +endif() + +set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The path to the cmake directory") +list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR}) + +set(CPP_ETHEREUM_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Path to the root directory for cpp-ethereum") + +# set cmake_policies +include(EthPolicy) +eth_policy() + +if(CMAKE_CONFIGURATION_TYPES) + set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo" CACHE STRING "" FORCE) +endif() + +# Map current configuration to configurations of imported targets. +set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release) +set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) + +set(HUNTER_CONFIGURATION_TYPES Release) +set(HUNTER_JOBS_NUMBER 4) +set(HUNTER_CACHE_SERVERS "https://github.com/ethereum/hunter-cache") +include(HunterGate) +HunterGate( + URL "https://github.com/ruslo/hunter/archive/v0.19.232.tar.gz" + SHA1 "a412c45fe4c5a72fed386f62dd8d753bd4fd3d11" + LOCAL +) + +if(HUNTER_ENABLED) + # Find Python executable, + # prefer version 3 that has requests module included. + set(Python_ADDITIONAL_VERSIONS 3) + find_package(PythonInterp) + if(PYTHONINTERP_FOUND) + hunter_gate_self( + "${HUNTER_CACHED_ROOT}" + "${HUNTER_VERSION}" + "${HUNTER_SHA1}" + hunter_dir + ) + set(hunter_upload_script "${hunter_dir}/maintenance/upload-cache-to-github.py") + set(hunter_cache_dir "${HUNTER_GATE_ROOT}/_Base/Cache") + set(hunter_tmp_dir "${HUNTER_GATE_ROOT}/tmp") + add_custom_target( + hunter_upload_cache + ${PYTHON_EXECUTABLE} ${hunter_upload_script} + --username hunter-cache-bot + --repo-owner ethereum + --repo hunter-cache + --cache-dir ${hunter_cache_dir} + --temp-dir ${hunter_tmp_dir} + ) + endif() +endif() + +# project name and version should be set after cmake_policy CMP0048 +project(cpp-ethereum VERSION "1.3.0") + +if (NOT EXISTS ${CMAKE_SOURCE_DIR}/evmjit/.git) + message(FATAL_ERROR "Git submodules not initialized, execute:\n git submodule update --init") +endif() + +set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY On) + +include(EthCcache) +include(EthOptions) +include(EthCompilerSettings) +include(EthExecutableHelper) +include(EthDependencies) +include(EthUtils) + +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) +hunter_add_package(Boost COMPONENTS program_options filesystem system thread context fiber) +find_package(Boost CONFIG REQUIRED program_options filesystem system thread context fiber) + +hunter_add_package(jsoncpp) +find_package(jsoncpp CONFIG REQUIRED) + +hunter_add_package(Snappy) +find_package(Snappy CONFIG REQUIRED) + +hunter_add_package(cryptopp) +find_package(cryptopp CONFIG REQUIRED) + +hunter_add_package(libjson-rpc-cpp) +find_package(libjson-rpc-cpp CONFIG REQUIRED) + +include(ProjectSecp256k1) +include(ProjectLibFF) + +find_package(Threads) + +if(MINIUPNPC) + find_package(Miniupnpc 1.8.2013 REQUIRED) +endif() + +set(UTILS_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/utils") + +configure_project() + +if (HERA) + add_subdirectory(hera) +endif() + +#Global include path for all libs. +include_directories("${CMAKE_SOURCE_DIR}") + +add_subdirectory(libdevcore) +add_subdirectory(libdevcrypto) +add_subdirectory(libp2p) + +add_subdirectory(libethash) + +add_subdirectory(libethcore) +add_subdirectory(libevm) +add_subdirectory(libethereum) +add_subdirectory(libethashseal) + +add_subdirectory(libwebthree) +add_subdirectory(libweb3jsonrpc) + +if (EVMJIT) + add_subdirectory(evmjit) +endif() + +add_subdirectory(eth) + +if (TOOLS) + add_subdirectory(ethkey) + add_subdirectory(ethvm) + add_subdirectory(rlp) +endif() + +if (TESTS) + enable_testing() + add_subdirectory(test) +endif() + +# TODO: Split out json_spirit, libscrypt and sec256k1. +add_subdirectory(utils) diff --git a/hera b/hera deleted file mode 160000 index 2645bd4d1..000000000 --- a/hera +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2645bd4d189fb5bc24faea2183ea335506949b3e diff --git a/libevm b/libevm deleted file mode 120000 index 6db25189a..000000000 --- a/libevm +++ /dev/null @@ -1 +0,0 @@ -cpp-ethereum/libevm/ \ No newline at end of file diff --git a/libevm/CMakeLists.txt b/libevm/CMakeLists.txt new file mode 100644 index 000000000..9f93b03e0 --- /dev/null +++ b/libevm/CMakeLists.txt @@ -0,0 +1,35 @@ + +set(sources + ExtVMFace.cpp ExtVMFace.h + Instruction.cpp Instruction.h + VMFace.h + VMConfig.h + VM.cpp VM.h + VMCalls.cpp + VMOpt.cpp + VMSIMD.cpp + VMValidate.cpp + VMFactory.cpp VMFactory.h +) + +if(EVMJIT OR HERA) + list(APPEND sources + EVMC.cpp EVMC.h + ) +endif() + +add_library(evm ${sources}) + +target_link_libraries(evm PUBLIC ethcore devcore PRIVATE jsoncpp_lib_static Boost::program_options) +target_include_directories(evm PUBLIC ${CMAKE_SOURCE_DIR}/evmjit/include) +target_include_directories(evm PUBLIC ${CMAKE_SOURCE_DIR}/hera/src) + +if(EVMJIT) + target_link_libraries(evm PRIVATE evmjit) + target_compile_definitions(evm PRIVATE ETH_EVMJIT) +endif() + +if(HERA) + target_link_libraries(evm PRIVATE hera) + target_compile_definitions(evm PRIVATE ETH_HERA) +endif() diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp new file mode 100644 index 000000000..ea4dba38b --- /dev/null +++ b/libevm/EVMC.cpp @@ -0,0 +1,78 @@ +// Copyright 2018 cpp-ethereum Authors. +// Licensed under the GNU General Public License v3. See the LICENSE file. + +#include "EVMC.h" + +#include +#include +#include + +namespace dev +{ +namespace eth +{ +EVM::EVM(evm_instance* _instance) noexcept : m_instance(_instance) +{ + assert(m_instance != nullptr); + assert(m_instance->abi_version == EVM_ABI_VERSION); + + // Set the options. + for (auto& pair : evmcOptions()) + m_instance->set_option(m_instance, pair.first.c_str(), pair.second.c_str()); +} + +owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp) +{ + assert(_ext.envInfo().number() >= 0); + assert(_ext.envInfo().timestamp() >= 0); + + constexpr int64_t int64max = std::numeric_limits::max(); + + // TODO: The following checks should be removed by changing the types + // used for gas, block number and timestamp. + (void)int64max; + assert(io_gas <= int64max); + assert(_ext.envInfo().gasLimit() <= int64max); + assert(_ext.depth <= std::numeric_limits::max()); + + auto gas = static_cast(io_gas); + EVM::Result r = execute(_ext, gas); + + if (r.status() == EVM_REJECTED) + { + cwarn << "Execution rejected by EVM-C, executing with interpreter"; + return VMFactory::create(VMKind::Interpreter)->exec(io_gas, _ext, _onOp); + } + + // TODO: Add EVM-C result codes mapping with exception types. + if (r.status() == EVM_FAILURE) + BOOST_THROW_EXCEPTION(OutOfGas()); + + io_gas = r.gasLeft(); + + // FIXME: Copy the output for now, but copyless version possible. + owning_bytes_ref output{r.output().toVector(), 0, r.output().size()}; + + if (r.status() == EVM_REVERT) + throw RevertInstruction(std::move(output)); + + return output; +} + +evm_revision toRevision(EVMSchedule const& _schedule) +{ + if (_schedule.haveCreate2) + return EVM_CONSTANTINOPLE; + if (_schedule.haveRevert) + return EVM_BYZANTIUM; + if (_schedule.eip158Mode) + return EVM_SPURIOUS_DRAGON; + if (_schedule.eip150Mode) + return EVM_TANGERINE_WHISTLE; + if (_schedule.haveDelegateCall) + return EVM_HOMESTEAD; + return EVM_FRONTIER; +} + +} +} diff --git a/libevm/EVMC.h b/libevm/EVMC.h new file mode 100644 index 000000000..0e727db1b --- /dev/null +++ b/libevm/EVMC.h @@ -0,0 +1,98 @@ +// Copyright 2018 cpp-ethereum Authors. +// Licensed under the GNU General Public License v3. See the LICENSE file. + +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ +/// Translate the EVMSchedule to EVM-C revision. +evm_revision toRevision(EVMSchedule const& _schedule); + +/// The RAII wrapper for an EVM-C instance. +class EVM +{ +public: + explicit EVM(evm_instance* _instance) noexcept; + + ~EVM() { m_instance->destroy(m_instance); } + + EVM(EVM const&) = delete; + EVM& operator=(EVM) = delete; + + class Result + { + public: + explicit Result(evm_result const& _result): + m_result(_result) + {} + + ~Result() + { + if (m_result.release) + m_result.release(&m_result); + } + + Result(Result&& _other) noexcept: + m_result(_other.m_result) + { + // Disable releaser of the rvalue object. + _other.m_result.release = nullptr; + } + + Result(Result const&) = delete; + Result& operator=(Result const&) = delete; + + evm_status_code status() const + { + return m_result.status_code; + } + + int64_t gasLeft() const + { + return m_result.gas_left; + } + + bytesConstRef output() const + { + return {m_result.output_data, m_result.output_size}; + } + + private: + evm_result m_result; + }; + + /// Handy wrapper for evm_execute(). + Result execute(ExtVMFace& _ext, int64_t gas) + { + auto mode = toRevision(_ext.evmSchedule()); + uint32_t flags = _ext.staticCall ? EVM_STATIC : 0; + evm_message msg = {toEvmC(_ext.myAddress), toEvmC(_ext.caller), + toEvmC(_ext.value), _ext.data.data(), + _ext.data.size(), toEvmC(_ext.codeHash), gas, + static_cast(_ext.depth), EVM_CALL, flags}; + return Result{m_instance->execute( + m_instance, &_ext, mode, &msg, _ext.code.data(), _ext.code.size() + )}; + } + +private: + /// The VM instance created with EVM-C _create() function. + evm_instance* m_instance = nullptr; +}; + + +/// The wrapper implementing the VMFace interface with a EVM-C VM as a backend. +class EVMC : public EVM, public VMFace +{ +public: + explicit EVMC(evm_instance* _instance) : EVM(_instance) {} + + owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) final; +}; +} +} diff --git a/libevm/ExtVMFace.cpp b/libevm/ExtVMFace.cpp new file mode 100644 index 000000000..80c675b51 --- /dev/null +++ b/libevm/ExtVMFace.cpp @@ -0,0 +1,301 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + +#include "ExtVMFace.h" + +namespace dev +{ +namespace eth +{ +namespace +{ + +static_assert(sizeof(Address) == sizeof(evm_address), "Address types size mismatch"); +static_assert(alignof(Address) == alignof(evm_address), "Address types alignment mismatch"); + +inline Address fromEvmC(evm_address const& _addr) +{ + return reinterpret_cast
(_addr); +} + +static_assert(sizeof(h256) == sizeof(evm_uint256be), "Hash types size mismatch"); +static_assert(alignof(h256) == alignof(evm_uint256be), "Hash types alignment mismatch"); + +inline u256 fromEvmC(evm_uint256be const& _n) +{ + return fromBigEndian(_n.bytes); +} + +int accountExists(evm_context* _context, evm_address const* _addr) noexcept +{ + auto& env = static_cast(*_context); + Address addr = fromEvmC(*_addr); + return env.exists(addr) ? 1 : 0; +} + +void getStorage( + evm_uint256be* o_result, + evm_context* _context, + evm_address const* _addr, + evm_uint256be const* _key +) noexcept +{ + (void) _addr; + auto& env = static_cast(*_context); + assert(fromEvmC(*_addr) == env.myAddress); + u256 key = fromEvmC(*_key); + *o_result = toEvmC(env.store(key)); +} + +void setStorage( + evm_context* _context, + evm_address const* _addr, + evm_uint256be const* _key, + evm_uint256be const* _value +) noexcept +{ + (void) _addr; + auto& env = static_cast(*_context); + assert(fromEvmC(*_addr) == env.myAddress); + u256 index = fromEvmC(*_key); + u256 value = fromEvmC(*_value); + if (value == 0 && env.store(index) != 0) // If delete + env.sub.refunds += env.evmSchedule().sstoreRefundGas; // Increase refund counter + + env.setStore(index, value); // Interface uses native endianness +} + +void getBalance( + evm_uint256be* o_result, + evm_context* _context, + evm_address const* _addr +) noexcept +{ + auto& env = static_cast(*_context); + *o_result = toEvmC(env.balance(fromEvmC(*_addr))); +} + +size_t getCode(byte const** o_code, evm_context* _context, evm_address const* _addr) +{ + auto& env = static_cast(*_context); + Address addr = fromEvmC(*_addr); + if (o_code != nullptr) + { + auto& code = env.codeAt(addr); + *o_code = code.data(); + return code.size(); + } + return env.codeSizeAt(addr); +} + +void selfdestruct( + evm_context* _context, + evm_address const* _addr, + evm_address const* _beneficiary +) noexcept +{ + (void) _addr; + auto& env = static_cast(*_context); + assert(fromEvmC(*_addr) == env.myAddress); + env.suicide(fromEvmC(*_beneficiary)); +} + + +void log( + evm_context* _context, + evm_address const* _addr, + uint8_t const* _data, + size_t _dataSize, + evm_uint256be const _topics[], + size_t _numTopics +) noexcept +{ + (void) _addr; + auto& env = static_cast(*_context); + assert(fromEvmC(*_addr) == env.myAddress); + h256 const* pTopics = reinterpret_cast(_topics); + env.log(h256s{pTopics, pTopics + _numTopics}, + bytesConstRef{_data, _dataSize}); +} + +void getTxContext(evm_tx_context* result, evm_context* _context) noexcept +{ + auto& env = static_cast(*_context); + result->tx_gas_price = toEvmC(env.gasPrice); + result->tx_origin = toEvmC(env.origin); + result->block_coinbase = toEvmC(env.envInfo().author()); + result->block_number = static_cast(env.envInfo().number()); + result->block_timestamp = static_cast(env.envInfo().timestamp()); + result->block_gas_limit = static_cast(env.envInfo().gasLimit()); + result->block_difficulty = toEvmC(env.envInfo().difficulty()); +} + +void getBlockHash(evm_uint256be* o_hash, evm_context* _envPtr, int64_t _number) +{ + auto& env = static_cast(*_envPtr); + *o_hash = toEvmC(env.blockHash(_number)); +} + +void create(evm_result* o_result, ExtVMFace& _env, evm_message const* _msg) noexcept +{ + u256 gas = _msg->gas; + u256 value = fromEvmC(_msg->value); + bytesConstRef init = {_msg->input_data, _msg->input_size}; + // ExtVM::create takes the sender address from .myAddress. + assert(fromEvmC(_msg->sender) == _env.myAddress); + + h160 addr; + owning_bytes_ref output; + std::tie(addr, output) = _env.create( + value, gas, init, Instruction::CREATE, u256(0), {} + ); + o_result->gas_left = static_cast(gas); + o_result->release = nullptr; + if (addr) + { + o_result->status_code = EVM_SUCCESS; + o_result->create_address = toEvmC(addr); + o_result->output_data = nullptr; + o_result->output_size = 0; + } + else + { + o_result->status_code = EVM_REVERT; + + // Pass the output to the EVM without a copy. The EVM will delete it + // when finished with it. + + // First assign reference. References are not invalidated when vector + // of bytes is moved. See `.takeBytes()` below. + o_result->output_data = output.data(); + o_result->output_size = output.size(); + + // Place a new vector of bytes containing output in result's reserved memory. + auto* data = evm_get_optional_data(o_result); + static_assert(sizeof(bytes) <= sizeof(*data), "Vector is too big"); + new(data) bytes(output.takeBytes()); + // Set the destructor to delete the vector. + o_result->release = [](evm_result const* _result) + { + auto* data = evm_get_const_optional_data(_result); + auto& output = reinterpret_cast(*data); + // Explicitly call vector's destructor to release its data. + // This is normal pattern when placement new operator is used. + output.~bytes(); + }; + } +} + +void call(evm_result* o_result, evm_context* _context, evm_message const* _msg) noexcept +{ + assert(_msg->gas >= 0 && "Invalid gas value"); + auto& env = static_cast(*_context); + + // Handle CREATE separately. + if (_msg->kind == EVM_CREATE) + return create(o_result, env, _msg); + + CallParameters params; + params.gas = _msg->gas; + params.apparentValue = fromEvmC(_msg->value); + params.valueTransfer = + _msg->kind == EVM_DELEGATECALL ? 0 : params.apparentValue; + params.senderAddress = fromEvmC(_msg->sender); + params.codeAddress = fromEvmC(_msg->destination); + params.receiveAddress = + _msg->kind == EVM_CALL ? params.codeAddress : env.myAddress; + params.data = {_msg->input_data, _msg->input_size}; + params.staticCall = (_msg->flags & EVM_STATIC) != 0; + params.onOp = {}; + + bool success = false; + owning_bytes_ref output; + std::tie(success, output) = env.call(params); + // FIXME: We have a mess here. It is hard to distinguish reverts from failures. + // In first case we want to keep the output, in the second one the output + // is optional and should not be passed to the contract, but can be useful + // for EVM in general. + o_result->status_code = success ? EVM_SUCCESS : EVM_REVERT; + o_result->gas_left = static_cast(params.gas); + + // Pass the output to the EVM without a copy. The EVM will delete it + // when finished with it. + + // First assign reference. References are not invalidated when vector + // of bytes is moved. See `.takeBytes()` below. + o_result->output_data = output.data(); + o_result->output_size = output.size(); + + // Place a new vector of bytes containing output in result's reserved memory. + auto* data = evm_get_optional_data(o_result); + static_assert(sizeof(bytes) <= sizeof(*data), "Vector is too big"); + new(data) bytes(output.takeBytes()); + // Set the destructor to delete the vector. + o_result->release = [](evm_result const* _result) + { + auto* data = evm_get_const_optional_data(_result); + auto& output = reinterpret_cast(*data); + // Explicitly call vector's destructor to release its data. + // This is normal pattern when placement new operator is used. + output.~bytes(); + }; +} + +evm_context_fn_table const fnTable = { + accountExists, + getStorage, + setStorage, + getBalance, + getCode, + selfdestruct, + eth::call, + getTxContext, + getBlockHash, + eth::log +}; + +} + +ExtVMFace::ExtVMFace( + EnvInfo const& _envInfo, + Address _myAddress, + Address _caller, + Address _origin, + u256 _value, + u256 _gasPrice, + bytesConstRef _data, + bytes _code, + h256 const& _codeHash, + unsigned _depth, + bool _staticCall +): + evm_context{&fnTable}, + m_envInfo(_envInfo), + myAddress(_myAddress), + caller(_caller), + origin(_origin), + value(_value), + gasPrice(_gasPrice), + data(_data), + code(std::move(_code)), + codeHash(_codeHash), + depth(_depth), + staticCall(_staticCall) +{} + +} +} diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h new file mode 100644 index 000000000..b36f100e5 --- /dev/null +++ b/libevm/ExtVMFace.h @@ -0,0 +1,257 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + +#pragma once + +#include "Instruction.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +/// Reference to a slice of buffer that also owns the buffer. +/// +/// This is extension to the concept C++ STL library names as array_view +/// (also known as gsl::span, array_ref, here vector_ref) -- reference to +/// continuous non-modifiable memory. The extension makes the object also owning +/// the referenced buffer. +/// +/// This type is used by VMs to return output coming from RETURN instruction. +/// To avoid memory copy, a VM returns its whole memory + the information what +/// part of this memory is actually the output. This simplifies the VM design, +/// because there are multiple options how the output will be used (can be +/// ignored, part of it copied, or all of it copied). The decision what to do +/// with it was moved out of VM interface making VMs "stateless". +/// +/// The type is movable, but not copyable. Default constructor available. +class owning_bytes_ref: public vector_ref +{ +public: + owning_bytes_ref() = default; + + /// @param _bytes The buffer. + /// @param _begin The index of the first referenced byte. + /// @param _size The number of referenced bytes. + owning_bytes_ref(bytes&& _bytes, size_t _begin, size_t _size): + m_bytes(std::move(_bytes)) + { + // Set the reference *after* the buffer is moved to avoid + // pointer invalidation. + retarget(&m_bytes[_begin], _size); + } + + owning_bytes_ref(owning_bytes_ref const&) = delete; + owning_bytes_ref(owning_bytes_ref&&) = default; + owning_bytes_ref& operator=(owning_bytes_ref const&) = delete; + owning_bytes_ref& operator=(owning_bytes_ref&&) = default; + + /// Moves the bytes vector out of here. The object cannot be used any more. + bytes&& takeBytes() + { + reset(); // Reset reference just in case. + return std::move(m_bytes); + } + +private: + bytes m_bytes; +}; + +struct SubState +{ + std::set
suicides; ///< Any accounts that have suicided. + LogEntries logs; ///< Any logs. + u256 refunds; ///< Refund counter of SSTORE nonzero->zero. + + SubState& operator+=(SubState const& _s) + { + suicides += _s.suicides; + refunds += _s.refunds; + logs += _s.logs; + return *this; + } + + void clear() + { + suicides.clear(); + logs.clear(); + refunds = 0; + } +}; + +class ExtVMFace; +class LastBlockHashesFace; +class VM; + +using OnOpFunc = std::function; + +struct CallParameters +{ + CallParameters() = default; + CallParameters( + Address _senderAddress, + Address _codeAddress, + Address _receiveAddress, + u256 _valueTransfer, + u256 _apparentValue, + u256 _gas, + bytesConstRef _data, + OnOpFunc _onOpFunc + ): senderAddress(_senderAddress), codeAddress(_codeAddress), receiveAddress(_receiveAddress), + valueTransfer(_valueTransfer), apparentValue(_apparentValue), gas(_gas), data(_data), onOp(_onOpFunc) {} + Address senderAddress; + Address codeAddress; + Address receiveAddress; + u256 valueTransfer; + u256 apparentValue; + u256 gas; + bytesConstRef data; + bool staticCall = false; + OnOpFunc onOp; +}; + +class EnvInfo +{ +public: + EnvInfo(BlockHeader const& _current, LastBlockHashesFace const& _lh, u256 const& _gasUsed): + m_headerInfo(_current), + m_lastHashes(_lh), + m_gasUsed(_gasUsed) + {} + // Constructor with custom gasLimit - used in some synthetic scenarios like eth_estimateGas RPC method + EnvInfo(BlockHeader const& _current, LastBlockHashesFace const& _lh, u256 const& _gasUsed, u256 const& _gasLimit): + EnvInfo(_current, _lh, _gasUsed) + { + m_headerInfo.setGasLimit(_gasLimit); + } + + BlockHeader const& header() const { return m_headerInfo; } + + int64_t number() const { return m_headerInfo.number(); } + Address const& author() const { return m_headerInfo.author(); } + int64_t timestamp() const { return m_headerInfo.timestamp(); } + u256 const& difficulty() const { return m_headerInfo.difficulty(); } + u256 const& gasLimit() const { return m_headerInfo.gasLimit(); } + LastBlockHashesFace const& lastHashes() const { return m_lastHashes; } + u256 const& gasUsed() const { return m_gasUsed; } + +private: + BlockHeader m_headerInfo; + LastBlockHashesFace const& m_lastHashes; + u256 m_gasUsed; +}; + +/** + * @brief Interface and null implementation of the class for specifying VM externalities. + */ +class ExtVMFace: public evm_context +{ +public: + /// Null constructor. + ExtVMFace() = default; + + /// Full constructor. + ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth, bool _staticCall); + + virtual ~ExtVMFace() = default; + + ExtVMFace(ExtVMFace const&) = delete; + ExtVMFace& operator=(ExtVMFace const&) = delete; + + /// Read storage location. + virtual u256 store(u256) { return 0; } + + /// Write a value in storage. + virtual void setStore(u256, u256) {} + + /// Read address's balance. + virtual u256 balance(Address) { return 0; } + + /// Read address's code. + virtual bytes const& codeAt(Address) { return NullBytes; } + + /// @returns the size of the code in bytes at the given address. + virtual size_t codeSizeAt(Address) { return 0; } + + /// Does the account exist? + virtual bool exists(Address) { return false; } + + /// Suicide the associated contract and give proceeds to the given address. + virtual void suicide(Address) { sub.suicides.insert(myAddress); } + + /// Create a new (contract) account. + virtual std::pair create(u256, u256&, bytesConstRef, Instruction, u256, OnOpFunc const&) = 0; + + /// Make a new message call. + /// @returns success flag and output data, if any. + virtual std::pair call(CallParameters&) = 0; + + /// Revert any changes made (by any of the other calls). + virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); } + + /// Hash of a block if within the last 256 blocks, or h256() otherwise. + virtual h256 blockHash(u256 _number) = 0; + + /// Get the execution environment information. + EnvInfo const& envInfo() const { return m_envInfo; } + + /// Return the EVM gas-price schedule for this execution context. + virtual EVMSchedule const& evmSchedule() const { return DefaultSchedule; } + +private: + EnvInfo const& m_envInfo; + +public: + // TODO: make private + Address myAddress; ///< Address associated with executing code (a contract, or contract-to-be). + Address caller; ///< Address which sent the message (either equal to origin or a contract). + Address origin; ///< Original transactor. + u256 value; ///< Value (in Wei) that was passed to this address. + u256 gasPrice; ///< Price of gas (that we already paid). + bytesConstRef data; ///< Current input data. + bytes code; ///< Current code that is executing. + h256 codeHash; ///< SHA3 hash of the executing code + SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). + unsigned depth = 0; ///< Depth of the present call. + bool staticCall = false; ///< Throw on state changing. +}; + +inline evm_address toEvmC(Address const& _addr) +{ + return reinterpret_cast(_addr); +} + +inline evm_uint256be toEvmC(h256 const& _h) +{ + return reinterpret_cast(_h); +} + +} +} diff --git a/libevm/Instruction.cpp b/libevm/Instruction.cpp new file mode 100644 index 000000000..d3c3e2685 --- /dev/null +++ b/libevm/Instruction.cpp @@ -0,0 +1,229 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + +#include "Instruction.h" +#include + +namespace dev +{ +namespace eth +{ + +static const std::map c_instructionInfo = +{ // Add, Args, Ret, GasPriceTier + { Instruction::STOP, { "STOP", 0, 0, 0, Tier::Zero } }, + { Instruction::ADD, { "ADD", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SUB, { "SUB", 0, 2, 1, Tier::VeryLow } }, + { Instruction::MUL, { "MUL", 0, 2, 1, Tier::Low } }, + { Instruction::DIV, { "DIV", 0, 2, 1, Tier::Low } }, + { Instruction::SDIV, { "SDIV", 0, 2, 1, Tier::Low } }, + { Instruction::MOD, { "MOD", 0, 2, 1, Tier::Low } }, + { Instruction::SMOD, { "SMOD", 0, 2, 1, Tier::Low } }, + { Instruction::EXP, { "EXP", 0, 2, 1, Tier::Special } }, + { Instruction::NOT, { "NOT", 0, 1, 1, Tier::VeryLow } }, + { Instruction::LT, { "LT", 0, 2, 1, Tier::VeryLow } }, + { Instruction::GT, { "GT", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SLT, { "SLT", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SGT, { "SGT", 0, 2, 1, Tier::VeryLow } }, + { Instruction::EQ, { "EQ", 0, 2, 1, Tier::VeryLow } }, + { Instruction::ISZERO, { "ISZERO", 0, 1, 1, Tier::VeryLow } }, + { Instruction::AND, { "AND", 0, 2, 1, Tier::VeryLow } }, + { Instruction::OR, { "OR", 0, 2, 1, Tier::VeryLow } }, + { Instruction::XOR, { "XOR", 0, 2, 1, Tier::VeryLow } }, + { Instruction::BYTE, { "BYTE", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SHL, { "SHL", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SHR, { "SHR", 0, 2, 1, Tier::VeryLow } }, + { Instruction::SAR, { "SAR", 0, 2, 1, Tier::VeryLow } }, + { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, Tier::Mid } }, + { Instruction::MULMOD, { "MULMOD", 0, 3, 1, Tier::Mid } }, + { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, Tier::Low } }, + { Instruction::SHA3, { "SHA3", 0, 2, 1, Tier::Special } }, + { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, Tier::Base } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1, Tier::Special } }, + { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, Tier::Base } }, + { Instruction::CALLER, { "CALLER", 0, 0, 1, Tier::Base } }, + { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, Tier::Base } }, + { Instruction::CALLDATALOAD, { "CALLDATALOAD", 0, 1, 1, Tier::VeryLow } }, + { Instruction::CALLDATASIZE, { "CALLDATASIZE", 0, 0, 1, Tier::Base } }, + { Instruction::CALLDATACOPY, { "CALLDATACOPY", 0, 3, 0, Tier::VeryLow } }, + { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, Tier::Base } }, + { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, Tier::VeryLow } }, + { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, Tier::Base } }, + { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, Tier::Special } }, + { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, Tier::Special } }, + { Instruction::RETURNDATASIZE,{"RETURNDATASIZE", 0, 0, 1, Tier::Base } }, + { Instruction::RETURNDATACOPY,{"RETURNDATACOPY", 0, 3, 0, Tier::VeryLow } }, + { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, Tier::Special } }, + { Instruction::COINBASE, { "COINBASE", 0, 0, 1, Tier::Base } }, + { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, Tier::Base } }, + { Instruction::NUMBER, { "NUMBER", 0, 0, 1, Tier::Base } }, + { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, Tier::Base } }, + { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, Tier::Base } }, + { Instruction::POP, { "POP", 0, 1, 0, Tier::Base } }, + { Instruction::MLOAD, { "MLOAD", 0, 1, 1, Tier::VeryLow } }, + { Instruction::MSTORE, { "MSTORE", 0, 2, 0, Tier::VeryLow } }, + { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, Tier::VeryLow } }, + { Instruction::SLOAD, { "SLOAD", 0, 1, 1, Tier::Special } }, + { Instruction::SSTORE, { "SSTORE", 0, 2, 0, Tier::Special } }, + { Instruction::JUMP, { "JUMP", 0, 1, 0, Tier::Mid } }, + { Instruction::JUMPI, { "JUMPI", 0, 2, 0, Tier::High } }, + { Instruction::PC, { "PC", 0, 0, 1, Tier::Base } }, + { Instruction::MSIZE, { "MSIZE", 0, 0, 1, Tier::Base } }, + { Instruction::GAS, { "GAS", 0, 0, 1, Tier::Base } }, + { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, Tier::Special } }, + { Instruction::PUSH1, { "PUSH1", 1, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH2, { "PUSH2", 2, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH3, { "PUSH3", 3, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH4, { "PUSH4", 4, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH5, { "PUSH5", 5, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH6, { "PUSH6", 6, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH7, { "PUSH7", 7, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH8, { "PUSH8", 8, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH9, { "PUSH9", 9, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH10, { "PUSH10", 10, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH11, { "PUSH11", 11, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH12, { "PUSH12", 12, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH13, { "PUSH13", 13, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH14, { "PUSH14", 14, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH15, { "PUSH15", 15, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH16, { "PUSH16", 16, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH17, { "PUSH17", 17, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH18, { "PUSH18", 18, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH19, { "PUSH19", 19, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH20, { "PUSH20", 20, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH21, { "PUSH21", 21, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH22, { "PUSH22", 22, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH23, { "PUSH23", 23, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH24, { "PUSH24", 24, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH25, { "PUSH25", 25, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH26, { "PUSH26", 26, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH27, { "PUSH27", 27, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH28, { "PUSH28", 28, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH29, { "PUSH29", 29, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH30, { "PUSH30", 30, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH31, { "PUSH31", 31, 0, 1, Tier::VeryLow } }, + { Instruction::PUSH32, { "PUSH32", 32, 0, 1, Tier::VeryLow } }, + { Instruction::DUP1, { "DUP1", 0, 1, 2, Tier::VeryLow } }, + { Instruction::DUP2, { "DUP2", 0, 2, 3, Tier::VeryLow } }, + { Instruction::DUP3, { "DUP3", 0, 3, 4, Tier::VeryLow } }, + { Instruction::DUP4, { "DUP4", 0, 4, 5, Tier::VeryLow } }, + { Instruction::DUP5, { "DUP5", 0, 5, 6, Tier::VeryLow } }, + { Instruction::DUP6, { "DUP6", 0, 6, 7, Tier::VeryLow } }, + { Instruction::DUP7, { "DUP7", 0, 7, 8, Tier::VeryLow } }, + { Instruction::DUP8, { "DUP8", 0, 8, 9, Tier::VeryLow } }, + { Instruction::DUP9, { "DUP9", 0, 9, 10, Tier::VeryLow } }, + { Instruction::DUP10, { "DUP10", 0, 10, 11, Tier::VeryLow } }, + { Instruction::DUP11, { "DUP11", 0, 11, 12, Tier::VeryLow } }, + { Instruction::DUP12, { "DUP12", 0, 12, 13, Tier::VeryLow } }, + { Instruction::DUP13, { "DUP13", 0, 13, 14, Tier::VeryLow } }, + { Instruction::DUP14, { "DUP14", 0, 14, 15, Tier::VeryLow } }, + { Instruction::DUP15, { "DUP15", 0, 15, 16, Tier::VeryLow } }, + { Instruction::DUP16, { "DUP16", 0, 16, 17, Tier::VeryLow } }, + { Instruction::SWAP1, { "SWAP1", 0, 2, 2, Tier::VeryLow } }, + { Instruction::SWAP2, { "SWAP2", 0, 3, 3, Tier::VeryLow } }, + { Instruction::SWAP3, { "SWAP3", 0, 4, 4, Tier::VeryLow } }, + { Instruction::SWAP4, { "SWAP4", 0, 5, 5, Tier::VeryLow } }, + { Instruction::SWAP5, { "SWAP5", 0, 6, 6, Tier::VeryLow } }, + { Instruction::SWAP6, { "SWAP6", 0, 7, 7, Tier::VeryLow } }, + { Instruction::SWAP7, { "SWAP7", 0, 8, 8, Tier::VeryLow } }, + { Instruction::SWAP8, { "SWAP8", 0, 9, 9, Tier::VeryLow } }, + { Instruction::SWAP9, { "SWAP9", 0, 10, 10, Tier::VeryLow } }, + { Instruction::SWAP10, { "SWAP10", 0, 11, 11, Tier::VeryLow } }, + { Instruction::SWAP11, { "SWAP11", 0, 12, 12, Tier::VeryLow } }, + { Instruction::SWAP12, { "SWAP12", 0, 13, 13, Tier::VeryLow } }, + { Instruction::SWAP13, { "SWAP13", 0, 14, 14, Tier::VeryLow } }, + { Instruction::SWAP14, { "SWAP14", 0, 15, 15, Tier::VeryLow } }, + { Instruction::SWAP15, { "SWAP15", 0, 16, 16, Tier::VeryLow } }, + { Instruction::SWAP16, { "SWAP16", 0, 17, 17, Tier::VeryLow } }, + { Instruction::LOG0, { "LOG0", 0, 2, 0, Tier::Special } }, + { Instruction::LOG1, { "LOG1", 0, 3, 0, Tier::Special } }, + { Instruction::LOG2, { "LOG2", 0, 4, 0, Tier::Special } }, + { Instruction::LOG3, { "LOG3", 0, 5, 0, Tier::Special } }, + { Instruction::LOG4, { "LOG4", 0, 6, 0, Tier::Special } }, + + { Instruction::JUMPTO, { "JUMPTO", 2, 1, 0, Tier::VeryLow } }, + { Instruction::JUMPIF, { "JUMPIF", 2, 2, 0, Tier::Low } }, + { Instruction::JUMPV, { "JUMPV", 2, 1, 0, Tier::Mid } }, + { Instruction::JUMPSUB, { "JUMPSUB", 2, 1, 0, Tier::Low } }, + { Instruction::JUMPSUBV, { "JUMPSUBV", 2, 1, 0, Tier::Mid } }, + { Instruction::BEGINSUB, { "BEGINSUB", 0, 0, 0, Tier::Special } }, + { Instruction::BEGINDATA, { "BEGINDATA", 0, 0, 0, Tier::Special } }, + { Instruction::RETURNSUB, { "RETURNSUB", 0, 1, 0, Tier::Mid } }, + { Instruction::PUTLOCAL, { "PUTLOCAL", 2, 1, 0, Tier::VeryLow } }, + { Instruction::GETLOCAL, { "GETLOCAL", 2, 0, 1, Tier::VeryLow } }, + + { Instruction::XADD, { "XADD", 1, 0, 0, Tier::Special } }, + { Instruction::XMUL, { "XMUL", 1, 2, 1, Tier::Special } }, + { Instruction::XSUB, { "XSUB", 1, 2, 1, Tier::Special } }, + { Instruction::XDIV, { "XDIV", 1, 2, 1, Tier::Special } }, + { Instruction::XSDIV, { "XSDIV", 1, 2, 1, Tier::Special } }, + { Instruction::XMOD, { "XMOD", 1, 2, 1, Tier::Special } }, + { Instruction::XSMOD, { "XSMOD", 1, 2, 1, Tier::Special } }, + { Instruction::XLT, { "XLT", 1, 2, 1, Tier::Special } }, + { Instruction::XGT, { "XGT", 1, 2, 1, Tier::Special } }, + { Instruction::XSLT, { "XSLT", 1, 2, 1, Tier::Special } }, + { Instruction::XSGT, { "XSGT", 1, 2, 1, Tier::Special } }, + { Instruction::XEQ, { "XEQ", 1, 2, 1, Tier::Special } }, + { Instruction::XISZERO, { "XISZERO", 1, 2, 1, Tier::Special } }, + { Instruction::XAND, { "XAND", 1, 1, 1, Tier::Special } }, + { Instruction::XOR, { "XOR", 1, 2, 1, Tier::Special } }, + { Instruction::XXOR, { "XXOR", 1, 2, 1, Tier::Special } }, + { Instruction::XNOT, { "XNOT", 1, 2, 1, Tier::Special } }, + { Instruction::XSHL, { "XSHL", 1, 2, 1, Tier::Special } }, + { Instruction::XSHR, { "XSHR", 1, 2, 1, Tier::Special } }, + { Instruction::XSAR, { "XSAR", 1, 2, 1, Tier::Special } }, + { Instruction::XROL, { "XROL", 1, 2, 1, Tier::Special } }, + { Instruction::XROR, { "XROR", 1, 2, 1, Tier::Special } }, + { Instruction::XPUSH, { "XPUSH", 1, 1, 1, Tier::VeryLow } }, + { Instruction::XMLOAD, { "XMLOAD", 1, 1, 1, Tier::VeryLow } }, + { Instruction::XMSTORE, { "XMSTORE", 1, 2, 0, Tier::VeryLow } }, + { Instruction::XSLOAD, { "XSLOAD", 1, 1, 1, Tier::Special } }, + { Instruction::XSSTORE, { "XSSTORE", 1, 2, 0, Tier::Special } }, + { Instruction::XVTOWIDE, { "XVTOWIDE", 1, 1, 1, Tier::VeryLow } }, + { Instruction::XWIDETOV, { "XWIDETOV", 1, 1, 1, Tier::VeryLow } }, + { Instruction::XPUT, { "XPUT", 1, 3, 1, Tier::Special } }, + { Instruction::XGET, { "XGET", 1, 2, 1, Tier::Special } }, + { Instruction::XSWIZZLE, { "XSWIZZLE", 1, 2, 1, Tier::Special } }, + { Instruction::XSHUFFLE, { "XSHUFFLE", 1, 3, 1, Tier::Special } }, + { Instruction::CREATE, { "CREATE", 0, 3, 1, Tier::Special } }, + { Instruction::CREATE2, { "CREATE2", 0, 4, 1, Tier::Special } }, + { Instruction::CALL, { "CALL", 0, 7, 1, Tier::Special } }, + { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, Tier::Special } }, + { Instruction::RETURN, { "RETURN", 0, 2, 0, Tier::Zero } }, + { Instruction::STATICCALL, { "STATICCALL", 0, 6, 1, Tier::Special } }, + { Instruction::DELEGATECALL, { "DELEGATECALL", 0, 6, 1, Tier::Special } }, + { Instruction::REVERT, { "REVERT", 0, 2, 0, Tier::Special } }, + { Instruction::INVALID, { "INVALID", 0, 0, 0, Tier::Zero } }, + { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, Tier::Special } }, + + // these are generated by the interpreter - should never be in user code + { Instruction::PUSHC, { "PUSHC", 3, 0, 1, Tier::VeryLow } }, + { Instruction::JUMPC, { "JUMPC", 0, 1, 0, Tier::Mid } }, + { Instruction::JUMPCI, { "JUMPCI", 0, 2, 0, Tier::High } }, +}; + +InstructionInfo instructionInfo(Instruction _inst) +{ + auto it = c_instructionInfo.find(_inst); + if (it != c_instructionInfo.end()) + return it->second; + return InstructionInfo{{}, 0, 0, 0, Tier::Invalid}; +} + + +} +} \ No newline at end of file diff --git a/libevm/Instruction.h b/libevm/Instruction.h new file mode 100644 index 000000000..876cd509b --- /dev/null +++ b/libevm/Instruction.h @@ -0,0 +1,259 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +/// Virtual machine bytecode instruction. +enum class Instruction: uint8_t +{ + STOP = 0x00, ///< halts execution + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + EXP, ///< exponential operation + SIGNEXTEND, ///< extend length of signed integer + + LT = 0x10, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + ISZERO, ///< simple not operator + AND, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + NOT, ///< bitwise NOT operation + BYTE, ///< retrieve single byte from word + SHL, ///< logical shift left operation + SHR, ///< logical shift right operation + SAR, ///< arithmetic shift right operation + + SHA3 = 0x20, ///< compute SHA3-256 hash + + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + RETURNDATASIZE = 0x3d, ///< size of data returned from previous call + RETURNDATACOPY = 0x3e, ///< copy data returned from previous call to memory + + BLOCKHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter to a jumpdest + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack + + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack + + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. + + // these are generated by the interpreter - should never be in user code + PUSHC = 0xac, ///< push value from constant pool + JUMPC, ///< alter the program counter - pre-verified + JUMPCI, ///< conditionally alter the program counter - pre-verified + + JUMPTO = 0xb0, ///< alter the program counter to a jumpdest + JUMPIF, ///< conditionally alter the program counter + JUMPSUB, ///< alter the program counter to a beginsub + JUMPV, ///< alter the program counter to a jumpdest + JUMPSUBV, ///< alter the program counter to a beginsub + BEGINSUB, ///< set a potential jumpsub destination + BEGINDATA, ///< begine the data section + RETURNSUB, ///< return to subroutine jumped from + PUTLOCAL, ///< pop top of stack to local variable + GETLOCAL, ///< push local variable to top of stack + + XADD = 0xc1, ///< addition operation + XMUL, ///< mulitplication operation + XSUB, ///< subtraction operation + XDIV, ///< integer division operation + XSDIV, ///< signed integer division operation + XMOD, ///< modulo remainder operation + XSMOD, ///< signed modulo remainder operation + XLT = 0xd0, ///< less-than comparision + XGT, ///< greater-than comparision + XSLT, ///< signed less-than comparision + XSGT, ///< signed greater-than comparision + XEQ, ///< equality comparision + XISZERO, ///< simple not operator + XAND, ///< bitwise AND operation + XOOR, ///< bitwise OR operation + XXOR, ///< bitwise XOR operation + XNOT, ///< bitwise NOT opertation + XSHL = 0xdb, ///< shift left opertation + XSHR, ///< shift right opertation + XSAR, ///< shift arithmetic right opertation + XROL, ///< rotate left opertation + XROR, ///< rotate right opertation + XPUSH = 0xe0, ///< push vector to stack + XMLOAD, ///< load vector from memory + XMSTORE, ///< save vector to memory + XSLOAD = 0xe4, ///< load vector from storage + XSSTORE, ///< save vector to storage + XVTOWIDE, ///< convert vector to wide integer + XWIDETOV, ///< convert wide integer to vector + XGET, ///< get data from vector + XPUT, ///< put data in vector + XSWIZZLE, ///< permute data in vector + XSHUFFLE, ///< permute data in two vectors + + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only + RETURN, ///< halt execution returning output data + DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender + STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) + CREATE2 = 0xfb, ///< create a new account with associated code. sha3((sender + salt + sha3(code)) + REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas + INVALID = 0xfe, ///< dedicated invalid instruction + SUICIDE = 0xff ///< halt execution and register account for later deletion +}; + +enum class Tier : unsigned +{ + Zero = 0, // 0, Zero + Base, // 2, Quick + VeryLow, // 3, Fastest + Low, // 5, Fast + Mid, // 8, Mid + High, // 10, Slow + Ext, // 20, Ext + Special, // multiparam or otherwise special + Invalid // Invalid. +}; + +/// Information structure for a particular instruction. +struct InstructionInfo +{ + std::string name; ///< The name of the instruction. + int additional; ///< Additional items required in memory for this instructions (only for PUSH). + int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). + int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. + Tier gasPriceTier; ///< Tier for gas pricing. +}; + +/// Information on all the instructions. +InstructionInfo instructionInfo(Instruction _inst); + +} +} diff --git a/libevm/VM.cpp b/libevm/VM.cpp new file mode 100755 index 000000000..d122481f3 --- /dev/null +++ b/libevm/VM.cpp @@ -0,0 +1,1633 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** @file VM.cpp + */ + +#include +#include "VMConfig.h" +#include "VM.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +uint64_t VM::memNeed(u256 _offset, u256 _size) +{ + return toInt63(_size ? u512(_offset) + _size : u512(0)); +} + +template S divWorkaround(S const& _a, S const& _b) +{ + return (S)(s512(_a) / s512(_b)); +} + +template S modWorkaround(S const& _a, S const& _b) +{ + return (S)(s512(_a) % s512(_b)); +} + + +// +// for decoding destinations of JUMPTO, JUMPV, JUMPSUB and JUMPSUBV +// + +uint64_t VM::decodeJumpDest(const byte* const _code, uint64_t& _pc) +{ + // turn 2 MSB-first bytes in the code into a native-order integer + uint64_t dest = _code[_pc++]; + dest = (dest << 8) | _code[_pc++]; + return dest; +} + +uint64_t VM::decodeJumpvDest(const byte* const _code, uint64_t& _pc, byte _voff) +{ + // Layout of jump table in bytecode... + // byte opcode + // byte n_jumps + // byte table[n_jumps][2] + // + uint64_t pc = _pc; + byte n = _code[++pc]; // byte after opcode is number of jumps + if (_voff >= n) _voff = n - 1; // if offset overflows use default jump + pc += _voff * 2; // adjust inout pc before index destination in table + + uint64_t dest = decodeJumpDest(_code, pc); + + _pc += 1 + n * 2; // adust inout _pc to opcode after table + return dest; +} + + +// +// for tracing, checking, metering, measuring ... +// +void VM::onOperation() +{ + if (m_onOp) + (m_onOp)(++m_nSteps, m_PC, m_OP, + m_newMemSize > m_mem.size() ? (m_newMemSize - m_mem.size()) / 32 : uint64_t(0), + m_runGas, m_io_gas, this, m_ext); +} + +// +// set current SP to SP', adjust SP' per _removed and _added items +// +void VM::adjustStack(unsigned _removed, unsigned _added) +{ + m_SP = m_SPP; + + // adjust stack and check bounds + m_SPP += _removed; + if (m_stackEnd < m_SPP) + throwBadStack(_removed, _added); + m_SPP -= _added; + if (m_SPP < m_stack) + throwBadStack(_removed, _added); +} + +void VM::updateSSGas() +{ + if (!m_ext->store(m_SP[0]) && m_SP[1]) + m_runGas = toInt63(m_schedule->sstoreSetGas); + else if (m_ext->store(m_SP[0]) && !m_SP[1]) + { + m_runGas = toInt63(m_schedule->sstoreResetGas); + m_ext->sub.refunds += m_schedule->sstoreRefundGas; + } + else + m_runGas = toInt63(m_schedule->sstoreResetGas); +} + + +uint64_t VM::gasForMem(u512 _size) +{ + u512 s = _size / 32; + return toInt63((u512)m_schedule->memoryGas * s + s * s / m_schedule->quadCoeffDiv); +} + +void VM::updateIOGas() +{ + if (m_io_gas < m_runGas) + throwOutOfGas(); + m_io_gas -= m_runGas; +} + +void VM::updateGas() +{ + if (m_newMemSize > m_mem.size()) + m_runGas += toInt63(gasForMem(m_newMemSize) - gasForMem(m_mem.size())); + m_runGas += (m_schedule->copyGas * ((m_copyMemSize + 31) / 32)); + if (m_io_gas < m_runGas) + throwOutOfGas(); +} + +void VM::updateMem(uint64_t _newMem) +{ + m_newMemSize = (_newMem + 31) / 32 * 32; + updateGas(); + if (m_newMemSize > m_mem.size()) + m_mem.resize(m_newMemSize); +} + +void VM::logGasMem() +{ + unsigned n = (unsigned)m_OP - (unsigned)Instruction::LOG0; + m_runGas = toInt63(m_schedule->logGas + m_schedule->logTopicGas * n + u512(m_schedule->logDataGas) * m_SP[1]); + updateMem(memNeed(m_SP[0], m_SP[1])); +} + +void VM::fetchInstruction() +{ + m_OP = Instruction(m_code[m_PC]); + const InstructionMetric& metric = c_metrics[static_cast(m_OP)]; + adjustStack(metric.args, metric.ret); + + // FEES... + m_runGas = toInt63(m_schedule->tierStepGas[static_cast(metric.gasPriceTier)]); + m_newMemSize = m_mem.size(); + m_copyMemSize = 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// interpreter entry point + +owning_bytes_ref VM::exec(u256& _io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) +{ + m_io_gas_p = &_io_gas; + m_io_gas = uint64_t(_io_gas); + m_ext = &_ext; + m_schedule = &m_ext->evmSchedule(); + m_onOp = _onOp; + m_onFail = &VM::onOperation; // this results in operations that fail being logged twice in the trace + m_PC = 0; + + try + { + // trampoline to minimize depth of call stack when calling out + m_bounce = &VM::initEntry; + do + (this->*m_bounce)(); + while (m_bounce); + + } + catch (...) + { + *m_io_gas_p = m_io_gas; + throw; + } + + *m_io_gas_p = m_io_gas; + return std::move(m_output); +} + +// +// main interpreter loop and switch +// +void VM::interpretCases() +{ + INIT_CASES + DO_CASES + { + // + // Call-related instructions + // + + CASE(CREATE2) + { + ON_OP(); + if (!m_schedule->haveCreate2) + throwBadInstruction(); + + m_bounce = &VM::caseCreate; + } + BREAK + + CASE(CREATE) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + m_bounce = &VM::caseCreate; + } + BREAK + + CASE(DELEGATECALL) + CASE(STATICCALL) + CASE(CALL) + CASE(CALLCODE) + { + ON_OP(); + if (m_OP == Instruction::DELEGATECALL && !m_schedule->haveDelegateCall) + throwBadInstruction(); + if (m_OP == Instruction::STATICCALL && !m_schedule->haveStaticCall) + throwBadInstruction(); + if (m_OP == Instruction::CALL && m_ext->staticCall && m_SP[2] != 0) + throwDisallowedStateChange(); + m_bounce = &VM::caseCall; + } + BREAK + + CASE(RETURN) + { + ON_OP(); + m_copyMemSize = 0; + updateMem(memNeed(m_SP[0], m_SP[1])); + updateIOGas(); + + uint64_t b = (uint64_t)m_SP[0]; + uint64_t s = (uint64_t)m_SP[1]; + m_output = owning_bytes_ref{std::move(m_mem), b, s}; + m_bounce = 0; + } + BREAK + + CASE(REVERT) + { + // Pre-byzantium + if (!m_schedule->haveRevert) + throwBadInstruction(); + + ON_OP(); + m_copyMemSize = 0; + updateMem(memNeed(m_SP[0], m_SP[1])); + updateIOGas(); + + uint64_t b = (uint64_t)m_SP[0]; + uint64_t s = (uint64_t)m_SP[1]; + owning_bytes_ref output{move(m_mem), b, s}; + throwRevertInstruction(move(output)); + } + BREAK; + + CASE(SUICIDE) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + m_runGas = toInt63(m_schedule->suicideGas); + Address dest = asAddress(m_SP[0]); + + // After EIP158 zero-value suicides do not have to pay account creation gas. + if (m_ext->balance(m_ext->myAddress) > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) + // After EIP150 hard fork charge additional cost of sending + // ethers to non-existing account. + if (m_schedule->suicideChargesNewAccountGas() && !m_ext->exists(dest)) + m_runGas += m_schedule->callNewAccountGas; + + updateIOGas(); + m_ext->suicide(dest); + m_bounce = 0; + } + BREAK + + CASE(STOP) + { + ON_OP(); + updateIOGas(); + m_bounce = 0; + } + BREAK + + + // + // instructions potentially expanding memory + // + + CASE(MLOAD) + { + ON_OP(); + updateMem(toInt63(m_SP[0]) + 32); + updateIOGas(); + + m_SPP[0] = (u256)*(h256 const*)(m_mem.data() + (unsigned)m_SP[0]); + } + NEXT + + CASE(MSTORE) + { + ON_OP(); + updateMem(toInt63(m_SP[0]) + 32); + updateIOGas(); + + *(h256*)&m_mem[(unsigned)m_SP[0]] = (h256)m_SP[1]; + } + NEXT + + CASE(MSTORE8) + { + ON_OP(); + updateMem(toInt63(m_SP[0]) + 1); + updateIOGas(); + + m_mem[(unsigned)m_SP[0]] = (byte)(m_SP[1] & 0xff); + } + NEXT + + CASE(SHA3) + { + ON_OP(); + m_runGas = toInt63(m_schedule->sha3Gas + (u512(m_SP[1]) + 31) / 32 * m_schedule->sha3WordGas); + updateMem(memNeed(m_SP[0], m_SP[1])); + updateIOGas(); + + uint64_t inOff = (uint64_t)m_SP[0]; + uint64_t inSize = (uint64_t)m_SP[1]; + m_SPP[0] = (u256)sha3(bytesConstRef(m_mem.data() + inOff, inSize)); + } + NEXT + + CASE(LOG0) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + logGasMem(); + updateIOGas(); + + m_ext->log({}, bytesConstRef(m_mem.data() + (uint64_t)m_SP[0], (uint64_t)m_SP[1])); + } + NEXT + + CASE(LOG1) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + logGasMem(); + updateIOGas(); + + m_ext->log({m_SP[2]}, bytesConstRef(m_mem.data() + (uint64_t)m_SP[0], (uint64_t)m_SP[1])); + } + NEXT + + CASE(LOG2) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + logGasMem(); + updateIOGas(); + + m_ext->log({m_SP[2], m_SP[3]}, bytesConstRef(m_mem.data() + (uint64_t)m_SP[0], (uint64_t)m_SP[1])); + } + NEXT + + CASE(LOG3) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + logGasMem(); + updateIOGas(); + + m_ext->log({m_SP[2], m_SP[3], m_SP[4]}, bytesConstRef(m_mem.data() + (uint64_t)m_SP[0], (uint64_t)m_SP[1])); + } + NEXT + + CASE(LOG4) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + logGasMem(); + updateIOGas(); + + m_ext->log({m_SP[2], m_SP[3], m_SP[4], m_SP[5]}, bytesConstRef(m_mem.data() + (uint64_t)m_SP[0], (uint64_t)m_SP[1])); + } + NEXT + + CASE(EXP) + { + u256 expon = m_SP[1]; + m_runGas = toInt63(m_schedule->expGas + m_schedule->expByteGas * (32 - (h256(expon).firstBitSet() / 8))); + ON_OP(); + updateIOGas(); + + u256 base = m_SP[0]; + m_SPP[0] = exp256(base, expon); + } + NEXT + + // + // ordinary instructions + // + + CASE(ADD) + { + ON_OP(); + updateIOGas(); + + //pops two items and pushes their sum mod 2^256. + m_SPP[0] = m_SP[0] + m_SP[1]; + } + NEXT + + CASE(MUL) + { + ON_OP(); + updateIOGas(); + + //pops two items and pushes their product mod 2^256. + m_SPP[0] = m_SP[0] * m_SP[1]; + } + NEXT + + CASE(SUB) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] - m_SP[1]; + } + NEXT + + CASE(DIV) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[1] ? divWorkaround(m_SP[0], m_SP[1]) : 0; + } + NEXT + + CASE(SDIV) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[1] ? s2u(divWorkaround(u2s(m_SP[0]), u2s(m_SP[1]))) : 0; + --m_SP; + } + NEXT + + CASE(MOD) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[1] ? modWorkaround(m_SP[0], m_SP[1]) : 0; + } + NEXT + + CASE(SMOD) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[1] ? s2u(modWorkaround(u2s(m_SP[0]), u2s(m_SP[1]))) : 0; + } + NEXT + + CASE(NOT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = ~m_SP[0]; + } + NEXT + + CASE(LT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] < m_SP[1] ? 1 : 0; + } + NEXT + + CASE(GT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] > m_SP[1] ? 1 : 0; + } + NEXT + + CASE(SLT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = u2s(m_SP[0]) < u2s(m_SP[1]) ? 1 : 0; + } + NEXT + + CASE(SGT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = u2s(m_SP[0]) > u2s(m_SP[1]) ? 1 : 0; + } + NEXT + + CASE(EQ) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] == m_SP[1] ? 1 : 0; + } + NEXT + + CASE(ISZERO) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] ? 0 : 1; + } + NEXT + + CASE(AND) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] & m_SP[1]; + } + NEXT + + CASE(OR) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] | m_SP[1]; + } + NEXT + + CASE(XOR) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] ^ m_SP[1]; + } + NEXT + + CASE(BYTE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[0] < 32 ? (m_SP[1] >> (unsigned)(8 * (31 - m_SP[0]))) & 0xff : 0; + } + NEXT + + CASE(SHL) + { + // Pre-constantinople + if (!m_schedule->haveBitwiseShifting) + throwBadInstruction(); + + ON_OP(); + updateIOGas(); + + if (m_SP[0] >= 256) + m_SPP[0] = 0; + else + m_SPP[0] = m_SP[1] << unsigned(m_SP[0]); + } + NEXT + + CASE(SHR) + { + // Pre-constantinople + if (!m_schedule->haveBitwiseShifting) + throwBadInstruction(); + + ON_OP(); + updateIOGas(); + + if (m_SP[0] >= 256) + m_SPP[0] = 0; + else + m_SPP[0] = m_SP[1] >> unsigned(m_SP[0]); + } + NEXT + + CASE(SAR) + { + // Pre-constantinople + if (!m_schedule->haveBitwiseShifting) + throwBadInstruction(); + + ON_OP(); + updateIOGas(); + + static u256 const hibit = u256(1) << 255; + static u256 const allbits = + u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + u256 shiftee = m_SP[1]; + if (m_SP[0] >= 256) + { + if (shiftee & hibit) + m_SPP[0] = allbits; + else + m_SPP[0] = 0; + } + else + { + unsigned amount = unsigned(m_SP[0]); + m_SPP[0] = shiftee >> amount; + if (shiftee & hibit) + m_SPP[0] |= allbits << (256 - amount); + } + } + NEXT + + CASE(ADDMOD) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[2] ? u256((u512(m_SP[0]) + u512(m_SP[1])) % m_SP[2]) : 0; + } + NEXT + + CASE(MULMOD) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_SP[2] ? u256((u512(m_SP[0]) * u512(m_SP[1])) % m_SP[2]) : 0; + } + NEXT + + CASE(SIGNEXTEND) + { + ON_OP(); + updateIOGas(); + + if (m_SP[0] < 31) + { + unsigned testBit = static_cast(m_SP[0]) * 8 + 7; + u256& number = m_SP[1]; + u256 mask = ((u256(1) << testBit) - 1); + if (boost::multiprecision::bit_test(number, testBit)) + number |= ~mask; + else + number &= mask; + } + } + NEXT + +#if EIP_615 + CASE(JUMPTO) + { + ON_OP(); + updateIOGas(); + + m_PC = decodeJumpDest(m_code.data(), m_PC); + } + CONTINUE + + CASE(JUMPIF) + { + ON_OP(); + updateIOGas(); + + if (m_SP[0]) + m_PC = decodeJumpDest(m_code.data(), m_PC); + else + ++m_PC; + } + CONTINUE + + CASE(JUMPV) + { + ON_OP(); + updateIOGas(); + m_PC = decodeJumpvDest(m_code.data(), m_PC, byte(m_SP[0])); + } + CONTINUE + + CASE(JUMPSUB) + { + ON_OP(); + updateIOGas(); + *m_RP++ = m_PC++; + m_PC = decodeJumpDest(m_code.data(), m_PC); + } + CONTINUE + + CASE(JUMPSUBV) + { + ON_OP(); + updateIOGas(); + *m_RP++ = m_PC; + m_PC = decodeJumpvDest(m_code.data(), m_PC, byte(m_SP[0])); + } + CONTINUE + + CASE(RETURNSUB) + { + ON_OP(); + updateIOGas(); + + m_PC = *m_RP--; + } + NEXT + + CASE(BEGINSUB) + { + ON_OP(); + updateIOGas(); + } + NEXT + + + CASE(BEGINDATA) + { + ON_OP(); + updateIOGas(); + } + NEXT + + CASE(GETLOCAL) + { + ON_OP(); + updateIOGas(); + } + NEXT + + CASE(PUTLOCAL) + { + ON_OP(); + updateIOGas(); + } + NEXT + +#else + CASE(JUMPTO) + CASE(JUMPIF) + CASE(JUMPV) + CASE(JUMPSUB) + CASE(JUMPSUBV) + CASE(RETURNSUB) + CASE(BEGINSUB) + CASE(BEGINDATA) + CASE(GETLOCAL) + CASE(PUTLOCAL) + { + throwBadInstruction(); + } + CONTINUE +#endif + +#if EIP_616 + + CASE(XADD) + { + ON_OP(); + updateIOGas(); + + xadd(simdType()); + } + CONTINUE + + CASE(XMUL) + { + ON_OP(); + updateIOGas(); + + xmul(simdType()); + } + CONTINUE + + CASE(XSUB) + { + ON_OP(); + updateIOGas(); + + xsub(simdType()); + } + CONTINUE + + CASE(XDIV) + { + ON_OP(); + updateIOGas(); + + xdiv(simdType()); + } + CONTINUE + + CASE(XSDIV) + { + ON_OP(); + updateIOGas(); + + xsdiv(simdType()); + } + CONTINUE + + CASE(XMOD) + { + ON_OP(); + updateIOGas(); + + xmod(simdType()); + } + CONTINUE + + CASE(XSMOD) + { + ON_OP(); + updateIOGas(); + + xsmod(simdType()); + } + CONTINUE + + CASE(XLT) + { + ON_OP(); + updateIOGas(); + + xlt(simdType()); + } + CONTINUE + + CASE(XGT) + { + ON_OP(); + updateIOGas(); + + xgt(simdType()); + } + CONTINUE + + CASE(XSLT) + { + ON_OP(); + updateIOGas(); + + xslt(simdType()); + } + CONTINUE + + CASE(XSGT) + { + ON_OP(); + updateIOGas(); + + xsgt(simdType()); + } + CONTINUE + + CASE(XEQ) + { + ON_OP(); + updateIOGas(); + + xeq(simdType()); + } + CONTINUE + + CASE(XISZERO) + { + ON_OP(); + updateIOGas(); + + xzero(simdType()); + } + CONTINUE + + CASE(XAND) + { + ON_OP(); + updateIOGas(); + + xand(simdType()); + } + CONTINUE + + CASE(XOOR) + { + ON_OP(); + updateIOGas(); + + xoor(simdType()); + } + CONTINUE + + CASE(XXOR) + { + ON_OP(); + updateIOGas(); + + xxor(simdType()); + } + CONTINUE + + CASE(XNOT) + { + ON_OP(); + updateIOGas(); + + xnot(simdType()); + } + CONTINUE + + CASE(XSHL) + { + ON_OP(); + updateIOGas(); + + xshl(simdType()); + } + CONTINUE + + CASE(XSHR) + { + ON_OP(); + updateIOGas(); + + xshr(simdType()); + } + CONTINUE + + CASE(XSAR) + { + ON_OP(); + updateIOGas(); + + xsar(simdType()); + } + CONTINUE + + CASE(XROL) + { + ON_OP(); + updateIOGas(); + + xrol(simdType()); + } + CONTINUE + + CASE(XROR) + { + ON_OP(); + updateIOGas(); + + xror(simdType()); + } + CONTINUE + + CASE(XMLOAD) + { + updateMem(toInt63(m_SP[0]) + 32); + ON_OP(); + updateIOGas(); + + xmload(simdType()); + } + CONTINUE + + CASE(XMSTORE) + { + updateMem(toInt63(m_SP[0]) + 32); + ON_OP(); + updateIOGas(); + + xmstore(simdType()); + } + CONTINUE + + CASE(XSLOAD) + { + m_runGas = toInt63(m_schedule->sloadGas); + ON_OP(); + updateIOGas(); + + xsload(simdType()); + } + CONTINUE + + CASE(XSSTORE) + { + if (m_ext->staticCall) + throwDisallowedStateChange(); + + updateSSGas(); + ON_OP(); + updateIOGas(); + + xsstore(simdType()); + } + CONTINUE + + CASE(XVTOWIDE) + { + ON_OP(); + updateIOGas(); + + xvtowide(simdType()); + } + CONTINUE + + CASE(XWIDETOV) + { + ON_OP(); + updateIOGas(); + + xwidetov(simdType()); + } + CONTINUE + + CASE(XPUSH) + { + ON_OP(); + updateIOGas(); + + xpush(simdType()); + } + CONTINUE + + CASE(XPUT) + { + ON_OP(); + updateIOGas(); + + uint8_t b = ++m_PC; + uint8_t c = ++m_PC; + xput(m_code[b], m_code[c]); + ++m_PC; + } + CONTINUE + + CASE(XGET) + { + ON_OP(); + updateIOGas(); + + uint8_t b = ++m_PC; + uint8_t c = ++m_PC; + xget(m_code[b], m_code[c]); + ++m_PC; + } + CONTINUE + + CASE(XSWIZZLE) + { + ON_OP(); + updateIOGas(); + + xswizzle(simdType()); + } + CONTINUE + + CASE(XSHUFFLE) + { + ON_OP(); + updateIOGas(); + + xshuffle(simdType()); + } + CONTINUE +#else + CASE(XADD) + CASE(XMUL) + CASE(XSUB) + CASE(XDIV) + CASE(XSDIV) + CASE(XMOD) + CASE(XSMOD) + CASE(XLT) + CASE(XGT) + CASE(XSLT) + CASE(XSGT) + CASE(XEQ) + CASE(XISZERO) + CASE(XAND) + CASE(XOOR) + CASE(XXOR) + CASE(XNOT) + CASE(XSHL) + CASE(XSHR) + CASE(XSAR) + CASE(XROL) + CASE(XROR) + CASE(XMLOAD) + CASE(XMSTORE) + CASE(XSLOAD) + CASE(XSSTORE) + CASE(XVTOWIDE) + CASE(XWIDETOV) + CASE(XPUSH) + CASE(XPUT) + CASE(XGET) + CASE(XSWIZZLE) + CASE(XSHUFFLE) + { + throwBadInstruction(); + } + CONTINUE +#endif + + CASE(ADDRESS) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = fromAddress(m_ext->myAddress); + } + NEXT + + CASE(ORIGIN) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = fromAddress(m_ext->origin); + } + NEXT + + CASE(BALANCE) + { + m_runGas = toInt63(m_schedule->balanceGas); + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->balance(asAddress(m_SP[0])); + } + NEXT + + + CASE(CALLER) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = fromAddress(m_ext->caller); + } + NEXT + + CASE(CALLVALUE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->value; + } + NEXT + + + CASE(CALLDATALOAD) + { + ON_OP(); + updateIOGas(); + + if (u512(m_SP[0]) + 31 < m_ext->data.size()) + m_SP[0] = (u256)*(h256 const*)(m_ext->data.data() + (size_t)m_SP[0]); + else if (m_SP[0] >= m_ext->data.size()) + m_SP[0] = u256(0); + else + { h256 r; + for (uint64_t i = (uint64_t)m_SP[0], e = (uint64_t)m_SP[0] + (uint64_t)32, j = 0; i < e; ++i, ++j) + r[j] = i < m_ext->data.size() ? m_ext->data[i] : 0; + m_SP[0] = (u256)r; + }; + } + NEXT + + + CASE(CALLDATASIZE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->data.size(); + } + NEXT + + CASE(RETURNDATASIZE) + { + if (!m_schedule->haveReturnData) + throwBadInstruction(); + + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_returnData.size(); + } + NEXT + + CASE(CODESIZE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->code.size(); + } + NEXT + + CASE(EXTCODESIZE) + { + m_runGas = toInt63(m_schedule->extcodesizeGas); + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->codeSizeAt(asAddress(m_SP[0])); + } + NEXT + + CASE(CALLDATACOPY) + { + ON_OP(); + m_copyMemSize = toInt63(m_SP[2]); + updateMem(memNeed(m_SP[0], m_SP[2])); + updateIOGas(); + + copyDataToMemory(m_ext->data, m_SP); + } + NEXT + + CASE(RETURNDATACOPY) + { + ON_OP(); + if (!m_schedule->haveReturnData) + throwBadInstruction(); + bigint const endOfAccess = bigint(m_SP[1]) + bigint(m_SP[2]); + if (m_returnData.size() < endOfAccess) + throwBufferOverrun(endOfAccess); + + m_copyMemSize = toInt63(m_SP[2]); + updateMem(memNeed(m_SP[0], m_SP[2])); + updateIOGas(); + + copyDataToMemory(&m_returnData, m_SP); + } + NEXT + + CASE(CODECOPY) + { + ON_OP(); + m_copyMemSize = toInt63(m_SP[2]); + updateMem(memNeed(m_SP[0], m_SP[2])); + updateIOGas(); + + copyDataToMemory(&m_ext->code, m_SP); + } + NEXT + + CASE(EXTCODECOPY) + { + ON_OP(); + m_runGas = toInt63(m_schedule->extcodecopyGas); + m_copyMemSize = toInt63(m_SP[3]); + updateMem(memNeed(m_SP[1], m_SP[3])); + updateIOGas(); + + Address a = asAddress(m_SP[0]); + copyDataToMemory(&m_ext->codeAt(a), m_SP + 1); + } + NEXT + + + CASE(GASPRICE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->gasPrice; + } + NEXT + + CASE(BLOCKHASH) + { + ON_OP(); + m_runGas = toInt63(m_schedule->blockhashGas); + updateIOGas(); + + m_SPP[0] = (u256)m_ext->blockHash(m_SP[0]); + } + NEXT + + CASE(COINBASE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = (u160)m_ext->envInfo().author(); + } + NEXT + + CASE(TIMESTAMP) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->envInfo().timestamp(); + } + NEXT + + CASE(NUMBER) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->envInfo().number(); + } + NEXT + + CASE(DIFFICULTY) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->envInfo().difficulty(); + } + NEXT + + CASE(GASLIMIT) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->envInfo().gasLimit(); + } + NEXT + + CASE(POP) + { + ON_OP(); + updateIOGas(); + + --m_SP; + } + NEXT + + CASE(PUSHC) + { +#if EVM_USE_CONSTANT_POOL + ON_OP(); + updateIOGas(); + + // get val at two-byte offset into const pool and advance pc by one-byte remainder + TRACE_OP(2, m_PC, m_OP); + unsigned off; + ++m_PC; + off = m_code[m_PC++] << 8; + off |= m_code[m_PC++]; + m_PC += m_code[m_PC]; + m_SPP[0] = m_pool[off]; + TRACE_VAL(2, "Retrieved pooled const", m_SPP[0]); +#else + throwBadInstruction(); +#endif + } + CONTINUE + + CASE(PUSH1) + { + ON_OP(); + updateIOGas(); + ++m_PC; + m_SPP[0] = m_code[m_PC]; + ++m_PC; + } + CONTINUE + + CASE(PUSH2) + CASE(PUSH3) + CASE(PUSH4) + CASE(PUSH5) + CASE(PUSH6) + CASE(PUSH7) + CASE(PUSH8) + CASE(PUSH9) + CASE(PUSH10) + CASE(PUSH11) + CASE(PUSH12) + CASE(PUSH13) + CASE(PUSH14) + CASE(PUSH15) + CASE(PUSH16) + CASE(PUSH17) + CASE(PUSH18) + CASE(PUSH19) + CASE(PUSH20) + CASE(PUSH21) + CASE(PUSH22) + CASE(PUSH23) + CASE(PUSH24) + CASE(PUSH25) + CASE(PUSH26) + CASE(PUSH27) + CASE(PUSH28) + CASE(PUSH29) + CASE(PUSH30) + CASE(PUSH31) + CASE(PUSH32) + { + ON_OP(); + updateIOGas(); + + int numBytes = (int)m_OP - (int)Instruction::PUSH1 + 1; + m_SPP[0] = 0; + // Construct a number out of PUSH bytes. + // This requires the code has been copied and extended by 32 zero + // bytes to handle "out of code" push data here. + for (++m_PC; numBytes--; ++m_PC) + m_SPP[0] = (m_SPP[0] << 8) | m_code[m_PC]; + } + CONTINUE + + CASE(JUMP) + { + ON_OP(); + updateIOGas(); + m_PC = verifyJumpDest(m_SP[0]); + } + CONTINUE + + CASE(JUMPI) + { + ON_OP(); + updateIOGas(); + if (m_SP[1]) + m_PC = verifyJumpDest(m_SP[0]); + else + ++m_PC; + } + CONTINUE + + CASE(JUMPC) + { +#if EVM_REPLACE_CONST_JUMP + ON_OP(); + updateIOGas(); + + m_PC = uint64_t(m_SP[0]); +#else + throwBadInstruction(); +#endif + } + CONTINUE + + CASE(JUMPCI) + { +#if EVM_REPLACE_CONST_JUMP + ON_OP(); + updateIOGas(); + + if (m_SP[1]) + m_PC = uint64_t(m_SP[0]); + else + ++m_PC; +#else + throwBadInstruction(); +#endif + } + CONTINUE + + CASE(DUP1) + CASE(DUP2) + CASE(DUP3) + CASE(DUP4) + CASE(DUP5) + CASE(DUP6) + CASE(DUP7) + CASE(DUP8) + CASE(DUP9) + CASE(DUP10) + CASE(DUP11) + CASE(DUP12) + CASE(DUP13) + CASE(DUP14) + CASE(DUP15) + CASE(DUP16) + { + ON_OP(); + updateIOGas(); + + unsigned n = (unsigned)m_OP - (unsigned)Instruction::DUP1; + *(uint64_t*)m_SPP = *(uint64_t*)(m_SP + n); + + // the stack slot being copied into may no longer hold a u256 + // so we construct a new one in the memory, rather than assign + new(m_SPP) u256(m_SP[n]); + } + NEXT + + + CASE(SWAP1) + CASE(SWAP2) + CASE(SWAP3) + CASE(SWAP4) + CASE(SWAP5) + CASE(SWAP6) + CASE(SWAP7) + CASE(SWAP8) + CASE(SWAP9) + CASE(SWAP10) + CASE(SWAP11) + CASE(SWAP12) + CASE(SWAP13) + CASE(SWAP14) + CASE(SWAP15) + CASE(SWAP16) + { + ON_OP(); + updateIOGas(); + + unsigned n = (unsigned)m_OP - (unsigned)Instruction::SWAP1 + 1; + std::swap(m_SP[0], m_SP[n]); + } + NEXT + + + CASE(SLOAD) + { + m_runGas = toInt63(m_schedule->sloadGas); + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_ext->store(m_SP[0]); + } + NEXT + + CASE(SSTORE) + { + ON_OP(); + if (m_ext->staticCall) + throwDisallowedStateChange(); + + updateSSGas(); + updateIOGas(); + + m_ext->setStore(m_SP[0], m_SP[1]); + } + NEXT + + CASE(PC) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_PC; + } + NEXT + + CASE(MSIZE) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_mem.size(); + } + NEXT + + CASE(GAS) + { + ON_OP(); + updateIOGas(); + + m_SPP[0] = m_io_gas; + } + NEXT + + CASE(JUMPDEST) + { + m_runGas = 1; + ON_OP(); + updateIOGas(); + } + NEXT + + CASE(INVALID) + DEFAULT + { + throwBadInstruction(); + } + } + WHILE_CASES +} diff --git a/libevm/VM.h b/libevm/VM.h new file mode 100644 index 000000000..92d7002b1 --- /dev/null +++ b/libevm/VM.h @@ -0,0 +1,251 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** @file VM.h + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "VMFace.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ + +// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. +// Currently we just pull out the right (low-order in BE) 160-bits. +inline Address asAddress(u256 _item) +{ + return right160(h256(_item)); +} + +inline u256 fromAddress(Address _a) +{ + return (u160)_a; +} + + +struct InstructionMetric +{ + Tier gasPriceTier; + int args; + int ret; +}; + +/** + */ +class VM: public VMFace +{ +public: + virtual owning_bytes_ref exec(u256& _io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; + +#if EIP_615 + // invalid code will throw an exeption + void validate(ExtVMFace& _ext); + void validateSubroutine(uint64_t _PC, uint64_t* _rp, u256* _sp); +#endif + + bytes const& memory() const { return m_mem; } + u256s stack() const { + u256s stack(m_SP, m_stackEnd); + reverse(stack.begin(), stack.end()); + return stack; + }; + +private: + + u256* m_io_gas_p = 0; + uint64_t m_io_gas = 0; + ExtVMFace* m_ext = 0; + OnOpFunc m_onOp; + + static std::array c_metrics; + static void initMetrics(); + static u256 exp256(u256 _base, u256 _exponent); + void copyCode(int); + typedef void (VM::*MemFnPtr)(); + MemFnPtr m_bounce = 0; + MemFnPtr m_onFail = 0; + uint64_t m_nSteps = 0; + EVMSchedule const* m_schedule = nullptr; + + // return bytes + owning_bytes_ref m_output; + + // space for memory + bytes m_mem; + + // space for code + bytes m_code; + + /// RETURNDATA buffer for memory returned from direct subcalls. + bytes m_returnData; + + // space for data stack, grows towards smaller addresses from the end + u256 m_stack[1024]; + u256 *m_stackEnd = &m_stack[1024]; + size_t stackSize() { return m_stackEnd - m_SP; } + +#if EIP_615 + // space for return stack + uint64_t m_return[1024]; + + // mark PCs with frame size to detect cycles and stack mismatch + std::vector m_frameSize; +#endif + + // constant pool + std::vector m_pool; + + // interpreter state + Instruction m_OP; // current operation + uint64_t m_PC = 0; // program counter + u256* m_SP = m_stackEnd; // stack pointer + u256* m_SPP = m_SP; // stack pointer prime (next SP) +#if EIP_615 + uint64_t* m_RP = m_return - 1; // return pointer +#endif + + // metering and memory state + uint64_t m_runGas = 0; + uint64_t m_newMemSize = 0; + uint64_t m_copyMemSize = 0; + + // initialize interpreter + void initEntry(); + void optimize(); + + // interpreter loop & switch + void interpretCases(); + + // interpreter cases that call out + void caseCreate(); + bool caseCallSetup(CallParameters*, bytesRef& o_output); + void caseCall(); + + void copyDataToMemory(bytesConstRef _data, u256*_sp); + uint64_t memNeed(u256 _offset, u256 _size); + + void throwOutOfGas(); + void throwBadInstruction(); + void throwBadJumpDestination(); + void throwBadStack(unsigned _removed, unsigned _added); + void throwRevertInstruction(owning_bytes_ref&& _output); + void throwDisallowedStateChange(); + void throwBufferOverrun(bigint const& _enfOfAccess); + + void reportStackUse(); + + std::vector m_beginSubs; + std::vector m_jumpDests; + int64_t verifyJumpDest(u256 const& _dest, bool _throw = true); + + int poolConstant(const u256&); + + void onOperation(); + void adjustStack(unsigned _removed, unsigned _added); + uint64_t gasForMem(u512 _size); + void updateSSGas(); + void updateIOGas(); + void updateGas(); + void updateMem(uint64_t _newMem); + void logGasMem(); + void fetchInstruction(); + + uint64_t decodeJumpDest(const byte* const _code, uint64_t& _pc); + uint64_t decodeJumpvDest(const byte* const _code, uint64_t& _pc, byte _voff); + + template uint64_t toInt63(T v) + { + // check for overflow + if (v > 0x7FFFFFFFFFFFFFFF) + throwOutOfGas(); + uint64_t w = uint64_t(v); + return w; + } + + template uint64_t toInt15(T v) + { + // check for overflow + if (v > 0x7FFF) + throwOutOfGas(); + uint64_t w = uint64_t(v); + return w; + } + + // + // implementations of simd opcodes + // + // input bytes are the inline simd type descriptors for the operand vectors on the stack + // +#if EIP_616 + + void xadd (uint8_t); + void xmul (uint8_t); + void xsub (uint8_t); + void xdiv (uint8_t); + void xsdiv (uint8_t); + void xmod (uint8_t); + void xsmod (uint8_t); + void xlt (uint8_t); + void xslt (uint8_t); + void xgt (uint8_t); + void xsgt (uint8_t); + void xeq (uint8_t); + void xzero (uint8_t); + void xand (uint8_t); + void xoor (uint8_t); + void xxor (uint8_t); + void xnot (uint8_t); + void xshr (uint8_t); + void xsar (uint8_t); + void xshl (uint8_t); + void xrol (uint8_t); + void xror (uint8_t); + void xmload (uint8_t); + void xmstore (uint8_t); + void xsload (uint8_t); + void xsstore (uint8_t); + void xvtowide(uint8_t); + void xwidetov(uint8_t); + void xpush (uint8_t); + void xput (uint8_t, uint8_t); + void xget (uint8_t, uint8_t); + void xswizzle(uint8_t); + void xshuffle(uint8_t); + + u256 vtow(uint8_t _b, const u256& _in); + void wtov(uint8_t _b, u256 _in, u256& _o_out); + + uint8_t simdType() + { + uint8_t nt = m_code[++m_PC]; // advance PC and get simd type from code + ++m_PC; // advance PC to next opcode, ready to continue + return nt; + } + +#endif +}; + +} +} diff --git a/libevm/VMCalls.cpp b/libevm/VMCalls.cpp new file mode 100644 index 000000000..976c3608b --- /dev/null +++ b/libevm/VMCalls.cpp @@ -0,0 +1,303 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + + +#include +#include "VMConfig.h" +#include "VM.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + + + +void VM::copyDataToMemory(bytesConstRef _data, u256*_sp) +{ + auto offset = static_cast(_sp[0]); + s512 bigIndex = _sp[1]; + auto index = static_cast(bigIndex); + auto size = static_cast(_sp[2]); + + size_t sizeToBeCopied = bigIndex + size > _data.size() ? _data.size() < bigIndex ? 0 : _data.size() - index : size; + + if (sizeToBeCopied > 0) + std::memcpy(m_mem.data() + offset, _data.data() + index, sizeToBeCopied); + if (size > sizeToBeCopied) + std::memset(m_mem.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); +} + + +// consolidate exception throws to avoid spraying boost code all over interpreter + +void VM::throwOutOfGas() +{ + // disabled to prevent duplicate steps in vmtrace log + //if (m_onFail) + // (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(OutOfGas()); +} + +void VM::throwBadInstruction() +{ + // disabled to prevent duplicate steps in vmtrace log + //if (m_onFail) + // (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(BadInstruction()); +} + +void VM::throwBadJumpDestination() +{ + // disabled to prevent duplicate steps in vmtrace log + //if (m_onFail) + // (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(BadJumpDestination()); +} + +void VM::throwDisallowedStateChange() +{ + // disabled to prevent duplicate steps in vmtrace log + //if (m_onFail) + // (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(DisallowedStateChange()); +} + +// throwBadStack is called from fetchInstruction() -> adjustStack() +// its the only exception that can happen before ON_OP() log is done for an opcode case in VM.cpp +// so the call to m_onFail is needed here +void VM::throwBadStack(unsigned _removed, unsigned _added) +{ + bigint size = m_stackEnd - m_SPP; + if (size < _removed) + { + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_removed, size)); + } + else + { + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_added - _removed), size)); + } +} + +void VM::throwRevertInstruction(owning_bytes_ref&& _output) +{ + // We can't use BOOST_THROW_EXCEPTION here because it makes a copy of exception inside and RevertInstruction has no copy constructor + throw RevertInstruction(move(_output)); +} + +void VM::throwBufferOverrun(bigint const& _endOfAccess) +{ + // todo: disable this m_onFail, may result in duplicate log step in the trace + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(BufferOverrun() << RequirementError(_endOfAccess, bigint(m_returnData.size()))); +} + +int64_t VM::verifyJumpDest(u256 const& _dest, bool _throw) +{ + // check for overflow + if (_dest <= 0x7FFFFFFFFFFFFFFF) { + + // check for within bounds and to a jump destination + // use binary search of array because hashtable collisions are exploitable + uint64_t pc = uint64_t(_dest); + if (std::binary_search(m_jumpDests.begin(), m_jumpDests.end(), pc)) + return pc; + } + if (_throw) + throwBadJumpDestination(); + return -1; +} + + +// +// interpreter cases that call out +// + +void VM::caseCreate() +{ + m_bounce = &VM::interpretCases; + m_runGas = toInt63(m_schedule->createGas); + + // Collect arguments. + u256 endowment = m_SP[0]; + u256 salt; + u256 initOff; + u256 initSize; + + if (m_OP == Instruction::CREATE) + { + initOff = m_SP[1]; + initSize = m_SP[2]; + } + else + { + salt = m_SP[1]; + initOff = m_SP[2]; + initSize = m_SP[3]; + } + + updateMem(memNeed(initOff, initSize)); + updateIOGas(); + + // Clear the return data buffer. This will not free the memory. + m_returnData.clear(); + + if (m_ext->balance(m_ext->myAddress) >= endowment && m_ext->depth < 1024) + { + *m_io_gas_p = m_io_gas; + u256 createGas = *m_io_gas_p; + if (!m_schedule->staticCallDepthLimit()) + createGas -= createGas / 64; + u256 gas = createGas; + + // Get init code. Casts are safe because the memory cost has been paid. + auto off = static_cast(initOff); + auto size = static_cast(initSize); + bytesConstRef initCode{m_mem.data() + off, size}; + + + h160 addr; + owning_bytes_ref output; + std::tie(addr, output) = m_ext->create(endowment, gas, initCode, m_OP, salt, m_onOp); + m_SPP[0] = (u160)addr; // Convert address to integer. + m_returnData = output.toBytes(); + + *m_io_gas_p -= (createGas - gas); + m_io_gas = uint64_t(*m_io_gas_p); + } + else + m_SPP[0] = 0; + ++m_PC; +} + +void VM::caseCall() +{ + m_bounce = &VM::interpretCases; + + // TODO: Please check if that does not actually increases the stack size. + // That was the case before. + unique_ptr callParams(new CallParameters()); + + // Clear the return data buffer. This will not free the memory. + m_returnData.clear(); + + bytesRef output; + if (caseCallSetup(callParams.get(), output)) + { + bool success = false; + owning_bytes_ref outputRef; + std::tie(success, outputRef) = m_ext->call(*callParams); + outputRef.copyTo(output); + + // Here we have 2 options: + // 1. Keep the whole returned memory buffer (owning_bytes_ref): + // higher memory footprint, no memory copy. + // 2. Copy only the return data from the returned memory buffer: + // minimal memory footprint, additional memory copy. + // Option 2 used: + m_returnData = outputRef.toBytes(); + + m_SPP[0] = success ? 1 : 0; + } + else + m_SPP[0] = 0; + m_io_gas += uint64_t(callParams->gas); + ++m_PC; +} + +bool VM::caseCallSetup(CallParameters *callParams, bytesRef& o_output) +{ + // Make sure the params were properly initialized. + assert(callParams->valueTransfer == 0); + assert(callParams->apparentValue == 0); + + m_runGas = toInt63(m_schedule->callGas); + + callParams->staticCall = (m_OP == Instruction::STATICCALL || m_ext->staticCall); + + bool const haveValueArg = m_OP == Instruction::CALL || m_OP == Instruction::CALLCODE; + + Address destinationAddr = asAddress(m_SP[1]); + if (m_OP == Instruction::CALL && !m_ext->exists(destinationAddr)) + if (m_SP[2] > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) + m_runGas += toInt63(m_schedule->callNewAccountGas); + + if (haveValueArg && m_SP[2] > 0) + m_runGas += toInt63(m_schedule->callValueTransferGas); + + size_t const sizesOffset = haveValueArg ? 3 : 2; + u256 inputOffset = m_SP[sizesOffset]; + u256 inputSize = m_SP[sizesOffset + 1]; + u256 outputOffset = m_SP[sizesOffset + 2]; + u256 outputSize = m_SP[sizesOffset + 3]; + uint64_t inputMemNeed = memNeed(inputOffset, inputSize); + uint64_t outputMemNeed = memNeed(outputOffset, outputSize); + + m_newMemSize = std::max(inputMemNeed, outputMemNeed); + updateMem(m_newMemSize); + updateIOGas(); + + // "Static" costs already applied. Calculate call gas. + if (m_schedule->staticCallDepthLimit()) + { + // With static call depth limit we just charge the provided gas amount. + callParams->gas = m_SP[0]; + } + else + { + // Apply "all but one 64th" rule. + u256 maxAllowedCallGas = m_io_gas - m_io_gas / 64; + callParams->gas = std::min(m_SP[0], maxAllowedCallGas); + } + + m_runGas = toInt63(callParams->gas); + updateIOGas(); + + if (haveValueArg && m_SP[2] > 0) + callParams->gas += m_schedule->callStipend; + + callParams->codeAddress = destinationAddr; + + if (haveValueArg) + { + callParams->valueTransfer = m_SP[2]; + callParams->apparentValue = m_SP[2]; + } + else if (m_OP == Instruction::DELEGATECALL) + // Forward VALUE. + callParams->apparentValue = m_ext->value; + + uint64_t inOff = (uint64_t)inputOffset; + uint64_t inSize = (uint64_t)inputSize; + uint64_t outOff = (uint64_t)outputOffset; + uint64_t outSize = (uint64_t)outputSize; + + if (m_ext->balance(m_ext->myAddress) >= callParams->valueTransfer && m_ext->depth < 1024) + { + callParams->onOp = m_onOp; + callParams->senderAddress = m_OP == Instruction::DELEGATECALL ? m_ext->caller : m_ext->myAddress; + callParams->receiveAddress = (m_OP == Instruction::CALL || m_OP == Instruction::STATICCALL) ? callParams->codeAddress : m_ext->myAddress; + callParams->data = bytesConstRef(m_mem.data() + inOff, inSize); + o_output = bytesRef(m_mem.data() + outOff, outSize); + return true; + } + return false; +} + diff --git a/libevm/VMConfig.h b/libevm/VMConfig.h new file mode 100644 index 000000000..c8c709e82 --- /dev/null +++ b/libevm/VMConfig.h @@ -0,0 +1,433 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +#pragma once + +namespace dev +{ +namespace eth +{ + +/////////////////////////////////////////////////////////////////////////////// +// +// interpreter configuration macros for development, optimizations and tracing +// +// EIP_615 - subroutines and static jumps +// EIP_616 - SIMD +// +// EVM_OPTIMIZE - all optimizations off when false (TO DO - MAKE DYNAMIC) +// +// EVM_SWITCH_DISPATCH - dispatch via loop and switch +// EVM_JUMP_DISPATCH - dispatch via a jump table - available only on GCC +// +// EVM_USE_CONSTANT_POOL - constants unpacked and ready to assign to stack +// +// EVM_REPLACE_CONST_JUMP - pre-verified jumps to save runtime lookup +// +// EVM_TRACE - provides various levels of tracing + +#ifndef EIP_615 + #define EIP_615 false +#endif + +#ifndef EIP_616 + #define EIP_616 false +#endif + +#ifndef EVM_JUMP_DISPATCH + #ifdef __GNUC__ + #define EVM_JUMP_DISPATCH true + #else + #define EVM_JUMP_DISPATCH false + #endif +#endif +#if EVM_JUMP_DISPATCH + #ifndef __GNUC__ + #error "address of label extension available only on Gnu" + #endif +#else + #define EVM_SWITCH_DISPATCH true +#endif + +#ifndef EVM_OPTIMIZE + #define EVM_OPTIMIZE true +#endif +#if EVM_OPTIMIZE + #define EVM_REPLACE_CONST_JUMP true + #define EVM_USE_CONSTANT_POOL true + #define EVM_DO_FIRST_PASS_OPTIMIZATION ( \ + EVM_REPLACE_CONST_JUMP || \ + EVM_USE_CONSTANT_POOL \ + ) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// set EVM_TRACE to 3, 2, 1, or 0 for lots to no tracing to cerr +// +#ifndef EVM_TRACE + #define EVM_TRACE 0 +#endif +#if EVM_TRACE > 0 + + #undef ON_OP + #if EVM_TRACE > 2 + #define ON_OP() \ + (cerr <<"### "<< ++m_nSteps <<": "<< m_PC <<" "<< instructionInfo(m_OP).name <>> "<< (pc) <<" "<< instructionInfo(op).name < 0 + #define THROW_EXCEPTION(X) \ + ((cerr << "!!! EVM EXCEPTION " << (X).what() << endl), BOOST_THROW_EXCEPTION(X)) + #else + #define THROW_EXCEPTION(X) BOOST_THROW_EXCEPTION(X) + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// build a simple loop-and-switch interpreter +// +#if EVM_SWITCH_DISPATCH + + #define INIT_CASES + #define DO_CASES for(;;) { fetchInstruction(); switch(m_OP) { + #define CASE(name) case Instruction::name: + #define NEXT ++m_PC; break; + #define CONTINUE continue; + #define BREAK return; + #define DEFAULT default: + #define WHILE_CASES } } + + +/////////////////////////////////////////////////////////////////////////////// +// +// build an indirect-threaded interpreter using a jump table of +// label addresses (a gcc extension) +// +#elif EVM_JUMP_DISPATCH + + #define INIT_CASES \ + \ + static const void * const jumpTable[256] = \ + { \ + &&STOP, /* 00 */ \ + &&ADD, \ + &&MUL, \ + &&SUB, \ + &&DIV, \ + &&SDIV, \ + &&MOD, \ + &&SMOD, \ + &&ADDMOD, \ + &&MULMOD, \ + &&EXP, \ + &&SIGNEXTEND, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &<, /* 10, */ \ + &>, \ + &&SLT, \ + &&SGT, \ + &&EQ, \ + &&ISZERO, \ + &&AND, \ + &&OR, \ + &&XOR, \ + &&NOT, \ + &&BYTE, \ + &&SHL, \ + &&SHR, \ + &&SAR, \ + &&INVALID, \ + &&INVALID, \ + &&SHA3, /* 20, */ \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&ADDRESS, /* 30, */ \ + &&BALANCE, \ + &&ORIGIN, \ + &&CALLER, \ + &&CALLVALUE, \ + &&CALLDATALOAD, \ + &&CALLDATASIZE, \ + &&CALLDATACOPY, \ + &&CODESIZE, \ + &&CODECOPY, \ + &&GASPRICE, \ + &&EXTCODESIZE, \ + &&EXTCODECOPY, \ + &&RETURNDATASIZE, \ + &&RETURNDATACOPY, \ + &&INVALID, \ + &&BLOCKHASH, /* 40, */ \ + &&COINBASE, \ + &&TIMESTAMP, \ + &&NUMBER, \ + &&DIFFICULTY, \ + &&GASLIMIT, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&POP, /* 50, */ \ + &&MLOAD, \ + &&MSTORE, \ + &&MSTORE8, \ + &&SLOAD, \ + &&SSTORE, \ + &&JUMP, \ + &&JUMPI, \ + &&PC, \ + &&MSIZE, \ + &&GAS, \ + &&JUMPDEST, \ + &&BEGINDATA, \ + &&BEGINSUB, \ + &&INVALID, \ + &&INVALID, \ + &&PUSH1, /* 60, */ \ + &&PUSH2, \ + &&PUSH3, \ + &&PUSH4, \ + &&PUSH5, \ + &&PUSH6, \ + &&PUSH7, \ + &&PUSH8, \ + &&PUSH9, \ + &&PUSH10, \ + &&PUSH11, \ + &&PUSH12, \ + &&PUSH13, \ + &&PUSH14, \ + &&PUSH15, \ + &&PUSH16, \ + &&PUSH17, /* 70, */ \ + &&PUSH18, \ + &&PUSH19, \ + &&PUSH20, \ + &&PUSH21, \ + &&PUSH22, \ + &&PUSH23, \ + &&PUSH24, \ + &&PUSH25, \ + &&PUSH26, \ + &&PUSH27, \ + &&PUSH28, \ + &&PUSH29, \ + &&PUSH30, \ + &&PUSH31, \ + &&PUSH32, \ + &&DUP1, /* 80, */ \ + &&DUP2, \ + &&DUP3, \ + &&DUP4, \ + &&DUP5, \ + &&DUP6, \ + &&DUP7, \ + &&DUP8, \ + &&DUP9, \ + &&DUP10, \ + &&DUP11, \ + &&DUP12, \ + &&DUP13, \ + &&DUP14, \ + &&DUP15, \ + &&DUP16, \ + &&SWAP1, /* 90, */ \ + &&SWAP2, \ + &&SWAP3, \ + &&SWAP4, \ + &&SWAP5, \ + &&SWAP6, \ + &&SWAP7, \ + &&SWAP8, \ + &&SWAP9, \ + &&SWAP10, \ + &&SWAP11, \ + &&SWAP12, \ + &&SWAP13, \ + &&SWAP14, \ + &&SWAP15, \ + &&SWAP16, \ + &&LOG0, /* A0, */ \ + &&LOG1, \ + &&LOG2, \ + &&LOG3, \ + &&LOG4, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&PUSHC, \ + &&JUMPC, \ + &&JUMPCI, \ + &&INVALID, \ + &&JUMPTO, /* B0, */ \ + &&JUMPIF, \ + &&JUMPSUB, \ + &&JUMPV, \ + &&JUMPSUBV, \ + &&BEGINSUB, \ + &&BEGINDATA, \ + &&RETURNSUB, \ + &&PUTLOCAL, \ + &&GETLOCAL, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, /* C0, */ \ + &&XADD, \ + &&XMUL, \ + &&XSUB, \ + &&XDIV, \ + &&XSDIV, \ + &&XMOD, \ + &&XSMOD, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&XLT, /* D0 */ \ + &&XGT, \ + &&XSLT, \ + &&XSGT, \ + &&XEQ, \ + &&XISZERO, \ + &&XAND, \ + &&XOOR, \ + &&XXOR, \ + &&XNOT, \ + &&INVALID, \ + &&XSHL, \ + &&XSHR, \ + &&XSAR, \ + &&XROL, \ + &&XROR, \ + &&XPUSH, /* E0, */ \ + &&XMLOAD, \ + &&XMSTORE, \ + &&INVALID, \ + &&XSLOAD, \ + &&XSSTORE, \ + &&XVTOWIDE, \ + &&XWIDETOV, \ + &&XGET, \ + &&XPUT, \ + &&XSWIZZLE, \ + &&XSHUFFLE, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&CREATE, /* F0, */ \ + &&CALL, \ + &&CALLCODE, \ + &&RETURN, \ + &&DELEGATECALL, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&INVALID, \ + &&STATICCALL, \ + &&CREATE2, \ + &&INVALID, \ + &&REVERT, \ + &&INVALID, \ + &&SUICIDE, \ + }; + + #define DO_CASES fetchInstruction(); goto *jumpTable[(int)m_OP]; + #define CASE(name) name: + #define NEXT ++m_PC; fetchInstruction(); goto *jumpTable[(int)m_OP]; + #define CONTINUE fetchInstruction(); goto *jumpTable[(int)m_OP]; + #define BREAK return; + #define DEFAULT + #define WHILE_CASES + +#else + #error No opcode dispatch configured +#endif + +}} diff --git a/libevm/VMFace.h b/libevm/VMFace.h new file mode 100644 index 000000000..e87e501f8 --- /dev/null +++ b/libevm/VMFace.h @@ -0,0 +1,69 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +#pragma once + +#include +#include +#include "ExtVMFace.h" + +namespace dev +{ +namespace eth +{ + +struct VMException: Exception {}; +#define ETH_SIMPLE_EXCEPTION_VM(X) struct X: VMException { const char* what() const noexcept override { return #X; } } +ETH_SIMPLE_EXCEPTION_VM(BadInstruction); +ETH_SIMPLE_EXCEPTION_VM(BadJumpDestination); +ETH_SIMPLE_EXCEPTION_VM(OutOfGas); +ETH_SIMPLE_EXCEPTION_VM(OutOfStack); +ETH_SIMPLE_EXCEPTION_VM(StackUnderflow); +ETH_SIMPLE_EXCEPTION_VM(DisallowedStateChange); +ETH_SIMPLE_EXCEPTION_VM(BufferOverrun); + +struct RevertInstruction: VMException +{ + explicit RevertInstruction(owning_bytes_ref&& _output) : m_output(std::move(_output)) {} + RevertInstruction(RevertInstruction const&) = delete; + RevertInstruction(RevertInstruction&&) = default; + RevertInstruction& operator=(RevertInstruction const&) = delete; + RevertInstruction& operator=(RevertInstruction&&) = default; + + char const* what() const noexcept override { return "Revert instruction"; } + + owning_bytes_ref&& output() { return std::move(m_output); } + +private: + owning_bytes_ref m_output; +}; + + +/// EVM Virtual Machine interface +class VMFace +{ +public: + VMFace() = default; + virtual ~VMFace() = default; + VMFace(VMFace const&) = delete; + VMFace& operator=(VMFace const&) = delete; + + /// VM implementation + virtual owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) = 0; +}; + +} +} diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp new file mode 100644 index 000000000..2c44e93bd --- /dev/null +++ b/libevm/VMFactory.cpp @@ -0,0 +1,175 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ + +#include "VMFactory.h" +#include "EVMC.h" +#include "VM.h" +#include + +namespace po = boost::program_options; + +namespace dev +{ +namespace eth +{ +namespace +{ +auto g_kind = VMKind::Interpreter; + +/// A helper type to build the tabled of VM implementations. +/// +/// More readable than std::tuple. +/// Fields are not initialized to allow usage of construction with initializer lists {}. +struct VMKindTableEntry +{ + VMKind kind; + const char* name; +}; + +/// The table of available VM implementations. +/// +/// We don't use a map to avoid complex dynamic initialization. This list will never be long, +/// so linear search only to parse command line arguments is not a problem. +VMKindTableEntry vmKindsTable[] = { + {VMKind::Interpreter, "interpreter"}, +#if ETH_EVMJIT + {VMKind::JIT, "jit"}, +#endif +#if ETH_HERA + {VMKind::Hera, "hera"}, +#endif +}; +} + +void validate(boost::any& v, const std::vector& values, VMKind* /* target_type */, int) +{ + // Make sure no previous assignment to 'v' was made. + po::validators::check_first_occurrence(v); + + // Extract the first string from 'values'. If there is more than + // one string, it's an error, and exception will be thrown. + const std::string& s = po::validators::get_single_string(values); + + for (auto& entry : vmKindsTable) + { + // Try to find a match in the table of VMs. + if (s == entry.name) + { + v = entry.kind; + return; + } + } + + throw po::validation_error(po::validation_error::invalid_option_value); +} + +namespace +{ +/// The name of the program option --evmc. The boost will trim the tailing +/// space and we can reuse this variable in exception message. +const char c_evmcPrefix[] = "evmc "; + +/// The list of EVM-C options stored as pairs of (name, value). +std::vector> s_evmcOptions; + +/// The additional parser for EVM-C options. The options should look like +/// `--evmc name=value` or `--evmc=name=value`. The boost pass the strings +/// of `name=value` here. This function splits the name and value or reports +/// the syntax error if the `=` character is missing. +void parseEvmcOptions(const std::vector& _opts) +{ + for (auto& s : _opts) + { + auto separatorPos = s.find('='); + if (separatorPos == s.npos) + throw po::invalid_syntax{po::invalid_syntax::missing_parameter, c_evmcPrefix + s}; + auto name = s.substr(0, separatorPos); + auto value = s.substr(separatorPos + 1); + s_evmcOptions.emplace_back(std::move(name), std::move(value)); + } +} +} + +std::vector>& evmcOptions() noexcept +{ + return s_evmcOptions; +}; + +po::options_description vmProgramOptions(unsigned _lineLength) +{ + // It must be a static object because boost expects const char*. + static const std::string description = [] { + std::string names; + for (auto& entry : vmKindsTable) + { + if (!names.empty()) + names += ", "; + names += entry.name; + } + + return "Select VM implementation. Available options are: " + names + "."; + }(); + + po::options_description opts("VM Options", _lineLength); + auto add = opts.add_options(); + + add("vm", + po::value() + ->value_name("") + ->default_value(VMKind::Interpreter, "interpreter") + ->notifier(VMFactory::setKind), + description.data()); + + add(c_evmcPrefix, + po::value>() + ->value_name("