EVMC
mocked_host.hpp
1 // EVMC: Ethereum Client-VM Connector API.
2 // Copyright 2019 The EVMC Authors.
3 // Licensed under the Apache License, Version 2.0.
4 #pragma once
5 
6 #include <evmc/evmc.hpp>
7 #include <algorithm>
8 #include <string>
9 #include <unordered_map>
10 #include <vector>
11 
12 namespace evmc
13 {
15 using bytes = std::basic_string<uint8_t>;
16 
19 {
22 
24  bool dirty{false};
25 
27  storage_value() noexcept = default;
28 
30  storage_value(const bytes32& _value, bool _dirty = false) noexcept // NOLINT
31  : value{_value}, dirty{_dirty}
32  {}
33 };
34 
37 {
39  int nonce = 0;
40 
43 
46 
49 
51  std::unordered_map<bytes32, storage_value> storage;
52 
54  void set_balance(uint64_t x) noexcept
55  {
56  balance = uint256be{};
57  for (std::size_t i = 0; i < sizeof(x); ++i)
58  balance.bytes[sizeof(balance) - 1 - i] = static_cast<uint8_t>(x >> (8 * i));
59  }
60 };
61 
63 class MockedHost : public Host
64 {
65 public:
67  struct log_record
68  {
71 
74 
76  std::vector<bytes32> topics;
77 
79  bool operator==(const log_record& other) const noexcept
80  {
81  return creator == other.creator && data == other.data && topics == other.topics;
82  }
83  };
84 
87  {
90 
93 
95  bool operator==(const selfdestuct_record& other) const noexcept
96  {
97  return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary;
98  }
99  };
100 
102  std::unordered_map<address, MockedAccount> accounts;
103 
105  evmc_tx_context tx_context = {};
106 
108  bytes32 block_hash = {};
109 
111  evmc_result call_result = {};
112 
114  mutable std::vector<int64_t> recorded_blockhashes;
115 
117  mutable std::vector<address> recorded_account_accesses;
118 
121  static constexpr auto max_recorded_account_accesses = 200;
122 
124  std::vector<evmc_message> recorded_calls;
125 
128  static constexpr auto max_recorded_calls = 100;
129 
131  std::vector<log_record> recorded_logs;
132 
134  std::vector<selfdestuct_record> recorded_selfdestructs;
135 
136 private:
138  std::vector<bytes> m_recorded_calls_inputs;
139 
140 public:
143  void record_account_access(const address& addr) const
144  {
145  if (recorded_account_accesses.empty())
146  recorded_account_accesses.reserve(max_recorded_account_accesses);
147 
148  if (recorded_account_accesses.size() < max_recorded_account_accesses)
149  recorded_account_accesses.emplace_back(addr);
150  }
151 
153  bool account_exists(const address& addr) const noexcept override
154  {
155  record_account_access(addr);
156  return accounts.count(addr) != 0;
157  }
158 
160  bytes32 get_storage(const address& addr, const bytes32& key) const noexcept override
161  {
162  record_account_access(addr);
163 
164  const auto account_iter = accounts.find(addr);
165  if (account_iter == accounts.end())
166  return {};
167 
168  const auto storage_iter = account_iter->second.storage.find(key);
169  if (storage_iter != account_iter->second.storage.end())
170  return storage_iter->second.value;
171  return {};
172  }
173 
176  const bytes32& key,
177  const bytes32& value) noexcept override
178  {
179  record_account_access(addr);
180  const auto it = accounts.find(addr);
181  if (it == accounts.end())
182  return EVMC_STORAGE_UNCHANGED;
183 
184  auto& old = it->second.storage[key];
185 
186  // Follow https://eips.ethereum.org/EIPS/eip-1283 specification.
187  // WARNING! This is not complete implementation as refund is not handled here.
188 
189  if (old.value == value)
190  return EVMC_STORAGE_UNCHANGED;
191 
192  evmc_storage_status status{};
193  if (!old.dirty)
194  {
195  old.dirty = true;
196  if (!old.value)
197  status = EVMC_STORAGE_ADDED;
198  else if (value)
199  status = EVMC_STORAGE_MODIFIED;
200  else
201  status = EVMC_STORAGE_DELETED;
202  }
203  else
205 
206  old.value = value;
207  return status;
208  }
209 
211  uint256be get_balance(const address& addr) const noexcept override
212  {
213  record_account_access(addr);
214  const auto it = accounts.find(addr);
215  if (it == accounts.end())
216  return {};
217 
218  return it->second.balance;
219  }
220 
222  size_t get_code_size(const address& addr) const noexcept override
223  {
224  record_account_access(addr);
225  const auto it = accounts.find(addr);
226  if (it == accounts.end())
227  return 0;
228  return it->second.code.size();
229  }
230 
232  bytes32 get_code_hash(const address& addr) const noexcept override
233  {
234  record_account_access(addr);
235  const auto it = accounts.find(addr);
236  if (it == accounts.end())
237  return {};
238  return it->second.codehash;
239  }
240 
242  size_t copy_code(const address& addr,
243  size_t code_offset,
244  uint8_t* buffer_data,
245  size_t buffer_size) const noexcept override
246  {
247  record_account_access(addr);
248  const auto it = accounts.find(addr);
249  if (it == accounts.end())
250  return 0;
251 
252  const auto& code = it->second.code;
253 
254  if (code_offset >= code.size())
255  return 0;
256 
257  const auto n = std::min(buffer_size, code.size() - code_offset);
258 
259  if (n > 0)
260  std::copy_n(&code[code_offset], n, buffer_data);
261  return n;
262  }
263 
265  void selfdestruct(const address& addr, const address& beneficiary) noexcept override
266  {
267  record_account_access(addr);
268  recorded_selfdestructs.push_back({addr, beneficiary});
269  }
270 
272  result call(const evmc_message& msg) noexcept override
273  {
274  record_account_access(msg.destination);
275 
276  if (recorded_calls.empty())
277  {
278  recorded_calls.reserve(max_recorded_calls);
279  m_recorded_calls_inputs.reserve(max_recorded_calls); // Iterators will not invalidate.
280  }
281 
282  if (recorded_calls.size() < max_recorded_calls)
283  {
284  recorded_calls.emplace_back(msg);
285  auto& call_msg = recorded_calls.back();
286  if (call_msg.input_size > 0)
287  {
288  m_recorded_calls_inputs.emplace_back(call_msg.input_data, call_msg.input_size);
289  const auto& input_copy = m_recorded_calls_inputs.back();
290  call_msg.input_data = input_copy.data();
291  }
292  }
293  return result{call_result};
294  }
295 
297  evmc_tx_context get_tx_context() const noexcept override { return tx_context; }
298 
300  bytes32 get_block_hash(int64_t block_number) const noexcept override
301  {
302  recorded_blockhashes.emplace_back(block_number);
303  return block_hash;
304  }
305 
307  void emit_log(const address& addr,
308  const uint8_t* data,
309  size_t data_size,
310  const bytes32 topics[],
311  size_t topics_count) noexcept override
312  {
313  recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
314  }
315 };
316 } // namespace evmc
bool dirty
True means this value has been modified already by the current transaction.
Definition: mocked_host.hpp:24
bytes32 codehash
The code hash. Can be a value not related to the actual code.
Definition: mocked_host.hpp:45
std::basic_string< uint8_t > bytes
The string of bytes.
Definition: mocked_host.hpp:15
A storage item has been deleted: X -> 0.
Definition: evmc.h:484
bytes32 get_block_hash(int64_t block_number) const noexcept override
Get the block header hash (EVMC host method).
address beneficiary
The address of the beneficiary account.
Definition: mocked_host.hpp:92
std::unordered_map< address, MockedAccount > accounts
The set of all accounts in the Host, organized by their addresses.
Mocked EVMC Host implementation.
Definition: mocked_host.hpp:63
std::vector< log_record > recorded_logs
The record of all LOGs passed to the emit_log() method.
evmc_storage_status
The effect of an attempt to modify a contract storage item.
Definition: evmc.h:459
std::vector< bytes32 > topics
The log topics.
Definition: mocked_host.hpp:76
The EVM code execution result.
Definition: evmc.hpp:331
uint256be balance
The account balance.
Definition: mocked_host.hpp:48
The fixed size array of 32 bytes for storing 256-bit EVM values.
Definition: evmc.hpp:62
bool account_exists(const address &addr) const noexcept override
Returns true if an account exists (EVMC Host method).
bool operator==(const log_record &other) const noexcept
Equal operator.
Definition: mocked_host.hpp:79
bytes data
The data attached to the log.
Definition: mocked_host.hpp:73
void emit_log(const address &addr, const uint8_t *data, size_t data_size, const bytes32 topics[], size_t topics_count) noexcept override
Emit LOG (EVMC host method).
storage_value(const bytes32 &_value, bool _dirty=false) noexcept
Constructor.
Definition: mocked_host.hpp:30
evmc_tx_context get_tx_context() const noexcept override
Get transaction context (EVMC host method).
std::vector< selfdestuct_record > recorded_selfdestructs
The record of all SELFDESTRUCTs from the selfdestruct() method.
bytes32 get_storage(const address &addr, const bytes32 &key) const noexcept override
Get the account&#39;s storage value at the given key (EVMC Host method).
bytes32 get_code_hash(const address &addr) const noexcept override
Get the account&#39;s code hash (EVMC host method).
The value of a storage item has been modified: X -> Y.
Definition: evmc.h:469
std::vector< evmc_message > recorded_calls
The record of all call messages requested in the call() method.
uint8_t bytes[32]
The 32 bytes.
Definition: evmc.h:59
void record_account_access(const address &addr) const
Record an account access.
bytes code
The account code.
Definition: mocked_host.hpp:42
uint256be get_balance(const address &addr) const noexcept override
Get the account&#39;s balance (EVMC Host method).
std::vector< int64_t > recorded_blockhashes
The record of all block numbers for which get_block_hash() was called.
result call(const evmc_message &msg) noexcept override
Call/create other contract (EVMC host method).
The message describing an EVM call, including a zero-depth calls from a transaction origin...
Definition: evmc.h:95
address selfdestructed
The address of the account which has self-destructed.
Definition: mocked_host.hpp:89
bytes32 value
The storage value.
Definition: mocked_host.hpp:21
void selfdestruct(const address &addr, const address &beneficiary) noexcept override
Selfdestruct the account (EVMC host method).
storage_value() noexcept=default
Default constructor.
address creator
The address of the account which created the log.
Definition: mocked_host.hpp:70
std::unordered_map< bytes32, storage_value > storage
The account storage map.
Definition: mocked_host.hpp:51
A storage item has been modified after being modified before: X -> Y -> Z.
Definition: evmc.h:474
bool operator==(const selfdestuct_record &other) const noexcept
Equal operator.
Definition: mocked_host.hpp:95
The EVM code execution result.
Definition: evmc.h:340
The big-endian 160-bit hash suitable for keeping an Ethereum address.
Definition: evmc.hpp:21
size_t copy_code(const address &addr, size_t code_offset, uint8_t *buffer_data, size_t buffer_size) const noexcept override
Copy the account&#39;s code to the given buffer (EVMC host method).
evmc_storage_status set_storage(const address &addr, const bytes32 &key, const bytes32 &value) noexcept override
Set the account&#39;s storage value (EVMC Host method).
The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
Definition: evmc.h:464
Abstract class to be used by Host implementations.
Definition: evmc.hpp:560
EVMC C++ API - wrappers and bindings for C++.
Definition: evmc.hpp:16
void set_balance(uint64_t x) noexcept
Helper method for setting balance by numeric type.
Definition: mocked_host.hpp:54
size_t get_code_size(const address &addr) const noexcept override
Get the account&#39;s code size (EVMC host method).
A new storage item has been added: 0 -> X.
Definition: evmc.h:479
Extended value (by dirty flag) for account storage.
Definition: mocked_host.hpp:18
EVMC C++ API - wrappers and bindings for C++.
The transaction and block data for execution.
Definition: evmc.h:147
std::vector< address > recorded_account_accesses
The record of all account accesses.
Mocked account.
Definition: mocked_host.hpp:36