mirror of
https://github.com/status-im/evmc.git
synced 2025-02-22 07:58:19 +00:00
add C mocked host
This commit is contained in:
parent
b02940241e
commit
a0269efd5b
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ build/
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.exe
|
941
tests/evmc_c/evmc.h
Normal file
941
tests/evmc_c/evmc.h
Normal 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
805
tests/evmc_c/evmc.hpp
Normal 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
|
180
tests/evmc_c/example_host.cpp
Normal file
180
tests/evmc_c/example_host.cpp
Normal 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
232
tests/evmc_c/example_vm.c
Normal 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
212
tests/evmc_c/helpers.h
Normal 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
17
tests/test_host_vm.nim
Normal 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()
|
Loading…
x
Reference in New Issue
Block a user