Move evmc_host_interface out of evmc_host_context

This commit is contained in:
Paweł Bylica 2019-10-31 10:38:17 +01:00
parent bd65396c7e
commit e3b84b8b48
No known key found for this signature in database
GPG Key ID: 7A0C037434FE77EF
10 changed files with 115 additions and 74 deletions

View File

@ -48,6 +48,7 @@ int main(int argc, char* argv[])
tx_context.block_number = 42;
tx_context.block_timestamp = 66;
tx_context.block_gas_limit = gas * 2;
const struct evmc_host_interface* host = example_host_get_interface();
struct evmc_host_context* ctx = example_host_create_context(tx_context);
struct evmc_message msg;
msg.kind = EVMC_CALL;
@ -58,7 +59,7 @@ int main(int argc, char* argv[])
msg.input_size = sizeof(input);
msg.gas = gas;
msg.depth = 0;
struct evmc_result result = evmc_execute(vm, ctx, EVMC_HOMESTEAD, &msg, code, code_size);
struct evmc_result result = evmc_execute(vm, host, ctx, EVMC_HOMESTEAD, &msg, code, code_size);
printf("Execution result:\n");
int exit_code = 0;
if (result.status_code != EVMC_SUCCESS)
@ -77,7 +78,7 @@ int main(int argc, char* argv[])
printf("%02x", result.output_data[i]);
printf("\n");
const evmc_bytes32 storage_key = {{0}};
evmc_bytes32 storage_value = ctx->host->get_storage(ctx, &msg.destination, &storage_key);
evmc_bytes32 storage_value = host->get_storage(ctx, &msg.destination, &storage_key);
printf(" Storage at 0x00..00: ");
for (i = 0; i < sizeof(storage_value.bytes) / sizeof(storage_value.bytes[0]); i++)
printf("%02x", storage_value.bytes[i]);

View File

@ -160,13 +160,19 @@ public:
extern "C" {
const evmc_host_interface* example_host_get_interface()
{
return evmc::Host::get_interface();
}
evmc_host_context* example_host_create_context(evmc_tx_context tx_context)
{
return new ExampleHost(tx_context);
auto host = new ExampleHost{tx_context};
return host->to_context();
}
void example_host_destroy_context(evmc_host_context* context)
{
delete static_cast<ExampleHost*>(context);
delete evmc::Host::from_context<ExampleHost>(context);
}
}

View File

@ -9,6 +9,8 @@
extern "C" {
#endif
const struct evmc_host_interface* example_host_get_interface();
struct evmc_host_context* example_host_create_context(struct evmc_tx_context tx_context);
void example_host_destroy_context(struct evmc_host_context* context);

View File

@ -48,8 +48,9 @@ static evmc_result not_implemented()
return result;
}
static evmc_result execute(evmc_vm*,
evmc_host_context*,
static evmc_result execute(evmc_vm* /*unused*/,
const evmc_host_interface* /*unused*/,
evmc_host_context* /*unused*/,
enum evmc_revision rev,
const evmc_message* msg,
const uint8_t*,

View File

@ -75,6 +75,7 @@ static void free_result_output_data(const struct evmc_result* result)
/// The example implementation of the evmc_vm::execute() method.
static struct evmc_result execute(struct evmc_vm* instance,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,
@ -142,16 +143,16 @@ static struct evmc_result execute(struct evmc_vm* instance,
strncmp((const char*)code, counter, code_size) == 0)
{
const evmc_bytes32 key = {{0}};
evmc_bytes32 value = context->host->get_storage(context, &msg->destination, &key);
evmc_bytes32 value = host->get_storage(context, &msg->destination, &key);
value.bytes[31]++;
context->host->set_storage(context, &msg->destination, &key, &value);
host->set_storage(context, &msg->destination, &key, &value);
ret.status_code = EVMC_SUCCESS;
return ret;
}
else if (code_size == (sizeof(return_block_number) - 1) &&
strncmp((const char*)code, return_block_number, code_size) == 0)
{
const struct evmc_tx_context tx_context = context->host->get_tx_context(context);
const struct evmc_tx_context tx_context = host->get_tx_context(context);
const size_t output_size = 20;
uint8_t* output_data = (uint8_t*)calloc(1, output_size);
@ -166,7 +167,7 @@ static struct evmc_result execute(struct evmc_vm* instance,
else if (code_size == (sizeof(save_return_block_number) - 1) &&
strncmp((const char*)code, save_return_block_number, code_size) == 0)
{
const struct evmc_tx_context tx_context = context->host->get_tx_context(context);
const struct evmc_tx_context tx_context = host->get_tx_context(context);
const size_t output_size = 20;
// Store block number.
@ -174,7 +175,7 @@ static struct evmc_result execute(struct evmc_vm* instance,
evmc_bytes32 value = {{0}};
// NOTE: assume block number is <= 255
value.bytes[31] = (uint8_t)tx_context.block_number;
context->host->set_storage(context, &msg->destination, &key, &value);
host->set_storage(context, &msg->destination, &key, &value);
// Return block number.
uint8_t* output_data = (uint8_t*)calloc(1, output_size);
@ -195,7 +196,7 @@ static struct evmc_result execute(struct evmc_vm* instance,
call_msg.depth = msg->depth + 1;
call_msg.gas = msg->gas - (msg->gas / 64);
call_msg.sender = msg->destination;
return context->host->call(context, &call_msg);
return host->call(context, &call_msg);
}
ret.status_code = EVMC_FAILURE;

View File

@ -156,6 +156,11 @@ struct evmc_tx_context
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
};
/**
* @struct evmc_host_context
* The opaque data type representing the Host execution context.
* @see evmc_execute_fn().
*/
struct evmc_host_context;
/**
@ -653,22 +658,6 @@ struct evmc_host_interface
};
/**
* Execution context managed by the Host.
*
* The Host MUST pass the pointer to the execution context to ::evmc_execute_fn.
* The VM MUST pass the same pointer back to the Host in every callback function.
* The context MUST contain at least the function table defining
* the context callback interface.
* Optionally, the Host MAY include in the context additional data.
*/
struct evmc_host_context
{
/** The Host interface. */
const struct evmc_host_interface* host;
};
/* Forward declaration. */
struct evmc_vm;
@ -791,10 +780,12 @@ enum evmc_revision
* This function MAY be invoked multiple times for a single VM instance.
*
* @param vm The VM instance. This argument MUST NOT be NULL.
* @param context The pointer to the Host execution context to be passed
* to the Host interface methods (::evmc_host_interface).
* This argument MUST NOT be NULL unless
* @param host The Host interface. This argument MUST NOT be NULL unless
* the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability.
* @param context The opaque pointer to the Host execution context.
* This argument MAY be NULL. The VM MUST pass the same
* pointer to the methods of the @p host interface.
* The VM MUST NOT dereference the pointer.
* @param rev The requested EVM specification revision.
* @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL.
* @param code The reference to the code to be executed. This argument MAY be NULL.
@ -802,6 +793,7 @@ enum evmc_revision
* @return The execution result.
*/
typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,

View File

@ -398,13 +398,14 @@ public:
}
/// @copydoc evmc_execute()
result execute(evmc_host_context& ctx,
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, &ctx, rev, &msg, code, code_size)};
return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
}
private:
@ -478,43 +479,46 @@ public:
/// To be used by VM implementations as better alternative to using ::evmc_host_context directly.
class HostContext : public HostInterface
{
const evmc_host_interface* host = nullptr;
evmc_host_context* context = nullptr;
evmc_tx_context tx_context = {};
public:
/// Implicit converting constructor from evmc_host_context.
HostContext(evmc_host_context* ctx) noexcept : context{ctx} {} // NOLINT
HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept
: host{interface}, context{ctx}
{}
bool account_exists(const address& address) noexcept final
{
return context->host->account_exists(context, &address);
return host->account_exists(context, &address);
}
bytes32 get_storage(const address& address, const bytes32& key) noexcept final
{
return context->host->get_storage(context, &address, &key);
return host->get_storage(context, &address, &key);
}
evmc_storage_status set_storage(const address& address,
const bytes32& key,
const bytes32& value) noexcept final
{
return context->host->set_storage(context, &address, &key, &value);
return host->set_storage(context, &address, &key, &value);
}
uint256be get_balance(const address& address) noexcept final
{
return context->host->get_balance(context, &address);
return host->get_balance(context, &address);
}
size_t get_code_size(const address& address) noexcept final
{
return context->host->get_code_size(context, &address);
return host->get_code_size(context, &address);
}
bytes32 get_code_hash(const address& address) noexcept final
{
return context->host->get_code_hash(context, &address);
return host->get_code_hash(context, &address);
}
size_t copy_code(const address& address,
@ -522,17 +526,17 @@ public:
uint8_t* buffer_data,
size_t buffer_size) noexcept final
{
return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
context->host->selfdestruct(context, &addr, &beneficiary);
host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
{
return result{context->host->call(context, &message)};
return result{host->call(context, &message)};
}
/// @copydoc HostInterface::get_tx_context()
@ -544,13 +548,13 @@ public:
evmc_tx_context get_tx_context() noexcept final
{
if (tx_context.block_timestamp == 0)
tx_context = context->host->get_tx_context(context);
tx_context = host->get_tx_context(context);
return tx_context;
}
bytes32 get_block_hash(int64_t number) noexcept final
{
return context->host->get_block_hash(context, number);
return host->get_block_hash(context, number);
}
void emit_log(const address& addr,
@ -559,7 +563,7 @@ public:
const bytes32 topics[],
size_t topics_count) noexcept final
{
context->host->emit_log(context, &addr, data, data_size, topics, topics_count);
host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
};
@ -567,43 +571,63 @@ public:
///
/// When implementing EVMC Host, you can directly inherit from the evmc::Host class.
/// This way your implementation will be simpler by avoiding manual handling
/// of the ::evmc_host_context and the ::evmc_host_context::host.
class Host : public HostInterface, public evmc_host_context
/// of the ::evmc_host_context and the ::evmc_host_interface.
class Host : public HostInterface
{
public:
inline Host() noexcept;
/// Provides access to the global host interface.
/// @returns Pointer to the host interface object.
static const evmc_host_interface* get_interface() noexcept;
/// Converts the Host object to the opaque host context pointer.
/// @returns Pointer to evmc_host_context.
evmc_host_context* to_context() noexcept { return reinterpret_cast<evmc_host_context*>(this); }
/// Converts the opaque host context pointer back to the original Host object.
/// @tparam DerivedClass The class derived from the Host class.
/// @param context The opaque host context pointer.
/// @returns The pointer to DerivedClass.
template <typename DerivedClass>
static DerivedClass* from_context(evmc_host_context* context) noexcept
{
// Get pointer of the Host base class.
auto* h = reinterpret_cast<Host*>(context);
// Additional downcast, only possible if DerivedClass inherits from Host.
return static_cast<DerivedClass*>(h);
}
};
namespace internal
{
inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept
{
return static_cast<Host*>(h)->account_exists(*addr);
return reinterpret_cast<Host*>(h)->account_exists(*addr);
}
inline evmc_bytes32 get_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key) noexcept
{
return static_cast<Host*>(h)->get_storage(*addr, *key);
return reinterpret_cast<Host*>(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 static_cast<Host*>(h)->set_storage(*addr, *key, *value);
return reinterpret_cast<Host*>(h)->set_storage(*addr, *key, *value);
}
inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept
{
return static_cast<Host*>(h)->get_balance(*addr);
return reinterpret_cast<Host*>(h)->get_balance(*addr);
}
inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept
{
return static_cast<Host*>(h)->get_code_size(*addr);
return reinterpret_cast<Host*>(h)->get_code_size(*addr);
}
inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept
{
return static_cast<Host*>(h)->get_code_hash(*addr);
return reinterpret_cast<Host*>(h)->get_code_hash(*addr);
}
inline size_t copy_code(evmc_host_context* h,
const evmc_address* addr,
@ -611,25 +635,25 @@ inline size_t copy_code(evmc_host_context* h,
uint8_t* buffer_data,
size_t buffer_size) noexcept
{
return static_cast<Host*>(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
return reinterpret_cast<Host*>(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
{
static_cast<Host*>(h)->selfdestruct(*addr, *beneficiary);
reinterpret_cast<Host*>(h)->selfdestruct(*addr, *beneficiary);
}
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
{
return static_cast<Host*>(h)->call(*msg).release_raw();
return reinterpret_cast<Host*>(h)->call(*msg).release_raw();
}
inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept
{
return static_cast<Host*>(h)->get_tx_context();
return reinterpret_cast<Host*>(h)->get_tx_context();
}
inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept
{
return static_cast<Host*>(h)->get_block_hash(block_number);
return reinterpret_cast<Host*>(h)->get_block_hash(block_number);
}
inline void emit_log(evmc_host_context* h,
const evmc_address* addr,
@ -638,16 +662,25 @@ inline void emit_log(evmc_host_context* h,
const evmc_bytes32 topics[],
size_t num_topics) noexcept
{
static_cast<Host*>(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
num_topics);
reinterpret_cast<Host*>(h)->emit_log(*addr, data, data_size,
static_cast<const bytes32*>(topics), num_topics);
}
constexpr evmc_host_interface interface{
account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash,
copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log};
} // namespace internal
inline Host::Host() noexcept : evmc_host_context{&evmc::internal::interface} {}
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};
return &interface;
}
} // namespace evmc

View File

@ -85,13 +85,14 @@ static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm,
* @see evmc_execute_fn.
*/
static inline struct evmc_result evmc_execute(struct evmc_vm* vm,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,
uint8_t const* code,
size_t code_size)
{
return vm->execute(vm, context, rev, msg, code, code_size);
return vm->execute(vm, host, context, rev, msg, code, code_size);
}
/// The evmc_result release function using free() for releasing the memory.

View File

@ -268,8 +268,8 @@ TEST(cpp, vm)
EXPECT_EQ(vm.name(), std::string{"example_vm"});
EXPECT_NE(vm.version()[0], 0);
auto ctx = evmc_host_context{};
auto res = vm.execute(ctx, EVMC_MAX_REVISION, {}, nullptr, 0);
const auto host = evmc_host_interface{};
auto res = vm.execute(host, nullptr, EVMC_MAX_REVISION, {}, nullptr, 0);
EXPECT_EQ(res.status_code, EVMC_FAILURE);
EXPECT_TRUE(vm.get_capabilities() & EVMC_CAPABILITY_EVM1);
@ -348,8 +348,9 @@ TEST(cpp, host)
{
// Use example host to execute all methods from the C++ host wrapper.
auto* host_interface = example_host_get_interface();
auto* host_context = example_host_create_context(evmc_tx_context{});
auto host = evmc::HostContext{host_context};
auto host = evmc::HostContext{host_interface, host_context};
const auto a = evmc::address{{{1}}};
const auto v = evmc::bytes32{{{7, 7, 7}}};
@ -382,8 +383,9 @@ TEST(cpp, host_call)
{
// Use example host to test Host::call() method.
auto* host_interface = example_host_get_interface();
auto* host_context = example_host_create_context(evmc_tx_context{});
auto host = evmc::HostContext{host_context};
auto host = evmc::HostContext{host_interface, host_context};
EXPECT_EQ(host.call({}).gas_left, 0);

View File

@ -59,12 +59,13 @@ TEST_F(evmc_vm_test, capabilities)
TEST_F(evmc_vm_test, execute_call)
{
const evmc_host_interface* host = example_host_get_interface();
evmc_host_context* context = example_host_create_context(evmc_tx_context{});
evmc_message msg{};
std::array<uint8_t, 2> code = {{0xfe, 0x00}};
evmc_result result =
vm->execute(vm, context, EVMC_MAX_REVISION, &msg, code.data(), code.size());
vm->execute(vm, host, context, EVMC_MAX_REVISION, &msg, code.data(), code.size());
// Validate some constraints
if (result.status_code != EVMC_SUCCESS && result.status_code != EVMC_REVERT)
@ -92,6 +93,7 @@ TEST_F(evmc_vm_test, execute_call)
TEST_F(evmc_vm_test, execute_create)
{
const evmc_host_interface* host = example_host_get_interface();
evmc_host_context* context = example_host_create_context(evmc_tx_context{});
evmc_message msg{
EVMC_CREATE, 0, 0, 65536, evmc_address{}, evmc_address{}, nullptr, 0, evmc_uint256be{},
@ -99,7 +101,7 @@ TEST_F(evmc_vm_test, execute_create)
std::array<uint8_t, 2> code = {{0xfe, 0x00}};
evmc_result result =
vm->execute(vm, context, EVMC_MAX_REVISION, &msg, code.data(), code.size());
vm->execute(vm, host, context, EVMC_MAX_REVISION, &msg, code.data(), code.size());
// Validate some constraints
if (result.status_code != EVMC_SUCCESS && result.status_code != EVMC_REVERT)
@ -181,7 +183,7 @@ TEST_F(evmc_vm_test, precompile_test)
EVMC_CALL, 0, 0, 65536, destination, evmc_address{}, nullptr, 0, evmc_uint256be{},
evmc_bytes32{}};
evmc_result result = vm->execute(vm, nullptr, EVMC_MAX_REVISION, &msg, nullptr, 0);
evmc_result result = vm->execute(vm, nullptr, nullptr, EVMC_MAX_REVISION, &msg, nullptr, 0);
// Validate some constraints