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 77 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 442 of file mocked_host.hpp.

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

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

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

◆ call()

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

Call/create other contract (EVMC host method).

Implements evmc::HostInterface.

Definition at line 382 of file mocked_host.hpp.

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

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

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

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

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

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

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

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

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

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

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

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

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

407{ 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 373 of file mocked_host.hpp.

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

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


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