#pragma once
#include <evmc/evmc.h>
#include <evmc/helpers.h>
#include <functional>
#include <initializer_list>
#include <utility>
namespace evmc
{
struct address : evmc_address
{
constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {}
constexpr explicit address(uint64_t v) noexcept
: evmc_address{{0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
static_cast<uint8_t>(v >> 56),
static_cast<uint8_t>(v >> 48),
static_cast<uint8_t>(v >> 40),
static_cast<uint8_t>(v >> 32),
static_cast<uint8_t>(v >> 24),
static_cast<uint8_t>(v >> 16),
static_cast<uint8_t>(v >> 8),
static_cast<uint8_t>(v >> 0)}}
{}
inline constexpr explicit operator bool() const noexcept;
};
struct bytes32 : evmc_bytes32
{
constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {}
constexpr explicit bytes32(uint64_t v) noexcept
: evmc_bytes32{{0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
static_cast<uint8_t>(v >> 56),
static_cast<uint8_t>(v >> 48),
static_cast<uint8_t>(v >> 40),
static_cast<uint8_t>(v >> 32),
static_cast<uint8_t>(v >> 24),
static_cast<uint8_t>(v >> 16),
static_cast<uint8_t>(v >> 8),
static_cast<uint8_t>(v >> 0)}}
{}
constexpr inline explicit operator bool() const noexcept;
};
using uint256be = bytes32;
inline constexpr uint64_t load64be(const uint8_t* data) noexcept
{
return (uint64_t{data[0]} << 56) | (uint64_t{data[1]} << 48) | (uint64_t{data[2]} << 40) |
(uint64_t{data[3]} << 32) | (uint64_t{data[4]} << 24) | (uint64_t{data[5]} << 16) |
(uint64_t{data[6]} << 8) | uint64_t{data[7]};
}
inline constexpr uint64_t load64le(const uint8_t* data) noexcept
{
return uint64_t{data[0]} | (uint64_t{data[1]} << 8) | (uint64_t{data[2]} << 16) |
(uint64_t{data[3]} << 24) | (uint64_t{data[4]} << 32) | (uint64_t{data[5]} << 40) |
(uint64_t{data[6]} << 48) | (uint64_t{data[7]} << 56);
}
inline constexpr uint32_t load32be(const uint8_t* data) noexcept
{
return (uint32_t{data[0]} << 24) | (uint32_t{data[1]} << 16) | (uint32_t{data[2]} << 8) |
uint32_t{data[3]};
}
inline constexpr uint32_t load32le(const uint8_t* data) noexcept
{
return uint32_t{data[0]} | (uint32_t{data[1]} << 8) | (uint32_t{data[2]} << 16) |
(uint32_t{data[3]} << 24);
}
namespace fnv
{
constexpr auto prime = 0x100000001b3; constexpr auto offset_basis = 0xcbf29ce484222325;
inline constexpr uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept
{
return (h ^ x) * prime;
}
}
inline constexpr bool operator==(const address& a, const address& b) noexcept
{
return load64le(&a.bytes[0]) == load64le(&b.bytes[0]) &&
load64le(&a.bytes[8]) == load64le(&b.bytes[8]) &&
load32le(&a.bytes[16]) == load32le(&b.bytes[16]);
}
inline constexpr bool operator!=(const address& a, const address& b) noexcept
{
return !(a == b);
}
inline constexpr bool operator<(const address& a, const address& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
(load64be(&a.bytes[8]) < load64be(&b.bytes[8]) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) < load32be(&b.bytes[16]))));
}
inline constexpr bool operator>(const address& a, const address& b) noexcept
{
return b < a;
}
inline constexpr bool operator<=(const address& a, const address& b) noexcept
{
return !(b < a);
}
inline constexpr bool operator>=(const address& a, const address& b) noexcept
{
return !(a < b);
}
inline constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept
{
return load64le(&a.bytes[0]) == load64le(&b.bytes[0]) &&
load64le(&a.bytes[8]) == load64le(&b.bytes[8]) &&
load64le(&a.bytes[16]) == load64le(&b.bytes[16]) &&
load64le(&a.bytes[24]) == load64le(&b.bytes[24]);
}
inline constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept
{
return !(a == b);
}
inline constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
(load64be(&a.bytes[8]) < load64be(&b.bytes[8]) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
(load64be(&a.bytes[16]) < load64be(&b.bytes[16]) ||
(load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) < load64be(&b.bytes[24]))))));
}
inline constexpr bool operator>(const bytes32& a, const bytes32& b) noexcept
{
return b < a;
}
inline constexpr bool operator<=(const bytes32& a, const bytes32& b) noexcept
{
return !(b < a);
}
inline constexpr bool operator>=(const bytes32& a, const bytes32& b) noexcept
{
return !(a < b);
}
inline constexpr bool is_zero(const address& a) noexcept
{
return a == address{};
}
inline constexpr address::operator bool() const noexcept
{
return !is_zero(*this);
}
inline constexpr bool is_zero(const bytes32& a) noexcept
{
return a == bytes32{};
}
inline constexpr bytes32::operator bool() const noexcept
{
return !is_zero(*this);
}
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]));
}
template <typename T>
T from_hex(const char*) noexcept;
template <>
constexpr bytes32 from_hex<bytes32>(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), 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)}}};
}
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]);
}
}
template <char... c>
constexpr address operator""_address() noexcept
{
return internal::from_literal<address, c...>();
}
template <char... c>
constexpr bytes32 operator""_bytes32() noexcept
{
return internal::from_literal<bytes32, c...>();
}
}
using namespace literals;
constexpr auto make_result = evmc_make_result;
class result : private evmc_result
{
public:
using evmc_result::create_address;
using evmc_result::gas_left;
using evmc_result::output_data;
using evmc_result::output_size;
using evmc_result::status_code;
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_result const& res) noexcept : evmc_result{res} {}
~result() noexcept
{
if (release != nullptr)
release(this);
}
result(result&& other) noexcept : evmc_result{other}
{
other.release = nullptr; }
result& operator=(result&& other) noexcept
{
this->~result(); static_cast<evmc_result&>(*this) = other; other.release = nullptr; return *this;
}
evmc_result release_raw() noexcept
{
const auto out = evmc_result{*this}; this->release = nullptr; return out;
}
};
class HostInterface
{
public:
virtual ~HostInterface() noexcept = default;
virtual bool account_exists(const address& addr) const noexcept = 0;
virtual bytes32 get_storage(const address& addr, const bytes32& key) const noexcept = 0;
virtual evmc_storage_status set_storage(const address& addr,
const bytes32& key,
const bytes32& value) noexcept = 0;
virtual uint256be get_balance(const address& addr) const noexcept = 0;
virtual size_t get_code_size(const address& addr) const noexcept = 0;
virtual bytes32 get_code_hash(const address& addr) const noexcept = 0;
virtual size_t copy_code(const address& addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) const noexcept = 0;
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
virtual result call(const evmc_message& msg) noexcept = 0;
virtual evmc_tx_context get_tx_context() const noexcept = 0;
virtual bytes32 get_block_hash(int64_t block_number) const noexcept = 0;
virtual void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const bytes32 topics[],
size_t num_topics) noexcept = 0;
virtual evmc_access_status access_account(const address& addr) noexcept = 0;
virtual evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept = 0;
};
class HostContext : public HostInterface
{
const evmc_host_interface* host = nullptr;
evmc_host_context* context = nullptr;
mutable evmc_tx_context tx_context = {};
public:
HostContext() = default;
HostContext(const evmc_host_interface& interface, evmc_host_context* ctx) noexcept
: host{&interface}, context{ctx}
{}
bool account_exists(const address& address) const noexcept final
{
return host->account_exists(context, &address);
}
bytes32 get_storage(const address& address, const bytes32& key) const noexcept final
{
return host->get_storage(context, &address, &key);
}
evmc_storage_status set_storage(const address& address,
const bytes32& key,
const bytes32& value) noexcept final
{
return host->set_storage(context, &address, &key, &value);
}
uint256be get_balance(const address& address) const noexcept final
{
return host->get_balance(context, &address);
}
size_t get_code_size(const address& address) const noexcept final
{
return host->get_code_size(context, &address);
}
bytes32 get_code_hash(const address& address) const noexcept final
{
return host->get_code_hash(context, &address);
}
size_t copy_code(const address& address,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) const noexcept final
{
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
{
return result{host->call(context, &message)};
}
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;
}
bytes32 get_block_hash(int64_t number) const noexcept final
{
return host->get_block_hash(context, number);
}
void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const bytes32 topics[],
size_t topics_count) noexcept final
{
host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
evmc_access_status access_account(const address& address) noexcept final
{
return host->access_account(context, &address);
}
evmc_access_status access_storage(const address& address, const bytes32& key) noexcept final
{
return host->access_storage(context, &address, &key);
}
};
class Host : public HostInterface
{
public:
static const evmc_host_interface& get_interface() noexcept;
evmc_host_context* to_context() noexcept { return reinterpret_cast<evmc_host_context*>(this); }
template <typename DerivedClass = Host>
static DerivedClass* from_context(evmc_host_context* context) noexcept
{
auto* h = reinterpret_cast<Host*>(context);
return static_cast<DerivedClass*>(h);
}
};
class VM
{
public:
VM() noexcept = default;
explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {}
~VM() noexcept
{
if (m_instance != nullptr)
m_instance->destroy(m_instance);
}
VM(const VM&) = delete;
VM& operator=(const VM&) = delete;
VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
VM& operator=(VM&& other) noexcept
{
this->~VM();
m_instance = other.m_instance;
other.m_instance = nullptr;
return *this;
}
inline VM(evmc_vm* vm,
std::initializer_list<std::pair<const char*, const char*>> options) noexcept;
explicit operator bool() const noexcept { return m_instance != nullptr; }
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
char const* name() const noexcept { return m_instance->name; }
char const* version() const noexcept { return m_instance->version; }
bool has_capability(evmc_capabilities capability) const noexcept
{
return (get_capabilities() & static_cast<evmc_capabilities_flagset>(capability)) != 0;
}
evmc_capabilities_flagset get_capabilities() const noexcept
{
return m_instance->get_capabilities(m_instance);
}
evmc_set_option_result set_option(const char name[], const char value[]) noexcept
{
return evmc_set_option(m_instance, name, value);
}
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)};
}
result execute(Host& host,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size);
}
result execute(evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
}
evmc_vm* get_raw_pointer() const noexcept { return m_instance; }
private:
evmc_vm* m_instance = nullptr;
};
inline VM::VM(evmc_vm* vm,
std::initializer_list<std::pair<const char*, const char*>> options) noexcept
: m_instance{vm}
{
for (const auto& option : options)
set_option(option.first, option.second);
}
namespace internal
{
inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->account_exists(*addr);
}
inline evmc_bytes32 get_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key) noexcept
{
return Host::from_context(h)->get_storage(*addr, *key);
}
inline evmc_storage_status set_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key,
const evmc_bytes32* value) noexcept
{
return Host::from_context(h)->set_storage(*addr, *key, *value);
}
inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_balance(*addr);
}
inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_code_size(*addr);
}
inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_code_hash(*addr);
}
inline size_t copy_code(evmc_host_context* h,
const evmc_address* addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) noexcept
{
return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
}
inline void selfdestruct(evmc_host_context* h,
const evmc_address* addr,
const evmc_address* beneficiary) noexcept
{
Host::from_context(h)->selfdestruct(*addr, *beneficiary);
}
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
{
return Host::from_context(h)->call(*msg).release_raw();
}
inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept
{
return Host::from_context(h)->get_tx_context();
}
inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept
{
return Host::from_context(h)->get_block_hash(block_number);
}
inline void emit_log(evmc_host_context* h,
const evmc_address* addr,
const uint8_t* data,
size_t data_size,
const evmc_bytes32 topics[],
size_t num_topics) noexcept
{
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
num_topics);
}
inline evmc_access_status access_account(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->access_account(*addr);
}
inline evmc_access_status access_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key) noexcept
{
return Host::from_context(h)->access_storage(*addr, *key);
}
}
inline const evmc_host_interface& Host::get_interface() noexcept
{
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,
::evmc::internal::copy_code, ::evmc::internal::selfdestruct,
::evmc::internal::call, ::evmc::internal::get_tx_context,
::evmc::internal::get_block_hash, ::evmc::internal::emit_log,
::evmc::internal::access_account, ::evmc::internal::access_storage,
};
return interface;
}
}
namespace std
{
template <>
struct hash<evmc::address>
{
constexpr size_t operator()(const evmc::address& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(fnv1a_by64(
fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64le(&s.bytes[0])), load64le(&s.bytes[8])),
load32le(&s.bytes[16])));
}
};
template <>
struct hash<evmc::bytes32>
{
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64le(&s.bytes[0])),
load64le(&s.bytes[8])),
load64le(&s.bytes[16])),
load64le(&s.bytes[24])));
}
};
}