2018-08-30 16:27:44 +00:00
|
|
|
/* EVMC: Ethereum Client-VM Connector API.
|
|
|
|
* Copyright 2018 The EVMC Authors.
|
|
|
|
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
|
|
|
*/
|
2018-10-18 12:02:35 +00:00
|
|
|
|
|
|
|
/// @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.
|
|
|
|
|
2018-08-30 16:27:44 +00:00
|
|
|
#include "example_vm.h"
|
2017-09-22 17:27:30 +00:00
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
2016-08-22 17:22:34 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2018-08-28 19:14:32 +00:00
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// The example VM instance struct extending the evmc_instance.
|
2018-08-30 16:27:44 +00:00
|
|
|
struct example_vm
|
2016-12-20 15:50:23 +00:00
|
|
|
{
|
2018-10-18 13:38:10 +00:00
|
|
|
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.
|
2016-08-22 17:22:34 +00:00
|
|
|
};
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// The implementation of the evmc_instance::destroy() method.
|
|
|
|
static void destroy(struct evmc_instance* vm)
|
2016-08-22 17:22:34 +00:00
|
|
|
{
|
2018-10-18 13:38:10 +00:00
|
|
|
free(vm);
|
2016-08-22 17:22:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 13:42:01 +00:00
|
|
|
/// The example implementation of the evmc_instance::get_capabilities() method.
|
2018-09-08 16:23:27 +00:00
|
|
|
static evmc_capabilities_flagset get_capabilities(struct evmc_instance* vm)
|
|
|
|
{
|
|
|
|
(void)vm;
|
|
|
|
return EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM;
|
|
|
|
}
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// Example VM options.
|
2016-08-23 19:24:22 +00:00
|
|
|
///
|
2018-10-18 13:38:10 +00:00
|
|
|
/// The implementation of the evmc_instance::set_option() method.
|
|
|
|
/// VMs are allowed to omit this method implementation.
|
2018-09-05 17:24:02 +00:00
|
|
|
static enum evmc_set_option_result set_option(struct evmc_instance* instance,
|
2018-09-06 13:44:59 +00:00
|
|
|
const char* name,
|
|
|
|
const char* value)
|
2016-08-23 19:24:22 +00:00
|
|
|
{
|
2018-08-30 16:27:44 +00:00
|
|
|
struct example_vm* vm = (struct example_vm*)instance;
|
2018-04-13 06:39:47 +00:00
|
|
|
if (strcmp(name, "verbose") == 0)
|
|
|
|
{
|
2018-09-06 13:44:59 +00:00
|
|
|
if (!value)
|
|
|
|
return EVMC_SET_OPTION_INVALID_VALUE;
|
|
|
|
|
|
|
|
char* end = NULL;
|
|
|
|
long int v = strtol(value, &end, 0);
|
|
|
|
if (end == value) // Parsing the value failed.
|
|
|
|
return EVMC_SET_OPTION_INVALID_VALUE;
|
|
|
|
if (v > 9 || v < -1) // Not in the valid range.
|
2018-09-05 17:24:02 +00:00
|
|
|
return EVMC_SET_OPTION_INVALID_VALUE;
|
2017-09-22 17:27:30 +00:00
|
|
|
vm->verbose = (int)v;
|
2018-09-05 17:24:02 +00:00
|
|
|
return EVMC_SET_OPTION_SUCCESS;
|
2017-04-25 18:07:50 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 17:24:02 +00:00
|
|
|
return EVMC_SET_OPTION_INVALID_NAME;
|
2016-08-23 19:24:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// 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)
|
2017-04-24 15:32:28 +00:00
|
|
|
{
|
|
|
|
free((uint8_t*)result->output_data);
|
|
|
|
}
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// The example implementation of the evmc_instance::execute() method.
|
2018-05-11 11:24:33 +00:00
|
|
|
static struct evmc_result execute(struct evmc_instance* instance,
|
|
|
|
struct evmc_context* context,
|
|
|
|
enum evmc_revision rev,
|
|
|
|
const struct evmc_message* msg,
|
|
|
|
const uint8_t* code,
|
|
|
|
size_t code_size)
|
2016-08-22 17:22:34 +00:00
|
|
|
{
|
2018-04-13 06:39:47 +00:00
|
|
|
struct evmc_result ret = {.status_code = EVMC_INTERNAL_ERROR};
|
2018-04-11 12:56:14 +00:00
|
|
|
if (code_size == 0)
|
|
|
|
{
|
2017-04-24 15:32:28 +00:00
|
|
|
// In case of empty code return a fancy error message.
|
2018-04-13 06:39:47 +00:00
|
|
|
const char* error = rev == EVMC_BYZANTIUM ? "Welcome to Byzantium!" : "Hello Ethereum!";
|
2017-01-27 15:27:34 +00:00
|
|
|
ret.output_data = (const uint8_t*)error;
|
|
|
|
ret.output_size = strlen(error);
|
2018-03-28 13:09:07 +00:00
|
|
|
ret.status_code = EVMC_FAILURE;
|
2017-04-24 15:32:28 +00:00
|
|
|
ret.release = NULL; // We don't need to release the constant messages.
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-30 16:27:44 +00:00
|
|
|
struct example_vm* vm = (struct example_vm*)instance;
|
2017-04-24 15:32:28 +00:00
|
|
|
|
2017-04-25 15:01:46 +00:00
|
|
|
// Simulate executing by checking for some code patterns.
|
|
|
|
// Solidity inline assembly is used in the examples instead of EVM bytecode.
|
2016-08-22 17:22:34 +00:00
|
|
|
|
2017-04-25 15:01:46 +00:00
|
|
|
// Assembly: `{ mstore(0, address()) return(0, msize()) }`.
|
2018-08-28 18:47:00 +00:00
|
|
|
const char return_address[] = "\x30\x60\x00\x52\x59\x60\x00\xf3";
|
2017-04-25 18:07:50 +00:00
|
|
|
|
|
|
|
// Assembly: `{ sstore(0, add(sload(0), 1)) }`
|
2018-08-28 18:47:00 +00:00
|
|
|
const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55";
|
2017-04-25 18:07:50 +00:00
|
|
|
|
|
|
|
if (code_size == strlen(return_address) &&
|
2018-04-13 06:39:47 +00:00
|
|
|
strncmp((const char*)code, return_address, code_size) == 0)
|
|
|
|
{
|
2018-01-23 10:00:55 +00:00
|
|
|
static const size_t address_size = sizeof(msg->destination);
|
2017-04-24 15:32:28 +00:00
|
|
|
uint8_t* output_data = (uint8_t*)malloc(address_size);
|
2018-04-13 06:39:47 +00:00
|
|
|
if (!output_data)
|
|
|
|
{
|
2017-04-25 15:01:46 +00:00
|
|
|
// malloc failed, report internal error.
|
2018-03-28 13:09:07 +00:00
|
|
|
ret.status_code = EVMC_INTERNAL_ERROR;
|
2017-04-25 15:01:46 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2018-01-23 10:00:55 +00:00
|
|
|
memcpy(output_data, &msg->destination, address_size);
|
2018-03-28 13:09:07 +00:00
|
|
|
ret.status_code = EVMC_SUCCESS;
|
2017-04-24 15:32:28 +00:00
|
|
|
ret.output_data = output_data;
|
|
|
|
ret.output_size = address_size;
|
|
|
|
ret.release = &free_result_output_data;
|
|
|
|
return ret;
|
|
|
|
}
|
2018-04-13 06:39:47 +00:00
|
|
|
else if (code_size == strlen(counter) && strncmp((const char*)code, counter, code_size) == 0)
|
|
|
|
{
|
2018-09-06 21:21:49 +00:00
|
|
|
const evmc_bytes32 key = {{0}};
|
2018-09-08 23:42:24 +00:00
|
|
|
evmc_bytes32 value = context->host->get_storage(context, &msg->destination, &key);
|
2018-06-08 10:21:06 +00:00
|
|
|
value.bytes[31]++;
|
2018-09-06 17:01:46 +00:00
|
|
|
context->host->set_storage(context, &msg->destination, &key, &value);
|
2018-03-28 13:09:07 +00:00
|
|
|
ret.status_code = EVMC_SUCCESS;
|
2017-04-25 18:07:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2016-08-22 17:22:34 +00:00
|
|
|
|
2018-03-28 13:09:07 +00:00
|
|
|
ret.status_code = EVMC_FAILURE;
|
2016-08-22 17:22:34 +00:00
|
|
|
ret.gas_left = 0;
|
|
|
|
|
2017-09-22 17:27:30 +00:00
|
|
|
if (vm->verbose)
|
|
|
|
printf("Execution done.\n");
|
|
|
|
|
2016-08-22 17:22:34 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
/// The implementation of the optional evmc_instance::set_tracer() method.
|
2018-06-18 12:18:39 +00:00
|
|
|
static void set_tracer(struct evmc_instance* instance,
|
|
|
|
evmc_trace_callback callback,
|
|
|
|
struct evmc_tracer_context* context)
|
|
|
|
{
|
2018-08-30 16:27:44 +00:00
|
|
|
struct example_vm* vm = (struct example_vm*)instance;
|
2018-06-18 12:18:39 +00:00
|
|
|
vm->trace_callback = callback;
|
|
|
|
vm->tracer_context = context;
|
|
|
|
}
|
|
|
|
|
2018-10-18 13:38:10 +00:00
|
|
|
|
|
|
|
/// @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
|
|
|
|
|
2018-08-30 16:27:44 +00:00
|
|
|
struct evmc_instance* evmc_create_example_vm()
|
2016-12-20 15:50:23 +00:00
|
|
|
{
|
2018-03-28 13:09:07 +00:00
|
|
|
struct evmc_instance init = {
|
|
|
|
.abi_version = EVMC_ABI_VERSION,
|
2018-08-30 16:27:44 +00:00
|
|
|
.name = "example_vm",
|
2018-08-28 19:14:32 +00:00
|
|
|
.version = STR(PROJECT_VERSION),
|
2018-06-21 09:36:56 +00:00
|
|
|
.destroy = destroy,
|
2017-09-22 16:50:20 +00:00
|
|
|
.execute = execute,
|
2018-10-18 13:42:01 +00:00
|
|
|
.get_capabilities = get_capabilities,
|
2018-06-21 09:36:56 +00:00
|
|
|
.set_option = set_option,
|
2018-06-18 12:18:39 +00:00
|
|
|
.set_tracer = set_tracer,
|
2017-09-22 16:50:20 +00:00
|
|
|
};
|
2018-08-30 16:27:44 +00:00
|
|
|
struct example_vm* vm = calloc(1, sizeof(struct example_vm));
|
2018-03-28 13:09:07 +00:00
|
|
|
struct evmc_instance* interface = &vm->instance;
|
2017-09-22 16:50:20 +00:00
|
|
|
memcpy(interface, &init, sizeof(init));
|
2016-12-20 15:50:23 +00:00
|
|
|
return interface;
|
|
|
|
}
|