Upgrade to evmc 10.0.0

This commit is contained in:
Alex Beregszaszi 2022-09-16 12:32:12 +02:00
parent 5c139b60b8
commit 458857d0d6
10 changed files with 861 additions and 230 deletions

View File

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

View File

@ -2,7 +2,7 @@
* EVMC: Ethereum Client-VM Connector API
*
* @copyright
* Copyright 2016-2019 The EVMC Authors.
* Copyright 2016 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*
* @defgroup EVMC EVMC
@ -44,7 +44,7 @@ enum
*
* @see @ref versioning
*/
EVMC_ABI_VERSION = 9
EVMC_ABI_VERSION = 10
};
@ -89,8 +89,9 @@ enum evmc_flags
};
/**
* The message describing an EVM call,
* including a zero-depth calls from a transaction origin.
* The message describing an EVM call, including a zero-depth calls from a transaction origin.
*
* Most of the fields are modelled by the section 8. Message Call of the Ethereum Yellow Paper.
*/
struct evmc_message
{
@ -103,21 +104,48 @@ struct evmc_message
*/
uint32_t flags;
/** The call depth. */
/**
* The present depth of the message call stack.
*
* Defined as `e` in the Yellow Paper.
*/
int32_t depth;
/** The amount of gas for message execution. */
/**
* The amount of gas available to the message execution.
*
* Defined as `g` in the Yellow Paper.
*/
int64_t gas;
/** The destination of the message. */
evmc_address destination;
/**
* The recipient of the message.
*
* This is the address of the account which storage/balance/nonce is going to be modified
* by the message execution. In case of ::EVMC_CALL, this is also the account where the
* message value evmc_message::value is going to be transferred.
* For ::EVMC_CALLCODE or ::EVMC_DELEGATECALL, this may be different from
* the evmc_message::code_address.
*
* Defined as `r` in the Yellow Paper.
*/
evmc_address recipient;
/** The sender of the message. */
/**
* The sender of the message.
*
* The address of the sender of a message call defined as `s` in the Yellow Paper.
* This must be the message recipient of the message at the previous (lower) depth,
* except for the ::EVMC_DELEGATECALL where recipient is the 2 levels above the present depth.
* At the depth 0 this must be the transaction origin.
*/
evmc_address sender;
/**
* The message input data.
*
* The arbitrary length byte array of the input data of the call,
* defined as `d` in the Yellow Paper.
* This MAY be NULL.
*/
const uint8_t* input_data;
@ -131,30 +159,49 @@ struct evmc_message
/**
* The amount of Ether transferred with the message.
*
* This is transferred value for ::EVMC_CALL or apparent value for ::EVMC_DELEGATECALL.
* Defined as `v` or `v~` in the Yellow Paper.
*/
evmc_uint256be value;
/**
* The optional value used in new contract address construction.
*
* Ignored unless kind is EVMC_CREATE2.
* Needed only for a Host to calculate created address when kind is ::EVMC_CREATE2.
* Ignored in evmc_execute_fn().
*/
evmc_bytes32 create2_salt;
/**
* The address of the code to be executed.
*
* For ::EVMC_CALLCODE or ::EVMC_DELEGATECALL this may be different from
* the evmc_message::recipient.
* Not required when invoking evmc_execute_fn(), only when invoking evmc_call_fn().
* Ignored if kind is ::EVMC_CREATE or ::EVMC_CREATE2.
*
* In case of ::EVMC_CAPABILITY_PRECOMPILES implementation, this fields should be inspected
* to identify the requested precompile.
*
* Defined as `c` in the Yellow Paper.
*/
evmc_address code_address;
};
/** The transaction and block data for execution. */
struct evmc_tx_context
{
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
evmc_address tx_origin; /**< The transaction origin account. */
evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */
int64_t block_timestamp; /**< The block timestamp. */
int64_t block_gas_limit; /**< The block gas limit. */
evmc_uint256be block_difficulty; /**< The block difficulty. */
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
evmc_address tx_origin; /**< The transaction origin account. */
evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */
int64_t block_timestamp; /**< The block timestamp. */
int64_t block_gas_limit; /**< The block gas limit. */
evmc_uint256be block_prev_randao; /**< The block previous RANDAO (EIP-4399). */
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */
};
/**
@ -354,6 +401,14 @@ struct evmc_result
*/
int64_t gas_left;
/**
* The refunded gas accumulated from this execution and its sub-calls.
*
* The transaction gas refund limit is not applied.
* If evmc_result::status_code is other than ::EVMC_SUCCESS the value MUST be 0.
*/
int64_t gas_refund;
/**
* The reference to output data.
*
@ -396,12 +451,11 @@ struct evmc_result
evmc_release_result_fn release;
/**
* The address of the contract created by create instructions.
* The address of the possibly created contract.
*
* This field has valid value only if:
* - it is a result of the Host method evmc_host_interface::call
* - and the result describes successful contract creation
* (evmc_result::status_code is ::EVMC_SUCCESS).
* The create address may be provided even though the contract creation has failed
* (evmc_result::status_code is not ::EVMC_SUCCESS). This is useful in situations
* when the address is observable, e.g. access to it remains warm.
* In all other cases the address MUST be null bytes.
*/
evmc_address create_address;
@ -452,40 +506,97 @@ typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context,
/**
* The effect of an attempt to modify a contract storage item.
*
* See @ref storagestatus for additional information about design of this enum
* and analysis of the specification.
*
* For the purpose of explaining the meaning of each element, the following
* notation is used:
* - 0 is zero value,
* - X != 0 (X is any value other than 0),
* - Y != X, Y != 0 (Y is any value other than X and 0),
* - Z != Y (Z is any value other than Y),
* - the "->" means the change from one value to another.
* - Y != 0, Y != X, (Y is any value other than X and 0),
* - Z != 0, Z != X, Z != X (Z is any value other than Y and X and 0),
* - the "o -> c -> v" triple describes the change status in the context of:
* - o: original value (cold value before a transaction started),
* - c: current storage value,
* - v: new storage value to be set.
*
* The order of elements follows EIPs introducing net storage gas costs:
* - EIP-2200: https://eips.ethereum.org/EIPS/eip-2200,
* - EIP-1283: https://eips.ethereum.org/EIPS/eip-1283.
*/
enum evmc_storage_status
{
/**
* The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
* The new/same value is assigned to the storage item without affecting the cost structure.
*
* The storage value item is either:
* - left unchanged (c == v) or
* - the dirty value (o != c) is modified again (c != v).
* This is the group of cases related to minimal gas cost of only accessing warm storage.
* 0|X -> 0 -> 0 (current value unchanged)
* 0|X|Y -> Y -> Y (current value unchanged)
* 0|X -> Y -> Z (modified previously added/modified value)
*
* This is "catch all remaining" status. I.e. if all other statuses are correctly matched
* this status should be assigned to all remaining cases.
*/
EVMC_STORAGE_UNCHANGED = 0,
EVMC_STORAGE_ASSIGNED = 0,
/**
* The value of a storage item has been modified: X -> Y.
* A new storage item is added by changing
* the current clean zero to a nonzero value.
* 0 -> 0 -> Z
*/
EVMC_STORAGE_MODIFIED = 1,
EVMC_STORAGE_ADDED = 1,
/**
* A storage item has been modified after being modified before: X -> Y -> Z.
* A storage item is deleted by changing
* the current clean nonzero to the zero value.
* X -> X -> 0
*/
EVMC_STORAGE_MODIFIED_AGAIN = 2,
EVMC_STORAGE_DELETED = 2,
/**
* A new storage item has been added: 0 -> X.
* A storage item is modified by changing
* the current clean nonzero to other nonzero value.
* X -> X -> Z
*/
EVMC_STORAGE_ADDED = 3,
EVMC_STORAGE_MODIFIED = 3,
/**
* A storage item has been deleted: X -> 0.
* A storage item is added by changing
* the current dirty zero to a nonzero value other than the original value.
* X -> 0 -> Z
*/
EVMC_STORAGE_DELETED = 4
EVMC_STORAGE_DELETED_ADDED = 4,
/**
* A storage item is deleted by changing
* the current dirty nonzero to the zero value and the original value is not zero.
* X -> Y -> 0
*/
EVMC_STORAGE_MODIFIED_DELETED = 5,
/**
* A storage item is added by changing
* the current dirty zero to the original value.
* X -> 0 -> X
*/
EVMC_STORAGE_DELETED_RESTORED = 6,
/**
* A storage item is deleted by changing
* the current dirty nonzero to the original zero value.
* 0 -> Y -> 0
*/
EVMC_STORAGE_ADDED_DELETED = 7,
/**
* A storage item is modified by changing
* the current dirty nonzero to the original nonzero value other than the current value.
* X -> Y -> X
*/
EVMC_STORAGE_MODIFIED_RESTORED = 8
};
@ -495,7 +606,7 @@ enum evmc_storage_status
* This callback function is used by a VM to update the given account storage entry.
* The VM MUST make sure that the account exists. This requirement is only a formality because
* VM implementations only modify storage of the account of the current execution context
* (i.e. referenced by evmc_message::destination).
* (i.e. referenced by evmc_message::recipient).
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
@ -579,8 +690,10 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context,
* @param context The pointer to the Host execution context. See ::evmc_host_context.
* @param address The address of the contract to be selfdestructed.
* @param beneficiary The address where the remaining ETH is going to be transferred.
* @return The information if the given address has not been registered as
* selfdestructed yet. True if registered for the first time, false otherwise.
*/
typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
typedef bool (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_address* beneficiary);
@ -821,26 +934,40 @@ enum evmc_revision
/**
* The Berlin revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md
*/
EVMC_BERLIN = 8,
/**
* The London revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md
*/
EVMC_LONDON = 9,
/**
* The Paris revision (aka The Merge).
*
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md
*/
EVMC_PARIS = 10,
/**
* The Shanghai revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md
*/
EVMC_SHANGHAI = 10,
EVMC_SHANGHAI = 11,
/**
* The Cancun revision.
*
* The future next revision after Shanghai.
*/
EVMC_CANCUN = 12,
/** The maximum EVM revision supported. */
EVMC_MAX_REVISION = EVMC_SHANGHAI,
EVMC_MAX_REVISION = EVMC_CANCUN,
/**
* The latest known EVM revision with finalized specification.
@ -894,7 +1021,7 @@ enum evmc_capabilities
/**
* The VM is capable of executing the precompiled contracts
* defined for the range of destination addresses.
* defined for the range of code addresses.
*
* The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies
* the range 0x000...0000 - 0x000...ffff of addresses

View File

@ -1,20 +1,28 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2020 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <evmc/evmc.h>
#include <evmc/helpers.h>
#include <evmc/hex.hpp>
#include <functional>
#include <initializer_list>
#include <ostream>
#include <string_view>
#include <utility>
static_assert(EVMC_LATEST_STABLE_REVISION <= EVMC_MAX_REVISION,
"latest stable revision ill-defined");
/// EVMC C++ API - wrappers and bindings for C++
/// @ingroup cpp
namespace evmc
{
/// String view of uint8_t chars.
using bytes_view = std::basic_string_view<uint8_t>;
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
///
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
@ -54,6 +62,9 @@ struct address : evmc_address
/// Explicit operator converting to bool.
inline constexpr explicit operator bool() const noexcept;
/// Implicit operator converting to bytes_view.
inline constexpr operator bytes_view() const noexcept { return {bytes, sizeof(bytes)}; }
};
/// The fixed size array of 32 bytes for storing 256-bit EVM values.
@ -106,7 +117,10 @@ struct bytes32 : evmc_bytes32
{}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
inline constexpr explicit operator bool() const noexcept;
/// Implicit operator converting to bytes_view.
inline constexpr operator bytes_view() const noexcept { return {bytes, sizeof(bytes)}; }
};
/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
@ -267,75 +281,46 @@ inline constexpr bytes32::operator bool() const noexcept
namespace literals
{
namespace internal
{
constexpr int from_hex(char c) noexcept
{
return (c >= 'a' && c <= 'f') ? c - ('a' - 10) :
(c >= 'A' && c <= 'F') ? c - ('A' - 10) : c - '0';
}
constexpr uint8_t byte(const char* s, size_t i) noexcept
{
return static_cast<uint8_t>((from_hex(s[2 * i]) << 4) | from_hex(s[2 * i + 1]));
}
/// Converts a raw literal into value of type T.
///
/// This function is expected to be used on literals in constexpr context only.
/// In case the input is invalid the std::terminate() is called.
/// TODO(c++20): Use consteval.
template <typename T>
T from_hex(const char*) noexcept;
template <>
constexpr bytes32 from_hex<bytes32>(const char* s) noexcept
constexpr T parse(std::string_view s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19), byte(s, 20),
byte(s, 21), byte(s, 22), byte(s, 23), byte(s, 24), byte(s, 25), byte(s, 26), byte(s, 27),
byte(s, 28), byte(s, 29), byte(s, 30), byte(s, 31)}}};
return from_hex<T>(s).value();
}
template <>
constexpr address from_hex<address>(const char* s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19)}}};
}
template <typename T, char... c>
constexpr T from_literal() noexcept
{
constexpr auto size = sizeof...(c);
constexpr char literal[] = {c...};
constexpr bool is_simple_zero = size == 1 && literal[0] == '0';
static_assert(is_simple_zero || (literal[0] == '0' && literal[1] == 'x'),
"literal must be in hexadecimal notation");
static_assert(is_simple_zero || size == 2 * sizeof(T) + 2,
"literal must match the result type size");
return is_simple_zero ? T{} : from_hex<T>(&literal[2]);
}
} // namespace internal
/// Literal for evmc::address.
template <char... c>
constexpr address operator""_address() noexcept
constexpr address operator""_address(const char* s) noexcept
{
return internal::from_literal<address, c...>();
return parse<address>(s);
}
/// Literal for evmc::bytes32.
template <char... c>
constexpr bytes32 operator""_bytes32() noexcept
constexpr bytes32 operator""_bytes32(const char* s) noexcept
{
return internal::from_literal<bytes32, c...>();
return parse<bytes32>(s);
}
} // namespace literals
using namespace literals;
/// @copydoc evmc_status_code_to_string
inline const char* to_string(evmc_status_code status_code) noexcept
{
return evmc_status_code_to_string(status_code);
}
/// @copydoc evmc_revision_to_string
inline const char* to_string(evmc_revision rev) noexcept
{
return evmc_revision_to_string(rev);
}
/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;
@ -343,11 +328,12 @@ constexpr auto make_result = evmc_make_result;
///
/// This is a RAII wrapper for evmc_result and objects of this type
/// automatically release attached resources.
class result : private evmc_result
class Result : private evmc_result
{
public:
using evmc_result::create_address;
using evmc_result::gas_left;
using evmc_result::gas_refund;
using evmc_result::output_data;
using evmc_result::output_size;
using evmc_result::status_code;
@ -359,40 +345,70 @@ public:
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
/// @param _output_data The pointer to the output.
/// @param _output_size The output size.
result(evmc_status_code _status_code,
int64_t _gas_left,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
explicit Result(evmc_status_code _status_code,
int64_t _gas_left,
int64_t _gas_refund,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, _output_data, _output_size)}
{}
/// Creates the result without output.
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
explicit Result(evmc_status_code _status_code = EVMC_INTERNAL_ERROR,
int64_t _gas_left = 0,
int64_t _gas_refund = 0) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
{}
/// Creates the result of contract creation.
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
/// @param _create_address The address of the possibly created account.
explicit Result(evmc_status_code _status_code,
int64_t _gas_left,
int64_t _gas_refund,
const evmc_address& _create_address) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
{
create_address = _create_address;
}
/// Converting constructor from raw evmc_result.
explicit result(evmc_result const& res) noexcept : evmc_result{res} {}
///
/// This object takes ownership of the resources of @p res.
explicit Result(const evmc_result& res) noexcept : evmc_result{res} {}
/// Destructor responsible for automatically releasing attached resources.
~result() noexcept
~Result() noexcept
{
if (release != nullptr)
release(this);
}
/// Move constructor.
result(result&& other) noexcept : evmc_result{other}
Result(Result&& other) noexcept : evmc_result{other}
{
other.release = nullptr; // Disable releasing of the rvalue object.
}
/// Move assignment operator.
///
/// The self-assigment MUST never happen.
/// The self-assignment MUST never happen.
///
/// @param other The other result object.
/// @return The reference to the left-hand side object.
result& operator=(result&& other) noexcept
Result& operator=(Result&& other) noexcept
{
this->~result(); // Release this object.
this->~Result(); // Release this object.
static_cast<evmc_result&>(*this) = other; // Copy data.
other.release = nullptr; // Disable releasing of the rvalue object.
return *this;
@ -448,10 +464,10 @@ public:
size_t buffer_size) const noexcept = 0;
/// @copydoc evmc_host_interface::selfdestruct
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
virtual bool selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
/// @copydoc evmc_host_interface::call
virtual result call(const evmc_message& msg) noexcept = 0;
virtual Result call(const evmc_message& msg) noexcept = 0;
/// @copydoc evmc_host_interface::get_tx_context
virtual evmc_tx_context get_tx_context() const noexcept = 0;
@ -481,7 +497,6 @@ class HostContext : public HostInterface
{
const evmc_host_interface* host = nullptr;
evmc_host_context* context = nullptr;
mutable evmc_tx_context tx_context = {};
public:
/// Default constructor for null Host context.
@ -534,28 +549,18 @@ public:
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
bool selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
host->selfdestruct(context, &addr, &beneficiary);
return host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
Result call(const evmc_message& message) noexcept final
{
return result{host->call(context, &message)};
return Result{host->call(context, &message)};
}
/// @copydoc HostInterface::get_tx_context()
///
/// The implementation caches the received transaction context
/// by assuming that the block timestamp should never be zero.
///
/// @return The cached transaction context.
evmc_tx_context get_tx_context() const noexcept final
{
if (tx_context.block_timestamp == 0)
tx_context = host->get_tx_context(context);
return tx_context;
}
evmc_tx_context get_tx_context() const noexcept final { return host->get_tx_context(context); }
bytes32 get_block_hash(int64_t number) const noexcept final
{
@ -685,18 +690,18 @@ public:
}
/// @copydoc evmc_execute()
result execute(const evmc_host_interface& host,
Result execute(const evmc_host_interface& host,
evmc_host_context* ctx,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
return Result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
}
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
result execute(Host& host,
Result execute(Host& host,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
@ -713,12 +718,12 @@ public:
/// but without providing the Host context and interface.
/// This method is for experimental precompiles support where execution is
/// guaranteed not to require any Host access.
result execute(evmc_revision rev,
Result execute(evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{
return Result{
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
}
@ -789,11 +794,11 @@ inline size_t copy_code(evmc_host_context* h,
return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
}
inline void selfdestruct(evmc_host_context* h,
inline bool selfdestruct(evmc_host_context* h,
const evmc_address* addr,
const evmc_address* beneficiary) noexcept
{
Host::from_context(h)->selfdestruct(*addr, *beneficiary);
return Host::from_context(h)->selfdestruct(*addr, *beneficiary);
}
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
@ -837,7 +842,7 @@ inline evmc_access_status access_storage(evmc_host_context* h,
inline const evmc_host_interface& Host::get_interface() noexcept
{
static constexpr evmc_host_interface interface{
static constexpr evmc_host_interface interface = {
::evmc::internal::account_exists, ::evmc::internal::get_storage,
::evmc::internal::set_storage, ::evmc::internal::get_balance,
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
@ -851,6 +856,24 @@ inline const evmc_host_interface& Host::get_interface() noexcept
} // namespace evmc
/// "Stream out" operator implementation for ::evmc_status_code.
///
/// @note This is defined in global namespace to match ::evmc_status_code definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_status_code status_code)
{
return os << evmc::to_string(status_code);
}
/// "Stream out" operator implementation for ::evmc_revision.
///
/// @note This is defined in global namespace to match ::evmc_revision definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_revision rev)
{
return os << evmc::to_string(rev);
}
namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.

View File

@ -0,0 +1,105 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2022 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <iterator>
namespace evmc
{
/// The constexpr variant of std::isspace().
inline constexpr bool isspace(char ch) noexcept
{
// Implementation taken from LLVM's libc.
return ch == ' ' || (static_cast<unsigned>(ch) - '\t') < 5;
}
/// Checks if a character is not a white space.
inline constexpr bool is_not_space(char ch) noexcept
{
return !isspace(ch);
}
/// The filter iterator adaptor creates a view of an iterator range in which some elements of the
/// range are skipped. A predicate function controls which elements are skipped. When the predicate
/// is applied to an element, if it returns true then the element is retained and if it returns
/// false then the element is skipped over. When skipping over elements, it is necessary for the
/// filter adaptor to know when to stop so as to avoid going past the end of the underlying range.
/// A filter iterator is therefore constructed with pair of iterators indicating the range of
/// elements in the unfiltered sequence to be traversed.
///
/// Similar to boost::filter_iterator.
template <typename BaseIterator,
bool predicate(typename std::iterator_traits<BaseIterator>::value_type) noexcept>
struct filter_iterator
{
/// The iterator difference type.
using difference_type = typename std::iterator_traits<BaseIterator>::difference_type;
/// The iterator value type.
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
/// The iterator pointer type.
using pointer = typename std::iterator_traits<BaseIterator>::pointer;
/// The iterator reference type.
using reference = typename std::iterator_traits<BaseIterator>::reference;
/// The iterator category.
using iterator_category = std::input_iterator_tag;
private:
BaseIterator base;
BaseIterator base_end;
value_type value;
constexpr void forward_to_next_value() noexcept
{
for (; base != base_end; ++base)
{
value = *base;
if (predicate(value))
break;
}
}
public:
/// The constructor of the base iterator pair.
constexpr filter_iterator(BaseIterator it, BaseIterator end) noexcept : base{it}, base_end{end}
{
forward_to_next_value();
}
/// The dereference operator.
constexpr auto operator*() noexcept
{
// We should not read from an input base iterator twice. So the only read is in
// forward_to_next_value() and here we return the cached value.
return value;
}
/// The increment operator.
constexpr void operator++() noexcept
{
++base;
forward_to_next_value();
}
/// The equality operator.
constexpr bool operator==(const filter_iterator& o) const noexcept { return base == o.base; }
/// The inequality operator.
constexpr bool operator!=(const filter_iterator& o) const noexcept { return base != o.base; }
};
/// The input filter iterator which skips whitespace characters from the base input iterator.
template <typename BaseIterator>
struct skip_space_iterator : filter_iterator<BaseIterator, is_not_space>
{
using filter_iterator<BaseIterator, is_not_space>::filter_iterator;
};
/// Class template argument deduction guide.
template <typename BaseIterator>
skip_space_iterator(BaseIterator, BaseIterator) -> skip_space_iterator<BaseIterator>;
} // namespace evmc

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
/**
* EVMC Helpers
@ -10,8 +9,6 @@
* These are convenient for languages where invoking function pointers
* is "ugly" or impossible (such as Go).
*
* It also contains helpers (overloaded operators) for using EVMC types effectively in C++.
*
* @defgroup helpers EVMC Helpers
* @{
*/
@ -21,6 +18,12 @@
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
/**
* Returns true if the VM has a compatible ABI version.
*/
@ -116,10 +119,12 @@ static void evmc_free_result_memory(const struct evmc_result* result)
///
/// @param status_code The status code.
/// @param gas_left The amount of gas left.
/// @param gas_refund The amount of refunded gas.
/// @param output_data The pointer to the output.
/// @param output_size The output size.
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
int64_t gas_left,
int64_t gas_refund,
const uint8_t* output_data,
size_t output_size)
{
@ -144,6 +149,7 @@ static inline struct evmc_result evmc_make_result(enum evmc_status_code status_c
result.status_code = status_code;
result.gas_left = gas_left;
result.gas_refund = gas_refund;
return result;
}
@ -209,4 +215,95 @@ static inline const union evmc_result_optional_storage* evmc_get_const_optional_
/** @} */
/** Returns text representation of the ::evmc_status_code. */
static inline const char* evmc_status_code_to_string(enum evmc_status_code status_code)
{
switch (status_code)
{
case EVMC_SUCCESS:
return "success";
case EVMC_FAILURE:
return "failure";
case EVMC_REVERT:
return "revert";
case EVMC_OUT_OF_GAS:
return "out of gas";
case EVMC_INVALID_INSTRUCTION:
return "invalid instruction";
case EVMC_UNDEFINED_INSTRUCTION:
return "undefined instruction";
case EVMC_STACK_OVERFLOW:
return "stack overflow";
case EVMC_STACK_UNDERFLOW:
return "stack underflow";
case EVMC_BAD_JUMP_DESTINATION:
return "bad jump destination";
case EVMC_INVALID_MEMORY_ACCESS:
return "invalid memory access";
case EVMC_CALL_DEPTH_EXCEEDED:
return "call depth exceeded";
case EVMC_STATIC_MODE_VIOLATION:
return "static mode violation";
case EVMC_PRECOMPILE_FAILURE:
return "precompile failure";
case EVMC_CONTRACT_VALIDATION_FAILURE:
return "contract validation failure";
case EVMC_ARGUMENT_OUT_OF_RANGE:
return "argument out of range";
case EVMC_WASM_UNREACHABLE_INSTRUCTION:
return "wasm unreachable instruction";
case EVMC_WASM_TRAP:
return "wasm trap";
case EVMC_INSUFFICIENT_BALANCE:
return "insufficient balance";
case EVMC_INTERNAL_ERROR:
return "internal error";
case EVMC_REJECTED:
return "rejected";
case EVMC_OUT_OF_MEMORY:
return "out of memory";
}
return "<unknown>";
}
/** Returns the name of the ::evmc_revision. */
static inline const char* evmc_revision_to_string(enum evmc_revision rev)
{
switch (rev)
{
case EVMC_FRONTIER:
return "Frontier";
case EVMC_HOMESTEAD:
return "Homestead";
case EVMC_TANGERINE_WHISTLE:
return "Tangerine Whistle";
case EVMC_SPURIOUS_DRAGON:
return "Spurious Dragon";
case EVMC_BYZANTIUM:
return "Byzantium";
case EVMC_CONSTANTINOPLE:
return "Constantinople";
case EVMC_PETERSBURG:
return "Petersburg";
case EVMC_ISTANBUL:
return "Istanbul";
case EVMC_BERLIN:
return "Berlin";
case EVMC_LONDON:
return "London";
case EVMC_PARIS:
return "Paris";
case EVMC_SHANGHAI:
return "Shanghai";
case EVMC_CANCUN:
return "Cancun";
}
return "<unknown>";
}
/** @} */
#ifdef __cplusplus
#pragma GCC diagnostic pop
} // extern "C"
#endif

158
test/evmc/hex.hpp Normal file
View File

@ -0,0 +1,158 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2021 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <evmc/filter_iterator.hpp>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
namespace evmc
{
/// String of uint8_t chars.
using bytes = std::basic_string<uint8_t>;
/// String view of uint8_t chars.
using bytes_view = std::basic_string_view<uint8_t>;
/// Encode a byte to a hex string.
inline std::string hex(uint8_t b) noexcept
{
static constexpr auto hex_digits = "0123456789abcdef";
return {hex_digits[b >> 4], hex_digits[b & 0xf]};
}
/// Encodes bytes as hex string.
inline std::string hex(bytes_view bs)
{
std::string str;
str.reserve(bs.size() * 2);
for (const auto b : bs)
str += hex(b);
return str;
}
namespace internal
{
/// Extracts the nibble value out of a hex digit.
/// Returns -1 in case of invalid hex digit.
inline constexpr int from_hex_digit(char h) noexcept
{
if (h >= '0' && h <= '9')
return h - '0';
else if (h >= 'a' && h <= 'f')
return h - 'a' + 10;
else if (h >= 'A' && h <= 'F')
return h - 'A' + 10;
else
return -1;
}
} // namespace internal
/// Decodes hex-encoded sequence of characters.
///
/// It is guaranteed that the output will not be longer than half of the input length.
///
/// @param begin The input begin iterator. It only must satisfy input iterator concept.
/// @param end The input end iterator. It only must satisfy input iterator concept.
/// @param out The output iterator. It must satisfy output iterator concept.
/// @return True if successful, false if input is invalid hex.
template <typename InputIt, typename OutputIt>
inline constexpr bool from_hex(InputIt begin, InputIt end, OutputIt out) noexcept
{
int hi_nibble = -1; // Init with invalid value, should never be used.
size_t i = 0;
for (auto it = begin; it != end; ++it, ++i)
{
const auto h = *it;
const int v = evmc::internal::from_hex_digit(h);
if (v < 0)
{
if (i == 1 && hi_nibble == 0 && h == 'x') // 0x prefix
continue;
return false;
}
if (i % 2 == 0)
hi_nibble = v << 4;
else
*out++ = static_cast<uint8_t>(hi_nibble | v);
}
return i % 2 == 0;
}
/// Validates hex encoded string.
///
/// @return True if the input is valid hex.
inline bool validate_hex(std::string_view hex) noexcept
{
struct noop_output_iterator
{
uint8_t sink = {};
uint8_t& operator*() noexcept { return sink; }
noop_output_iterator operator++(int) noexcept { return *this; } // NOLINT(cert-dcl21-cpp)
};
return from_hex(hex.begin(), hex.end(), noop_output_iterator{});
}
/// Decodes hex encoded string to bytes.
///
/// In case the input is invalid the returned value is std::nullopt.
/// This can happen if a non-hex digit or odd number of digits is encountered.
inline std::optional<bytes> from_hex(std::string_view hex)
{
bytes bs;
bs.reserve(hex.size() / 2);
if (!from_hex(hex.begin(), hex.end(), std::back_inserter(bs)))
return {};
return bs;
}
/// Decodes hex-encoded string into custom type T with .bytes array of uint8_t.
///
/// When the input is smaller than the result type, the result is padded with zeros on the left
/// (the result bytes of lowest indices are filled with zeros).
/// TODO: Support optional left alignment.
template <typename T>
constexpr std::optional<T> from_hex(std::string_view s) noexcept
{
// Omit the optional 0x prefix.
if (s.size() >= 2 && s[0] == '0' && s[1] == 'x')
s.remove_prefix(2);
T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available.
constexpr auto num_out_bytes = std::size(r.bytes);
const auto num_in_bytes = s.length() / 2;
if (num_in_bytes > num_out_bytes)
return {};
if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes]))
return {};
return r;
}
/// Decodes hex encoded string to bytes. The whitespace in the input is ignored.
///
/// In case the input is invalid the returned value is std::nullopt.
/// This can happen if a non-hex digit or odd number of digits is encountered.
/// The whitespace (as defined by std::isspace) in the input is ignored.
template <typename InputIterator>
std::optional<bytes> from_spaced_hex(InputIterator begin, InputIterator end) noexcept
{
bytes bs;
if (!from_hex(skip_space_iterator{begin, end}, skip_space_iterator{end, end},
std::back_inserter(bs)))
return {};
return bs;
}
/// @copydoc from_spaced_hex
inline std::optional<bytes> from_spaced_hex(std::string_view hex) noexcept
{
return from_spaced_hex(hex.begin(), hex.end());
}
} // namespace evmc

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#include <evmc/loader.h>
@ -27,6 +26,7 @@
#define DLL_HANDLE void*
#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY)
#define DLL_CLOSE(handle) dlclose(handle)
// NOLINTNEXTLINE(performance-no-int-to-ptr)
#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name)
#define DLL_GET_ERROR_MSG() dlerror()
#endif
@ -60,17 +60,20 @@ static
dest[0] = 0;
return 1;
}
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
memcpy(dest, src, len);
dest[len] = 0;
return 0;
}
#define PATH_MAX_LENGTH 4096
enum
{
PATH_MAX_LENGTH = 4096,
LAST_ERROR_MSG_BUFFER_SIZE = 511
};
static const char* last_error_msg = NULL;
#define LAST_ERROR_MSG_BUFFER_SIZE 511
// Buffer for formatted error messages.
// It has one null byte extra to avoid buffer read overflow during concurrent access.
static char last_error_msg_buffer[LAST_ERROR_MSG_BUFFER_SIZE + 1];
@ -82,6 +85,7 @@ static enum evmc_loader_error_code set_error(enum evmc_loader_error_code error_c
{
va_list args;
va_start(args, format);
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) <
LAST_ERROR_MSG_BUFFER_SIZE)
last_error_msg = last_error_msg_buffer;
@ -181,7 +185,7 @@ exit:
return create_fn;
}
const char* evmc_last_error_msg()
const char* evmc_last_error_msg(void)
{
const char* m = last_error_msg;
last_error_msg = NULL;
@ -267,16 +271,15 @@ struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_err
if (!vm)
return NULL;
if (vm->set_option == NULL && strlen(options) != 0)
{
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
vm->name, path);
goto exit;
}
while (strlen(options) != 0)
{
if (vm->set_option == NULL)
{
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
vm->name, path);
goto exit;
}
char* option = get_token(&options, ',');
// Slit option into name and value by taking the name token.

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
/**
* EVMC Loader Library
@ -21,7 +20,10 @@ extern "C" {
/** The function pointer type for EVMC create functions. */
typedef struct evmc_vm* (*evmc_create_fn)(void);
/** Error codes for the EVMC loader. */
/// Error codes for the EVMC loader.
///
/// Objects of this type SHOULD be initialized with ::EVMC_LOADER_UNSPECIFIED_ERROR
/// before passing to the EVMC loader.
enum evmc_loader_error_code
{
/** The loader succeeded. */
@ -46,7 +48,11 @@ enum evmc_loader_error_code
EVMC_LOADER_INVALID_OPTION_NAME = 6,
/** The VM option value is invalid. */
EVMC_LOADER_INVALID_OPTION_VALUE = 7
EVMC_LOADER_INVALID_OPTION_VALUE = 7,
/// This error value will be never returned by the EVMC loader,
/// but can be used by users to init evmc_loader_error_code objects.
EVMC_LOADER_UNSPECIFIED_ERROR = -1
};
/**

View File

@ -5,6 +5,7 @@
#include <evmc/evmc.hpp>
#include <algorithm>
#include <cassert>
#include <string>
#include <map>
#include <unordered_map>
@ -16,7 +17,7 @@ namespace evmc
using bytes = std::basic_string<uint8_t>;
/// Extended value (with original value and access flag) for account storage.
struct storage_value
struct StorageValue
{
/// The current storage value.
bytes32 current;
@ -28,7 +29,20 @@ struct storage_value
evmc_access_status access_status = EVMC_ACCESS_COLD;
/// Default constructor.
storage_value() noexcept = default;
StorageValue() noexcept = default;
/// Constructor sets the current and original to the same value. Optional access status.
StorageValue(const bytes32& _value, // NOLINT(hicpp-explicit-conversions)
evmc_access_status _access_status = EVMC_ACCESS_COLD) noexcept
: current{_value}, original{_value}, access_status{_access_status}
{}
/// Constructor with original value and optional access status
StorageValue(const bytes32& _value,
const bytes32& _original,
evmc_access_status _access_status = EVMC_ACCESS_COLD) noexcept
: current{_value}, original{_original}, access_status{_access_status}
{}
};
/// Mocked account.
@ -47,7 +61,7 @@ struct MockedAccount
uint256be balance;
/// The account storage map.
std::map<bytes32, storage_value> storage;
std::map<bytes32, StorageValue> storage;
/// Helper method for setting balance by numeric type.
void set_balance(uint64_t x) noexcept
@ -81,22 +95,6 @@ public:
}
};
/// SELFDESTRUCT record.
struct selfdestruct_record
{
/// The address of the account which has self-destructed.
address selfdestructed;
/// The address of the beneficiary account.
address beneficiary;
/// Equal operator.
bool operator==(const selfdestruct_record& other) const noexcept
{
return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary;
}
};
/// The set of all accounts in the Host, organized by their addresses.
std::unordered_map<address, MockedAccount> accounts;
@ -129,8 +127,9 @@ public:
/// The record of all LOGs passed to the emit_log() method.
std::vector<log_record> recorded_logs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method.
std::vector<selfdestruct_record> recorded_selfdestructs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method
/// as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].
std::unordered_map<address, std::vector<address>> recorded_selfdestructs;
private:
/// The copy of call inputs for the recorded_calls record.
@ -176,32 +175,142 @@ public:
const bytes32& value) noexcept override
{
record_account_access(addr);
const auto it = accounts.find(addr);
if (it == accounts.end())
return EVMC_STORAGE_UNCHANGED;
auto& old = it->second.storage[key];
// Get the reference to the storage entry value.
// This will create the account in case it was not present.
// This is convenient for unit testing and standalone EVM execution to preserve the
// storage values after the execution terminates.
auto& s = accounts[addr].storage[key];
// Follow https://eips.ethereum.org/EIPS/eip-1283 specification.
// WARNING! This is not complete implementation as refund is not handled here.
// Follow the EIP-2200 specification as closely as possible.
// https://eips.ethereum.org/EIPS/eip-2200
// Warning: this is not the most efficient implementation. The storage status can be
// figured out by combining only 4 checks:
// - original != current (dirty)
// - original == value (restored)
// - current != 0
// - value != 0
const auto status = [&original = s.original, &current = s.current, &value]() {
// Clause 1 is irrelevant:
// 1. "If gasleft is less than or equal to gas stipend,
// fail the current call frame with out of gas exception"
if (old.current == value)
return EVMC_STORAGE_UNCHANGED;
evmc_storage_status status{};
if (old.original == old.current) // Storage slot not dirty
{
if (!old.current)
status = EVMC_STORAGE_ADDED;
else if (value)
status = EVMC_STORAGE_MODIFIED;
// 2. "If current value equals new value (this is a no-op)"
if (current == value)
{
// "SLOAD_GAS is deducted"
return EVMC_STORAGE_ASSIGNED;
}
// 3. "If current value does not equal new value"
else
status = EVMC_STORAGE_DELETED;
}
else
status = EVMC_STORAGE_MODIFIED_AGAIN;
{
// 3.1. "If original value equals current value
// (this storage slot has not been changed by the current execution context)"
if (original == current)
{
// 3.1.1 "If original value is 0"
if (is_zero(original))
{
// "SSTORE_SET_GAS is deducted"
return EVMC_STORAGE_ADDED;
}
// 3.1.2 "Otherwise"
else
{
// "SSTORE_RESET_GAS gas is deducted"
auto st = EVMC_STORAGE_MODIFIED;
old.current = value;
// "If new value is 0"
if (is_zero(value))
{
// "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
st = EVMC_STORAGE_DELETED;
}
return st;
}
}
// 3.2. "If original value does not equal current value
// (this storage slot is dirty),
// SLOAD_GAS gas is deducted.
// Apply both of the following clauses."
else
{
// Because we need to apply "both following clauses"
// we first collect information which clause is triggered
// then assign status code to combination of these clauses.
enum
{
None = 0,
RemoveClearsSchedule = 1 << 0,
AddClearsSchedule = 1 << 1,
RestoredBySet = 1 << 2,
RestoredByReset = 1 << 3,
};
int triggered_clauses = None;
// 3.2.1. "If original value is not 0"
if (!is_zero(original))
{
// 3.2.1.1. "If current value is 0"
if (is_zero(current))
{
// "(also means that new value is not 0)"
assert(!is_zero(value));
// "remove SSTORE_CLEARS_SCHEDULE gas from refund counter"
triggered_clauses |= RemoveClearsSchedule;
}
// 3.2.1.2. "If new value is 0"
if (is_zero(value))
{
// "(also means that current value is not 0)"
assert(!is_zero(current));
// "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
triggered_clauses |= AddClearsSchedule;
}
}
// 3.2.2. "If original value equals new value (this storage slot is reset)"
// Except: we use term 'storage slot restored'.
if (original == value)
{
// 3.2.2.1. "If original value is 0"
if (is_zero(original))
{
// "add SSTORE_SET_GAS - SLOAD_GAS to refund counter"
triggered_clauses |= RestoredBySet;
}
// 3.2.2.2. "Otherwise"
else
{
// "add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter"
triggered_clauses |= RestoredByReset;
}
}
switch (triggered_clauses)
{
case RemoveClearsSchedule:
return EVMC_STORAGE_DELETED_ADDED;
case AddClearsSchedule:
return EVMC_STORAGE_MODIFIED_DELETED;
case RemoveClearsSchedule | RestoredByReset:
return EVMC_STORAGE_DELETED_RESTORED;
case RestoredBySet:
return EVMC_STORAGE_ADDED_DELETED;
case RestoredByReset:
return EVMC_STORAGE_MODIFIED_RESTORED;
case None:
return EVMC_STORAGE_ASSIGNED;
default:
assert(false); // Other combinations are impossible.
return evmc_storage_status{};
}
}
}
}();
s.current = value; // Finally update the current storage value.
return status;
}
@ -260,16 +369,18 @@ public:
}
/// Selfdestruct the account (EVMC host method).
void selfdestruct(const address& addr, const address& beneficiary) noexcept override
bool selfdestruct(const address& addr, const address& beneficiary) noexcept override
{
record_account_access(addr);
recorded_selfdestructs.push_back({addr, beneficiary});
auto& beneficiaries = recorded_selfdestructs[addr];
beneficiaries.emplace_back(beneficiary);
return beneficiaries.size() == 1;
}
/// Call/create other contract (EVMC host method).
result call(const evmc_message& msg) noexcept override
Result call(const evmc_message& msg) noexcept override
{
record_account_access(msg.destination);
record_account_access(msg.recipient);
if (recorded_calls.empty())
{
@ -288,7 +399,7 @@ public:
call_msg.input_data = input_copy.data();
}
}
return result{call_result};
return Result{call_result};
}
/// Get transaction context (EVMC host method).
@ -316,11 +427,11 @@ public:
/// 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
/// 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.destination);
/// mocked_host.access_account(msg.recipient);
///
/// The same way you can mock transaction access list (EIP-2930) for account addresses.
///
@ -346,16 +457,18 @@ public:
/// 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:
/// 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};
/// mocked_host.accounts[msg.recipient].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,
/// @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
{

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once