Upgrade to EVMC 8.0.0

This commit is contained in:
Alex Beregszaszi 2021-04-21 21:02:33 +01:00 committed by Daniel Kirchner
parent 98e2b4e5ed
commit 5bfd26287c
5 changed files with 153 additions and 7 deletions

View File

@ -148,6 +148,8 @@ void EVMHost::reset()
recorded_selfdestructs.clear();
// Clear call records
recorded_calls.clear();
// Clear EIP-2929 account access indicator
recorded_account_accesses.clear();
// Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161).
// NOTE: keep this in sync with `EVMHost::call` below.

View File

@ -1,5 +1,5 @@
# EVMC
This is an import of [EVMC](https://github.com/ethereum/evmc) version [7.5.0](https://github.com/ethereum/evmc/releases/tag/v7.5.0).
This is an import of [EVMC](https://github.com/ethereum/evmc) version [8.0.0](https://github.com/ethereum/evmc/releases/tag/v8.0.0).
Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing.

View File

@ -44,7 +44,7 @@ enum
*
* @see @ref versioning
*/
EVMC_ABI_VERSION = 7
EVMC_ABI_VERSION = 8
};
@ -603,6 +603,52 @@ typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context,
const evmc_bytes32 topics[],
size_t topics_count);
/**
* Access status per EIP-2929: Gas cost increases for state access opcodes.
*/
enum evmc_access_status
{
/**
* The entry hasn't been accessed before it's the first access.
*/
EVMC_ACCESS_COLD = 0,
/**
* The entry is already in accessed_addresses or accessed_storage_keys.
*/
EVMC_ACCESS_WARM = 1
};
/**
* Access account callback function.
*
* This callback function is used by a VM to add the given address
* to accessed_addresses substate (EIP-2929).
*
* @param context The Host execution context.
* @param address The address of the account.
* @return EVMC_ACCESS_WARM if accessed_addresses already contained the address
* or EVMC_ACCESS_COLD otherwise.
*/
typedef enum evmc_access_status (*evmc_access_account_fn)(struct evmc_host_context* context,
const evmc_address* address);
/**
* Access storage callback function.
*
* This callback function is used by a VM to add the given account storage entry
* to accessed_storage_keys substate (EIP-2929).
*
* @param context The Host execution context.
* @param address The address of the account.
* @param key The index of the account's storage entry.
* @return EVMC_ACCESS_WARM if accessed_storage_keys already contained the key
* or EVMC_ACCESS_COLD otherwise.
*/
typedef enum evmc_access_status (*evmc_access_storage_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_bytes32* key);
/**
* Pointer to the callback function supporting EVM calls.
*
@ -658,6 +704,12 @@ struct evmc_host_interface
/** Emit log callback function. */
evmc_emit_log_fn emit_log;
/** Access account callback function. */
evmc_access_account_fn access_account;
/** Access storage callback function. */
evmc_access_storage_fn access_storage;
};

View File

@ -465,6 +465,12 @@ public:
size_t data_size,
const bytes32 topics[],
size_t num_topics) noexcept = 0;
/// @copydoc evmc_host_interface::access_account
virtual evmc_access_status access_account(const address& addr) noexcept = 0;
/// @copydoc evmc_host_interface::access_storage
virtual evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept = 0;
};
@ -564,6 +570,16 @@ public:
{
host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
evmc_access_status access_account(const address& address) noexcept final
{
return host->access_account(context, &address);
}
evmc_access_status access_storage(const address& address, const bytes32& key) noexcept final
{
return host->access_storage(context, &address, &key);
}
};
@ -805,6 +821,18 @@ inline void emit_log(evmc_host_context* h,
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
num_topics);
}
inline evmc_access_status access_account(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->access_account(*addr);
}
inline evmc_access_status access_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key) noexcept
{
return Host::from_context(h)->access_storage(*addr, *key);
}
} // namespace internal
inline const evmc_host_interface& Host::get_interface() noexcept
@ -815,7 +843,9 @@ inline const evmc_host_interface& Host::get_interface() noexcept
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
::evmc::internal::copy_code, ::evmc::internal::selfdestruct,
::evmc::internal::call, ::evmc::internal::get_tx_context,
::evmc::internal::get_block_hash, ::evmc::internal::emit_log};
::evmc::internal::get_block_hash, ::evmc::internal::emit_log,
::evmc::internal::access_account, ::evmc::internal::access_storage,
};
return interface;
}
} // namespace evmc

View File

@ -24,6 +24,9 @@ struct storage_value
/// True means this value has been modified already by the current transaction.
bool dirty{false};
/// Is the storage key cold or warm.
evmc_access_status access_status{EVMC_ACCESS_COLD};
/// Default constructor.
storage_value() noexcept = default;
@ -31,6 +34,11 @@ struct storage_value
storage_value(const bytes32& _value, bool _dirty = false) noexcept // NOLINT
: value{_value}, dirty{_dirty}
{}
/// Constructor with initial access status.
storage_value(const bytes32& _value, evmc_access_status _access_status) noexcept
: value{_value}, access_status{_access_status}
{}
};
/// Mocked account.
@ -84,7 +92,7 @@ public:
};
/// SELFDESTRUCT record.
struct selfdestuct_record
struct selfdestruct_record
{
/// The address of the account which has self-destructed.
address selfdestructed;
@ -93,7 +101,7 @@ public:
address beneficiary;
/// Equal operator.
bool operator==(const selfdestuct_record& other) const noexcept
bool operator==(const selfdestruct_record& other) const noexcept
{
return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary;
}
@ -132,13 +140,12 @@ public:
std::vector<log_record> recorded_logs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method.
std::vector<selfdestuct_record> recorded_selfdestructs;
std::vector<selfdestruct_record> recorded_selfdestructs;
private:
/// The copy of call inputs for the recorded_calls record.
std::vector<bytes> m_recorded_calls_inputs;
public:
/// Record an account access.
/// @param addr The address of the accessed account.
void record_account_access(const address& addr) const
@ -150,6 +157,7 @@ public:
recorded_account_accesses.emplace_back(addr);
}
public:
/// Returns true if an account exists (EVMC Host method).
bool account_exists(const address& addr) const noexcept override
{
@ -313,5 +321,59 @@ public:
{
recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
}
/// 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::destination are always
/// ::EVMC_ACCESS_WARM. Therefore, you should init the MockedHost with:
///
/// mocked_host.access_account(msg.sender);
/// mocked_host.access_account(msg.destination);
///
/// The same way you can mock transaction access list (EIP-2930) for account addresses.
///
/// @param addr The address of the accessed account.
/// @returns The ::EVMC_ACCESS_WARM if the account has been accessed before,
/// the ::EVMC_ACCESS_COLD otherwise.
evmc_access_status access_account(const address& addr) noexcept override
{
// Check if the address have been already accessed.
const auto already_accessed =
std::find(recorded_account_accesses.begin(), recorded_account_accesses.end(), addr) !=
recorded_account_accesses.end();
record_account_access(addr);
// Accessing precompiled contracts is always warm.
if (addr >= 0x0000000000000000000000000000000000000001_address &&
addr <= 0x0000000000000000000000000000000000000009_address)
return EVMC_ACCESS_WARM;
return already_accessed ? EVMC_ACCESS_WARM : EVMC_ACCESS_COLD;
}
/// 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.destination].storage[key] = {value, EVMC_ACCESS_WARM};
///
/// @param addr The account address.
/// @param key The account's storage key.
/// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed before,
/// the ::EVMC_ACCESS_COLD otherwise.
evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override
{
auto& value = accounts[addr].storage[key];
const auto access_status = value.access_status;
value.access_status = EVMC_ACCESS_WARM;
return access_status;
}
};
} // namespace evmc