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 78 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 443 of file mocked_host.hpp.

444 {
445 // Check if the address have been already accessed.
446 const auto already_accessed =
447 std::find(recorded_account_accesses.begin(), recorded_account_accesses.end(), addr) !=
449
450 record_account_access(addr);
451
452 // Accessing precompiled contracts is always warm.
453 if (addr >= 0x0000000000000000000000000000000000000001_address &&
454 addr <= 0x0000000000000000000000000000000000000009_address)
455 return EVMC_ACCESS_WARM;
456
457 return already_accessed ? EVMC_ACCESS_WARM : EVMC_ACCESS_COLD;
458 }
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:766
@ EVMC_ACCESS_WARM
The entry is already in accessed_addresses or accessed_storage_keys.
Definition: evmc.h:771

◆ 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 474 of file mocked_host.hpp.

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

◆ 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 153 of file mocked_host.hpp.

154 {
155 record_account_access(addr);
156 return accounts.count(addr) != 0;
157 }

◆ call()

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

Call/create other contract (EVMC host method).

Implements evmc::HostInterface.

Definition at line 383 of file mocked_host.hpp.

384 {
385 record_account_access(msg.recipient);
386
387 if (recorded_calls.empty())
388 {
390 m_recorded_calls_inputs.reserve(max_recorded_calls); // Iterators will not invalidate.
391 }
392
394 {
395 recorded_calls.emplace_back(msg);
396 auto& call_msg = recorded_calls.back();
397 if (call_msg.input_size > 0)
398 {
399 m_recorded_calls_inputs.emplace_back(call_msg.input_data, call_msg.input_size);
400 const auto& input_copy = m_recorded_calls_inputs.back();
401 call_msg.input_data = input_copy.data();
402 }
403 }
404 return Result{call_result};
405 }
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:132

◆ 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 351 of file mocked_host.hpp.

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

◆ 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 418 of file mocked_host.hpp.

423 {
424 recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
425 }
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 320 of file mocked_host.hpp.

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

◆ 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 411 of file mocked_host.hpp.

412 {
413 recorded_blockhashes.emplace_back(block_number);
414 return block_hash;
415 }
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 341 of file mocked_host.hpp.

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

◆ 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 331 of file mocked_host.hpp.

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

◆ 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 160 of file mocked_host.hpp.

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.current;
171 return {};
172 }

◆ 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 487 of file mocked_host.hpp.

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

◆ 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 408 of file mocked_host.hpp.

408{ 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 374 of file mocked_host.hpp.

375 {
376 record_account_access(addr);
377 auto& beneficiaries = recorded_selfdestructs[addr];
378 beneficiaries.emplace_back(beneficiary);
379 return beneficiaries.size() == 1;
380 }
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 175 of file mocked_host.hpp.

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

◆ 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 506 of file mocked_host.hpp.

509 {
510 record_account_access(addr);
511 accounts[addr].transient_storage[key] = value;
512 }

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 101 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 107 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 110 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 120 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 127 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 116 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 113 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 123 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 130 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 134 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 104 of file mocked_host.hpp.


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