vendor: Upgrade EVMC to version 6.0.0

This commit is contained in:
Paweł Bylica 2018-10-18 18:32:19 +02:00
parent d7fefbbc78
commit a54ce7f361
No known key found for this signature in database
GPG key ID: 7A0C037434FE77EF
8 changed files with 401 additions and 248 deletions

View file

@ -15,9 +15,9 @@ package evmc
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static inline int set_option(struct evmc_instance* instance, char* name, char* value) static inline enum evmc_set_option_result set_option(struct evmc_instance* instance, char* name, char* value)
{ {
int ret = evmc_set_option(instance, name, value); enum evmc_set_option_result ret = evmc_set_option(instance, name, value);
free(name); free(name);
free(value); free(value);
return ret; return ret;
@ -29,29 +29,29 @@ struct extended_context
int64_t index; int64_t index;
}; };
extern const struct evmc_context_fn_table evmc_go_fn_table; extern const struct evmc_host_interface evmc_go_host;
static struct evmc_result execute_wrapper(struct evmc_instance* instance, int64_t context_index, enum evmc_revision rev, static struct evmc_result execute_wrapper(struct evmc_instance* instance,
const struct evmc_address* destination, const struct evmc_address* sender, const struct evmc_uint256be* value, int64_t context_index, enum evmc_revision rev,
const uint8_t* input_data, size_t input_size, const struct evmc_uint256be* code_hash, int64_t gas, enum evmc_call_kind kind, uint32_t flags, int32_t depth, int64_t gas,
int32_t depth, enum evmc_call_kind kind, uint32_t flags, const uint8_t* code, size_t code_size) const evmc_address* destination, const evmc_address* sender,
const uint8_t* input_data, size_t input_size, const evmc_uint256be* value,
const uint8_t* code, size_t code_size, const evmc_bytes32* create2_salt)
{ {
struct evmc_uint256be create2_salt = {};
struct evmc_message msg = { struct evmc_message msg = {
*destination,
*sender,
*value,
input_data,
input_size,
*code_hash,
create2_salt,
gas,
depth,
kind, kind,
flags, flags,
depth,
gas,
*destination,
*sender,
input_data,
input_size,
*value,
*create2_salt,
}; };
struct extended_context ctx = {{&evmc_go_fn_table}, context_index}; struct extended_context ctx = {{&evmc_go_host}, context_index};
return evmc_execute(instance, &ctx.context, rev, &msg, code, code_size); return evmc_execute(instance, &ctx.context, rev, &msg, code, code_size);
} }
*/ */
@ -68,10 +68,10 @@ import (
// Static asserts. // Static asserts.
const ( const (
_ = uint(common.HashLength - C.sizeof_struct_evmc_uint256be) // The size of evmc_uint256be equals the size of Hash. _ = uint(common.HashLength - C.sizeof_evmc_bytes32) // The size of evmc_bytes32 equals the size of Hash.
_ = uint(C.sizeof_struct_evmc_uint256be - common.HashLength) _ = uint(C.sizeof_evmc_bytes32 - common.HashLength)
_ = uint(common.AddressLength - C.sizeof_struct_evmc_address) // The size of evmc_address equals the size of Address. _ = uint(common.AddressLength - C.sizeof_evmc_address) // The size of evmc_address equals the size of Address.
_ = uint(C.sizeof_struct_evmc_address - common.AddressLength) _ = uint(C.sizeof_evmc_address - common.AddressLength)
) )
type Error int32 type Error int32
@ -185,18 +185,34 @@ func (instance *Instance) Version() string {
return C.GoString(instance.handle.version) return C.GoString(instance.handle.version)
} }
type Capability uint32
const (
CapabilityEVM1 Capability = C.EVMC_CAPABILITY_EVM1
CapabilityEWASM Capability = C.EVMC_CAPABILITY_EWASM
)
func (instance *Instance) HasCapability(capability Capability) bool {
return bool(C.evmc_vm_has_capability(instance.handle, uint32(capability)))
}
func (instance *Instance) SetOption(name string, value string) (err error) { func (instance *Instance) SetOption(name string, value string) (err error) {
r := C.set_option(instance.handle, C.CString(name), C.CString(value)) r := C.set_option(instance.handle, C.CString(name), C.CString(value))
if r != 1 { switch r {
case C.EVMC_SET_OPTION_INVALID_NAME:
err = fmt.Errorf("evmc: option '%s' not accepted", name) err = fmt.Errorf("evmc: option '%s' not accepted", name)
case C.EVMC_SET_OPTION_INVALID_VALUE:
err = fmt.Errorf("evmc: option '%s' has invalid value", name)
case C.EVMC_SET_OPTION_SUCCESS:
} }
return err return err
} }
func (instance *Instance) Execute(ctx HostContext, rev Revision, func (instance *Instance) Execute(ctx HostContext, rev Revision,
destination common.Address, sender common.Address, value common.Hash, input []byte, codeHash common.Hash, gas int64, kind CallKind, static bool, depth int, gas int64,
depth int, kind CallKind, static bool, code []byte) (output []byte, gasLeft int64, err error) { destination common.Address, sender common.Address, input []byte, value common.Hash,
code []byte, create2Salt common.Hash) (output []byte, gasLeft int64, err error) {
flags := C.uint32_t(0) flags := C.uint32_t(0)
if static { if static {
@ -207,11 +223,12 @@ func (instance *Instance) Execute(ctx HostContext, rev Revision,
// FIXME: Clarify passing by pointer vs passing by value. // FIXME: Clarify passing by pointer vs passing by value.
evmcDestination := evmcAddress(destination) evmcDestination := evmcAddress(destination)
evmcSender := evmcAddress(sender) evmcSender := evmcAddress(sender)
evmcValue := evmcUint256be(value) evmcValue := evmcBytes32(value)
evmcCodeHash := evmcUint256be(codeHash) evmcCreate2Salt := evmcBytes32(create2Salt)
result := C.execute_wrapper(instance.handle, C.int64_t(ctxId), uint32(rev), &evmcDestination, &evmcSender, &evmcValue, result := C.execute_wrapper(instance.handle, C.int64_t(ctxId), uint32(rev),
bytesPtr(input), C.size_t(len(input)), &evmcCodeHash, C.int64_t(gas), C.int32_t(depth), C.enum_evmc_call_kind(kind), C.enum_evmc_call_kind(kind), flags, C.int32_t(depth), C.int64_t(gas),
flags, bytesPtr(code), C.size_t(len(code))) &evmcDestination, &evmcSender, bytesPtr(input), C.size_t(len(input)), &evmcValue,
bytesPtr(code), C.size_t(len(code)), &evmcCreate2Salt)
removeHostContext(ctxId) removeHostContext(ctxId)
output = C.GoBytes(unsafe.Pointer(result.output_data), C.int(result.output_size)) output = C.GoBytes(unsafe.Pointer(result.output_data), C.int(result.output_size))
@ -255,16 +272,16 @@ func getHostContext(idx int) HostContext {
return ctx return ctx
} }
func evmcUint256be(in common.Hash) C.struct_evmc_uint256be { func evmcBytes32(in common.Hash) C.evmc_bytes32 {
out := C.struct_evmc_uint256be{} out := C.evmc_bytes32{}
for i := 0; i < len(in); i++ { for i := 0; i < len(in); i++ {
out.bytes[i] = C.uint8_t(in[i]) out.bytes[i] = C.uint8_t(in[i])
} }
return out return out
} }
func evmcAddress(address common.Address) C.struct_evmc_address { func evmcAddress(address common.Address) C.evmc_address {
r := C.struct_evmc_address{} r := C.evmc_address{}
for i := 0; i < len(address); i++ { for i := 0; i < len(address); i++ {
r.bytes[i] = C.uint8_t(address[i]) r.bytes[i] = C.uint8_t(address[i])
} }

View file

@ -11,6 +11,7 @@
#ifndef EVMC_H #ifndef EVMC_H
#define EVMC_H #define EVMC_H
#include <stdbool.h> /* Definition of bool, true and false. */
#include <stddef.h> /* Definition of size_t. */ #include <stddef.h> /* Definition of size_t. */
#include <stdint.h> /* Definition of int64_t, uint64_t. */ #include <stdint.h> /* Definition of int64_t, uint64_t. */
@ -22,30 +23,40 @@ extern "C" {
enum enum
{ {
/** The EVMC ABI version number of the interface declared in this file. */ /**
EVMC_ABI_VERSION = 5 * 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 = 6
}; };
/** /**
* Big-endian 256-bit integer. * The fixed size array of 32 bytes.
* *
* 32 bytes of data representing big-endian 256-bit integer. I.e. bytes[0] is * 32 bytes of data capable of storing e.g. 256-bit hashes.
* the most significant byte, bytes[31] is the least significant byte.
* This type is used to transfer to/from the VM values interpreted by the user
* as both 256-bit integers and 256-bit hashes.
*/ */
struct evmc_uint256be typedef struct evmc_bytes32
{ {
/** The 32 bytes of the big-endian integer or hash. */ /** The 32 bytes. */
uint8_t bytes[32]; 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. */ /** Big-endian 160-bit hash suitable for keeping an Ethereum address. */
struct evmc_address typedef struct evmc_address
{ {
/** The 20 bytes of the hash. */ /** The 20 bytes of the hash. */
uint8_t bytes[20]; uint8_t bytes[20];
}; } evmc_address;
/** The kind of call-like instruction. */ /** The kind of call-like instruction. */
enum evmc_call_kind enum evmc_call_kind
@ -70,16 +81,26 @@ enum evmc_flags
*/ */
struct evmc_message struct evmc_message
{ {
/** The destination of the message. */ /** The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used. */
struct evmc_address destination; enum evmc_call_kind kind;
/** The sender of the message. */
struct evmc_address sender;
/** /**
* The amount of Ether transferred with the message. * Additional flags modifying the call execution behavior.
* In the current version the only valid values are ::EVMC_STATIC or 0.
*/ */
struct evmc_uint256be value; 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. * The message input data.
@ -96,45 +117,29 @@ struct evmc_message
size_t input_size; size_t input_size;
/** /**
* The optional hash of the code of the destination account. * The amount of Ether transferred with the message.
* The null hash MUST be used when not specified.
*/ */
struct evmc_uint256be code_hash; evmc_uint256be value;
/** /**
* The optional value used in new contract address construction. * The optional value used in new contract address construction.
* *
* Ignored unless kind is EVMC_CREATE2. * Ignored unless kind is EVMC_CREATE2.
*/ */
struct evmc_uint256be create2_salt; evmc_bytes32 create2_salt;
/** The amount of gas for message execution. */
int64_t gas;
/** The call depth. */
int32_t depth;
/** 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 transaction and block data for execution. */ /** The transaction and block data for execution. */
struct evmc_tx_context struct evmc_tx_context
{ {
struct evmc_uint256be tx_gas_price; /**< The transaction gas price. */ evmc_uint256be tx_gas_price; /**< The transaction gas price. */
struct evmc_address tx_origin; /**< The transaction origin account. */ evmc_address tx_origin; /**< The transaction origin account. */
struct evmc_address block_coinbase; /**< The miner of the block. */ evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */ int64_t block_number; /**< The block number. */
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. */
struct evmc_uint256be block_difficulty; /**< The block difficulty. */ evmc_uint256be block_difficulty; /**< The block difficulty. */
}; };
struct evmc_context; struct evmc_context;
@ -145,28 +150,24 @@ struct evmc_context;
* This callback function is used by an EVM to retrieve the transaction and * This callback function is used by an EVM to retrieve the transaction and
* block context. * block context.
* *
* @param[out] result The returned transaction context.
* @see ::evmc_tx_context.
* @param context The pointer to the Host execution context. * @param context The pointer to the Host execution context.
* @see ::evmc_context. * @return The transaction context.
*/ */
typedef void (*evmc_get_tx_context_fn)(struct evmc_tx_context* result, typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context);
struct evmc_context* context);
/** /**
* Get block hash callback function. * Get block hash callback function.
* *
* This callback function is used by an EVM to query the block hash of * This callback function is used by a VM to query the hash of the header of the given block.
* a given block. * If the information about the requested block is not available, then this is signalled by
* returning null bytes.
* *
* @param[out] result The returned block hash value.
* @param context The pointer to the Host execution context. * @param context The pointer to the Host execution context.
* @param number The block number. Must be a value between * @param number The block number.
* (and including) 0 and 255. * @return The block hash or null bytes
* if the information about the block is not available.
*/ */
typedef void (*evmc_get_block_hash_fn)(struct evmc_uint256be* result, typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number);
struct evmc_context* context,
int64_t number);
/** /**
* The execution status code. * The execution status code.
@ -265,7 +266,7 @@ enum evmc_status_code
EVMC_ARGUMENT_OUT_OF_RANGE = 14, EVMC_ARGUMENT_OUT_OF_RANGE = 14,
/** /**
* A WebAssembly `unreachable` instruction has been hit during exection. * A WebAssembly `unreachable` instruction has been hit during execution.
*/ */
EVMC_WASM_UNREACHABLE_INSTRUCTION = 15, EVMC_WASM_UNREACHABLE_INSTRUCTION = 15,
@ -376,7 +377,7 @@ struct evmc_result
* This field has valid value only if the result describes successful * This field has valid value only if the result describes successful
* CREATE (evmc_result::status_code is ::EVMC_SUCCESS). * CREATE (evmc_result::status_code is ::EVMC_SUCCESS).
*/ */
struct evmc_address create_address; evmc_address create_address;
/** /**
* Reserved data that MAY be used by a evmc_result object creator. * Reserved data that MAY be used by a evmc_result object creator.
@ -394,33 +395,30 @@ struct evmc_result
/** /**
* Check account existence callback function * Check account existence callback function.
* *
* This callback function is used by the EVM to check if * This callback function is used by the VM to check if
* there exists an account at given address. * there exists an account at given address.
* @param context The pointer to the Host execution context. * @param context The pointer to the Host execution context.
* @see ::evmc_context.
* @param address The address of the account the query is about. * @param address The address of the account the query is about.
* @return 1 if exists, 0 otherwise. * @return true if exists, false otherwise.
*/ */
typedef int (*evmc_account_exists_fn)(struct evmc_context* context, typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address);
const struct evmc_address* address);
/** /**
* Get storage callback function. * Get storage callback function.
* *
* This callback function is used by an EVM to query the given contract * This callback function is used by a VM to query the given account storage entry.
* storage entry. *
* @param[out] result The returned storage value. * @param context The Host execution context.
* @param context The pointer to the Host execution context. * @param address The address of the account.
* @see ::evmc_context. * @param key The index of the account's storage entry.
* @param address The address of the contract. * @return The storage value at the given storage key or null bytes
* @param key The index of the storage entry. * if the account does not exist.
*/ */
typedef void (*evmc_get_storage_fn)(struct evmc_uint256be* result, typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context,
struct evmc_context* context, const evmc_address* address,
const struct evmc_address* address, const evmc_bytes32* key);
const struct evmc_uint256be* key);
/** /**
@ -431,6 +429,7 @@ typedef void (*evmc_get_storage_fn)(struct evmc_uint256be* result,
* - 0 is zero value, * - 0 is zero value,
* - X != 0 (X is any value other than 0), * - X != 0 (X is any value other than 0),
* - Y != X, Y != 0 (Y is any value other than X and 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. * - the "->" means the change from one value to another.
*/ */
enum evmc_storage_status enum evmc_storage_status
@ -445,70 +444,79 @@ enum evmc_storage_status
*/ */
EVMC_STORAGE_MODIFIED = 1, 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. * A new storage item has been added: 0 -> X.
*/ */
EVMC_STORAGE_ADDED = 2, EVMC_STORAGE_ADDED = 3,
/** /**
* A storage item has been deleted: X -> 0. * A storage item has been deleted: X -> 0.
*/ */
EVMC_STORAGE_DELETED = 3 EVMC_STORAGE_DELETED = 4
}; };
/** /**
* Set storage callback function. * Set storage callback function.
* *
* This callback function is used by an EVM to update the given contract * This callback function is used by a VM to update the given account storage entry.
* 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 context The pointer to the Host execution context.
* @see ::evmc_context. * @param address The address of the account.
* @param address The address of the contract.
* @param key The index of the storage entry. * @param key The index of the storage entry.
* @param value The value to be stored. * @param value The value to be stored.
* @return The effect on the storage item, @see ::evmc_storage_status. * @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_context* context,
const struct evmc_address* address, const evmc_address* address,
const struct evmc_uint256be* key, const evmc_bytes32* key,
const struct evmc_uint256be* value); const evmc_bytes32* value);
/** /**
* Get balance callback function. * Get balance callback function.
* *
* This callback function is used by an EVM to query the balance of the given * This callback function is used by a VM to query the balance of the given account.
* address. *
* @param[out] result The returned balance value.
* @param context The pointer to the Host execution context. * @param context The pointer to the Host execution context.
* @see ::evmc_context. * @param address The address of the account.
* @param address The address. * @return The balance of the given account or 0 if the account does not exist.
*/ */
typedef void (*evmc_get_balance_fn)(struct evmc_uint256be* result, typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context,
struct evmc_context* context, const evmc_address* address);
const struct evmc_address* address);
/** /**
* Get code size callback function. * Get code size callback function.
* *
* This callback function is used by an EVM to get the size of the code stored * This callback function is used by a VM to get the size of the code stored
* in the account at the given address. For accounts not having a code, this * in the account at the given address.
* function returns 0. *
* @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_context* context, typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address);
const struct evmc_address* address);
/** /**
* Get code size callback function. * Get code size callback function.
* *
* This callback function is used by an EVM to get the keccak256 hash of the code stored * 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 accounts not having a code, this * in the account at the given address. For existing accounts not having a code, this
* function returns keccak256 hash of empty data. For accounts not existing in the state, * function returns keccak256 hash of empty data.
* this function returns 0. *
* @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 void (*evmc_get_code_hash_fn)(struct evmc_uint256be* result, typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context,
struct evmc_context* context, const evmc_address* address);
const struct evmc_address* address);
/** /**
* Copy code callback function. * Copy code callback function.
@ -529,7 +537,7 @@ typedef void (*evmc_get_code_hash_fn)(struct evmc_uint256be* result,
* @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_context* context,
const struct evmc_address* address, const evmc_address* address,
size_t code_offset, size_t code_offset,
uint8_t* buffer_data, uint8_t* buffer_data,
size_t buffer_size); size_t buffer_size);
@ -547,8 +555,8 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context,
* transferred. * transferred.
*/ */
typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context, typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context,
const struct evmc_address* address, const evmc_address* address,
const struct evmc_address* beneficiary); const evmc_address* beneficiary);
/** /**
* Log callback function. * Log callback function.
@ -565,35 +573,31 @@ typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context,
* 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_context* context,
const struct evmc_address* address, const evmc_address* address,
const uint8_t* data, const uint8_t* data,
size_t data_size, size_t data_size,
const struct evmc_uint256be topics[], const evmc_bytes32 topics[],
size_t topics_count); size_t topics_count);
/** /**
* Pointer to the callback function supporting EVM calls. * Pointer to the callback function supporting EVM calls.
* *
* @param[out] result The result of the call. The result object is not
* initialized by the EVM, the Client MUST correctly
* initialize all expected fields of the structure.
* @param context The pointer to the Host execution context. * @param context The pointer to the Host execution context.
* @see ::evmc_context. * @param msg The call parameters.
* @param msg Call parameters. @see ::evmc_message. * @return The result of the call.
*/ */
typedef void (*evmc_call_fn)(struct evmc_result* result, typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context,
struct evmc_context* context,
const struct evmc_message* msg); const struct evmc_message* msg);
/** /**
* The context interface. * The Host interface.
* *
* The set of all callback functions expected by EVM instances. This is C * The set of all callback functions expected by VM instances. This is C
* realisation of vtable for OOP interface (only virtual methods, no data). * realisation of vtable for OOP interface (only virtual methods, no data).
* Host implementations SHOULD create constant singletons of this (similarly * Host implementations SHOULD create constant singletons of this (similarly
* to vtables) to lower the maintenance and memory management cost. * to vtables) to lower the maintenance and memory management cost.
*/ */
struct evmc_context_fn_table struct evmc_host_interface
{ {
/** Check account existence callback function. */ /** Check account existence callback function. */
evmc_account_exists_fn account_exists; evmc_account_exists_fn account_exists;
@ -645,8 +649,8 @@ struct evmc_context_fn_table
*/ */
struct evmc_context struct evmc_context
{ {
/** Function table defining the context interface (vtable). */ /** The Host interface. */
const struct evmc_context_fn_table* fn_table; const struct evmc_host_interface* host;
}; };
@ -660,6 +664,15 @@ struct evmc_instance;
*/ */
typedef void (*evmc_destroy_fn)(struct evmc_instance* evm); typedef void (*evmc_destroy_fn)(struct evmc_instance* evm);
/**
* 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 EVM instance. * Configures the EVM instance.
@ -672,9 +685,11 @@ typedef void (*evmc_destroy_fn)(struct evmc_instance* evm);
* @param evm The EVM instance to be configured. * @param evm The EVM 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 1 if the option set successfully, 0 otherwise. * @return The outcome of the operation.
*/ */
typedef int (*evmc_set_option_fn)(struct evmc_instance* evm, char const* name, char const* value); typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm,
char const* name,
char const* value);
/** /**
@ -717,6 +732,32 @@ typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance,
uint8_t const* code, uint8_t const* code,
size_t code_size); size_t code_size);
/**
* Possible capabilities of a VM.
*/
enum evmc_capabilities
{
EVMC_CAPABILITY_EVM1 = (1u << 0), /**< The VM is capable of executing EVM1 bytecode. */
EVMC_CAPABILITY_EWASM = (1u << 1) /**< The VM is capable of execution ewasm bytecode. */
};
/**
* 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_instance::set_option.
*
* @param instance The EVM instance.
* @return The supported capabilities of the VM. @see evmc_capabilities.
*/
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance);
/** The opaque type representing a Client-side tracer object. */ /** The opaque type representing a Client-side tracer object. */
struct evmc_tracer_context; struct evmc_tracer_context;
@ -762,7 +803,7 @@ typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context,
enum evmc_status_code status_code, enum evmc_status_code status_code,
int64_t gas_left, int64_t gas_left,
size_t stack_num_items, size_t stack_num_items,
const struct evmc_uint256be* pushed_stack_item, const evmc_uint256be* pushed_stack_item,
size_t memory_size, size_t memory_size,
size_t changed_memory_offset, size_t changed_memory_offset,
size_t changed_memory_size, size_t changed_memory_size,
@ -824,6 +865,16 @@ struct evmc_instance
/** Pointer to function executing a code by the EVM instance. */ /** Pointer to function executing a code by the EVM instance. */
evmc_execute_fn execute; evmc_execute_fn execute;
/**
* Pointer to function returning capabilities supported by the VM instance.
*
* The value returned might change when different options are requested via set_option.
*
* 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_capabilities;
/** /**
* Optional pointer to function setting the EVM instruction tracer. * Optional pointer to function setting the EVM instruction tracer.
* *
@ -857,7 +908,7 @@ struct evmc_instance
* *
* @return EVM instance or NULL indicating instance creation failure. * @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 #endif
#if __cplusplus #if __cplusplus

View file

@ -10,6 +10,8 @@
* These are convenient for languages where invoking function pointers * These are convenient for languages where invoking function pointers
* is "ugly" or impossible (such as Go). * is "ugly" or impossible (such as Go).
* *
* It also contains helpers (overloaded operators) for using EVMC types effectively in C++.
*
* @defgroup helpers EVMC Helpers * @defgroup helpers EVMC Helpers
* @{ * @{
*/ */
@ -41,6 +43,17 @@ static inline const char* evmc_vm_version(struct evmc_instance* instance)
return instance->version; return instance->version;
} }
/**
* Checks if the VM instance has the given capability.
*
* @see evmc_get_capabilities_fn
*/
static inline bool evmc_vm_has_capability(struct evmc_instance* vm,
enum evmc_capabilities capability)
{
return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0;
}
/** /**
* Destroys the VM instance. * Destroys the VM instance.
* *
@ -56,13 +69,13 @@ static inline void evmc_destroy(struct evmc_instance* instance)
* *
* @see evmc_set_option_fn * @see evmc_set_option_fn
*/ */
static inline int evmc_set_option(struct evmc_instance* instance, static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance,
char const* name, char const* name,
char const* value) char const* value)
{ {
if (instance->set_option) if (instance->set_option)
return instance->set_option(instance, name, value); return instance->set_option(instance, name, value);
return 0; return EVMC_SET_OPTION_INVALID_NAME;
} }
/** /**

View file

@ -7,7 +7,20 @@
#include <stdlib.h> #include <stdlib.h>
const struct evmc_context_fn_table evmc_go_fn_table = {
void evmc_go_free_result_output(const struct evmc_result* result)
{
free((void*)result->output_data);
}
/* Go does not support exporting functions with parameters with const modifiers,
* so we have to cast function pointers to the function types defined in EVMC.
* This disables any type checking of exported Go functions. To mitigate this
* problem the go_exported_functions_type_checks() function simulates usage
* of Go exported functions with expected types to check them during compilation.
*/
const struct evmc_host_interface evmc_go_host = {
(evmc_account_exists_fn)accountExists, (evmc_account_exists_fn)accountExists,
(evmc_get_storage_fn)getStorage, (evmc_get_storage_fn)getStorage,
(evmc_set_storage_fn)setStorage, (evmc_set_storage_fn)setStorage,
@ -22,7 +35,74 @@ const struct evmc_context_fn_table evmc_go_fn_table = {
(evmc_emit_log_fn)emitLog, (evmc_emit_log_fn)emitLog,
}; };
void evmc_go_free_result_output(const struct evmc_result* result)
#pragma GCC diagnostic error "-Wconversion"
static inline void go_exported_functions_type_checks()
{ {
free((void*)result->output_data); struct evmc_context* context = NULL;
evmc_address* address = NULL;
evmc_bytes32 bytes32;
uint8_t* data = NULL;
size_t size = 0;
int64_t number = 0;
struct evmc_message* message = NULL;
evmc_uint256be uint256be;
(void)uint256be;
struct evmc_tx_context tx_context;
(void)tx_context;
struct evmc_result result;
(void)result;
enum evmc_storage_status storage_status;
(void)storage_status;
bool bool_flag;
(void)bool_flag;
evmc_account_exists_fn account_exists_fn = NULL;
bool_flag = account_exists_fn(context, address);
bool_flag = accountExists(context, address);
evmc_get_storage_fn get_storage_fn = NULL;
bytes32 = get_storage_fn(context, address, &bytes32);
bytes32 = getStorage(context, address, &bytes32);
evmc_set_storage_fn set_storage_fn = NULL;
storage_status = set_storage_fn(context, address, &bytes32, &bytes32);
storage_status = setStorage(context, address, &bytes32, &bytes32);
evmc_get_balance_fn get_balance_fn = NULL;
uint256be = get_balance_fn(context, address);
uint256be = getBalance(context, address);
evmc_get_code_size_fn get_code_size_fn = NULL;
size = get_code_size_fn(context, address);
size = getCodeSize(context, address);
evmc_get_code_hash_fn get_code_hash_fn = NULL;
bytes32 = get_code_hash_fn(context, address);
bytes32 = getCodeHash(context, address);
evmc_copy_code_fn copy_code_fn = NULL;
size = copy_code_fn(context, address, size, data, size);
size = copyCode(context, address, size, data, size);
evmc_selfdestruct_fn selfdestruct_fn = NULL;
selfdestruct_fn(context, address, address);
selfdestruct(context, address, address);
evmc_call_fn call_fn = NULL;
result = call_fn(context, message);
result = call(context, message);
evmc_get_tx_context_fn get_tx_context_fn = NULL;
tx_context = get_tx_context_fn(context);
tx_context = getTxContext(context);
evmc_get_block_hash_fn get_block_hash_fn = NULL;
bytes32 = get_block_hash_fn(context, number);
bytes32 = getBlockHash(context, number);
evmc_emit_log_fn emit_log_fn = NULL;
emit_log_fn(context, address, data, size, &bytes32, size);
emitLog(context, address, data, size, &bytes32, size);
} }

View file

@ -41,11 +41,12 @@ type StorageStatus int
const ( const (
StorageUnchanged StorageStatus = C.EVMC_STORAGE_UNCHANGED StorageUnchanged StorageStatus = C.EVMC_STORAGE_UNCHANGED
StorageModified StorageStatus = C.EVMC_STORAGE_MODIFIED StorageModified StorageStatus = C.EVMC_STORAGE_MODIFIED
StorageModifiedAgain StorageStatus = C.EVMC_STORAGE_MODIFIED_AGAIN
StorageAdded StorageStatus = C.EVMC_STORAGE_ADDED StorageAdded StorageStatus = C.EVMC_STORAGE_ADDED
StorageDeleted StorageStatus = C.EVMC_STORAGE_DELETED StorageDeleted StorageStatus = C.EVMC_STORAGE_DELETED
) )
func goAddress(in C.struct_evmc_address) common.Address { func goAddress(in C.evmc_address) common.Address {
out := common.Address{} out := common.Address{}
for i := 0; i < len(out); i++ { for i := 0; i < len(out); i++ {
out[i] = byte(in.bytes[i]) out[i] = byte(in.bytes[i])
@ -53,7 +54,7 @@ func goAddress(in C.struct_evmc_address) common.Address {
return out return out
} }
func goHash(in C.struct_evmc_uint256be) common.Hash { func goHash(in C.evmc_bytes32) common.Hash {
out := common.Hash{} out := common.Hash{}
for i := 0; i < len(out); i++ { for i := 0; i < len(out); i++ {
out[i] = byte(in.bytes[i]) out[i] = byte(in.bytes[i])
@ -87,56 +88,49 @@ type HostContext interface {
} }
//export accountExists //export accountExists
func accountExists(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address) C.int { func accountExists(pCtx unsafe.Pointer, pAddr *C.evmc_address) C.bool {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
exists := ctx.AccountExists(goAddress(*pAddr)) return C.bool(ctx.AccountExists(goAddress(*pAddr)))
r := C.int(0)
if exists {
r = 1
}
return r
} }
//export getStorage //export getStorage
func getStorage(pResult *C.struct_evmc_uint256be, pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pKey *C.struct_evmc_uint256be) { func getStorage(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pKey *C.evmc_bytes32) C.evmc_bytes32 {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
value := ctx.GetStorage(goAddress(*pAddr), goHash(*pKey)) return evmcBytes32(ctx.GetStorage(goAddress(*pAddr), goHash(*pKey)))
*pResult = evmcUint256be(value)
} }
//export setStorage //export setStorage
func setStorage(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pKey *C.struct_evmc_uint256be, pVal *C.struct_evmc_uint256be) C.enum_evmc_storage_status { func setStorage(pCtx unsafe.Pointer, pAddr *C.evmc_address, pKey *C.evmc_bytes32, pVal *C.evmc_bytes32) C.enum_evmc_storage_status {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
return C.enum_evmc_storage_status(ctx.SetStorage(goAddress(*pAddr), goHash(*pKey), goHash(*pVal))) return C.enum_evmc_storage_status(ctx.SetStorage(goAddress(*pAddr), goHash(*pKey), goHash(*pVal)))
} }
//export getBalance //export getBalance
func getBalance(pResult *C.struct_evmc_uint256be, pCtx unsafe.Pointer, pAddr *C.struct_evmc_address) { func getBalance(pCtx unsafe.Pointer, pAddr *C.evmc_address) C.evmc_uint256be {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
balance := ctx.GetBalance(goAddress(*pAddr)) return evmcBytes32(ctx.GetBalance(goAddress(*pAddr)))
*pResult = evmcUint256be(balance)
} }
//export getCodeSize //export getCodeSize
func getCodeSize(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address) C.size_t { func getCodeSize(pCtx unsafe.Pointer, pAddr *C.evmc_address) C.size_t {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
return C.size_t(ctx.GetCodeSize(goAddress(*pAddr))) return C.size_t(ctx.GetCodeSize(goAddress(*pAddr)))
} }
//export getCodeHash //export getCodeHash
func getCodeHash(pResult *C.struct_evmc_uint256be, pCtx unsafe.Pointer, pAddr *C.struct_evmc_address) { func getCodeHash(pCtx unsafe.Pointer, pAddr *C.evmc_address) C.evmc_bytes32 {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
*pResult = evmcUint256be(ctx.GetCodeHash(goAddress(*pAddr))) return evmcBytes32(ctx.GetCodeHash(goAddress(*pAddr)))
} }
//export copyCode //export copyCode
func copyCode(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, offset C.size_t, p *C.uint8_t, size C.size_t) C.size_t { func copyCode(pCtx unsafe.Pointer, pAddr *C.evmc_address, offset C.size_t, p *C.uint8_t, size C.size_t) C.size_t {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
code := ctx.GetCode(goAddress(*pAddr)) code := ctx.GetCode(goAddress(*pAddr))
@ -157,40 +151,39 @@ func copyCode(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, offset C.size_t
} }
//export selfdestruct //export selfdestruct
func selfdestruct(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pBeneficiary *C.struct_evmc_address) { func selfdestruct(pCtx unsafe.Pointer, pAddr *C.evmc_address, pBeneficiary *C.evmc_address) {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
ctx.Selfdestruct(goAddress(*pAddr), goAddress(*pBeneficiary)) ctx.Selfdestruct(goAddress(*pAddr), goAddress(*pBeneficiary))
} }
//export getTxContext //export getTxContext
func getTxContext(pResult unsafe.Pointer, pCtx unsafe.Pointer) { func getTxContext(pCtx unsafe.Pointer) C.struct_evmc_tx_context {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
gasPrice, origin, coinbase, number, timestamp, gasLimit, difficulty := ctx.GetTxContext() gasPrice, origin, coinbase, number, timestamp, gasLimit, difficulty := ctx.GetTxContext()
*(*C.struct_evmc_tx_context)(pResult) = C.struct_evmc_tx_context{ return C.struct_evmc_tx_context{
evmcUint256be(gasPrice), evmcBytes32(gasPrice),
evmcAddress(origin), evmcAddress(origin),
evmcAddress(coinbase), evmcAddress(coinbase),
C.int64_t(number), C.int64_t(number),
C.int64_t(timestamp), C.int64_t(timestamp),
C.int64_t(gasLimit), C.int64_t(gasLimit),
evmcUint256be(difficulty), evmcBytes32(difficulty),
} }
} }
//export getBlockHash //export getBlockHash
func getBlockHash(pResult *C.struct_evmc_uint256be, pCtx unsafe.Pointer, number int64) { func getBlockHash(pCtx unsafe.Pointer, number int64) C.evmc_bytes32 {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
return evmcBytes32(ctx.GetBlockHash(number))
*pResult = evmcUint256be(ctx.GetBlockHash(number))
} }
//export emitLog //export emitLog
func emitLog(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pData unsafe.Pointer, dataSize C.size_t, pTopics unsafe.Pointer, topicsCount C.size_t) { func emitLog(pCtx unsafe.Pointer, pAddr *C.evmc_address, pData unsafe.Pointer, dataSize C.size_t, pTopics unsafe.Pointer, topicsCount C.size_t) {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
@ -208,7 +201,7 @@ func emitLog(pCtx unsafe.Pointer, pAddr *C.struct_evmc_address, pData unsafe.Poi
} }
//export call //export call
func call(pResult *C.struct_evmc_result, pCtx unsafe.Pointer, msg *C.struct_evmc_message) { func call(pCtx unsafe.Pointer, msg *C.struct_evmc_message) C.struct_evmc_result {
idx := int((*C.struct_extended_context)(pCtx).index) idx := int((*C.struct_extended_context)(pCtx).index)
ctx := getHostContext(idx) ctx := getHostContext(idx)
@ -234,5 +227,5 @@ func call(pResult *C.struct_evmc_result, pCtx unsafe.Pointer, msg *C.struct_evmc
result.release = (C.evmc_release_result_fn)(C.evmc_go_free_result_output) result.release = (C.evmc_release_result_fn)(C.evmc_go_free_result_output)
} }
*pResult = result return result
} }

View file

@ -16,7 +16,7 @@
#define DLL_HANDLE HMODULE #define DLL_HANDLE HMODULE
#define DLL_OPEN(filename) LoadLibrary(filename) #define DLL_OPEN(filename) LoadLibrary(filename)
#define DLL_CLOSE(handle) FreeLibrary(handle) #define DLL_CLOSE(handle) FreeLibrary(handle)
#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn) GetProcAddress(handle, name) #define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) GetProcAddress(handle, name)
#define HAVE_STRCPY_S 1 #define HAVE_STRCPY_S 1
#else #else
#include <dlfcn.h> #include <dlfcn.h>
@ -69,8 +69,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
// Create name buffer with the prefix. // Create name buffer with the prefix.
const char prefix[] = "evmc_create_"; const char prefix[] = "evmc_create_";
const size_t prefix_length = strlen(prefix); const size_t prefix_length = strlen(prefix);
char name[sizeof(prefix) + PATH_MAX_LENGTH]; char prefixed_name[sizeof(prefix) + PATH_MAX_LENGTH];
strcpy_s(name, sizeof(name), prefix); strcpy_s(prefixed_name, sizeof(prefixed_name), prefix);
// Find filename in the path. // Find filename in the path.
const char* sep_pos = strrchr(filename, '/'); const char* sep_pos = strrchr(filename, '/');
@ -87,30 +87,28 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
if (strncmp(name_pos, lib_prefix, lib_prefix_length) == 0) if (strncmp(name_pos, lib_prefix, lib_prefix_length) == 0)
name_pos += lib_prefix_length; name_pos += lib_prefix_length;
strcpy_s(name + prefix_length, PATH_MAX_LENGTH, name_pos); char* base_name = prefixed_name + prefix_length;
strcpy_s(base_name, PATH_MAX_LENGTH, name_pos);
// Trim the file extension. // Trim the file extension.
char* ext_pos = strrchr(name, '.'); char* ext_pos = strrchr(prefixed_name, '.');
if (ext_pos) if (ext_pos)
*ext_pos = 0; *ext_pos = 0;
// Replace all "-" with "_". // Replace all "-" with "_".
char* dash_pos = name; char* dash_pos = base_name;
while ((dash_pos = strchr(dash_pos, '-')) != NULL) while ((dash_pos = strchr(dash_pos, '-')) != NULL)
*dash_pos++ = '_'; *dash_pos++ = '_';
// Search for the "full name" based function name. // Search for the built function name.
create_fn = DLL_GET_CREATE_FN(handle, name); while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL)
if (!create_fn)
{ {
// Try the "short name" based function name. // Shorten the base name by skipping the `word_` segment.
const char* short_name_pos = strrchr(name, '_'); const char* shorter_name_pos = strchr(base_name, '_');
if (short_name_pos) if (!shorter_name_pos)
{ break;
short_name_pos += 1;
memmove(name + prefix_length, short_name_pos, strlen(short_name_pos) + 1); memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1);
create_fn = DLL_GET_CREATE_FN(handle, name);
}
} }
if (!create_fn) if (!create_fn)

View file

@ -60,15 +60,16 @@ enum evmc_loader_error_code
* "libexample-interpreter.so", * "libexample-interpreter.so",
* - the "lib" prefix and file extension are stripped from the name: * - the "lib" prefix and file extension are stripped from the name:
* "example-interpreter" * "example-interpreter"
* - all "-" are replaced with "_" to construct _full name_: * - all "-" are replaced with "_" to construct _base name_:
* "example_interpreter", * "example_interpreter",
* - the _full name_ is split by "_" char and the last item is taken to form the _short name_: * - the function name "evmc_create_" + _base name_ is searched in the library:
* "interpreter",
* - the name "evmc_create_" + _full name_ is checked in the library:
* "evmc_create_example_interpreter", * "evmc_create_example_interpreter",
* - then, the name "evmc_create_" + _short name_ is checked in the library: * - if function not found, the _base name_ is shorten by skipping the first word separated by "_":
* "evmc_create_interpreter". * "interpreter",
* - lastly, the name "evmc_create" is checked in the library * - 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.

10
vendor/vendor.json vendored
View file

@ -105,12 +105,12 @@
"revisionTime": "2018-01-22T22:25:45Z" "revisionTime": "2018-01-22T22:25:45Z"
}, },
{ {
"checksumSHA1": "gvcZHEaRD0J5Y6QrFTo0GrR2TkE=", "checksumSHA1": "HxlAMyHDmEEDsOIlULQD2HPoN40=",
"path": "github.com/ethereum/evmc/bindings/go/evmc", "path": "github.com/ethereum/evmc/bindings/go/evmc",
"revision": "224080ef8c8d99f5cfe59067a3bd995be23349bf", "revision": "3b3aab4f7720f80ba0b989da88b60907c2a8f733",
"revisionTime": "2018-08-28T21:11:56Z", "revisionTime": "2018-10-18T15:29:18Z",
"version": "=v5.2.0", "version": "v6",
"versionExact": "v5.2.0" "versionExact": "v6"
}, },
{ {
"checksumSHA1": "7oFpbmDfGobwKsFLIf6wMUvVoKw=", "checksumSHA1": "7oFpbmDfGobwKsFLIf6wMUvVoKw=",