EVMC
evmc::MockedHost Class Reference

Mocked EVMC Host implementation. More...

#include <mocked_host.hpp>

Inheritance diagram for evmc::MockedHost:
evmc::Host evmc::HostInterface

Classes

struct  log_record
 LOG record. More...
 

Public Member Functions

bool account_exists (const address &addr) const noexcept override
 Returns true if an account exists (EVMC Host method).
 
bytes32 get_storage (const address &addr, const bytes32 &key) const noexcept override
 Get the account's storage value at the given key (EVMC Host method).
 
evmc_storage_status set_storage (const address &addr, const bytes32 &key, const bytes32 &value) noexcept override
 Set the account's storage value (EVMC Host method).
 
uint256be get_balance (const address &addr) const noexcept override
 Get the account's balance (EVMC Host method).
 
size_t get_code_size (const address &addr) const noexcept override
 Get the account's code size (EVMC host method).
 
bytes32 get_code_hash (const address &addr) const noexcept override
 Get the account's code hash (EVMC host method).
 
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's code to the given buffer (EVMC host method).
 
bool selfdestruct (const address &addr, const address &beneficiary) noexcept override
 Selfdestruct the account (EVMC host method).
 
Result call (const evmc_message &msg) noexcept override
 Call/create other contract (EVMC host method).
 
evmc_tx_context get_tx_context () const noexcept override
 Get transaction context (EVMC host method).
 
bytes32 get_block_hash (int64_t block_number) const noexcept override
 Get the block header hash (EVMC host method).
 
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).
 
evmc_access_status access_account (const address &addr) noexcept override
 Record an account access.
 
evmc_access_status access_storage (const address &addr, const bytes32 &key) noexcept override
 Access the account's storage value at the given key.
 
bytes32 get_transient_storage (const address &addr, const bytes32 &key) const noexcept override
 Get account's transient storage.
 
void set_transient_storage (const address &addr, const bytes32 &key, const bytes32 &value) noexcept override
 Set account's transient storage.
 
- Public Member Functions inherited from evmc::Host
evmc_host_contextto_context () noexcept
 Converts the Host object to the opaque host context pointer.
 
virtual bool account_exists (const address &addr) const noexcept=0
 Check account existence callback function.
 
virtual bytes32 get_storage (const address &addr, const bytes32 &key) const noexcept=0
 Get storage callback function.
 
virtual evmc_storage_status set_storage (const address &addr, const bytes32 &key, const bytes32 &value) noexcept=0
 Set storage callback function.
 
virtual uint256be get_balance (const address &addr) const noexcept=0
 Get balance callback function.
 
virtual size_t get_code_size (const address &addr) const noexcept=0
 Get code size callback function.
 
virtual bytes32 get_code_hash (const address &addr) const noexcept=0
 Get code hash callback function.
 
virtual size_t copy_code (const address &addr, size_t code_offset, uint8_t *buffer_data, size_t buffer_size) const noexcept=0
 Copy code callback function.
 
virtual bool selfdestruct (const address &addr, const address &beneficiary) noexcept=0
 Selfdestruct callback function.
 
virtual Result call (const evmc_message &msg) noexcept=0
 Call callback function.
 
virtual evmc_tx_context get_tx_context () const noexcept=0
 Get transaction context callback function.
 
virtual bytes32 get_block_hash (int64_t block_number) const noexcept=0
 Get block hash callback function.
 
virtual void emit_log (const address &addr, const uint8_t *data, size_t data_size, const bytes32 topics[], size_t num_topics) noexcept=0
 Emit log callback function.
 
virtual evmc_access_status access_account (const address &addr) noexcept=0
 Access account callback function.
 
virtual evmc_access_status access_storage (const address &addr, const bytes32 &key) noexcept=0
 Access storage callback function.
 
virtual bytes32 get_transient_storage (const address &addr, const bytes32 &key) const noexcept=0
 Get transient storage callback function.
 
virtual void set_transient_storage (const address &addr, const bytes32 &key, const bytes32 &value) noexcept=0
 Set transient storage callback function.
 

Public Attributes

std::unordered_map< address, MockedAccountaccounts
 The set of all accounts in the Host, organized by their addresses.
 
evmc_tx_context tx_context = {}
 The EVMC transaction context to be returned by get_tx_context().
 
bytes32 block_hash = {}
 The block header hash value to be returned by get_block_hash().
 
evmc_result call_result = {}
 The call result to be returned by the call() method.
 
std::vector< int64_t > recorded_blockhashes
 The record of all block numbers for which get_block_hash() was called.
 
std::vector< addressrecorded_account_accesses
 The record of all account accesses.
 
std::vector< evmc_messagerecorded_calls
 The record of all call messages requested in the call() method.
 
std::vector< log_recordrecorded_logs
 The record of all LOGs passed to the emit_log() method.
 
std::unordered_map< address, std::vector< address > > recorded_selfdestructs
 The record of all SELFDESTRUCTs from the selfdestruct() method as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].
 

Static Public Attributes

static constexpr auto max_recorded_account_accesses = 200
 The maximum number of entries in recorded_account_accesses record.
 
static constexpr auto max_recorded_calls = 100
 The maximum number of entries in recorded_calls record.
 

Additional Inherited Members

- Static Public Member Functions inherited from evmc::Host
static const evmc_host_interfaceget_interface () noexcept
 Provides access to the global host interface.
 
template<typename DerivedClass = Host>
static DerivedClass * from_context (evmc_host_context *context) noexcept
 Converts the opaque host context pointer back to the original Host object.
 

Detailed Description

Mocked EVMC Host implementation.

Definition at line 75 of file mocked_host.hpp.

Member Function Documentation

◆ access_account()

evmc_access_status evmc::MockedHost::access_account ( const address addr)
inlineoverridevirtualnoexcept

Record an account access.

This method is required by EIP-2929 introduced in EVMC_BERLIN. It will record the account access in MockedHost::recorded_account_accesses and return previous access status. This methods returns EVMC_ACCESS_WARM for known addresses of precompiles. The EIP-2929 specifies that evmc_message::sender and evmc_message::recipient are always EVMC_ACCESS_WARM. Therefore, you should init the MockedHost with:

mocked_host.access_account(msg.sender);
mocked_host.access_account(msg.recipient);

The same way you can mock transaction access list (EIP-2930) for account addresses.

Parameters
addrThe address of the accessed account.
Returns
The EVMC_ACCESS_WARM if the account has been accessed before, the EVMC_ACCESS_COLD otherwise.

Implements evmc::HostInterface.

Definition at line 440 of file mocked_host.hpp.

441 {
442 // Check if the address have been already accessed.
443 const auto already_accessed =
444 std::find(recorded_account_accesses.begin(), recorded_account_accesses.end(), addr) !=
446
447 record_account_access(addr);
448
449 // Accessing precompiled contracts is always warm.
450 if (addr >= 0x0000000000000000000000000000000000000001_address &&
451 addr <= 0x0000000000000000000000000000000000000009_address)
452 return EVMC_ACCESS_WARM;
453
454 return already_accessed ? EVMC_ACCESS_WARM : EVMC_ACCESS_COLD;
455 }
std::vector< address > recorded_account_accesses
The record of all account accesses.
@ EVMC_ACCESS_COLD
The entry hasn't been accessed before – it's the first access.
Definition: evmc.h:788
@ EVMC_ACCESS_WARM
The entry is already in accessed_addresses or accessed_storage_keys.
Definition: evmc.h:793

◆ access_storage()

evmc_access_status evmc::MockedHost::access_storage ( const address addr,
const bytes32 key 
)
inlineoverridevirtualnoexcept

Access the account's storage value at the given key.

This method is required by EIP-2929 introduced in EVMC_BERLIN. In records that the given account's storage key has been access and returns the previous access status. To mock storage access list (EIP-2930), you can pre-init account's storage values with the EVMC_ACCESS_WARM flag:

mocked_host.accounts[msg.recipient].storage[key] = {value,
EVMC_ACCESS_WARM};
Parameters
addrThe account address.
keyThe account's storage key.
Returns
The EVMC_ACCESS_WARM if the storage key has been accessed before, the EVMC_ACCESS_COLD otherwise.

Implements evmc::HostInterface.

Definition at line 471 of file mocked_host.hpp.

472 {
473 auto& value = accounts[addr].storage[key];
474 const auto access_status = value.access_status;
475 value.access_status = EVMC_ACCESS_WARM;
476 return access_status;
477 }
std::unordered_map< address, MockedAccount > accounts
The set of all accounts in the Host, organized by their addresses.
Definition: mocked_host.hpp:98

◆ account_exists()

bool evmc::MockedHost::account_exists ( const address addr) const
inlineoverridevirtualnoexcept

Returns true if an account exists (EVMC Host method).

Implements evmc::HostInterface.

Definition at line 150 of file mocked_host.hpp.

151 {
152 record_account_access(addr);
153 return accounts.count(addr) != 0;
154 }

◆ call()

Result evmc::MockedHost::call ( const evmc_message msg)
inlineoverridevirtualnoexcept

Call/create other contract (EVMC host method).

Implements evmc::HostInterface.

Definition at line 380 of file mocked_host.hpp.

381 {
382 record_account_access(msg.recipient);
383
384 if (recorded_calls.empty())
385 {
387 m_recorded_calls_inputs.reserve(max_recorded_calls); // Iterators will not invalidate.
388 }
389
391 {
392 recorded_calls.emplace_back(msg);
393 auto& call_msg = recorded_calls.back();
394 if (call_msg.input_size > 0)
395 {
396 m_recorded_calls_inputs.emplace_back(call_msg.input_data, call_msg.input_size);
397 const auto& input_copy = m_recorded_calls_inputs.back();
398 call_msg.input_data = input_copy.data();
399 }
400 }
401 return Result{call_result};
402 }
evmc_result call_result
The call result to be returned by the call() method.
static constexpr auto max_recorded_calls
The maximum number of entries in recorded_calls record.
std::vector< evmc_message > recorded_calls
The record of all call messages requested in the call() method.
evmc_address recipient
The recipient of the message.
Definition: evmc.h:133

◆ copy_code()

size_t evmc::MockedHost::copy_code ( const address addr,
size_t  code_offset,
uint8_t *  buffer_data,
size_t  buffer_size 
) const
inlineoverridevirtualnoexcept

Copy the account's code to the given buffer (EVMC host method).

Implements evmc::HostInterface.

Definition at line 348 of file mocked_host.hpp.

352 {
353 record_account_access(addr);
354 const auto it = accounts.find(addr);
355 if (it == accounts.end())
356 return 0;
357
358 const auto& code = it->second.code;
359
360 if (code_offset >= code.size())
361 return 0;
362
363 const auto n = std::min(buffer_size, code.size() - code_offset);
364
365 if (n > 0)
366 std::copy_n(&code[code_offset], n, buffer_data);
367 return n;
368 }

◆ emit_log()

void evmc::MockedHost::emit_log ( const address addr,
const uint8_t *  data,
size_t  data_size,
const bytes32  topics[],
size_t  topics_count 
)
inlineoverridevirtualnoexcept

Emit LOG (EVMC host method).

Implements evmc::HostInterface.

Definition at line 415 of file mocked_host.hpp.

420 {
421 recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
422 }
std::vector< log_record > recorded_logs
The record of all LOGs passed to the emit_log() method.

◆ get_balance()

uint256be evmc::MockedHost::get_balance ( const address addr) const
inlineoverridevirtualnoexcept

Get the account's balance (EVMC Host method).

Implements evmc::HostInterface.

Definition at line 317 of file mocked_host.hpp.

318 {
319 record_account_access(addr);
320 const auto it = accounts.find(addr);
321 if (it == accounts.end())
322 return {};
323
324 return it->second.balance;
325 }

◆ get_block_hash()

bytes32 evmc::MockedHost::get_block_hash ( int64_t  block_number) const
inlineoverridevirtualnoexcept

Get the block header hash (EVMC host method).

Implements evmc::HostInterface.

Definition at line 408 of file mocked_host.hpp.

409 {
410 recorded_blockhashes.emplace_back(block_number);
411 return block_hash;
412 }
bytes32 block_hash
The block header hash value to be returned by get_block_hash().
std::vector< int64_t > recorded_blockhashes
The record of all block numbers for which get_block_hash() was called.

◆ get_code_hash()

bytes32 evmc::MockedHost::get_code_hash ( const address addr) const
inlineoverridevirtualnoexcept

Get the account's code hash (EVMC host method).

Implements evmc::HostInterface.

Definition at line 338 of file mocked_host.hpp.

339 {
340 record_account_access(addr);
341 const auto it = accounts.find(addr);
342 if (it == accounts.end())
343 return {};
344 return it->second.codehash;
345 }

◆ get_code_size()

size_t evmc::MockedHost::get_code_size ( const address addr) const
inlineoverridevirtualnoexcept

Get the account's code size (EVMC host method).

Implements evmc::HostInterface.

Definition at line 328 of file mocked_host.hpp.

329 {
330 record_account_access(addr);
331 const auto it = accounts.find(addr);
332 if (it == accounts.end())
333 return 0;
334 return it->second.code.size();
335 }

◆ get_storage()

bytes32 evmc::MockedHost::get_storage ( const address addr,
const bytes32 key 
) const
inlineoverridevirtualnoexcept

Get the account's storage value at the given key (EVMC Host method).

Implements evmc::HostInterface.

Definition at line 157 of file mocked_host.hpp.

158 {
159 record_account_access(addr);
160
161 const auto account_iter = accounts.find(addr);
162 if (account_iter == accounts.end())
163 return {};
164
165 const auto storage_iter = account_iter->second.storage.find(key);
166 if (storage_iter != account_iter->second.storage.end())
167 return storage_iter->second.current;
168 return {};
169 }

◆ get_transient_storage()

bytes32 evmc::MockedHost::get_transient_storage ( const address addr,
const bytes32 key 
) const
inlineoverridevirtualnoexcept

Get account's transient storage.

Parameters
addrThe account address.
keyThe account's transient storage key.
Returns
The transient storage value. Null value in case the account does not exist.

Implements evmc::HostInterface.

Definition at line 484 of file mocked_host.hpp.

485 {
486 record_account_access(addr);
487
488 const auto account_iter = accounts.find(addr);
489 if (account_iter == accounts.end())
490 return {};
491
492 const auto storage_iter = account_iter->second.transient_storage.find(key);
493 if (storage_iter != account_iter->second.transient_storage.end())
494 return storage_iter->second;
495 return {};
496 }

◆ get_tx_context()

evmc_tx_context evmc::MockedHost::get_tx_context ( ) const
inlineoverridevirtualnoexcept

Get transaction context (EVMC host method).

Implements evmc::HostInterface.

Definition at line 405 of file mocked_host.hpp.

405{ return tx_context; }
evmc_tx_context tx_context
The EVMC transaction context to be returned by get_tx_context().

◆ selfdestruct()

bool evmc::MockedHost::selfdestruct ( const address addr,
const address beneficiary 
)
inlineoverridevirtualnoexcept

Selfdestruct the account (EVMC host method).

Implements evmc::HostInterface.

Definition at line 371 of file mocked_host.hpp.

372 {
373 record_account_access(addr);
374 auto& beneficiaries = recorded_selfdestructs[addr];
375 beneficiaries.emplace_back(beneficiary);
376 return beneficiaries.size() == 1;
377 }
std::unordered_map< address, std::vector< address > > recorded_selfdestructs
The record of all SELFDESTRUCTs from the selfdestruct() method as a map selfdestructed_address => [be...

◆ set_storage()

evmc_storage_status evmc::MockedHost::set_storage ( const address addr,
const bytes32 key,
const bytes32 value 
)
inlineoverridevirtualnoexcept

Set the account's storage value (EVMC Host method).

Implements evmc::HostInterface.

Definition at line 172 of file mocked_host.hpp.

175 {
176 record_account_access(addr);
177
178 // Get the reference to the storage entry value.
179 // This will create the account in case it was not present.
180 // This is convenient for unit testing and standalone EVM execution to preserve the
181 // storage values after the execution terminates.
182 auto& s = accounts[addr].storage[key];
183
184 // Follow the EIP-2200 specification as closely as possible.
185 // https://eips.ethereum.org/EIPS/eip-2200
186 // Warning: this is not the most efficient implementation. The storage status can be
187 // figured out by combining only 4 checks:
188 // - original != current (dirty)
189 // - original == value (restored)
190 // - current != 0
191 // - value != 0
192 const auto status = [&original = s.original, &current = s.current, &value]() {
193 // Clause 1 is irrelevant:
194 // 1. "If gasleft is less than or equal to gas stipend,
195 // fail the current call frame with ‘out of gas’ exception"
196
197 // 2. "If current value equals new value (this is a no-op)"
198 if (current == value)
199 {
200 // "SLOAD_GAS is deducted"
202 }
203 // 3. "If current value does not equal new value"
204 else
205 {
206 // 3.1. "If original value equals current value
207 // (this storage slot has not been changed by the current execution context)"
208 if (original == current)
209 {
210 // 3.1.1 "If original value is 0"
211 if (is_zero(original))
212 {
213 // "SSTORE_SET_GAS is deducted"
214 return EVMC_STORAGE_ADDED;
215 }
216 // 3.1.2 "Otherwise"
217 else
218 {
219 // "SSTORE_RESET_GAS gas is deducted"
220 auto st = EVMC_STORAGE_MODIFIED;
221
222 // "If new value is 0"
223 if (is_zero(value))
224 {
225 // "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
227 }
228
229 return st;
230 }
231 }
232 // 3.2. "If original value does not equal current value
233 // (this storage slot is dirty),
234 // SLOAD_GAS gas is deducted.
235 // Apply both of the following clauses."
236 else
237 {
238 // Because we need to apply "both following clauses"
239 // we first collect information which clause is triggered
240 // then assign status code to combination of these clauses.
241 enum
242 {
243 None = 0,
244 RemoveClearsSchedule = 1 << 0,
245 AddClearsSchedule = 1 << 1,
246 RestoredBySet = 1 << 2,
247 RestoredByReset = 1 << 3,
248 };
249 int triggered_clauses = None;
250
251 // 3.2.1. "If original value is not 0"
252 if (!is_zero(original))
253 {
254 // 3.2.1.1. "If current value is 0"
255 if (is_zero(current))
256 {
257 // "(also means that new value is not 0)"
258 assert(!is_zero(value));
259 // "remove SSTORE_CLEARS_SCHEDULE gas from refund counter"
260 triggered_clauses |= RemoveClearsSchedule;
261 }
262 // 3.2.1.2. "If new value is 0"
263 if (is_zero(value))
264 {
265 // "(also means that current value is not 0)"
266 assert(!is_zero(current));
267 // "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
268 triggered_clauses |= AddClearsSchedule;
269 }
270 }
271
272 // 3.2.2. "If original value equals new value (this storage slot is reset)"
273 // Except: we use term 'storage slot restored'.
274 if (original == value)
275 {
276 // 3.2.2.1. "If original value is 0"
277 if (is_zero(original))
278 {
279 // "add SSTORE_SET_GAS - SLOAD_GAS to refund counter"
280 triggered_clauses |= RestoredBySet;
281 }
282 // 3.2.2.2. "Otherwise"
283 else
284 {
285 // "add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter"
286 triggered_clauses |= RestoredByReset;
287 }
288 }
289
290 switch (triggered_clauses)
291 {
292 case RemoveClearsSchedule:
294 case AddClearsSchedule:
296 case RemoveClearsSchedule | RestoredByReset:
298 case RestoredBySet:
300 case RestoredByReset:
302 case None:
304 default:
305 assert(false); // Other combinations are impossible.
306 return evmc_storage_status{};
307 }
308 }
309 }
310 }();
311
312 s.current = value; // Finally update the current storage value.
313 return status;
314 }
evmc_storage_status
The effect of an attempt to modify a contract storage item.
Definition: evmc.h:569
@ EVMC_STORAGE_ADDED_DELETED
A storage item is deleted by changing the current dirty nonzero to the original zero value.
Definition: evmc.h:633
@ EVMC_STORAGE_MODIFIED_RESTORED
A storage item is modified by changing the current dirty nonzero to the original nonzero value other ...
Definition: evmc.h:640
@ EVMC_STORAGE_DELETED_RESTORED
A storage item is added by changing the current dirty zero to the original value.
Definition: evmc.h:626
@ EVMC_STORAGE_ADDED
A new storage item is added by changing the current clean zero to a nonzero value.
Definition: evmc.h:591
@ EVMC_STORAGE_MODIFIED_DELETED
A storage item is deleted by changing the current dirty nonzero to the zero value and the original va...
Definition: evmc.h:619
@ EVMC_STORAGE_ASSIGNED
The new/same value is assigned to the storage item without affecting the cost structure.
Definition: evmc.h:584
@ EVMC_STORAGE_DELETED
A storage item is deleted by changing the current clean nonzero to the zero value.
Definition: evmc.h:598
@ EVMC_STORAGE_DELETED_ADDED
A storage item is added by changing the current dirty zero to a nonzero value other than the original...
Definition: evmc.h:612
@ EVMC_STORAGE_MODIFIED
A storage item is modified by changing the current clean nonzero to other nonzero value.
Definition: evmc.h:605
constexpr bool is_zero(const address &a) noexcept
Checks if the given address is the zero address.
Definition: evmc.hpp:258

◆ set_transient_storage()

void evmc::MockedHost::set_transient_storage ( const address addr,
const bytes32 key,
const bytes32 value 
)
inlineoverridevirtualnoexcept

Set account's transient storage.

Parameters
addrThe account address.
keyThe account's transient storage key.
valueThe value to be stored.

Implements evmc::HostInterface.

Definition at line 503 of file mocked_host.hpp.

506 {
507 record_account_access(addr);
508 accounts[addr].transient_storage[key] = value;
509 }

Member Data Documentation

◆ accounts

std::unordered_map<address, MockedAccount> evmc::MockedHost::accounts

The set of all accounts in the Host, organized by their addresses.

Definition at line 98 of file mocked_host.hpp.

◆ block_hash

bytes32 evmc::MockedHost::block_hash = {}

The block header hash value to be returned by get_block_hash().

Definition at line 104 of file mocked_host.hpp.

◆ call_result

evmc_result evmc::MockedHost::call_result = {}

The call result to be returned by the call() method.

Definition at line 107 of file mocked_host.hpp.

◆ max_recorded_account_accesses

constexpr auto evmc::MockedHost::max_recorded_account_accesses = 200
staticconstexpr

The maximum number of entries in recorded_account_accesses record.

This is arbitrary value useful in fuzzing when we don't want the record to explode.

Definition at line 117 of file mocked_host.hpp.

◆ max_recorded_calls

constexpr auto evmc::MockedHost::max_recorded_calls = 100
staticconstexpr

The maximum number of entries in recorded_calls record.

This is arbitrary value useful in fuzzing when we don't want the record to explode.

Definition at line 124 of file mocked_host.hpp.

◆ recorded_account_accesses

std::vector<address> evmc::MockedHost::recorded_account_accesses
mutable

The record of all account accesses.

Definition at line 113 of file mocked_host.hpp.

◆ recorded_blockhashes

std::vector<int64_t> evmc::MockedHost::recorded_blockhashes
mutable

The record of all block numbers for which get_block_hash() was called.

Definition at line 110 of file mocked_host.hpp.

◆ recorded_calls

std::vector<evmc_message> evmc::MockedHost::recorded_calls

The record of all call messages requested in the call() method.

Definition at line 120 of file mocked_host.hpp.

◆ recorded_logs

std::vector<log_record> evmc::MockedHost::recorded_logs

The record of all LOGs passed to the emit_log() method.

Definition at line 127 of file mocked_host.hpp.

◆ recorded_selfdestructs

std::unordered_map<address, std::vector<address> > evmc::MockedHost::recorded_selfdestructs

The record of all SELFDESTRUCTs from the selfdestruct() method as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].

Definition at line 131 of file mocked_host.hpp.

◆ tx_context

evmc_tx_context evmc::MockedHost::tx_context = {}

The EVMC transaction context to be returned by get_tx_context().

Definition at line 101 of file mocked_host.hpp.


The documentation for this class was generated from the following file: