From 882cd3e2854dd03da851e7bb0a2295719ff08c20 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 10:34:12 +0000 Subject: [PATCH 1/2] Update EVMC to 6.3.1 --- test/EVMHost.cpp | 33 ++- test/EVMHost.h | 49 +++-- test/ExecutionFramework.cpp | 3 +- test/evmc/evmc.h | 35 +++- test/evmc/evmc.hpp | 388 ++++++++++++++++++++++++++++++++---- test/evmc/helpers.h | 55 +++++ test/evmc/helpers.hpp | 64 +++--- test/evmc/loader.c | 99 ++++++++- test/evmc/loader.h | 45 ++++- 9 files changed, 652 insertions(+), 119 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 600b64c3f..4cefb97b2 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -21,7 +21,6 @@ #include -#include #include #include @@ -91,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; } @@ -190,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; @@ -231,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 { @@ -253,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; diff --git a/test/EVMHost.h b/test/EVMHost.h index f66bdcc14..c01d27ad0 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -23,7 +23,6 @@ #include #include -#include #include @@ -47,11 +46,11 @@ public: struct Account { - evmc_uint256be balance = {}; + evmc::uint256be balance = {}; size_t nonce = 0; bytes code; - evmc_bytes32 codeHash = {}; - std::map storage; + evmc::bytes32 codeHash = {}; + std::map storage; }; struct LogEntry @@ -65,11 +64,11 @@ public: { size_t blockNumber; uint64_t timestamp; - std::map accounts; + std::map accounts; std::vector 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; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index a6b8188a1..9c08394ef 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -26,7 +26,6 @@ #include #include -#include #include @@ -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; diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 81cba6cff..c826bfeae 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -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; diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index 7d2344cdb..ff486a232 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -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 +struct integer_sequence +{ +}; + +template +using byte_sequence = integer_sequence; + +template +using char_sequence = integer_sequence; + + +template +struct concatenate; + +template +struct concatenate, byte_sequence> +{ + using type = byte_sequence; +}; + +template +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( + (D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10); +} + + +template +struct parse_digits; + +template +struct parse_digits> +{ + using type = byte_sequence(parse_hex_digit() << 4) | + parse_hex_digit()>; +}; + +template +struct parse_digits> +{ + using type = typename concatenate>::type, + typename parse_digits>::type>::type; +}; + + +template +struct parse_literal; + +template +struct parse_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 + static constexpr T create_from(byte_sequence) noexcept + { + return T{{{Bytes...}}}; + } + + static constexpr T get() noexcept + { + return create_from(typename parse_digits>::type{}); + } +}; + +template +struct parse_literal> +{ + static_assert(Digit == '0', "only 0 is allowed as a single digit literal"); + static constexpr T get() noexcept { return {}; } +}; + +template +constexpr T parse() noexcept +{ + return parse_literal>::get(); +} +} // namespace internal + +/// Literal for evmc::address. +template +constexpr address operator"" _address() noexcept +{ + return internal::parse(); +} + +/// Literal for evmc::bytes32. +template +constexpr bytes32 operator"" _bytes32() noexcept +{ + return internal::parse(); +} +} // 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(h)->emit_log(*addr, data, data_size, topics, num_topics); + static_cast(h)->emit_log(*addr, data, data_size, static_cast(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 +{ + /// 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(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 +{ + /// 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( + 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 diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h index e43cfd76a..2c4dbceae 100644 --- a/test/evmc/helpers.h +++ b/test/evmc/helpers.h @@ -18,6 +18,8 @@ #pragma once #include +#include +#include /** * 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. * diff --git a/test/evmc/helpers.hpp b/test/evmc/helpers.hpp index 1b420b2e5..f30eec87a 100644 --- a/test/evmc/helpers.hpp +++ b/test/evmc/helpers.hpp @@ -12,58 +12,78 @@ */ #pragma once -#include +#include #include #include +using evmc::is_zero; + /// The comparator for std::map. +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_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 +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; + + 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 +struct EVMC_DEPRECATED hash { /// Hash operator using FNV1a. - std::enable_if::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 +struct EVMC_DEPRECATED hash { /// Hash operator using FNV1a. - std::enable_if::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 diff --git a/test/evmc/loader.c b/test/evmc/loader.c index 51d7a85fb..30bb39023 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -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; +} diff --git a/test/evmc/loader.h b/test/evmc/loader.h index d8531af1d..0c50a81f4 100644 --- a/test/evmc/loader.h +++ b/test/evmc/loader.h @@ -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: + * + * ("," ["=" ])* + * + * 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. From 18542ebf31f26377eb9ccf31056a4f9b479d370c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 10:42:41 +0000 Subject: [PATCH 2/2] Use evmc_load_and_configure to support passing EVMC options --- test/EVMHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 4cefb97b2..b7755f28f 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -41,7 +41,7 @@ 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) { if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1))