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