From 1a3ca7618da8df3a428d590c717b95564e238229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Mar 2019 16:23:23 +0100 Subject: [PATCH 1/5] cpp: Rename host to HostContext --- include/evmc/evmc.hpp | 4 ++-- test/unittests/test_cpp.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index ac7932c..6a8b391 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -80,13 +80,13 @@ private: /// Wrapper around EVMC host context / host interface. -class host +class HostContext { evmc_context* context = nullptr; evmc_tx_context tx_context = {}; public: - host(evmc_context* context) noexcept : context{context} {} + HostContext(evmc_context* context) noexcept : context{context} {} // NOLINT bool account_exists(const evmc_address& address) noexcept { diff --git a/test/unittests/test_cpp.cpp b/test/unittests/test_cpp.cpp index 373a725..2012d90 100644 --- a/test/unittests/test_cpp.cpp +++ b/test/unittests/test_cpp.cpp @@ -69,7 +69,7 @@ TEST(cpp, host) // Use example host to execute all methods from the C++ host wrapper. auto* host_context = example_host_create_context(); - auto host = evmc::host{host_context}; + auto host = evmc::HostContext{host_context}; auto a = evmc_address{{1}}; auto v = evmc_bytes32{{7, 7, 7}}; From e3d49fe7d0380518b258fdc029aa2f1e8ad889f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Mar 2019 16:37:30 +0100 Subject: [PATCH 2/5] cpp: Add HostInterface interface --- include/evmc/evmc.hpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index 6a8b391..b70f761 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -79,6 +79,48 @@ private: }; +class HostInterface +{ +public: + virtual ~HostInterface() noexcept = default; + + virtual bool account_exists(const evmc_address& addr) noexcept = 0; + + virtual evmc_bytes32 get_storage(const evmc_address& addr, + const evmc_bytes32& key) noexcept = 0; + + virtual evmc_storage_status set_storage(const evmc_address& addr, + const evmc_bytes32& key, + const evmc_bytes32& value) noexcept = 0; + + virtual evmc_uint256be get_balance(const evmc_address& addr) noexcept = 0; + + virtual size_t get_code_size(const evmc_address& addr) noexcept = 0; + + virtual evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept = 0; + + virtual size_t copy_code(const evmc_address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept = 0; + + virtual void selfdestruct(const evmc_address& addr, + const evmc_address& beneficiary) noexcept = 0; + + virtual result call(const evmc_message& msg) noexcept = 0; + + virtual evmc_tx_context get_tx_context() noexcept = 0; + + virtual evmc_bytes32 get_block_hash(int64_t block_number) noexcept = 0; + + virtual void emit_log(const evmc_address& addr, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t num_topics) noexcept = 0; +}; + + /// Wrapper around EVMC host context / host interface. class HostContext { From e8249421496368ee4289e6a0949a309c0a48bfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Mar 2019 16:43:23 +0100 Subject: [PATCH 3/5] cpp: Make HostContext to implement HostInterface --- include/evmc/evmc.hpp | 30 ++++++++++++++++-------------- test/unittests/test_cpp.cpp | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index b70f761..ddd0ae7 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -122,7 +122,9 @@ public: /// Wrapper around EVMC host context / host interface. -class HostContext +/// +/// To be used by VM implementations as better alternative to using ::evmc_context directly. +class HostContext : public HostInterface { evmc_context* context = nullptr; evmc_tx_context tx_context = {}; @@ -130,34 +132,34 @@ class HostContext public: HostContext(evmc_context* context) noexcept : context{context} {} // NOLINT - bool account_exists(const evmc_address& address) noexcept + bool account_exists(const evmc_address& address) noexcept final { return context->host->account_exists(context, &address); } - evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept + evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept final { return context->host->get_storage(context, &address, &key); } evmc_storage_status set_storage(const evmc_address& address, const evmc_bytes32& key, - const evmc_bytes32& value) noexcept + const evmc_bytes32& value) noexcept final { return context->host->set_storage(context, &address, &key, &value); } - evmc_uint256be get_balance(const evmc_address& address) noexcept + evmc_uint256be get_balance(const evmc_address& address) noexcept final { return context->host->get_balance(context, &address); } - size_t get_code_size(const evmc_address& address) noexcept + size_t get_code_size(const evmc_address& address) noexcept final { return context->host->get_code_size(context, &address); } - evmc_bytes32 get_code_hash(const evmc_address& address) noexcept + evmc_bytes32 get_code_hash(const evmc_address& address) noexcept final { return context->host->get_code_hash(context, &address); } @@ -165,17 +167,17 @@ public: size_t copy_code(const evmc_address& address, size_t code_offset, uint8_t* buffer_data, - size_t buffer_size) noexcept + size_t buffer_size) noexcept final { return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } - void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) + void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) noexcept final { context->host->selfdestruct(context, &address, &beneficiary); } - result call(const evmc_message& message) noexcept + result call(const evmc_message& message) noexcept final { return result{context->host->call(context, &message)}; } @@ -185,15 +187,15 @@ public: /// The implementation caches the received transaction context /// by assuming that the block timestamp should never be zero. /// - /// @return Reference to the cached transaction context. - const evmc_tx_context& get_tx_context() noexcept + /// @return The cached transaction context. + evmc_tx_context get_tx_context() noexcept final { if (tx_context.block_timestamp == 0) tx_context = context->host->get_tx_context(context); return tx_context; } - evmc_bytes32 get_block_hash(int64_t number) noexcept + evmc_bytes32 get_block_hash(int64_t number) noexcept final { return context->host->get_block_hash(context, number); } @@ -202,7 +204,7 @@ public: const uint8_t* data, size_t data_size, const evmc_bytes32 topics[], - size_t topics_count) noexcept + size_t topics_count) noexcept final { context->host->emit_log(context, &address, data, data_size, topics, topics_count); } diff --git a/test/unittests/test_cpp.cpp b/test/unittests/test_cpp.cpp index 2012d90..8a5080a 100644 --- a/test/unittests/test_cpp.cpp +++ b/test/unittests/test_cpp.cpp @@ -89,8 +89,8 @@ TEST(cpp, host) host.selfdestruct(a, a); EXPECT_EQ(host.call({}).gas_left, 0); - auto* tx = &host.get_tx_context(); - EXPECT_EQ(&host.get_tx_context(), tx); + auto tx = host.get_tx_context(); + EXPECT_EQ(host.get_tx_context().block_number, tx.block_number); EXPECT_EQ(host.get_block_hash(0), evmc_bytes32{}); From 9eb9608b4aa5a7fbe4615fad8ad5ed25b80619c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Mar 2019 16:50:18 +0100 Subject: [PATCH 4/5] cpp: Add Host abstract class for Host implementations --- include/evmc/evmc.hpp | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index ddd0ae7..a78c6fa 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -209,4 +209,91 @@ public: context->host->emit_log(context, &address, data, data_size, topics, topics_count); } }; + +/// Abstract class to be used by Host implementations. +/// +/// 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_context and the ::evmc_context::host. +class Host : public HostInterface, public evmc_context +{ +public: + inline Host() noexcept; +}; + +namespace internal +{ +inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->account_exists(*addr); +} +inline evmc_bytes32 get_storage(evmc_context* h, + const evmc_address* addr, + const evmc_bytes32* key) noexcept +{ + return static_cast(h)->get_storage(*addr, *key); +} +inline evmc_storage_status set_storage(evmc_context* h, + const evmc_address* addr, + const evmc_bytes32* key, + const evmc_bytes32* value) noexcept +{ + return static_cast(h)->set_storage(*addr, *key, *value); +} +inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_balance(*addr); +} +inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_code_size(*addr); +} +inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept +{ + return static_cast(h)->get_code_hash(*addr); +} +inline size_t copy_code(evmc_context* h, + const evmc_address* addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept +{ + return static_cast(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); +} +inline void selfdestruct(evmc_context* h, + const evmc_address* addr, + const evmc_address* beneficiary) noexcept +{ + static_cast(h)->selfdestruct(*addr, *beneficiary); +} +inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept +{ + return static_cast(h)->call(*msg); +} +inline evmc_tx_context get_tx_context(evmc_context* h) noexcept +{ + return static_cast(h)->get_tx_context(); +} +inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept +{ + return static_cast(h)->get_block_hash(block_number); +} +inline void emit_log(evmc_context* h, + const evmc_address* addr, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t num_topics) noexcept +{ + static_cast(h)->emit_log(*addr, data, data_size, 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_context{&internal::interface} {} + } // namespace evmc From 0c1e13b485b5b9ba37da1630de23ca1417ff32fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Mar 2019 17:07:34 +0100 Subject: [PATCH 5/5] examples: Rewrite Host example to use Host abstract class --- examples/example_host.cpp | 241 +++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 136 deletions(-) diff --git a/examples/example_host.cpp b/examples/example_host.cpp index 4d8ea02..7516a5d 100644 --- a/examples/example_host.cpp +++ b/examples/example_host.cpp @@ -8,7 +8,7 @@ #include "example_host.h" -#include +#include #include #include @@ -21,153 +21,122 @@ struct account std::map storage; }; -struct example_host_context : evmc_context +class ExampleHost : public evmc::Host { - example_host_context(); - - evmc_tx_context tx_context = {}; - std::map accounts; + +public: + bool account_exists(const evmc_address& addr) noexcept final + { + return accounts.find(addr) != accounts.end(); + } + + evmc_bytes32 get_storage(const evmc_address& addr, const evmc_bytes32& key) noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.storage[key]; + return {}; + } + + evmc_storage_status set_storage(const evmc_address& addr, + const evmc_bytes32& key, + const evmc_bytes32& value) noexcept final + { + auto& account = accounts[addr]; + auto prev_value = account.storage[key]; + account.storage[key] = value; + + return (prev_value == value) ? EVMC_STORAGE_UNCHANGED : EVMC_STORAGE_MODIFIED; + } + + evmc_uint256be get_balance(const evmc_address& addr) noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.balance; + return {}; + } + + size_t get_code_size(const evmc_address& addr) noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.code_size; + return 0; + } + + evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept final + { + auto it = accounts.find(addr); + if (it != accounts.end()) + return it->second.code_hash; + return {}; + } + + size_t copy_code(const evmc_address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) noexcept final + { + (void)addr; + (void)code_offset; + (void)buffer_data; + (void)buffer_size; + return 0; + } + + void selfdestruct(const evmc_address& addr, const evmc_address& beneficiary) noexcept final + { + (void)addr; + (void)beneficiary; + } + + evmc::result call(const evmc_message& msg) noexcept final + { + (void)msg; + // TODO: Improve C++ API for result creation. + evmc_result res{}; + res.status_code = EVMC_FAILURE; + return evmc::result{res}; + } + + evmc_tx_context get_tx_context() noexcept final { return {}; } + + evmc_bytes32 get_block_hash(int64_t number) noexcept final + { + int64_t current_block_number = get_tx_context().block_number; + + auto example_block_hash = evmc_bytes32{}; + if (number < current_block_number && number >= current_block_number - 256) + example_block_hash = {{1, 1, 1, 1}}; + return example_block_hash; + } + + void emit_log(const evmc_address& addr, + const uint8_t* data, + size_t data_size, + const evmc_bytes32 topics[], + size_t topics_count) noexcept final + { + (void)addr; + (void)data; + (void)data_size; + (void)topics; + (void)topics_count; + } }; -static bool account_exists(evmc_context* context, const evmc_address* address) -{ - auto* host = static_cast(context); - return host->accounts.find(*address) != host->accounts.end(); -} - -static evmc_bytes32 get_storage(evmc_context* context, - const evmc_address* address, - const evmc_bytes32* key) -{ - auto* host = static_cast(context); - auto it = host->accounts.find(*address); - if (it != host->accounts.end()) - return it->second.storage[*key]; - return {}; -} - -static evmc_storage_status set_storage(evmc_context* context, - const evmc_address* address, - const evmc_bytes32* key, - const evmc_bytes32* value) -{ - auto* host = static_cast(context); - auto& account = host->accounts[*address]; - auto prev_value = account.storage[*key]; - account.storage[*key] = *value; - - return (prev_value == *value) ? EVMC_STORAGE_UNCHANGED : EVMC_STORAGE_MODIFIED; -} - -static evmc_uint256be get_balance(evmc_context* context, const evmc_address* address) -{ - auto* host = static_cast(context); - auto it = host->accounts.find(*address); - if (it != host->accounts.end()) - return it->second.balance; - return {}; -} - -static size_t get_code_size(evmc_context* context, const evmc_address* address) -{ - auto* host = static_cast(context); - auto it = host->accounts.find(*address); - if (it != host->accounts.end()) - return it->second.code_size; - return 0; -} - -static evmc_bytes32 get_code_hash(evmc_context* context, const evmc_address* address) -{ - auto* host = static_cast(context); - auto it = host->accounts.find(*address); - if (it != host->accounts.end()) - return it->second.code_hash; - return {}; -} - -static size_t copy_code(evmc_context* context, - const evmc_address* address, - size_t code_offset, - uint8_t* buffer_data, - size_t buffer_size) -{ - (void)context; - (void)address; - (void)code_offset; - (void)buffer_data; - (void)buffer_size; - return 0; -} - -static void selfdestruct(evmc_context* context, - const evmc_address* address, - const evmc_address* beneficiary) -{ - (void)context; - (void)address; - (void)beneficiary; -} - -static evmc_result call(evmc_context* context, const evmc_message* msg) -{ - (void)context; - (void)msg; - evmc_result result{}; - result.status_code = EVMC_FAILURE; - return result; -} - -static evmc_tx_context get_tx_context(evmc_context* context) -{ - (void)context; - evmc_tx_context result{}; - return result; -} - -static evmc_bytes32 get_block_hash(evmc_context* context, int64_t number) -{ - auto* host = static_cast(context); - int64_t current_block_number = host->tx_context.block_number; - - auto example_block_hash = evmc_bytes32{}; - if (number < current_block_number && number >= current_block_number - 256) - example_block_hash = {{1, 1, 1, 1}}; - return example_block_hash; -} - -static void emit_log(evmc_context* context, - const evmc_address* address, - const uint8_t* data, - size_t data_size, - const evmc_bytes32 topics[], - size_t topics_count) -{ - (void)context; - (void)address; - (void)data; - (void)data_size; - (void)topics; - (void)topics_count; -} - -static const 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, -}; - -example_host_context::example_host_context() : evmc_context{&interface} {} extern "C" { evmc_context* example_host_create_context() { - return new example_host_context; + return new ExampleHost; } void example_host_destroy_context(evmc_context* context) { - delete context; + delete static_cast(context); } }