diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index c826bfeae..fe950d379 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 6 + EVMC_ABI_VERSION = 7 }; @@ -153,9 +153,15 @@ struct evmc_tx_context int64_t block_timestamp; /**< The block timestamp. */ int64_t block_gas_limit; /**< The block gas limit. */ evmc_uint256be block_difficulty; /**< The block difficulty. */ + evmc_uint256be chain_id; /**< The blockchain's ChainID. */ }; -struct evmc_context; +/** + * @struct evmc_host_context + * The opaque data type representing the Host execution context. + * @see evmc_execute_fn(). + */ +struct evmc_host_context; /** * Get transaction context callback function. @@ -166,7 +172,7 @@ struct evmc_context; * @param context The pointer to the Host execution context. * @return The transaction context. */ -typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context); +typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_host_context* context); /** * Get block hash callback function. @@ -180,7 +186,7 @@ typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* co * @return The block hash or null bytes * if the information about the block is not available. */ -typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number); +typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context, int64_t number); /** * The execution status code. @@ -420,7 +426,8 @@ struct evmc_result * @param address The address of the account the query is about. * @return true if exists, false otherwise. */ -typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address); +typedef bool (*evmc_account_exists_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get storage callback function. @@ -433,7 +440,7 @@ typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_ * @return The storage value at the given storage key or null bytes * if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key); @@ -492,7 +499,7 @@ enum evmc_storage_status * @param value The value to be stored. * @return The effect on the storage item. */ -typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* context, +typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key, const evmc_bytes32* value); @@ -506,7 +513,7 @@ typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* con * @param address The address of the account. * @return The balance of the given account or 0 if the account does not exist. */ -typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, +typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_host_context* context, const evmc_address* address); /** @@ -519,7 +526,8 @@ typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, * @param address The address of the account. * @return The size of the code in the account or 0 if the account does not exist. */ -typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address); +typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get code size callback function. @@ -532,28 +540,27 @@ typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc * @param address The address of the account. * @return The hash of the code in the account or null bytes if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_host_context* context, const evmc_address* address); /** * Copy code callback function. * - * This callback function is used by an EVM to request a copy of the code - * of the given account to the memory buffer provided by the EVM. - * The Client MUST copy the requested code, starting with the given offset, - * to the provided memory buffer up to the size of the buffer or the size of - * the code, whichever is smaller. + * This callback function is used by an EVM to request a copy of the code + * of the given account to the memory buffer provided by the EVM. + * The Client MUST copy the requested code, starting with the given offset, + * to the provided memory buffer up to the size of the buffer or the size of + * the code, whichever is smaller. * - * @param context The pointer to the Client execution context. - * @see ::evmc_context. - * @param address The address of the account. - * @param code_offset The offset of the code to copy. - * @param buffer_data The pointer to the memory buffer allocated by the EVM - * to store a copy of the requested code. - * @param buffer_size The size of the memory buffer. - * @return The number of bytes copied to the buffer by the Client. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the account. + * @param code_offset The offset of the code to copy. + * @param buffer_data The pointer to the memory buffer allocated by the EVM + * to store a copy of the requested code. + * @param buffer_size The size of the memory buffer. + * @return The number of bytes copied to the buffer by the Client. */ -typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, +typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context, const evmc_address* address, size_t code_offset, uint8_t* buffer_data, @@ -562,34 +569,31 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, /** * Selfdestruct callback function. * - * This callback function is used by an EVM to SELFDESTRUCT given contract. - * The execution of the contract will not be stopped, that is up to the EVM. + * This callback function is used by an EVM to SELFDESTRUCT given contract. + * The execution of the contract will not be stopped, that is up to the EVM. * - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract to be selfdestructed. - * @param beneficiary The address where the remaining ETH is going to be - * transferred. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract to be selfdestructed. + * @param beneficiary The address where the remaining ETH is going to be transferred. */ -typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context, +typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_address* beneficiary); /** * Log callback function. * - * This callback function is used by an EVM to inform about a LOG that happened - * during an EVM bytecode execution. - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract that generated the log. - * @param data The pointer to unindexed data attached to the log. - * @param data_size The length of the data. - * @param topics The pointer to the array of topics attached to the log. - * @param topics_count The number of the topics. Valid values are between - * 0 and 4 inclusively. + * This callback function is used by an EVM to inform about a LOG that happened + * during an EVM bytecode execution. + * + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract that generated the log. + * @param data The pointer to unindexed data attached to the log. + * @param data_size The length of the data. + * @param topics The pointer to the array of topics attached to the log. + * @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively. */ -typedef void (*evmc_emit_log_fn)(struct evmc_context* context, +typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context, const evmc_address* address, const uint8_t* data, size_t data_size, @@ -599,11 +603,11 @@ typedef void (*evmc_emit_log_fn)(struct evmc_context* context, /** * Pointer to the callback function supporting EVM calls. * - * @param context The pointer to the Host execution context. - * @param msg The call parameters. + * @param context The pointer to the Host execution context. + * @param msg The call parameters. * @return The result of the call. */ -typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context, +typedef struct evmc_result (*evmc_call_fn)(struct evmc_host_context* context, const struct evmc_message* msg); /** @@ -654,31 +658,15 @@ struct evmc_host_interface }; -/** - * Execution context managed by the Host. - * - * The Host MUST pass the pointer to the execution context to ::evmc_execute_fn. - * The VM MUST pass the same pointer back to the Host in every callback function. - * The context MUST contain at least the function table defining - * the context callback interface. - * Optionally, the Host MAY include in the context additional data. - */ -struct evmc_context -{ - /** The Host interface. */ - const struct evmc_host_interface* host; -}; - - /* Forward declaration. */ -struct evmc_instance; +struct evmc_vm; /** - * Destroys the EVM instance. + * Destroys the VM instance. * - * @param evm The EVM instance to be destroyed. + * @param vm The VM instance to be destroyed. */ -typedef void (*evmc_destroy_fn)(struct evmc_instance* evm); +typedef void (*evmc_destroy_fn)(struct evmc_vm* vm); /** * Possible outcomes of evmc_set_option. @@ -691,19 +679,19 @@ enum evmc_set_option_result }; /** - * Configures the EVM instance. + * Configures the VM instance. * - * Allows modifying options of the EVM instance. - * Options: - * - code cache behavior: on, off, read-only, ... - * - optimizations, + * Allows modifying options of the VM instance. + * Options: + * - code cache behavior: on, off, read-only, ... + * - optimizations, * - * @param evm The EVM instance to be configured. - * @param name The option name. NULL-terminated string. Cannot be NULL. - * @param value The new option value. NULL-terminated string. Cannot be NULL. - * @return The outcome of the operation. + * @param vm The VM instance to be configured. + * @param name The option name. NULL-terminated string. Cannot be NULL. + * @param value The new option value. NULL-terminated string. Cannot be NULL. + * @return The outcome of the operation. */ -typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm, +typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_vm* vm, char const* name, char const* value); @@ -762,6 +750,7 @@ enum evmc_revision * The Petersburg revision. * * Other names: Constantinople2, ConstantinopleFix. + * * https://eips.ethereum.org/EIPS/eip-1716 */ EVMC_PETERSBURG = 6, @@ -773,23 +762,15 @@ enum evmc_revision */ EVMC_ISTANBUL = 7, + /** + * The Berlin revision. + * + * The spec draft: https://eips.ethereum.org/EIPS/eip-2070. + */ + EVMC_BERLIN = 8, + /** The maximum EVM revision supported. */ - EVMC_MAX_REVISION = EVMC_ISTANBUL, - - - /** - * Reserved for the post-Constantinople upgrade. - * - * @deprecated Replaced with ::EVMC_PETERSBURG. - */ - EVMC_CONSTANTINOPLE2 EVMC_DEPRECATED = EVMC_PETERSBURG, - - /** - * The latests EVM revision supported. - * - * @deprecated Replaced with ::EVMC_MAX_REVISION. - */ - EVMC_LATEST_REVISION EVMC_DEPRECATED = EVMC_MAX_REVISION + EVMC_MAX_REVISION = EVMC_BERLIN }; @@ -798,19 +779,22 @@ enum evmc_revision * * This function MAY be invoked multiple times for a single VM instance. * - * @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 vm The VM instance. This argument MUST NOT be NULL. + * @param host The Host interface. This argument MUST NOT be NULL unless + * the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param context The opaque pointer to the Host execution context. + * This argument MAY be NULL. The VM MUST pass the same + * pointer to the methods of the @p host interface. + * The VM MUST NOT dereference the pointer. * @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, - struct evmc_context* context, +typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, @@ -855,98 +839,20 @@ typedef uint32_t evmc_capabilities_flagset; * Return the supported capabilities of the VM instance. * * This function MAY be invoked multiple times for a single VM instance, - * and its value MAY be influenced by calls to evmc_instance::set_option. + * and its value MAY be influenced by calls to evmc_vm::set_option. * - * @param instance The EVM instance. - * @return The supported capabilities of the VM. @see evmc_capabilities. + * @param vm The VM instance. + * @return The supported capabilities of the VM. @see evmc_capabilities. */ -typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance); - -/** - * The opaque type representing a Client-side tracer object. - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - */ -struct evmc_tracer_context; - -/** - * The callback to trace instructions execution in an EVM. - * - * This function informs the Client what instruction has been executed in the EVM implementation - * and what are the results of executing this particular instruction. - * The message level information (like call depth, destination address, etc.) are not provided here. - * 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. - * @param status_code The status code of the instruction execution. - * @param gas_left The amount of the gas left after the instruction execution. - * @param stack_num_items The current EVM stack height after the instruction execution. - * @param pushed_stack_item The top EVM stack item pushed as the result of the instruction - * execution. This value is null when the instruction does not push - * anything to the stack. - * @param memory_size The size of the EVM memory after the instruction execution. - * @param changed_memory_offset The offset in number of bytes of the beginning of the memory area - * modified as the result of the instruction execution. - * The Client MAY use this information together with - * @p changed_memory_size and @p changed_memory to incrementally - * update the copy of the full VM's memory. - * @param changed_memory_size The size of the memory area modified as the result of - * the instruction execution. - * @param changed_memory The pointer to the memory area modified as the result of - * the instruction execution. - * The Client MAY access the pointed memory area - * (limited by the @p changed_memory_size) only during the current - * execution of the evmc_trace_callback(). - * The pointer MUST NOT be stored by the Client. - * The Client MUST NOT assume that - * `changed_memory - changed_memory_offset` is a valid base pointer - * of the VM memory. - */ -typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context, - size_t code_offset, - enum evmc_status_code status_code, - int64_t gas_left, - size_t stack_num_items, - const evmc_uint256be* pushed_stack_item, - size_t memory_size, - size_t changed_memory_offset, - size_t changed_memory_size, - const uint8_t* changed_memory); - -/** - * Sets the EVM instruction tracer. - * - * When the tracer is set in the EVM instance, the EVM SHOULD call back the tracer with information - * about instructions execution in the EVM. - * @see ::evmc_trace_callback. - * - * 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. - * @param context The Client-side tracer context. This argument MAY be NULL in case the tracer - * does not require any context. This argument MUST be NULL if the callback - * argument is NULL. - */ -typedef void (*evmc_set_tracer_fn)(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context); +typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_vm* vm); /** - * The EVM instance. + * The VM instance. * * Defines the base struct of the VM implementation. */ -struct evmc_instance +struct evmc_vm { /** * EVMC ABI version implemented by the VM instance. @@ -973,14 +879,14 @@ struct evmc_instance const char* version; /** - * Pointer to function destroying the EVM instance. + * Pointer to function destroying the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ evmc_destroy_fn destroy; /** - * Pointer to function executing a code by the EVM instance. + * Pointer to function executing a code by the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ @@ -998,17 +904,6 @@ struct evmc_instance */ evmc_get_capabilities_fn get_capabilities; - /** - * 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; - /** * Optional pointer to function modifying VM's options. * @@ -1033,9 +928,9 @@ struct evmc_instance * For example, the shared library with the "beta-interpreter" implementation may be named * `libbeta-interpreter.so`. * - * @return EVM instance or NULL indicating instance creation failure. + * @return The VM instance or NULL indicating instance creation failure. */ -struct evmc_instance* evmc_create_example_vm(void); +struct evmc_vm* evmc_create_example_vm(void); #endif #if __cplusplus diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index ff486a232..1b52ae8d0 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -334,49 +334,46 @@ public: } }; -/// @copybrief evmc_instance +class Host; + +/// @copybrief evmc_vm /// -/// This is a RAII wrapper for evmc_instance and objects of this type +/// This is a RAII wrapper for evmc_vm and objects of this type /// automatically destroys the VM instance. -class vm +class VM { public: - vm() noexcept = default; + VM() noexcept = default; - /// Converting constructor from evmc_instance. - explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {} + /// Converting constructor from evmc_vm. + explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} /// Destructor responsible for automatically destroying the VM instance. - ~vm() noexcept + ~VM() noexcept { if (m_instance) m_instance->destroy(m_instance); } - vm(const vm&) = delete; - vm& operator=(const vm&) = delete; + VM(const VM&) = delete; + VM& operator=(const VM&) = delete; /// Move constructor. - vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } + VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } /// Move assignment operator. - vm& operator=(vm&& other) noexcept + VM& operator=(VM&& other) noexcept { - this->~vm(); + 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. - vm(evmc_instance* instance, - std::initializer_list> options) noexcept - : m_instance{instance} - { - for (auto option : options) - set_option(option.first, option.second); - } + /// with the provided list of options. + inline VM(evmc_vm* vm, + std::initializer_list> options) noexcept; /// Checks if contains a valid pointer to the VM instance. explicit operator bool() const noexcept { return m_instance != nullptr; } @@ -384,13 +381,13 @@ public: /// Checks whenever the VM instance is ABI compatible with the current EVMC API. bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } - /// @copydoc evmc_instance::name + /// @copydoc evmc_vm::name char const* name() const noexcept { return m_instance->name; } - /// @copydoc evmc_instance::version + /// @copydoc evmc_vm::version char const* version() const noexcept { return m_instance->version; } - /// @copydoc evmc::instance::get_capabilities + /// @copydoc evmc::vm::get_capabilities evmc_capabilities_flagset get_capabilities() const noexcept { return m_instance->get_capabilities(m_instance); @@ -403,19 +400,53 @@ public: } /// @copydoc evmc_execute() - result execute(evmc_context& ctx, + result execute(const evmc_host_interface& host, + evmc_host_context* ctx, evmc_revision rev, const evmc_message& msg, const uint8_t* code, size_t code_size) noexcept { - return result{m_instance->execute(m_instance, &ctx, rev, &msg, code, code_size)}; + return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)}; + } + + /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. + inline result execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept; + + /// Executes code without the Host context. + /// + /// The same as + /// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision, + /// const evmc_message&, const uint8_t*, size_t), + /// but without providing the Host context and interface. + /// This method is for experimental precompiles support where execution is + /// guaranteed not to require any Host access. + result execute(evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{ + m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)}; } private: - evmc_instance* m_instance = nullptr; + evmc_vm* m_instance = nullptr; }; +inline VM::VM(evmc_vm* vm, + std::initializer_list> options) noexcept + : m_instance{vm} +{ + for (const auto& option : options) + set_option(option.first, option.second); +} + + /// The EVMC Host interface class HostInterface { @@ -471,46 +502,52 @@ public: /// Wrapper around EVMC host context / host interface. /// -/// To be used by VM implementations as better alternative to using ::evmc_context directly. +/// To be used by VM implementations as better alternative to using ::evmc_host_context directly. class HostContext : public HostInterface { - evmc_context* context = nullptr; + const evmc_host_interface* host = nullptr; + evmc_host_context* context = nullptr; evmc_tx_context tx_context = {}; public: - /// Implicit converting constructor from evmc_context. - HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT + /// Default constructor for null Host context. + HostContext() = default; + + /// Constructor from the EVMC Host primitives. + HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept + : host{interface}, context{ctx} + {} bool account_exists(const address& address) noexcept final { - return context->host->account_exists(context, &address); + return host->account_exists(context, &address); } bytes32 get_storage(const address& address, const bytes32& key) noexcept final { - return context->host->get_storage(context, &address, &key); + return host->get_storage(context, &address, &key); } 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); + return host->set_storage(context, &address, &key, &value); } uint256be get_balance(const address& address) noexcept final { - return context->host->get_balance(context, &address); + return host->get_balance(context, &address); } size_t get_code_size(const address& address) noexcept final { - return context->host->get_code_size(context, &address); + return host->get_code_size(context, &address); } bytes32 get_code_hash(const address& address) noexcept final { - return context->host->get_code_hash(context, &address); + return host->get_code_hash(context, &address); } size_t copy_code(const address& address, @@ -518,17 +555,17 @@ public: uint8_t* buffer_data, size_t buffer_size) noexcept final { - return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); + return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } void selfdestruct(const address& addr, const address& beneficiary) noexcept final { - context->host->selfdestruct(context, &addr, &beneficiary); + host->selfdestruct(context, &addr, &beneficiary); } result call(const evmc_message& message) noexcept final { - return result{context->host->call(context, &message)}; + return result{host->call(context, &message)}; } /// @copydoc HostInterface::get_tx_context() @@ -540,13 +577,13 @@ public: evmc_tx_context get_tx_context() noexcept final { if (tx_context.block_timestamp == 0) - tx_context = context->host->get_tx_context(context); + tx_context = host->get_tx_context(context); return tx_context; } bytes32 get_block_hash(int64_t number) noexcept final { - return context->host->get_block_hash(context, number); + return host->get_block_hash(context, number); } void emit_log(const address& addr, @@ -555,7 +592,7 @@ public: const bytes32 topics[], size_t topics_count) noexcept final { - context->host->emit_log(context, &addr, data, data_size, topics, topics_count); + host->emit_log(context, &addr, data, data_size, topics, topics_count); } }; @@ -563,89 +600,135 @@ public: /// /// When implementing EVMC Host, you can directly inherit from the evmc::Host class. /// This way your implementation will be simpler by avoiding manual handling -/// of the ::evmc_context and the ::evmc_context::host. -class Host : public HostInterface, public evmc_context +/// of the ::evmc_host_context and the ::evmc_host_interface. +class Host : public HostInterface { public: - inline Host() noexcept; + /// Provides access to the global host interface. + /// @returns Reference to the host interface object. + static const evmc_host_interface& get_interface() noexcept; + + /// Converts the Host object to the opaque host context pointer. + /// @returns Pointer to evmc_host_context. + evmc_host_context* to_context() noexcept { return reinterpret_cast(this); } + + /// Converts the opaque host context pointer back to the original Host object. + /// @tparam DerivedClass The class derived from the Host class. + /// @param context The opaque host context pointer. + /// @returns The pointer to DerivedClass. + template + static DerivedClass* from_context(evmc_host_context* context) noexcept + { + // Get pointer of the Host base class. + auto* h = reinterpret_cast(context); + + // Additional downcast, only possible if DerivedClass inherits from Host. + return static_cast(h); + } }; + +inline result VM::execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept +{ + return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size); +} + + namespace internal { -inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept +inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->account_exists(*addr); + return Host::from_context(h)->account_exists(*addr); } -inline evmc_bytes32 get_storage(evmc_context* h, + +inline evmc_bytes32 get_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key) noexcept { - return static_cast(h)->get_storage(*addr, *key); + return Host::from_context(h)->get_storage(*addr, *key); } -inline evmc_storage_status set_storage(evmc_context* h, + +inline evmc_storage_status set_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key, const evmc_bytes32* value) noexcept { - return static_cast(h)->set_storage(*addr, *key, *value); + return Host::from_context(h)->set_storage(*addr, *key, *value); } -inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_balance(*addr); + return Host::from_context(h)->get_balance(*addr); } -inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept + +inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_size(*addr); + return Host::from_context(h)->get_code_size(*addr); } -inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_hash(*addr); + return Host::from_context(h)->get_code_hash(*addr); } -inline size_t copy_code(evmc_context* h, + +inline size_t copy_code(evmc_host_context* h, const evmc_address* addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) noexcept { - return static_cast(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); + return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); } -inline void selfdestruct(evmc_context* h, + +inline void selfdestruct(evmc_host_context* h, const evmc_address* addr, const evmc_address* beneficiary) noexcept { - static_cast(h)->selfdestruct(*addr, *beneficiary); + Host::from_context(h)->selfdestruct(*addr, *beneficiary); } -inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept + +inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept { - return static_cast(h)->call(*msg).release_raw(); + return Host::from_context(h)->call(*msg).release_raw(); } -inline evmc_tx_context get_tx_context(evmc_context* h) noexcept + +inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept { - return static_cast(h)->get_tx_context(); + return Host::from_context(h)->get_tx_context(); } -inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept + +inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept { - return static_cast(h)->get_block_hash(block_number); + return Host::from_context(h)->get_block_hash(block_number); } -inline void emit_log(evmc_context* h, + +inline void emit_log(evmc_host_context* h, const evmc_address* addr, const uint8_t* data, size_t data_size, const evmc_bytes32 topics[], size_t num_topics) noexcept { - static_cast(h)->emit_log(*addr, data, data_size, static_cast(topics), + Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics), num_topics); } - -constexpr evmc_host_interface interface{ - account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash, - copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log, -}; } // namespace internal -inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {} - +inline const evmc_host_interface& Host::get_interface() noexcept +{ + static constexpr evmc_host_interface interface{ + ::evmc::internal::account_exists, ::evmc::internal::get_storage, + ::evmc::internal::set_storage, ::evmc::internal::get_balance, + ::evmc::internal::get_code_size, ::evmc::internal::get_code_hash, + ::evmc::internal::copy_code, ::evmc::internal::selfdestruct, + ::evmc::internal::call, ::evmc::internal::get_tx_context, + ::evmc::internal::get_block_hash, ::evmc::internal::emit_log}; + return interface; +} } // namespace evmc diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h index 2c4dbceae..36febfd72 100644 --- a/test/evmc/helpers.h +++ b/test/evmc/helpers.h @@ -22,36 +22,35 @@ #include /** - * Returns true if the VM instance has a compatible ABI version. + * Returns true if the VM has a compatible ABI version. */ -static inline int evmc_is_abi_compatible(struct evmc_instance* instance) +static inline bool evmc_is_abi_compatible(struct evmc_vm* vm) { - return instance->abi_version == EVMC_ABI_VERSION; + return vm->abi_version == EVMC_ABI_VERSION; } /** - * Returns the name of the VM instance. + * Returns the name of the VM. */ -static inline const char* evmc_vm_name(struct evmc_instance* instance) +static inline const char* evmc_vm_name(struct evmc_vm* vm) { - return instance->name; + return vm->name; } /** - * Returns the version of the VM instance. + * Returns the version of the VM. */ -static inline const char* evmc_vm_version(struct evmc_instance* instance) +static inline const char* evmc_vm_version(struct evmc_vm* vm) { - return instance->version; + return vm->version; } /** - * Checks if the VM instance has the given capability. + * Checks if the VM has the given capability. * * @see evmc_get_capabilities_fn */ -static inline bool evmc_vm_has_capability(struct evmc_instance* vm, - enum evmc_capabilities capability) +static inline bool evmc_vm_has_capability(struct evmc_vm* vm, enum evmc_capabilities capability) { return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0; } @@ -61,52 +60,39 @@ static inline bool evmc_vm_has_capability(struct evmc_instance* vm, * * @see evmc_destroy_fn */ -static inline void evmc_destroy(struct evmc_instance* instance) +static inline void evmc_destroy(struct evmc_vm* vm) { - instance->destroy(instance); + vm->destroy(vm); } /** - * Sets the option for the VM instance, if the feature is supported by the VM. + * Sets the option for the VM, if the feature is supported by the VM. * * @see evmc_set_option_fn */ -static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance, +static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm, char const* name, char const* value) { - if (instance->set_option) - return instance->set_option(instance, name, value); + if (vm->set_option) + return vm->set_option(vm, name, value); return EVMC_SET_OPTION_INVALID_NAME; } -/** - * Sets the tracer callback for the VM instance, if the feature is supported by the VM. - * - * @see evmc_set_tracer_fn - */ -EVMC_DEPRECATED -static inline void evmc_set_tracer(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context) -{ - if (instance->set_tracer) - instance->set_tracer(instance, callback, context); -} - /** * Executes code in the VM instance. * * @see evmc_execute_fn. */ -static inline struct evmc_result evmc_execute(struct evmc_instance* instance, - struct evmc_context* context, +static inline struct evmc_result evmc_execute(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, size_t code_size) { - return instance->execute(instance, context, rev, msg, code, code_size); + return vm->execute(vm, host, context, rev, msg, code, code_size); } /// The evmc_result release function using free() for releasing the memory. diff --git a/test/evmc/loader.c b/test/evmc/loader.c index 30bb39023..d82005438 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -152,8 +152,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro char* base_name = prefixed_name + prefix_length; strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos); - // Trim the file extension. - char* ext_pos = strrchr(prefixed_name, '.'); + // Trim all file extensions. + char* ext_pos = strchr(prefixed_name, '.'); if (ext_pos) *ext_pos = 0; @@ -163,15 +163,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro *dash_pos++ = '_'; // Search for the built function name. - while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL) - { - // Shorten the base name by skipping the `word_` segment. - const char* shorter_name_pos = strchr(base_name, '_'); - if (!shorter_name_pos) - break; - - memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1); - } + create_fn = DLL_GET_CREATE_FN(handle, prefixed_name); if (!create_fn) create_fn = DLL_GET_CREATE_FN(handle, "evmc_create"); @@ -196,8 +188,7 @@ const char* evmc_last_error_msg() return m; } -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code) +struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code) { // First load the DLL. This also resets the last_error_msg; evmc_create_fn create_fn = evmc_load(filename, error_code); @@ -207,21 +198,21 @@ struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; - struct evmc_instance* instance = create_fn(); - if (!instance) + struct evmc_vm* vm = create_fn(); + if (!vm) { - ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, - "creating EVMC instance of %s has failed", filename); + ec = set_error(EVMC_LOADER_VM_CREATION_FAILURE, "creating EVMC VM of %s has failed", + filename); goto exit; } - if (!evmc_is_abi_compatible(instance)) + if (!evmc_is_abi_compatible(vm)) { ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, "EVMC ABI version %d of %s mismatches the expected version %d", - instance->abi_version, filename, EVMC_ABI_VERSION); - evmc_destroy(instance); - instance = NULL; + vm->abi_version, filename, EVMC_ABI_VERSION); + evmc_destroy(vm); + vm = NULL; goto exit; } @@ -229,7 +220,7 @@ exit: if (error_code) *error_code = ec; - return instance; + return vm; } /// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr. @@ -255,11 +246,10 @@ static char* get_token(char** str_ptr, char delim) return str; } -struct evmc_instance* evmc_load_and_configure(const char* config, - enum evmc_loader_error_code* error_code) +struct evmc_vm* 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; + struct evmc_vm* vm = NULL; char config_copy_buffer[PATH_MAX_LENGTH]; if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0) @@ -273,14 +263,14 @@ struct evmc_instance* evmc_load_and_configure(const char* config, char* options = config_copy_buffer; const char* path = get_token(&options, ','); - instance = evmc_load_and_create(path, error_code); - if (!instance) + vm = evmc_load_and_create(path, error_code); + if (!vm) return NULL; - if (instance->set_option == NULL && strlen(options) != 0) + if (vm->set_option == NULL && strlen(options) != 0) { ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options", - instance->name, path); + vm->name, path); goto exit; } @@ -293,18 +283,18 @@ struct evmc_instance* evmc_load_and_configure(const char* config, // 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); + enum evmc_set_option_result r = vm->set_option(vm, 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); + vm->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, + "%s (%s): unsupported value '%s' for option '%s'", vm->name, path, option, name); goto exit; } @@ -315,9 +305,9 @@ exit: *error_code = ec; if (ec == EVMC_LOADER_SUCCESS) - return instance; + return vm; - if (instance) - evmc_destroy(instance); + if (vm) + evmc_destroy(vm); return NULL; } diff --git a/test/evmc/loader.h b/test/evmc/loader.h index 0c50a81f4..523139e31 100644 --- a/test/evmc/loader.h +++ b/test/evmc/loader.h @@ -19,7 +19,7 @@ extern "C" { #endif /** The function pointer type for EVMC create functions. */ -typedef struct evmc_instance* (*evmc_create_fn)(void); +typedef struct evmc_vm* (*evmc_create_fn)(void); /** Error codes for the EVMC loader. */ enum evmc_loader_error_code @@ -37,7 +37,7 @@ enum evmc_loader_error_code EVMC_LOADER_INVALID_ARGUMENT = 3, /** The creation of a VM instance has failed. */ - EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, + EVMC_LOADER_VM_CREATION_FAILURE = 4, /** The ABI version of the VM instance has mismatched. */ EVMC_LOADER_ABI_VERSION_MISMATCH = 5, @@ -61,21 +61,16 @@ enum evmc_loader_error_code * After the DLL is successfully loaded the function tries to find the EVM create function in the * library. The `filename` is used to guess the EVM name and the name of the create function. * The create function name is constructed by the following rules. Consider example path: - * "/ethereum/libexample-interpreter.so". + * "/ethereum/libexample-interpreter.so.1.0". * - the filename is taken from the path: - * "libexample-interpreter.so", - * - the "lib" prefix and file extension are stripped from the name: + * "libexample-interpreter.so.1.0", + * - the "lib" prefix and all file extensions are stripped from the name: * "example-interpreter" * - all "-" are replaced with "_" to construct _base name_: * "example_interpreter", * - the function name "evmc_create_" + _base name_ is searched in the library: * "evmc_create_example_interpreter", - * - if function not found, the _base name_ is shorten by skipping the first word separated by "_": - * "interpreter", - * - then, the function of the shorter name "evmc_create_" + _base name_ is searched in the library: - * "evmc_create_interpreter", - * - the name shortening continues until a function is found or the name cannot be shorten more, - * - lastly, when no function found, the function name "evmc_create" is searched in the library. + * - if the function is not found, the function name "evmc_create" is searched in the library. * * If the create function is found in the library, the pointer to the function is returned. * Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. @@ -98,7 +93,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * * This is a macro for creating the VM instance with the function returned from evmc_load(). * The function signals the same errors as evmc_load() and additionally: - * - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL, + * - ::EVMC_LOADER_VM_CREATION_FAILURE when the create function returns NULL, * - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different * from the ABI version of this library (::EVMC_ABI_VERSION). * @@ -114,8 +109,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the created VM or NULL in case of error. */ -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code); +struct evmc_vm* 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. @@ -151,8 +145,8 @@ struct evmc_instance* evmc_load_and_create(const char* filename, * ::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); +struct evmc_vm* evmc_load_and_configure(const char* config, + enum evmc_loader_error_code* error_code); /** * Returns the human-readable message describing the most recent error