Merge pull request #160 from ethereum/vm_guide

VM Implementation Guide
This commit is contained in:
Paweł Bylica 2018-10-19 17:34:45 +02:00 committed by GitHub
commit 7ae0021859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 35 deletions

View File

@ -101,7 +101,8 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
INPUT = \
include/evmc/evmc.h include/evmc/helpers.h include/evmc/helpers.hpp include/evmc/loader.h include/evmc/utils.h include/evmc/instructions.h \
docs/
docs/ \
examples/example_vm.c
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
RECURSIVE = NO

View File

@ -9,6 +9,7 @@ to access Ethereum environment and state.
# Guides
- [Host Implementation Guide](@ref hostguide)
- [VM Implementation Guide](@ref vmguide)
# Versioning {#versioning}

View File

@ -20,7 +20,7 @@ When Host implementation is ready it's time to start using EVMC VMs.
1. Firstly, create a VM instance. You need to know what is the name of the "create"
function in particular VM implementation. The EVMC recommends to name the
function by the VM codename, e.g. ::evmc_create_examplevm().
function by the VM codename, e.g. ::evmc_create_example_vm().
Invoking the create function will give you the VM instance (::evmc_instance).
It is recommended to create the VM instance once.

46
docs/VM_Guide.md Normal file
View File

@ -0,0 +1,46 @@
# EVMC VM Implementation Guide {#vmguide}
> How to add EVMC interface to Your Ethereum VM implementation.
## An example
You can start with [the example implementation of EVMC VM interface in C](@ref example_vm.c).
## VM instance
The VM instance is described by the ::evmc_instance struct. It contains the
basic static information about the VM like name and version. The struct also
includes the VM methods (in form of function pointers) to allow the Host
to interact with the VM.
Some methods are optional. The VM must implement at least all mandatory ones.
The instance struct must also include the EVMC ABI version (::EVMC_ABI_VERSION)
it was build with. This allows the Host to check the ABI compatibility when
loading VMs dynamically.
The VM instance is created and returned as a pointer from a special "create"
function. The EVMC recommends to name the function by the VM codename,
e.g. ::evmc_create_example_vm().
## VM methods implementation
Each VM methods takes the pointer to the ::evmc_instance as the first argument.
The VM implementation can extend the ::evmc_instance struct for storing internal
data. This allow implementing the VM in object-oriented manner.
The most important method is ::evmc_instance::execute() because it executes EVM code.
Remember that the Host is allowed to invoke the execute method concurrently
so do not store data related to a particular execution context in the VM instance.
## Resource management
All additional resources allocated when the VM instance is created must be
freed when the destroy method is invoked.
The VM implementation can also attach additional resources to the ::evmc_result
of an execution. These resource must be freed when the ::evmc_result::release()
method is invoked.
*Have fun!*

View File

@ -2,6 +2,15 @@
* Copyright 2018 The EVMC Authors.
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
*/
/// @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 inplementation is done in C only, but could be done in C++ in very
/// similar way.
#include "example_vm.h"
#include <limits.h>
@ -9,34 +18,33 @@
#include <stdlib.h>
#include <string.h>
#define STR(x) #x
#if !defined(PROJECT_VERSION)
#define PROJECT_VERSION 0.0.0
#endif
/// The example VM instance struct extending the evmc_instance.
struct example_vm
{
struct evmc_instance instance;
int verbose;
evmc_trace_callback trace_callback;
struct evmc_tracer_context* tracer_context;
struct evmc_instance instance; ///< The base struct.
int verbose; ///< The verbosity level.
evmc_trace_callback trace_callback; ///< The trace callback.
struct evmc_tracer_context* tracer_context; ///< The tracer context.
};
static void destroy(struct evmc_instance* evm)
/// The implementation of the evmc_instance::destroy() method.
static void destroy(struct evmc_instance* vm)
{
free(evm);
free(vm);
}
/// The example implementation of the evmc_instance::get_capabilities() method.
static evmc_capabilities_flagset get_capabilities(struct evmc_instance* vm)
{
(void)vm;
return EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM;
}
/// Example options.
/// Example VM options.
///
/// VMs are allowed to omit this function implementation.
/// The implementation of the evmc_instance::set_option() method.
/// VMs are allowed to omit this method implementation.
static enum evmc_set_option_result set_option(struct evmc_instance* instance,
const char* name,
const char* value)
@ -60,16 +68,14 @@ static enum evmc_set_option_result set_option(struct evmc_instance* instance,
return EVMC_SET_OPTION_INVALID_NAME;
}
static void release_result(struct evmc_result const* result)
{
(void)result;
}
static void free_result_output_data(struct evmc_result const* result)
/// 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_instance::execute() method.
static struct evmc_result execute(struct evmc_instance* instance,
struct evmc_context* context,
enum evmc_revision rev,
@ -128,7 +134,6 @@ static struct evmc_result execute(struct evmc_instance* instance,
return ret;
}
ret.release = release_result;
ret.status_code = EVMC_FAILURE;
ret.gas_left = 0;
@ -138,6 +143,7 @@ static struct evmc_result execute(struct evmc_instance* instance,
return ret;
}
/// The implementation of the optional evmc_instance::set_tracer() method.
static void set_tracer(struct evmc_instance* instance,
evmc_trace_callback callback,
struct evmc_tracer_context* context)
@ -147,6 +153,19 @@ static void set_tracer(struct evmc_instance* instance,
vm->tracer_context = context;
}
/// @cond internal
/// Stringify the argument.
#define STR(x) #x
#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_instance* evmc_create_example_vm()
{
struct evmc_instance init = {
@ -155,7 +174,7 @@ struct evmc_instance* evmc_create_example_vm()
.version = STR(PROJECT_VERSION),
.destroy = destroy,
.execute = execute,
.get_capabilites = get_capabilities,
.get_capabilities = get_capabilities,
.set_option = set_option,
.set_tracer = set_tracer,
};

View File

@ -876,7 +876,7 @@ struct evmc_instance
* A Client SHOULD only rely on the value returned here if it has queried it after
* it has called set_option.
*/
evmc_get_capabilities_fn get_capabilites;
evmc_get_capabilities_fn get_capabilities;
/**
* Optional pointer to function setting the EVM instruction tracer.
@ -911,7 +911,7 @@ struct evmc_instance
*
* @return EVM instance or NULL indicating instance creation failure.
*/
struct evmc_instance* evmc_create_examplevm(void);
struct evmc_instance* evmc_create_example_vm(void);
#endif
#if __cplusplus

View File

@ -51,7 +51,7 @@ static inline const char* evmc_vm_version(struct evmc_instance* instance)
static inline bool evmc_vm_has_capability(struct evmc_instance* vm,
enum evmc_capabilities capability)
{
return (vm->get_capabilites(vm) & (evmc_capabilities_flagset)capability) != 0;
return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0;
}
/**