add C mocked host

This commit is contained in:
andri lim 2019-12-15 17:25:23 +07:00
parent b02940241e
commit a0269efd5b
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
10 changed files with 2388 additions and 0 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ build/
*.dll
*.so
*.dylib
*.exe

941
tests/evmc_c/evmc.h Normal file
View File

@ -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 <stdbool.h> /* Definition of bool, true and false. */
#include <stddef.h> /* Definition of size_t. */
#include <stdint.h> /* 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_<vm-name>(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
/** @} */

805
tests/evmc_c/evmc.hpp Normal file
View File

@ -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 <functional>
#include <initializer_list>
#include <utility>
/// 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 <typename T, T... Ints>
struct integer_sequence
{
};
template <uint8_t... Bytes>
using byte_sequence = integer_sequence<uint8_t, Bytes...>;
template <char... Chars>
using char_sequence = integer_sequence<char, Chars...>;
template <typename, typename>
struct concatenate;
template <uint8_t... Bytes1, uint8_t... Bytes2>
struct concatenate<byte_sequence<Bytes1...>, byte_sequence<Bytes2...>>
{
using type = byte_sequence<Bytes1..., Bytes2...>;
};
template <uint8_t D>
constexpr uint8_t parse_hex_digit() noexcept
{
static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'),
"literal must be hexadecimal integer");
return static_cast<uint8_t>(
(D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10);
}
template <typename>
struct parse_digits;
template <uint8_t Digit1, uint8_t Digit2>
struct parse_digits<byte_sequence<Digit1, Digit2>>
{
using type = byte_sequence<static_cast<uint8_t>(parse_hex_digit<Digit1>() << 4) |
parse_hex_digit<Digit2>()>;
};
template <uint8_t Digit1, uint8_t Digit2, uint8_t... Rest>
struct parse_digits<byte_sequence<Digit1, Digit2, Rest...>>
{
using type = typename concatenate<typename parse_digits<byte_sequence<Digit1, Digit2>>::type,
typename parse_digits<byte_sequence<Rest...>>::type>::type;
};
template <typename, typename>
struct parse_literal;
template <typename T, char Prefix1, char Prefix2, char... Literal>
struct parse_literal<T, char_sequence<Prefix1, Prefix2, Literal...>>
{
static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation");
static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size");
template <uint8_t... Bytes>
static constexpr T create_from(byte_sequence<Bytes...>) noexcept
{
return T{{{Bytes...}}};
}
static constexpr T get() noexcept
{
return create_from(typename parse_digits<byte_sequence<Literal...>>::type{});
}
};
template <typename T, char Digit>
struct parse_literal<T, char_sequence<Digit>>
{
static_assert(Digit == '0', "only 0 is allowed as a single digit literal");
static constexpr T get() noexcept { return {}; }
};
template <typename T, char... Literal>
constexpr T parse() noexcept
{
return parse_literal<T, char_sequence<Literal...>>::get();
}
} // namespace internal
/// Literal for evmc::address.
template <char... Literal>
constexpr address operator"" _address() noexcept
{
return internal::parse<address, Literal...>();
}
/// Literal for evmc::bytes32.
template <char... Literal>
constexpr bytes32 operator"" _bytes32() noexcept
{
return internal::parse<bytes32, Literal...>();
}
} // namespace literals
using namespace literals;
/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;
/// @copydoc evmc_result
///
/// This is a RAII wrapper for evmc_result and objects of this type
/// 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<evmc_result&>(*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<evmc_host_context*>(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 <typename DerivedClass = Host>
static DerivedClass* from_context(evmc_host_context* context) noexcept
{
// Get pointer of the Host base class.
auto* h = reinterpret_cast<Host*>(context);
// Additional downcast, only possible if DerivedClass inherits from Host.
return static_cast<DerivedClass*>(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<std::pair<const char*, const char*>> 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<evmc_capabilities_flagset>(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<std::pair<const char*, const char*>> 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<const bytes32*>(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<evmc::address>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::address& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(fnv1a_by64(
fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])),
load32be(&s.bytes[16])));
}
};
/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers.
template <>
struct hash<evmc::bytes32>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])),
load64be(&s.bytes[8])),
load64be(&s.bytes[16])),
load64be(&s.bytes[24])));
}
};
} // namespace std

View File

@ -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 <algorithm>
#include <map>
#include <vector>
using namespace evmc::literals;
namespace evmc
{
struct account
{
evmc::uint256be balance = {};
std::vector<uint8_t> code;
std::map<evmc::bytes32, evmc::bytes32> storage;
virtual evmc::bytes32 code_hash() const
{
// Extremely dumb "hash" function.
evmc::bytes32 ret{};
for (std::vector<uint8_t>::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<evmc::address, account>;
} // 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<ExampleHost>(context);
}
}

232
tests/evmc_c/example_vm.c Normal file
View File

@ -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 <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/// 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;
}

212
tests/evmc_c/helpers.h Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
/**
* 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;
}
/** @} */
/** @} */

17
tests/test_host_vm.nim Normal file
View File

@ -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()