diff --git a/.gitignore b/.gitignore index 4e28ef6..ec3abc0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ *.dll *.so *.dylib +*.exe \ No newline at end of file diff --git a/src/evmc.nim b/evmc/evmc.nim similarity index 100% rename from src/evmc.nim rename to evmc/evmc.nim diff --git a/src/evmc_nim.nim b/evmc/evmc_nim.nim similarity index 100% rename from src/evmc_nim.nim rename to evmc/evmc_nim.nim diff --git a/src/evmjit.nim b/evmc/evmjit.nim similarity index 100% rename from src/evmjit.nim rename to evmc/evmjit.nim diff --git a/tests/evmc_c/evmc.h b/tests/evmc_c/evmc.h new file mode 100644 index 0000000..3fc3ca6 --- /dev/null +++ b/tests/evmc_c/evmc.h @@ -0,0 +1,941 @@ +/** + * EVMC: Ethereum Client-VM Connector API + * + * @copyright + * Copyright 2016-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + * + * @defgroup EVMC EVMC + * @{ + */ +#ifndef EVMC_H +#define EVMC_H + +#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 6) +/** + * Portable declaration of "deprecated" attribute. + * + * Available for clang and GCC 6+ compilers. The older GCC compilers know + * this attribute, but it cannot be applied to enum elements. + */ +#define EVMC_DEPRECATED __attribute__((deprecated)) +#else +#define EVMC_DEPRECATED +#endif + + +#include /* Definition of bool, true and false. */ +#include /* Definition of size_t. */ +#include /* Definition of int64_t, uint64_t. */ + +#if __cplusplus +extern "C" { +#endif + +/* BEGIN Python CFFI declarations */ + +enum +{ + /** + * The EVMC ABI version number of the interface declared in this file. + * + * The EVMC ABI version always equals the major version number of the EVMC project. + * The Host SHOULD check if the ABI versions match when dynamically loading VMs. + * + * @see @ref versioning + */ + EVMC_ABI_VERSION = 7 +}; + + +/** + * The fixed size array of 32 bytes. + * + * 32 bytes of data capable of storing e.g. 256-bit hashes. + */ +typedef struct evmc_bytes32 +{ + /** The 32 bytes. */ + uint8_t bytes[32]; +} evmc_bytes32; + +/** + * The alias for evmc_bytes32 to represent a big-endian 256-bit integer. + */ +typedef struct evmc_bytes32 evmc_uint256be; + +/** Big-endian 160-bit hash suitable for keeping an Ethereum address. */ +typedef struct evmc_address +{ + /** The 20 bytes of the hash. */ + uint8_t bytes[20]; +} evmc_address; + +/** The kind of call-like instruction. */ +enum evmc_call_kind +{ + EVMC_CALL = 0, /**< Request CALL. */ + EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. Valid since Homestead. + The value param ignored. */ + EVMC_CALLCODE = 2, /**< Request CALLCODE. */ + EVMC_CREATE = 3, /**< Request CREATE. */ + EVMC_CREATE2 = 4 /**< Request CREATE2. Valid since Constantinople.*/ +}; + +/** The flags for ::evmc_message. */ +enum evmc_flags +{ + EVMC_STATIC = 1 /**< Static call mode. */ +}; + +/** + * The message describing an EVM call, + * including a zero-depth calls from a transaction origin. + */ +struct evmc_message +{ + /** The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used. */ + enum evmc_call_kind kind; + + /** + * Additional flags modifying the call execution behavior. + * In the current version the only valid values are ::EVMC_STATIC or 0. + */ + uint32_t flags; + + /** The call depth. */ + int32_t depth; + + /** The amount of gas for message execution. */ + int64_t gas; + + /** The destination of the message. */ + evmc_address destination; + + /** The sender of the message. */ + evmc_address sender; + + /** + * The message input data. + * + * This MAY be NULL. + */ + const uint8_t* input_data; + + /** + * The size of the message input data. + * + * If input_data is NULL this MUST be 0. + */ + size_t input_size; + + /** + * The amount of Ether transferred with the message. + */ + evmc_uint256be value; + + /** + * The optional value used in new contract address construction. + * + * Ignored unless kind is EVMC_CREATE2. + */ + evmc_bytes32 create2_salt; +}; + + +/** The transaction and block data for execution. */ +struct evmc_tx_context +{ + evmc_uint256be tx_gas_price; /**< The transaction gas price. */ + evmc_address tx_origin; /**< The transaction origin account. */ + evmc_address block_coinbase; /**< The miner of the block. */ + int64_t block_number; /**< The block number. */ + 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_host_context + * The opaque data type representing the Host execution context. + * @see evmc_execute_fn(). + */ +struct evmc_host_context; + +/** + * Get transaction context callback function. + * + * This callback function is used by an EVM to retrieve the transaction and + * block 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_host_context* context); + +/** + * Get block hash callback function. + * + * This callback function is used by a VM to query the hash of the header of the given block. + * If the information about the requested block is not available, then this is signalled by + * returning null bytes. + * + * @param context The pointer to the Host execution context. + * @param number The block number. + * @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_host_context* context, int64_t number); + +/** + * The execution status code. + * + * Successful execution is represented by ::EVMC_SUCCESS having value 0. + * + * Positive values represent failures defined by VM specifications with generic + * ::EVMC_FAILURE code of value 1. + * + * Status codes with negative values represent VM internal errors + * not provided by EVM specifications. These errors MUST not be passed back + * to the caller. They MAY be handled by the Client in predefined manner + * (see e.g. ::EVMC_REJECTED), otherwise internal errors are not recoverable. + * The generic representant of errors is ::EVMC_INTERNAL_ERROR but + * an EVM implementation MAY return negative status codes that are not defined + * in the EVMC documentation. + * + * @note + * In case new status codes are needed, please create an issue or pull request + * in the EVMC repository (https://github.com/ethereum/evmc). + */ +enum evmc_status_code +{ + /** Execution finished with success. */ + EVMC_SUCCESS = 0, + + /** Generic execution failure. */ + EVMC_FAILURE = 1, + + /** + * Execution terminated with REVERT opcode. + * + * In this case the amount of gas left MAY be non-zero and additional output + * data MAY be provided in ::evmc_result. + */ + EVMC_REVERT = 2, + + /** The execution has run out of gas. */ + EVMC_OUT_OF_GAS = 3, + + /** + * The designated INVALID instruction has been hit during execution. + * + * The EIP-141 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) + * defines the instruction 0xfe as INVALID instruction to indicate execution + * abortion coming from high-level languages. This status code is reported + * in case this INVALID instruction has been encountered. + */ + EVMC_INVALID_INSTRUCTION = 4, + + /** An undefined instruction has been encountered. */ + EVMC_UNDEFINED_INSTRUCTION = 5, + + /** + * The execution has attempted to put more items on the EVM stack + * than the specified limit. + */ + EVMC_STACK_OVERFLOW = 6, + + /** Execution of an opcode has required more items on the EVM stack. */ + EVMC_STACK_UNDERFLOW = 7, + + /** Execution has violated the jump destination restrictions. */ + EVMC_BAD_JUMP_DESTINATION = 8, + + /** + * Tried to read outside memory bounds. + * + * An example is RETURNDATACOPY reading past the available buffer. + */ + EVMC_INVALID_MEMORY_ACCESS = 9, + + /** Call depth has exceeded the limit (if any) */ + EVMC_CALL_DEPTH_EXCEEDED = 10, + + /** Tried to execute an operation which is restricted in static mode. */ + EVMC_STATIC_MODE_VIOLATION = 11, + + /** + * A call to a precompiled or system contract has ended with a failure. + * + * An example: elliptic curve functions handed invalid EC points. + */ + EVMC_PRECOMPILE_FAILURE = 12, + + /** + * Contract validation has failed (e.g. due to EVM 1.5 jump validity, + * Casper's purity checker or ewasm contract rules). + */ + EVMC_CONTRACT_VALIDATION_FAILURE = 13, + + /** + * An argument to a state accessing method has a value outside of the + * accepted range of values. + */ + EVMC_ARGUMENT_OUT_OF_RANGE = 14, + + /** + * A WebAssembly `unreachable` instruction has been hit during execution. + */ + EVMC_WASM_UNREACHABLE_INSTRUCTION = 15, + + /** + * A WebAssembly trap has been hit during execution. This can be for many + * reasons, including division by zero, validation errors, etc. + */ + EVMC_WASM_TRAP = 16, + + /** EVM implementation generic internal error. */ + EVMC_INTERNAL_ERROR = -1, + + /** + * The execution of the given code and/or message has been rejected + * by the EVM implementation. + * + * This error SHOULD be used to signal that the EVM is not able to or + * willing to execute the given code type or message. + * If an EVM returns the ::EVMC_REJECTED status code, + * the Client MAY try to execute it in other EVM implementation. + * 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, + + /** The VM failed to allocate the amount of memory needed for execution. */ + EVMC_OUT_OF_MEMORY = -3 +}; + +/* Forward declaration. */ +struct evmc_result; + +/** + * Releases resources assigned to an execution result. + * + * This function releases memory (and other resources, if any) assigned to the + * specified execution result making the result object invalid. + * + * @param result The execution result which resources are to be released. The + * result itself it not modified by this function, but becomes + * invalid and user MUST discard it as well. + * This MUST NOT be NULL. + * + * @note + * The result is passed by pointer to avoid (shallow) copy of the ::evmc_result + * struct. Think of this as the best possible C language approximation to + * passing objects by reference. + */ +typedef void (*evmc_release_result_fn)(const struct evmc_result* result); + +/** The EVM code execution result. */ +struct evmc_result +{ + /** The execution status code. */ + enum evmc_status_code status_code; + + /** + * The amount of gas left after the execution. + * + * If evmc_result::code is not ::EVMC_SUCCESS nor ::EVMC_REVERT + * the value MUST be 0. + */ + int64_t gas_left; + + /** + * The reference to output data. + * + * The output contains data coming from RETURN opcode (iff evmc_result::code + * field is ::EVMC_SUCCESS) or from REVERT opcode. + * + * The memory containing the output data is owned by EVM and has to be + * freed with evmc_result::release(). + * + * This MAY be NULL. + */ + const uint8_t* output_data; + + /** + * The size of the output data. + * + * If output_data is NULL this MUST be 0. + */ + size_t output_size; + + /** + * The method releasing all resources associated with the result object. + * + * This method (function pointer) is optional (MAY be NULL) and MAY be set + * by the VM implementation. If set it MUST be called by the user once to + * release memory and other resources associated with the result object. + * Once the resources are released the result object MUST NOT be used again. + * + * The suggested code pattern for releasing execution results: + * @code + * struct evmc_result result = ...; + * if (result.release) + * result.release(&result); + * @endcode + * + * @note + * It works similarly to C++ virtual destructor. Attaching the release + * function to the result itself allows VM composition. + */ + evmc_release_result_fn release; + + /** + * The address of the contract created by create instructions. + * + * This field has valid value only if: + * - it is a result of the Host method evmc_host_interface::call + * - and the result describes successful contract creation + * (evmc_result::status_code is ::EVMC_SUCCESS). + * In all other cases the address MUST be null bytes. + */ + evmc_address create_address; + + /** + * Reserved data that MAY be used by a evmc_result object creator. + * + * This reserved 4 bytes together with 20 bytes from create_address form + * 24 bytes of memory called "optional data" within evmc_result struct + * to be optionally used by the evmc_result object creator. + * + * @see evmc_result_optional_data, evmc_get_optional_data(). + * + * Also extends the size of the evmc_result to 64 bytes (full cache line). + */ + uint8_t padding[4]; +}; + + +/** + * Check account existence callback function. + * + * This callback function is used by the VM to check if + * there exists an account at given address. + * @param context The pointer to the Host execution context. + * @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_host_context* context, + const evmc_address* address); + +/** + * Get storage callback function. + * + * This callback function is used by a VM to query the given account storage entry. + * + * @param context The Host execution context. + * @param address The address of the account. + * @param key The index of the account's storage entry. + * @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_host_context* context, + const evmc_address* address, + const evmc_bytes32* key); + + +/** + * The effect of an attempt to modify a contract storage item. + * + * For the purpose of explaining the meaning of each element, the following + * notation is used: + * - 0 is zero value, + * - X != 0 (X is any value other than 0), + * - Y != X, Y != 0 (Y is any value other than X and 0), + * - Z != Y (Z is any value other than Y), + * - the "->" means the change from one value to another. + */ +enum evmc_storage_status +{ + /** + * The value of a storage item has been left unchanged: 0 -> 0 and X -> X. + */ + EVMC_STORAGE_UNCHANGED = 0, + + /** + * The value of a storage item has been modified: X -> Y. + */ + EVMC_STORAGE_MODIFIED = 1, + + /** + * A storage item has been modified after being modified before: X -> Y -> Z. + */ + EVMC_STORAGE_MODIFIED_AGAIN = 2, + + /** + * A new storage item has been added: 0 -> X. + */ + EVMC_STORAGE_ADDED = 3, + + /** + * A storage item has been deleted: X -> 0. + */ + EVMC_STORAGE_DELETED = 4 +}; + + +/** + * Set storage callback function. + * + * This callback function is used by a VM to update the given account storage entry. + * The VM MUST make sure that the account exists. This requirement is only a formality because + * VM implementations only modify storage of the account of the current execution context + * (i.e. referenced by evmc_message::destination). + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @param key The index of the storage entry. + * @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_host_context* context, + const evmc_address* address, + const evmc_bytes32* key, + const evmc_bytes32* value); + +/** + * Get balance callback function. + * + * This callback function is used by a VM to query the balance of the given account. + * + * @param context The pointer to the Host execution context. + * @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_host_context* context, + const evmc_address* address); + +/** + * Get code size callback function. + * + * This callback function is used by a VM to get the size of the code stored + * in the account at the given address. + * + * @param context The pointer to the Host execution 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_host_context* context, + const evmc_address* address); + +/** + * Get code hash callback function. + * + * This callback function is used by a VM to get the keccak256 hash of the code stored + * in the account at the given address. For existing accounts not having a code, this + * function returns keccak256 hash of empty data. + * + * @param context The pointer to the Host execution context. + * @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_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. + * + * @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_host_context* context, + const evmc_address* address, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size); + +/** + * 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. + * + * @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_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_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_host_context* context, + const evmc_address* address, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t topics_count); + +/** + * Pointer to the callback function supporting EVM calls. + * + * @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_host_context* context, + const struct evmc_message* msg); + +/** + * The Host interface. + * + * The set of all callback functions expected by VM instances. This is C + * realisation of vtable for OOP interface (only virtual methods, no data). + * Host implementations SHOULD create constant singletons of this (similarly + * to vtables) to lower the maintenance and memory management cost. + */ +struct evmc_host_interface +{ + /** Check account existence callback function. */ + evmc_account_exists_fn account_exists; + + /** Get storage callback function. */ + evmc_get_storage_fn get_storage; + + /** Set storage callback function. */ + evmc_set_storage_fn set_storage; + + /** Get balance callback function. */ + evmc_get_balance_fn get_balance; + + /** Get code size callback function. */ + evmc_get_code_size_fn get_code_size; + + /** Get code hash callback function. */ + evmc_get_code_hash_fn get_code_hash; + + /** Copy code callback function. */ + evmc_copy_code_fn copy_code; + + /** Selfdestruct callback function. */ + evmc_selfdestruct_fn selfdestruct; + + /** Call callback function. */ + evmc_call_fn call; + + /** Get transaction context callback function. */ + evmc_get_tx_context_fn get_tx_context; + + /** Get block hash callback function. */ + evmc_get_block_hash_fn get_block_hash; + + /** Emit log callback function. */ + evmc_emit_log_fn emit_log; +}; + + +/* Forward declaration. */ +struct evmc_vm; + +/** + * Destroys the VM instance. + * + * @param vm The VM instance to be destroyed. + */ +typedef void (*evmc_destroy_fn)(struct evmc_vm* vm); + +/** + * Possible outcomes of evmc_set_option. + */ +enum evmc_set_option_result +{ + EVMC_SET_OPTION_SUCCESS = 0, + EVMC_SET_OPTION_INVALID_NAME = 1, + EVMC_SET_OPTION_INVALID_VALUE = 2 +}; + +/** + * Configures the VM instance. + * + * Allows modifying options of the VM instance. + * Options: + * - code cache behavior: on, off, read-only, ... + * - optimizations, + * + * @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_vm* vm, + char const* name, + char const* value); + + +/** + * EVM revision. + * + * The revision of the EVM specification based on the Ethereum + * upgrade / hard fork codenames. + */ +enum evmc_revision +{ + /** + * The Frontier revision. + * + * The one Ethereum launched with. + */ + EVMC_FRONTIER = 0, + + /** + * The Homestead revision. + * + * https://eips.ethereum.org/EIPS/eip-606 + */ + EVMC_HOMESTEAD = 1, + + /** + * The Tangerine Whistle revision. + * + * https://eips.ethereum.org/EIPS/eip-608 + */ + EVMC_TANGERINE_WHISTLE = 2, + + /** + * The Spurious Dragon revision. + * + * https://eips.ethereum.org/EIPS/eip-607 + */ + EVMC_SPURIOUS_DRAGON = 3, + + /** + * The Byzantium revision. + * + * https://eips.ethereum.org/EIPS/eip-609 + */ + EVMC_BYZANTIUM = 4, + + /** + * The Constantinople revision. + * + * https://eips.ethereum.org/EIPS/eip-1013 + */ + EVMC_CONSTANTINOPLE = 5, + + /** + * The Petersburg revision. + * + * Other names: Constantinople2, ConstantinopleFix. + * + * https://eips.ethereum.org/EIPS/eip-1716 + */ + EVMC_PETERSBURG = 6, + + /** + * The Istanbul revision. + * + * The spec draft: https://eips.ethereum.org/EIPS/eip-1679. + */ + 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_BERLIN +}; + + +/** + * Executes the given code using the input from the message. + * + * This function MAY be invoked multiple times for a single VM instance. + * + * @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_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); + +/** + * Possible capabilities of a VM. + */ +enum evmc_capabilities +{ + /** + * The VM is capable of executing EVM1 bytecode. + */ + EVMC_CAPABILITY_EVM1 = (1u << 0), + + /** + * The VM is capable of executing ewasm bytecode. + */ + EVMC_CAPABILITY_EWASM = (1u << 1), + + /** + * The VM is capable of executing the precompiled contracts + * defined for the range of destination addresses. + * + * The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies + * the range 0x000...0000 - 0x000...ffff of addresses + * reserved for precompiled and system contracts. + * + * This capability is **experimental** and MAY be removed without notice. + */ + EVMC_CAPABILITY_PRECOMPILES = (1u << 2) +}; + +/** + * Alias for unsigned integer representing a set of bit flags of EVMC capabilities. + * + * @see evmc_capabilities + */ +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_vm::set_option. + * + * @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_vm* vm); + + +/** + * The VM instance. + * + * Defines the base struct of the VM implementation. + */ +struct evmc_vm +{ + /** + * EVMC ABI version implemented by the VM instance. + * + * Can be used to detect ABI incompatibilities. + * The EVMC ABI version represented by this file is in ::EVMC_ABI_VERSION. + */ + const int abi_version; + + /** + * The name of the EVMC VM implementation. + * + * It MUST be a NULL-terminated not empty string. + * The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed). + */ + const char* name; + + /** + * The version of the EVMC VM implementation, e.g. "1.2.3b4". + * + * It MUST be a NULL-terminated not empty string. + * The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed). + */ + const char* version; + + /** + * 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 VM instance. + * + * This is a mandatory method and MUST NOT be set to NULL. + */ + evmc_execute_fn execute; + + /** + * A method returning capabilities supported by the VM instance. + * + * The value returned MAY change when different options are set via the set_option() method. + * + * A Client SHOULD only rely on the value returned if it has queried it after + * it has called the set_option(). + * + * This is a mandatory method and MUST NOT be set to NULL. + */ + evmc_get_capabilities_fn get_capabilities; + + /** + * Optional pointer to function modifying VM's options. + * + * If the VM does not support this feature the pointer can be NULL. + */ + evmc_set_option_fn set_option; +}; + +/* END Python CFFI declarations */ + +#if EVMC_DOCUMENTATION +/** + * Example of a function creating an instance of an example EVM implementation. + * + * Each EVM implementation MUST provide a function returning an EVM instance. + * The function SHOULD be named `evmc_create_(void)`. If the VM name contains hyphens + * replaces them with underscores in the function names. + * + * @par Binaries naming convention + * For VMs distributed as shared libraries, the name of the library SHOULD match the VM name. + * The convetional library filename prefixes and extensions SHOULD be ignored by the Client. + * For example, the shared library with the "beta-interpreter" implementation may be named + * `libbeta-interpreter.so`. + * + * @return The VM instance or NULL indicating instance creation failure. + */ +struct evmc_vm* evmc_create_example_vm(void); +#endif + +#if __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/tests/evmc_c/evmc.hpp b/tests/evmc_c/evmc.hpp new file mode 100644 index 0000000..b9c4151 --- /dev/null +++ b/tests/evmc_c/evmc.hpp @@ -0,0 +1,805 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ +#pragma once + +#include "evmc.h" +#include "helpers.h" + +#include +#include +#include + +/// EVMC C++ API - wrappers and bindings for C++ +/// @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 +{ + 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 to" 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 to" comparison operator for the evmc::address type. +constexpr bool operator!=(const address& a, const address& b) noexcept +{ + return !(a == b); +} + +/// The "less than" 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 "greater than" comparison operator for the evmc::address type. +constexpr bool operator>(const address& a, const address& b) noexcept +{ + return b < a; +} + +/// The "less than or equal to" comparison operator for the evmc::address type. +constexpr bool operator<=(const address& a, const address& b) noexcept +{ + return !(b < a); +} + +/// The "greater than or equal to" comparison operator for the evmc::address type. +constexpr bool operator>=(const address& a, const address& b) noexcept +{ + return !(a < b); +} + +/// The "equal to" 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 to" comparison operator for the evmc::bytes32 type. +constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept +{ + return !(a == b); +} + +/// The "less than" 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])); +} + +/// The "greater than" comparison operator for the evmc::bytes32 type. +constexpr bool operator>(const bytes32& a, const bytes32& b) noexcept +{ + return b < a; +} + +/// The "less than or equal to" comparison operator for the evmc::bytes32 type. +constexpr bool operator<=(const bytes32& a, const bytes32& b) noexcept +{ + return !(b < a); +} + +/// The "greater than or equal to" comparison operator for the evmc::bytes32 type. +constexpr bool operator>=(const bytes32& a, const bytes32& b) noexcept +{ + return !(a < b); +} + +/// 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 +/// automatically release attached resources. +class result : private evmc_result +{ +public: + using evmc_result::create_address; + using evmc_result::gas_left; + using evmc_result::output_data; + 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} {} + + /// Destructor responsible for automatically releasing attached resources. + ~result() noexcept + { + if (release != nullptr) + release(this); + } + + /// Move constructor. + result(result&& other) noexcept : evmc_result{other} + { + other.release = nullptr; // Disable releasing of the rvalue object. + } + + /// Move assignment operator. + /// + /// The self-assigment MUST never happen. + /// + /// @param other The other result object. + /// @return The reference to the left-hand side object. + result& operator=(result&& other) noexcept + { + this->~result(); // Release this object. + static_cast(*this) = other; // Copy data. + other.release = nullptr; // Disable releasing of the rvalue object. + return *this; + } + + /// Releases the ownership and returns the raw copy of evmc_result. + /// + /// This method drops the ownership of the result + /// (result's resources are not going to be released when this object is destructed). + /// It is the caller's responsibility having the returned copy of the result to release it. + /// This object MUST NOT be used after this method is invoked. + /// + /// @return The copy of this object converted to raw evmc_result. + evmc_result release_raw() noexcept + { + const auto out = evmc_result{*this}; // Copy data. + this->release = nullptr; // Disable releasing of this object. + return out; + } +}; + + +/// The EVMC Host interface +class HostInterface +{ +public: + virtual ~HostInterface() noexcept = default; + + /// @copydoc evmc_host_interface::account_exists + virtual bool account_exists(const address& addr) const noexcept = 0; + + /// @copydoc evmc_host_interface::get_storage + virtual bytes32 get_storage(const address& addr, const bytes32& key) const noexcept = 0; + + /// @copydoc evmc_host_interface::set_storage + virtual evmc_storage_status set_storage(const address& addr, + const bytes32& key, + const bytes32& value) noexcept = 0; + + /// @copydoc evmc_host_interface::get_balance + virtual uint256be get_balance(const address& addr) const noexcept = 0; + + /// @copydoc evmc_host_interface::get_code_size + virtual size_t get_code_size(const address& addr) const noexcept = 0; + + /// @copydoc evmc_host_interface::get_code_hash + virtual bytes32 get_code_hash(const address& addr) const noexcept = 0; + + /// @copydoc evmc_host_interface::copy_code + virtual size_t copy_code(const address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) const noexcept = 0; + + /// @copydoc evmc_host_interface::selfdestruct + 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; + + /// @copydoc evmc_host_interface::get_tx_context + virtual evmc_tx_context get_tx_context() const noexcept = 0; + + /// @copydoc evmc_host_interface::get_block_hash + virtual bytes32 get_block_hash(int64_t block_number) const noexcept = 0; + + /// @copydoc evmc_host_interface::emit_log + virtual void emit_log(const address& addr, + const uint8_t* data, + size_t data_size, + const bytes32 topics[], + size_t num_topics) noexcept = 0; +}; + + +/// Wrapper around EVMC host context / host interface. +/// +/// To be used by VM implementations as better alternative to using ::evmc_host_context directly. +class HostContext : public HostInterface +{ + const evmc_host_interface* host = nullptr; + evmc_host_context* context = nullptr; + mutable evmc_tx_context tx_context = {}; + +public: + /// Default constructor for null Host context. + HostContext() = default; + + /// Constructor from the EVMC Host primitives. + /// @param interface The reference to the Host interface. + /// @param ctx The pointer to the Host context object. This parameter MAY be null. + HostContext(const evmc_host_interface& interface, evmc_host_context* ctx) noexcept + : host{&interface}, context{ctx} + {} + + bool account_exists(const address& address) const noexcept final + { + return host->account_exists(context, &address); + } + + bytes32 get_storage(const address& address, const bytes32& key) const noexcept final + { + return host->get_storage(context, &address, &key); + } + + evmc_storage_status set_storage(const address& address, + const bytes32& key, + const bytes32& value) noexcept final + { + return host->set_storage(context, &address, &key, &value); + } + + uint256be get_balance(const address& address) const noexcept final + { + return host->get_balance(context, &address); + } + + size_t get_code_size(const address& address) const noexcept final + { + return host->get_code_size(context, &address); + } + + bytes32 get_code_hash(const address& address) const noexcept final + { + return host->get_code_hash(context, &address); + } + + size_t copy_code(const address& address, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) const noexcept final + { + return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); + } + + void selfdestruct(const address& addr, const address& beneficiary) noexcept final + { + host->selfdestruct(context, &addr, &beneficiary); + } + + result call(const evmc_message& message) noexcept final + { + return result{host->call(context, &message)}; + } + + /// @copydoc HostInterface::get_tx_context() + /// + /// The implementation caches the received transaction context + /// by assuming that the block timestamp should never be zero. + /// + /// @return The cached transaction context. + evmc_tx_context get_tx_context() const noexcept final + { + if (tx_context.block_timestamp == 0) + tx_context = host->get_tx_context(context); + return tx_context; + } + + bytes32 get_block_hash(int64_t number) const noexcept final + { + return host->get_block_hash(context, number); + } + + void emit_log(const address& addr, + const uint8_t* data, + size_t data_size, + const bytes32 topics[], + size_t topics_count) noexcept final + { + host->emit_log(context, &addr, data, data_size, topics, topics_count); + } +}; + + +/// Abstract class to be used by Host implementations. +/// +/// 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_host_context and the ::evmc_host_interface. +class Host : public HostInterface +{ +public: + /// 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); + } +}; + + +/// @copybrief evmc_vm +/// +/// This is a RAII wrapper for evmc_vm, and object of this type +/// automatically destroys the VM instance. +class VM +{ +public: + VM() noexcept = default; + + /// Converting constructor from evmc_vm. + explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} + + /// Destructor responsible for automatically destroying the VM instance. + ~VM() noexcept + { + if (m_instance != nullptr) + 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 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; } + + /// 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_vm::name + char const* name() const noexcept { return m_instance->name; } + + /// @copydoc evmc_vm::version + char const* version() const noexcept { return m_instance->version; } + + /// Checks if the VM has the given capability. + bool has_capability(evmc_capabilities capability) const noexcept + { + return (get_capabilities() & static_cast(capability)) != 0; + } + + /// @copydoc evmc::vm::get_capabilities + evmc_capabilities_flagset get_capabilities() const noexcept + { + return m_instance->get_capabilities(m_instance); + } + + /// @copydoc evmc_set_option() + evmc_set_option_result set_option(const char name[], const char value[]) noexcept + { + return evmc_set_option(m_instance, name, value); + } + + /// @copydoc evmc_execute() + 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, &host, ctx, rev, &msg, code, code_size)}; + } + + /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. + result 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); + } + + /// 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_vm* m_instance = nullptr; +}; + +inline VM::VM(evmc_vm* vm, + std::initializer_list> options) noexcept + : m_instance{vm} +{ + // This constructor is implemented outside of the class definition to workaround a doxygen bug. + for (const auto& option : options) + set_option(option.first, option.second); +} + + +namespace internal +{ +inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept +{ + return Host::from_context(h)->account_exists(*addr); +} + +inline evmc_bytes32 get_storage(evmc_host_context* h, + const evmc_address* addr, + const evmc_bytes32* key) noexcept +{ + return Host::from_context(h)->get_storage(*addr, *key); +} + +inline evmc_storage_status set_storage(evmc_host_context* h, + const evmc_address* addr, + const evmc_bytes32* key, + const evmc_bytes32* value) noexcept +{ + return Host::from_context(h)->set_storage(*addr, *key, *value); +} + +inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept +{ + return Host::from_context(h)->get_balance(*addr); +} + +inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept +{ + return Host::from_context(h)->get_code_size(*addr); +} + +inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept +{ + return Host::from_context(h)->get_code_hash(*addr); +} + +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 Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); +} + +inline void selfdestruct(evmc_host_context* h, + const evmc_address* addr, + const evmc_address* beneficiary) noexcept +{ + Host::from_context(h)->selfdestruct(*addr, *beneficiary); +} + +inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept +{ + return Host::from_context(h)->call(*msg).release_raw(); +} + +inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept +{ + return Host::from_context(h)->get_tx_context(); +} + +inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept +{ + return Host::from_context(h)->get_block_hash(block_number); +} + +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 +{ + Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics), + num_topics); +} +} // namespace internal + +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 + + +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/tests/evmc_c/example_host.cpp b/tests/evmc_c/example_host.cpp new file mode 100644 index 0000000..e3991fa --- /dev/null +++ b/tests/evmc_c/example_host.cpp @@ -0,0 +1,180 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2016-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/// @file +/// Example implementation of an EVMC Host. +#include "evmc.hpp" + +#include +#include +#include + +using namespace evmc::literals; + +namespace evmc +{ +struct account +{ + evmc::uint256be balance = {}; + std::vector code; + std::map storage; + + virtual evmc::bytes32 code_hash() const + { + // Extremely dumb "hash" function. + evmc::bytes32 ret{}; + for (std::vector::size_type i = 0; i != code.size(); i++) + { + auto v = code[i]; + ret.bytes[v % sizeof(ret.bytes)] ^= v; + } + return ret; + } +}; + +using accounts = std::map; + +} // namespace evmc + +class ExampleHost : public evmc::Host +{ + evmc::accounts accounts; + evmc_tx_context tx_context{}; + +public: + ExampleHost() = default; + explicit ExampleHost(evmc_tx_context& _tx_context) noexcept : tx_context{_tx_context} {}; + ExampleHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts) noexcept + : accounts{_accounts}, tx_context{_tx_context} {}; + + bool account_exists(const evmc::address& addr) const noexcept final + { + return accounts.find(addr) != accounts.end(); + } + + evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const + noexcept final + { + const auto account_iter = accounts.find(addr); + if (account_iter == accounts.end()) + return {}; + + const auto storage_iter = account_iter->second.storage.find(key); + if (storage_iter != account_iter->second.storage.end()) + return storage_iter->second; + return {}; + } + + evmc_storage_status set_storage(const evmc::address& addr, + const evmc::bytes32& key, + const evmc::bytes32& value) noexcept final + { + auto& account = accounts[addr]; + auto prev_value = account.storage[key]; + account.storage[key] = value; + + return (prev_value == value) ? EVMC_STORAGE_UNCHANGED : EVMC_STORAGE_MODIFIED; + } + + evmc::uint256be get_balance(const evmc::address& addr) const noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.balance; + return {}; + } + + size_t get_code_size(const evmc::address& addr) const noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.code.size(); + return 0; + } + + evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.code_hash(); + return {}; + } + + size_t copy_code(const evmc::address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) const noexcept final + { + const auto it = accounts.find(addr); + if (it == accounts.end()) + return 0; + + const auto& code = it->second.code; + + if (code_offset >= code.size()) + return 0; + + const auto n = std::min(buffer_size, code.size() - code_offset); + + if (n > 0) + std::copy_n(&code[code_offset], n, buffer_data); + return n; + } + + void selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept final + { + (void)addr; + (void)beneficiary; + } + + evmc::result call(const evmc_message& msg) noexcept final + { + return {EVMC_REVERT, msg.gas, msg.input_data, msg.input_size}; + } + + evmc_tx_context get_tx_context() const noexcept final { return tx_context; } + + evmc::bytes32 get_block_hash(int64_t number) const noexcept final + { + const int64_t current_block_number = get_tx_context().block_number; + + return (number < current_block_number && number >= current_block_number - 256) ? + 0xb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5f_bytes32 : + 0_bytes32; + } + + void emit_log(const evmc::address& addr, + const uint8_t* data, + size_t data_size, + const evmc::bytes32 topics[], + size_t topics_count) noexcept final + { + (void)addr; + (void)data; + (void)data_size; + (void)topics; + (void)topics_count; + } +}; + + +extern "C" { + +const evmc_host_interface* example_host_get_interface() +{ + return &evmc::Host::get_interface(); +} + +evmc_host_context* example_host_create_context(evmc_tx_context tx_context) +{ + auto host = new ExampleHost{tx_context}; + return host->to_context(); +} + +void example_host_destroy_context(evmc_host_context* context) +{ + delete evmc::Host::from_context(context); +} +} diff --git a/tests/evmc_c/example_vm.c b/tests/evmc_c/example_vm.c new file mode 100644 index 0000000..5884242 --- /dev/null +++ b/tests/evmc_c/example_vm.c @@ -0,0 +1,232 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2016-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/// @file +/// Example implementation of the EVMC VM interface. +/// +/// This VM does not do anything useful except for showing +/// how EVMC VM API should be implemented. +/// The implementation is done in C only, but could be done in C++ in very +/// similar way. + +#include "evmc.h" +#include +#include +#include +#include + + +/// The example VM instance struct extending the evmc_vm. +struct example_vm +{ + struct evmc_vm instance; ///< The base struct. + int verbose; ///< The verbosity level. +}; + +/// The implementation of the evmc_vm::destroy() method. +static void destroy(struct evmc_vm* vm) +{ + free(vm); +} + +/// The example implementation of the evmc_vm::get_capabilities() method. +static evmc_capabilities_flagset get_capabilities(struct evmc_vm* vm) +{ + (void)vm; + return EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM; +} + +/// Example VM options. +/// +/// The implementation of the evmc_vm::set_option() method. +/// VMs are allowed to omit this method implementation. +static enum evmc_set_option_result set_option(struct evmc_vm* instance, + const char* name, + const char* value) +{ + struct example_vm* vm = (struct example_vm*)instance; + if (strcmp(name, "verbose") == 0) + { + if (!value) + return EVMC_SET_OPTION_INVALID_VALUE; + + char* end = NULL; + long int v = strtol(value, &end, 0); + if (end == value) // Parsing the value failed. + return EVMC_SET_OPTION_INVALID_VALUE; + if (v > 9 || v < -1) // Not in the valid range. + return EVMC_SET_OPTION_INVALID_VALUE; + vm->verbose = (int)v; + return EVMC_SET_OPTION_SUCCESS; + } + + return EVMC_SET_OPTION_INVALID_NAME; +} + +/// The implementation of the evmc_result::release() method that frees +/// the output buffer attached to the result object. +static void free_result_output_data(const struct evmc_result* result) +{ + free((uint8_t*)result->output_data); +} + +/// The example implementation of the evmc_vm::execute() method. +static struct evmc_result execute(struct evmc_vm* instance, + const struct evmc_host_interface* host, + struct evmc_host_context* context, + enum evmc_revision rev, + const struct evmc_message* msg, + const uint8_t* code, + size_t code_size) +{ + struct evmc_result ret = {.status_code = EVMC_INTERNAL_ERROR}; + if (code_size == 0) + { + // In case of empty code return a fancy error message. + const char* error = rev == EVMC_BYZANTIUM ? "Welcome to Byzantium!" : "Hello Ethereum!"; + ret.output_data = (const uint8_t*)error; + ret.output_size = strlen(error); + ret.status_code = EVMC_FAILURE; + ret.gas_left = msg->gas / 10; + ret.release = NULL; // We don't need to release the constant messages. + return ret; + } + + struct example_vm* vm = (struct example_vm*)instance; + + // Simulate executing by checking for some code patterns. + // Solidity inline assembly is used in the examples instead of EVM bytecode. + + // Assembly: `{ mstore(0, address()) return(0, msize()) }`. + const char return_address[] = "\x30\x60\x00\x52\x59\x60\x00\xf3"; + + // Assembly: `{ sstore(0, add(sload(0), 1)) }` + const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55"; + + // Assembly: `{ mstore(0, number()) return(0, msize()) }` + const char return_block_number[] = "\x43\x60\x00\x52\x59\x60\x00\xf3"; + + // Assembly: `{ sstore(0, number()) mstore(0, number()) return(0, msize()) }` + const char save_return_block_number[] = "\x43\x60\x00\x55\x43\x60\x00\x52\x59\x60\x00\xf3"; + + // Assembly: PUSH(0) 6x DUP1 CALL + const char make_a_call[] = "\x60\x00\x80\x80\x80\x80\x80\x80\xf1"; + + if (msg->kind == EVMC_CREATE) + { + ret.status_code = EVMC_SUCCESS; + ret.gas_left = msg->gas / 10; + return ret; + } + else if (code_size == (sizeof(return_address) - 1) && + strncmp((const char*)code, return_address, code_size) == 0) + { + static const size_t address_size = sizeof(msg->destination); + uint8_t* output_data = (uint8_t*)malloc(address_size); + if (!output_data) + { + // malloc failed, report internal error. + ret.status_code = EVMC_INTERNAL_ERROR; + return ret; + } + memcpy(output_data, &msg->destination, address_size); + ret.status_code = EVMC_SUCCESS; + ret.output_data = output_data; + ret.output_size = address_size; + ret.release = &free_result_output_data; + return ret; + } + else if (code_size == (sizeof(counter) - 1) && + strncmp((const char*)code, counter, code_size) == 0) + { + const evmc_bytes32 key = {{0}}; + evmc_bytes32 value = host->get_storage(context, &msg->destination, &key); + value.bytes[31]++; + host->set_storage(context, &msg->destination, &key, &value); + ret.status_code = EVMC_SUCCESS; + return ret; + } + else if (code_size == (sizeof(return_block_number) - 1) && + strncmp((const char*)code, return_block_number, code_size) == 0) + { + const struct evmc_tx_context tx_context = host->get_tx_context(context); + const size_t output_size = 20; + + uint8_t* output_data = (uint8_t*)calloc(1, output_size); + snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number); + ret.status_code = EVMC_SUCCESS; + ret.gas_left = msg->gas / 2; + ret.output_data = output_data; + ret.output_size = output_size; + ret.release = &free_result_output_data; + return ret; + } + else if (code_size == (sizeof(save_return_block_number) - 1) && + strncmp((const char*)code, save_return_block_number, code_size) == 0) + { + const struct evmc_tx_context tx_context = host->get_tx_context(context); + const size_t output_size = 20; + + // Store block number. + const evmc_bytes32 key = {{0}}; + evmc_bytes32 value = {{0}}; + // NOTE: assume block number is <= 255 + value.bytes[31] = (uint8_t)tx_context.block_number; + host->set_storage(context, &msg->destination, &key, &value); + + // Return block number. + uint8_t* output_data = (uint8_t*)calloc(1, output_size); + snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number); + ret.status_code = EVMC_SUCCESS; + ret.gas_left = msg->gas / 2; + ret.output_data = output_data; + ret.output_size = output_size; + ret.release = &free_result_output_data; + return ret; + } + else if (code_size == (sizeof(make_a_call) - 1) && + strncmp((const char*)code, make_a_call, code_size) == 0) + { + struct evmc_message call_msg; + memset(&call_msg, 0, sizeof(call_msg)); + call_msg.kind = EVMC_CALL; + call_msg.depth = msg->depth + 1; + call_msg.gas = msg->gas - (msg->gas / 64); + call_msg.sender = msg->destination; + return host->call(context, &call_msg); + } + + ret.status_code = EVMC_FAILURE; + ret.gas_left = 0; + + if (vm->verbose) + printf("Execution done.\n"); + + return ret; +} + +/// @cond internal +#if !defined(PROJECT_VERSION) +/// The dummy project version if not provided by the build system. +#define PROJECT_VERSION "0.0.0" +#endif +/// @endcond + +struct evmc_vm* evmc_create_example_vm() +{ + struct evmc_vm init = { + .abi_version = EVMC_ABI_VERSION, + .name = "example_vm", + .version = PROJECT_VERSION, + .destroy = destroy, + .execute = execute, + .get_capabilities = get_capabilities, + .set_option = set_option, + }; + struct example_vm* vm = calloc(1, sizeof(struct example_vm)); + struct evmc_vm* interface = &vm->instance; + memcpy(interface, &init, sizeof(init)); + return interface; +} diff --git a/tests/evmc_c/helpers.h b/tests/evmc_c/helpers.h new file mode 100644 index 0000000..345e351 --- /dev/null +++ b/tests/evmc_c/helpers.h @@ -0,0 +1,212 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018-2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * EVMC Helpers + * + * A collection of C helper functions for invoking a VM instance methods. + * These are convenient for languages where invoking function pointers + * is "ugly" or impossible (such as Go). + * + * It also contains helpers (overloaded operators) for using EVMC types effectively in C++. + * + * @defgroup helpers EVMC Helpers + * @{ + */ +#pragma once + +#include "evmc.h" +#include +#include + +/** + * Returns true if the VM has a compatible ABI version. + */ +static inline bool evmc_is_abi_compatible(struct evmc_vm* vm) +{ + return vm->abi_version == EVMC_ABI_VERSION; +} + +/** + * Returns the name of the VM. + */ +static inline const char* evmc_vm_name(struct evmc_vm* vm) +{ + return vm->name; +} + +/** + * Returns the version of the VM. + */ +static inline const char* evmc_vm_version(struct evmc_vm* vm) +{ + return vm->version; +} + +/** + * Checks if the VM has the given capability. + * + * @see evmc_get_capabilities_fn + */ +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; +} + +/** + * Destroys the VM instance. + * + * @see evmc_destroy_fn + */ +static inline void evmc_destroy(struct evmc_vm* vm) +{ + vm->destroy(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_vm* vm, + char const* name, + char const* value) +{ + if (vm->set_option) + return vm->set_option(vm, name, value); + return EVMC_SET_OPTION_INVALID_NAME; +} + +/** + * Executes code in the VM instance. + * + * @see evmc_execute_fn. + */ +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 vm->execute(vm, host, 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. + * + * @param result The result object to be released. MUST NOT be NULL. + * + * @see evmc_result::release() evmc_release_result_fn + */ +static inline void evmc_release_result(struct evmc_result* result) +{ + if (result->release) + result->release(result); +} + + +/** + * Helpers for optional storage of evmc_result. + * + * In some contexts (i.e. evmc_result::create_address is unused) objects of + * type evmc_result contains a memory storage that MAY be used by the object + * owner. This group defines helper types and functions for accessing + * the optional storage. + * + * @defgroup result_optional_storage Result Optional Storage + * @{ + */ + +/** + * The union representing evmc_result "optional storage". + * + * The evmc_result struct contains 24 bytes of optional storage that can be + * reused by the object creator if the object does not contain + * evmc_result::create_address. + * + * A VM implementation MAY use this memory to keep additional data + * when returning result from evmc_execute_fn(). + * The host application MAY use this memory to keep additional data + * when returning result of performed calls from evmc_call_fn(). + * + * @see evmc_get_optional_storage(), evmc_get_const_optional_storage(). + */ +union evmc_result_optional_storage +{ + uint8_t bytes[24]; /**< 24 bytes of optional storage. */ + void* pointer; /**< Optional pointer. */ +}; + +/** Provides read-write access to evmc_result "optional storage". */ +static inline union evmc_result_optional_storage* evmc_get_optional_storage( + struct evmc_result* result) +{ + return (union evmc_result_optional_storage*)&result->create_address; +} + +/** Provides read-only access to evmc_result "optional storage". */ +static inline const union evmc_result_optional_storage* evmc_get_const_optional_storage( + const struct evmc_result* result) +{ + return (const union evmc_result_optional_storage*)&result->create_address; +} + +/** @} */ + +/** @} */ diff --git a/tests/test_host_vm.nim b/tests/test_host_vm.nim new file mode 100644 index 0000000..6c9976f --- /dev/null +++ b/tests/test_host_vm.nim @@ -0,0 +1,17 @@ +import ../evmc/[evmc, evmc_nim] + +{.compile: "evmc_c/example_host.cpp".} +{.compile: "evmc_c/example_vm.c".} +{.passL: "-lstdc++"} + +proc example_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.} +proc example_host_create_context(tx_context: evmc_tx_context): ptr evmc_host_context {.importc, cdecl.} +proc example_host_destroy_context(context: ptr evmc_host_context) {.importc, cdecl.} +proc evmc_create_example_vm(): ptr evmc_vm {.importc, cdecl.} + +proc main() = + echo "tx_context: ", sizeof(evmc_tx_context) + var vm = evmc_create_example_vm() + var host = example_host_get_interface() + +main()