EVMC
example_vm.c
Go to the documentation of this file.
1 /* EVMC: Ethereum Client-VM Connector API.
2  * Copyright 2016-2019 The EVMC Authors.
3  * Licensed under the Apache License, Version 2.0.
4  */
5 
13 
14 #include "example_vm.h"
15 
16 #include <limits.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 
23 struct example_vm
24 {
25  struct evmc_vm instance;
26  int verbose;
27 };
28 
30 static void destroy(struct evmc_vm* vm)
31 {
32  free(vm);
33 }
34 
37 {
38  (void)vm;
40 }
41 
47  const char* name,
48  const char* value)
49 {
50  struct example_vm* vm = (struct example_vm*)instance;
51  if (strcmp(name, "verbose") == 0)
52  {
53  if (!value)
54  return EVMC_SET_OPTION_INVALID_VALUE;
55 
56  char* end = NULL;
57  long int v = strtol(value, &end, 0);
58  if (end == value) // Parsing the value failed.
59  return EVMC_SET_OPTION_INVALID_VALUE;
60  if (v > 9 || v < -1) // Not in the valid range.
61  return EVMC_SET_OPTION_INVALID_VALUE;
62  vm->verbose = (int)v;
63  return EVMC_SET_OPTION_SUCCESS;
64  }
65 
66  return EVMC_SET_OPTION_INVALID_NAME;
67 }
68 
71 static void free_result_output_data(const struct evmc_result* result)
72 {
73  free((uint8_t*)result->output_data);
74 }
75 
77 static struct evmc_result execute(struct evmc_vm* instance,
78  const struct evmc_host_interface* host,
79  struct evmc_host_context* context,
80  enum evmc_revision rev,
81  const struct evmc_message* msg,
82  const uint8_t* code,
83  size_t code_size)
84 {
86  if (code_size == 0)
87  {
88  // In case of empty code return a fancy error message.
89  const char* error = rev == EVMC_BYZANTIUM ? "Welcome to Byzantium!" : "Hello Ethereum!";
90  ret.output_data = (const uint8_t*)error;
91  ret.output_size = strlen(error);
93  ret.gas_left = msg->gas / 10;
94  ret.release = NULL; // We don't need to release the constant messages.
95  return ret;
96  }
97 
98  struct example_vm* vm = (struct example_vm*)instance;
99 
100  // Simulate executing by checking for some code patterns.
101  // Solidity inline assembly is used in the examples instead of EVM bytecode.
102 
103  // Assembly: `{ mstore(0, address()) return(0, msize()) }`.
104  const char return_address[] = "\x30\x60\x00\x52\x59\x60\x00\xf3";
105 
106  // Assembly: `{ sstore(0, add(sload(0), 1)) }`
107  const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55";
108 
109  // Assembly: `{ mstore(0, number()) return(0, msize()) }`
110  const char return_block_number[] = "\x43\x60\x00\x52\x59\x60\x00\xf3";
111 
112  // Assembly: `{ sstore(0, number()) mstore(0, number()) return(0, msize()) }`
113  const char save_return_block_number[] = "\x43\x60\x00\x55\x43\x60\x00\x52\x59\x60\x00\xf3";
114 
115  // Assembly: PUSH(0) 6x DUP1 CALL
116  const char make_a_call[] = "\x60\x00\x80\x80\x80\x80\x80\x80\xf1";
117 
118  if (msg->kind == EVMC_CREATE)
119  {
121  ret.gas_left = msg->gas / 10;
122  return ret;
123  }
124  else if (code_size == (sizeof(return_address) - 1) &&
125  strncmp((const char*)code, return_address, code_size) == 0)
126  {
127  static const size_t address_size = sizeof(msg->destination);
128  uint8_t* output_data = (uint8_t*)malloc(address_size);
129  if (!output_data)
130  {
131  // malloc failed, report internal error.
133  return ret;
134  }
135  memcpy(output_data, &msg->destination, address_size);
137  ret.output_data = output_data;
138  ret.output_size = address_size;
140  return ret;
141  }
142  else if (code_size == (sizeof(counter) - 1) &&
143  strncmp((const char*)code, counter, code_size) == 0)
144  {
145  const evmc_bytes32 key = {{0}};
146  evmc_bytes32 value = host->get_storage(context, &msg->destination, &key);
147  value.bytes[31]++;
148  host->set_storage(context, &msg->destination, &key, &value);
150  return ret;
151  }
152  else if (code_size == (sizeof(return_block_number) - 1) &&
153  strncmp((const char*)code, return_block_number, code_size) == 0)
154  {
155  const struct evmc_tx_context tx_context = host->get_tx_context(context);
156  const size_t output_size = 20;
157 
158  uint8_t* output_data = (uint8_t*)calloc(1, output_size);
159  snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number);
161  ret.gas_left = msg->gas / 2;
162  ret.output_data = output_data;
163  ret.output_size = output_size;
165  return ret;
166  }
167  else if (code_size == (sizeof(save_return_block_number) - 1) &&
168  strncmp((const char*)code, save_return_block_number, code_size) == 0)
169  {
170  const struct evmc_tx_context tx_context = host->get_tx_context(context);
171  const size_t output_size = 20;
172 
173  // Store block number.
174  const evmc_bytes32 key = {{0}};
175  evmc_bytes32 value = {{0}};
176  // NOTE: assume block number is <= 255
177  value.bytes[31] = (uint8_t)tx_context.block_number;
178  host->set_storage(context, &msg->destination, &key, &value);
179 
180  // Return block number.
181  uint8_t* output_data = (uint8_t*)calloc(1, output_size);
182  snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number);
184  ret.gas_left = msg->gas / 2;
185  ret.output_data = output_data;
186  ret.output_size = output_size;
188  return ret;
189  }
190  else if (code_size == (sizeof(make_a_call) - 1) &&
191  strncmp((const char*)code, make_a_call, code_size) == 0)
192  {
193  struct evmc_message call_msg;
194  memset(&call_msg, 0, sizeof(call_msg));
195  call_msg.kind = EVMC_CALL;
196  call_msg.depth = msg->depth + 1;
197  call_msg.gas = msg->gas - (msg->gas / 64);
198  call_msg.sender = msg->destination;
199  return host->call(context, &call_msg);
200  }
201 
203  ret.gas_left = 0;
204 
205  if (vm->verbose)
206  printf("Execution done.\n");
207 
208  return ret;
209 }
210 
211 
213 #if !defined(PROJECT_VERSION)
214 #define PROJECT_VERSION "0.0.0"
216 #endif
217 
220 {
221  struct evmc_vm init = {
223  .name = "example_vm",
224  .version = PROJECT_VERSION,
225  .destroy = destroy,
226  .execute = execute,
227  .get_capabilities = get_capabilities,
228  .set_option = set_option,
229  };
230  struct example_vm* vm = calloc(1, sizeof(struct example_vm));
231  struct evmc_vm* interface = &vm->instance;
232  memcpy(interface, &init, sizeof(init));
233  return interface;
234 }
static enum evmc_set_option_result set_option(struct evmc_vm *instance, const char *name, const char *value)
Example VM options.
Definition: example_vm.c:46
evmc_set_storage_fn set_storage
Set storage callback function.
Definition: evmc.h:630
struct evmc_vm * evmc_create_example_vm()
Example of a function creating an instance of an example EVM implementation.
Definition: example_vm.c:219
static evmc_capabilities_flagset get_capabilities(struct evmc_vm *vm)
The example implementation of the evmc_vm::get_capabilities() method.
Definition: example_vm.c:36
evmc_address destination
The destination of the message.
Definition: evmc.h:113
const char * name
The name of the EVMC VM implementation.
Definition: evmc.h:871
static void destroy(struct evmc_vm *vm)
The implementation of the evmc_vm::destroy() method.
Definition: example_vm.c:30
struct evmc_vm instance
The base struct.
Definition: example_vm.c:25
enum evmc_status_code status_code
The execution status code.
Definition: evmc.h:343
The VM is capable of executing ewasm bytecode.
Definition: evmc.h:816
const int abi_version
EVMC ABI version implemented by the VM instance.
Definition: evmc.h:863
The example VM instance struct extending the evmc_vm.
Definition: example_vm.c:23
evmc_release_result_fn release
The method releasing all resources associated with the result object.
Definition: evmc.h:392
Request CREATE.
Definition: evmc.h:81
The VM is capable of executing EVM1 bytecode.
Definition: evmc.h:811
uint8_t bytes[32]
The 32 bytes.
Definition: evmc.h:59
size_t output_size
The size of the output data.
Definition: evmc.h:371
int64_t gas_left
The amount of gas left after the execution.
Definition: evmc.h:351
The EVMC ABI version number of the interface declared in this file.
Definition: evmc.h:47
EVM implementation generic internal error.
Definition: evmc.h:299
int verbose
The verbosity level.
Definition: example_vm.c:26
The Host interface.
Definition: evmc.h:621
evmc_get_storage_fn get_storage
Get storage callback function.
Definition: evmc.h:627
The VM instance.
Definition: evmc.h:855
uint32_t evmc_capabilities_flagset
Alias for unsigned integer representing a set of bit flags of EVMC capabilities.
Definition: evmc.h:836
The opaque data type representing the Host execution context.
int32_t depth
The call depth.
Definition: evmc.h:107
static struct evmc_result execute(struct evmc_vm *instance, const struct evmc_host_interface *host, struct evmc_host_context *context, enum evmc_revision rev, const struct evmc_message *msg, const uint8_t *code, size_t code_size)
The example implementation of the evmc_vm::execute() method.
Definition: example_vm.c:77
evmc_get_tx_context_fn get_tx_context
Get transaction context callback function.
Definition: evmc.h:651
enum evmc_call_kind kind
The kind of the call.
Definition: evmc.h:98
The message describing an EVM call, including a zero-depth calls from a transaction origin...
Definition: evmc.h:95
evmc_set_option_result
Possible outcomes of evmc_set_option.
Definition: evmc.h:674
static void free_result_output_data(const struct evmc_result *result)
The implementation of the evmc_result::release() method that frees the output buffer attached to the ...
Definition: example_vm.c:71
int64_t block_number
The block number.
Definition: evmc.h:152
evmc_call_fn call
Call callback function.
Definition: evmc.h:648
int64_t gas
The amount of gas for message execution.
Definition: evmc.h:110
The EVM code execution result.
Definition: evmc.h:340
The fixed size array of 32 bytes.
Definition: evmc.h:56
evmc_address sender
The sender of the message.
Definition: evmc.h:116
const uint8_t * output_data
The reference to output data.
Definition: evmc.h:364
evmc_revision
EVM revision.
Definition: evmc.h:705
Request CALL.
Definition: evmc.h:77
Generic execution failure.
Definition: evmc.h:217
The transaction and block data for execution.
Definition: evmc.h:147
Execution finished with success.
Definition: evmc.h:214
The Byzantium revision.
Definition: evmc.h:740