Merge pull request #7681 from ethereum/develop

Merge develop into develop_060
This commit is contained in:
chriseth
2019-11-11 16:42:03 +01:00
committed by GitHub
44 changed files with 1885 additions and 517 deletions
+154 -29
View File
@@ -1,34 +1,159 @@
file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
set(sources
boostTest.cpp
Common.cpp
Common.h
EVMHost.cpp
EVMHost.h
ExecutionFramework.cpp
ExecutionFramework.h
InteractiveTests.h
Metadata.cpp
Metadata.h
Options.cpp
Options.h
TestCase.cpp
TestCase.h
)
detect_stray_source_files("${sources}" ".")
file(GLOB contracts_sources "contracts/*.cpp")
file(GLOB contracts_headers "contracts/*.h")
file(GLOB libdevcore_sources "libdevcore/*.cpp")
file(GLOB libdevcore_headers "libdevcore/*.h")
file(GLOB liblangutil_sources "liblangutil/*.cpp")
file(GLOB liblangutil_headers "liblangutil/*.h")
file(GLOB libevmasm_sources "libevmasm/*.cpp")
file(GLOB libevmasm_headers "libevmasm/*.h")
file(GLOB libyul_sources "libyul/*.cpp")
file(GLOB libyul_headers "libyul/*.h")
if (LLL)
file(GLOB liblll_sources "liblll/*.cpp")
file(GLOB liblll_headers "liblll/*.h")
endif()
file(GLOB libsolidity_sources "libsolidity/*.cpp")
file(GLOB libsolidity_headers "libsolidity/*.h")
file(GLOB libsolidity_util_sources "libsolidity/util/*.cpp")
file(GLOB libsolidity_util_headers "libsolidity/util/*.h")
set(contracts_sources
contracts/AuctionRegistrar.cpp
contracts/ContractInterface.h
contracts/FixedFeeRegistrar.cpp
contracts/Wallet.cpp
)
detect_stray_source_files("${contracts_sources}" "contracts/")
add_executable(soltest ${sources} ${headers}
${contracts_sources} ${contracts_headers}
${libdevcore_sources} ${libdevcore_headers}
${liblangutil_sources} ${liblangutil_headers}
${libevmasm_sources} ${libevmasm_headers}
${libyul_sources} ${libyul_headers}
${liblll_sources} ${liblll_headers}
${libsolidity_sources} ${libsolidity_headers}
${libsolidity_util_sources} ${libsolidity_util_headers}
set(libdevcore_sources
libdevcore/Checksum.cpp
libdevcore/CommonData.cpp
libdevcore/IndentedWriter.cpp
libdevcore/IpfsHash.cpp
libdevcore/IterateReplacing.cpp
libdevcore/JSON.cpp
libdevcore/Keccak256.cpp
libdevcore/StringUtils.cpp
libdevcore/SwarmHash.cpp
libdevcore/UTF8.cpp
libdevcore/Whiskers.cpp
)
detect_stray_source_files("${libdevcore_sources}" "libdevcore/")
set(libevmasm_sources
libevmasm/Assembler.cpp
libevmasm/Optimiser.cpp
)
detect_stray_source_files("${libevmasm_sources}" "libevmasm/")
set(liblangutil_sources
liblangutil/CharStream.cpp
liblangutil/SourceLocation.cpp
)
detect_stray_source_files("${liblangutil_sources}" "liblangutil/")
if(LLL)
set (liblll_sources
liblll/Compiler.cpp
liblll/EndToEndTest.cpp
liblll/ExecutionFramework.cpp
liblll/ExecutionFramework.h
liblll/LLL_ENS.cpp
liblll/LLL_ERC20.cpp
liblll/Parser.cpp
)
detect_stray_source_files("${liblll_sources}" "liblll/")
endif(LLL)
set(libsolidity_sources
libsolidity/ABIDecoderTests.cpp
libsolidity/ABIEncoderTests.cpp
libsolidity/ABIJsonTest.cpp
libsolidity/ABIJsonTest.h
libsolidity/ABITestsCommon.h
libsolidity/AnalysisFramework.cpp
libsolidity/AnalysisFramework.h
libsolidity/Assembly.cpp
libsolidity/ASTJSONTest.cpp
libsolidity/ASTJSONTest.h
libsolidity/ErrorCheck.cpp
libsolidity/ErrorCheck.h
libsolidity/GasCosts.cpp
libsolidity/GasMeter.cpp
libsolidity/GasTest.cpp
libsolidity/GasTest.h
libsolidity/Imports.cpp
libsolidity/InlineAssembly.cpp
libsolidity/LibSolc.cpp
libsolidity/Metadata.cpp
libsolidity/SemanticTest.cpp
libsolidity/SemanticTest.h
libsolidity/SemVerMatcher.cpp
libsolidity/SMTChecker.cpp
libsolidity/SMTCheckerJSONTest.cpp
libsolidity/SMTCheckerJSONTest.h
libsolidity/SolidityCompiler.cpp
libsolidity/SolidityEndToEndTest.cpp
libsolidity/SolidityExecutionFramework.cpp
libsolidity/SolidityExecutionFramework.h
libsolidity/SolidityExpressionCompiler.cpp
libsolidity/SolidityNameAndTypeResolution.cpp
libsolidity/SolidityNatspecJSON.cpp
libsolidity/SolidityOptimizer.cpp
libsolidity/SolidityParser.cpp
libsolidity/SolidityScanner.cpp
libsolidity/SolidityTypes.cpp
libsolidity/StandardCompiler.cpp
libsolidity/SyntaxTest.cpp
libsolidity/SyntaxTest.h
libsolidity/ViewPureChecker.cpp
)
detect_stray_source_files("${libsolidity_sources}" "libsolidity/")
set(libsolidity_util_sources
libsolidity/util/BytesUtils.cpp
libsolidity/util/BytesUtils.h
libsolidity/util/ContractABIUtils.cpp
libsolidity/util/ContractABIUtils.h
libsolidity/util/SoltestErrors.h
libsolidity/util/SoltestTypes.h
libsolidity/util/TestFileParser.cpp
libsolidity/util/TestFileParser.h
libsolidity/util/TestFileParserTests.cpp
libsolidity/util/TestFunctionCall.cpp
libsolidity/util/TestFunctionCall.h
libsolidity/util/TestFunctionCallTests.cpp
)
detect_stray_source_files("${libsolidity_util_sources}" "libsolidity/util/")
set(libyul_sources
libyul/Common.cpp
libyul/Common.h
libyul/CompilabilityChecker.cpp
libyul/FunctionSideEffects.cpp
libyul/FunctionSideEffects.h
libyul/Inliner.cpp
libyul/Metrics.cpp
libyul/ObjectCompilerTest.cpp
libyul/ObjectCompilerTest.h
libyul/ObjectParser.cpp
libyul/Parser.cpp
libyul/StackReuseCodegen.cpp
libyul/YulInterpreterTest.cpp
libyul/YulInterpreterTest.h
libyul/YulOptimizerTest.cpp
libyul/YulOptimizerTest.h
)
detect_stray_source_files("${libyul_sources}" "libyul/")
add_executable(soltest ${sources}
${contracts_sources}
${libdevcore_sources}
${liblangutil_sources}
${libevmasm_sources}
${libyul_sources}
${liblll_sources}
${libsolidity_sources}
${libsolidity_util_sources}
)
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm devcore Boost::boost Boost::program_options Boost::unit_test_framework evmc)
+26 -19
View File
@@ -21,7 +21,6 @@
#include <test/EVMHost.h>
#include <test/evmc/helpers.hpp>
#include <test/evmc/loader.h>
#include <libevmasm/GasMeter.h>
@@ -42,9 +41,17 @@ evmc::vm* EVMHost::getVM(string const& _path)
if (!theVM && !_path.empty())
{
evmc_loader_error_code errorCode = {};
evmc_instance* vm = evmc_load_and_create(_path.c_str(), &errorCode);
evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode);
if (vm && errorCode == EVMC_LOADER_SUCCESS)
theVM = make_unique<evmc::vm>(vm);
{
if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1))
theVM = make_unique<evmc::vm>(vm);
else
{
evmc_destroy(vm);
cerr << "VM loaded does not support EVM1" << endl;
}
}
else
{
cerr << "Error loading VM from " << _path;
@@ -83,27 +90,27 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
m_evmVersion = EVMC_PETERSBURG;
}
evmc_storage_status EVMHost::set_storage(const evmc_address& _addr, const evmc_bytes32& _key, const evmc_bytes32& _value) noexcept
evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept
{
evmc_bytes32 previousValue = m_state.accounts[_addr].storage[_key];
evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key];
m_state.accounts[_addr].storage[_key] = _value;
// TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used
if (previousValue == _value)
return EVMC_STORAGE_UNCHANGED;
else if (previousValue == evmc_bytes32{})
else if (previousValue == evmc::bytes32{})
return EVMC_STORAGE_ADDED;
else if (_value == evmc_bytes32{})
else if (_value == evmc::bytes32{})
return EVMC_STORAGE_DELETED;
else
return EVMC_STORAGE_MODIFIED;
}
void EVMHost::selfdestruct(const evmc_address& _addr, const evmc_address& _beneficiary) noexcept
void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept
{
// TODO actual selfdestruct is even more complicated.
evmc_uint256be balance = m_state.accounts[_addr].balance;
evmc::uint256be balance = m_state.accounts[_addr].balance;
m_state.accounts.erase(_addr);
m_state.accounts[_beneficiary].balance = balance;
}
@@ -182,7 +189,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value);
}
evmc_address currentAddress = m_currentAddress;
evmc::address currentAddress = m_currentAddress;
m_currentAddress = message.destination;
evmc::result result = m_vm->execute(*this, m_evmVersion, message, code.data(), code.size());
m_currentAddress = currentAddress;
@@ -223,16 +230,16 @@ evmc_tx_context EVMHost::get_tx_context() noexcept
return ctx;
}
evmc_bytes32 EVMHost::get_block_hash(int64_t _number) noexcept
evmc::bytes32 EVMHost::get_block_hash(int64_t _number) noexcept
{
return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number);
}
void EVMHost::emit_log(
evmc_address const& _addr,
evmc::address const& _addr,
uint8_t const* _data,
size_t _dataSize,
evmc_bytes32 const _topics[],
evmc::bytes32 const _topics[],
size_t _topicsCount
) noexcept
{
@@ -245,27 +252,27 @@ void EVMHost::emit_log(
}
Address EVMHost::convertFromEVMC(evmc_address const& _addr)
Address EVMHost::convertFromEVMC(evmc::address const& _addr)
{
return Address(bytes(begin(_addr.bytes), end(_addr.bytes)));
}
evmc_address EVMHost::convertToEVMC(Address const& _addr)
evmc::address EVMHost::convertToEVMC(Address const& _addr)
{
evmc_address a;
evmc::address a;
for (size_t i = 0; i < 20; ++i)
a.bytes[i] = _addr[i];
return a;
}
h256 EVMHost::convertFromEVMC(evmc_bytes32 const& _data)
h256 EVMHost::convertFromEVMC(evmc::bytes32 const& _data)
{
return h256(bytes(begin(_data.bytes), end(_data.bytes)));
}
evmc_bytes32 EVMHost::convertToEVMC(h256 const& _data)
evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data)
{
evmc_bytes32 d;
evmc::bytes32 d;
for (size_t i = 0; i < 32; ++i)
d.bytes[i] = _data[i];
return d;
+24 -25
View File
@@ -23,7 +23,6 @@
#include <test/evmc/evmc.hpp>
#include <test/evmc/evmc.h>
#include <test/evmc/helpers.hpp>
#include <liblangutil/EVMVersion.h>
@@ -47,11 +46,11 @@ public:
struct Account
{
evmc_uint256be balance = {};
evmc::uint256be balance = {};
size_t nonce = 0;
bytes code;
evmc_bytes32 codeHash = {};
std::map<evmc_bytes32, evmc_bytes32> storage;
evmc::bytes32 codeHash = {};
std::map<evmc::bytes32, evmc::bytes32> storage;
};
struct LogEntry
@@ -65,11 +64,11 @@ public:
{
size_t blockNumber;
uint64_t timestamp;
std::map<evmc_address, Account> accounts;
std::map<evmc::address, Account> accounts;
std::vector<LogEntry> logs;
};
Account* account(evmc_address const& _address)
Account* account(evmc::address const& _address)
{
// Make all precompiled contracts exist.
// Be future-proof and consider everything below 1024 as precompiled contract.
@@ -87,12 +86,12 @@ public:
m_state.logs.clear();
}
bool account_exists(evmc_address const& _addr) noexcept final
bool account_exists(evmc::address const& _addr) noexcept final
{
return account(_addr) != nullptr;
}
evmc_bytes32 get_storage(evmc_address const& _addr, evmc_bytes32 const& _key) noexcept final
evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) noexcept final
{
if (Account* acc = account(_addr))
return acc->storage[_key];
@@ -100,26 +99,26 @@ public:
}
evmc_storage_status set_storage(
evmc_address const& _addr,
evmc_bytes32 const& _key,
evmc_bytes32 const& _value
evmc::address const& _addr,
evmc::bytes32 const& _key,
evmc::bytes32 const& _value
) noexcept;
evmc_uint256be get_balance(evmc_address const& _addr) noexcept final
evmc::uint256be get_balance(evmc::address const& _addr) noexcept final
{
if (Account const* acc = account(_addr))
return acc->balance;
return {};
}
size_t get_code_size(evmc_address const& _addr) noexcept final
size_t get_code_size(evmc::address const& _addr) noexcept final
{
if (Account const* acc = account(_addr))
return acc->code.size();
return 0;
}
evmc_bytes32 get_code_hash(evmc_address const& _addr) noexcept final
evmc::bytes32 get_code_hash(evmc::address const& _addr) noexcept final
{
if (Account const* acc = account(_addr))
return acc->codeHash;
@@ -127,7 +126,7 @@ public:
}
size_t copy_code(
evmc_address const& _addr,
evmc::address const& _addr,
size_t _codeOffset,
uint8_t* _bufferData,
size_t _bufferSize
@@ -140,31 +139,31 @@ public:
return i;
}
void selfdestruct(evmc_address const& _addr, evmc_address const& _beneficiary) noexcept;
void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept;
evmc::result call(evmc_message const& _message) noexcept;
evmc_tx_context get_tx_context() noexcept;
evmc_bytes32 get_block_hash(int64_t number) noexcept;
evmc::bytes32 get_block_hash(int64_t number) noexcept;
void emit_log(
evmc_address const& _addr,
evmc::address const& _addr,
uint8_t const* _data,
size_t _dataSize,
evmc_bytes32 const _topics[],
evmc::bytes32 const _topics[],
size_t _topicsCount
) noexcept;
static Address convertFromEVMC(evmc_address const& _addr);
static evmc_address convertToEVMC(Address const& _addr);
static h256 convertFromEVMC(evmc_bytes32 const& _data);
static evmc_bytes32 convertToEVMC(h256 const& _data);
static Address convertFromEVMC(evmc::address const& _addr);
static evmc::address convertToEVMC(Address const& _addr);
static h256 convertFromEVMC(evmc::bytes32 const& _data);
static evmc::bytes32 convertToEVMC(h256 const& _data);
State m_state;
evmc_address m_currentAddress = {};
evmc_address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878"));
evmc::address m_currentAddress = {};
evmc::address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878"));
private:
evmc::result precompileECRecover(evmc_message const& _message) noexcept;
+1 -2
View File
@@ -26,7 +26,6 @@
#include <test/evmc/evmc.hpp>
#include <test/evmc/loader.h>
#include <test/evmc/helpers.hpp>
#include <libdevcore/CommonIO.h>
@@ -235,7 +234,7 @@ bool ExecutionFramework::storageEmpty(Address const& _addr)
if (EVMHost::Account const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr)))
{
for (auto const& entry: acc->storage)
if (!(entry.second == evmc_bytes32{}))
if (!(entry.second == evmc::bytes32{}))
return false;
}
return true;
+26 -9
View File
@@ -303,7 +303,10 @@ enum evmc_status_code
* For example, the Client tries running a code in the EVM 1.5. If the
* code is not supported there, the execution falls back to the EVM 1.0.
*/
EVMC_REJECTED = -2
EVMC_REJECTED = -2,
/** The VM failed to allocate the amount of memory needed for execution. */
EVMC_OUT_OF_MEMORY = -3
};
/* Forward declaration. */
@@ -795,13 +798,15 @@ enum evmc_revision
*
* This function MAY be invoked multiple times for a single VM instance.
*
* @param instance The VM instance.
* @param context The pointer to the Client execution context to be passed
* to the callback functions. See ::evmc_context.
* @param rev Requested EVM specification revision.
* @param msg Call parameters. See ::evmc_message.
* @param code Reference to the code to be executed.
* @param code_size The length of the code.
* @param instance The VM instance. This argument MUST NOT be NULL.
* @param context The pointer to the Host execution context to be passed
* to the Host interface methods (::evmc_host_interface).
* This argument MUST NOT be NULL unless
* the @p instance has the ::EVMC_CAPABILITY_PRECOMPILES capability.
* @param rev The requested EVM specification revision.
* @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL.
* @param code The reference to the code to be executed. This argument MAY be NULL.
* @param code_size The length of the code. If @p code is NULL this argument MUST be 0.
* @return The execution result.
*/
typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance,
@@ -857,7 +862,11 @@ typedef uint32_t evmc_capabilities_flagset;
*/
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance);
/** The opaque type representing a Client-side tracer object. */
/**
* The opaque type representing a Client-side tracer object.
*
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
*/
struct evmc_tracer_context;
/**
@@ -869,6 +878,8 @@ struct evmc_tracer_context;
* This piece of information can be acquired by inspecting messages being sent to the EVM in
* ::evmc_execute_fn and the results of the messages execution.
*
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
*
* @param context The pointer to the Client-side tracing context. This allows to
* implement the tracer in OOP manner.
* @param code_offset The current instruction position in the code.
@@ -916,6 +927,8 @@ typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context,
*
* This will overwrite the previous settings (the callback and the context).
*
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
*
* @param instance The EVM instance.
* @param callback The tracer callback function. This argument MAY be NULL to disable previously
* set tracer.
@@ -989,6 +1002,10 @@ struct evmc_instance
* Optional pointer to function setting the EVM instruction tracer.
*
* If the EVM does not support this feature the pointer can be NULL.
*
* @deprecated
* Since EVMC 6.3, the tracing API has been deprecated as there have been some
* design flaws discovered. New API is expected to be introduced in future.
*/
evmc_set_tracer_fn set_tracer;
+354 -34
View File
@@ -7,6 +7,7 @@
#include <evmc/evmc.h>
#include <evmc/helpers.h>
#include <functional>
#include <initializer_list>
#include <utility>
@@ -14,6 +15,250 @@
/// @ingroup cpp
namespace evmc
{
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
///
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
struct address : evmc_address
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};
/// The fixed size array of 32 bytes for storing 256-bit EVM values.
///
/// This type wraps C ::evmc_bytes32 to make sure objects of this type are always initialized.
struct bytes32 : evmc_bytes32
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};
/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
using uint256be = bytes32;
/// Loads 64 bits / 8 bytes of data from the given @p bytes array in big-endian order.
constexpr inline uint64_t load64be(const uint8_t* bytes) noexcept
{
// TODO: Report bug in clang incorrectly optimizing this with AVX2 enabled.
return (uint64_t{bytes[0]} << 56) | (uint64_t{bytes[1]} << 48) | (uint64_t{bytes[2]} << 40) |
(uint64_t{bytes[3]} << 32) | (uint64_t{bytes[4]} << 24) | (uint64_t{bytes[5]} << 16) |
(uint64_t{bytes[6]} << 8) | uint64_t{bytes[7]};
}
/// Loads 32 bits / 4 bytes of data from the given @p bytes array in big-endian order.
constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept
{
return (uint32_t{bytes[0]} << 24) | (uint32_t{bytes[1]} << 16) | (uint32_t{bytes[2]} << 8) |
uint32_t{bytes[3]};
}
namespace fnv
{
constexpr auto prime = 0x100000001b3; ///< The 64-bit FNV prime number.
constexpr auto offset_basis = 0xcbf29ce484222325; ///< The 64-bit FNV offset basis.
/// The hashing transformation for 64-bit inputs based on the FNV-1a formula.
constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept
{
return (h ^ x) * prime;
}
} // namespace fnv
/// The "equal" comparison operator for the evmc::address type.
constexpr bool operator==(const address& a, const address& b) noexcept
{
// TODO: Report bug in clang keeping unnecessary bswap.
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) == load32be(&b.bytes[16]);
}
/// The "not equal" comparison operator for the evmc::address type.
constexpr bool operator!=(const address& a, const address& b) noexcept
{
return !(a == b);
}
/// The "less" comparison operator for the evmc::address type.
constexpr bool operator<(const address& a, const address& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) < load32be(&b.bytes[16]));
}
/// The "equal" comparison operator for the evmc::bytes32 type.
constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) == load64be(&b.bytes[24]);
}
/// The "not equal" comparison operator for the evmc::bytes32 type.
constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept
{
return !(a == b);
}
/// The "less" comparison operator for the evmc::bytes32 type.
constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) < load64be(&b.bytes[16])) ||
(load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) < load64be(&b.bytes[24]));
}
/// Checks if the given address is the zero address.
constexpr inline bool is_zero(const address& a) noexcept
{
return a == address{};
}
constexpr address::operator bool() const noexcept
{
return !is_zero(*this);
}
/// Checks if the given bytes32 object has all zero bytes.
constexpr inline bool is_zero(const bytes32& a) noexcept
{
return a == bytes32{};
}
constexpr bytes32::operator bool() const noexcept
{
return !is_zero(*this);
}
namespace literals
{
namespace internal
{
template <typename T, T... Ints>
struct integer_sequence
{
};
template <uint8_t... Bytes>
using byte_sequence = integer_sequence<uint8_t, Bytes...>;
template <char... Chars>
using char_sequence = integer_sequence<char, Chars...>;
template <typename, typename>
struct concatenate;
template <uint8_t... Bytes1, uint8_t... Bytes2>
struct concatenate<byte_sequence<Bytes1...>, byte_sequence<Bytes2...>>
{
using type = byte_sequence<Bytes1..., Bytes2...>;
};
template <uint8_t D>
constexpr uint8_t parse_hex_digit() noexcept
{
static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'),
"literal must be hexadecimal integer");
return static_cast<uint8_t>(
(D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10);
}
template <typename>
struct parse_digits;
template <uint8_t Digit1, uint8_t Digit2>
struct parse_digits<byte_sequence<Digit1, Digit2>>
{
using type = byte_sequence<static_cast<uint8_t>(parse_hex_digit<Digit1>() << 4) |
parse_hex_digit<Digit2>()>;
};
template <uint8_t Digit1, uint8_t Digit2, uint8_t... Rest>
struct parse_digits<byte_sequence<Digit1, Digit2, Rest...>>
{
using type = typename concatenate<typename parse_digits<byte_sequence<Digit1, Digit2>>::type,
typename parse_digits<byte_sequence<Rest...>>::type>::type;
};
template <typename, typename>
struct parse_literal;
template <typename T, char Prefix1, char Prefix2, char... Literal>
struct parse_literal<T, char_sequence<Prefix1, Prefix2, Literal...>>
{
static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation");
static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size");
template <uint8_t... Bytes>
static constexpr T create_from(byte_sequence<Bytes...>) noexcept
{
return T{{{Bytes...}}};
}
static constexpr T get() noexcept
{
return create_from(typename parse_digits<byte_sequence<Literal...>>::type{});
}
};
template <typename T, char Digit>
struct parse_literal<T, char_sequence<Digit>>
{
static_assert(Digit == '0', "only 0 is allowed as a single digit literal");
static constexpr T get() noexcept { return {}; }
};
template <typename T, char... Literal>
constexpr T parse() noexcept
{
return parse_literal<T, char_sequence<Literal...>>::get();
}
} // namespace internal
/// Literal for evmc::address.
template <char... Literal>
constexpr address operator"" _address() noexcept
{
return internal::parse<address, Literal...>();
}
/// Literal for evmc::bytes32.
template <char... Literal>
constexpr bytes32 operator"" _bytes32() noexcept
{
return internal::parse<bytes32, Literal...>();
}
} // namespace literals
using namespace literals;
/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;
/// @copydoc evmc_result
///
/// This is a RAII wrapper for evmc_result and objects of this type
@@ -27,6 +272,22 @@ public:
using evmc_result::output_size;
using evmc_result::status_code;
/// Creates the result from the provided arguments.
///
/// The provided output is copied to memory allocated with malloc()
/// and the evmc_result::release function is set to one invoking free().
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _output_data The pointer to the output.
/// @param _output_size The output size.
result(evmc_status_code _status_code,
int64_t _gas_left,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
{}
/// Converting constructor from raw evmc_result.
explicit result(evmc_result const& res) noexcept : evmc_result{res} {}
@@ -80,11 +341,32 @@ public:
class vm
{
public:
vm() noexcept = default;
/// Converting constructor from evmc_instance.
explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {}
/// Destructor responsible for automatically destroying the VM instance.
~vm() noexcept { m_instance->destroy(m_instance); }
~vm() noexcept
{
if (m_instance)
m_instance->destroy(m_instance);
}
vm(const vm&) = delete;
vm& operator=(const vm&) = delete;
/// Move constructor.
vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
/// Move assignment operator.
vm& operator=(vm&& other) noexcept
{
this->~vm();
m_instance = other.m_instance;
other.m_instance = nullptr;
return *this;
}
/// The constructor that captures a VM instance and configures the instance
/// with provided list of options.
@@ -96,6 +378,9 @@ public:
set_option(option.first, option.second);
}
/// Checks if contains a valid pointer to the VM instance.
explicit operator bool() const noexcept { return m_instance != nullptr; }
/// Checks whenever the VM instance is ABI compatible with the current EVMC API.
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
@@ -128,7 +413,7 @@ public:
}
private:
evmc_instance* const m_instance = nullptr;
evmc_instance* m_instance = nullptr;
};
/// The EVMC Host interface
@@ -138,35 +423,33 @@ public:
virtual ~HostInterface() noexcept = default;
/// @copydoc evmc_host_interface::account_exists
virtual bool account_exists(const evmc_address& addr) noexcept = 0;
virtual bool account_exists(const address& addr) noexcept = 0;
/// @copydoc evmc_host_interface::get_storage
virtual evmc_bytes32 get_storage(const evmc_address& addr,
const evmc_bytes32& key) noexcept = 0;
virtual bytes32 get_storage(const address& addr, const bytes32& key) noexcept = 0;
/// @copydoc evmc_host_interface::set_storage
virtual evmc_storage_status set_storage(const evmc_address& addr,
const evmc_bytes32& key,
const evmc_bytes32& value) noexcept = 0;
virtual evmc_storage_status set_storage(const address& addr,
const bytes32& key,
const bytes32& value) noexcept = 0;
/// @copydoc evmc_host_interface::get_balance
virtual evmc_uint256be get_balance(const evmc_address& addr) noexcept = 0;
virtual uint256be get_balance(const address& addr) noexcept = 0;
/// @copydoc evmc_host_interface::get_code_size
virtual size_t get_code_size(const evmc_address& addr) noexcept = 0;
virtual size_t get_code_size(const address& addr) noexcept = 0;
/// @copydoc evmc_host_interface::get_code_hash
virtual evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept = 0;
virtual bytes32 get_code_hash(const address& addr) noexcept = 0;
/// @copydoc evmc_host_interface::copy_code
virtual size_t copy_code(const evmc_address& addr,
virtual size_t copy_code(const address& addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) noexcept = 0;
/// @copydoc evmc_host_interface::selfdestruct
virtual void selfdestruct(const evmc_address& addr,
const evmc_address& beneficiary) noexcept = 0;
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
/// @copydoc evmc_host_interface::call
virtual result call(const evmc_message& msg) noexcept = 0;
@@ -175,13 +458,13 @@ public:
virtual evmc_tx_context get_tx_context() noexcept = 0;
/// @copydoc evmc_host_interface::get_block_hash
virtual evmc_bytes32 get_block_hash(int64_t block_number) noexcept = 0;
virtual bytes32 get_block_hash(int64_t block_number) noexcept = 0;
/// @copydoc evmc_host_interface::emit_log
virtual void emit_log(const evmc_address& addr,
virtual void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const evmc_bytes32 topics[],
const bytes32 topics[],
size_t num_topics) noexcept = 0;
};
@@ -198,39 +481,39 @@ public:
/// Implicit converting constructor from evmc_context.
HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT
bool account_exists(const evmc_address& address) noexcept final
bool account_exists(const address& address) noexcept final
{
return context->host->account_exists(context, &address);
}
evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept final
bytes32 get_storage(const address& address, const bytes32& key) noexcept final
{
return context->host->get_storage(context, &address, &key);
}
evmc_storage_status set_storage(const evmc_address& address,
const evmc_bytes32& key,
const evmc_bytes32& value) noexcept final
evmc_storage_status set_storage(const address& address,
const bytes32& key,
const bytes32& value) noexcept final
{
return context->host->set_storage(context, &address, &key, &value);
}
evmc_uint256be get_balance(const evmc_address& address) noexcept final
uint256be get_balance(const address& address) noexcept final
{
return context->host->get_balance(context, &address);
}
size_t get_code_size(const evmc_address& address) noexcept final
size_t get_code_size(const address& address) noexcept final
{
return context->host->get_code_size(context, &address);
}
evmc_bytes32 get_code_hash(const evmc_address& address) noexcept final
bytes32 get_code_hash(const address& address) noexcept final
{
return context->host->get_code_hash(context, &address);
}
size_t copy_code(const evmc_address& address,
size_t copy_code(const address& address,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) noexcept final
@@ -238,9 +521,9 @@ public:
return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) noexcept final
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
context->host->selfdestruct(context, &address, &beneficiary);
context->host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
@@ -261,18 +544,18 @@ public:
return tx_context;
}
evmc_bytes32 get_block_hash(int64_t number) noexcept final
bytes32 get_block_hash(int64_t number) noexcept final
{
return context->host->get_block_hash(context, number);
}
void emit_log(const evmc_address& address,
void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const evmc_bytes32 topics[],
const bytes32 topics[],
size_t topics_count) noexcept final
{
context->host->emit_log(context, &address, data, data_size, topics, topics_count);
context->host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
};
@@ -351,7 +634,8 @@ inline void emit_log(evmc_context* h,
const evmc_bytes32 topics[],
size_t num_topics) noexcept
{
static_cast<Host*>(h)->emit_log(*addr, data, data_size, topics, num_topics);
static_cast<Host*>(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
num_topics);
}
constexpr evmc_host_interface interface{
@@ -360,6 +644,42 @@ constexpr evmc_host_interface interface{
};
} // namespace internal
inline Host::Host() noexcept : evmc_context{&internal::interface} {}
inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {}
} // namespace evmc
namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
template <>
struct hash<evmc::address>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::address& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(fnv1a_by64(
fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])),
load32be(&s.bytes[16])));
}
};
/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers.
template <>
struct hash<evmc::bytes32>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])),
load64be(&s.bytes[8])),
load64be(&s.bytes[16])),
load64be(&s.bytes[24])));
}
};
} // namespace std
+55
View File
@@ -18,6 +18,8 @@
#pragma once
#include <evmc/evmc.h>
#include <stdlib.h>
#include <string.h>
/**
* Returns true if the VM instance has a compatible ABI version.
@@ -83,6 +85,7 @@ static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance*
*
* @see evmc_set_tracer_fn
*/
EVMC_DEPRECATED
static inline void evmc_set_tracer(struct evmc_instance* instance,
evmc_trace_callback callback,
struct evmc_tracer_context* context)
@@ -106,6 +109,58 @@ static inline struct evmc_result evmc_execute(struct evmc_instance* instance,
return instance->execute(instance, context, rev, msg, code, code_size);
}
/// The evmc_result release function using free() for releasing the memory.
///
/// This function is used in the evmc_make_result(),
/// but may be also used in other case if convenient.
///
/// @param result The result object.
static void evmc_free_result_memory(const struct evmc_result* result)
{
free((uint8_t*)result->output_data);
}
/// Creates the result from the provided arguments.
///
/// The provided output is copied to memory allocated with malloc()
/// and the evmc_result::release function is set to one invoking free().
///
/// In case of memory allocation failure, the result has all fields zeroed
/// and only evmc_result::status_code is set to ::EVMC_OUT_OF_MEMORY internal error.
///
/// @param status_code The status code.
/// @param gas_left The amount of gas left.
/// @param output_data The pointer to the output.
/// @param output_size The output size.
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
int64_t gas_left,
const uint8_t* output_data,
size_t output_size)
{
struct evmc_result result;
memset(&result, 0, sizeof(result));
if (output_size != 0)
{
uint8_t* buffer = (uint8_t*)malloc(output_size);
if (!buffer)
{
result.status_code = EVMC_OUT_OF_MEMORY;
return result;
}
memcpy(buffer, output_data, output_size);
result.output_data = buffer;
result.output_size = output_size;
result.release = evmc_free_result_memory;
}
result.status_code = status_code;
result.gas_left = gas_left;
return result;
}
/**
* Releases the resources allocated to the execution result.
*
+41 -23
View File
@@ -12,58 +12,78 @@
*/
#pragma once
#include <evmc/evmc.h>
#include <evmc/evmc.hpp>
#include <cstring>
#include <functional>
using evmc::is_zero;
/// The comparator for std::map<evmc_address, ...>.
EVMC_DEPRECATED
inline bool operator<(const evmc_address& a, const evmc_address& b)
{
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0;
}
/// The comparator for std::map<evmc_bytes32, ...>.
EVMC_DEPRECATED
inline bool operator<(const evmc_bytes32& a, const evmc_bytes32& b)
{
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0;
}
/// The comparator for equality.
EVMC_DEPRECATED
inline bool operator==(const evmc_address& a, const evmc_address& b)
{
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0;
}
/// The comparator for equality.
EVMC_DEPRECATED
inline bool operator==(const evmc_bytes32& a, const evmc_bytes32& b)
{
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0;
}
/// Check if the address is zero (all bytes are zeros).
inline bool is_zero(const evmc_address& address) noexcept
/// Parameters for the fnv1a hash function, specialized by the hash result size (size_t).
///
/// The values for the matching size are taken from
/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters.
///
/// @tparam size The size of the hash result (size_t).
template <size_t size>
struct fnv1_params
{
return address == evmc_address{};
}
};
/// Check if the hash is zero (all bytes are zeros).
inline bool is_zero(const evmc_bytes32& x) noexcept
/// Parameters for the fnv1a hash function, specialized for the hash result of 4 bytes.
template <>
struct fnv1_params<4>
{
return x == evmc_bytes32{};
}
static constexpr auto prime = 0x1000193; ///< The FNV prime.
static constexpr auto offset_basis = 0x811c9dc5; ///< The FNV offset basis.
};
/// FNV1a hash function with 64-bit result.
inline uint64_t fnv1a_64(const uint8_t* ptr, size_t len)
/// Parameters for the fnv1a hash function, specialized for the hash result of 8 bytes.
template <>
struct fnv1_params<8>
{
constexpr uint64_t prime = 1099511628211ULL;
constexpr uint64_t offset_basis = 14695981039346656037ULL;
static constexpr auto prime = 0x100000001b3; ///< The FNV prime.
static constexpr auto offset_basis = 0xcbf29ce484222325; ///< The FNV offset basis.
};
uint64_t ret = offset_basis;
/// FNV1a hash function.
inline size_t fnv1a(const uint8_t* ptr, size_t len) noexcept
{
using params = fnv1_params<sizeof(size_t)>;
auto ret = size_t{params::offset_basis};
for (size_t i = 0; i < len; i++)
{
ret ^= ptr[i];
ret *= prime;
ret *= params::prime;
}
return ret;
}
@@ -72,25 +92,23 @@ namespace std
{
/// Hash operator template specialization for evmc_address needed for unordered containers.
template <>
struct hash<evmc_address>
struct EVMC_DEPRECATED hash<evmc_address>
{
/// Hash operator using FNV1a.
std::enable_if<sizeof(size_t) == 8, std::size_t>::type operator()(const evmc_address& s) const
noexcept
size_t operator()(const evmc_address& s) const noexcept
{
return fnv1a_64(s.bytes, sizeof(s.bytes));
return fnv1a(s.bytes, sizeof(s.bytes));
}
};
/// Hash operator template needed for std::unordered_set and others using hashes.
template <>
struct hash<evmc_bytes32>
struct EVMC_DEPRECATED hash<evmc_bytes32>
{
/// Hash operator using FNV1a.
std::enable_if<sizeof(size_t) == 8, std::size_t>::type operator()(const evmc_bytes32& s) const
noexcept
size_t operator()(const evmc_bytes32& s) const noexcept
{
return fnv1a_64(s.bytes, sizeof(s.bytes));
return fnv1a(s.bytes, sizeof(s.bytes));
}
};
} // namespace std
+91 -8
View File
@@ -42,20 +42,14 @@
#define ATTR_FORMAT(...)
#endif
#if _WIN32
#define strcpy_sx strcpy_s
#else
/*
* Limited variant of strcpy_s().
*
* Provided for C standard libraries where strcpy_s() is not available.
* The availability check might need to adjusted for other C standard library implementations.
*/
#if !defined(EVMC_LOADER_MOCK)
static
#endif
int
strcpy_sx(char* restrict dest, size_t destsz, const char* restrict src)
strcpy_sx(char* dest, size_t destsz, const char* src)
{
size_t len = strlen(src);
if (len >= destsz)
@@ -70,7 +64,6 @@ static
dest[len] = 0;
return 0;
}
#endif
#define PATH_MAX_LENGTH 4096
@@ -238,3 +231,93 @@ exit:
return instance;
}
/// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr.
/// If the delimiter is not found, the whole string is returned.
/// The @p str_ptr is also slided after the delimiter or to the string end
/// if the delimiter is not found (in this case the @p str_ptr points to an empty string).
static char* get_token(char** str_ptr, char delim)
{
char* str = *str_ptr;
char* delim_pos = strchr(str, delim);
if (delim_pos)
{
// If the delimiter is found, null it to get null-terminated prefix
// and slide the str_ptr after the delimiter.
*delim_pos = '\0';
*str_ptr = delim_pos + 1;
}
else
{
// Otherwise, slide the str_ptr to the end and return the whole string as the prefix.
*str_ptr += strlen(str);
}
return str;
}
struct evmc_instance* evmc_load_and_configure(const char* config,
enum evmc_loader_error_code* error_code)
{
enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS;
struct evmc_instance* instance = NULL;
char config_copy_buffer[PATH_MAX_LENGTH];
if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0)
{
ec = set_error(EVMC_LOADER_INVALID_ARGUMENT,
"invalid argument: configuration is too long (maximum allowed length is %d)",
(int)sizeof(config_copy_buffer));
goto exit;
}
char* options = config_copy_buffer;
const char* path = get_token(&options, ',');
instance = evmc_load_and_create(path, error_code);
if (!instance)
return NULL;
if (instance->set_option == NULL && strlen(options) != 0)
{
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
instance->name, path);
goto exit;
}
while (strlen(options) != 0)
{
char* option = get_token(&options, ',');
// Slit option into name and value by taking the name token.
// The option variable will have the value, can be empty.
const char* name = get_token(&option, '=');
enum evmc_set_option_result r = instance->set_option(instance, name, option);
switch (r)
{
case EVMC_SET_OPTION_SUCCESS:
break;
case EVMC_SET_OPTION_INVALID_NAME:
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s): unknown option '%s'",
instance->name, path, name);
goto exit;
case EVMC_SET_OPTION_INVALID_VALUE:
ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE,
"%s (%s): unsupported value '%s' for option '%s'", instance->name, path,
option, name);
goto exit;
}
}
exit:
if (error_code)
*error_code = ec;
if (ec == EVMC_LOADER_SUCCESS)
return instance;
if (instance)
evmc_destroy(instance);
return NULL;
}
+44 -1
View File
@@ -40,7 +40,13 @@ enum evmc_loader_error_code
EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4,
/** The ABI version of the VM instance has mismatched. */
EVMC_LOADER_ABI_VERSION_MISMATCH = 5
EVMC_LOADER_ABI_VERSION_MISMATCH = 5,
/** The VM option is invalid. */
EVMC_LOADER_INVALID_OPTION_NAME = 6,
/** The VM option value is invalid. */
EVMC_LOADER_INVALID_OPTION_VALUE = 7
};
/**
@@ -111,6 +117,43 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
struct evmc_instance* evmc_load_and_create(const char* filename,
enum evmc_loader_error_code* error_code);
/**
* Dynamically loads the EVMC module, then creates and configures the VM instance.
*
* This function performs the following actions atomically:
* - loads the EVMC module (as evmc_load()),
* - creates the VM instance,
* - configures the VM instance with options provided in the @p config parameter.
*
* The configuration string (@p config) has the following syntax:
*
* <path> ("," <option-name> ["=" <option-value>])*
*
* In this syntax, an option without a value can be specified (`,option,`)
* as a shortcut for using empty value (`,option=,`).
*
* Options are passed to a VM in the order they are specified in the configuration string.
* It is up to the VM implementation how to handle duplicated options and other conflicts.
*
* Example configuration string:
*
* ./modules/vm.so,engine=compiler,trace,verbosity=2
*
* The function signals the same errors as evmc_load_and_create() and additionally:
* - ::EVMC_LOADER_INVALID_OPTION_NAME
* when the provided options list contains an option unknown for the VM,
* - ::EVMC_LOADER_INVALID_OPTION_VALUE
* when there exists unsupported value for a given VM option.
*
* @param config The path to the EVMC module with additional configuration options.
* @param error_code The pointer to the error code. If not NULL the value is set to
* ::EVMC_LOADER_SUCCESS on success or any other error code as described above.
* @return The pointer to the created VM or NULL in case of error.
*/
struct evmc_instance* evmc_load_and_configure(const char* config,
enum evmc_loader_error_code* error_code);
/**
* Returns the human-readable message describing the most recent error
* that occurred in EVMC loading since the last call to this function.
@@ -0,0 +1,10 @@
pragma experimental SMTChecker;
contract C {
bytes20 x;
function f(bytes16 b) public view {
b[uint8(x[2])];
}
}
// ----
// Warning: (116-120): Assertion checker does not yet support index accessing fixed bytes.
// Warning: (108-122): Assertion checker does not yet support index accessing fixed bytes.
+5
View File
@@ -92,8 +92,13 @@ message TestFunction {
}
message Contract {
enum Test {
CALLDATA_CODER = 1;
RETURNDATA_CODER = 2;
}
required VarDecl state_vars = 1;
required TestFunction testfunction = 2;
required Test test = 3;
}
package dev.test.abiv2fuzzer;
+149 -33
View File
@@ -114,7 +114,14 @@ template <typename T>
pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
{
ostringstream local, global;
auto [varName, paramName] = newVarNames(getNextVarCounter());
auto [varName, paramName] = newVarNames(getNextVarCounter(), m_isStateVar);
// Add variable name to the argument list of coder function call
if (m_argsCoder.str().empty())
m_argsCoder << varName;
else
m_argsCoder << ", " << varName;
string location{};
if (!m_isStateVar && !_isValueType)
location = "memory";
@@ -177,6 +184,16 @@ pair<string, string> ProtoConverter::varDecl(
_paramName,
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
);
appendTypes(
_isValueType,
typeStr,
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
);
appendTypedReturn(
_isValueType,
typeStr,
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
);
// Update dyn param only if necessary
if (tVisitor.isLastDynParamRightPadded())
@@ -259,6 +276,41 @@ void ProtoConverter::appendTypedParams(
}
}
void ProtoConverter::appendTypes(
bool _isValueType,
string const& _typeString,
Delimiter _delimiter
)
{
string qualifiedTypeString = (
_isValueType ?
_typeString :
_typeString + " memory"
);
m_types << Whiskers(R"(<delimiter><type>)")
("delimiter", delimiterToString(_delimiter))
("type", qualifiedTypeString)
.render();
}
void ProtoConverter::appendTypedReturn(
bool _isValueType,
string const& _typeString,
Delimiter _delimiter
)
{
string qualifiedTypeString = (
_isValueType ?
_typeString :
_typeString + " memory"
);
m_typedReturn << Whiskers(R"(<delimiter><type> <varName>)")
("delimiter", delimiterToString(_delimiter))
("type", qualifiedTypeString)
("varName", "lv_" + to_string(m_varCounter - 1))
.render();
}
// Adds the qualifier "calldata" to non-value parameter of an external function.
void ProtoConverter::appendTypedParamsExternal(
bool _isValueType,
@@ -310,7 +362,6 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
}
}
// Test function to be called externally.
string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
{
// TODO: Support more than one but less than N local variables
@@ -320,38 +371,76 @@ string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDe
string localVarDefs = localVarBuffers.second;
ostringstream testBuffer;
string functionDecl = "function test() public returns (uint)";
string testFunction = Whiskers(R"(
function test() public returns (uint) {
<?calldata>return test_calldata_coding();</calldata>
<?returndata>return test_returndata_coding();</returndata>
})")
("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
.render();
string functionDeclCalldata = "function test_calldata_coding() internal returns (uint)";
string functionDeclReturndata = "function test_returndata_coding() internal returns (uint)";
testBuffer << Whiskers(R"(<structTypeDecl>
<functionDecl> {
<testFunction>
<?calldata>
<functionDeclCalldata> {
<storageVarDefs>
<localVarDefs>
<testCode>
})")
<calldataTestCode>
}
<calldataHelperFuncs>
</calldata>
<?returndata>
<functionDeclReturndata> {
<returndataTestCode>
}
<?varsPresent>
function coder_returndata_external() external returns (<return_types>) {
<storageVarDefs>
<localVarDefs>
return (<return_values>);
}
</varsPresent>
</returndata>)")
("testFunction", testFunction)
("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
("calldataHelperFuncs", calldataHelperFunctions())
("varsPresent", !m_types.str().empty())
("structTypeDecl", structTypeDecl)
("functionDecl", functionDecl)
("functionDeclCalldata", functionDeclCalldata)
("functionDeclReturndata", functionDeclReturndata)
("storageVarDefs", _storageVarDefs)
("localVarDefs", localVarDefs)
("testCode", testCode(_x.invalid_encoding_length()))
("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length()))
("returndataTestCode", testReturnDataFunction())
("return_types", m_types.str())
("return_values", m_argsCoder.str())
.render();
return testBuffer.str();
}
string ProtoConverter::testCode(unsigned _invalidLength)
string ProtoConverter::testCallDataFunction(unsigned _invalidLength)
{
return Whiskers(R"(
uint returnVal = this.coder_public(<parameterNames>);
uint returnVal = this.coder_calldata_public(<argumentNames>);
if (returnVal != 0)
return returnVal;
returnVal = this.coder_external(<parameterNames>);
returnVal = this.coder_calldata_external(<argumentNames>);
if (returnVal != 0)
return uint(200000) + returnVal;
<?atLeastOneVar>
bytes memory argumentEncoding = abi.encode(<parameterNames>);
bytes memory argumentEncoding = abi.encode(<argumentNames>);
returnVal = checkEncodedCall(
this.coder_public.selector,
this.coder_calldata_public.selector,
argumentEncoding,
<invalidLengthFuzz>,
<isRightPadded>
@@ -360,7 +449,7 @@ string ProtoConverter::testCode(unsigned _invalidLength)
return returnVal;
returnVal = checkEncodedCall(
this.coder_external.selector,
this.coder_calldata_external.selector,
argumentEncoding,
<invalidLengthFuzz>,
<isRightPadded>
@@ -370,26 +459,32 @@ string ProtoConverter::testCode(unsigned _invalidLength)
</atLeastOneVar>
return 0;
)")
("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
("argumentNames", m_argsCoder.str())
("invalidLengthFuzz", std::to_string(_invalidLength))
("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
("atLeastOneVar", m_varCounter > 0)
.render();
}
string ProtoConverter::helperFunctions()
string ProtoConverter::testReturnDataFunction()
{
stringstream helperFuncs;
helperFuncs << R"(
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
if(a.length != b.length)
return false;
for (uint i = 0; i < a.length; i++)
if (a[i] != b[i])
return false;
return true;
}
return Whiskers(R"(
<?varsPresent>
(<varDecl>) = this.coder_returndata_external();
<equality_checks>
</varsPresent>
return 0;
)")
("varsPresent", !m_typedReturn.str().empty())
("varDecl", m_typedReturn.str())
("equality_checks", m_checks.str())
.render();
}
string ProtoConverter::calldataHelperFunctions()
{
stringstream calldataHelperFuncs;
calldataHelperFuncs << R"(
/// Accepts function selector, correct argument encoding, and length of
/// invalid encoding and returns the correct and incorrect abi encoding
/// for calling the function specified by the function selector.
@@ -452,19 +547,18 @@ string ProtoConverter::helperFunctions()
if (success == true)
return 400001;
return 0;
}
)";
})";
// These are callee functions that encode from storage, decode to
// memory/calldata and check if decoded value matches storage value
// return true on successful match, false otherwise
helperFuncs << Whiskers(R"(
function coder_public(<parameters_memory>) public pure returns (uint) {
calldataHelperFuncs << Whiskers(R"(
function coder_calldata_public(<parameters_memory>) public pure returns (uint) {
<equality_checks>
return 0;
}
function coder_external(<parameters_calldata>) external pure returns (uint) {
function coder_calldata_external(<parameters_calldata>) external pure returns (uint) {
<equality_checks>
return 0;
}
@@ -473,6 +567,25 @@ string ProtoConverter::helperFunctions()
("equality_checks", equalityChecksAsString())
("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
.render();
return calldataHelperFuncs.str();
}
string ProtoConverter::commonHelperFunctions()
{
stringstream helperFuncs;
helperFuncs << R"(
/// Compares bytes, returning true if they are equal and false otherwise.
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
if(a.length != b.length)
return false;
for (uint i = 0; i < a.length; i++)
if (a[i] != b[i])
return false;
return true;
}
)";
return helperFuncs.str();
}
@@ -481,6 +594,9 @@ void ProtoConverter::visit(Contract const& _x)
string pragmas = R"(pragma solidity >=0.0;
pragma experimental ABIEncoderV2;)";
// Record test spec
m_test = _x.test();
// TODO: Support more than one but less than N state variables
auto storageBuffers = visit(_x.state_vars());
string storageVarDecls = storageBuffers.first;
@@ -499,7 +615,7 @@ pragma experimental ABIEncoderV2;)";
ostringstream contractBody;
contractBody << storageVarDecls
<< testFunction
<< helperFunctions();
<< commonHelperFunctions();
m_output << Whiskers(R"(<pragmas>
<contractStart>
<contractBody>
@@ -1085,4 +1201,4 @@ string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHe
_counter,
_isHexLiteral
);
}
}
+158 -80
View File
@@ -19,84 +19,118 @@
/**
* Template of the solidity test program generated by this converter is as follows:
*
* pragma solidity >=0.0;
* pragma experimental ABIEncoderV2;
* pragma solidity >=0.0;
* pragma experimental ABIEncoderV2;
*
* contract C {
* // State variable
* string x_0;
* // Test function that is called by the VM.
* function test() public returns (uint) {
* // Local variable
* bytes x_1 = "1";
* x_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
* uint returnVal = this.coder_public(x_0, x_1);
* if (returnVal != 0)
* return returnVal;
* // Since the return codes in the public and external coder functions are identical
* // we offset error code by a fixed amount (200000) for differentiation.
* returnVal = this.coder_external(x_0, x_1);
* if (returnVal != 0)
* return 200000 + returnVal;
* // Encode parameters
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
* if (returnVal != 0)
* return returnVal;
* contract C {
* // State variable
* string sv_0;
* // Test function that is called by the VM.
* // There are 2 variations of this function: one returns
* // the output of test_calldata_coding() and the other
* // returns the output of test_returndata_coding(). The
* // proto field called Test decides which one of the two
* // are chosen for creating a test case.
* function test() public returns (uint) {
* // The protobuf field "Contract.test" decides which of
* // the two internal functions "test_calldata_coding()"
* // and "test_returndata_coding()" are called. Here,
* // we assume that the protobuf field equals "CALLDATA_CODER"
* return this.test_calldata_coding()
* }
*
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
* if (returnVal != 0)
* return uint(200000) + returnVal;
* // Return zero if all checks pass.
* return 0;
* }
* // The following function is generated if the protobuf field
* // "Contract.test" is equal to "RETURNDATA_CODER".
* function test_returndata_coding() internal returns (uint) {
* string memory lv_0, bytes memory lv_1 = test_returndata_external();
* if (lv_0 != 044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d)
* return 1;
* if (lv_1 != "1")
* return 2;
* return 0;
* }
*
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
* /// succeeds. Returns zero if both calls meet expectation.
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
* public returns (uint) {
* ...
* }
* // The following function is generated if the protobuf field
* // "Contract.test" is equal to "RETURNDATA_CODER".
* function test_returndata_external() external returns (string memory, bytes memory)
* {
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
* bytes memory lv_0 = "1";
* return (sv_0, lv_0);
* }
*
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
* internal pure returns (bytes memory, bytes memory) {
* ...
* }
* // The following function is generated if the protobuf field
* // "Contract.test" is equal to "CALLDATA_CODER".
* function test_calldata_coding() internal returns (uint) {
* // Local variable
* bytes lv_1 = "1";
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
* uint returnVal = this.coder_public(sv_0, lv_1);
* if (returnVal != 0)
* return returnVal;
* // Since the return codes in the public and external coder functions are identical
* // we offset error code by a fixed amount (200000) for differentiation.
* returnVal = this.coder_external(sv_0, lv_1);
* if (returnVal != 0)
* return 200000 + returnVal;
* // Encode parameters
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
* if (returnVal != 0)
* return returnVal;
*
* /// Compares two dynamically sized bytes arrays for equality.
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
* ...
* }
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
* if (returnVal != 0)
* return uint(200000) + returnVal;
* // Return zero if all checks pass.
* return 0;
* }
*
* // Public function that is called by test() function. Accepts one or more arguments and returns
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
* function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) {
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
* return 1;
* if (!bytesCompare(c_1, "1"))
* return 2;
* return 0;
* }
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
* /// succeeds. Returns zero if both calls meet expectation.
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
* public returns (uint) {
* ...
* }
*
* // External function that is called by test() function. Accepts one or more arguments and returns
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
* return 1;
* if (!bytesCompare(c_1, "1"))
* return 2;
* return 0;
* }
* }
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
* internal pure returns (bytes memory, bytes memory) {
* ...
* }
*
* /// Compares two dynamically sized bytes arrays for equality.
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
* ...
* }
*
* // Public function that is called by test() function. Accepts one or more arguments and returns
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
* function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) {
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
* return 1;
* if (!bytesCompare(c_1, "1"))
* return 2;
* return 0;
* }
*
* // External function that is called by test() function. Accepts one or more arguments and returns
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
* return 1;
* if (!bytesCompare(c_1, "1"))
* return 2;
* return 0;
* }
* }
*/
namespace dev
{
namespace test
@@ -242,6 +276,20 @@ private:
Delimiter _delimiter = Delimiter::ADD
);
/// Append types to typed stream used by returndata coders.
void appendTypes(
bool _isValueType,
std::string const& _typeString,
Delimiter _delimiter
);
/// Append typed return value.
void appendTypedReturn(
bool _isValueType,
std::string const& _typeString,
Delimiter _delimiter
);
/// Returns a Solidity variable declaration statement
/// @param _type: string containing Solidity type of the
/// variable to be declared.
@@ -262,11 +310,17 @@ private:
/// Return comma separated typed function parameters as string
std::string typedParametersAsString(CalleeType _calleeType);
/// Return Solidity helper functions as string
std::string helperFunctions();
/// Return commonly used Solidity helper functions as string
std::string commonHelperFunctions();
/// Return top-level test code as string
std::string testCode(unsigned _invalidLength);
/// Return helper functions used to test calldata coding
std::string calldataHelperFunctions();
/// Return top-level calldata coder test function as string
std::string testCallDataFunction(unsigned _invalidLength);
/// Return top-level returndata coder test function as string
std::string testReturnDataFunction();
/// Return the next variable count that is used for
/// variable naming.
@@ -276,15 +330,30 @@ private:
}
/// Return a pair of names for Solidity variable and the same variable when
/// passed as a function parameter.
static std::pair<std::string, std::string> newVarNames(unsigned _varCounter)
/// passed either as a function parameter or used to store the tuple
/// returned from a function.
/// @param _varCounter: name suffix
/// @param _stateVar: predicate that is true for state variables, false otherwise
std::pair<std::string, std::string> newVarNames(unsigned _varCounter, bool _stateVar)
{
std::string varName = _stateVar ? s_stateVarNamePrefix : s_localVarNamePrefix;
return std::make_pair(
s_varNamePrefix + std::to_string(_varCounter),
s_paramNamePrefix + std::to_string(_varCounter)
varName + std::to_string(_varCounter),
paramName() + std::to_string(_varCounter)
);
}
std::string paramName()
{
switch (m_test)
{
case Contract_Test::Contract_Test_CALLDATA_CODER:
return s_paramNamePrefix;
case Contract_Test::Contract_Test_RETURNDATA_CODER:
return s_localVarNamePrefix;
}
}
/// Checks if the last dynamically encoded Solidity type is right
/// padded, returning true if it is and false otherwise.
bool isLastDynParamRightPadded()
@@ -303,6 +372,12 @@ private:
/// Contains typed parameter list to be passed to callee functions
std::ostringstream m_typedParamsExternal;
std::ostringstream m_typedParamsPublic;
/// Contains type stream to be used in returndata coder function
/// signature
std::ostringstream m_types;
std::ostringstream m_typedReturn;
/// Argument names to be passed to coder functions
std::ostringstream m_argsCoder;
/// Predicate that is true if we are in contract scope
bool m_isStateVar;
unsigned m_counter;
@@ -316,9 +391,12 @@ private:
/// Struct counter
unsigned m_structCounter;
unsigned m_numStructsAdded;
/// Enum stating abiv2 coder to be tested
Contract_Test m_test;
/// Prefixes for declared and parameterized variable names
static auto constexpr s_varNamePrefix = "x_";
static auto constexpr s_paramNamePrefix = "c_";
static auto constexpr s_localVarNamePrefix = "lv_";
static auto constexpr s_stateVarNamePrefix = "sv_";
static auto constexpr s_paramNamePrefix = "p_";
};
/// Visitor interface for Solidity protobuf types.
@@ -857,4 +935,4 @@ public:
};
}
}
}
}