EVMC
example_vm.cpp
Go to the documentation of this file.
1 // EVMC: Ethereum Client-VM Connector API.
2 // Copyright 2016-2020 The EVMC Authors.
3 // Licensed under the Apache License, Version 2.0.
4 
15 
16 #include "example_vm.h"
17 #include <evmc/evmc.h>
18 #include <evmc/helpers.h>
19 #include <evmc/instructions.h>
20 #include <algorithm>
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cstring>
24 
28 namespace
29 {
31 struct ExampleVM : evmc_vm
32 {
33  int verbose = 0;
34  ExampleVM();
35 };
36 
38 void destroy(evmc_vm* instance)
39 {
40  delete static_cast<ExampleVM*>(instance);
41 }
42 
44 evmc_capabilities_flagset get_capabilities(evmc_vm* /*instance*/)
45 {
46  return EVMC_CAPABILITY_EVM1;
47 }
48 
53 enum evmc_set_option_result set_option(evmc_vm* instance, const char* name, const char* value)
54 {
55  ExampleVM* vm = static_cast<ExampleVM*>(instance);
56  if (std::strcmp(name, "verbose") == 0)
57  {
58  if (value == nullptr)
59  return EVMC_SET_OPTION_INVALID_VALUE;
60 
61  char* end = nullptr;
62  long int v = std::strtol(value, &end, 0);
63  if (end == value) // Parsing the value failed.
64  return EVMC_SET_OPTION_INVALID_VALUE;
65  if (v > 9 || v < -1) // Not in the valid range.
66  return EVMC_SET_OPTION_INVALID_VALUE;
67  vm->verbose = (int)v;
68  return EVMC_SET_OPTION_SUCCESS;
69  }
70 
71  return EVMC_SET_OPTION_INVALID_NAME;
72 }
73 
75 struct Stack
76 {
77  evmc_uint256be items[1024];
78  evmc_uint256be* pointer = items;
79 
81  evmc_uint256be pop() { return *--pointer; }
82 
84  void push(evmc_uint256be value) { *pointer++ = value; }
85 };
86 
88 struct Memory
89 {
90  uint32_t size = 0;
91  uint8_t data[1024] = {};
92 
98  uint8_t* expand(uint32_t offset, uint32_t region_size)
99  {
100  uint32_t new_size = offset + region_size;
101  if (new_size > sizeof(data))
102  return nullptr; // Cannot expand more than fixed max memory size.
103 
104  if (new_size > size)
105  size = new_size; // Update current memory size.
106 
107  return &data[offset];
108  }
109 
113  bool store(uint32_t offset, const uint8_t* value_data, uint32_t value_size)
114  {
115  uint8_t* p = expand(offset, value_size);
116  if (p == nullptr)
117  return false;
118 
119  std::memcpy(p, value_data, value_size);
120  return true;
121  }
122 };
123 
125 inline evmc_uint256be to_uint256(uint32_t x)
126 {
127  evmc_uint256be value = {};
128  value.bytes[31] = static_cast<uint8_t>(x);
129  value.bytes[30] = static_cast<uint8_t>(x >> 8);
130  value.bytes[29] = static_cast<uint8_t>(x >> 16);
131  value.bytes[28] = static_cast<uint8_t>(x >> 24);
132  return value;
133 }
134 
136 inline evmc_uint256be to_uint256(evmc_address address)
137 {
138  evmc_uint256be value = {};
139  size_t offset = sizeof(value) - sizeof(address);
140  std::memcpy(&value.bytes[offset], address.bytes, sizeof(address.bytes));
141  return value;
142 }
143 
145 inline uint32_t to_uint32(evmc_uint256be value)
146 {
147  return (uint32_t{value.bytes[28]} << 24) | (uint32_t{value.bytes[29]} << 16) |
148  (uint32_t{value.bytes[30]} << 8) | (uint32_t{value.bytes[31]});
149 }
150 
152 inline evmc_address to_address(evmc_uint256be value)
153 {
154  evmc_address address = {};
155  size_t offset = sizeof(value) - sizeof(address);
156  std::memcpy(address.bytes, &value.bytes[offset], sizeof(address.bytes));
157  return address;
158 }
159 
160 
162 evmc_result execute(evmc_vm* instance,
163  const evmc_host_interface* host,
164  evmc_host_context* context,
165  enum evmc_revision rev,
166  const evmc_message* msg,
167  const uint8_t* code,
168  size_t code_size)
169 {
170  ExampleVM* vm = static_cast<ExampleVM*>(instance);
171 
172  if (vm->verbose > 0)
173  std::puts("execution started\n");
174 
175  int64_t gas_left = msg->gas;
176  Stack stack;
177  Memory memory;
178 
179  for (size_t pc = 0; pc < code_size; ++pc)
180  {
181  // Check remaining gas, assume each instruction costs 1.
182  gas_left -= 1;
183  if (gas_left < 0)
184  return evmc_make_result(EVMC_OUT_OF_GAS, 0, nullptr, 0);
185 
186  switch (code[pc])
187  {
188  default:
189  return evmc_make_result(EVMC_UNDEFINED_INSTRUCTION, 0, nullptr, 0);
190 
191  case OP_STOP:
192  return evmc_make_result(EVMC_SUCCESS, gas_left, nullptr, 0);
193 
194  case OP_ADD:
195  {
196  uint32_t a = to_uint32(stack.pop());
197  uint32_t b = to_uint32(stack.pop());
198  uint32_t sum = a + b;
199  stack.push(to_uint256(sum));
200  break;
201  }
202 
203  case OP_ADDRESS:
204  {
205  evmc_uint256be value = to_uint256(msg->recipient);
206  stack.push(value);
207  break;
208  }
209 
210  case OP_CALLDATALOAD:
211  {
212  uint32_t offset = to_uint32(stack.pop());
213  evmc_uint256be value = {};
214 
215  if (offset < msg->input_size)
216  {
217  size_t copy_size = std::min(msg->input_size - offset, sizeof(value));
218  std::memcpy(value.bytes, &msg->input_data[offset], copy_size);
219  }
220 
221  stack.push(value);
222  break;
223  }
224 
225  case OP_NUMBER:
226  {
227  evmc_uint256be value =
228  to_uint256(static_cast<uint32_t>(host->get_tx_context(context).block_number));
229  stack.push(value);
230  break;
231  }
232 
233  case OP_MSTORE:
234  {
235  uint32_t index = to_uint32(stack.pop());
236  evmc_uint256be value = stack.pop();
237  if (!memory.store(index, value.bytes, sizeof(value)))
238  return evmc_make_result(EVMC_FAILURE, 0, nullptr, 0);
239  break;
240  }
241 
242  case OP_SLOAD:
243  {
244  evmc_uint256be index = stack.pop();
245  evmc_uint256be value = host->get_storage(context, &msg->recipient, &index);
246  stack.push(value);
247  break;
248  }
249 
250  case OP_SSTORE:
251  {
252  evmc_uint256be index = stack.pop();
253  evmc_uint256be value = stack.pop();
254  host->set_storage(context, &msg->recipient, &index, &value);
255  break;
256  }
257 
258  case OP_MSIZE:
259  {
260  evmc_uint256be value = to_uint256(memory.size);
261  stack.push(value);
262  break;
263  }
264 
265  case OP_PUSH1:
266  case OP_PUSH2:
267  case OP_PUSH3:
268  case OP_PUSH4:
269  case OP_PUSH5:
270  case OP_PUSH6:
271  case OP_PUSH7:
272  case OP_PUSH8:
273  case OP_PUSH9:
274  case OP_PUSH10:
275  case OP_PUSH11:
276  case OP_PUSH12:
277  case OP_PUSH13:
278  case OP_PUSH14:
279  case OP_PUSH15:
280  case OP_PUSH16:
281  case OP_PUSH17:
282  case OP_PUSH18:
283  case OP_PUSH19:
284  case OP_PUSH20:
285  case OP_PUSH21:
286  case OP_PUSH22:
287  case OP_PUSH23:
288  case OP_PUSH24:
289  case OP_PUSH25:
290  case OP_PUSH26:
291  case OP_PUSH27:
292  case OP_PUSH28:
293  case OP_PUSH29:
294  case OP_PUSH30:
295  case OP_PUSH31:
296  case OP_PUSH32:
297  {
298  evmc_uint256be value = {};
299  size_t num_push_bytes = size_t{code[pc]} - OP_PUSH1 + 1;
300  size_t offset = sizeof(value) - num_push_bytes;
301  std::memcpy(&value.bytes[offset], &code[pc + 1], num_push_bytes);
302  pc += num_push_bytes;
303  stack.push(value);
304  break;
305  }
306 
307  case OP_DUP1:
308  {
309  evmc_uint256be value = stack.pop();
310  stack.push(value);
311  stack.push(value);
312  break;
313  }
314 
315  case OP_CALL:
316  {
317  evmc_message call_msg = {};
318  call_msg.gas = to_uint32(stack.pop());
319  call_msg.recipient = to_address(stack.pop());
320  call_msg.value = stack.pop();
321 
322  uint32_t call_input_offset = to_uint32(stack.pop());
323  uint32_t call_input_size = to_uint32(stack.pop());
324  call_msg.input_data = memory.expand(call_input_offset, call_input_size);
325  call_msg.input_size = call_input_size;
326 
327  uint32_t call_output_offset = to_uint32(stack.pop());
328  uint32_t call_output_size = to_uint32(stack.pop());
329  uint8_t* call_output_ptr = memory.expand(call_output_offset, call_output_size);
330 
331  if (call_msg.input_data == nullptr || call_output_ptr == nullptr)
332  return evmc_make_result(EVMC_FAILURE, 0, nullptr, 0);
333 
334  evmc_result call_result = host->call(context, &call_msg);
335 
336  evmc_uint256be value = to_uint256(call_result.status_code == EVMC_SUCCESS);
337  stack.push(value);
338 
339  if (call_output_size > call_result.output_size)
340  call_output_size = static_cast<uint32_t>(call_result.output_size);
341  memory.store(call_output_offset, call_result.output_data, call_output_size);
342 
343  if (call_result.release != nullptr)
344  call_result.release(&call_result);
345  break;
346  }
347 
348  case OP_RETURN:
349  {
350  uint32_t output_offset = to_uint32(stack.pop());
351  uint32_t output_size = to_uint32(stack.pop());
352  uint8_t* output_ptr = memory.expand(output_offset, output_size);
353  if (output_ptr == nullptr)
354  return evmc_make_result(EVMC_FAILURE, 0, nullptr, 0);
355 
356  return evmc_make_result(EVMC_SUCCESS, gas_left, output_ptr, output_size);
357  }
358 
359  case OP_REVERT:
360  {
361  if (rev < EVMC_BYZANTIUM)
362  return evmc_make_result(EVMC_UNDEFINED_INSTRUCTION, 0, nullptr, 0);
363 
364  uint32_t output_offset = to_uint32(stack.pop());
365  uint32_t output_size = to_uint32(stack.pop());
366  uint8_t* output_ptr = memory.expand(output_offset, output_size);
367  if (output_ptr == nullptr)
368  return evmc_make_result(EVMC_FAILURE, 0, nullptr, 0);
369 
370  return evmc_make_result(EVMC_REVERT, gas_left, output_ptr, output_size);
371  }
372  }
373  }
374 
375  return evmc_make_result(EVMC_SUCCESS, gas_left, nullptr, 0);
376 }
377 
378 
380 #if !defined(PROJECT_VERSION)
382 #define PROJECT_VERSION "0.0.0"
383 #endif
385 
386 ExampleVM::ExampleVM()
387  : evmc_vm{EVMC_ABI_VERSION, "example_vm", PROJECT_VERSION, ::destroy,
388  ::execute, ::get_capabilities, ::set_option}
389 {}
390 } // namespace
391 
393 {
394  return new ExampleVM;
395 }
EVMC: Ethereum Client-VM Connector API.
evmc_set_option_result
Possible outcomes of evmc_set_option.
Definition: evmc.h:778
uint32_t evmc_capabilities_flagset
Alias for unsigned integer representing a set of bit flags of EVMC capabilities.
Definition: evmc.h:960
evmc_vm * evmc_create_example_vm()
Example of a function creating an instance of an example EVM implementation.
Definition: example_vm.cpp:392
evmc_revision
EVM revision.
Definition: evmc.h:809
@ EVMC_ABI_VERSION
The EVMC ABI version number of the interface declared in this file.
Definition: evmc.h:47
@ EVMC_CAPABILITY_EVM1
The VM is capable of executing EVM1 bytecode.
Definition: evmc.h:935
@ EVMC_UNDEFINED_INSTRUCTION
An undefined instruction has been encountered.
Definition: evmc.h:289
@ EVMC_SUCCESS
Execution finished with success.
Definition: evmc.h:262
@ EVMC_OUT_OF_GAS
The execution has run out of gas.
Definition: evmc.h:276
@ EVMC_FAILURE
Generic execution failure.
Definition: evmc.h:265
@ EVMC_REVERT
Execution terminated with REVERT opcode.
Definition: evmc.h:273
@ EVMC_BYZANTIUM
The Byzantium revision.
Definition: evmc.h:843
static struct evmc_result evmc_make_result(enum evmc_status_code status_code, int64_t gas_left, const uint8_t *output_data, size_t output_size)
Creates the result from the provided arguments.
Definition: helpers.h:121
EVMC Helpers.
EVM Instruction Tables.
Big-endian 160-bit hash suitable for keeping an Ethereum address.
Definition: evmc.h:69
uint8_t bytes[20]
The 20 bytes of the hash.
Definition: evmc.h:71
The fixed size array of 32 bytes.
Definition: evmc.h:57
uint8_t bytes[32]
The 32 bytes.
Definition: evmc.h:59
The opaque data type representing the Host execution context.
The Host interface.
Definition: evmc.h:719
evmc_call_fn call
Call callback function.
Definition: evmc.h:745
evmc_get_storage_fn get_storage
Get storage callback function.
Definition: evmc.h:724
evmc_set_storage_fn set_storage
Set storage callback function.
Definition: evmc.h:727
evmc_get_tx_context_fn get_tx_context
Get transaction context callback function.
Definition: evmc.h:748
The message describing an EVM call, including a zero-depth calls from a transaction origin.
Definition: evmc.h:97
const uint8_t * input_data
The message input data.
Definition: evmc.h:151
size_t input_size
The size of the message input data.
Definition: evmc.h:158
evmc_address recipient
The recipient of the message.
Definition: evmc.h:132
evmc_uint256be value
The amount of Ether transferred with the message.
Definition: evmc.h:166
int64_t gas
The amount of gas available to the message execution.
Definition: evmc.h:119
The EVM code execution result.
Definition: evmc.h:392
const uint8_t * output_data
The reference to output data.
Definition: evmc.h:415
enum evmc_status_code status_code
The execution status code.
Definition: evmc.h:394
evmc_release_result_fn release
The method releasing all resources associated with the result object.
Definition: evmc.h:443
size_t output_size
The size of the output data.
Definition: evmc.h:422
The VM instance.
Definition: evmc.h:980