EVMC
example_vm.cpp
Go to the documentation of this file.
1// EVMC: Ethereum Client-VM Connector API.
2// Copyright 2016 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
28namespace
29{
31struct ExampleVM : evmc_vm
32{
33 int verbose = 0;
34 ExampleVM();
35};
36
38void destroy(evmc_vm* instance)
39{
40 delete static_cast<ExampleVM*>(instance);
41}
42
44evmc_capabilities_flagset get_capabilities(evmc_vm* /*instance*/)
45{
47}
48
53enum evmc_set_option_result set_option(evmc_vm* instance, const char* name, const char* value)
54{
55 auto* 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 auto 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 = static_cast<int>(v);
68 return EVMC_SET_OPTION_SUCCESS;
69 }
70
71 return EVMC_SET_OPTION_INVALID_NAME;
72}
73
75struct 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
88struct 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
125inline 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
136inline 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
145inline 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
152inline 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
162evmc_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 auto* 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, 0, nullptr, 0);
185
186 switch (code[pc])
187 {
188 default:
189 return evmc_make_result(EVMC_UNDEFINED_INSTRUCTION, 0, 0, nullptr, 0);
190
191 case OP_STOP:
192 return evmc_make_result(EVMC_SUCCESS, gas_left, 0, 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, 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, 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 ? 1 : 0);
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, 0, nullptr, 0);
355
356 return evmc_make_result(EVMC_SUCCESS, gas_left, 0, 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, 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, 0, nullptr, 0);
369
370 return evmc_make_result(EVMC_REVERT, gas_left, 0, output_ptr, output_size);
371 }
372 }
373 }
374
375 return evmc_make_result(EVMC_SUCCESS, gas_left, 0, nullptr, 0);
376}
377
378
380#if !defined(PROJECT_VERSION)
382#define PROJECT_VERSION "0.0.0"
383#endif
385
386ExampleVM::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_set_option_result
Possible outcomes of evmc_set_option.
Definition: evmc.h:910
evmc_vm * evmc_create_example_vm()
Example of a function creating an instance of an example EVM implementation.
Definition: example_vm.cpp:392
uint32_t evmc_capabilities_flagset
Alias for unsigned integer representing a set of bit flags of EVMC capabilities.
Definition: evmc.h:1120
evmc_revision
EVM revision.
Definition: evmc.h:941
@ 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:1095
@ EVMC_UNDEFINED_INSTRUCTION
An undefined instruction has been encountered.
Definition: evmc.h:313
@ EVMC_SUCCESS
Execution finished with success.
Definition: evmc.h:286
@ EVMC_OUT_OF_GAS
The execution has run out of gas.
Definition: evmc.h:300
@ EVMC_FAILURE
Generic execution failure.
Definition: evmc.h:289
@ EVMC_REVERT
Execution terminated with REVERT opcode.
Definition: evmc.h:297
@ EVMC_BYZANTIUM
The Byzantium revision.
Definition: evmc.h:975
static struct evmc_result evmc_make_result(enum evmc_status_code status_code, int64_t gas_left, int64_t gas_refund, const uint8_t *output_data, size_t output_size)
Creates the result from the provided arguments.
Definition: helpers.h:127
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:845
evmc_call_fn call
Call callback function.
Definition: evmc.h:871
evmc_get_storage_fn get_storage
Get storage callback function.
Definition: evmc.h:850
evmc_set_storage_fn set_storage
Set storage callback function.
Definition: evmc.h:853
evmc_get_tx_context_fn get_tx_context
Get transaction context callback function.
Definition: evmc.h:874
The message describing an EVM call, including a zero-depth calls from a transaction origin.
Definition: evmc.h:98
const uint8_t * input_data
The message input data.
Definition: evmc.h:152
size_t input_size
The size of the message input data.
Definition: evmc.h:159
evmc_address recipient
The recipient of the message.
Definition: evmc.h:133
evmc_uint256be value
The amount of Ether transferred with the message.
Definition: evmc.h:167
int64_t gas
The amount of gas available to the message execution.
Definition: evmc.h:120
The EVM code execution result.
Definition: evmc.h:416
const uint8_t * output_data
The reference to output data.
Definition: evmc.h:448
enum evmc_status_code status_code
The execution status code.
Definition: evmc.h:418
evmc_release_result_fn release
The method releasing all resources associated with the result object.
Definition: evmc.h:476
size_t output_size
The size of the output data.
Definition: evmc.h:455
The VM instance.
Definition: evmc.h:1140