mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Upgrade to evmc 10.0.0
This commit is contained in:
parent
5c139b60b8
commit
458857d0d6
@ -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.
|
||||
|
217
test/evmc/evmc.h
217
test/evmc/evmc.h
@ -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
|
||||
|
@ -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.
|
||||
|
105
test/evmc/filter_iterator.hpp
Normal file
105
test/evmc/filter_iterator.hpp
Normal 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
|
@ -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
158
test/evmc/hex.hpp
Normal 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
|
@ -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.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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, ¤t = 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
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user