mirror of https://github.com/status-im/evmc.git
commit
1bb7ccd7a4
|
@ -334,118 +334,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class Host;
|
||||
|
||||
/// @copybrief evmc_vm
|
||||
///
|
||||
/// This is a RAII wrapper for evmc_vm and objects of this type
|
||||
/// automatically destroys the VM instance.
|
||||
class VM
|
||||
{
|
||||
public:
|
||||
VM() noexcept = default;
|
||||
|
||||
/// Converting constructor from evmc_vm.
|
||||
explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {}
|
||||
|
||||
/// Destructor responsible for automatically destroying the VM instance.
|
||||
~VM() noexcept
|
||||
{
|
||||
if (m_instance)
|
||||
m_instance->destroy(m_instance);
|
||||
}
|
||||
|
||||
VM(const VM&) = delete;
|
||||
VM& operator=(const VM&) = delete;
|
||||
|
||||
/// Move constructor.
|
||||
VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
|
||||
|
||||
/// Move assignment operator.
|
||||
VM& operator=(VM&& other) noexcept
|
||||
{
|
||||
this->~VM();
|
||||
m_instance = other.m_instance;
|
||||
other.m_instance = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// The constructor that captures a VM instance and configures the instance
|
||||
/// with the provided list of options.
|
||||
inline VM(evmc_vm* vm,
|
||||
std::initializer_list<std::pair<const char*, const char*>> options) noexcept;
|
||||
|
||||
/// Checks if contains a valid pointer to the VM instance.
|
||||
explicit operator bool() const noexcept { return m_instance != nullptr; }
|
||||
|
||||
/// Checks whenever the VM instance is ABI compatible with the current EVMC API.
|
||||
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
|
||||
|
||||
/// @copydoc evmc_vm::name
|
||||
char const* name() const noexcept { return m_instance->name; }
|
||||
|
||||
/// @copydoc evmc_vm::version
|
||||
char const* version() const noexcept { return m_instance->version; }
|
||||
|
||||
/// @copydoc evmc::vm::get_capabilities
|
||||
evmc_capabilities_flagset get_capabilities() const noexcept
|
||||
{
|
||||
return m_instance->get_capabilities(m_instance);
|
||||
}
|
||||
|
||||
/// @copydoc evmc_set_option()
|
||||
evmc_set_option_result set_option(const char name[], const char value[]) noexcept
|
||||
{
|
||||
return evmc_set_option(m_instance, name, value);
|
||||
}
|
||||
|
||||
/// @copydoc evmc_execute()
|
||||
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)};
|
||||
}
|
||||
|
||||
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
|
||||
inline result execute(Host& host,
|
||||
evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept;
|
||||
|
||||
/// Executes code without the Host context.
|
||||
///
|
||||
/// The same as
|
||||
/// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision,
|
||||
/// const evmc_message&, const uint8_t*, size_t),
|
||||
/// 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,
|
||||
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)};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// The EVMC Host interface
|
||||
class HostInterface
|
||||
|
@ -596,6 +484,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/// Abstract class to be used by Host implementations.
|
||||
///
|
||||
/// When implementing EVMC Host, you can directly inherit from the evmc::Host class.
|
||||
|
@ -628,13 +517,118 @@ public:
|
|||
};
|
||||
|
||||
|
||||
inline result VM::execute(Host& host,
|
||||
evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept
|
||||
/// @copybrief evmc_vm
|
||||
///
|
||||
/// This is a RAII wrapper for evmc_vm, and object of this type
|
||||
/// automatically destroys the VM instance.
|
||||
class VM
|
||||
{
|
||||
return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size);
|
||||
public:
|
||||
VM() noexcept = default;
|
||||
|
||||
/// Converting constructor from evmc_vm.
|
||||
explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {}
|
||||
|
||||
/// Destructor responsible for automatically destroying the VM instance.
|
||||
~VM() noexcept
|
||||
{
|
||||
if (m_instance)
|
||||
m_instance->destroy(m_instance);
|
||||
}
|
||||
|
||||
VM(const VM&) = delete;
|
||||
VM& operator=(const VM&) = delete;
|
||||
|
||||
/// Move constructor.
|
||||
VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
|
||||
|
||||
/// Move assignment operator.
|
||||
VM& operator=(VM&& other) noexcept
|
||||
{
|
||||
this->~VM();
|
||||
m_instance = other.m_instance;
|
||||
other.m_instance = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// The constructor that captures a VM instance and configures the instance
|
||||
/// with the provided list of options.
|
||||
inline VM(evmc_vm* vm,
|
||||
std::initializer_list<std::pair<const char*, const char*>> options) noexcept;
|
||||
|
||||
/// Checks if contains a valid pointer to the VM instance.
|
||||
explicit operator bool() const noexcept { return m_instance != nullptr; }
|
||||
|
||||
/// Checks whenever the VM instance is ABI compatible with the current EVMC API.
|
||||
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
|
||||
|
||||
/// @copydoc evmc_vm::name
|
||||
char const* name() const noexcept { return m_instance->name; }
|
||||
|
||||
/// @copydoc evmc_vm::version
|
||||
char const* version() const noexcept { return m_instance->version; }
|
||||
|
||||
/// @copydoc evmc::vm::get_capabilities
|
||||
evmc_capabilities_flagset get_capabilities() const noexcept
|
||||
{
|
||||
return m_instance->get_capabilities(m_instance);
|
||||
}
|
||||
|
||||
/// @copydoc evmc_set_option()
|
||||
evmc_set_option_result set_option(const char name[], const char value[]) noexcept
|
||||
{
|
||||
return evmc_set_option(m_instance, name, value);
|
||||
}
|
||||
|
||||
/// @copydoc evmc_execute()
|
||||
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)};
|
||||
}
|
||||
|
||||
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Executes code without the Host context.
|
||||
///
|
||||
/// The same as
|
||||
/// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision,
|
||||
/// const evmc_message&, const uint8_t*, size_t),
|
||||
/// 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,
|
||||
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)};
|
||||
}
|
||||
|
||||
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}
|
||||
{
|
||||
// This constructor is implemented outside of the class definition to workaround a doxygen bug.
|
||||
for (const auto& option : options)
|
||||
set_option(option.first, option.second);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,47 @@
|
|||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
class NullHost : public evmc::Host
|
||||
{
|
||||
public:
|
||||
bool account_exists(const evmc::address&) noexcept final { return false; }
|
||||
|
||||
evmc::bytes32 get_storage(const evmc::address&, const evmc::bytes32&) noexcept final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
evmc_storage_status set_storage(const evmc::address&,
|
||||
const evmc::bytes32&,
|
||||
const evmc::bytes32&) noexcept final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
evmc::uint256be get_balance(const evmc::address&) noexcept final { return {}; }
|
||||
|
||||
size_t get_code_size(const evmc::address&) noexcept final { return 0; }
|
||||
|
||||
evmc::bytes32 get_code_hash(const evmc::address&) noexcept final { return {}; }
|
||||
|
||||
size_t copy_code(const evmc::address&, size_t, uint8_t*, size_t) noexcept final { return 0; }
|
||||
|
||||
void selfdestruct(const evmc::address&, const evmc::address&) noexcept final {}
|
||||
|
||||
evmc::result call(const evmc_message&) noexcept final { return evmc::result{evmc_result{}}; }
|
||||
|
||||
evmc_tx_context get_tx_context() noexcept final { return {}; }
|
||||
|
||||
evmc::bytes32 get_block_hash(int64_t) noexcept final { return {}; }
|
||||
|
||||
void emit_log(const evmc::address&,
|
||||
const uint8_t*,
|
||||
size_t,
|
||||
const evmc::bytes32[],
|
||||
size_t) noexcept final
|
||||
{}
|
||||
};
|
||||
|
||||
TEST(cpp, address)
|
||||
{
|
||||
evmc::address a;
|
||||
|
@ -285,6 +326,23 @@ TEST(cpp, vm_set_option)
|
|||
EXPECT_EQ(vm.set_option("1", "2"), EVMC_SET_OPTION_INVALID_NAME);
|
||||
}
|
||||
|
||||
TEST(cpp, vm_set_option_in_constructor)
|
||||
{
|
||||
static int num_calls = 0;
|
||||
const auto set_option_method = [](evmc_vm* /*unused*/, const char* name, const char* value) {
|
||||
++num_calls;
|
||||
EXPECT_STREQ(name, "o");
|
||||
EXPECT_EQ(value, std::to_string(num_calls));
|
||||
return EVMC_SET_OPTION_INVALID_NAME;
|
||||
};
|
||||
|
||||
evmc_vm raw{EVMC_ABI_VERSION, "", "", nullptr, nullptr, nullptr, set_option_method};
|
||||
raw.destroy = [](evmc_vm*) {};
|
||||
|
||||
const auto vm = evmc::VM{&raw, {{"o", "1"}, {"o", "2"}}};
|
||||
EXPECT_EQ(num_calls, 2);
|
||||
}
|
||||
|
||||
TEST(cpp, vm_null)
|
||||
{
|
||||
evmc::VM vm;
|
||||
|
@ -365,6 +423,19 @@ TEST(cpp, vm_execute_precompiles)
|
|||
EXPECT_TRUE(std::equal(input.begin(), input.end(), res.output_data));
|
||||
}
|
||||
|
||||
TEST(cpp, vm_execute_with_null_host)
|
||||
{
|
||||
// This tests only if the used VM::execute() overload is at least implemented.
|
||||
// We know that the example VM will not use the host context in this case.
|
||||
|
||||
auto host = NullHost{};
|
||||
|
||||
auto vm = evmc::VM{evmc_create_example_vm()};
|
||||
evmc_message msg{};
|
||||
auto res = vm.execute(host, EVMC_MAX_REVISION, msg, nullptr, 0);
|
||||
EXPECT_EQ(res.status_code, EVMC_FAILURE);
|
||||
}
|
||||
|
||||
TEST(cpp, host)
|
||||
{
|
||||
// Use example host to execute all methods from the C++ host wrapper.
|
||||
|
|
Loading…
Reference in New Issue