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). More...
 
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). More...
 
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). More...
 
uint256be get_balance (const address &addr) const noexcept override
 Get the account's balance (EVMC Host method). More...
 
size_t get_code_size (const address &addr) const noexcept override
 Get the account's code size (EVMC host method). More...
 
bytes32 get_code_hash (const address &addr) const noexcept override
 Get the account's code hash (EVMC host method). More...
 
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). More...
 
bool selfdestruct (const address &addr, const address &beneficiary) noexcept override
 Selfdestruct the account (EVMC host method). More...
 
Result call (const evmc_message &msg) noexcept override
 Call/create other contract (EVMC host method). More...
 
evmc_tx_context get_tx_context () const noexcept override
 Get transaction context (EVMC host method). More...
 
bytes32 get_block_hash (int64_t block_number) const noexcept override
 Get the block header hash (EVMC host method). More...
 
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). More...
 
evmc_access_status access_account (const address &addr) noexcept override
 Record an account access. More...
 
evmc_access_status access_storage (const address &addr, const bytes32 &key) noexcept override
 Access the account's storage value at the given key. More...
 
- Public Member Functions inherited from evmc::Host
evmc_host_contextto_context () noexcept
 Converts the Host object to the opaque host context pointer. More...
 
virtual bool account_exists (const address &addr) const noexcept=0
 Check account existence callback function. More...
 
virtual bytes32 get_storage (const address &addr, const bytes32 &key) const noexcept=0
 Get storage callback function. More...
 
virtual evmc_storage_status set_storage (const address &addr, const bytes32 &key, const bytes32 &value) noexcept=0
 Set storage callback function. More...
 
virtual uint256be get_balance (const address &addr) const noexcept=0
 Get balance callback function. More...
 
virtual size_t get_code_size (const address &addr) const noexcept=0
 Get code size callback function. More...
 
virtual bytes32 get_code_hash (const address &addr) const noexcept=0
 Get code hash callback function. More...
 
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. More...
 
virtual bool selfdestruct (const address &addr, const address &beneficiary) noexcept=0
 Selfdestruct callback function. More...
 
virtual Result call (const evmc_message &msg) noexcept=0
 Call callback function. More...
 
virtual evmc_tx_context get_tx_context () const noexcept=0
 Get transaction context callback function. More...
 
virtual bytes32 get_block_hash (int64_t block_number) const noexcept=0
 Get block hash callback function. More...
 
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. More...
 
virtual evmc_access_status access_account (const address &addr) noexcept=0
 Access account callback function. More...
 
virtual evmc_access_status access_storage (const address &addr, const bytes32 &key) noexcept=0
 Access storage callback function. More...
 

Public Attributes

std::unordered_map< address, MockedAccountaccounts
 The set of all accounts in the Host, organized by their addresses. More...
 
evmc_tx_context tx_context = {}
 The EVMC transaction context to be returned by get_tx_context(). More...
 
bytes32 block_hash = {}
 The block header hash value to be returned by get_block_hash(). More...
 
evmc_result call_result = {}
 The call result to be returned by the call() method. More...
 
std::vector< int64_t > recorded_blockhashes
 The record of all block numbers for which get_block_hash() was called. More...
 
std::vector< addressrecorded_account_accesses
 The record of all account accesses. More...
 
std::vector< evmc_messagerecorded_calls
 The record of all call messages requested in the call() method. More...
 
std::vector< log_recordrecorded_logs
 The record of all LOGs passed to the emit_log() method. More...
 
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, ...]. More...
 

Static Public Attributes

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

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. More...
 
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. More...
 

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:729
@ EVMC_ACCESS_WARM
The entry is already in accessed_addresses or accessed_storage_keys.
Definition: evmc.h:734

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

473 {
474 auto& value = accounts[addr].storage[key];
475 const auto access_status = value.access_status;
476 value.access_status = EVMC_ACCESS_WARM;
477 return access_status;
478 }
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: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 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_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:529
@ EVMC_STORAGE_ADDED_DELETED
A storage item is deleted by changing the current dirty nonzero to the original zero value.
Definition: evmc.h:593
@ EVMC_STORAGE_MODIFIED_RESTORED
A storage item is modified by changing the current dirty nonzero to the original nonzero value other ...
Definition: evmc.h:600
@ EVMC_STORAGE_DELETED_RESTORED
A storage item is added by changing the current dirty zero to the original value.
Definition: evmc.h:586
@ EVMC_STORAGE_ADDED
A new storage item is added by changing the current clean zero to a nonzero value.
Definition: evmc.h:551
@ 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:579
@ EVMC_STORAGE_ASSIGNED
The new/same value is assigned to the storage item without affecting the cost structure.
Definition: evmc.h:544
@ EVMC_STORAGE_DELETED
A storage item is deleted by changing the current clean nonzero to the zero value.
Definition: evmc.h:558
@ 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:572
@ EVMC_STORAGE_MODIFIED
A storage item is modified by changing the current clean nonzero to other nonzero value.
Definition: evmc.h:565
constexpr bool is_zero(const address &a) noexcept
Checks if the given address is the zero address.
Definition: evmc.hpp:261

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: